Merge branch 'integration_2025_w13' into 'develop'

Integration: `2025.w13`

Closes #923, #924, #928, and #925

See merge request oai/openairinterface5g!3345

* !3300 Allow RSRP report handling in phy-test mode
* !3318 Improve handling of some pointers causing potential segfault
* !3321 DLSCH scheduler LOGs assertion prevention in case of HARQ disabled
* !3335 Other compilation improvements
* !3338 prevent access to NULL pointer in RELEASE_IE_FROMLIST
* !3337 Fix for wrong NSA init at UE
* !3339 fix NTN regression by correctly setting cellBarredNTN_r17 for NTN band again
* !3340 NR UE: fix nr_pbch_channel_estimation()
* !3260 Shared memory realtime radio simulation
* !3341 rfsimulator: apply deadlock detection only if more than one client is connected
* !3343 K-offset updated with configured sub carrier spacing
* !3331 Add documentation for Foxconn RU
* !3216 FHI72: Management plane support
* !3295 Fix SRS report TLV unpack
This commit is contained in:
Jaroslava Fiedlerova
2025-04-01 09:36:38 +00:00
98 changed files with 15348 additions and 439 deletions
+4 -4
View File
@@ -450,7 +450,7 @@ add_library(f1ap
${F1AP_DIR}/f1ap_handlers.c
${F1AP_DIR}/f1ap_itti_messaging.c)
target_include_directories(f1ap PUBLIC F1AP_DIR)
target_link_libraries(f1ap PUBLIC asn1_f1ap L2_NR)
target_link_libraries(f1ap PUBLIC asn1_f1ap GTPV1U)
target_link_libraries(f1ap PRIVATE ngap nr_rrc HASHTABLE f1ap_lib)
target_include_directories(f1ap PRIVATE ${F1AP_DIR}/lib)
@@ -1351,7 +1351,7 @@ target_link_libraries(L2_UE PRIVATE GTPV1U)
target_link_libraries(L2_UE PRIVATE asn1_lte_rrc_hdrs)
add_library( NR_L2_UE ${NR_L2_SRC_UE} ${MAC_NR_SRC_UE} )
target_link_libraries(NR_L2_UE PRIVATE f1ap nr_rlc)
target_link_libraries(NR_L2_UE PRIVATE nr_rlc)
target_link_libraries(NR_L2_UE PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_common nr_ue_power_procedures nr_ue_ra_procedures)
target_link_libraries(NR_L2_UE PRIVATE nr_nas)
@@ -1885,14 +1885,14 @@ add_executable(nr-uesoftmodem
${OPENAIR2_DIR}/LAYER2/NR_MAC_COMMON/nr_mac_common.c
${OPENAIR2_DIR}/LAYER2/NR_MAC_COMMON/nr_mac_common_tdd.c
${OPENAIR1_DIR}/PHY/TOOLS/phy_scope_interface.c
${NFAPI_USER_DIR}/nfapi.c
${PHY_INTERFACE_DIR}/queue_t.c
)
target_link_libraries(nr-uesoftmodem PRIVATE
-Wl,--start-group
nr_rrc SECURITY UTIL HASHTABLE SCHED_RU_LIB SCHED_NR_UE_LIB
PHY_COMMON PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB NFAPI_PNF_LIB
NFAPI_USER_LIB MISC_NFAPI_NR_LIB
PHY_COMMON PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB
ITTI SIMU shlib_loader
-Wl,--end-group z dl)
@@ -43,7 +43,7 @@
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<svr_id>0</svr_id>
<images>oai-gnb-asan oai-nr-ue-asan</images>
<images>oai-gnb oai-nr-ue</images>
</testCase>
<testCase id="800813">
<class>Create_Workspace</class>
@@ -134,7 +134,7 @@
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<svr_id>0</svr_id>
<images>oai-gnb-asan oai-nr-ue-asan</images>
<images>oai-gnb oai-nr-ue</images>
</testCase>
</testCaseList>
@@ -86,8 +86,7 @@ services:
cap_drop:
- ALL
environment:
USE_ADDITIONAL_OPTIONS: -E --rfsim --log_config.global_log_options level,nocolor,time
ASAN_OPTIONS: detect_leaks=0
USE_ADDITIONAL_OPTIONS: -E --log_config.global_log_options level,nocolor,time --device.name vrtsim --vrtsim.role server --vrtsim.timescale 0.5
depends_on:
- oai-ext-dn
networks:
@@ -95,11 +94,13 @@ services:
ipv4_address: 192.168.71.140
volumes:
- ../../conf_files/gnb.sa.band66.106prb.rfsim.conf:/opt/oai-gnb/etc/gnb.conf
- tmp_data:/tmp/
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
interval: 10s
timeout: 5s
retries: 5
ipc: host
oai-nr-ue:
image: ${REGISTRY:-oaisoftwarealliance}/${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
@@ -109,7 +110,7 @@ services:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
environment:
USE_ADDITIONAL_OPTIONS: -E --rfsim -r 106 --numerology 1 --uicc0.imsi 208990100001100 --band 66 -C 2169090000 --CO -400000000 --ssb 378 --rfsimulator.serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
USE_ADDITIONAL_OPTIONS: -E -r 106 --numerology 1 --uicc0.imsi 208990100001100 --band 66 -C 2169090000 --CO -400000000 --ssb 378 --log_config.global_log_options level,nocolor,time --device.name vrtsim
depends_on:
- oai-gnb
networks:
@@ -119,11 +120,13 @@ services:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
- tmp_data:/tmp/
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
interval: 10s
timeout: 5s
retries: 5
ipc: host
networks:
public_net:
@@ -142,3 +145,6 @@ networks:
- subnet: 192.168.72.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-traffic"
volumes:
tmp_data:
+6 -8
View File
@@ -99,7 +99,7 @@ Options:
USRP, BLADERF, LMSSDR, IRIS, SIMU, AW2SORI, AERIAL, None (Default)
Adds this RF board support (in external packages installation and in compilation)
-t | --transport
Selects the transport protocol type, options: None, Ethernet, benetel4g, benetel5g, oran_fhlib_5g
Selects the transport protocol type, options: None, Ethernet, benetel4g, benetel5g, oran_fhlib_5g, oran_fhlib_5g_mplane
-P | --phy_simulators
Makes the unitary tests Layer 1 simulators
-s | --check
@@ -304,6 +304,10 @@ function main() {
TARGET_LIST="$TARGET_LIST $2"
CMAKE_CMD="$CMAKE_CMD -DOAI_FHI72=ON"
;;
"oran_fhlib_5g_mplane")
TARGET_LIST="$TARGET_LIST $2"
CMAKE_CMD="$CMAKE_CMD -DOAI_FHI72=ON -DOAI_FHI72_MPLANE=ON"
;;
"None")
;;
*)
@@ -485,7 +489,7 @@ function main() {
# add some default libraries that should always be built
# for eNB, gNB, UEs, simulators
if [[ $gNB == 1 || $eNB == 1 || $UE == 1 || $nrUE == 1 || $SIMUS_PHY == 1 || $RU == 1 ]]; then
TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml"
TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml vrtsim"
fi
mkdir -p $DIR/$BUILD_DIR/build
@@ -504,12 +508,6 @@ function main() {
echo_info "Built Doxygen based documentation. The documentation file is located here: $DIR/$BUILD_DIR/build/doc/html/index.html"
fi
# TODO: once we got the CMakeLists.txt file done for the ORAN files, remove the following lines
if [[ $TARGET_LIST =~ "oran_fhlib_5g" ]]; then
rm -f liboai_transpro.so
ln -s liboran_fhlib_5g.so liboai_transpro.so
fi
if [ "$UE" = 1 ] ; then
echo_info "Compiling UE specific part"
@@ -1,53 +0,0 @@
# - Try to find the LibXml2 xml processing library
# Once done this will define
#
# LIBXML2_FOUND - System has LibXml2
# LIBXML2_INCLUDE_DIR - The LibXml2 include directory
# LIBXML2_LIBRARIES - The libraries needed to use LibXml2
# LIBXML2_DEFINITIONS - Compiler switches required for using LibXml2
# LIBXML2_XMLLINT_EXECUTABLE - The XML checking tool xmllint coming with LibXml2
#=============================================================================
# Copyright 2006-2009 Kitware, Inc.
# Copyright 2006 Alexander Neundorf <neundorf@kde.org>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distributed this file outside of CMake, substitute the full
# License text for the above reference.)
# use pkg-config to get the directories and then use these values
# in the FIND_PATH() and FIND_LIBRARY() calls
FIND_PACKAGE(PkgConfig)
PKG_CHECK_MODULES(PC_LIBXML libxml-2.0)
SET(LIBXML2_DEFINITIONS ${PC_LIBXML_CFLAGS_OTHER})
FIND_PATH(LIBXML2_INCLUDE_DIR NAMES libxml/xpath.h
HINTS
${PC_LIBXML_INCLUDEDIR}
${PC_LIBXML_INCLUDE_DIRS}
PATH_SUFFIXES libxml2
)
FIND_LIBRARY(LIBXML2_LIBRARIES NAMES xml2 libxml2
HINTS
${PC_LIBXML_LIBDIR}
${PC_LIBXML_LIBRARY_DIRS}
)
FIND_PROGRAM(LIBXML2_XMLLINT_EXECUTABLE xmllint)
# for backwards compat. with KDE 4.0.x:
SET(XMLLINT_EXECUTABLE "${LIBXML2_XMLLINT_EXECUTABLE}")
# handle the QUIETLY and REQUIRED arguments and set LIBXML2_FOUND to TRUE if
# all listed variables are TRUE
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibXml2 DEFAULT_MSG LIBXML2_LIBRARIES LIBXML2_INCLUDE_DIR)
MARK_AS_ADVANCED(LIBXML2_INCLUDE_DIR LIBXML2_LIBRARIES LIBXML2_XMLLINT_EXECUTABLE)
+6 -4
View File
@@ -75,7 +75,6 @@ endif()
find_path(xran_INCLUDE_DIR
NAMES
xran_common.h
xran_compression.h
xran_cp_api.h
xran_ecpri_owd_measurements.h
@@ -84,7 +83,7 @@ find_path(xran_INCLUDE_DIR
xran_pkt_up.h
xran_sync_api.h
HINTS ${xran_LOCATION}
PATH_SUFFIXES api include
PATH_SUFFIXES api
NO_DEFAULT_PATH
)
find_library(xran_LIBRARY
@@ -127,16 +126,19 @@ find_package_handle_standard_args(xran
VERSION_VAR xran_VERSION
)
# in proper usage of cmake, include directory should only contain "api", but not header files under "src" directory;
# however, we use xran_dev_get_ctx() and xran_dev_get_ctx_by_id() functions which are defined in xran_common.h;
# since xran_common.h is under "src" directory, we have to include it in ${xran_INCLUDE_DIRS}
if(xran_FOUND)
set(xran_LIBRARIES ${xran_LIBRARY})
set(xran_INCLUDE_DIRS ${xran_INCLUDE_DIR})
set(xran_INCLUDE_DIRS ${xran_INCLUDE_DIR} ${xran_INCLUDE_DIR}/../src)
endif()
if(xran_FOUND AND NOT TARGET xran::xran)
add_library(xran::xran UNKNOWN IMPORTED)
set_target_properties(xran::xran PROPERTIES
IMPORTED_LOCATION "${xran_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${xran_INCLUDE_DIR}"
INTERFACE_INCLUDE_DIRECTORIES "${xran_INCLUDE_DIRS}"
)
endif()
+8 -2
View File
@@ -546,7 +546,10 @@ check_install_additional_tools (){
libforms-bin \
libforms-dev \
libxft-dev \
xmlstarlet"
xmlstarlet \
libpcre3-dev \
libssh-dev \
libxml2-dev"
elif [[ "$OS_DISTRO" == "rhel" ]] || [[ "$OS_DISTRO" == "centos" ]] || [[ "$OS_DISTRO" == "rocky" ]] || [[ "$OS_DISTRO" == "fedora" ]]; then
PACKAGE_LIST="\
doxygen \
@@ -556,7 +559,10 @@ check_install_additional_tools (){
libXft-devel \
xforms \
xforms-devel \
xmlstarlet"
xmlstarlet \
pcre-devel \
libssh-devel \
libxml2-devel"
fi
$SUDO $INSTALLER install -y $PACKAGE_LIST $optional_packages
}
+1
View File
@@ -21,3 +21,4 @@ if (ENABLE_TESTS)
endif()
add_subdirectory(barrier)
add_subdirectory(actor)
add_subdirectory(shm_iq_channel)
+19 -11
View File
@@ -52,6 +52,13 @@
// main log variables
/** @defgroup _max_length Maximum Length of LOG
* @ingroup _macro
* @brief the macros that describe the maximum length of LOG
* @{*/
#define MAX_LOG_TOTAL 16384 /*!< \brief the maximum length of a log */
// Fixme: a better place to be shure it is called
void read_cpu_hardware (void) __attribute__ ((constructor));
#if !defined(__arm__) && !defined(__aarch64__)
@@ -877,31 +884,32 @@ static void log_output_memory(log_component_t *c, const char *file, const char *
* correctly. It was not a big problem because in practice MAX_LOG_TOTAL is
* big enough so that the buffer is never full.
*/
char log_buffer[MAX_LOG_TOTAL];
static_assert(4 * MAX_LOG_TOTAL <= 65536, "log buffer limited to 64kB, please reduce MAX_LOG_TOTAL\n");
char log_buffer[4 * MAX_LOG_TOTAL];
// make sure that for log trace the extra info is only printed once, reset when the level changes
if (level < OAILOG_TRACE) {
int n = log_header(c, log_buffer+len, MAX_LOG_TOTAL, file, func, line, level);
int n = log_header(c, log_buffer+len, sizeof(log_buffer), file, func, line, level);
if (n > 0) {
len += n;
if (len > MAX_LOG_TOTAL) {
len = MAX_LOG_TOTAL;
if (len > sizeof(log_buffer)) {
len = sizeof(log_buffer);
}
}
}
int n = vsnprintf(log_buffer+len, MAX_LOG_TOTAL-len, format, args);
int n = vsnprintf(log_buffer+len, sizeof(log_buffer)-len, format, args);
if (n > 0) {
len += n;
if (len > MAX_LOG_TOTAL) {
len = MAX_LOG_TOTAL;
if (len > sizeof(log_buffer)) {
len = sizeof(log_buffer);
}
}
if (!((g_log->flag) & FLAG_NOCOLOR)) {
int n = snprintf(log_buffer+len, MAX_LOG_TOTAL-len, "%s", log_level_highlight_end[level]);
int n = snprintf(log_buffer+len, sizeof(log_buffer)-len, "%s", log_level_highlight_end[level]);
if (n > 0) {
len += n;
if (len > MAX_LOG_TOTAL) {
len = MAX_LOG_TOTAL;
if (len > sizeof(log_buffer)) {
len = sizeof(log_buffer);
}
}
}
@@ -941,7 +949,7 @@ static void log_output_memory(log_component_t *c, const char *file, const char *
}
}
}else{
AssertFatal(len >= 0 && len <= MAX_LOG_TOTAL, "Bad len %d\n", len);
AssertFatal(len >= 0 && len <= sizeof(log_buffer), "Bad len %d\n", len);
if (write(fileno(c->stream), log_buffer, len)) {};
}
}
-6
View File
@@ -65,12 +65,6 @@
extern "C" {
#endif
/** @defgroup _max_length Maximum Length of LOG
* @ingroup _macro
* @brief the macros that describe the maximum length of LOG
* @{*/
#define MAX_LOG_TOTAL 16384 /*!< \brief the maximum length of a log */
/** @}*/
/** @defgroup _log_level Message levels defined by LOG
+2 -2
View File
@@ -1,6 +1,6 @@
add_library(actor actor.c)
target_include_directories(actor PUBLIC ./)
target_link_libraries(actor PUBLIC thread-pool)
# Only include header files due to issues with ODR in threadPool binaries
target_include_directories(actor PUBLIC ./ ../threadPool/)
if (ENABLE_TESTS)
add_subdirectory(tests)
endif()
+4
View File
@@ -78,6 +78,10 @@
// Prints an error if ID not found in list.
#define RELEASE_IE_FROMLIST(SOURCE, TARGET, FIELD) \
do { \
if (!TARGET) { \
LOG_E(NR_MAC, "Target list not present, impossible to release element\n"); \
break; \
} \
for (int iI = 0; iI < SOURCE->list.count; iI++) { \
long eL = *SOURCE->list.array[iI]; \
int iJ; \
@@ -0,0 +1,6 @@
add_library(shm_td_iq_channel shm_td_iq_channel.c)
target_include_directories(shm_td_iq_channel PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
if (ENABLE_TESTS)
add_subdirectory(tests)
endif()
@@ -0,0 +1,286 @@
/*
* 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 "shm_td_iq_channel.h"
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include "assertions.h"
#include "utils.h"
#include "common/utils/threadPool/pthread_utils.h"
#define CIRCULAR_BUFFER_SIZE (30720 * 14 * 20)
typedef struct {
int magic;
int num_antennas_tx;
int num_antennas_rx;
uint64_t timestamp;
bool is_connected;
pthread_mutex_t mutex;
pthread_cond_t cond;
} ShmTDIQChannelData;
typedef struct ShmTDIQChannel_s {
IQChannelType type;
ShmTDIQChannelData *data;
uint64_t last_timestamp;
char name[256];
sample_t *tx_iq_data;
sample_t *rx_iq_data;
} ShmTDIQChannel;
ShmTDIQChannel *shm_td_iq_channel_create(const char *name, int num_tx_ant, int num_rx_ant)
{
// Create shared memory segment
int fd = shm_open(name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
AssertFatal(fd != -1, "shm_open failed: %s\n", strerror(errno));
size_t tx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * num_tx_ant;
size_t rx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * num_rx_ant;
size_t total_size = sizeof(ShmTDIQChannelData) + tx_buffer_size + rx_buffer_size;
// Set the size of the shared memory segment
int res = ftruncate(fd, total_size);
AssertFatal(res != -1, "ftruncate failed: %s\n", strerror(errno));
// Map shared memory segment to address space
ShmTDIQChannelData *shm_ptr = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
AssertFatal(shm_ptr != MAP_FAILED, "mmap failed: %s\n", strerror(errno));
// Initialize shared memory
memset(shm_ptr, 0, total_size);
shm_ptr->num_antennas_tx = num_tx_ant;
shm_ptr->num_antennas_rx = num_rx_ant;
shm_ptr->is_connected = false;
ShmTDIQChannel *channel = calloc_or_fail(1, sizeof(ShmTDIQChannel));
strncpy(channel->name, name, sizeof(channel->name) - 1);
channel->tx_iq_data = (sample_t *)(shm_ptr + 1);
channel->rx_iq_data = channel->tx_iq_data + tx_buffer_size / sizeof(sample_t);
channel->data = shm_ptr;
channel->type = IQ_CHANNEL_TYPE_SERVER;
channel->last_timestamp = 0;
pthread_mutexattr_t mutex_attr;
pthread_condattr_t cond_attr;
int ret = pthread_mutexattr_init(&mutex_attr);
AssertFatal(ret == 0, "pthread_mutexattr_init() failed: errno %d, %s\n", errno, strerror(errno));
ret = pthread_condattr_init(&cond_attr);
AssertFatal(ret == 0, "pthread_condattr_init() failed: errno %d, %s\n", errno, strerror(errno));
ret = pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
AssertFatal(ret == 0, "pthread_mutexattr_setpshared() failed: errno %d, %s\n", errno, strerror(errno));
ret = pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
AssertFatal(ret == 0, "pthread_condattr_setpshared() failed: errno %d, %s\n", errno, strerror(errno));
ret = pthread_mutex_init(&shm_ptr->mutex, &mutex_attr);
AssertFatal(ret == 0, "pthread_mutex_init() failed: errno %d, %s\n", errno, strerror(errno));
ret = pthread_cond_init(&shm_ptr->cond, &cond_attr);
AssertFatal(ret == 0, "pthread_cond_init() failed: errno %d, %s\n", errno, strerror(errno));
shm_ptr->magic = SHM_MAGIC_NUMBER;
close(fd);
return channel;
}
ShmTDIQChannel *shm_td_iq_channel_connect(const char *name, int timeout_in_seconds)
{
// Create shared memory segment
int fd = -1;
while (timeout_in_seconds > 0 && fd == -1) {
fd = shm_open(name, O_RDWR, S_IRUSR | S_IWUSR);
timeout_in_seconds--;
printf("Waiting for server to create shared memory segment\n");
sleep(1);
}
AssertFatal(fd != -1, "shm_open() failed: errno %d, %s", errno, strerror(errno));
struct stat buf;
fstat(fd, &buf);
size_t total_size = buf.st_size;
// Map shared memory segment to address space
ShmTDIQChannelData *shm_ptr = mmap(NULL, total_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shm_ptr == MAP_FAILED) {
perror("mmap");
exit(1);
}
ShmTDIQChannel *channel = calloc_or_fail(1, sizeof(ShmTDIQChannel));
channel->data = shm_ptr;
channel->tx_iq_data = (sample_t *)(shm_ptr + 1);
size_t tx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * channel->data->num_antennas_tx;
channel->rx_iq_data = channel->tx_iq_data + tx_buffer_size / sizeof(sample_t);
channel->type = IQ_CHANNEL_TYPE_CLIENT;
channel->last_timestamp = 0;
while (shm_ptr->magic != SHM_MAGIC_NUMBER) {
printf("Waiting for server to initialize shared memory\n");
sleep(1);
}
shm_ptr->is_connected = true;
close(fd);
return channel;
}
IQChannelErrorType shm_td_iq_channel_tx(ShmTDIQChannel *channel,
uint64_t timestamp,
uint64_t num_samples,
int antenna,
const sample_t *tx_iq_data)
{
ShmTDIQChannelData *data = channel->data;
if (data->is_connected == false) {
return CHANNEL_ERROR_NOT_CONNECTED;
}
// timestamp in the past
uint64_t current_time = data->timestamp;
if (timestamp < current_time) {
return CHANNEL_ERROR_TOO_LATE;
}
// timestamp is too far in the future
if (timestamp - current_time + num_samples >= CIRCULAR_BUFFER_SIZE) {
return CHANNEL_ERROR_TOO_EARLY;
}
sample_t *base_ptr;
if (channel->type == IQ_CHANNEL_TYPE_CLIENT) {
base_ptr = channel->rx_iq_data + antenna * CIRCULAR_BUFFER_SIZE;
} else {
base_ptr = channel->tx_iq_data + antenna * CIRCULAR_BUFFER_SIZE;
}
uint64_t first_sample = timestamp % CIRCULAR_BUFFER_SIZE;
uint64_t last_sample = first_sample + num_samples - 1;
if (last_sample >= CIRCULAR_BUFFER_SIZE) {
size_t num_samples_first_copy = CIRCULAR_BUFFER_SIZE - first_sample;
memcpy(base_ptr + first_sample, tx_iq_data, num_samples_first_copy * sizeof(sample_t));
memcpy(base_ptr, tx_iq_data + num_samples_first_copy, (num_samples - num_samples_first_copy) * sizeof(sample_t));
} else {
memcpy(base_ptr + first_sample, tx_iq_data, num_samples * sizeof(sample_t));
}
return CHANNEL_NO_ERROR;
}
IQChannelErrorType shm_td_iq_channel_rx(ShmTDIQChannel *channel,
uint64_t timestamp,
uint64_t num_samples,
int antenna,
sample_t *tx_iq_data)
{
ShmTDIQChannelData *data = channel->data;
if (data->is_connected == false) {
return CHANNEL_ERROR_NOT_CONNECTED;
}
// timestamp in the future
uint64_t current_time = data->timestamp;
if (timestamp > current_time) {
return CHANNEL_ERROR_TOO_EARLY;
}
// timestamp is too far in the past
if (current_time - timestamp >= CIRCULAR_BUFFER_SIZE) {
return CHANNEL_ERROR_TOO_LATE;
}
sample_t *base_ptr;
if (channel->type == IQ_CHANNEL_TYPE_CLIENT) {
base_ptr = channel->tx_iq_data + antenna * CIRCULAR_BUFFER_SIZE;
} else {
base_ptr = channel->rx_iq_data + antenna * CIRCULAR_BUFFER_SIZE;
}
uint64_t first_sample = timestamp % CIRCULAR_BUFFER_SIZE;
uint64_t last_sample = first_sample + num_samples - 1;
if (last_sample >= CIRCULAR_BUFFER_SIZE) {
size_t num_samples_first_copy = CIRCULAR_BUFFER_SIZE - first_sample;
memcpy(tx_iq_data, base_ptr + first_sample, num_samples_first_copy * sizeof(sample_t));
memcpy(tx_iq_data + num_samples_first_copy, base_ptr, (num_samples - num_samples_first_copy) * sizeof(sample_t));
} else {
memcpy(tx_iq_data, base_ptr + first_sample, num_samples * sizeof(sample_t));
}
return CHANNEL_NO_ERROR;
}
void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, size_t num_samples)
{
ShmTDIQChannelData *data = channel->data;
if (channel->type != IQ_CHANNEL_TYPE_SERVER) {
return;
}
if (data->is_connected == false) {
return;
}
mutexlock(data->mutex);
data->timestamp += num_samples;
condbroadcast(data->cond);
mutexunlock(data->mutex);
}
void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp)
{
ShmTDIQChannelData *data = channel->data;
if (data->is_connected == false) {
abort();
return;
}
size_t current_timestamp = data->timestamp;
if (current_timestamp >= timestamp) {
return;
}
mutexlock(data->mutex);
while (current_timestamp < timestamp) {
condwait(data->cond, data->mutex);
current_timestamp = data->timestamp;
}
mutexunlock(data->mutex);
return;
}
uint64_t shm_td_iq_channel_get_current_sample(const ShmTDIQChannel *channel)
{
ShmTDIQChannelData *data = channel->data;
return data->timestamp;
}
bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel)
{
return channel->data->is_connected;
}
void shm_td_iq_channel_destroy(ShmTDIQChannel *channel)
{
ShmTDIQChannelData *data = channel->data;
size_t tx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * data->num_antennas_tx;
size_t rx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * data->num_antennas_rx;
size_t total_size = sizeof(ShmTDIQChannelData) + tx_buffer_size + rx_buffer_size;
munmap(data, total_size);
if (channel->type == IQ_CHANNEL_TYPE_SERVER) {
shm_unlink(channel->name);
}
free(channel);
}
@@ -0,0 +1,148 @@
/*
* 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 SHM_IQ_CHANNEL_H
#define SHM_IQ_CHANNEL_H
#include "../threadPool/pthread_utils.h"
#include <stdint.h>
#include <stdbool.h>
#include <semaphore.h>
#define SHM_MAGIC_NUMBER 0x12345678
/**
* ShmTdIqChannel is a shared memory bidirectional time domain IQ channel with a single clock
* source. The server (clock source) shall create the channel while the client should connect to
* it.
*
* To write samples, send them via a pointer passed to shm_td_iq_channel_tx.
* To read samples, receive them via a pointer passed to shm_td_iq_channel_rx. You can either wait until
* the sample are available using shm_td_iq_channel_wait or poll using shm_td_iq_channel_rx until the return
* value is not CHANNEL_ERROR_TOO_EARLY.
* To indicate that samples are ready to be read by the client, call shm_td_iq_channel_produce_samples
* (server only).
*
* The timestamps used in the API are in absolute samples from the time the client/server connection started.
*/
typedef enum IQChannelType { IQ_CHANNEL_TYPE_SERVER, IQ_CHANNEL_TYPE_CLIENT } IQChannelType;
typedef uint32_t sample_t;
typedef enum IQChannelErrorType {
CHANNEL_NO_ERROR,
CHANNEL_ERROR_TOO_LATE,
CHANNEL_ERROR_TOO_EARLY,
CHANNEL_ERROR_NOT_CONNECTED
} IQChannelErrorType;
typedef struct ShmTDIQChannel_s ShmTDIQChannel;
/**
* @brief Creates a shared memory IQ channel.
*
* @param name The name of the shared memory segment.
* @param num_tx_ant The number of TX antennas.
* @param num_rx_ant The number of RX antennas.
* @return A pointer to the created ShmTDIQChannel structure.
*/
ShmTDIQChannel *shm_td_iq_channel_create(const char *name, int num_tx_ant, int num_rx_ant);
/**
* @brief Connects to an existing shared memory IQ channel.
*
* @param name The name of the shared memory segment.
* @param timeout_in_seconds The timeout in seconds for the connection attempt.
* @return A pointer to the connected ShmTDIQChannel structure.
*/
ShmTDIQChannel *shm_td_iq_channel_connect(const char *name, int timeout_in_seconds);
/**
* @brief Transmit data in the channel
*
* @param channel The ShmTDIQChannel structure.
* @param timestamp The timestamp for which to get the TX IQ data slot.
* @param num_samples The number of samples to write.
* @param antenna The antenna index.
* @param tx_iq_data The TX IQ data to write.
*
* @return CHANNEL_NO_ERROR if successful, error type otherwise
*/
IQChannelErrorType shm_td_iq_channel_tx(ShmTDIQChannel *channel,
uint64_t timestamp,
uint64_t num_samples,
int antenna,
const sample_t *tx_iq_data);
/**
* @brief Receive iq data from the channel
*
* @param channel The ShmTDIQChannel structure.
* @param timestamp The timestamp for which to get the RX IQ data slot.
* @param num_samples The number of samples to read.
* @param antenna The antenna index.
* @param tx_iq_data pointer to the RX IQ data slot.
* @return CHANNEL_NO_ERROR if successful, error type otherwise
*/
IQChannelErrorType shm_td_iq_channel_rx(ShmTDIQChannel *channel,
uint64_t timestamp,
uint64_t num_samples,
int antenna,
sample_t *tx_iq_data);
/**
* @brief Advances the time in the channel by specified number of samples
*
* @param channel The ShmTDIQChannel structure.
* @param num_symbols The number of samples to produce.
*/
void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, uint64_t num_samples);
/**
* @brief Wait until sample at the specified timestamp is available
*
* @param channel The ShmTDIQChannel structure.
*/
void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp);
/**
* @brief Checks if the IQ channel is connected.
*
* @param channel The ShmTDIQChannel structure.
* @return True if the channel is connected, false otherwise.
*/
bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel);
/**
* @brief Destroys the shared memory IQ channel.
*
* @param channel The ShmTDIQChannel structure.
*/
void shm_td_iq_channel_destroy(ShmTDIQChannel *channel);
/**
* @brief Returns current sample
*
* @param channel The ShmTDIQChannel structure.
*
* @return Current time as sample count since beginning of transmission
*/
uint64_t shm_td_iq_channel_get_current_sample(const ShmTDIQChannel *channel);
#endif
@@ -0,0 +1,5 @@
add_executable(test_shm_td_iq_channel test_shm_td_iq_channel.c)
target_link_libraries(test_shm_td_iq_channel shm_td_iq_channel minimal_lib pthread)
add_dependencies(tests test_shm_td_iq_channel)
# Commented out due to issues with ASAN in the unit test docker container
#add_test(test_shm_td_iq_channel ./test_shm_td_iq_channel)
@@ -0,0 +1,169 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <errno.h>
#include "assertions.h"
#define SHM_CHANNEL_NAME "shm_iq_channel_test_file"
#include "shm_td_iq_channel.h"
void server(void);
int client(void);
enum { MODE_SERVER, MODE_CLIENT, MODE_FORK };
int main(int argc, char *argv[])
{
int mode = MODE_FORK;
if (argc == 2) {
if (strcmp(argv[1], "server") == 0) {
mode = MODE_SERVER;
} else if (strcmp(argv[1], "client") == 0) {
mode = MODE_CLIENT;
}
} else if (argc > 2) {
printf("Usage: %s [server|client]\n", argv[0]);
return 1;
}
switch (mode) {
case MODE_SERVER:
server();
break;
case MODE_CLIENT:
return client();
break;
case MODE_FORK: {
char file[256];
snprintf(file, sizeof(file), "/dev/shm/%s", SHM_CHANNEL_NAME);
remove(file);
int pid = fork();
if (pid == 0) {
server();
} else {
return client();
}
} break;
}
return 0;
}
const int num_updates = 50;
const int num_samples_per_update = 30720;
void *produce_symbols(void *arg)
{
ShmTDIQChannel *channel = (ShmTDIQChannel *)arg;
for (int i = 0; i < num_updates; i++) {
shm_td_iq_channel_produce_samples(channel, num_samples_per_update);
usleep(20000);
}
return 0;
}
void server(void)
{
int num_ant_tx = 1;
int num_ant_rx = 1;
ShmTDIQChannel *channel = shm_td_iq_channel_create(SHM_CHANNEL_NAME, num_ant_tx, num_ant_rx);
for (int i = 0; i < 10; i++) {
if (shm_td_iq_channel_is_connected(channel)) {
printf("Server connected\n");
break;
}
printf("Waiting for client\n");
sleep(1);
}
AssertFatal(shm_td_iq_channel_is_connected(channel), "Server failed to connect\n");
pthread_t producer_thread;
int ret = pthread_create(&producer_thread, NULL, produce_symbols, channel);
AssertFatal(ret == 0, "pthread_create() failed: errno %d, %s\n", errno, strerror(errno));
uint64_t timestamp = 0;
int iq_contents = 0;
while (timestamp < num_samples_per_update * num_updates) {
shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update);
uint64_t target_timestamp = timestamp + num_samples_per_update;
timestamp += num_samples_per_update;
uint32_t iq_data[num_samples_per_update];
for (int i = 0; i < num_samples_per_update; i++) {
iq_data[i] = iq_contents;
}
int result = shm_td_iq_channel_tx(channel, target_timestamp, num_samples_per_update, 0, iq_data);
AssertFatal(result == CHANNEL_NO_ERROR, "Failed to write data\n");
iq_contents++;
}
printf("Finished writing data\n");
ret = pthread_join(producer_thread, NULL);
AssertFatal(ret == 0, "pthread_join() failed: errno %d, %s\n", errno, strerror(errno));
shm_td_iq_channel_destroy(channel);
}
int client(void)
{
int total_errors = 0;
ShmTDIQChannel *channel = shm_td_iq_channel_connect(SHM_CHANNEL_NAME, 10);
for (int i = 0; i < 10; i++) {
if (shm_td_iq_channel_is_connected(channel)) {
printf("Client connected\n");
break;
}
printf("Waiting for server\n");
sleep(1);
}
AssertFatal(shm_td_iq_channel_is_connected(channel), "Client failed to connect\n");
uint64_t timestamp = 0;
int iq_contents = 0;
while (timestamp < num_samples_per_update * num_updates) {
shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update);
// Server starts producing from second slot
if (timestamp > num_samples_per_update) {
uint64_t target_timestamp = timestamp - num_samples_per_update;
uint32_t iq_data[num_samples_per_update];
int result = shm_td_iq_channel_rx(channel, target_timestamp, num_samples_per_update, 0, iq_data);
AssertFatal(result == CHANNEL_NO_ERROR, "Failed to read data\n");
int num_errors = 0;
for (int i = 0; i < num_samples_per_update; i++) {
if (iq_data[i] != iq_contents) {
num_errors++;
}
}
if (num_errors) {
printf("Found %d errors, value = %d, reference = %d\n", num_errors, iq_data[0], iq_contents);
total_errors += num_errors;
}
iq_contents++;
}
timestamp += num_samples_per_update;
}
printf("Finished reading data\n");
shm_td_iq_channel_destroy(channel);
return total_errors;
}
+849 -17
View File
@@ -72,6 +72,7 @@ tested any RU without S-plane. Radio units we are testing/integrating:
|LiteON RU |01.00.08/02.00.03 |
|Benetel 650 |RAN650-1v1.0.4-dda1bf5|
|Benetel 550 CAT-A|RAN550-1v1.0.4-605a25a|
|Foxconn RPQN |v3.1.15q.551_rc10 |
Tested libxran releases:
@@ -671,6 +672,40 @@ The RU configuration is stored in `/etc/rumanager.conf`. The required modificati
At this stage, RU must be rebooted so the changes apply.
### Foxconn RPQN RU
**Version v3.1.15q.551_rc10**
The OAI configuration file [`gnb.sa.band78.273prb.fhi72.4X4-foxconn.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4X4-foxconn.conf) corresponds to:
- TDD pattern `DDDSU`, 2.5ms
- Bandwidth 100MHz
- MTU 8000
#### RU configuration
After switching on or rebooting the RU, the `/home/root/test/init_rrh_config_enable_cuplane` script should be run.
The RU configuration file is located in `/home/root/test/RRHconfig_xran.xml`. The required modifications:
1. `RRH_DST_MAC_ADDR`
2. `RRH_SRC_MAC_ADDR`
3. `RRH_EAXC_ID_TYPE1`
4. `RRH_EAXC_ID_TYPE3`
5. `RRH_CMPR_HDR_PRESENT` -> `0`
6. `RRH_C_PLANE_VLAN_TAG`
7. `RRH_U_PLANE_VLAN_TAG`
8. `RRH_LO_FREQUENCY_KHZ` -> `3750000, 0`
9. `RRH_DISABLE_USING_CAL_TABLES` -> `YES`
10. `RRH_TX_ATTENUATION` -> must be larger than 10dB
11. `RRH_RX_ATTENUATION` -> must be lower than 30dB
RU must be rebooted so the changes apply.
**Note**
- The RU was tested with the `2024.w30` tag of OAI.
- The measured throughput was **520 Mbps DL** and **40 Mbps UL**.
- With newer OAI versions, throughput degrades. This issue is currently under investigation.
## Configure Network Interfaces and DPDK VFs
The 7.2 fronthaul uses the xran library, which requires DPDK. In this step, we
@@ -942,10 +977,8 @@ Edit the sample OAI gNB configuration file and check following parameters:
including the advantages and disadvantages of each mode, refer to
[Memory in DPDK](https://www.dpdk.org/memory-in-dpdk-part-2-deep-dive-into-iova/)
* `owdm_enable`: used for eCPRI One-Way Delay Measurements; it depends if the RU supports it; if not set to 1 (enabled), default value is 0 (disabled)
* `fh_config`: parameters that need to match RU parameters
* timing parameters (starting with `T`) depend on the RU: `Tadv_cp_dl` is a
single number, the rest pairs of numbers `(x, y)` specifying minimum and
maximum delays
* `fh_config`
* DU delay profile (`T1a` and `Ta4`): pairs of numbers `(x, y)` specifying minimum and maximum delays
* `ru_config`: RU-specific configuration:
* `iq_width`: Width of DL/UL IQ samples: if 16, no compression, if <16, applies
compression
@@ -966,7 +999,7 @@ Layer mapping (eAxC offsets) happens as follows:
**Note**
- At the moment, OAI is compatible with CAT A O-RU only. Therefore, SRS is not supported.
- XRAN retreives DU MAC address with `rte_eth_macaddr_get()` function. Hence, `fhi_72.du_addr` parameter is not taken into account.
- XRAN retrieves DU MAC address with `rte_eth_macaddr_get()` function. Hence, `fhi_72.du_addr` parameter is not taken into account.
# Start and Operation of OAI gNB
@@ -1094,10 +1127,10 @@ fhi_72 = {
// mtu
fh_config = (
{
// timing, ru_config, prach_config of RU1
// DU delay profile, ru_config, prach_config of RU1
},
{
// timing, ru_config, prach_config of RU2
// DU delay profile, ru_config, prach_config of RU2
}
);
};
@@ -1121,11 +1154,6 @@ fhi_72 = {
fh_config = (
# RAN650 #1
{
Tadv_cp_dl = 125;
T2a_cp_dl = (259, 500);
T2a_cp_ul = (25, 500);
T2a_up = (134, 375);
Ta3 = (152, 160);
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
@@ -1137,11 +1165,6 @@ fhi_72 = {
},
# RAN650 #2
{
Tadv_cp_dl = 125;
T2a_cp_dl = (259, 500);
T2a_cp_ul = (25, 500);
T2a_up = (134, 375);
Ta3 = (152, 160);
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
@@ -1185,6 +1208,815 @@ Note the eight entries after `avg_IO`.
You should be able to connect a UE now.
# OAI Management Plane
We support Configuration Management in OAI gNB, where gNB configures CU-planes, interfaces, TX/RX antennas, and TX/RX carriers for the RU.
The reference specifications:
* `O-RAN.WG4.MP.0-v05.00`
* `O-RAN.WG4.MP-YANGs-v04.00`
## M-plane prerequisites
Before proceeding, please make sure you have a support for 7.2 interface, as described in [Prerequisites](#prerequisites).
### DHCP server
The M-plane requires a DHCP server, where the M-plane connection can be established over untagged or tagged VLAN. We tested with untagged (the default VLAN is 1).
Please modify `/etc/dhcp/dhcpd.conf` configuration based on your testbed.
<details>
<summary>Example DHCP server configuration</summary>
```
class "vendor-class" {
match option vendor-class-identifier;
}
subclass "vendor-class" "o-ran-ru2/Benetel" {
vendor-option-space VC;
}
option space VC;
option VC.server-address code 129 = array of ip-address;
option VC.server-fqdn code 130 = string;
# Netconf client IP address - DHCP option 43
option VC.server-address 192.168.80.1;
option VC.server-fqdn "o_du_1.operator.com";
set vendor-string = option vendor-class-identifier;
# option 143 - DHCPv4 SZTP Redirect Option (RFC8572)
# 2 bytes of URI's length + URI
option sztp code 143 = { unsigned integer 16, string };
option sztp 15 "https://192.168.80.1";
# port is optional in URI, e.g. "https://192.168.80.1:222"
#option sztp 20 "https://192.168.80.1:2222";
subnet 192.168.80.0 netmask 255.255.255.0 {
option routers 192.168.80.1;
option subnet-mask 255.255.255.0;
option domain-name "oai.com";
option domain-name-servers 172.21.3.100;
host benetel_ru {
# RU MAC address
hardware ethernet <ru-mac-address>;
# RU IP address
fixed-address <desired-ru-ip-address>;
}
}
```
</details>
Please, configure the interface as:
```bash
sudo ip address add 192.168.80.1/24 dev <interface>
```
### Mandatory packages
* On Fedora (we haven't yet tested RHEL):
```bash
sudo dnf install pcre-devel libssh-devel libxml2-devel libyang2-devel libnetconf2-devel
```
* On Ubuntu:
```bash
sudo apt-get install libpcre3-dev libssh-dev libxml2-dev
```
On Ubuntu, please note: `sudo apt-get install libyang2-dev libnetconf2-dev` will install unsupported versions (i.e. v2.0.112/v2.0.24 for `libyang2-dev`/`libnetconf2-dev`, but minimum required are v2.1.4/v2.1.25).
Therefore, please compile these libraries from source, as following:
<details>
<summary>Installing latest v2 libyang2 and libnetconf2 libraries</summary>
```
rm -rf /tmp/build_mplane_v2
mkdir /tmp/build_mplane_v2
# libyang
cd /tmp/build_mplane_v2
git clone https://github.com/CESNET/libyang.git
cd libyang
git checkout v2.1.111
mkdir build && cd build
cmake -DENABLE_TESTS=OFF \
-DENABLE_VALGRIND_TESTS=OFF \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_INSTALL_RPATH=/usr/local/lib \
-DPLUGINS_DIR=/usr/local/lib/libyang \
-DPLUGINS_DIR_EXTENSIONS=/usr/local/lib/libyang/extensions \
-DPLUGINS_DIR_TYPES=/usr/local/lib/libyang/types \
-DYANG_MODULE_DIR=/usr/local/share/yang/modules/libyang ..
make -j8
sudo make install
sudo ldconfig
#libnetconf
cd /tmp/build_mplane_v2
git clone https://github.com/CESNET/libnetconf2.git
cd libnetconf2
git checkout v2.1.37
mkdir build && cd build
cmake -DENABLE_TESTS=OFF \
-DENABLE_EXAMPLES=OFF \
-DENABLE_VALGRIND_TESTS=OFF \
-DCLIENT_SEARCH_DIR=/usr/local/share/yang/modules \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DCMAKE_INSTALL_RPATH=/usr/local/lib \
-DLIBYANG_INCLUDE_DIR=/usr/local/include \
-DLIBYANG_LIBRARY=/usr/local/lib/libyang.so \
-DLY_VERSION_PATH=/usr/local/include \
-DYANG_MODULE_DIR=/usr/local/share/yang/modules/libnetconf2 ..
make -j8
sudo make install
sudo ldconfig
# to uninstall libraries
# cd /tmp/build_mplane_v2/libyang/build && sudo make uninstall
# cd /tmp/build_mplane_v2/libnetconf2/build && sudo make uninstall
# cd
# rm -rf /tmp/build_mplane_v2
```
</details>
If you would like to install these libraries in the custom path, please replace `/usr/local` default path to e.g. `/opt/mplane-v2`.
## Benetel O-RU
Note: Only v1.2.2 RAN550 and RAN650 have been successfully tested.
### One time steps
Connect to the RU as user `root`, enable the mplane service, and reboot:
```bash
ssh root@<ru-ip-address>
systemctl enable mplane
reboot
```
Once the mplane service is successfully enabled on the RU, two new users are being added in `/etc/passwd`:
```bash
...
oranbenetel:x:1000:1000::/home/oranbenetel:/bin/sh
oranext:x:1001:1001::/home/oranext:/bin/sh
```
Create `oranbenetel` home directory:
```bash
mkdir /home/oranbenetel && chown oranbenetel:oranbenetel /home/oranbenetel
```
Connect to the RU as user `oranbenetel`, generate ssh keys, and copy DU public key into RU for NETCONF authentication:
```bash
ssh oranbenetel@<ru-ip-address>
ssh-keygen
echo "<DU-pub-key>" >> ~/.ssh/authorized_keys
```
## gNB configuration
The reference gNB configuration file for one Benetel RAN550:
[`gnb.sa.band78.273prb.fhi72.4x4-benetel550-mplane.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.273prb.fhi72.4x4-benetel550-mplane.conf)
The reference DU configuration file for two Benetel RAN650:
[gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.8x8-benetel650_650-mplane.conf)
In order to run gNB/DU with M-plane, we need to modify the `fhi_72` section in the configuration file.
Example for one RU:
```bash
fhi_72 = {
dpdk_devices = ("0000:c3:11.0", "0000:c3:11.1"); # one VF can be used as well
system_core = 0;
io_core = 1;
worker_cores = (2);
du_key_pair = ("<path-to>/.ssh/id_rsa.pub", "<path-to>/.ssh/id_rsa");
du_addr = ("00:11:22:33:44:66", "00:11:22:33:44:67"); # only one needed if one VF configured
vlan_tag = (9, 9); # only one needed if one VF configured
ru_ip_addr = ("192.168.80.9");
fh_config = ({
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
Ta4 = (0, 200);
});
};
```
Example for two RUs:
```bash
fhi_72 = {
dpdk_devices = ("0000:c3:11.0", "0000:c3:11.1", "0000:c3:11.2", "0000:c3:11.3"); # two VFs can be used as well
system_core = 0;
io_core = 1;
worker_cores = (2);
du_key_pair = ("/home/oaicicd/.ssh/id_rsa.pub", "/home/oaicicd/.ssh/id_rsa");
du_addr = ("00:11:22:33:44:66", "00:11:22:33:44:67", "00:11:22:33:44:68", "00:11:22:33:44:69"); # only two needed if two VFs configured
vlan_tag = (9, 9, 11, 11); # only two needed if two VFs configured
ru_ip_addr = ("192.168.80.9", "192.168.80.10");
fh_config = (
# RAN550 #1
{
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
Ta4 = (0, 200);
},
# RAN550 #2
{
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
Ta4 = (0, 200);
});
};
```
* `fhi_72` :
* `dpdk_devices`: [*]
* `system_core`: [*]
* `io_core`: [*]
* `worker_cores`: [*]
* `file_prefix`: [*]
* `du_key_pair`: ssh public and private keys to authenticate RU with NETCONF
* `du_addr`: DU MAC address(es) to create CU-plane interface(s) in the RU
* `vlan_tag`: VLAN U and C plane tags to create CU-plane interface(s) in the RU
* `ru_ip_addr`: RU IP address to connect to the RU via M-plane
* `dpdk_mem_size`: [*]
* `dpdk_iova_mode`: [*]
* `owdm_enable`: [*]
* `fh_config`: only DU delay profile (`T1a` and `Ta4`)
[*] see [Configure OAI gNB](#configure-oai-gnb) for more details
The following parameters are retrieved from the RU and forwarded to the xran:
* `MTU`
* `RU MAC address`
* `IQ compression`: if RU supports multiple, the first value in the list is taken; please note that the same value is used for PxSCH/PRACH
* `PRACH offset`: hardcoded based on the RU vendor (i.e. for Benetel `max(Nrx,Ntx)`)
## Build and compile gNB
The following cmake options are available:
* `OAI_FHI72` = CUS support
* `OAI_FHI72_MPLANE` = M support
Compiled libraries:
* `OAI_FHI72` <=> `oran_fhlib_5g`
* `OAI_FHI72` && `OAI_FHI72_MPLANE` <=> `oran_fhlib_5g` (CUS) && `oran_fhlib_5g_mplane` (CUSM)
### Using build_oai script
```bash
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
cd ~/openairinterface5g/cmake_targets/
./build_oai -I # if you never installed OAI, use this command once before the next line
./build_oai --install-optional-packages # for pcre/libpcre3, libssh, and libxml2 library installation
./build_oai --gNB --ninja -t oran_fhlib_5g_mplane --cmake-opt -Dxran_LOCATION=$HOME/phy/fhi_lib/lib
# if libyang2 and libnetconf2 are installed in `/opt/mplane-v2`, please use the following command:
PKG_CONFIG_PATH=/opt/mplane-v2/lib/pkgconfig ./build_oai --gNB --ninja -t oran_fhlib_5g_mplane --cmake-opt -Dxran_LOCATION=$HOME/phy/fhi_lib/lib
```
### Using cmake directly
```bash
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
cd ~/openairinterface5g/
mkdir build && cd build
cmake .. -GNinja -DOAI_FHI72=ON -DOAI_FHI72_MPLANE=ON -Dxran_LOCATION=$HOME/phy/fhi_lib/lib
# if libyang2 and libnetconf2 are installed in `/opt/mplane-v2`, please use the following command:
PKG_CONFIG_PATH=/opt/mplane-v2/lib/pkgconfig cmake .. -GNinja -DOAI_FHI72=ON -DOAI_FHI72_MPLANE=ON -Dxran_LOCATION=$HOME/phy/fhi_lib/lib
ninja nr-softmodem oran_fhlib_5g_mplane params_libconfig
```
## Start the gNB
Run the `nr-softmodem` from the build directory:
```bash
cd ~/openairinterface5g/cmake_targets/ran_build/build
sudo ./nr-softmodem -O <mplane-configuration file> --thread-pool <list of non isolated cpus>
```
**Warning**: Make sure that the configuration file you add after the `-O` option is adapted to your machine, especially to its isolated cores.
M-plane sequence diagram:
```mermaid
sequenceDiagram
participant dhcp as DHCP server
participant du as O-DU
participant ru as O-RU
dhcp->ru: 1. Transport Layer Initialization
note over dhcp,ru: (a) perform VLAN scan (untagged or tagged VLAN)<br/>(b) DHCP assigns IP address to RU<br/>(c) DHCP sends DU IP address to RU
du->ru: 2. NETCONF/SSH connect
du->>ru: 3. DU retrieves RU info
note left of du: <get>
note over du: Check if RU is PTP synced
du->>ru: 4. DU subscribes to all RU notifications
note left of du: <subscribe>
du->>ru: 5. DU updates the supervision timer
note left of du: <supervision-watchdog-reset>
note over du: Store RU MAC, MTU, IQ bitwidth, and PRACH offset info for xran
note over du: Store all the RU U-plane info - interface name, TX/RX carrier, and TX/RX endpoint names
du->>ru: 6. DU loads yang models
note left of du: <get-schema>
note right of ru: ietf-netconf-monitoring.yang
du->>ru: 7. DU performs CU-plane configuration
note left of du: <edit-config>
note over ru: (1) o-ran-interface.yang - create new interface with VLAN tag, DU and RU MAC addresses<br/>(2)(opt.) o-ran-transceiver.yang - for file upload<br/>(3) o-ran-processing-elements.yang - element with which RU ports should be assigned<br/>(4) o-ran-uplane-conf.yang<br/>#8193;(4a) low-level-tx/rx-endpoints - PxSCH, PRACH<br/>#8193;(4b) tx/rx-array-carriers - center frequency, BW, gain, ACTIVE,...<br/>#8193;(4c) low-level-tx/rx-links - mapping endpoints, carriers and processing element<br/>#8193;(4d)(opt.) if CAT B, SRS configuration<br/>#8193;(4e)(opt.) TDD configuration
du->>ru: 8. DU checks if CU-plane configuration is valid
note left of du: <validate>
du->>ru: 9. If valid, DU commits the changes
note left of du: <commit>
du->>ru: 10. DU retrieves RU states
note right of ru: ietf-hardware.yang
note left of du: <get>
note over ru: admin-state, power-state, oper-state,<br/>availability-state, usage-state
ru->>du: 11. RU notifies DU about the configuration change
note over du: DU configures xran
du->ru: 12. DU and RU exchange packets
```
<details>
<summary>4x4 MIMO and 100MHz BW with Benetel 550 RU example run</summary>
```
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <connect> with username "oranbenetel" and port ID "830".
[HW] [MPLANE] Successfuly connected to RU "192.168.80.9" with username "oranbenetel" and port ID "830".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get> operational datastore.
[HW] [MPLANE] Successfully retrieved operational datastore from RU "192.168.80.9".
[HW] [MPLANE] RU is already PTP synchronized.
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <subscribe> with stream "NETCONF" and filter "(null)".
[HW] [MPLANE] RPC reply = OK.
[HW] [MPLANE] Successfully subscribed to all notifications from RU "192.168.80.9".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = "<supervision-watchdog-reset xmlns="urn:o-ran:supervision:1.0">
<supervision-notification-interval>65535</supervision-notification-interval>
<guard-timer-overhead>65535</guard-timer-overhead>
</supervision-watchdog-reset>".
[HW] [MPLANE] Successfully updated supervision timer to (65535+65535)[s] for RU "192.168.80.9".
[HW] [MPLANE] Watchdog timer answer:
<next-update-at xmlns="urn:o-ran:supervision:1.0">2025-03-30T08:52:31+02:00</next-update-at>
[HW] [MPLANE] Interface MTU 1500 unreliable/not correctly reported by Benetel O-RU, hardcoding to 9600.
[HW] [MPLANE] IQ bitwidth 16 unreliable/not correctly reported by Benetel O-RU, hardcoding to 9.
[HW] [MPLANE] Storing the following information to forward to xran:
RU MAC address 8c:1f:64:d1:11:c0
MTU 9600
IQ bitwidth 9
PRACH offset 4
DU port bitmask 61440
Band sector bitmask 3840
CC ID bitmask 240
RU port ID bitmask 15
DU port ID 0
Band sector ID 0
CC ID 0
RU port ID 0
[HW] [MPLANE] Successfully retrieved all the U-plane info - interface name, TX/RX carrier names, and TX/RX endpoint names.
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-yang-metadata".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "yang".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-inet-types".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-yang-types".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-yang-schema-mount".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-yang-structure-ext".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-datastores".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "sysrepo".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-netconf-acm".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-factory-default".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "sysrepo-factory-default".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-yang-library".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "sysrepo-monitoring".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "sysrepo-plugind".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-netconf".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-netconf-with-defaults".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-netconf-notifications".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-origin".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-netconf-monitoring".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "ietf-netconf-nmda".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <get-schema> for module "nc-notifications".
[HW] [MPLANE] [LIBYANG] ERROR: Data model "notifications" not found in local searchdirs. (path: (null)).
[HW] [MPLANE] [LIBYANG] ERROR: Loading "notifications" module failed. (path: (null)).
[HW] [MPLANE] [LIBYANG] ERROR: Parsing module "nc-notifications" failed. (path: (null)).
[HW] [MPLANE] Unable to load module "nc-notifications" from RU "192.168.80.9".
[HW] [MPLANE] Unable to load all yang modules from operational datastore for RU "192.168.80.9". Using yang models present in "models" subfolder.
[HW] [MPLANE] Successfully loaded all yang modules for RU "192.168.80.9".
[HW] [MPLANE] The VLAN tags for C and U plane for the RU "192.168.80.9" are the same. Therefore, configuring one common interface and one processing element.
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <edit-config>:
<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
<interface>
<name>INTERFACE_0</name>
<type xmlns:ianaift="urn:ietf:params:xml:ns:yang:iana-if-type">ianaift:l2vlan</type>
<enabled>true</enabled>
<mac-address xmlns="urn:o-ran:interfaces:1.0">8c:1f:64:d1:11:c0</mac-address>
<base-interface xmlns="urn:o-ran:interfaces:1.0">eth0</base-interface>
<vlan-id xmlns="urn:o-ran:interfaces:1.0">9</vlan-id>
</interface>
</interfaces>
<processing-elements xmlns="urn:o-ran:processing-element:1.0">
<transport-session-type>ETH-INTERFACE</transport-session-type>
<ru-elements>
<name>PLANE_0</name>
<transport-flow>
<interface-name>INTERFACE_0</interface-name>
<eth-flow>
<ru-mac-address>8c:1f:64:d1:11:c0</ru-mac-address>
<vlan-id>9</vlan-id>
<o-du-mac-address>00:11:22:33:44:66</o-du-mac-address>
</eth-flow>
</transport-flow>
</ru-elements>
</processing-elements>
<user-plane-configuration xmlns="urn:o-ran:uplane-conf:1.0">
<low-level-tx-links>
<name>PdschLink0</name>
<processing-element>PLANE_0</processing-element>
<tx-array-carrier>TxArray0</tx-array-carrier>
<low-level-tx-endpoint>LowLevelTxEndpoint0</low-level-tx-endpoint>
</low-level-tx-links>
<low-level-tx-links>
<name>PdschLink1</name>
<processing-element>PLANE_0</processing-element>
<tx-array-carrier>TxArray0</tx-array-carrier>
<low-level-tx-endpoint>LowLevelTxEndpoint1</low-level-tx-endpoint>
</low-level-tx-links>
<low-level-tx-links>
<name>PdschLink2</name>
<processing-element>PLANE_0</processing-element>
<tx-array-carrier>TxArray0</tx-array-carrier>
<low-level-tx-endpoint>LowLevelTxEndpoint2</low-level-tx-endpoint>
</low-level-tx-links>
<low-level-tx-links>
<name>PdschLink3</name>
<processing-element>PLANE_0</processing-element>
<tx-array-carrier>TxArray0</tx-array-carrier>
<low-level-tx-endpoint>LowLevelTxEndpoint3</low-level-tx-endpoint>
</low-level-tx-links>
<low-level-rx-links>
<name>PuschLink0</name>
<processing-element>PLANE_0</processing-element>
<rx-array-carrier>RxArray0</rx-array-carrier>
<low-level-rx-endpoint>LowLevelRxEndpoint0</low-level-rx-endpoint>
</low-level-rx-links>
<low-level-rx-links>
<name>PrachLink0</name>
<processing-element>PLANE_0</processing-element>
<rx-array-carrier>RxArray0</rx-array-carrier>
<low-level-rx-endpoint>LowLevelRxPrachEndpoint0</low-level-rx-endpoint>
</low-level-rx-links>
<low-level-rx-links>
<name>PuschLink1</name>
<processing-element>PLANE_0</processing-element>
<rx-array-carrier>RxArray0</rx-array-carrier>
<low-level-rx-endpoint>LowLevelRxEndpoint1</low-level-rx-endpoint>
</low-level-rx-links>
<low-level-rx-links>
<name>PrachLink1</name>
<processing-element>PLANE_0</processing-element>
<rx-array-carrier>RxArray0</rx-array-carrier>
<low-level-rx-endpoint>LowLevelRxPrachEndpoint1</low-level-rx-endpoint>
</low-level-rx-links>
<low-level-rx-links>
<name>PuschLink2</name>
<processing-element>PLANE_0</processing-element>
<rx-array-carrier>RxArray0</rx-array-carrier>
<low-level-rx-endpoint>LowLevelRxEndpoint2</low-level-rx-endpoint>
</low-level-rx-links>
<low-level-rx-links>
<name>PrachLink2</name>
<processing-element>PLANE_0</processing-element>
<rx-array-carrier>RxArray0</rx-array-carrier>
<low-level-rx-endpoint>LowLevelRxPrachEndpoint2</low-level-rx-endpoint>
</low-level-rx-links>
<low-level-rx-links>
<name>PuschLink3</name>
<processing-element>PLANE_0</processing-element>
<rx-array-carrier>RxArray0</rx-array-carrier>
<low-level-rx-endpoint>LowLevelRxEndpoint3</low-level-rx-endpoint>
</low-level-rx-links>
<low-level-rx-links>
<name>PrachLink3</name>
<processing-element>PLANE_0</processing-element>
<rx-array-carrier>RxArray0</rx-array-carrier>
<low-level-rx-endpoint>LowLevelRxPrachEndpoint3</low-level-rx-endpoint>
</low-level-rx-links>
<low-level-tx-endpoints>
<name>LowLevelTxEndpoint0</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>0</eaxc-id>
</e-axcid>
</low-level-tx-endpoints>
<low-level-tx-endpoints>
<name>LowLevelTxEndpoint1</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>1</eaxc-id>
</e-axcid>
</low-level-tx-endpoints>
<low-level-tx-endpoints>
<name>LowLevelTxEndpoint2</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>2</eaxc-id>
</e-axcid>
</low-level-tx-endpoints>
<low-level-tx-endpoints>
<name>LowLevelTxEndpoint3</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>3</eaxc-id>
</e-axcid>
</low-level-tx-endpoints>
<low-level-rx-endpoints>
<name>LowLevelRxEndpoint0</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>0</eaxc-id>
</e-axcid>
</low-level-rx-endpoints>
<low-level-rx-endpoints>
<name>LowLevelRxPrachEndpoint0</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>4</eaxc-id>
</e-axcid>
</low-level-rx-endpoints>
<low-level-rx-endpoints>
<name>LowLevelRxEndpoint1</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>1</eaxc-id>
</e-axcid>
</low-level-rx-endpoints>
<low-level-rx-endpoints>
<name>LowLevelRxPrachEndpoint1</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>5</eaxc-id>
</e-axcid>
</low-level-rx-endpoints>
<low-level-rx-endpoints>
<name>LowLevelRxEndpoint2</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>2</eaxc-id>
</e-axcid>
</low-level-rx-endpoints>
<low-level-rx-endpoints>
<name>LowLevelRxPrachEndpoint2</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>6</eaxc-id>
</e-axcid>
</low-level-rx-endpoints>
<low-level-rx-endpoints>
<name>LowLevelRxEndpoint3</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>3</eaxc-id>
</e-axcid>
</low-level-rx-endpoints>
<low-level-rx-endpoints>
<name>LowLevelRxPrachEndpoint3</name>
<compression>
<iq-bitwidth>9</iq-bitwidth>
<compression-type>STATIC</compression-type>
</compression>
<cp-length>0</cp-length>
<cp-length-other>0</cp-length-other>
<offset-to-absolute-frequency-center>0</offset-to-absolute-frequency-center>
<e-axcid>
<o-du-port-bitmask>61440</o-du-port-bitmask>
<band-sector-bitmask>3840</band-sector-bitmask>
<ccid-bitmask>240</ccid-bitmask>
<ru-port-bitmask>15</ru-port-bitmask>
<eaxc-id>7</eaxc-id>
</e-axcid>
</low-level-rx-endpoints>
<tx-array-carriers>
<name>TxArray0</name>
<absolute-frequency-center>663360</absolute-frequency-center>
<center-of-channel-bandwidth>3950400000</center-of-channel-bandwidth>
<channel-bandwidth>100000000</channel-bandwidth>
<active>ACTIVE</active>
<gain>0.0</gain>
<downlink-radio-frame-offset>0</downlink-radio-frame-offset>
<downlink-sfn-offset>0</downlink-sfn-offset>
</tx-array-carriers>
<rx-array-carriers>
<name>RxArray0</name>
<absolute-frequency-center>663360</absolute-frequency-center>
<center-of-channel-bandwidth>3950400000</center-of-channel-bandwidth>
<channel-bandwidth>100000000</channel-bandwidth>
<active>ACTIVE</active>
<downlink-radio-frame-offset>0</downlink-radio-frame-offset>
<downlink-sfn-offset>0</downlink-sfn-offset>
<gain-correction>0.0</gain-correction>
<n-ta-offset>0</n-ta-offset>
</rx-array-carriers>
</user-plane-configuration>
[HW] [MPLANE] RPC reply = OK.
[HW] [MPLANE] Successfully edited the candidate datastore for RU "192.168.80.9".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <validate> candidate datastore.
[HW] [MPLANE] RPC reply = OK.
[HW] [MPLANE] Successfully validated candidate datastore for RU "192.168.80.9".
[HW] [MPLANE] RPC request to RU "192.168.80.9" = <commit> candidate datastore.
[HW] [MPLANE] RPC reply = OK.
[HW] [MPLANE] Successfully commited CU-planes configuration into running datastore for RU "192.168.80.9".
[HW] [MPLANE] Usage state = "idle" for RU "192.168.80.9".
[HW] [MPLANE] Received notification from RU "192.168.80.9" at (2025-03-29T12:40:23.049085102+00:00)
{
"o-ran-uplane-conf:rx-array-carriers-state-change": {
"rx-array-carriers": [
{
"name": "RxArray0",
"state": "BUSY"
}
]
}
}
[HW] [MPLANE] Received notification from RU "192.168.80.9" at (2025-03-29T12:40:23.058136880+00:00)
{
"o-ran-uplane-conf:tx-array-carriers-state-change": {
"tx-array-carriers": [
{
"name": "TxArray0",
"state": "BUSY"
}
]
}
}
[HW] [MPLANE] Received notification from RU "192.168.80.9" at (2025-03-29T12:40:23.078776163+00:00)
{
"o-ran-uplane-conf:rx-array-carriers-state-change": {
"rx-array-carriers": [
{
"name": "RxArray0",
"state": "READY"
}
]
}
}
[HW] [MPLANE] Received notification from RU "192.168.80.9" at (2025-03-29T12:40:23.093039138+00:00)
{
"o-ran-uplane-conf:tx-array-carriers-state-change": {
"tx-array-carriers": [
{
"name": "TxArray0",
"state": "READY"
}
]
}
}
[HW] [MPLANE] Received notification from RU "192.168.80.9" at (2025-03-29T12:40:23.452751936+00:00)
{
"ietf-netconf-notifications:netconf-config-change": {
"changed-by": {
"username": "root",
"session-id": 0
},
"datastore": "running",
"edit": [
{
"target": "/ietf-interfaces:interfaces/interface[name='INTERFACE_0']",
"operation": "create"
},
{
"target": "/ietf-interfaces:interfaces/interface[name='INTERFACE_0']/name",
"operation": "create"
},
...
}
[HW] [MPLANE] RU "192.168.80.9" is now ready.
```
</details>
Note: If you wish to run the fronthaul without M-plane, no need for recompilation, as the library `oran_fhlib_5g` already exists.
The only mandatory step is to link `oran_fhlib_5g` to `oai_transpro` library.
```bash
cd ~/openairinterface5g/cmake_targets/ran_build/build
rm liboai_transpro.so
ln -s liboran_fhlib_5g.so liboai_transpro.so
sudo ./nr-softmodem -O <without-mplane-configuration file> --thread-pool <list of non isolated cpus>
```
# Contact in case of questions
You can ask your question on the [mailing lists](https://gitlab.eurecom.fr/oai/openairinterface5g/-/wikis/MailingList).
+1
View File
@@ -118,6 +118,7 @@ Some directories under `radio` contain READMEs:
- [BladeRF](../radio/BLADERF/README)
- [IQPlayer](../radio/iqplayer/DOC/iqrecordplayer_usage.md), and [general documentation](./iqrecordplayer_usage.md)
- [fhi_72](../radio/fhi_72/README.md)
- [vrtsim](../radio/vrtsim/README.md)
The other SDRs (AW2S, LimeSDR, ...) have no READMEs.
+1
View File
@@ -72,6 +72,7 @@ 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/libvrtsim.so \
/usr/local/lib/
COPY --from=gnb-base \
+1
View File
@@ -69,6 +69,7 @@ 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/libvrtsim.so \
/usr/local/lib/
COPY --from=gnb-base \
+1
View File
@@ -110,6 +110,7 @@ COPY --from=gnb-build \
/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 \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
# Now we are copying from builder-image the UHD files.
+1
View File
@@ -75,6 +75,7 @@ COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
COPY --from=nr-ue-base \
+1
View File
@@ -72,6 +72,7 @@ COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
COPY --from=nr-ue-base \
+1
View File
@@ -110,6 +110,7 @@ COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ciUE.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/usr/local/lib/
# Now we are copying from builder-image the UHD files.
+15 -11
View File
@@ -872,18 +872,22 @@ static inline void apply_ntn_config(PHY_VARS_NR_UE *UE,
if (*update_ntn_system_information) {
*update_ntn_system_information = false;
*duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + UE->ntn_config_message->ntn_config_params.cell_specific_k_offset;
UE->timing_advance = fp->samples_per_subframe * UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms;
*timing_advance +=
fp->get_samples_slot_timestamp(slot_nr,
fp,
UE->ntn_config_message->ntn_config_params.cell_specific_k_offset - *ntn_koffset);
*ntn_koffset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset;
double total_ta_ms = UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms;
UE->timing_advance = fp->samples_per_subframe * total_ta_ms;
int mu = fp->numerology_index;
int koffset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset;
if (*ntn_koffset != koffset) {
*duration_rx_to_tx = NR_UE_CAPABILITY_SLOT_RX_TO_TX + (koffset << mu);
*timing_advance += fp->get_samples_slot_timestamp(slot_nr, fp, (koffset - *ntn_koffset) << mu);
*ntn_koffset = koffset;
}
LOG_I(PHY,
"cell_specific_k_offset = %d ms, ntn_total_time_advance_ms = %f ms (%d samples)\n",
"k_offset = %d ms (%d slots), ntn_total_time_advance_ms = %f ms (%d samples)\n",
*ntn_koffset,
UE->ntn_config_message->ntn_config_params.ntn_total_time_advance_ms,
*ntn_koffset << mu,
total_ta_ms,
UE->timing_advance);
}
}
@@ -1080,7 +1084,7 @@ void *UE_thread(void *arg)
if (UE->ntn_config_message->update) {
UE->ntn_config_message->update = false;
update_ntn_system_information = true;
nr_slot_tx_offset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset;
nr_slot_tx_offset = UE->ntn_config_message->ntn_config_params.cell_specific_k_offset << fp->numerology_index;
}
int slot_nr = absolute_slot % nb_slot_frame;
@@ -1214,7 +1218,7 @@ void init_NR_UE(int nb_inst, char *uecap_file, char *reconfig_file, char *rbconf
NR_UE_MAC_INST_t *mac = get_mac_inst(i);
mac->if_module = nr_ue_if_module_init(i);
AssertFatal(mac->if_module, "can not initialize IF module\n");
if (!IS_SA_MODE(get_softmodem_params()) || !get_softmodem_params()->sl_mode) {
if (!IS_SA_MODE(get_softmodem_params()) && !get_softmodem_params()->sl_mode) {
init_nsa_message(&rrc_inst[i], reconfig_file, rbconfig_file);
nr_rlc_activate_srb0(mac_inst[i].crnti, NULL, send_srb0_rrc);
}
+2 -5
View File
@@ -309,11 +309,8 @@ uint8_t aerial_unpack_nr_srs_indication(uint8_t **ppReadPackedMsg,
nfapi_nr_srs_indication_t *srs_ind = (nfapi_nr_srs_indication_t *)msg;
for (uint8_t pdu_idx = 0; pdu_idx < srs_ind->number_of_pdus; pdu_idx++) {
nfapi_nr_srs_indication_pdu_t *pdu = &srs_ind->pdu_list[pdu_idx];
nfapi_srs_report_tlv_t *report_tlv = &pdu->report_tlv;
for (int i = 0; i < (report_tlv->length + 3) / 4; i++) {
if (!pull32(pDataMsg, &report_tlv->value[i], data_end)) {
return 0;
}
if (!unpack_nr_srs_report_tlv_value(&pdu->report_tlv, pDataMsg, data_end)) {
return 0;
}
}
return retval;
-1
View File
@@ -54,7 +54,6 @@
#include "PHY/INIT/phy_init.h"
#include "PHY/INIT/nr_phy_init.h"
#include "PHY/LTE_TRANSPORT/transport_proto.h"
#include "openair2/LAYER2/NR_MAC_gNB/mac_proto.h"
#include "openair1/SCHED_NR/fapi_nr_l1.h"
#include "openair1/PHY/NR_TRANSPORT/nr_dlsch.h"
#include "openair1/PHY/defs_gNB.h"
@@ -36,6 +36,18 @@ uint8_t pull8(uint8_t **in, uint8_t *out, uint8_t *end);
uint8_t pulls8(uint8_t **in, int8_t *out, uint8_t *end);
uint8_t pull16(uint8_t **in, uint16_t *out, uint8_t *end);
uint8_t pulls16(uint8_t **in, int16_t *out, uint8_t *end);
/*! \brief Pull an arbitrary amount of bytes ( 0 to 4 ) into an uint32_t
* \param length the amount of bytes to pull
* \param in the buffer from where to pull the bytes
* \param out a pointer to the destination uint32_t
* \param end a pointer to the end of the buffer in
*
* This function is used to pull `length` amount of bytes from `in` into `out`.
* Used where pulling the full 32 bits would cause the `in` pointer to go over `end`.
* It's currently used to pull the last bytes of a payload where the buffer does not contain padding at the end.
*/
uint8_t pullx32(uint8_t length, uint8_t **in, uint32_t *out, uint8_t *end);
uint8_t pull32(uint8_t **in, uint32_t *out, uint8_t *end);
uint8_t pulls32(uint8_t **in, int32_t *out, uint8_t *end);
+25
View File
@@ -259,6 +259,31 @@ uint8_t pulls16(uint8_t **in, int16_t *out, uint8_t *end)
}
}
uint8_t pullx32(uint8_t length, uint8_t **in, uint32_t *out, uint8_t *end)
{
uint8_t *pIn = *in;
if (length > 4) {
NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s Can't pull more than 4 bytes (%d) into a uint32_t\n", __FUNCTION__, length);
on_error();
return 0;
}
if ((end - pIn) >= length) {
for (int i = 0; i < length; i++) {
#ifdef FAPI_BYTE_ORDERING_BIG_ENDIAN
*out |= ((uint32_t)pIn[i] << (8 * i));
#else
*out = ((uint32_t)pIn[i] << (8 * (length - 1 - i)));
#endif
}
(*in) += length;
return length;
} else {
NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s no space in buffer\n", __FUNCTION__);
on_error();
return 0;
}
}
uint8_t pull32(uint8_t **in, uint32_t *out, uint8_t *end)
{
uint8_t *pIn = *in;
+1
View File
@@ -50,6 +50,7 @@ uint8_t pack_nr_uci_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *e
uint8_t unpack_nr_uci_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config);
uint8_t pack_nr_srs_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config);
uint8_t unpack_nr_srs_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config);
uint8_t unpack_nr_srs_report_tlv_value(nfapi_srs_report_tlv_t *report_tlv, uint8_t **ppReadPackedMsg, uint8_t *end);
uint8_t pack_nr_rach_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *end, nfapi_p7_codec_config_t *config);
uint8_t unpack_nr_rach_indication(uint8_t **ppReadPackedMsg, uint8_t *end, void *msg, nfapi_p7_codec_config_t *config);
#endif // OPENAIRINTERFACE_NR_FAPI_P7_H
+24 -4
View File
@@ -2300,6 +2300,28 @@ uint8_t pack_nr_srs_indication(void *msg, uint8_t **ppWritePackedMsg, uint8_t *e
return 1;
}
uint8_t unpack_nr_srs_report_tlv_value(nfapi_srs_report_tlv_t *report_tlv, uint8_t **ppReadPackedMsg, uint8_t *end)
{
#ifndef ENABLE_AERIAL
for (int i = 0; i < (report_tlv->length + 3) / 4; i++) {
if (!pull32(ppReadPackedMsg, &report_tlv->value[i], end)) {
return 0;
}
}
#else
const uint16_t last_idx = ((report_tlv->length + 3) / 4) - 1;
for (int i = 0; i < last_idx; i++) {
if (!pull32(ppReadPackedMsg, &report_tlv->value[i], end)) {
return 0;
}
}
// Pull last bytes according to how much padding it would need to be 32-bit aligned
const uint8_t padding = get_tlv_padding(report_tlv->length);
pullx32(4 - padding, ppReadPackedMsg, &report_tlv->value[last_idx], end);
#endif
return 1;
}
static uint8_t unpack_nr_srs_report_tlv(nfapi_srs_report_tlv_t *report_tlv, uint8_t **ppReadPackedMsg, uint8_t *end) {
if(!(pull16(ppReadPackedMsg, &report_tlv->tag, end) &&
@@ -2307,10 +2329,8 @@ static uint8_t unpack_nr_srs_report_tlv(nfapi_srs_report_tlv_t *report_tlv, uint
return 0;
}
#ifndef ENABLE_AERIAL
for (int i = 0; i < (report_tlv->length + 3) / 4; i++) {
if (!pull32(ppReadPackedMsg, &report_tlv->value[i], end)) {
return 0;
}
if (!unpack_nr_srs_report_tlv_value(report_tlv, ppReadPackedMsg, end)) {
return 0;
}
#endif
return 1;
+13 -1
View File
@@ -2684,6 +2684,18 @@ void dump_uci_indication(const nfapi_nr_uci_indication_t *msg)
}
}
static void dump_srs_report_tlv(const nfapi_srs_report_tlv_t* tlv, int depth)
{
depth++;
INDENTED_PRINTF("TAG = 0x%02x\n", tlv->tag);
INDENTED_PRINTF("LENGTH = 0x%02x\n", tlv->length);
INDENTED_PRINTF("VALUE = ");
for (int i = 0; i < ((tlv->length + 3) / 4); ++i) {
printf("0x%04x ", tlv->value[i]);
}
printf("\n");
}
void dump_srs_indication(const nfapi_nr_srs_indication_t *msg)
{
int depth = 0;
@@ -2704,7 +2716,7 @@ void dump_srs_indication(const nfapi_nr_srs_indication_t *msg)
INDENTED_PRINTF("Timing advance offset in nanoseconds = 0x%02x\n", pdu->timing_advance_offset_nsec);
INDENTED_PRINTF("SRS usage = 0x%02x\n", pdu->srs_usage);
INDENTED_PRINTF("Report Type = 0x%02x\n", pdu->report_type);
/* call to dump_srs_report_tlv */
dump_srs_report_tlv(&pdu->report_tlv, depth);
depth--;
}
}
-1
View File
@@ -41,7 +41,6 @@
#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"
+6 -3
View File
@@ -24,11 +24,14 @@
static void fil_srs_indication_report_tlv(nfapi_srs_report_tlv_t *tlv)
{
tlv->tag = rand16_range(0,3);
tlv->length = rand32_range(0,sizeof(tlv->value));
for (int i = 0; i < (tlv->length + 3) / 4; ++i) {
tlv->tag = rand16_range(0, 3);
tlv->length = rand32_range(1, sizeof(tlv->value));
const uint16_t last_idx = ((tlv->length + 3) / 4) - 1;
for (int i = 0; i < last_idx; ++i) {
tlv->value[i] = rand32();
}
const uint8_t num_bytes = 4 - get_tlv_padding(tlv->length);
tlv->value[last_idx] = rand32_range(0, 1 << (8 * num_bytes));
}
static void fill_srs_indication_PDU(nfapi_nr_srs_indication_pdu_t *pdu)
@@ -575,8 +575,8 @@ c32_t nr_pbch_dmrs_correlation(const NR_DL_FRAME_PARMS *fp,
DEBUG_PBCH("PBCH DMRS Correlation : gNB_id %d , OFDM size %d, Ncp=%d, k=%u symbol %d\n",
proc->gNB_id,
ue->frame_parms.ofdm_symbol_size,
ue->frame_parms.Ncp,
fp->ofdm_symbol_size,
fp->Ncp,
k,
symbol);
@@ -590,8 +590,8 @@ c32_t nr_pbch_dmrs_correlation(const NR_DL_FRAME_PARMS *fp,
c16_t *pil = pilot;
const c16_t *rxF = &rxdataF[aarx][symbol_offset + k];
DEBUG_PBCH("pbch ch est pilot RB_DL %d\n", ue->frame_parms.N_RB_DL);
DEBUG_PBCH("k %u, first_carrier %d\n", k, ue->frame_parms.first_carrier_offset);
DEBUG_PBCH("pbch ch est pilot RB_DL %d\n", fp->N_RB_DL);
DEBUG_PBCH("k %u, first_carrier %d\n", k, fp->first_carrier_offset);
// Treat first 2 pilots specially (left edge)
computed_val = c32x16maddShift(*pil, rxF[re_offset], computed_val, 15);
@@ -769,7 +769,7 @@ int nr_pbch_channel_estimation(const NR_DL_FRAME_PARMS *fp,
multaddRealVectorComplexScalar(fr, ch, dl_ch, 16);
pil++;
re_offset = (re_offset + 4) % fp->ofdm_symbol_size;
dl_ch += 24;
dl_ch += 12;
for (int pilot_cnt = 3; pilot_cnt < (3 * num_rbs); pilot_cnt += 3) {
// if (pilot_cnt == 30)
@@ -779,7 +779,7 @@ int nr_pbch_channel_estimation(const NR_DL_FRAME_PARMS *fp,
if (dmrss == 1 && pilot_cnt == 12) {
pilot_cnt=48;
re_offset = (re_offset + 144) % fp->ofdm_symbol_size;
dl_ch += 288;
dl_ch += 144;
}
ch = c16mulShift(*pil, rxF[re_offset], 15);
DEBUG_PBCH("pilot %u: rxF= (%d,%d), ch= (%d,%d), pil=(%d,%d)\n",
+1
View File
@@ -50,6 +50,7 @@ typedef enum {
#define CHANMODEL_FREE_RSQRT_6 1<<1
#define CHANMODEL_FREE_RSQRT_NTAPS 1<<2
#define CHANMODEL_FREE_AMPS 1<<3
#define SHR3 (jz = jsr, jsr ^= (jsr << 13), jsr ^= (jsr >> 17), jsr ^= (jsr << 5), jz + jsr)
typedef enum {
CORR_LEVEL_LOW,
+2 -1
View File
@@ -6,13 +6,14 @@ add_library(e2_ran_func_cuup STATIC
O-RAN/ran_func_kpm_subs.c
O-RAN/ran_func_rc.c
O-RAN/ran_func_rc_subs.c
../flexric/src/sm/rc_sm/ie/rc_data_ie.c
CUSTOMIZED/ran_func_gtp.c # GTP SM not yet implemented in CU-UP
CUSTOMIZED/ran_func_pdcp.c
CUSTOMIZED/ran_func_tc.c # currently, not implemented; therefore, filling rnd data
../flexric/test/rnd/fill_rnd_data_tc.c
)
target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj sm_common_ie_obj ds)
target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj ds alg)
target_compile_definitions(e2_ran_func_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_CUUP)
@@ -49,7 +49,6 @@
#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"
#include "openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.h"
+144 -132
View File
@@ -30,7 +30,8 @@
*/
#include "LAYER2/NR_MAC_gNB/mac_proto.h"
#include "nr_mac.h"
#include "nr_mac_common.h"
#include "common/utils/nr/nr_common.h"
#include <limits.h>
#include <executables/softmodem-common.h>
@@ -2431,6 +2432,19 @@ uint8_t get_pusch_nb_antenna_ports(NR_PUSCH_Config_t *pusch_Config,
return n_antenna_port;
}
static int binomial(int n, int k)
{
if (k > n - k)
k = n - k;
int c = 1;
for (int i = 1; i <= k; i++, n--) {
if (c / i > UINT_MAX/n) // return 0 on overflow
return 0;
c = c / i * n + c % i * n / i;
}
return c;
}
// #define DEBUG_SRS_RESOURCE_IND
uint8_t compute_srs_resource_indicator(long *maxMIMO_Layers,
NR_PUSCH_Config_t *pusch_Config,
@@ -3422,21 +3436,6 @@ uint8_t get_pusch_mcs_table(long *mcs_Table,
}
int binomial(int n, int k) {
int c = 1, i;
if (k > n-k)
k = n-k;
for (i = 1; i <= k; i++, n--) {
if (c/i > UINT_MAX/n) // return 0 on overflow
return 0;
c = c / i * n + c % i * n / i;
}
return c;
}
/* extract PTRS values from RC and validate it based upon 38.214 5.1.6.3 */
bool set_dl_ptrs_values(NR_PTRS_DownlinkConfig_t *ptrs_config,
uint16_t rbSize,uint8_t mcsIndex, uint8_t mcsTable,
@@ -4357,127 +4356,132 @@ bool set_ul_ptrs_values(NR_PTRS_UplinkConfig_t *ul_ptrs_config,
}
//! Calculating number of bits set
uint8_t number_of_bits_set(uint8_t buf) {
static uint8_t number_of_bits_set(uint8_t buf)
{
uint8_t nb_of_bits_set = 0;
uint8_t mask = 0xff;
uint8_t index = 0;
for (index=7; (buf & mask) && (index>=0) ; index--){
if (buf & (1<<index))
for (int index = 7; (buf & mask) && (index >= 0); index--) {
if (buf & (1 << index))
nb_of_bits_set++;
mask>>=1;
mask >>= 1;
}
return nb_of_bits_set;
}
void compute_rsrp_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
uint8_t nb_resources,
nr_csi_report_t *csi_report) {
static void compute_rsrp_bitlen(const NR_CSI_ReportConfig_t *csi_reportconfig, uint8_t nb_resources, nr_csi_report_t *csi_report)
{
if (NR_CSI_ReportConfig__groupBasedBeamReporting_PR_disabled == csi_reportconfig->groupBasedBeamReporting.present) {
if (NULL != csi_reportconfig->groupBasedBeamReporting.choice.disabled->nrofReportedRS)
csi_report->CSI_report_bitlen.nb_ssbri_cri = *(csi_reportconfig->groupBasedBeamReporting.choice.disabled->nrofReportedRS)+1;
if (csi_reportconfig->groupBasedBeamReporting.choice.disabled->nrofReportedRS)
csi_report->CSI_report_bitlen.nb_ssbri_cri = *(csi_reportconfig->groupBasedBeamReporting.choice.disabled->nrofReportedRS) + 1;
else
/*! From Spec 38.331
* nrofReportedRS
* The number (N) of measured RS resources to be reported per report setting in a non-group-based report. N <= N_max, where N_max is either 2 or 4 depending on UE
* The number (N) of measured RS resources to be reported per report setting in a non-group-based report.
* N <= N_max, where N_max is either 2 or 4 depending on UE
* capability. FFS: The signaling mechanism for the gNB to select a subset of N beams for the UE to measure and report.
* When the field is absent the UE applies the value 1
*/
csi_report->CSI_report_bitlen.nb_ssbri_cri= 1;
csi_report->CSI_report_bitlen.nb_ssbri_cri = 1;
} else
csi_report->CSI_report_bitlen.nb_ssbri_cri= 2;
csi_report->CSI_report_bitlen.nb_ssbri_cri = 2;
if (nb_resources) {
csi_report->CSI_report_bitlen.cri_ssbri_bitlen =ceil(log2 (nb_resources));
csi_report->CSI_report_bitlen.rsrp_bitlen = 7; //From spec 38.212 Table 6.3.1.1.2-6: CRI, SSBRI, and RSRP
csi_report->CSI_report_bitlen.diff_rsrp_bitlen =4; //From spec 38.212 Table 6.3.1.1.2-6: CRI, SSBRI, and RSRP
csi_report->CSI_report_bitlen.rsrp_bitlen = 7; // From spec 38.212 Table 6.3.1.1.2-6: CRI, SSBRI, and RSRP
csi_report->CSI_report_bitlen.diff_rsrp_bitlen = 4; // From spec 38.212 Table 6.3.1.1.2-6: CRI, SSBRI, and RSRP
} else {
csi_report->CSI_report_bitlen.cri_ssbri_bitlen =0;
csi_report->CSI_report_bitlen.cri_ssbri_bitlen = 0;
csi_report->CSI_report_bitlen.rsrp_bitlen = 0;
csi_report->CSI_report_bitlen.diff_rsrp_bitlen =0;
csi_report->CSI_report_bitlen.diff_rsrp_bitlen = 0;
}
}
uint8_t compute_ri_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
nr_csi_report_t *csi_report)
static uint8_t compute_ri_bitlen(const NR_CSI_ReportConfig_t *csi_reportconfig, nr_csi_report_t *csi_report)
{
struct NR_CodebookConfig *codebookConfig = csi_reportconfig->codebookConfig;
uint8_t nb_allowed_ri, ri_bitlen;
uint8_t ri_restriction = 0;
if (codebookConfig == NULL) {
csi_report->csi_meas_bitlen.ri_bitlen = 0;
return ri_restriction;
return 0;
}
struct NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel *type1single = NULL;
struct NR_CodebookConfig__codebookType__type1 *type1 = NULL;
if (codebookConfig->codebookType.present == NR_CodebookConfig__codebookType_PR_type1)
type1 = codebookConfig->codebookType.choice.type1;
else
LOG_E(NR_MAC, "Only type1 codebook configuration is supported\n");
if (type1 && type1->subType.present == NR_CodebookConfig__codebookType__type1__subType_PR_typeI_SinglePanel)
type1single = type1->subType.choice.typeI_SinglePanel;
else
LOG_E(NR_MAC, "Only type1 single panel codebook configuration is supported\n");
if (!type1single)
return -1;
uint8_t ri_restriction = type1single->typeI_SinglePanel_ri_Restriction.buf[0];
// codebook type1 single panel
if (NR_CodebookConfig__codebookType__type1__subType_PR_typeI_SinglePanel == codebookConfig->codebookType.choice.type1->subType.present) {
struct NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel *type1single = codebookConfig->codebookType.choice.type1->subType.choice.typeI_SinglePanel;
if (type1single->nrOfAntennaPorts.present == NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts_PR_two) {
ri_restriction = csi_reportconfig->codebookConfig->codebookType.choice.type1->subType.choice.typeI_SinglePanel->typeI_SinglePanel_ri_Restriction.buf[0];
if (type1single->nrOfAntennaPorts.present == NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts_PR_two) {
nb_allowed_ri = number_of_bits_set(ri_restriction);
ri_bitlen = ceil(log2(nb_allowed_ri));
// from the spec 38.212 and table 6.3.1.1.2-3: RI, LI, CQI, and CRI of codebookType=typeI-SinglePanel
ri_bitlen = ri_bitlen < 1 ? ri_bitlen : 1;
csi_report->csi_meas_bitlen.ri_bitlen = ri_bitlen;
}
if (type1single->nrOfAntennaPorts.present == NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts_PR_moreThanTwo) {
if (type1single->nrOfAntennaPorts.choice.moreThanTwo->n1_n2.present ==
NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts__moreThanTwo__n1_n2_PR_two_one_TypeI_SinglePanel_Restriction) {
// 4 ports
nb_allowed_ri = number_of_bits_set(ri_restriction);
ri_bitlen = ceil(log2(nb_allowed_ri));
ri_bitlen = ri_bitlen < 1 ? ri_bitlen : 1; //from the spec 38.212 and table 6.3.1.1.2-3: RI, LI, CQI, and CRI of codebookType=typeI-SinglePanel
// from the spec 38.212 and table 6.3.1.1.2-3: RI, LI, CQI, and CRI of codebookType=typeI-SinglePanel
ri_bitlen = ri_bitlen < 2 ? ri_bitlen : 2;
csi_report->csi_meas_bitlen.ri_bitlen = ri_bitlen;
}
if (type1single->nrOfAntennaPorts.present == NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts_PR_moreThanTwo){
if (type1single->nrOfAntennaPorts.choice.moreThanTwo->n1_n2.present ==
NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts__moreThanTwo__n1_n2_PR_two_one_TypeI_SinglePanel_Restriction) {
// 4 ports
ri_restriction = csi_reportconfig->codebookConfig->codebookType.choice.type1->subType.choice.typeI_SinglePanel->typeI_SinglePanel_ri_Restriction.buf[0];
nb_allowed_ri = number_of_bits_set(ri_restriction);
ri_bitlen = ceil(log2(nb_allowed_ri));
ri_bitlen = ri_bitlen < 2 ? ri_bitlen : 2; //from the spec 38.212 and table 6.3.1.1.2-3: RI, LI, CQI, and CRI of codebookType=typeI-SinglePanel
csi_report->csi_meas_bitlen.ri_bitlen = ri_bitlen;
}
else {
// more than 4 ports
ri_restriction = csi_reportconfig->codebookConfig->codebookType.choice.type1->subType.choice.typeI_SinglePanel->typeI_SinglePanel_ri_Restriction.buf[0];
nb_allowed_ri = number_of_bits_set(ri_restriction);
ri_bitlen = ceil(log2(nb_allowed_ri));
csi_report->csi_meas_bitlen.ri_bitlen = ri_bitlen;
}
}
return ri_restriction;
}
else
AssertFatal(1==0,"Other configurations not yet implemented\n");
return -1;
}
void compute_li_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
uint8_t ri_restriction,
nr_csi_report_t *csi_report) {
struct NR_CodebookConfig *codebookConfig = csi_reportconfig->codebookConfig;
for(int i=0; i<8; i++) {
if (codebookConfig == NULL || ((ri_restriction>>i)&0x01) == 0)
csi_report->csi_meas_bitlen.li_bitlen[i]=0;
else {
// codebook type1 single panel
if (NR_CodebookConfig__codebookType__type1__subType_PR_typeI_SinglePanel==codebookConfig->codebookType.choice.type1->subType.present)
csi_report->csi_meas_bitlen.li_bitlen[i]=ceil(log2(i+1))<2?ceil(log2(i+1)):2;
// more than 4 ports
nb_allowed_ri = number_of_bits_set(ri_restriction);
ri_bitlen = ceil(log2(nb_allowed_ri));
csi_report->csi_meas_bitlen.ri_bitlen = ri_bitlen;
}
}
return ri_restriction;
}
static void compute_li_bitlen(const NR_CSI_ReportConfig_t *csi_reportconfig, uint8_t ri_restriction, nr_csi_report_t *csi_report)
{
struct NR_CodebookConfig *codebookConfig = csi_reportconfig->codebookConfig;
for(int i = 0; i < 8; i++) {
if (codebookConfig == NULL || ((ri_restriction >> i) & 0x01) == 0)
csi_report->csi_meas_bitlen.li_bitlen[i] = 0;
else {
struct NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel *type1single = NULL;
struct NR_CodebookConfig__codebookType__type1 *type1 = NULL;
if (codebookConfig && codebookConfig->codebookType.present == NR_CodebookConfig__codebookType_PR_type1)
type1 = codebookConfig->codebookType.choice.type1;
else
AssertFatal(1==0,"Other configurations not yet implemented\n");
LOG_E(NR_MAC, "Only type1 codebook configuration is supported\n");
if (type1 && type1->subType.present == NR_CodebookConfig__codebookType__type1__subType_PR_typeI_SinglePanel)
type1single = type1->subType.choice.typeI_SinglePanel;
else
LOG_E(NR_MAC, "Only type1 single panel codebook configuration is supported\n");
// codebook type1 single panel
if (type1single)
csi_report->csi_meas_bitlen.li_bitlen[i] = ceil(log2(i + 1)) < 2 ? ceil(log2(i + 1)) : 2;
else
csi_report->csi_meas_bitlen.li_bitlen[i] = 0;
}
}
}
void get_n1n2_o1o2_singlepanel(int *n1, int *n2, int *o1, int *o2,
struct NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts__moreThanTwo *morethantwo) {
static void get_n1n2_o1o2_singlepanel(int *n1,
int *n2,
int *o1,
int *o2,
const struct NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts__moreThanTwo *morethantwo)
{
// Table 5.2.2.2.1-2 in 38.214 for supported configurations
switch(morethantwo->n1_n2.present){
case (NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts__moreThanTwo__n1_n2_PR_two_one_TypeI_SinglePanel_Restriction):
@@ -4559,7 +4563,7 @@ void get_n1n2_o1o2_singlepanel(int *n1, int *n2, int *o1, int *o2,
*o2 = 1;
break;
default:
AssertFatal(1==0,"Not supported configuration for n1_n2 in codebook configuration");
AssertFatal(false, "Not supported configuration for n1_n2 in codebook configuration");
}
}
@@ -4691,15 +4695,13 @@ static void set_bitlen_size_singlepanel(CSI_Meas_bitlen_t *csi_bitlen, int n1, i
}
break;
default:
AssertFatal(1==0,"Invalid rank in x1 x2 bit length computation\n");
AssertFatal(false, "Invalid rank in x1 x2 bit length computation\n");
}
csi_bitlen->pmi_x1_bitlen[i] = csi_bitlen->pmi_i11_bitlen[i] + csi_bitlen->pmi_i12_bitlen[i] + csi_bitlen->pmi_i13_bitlen[i];
}
void compute_pmi_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
uint8_t ri_restriction,
nr_csi_report_t *csi_report)
static void compute_pmi_bitlen(const NR_CSI_ReportConfig_t *csi_reportconfig, uint8_t ri_restriction, nr_csi_report_t *csi_report)
{
NR_CodebookConfig_t *codebookConfig = csi_reportconfig->codebookConfig;
for(int i = 0; i < 8; i++) {
@@ -4731,45 +4733,51 @@ void compute_pmi_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
}
}
else
AssertFatal(1==0,"Type1 Multi-panel Codebook Config not yet implemented\n");
AssertFatal(false, "Type1 Multi-panel Codebook Config not yet implemented\n");
}
else
AssertFatal(1==0,"Type2 Codebook Config not yet implemented\n");
AssertFatal(false, "Type2 Codebook Config not yet implemented\n");
}
}
}
void compute_cqi_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
uint8_t ri_restriction,
nr_csi_report_t *csi_report) {
static void compute_cqi_bitlen(const NR_CSI_ReportConfig_t *csi_reportconfig, uint8_t ri_restriction, nr_csi_report_t *csi_report)
{
NR_CodebookConfig_t *codebookConfig = csi_reportconfig->codebookConfig;
struct NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel *type1single = NULL;
struct NR_CodebookConfig__codebookType__type1 *type1 = NULL;
if (codebookConfig && codebookConfig->codebookType.present == NR_CodebookConfig__codebookType_PR_type1)
type1 = codebookConfig->codebookType.choice.type1;
else
LOG_E(NR_MAC, "Only type1 codebook configuration is supported\n");
if (type1 && type1->subType.present == NR_CodebookConfig__codebookType__type1__subType_PR_typeI_SinglePanel)
type1single = type1->subType.choice.typeI_SinglePanel;
else
LOG_E(NR_MAC, "Only type1 single panel codebook configuration is supported\n");
struct NR_CodebookConfig *codebookConfig = csi_reportconfig->codebookConfig;
struct NR_CSI_ReportConfig__reportFreqConfiguration *freq_config = csi_reportconfig->reportFreqConfiguration;
if (!freq_config
|| *freq_config->cqi_FormatIndicator != NR_CSI_ReportConfig__reportFreqConfiguration__cqi_FormatIndicator_widebandCQI) {
LOG_E(NR_MAC, "Only wide-band CQI reporting is supported\n");
return;
}
if (*freq_config->cqi_FormatIndicator == NR_CSI_ReportConfig__reportFreqConfiguration__cqi_FormatIndicator_widebandCQI) {
for(int i=0; i<8; i++) {
if ((ri_restriction>>i)&0x01) {
csi_report->csi_meas_bitlen.cqi_bitlen[i] = 4;
if(codebookConfig != NULL) {
if (NR_CodebookConfig__codebookType__type1__subType_PR_typeI_SinglePanel == codebookConfig->codebookType.choice.type1->subType.present){
struct NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel *type1single = codebookConfig->codebookType.choice.type1->subType.choice.typeI_SinglePanel;
if (type1single->nrOfAntennaPorts.present == NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts_PR_moreThanTwo) {
if (type1single->nrOfAntennaPorts.choice.moreThanTwo->n1_n2.present >
NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts__moreThanTwo__n1_n2_PR_two_one_TypeI_SinglePanel_Restriction) {
// more than 4 antenna ports
if (i > 4)
csi_report->csi_meas_bitlen.cqi_bitlen[i] += 4; // CQI for second TB
}
}
}
for(int i = 0; i < 8; i++) {
if ((ri_restriction >> i) & 0x01) {
csi_report->csi_meas_bitlen.cqi_bitlen[i] = 4;
if (!type1single)
continue;
if (type1single->nrOfAntennaPorts.present == NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts_PR_moreThanTwo) {
if (type1single->nrOfAntennaPorts.choice.moreThanTwo->n1_n2.present >
NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts__moreThanTwo__n1_n2_PR_two_one_TypeI_SinglePanel_Restriction) {
// more than 4 antenna ports
if (i > 4)
csi_report->csi_meas_bitlen.cqi_bitlen[i] += 4; // CQI for second TB
}
}
else
csi_report->csi_meas_bitlen.cqi_bitlen[i] = 0;
}
} else
csi_report->csi_meas_bitlen.cqi_bitlen[i] = 0;
}
else
AssertFatal(1==0,"Sub-band CQI reporting not yet supported");
}
//!TODO : same function can be written to handle csi_resources
@@ -4779,12 +4787,16 @@ void compute_csi_bitlen(const NR_CSI_MeasConfig_t *csi_MeasConfig, nr_csi_report
uint8_t nb_resources = 0;
NR_CSI_ReportConfig__reportQuantity_PR reportQuantity_type;
NR_CSI_ResourceConfigId_t csi_ResourceConfigId;
struct NR_CSI_ResourceConfig *csi_resourceconfig;
NR_CSI_ResourceConfig_t *csi_resourceconfig;
if (!csi_MeasConfig->csi_ReportConfigToAddModList) {
LOG_E(NR_MAC, "csi_ReportConfigToAddModList is NULL, not expected\n");
return;
}
// for each CSI measurement report configuration (list of CSI-ReportConfig)
LOG_D(NR_MAC,"Searching %d csi_reports\n",csi_MeasConfig->csi_ReportConfigToAddModList->list.count);
for (csi_report_id = 0; csi_report_id < csi_MeasConfig->csi_ReportConfigToAddModList->list.count; csi_report_id++) {
struct NR_CSI_ReportConfig *csi_reportconfig = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id];
NR_CSI_ReportConfig_t *csi_reportconfig = csi_MeasConfig->csi_ReportConfigToAddModList->list.array[csi_report_id];
// MAC structure for CSI measurement reports (per UE and per report)
nr_csi_report_t *csi_report = &csi_report_template[csi_report_id];
// csi-ResourceConfigId of a CSI-ResourceConfig included in the configuration
@@ -4848,18 +4860,18 @@ void compute_csi_bitlen(const NR_CSI_MeasConfig_t *csi_MeasConfig, nr_csi_report
compute_rsrp_bitlen(csi_reportconfig, nb_resources, csi_report);
break;
case (NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_CQI):
csi_report->csi_meas_bitlen.cri_bitlen=ceil(log2(nb_resources));
csi_report->csi_meas_bitlen.cri_bitlen = ceil(log2(nb_resources));
csi_report->csi_meas_bitlen.ri_restriction = compute_ri_bitlen(csi_reportconfig, csi_report);
compute_cqi_bitlen(csi_reportconfig, csi_report->csi_meas_bitlen.ri_restriction, csi_report);
break;
case (NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_PMI_CQI):
csi_report->csi_meas_bitlen.cri_bitlen=ceil(log2(nb_resources));
csi_report->csi_meas_bitlen.cri_bitlen = ceil(log2(nb_resources));
csi_report->csi_meas_bitlen.ri_restriction = compute_ri_bitlen(csi_reportconfig, csi_report);
compute_cqi_bitlen(csi_reportconfig, csi_report->csi_meas_bitlen.ri_restriction, csi_report);
compute_pmi_bitlen(csi_reportconfig, csi_report->csi_meas_bitlen.ri_restriction, csi_report);
break;
case (NR_CSI_ReportConfig__reportQuantity_PR_cri_RI_LI_PMI_CQI):
csi_report->csi_meas_bitlen.cri_bitlen=ceil(log2(nb_resources));
csi_report->csi_meas_bitlen.cri_bitlen = ceil(log2(nb_resources));
csi_report->csi_meas_bitlen.ri_restriction = compute_ri_bitlen(csi_reportconfig, csi_report);
compute_li_bitlen(csi_reportconfig, csi_report->csi_meas_bitlen.ri_restriction, csi_report);
compute_cqi_bitlen(csi_reportconfig, csi_report->csi_meas_bitlen.ri_restriction, csi_report);
@@ -290,33 +290,6 @@ bool set_ul_ptrs_values(NR_PTRS_UplinkConfig_t *ul_ptrs_config,
@returns transformPrecoding value */
long get_transformPrecoding(const NR_UE_UL_BWP_t *current_UL_BWP, nr_dci_format_t dci_format, uint8_t configuredGrant);
uint8_t number_of_bits_set(uint8_t buf);
void compute_rsrp_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
uint8_t nb_resources,
nr_csi_report_t *csi_report);
uint8_t compute_ri_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
nr_csi_report_t *csi_report);
void compute_li_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
uint8_t ri_restriction,
nr_csi_report_t *csi_report);
void get_n1n2_o1o2_singlepanel(int *n1, int *n2, int *o1, int *o2,
struct NR_CodebookConfig__codebookType__type1__subType__typeI_SinglePanel__nrOfAntennaPorts__moreThanTwo *morethantwo);
void get_x1x2_bitlen_singlepanel(int n1, int n2, int o1, int o2,
int *x1, int *x2, int rank, int codebook_mode);
void compute_pmi_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
uint8_t ri_restriction,
nr_csi_report_t *csi_report);
void compute_cqi_bitlen(struct NR_CSI_ReportConfig *csi_reportconfig,
uint8_t ri_restriction,
nr_csi_report_t *csi_report);
void compute_csi_bitlen(const NR_CSI_MeasConfig_t *csi_MeasConfig, nr_csi_report_t *csi_report_template);
uint16_t nr_get_csi_bitlen(nr_csi_report_t *csi_report);
+7 -1
View File
@@ -34,7 +34,6 @@
#define SPEED_OF_LIGHT 299792458
#include "mac_defs.h"
#include <NR_MAC_gNB/mac_proto.h>
#include "NR_MAC_UE/mac_proto.h"
#include "NR_MAC-CellGroupConfig.h"
#include "LAYER2/NR_MAC_COMMON/nr_mac_common.h"
@@ -306,6 +305,13 @@ void configure_ntn_ta(module_id_t module_id, ntn_timing_advance_componets_t *ntn
ntn_ta->ntn_ta_commondrift = *ntn_Config_r17->ta_Info_r17->ta_CommonDrift_r17 * 0.2e-3;
}
ntn_ta->ntn_params_changed = true;
LOG_D(NR_MAC,
"SIB19 Rxd. k_offset:%ld, N_Common_Ta:%f,drift:%f,N_UE_TA:%f \n",
ntn_ta->cell_specific_k_offset,
ntn_ta->N_common_ta_adj,
ntn_ta->ntn_ta_commondrift,
ntn_ta->N_UE_TA_adj);
}
static void config_common_ue(NR_UE_MAC_INST_t *mac, NR_ServingCellConfigCommon_t *scc, int cc_idP)
+2 -2
View File
@@ -649,9 +649,9 @@ static inline double GET_COMPLETE_TIME_ADVANCE_MS(const ntn_timing_advance_compo
return ntn_ta->N_common_ta_adj + ntn_ta->N_UE_TA_adj;
}
static inline long GET_DURATION_RX_TO_TX(const ntn_timing_advance_componets_t *ntn_ta)
static inline long GET_DURATION_RX_TO_TX(const ntn_timing_advance_componets_t *ntn_ta, int scs)
{
return NR_UE_CAPABILITY_SLOT_RX_TO_TX + ntn_ta->cell_specific_k_offset;
return NR_UE_CAPABILITY_SLOT_RX_TO_TX + (ntn_ta->cell_specific_k_offset << scs);
}
/*@}*/
@@ -415,6 +415,8 @@ void get_monitoring_period_offset(const NR_SearchSpace_t *ss, int *period, int *
bool is_ss_monitor_occasion(const int frame, const int slot, const int slots_per_frame, const NR_SearchSpace_t *ss)
{
if (!ss)
return false;
const int duration = ss->duration ? *ss->duration : 1;
bool monitor = false;
int period, offset;
+4 -4
View File
@@ -927,10 +927,10 @@ static int nr_ue_process_dci_dl_10(NR_UE_MAC_INST_t *mac,
const uint16_t feedback_ti = 1 + dci->pdsch_to_harq_feedback_timing_indicator.val + ntn_ue_koffset;
if (rnti_type != TYPE_RA_RNTI_ && rnti_type != TYPE_SI_RNTI_) {
AssertFatal(feedback_ti > GET_DURATION_RX_TO_TX(&mac->ntn_ta),
AssertFatal(feedback_ti > GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing),
"PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%ld).\n",
feedback_ti,
GET_DURATION_RX_TO_TX(&mac->ntn_ta));
GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing));
// set the harq status at MAC for feedback
const int tpc[] = {-1, 0, 1, 3};
set_harq_status(mac,
@@ -1220,11 +1220,11 @@ static int nr_ue_process_dci_dl_11(NR_UE_MAC_INST_t *mac,
const int ntn_ue_koffset = GET_NTN_UE_K_OFFSET(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing);
const uint16_t feedback_ti = pucch_Config->dl_DataToUL_ACK->list.array[dci->pdsch_to_harq_feedback_timing_indicator.val][0] + ntn_ue_koffset;
AssertFatal(feedback_ti > GET_DURATION_RX_TO_TX(&mac->ntn_ta),
AssertFatal(feedback_ti > GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing),
"PDSCH to HARQ feedback time (%d) needs to be higher than DURATION_RX_TO_TX (%ld). Min feedback time set in config "
"file (min_rxtxtime).\n",
feedback_ti,
GET_DURATION_RX_TO_TX(&mac->ntn_ta));
GET_DURATION_RX_TO_TX(&mac->ntn_ta, dlsch_pdu->SubcarrierSpacing));
// set the harq status at MAC for feedback
const int tpc[] = {-1, 0, 1, 3};
+17 -14
View File
@@ -1037,9 +1037,11 @@ void nr_ue_aperiodic_srs_scheduling(NR_UE_MAC_INST_t *mac, long resource_trigger
return;
}
AssertFatal(slot_offset > GET_DURATION_RX_TO_TX(&mac->ntn_ta),
int scs = mac->current_UL_BWP->scs;
AssertFatal(slot_offset > GET_DURATION_RX_TO_TX(&mac->ntn_ta, scs),
"Slot offset between DCI and aperiodic SRS (%d) needs to be higher than DURATION_RX_TO_TX (%ld)\n",
slot_offset, GET_DURATION_RX_TO_TX(&mac->ntn_ta));
slot_offset,
GET_DURATION_RX_TO_TX(&mac->ntn_ta, scs));
int n_slots_frame = mac->frame_structure.numb_slots_frame;
int sched_slot = (slot + slot_offset) % n_slots_frame;
if (!is_ul_slot(sched_slot, &mac->frame_structure)) {
@@ -1563,23 +1565,24 @@ int nr_ue_pusch_scheduler(const NR_UE_MAC_INST_t *mac,
AssertFatal(1 == 0, "Invalid numerology %i\n", mu);
}
AssertFatal((k2 + delta) > GET_DURATION_RX_TO_TX(&mac->ntn_ta),
"Slot offset (%ld) for Msg3 needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to %ld in gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n",
AssertFatal((k2 + delta) > GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu),
"Slot offset (%ld) for Msg3 needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to "
"%ld in gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n",
k2,
GET_DURATION_RX_TO_TX(&mac->ntn_ta),
GET_DURATION_RX_TO_TX(&mac->ntn_ta),
GET_DURATION_RX_TO_TX(&mac->ntn_ta));
GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu),
GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu),
GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu));
*slot_tx = (current_slot + k2 + delta) % slots_per_frame;
*frame_tx = (current_frame + (current_slot + k2 + delta) / slots_per_frame) % MAX_FRAME_NUMBER;
} else {
AssertFatal(k2 > GET_DURATION_RX_TO_TX(&mac->ntn_ta),
"Slot offset K2 (%ld) needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to %ld in gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n",
AssertFatal(k2 > GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu),
"Slot offset K2 (%ld) needs to be higher than DURATION_RX_TO_TX (%ld). Please set min_rxtxtime at least to %ld in "
"gNB config file or gNBs.[0].min_rxtxtime=%ld via command line.\n",
k2,
GET_DURATION_RX_TO_TX(&mac->ntn_ta),
GET_DURATION_RX_TO_TX(&mac->ntn_ta),
GET_DURATION_RX_TO_TX(&mac->ntn_ta));
GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu),
GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu),
GET_DURATION_RX_TO_TX(&mac->ntn_ta, mu));
if (k2 < 0) { // This can happen when a false DCI is received
LOG_W(PHY, "%d.%d. Received k2 %ld\n", current_frame, current_slot, k2);
@@ -2203,7 +2206,7 @@ static void nr_ue_prach_scheduler(NR_UE_MAC_INST_t *mac, frame_t frameP, slot_t
add_slots++;
}
nr_timer_setup(&ra->response_window_timer,
ra->response_window_setup_time + add_slots + GET_DURATION_RX_TO_TX(&mac->ntn_ta),
ra->response_window_setup_time + add_slots + GET_DURATION_RX_TO_TX(&mac->ntn_ta, mac->current_DL_BWP->scs),
1);
nr_timer_start(&ra->response_window_timer);
} else if (ra->ra_type == RA_2_STEP) {
@@ -1032,9 +1032,9 @@ void nr_schedule_ue_spec(module_id_t module_id,
harq->round,
nr_get_rv(harq->round % 4),
harq->ndi,
pucch->timing_indicator,
pucch->frame,
pucch->ul_slot,
pucch ? pucch->timing_indicator : 0,
pucch ? pucch->frame : 0,
pucch ? pucch->ul_slot : 0,
sched_pdsch->pucch_allocation,
sched_ctrl->tpc1);
@@ -1222,7 +1222,7 @@ void nr_schedule_ue_spec(module_id_t module_id,
dci_payload.ndi,
dci_payload.rv,
dci_payload.tpc,
pucch->timing_indicator);
pucch ? pucch->timing_indicator : 0);
const int rnti_type = TYPE_C_RNTI_;
fill_dci_pdu_rel15(&UE->sc_info,
@@ -650,6 +650,11 @@ static void extract_pucch_csi_report(NR_CSI_MeasConfig_t *csi_MeasConfig,
// verify if report with current id has been scheduled for this frame and slot
if ((n_slots_frame*frame + slot - offset)%period == 0) {
reportQuantity_type = csi_report->reportQuantity_type;
// phy-test has hardcoded allocation, so no use to handle CSI reports except RSRP
if (get_softmodem_params()->phy_test
&& reportQuantity_type != NR_CSI_ReportConfig__reportQuantity_PR_ssb_Index_RSRP
&& reportQuantity_type != NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP)
continue;
LOG_D(MAC,"SFN/SF:%d/%d reportQuantity type = %d\n",frame,slot,reportQuantity_type);
switch(reportQuantity_type) {
case NR_CSI_ReportConfig__reportQuantity_PR_cri_RSRP:
@@ -869,8 +874,7 @@ void handle_nr_uci_pucch_2_3_4(module_id_t mod_id, frame_t frame, slot_t slot, c
}
free(uci_234->harq.harq_payload);
}
/* phy-test has hardcoded allocation, so no use to handle CSI reports */
if ((uci_234->pduBitmap >> 2) & 0x01 && !get_softmodem_params()->phy_test) {
if ((uci_234->pduBitmap >> 2) & 0x01) {
LOG_D(NR_MAC, "CSI CRC %d\n", uci_234->csi_part1.csi_part1_crc);
if (uci_234->csi_part1.csi_part1_crc != 1) {
// API to parse the csi report and store it into sched_ctrl
-2
View File
@@ -337,8 +337,6 @@ int nr_write_ce_dlsch_pdu(module_id_t module_idP,
unsigned char drx_cmd,
unsigned char *ue_cont_res_id);
int binomial(int n, int k);
/* \brief Function to indicate a received SDU on ULSCH.
@param Mod_id Instance ID of gNB
@param CC_id Component carrier index
+7 -11
View File
@@ -2460,17 +2460,6 @@ NR_BCCH_DL_SCH_Message_t *get_SIB1_NR(const NR_ServingCellConfigCommon_t *scc,
asn1cSeqAdd(&sib1->si_SchedulingInfo->schedulingInfoList.list,schedulingInfo);*/
const NR_FreqBandIndicatorNR_t band = *scc->downlinkConfigCommon->frequencyInfoDL->frequencyBandList.list.array[0];
if (is_ntn_band(band)) {
sib1->nonCriticalExtension = CALLOC(1, sizeof(struct NR_SIB1_v1610_IEs));
sib1->nonCriticalExtension->nonCriticalExtension = CALLOC(1, sizeof(struct NR_SIB1_v1630_IEs));
sib1->nonCriticalExtension->nonCriticalExtension->nonCriticalExtension = CALLOC(1, sizeof(struct NR_SIB1_v1700_IEs));
// If cell provides NTN access, set cellBarredNTN to notBarred.
struct NR_SIB1_v1700_IEs *sib1_v1700 = sib1->nonCriticalExtension->nonCriticalExtension->nonCriticalExtension;
sib1_v1700->cellBarredNTN_r17 = CALLOC(1, sizeof(long));
*sib1_v1700->cellBarredNTN_r17 = NR_SIB1_v1700_IEs__cellBarredNTN_r17_notBarred;
}
// servingCellConfigCommon
asn1cCalloc(sib1->servingCellConfigCommon, ServCellCom);
NR_BWP_DownlinkCommon_t *initialDownlinkBWP = &ServCellCom->downlinkConfigCommon.initialDownlinkBWP;
@@ -2485,6 +2474,7 @@ NR_BCCH_DL_SCH_Message_t *get_SIB1_NR(const NR_ServingCellConfigCommon_t *scc,
frequencyInfoDL->frequencyBandList.list.array[i];
}
const NR_FreqBandIndicatorNR_t band = *frequencyInfoDL->frequencyBandList.list.array[0];
frequency_range_t frequency_range = get_freq_range_from_band(band);
sib1->servingCellConfigCommon->downlinkConfigCommon.frequencyInfoDL.offsetToPointA = get_ssb_offset_to_pointA(*scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencySSB,
scc->downlinkConfigCommon->frequencyInfoDL->absoluteFrequencyPointA,
@@ -2712,6 +2702,12 @@ NR_BCCH_DL_SCH_Message_t *get_SIB1_NR(const NR_ServingCellConfigCommon_t *scc,
asn1cSeqAdd(&si_schedulinginfo2_r17->sib_MappingInfo_r17.list, sib_type_info);
asn1cSeqAdd(&sib_v17_scheduling_info->schedulingInfoList2_r17.list, si_schedulinginfo2_r17);
sib1_1700->si_SchedulingInfo_v1700 = sib_v17_scheduling_info;
if (is_ntn_band(band)) {
// If cell provides NTN access, set cellBarredNTN to notBarred.
sib1_1700->cellBarredNTN_r17 = CALLOC(1, sizeof(long));
*sib1_1700->cellBarredNTN_r17 = NR_SIB1_v1700_IEs__cellBarredNTN_r17_notBarred;
}
}
}
+5
View File
@@ -37,3 +37,8 @@ add_boolean_option(OAI_USRP OFF "Activate OAI's USRP driver" OFF)
if(OAI_USRP)
add_subdirectory(USRP)
endif()
add_boolean_option(VRTSIM_RADIO ON "Activate OAI's shared memory radio driver" OFF)
if(VRTSIM_RADIO)
add_subdirectory(vrtsim)
endif()
+8 -7
View File
@@ -30,18 +30,19 @@ endif()
add_compile_options(-Wno-packed-not-aligned)
set_target_properties(oran_fhlib_5g PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -march=native")
target_link_libraries(oran_fhlib_5g PRIVATE xran::xran)
target_link_libraries(oran_fhlib_5g PRIVATE ${dpdk_LINK_LIBRARIES})
target_link_libraries(oran_fhlib_5g PRIVATE xran::xran ${dpdk_LINK_LIBRARIES} ${T_LIB} pthread dl rt m numa)
target_include_directories(oran_fhlib_5g PRIVATE ${dpdk_INCLUDE_DIRS})
target_link_libraries(oran_fhlib_5g PRIVATE pthread dl rt m numa)
target_link_libraries(oran_fhlib_5g PRIVATE ${T_LIB})
# TODO: can be removed?
target_include_directories(oran_fhlib_5g PRIVATE ${xran_INCLUDE_DIRS}/../src/)
add_boolean_option(OAI_FHI72_USE_POLLING OFF "Enable polling in FHI72 driver" ON)
message(STATUS "Building FHI72 CUS library")
set_target_properties(oran_fhlib_5g PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_custom_command(TARGET oran_fhlib_5g POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink liboran_fhlib_5g.so liboai_transpro.so
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
add_boolean_option(OAI_FHI72_MPLANE OFF "Activate OAI's FHI 7.2 M-plane support" OFF)
if(OAI_FHI72_MPLANE)
add_subdirectory(mplane)
endif()
+73
View File
@@ -0,0 +1,73 @@
add_library(oran_fhlib_5g_mplane MODULE
../oran_isolate.c
../oaioran.c
../oran-config.c
../oran-init.c
)
add_dependencies(oran_fhlib_5g_mplane oran_fhlib_5g)
if(xran_VERSION VERSION_EQUAL 5.1.4)
target_compile_definitions(oran_fhlib_5g_mplane PRIVATE E_RELEASE)
elseif(xran_VERSION VERSION_EQUAL 6.1.1)
target_compile_definitions(oran_fhlib_5g_mplane PRIVATE F_RELEASE)
endif()
# Ignore xran-specific warning: we don't care/can't change the following warning, so suppress
# alignment 1 of 'struct XYZ' is less than 2
add_compile_options(-Wno-packed-not-aligned)
set_target_properties(oran_fhlib_5g_mplane PROPERTIES COMPILE_FLAGS "-fvisibility=hidden -march=native")
target_link_libraries(oran_fhlib_5g_mplane PRIVATE xran::xran ${dpdk_LINK_LIBRARIES} ${T_LIB} pthread dl rt m numa)
target_include_directories(oran_fhlib_5g_mplane PRIVATE ${dpdk_INCLUDE_DIRS})
target_compile_definitions(oran_fhlib_5g_mplane PRIVATE OAI_MPLANE)
###################################################
target_sources(oran_fhlib_5g_mplane PRIVATE
init-mplane.c
connect-mplane.c
rpc-send-recv.c
get-mplane.c
subscribe-mplane.c
config-mplane.c
ru-mplane-api.c
xml/get-xml.c
yang/get-yang.c
yang/create-yang-config.c
)
pkg_check_modules(libyang REQUIRED libyang)
pkg_check_modules(libnetconf2 REQUIRED libnetconf2)
if(libyang_VERSION VERSION_LESS_EQUAL 1.0.240 AND libnetconf2_VERSION VERSION_LESS_EQUAL 1.1.46)
target_compile_definitions(oran_fhlib_5g_mplane PRIVATE MPLANE_V1)
elseif((libyang_VERSION VERSION_GREATER 1.0.240 AND libyang_VERSION VERSION_LESS 2.1.4))
message(FATAL_ERROR "Minimum libyang v2 subversion required is v2.1.4, but found ${libyang_VERSION}")
elseif((libnetconf2_VERSION VERSION_GREATER 1.1.46 AND libnetconf2_VERSION VERSION_LESS 2.1.25))
message(FATAL_ERROR "Minimum libnetconf2 v2 subversion required is v2.1.25, but found ${libnetconf2_VERSION}")
elseif((libyang_VERSION VERSION_GREATER_EQUAL 2.1.4 AND libyang_VERSION VERSION_LESS_EQUAL 2.1.111) AND (libnetconf2_VERSION VERSION_GREATER_EQUAL 2.1.25 AND libnetconf2_VERSION VERSION_LESS_EQUAL 2.1.37))
target_compile_definitions(oran_fhlib_5g_mplane PRIVATE MPLANE_V2)
else()
message(FATAL_ERROR "Unknown libyang version ${libyang_VERSION} and libnetconf2 version ${libnetconf2_VERSION}")
endif()
target_include_directories(oran_fhlib_5g_mplane PRIVATE ${libyang_INCLUDE_DIRS} ${libnetconf2_INCLUDE_DIRS})
target_link_libraries(oran_fhlib_5g_mplane PRIVATE ${libyang_LDFLAGS} ${libyang_LINK_LIBRARIES} ${libnetconf2_LINK_LIBRARIES})
pkg_check_modules(libssh REQUIRED libssh)
target_include_directories(oran_fhlib_5g_mplane PRIVATE ${libssh_INCLUDE_DIRS})
target_link_libraries(oran_fhlib_5g_mplane PRIVATE ${libssh_LINK_LIBRARIES})
find_package(LibXml2 REQUIRED)
target_link_libraries(oran_fhlib_5g_mplane PRIVATE LibXml2::LibXml2)
set(YANG_MODELS "${CMAKE_SOURCE_DIR}/radio/fhi_72/mplane/yang/models")
target_compile_definitions(oran_fhlib_5g_mplane PRIVATE YANG_MODELS="${YANG_MODELS}")
message(STATUS "Building FHI72 CUSM library")
set_target_properties(oran_fhlib_5g_mplane PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_custom_command(TARGET oran_fhlib_5g_mplane POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink liboran_fhlib_5g_mplane.so liboai_transpro.so
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
+117
View File
@@ -0,0 +1,117 @@
/*
* 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 "config-mplane.h"
#include "rpc-send-recv.h"
#include "yang/get-yang.h"
#include "yang/create-yang-config.h"
#include "common/utils/assertions.h"
#include <libyang/libyang.h>
#include <nc_client.h>
bool edit_config_mplane(ru_session_t *ru_session, const char *buffer, const openair0_config_t *oai, const size_t num_rus)
{
int timeout = CLI_RPC_REPLY_TIMEOUT;
struct nc_rpc *rpc;
NC_WD_MODE wd = NC_WD_ALL;
NC_PARAMTYPE param = NC_PARAMTYPE_CONST;
NC_DATASTORE target = NC_DATASTORE_CANDIDATE;
NC_RPC_EDIT_DFLTOP op = NC_RPC_EDIT_DFLTOP_MERGE;
NC_RPC_EDIT_TESTOPT test = NC_RPC_EDIT_TESTOPT_UNKNOWN;
NC_RPC_EDIT_ERROPT err = NC_RPC_EDIT_ERROPT_UNKNOWN;
bool success = false;
struct ly_ctx *ctx = NULL;
success = load_yang_models(ru_session, buffer, &ctx);
AssertError(success, return false, "[MPLANE] Unable to continue.\n");
char *content = NULL;
success = configure_ru_from_yang(&ctx, ru_session, oai, num_rus, &content);
AssertError(success, return false, "[MPLANE] Unable to create content for <edit-config> RPC.\n");
MP_LOG_I("RPC request to RU \"%s\" = <edit-config>:\n%s\n", ru_session->ru_ip_add, content);
rpc = nc_rpc_edit(target, op, test, err, content, param);
AssertError(rpc != NULL, return false, "[MPLANE] <edit-config> RPC creation failed.\n");
success = rpc_send_recv((struct nc_session *)ru_session->session, rpc, wd, timeout, NULL);
AssertError(success, return false, "[MPLANE] Failed to edit configuration for the candidate datastore.\n");
MP_LOG_I("Successfully edited the candidate datastore for RU \"%s\".\n", ru_session->ru_ip_add);
nc_rpc_free(rpc);
free(content);
#ifdef MPLANE_V1
ly_ctx_destroy(ctx, NULL);
#elif defined MPLANE_V2
ly_ctx_destroy(ctx);
#endif
return true;
}
bool validate_config_mplane(ru_session_t *ru_session)
{
int timeout = CLI_RPC_REPLY_TIMEOUT;
struct nc_rpc *rpc;
NC_WD_MODE wd = NC_WD_ALL;
NC_PARAMTYPE param = NC_PARAMTYPE_CONST;
char *src_start = NULL;
NC_DATASTORE source = NC_DATASTORE_CANDIDATE;
MP_LOG_I("RPC request to RU \"%s\" = <validate> candidate datastore.\n", ru_session->ru_ip_add);
rpc = nc_rpc_validate(source, src_start, param);
AssertError(rpc != NULL, return false, "[MPLANE] <validate> RPC creation failed.\n");
bool success = rpc_send_recv((struct nc_session *)ru_session->session, rpc, wd, timeout, NULL);
AssertError(success, return false, "[MPLANE] Failed to validate candidate datastore.\n");
MP_LOG_I("Successfully validated candidate datastore for RU \"%s\".\n", ru_session->ru_ip_add);
nc_rpc_free(rpc);
return true;
}
bool commit_config_mplane(ru_session_t *ru_session)
{
int timeout = CLI_RPC_REPLY_TIMEOUT;
struct nc_rpc *rpc;
NC_WD_MODE wd = NC_WD_ALL;
NC_PARAMTYPE param = NC_PARAMTYPE_CONST;
int confirmed = 0;
int32_t confirm_timeout = 0;
char *persist = NULL, *persist_id = NULL;
MP_LOG_I("RPC request to RU \"%s\" = <commit> candidate datastore.\n", ru_session->ru_ip_add);
rpc = nc_rpc_commit(confirmed, confirm_timeout, persist, persist_id, param);
AssertError(rpc != NULL, return false, "[MPLANE] <commit> RPC creation failed.\n");
bool success = rpc_send_recv((struct nc_session *)ru_session->session, rpc, wd, timeout, NULL);
AssertError(success, return false, "[MPLANE] Failed to commit candidate datastore.\n");
MP_LOG_I("Successfully commited CU-planes configuration into running datastore for RU \"%s\".\n", ru_session->ru_ip_add);
nc_rpc_free(rpc);
return true;
}
+34
View File
@@ -0,0 +1,34 @@
/*
* 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 CONFIGURE_MPLANE_H
#define CONFIGURE_MPLANE_H
#include "ru-mplane-api.h"
#include "radio/COMMON/common_lib.h"
bool edit_config_mplane(ru_session_t *ru_session, const char *buffer, const openair0_config_t *oai, const size_t num_rus);
bool validate_config_mplane(ru_session_t *ru_session);
bool commit_config_mplane(ru_session_t *ru_session);
#endif /* CONFIGURE_MPLANE_H */
+73
View File
@@ -0,0 +1,73 @@
/*
* 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 "connect-mplane.h"
#include "common/utils/assertions.h"
#include <libyang/libyang.h>
#include <nc_client.h>
static int my_auth_hostkey_check(const char *hostname, ssh_session session, void *priv)
{
(void)hostname;
(void)session;
(void)priv;
return 0;
}
bool connect_mplane(ru_session_t *ru_session)
{
int port = NC_PORT_SSH;
char *user = "oranbenetel";
nc_client_ssh_set_username(user);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 1); // ssh-key identification
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1);
nc_client_ssh_set_auth_hostkey_check_clb(my_auth_hostkey_check, "DATA"); // host-key identification
MP_LOG_I("RPC request to RU \"%s\" = <connect> with username \"%s\" and port ID \"%d\".\n", ru_session->ru_ip_add, user, port);
ru_session->session = nc_connect_ssh(ru_session->ru_ip_add, port, NULL);
AssertError(ru_session->session != NULL, return false, "[MPLANE] RU IP address %s unreachable. Maybe M-plane is disabled on the RU?\n", ru_session->ru_ip_add);
MP_LOG_I("Successfuly connected to RU \"%s\" with username \"%s\" and port ID \"%d\".\n", ru_session->ru_ip_add, user, port);
return true;
}
void disconnect_mplane(void *rus_disconnect)
{
ru_session_list_t *ru_session_list = (ru_session_list_t *)rus_disconnect;
for (size_t i = 0; i <ru_session_list->num_rus; i++) {
ru_session_t *ru_session = &ru_session_list->ru_session[i];
if (ru_session->session == NULL)
continue;
MP_LOG_I("Disconnecting from RU \"%s\".\n", ru_session->ru_ip_add);
nc_session_free(ru_session->session, NULL);
ru_session->session = NULL;
}
nc_client_destroy();
}
+31
View File
@@ -0,0 +1,31 @@
/*
* 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 CONNECT_MPLANE_H
#define CONNECT_MPLANE_H
#include "ru-mplane-api.h"
bool connect_mplane(ru_session_t *ru_session);
void disconnect_mplane(void *rus_disconnect);
#endif /* CONNECT_MPLANE_H */
+47
View File
@@ -0,0 +1,47 @@
/*
* 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 "get-mplane.h"
#include "rpc-send-recv.h"
#include "common/utils/assertions.h"
bool get_mplane(ru_session_t *ru_session, char **answer)
{
int timeout = CLI_RPC_REPLY_TIMEOUT;
struct nc_rpc *rpc;
char *filter = NULL; // e.g. "/o-ran-delay-management:delay-management";
NC_WD_MODE wd = NC_WD_ALL;
NC_PARAMTYPE param = NC_PARAMTYPE_CONST;
MP_LOG_I("RPC request to RU \"%s\" = <get> operational datastore.\n", ru_session->ru_ip_add);
rpc = nc_rpc_get(filter, wd, param);
AssertError(rpc != NULL, return false, "[MPLANE] <get> RPC creation failed.\n");
bool success = rpc_send_recv((struct nc_session *)ru_session->session, rpc, wd, timeout, answer);
AssertError(success, return false, "[MPLANE] Unable to retrieve operational datastore.\n");
MP_LOG_I("Successfully retrieved operational datastore from RU \"%s\".\n", ru_session->ru_ip_add);
nc_rpc_free(rpc);
return true;
}
+29
View File
@@ -0,0 +1,29 @@
/*
* 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 GET_MPLANE_H
#define GET_MPLANE_H
#include "ru-mplane-api.h"
bool get_mplane(ru_session_t *ru_session, char **answer);
#endif /* GET_MPLANE_H */
+205
View File
@@ -0,0 +1,205 @@
/*
* 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 "init-mplane.h"
#include "radio/fhi_72/oran-params.h"
#include "get-mplane.h"
#include "subscribe-mplane.h"
#include "config-mplane.h"
#include "xml/get-xml.h"
#include <libyang/libyang.h>
#include <nc_client.h>
static void lnc2_print_clb(NC_VERB_LEVEL level, const char *msg)
{
switch (level) {
case NC_VERB_ERROR:
MP_LOG_I("[LIBNETCONF2] ERROR: %s.\n", msg);
break;
case NC_VERB_WARNING:
MP_LOG_I("[LIBNETCONF2] WARNING: %s.\n", msg);
break;
case NC_VERB_VERBOSE:
MP_LOG_I("[LIBNETCONF2] VERBOSE: %s.\n", msg);
break;
case NC_VERB_DEBUG:
case NC_VERB_DEBUG_LOWLVL:
MP_LOG_I("[LIBNETCONF2] DEBUG: %s.\n", msg);
break;
default:
assert(false && "[LIBNETCONF2] Unknown log level.");
}
}
static void ly_print_clb(LY_LOG_LEVEL level, const char *msg, const char *path)
{
switch (level) {
case LY_LLERR:
MP_LOG_I("[LIBYANG] ERROR: %s (path: %s).\n", msg, path);
break;
case LY_LLWRN:
MP_LOG_I("[LIBYANG] WARNING: %s (path: %s).\n", msg, path);
break;
case LY_LLVRB:
MP_LOG_I("[LIBYANG] VERBOSE: %s (path: %s).\n", msg, path);
break;
case LY_LLDBG:
MP_LOG_I("[LIBYANG] DEBUG: %s (path: %s).\n", msg, path);
break;
default:
assert(false && "[LIBYANG] Unknown log level.");
}
}
static const paramdef_t *gpd(const paramdef_t *pd, int num, const char *name)
{
/* the config module does not know const-correctness... */
int idx = config_paramidx_fromname((paramdef_t *)pd, num, (char *)name);
DevAssert(idx >= 0);
return &pd[idx];
}
bool init_mplane(ru_session_list_t *ru_session_list)
{
paramdef_t fhip[] = ORAN_GLOBALPARAMS_DESC;
int nump = sizeofArray(fhip);
int ret = config_get(config_get_if(), fhip, nump, CONFIG_STRING_ORAN);
if (ret <= 0) {
MP_LOG_I("Problem reading section \"%s\".\n", CONFIG_STRING_ORAN);
return false;
}
ru_session_list->du_key_pair = gpd(fhip, nump, ORAN_CONFIG_DU_KEYPAIR)->strlistptr;
int num_keys = gpd(fhip, nump, ORAN_CONFIG_DU_KEYPAIR)->numelt;
AssertError(num_keys == 2, return false, "[MPLANE] Expected {pub-key-path, priv-key-path}. Loaded {%s, %s}.\n", ru_session_list->du_key_pair[0], ru_session_list->du_key_pair[1]);
char **ru_ip_addrs = gpd(fhip, nump, ORAN_CONFIG_RU_IP_ADDR)->strlistptr;
int num_rus = gpd(fhip, nump, ORAN_CONFIG_RU_IP_ADDR)->numelt;
char **du_mac_addr = gpd(fhip, nump, ORAN_CONFIG_DU_ADDR)->strlistptr;
int num_dus = gpd(fhip, nump, ORAN_CONFIG_DU_ADDR)->numelt;
int32_t *vlan_tag = gpd(fhip, nump, ORAN_CONFIG_VLAN_TAG)->iptr;
int num_vlan_tags = gpd(fhip, nump, ORAN_CONFIG_VLAN_TAG)->numelt;
AssertError(num_dus == num_vlan_tags, return false, "[MPLANE] Number of DU MAC addresses should be equal to the number of VLAN tags.\n");
int num_cu_planes = num_dus / num_rus;
ru_session_list->num_rus = num_rus;
ru_session_list->ru_session = calloc(num_rus, sizeof(ru_session_t));
for (size_t i = 0; i < num_rus; i++) {
ru_session_t *ru_session = &ru_session_list->ru_session[i];
ru_session->session = NULL;
ru_session->ru_ip_add = calloc(strlen(ru_ip_addrs[i]) + 1, sizeof(char));
memcpy(ru_session->ru_ip_add, ru_ip_addrs[i], strlen(ru_ip_addrs[i]) + 1);
// store DU MAC addresses and VLAN tags
ru_session->ru_mplane_config.num_cu_planes = num_cu_planes;
ru_session->ru_mplane_config.du_mac_addr = calloc_or_fail(num_cu_planes, sizeof(char*));
ru_session->ru_mplane_config.vlan_tag = calloc_or_fail(num_cu_planes, sizeof(int32_t));
for (int j = 0; j < num_cu_planes; j++) {
const int idx = i*num_cu_planes+j;
ru_session->ru_mplane_config.du_mac_addr[j] = calloc(1, strlen(du_mac_addr[idx]) + 1);
memcpy(ru_session->ru_mplane_config.du_mac_addr[j], du_mac_addr[idx], strlen(du_mac_addr[idx]) + 1);
ru_session->ru_mplane_config.vlan_tag[j] = vlan_tag[idx];
}
}
nc_client_init();
int keypair_ret = nc_client_ssh_add_keypair(ru_session_list->du_key_pair[0], ru_session_list->du_key_pair[1]);
AssertError(keypair_ret == 0, return false, "[MPLANE] Unable to add DU ssh key pair.\n");
// logs for netconf2 and yang libraries
nc_set_print_clb(lnc2_print_clb);
ly_set_log_clb(ly_print_clb, 1);
return true;
}
bool manage_ru(ru_session_t *ru_session, const openair0_config_t *oai, const size_t num_rus)
{
bool success = false;
char *operational_ds = NULL;
success = get_mplane(ru_session, &operational_ds);
AssertError(success, return false, "[MPLANE] Unable to continue: could not get RU answer via get_mplane().\n");
bool ptp_state = false;
const char *sync_state = get_ru_xml_node(operational_ds, "sync-state");
if (strcmp(sync_state, "LOCKED") == 0) {
MP_LOG_I("RU is already PTP synchronized.\n");
ptp_state = true;
}
/* 1) as per M-plane spec, RU must be in supervised mode,
where stream = NULL && filter = "/o-ran-supervision:supervision-notification";
2) additionally, we want to subscribe to PTP state change,
where stream = NULL && filter = "/o-ran-sync:synchronization-state-change";
=> since more than one subscription at the time within one session is not possible, we will subscribe to all notifications */
const char *stream = "NETCONF";
const char *filter = NULL;
ru_session->ru_notif.ptp_state = ptp_state;
success = subscribe_mplane(ru_session, stream, filter, (void *)&ru_session->ru_notif);
AssertError(success, return false, "[MPLANE] Unable to continue: could not get RU answer via subscribe_mplane().\n");
// when subscribed to the supervision notification, the watchdog timer needs to be updated
char *watchdog_answer = NULL;
success = update_timer_mplane(ru_session, &watchdog_answer);
AssertError(success, return false, "[MPLANE] Unable to update the watchdog timer. RU will do a reset after default supervision timer of (60+10)[s] expires.\n");
MP_LOG_I("Watchdog timer answer: \n\t%s\n", watchdog_answer);
// save RU info for xran
const int max_num_ant = max(oai->tx_num_channels/num_rus, oai->rx_num_channels/num_rus);
success = get_config_for_xran(operational_ds, max_num_ant, &ru_session->xran_mplane);
AssertError(success, return false, "[MPLANE] Unable to retrieve required info for xran from RU \"%s\".\n", ru_session->ru_ip_add);
// save the U-plane info
success = get_uplane_info(operational_ds, &ru_session->ru_mplane_config);
AssertError(success, return false, "[MPLANE] Unable to get U-plane info from RU operational datastore.\n");
if (ru_session->ru_notif.ptp_state) {
success = edit_config_mplane(ru_session, operational_ds, oai, num_rus);
AssertError(success, return false, "[MPLANE] Unable to edit the RU configuration.\n");
success = validate_config_mplane(ru_session);
AssertError(success, return false, "[MPLANE] Unable to validate the RU configuration.\n");
success = commit_config_mplane(ru_session);
AssertError(success, return false, "[MPLANE] Unable to commit the RU configuration.\n");
}
const char *usage_state = get_ru_xml_node(operational_ds, "usage-state");
MP_LOG_I("Usage state = \"%s\" for RU \"%s\".\n", usage_state, ru_session->ru_ip_add);
if (strcmp(usage_state, "busy") == 0) { // carriers are already activated
ru_session->ru_notif.rx_carrier_state = true;
ru_session->ru_notif.tx_carrier_state = true;
ru_session->ru_notif.config_change = true;
} else {
ru_session->ru_notif.rx_carrier_state = false;
ru_session->ru_notif.tx_carrier_state = false;
ru_session->ru_notif.config_change = false;
}
free(operational_ds);
free(watchdog_answer);
return true;
}
+32
View File
@@ -0,0 +1,32 @@
/*
* 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 OAI_MPLANE_H
#define OAI_MPLANE_H
#include "ru-mplane-api.h"
#include "radio/COMMON/common_lib.h"
bool init_mplane(ru_session_list_t *ru_session_list);
bool manage_ru(ru_session_t *ru_session, const openair0_config_t *oai, const size_t num_rus);
#endif /* OAI_MPLANE_H */
+296
View File
@@ -0,0 +1,296 @@
/*
* 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 "rpc-send-recv.h"
#include "common/utils/assertions.h"
#ifdef MPLANE_V1
static bool recv_v1(struct nc_session *session, struct nc_rpc *rpc, NC_MSG_TYPE msgtype, const uint64_t msgid, const NC_WD_MODE wd_mode, const int timeout_ms, char **answer)
{
struct nc_reply *reply;
struct nc_reply_data *data_rpl;
struct nc_reply_error *error;
LYD_FORMAT output_format = LYD_XML;
uint32_t ly_wd;
while (1) {
msgtype = nc_recv_reply(session, rpc, msgid, timeout_ms, 0, &reply);
if (msgtype == NC_MSG_ERROR) {
AssertError(false, return false, "[MPLANE] Failed to receive a reply.\n");
} else if (msgtype == NC_MSG_WOULDBLOCK) {
AssertError(false, return false, "[MPLANE] Timeout for receiving a reply expired.\n");
} else if (msgtype == NC_MSG_NOTIF) {
/* read again */
continue;
} else if (msgtype == NC_MSG_REPLY_ERR_MSGID) {
/* unexpected message, try reading again to get the correct reply */
MP_LOG_I("Unexpected reply received - ignoring and waiting for the correct reply.\n");
nc_reply_free(reply);
continue;
}
break;
}
switch (reply->type) {
case NC_RPL_OK:
MP_LOG_I("RPC reply = OK.\n");
break;
case NC_RPL_DATA:
data_rpl = (struct nc_reply_data *)reply;
switch (wd_mode) {
case NC_WD_ALL:
ly_wd = LYP_WD_ALL;
break;
case NC_WD_ALL_TAG:
ly_wd = LYP_WD_ALL_TAG;
break;
case NC_WD_TRIM:
ly_wd = LYP_WD_TRIM;
break;
case NC_WD_EXPLICIT:
ly_wd = LYP_WD_EXPLICIT;
break;
default:
ly_wd = 0; // when wd_mode = NC_WD_UNKNOWN
break;
}
if (nc_rpc_get_type(rpc) == NC_RPC_GETSCHEMA) {
AssertError((!data_rpl->data || (data_rpl->data->schema->nodetype != LYS_RPC) || (data_rpl->data->child == NULL)
|| (data_rpl->data->child->schema->nodetype != LYS_ANYXML)), return false, "[MPLANE] Cannot get schema.\n");
struct lyd_node_anydata *any = (struct lyd_node_anydata *)data_rpl->data->child;
switch (any->value_type) {
case LYD_ANYDATA_CONSTSTRING:
case LYD_ANYDATA_STRING:
*answer = strdup(any->value.str);
break;
case LYD_ANYDATA_DATATREE:
lyd_print_mem(answer, any->value.tree, output_format, LYP_WITHSIBLINGS);
break;
case LYD_ANYDATA_XML:
lyxml_print_mem(answer, any->value.xml, LYXML_PRINT_SIBLINGS);
break;
default:
AssertError(false, return false, "[MPLANE] Unknown yang type.\n");
}
break;
} else if (nc_rpc_get_type(rpc) == NC_RPC_GETCONFIG || nc_rpc_get_type(rpc) == NC_RPC_GET) {
const char *tag = nc_rpc_get_type(rpc) == NC_RPC_GETCONFIG ? "config" : "data";
char *buffer = NULL;
lyd_print_mem(&buffer, data_rpl->data, output_format, LYP_WITHSIBLINGS | LYP_NETCONF | ly_wd);
*answer = calloc(strlen(buffer)+128, sizeof(char));
sprintf(*answer, "<%s xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n%s</%s>", tag, buffer, tag);
} else {
AssertError(false, return false, "[MPLANE] Unknown RPC type.\n");
}
break;
case NC_RPL_ERROR:
error = (struct nc_reply_error *)reply;
for (int i = 0; i < error->count; ++i) {
const struct nc_err *err = &error->err[i];
MP_LOG_I("RPC reply = ERROR\n\
\ttype: %s\n\
\ttag: %s\n\
\tseverity: %s\n\
\tapp-tag: %s\n\
\tpath: %s\n\
\tmessage: %s\n\
\tSID: %s\n",
err->type,
err->tag,
err->severity,
err->apptag,
err->path,
err->message,
err->sid);
for (int j = 0; j < err->attr_count; ++j) {
MP_LOG_I("\tbad-attr #%d: %s\n", j + 1, err->attr[j]);
}
for (int j = 0; j < err->elem_count; ++j) {
MP_LOG_I("\tbad-elem #%d: %s\n", j + 1, err->elem[j]);
}
for (int j = 0; j < err->ns_count; ++j) {
MP_LOG_I("\tbad-ns #%d: %s\n", j + 1, err->ns[j]);
}
for (int j = 0; j < err->other_count; ++j) {
char *str = NULL;
lyxml_print_mem(&str, err->other[j], 0);
MP_LOG_I("\tother #%d:\n%s\n", j + 1, str);
free(str);
}
}
AssertError(false, return false, "[MPLANE] Unable to continue.\n");
break;
default:
AssertError(false, return false, "[MPLANE] Internal error.\n");
nc_reply_free(reply);
}
nc_reply_free(reply);
return true;
}
#elif defined MPLANE_V2
static bool recv_v2(struct nc_session *session, struct nc_rpc *rpc, NC_MSG_TYPE msgtype, const uint64_t msgid, const NC_WD_MODE wd_mode, const int timeout_ms, char **answer)
{
struct lyd_node *envp, *op, *err;
LYD_FORMAT output_format = LYD_XML;
uint32_t ly_wd;
while (1) {
msgtype = nc_recv_reply(session, rpc, msgid, timeout_ms, &envp, &op);
if (msgtype == NC_MSG_ERROR) {
AssertError(false, return false, "[MPLANE] Failed to receive a reply.\n");
} else if (msgtype == NC_MSG_WOULDBLOCK) {
AssertError(false, return false, "[MPLANE] Timeout for receiving a reply for RPC expired.\n");
} else if (msgtype == NC_MSG_NOTIF) { // for SUBSCRIBE
/* read again */
continue;
} else if (msgtype == NC_MSG_REPLY_ERR_MSGID) {
/* unexpected message, try reading again to get the correct reply */
MP_LOG_I("Unexpected reply received - ignoring and waiting for the correct reply.\n");
lyd_free_tree(envp);
lyd_free_tree(op);
continue;
}
break;
}
/* get functionality */
if (op) {
/* data reply */
if (nc_rpc_get_type(rpc) == NC_RPC_GETSCHEMA) {
/* special case */
if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) {
AssertError(false, return false, "[MPLANE] Cannot get schema.");
}
struct lyd_node_any *any = (struct lyd_node_any *)lyd_child(op);
switch (any->value_type) {
case LYD_ANYDATA_STRING:
case LYD_ANYDATA_XML:
*answer = strdup(any->value.str);
break;
case LYD_ANYDATA_DATATREE:
lyd_print_mem(answer, any->value.tree, output_format, LYD_PRINT_WITHSIBLINGS);
break;
default:
AssertError(false, return false, "[MPLANE] Unexpected yang type.");
}
} else {
switch (wd_mode) {
case NC_WD_ALL:
ly_wd = LYD_PRINT_WD_ALL;
break;
case NC_WD_ALL_TAG:
ly_wd = LYD_PRINT_WD_ALL_TAG;
break;
case NC_WD_TRIM:
ly_wd = LYD_PRINT_WD_TRIM;
break;
case NC_WD_EXPLICIT:
ly_wd = LYD_PRINT_WD_EXPLICIT;
break;
default:
ly_wd = 0; // when wd_mode = NC_WD_UNKNOWN
break;
}
lyd_print_mem(answer, lyd_child(op), output_format, LYD_PRINT_WITHSIBLINGS | ly_wd);
}
/* edit/validate/commit functionalities */
} else if (!strcmp(LYD_NAME(lyd_child(envp)), "ok")) {
MP_LOG_I("RPC reply = OK.\n");
} else {
LY_LIST_FOR(lyd_child(envp), err) {
struct lyd_node *type, *tag, *severity, *app_tag, *path, *er_msg, *info = NULL;
lyd_find_sibling_opaq_next(lyd_child(err), "error-type", &type);
lyd_find_sibling_opaq_next(lyd_child(err), "error-tag", &tag);
lyd_find_sibling_opaq_next(lyd_child(err), "error-severity", &severity);
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &app_tag);
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &path);
lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &er_msg);
MP_LOG_I("RPC reply = ERROR\n\
\ttype: %s\n\
\ttag: %s\n\
\tseverity: %s\n\
\tapp-tag: %s\n\
\tpath: %s\n\
\tmessage: %s\n",
(type ? ((struct lyd_node_opaq *)type)->value : NULL),
(tag ? ((struct lyd_node_opaq *)tag)->value : NULL),
(severity ? ((struct lyd_node_opaq *)severity)->value : NULL),
(app_tag ? ((struct lyd_node_opaq *)app_tag)->value : NULL),
(path ? ((struct lyd_node_opaq *)path)->value : NULL),
(er_msg ? ((struct lyd_node_opaq *)er_msg)->value : NULL));
info = lyd_child(err);
while (!lyd_find_sibling_opaq_next(info, "error-info", &info)) {
char *str = NULL;
lyd_print_mem(&str, lyd_child(info), output_format, LYD_PRINT_WITHSIBLINGS);
MP_LOG_I("\tinfo: %s\n", str);
info = info->next;
free(str);
}
lyd_free_tree(type);
lyd_free_tree(tag);
lyd_free_tree(severity);
lyd_free_tree(app_tag);
lyd_free_tree(path);
lyd_free_tree(er_msg);
lyd_free_tree(info);
}
lyd_free_tree(envp);
lyd_free_tree(op);
AssertError(false, return false, "[MPLANE] Unable to continue.\n");
}
lyd_free_tree(envp);
lyd_free_tree(op);
return true;
}
#endif
bool rpc_send_recv(struct nc_session *session, struct nc_rpc *rpc, const NC_WD_MODE wd_mode, const int timeout_ms, char **answer)
{
uint64_t msgid;
NC_MSG_TYPE msgtype;
msgtype = nc_send_rpc(session, rpc, 1000, &msgid);
if (msgtype == NC_MSG_ERROR) {
AssertError(false, return false, "[MPLANE] Failed to send the RPC.\n");
} else if (msgtype == NC_MSG_WOULDBLOCK) {
AssertError(false, return false, "[MPLANE] Timeout for sending the RPC expired.\n");
}
#ifdef MPLANE_V1
return recv_v1(session, rpc, msgtype, msgid, wd_mode, timeout_ms, answer);
#elif defined MPLANE_V2
return recv_v2(session, rpc, msgtype, msgid, wd_mode, timeout_ms, answer);
#else
AssertError(false, return false, "[MPLANE] Unknown M-plane version found. Tried \"MPLANE_V1\" and \"MPLANE_V2\".\n");
#endif
}
+34
View File
@@ -0,0 +1,34 @@
/*
* 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 RPC_SEND_RECEIVE_MPLANE_H
#define RPC_SEND_RECEIVE_MPLANE_H
#include "ru-mplane-api.h"
#include <libyang/libyang.h>
#include <nc_client.h>
#define CLI_RPC_REPLY_TIMEOUT 60000 // time [ms] to wait for server reply
bool rpc_send_recv(struct nc_session *session, struct nc_rpc *rpc, const NC_WD_MODE wd_mode, const int timeout_ms, char **answer);
#endif /* RPC_SEND_RECEIVE_MPLANE_H */
+157
View File
@@ -0,0 +1,157 @@
/*
* 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 "ru-mplane-api.h"
#include "xml/get-xml.h"
#include "common/utils/assertions.h"
#include <string.h>
static void free_match_list(char **match_list, size_t count)
{
for (size_t i = 0; i < count; i++) {
free(match_list[i]);
}
free(match_list);
}
static void fix_benetel_setting(xran_mplane_t *xran_mplane, const uint32_t interface_mtu, const int16_t first_iq_width, const int max_num_ant)
{
if (interface_mtu == 1500) {
MP_LOG_I("Interface MTU %d unreliable/not correctly reported by Benetel O-RU, hardcoding to 9600.\n", interface_mtu);
xran_mplane->mtu = 9600;
} else {
xran_mplane->mtu = interface_mtu;
}
if (first_iq_width != 9) {
MP_LOG_I("IQ bitwidth %d unreliable/not correctly reported by Benetel O-RU, hardcoding to 9.\n", first_iq_width);
xran_mplane->iq_width = 9;
} else {
xran_mplane->iq_width = first_iq_width;
}
xran_mplane->prach_offset = max_num_ant;
}
bool get_config_for_xran(const char *buffer, const int max_num_ant, xran_mplane_t *xran_mplane)
{
/* some O-RU vendors are not fully compliant as per M-plane specifications */
const char *ru_vendor = get_ru_xml_node(buffer, "mfg-name");
// RU MAC
xran_mplane->ru_mac_addr = get_ru_xml_node(buffer, "mac-address"); // TODO: support for VVDN, as it defines multiple MAC addresses
// MTU
const uint32_t interface_mtu = (uint32_t)atoi(get_ru_xml_node(buffer, "interface-mtu"));
// IQ bitwidth
char **match_list = NULL;
size_t count = 0;
get_ru_xml_list(buffer, "iq-bitwidth", &match_list, &count);
const int16_t first_iq_width = (int16_t)atoi((char *)match_list[0]);
free_match_list(match_list, count);
// PRACH offset
// xran_mplane->prach_offset
// DU port ID bitmask
xran_mplane->du_port_bitmask = 0xf000;
// Band sector bitmask
xran_mplane->band_sector_bitmask = 0x0f00;
// CC ID bitmask
xran_mplane->ccid_bitmask = 0x00f0;
// RU port ID bitmask
xran_mplane->ru_port_bitmask = 0x000f;
// DU port ID
xran_mplane->du_port = 0;
// Band sector
xran_mplane->band_sector = 0;
// CC ID
xran_mplane->ccid = 0;
// RU port ID
xran_mplane->ru_port = 0;
if (strcasecmp(ru_vendor, "BENETEL") == 0 /* || strcmp(ru_vendor, "VVDN-LPRU") == 0 || strcmp(ru_vendor, "Metanoia") == 0 */) {
fix_benetel_setting(xran_mplane, interface_mtu, first_iq_width, max_num_ant);
} else {
AssertError(false, return false, "[MPLANE] %s RU currently not supported.\n", ru_vendor);
}
MP_LOG_I("Storing the following information to forward to xran:\n\
RU MAC address %s\n\
MTU %d\n\
IQ bitwidth %d\n\
PRACH offset %d\n\
DU port bitmask %d\n\
Band sector bitmask %d\n\
CC ID bitmask %d\n\
RU port ID bitmask %d\n\
DU port ID %d\n\
Band sector ID %d\n\
CC ID %d\n\
RU port ID %d\n",
xran_mplane->ru_mac_addr,
xran_mplane->mtu,
xran_mplane->iq_width,
xran_mplane->prach_offset,
xran_mplane->du_port_bitmask,
xran_mplane->band_sector_bitmask,
xran_mplane->ccid_bitmask,
xran_mplane->ru_port_bitmask,
xran_mplane->du_port,
xran_mplane->band_sector,
xran_mplane->ccid,
xran_mplane->ru_port);
return true;
}
bool get_uplane_info(const char *buffer, ru_mplane_config_t *ru_mplane_config)
{
// Interface name
ru_mplane_config->interface_name = get_ru_xml_node(buffer, "interface");
// PDSCH
uplane_info_t *tx_end = &ru_mplane_config->tx_endpoints;
get_ru_xml_list(buffer, "static-low-level-tx-endpoints", &tx_end->name, &tx_end->num);
AssertError(tx_end->name != NULL, return false, "[MPLANE] Cannot get TX endpoint names.\n");
// TX carriers
uplane_info_t *tx_carriers = &ru_mplane_config->tx_carriers;
get_ru_xml_list(buffer, "tx-arrays", &tx_carriers->name, &tx_carriers->num);
AssertError(tx_carriers->name != NULL, return false, "[MPLANE] Cannot get TX carrier names.\n");
// PUSCH and PRACH
uplane_info_t *rx_end = &ru_mplane_config->rx_endpoints;
get_ru_xml_list(buffer, "static-low-level-rx-endpoints", &rx_end->name, &rx_end->num);
AssertError(rx_end->name != NULL, return false, "[MPLANE] Cannot get RX endpoint names.\n");
// RX carriers
uplane_info_t *rx_carriers = &ru_mplane_config->rx_carriers;
get_ru_xml_list(buffer, "rx-arrays", &rx_carriers->name, &rx_carriers->num);
AssertError(rx_carriers->name != NULL, return false, "[MPLANE] Cannot get RX carrier names.\n");
MP_LOG_I("Successfully retrieved all the U-plane info - interface name, TX/RX carrier names, and TX/RX endpoint names.\n");
return true;
}
+100
View File
@@ -0,0 +1,100 @@
/*
* 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 RU_MPLANE_API_H
#define RU_MPLANE_API_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "common/utils/LOG/log.h"
#define MP_LOG_I(x, args...) LOG_I(HW, "[MPLANE] " x, ##args)
#define MP_LOG_W(x, args...) LOG_W(HW, "[MPLANE] " x, ##args)
typedef struct {
bool ptp_state;
bool rx_carrier_state;
bool tx_carrier_state;
bool config_change;
// to be extended with any notification callback
} ru_notif_t;
typedef struct {
char *ru_mac_addr;
uint32_t mtu;
int16_t iq_width;
uint8_t prach_offset;
// DU sends to RU and xran
uint16_t du_port_bitmask;
uint16_t band_sector_bitmask;
uint16_t ccid_bitmask;
uint16_t ru_port_bitmask;
// DU retrieves from RU, and sends to xran
uint8_t du_port;
uint8_t band_sector;
uint8_t ccid;
uint8_t ru_port;
} xran_mplane_t;
typedef struct {
size_t num;
char **name;
} uplane_info_t;
typedef struct {
size_t num_cu_planes;
char **du_mac_addr; // one or two VF(s) for CU-planes
int32_t *vlan_tag; // one or two VF(s) for CU-planes
char *interface_name;
uplane_info_t tx_endpoints;
uplane_info_t rx_endpoints;
uplane_info_t tx_carriers;
uplane_info_t rx_carriers;
} ru_mplane_config_t;
typedef struct {
char *ru_ip_add;
ru_mplane_config_t ru_mplane_config;
void *session;
xran_mplane_t xran_mplane;
ru_notif_t ru_notif;
} ru_session_t;
typedef struct {
size_t num_rus;
ru_session_t *ru_session;
char **du_key_pair;
} ru_session_list_t;
bool get_config_for_xran(const char *buffer, const int max_num_ant, xran_mplane_t *xran_mplane);
bool get_uplane_info(const char *buffer, ru_mplane_config_t *ru_mplane_config);
#endif /* RU_MPLANE_API_H */
+160
View File
@@ -0,0 +1,160 @@
/*
* 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 "subscribe-mplane.h"
#include "rpc-send-recv.h"
#include "common/utils/assertions.h"
#include <libyang/libyang.h>
#include <nc_client.h>
#ifdef MPLANE_V1
static void recv_notif_v1(const struct nc_notif *notif, ru_notif_t *answer)
{
const char *node_name = notif->tree->child->attr->name;
const char *value = notif->tree->child->attr->value_str;
if (strcmp(node_name, "sync-state")) {
if (strcmp(value, "LOCKED") == 0) {
answer->ptp_state = true;
} else {
answer->ptp_state = false;
}
}
// carriers state - to be filled
}
static void notif_clb_v1(struct nc_session *session, const struct nc_notif *notif)
{
ru_notif_t *answer = nc_session_get_data(session);
LYD_FORMAT output_format = LYD_JSON;
char *subs_reply = NULL;
lyd_print_mem(&subs_reply, notif->tree, output_format, LYP_WITHSIBLINGS);
MP_LOG_I("\nReceived notification at (%s)\n%s\n", notif->datetime, subs_reply);
recv_notif_v1(notif, answer);
}
#elif MPLANE_V2
static void recv_notif_v2(struct lyd_node_inner *op, ru_notif_t *answer)
{
const char *notif = op->schema->name;
if (strcmp(notif, "synchronization-state-change") == 0) {
const char *value = lyd_get_value(op->child);
if (strcmp(value, "LOCKED") == 0) {
answer->ptp_state = true;
} else { // "FREERUN" or "HOLDOVER"
answer->ptp_state = false;
}
} else if (strcmp(notif, "rx-array-carriers-state-change") == 0) {
const char *value = lyd_get_value(lyd_child(op->child)->next);
if (strcmp(value, "READY") == 0) {
answer->rx_carrier_state = true;
} else { // "DISABLED" or "BUSY"
answer->rx_carrier_state = false;
}
} else if (strcmp(notif, "tx-array-carriers-state-change") == 0) {
const char *value = lyd_get_value(lyd_child(op->child)->next);
if (strcmp(value, "READY") == 0) {
answer->tx_carrier_state = true;
} else { // "DISABLED" or "BUSY"
answer->tx_carrier_state = false;
}
} else if (strcmp(notif, "netconf-config-change") == 0) {
answer->config_change = true;
}
}
static void notif_clb_v2(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op, void *user_data)
{
ru_notif_t *answer = (ru_notif_t *)user_data;
LYD_FORMAT output_format = LYD_JSON;
char *subs_reply = NULL;
lyd_print_mem(&subs_reply, op, output_format, LYD_PRINT_WITHSIBLINGS);
const char *ru_ip_add = nc_session_get_host(session);
MP_LOG_I("Received notification from RU \"%s\" at (%s)\n%s\n", ru_ip_add, ((struct lyd_node_opaq *)lyd_child(envp))->value, subs_reply);
recv_notif_v2((struct lyd_node_inner *)op, answer);
}
#endif
bool subscribe_mplane(ru_session_t *ru_session, const char *stream, const char *filter, void *answer)
{
int timeout = CLI_RPC_REPLY_TIMEOUT;
struct nc_rpc *rpc;
NC_WD_MODE wd = NC_WD_ALL;
NC_PARAMTYPE param = NC_PARAMTYPE_CONST;
char *start_time = NULL, *stop_time = NULL;
MP_LOG_I("RPC request to RU \"%s\" = <subscribe> with stream \"%s\" and filter \"%s\".\n", ru_session->ru_ip_add, stream, filter);
rpc = nc_rpc_subscribe(stream, NULL, start_time, stop_time, param);
AssertError(rpc != NULL, return false, "[MPLANE] <subscribe> RPC creation failed.\n");
/* create notification thread so that notifications can immediately be received */
#ifdef MPLANE_V1
if (!nc_session_ntf_thread_running(ru_session->session)) {
nc_session_set_data(ru_session->session, answer);
int ret = nc_recv_notif_dispatch(ru_session->session, notif_clb_v1);
AssertError(ret == 0, return false, "[MPLANE] Failed to create notification thread.\n");
} else {
MP_LOG_I("Notification thread is already running for RU %s.\n", ru_session->ru_ip_add);
}
#elif MPLANE_V2
int ret = nc_recv_notif_dispatch_data(ru_session->session, notif_clb_v2, answer, NULL);
AssertError(ret == 0, return false, "[MPLANE] Failed to create notification thread.\n");
#endif
bool success = rpc_send_recv((struct nc_session *)ru_session->session, rpc, wd, timeout, NULL);
AssertError(success, return false, "[MPLANE] Failed to subscribe to: stream \"%s\", filter \"%s\".\n", stream, filter);
MP_LOG_I("Successfully subscribed to all notifications from RU \"%s\".\n", ru_session->ru_ip_add);
nc_rpc_free(rpc);
return true;
}
bool update_timer_mplane(ru_session_t *ru_session, char **answer)
{
int timeout = CLI_RPC_REPLY_TIMEOUT;
struct nc_rpc *rpc;
NC_WD_MODE wd = NC_WD_ALL;
NC_PARAMTYPE param = NC_PARAMTYPE_CONST;
const char *content = "<supervision-watchdog-reset xmlns=\"urn:o-ran:supervision:1.0\">\n\
<supervision-notification-interval>65535</supervision-notification-interval>\n\
<guard-timer-overhead>65535</guard-timer-overhead>\n\
</supervision-watchdog-reset>";
MP_LOG_I("RPC request to RU \"%s\" = \"%s\".\n", ru_session->ru_ip_add, content);
rpc = nc_rpc_act_generic_xml(content, param);
AssertError(rpc != NULL, return false, "[MPLANE] <supervision-watchdog-reset> RPC creation failed.\n");
bool success = rpc_send_recv((struct nc_session *)ru_session->session, rpc, wd, timeout, answer);
AssertError(success, return false, "[MPLANE] Failed to update the watchdog timer.\n");
MP_LOG_I("Successfully updated supervision timer to (65535+65535)[s] for RU \"%s\".\n", ru_session->ru_ip_add);
nc_rpc_free(rpc);
return true;
}
+33
View File
@@ -0,0 +1,33 @@
/*
* 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 SUBSCRIBE_MPLANE_H
#define SUBSCRIBE_MPLANE_H
#include "ru-mplane-api.h"
#include <stdbool.h>
bool subscribe_mplane(ru_session_t *ru_session, const char *stream, const char *filter, void *answer);
bool update_timer_mplane(ru_session_t *ru_session, char **answer);
#endif /* SUBSCRIBE_MPLANE_H */
+95
View File
@@ -0,0 +1,95 @@
/*
* 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 "get-xml.h"
#include <libxml/parser.h>
#include <string.h>
static char *find_ru_xml_node(xmlNode *node, const char *filter)
{
for (xmlNode *cur_node = node; cur_node; cur_node = cur_node->next) {
if (cur_node->type != XML_ELEMENT_NODE)
continue;
if (strcmp((const char *)cur_node->name, filter) == 0) {
xmlNode *target_node = cur_node;
for(xmlNode *cur_node2 = cur_node->children; cur_node2; cur_node2 = cur_node2->next) {
if (cur_node2->type == XML_ELEMENT_NODE && strcmp((const char *)cur_node2->name, "name") == 0) {
target_node = cur_node2;
break;
}
}
return (char *)xmlNodeGetContent(target_node);
}
char *answer = find_ru_xml_node(cur_node->children, filter);
if (answer != NULL) {
return answer;
}
}
return NULL;
}
char *get_ru_xml_node(const char *buffer, const char *filter)
{
// Initialize the xml file
size_t len = strlen(buffer) + 1;
xmlDoc *doc = xmlReadMemory(buffer, len, NULL, NULL, 0);
xmlNode *root_element = xmlDocGetRootElement(doc);
return find_ru_xml_node(root_element->children, filter);
}
static void find_ru_xml_list(xmlNode *node, const char *filter, char ***match_list, size_t *count)
{
for (xmlNode *cur_node = node; cur_node; cur_node = cur_node->next) {
if (cur_node->type != XML_ELEMENT_NODE)
continue;
if (strcmp((const char *)cur_node->name, filter) == 0) {
xmlNode *name_node = NULL;
for (xmlNode *cur_node2 = cur_node->children; cur_node2; cur_node2 = cur_node2->next) {
if (cur_node2->type == XML_ELEMENT_NODE && strcmp((const char *)cur_node2->name, "name") == 0) {
name_node = cur_node2;
break;
}
}
const char *content = (const char *)xmlNodeGetContent(name_node ? name_node : cur_node);
if (content) {
*match_list = realloc(*match_list, (*count + 1) * sizeof(char *));
(*match_list)[*count] = strdup(content);
(*count)++;
}
}
find_ru_xml_list(cur_node->children, filter, match_list, count);
}
}
void get_ru_xml_list(const char *buffer, const char *filter, char ***match_list, size_t *count)
{
// Initialize the xml file
size_t len = strlen(buffer) + 1;
xmlDoc *doc = xmlReadMemory(buffer, len, NULL, NULL, 0);
xmlNode *root_element = xmlDocGetRootElement(doc);
find_ru_xml_list(root_element->children, filter, match_list, count);
}
+48
View File
@@ -0,0 +1,48 @@
/*
* 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 GET_MPLANE_INFO_XML_H
#define GET_MPLANE_INFO_XML_H
#include <stdio.h>
#include <stdbool.h>
/**
* @brief retrieves the node value that matches filter.
*
* @param[in] buffer Buffer in xml format.
* @param[in] filter The node name (matching criteria).
* @return Expected one string that matches the node name.
*/
char *get_ru_xml_node(const char *buffer, const char *filter);
/**
* @brief retrieves the node values that matches filter.
*
* @param[in] buffer Buffer in xml format.
* @param[in] filter The node name (matching criteria).
* @param[out] match_list Resulting list containing node values.
* @param[out] count The number of nodes that match criteria.
* @return Void.
*/
void get_ru_xml_list(const char *buffer, const char *filter, char ***match_list, size_t *count);
#endif /* GET_MPLANE_INFO_XML_H */
@@ -0,0 +1,392 @@
/*
* 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 "create-yang-config.h"
#include "common/utils/nr/nr_common.h"
#define VERIFY_SUCCESS(var, message, args...) AssertError(var, return false, message, ##args)
#ifdef MPLANE_V2
static bool create_cu_interface_v2(struct ly_ctx **ctx, const ru_session_t *ru_session, const size_t idx, const char *int_name, struct lyd_node **root)
{
LY_ERR ret = LY_SUCCESS;
ret = lyd_new_path(NULL, *ctx, "/ietf-interfaces:interfaces", NULL, 0, root);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create root \"interfaces\" node.\n");
struct lyd_node *interface_node = NULL;
ret = lyd_new_list(*root, NULL, "interface", 0, &interface_node, int_name);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"interface\" node.\n");
struct lyd_node *type_node = NULL;
ret = lyd_new_term(interface_node, NULL, "type", "iana-if-type:l2vlan", 0, &type_node);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"type\" node.\n");
ret = lyd_new_term(interface_node, NULL, "enabled", "true", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"enabled\" node.\n");
const struct lys_module *oran_int_mod = ly_ctx_get_module_implemented(*ctx, "o-ran-interfaces");
VERIFY_SUCCESS(oran_int_mod != NULL, "[MPLANE] Failed to get \"o-ran-interfaces\" module.\n");
struct lyd_node *base_int = NULL;
ret = lyd_new_term(interface_node, oran_int_mod, "base-interface", ru_session->ru_mplane_config.interface_name, 0, &base_int);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"base-interface\" node with name \"%s\".\n", ru_session->ru_mplane_config.interface_name);
struct lyd_node *vlan_id = NULL;
char vlan_tag_str[8];
snprintf(vlan_tag_str, sizeof(vlan_tag_str), "%d", ru_session->ru_mplane_config.vlan_tag[idx]);
ret = lyd_new_term(interface_node, oran_int_mod, "vlan-id", vlan_tag_str, 0, &vlan_id);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"vlan-id\" node with \"%d\".\n", ru_session->ru_mplane_config.vlan_tag[idx]);
struct lyd_node *ru_mac_addr = NULL;
ret = lyd_new_term(interface_node, oran_int_mod, "mac-address", ru_session->xran_mplane.ru_mac_addr, 0, &ru_mac_addr);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"mac-address\" node with \"%s\".\n", ru_session->xran_mplane.ru_mac_addr);
return true;
}
static bool create_proc_elem_v2(struct ly_ctx **ctx, const ru_session_t *ru_session, const size_t idx, const char *int_name, struct lyd_node **root)
{
LY_ERR ret = LY_SUCCESS;
ret = lyd_new_path(NULL, *ctx, "/o-ran-processing-element:processing-elements", NULL, 0, root);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create root \"processing-elements\" node.\n");
ret = lyd_new_term(*root, NULL, "transport-session-type", "ETH-INTERFACE", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"transport-session-type\" node.\n");
struct lyd_node *ru_elem_node = NULL;
char plane_name[8];
snprintf(plane_name, sizeof(plane_name), "PLANE_%ld", idx);
ret = lyd_new_list(*root, NULL, "ru-elements", 0, &ru_elem_node, plane_name);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"ru-elements\" node.\n");
struct lyd_node *transp_flow = NULL;
ret = lyd_new_inner(ru_elem_node, NULL, "transport-flow", 0, &transp_flow);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"transport-flow\" node.\n");
ret = lyd_new_term(transp_flow, NULL, "interface-name", int_name, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"interface-name\" node.\n");
struct lyd_node *eth_flow = NULL;
ret = lyd_new_inner(transp_flow, NULL, "eth-flow", 0, &eth_flow);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"eth-flow\" node.\n");
char vlan_tag_str[8];
snprintf(vlan_tag_str, sizeof(vlan_tag_str), "%d", ru_session->ru_mplane_config.vlan_tag[idx]);
ret = lyd_new_term(eth_flow, NULL, "vlan-id", vlan_tag_str, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"vlan-id\" node.\n");
ret = lyd_new_term(eth_flow, NULL, "ru-mac-address", ru_session->xran_mplane.ru_mac_addr, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"ru-mac-address\" node.\n");
ret = lyd_new_term(eth_flow, NULL, "o-du-mac-address", ru_session->ru_mplane_config.du_mac_addr[idx], 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"o-du-mac-address\" node.\n");
return true;
}
static bool fill_uplane_ch_v2(const xran_mplane_t *xran_mplane, const size_t idx, struct lyd_node **root)
{
LY_ERR ret = LY_SUCCESS;
ret = lyd_new_term(*root, NULL, "cp-length", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"cp-length\" node.\n");
ret = lyd_new_term(*root, NULL, "cp-length-other", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"cp-length-other\" node.\n");
// to we actually store this from DU config file?
ret = lyd_new_term(*root, NULL, "offset-to-absolute-frequency-center", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"offset-to-absolute-frequency-center\" node.\n");
struct lyd_node *compression_node = NULL;
ret = lyd_new_inner(*root, NULL, "compression", 0, &compression_node);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"compression\" node.\n");
char iq_width_str[8];
snprintf(iq_width_str, sizeof(iq_width_str), "%d", xran_mplane->iq_width);
ret = lyd_new_term(compression_node, NULL, "iq-bitwidth", iq_width_str, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"iq-bitwidth\" node.\n");
if (xran_mplane->iq_width < 16) {
ret = lyd_new_term(compression_node, NULL, "compression-type", "STATIC", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"compression-type\" node.\n");
// ret = lyd_new_term(compression_node, NULL, "compression-method", "BLOCK_FLOATING_POINT", 0, NULL);
// VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"compression-method\" node.\n");
}
struct lyd_node *eaxc_conf = NULL;
ret = lyd_new_inner(*root, NULL, "e-axcid", 0, &eaxc_conf);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"e-axcid\" node.\n");
char eaxc_id[8];
snprintf(eaxc_id, sizeof(eaxc_id), "%ld", idx);
ret = lyd_new_term(eaxc_conf, NULL, "eaxc-id", eaxc_id, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"eaxc-id\" node.\n");
char du_port_bitmask[16];
snprintf(du_port_bitmask, sizeof(du_port_bitmask), "%d", xran_mplane->du_port_bitmask);
ret = lyd_new_term(eaxc_conf, NULL, "o-du-port-bitmask", du_port_bitmask, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"o-du-port-bitmask\" node.\n");
char band_sector_bitmask[8];
snprintf(band_sector_bitmask, sizeof(band_sector_bitmask), "%d", xran_mplane->band_sector_bitmask);
ret = lyd_new_term(eaxc_conf, NULL, "band-sector-bitmask", band_sector_bitmask, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"band-sector-bitmask\" node.\n");
char ccid_bitmask[8];
snprintf(ccid_bitmask, sizeof(ccid_bitmask), "%d", xran_mplane->ccid_bitmask);
ret = lyd_new_term(eaxc_conf, NULL, "ccid-bitmask", ccid_bitmask, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"ccid-bitmask\" node.\n");
char ru_port_bitmask[8];
snprintf(ru_port_bitmask, sizeof(ru_port_bitmask), "%d", xran_mplane->ru_port_bitmask);
ret = lyd_new_term(eaxc_conf, NULL, "ru-port-bitmask", ru_port_bitmask, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"ru-port-bitmask\" node.\n");
return true;
}
static bool create_uplane_conf_v2(struct ly_ctx **ctx, const ru_session_t *ru_session, const openair0_config_t *oai, const size_t num_rus, const char *u_proc_elem, struct lyd_node **root)
{
LY_ERR ret = LY_SUCCESS;
bool success = true;
ret = lyd_new_path(NULL, *ctx, "/o-ran-uplane-conf:user-plane-configuration", NULL, 0, root);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create root \"user-plane-configuration\" node.\n");
if (ru_session->ru_mplane_config.rx_carriers.num != 1 || ru_session->ru_mplane_config.tx_carriers.num !=1)
MP_LOG_I("Only one carrier supported at the moment.\n");
// RX carriers
struct lyd_node *rx_carrier_node = NULL;
ret = lyd_new_list(*root, NULL, "rx-array-carriers", 0, &rx_carrier_node, ru_session->ru_mplane_config.rx_carriers.name[0]);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"rx-array-carriers\" node.\n");
char rx_freq[16];
snprintf(rx_freq, sizeof(rx_freq), "%.0f", oai->rx_freq[0]);
ret = lyd_new_term(rx_carrier_node, NULL, "center-of-channel-bandwidth", rx_freq, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"center-of-channel-bandwidth\" node.\n");
char rx_arfcn[16];
snprintf(rx_arfcn, sizeof(rx_arfcn), "%d", to_nrarfcn(oai->nr_band, oai->rx_freq[0], oai->nr_scs_for_raster, oai->rx_bw));
ret = lyd_new_term(rx_carrier_node, NULL, "absolute-frequency-center", rx_arfcn, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"absolute-frequency-center\" node.\n");
char rx_bw[16];
snprintf(rx_bw, sizeof(rx_bw), "%.0f", oai->rx_bw);
ret = lyd_new_term(rx_carrier_node, NULL, "channel-bandwidth", rx_bw, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"channel-bandwidth\" node.\n");
ret = lyd_new_term(rx_carrier_node, NULL, "downlink-radio-frame-offset", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-radio-frame-offset\" node.\n");
ret = lyd_new_term(rx_carrier_node, NULL, "downlink-sfn-offset", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-sfn-offset\" node.\n");
// oai->rx_gain_offset is this the correction? if yes, test it later
ret = lyd_new_term(rx_carrier_node, NULL, "gain-correction", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"gain-correction\" node.\n");
ret = lyd_new_term(rx_carrier_node, NULL, "n-ta-offset", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"n-ta-offset\" node.\n");
ret = lyd_new_term(rx_carrier_node, NULL, "active", "ACTIVE", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"active\" node.\n");
// TX carriers
struct lyd_node *tx_carrier_node = NULL;
ret = lyd_new_list(*root, NULL, "tx-array-carriers", 0, &tx_carrier_node, ru_session->ru_mplane_config.tx_carriers.name[0]);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"tx-array-carriers\" node.\n");
char tx_freq[16];
snprintf(tx_freq, sizeof(tx_freq), "%.0f", oai->tx_freq[0]);
ret = lyd_new_term(tx_carrier_node, NULL, "center-of-channel-bandwidth", tx_freq, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"center-of-channel-bandwidth\" node.\n");
char tx_arfcn[16];
snprintf(tx_arfcn, sizeof(tx_arfcn), "%d", to_nrarfcn(oai->nr_band, oai->tx_freq[0], oai->nr_scs_for_raster, oai->tx_bw));
ret = lyd_new_term(tx_carrier_node, NULL, "absolute-frequency-center", tx_arfcn, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"absolute-frequency-center\" node.\n");
char tx_bw[16];
snprintf(tx_bw, sizeof(tx_bw), "%.0f", oai->tx_bw);
ret = lyd_new_term(tx_carrier_node, NULL, "channel-bandwidth", tx_bw, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"channel-bandwidth\" node.\n");
// oai->tx_gain to be tested
ret = lyd_new_term(tx_carrier_node, NULL, "gain", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"gain\" node.\n");
ret = lyd_new_term(tx_carrier_node, NULL, "downlink-radio-frame-offset", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-radio-frame-offset\" node.\n");
ret = lyd_new_term(tx_carrier_node, NULL, "downlink-sfn-offset", "0", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"downlink-sfn-offset\" node.\n");
ret = lyd_new_term(tx_carrier_node, NULL, "active", "ACTIVE", 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"active\" node.\n");
// PUSCH & PRACH endpoints
const size_t num_rx_ch = oai->rx_num_channels / num_rus;
for (size_t i = 0; i < num_rx_ch; i++) {
struct lyd_node *pusch_node = NULL;
ret = lyd_new_list(*root, NULL, "low-level-rx-endpoints", 0, &pusch_node, ru_session->ru_mplane_config.rx_endpoints.name[i]);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-rx-endpoints\" node.\n");
success = fill_uplane_ch_v2(&ru_session->xran_mplane, i, &pusch_node);
VERIFY_SUCCESS(success, "[MPLANE] Failed to fill \"low-level-rx-endpoints\" node for %s.\n", ru_session->ru_mplane_config.rx_endpoints.name[i]);
const size_t prach_endpoint_name_offset = i + (ru_session->ru_mplane_config.rx_endpoints.num / 2);
struct lyd_node *prach_node = NULL;
ret = lyd_new_list(*root, NULL, "low-level-rx-endpoints", 0, &prach_node, ru_session->ru_mplane_config.rx_endpoints.name[prach_endpoint_name_offset]);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-rx-endpoints\" node.\n");
success = fill_uplane_ch_v2(&ru_session->xran_mplane, i + ru_session->xran_mplane.prach_offset, &prach_node);
VERIFY_SUCCESS(success, "[MPLANE] Failed to fill \"low-level-rx-endpoints\" node for %s.\n", ru_session->ru_mplane_config.rx_endpoints.name[prach_endpoint_name_offset]);
}
// PDSCH endpoints
const size_t num_tx_ch = oai->tx_num_channels / num_rus;
for (size_t i = 0; i < num_tx_ch; i++) {
struct lyd_node *pdsch_node = NULL;
ret = lyd_new_list(*root, NULL, "low-level-tx-endpoints", 0, &pdsch_node, ru_session->ru_mplane_config.tx_endpoints.name[i]);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-tx-endpoints\" node.\n");
success = fill_uplane_ch_v2(&ru_session->xran_mplane, i, &pdsch_node);
VERIFY_SUCCESS(success, "[MPLANE] Failed to fill \"low-level-tx-endpoints\" node for %s.\n", ru_session->ru_mplane_config.tx_endpoints.name[i]);
}
// PUSCH and PRACH links
for (size_t i = 0; i < num_rx_ch; i++) {
// PUSCH
struct lyd_node *pusch_link_node = NULL;
char pusch_link[16];
snprintf(pusch_link, sizeof(pusch_link), "%s%ld", "PuschLink", i);
ret = lyd_new_list(*root, NULL, "low-level-rx-links", 0, &pusch_link_node, pusch_link);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-rx-links\" node.\n");
ret = lyd_new_term(pusch_link_node, NULL, "processing-element", u_proc_elem, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"processing-element\" node.\n");
ret = lyd_new_term(pusch_link_node, NULL, "rx-array-carrier", ru_session->ru_mplane_config.rx_carriers.name[0], 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"rx-array-carrier\" node.\n");
ret = lyd_new_term(pusch_link_node, NULL, "low-level-rx-endpoint", ru_session->ru_mplane_config.rx_endpoints.name[i], 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-rx-endpoint\" node.\n");
// PRACH
struct lyd_node *prach_link_node = NULL;
char prach_link[16];
snprintf(prach_link, sizeof(prach_link), "%s%ld", "PrachLink", i);
ret = lyd_new_list(*root, NULL, "low-level-rx-links", 0, &prach_link_node, prach_link);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-rx-links\" node.\n");
ret = lyd_new_term(prach_link_node, NULL, "processing-element", u_proc_elem, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"processing-element\" node.\n");
ret = lyd_new_term(prach_link_node, NULL, "rx-array-carrier", ru_session->ru_mplane_config.rx_carriers.name[0], 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"rx-array-carrier\" node.\n");
const size_t prach_endpoint_name_offset = i + (ru_session->ru_mplane_config.rx_endpoints.num / 2);
ret = lyd_new_term(prach_link_node, NULL, "low-level-rx-endpoint", ru_session->ru_mplane_config.rx_endpoints.name[prach_endpoint_name_offset], 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-rx-endpoint\" node.\n");
}
// PDSCH links
for (size_t i = 0; i < num_tx_ch; i++) {
struct lyd_node *pdsch_link_node = NULL;
char pdsch_link[16];
snprintf(pdsch_link, sizeof(pdsch_link), "%s%ld", "PdschLink", i);
ret = lyd_new_list(*root, NULL, "low-level-tx-links", 0, &pdsch_link_node, pdsch_link);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-tx-links\" node.\n");
ret = lyd_new_term(pdsch_link_node, NULL, "processing-element", u_proc_elem, 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"processing-element\" node.\n");
ret = lyd_new_term(pdsch_link_node, NULL, "tx-array-carrier", ru_session->ru_mplane_config.tx_carriers.name[0], 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"tx-array-carrier\" node.\n");
ret = lyd_new_term(pdsch_link_node, NULL, "low-level-tx-endpoint", ru_session->ru_mplane_config.tx_endpoints.name[i], 0, NULL);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Failed to create \"low-level-tx-endpoint\" node.\n");
}
return true;
}
#endif
bool configure_ru_from_yang(struct ly_ctx **ctx, const ru_session_t *ru_session, const openair0_config_t *oai, const size_t num_rus, char **result)
{
bool success = false;
#ifdef MPLANE_V1
VERIFY_SUCCESS(success, "[MPLANE] To be implemented and tested.\n");
#elif defined MPLANE_V2
const ru_mplane_config_t *ru_config = &ru_session->ru_mplane_config;
size_t num_cu_planes = ru_config->num_cu_planes;
if (num_cu_planes == 2 && ru_config->vlan_tag[0] == ru_config->vlan_tag[1]) {
MP_LOG_I("The VLAN tags for C and U plane for the RU \"%s\" are the same. Therefore, configuring one common interface and one processing element.\n", ru_session->ru_ip_add);
num_cu_planes = 1;
}
struct lyd_node *all_merge = NULL;
LY_ERR ret = LY_SUCCESS;
for (size_t i = 0; i < num_cu_planes; i++) {
// <ietf-interfaces>
struct lyd_node *cu_interface = NULL;
char int_name[12];
snprintf(int_name, sizeof(int_name), "INTERFACE_%ld", i);
success = create_cu_interface_v2(ctx, ru_session, i, int_name, &cu_interface);
VERIFY_SUCCESS(success, "[MPLANE] Cannot create CU-plane interface.\n");
// <o-ran-processing-element>
struct lyd_node *proc_elem = NULL;
success = create_proc_elem_v2(ctx, ru_session, i, int_name, &proc_elem);
VERIFY_SUCCESS(success, "[MPLANE] Cannot create CU-plane processing element.\n");
ret = lyd_merge_siblings(&all_merge, cu_interface, 0);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Cannot merge CU-plane interface.\n");
ret = lyd_merge_siblings(&all_merge, proc_elem, 0);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Cannot merge processing element.\n");
lyd_free_tree(cu_interface);
lyd_free_tree(proc_elem);
}
// <o-ran-uplane-conf>
struct lyd_node *uplane_conf = NULL;
success = create_uplane_conf_v2(ctx, ru_session, oai, num_rus, "PLANE_0", &uplane_conf);
VERIFY_SUCCESS(success, "[MPLANE] Cannot create U-plane configuration.\n");
ret = lyd_merge_siblings(&uplane_conf, all_merge, 0);
VERIFY_SUCCESS(ret == LY_SUCCESS, "[MPLANE] Cannot merge CU-plane interface, processing element and U-plane configuration.\n");
lyd_print_mem(result, uplane_conf, LYD_XML, LYD_PRINT_WITHSIBLINGS);
lyd_free_siblings(all_merge);
lyd_free_siblings(uplane_conf);
#endif
return true;
}
@@ -0,0 +1,32 @@
/*
* 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 CREATE_MPLANE_YANG_CONFIG_H
#define CREATE_MPLANE_YANG_CONFIG_H
#include "../ru-mplane-api.h"
#include "radio/COMMON/common_lib.h"
#include <libyang/libyang.h>
bool configure_ru_from_yang(struct ly_ctx **ctx, const ru_session_t *ru_session, const openair0_config_t *oai, const size_t num_rus, char **result);
#endif /* CREATE_MPLANE_YANG_CONFIG_H */
+141
View File
@@ -0,0 +1,141 @@
/*
* 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 "get-yang.h"
#include "../rpc-send-recv.h"
#include "common/utils/assertions.h"
#include <libxml/parser.h>
static bool store_schemas(xmlNode *node, ru_session_t *ru_session, struct ly_ctx **ctx)
{
int timeout = CLI_RPC_REPLY_TIMEOUT;
NC_WD_MODE wd = NC_WD_ALL;
NC_PARAMTYPE param = NC_PARAMTYPE_CONST;
LYS_INFORMAT ly_format = LYS_IN_YANG;
#ifdef MPLANE_V1
*ctx = ly_ctx_new(NULL, 0);
#elif defined MPLANE_V2
ly_ctx_new(NULL, 0, ctx);
#endif
for (xmlNode *cur_node = node; cur_node; cur_node = cur_node->next) {
if (strcmp((const char *)cur_node->name, "schema") == 0) {
xmlNode *name_node = xmlFirstElementChild(cur_node);
char *module_name = (char *)xmlNodeGetContent(name_node);
xmlNode *revision_node = xmlNextElementSibling(name_node);
char *module_revision = (char *)xmlNodeGetContent(revision_node);
xmlNode *format_node = xmlNextElementSibling(revision_node);
char *module_format = (char *)xmlNodeGetContent(format_node);
if (strcmp(module_format, "yang") != 0)
continue;
MP_LOG_I("RPC request to RU \"%s\" = <get-schema> for module \"%s\".\n", ru_session->ru_ip_add, module_name);
struct nc_rpc *get_schema_rpc = nc_rpc_getschema(module_name, module_revision, "yang", param);
char *schema_data = NULL;
bool success = rpc_send_recv((struct nc_session *)ru_session->session, get_schema_rpc, wd, timeout, &schema_data);
AssertError(success, return false, "[MPLANE] Unable to get schema for module \"%s\" from RU \"%s\".\n", module_name, ru_session->ru_ip_add);
if (schema_data) {
#ifdef MPLANE_V1
const struct lys_module *mod = lys_parse_mem(*ctx, schema_data, ly_format);
#elif defined MPLANE_V2
struct lys_module *mod = NULL;
lys_parse_mem(*ctx, schema_data, ly_format, &mod);
#endif
free(schema_data);
if (!mod) {
MP_LOG_W("Unable to load module \"%s\" from RU \"%s\".\n", module_name, ru_session->ru_ip_add);
nc_rpc_free(get_schema_rpc);
ly_ctx_destroy(*ctx);
return false;
}
}
nc_rpc_free(get_schema_rpc);
}
}
return true;
}
static bool load_from_operational_ds(xmlNode *node, ru_session_t *ru_session, struct ly_ctx **ctx)
{
for (xmlNode *schemas_node = node; schemas_node; schemas_node = schemas_node->next) {
if(schemas_node->type != XML_ELEMENT_NODE)
continue;
if (strcmp((const char *)schemas_node->name, "schemas") == 0) {
return store_schemas(schemas_node->children, ru_session, ctx);
} else {
bool success = load_from_operational_ds(schemas_node->children, ru_session, ctx);
if (success)
return true;
}
}
return false;
}
bool load_yang_models(ru_session_t *ru_session, const char *buffer, struct ly_ctx **ctx)
{
// Initialize the xml file
size_t len = strlen(buffer) + 1;
xmlDoc *doc = xmlReadMemory(buffer, len, NULL, NULL, 0);
xmlNode *root_element = xmlDocGetRootElement(doc);
bool success = load_from_operational_ds(root_element->children, ru_session, ctx);
if (success) {
MP_LOG_I("Successfully loaded all yang modules from operational datastore for RU \"%s\".\n", ru_session->ru_ip_add);
return true;
} else {
MP_LOG_W("Unable to load all yang modules from operational datastore for RU \"%s\". Using yang models present in \"models\" subfolder.\n", ru_session->ru_ip_add);
}
/* ideally, we should load the yang models from the operational datastore, but the issues are following:
1) the yang models order is not good - the dependancy models have to be loaded first
2) earlier O-RAN yang versions (e.g. v4) is not properly defined (i.e. optional parameters should not be included by default) */
const char *yang_dir = YANG_MODELS;
const char *yang_models[] = {"ietf-interfaces", "iana-if-type", "ietf-ip", "iana-hardware", "ietf-hardware", "o-ran-interfaces", "o-ran-module-cap", "o-ran-compression-factors", "o-ran-processing-element", "o-ran-uplane-conf"};
#ifdef MPLANE_V1
*ctx = ly_ctx_new(yang_dir, 0);
AssertError(*ctx != NULL, return false, "[MPLANE] Unable to create libyang context: %s.\n", ly_errmsg(*ctx));
#elif defined MPLANE_V2
LY_ERR ret = ly_ctx_new(yang_dir, 0, ctx);
AssertError(ret == LY_SUCCESS, return false, "[MPLANE] Unable to create libyang context: %s.\n", ly_errmsg(*ctx));
#endif
const size_t num_models = sizeof(yang_models) / sizeof(yang_models[0]);
for (size_t i = 0; i < num_models; ++i) {
#ifdef MPLANE_V1
const struct lys_module *mod = ly_ctx_load_module(*ctx, yang_models[i], NULL);
AssertError(mod != NULL, return false, "[MPLANE] Failed to load yang model %s.\n", yang_models[i]);
#elif defined MPLANE_V2
const struct lys_module *mod = ly_ctx_load_module(*ctx, yang_models[i], NULL, NULL);
AssertError(mod != NULL, return false, "[MPLANE] Failed to load yang model %s.\n", yang_models[i]);
#endif
}
MP_LOG_I("Successfully loaded all yang modules for RU \"%s\".\n", ru_session->ru_ip_add);
return true;
}
+31
View File
@@ -0,0 +1,31 @@
/*
* 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 GET_MPLANE_YANG_MODELS_H
#define GET_MPLANE_YANG_MODELS_H
#include "../ru-mplane-api.h"
#include <libyang/libyang.h>
bool load_yang_models(ru_session_t *ru_session, const char *buffer, struct ly_ctx **ctx);
#endif /* GET_MPLANE_YANG_MODELS_H */
+180
View File
@@ -0,0 +1,180 @@
module iana-hardware {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:iana-hardware";
prefix ianahw;
organization "IANA";
contact
" Internet Assigned Numbers Authority
Postal: ICANN
12025 Waterfront Drive, Suite 300
Los Angeles, CA 90094-2536
United States of America
Tel: +1 310 301 5800
E-Mail: iana@iana.org>";
description
"IANA-defined identities for hardware class.
The latest revision of this YANG module can be obtained from
the IANA website.
Requests for new values should be made to IANA via
email (iana@iana.org).
Copyright (c) 2018 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(https://trustee.ietf.org/license-info).
The initial version of this YANG module is part of RFC 8348;
see the RFC itself for full legal notices.";
reference
"https://www.iana.org/assignments/yang-parameters";
revision 2018-03-13 {
description
"Initial revision.";
reference
"RFC 8348: A YANG Data Model for Hardware Management";
}
/*
* Identities
*/
identity hardware-class {
description
"This identity is the base for all hardware class
identifiers.";
}
identity unknown {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is unknown
to the server.";
}
identity chassis {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is an
overall container for networking equipment. Any class of
physical component, except a stack, may be contained within a
chassis; a chassis may only be contained within a stack.";
}
identity backplane {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of device for aggregating and forwarding networking traffic,
such as a shared backplane in a modular ethernet switch. Note
that an implementation may model a backplane as a single
physical component, which is actually implemented as multiple
discrete physical components (within a chassis or stack).";
}
identity container {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is capable
of containing one or more removable physical entities,
possibly of different types. For example, each (empty or
full) slot in a chassis will be modeled as a container. Note
that all removable physical components should be modeled
within a container component, such as field-replaceable
modules, fans, or power supplies. Note that all known
containers should be modeled by the agent, including empty
containers.";
}
identity power-supply {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is a
power-supplying component.";
}
identity fan {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is a fan or
other heat-reduction component.";
}
identity sensor {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of sensor, such as a temperature sensor within a router
chassis.";
}
identity module {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of self-contained sub-system. If a module component is
removable, then it should be modeled within a container
component; otherwise, it should be modeled directly within
another physical component (e.g., a chassis or another
module).";
}
identity port {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of networking port capable of receiving and/or transmitting
networking traffic.";
}
identity stack {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of super-container (possibly virtual) intended to group
together multiple chassis entities. A stack may be realized
by a virtual cable, a real interconnect cable attached to
multiple chassis, or multiple interconnect cables. A stack
should not be modeled within any other physical components,
but a stack may be contained within another stack. Only
chassis components should be contained within a stack.";
}
identity cpu {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of central processing unit.";
}
identity energy-object {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of energy object, i.e., it is a piece of equipment that is
part of or attached to a communications network that is
monitored, it is controlled, or it aids in the management of
another device for Energy Management.";
}
identity battery {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of battery.";
}
identity storage-drive {
base ianahw:hardware-class;
description
"This identity is applicable if the hardware class is some sort
of component with data storage capability as its main
functionality, e.g., hard disk drive (HDD), solid-state device
(SSD), solid-state hybrid drive (SSHD), object storage device
(OSD), or other.";
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,860 @@
module ietf-ip {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
prefix ip;
import ietf-interfaces {
prefix if;
}
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
organization
"IETF NETMOD (Network Modeling) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"This module contains a collection of YANG definitions for
managing IP implementations.
Copyright (c) 2018 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, is permitted pursuant to, and subject
to the license terms contained in, the Simplified BSD License
set forth in Section 4.c of the IETF Trust's Legal Provisions
Relating to IETF Documents
(https://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 8344; see
the RFC itself for full legal notices.";
revision 2018-02-22 {
description
"Updated to support NMDA.";
reference
"RFC 8344: A YANG Data Model for IP Management";
}
revision 2014-06-16 {
description
"Initial revision.";
reference
"RFC 7277: A YANG Data Model for IP Management";
}
/*
* Features
*/
feature ipv4-non-contiguous-netmasks {
description
"Indicates support for configuring non-contiguous
subnet masks.";
}
feature ipv6-privacy-autoconf {
description
"Indicates support for privacy extensions for stateless address
autoconfiguration in IPv6.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6";
}
/*
* Typedefs
*/
typedef ip-address-origin {
type enumeration {
enum other {
description
"None of the following.";
}
enum static {
description
"Indicates that the address has been statically
configured -- for example, using the Network Configuration
Protocol (NETCONF) or a command line interface.";
}
enum dhcp {
description
"Indicates an address that has been assigned to this
system by a DHCP server.";
}
enum link-layer {
description
"Indicates an address created by IPv6 stateless
autoconfiguration that embeds a link-layer address in its
interface identifier.";
}
enum random {
description
"Indicates an address chosen by the system at
random, e.g., an IPv4 address within 169.254/16, a
temporary address as described in RFC 4941, or a
semantically opaque address as described in RFC 7217.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
RFC 7217: A Method for Generating Semantically Opaque
Interface Identifiers with IPv6 Stateless
Address Autoconfiguration (SLAAC)";
}
}
description
"The origin of an address.";
}
typedef neighbor-origin {
type enumeration {
enum other {
description
"None of the following.";
}
enum static {
description
"Indicates that the mapping has been statically
configured -- for example, using NETCONF or a command line
interface.";
}
enum dynamic {
description
"Indicates that the mapping has been dynamically resolved
using, for example, IPv4 ARP or the IPv6 Neighbor
Discovery protocol.";
}
}
description
"The origin of a neighbor entry.";
}
/*
* Data nodes
*/
augment "/if:interfaces/if:interface" {
description
"IP parameters on interfaces.
If an interface is not capable of running IP, the server
must not allow the client to configure these parameters.";
container ipv4 {
presence
"Enables IPv4 unless the 'enabled' leaf
(which defaults to 'true') is set to 'false'";
description
"Parameters for the IPv4 address family.";
leaf enabled {
type boolean;
default true;
description
"Controls whether IPv4 is enabled or disabled on this
interface. When IPv4 is enabled, this interface is
connected to an IPv4 stack, and the interface can send
and receive IPv4 packets.";
}
leaf forwarding {
type boolean;
default false;
description
"Controls IPv4 packet forwarding of datagrams received by,
but not addressed to, this interface. IPv4 routers
forward datagrams. IPv4 hosts do not (except those
source-routed via the host).";
}
leaf mtu {
type uint16 {
range "68..max";
}
units "octets";
description
"The size, in octets, of the largest IPv4 packet that the
interface will send and receive.
The server may restrict the allowed values for this leaf,
depending on the interface's type.
If this leaf is not configured, the operationally used MTU
depends on the interface's type.";
reference
"RFC 791: Internet Protocol";
}
list address {
key "ip";
description
"The list of IPv4 addresses on the interface.";
leaf ip {
type inet:ipv4-address-no-zone;
description
"The IPv4 address on the interface.";
}
choice subnet {
mandatory true;
description
"The subnet can be specified as a prefix length or,
if the server supports non-contiguous netmasks, as
a netmask.";
leaf prefix-length {
type uint8 {
range "0..32";
}
description
"The length of the subnet prefix.";
}
leaf netmask {
if-feature ipv4-non-contiguous-netmasks;
type yang:dotted-quad;
description
"The subnet specified as a netmask.";
}
}
leaf origin {
type ip-address-origin;
config false;
description
"The origin of this address.";
}
}
list neighbor {
key "ip";
description
"A list of mappings from IPv4 addresses to
link-layer addresses.
Entries in this list in the intended configuration are
used as static entries in the ARP Cache.
In the operational state, this list represents the ARP
Cache.";
reference
"RFC 826: An Ethernet Address Resolution Protocol";
leaf ip {
type inet:ipv4-address-no-zone;
description
"The IPv4 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
mandatory true;
description
"The link-layer address of the neighbor node.";
}
leaf origin {
type neighbor-origin;
config false;
description
"The origin of this neighbor entry.";
}
}
}
container ipv6 {
presence
"Enables IPv6 unless the 'enabled' leaf
(which defaults to 'true') is set to 'false'";
description
"Parameters for the IPv6 address family.";
leaf enabled {
type boolean;
default true;
description
"Controls whether IPv6 is enabled or disabled on this
interface. When IPv6 is enabled, this interface is
connected to an IPv6 stack, and the interface can send
and receive IPv6 packets.";
}
leaf forwarding {
type boolean;
default false;
description
"Controls IPv6 packet forwarding of datagrams received by,
but not addressed to, this interface. IPv6 routers
forward datagrams. IPv6 hosts do not (except those
source-routed via the host).";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 6.2.1, IsRouter";
}
leaf mtu {
type uint32 {
range "1280..max";
}
units "octets";
description
"The size, in octets, of the largest IPv6 packet that the
interface will send and receive.
The server may restrict the allowed values for this leaf,
depending on the interface's type.
If this leaf is not configured, the operationally used MTU
depends on the interface's type.";
reference
"RFC 8200: Internet Protocol, Version 6 (IPv6)
Specification
Section 5";
}
list address {
key "ip";
description
"The list of IPv6 addresses on the interface.";
leaf ip {
type inet:ipv6-address-no-zone;
description
"The IPv6 address on the interface.";
}
leaf prefix-length {
type uint8 {
range "0..128";
}
mandatory true;
description
"The length of the subnet prefix.";
}
leaf origin {
type ip-address-origin;
config false;
description
"The origin of this address.";
}
leaf status {
type enumeration {
enum preferred {
description
"This is a valid address that can appear as the
destination or source address of a packet.";
}
enum deprecated {
description
"This is a valid but deprecated address that should
no longer be used as a source address in new
communications, but packets addressed to such an
address are processed as expected.";
}
enum invalid {
description
"This isn't a valid address, and it shouldn't appear
as the destination or source address of a packet.";
}
enum inaccessible {
description
"The address is not accessible because the interface
to which this address is assigned is not
operational.";
}
enum unknown {
description
"The status cannot be determined for some reason.";
}
enum tentative {
description
"The uniqueness of the address on the link is being
verified. Addresses in this state should not be
used for general communication and should only be
used to determine the uniqueness of the address.";
}
enum duplicate {
description
"The address has been determined to be non-unique on
the link and so must not be used.";
}
enum optimistic {
description
"The address is available for use, subject to
restrictions, while its uniqueness on a link is
being verified.";
}
}
config false;
description
"The status of an address. Most of the states correspond
to states from the IPv6 Stateless Address
Autoconfiguration protocol.";
reference
"RFC 4293: Management Information Base for the
Internet Protocol (IP)
- IpAddressStatusTC
RFC 4862: IPv6 Stateless Address Autoconfiguration";
}
}
list neighbor {
key "ip";
description
"A list of mappings from IPv6 addresses to
link-layer addresses.
Entries in this list in the intended configuration are
used as static entries in the Neighbor Cache.
In the operational state, this list represents the
Neighbor Cache.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
leaf ip {
type inet:ipv6-address-no-zone;
description
"The IPv6 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
mandatory true;
description
"The link-layer address of the neighbor node.
In the operational state, if the neighbor's 'state' leaf
is 'incomplete', this leaf is not instantiated.";
}
leaf origin {
type neighbor-origin;
config false;
description
"The origin of this neighbor entry.";
}
leaf is-router {
type empty;
config false;
description
"Indicates that the neighbor node acts as a router.";
}
leaf state {
type enumeration {
enum incomplete {
description
"Address resolution is in progress, and the
link-layer address of the neighbor has not yet been
determined.";
}
enum reachable {
description
"Roughly speaking, the neighbor is known to have been
reachable recently (within tens of seconds ago).";
}
enum stale {
description
"The neighbor is no longer known to be reachable, but
until traffic is sent to the neighbor no attempt
should be made to verify its reachability.";
}
enum delay {
description
"The neighbor is no longer known to be reachable, and
traffic has recently been sent to the neighbor.
Rather than probe the neighbor immediately, however,
delay sending probes for a short while in order to
give upper-layer protocols a chance to provide
reachability confirmation.";
}
enum probe {
description
"The neighbor is no longer known to be reachable, and
unicast Neighbor Solicitation probes are being sent
to verify reachability.";
}
}
config false;
description
"The Neighbor Unreachability Detection state of this
entry.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 7.3.2";
}
}
leaf dup-addr-detect-transmits {
type uint32;
default 1;
description
"The number of consecutive Neighbor Solicitation messages
sent while performing Duplicate Address Detection on a
tentative address. A value of zero indicates that
Duplicate Address Detection is not performed on
tentative addresses. A value of one indicates a single
transmission with no follow-up retransmissions.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration";
}
container autoconf {
description
"Parameters to control the autoconfiguration of IPv6
addresses, as described in RFC 4862.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration";
leaf create-global-addresses {
type boolean;
default true;
description
"If enabled, the host creates global addresses as
described in RFC 4862.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration
Section 5.5";
}
leaf create-temporary-addresses {
if-feature ipv6-privacy-autoconf;
type boolean;
default false;
description
"If enabled, the host creates temporary addresses as
described in RFC 4941.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6";
}
leaf temporary-valid-lifetime {
if-feature ipv6-privacy-autoconf;
type uint32;
units "seconds";
default 604800;
description
"The time period during which the temporary address
is valid.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
- TEMP_VALID_LIFETIME";
}
leaf temporary-preferred-lifetime {
if-feature ipv6-privacy-autoconf;
type uint32;
units "seconds";
default 86400;
description
"The time period during which the temporary address is
preferred.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
- TEMP_PREFERRED_LIFETIME";
}
}
}
}
/*
* Legacy operational state data nodes
*/
augment "/if:interfaces-state/if:interface" {
status deprecated;
description
"Data nodes for the operational state of IP on interfaces.";
container ipv4 {
presence
"Present if IPv4 is enabled on this interface";
config false;
status deprecated;
description
"Interface-specific parameters for the IPv4 address family.";
leaf forwarding {
type boolean;
status deprecated;
description
"Indicates whether IPv4 packet forwarding is enabled or
disabled on this interface.";
}
leaf mtu {
type uint16 {
range "68..max";
}
units "octets";
status deprecated;
description
"The size, in octets, of the largest IPv4 packet that the
interface will send and receive.";
reference
"RFC 791: Internet Protocol";
}
list address {
key "ip";
status deprecated;
description
"The list of IPv4 addresses on the interface.";
leaf ip {
type inet:ipv4-address-no-zone;
status deprecated;
description
"The IPv4 address on the interface.";
}
choice subnet {
status deprecated;
description
"The subnet can be specified as a prefix length or,
if the server supports non-contiguous netmasks, as
a netmask.";
leaf prefix-length {
type uint8 {
range "0..32";
}
status deprecated;
description
"The length of the subnet prefix.";
}
leaf netmask {
if-feature ipv4-non-contiguous-netmasks;
type yang:dotted-quad;
status deprecated;
description
"The subnet specified as a netmask.";
}
}
leaf origin {
type ip-address-origin;
status deprecated;
description
"The origin of this address.";
}
}
list neighbor {
key "ip";
status deprecated;
description
"A list of mappings from IPv4 addresses to
link-layer addresses.
This list represents the ARP Cache.";
reference
"RFC 826: An Ethernet Address Resolution Protocol";
leaf ip {
type inet:ipv4-address-no-zone;
status deprecated;
description
"The IPv4 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
status deprecated;
description
"The link-layer address of the neighbor node.";
}
leaf origin {
type neighbor-origin;
status deprecated;
description
"The origin of this neighbor entry.";
}
}
}
container ipv6 {
presence
"Present if IPv6 is enabled on this interface";
config false;
status deprecated;
description
"Parameters for the IPv6 address family.";
leaf forwarding {
type boolean;
default false;
status deprecated;
description
"Indicates whether IPv6 packet forwarding is enabled or
disabled on this interface.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 6.2.1, IsRouter";
}
leaf mtu {
type uint32 {
range "1280..max";
}
units "octets";
status deprecated;
description
"The size, in octets, of the largest IPv6 packet that the
interface will send and receive.";
reference
"RFC 8200: Internet Protocol, Version 6 (IPv6)
Specification
Section 5";
}
list address {
key "ip";
status deprecated;
description
"The list of IPv6 addresses on the interface.";
leaf ip {
type inet:ipv6-address-no-zone;
status deprecated;
description
"The IPv6 address on the interface.";
}
leaf prefix-length {
type uint8 {
range "0..128";
}
mandatory true;
status deprecated;
description
"The length of the subnet prefix.";
}
leaf origin {
type ip-address-origin;
status deprecated;
description
"The origin of this address.";
}
leaf status {
type enumeration {
enum preferred {
description
"This is a valid address that can appear as the
destination or source address of a packet.";
}
enum deprecated {
description
"This is a valid but deprecated address that should
no longer be used as a source address in new
communications, but packets addressed to such an
address are processed as expected.";
}
enum invalid {
description
"This isn't a valid address, and it shouldn't appear
as the destination or source address of a packet.";
}
enum inaccessible {
description
"The address is not accessible because the interface
to which this address is assigned is not
operational.";
}
enum unknown {
description
"The status cannot be determined for some reason.";
}
enum tentative {
description
"The uniqueness of the address on the link is being
verified. Addresses in this state should not be
used for general communication and should only be
used to determine the uniqueness of the address.";
}
enum duplicate {
description
"The address has been determined to be non-unique on
the link and so must not be used.";
}
enum optimistic {
description
"The address is available for use, subject to
restrictions, while its uniqueness on a link is
being verified.";
}
}
status deprecated;
description
"The status of an address. Most of the states correspond
to states from the IPv6 Stateless Address
Autoconfiguration protocol.";
reference
"RFC 4293: Management Information Base for the
Internet Protocol (IP)
- IpAddressStatusTC
RFC 4862: IPv6 Stateless Address Autoconfiguration";
}
}
list neighbor {
key "ip";
status deprecated;
description
"A list of mappings from IPv6 addresses to
link-layer addresses.
This list represents the Neighbor Cache.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
leaf ip {
type inet:ipv6-address-no-zone;
status deprecated;
description
"The IPv6 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
status deprecated;
description
"The link-layer address of the neighbor node.";
}
leaf origin {
type neighbor-origin;
status deprecated;
description
"The origin of this neighbor entry.";
}
leaf is-router {
type empty;
status deprecated;
description
"Indicates that the neighbor node acts as a router.";
}
leaf state {
type enumeration {
enum incomplete {
description
"Address resolution is in progress, and the
link-layer address of the neighbor has not yet been
determined.";
}
enum reachable {
description
"Roughly speaking, the neighbor is known to have been
reachable recently (within tens of seconds ago).";
}
enum stale {
description
"The neighbor is no longer known to be reachable, but
until traffic is sent to the neighbor no attempt
should be made to verify its reachability.";
}
enum delay {
description
"The neighbor is no longer known to be reachable, and
traffic has recently been sent to the neighbor.
Rather than probe the neighbor immediately, however,
delay sending probes for a short while in order to
give upper-layer protocols a chance to provide
reachability confirmation.";
}
enum probe {
description
"The neighbor is no longer known to be reachable, and
unicast Neighbor Solicitation probes are being sent
to verify reachability.";
}
}
status deprecated;
description
"The Neighbor Unreachability Detection state of this
entry.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 7.3.2";
}
}
}
}
}
@@ -0,0 +1,368 @@
module o-ran-compression-factors {
yang-version 1.1;
namespace "urn:o-ran:compression-factors:1.0";
prefix "o-ran-compression-factors";
organization "O-RAN Alliance";
contact
"www.o-ran.org";
description
"This module defines the module capabilities for
the O-RAN Radio Unit U-Plane configuration.
Copyright 2020 the O-RAN Alliance.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the above disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the above disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the Members of the O-RAN Alliance nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.";
revision "2020-08-10" {
description
"version 4.0.0
1) supporting compression types per endpoint
2) adding feature for configurable fs-offset for compression";
reference "ORAN-WG4.M.0-v04.00";
}
revision "2020-04-17" {
description
"version 3.0.0
1) adding selective RE sending compression types";
reference "ORAN-WG4.M.0-v03.00";
}
revision "2019-07-03" {
description
"version 1.1.0
1) changes related to compression bitwidth presentation";
reference "ORAN-WG4.M.0-v01.00";
}
revision "2019-02-04" {
description
"version 1.0.0
1) imported model from xRAN
2) changed namespace and reference from xran to o-ran";
reference "ORAN-WG4.M.0-v01.00";
}
feature CONFIGURABLE-FS-OFFSET {
description
"Presence of this feature means that O-RU supports configurable fs-offset for compression.";
}
grouping compression-method-grouping {
description
"Grouping for compression method.";
leaf iq-bitwidth {
type uint8;
description
"Bitwidth to be used in compression";
}
leaf compression-method {
type enumeration {
enum NO_COMPRESSION {
description
"No compression will be used";
}
enum BLOCK_FLOATING_POINT {
description
"Block floating point compression and decompression will be used";
}
enum BLOCK_SCALING {
description
"Block scaling compression and decompresion will be used";
}
enum U_LAW {
description
"u-Law compression and decompresion method will be used";
}
enum BEAMSPACE {
description
"Beamspace compression and decompression will be used";
}
enum MODULATION {
description
"Modulation compression and decompression will be used";
}
enum BLOCK-FLOATING-POINT-SELECTIVE-RE-SENDING {
description
"block floating point with selective re sending
compression and decompression will be used";
}
enum MODULATION-COMPRESSION-SELECTIVE-RE-SENDING {
description
"modulation compression with selective re sending
compression and decompression will be used";
}
}
description
"Compresion method which can be supported by the O-RU";
}
}
grouping compression-formats {
description
"Grouping deicated to list compression formats as choice";
choice compression-format {
description
"Choice of compression format for particular element";
case no-compresison {
description "Compression for beam weights is not supported.";
}
case block-floating-point {
description "Block floating point compression and decompression is supported.";
leaf exponent {
type uint8 {
range "4";
}
description "Exponent bit width size in number of bits used when encoding in udCompParam.";
}
}
case block-floating-point-selective-re-sending {
description
"Block floating point with selective re sending compression and decompression is supported.";
leaf sres-exponent {
type uint8 {
range "4";
}
description "Exponent bit width size in number of bits used when encoding in udCompParam.";
}
}
case block-scaling {
description "Block scaling compression and decompresion is supported.";
leaf block-scalar {
type uint8;
description
"Common scaler for compressed PRB";
}
}
case u-law {
description "u-Law compression and decompresion method is supported.";
leaf comp-bit-width {
type uint8 {
range "0..15";
}
description "Bit with for u-law compression";
}
leaf comp-shift {
type uint8 {
range "0..15";
}
description
"the shift applied to the entire PRB";
}
}
case beam-space-compression {
description "Beamspace compression and decompression is supported. Applies to beamforming weights only.";
leaf-list active-beam-space-coeficient-mask {
type uint8;
description
"active beamspace coefficient indices associated with the compressed beamforming vector";
}
leaf block-scaler {
type uint8;
description
"Common scaler for compressed beamforming coefficients";
}
}
case modulation-compression {
description "Modulation compression and decompression is supported.";
leaf csf {
type uint8 {
range "0..1";
}
description "Constallation shift flag";
}
leaf mod-comp-scaler {
type uint16 {
range "0..32767";
}
description "Modulation compression scaler value.";
}
}
case modulation-compression-selective-re-sending {
description "Modulation compression with selective re sending and decompression is supported.";
leaf sres-csf {
type uint8 {
range "0..1";
}
description "Constallation shift flag";
}
leaf sres-mod-comp-scaler {
type uint16 {
range "0..32767";
}
description "Modulation compression scaler value.";
}
}
}
}
grouping compression-params {
description
"Parameters to define compression";
leaf compression-type {
type enumeration {
enum STATIC {
description
"Indicates that static compression method will be used (both compression and IQ bitwidth)";
}
enum DYNAMIC {
description
"Indicates that dynamic compression method will be used";
}
}
mandatory true;
description
"Compression type that O-DU wants to be supported";
}
// *********** TO BE REMOVED ***********
leaf bitwidth {
when "../compression-type = 'STATIC'";
type uint8;
status deprecated;
description
"Bitwidth to be used in compression.
This has since been replaced in M-Plane version
2.0.0 with the iq-bitwidth schema node";
}
// *************************************
uses compression-formats;
}
grouping compression-parameters {
description
"Parameters used to define description type";
leaf iq-bitwidth {
type uint8;
description
"Bitwidth to be used in compression";
}
uses compression-formats;
}
grouping format-of-iq-sample {
description
"Indicates module capabilities about IQ samples";
leaf dynamic-compression-supported {
type boolean;
description
"Informs if radio supports dynamic compression method";
}
leaf realtime-variable-bit-width-supported {
type boolean;
description
"Informs if O-RU supports realtime variable bit with";
}
list compression-method-supported {
uses compression-parameters;
description
"List of supported compression methods by O-RU
Note: if O-RU supports different compression methods per endpoint
then please refer do endpoints to have information what
exactly is supported on a paticular endpoint";
}
leaf syminc-supported {
type boolean;
description
"Informs if symbol number increment command in a C-Plane is
supported or not";
}
leaf regularization-factor-se-supported {
type boolean;
description
"Informs if regularizationFactor in section type 5 is
supported(true) or not(false)";
}
leaf little-endian-supported {
type boolean;
default false;
description
"All O-RUs support bigendian byte order. This node informs if module supports the
the optional capability for little endian byte order for C/U plane data flows.
Note - little endian support does not invalidate bigendian support.";
}
}
grouping compression-details {
description "";
leaf iq-bitwidth {
type uint8;
description
"Bitwidth to be used in compression";
}
uses compression-params;
}
}
@@ -0,0 +1,414 @@
module o-ran-interfaces {
yang-version 1.1;
namespace "urn:o-ran:interfaces:1.0";
prefix "o-ran-int";
import ietf-inet-types {
prefix "inet";
}
import iana-if-type {
prefix "ianaift";
}
import ietf-interfaces {
prefix "if";
}
import ietf-ip {
prefix "ip";
}
import ietf-hardware {
prefix "hw";
}
import ietf-yang-types {
prefix "yang";
}
import iana-hardware {
prefix "ianahw";
}
organization "O-RAN Alliance";
contact
"www.o-ran.org";
description
"This module defines the YANG definitions for managng the O-RAN
interfaces.
Copyright 2020 the O-RAN Alliance.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the above disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the above disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the Members of the O-RAN Alliance nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.";
revision "2020-04-17" {
description
"version 1.2.0
1) updated descriptions to clarify operation when vlan-tagging is false";
reference "ORAN-WG4.M.0-v03.00";
}
revision "2019-07-03" {
description
"version 1.1.0
1) increasing max elements for user plane DSCP markings to 64
2) re-organizing layout to facilitate cross-WG adoption, whilst ensuring
nodes are syntactically and semantically equivalent";
reference "ORAN-WG4.M.0-v01.00";
}
revision "2019-02-04" {
description
"version 1.0.0
1) imported model from xRAN
2) changed namespace and reference from xran to o-ran";
reference "ORAN-WG4.M.0-v01.00";
}
feature UDPIP-BASED-CU-PLANE {
description
"This feature indicates that the RU supports the UDP/IP based transport
for the CU plane.";
}
feature ALIASMAC-BASED-CU-PLANE {
description
"This feature indicates that the RU supports the alias MAC address
based transport for the CU plane.";
}
typedef pcp {
type uint8 {
range "0..7";
}
description
"Priority Code Point. PCP is a 3-bit field that refers to the
class of service applied to a VLAN tagged frame. The
field specifies a priority value between 0 and 7, these values
can be used by quality of service (QoS) to prioritize
different classes of traffic.";
reference
"IEEE 802.1Q-2014: Virtual Bridged Local Area Networks";
}
grouping cos-marking {
description
"Configuration data for CU Plane ethernet CoS marking.
This grouping is only applicable to an interface when vlan-tagging is
set to TRUE for that interface. In other cases, it may be ignored.";
container class-of-service {
description
"CoS Configuration";
leaf u-plane-marking {
type pcp;
default 7;
description
"Marking used for default u-plane flows.
7 represents highest priority for u-plane marking";
}
leaf c-plane-marking {
type pcp;
default 7;
description "7 represents highest priority for c-plane marking";
}
leaf m-plane-marking {
type pcp;
default 2;
description "2 represents highest excellent effort for m-plane marking";
}
leaf s-plane-marking {
type pcp;
default 7;
description "7 represents highest priority for s-plane marking";
}
leaf other-marking {
type pcp;
default 1;
description "1 represents best effort for other marking";
}
list enhanced-uplane-markings{
key "up-marking-name";
max-elements 4;
description
"list of mappings for enhanced (non-default) u-plane markings";
leaf up-marking-name {
type string;
description "The name of the marking";
}
leaf enhanced-marking {
type pcp;
description "the enhanced u-plane marking";
}
}
}
}
grouping dscp-marking {
description
"Configuration data for CU Plane DSCP marking";
container diffserv-markings {
description
"DSCP Configuration";
leaf u-plane-marking {
type inet:dscp;
default 46;
description
"Marking used for default u-plane flows.
46 represents expedited forwarding";
}
leaf c-plane-marking {
type inet:dscp;
default 46;
description "46 represents expedited forwarding";
}
leaf s-plane-marking {
type inet:dscp;
default 46;
description "46 represents expedited forwarding";
}
leaf other-marking {
type inet:dscp;
default 0;
description "0 represents best effort forwarding";
}
list enhanced-uplane-markings{
key up-marking-name;
max-elements 64;
description
"list of mappings for enhanced (non-default) u-plane markings";
leaf up-marking-name {
type string;
description "The name of the marking";
}
leaf enhanced-marking {
type inet:dscp;
description "the enhanced u-plane marking";
}
}
}
}
// Cross Working Group Augmentations Follow
// Cross Working Group augmentations for basic Ethernet leafs
augment "/if:interfaces/if:interface" {
when "if:type = 'ianaift:ethernetCsmacd'" {
description "Applies to Ethernet interfaces";
}
description
"Augment the interface model with parameters for
base Ethernet interface";
leaf l2-mtu {
type uint16 {
range "64 .. 65535";
}
units bytes;
default 1500;
description
"The maximum size of layer 2 frames that may be transmitted
or received on the interface (excluding any FCS overhead).
For Ethernet interfaces it also excludes the
4-8 byte overhead of any known (i.e. explicitly matched by
a child sub-interface) 801.1Q VLAN tags.";
}
leaf vlan-tagging {
type boolean;
default true;
description
"Indicates if VLAN tagging is used.
Default true is used to enable equipment to autonomously discover that
it is connected to a trunk port.
This may be set to false, for example, when the O-RU is directly
connected to the O-DU. In such cases, native Ethernet frames may be
used across the O-RAN interface, i.e., any PCP markings defined
in the cos-markings grouping are NOT used by the O-RU and any default
value or configured value using those leafs may be ignored by the O-RAN
equipment.";
}
uses cos-marking;
}
// Cross Working Group augmentation for l2vlan interfaces for VLAN definition
augment "/if:interfaces/if:interface" {
when "if:type = 'ianaift:l2vlan'";
description "augments for VLAN definition";
leaf base-interface {
type if:interface-ref;
must "/if:interfaces/if:interface[if:name = current()]"
+ "/o-ran-int:vlan-tagging = 'true'" {
description
"The base interface must have VLAN tagging enabled.";
}
description
"The base interface for the VLAN sub-interafce.";
}
leaf vlan-id {
type uint16 {
range "1..4094";
}
description
"The VLAN-ID.";
}
}
// Cross Working Group augmention for both ethernetCsmacd and l2vlan interfaces
augment "/if:interfaces/if:interface" {
when "(if:type = 'ianaift:ethernetCsmacd') or
(if:type = 'ianaift:l2vlan')" {
description "Applies to ethernetCsmacd and l2vlan interfaces";
}
description
"Augment the interface model with parameters for all
both ethernetCsmacd and l2vlan interfaces.";
leaf last-cleared {
type yang:date-and-time;
config false;
description
"Timestamp of the last time the interface counters were
cleared.";
}
}
// Cross Working Group augmention to ietf-ip covering DSCP for M-Plane
augment "/if:interfaces/if:interface/ip:ipv4" {
description "augments for IPv4 based M-Plane transport";
leaf m-plane-marking {
type inet:dscp;
default 18;
description "18 represents AF21 or 'immediate traffic'";
}
}
augment "/if:interfaces/if:interface/ip:ipv6" {
description "augments for IPv6 based M-Plane transport";
leaf m-plane-marking {
type inet:dscp;
default 18;
description "18 represents AF21 or 'immediate traffic'";
}
}
// WG4 Specific Augmentations Follow
// WG4 Augmentation for basic Ethernet leafs
augment "/if:interfaces/if:interface" {
if-feature ALIASMAC-BASED-CU-PLANE;
when "if:type = 'ianaift:ethernetCsmacd'" {
description
"Applies to WG4 Ethernet interfaces for alias MAC based CU-Plane";
}
description
"Augment the interface model with parameters for
base Ethernet interface";
leaf-list alias-macs {
type yang:mac-address;
description
"Augments interfaces with range of alias MAC addresses.";
}
}
// WG4 Augmention for both ethernetCsmacd and l2vlan interfaces
augment "/if:interfaces/if:interface" {
when "(if:type = 'ianaift:ethernetCsmacd') or
(if:type = 'ianaift:l2vlan')" {
description "Applies to ethernetCsmacd and l2vlan interfaces";
}
description
"Augment the interface model with parameters for all
both ethernetCsmacd and l2vlan interfaces.";
leaf mac-address {
type yang:mac-address;
description
"The MAC address of the interface.";
}
container port-reference {
description
"a port reference used by other O-RAN modules";
leaf port-name {
type leafref {
path '/hw:hardware/hw:component/hw:name';
}
must "derived-from-or-self(deref(current())/../hw:class, 'ianahw:port')";
// TAKE NOTE - depending on version of pyang, this may generate various
// warnings, e.g., warning: XPath for "port-name" does not exist
description
"O-RAN interfaces use a reference to a physical port component.
In this case, the component name referenced must be of class type
port, i.e., when /hw:hardware/hw:component/hw:class is derived from
ianahw:port";
}
leaf port-number {
type uint8;
description
"A number allocated by the server which identifies a port.
Port number value is 0 to N-1 where N is number of ports
in the device.
This value is fixed for the lifetime of the equipment, i.e., cannot be
changed during equipment reboots.";
}
}
}
// WG4 specific augmention to ietf-ip covering DSCP for CUS Plane
augment "/if:interfaces/if:interface/ip:ipv4" {
if-feature UDPIP-BASED-CU-PLANE;
description "augments for IPv4 based CUS transport";
uses dscp-marking;
}
augment "/if:interfaces/if:interface/ip:ipv6" {
if-feature UDPIP-BASED-CU-PLANE;
description "augments for IPv6 based CUS transport";
uses dscp-marking;
}
// Other Working Group Specific Augmentations Follow Here
rpc reset-interface-counters {
description
"Management plane triggered restart of the interface counters.";
}
}
@@ -0,0 +1,840 @@
module o-ran-module-cap {
yang-version 1.1;
namespace "urn:o-ran:module-cap:1.0";
prefix "o-ran-module-cap";
import o-ran-compression-factors {
prefix "cf";
revision-date 2020-08-10;
}
organization "O-RAN Alliance";
contact
"www.o-ran.org";
description
"This module defines the module capabilities for
the O-RAN Radio Unit.
Copyright 2020 the O-RAN Alliance.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the above disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the above disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the Members of the O-RAN Alliance nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.";
revision "2020-08-10" {
description
"version 4.0.0
1) added RO capability to expose O-RU's ability to support udCompLen record in U-Plane section header fields
2) feature indicating support for static PRACH configuration introduced
3) feature indicating support for static SRS configuration introduced
4) feature indicating support for TDD pattern configuration introduced
5) backward compatible change to introduce new O-RU's features related
to Section Description Priority to serve for CUS-Plane C";
reference "ORAN-WG4.M.0-v04.00";
}
revision "2020-04-17" {
description
"version 3.0.0
1) Adding optional little endian support
2) Added support for Dynamic Spectrum Sharing feature
3) Enable number-of-ru-ports to be different between dl and ul
4) Enable regularizationFactor to be signalled outside of section type 6
5) Enable PRACH preamble formats supported to be signalled by O-RU
6) adding support for selective RE sending
7) supporting new section extension for grouping multiple ports
8) signalling to enable O-RU to ndicate is requires unique ecpri sequence id
for eAxC_IDs serving for UL and DL for the same carrier component";
reference "ORAN-WG4.M.0-v03.00";
}
revision "2019-07-03" {
description
"version 1.1.0
1) backward compatible change to introduce new RW leafs for use in
constraints in uplane-config and laa models.
2) removing unnecessary relations to band 46 in uplink";
reference "ORAN-WG4.M.0-v01.00";
}
revision "2019-02-04" {
description
"version 1.0.0
1) imported model from xRAN
2) changed namespace and reference from xran to o-ran";
reference "ORAN-WG4.M.0-v01.00";
}
feature LAA {
description
"Indicates that the Radio Unit supports LAA.";
}
feature EAXC-ID-GROUP-SUPPORTED {
description
"indicates that the Radio Unit supports EAXC-ID-GROUP-SUPPORTED.";
}
feature TRANSPORT-FRAGMENTATION {
description
"This leaf is used to indicate whether the O-RU supports O-RAN Radio Transport Fragmentation";
}
feature DSS_LTE_NR {
description
"Indicates that the Radio Unit supports Dynamic Spectrum Sharing between LTE and NR.";
}
feature PRACH-STATIC-CONFIGURATION-SUPPORTED {
description
"This leaf is used to indicate O-RU's support for configuration of PRACH (Physical Random Access Channel) pattern in the static manner, so that PRACH U-Plane traffic can be processed by RU without receiving C-Plane messages conveying the PRACH configuration.";
}
feature SRS-STATIC-CONFIGURATION-SUPPORTED {
description
"This leaf is used to indicate O-RU's support for configuration of SRS (Sounding Reference Signal Channel) pattern in the static manner, so that SRS U-Plane traffic can be processed by RU without receiving C-Plane messages conveying the SRS configuration.";
}
feature CONFIGURABLE-TDD-PATTERN-SUPPORTED {
description
"This leaf is used to indicate O-RU's support for configurable TDD pattern.";
}
typedef sub-band-string {
type string {
pattern [ABCD];
}
description "Sub bands definition";
}
typedef scs-config-type {
type enumeration {
enum KHZ_15 {
value 0;
description
"15kHz sub carrier spacing";
}
enum KHZ_30 {
value 1;
description
"30kHz sub carrier spacing";
}
enum KHZ_60 {
value 2;
description
"60kHz sub carrier spacing";
}
enum KHZ_120 {
value 3;
description
"120kHz sub carrier spacing";
}
enum KHZ_240 {
value 4;
description
"240kHz sub carrier spacing";
}
enum KHZ_1_25 {
value 12;
description
"1,25kHz sub carrier spacing";
}
enum KHZ_3_75 {
value 13;
description
"3.75kHz sub carrier spacing";
}
enum KHZ_5 {
value 14;
description
"5kHz sub carrier spacing";
}
enum KHZ_7_5 {
value 15;
description
"7.5kHz sub carrier spacing";
}
}
description
"Scs configuration type definition";
}
grouping compression-method-grouping {
description
"Grouping for compression method.";
leaf compression-method {
type enumeration {
enum BLOCK_FLOATING_POINT {
description
"Block floating point compression and decompression will be used";
}
enum BLOCK_SCALING {
description
"Block scaling compression and decompresion will be used";
}
enum U_LAW {
description
"u-Law compression and decompresion method will be used";
}
enum BEAMSPACE {
description
"Beamspace compression and decompression will be used";
}
enum MODULATION {
description
"Modulation compression and decompression will be used";
}
enum BLOCK-FLOATING-POINT-SELECTIVE-RE-SENDING {
description
"block floating point with selective re sending
compression and decompression will be used";
}
enum MODULATION-COMPRESSION-SELECTIVE-RE-SENDING {
description
"modulation compression with selective re sending
compression and decompression will be used";
}
}
description
"Compresion method which can be supported by the O-RU.
An O-RU may further refine the applicability of compression
methods per endpoint using o-ran-uplane-conf.yang model";
}
}
grouping sub-band-max-min-ul-dl-frequency {
description
"Grouping for defining max and min supported frequency - dl and ul.";
leaf max-supported-frequency-dl {
type uint64;
description
"This value indicates Maximum supported downlink frequency in the
LAA subband. Value unit is Hz.";
}
leaf min-supported-frequency-dl {
type uint64;
description
"This value indicates Minimum supported downlink frequency in the
LAA subband. Value unit is Hz.";
}
}
grouping format-of-iq-sample {
description
"Indicates module capabilities about IQ samples";
leaf dynamic-compression-supported {
type boolean;
description
"Informs if radio supports dynamic compression method";
}
leaf realtime-variable-bit-width-supported {
type boolean;
description
"Informs if O-RU supports realtime variable bit with";
}
list compression-method-supported {
uses cf:compression-details;
leaf-list fs-offset {
if-feature cf:CONFIGURABLE-FS-OFFSET;
type uint8;
default 0;
description
"List of fs offset values supported with this IQ format / compression method;
fs-offset adjusts FS (full scale) value of IQ format relative to FS derived from unmodified IQ format.
Please refer to CU-Plane specification for details";
}
description
"List of supported compression methods by O-RU
Note: if O-RU supports different compression methods per endpoint
then please refer to endpoints to have information what
exactly is supported on paticular endpoint";
}
leaf variable-bit-width-per-channel-supported {
when "/module-capability/ru-capabilities/format-of-iq-sample/realtime-variable-bit-width-supported = 'true'";
type boolean;
description
"Informs if variable bit width per channel is supported or not";
}
leaf syminc-supported {
type boolean;
description
"Informs if symbol number increment command in a C-Plane is
supported or not";
}
leaf regularization-factor-se-supported {
type boolean;
description
"Informs if regularizationFactor in section type 5 is
supported(true) or not(false)";
}
leaf little-endian-supported {
type boolean;
default false;
description
"All O-RUs support bigendian byte order. This node informs if module supports the
the optional capability for little endian byte order for C/U plane data flows.
Note - little endian support does not invalidate bigendian support.";
}
}
grouping scs-a-b {
description
"Grouping for scs-a and scs-b";
leaf scs-a{
type scs-config-type;
description
"Sub-carrier spacing configuration";
}
leaf scs-b{
type scs-config-type;
description
"Sub-carrier spacing configuration";
}
}
grouping ul-mixed-num-required-guard-rbs {
description
"Required number of guard resource blocks for the combination of
subcarrier spacing values for uplink";
uses scs-a-b;
leaf number-of-guard-rbs-ul{
type uint8;
description
"This value indicates the required number of guard resource blocks
between the mixed numerologies, the RB using scs-a and the RB
using scs-b. It's number is based on scs-a";
}
}
grouping dl-mixed-num-required-guard-rbs {
description
"Required number of guard resource blocks for the combination of
subcarrier spacing values for uplink";
uses scs-a-b;
leaf number-of-guard-rbs-dl{
type uint8;
description
"This value indicates the required number of guard resource blocks
between the mixed numerologies, the RB using scs-a and the RB
using scs-b. It's number is based on scs-a";
}
}
grouping ru-capabilities {
description
"Structure representing set of capabilities.";
leaf ru-supported-category {
type enumeration {
enum CAT_A {
description
"Informs that precoding is not supported in O-RU";
}
enum CAT_B {
description
"Informs that precoding is supported in O-RU";
}
}
description
"Informs about which category O-RU supports";
}
leaf number-of-ru-ports {
type uint8;
status deprecated;
description
"Assuming all endpoints support time-managed traffic AND non-time-managed traffic (choice is as per configuration)
- the number of O-RU ports is the product of number of spatial streams (leaf number-of-spatial-streams) and number of numerologies O-RU supports.
For example, if the number of spatial streams is 4 then the number of O-RU ports is 8 when PUSCH and PRACH are processed in the different endpoints.
In case there are specific endpoints that support non-time-managed traffic only
- the number of O-RU ports calculated with above mentioned equation is extended by number of endpoints supporting only non-time-managed traffic.";
}
leaf number-of-ru-ports-ul {
type uint8;
description
"Assuming all endpoints support time-managed traffic AND non-time-managed traffic (choice is as per configuration)
- the number of O-RU ports for uplink is the product of number of spatial streams (leaf number-of-spatial-streams) and number of numerologies O-RU supports.
For example, if the number of spatial streams is 4 then the number of O-RU ports is 8 when PUSCH and PRACH are processed in the different endpoints.
In case there are specific endpoints that support non-time-managed traffic only
- the number of O-RU ports calculated with above mentioned equation is extended by number of endpoints supporting only non-time-managed traffic.";
}
leaf number-of-ru-ports-dl {
type uint8;
description
"Assuming all endpoints support time-managed traffic AND non-time-managed traffic (choice is as per configuration)
- the number of O-RU ports for downlink is the product of number of spatial streams (leaf number-of-spatial-streams) and number of numerologies O-RU supports.
For example, if the number of spatial streams is 4 then the number of O-RU ports is 8 when SSB and non-SSB are processed in the different endpoints.
In case there are specific endpoints that support non-time-managed traffic only
- the number of O-RU ports calculated with above mentioned equation is extended by number of endpoints supporting only non-time-managed traffic.";
}
leaf number-of-spatial-streams {
type uint8;
description
"This value indicates the number of spatial streams supported at O-RU for DL and UL.
For DL, it is same as the number of antenna ports specified in 3GPP TS38.214, Section 5.2 and 3GPP TS36.213, Section 5.2.";
}
leaf max-power-per-pa-antenna {
type decimal64{
fraction-digits 4;
}
description
"This value indicates Maximum Power per PA per antenna. Value unit is dBm.";
}
leaf min-power-per-pa-antenna {
type decimal64{
fraction-digits 4;
}
description
"This value indicates Minimum Power per PA per antenna. Value unit is dBm.";
}
leaf fronthaul-split-option {
type uint8 {
range "7";
}
description
"This value indicates the Fronthaul Split Option, i.e., 2 or 7 in this release.";
}
container format-of-iq-sample {
description
"Indicates module capabilities about IQ samples";
uses format-of-iq-sample;
}
list ul-mixed-num-required-guard-rbs {
key "scs-a scs-b";
description
"List of required number of guard resource blocks
for the combination of subcarrier spacing values for downlink";
uses ul-mixed-num-required-guard-rbs;
}
list dl-mixed-num-required-guard-rbs {
key "scs-a scs-b";
description
"List of required number of guard resource blocks
for the combination of subcarrier spacing values for uplink";
uses dl-mixed-num-required-guard-rbs;
}
leaf energy-saving-by-transmission-blanks {
type boolean;
mandatory true;
description
"Parameter informs if unit supports energy saving by transmission blanking";
}
container eaxcid-grouping-capabilities {
if-feature o-ran-module-cap:EAXC-ID-GROUP-SUPPORTED;
description
"a container with parameters for eaxcid grouping";
leaf max-num-tx-eaxc-id-groups {
type uint8;
description
"Maximum number of configurable tx-eaxc-id-group supported by O-RU.";
}
leaf max-num-tx-eaxc-ids-per-group {
type uint8;
description
"Maximum number of member-tx-eaxc-id in single tx-eaxc-id-group supported by O-RU.";
}
leaf max-num-rx-eaxc-id-groups {
type uint8;
description
"the maximum number of groups with the eAxC IDs those are assigned to low-level-rx-links.";
}
leaf max-num-rx-eaxc-ids-per-group {
type uint8;
description
"the maximum number of eAxC IDs per rx-eaxc-id-group.";
}
}
leaf dynamic-transport-delay-management-supported {
type boolean;
mandatory true;
description
"Parameter informs if unit supports dynamic transport delay management through eCPRI Msg 5";
}
leaf support-only-unique-ecpri-seqid-per-eaxc {
type boolean;
default false;
description
"Parameter informs if O-RU expects unique ecpri sequence id for eAxC_IDs serving
for UL and DL for the same carrier component.
Note: If this is set to TRUE, the O-DU can decide to either use different eAxC_IDs for UL and
DL or can generate unique sequence ID per eAxC_ID.
TAKE NOTE: This leaf is backwards compatible from an O-RU persepctive BUT an O-RU that
sets this leaf to TRUE may result in incompatibilities when operating with an O-DU
designed according to the O-RAN CUS-Plane Specification v02.00, e.g., if the O-DU is
incapable of using different eAxC values between UL and DL";
}
container coupling-methods {
description
"O-RU's capabilities related to supported C-Plane and U-Plane coupling methods";
leaf coupling-via-frequency-and-time {
type boolean;
description
"Coupling via frequency and time; see methods of coupling of C-plane and U-plane in CUS-Plane specification";
}
leaf coupling-via-frequency-and-time-with-priorities {
type boolean;
description
"Coupling via Frequency and time with priorities; see methods of coupling of C-plane and U-plane in CUS-Plane specification.
Note: If coupling-via-frequency-and-time-with-priorities is 'true' then coupling-via-frequency-and-time shall also be 'true'.";
}
}
leaf ud-comp-len-supported {
type boolean;
description
"This property informs if O-RU supports optional field udCompLen in U-Plane messages.
Only in case this leaf is present and its value is TRUE, O-RU supports U-Plane messages
containing udCompLen field in section header.";
}
}
grouping sub-band-info {
description "container for collection of leafs for LAA subband 46";
list sub-band-frequency-ranges {
key sub-band;
description "frequency information on a per sub-band basis";
leaf sub-band {
type sub-band-string;
description "Sub band when band 46";
}
uses sub-band-max-min-ul-dl-frequency;
}
leaf number-of-laa-scarriers {
type uint8;
description
"This value indicates the number of LAA secondary carriers supported at O-RU.";
}
leaf maximum-laa-buffer-size {
type uint16;
description
"Maximum O-RU buffer size in Kilobytes (KB) per CC. This parameter is
needed at the O-DU to know how much data can be sent in advance
and stored at the O-RU to address the LBT uncertainity.";
}
leaf maximum-processing-time {
type uint16;
units microseconds;
description
"Maximum O-RU Processing time in microseconds at the O-RU to handle the
received/transmitted packets from/to the O-DU. This parameter is
needed at the O-DU to determine the time where it needs to send
the data to the O-RU.";
}
leaf self-configure {
type boolean;
description "This value indicates that the O-RU can manage the contention window locally. ";
}
}
grouping support-for-dl {
description
"Grouping for DL specific parameters";
leaf max-supported-frequency-dl {
type uint64;
description
"This value indicates Maximum supported downlink frequency. Value unit is Hz.";
}
leaf min-supported-frequency-dl {
type uint64;
description
"This value indicates Minimum supported downlink frequency. Value unit is Hz.";
}
leaf max-supported-bandwidth-dl {
type uint64;
description
"This value indicates Maximum total downlink bandwidth in module. Value unit is Hz.";
}
leaf max-num-carriers-dl {
type uint32;
description
"This value indicates Maximum number of downlink carriers in module.";
}
leaf max-carrier-bandwidth-dl {
type uint64;
description
"This value indicates Maximum bandwidth per downlink carrier. Value unit is Hz.";
}
leaf min-carrier-bandwidth-dl {
type uint64;
description
"This value indicates Minimum bandwidth per downlink carrier. Value unit is Hz.";
}
leaf-list supported-technology-dl {
type enumeration{
enum LTE {
description "LTE is supported in DL path.";
}
enum NR {
description "NR is supported in DL path.";
}
enum DSS_LTE_NR {
if-feature DSS_LTE_NR;
description
"DSS is supported in the DL, which implicitly means LTE and NR are also
BOTH supported in the DL.";
}
}
min-elements 1;
description
"This list provides information regarding technologies supported in DL path";
}
}
grouping support-for-ul {
description
"Grouping for UL specific parameters";
leaf max-supported-frequency-ul {
type uint64;
description
"This value indicates Maximum supported uplink frequency. Value unit is Hz.";
}
leaf min-supported-frequency-ul {
type uint64;
description
"This value indicates Minimum supported uplink frequency. Value unit is Hz.";
}
leaf max-supported-bandwidth-ul {
type uint64;
description
"This value indicates Maximum total uplink bandwidth in module. Value unit is Hz.";
}
leaf max-num-carriers-ul {
type uint32;
description
"This value indicates Maximum number of uplink carriers in module.";
}
leaf max-carrier-bandwidth-ul {
type uint64;
description
"This value indicates Maximum bandwidth per uplink carrier. Value unit is Hz.";
}
leaf min-carrier-bandwidth-ul {
type uint64;
description
"This value indicates Minimum bandwidth per uplink carrier. Value unit is Hz.";
}
leaf-list supported-technology-ul {
type enumeration{
enum LTE {
description "LTE is supported in UL path.";
}
enum NR {
description "NR is supported in UL path.";
}
enum DSS_LTE_NR {
if-feature DSS_LTE_NR;
description
"DSS is supported in the UL, which implicitly means LTE and NR are also
BOTH supported in the UL.";
}
}
min-elements 1;
description
"This list provides information regarding technologies supported in UL path";
}
}
grouping band-capabilities {
description
"Capabilities that are needed to be defined per each band";
leaf band-number {
type uint16;
description
"Band number";
}
container sub-band-info {
when "../band-number = '46'";
if-feature "o-ran-module-cap:LAA";
description "container for collection of leafs for LAA subband 46";
uses sub-band-info;
}
uses support-for-dl;
uses support-for-ul;
leaf max-num-component-carriers {
type uint8;
description "maximum number of component carriers supported by the O-RU";
}
leaf max-num-bands {
type uint16;
description "maximum number of bands supported by the O-RU";
}
leaf max-num-sectors {
type uint8;
description "maximum number of sectors supported by the O-RU";
}
leaf max-power-per-antenna {
type decimal64{
fraction-digits 4;
}
description
"This value indicates Maximum Power per band per antenna. Value unit is dBm.";
}
leaf min-power-per-antenna {
type decimal64{
fraction-digits 4;
}
description
"This value indicates Minimum Power per band per antenna. Value unit is dBm.";
}
leaf codebook-configuration_ng {
type uint8;
description
"This parameter informs the precoder codebook_ng that are used for precoding";
}
leaf codebook-configuration_n1 {
type uint8;
description
"This parameter informs the precoder codebook_n1 that are used for precoding";
}
leaf codebook-configuration_n2 {
type uint8;
description
"This parameter informs the precoder codebook_n2 that are used for precoding";
}
}
container module-capability {
description
"module capability object responsible for providing module capability.";
container ru-capabilities {
config false;
description
"Structure representing set of capabilities.";
uses ru-capabilities;
}
list band-capabilities {
key band-number;
config false;
description
"Capabilities that are needed to be defined per each band";
uses band-capabilities;
}
container rw-sub-band-info {
if-feature "o-ran-module-cap:LAA";
description "config true leafrefs for use as constraints for config true leafs";
leaf rw-number-of-laa-scarriers {
type leafref {
path "/module-capability/band-capabilities/sub-band-info/number-of-laa-scarriers";
require-instance false;
}
description
"This value indicates the number of LAA secondary carriers supported at O-RU.";
}
leaf rw-self-configure {
type leafref {
path "/module-capability/band-capabilities/sub-band-info/self-configure";
require-instance false;
}
description
"This value indicates that the O-RU can manage the contention window locally.";
}
}
}
}
@@ -0,0 +1,362 @@
module o-ran-processing-element {
yang-version 1.1;
namespace "urn:o-ran:processing-element:1.0";
prefix "o-ran-elements";
import ietf-yang-types {
prefix yang;
}
import ietf-inet-types {
prefix "inet";
}
import ietf-interfaces {
prefix "if";
}
import ietf-ip {
prefix "ip";
}
import o-ran-interfaces {
prefix "o-ran-int";
}
organization "O-RAN Alliance";
contact
"www.o-ran.org";
description
"This module defines the YANG definitions for mapping of transport flows to
processing elements. Three options are supported:
i) virtual MAC based mapping
ii) MAC addrress + VLAN-ID based mapping
iii) UDP/IP based mapping
Copyright 2020 the O-RAN Alliance.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS'
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the above disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the above disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the Members of the O-RAN Alliance nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.";
revision "2020-04-17" {
description
"version 3.0.0
1) added new enum SHARED-CELL-ETH-INTERFACE in
transport-session-type and new containers north-eth-flow and
south-eth-flow to enable Shared cell scenario.";
reference "ORAN-WG4.M.0-v03.00";
}
revision "2019-07-03" {
description
"version 1.1.0
1) added new leaf to enable O-RU to report the maximum number of
transport flows it can support, e.g., due to restrictions on number
of VLAN-IDs when ethernet type transport is used.";
reference "ORAN-WG4.M.0-v01.00";
}
revision "2019-02-04" {
description
"version 1.0.0
1) imported model from xRAN
2) changed namespace and reference from xran to o-ran";
reference "ORAN-WG4.M.0-v01.00";
}
feature SHARED_CELL {
description
"Presence of feature indicates that this O-RU is capable to support
shared cell.";
}
// groupings
grouping pe-group {
leaf maximum-number-of-transport-flows {
type uint16 {
range "1..4094";
}
config false;
default 4094;
description
"The maximum number of transport flows that can be supported by an O-RU";
}
leaf transport-session-type {
type enumeration {
enum ETH-INTERFACE {
description "VLAN based CUS Transport ";
}
enum UDPIP-INTERFACE {
description "UDP/IP based CUS Transport ";
}
enum ALIASMAC-INTERFACE{
description "Alias MAC address based CUS Transport ";
}
enum SHARED-CELL-ETH-INTERFACE {
if-feature "SHARED_CELL";
description "VLAN based CUS Transport used for Shared Cell scenario";
}
}
default ETH-INTERFACE;
description
"the type of transport session used for identifying different processing
elements";
}
container enhanced-uplane-mapping {
presence "indicates that enhanced uplane mapping is used";
description "a mapping table for enhanced user plane marking";
list uplane-mapping {
key "up-marking-name";
description
"a mapping between up-link name and o-ran-interfaces:up-marking-name";
leaf up-marking-name {
type string;
description "a unique up marking name that is used for enhanced up marking";
}
choice up-markings {
description
"U-Plane markings";
case ethernet {
when "(../../transport-session-type = 'ALIASMAC-INTERFACE') or
(../../transport-session-type = 'ETH-INTERFACE') or
(../../transport-session-type = 'SHARED-CELL-ETH-INTERFACE')";
leaf up-cos-name {
type leafref {
path "/if:interfaces/if:interface/o-ran-int:class-of-service/o-ran-int:enhanced-uplane-markings/o-ran-int:up-marking-name";
}
description "the Ethernet U-plane transport marking as defined in o-ran-interfaces";
}
}
case ipv4 {
when "(../../transport-session-type = 'UDPIP-INTERFACE')";
leaf upv4-dscp-name {
if-feature o-ran-int:UDPIP-BASED-CU-PLANE;
type leafref {
path "/if:interfaces/if:interface/ip:ipv4/o-ran-int:diffserv-markings/o-ran-int:enhanced-uplane-markings/o-ran-int:up-marking-name";
}
description "the IPv4 U-plane transport marking as defined in o-ran-interfaces";
}
}
case ipv6 {
when "(../../transport-session-type = 'UDPIP-INTERFACE')";
leaf upv6-dscp-name {
if-feature o-ran-int:UDPIP-BASED-CU-PLANE;
type leafref {
path "/if:interfaces/if:interface/ip:ipv6/o-ran-int:diffserv-markings/o-ran-int:enhanced-uplane-markings/o-ran-int:up-marking-name";
}
description "the IPv6 U-plane transport marking as defined in o-ran-interfaces";
}
}
}
}
}
list ru-elements {
key "name";
description
"the list of transport definitions for each processing element";
leaf name {
type string {
length "1..255";
}
description
"A name that is unique across the O-RU that identifies a processing
element instance.
This name may be used in fault management to refer to a fault source
or affected object";
}
container transport-flow {
description
"container for the transport-flow used for CU plane";
leaf interface-name {
type leafref {
path "/if:interfaces/if:interface/if:name";
}
description "the interface name ";
}
container aliasmac-flow {
when "../../../transport-session-type = 'ALIASMAC-INTERFACE'";
if-feature o-ran-int:ALIASMAC-BASED-CU-PLANE;
description "leafs for virtual mac type data flows";
leaf ru-aliasmac-address {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:alias-macs";
}
mandatory true;
description
"O-RU's alias MAC address used for alias MAC based flow";
}
leaf vlan-id {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id";
}
description
"O-RU's VLAN-ID used for alias MAC based flow";
}
leaf o-du-mac-address {
type yang:mac-address;
mandatory true;
description
"O-DU's MAC address used for alias MAC based flow";
}
}
container eth-flow {
when "../../../transport-session-type = 'ETH-INTERFACE'";
description "leafs for mac + vlan-id type data flows";
leaf ru-mac-address {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address";
}
mandatory true;
description
"O-RU's MAC address used for Ethernet based flow";
}
leaf vlan-id {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id";
}
mandatory true;
description
"O-RU's VLAN-ID used for Ethernet based flow";
}
leaf o-du-mac-address {
type yang:mac-address;
mandatory true;
description
"O-DU's MAC address used for Ethernet based flow";
}
}
container udpip-flow {
when "../../../transport-session-type = 'UDPIP-INTERFACE'";
description "leafs for UDP/IP type data flows";
choice address {
leaf ru-ipv4-address {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/ip:ipv4/ip:address/ip:ip";
}
description "O-RU's IPv4 address";
}
leaf ru-ipv6-address {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/ip:ipv6/ip:address/ip:ip";
}
description "O-RU's IPv6 address";
}
mandatory true;
description "choice of O-RU IPv4 or IPv6 address";
}
leaf o-du-ip-address {
type inet:ip-address;
mandatory true;
description "O-DU's IPv address";
}
leaf ru-ephemeral-udp-port {
type inet:port-number;
mandatory true;
description
"ephemeral port used by O-RU";
}
leaf o-du-ephemeral-udp-port {
type inet:port-number;
mandatory true;
description
"ephemeral port used by O-DU";
}
leaf ecpri-destination-udp {
type inet:port-number;
mandatory true;
description "the well known UDP port number used by eCPRI";
// fixme - add in a default when allocated by IANA
}
}
container north-eth-flow {
when "../../../transport-session-type = 'SHARED-CELL-ETH-INTERFACE'";
if-feature "SHARED_CELL";
description "leafs for mac + vlan-id type data flows";
leaf ru-mac-address {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address";
}
description
"O-RU's MAC address used for Ethernet based flow";
}
leaf vlan-id {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id";
}
description
"O-RU's VLAN-ID used for Ethernet based flow";
}
leaf north-node-mac-address {
type yang:mac-address;
description
"North-node's MAC address used for Ethernet based flow";
}
}
container south-eth-flow {
when "../../../transport-session-type = 'SHARED-CELL-ETH-INTERFACE'";
if-feature "SHARED_CELL";
description "leafs for mac + vlan-id type data flows";
leaf ru-mac-address {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:mac-address";
}
description
"O-RU's MAC address used for Ethernet based flow";
}
leaf vlan-id {
type leafref {
path "/if:interfaces/if:interface[if:name = current()/../../interface-name]/o-ran-int:vlan-id";
}
description
"O-RU's VLAN-ID used for Ethernet based flow";
}
leaf south-node-mac-address {
type yang:mac-address;
description
"south-node's MAC address used for Ethernet based flow";
}
}
}
}
}
// top level container
container processing-elements {
description
"a model defining the mapping between transport flows and arbitrary
O-RAN processing elements. A processing element may be then defined for
handling connectivity or delay procedures, or defined with a corresponding
eaxcid for CU plane operations";
uses pe-group;
}
}
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -90,7 +90,7 @@ void oai_xran_fh_rx_callback(void *pCallbackTag, xran_status_t status)
rx_sym = callback_tag->symbol & 0xFF;
uint32_t ru_id = callback_tag->oXuId;
LOG_D(NR_PHY,
LOG_D(HW,
"rx_callback frame %d, subframe %d, slot %d, second %lld, rx_sym %d ru_id %d\n",
frame,
subframe,
@@ -127,7 +127,7 @@ void oai_xran_fh_rx_callback(void *pCallbackTag, xran_status_t status)
rx_RU[ru_id][slot2] = 1;
if (last_frame > 0 && frame > 0
&& ((slot2 > 0 && last_frame != frame) || (slot2 == 0 && last_frame != ((1024 + frame - 1) & 1023))))
LOG_E(PHY, "Jump in frame counter last_frame %d => %d, slot %d\n", last_frame, frame, slot2);
LOG_E(HW, "Jump in frame counter last_frame %d => %d, slot %d\n", last_frame, frame, slot2);
for (int i = 0; i < num_ports; i++) {
if (rx_RU[i][slot2] == 0)
return;
@@ -142,16 +142,16 @@ void oai_xran_fh_rx_callback(void *pCallbackTag, xran_status_t status)
info->tti = tti;
info->sl = slot2;
info->f = frame;
LOG_D(PHY, "Push %d.%d.%d (slot %d, subframe %d,last_slot %d)\n", frame, info->sl, slot, ru_id, subframe, last_slot);
LOG_D(HW, "Push %d.%d.%d (slot %d, subframe %d,last_slot %d)\n", frame, info->sl, slot, ru_id, subframe, last_slot);
pushNotifiedFIFO(&oran_sync_fifo, req);
#else
LOG_D(PHY, "Writing %d.%d.%d (slot %d, subframe %d,last_slot %d)\n", frame, slot2, ru_id, slot, subframe, last_slot);
LOG_D(HW, "Writing %d.%d.%d (slot %d, subframe %d,last_slot %d)\n", frame, slot2, ru_id, slot, subframe, last_slot);
oran_sync_info.tti = tti;
oran_sync_info.sl = slot2;
oran_sync_info.f = frame;
#endif
} else
LOG_E(PHY, "Cannot Push %d.%d.%d (slot %d, subframe %d,last_slot %d)\n", frame, slot2, ru_id, slot, subframe, last_slot);
LOG_E(HW, "Cannot Push %d.%d.%d (slot %d, subframe %d,last_slot %d)\n", frame, slot2, ru_id, slot, subframe, last_slot);
last_slot = slot2;
last_frame = frame;
} // rx_sym == 7
@@ -289,7 +289,7 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
while ((f = pollNotifiedFIFO(&oran_sync_fifo)) != NULL) {
oran_sync_info_t *old_info = NotifiedFifoData(res);
oran_sync_info_t *new_info = NotifiedFifoData(f);
LOG_E(PHY, "Detected double sync message %d.%d => %d.%d\n", old_info->f, old_info->sl, new_info->f, new_info->sl);
LOG_E(HW, "Detected double sync message %d.%d => %d.%d\n", old_info->f, old_info->sl, new_info->f, new_info->sl);
delNotifiedFIFO_elt(res);
res = f;
}
@@ -305,14 +305,14 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
uint32_t tti_in = oran_sync_info.tti;
static int last_slot = -1;
LOG_D(PHY, "oran slot %d, last_slot %d\n", *slot, last_slot);
LOG_D(HW, "oran slot %d, last_slot %d\n", *slot, last_slot);
int cnt = 0;
// while (*slot == last_slot) {
while (tti_in == oran_sync_info.tti) {
//*slot = oran_sync_info.sl;
cnt++;
}
LOG_D(PHY, "cnt %d, Reading %d.%d\n", cnt, *frame, *slot);
LOG_D(HW, "cnt %d, Reading %d.%d\n", cnt, *frame, *slot);
last_slot = *slot;
#endif
// return(0);
@@ -380,9 +380,9 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
int16_t payload_len = 0;
LOG_D(PHY, "pRbMap->nPrbElm %d\n", pRbMap->nPrbElm);
LOG_D(HW, "pRbMap->nPrbElm %d\n", pRbMap->nPrbElm);
for (idxElm = 0; idxElm < pRbMap->nPrbElm; idxElm++) {
LOG_D(PHY,
LOG_D(HW,
"prbMap[%d] : PRBstart %d nPRBs %d\n",
idxElm,
pRbMap->prbMap[idxElm].nRBStart,
@@ -436,7 +436,7 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
} // vv_inf
if ((*frame & 0x7f) == 0 && *slot == 0 && xran_get_common_counters(gxran_handle, &x_counters[0]) == XRAN_STATUS_SUCCESS) {
for (int o_xu_id = 0; o_xu_id < fh_init->xran_ports; o_xu_id++) {
LOG_I(NR_PHY,
LOG_I(HW,
"[%s%d][rx %7ld pps %7ld kbps %7ld][tx %7ld pps %7ld kbps %7ld][Total Msgs_Rcvd %ld]\n",
"o-du ",
o_xu_id,
@@ -448,7 +448,7 @@ int xran_fh_rx_read_slot(ru_info_t *ru, int *frame, int *slot)
x_counters[o_xu_id].tx_bytes_per_sec * 8 / 1000L,
x_counters[o_xu_id].Total_msgs_rcvd);
for (int rxant = 0; rxant < ru->nb_rx / fh_init->xran_ports; rxant++)
LOG_I(NR_PHY,
LOG_I(HW,
"[%s%d][pusch%d %7ld prach%d %7ld]\n",
"o_du",
o_xu_id,
+110 -26
View File
@@ -32,6 +32,10 @@
#include "stdio.h"
#include "string.h"
#ifdef OAI_MPLANE
#include "mplane/ru-mplane-api.h"
#endif
static void print_fh_eowd_cmn(unsigned index, const struct xran_ecpri_del_meas_cmn *eowd_cmn)
{
printf("\
@@ -522,6 +526,38 @@ static bool set_fh_io_cfg(struct xran_io_cfg *io_cfg, const paramdef_t *fhip, in
return true;
}
#ifdef OAI_MPLANE
static bool set_fh_eaxcid_conf_mplane(struct xran_eaxcid_config *eaxcid_conf, enum xran_category cat, const ru_session_list_t *ru_session_list)
{
xran_mplane_t *xran_mplane = &ru_session_list->ru_session[0].xran_mplane;
switch (cat) {
case XRAN_CATEGORY_A:
eaxcid_conf->mask_cuPortId = xran_mplane->du_port_bitmask;
eaxcid_conf->mask_bandSectorId = xran_mplane->band_sector_bitmask;
eaxcid_conf->mask_ccId = xran_mplane->ccid_bitmask;
eaxcid_conf->mask_ruPortId = xran_mplane->ru_port_bitmask;
eaxcid_conf->bit_cuPortId = xran_mplane->du_port;
eaxcid_conf->bit_bandSectorId = xran_mplane->band_sector; // total number of band sectors supported by O-RU should be retrieved by M-plane - <max-num-bands> && <max-num-sectors>
eaxcid_conf->bit_ccId = xran_mplane->ccid; // total number of CC supported by O-RU should be retrieved by M-plane - <max-num-component-carriers>
eaxcid_conf->bit_ruPortId = xran_mplane->ru_port;
break;
case XRAN_CATEGORY_B:
eaxcid_conf->mask_cuPortId = 0xf000;
eaxcid_conf->mask_bandSectorId = 0x0c00;
eaxcid_conf->mask_ccId = 0x0300;
eaxcid_conf->mask_ruPortId = 0x000f;
eaxcid_conf->bit_cuPortId = 12;
eaxcid_conf->bit_bandSectorId = 10;
eaxcid_conf->bit_ccId = 8;
eaxcid_conf->bit_ruPortId = 0;
break;
default:
return false;
}
return true;
}
#else
static bool set_fh_eaxcid_conf(struct xran_eaxcid_config *eaxcid_conf, enum xran_category cat)
{
/* CUS specification, section 3.1.3.1.6
@@ -540,8 +576,8 @@ static bool set_fh_eaxcid_conf(struct xran_eaxcid_config *eaxcid_conf, enum xran
eaxcid_conf->mask_ccId = 0x00f0;
eaxcid_conf->mask_ruPortId = 0x000f;
eaxcid_conf->bit_cuPortId = 0;
eaxcid_conf->bit_bandSectorId = 0; // total number of band sectors supported by O-RU should be retreived by M-plane - <max-num-bands> && <max-num-sectors>
eaxcid_conf->bit_ccId = 0; // total number of CC supported by O-RU should be retreived by M-plane - <max-num-component-carriers>
eaxcid_conf->bit_bandSectorId = 0; // total number of band sectors supported by O-RU should be retrieved by M-plane - <max-num-bands> && <max-num-sectors>
eaxcid_conf->bit_ccId = 0; // total number of CC supported by O-RU should be retrieved by M-plane - <max-num-component-carriers>
eaxcid_conf->bit_ruPortId = 0;
break;
case XRAN_CATEGORY_B:
@@ -560,6 +596,7 @@ static bool set_fh_eaxcid_conf(struct xran_eaxcid_config *eaxcid_conf, enum xran
return true;
}
#endif
uint8_t *get_ether_addr(const char *addr, struct rte_ether_addr *ether_addr)
{
@@ -573,7 +610,7 @@ uint8_t *get_ether_addr(const char *addr, struct rte_ether_addr *ether_addr)
return NULL;
}
static bool set_fh_init(struct xran_fh_init *fh_init, enum xran_category xran_cat)
static bool set_fh_init(void *mplane_api, struct xran_fh_init *fh_init, enum xran_category xran_cat)
{
memset(fh_init, 0, sizeof(*fh_init));
@@ -606,30 +643,46 @@ static bool set_fh_init(struct xran_fh_init *fh_init, enum xran_category xran_ca
const int nfh = sizeofArray(FHconfigs);
config_getlist(config_get_if(), &FH_ConfigList, FHconfigs, nfh, aprefix);
int num_rus = FH_ConfigList.numelt; // based on the number of fh_config sections -> number of RUs
#ifdef OAI_MPLANE
ru_session_list_t *ru_session_list = (ru_session_list_t *)mplane_api;
int num_rus = ru_session_list->num_rus;
fh_init->xran_ports = num_rus; // since we use xran as O-DU, xran_ports is set to the number of RUs
if (!set_fh_io_cfg(&fh_init->io_cfg, fhip, nump, num_rus))
return false;
if (!set_fh_eaxcid_conf_mplane(&fh_init->eAxCId_conf, xran_cat, ru_session_list))
return false;
/* maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be
communicated in a single xRAN network layer transaction. Supported 1500 bytes and 9600 bytes (Jumbo Frame);
xran only checks if (MTU <= 1500), therefore setting any value > 1500, xran assumes 9600 value is used */
fh_init->mtu = ru_session_list->ru_session[0].xran_mplane.mtu; // we suppose that each RU supports the same MTU size
int num_ru_addr = (fh_init->io_cfg.one_vf_cu_plane) ? num_rus : 2*num_rus;
fh_init->p_o_ru_addr = calloc(num_ru_addr, sizeof(struct rte_ether_addr));
AssertFatal(fh_init->p_o_ru_addr != NULL, "out of memory\n");
for (int i = 0; i < num_rus; i++) {
struct rte_ether_addr *ea = (struct rte_ether_addr *)fh_init->p_o_ru_addr;
for (int j = 0; j < num_ru_addr/num_rus; j++) {
if (get_ether_addr(ru_session_list->ru_session[i].xran_mplane.ru_mac_addr, &ea[i+j]) == NULL) {
printf("could not read ethernet address '%s' for RU!\n", ru_session_list->ru_session[i].xran_mplane.ru_mac_addr);
return false;
}
}
}
#else
int num_rus = FH_ConfigList.numelt; // based on the number of fh_config sections -> number of RUs
fh_init->xran_ports = num_rus; // since we use xran as O-DU, xran_ports is set to the number of RUs
if (!set_fh_io_cfg(&fh_init->io_cfg, fhip, nump, num_rus))
return false;
if (!set_fh_eaxcid_conf(&fh_init->eAxCId_conf, xran_cat))
return false;
fh_init->xran_ports = num_rus; // since we use xran as O-DU, xran_ports is set to the number of RUs
fh_init->dpdkBasebandFecMode = 0; // DPDK Baseband FEC device mode (0-SW, 1-HW); not used in xran
fh_init->dpdkBasebandDevice = NULL; // DPDK Baseband device address; not used in xran
/* used to specify a unique prefix for shared memory, and files created by multiple DPDK processes;
it is necessary */
fh_init->filePrefix = strdup(*gpd(fhip, nump, ORAN_CONFIG_FILE_PREFIX)->strptr);
/* maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be
communicated in a single xRAN network layer transaction. Supported 1500 bytes and 9600 bytes (Jumbo Frame);
xran only checks if (MTU <= 1500), therefore setting any value > 1500, xran assumes 9600 value is used */
fh_init->mtu = *gpd(fhip, nump, ORAN_CONFIG_MTU)->uptr;
fh_init->p_o_du_addr = NULL; // DPDK retreives DU MAC address within the xran library with rte_eth_macaddr_get() function
int num_ru_addr = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->numelt;
fh_init->p_o_ru_addr = calloc(num_ru_addr, sizeof(struct rte_ether_addr));
char **ru_addrs = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->strlistptr;
AssertFatal(fh_init->p_o_ru_addr != NULL, "out of memory\n");
char **ru_addrs = gpd(fhip, nump, ORAN_CONFIG_RU_ADDR)->strlistptr;
for (int i = 0; i < num_ru_addr; ++i) {
struct rte_ether_addr *ea = (struct rte_ether_addr *)fh_init->p_o_ru_addr;
if (get_ether_addr(ru_addrs[i], &ea[i]) == NULL) {
@@ -637,7 +690,14 @@ static bool set_fh_init(struct xran_fh_init *fh_init, enum xran_category xran_ca
return false;
}
}
#endif
fh_init->dpdkBasebandFecMode = 0; // DPDK Baseband FEC device mode (0-SW, 1-HW); not used in xran
fh_init->dpdkBasebandDevice = NULL; // DPDK Baseband device address; not used in xran
/* used to specify a unique prefix for shared memory, and files created by multiple DPDK processes;
it is necessary */
fh_init->filePrefix = strdup(*gpd(fhip, nump, ORAN_CONFIG_FILE_PREFIX)->strptr);
fh_init->p_o_du_addr = NULL; // DPDK retreives DU MAC address within the xran library with rte_eth_macaddr_get() function
fh_init->totalBfWeights = 0; // only used if id = O_RU (for emulation); C-plane extension types; section 5.4.6 of CUS spec
#ifdef F_RELEASE
@@ -656,7 +716,8 @@ static bool set_fh_init(struct xran_fh_init *fh_init, enum xran_category xran_ca
// this is a hack
int g_kbar;
static bool set_fh_prach_config(const openair0_config_t *oai0,
static bool set_fh_prach_config(void *mplane_api,
const openair0_config_t *oai0,
const uint32_t max_num_ant,
const paramdef_t *prachp,
int nprach,
@@ -692,8 +753,13 @@ static bool set_fh_prach_config(const openair0_config_t *oai0,
xran assumes PRACH offset >= max(Ntx, Nrx). However, we made a workaround that xran supports PRACH eAxC IDs same as PUSCH eAxC IDs.
This is achieved with is_prach and filter_id parameters in the patch.
Please note that this approach only applies to the RUs that support this functionality, e.g. LITEON RU. */
#ifdef OAI_MPLANE
xran_mplane_t *xran_mplane = (xran_mplane_t *)mplane_api;
prach_config->eAxC_offset = xran_mplane->prach_offset;
#else
uint8_t offset = *gpd(prachp, nprach, ORAN_PRACH_CONFIG_EAXC_OFFSET)->u8ptr;
prach_config->eAxC_offset = (offset != 0) ? offset : max_num_ant;
#endif
g_kbar = *gpd(prachp, nprach, ORAN_PRACH_CONFIG_KBAR)->uptr;
@@ -722,17 +788,24 @@ static bool set_fh_frame_config(const openair0_config_t *oai0, struct xran_frame
return true;
}
static bool set_fh_ru_config(const paramdef_t *rup, uint16_t fftSize, int nru, enum xran_category xran_cat, struct xran_ru_config *ru_config)
static bool set_fh_ru_config(void *mplane_api, const paramdef_t *rup, uint16_t fftSize, int nru, enum xran_category xran_cat, struct xran_ru_config *ru_config)
{
ru_config->xranTech = XRAN_RAN_5GNR; // 5GNR or LTE
ru_config->xranCat = xran_cat; // mode: Catergory A or Category B
ru_config->xranCompHdrType = XRAN_COMP_HDR_TYPE_STATIC; // dynamic or static udCompHdr handling
#ifdef OAI_MPLANE
xran_mplane_t *xran_mplane = (xran_mplane_t *)mplane_api;
ru_config->iqWidth = xran_mplane->iq_width;
ru_config->iqWidth_PRACH = xran_mplane->iq_width;
#else
ru_config->iqWidth = *gpd(rup, nru, ORAN_RU_CONFIG_IQWIDTH)->uptr; // IQ bit width
AssertFatal(ru_config->iqWidth <= 16, "IQ Width cannot be > 16!\n");
ru_config->compMeth = ru_config->iqWidth < 16 ? XRAN_COMPMETHOD_BLKFLOAT : XRAN_COMPMETHOD_NONE; // compression method
ru_config->iqWidth_PRACH = *gpd(rup, nru, ORAN_RU_CONFIG_IQWIDTH_PRACH)->uptr; // IQ bit width for PRACH
AssertFatal(ru_config->iqWidth_PRACH <= 16, "IQ Width for PRACH cannot be > 16!\n");
#endif
ru_config->compMeth = ru_config->iqWidth < 16 ? XRAN_COMPMETHOD_BLKFLOAT : XRAN_COMPMETHOD_NONE; // compression method
ru_config->compMeth_PRACH = ru_config->iqWidth_PRACH < 16 ? XRAN_COMPMETHOD_BLKFLOAT : XRAN_COMPMETHOD_NONE; // compression method for PRACH
AssertFatal(fftSize > 0, "FFT size cannot be 0\n");
ru_config->fftSize = fftSize; // FFT Size
ru_config->byteOrder = XRAN_NE_BE_BYTE_ORDER; // order of bytes in int16_t in buffer; big or little endian
@@ -757,7 +830,7 @@ static bool set_maxmin_pd(const paramdef_t *pd, int num, const char *name, uint1
return true;
}
static bool set_fh_config(int ru_idx, int num_rus, enum xran_category xran_cat, const openair0_config_t *oai0, struct xran_fh_config *fh_config)
static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_category xran_cat, const openair0_config_t *oai0, struct xran_fh_config *fh_config)
{
AssertFatal(num_rus == 1 || num_rus == 2, "only support 1 or 2 RUs as of now\n");
AssertFatal(ru_idx < num_rus, "illegal ru_idx %d: must be < %d\n", ru_idx, num_rus);
@@ -839,22 +912,22 @@ static bool set_fh_config(int ru_idx, int num_rus, enum xran_category xran_cat,
#endif
fh_config->puschMaskEnable = 0; // enable PUSCH mask; only used if id = O_RU
fh_config->puschMaskSlot = 0; // specific which slot PUSCH channel masked; only used if id = O_RU
fh_config->cp_vlan_tag = *gpd(fhp, nfh, ORAN_FH_CONFIG_CP_VLAN_TAG)->uptr; // C-plane VLAN tag; not used in xran; needed for M-plane
fh_config->up_vlan_tag = *gpd(fhp, nfh, ORAN_FH_CONFIG_UP_VLAN_TAG)->uptr; // U-plane VLAN tag; not used in xran; needed for M-plane
fh_config->cp_vlan_tag = 0; // C-plane VLAN tag; not used in xran; needed for M-plane
fh_config->up_vlan_tag = 0; // U-plane VLAN tag; not used in xran; needed for M-plane
fh_config->debugStop = 0; // enable auto stop; only used if id = O_RU
fh_config->debugStopCount = 0; // enable auto stop after number of Tx packets; not used in xran
fh_config->DynamicSectionEna = 0; // enable dynamic C-Plane section allocation
fh_config->GPS_Alpha = 0; // refers to alpha as defined in section 9.7.2 of ORAN spec. this value should be alpha*(1/1.2288ns), range 0 - 1e7 (ns); offset_nsec = (pConf->GPS_Beta - offset_sec * 100) * 1e7 + pConf->GPS_Alpha
fh_config->GPS_Beta = 0; // beta value as defined in section 9.7.2 of ORAN spec. range -32767 ~ +32767; offset_sec = pConf->GPS_Beta / 100
if (!set_fh_prach_config(oai0, fh_config->neAxc, prachp, nprach, &fh_config->prach_conf))
if (!set_fh_prach_config(mplane_api, oai0, fh_config->neAxc, prachp, nprach, &fh_config->prach_conf))
return false;
/* SRS only used if XRAN_CATEGORY_B
Note: srs_config->eAxC_offset >= prach_config->eAxC_offset + PRACH */
// fh_config->srs_conf = {0};
if (!set_fh_frame_config(oai0, &fh_config->frame_conf))
return false;
if (!set_fh_ru_config(rup, oai0->split7.fftSize, nru, xran_cat, &fh_config->ru_conf))
if (!set_fh_ru_config(mplane_api, rup, oai0->split7.fftSize, nru, xran_cat, &fh_config->ru_conf))
return false;
fh_config->bbdev_enc = NULL; // call back to poll BBDev encoder
@@ -889,24 +962,35 @@ static bool set_fh_config(int ru_idx, int num_rus, enum xran_category xran_cat,
return true;
}
bool get_xran_config(const openair0_config_t *openair0_cfg, struct xran_fh_init *fh_init, struct xran_fh_config *fh_config)
bool get_xran_config(void *mplane_api, const struct openair0_config *openair0_cfg, struct xran_fh_init *fh_init, struct xran_fh_config *fh_config)
{
/* This xran integration release is only valid for O-RU CAT A.
Therefore, each FH parameter is hardcoded to CAT A.
If you are interested in CAT B, please be aware that parameters of fh_init and fh_config structs must be modified accordingly. */
enum xran_category xran_cat = XRAN_CATEGORY_A;
if (!set_fh_init(fh_init, xran_cat)) {
if (!set_fh_init(mplane_api, fh_init, xran_cat)) {
printf("could not read FHI 7.2/ORAN config\n");
return false;
}
#ifdef OAI_MPLANE
ru_session_list_t *ru_session_list = (ru_session_list_t *)mplane_api;
for (int32_t o_xu_id = 0; o_xu_id < fh_init->xran_ports; o_xu_id++) {
if (!set_fh_config(o_xu_id, fh_init->xran_ports, xran_cat, openair0_cfg, &fh_config[o_xu_id])) {
xran_mplane_t *xran_mplane = &ru_session_list->ru_session[o_xu_id].xran_mplane;
if (!set_fh_config(xran_mplane, o_xu_id, fh_init->xran_ports, xran_cat, openair0_cfg, &fh_config[o_xu_id])) {
MP_LOG_I("could not read FHI 7.2/RU-specific config\n");
return false;
}
}
#else
for (int32_t o_xu_id = 0; o_xu_id < fh_init->xran_ports; o_xu_id++) {
if (!set_fh_config(NULL, o_xu_id, fh_init->xran_ports, xran_cat, openair0_cfg, &fh_config[o_xu_id])) {
printf("could not read FHI 7.2/RU-specific config\n");
return false;
}
}
#endif
return true;
}
+1 -1
View File
@@ -29,7 +29,7 @@ struct xran_fh_init;
struct xran_fh_config;
struct openair0_config;
bool get_xran_config(const struct openair0_config *openair0_cfg, struct xran_fh_init *fh_init, struct xran_fh_config *fh_config);
bool get_xran_config(void *mplane_api, const struct openair0_config *openair0_cfg, struct xran_fh_init *fh_init, struct xran_fh_config *fh_config);
void print_fh_init(const struct xran_fh_init *fh_init);
void print_fh_config(const struct xran_fh_config *fh_config);
+1 -1
View File
@@ -443,7 +443,7 @@ int *oai_oran_initialize(struct xran_fh_init *xran_fh_init, struct xran_fh_confi
pi->prach_tag = tag;
pi->pusch_tag = tag;
#ifdef E_RELEASE
LOG_W(PHY, "Please be aware that E release support will be removed in the future. Consider switching to F release.\n");
LOG_W(HW, "Please be aware that E release support will be removed in the future. Consider switching to F release.\n");
oran_allocate_buffers(gxran_handle, o_xu_id, 1, pi, &xran_fh_config[o_xu_id]);
#elif defined F_RELEASE
oran_allocate_buffers(gxran_handle, o_xu_id, 1, pi, xran_fh_init->mtu, &xran_fh_config[o_xu_id]);
+17 -9
View File
@@ -31,8 +31,12 @@
#define ORAN_CONFIG_SYSTEM_CORE "system_core"
#define ORAN_CONFIG_IO_CORE "io_core"
#define ORAN_CONFIG_WORKER_CORES "worker_cores"
#define ORAN_CONFIG_RU_ADDR "ru_addr"
#define ORAN_CONFIG_MTU "mtu"
#define ORAN_CONFIG_DU_KEYPAIR "du_key_pair" // only needed for M-plane
#define ORAN_CONFIG_DU_ADDR "du_addr" // only needed for M-plane
#define ORAN_CONFIG_VLAN_TAG "vlan_tag" // only needed for M-plane
#define ORAN_CONFIG_RU_ADDR "ru_addr" // not needed if M-plane used
#define ORAN_CONFIG_RU_IP_ADDR "ru_ip_addr" // only needed for M-plane
#define ORAN_CONFIG_MTU "mtu" // not needed if M-plane used
#define ORAN_CONFIG_FILE_PREFIX "file_prefix"
#define ORAN_CONFIG_NETHPERPORT "eth_lines"
#define ORAN_CONFIG_NETHSPEED "eth_speed"
@@ -48,7 +52,11 @@
{ORAN_CONFIG_SYSTEM_CORE, "DPDK control threads core\n", PARAMFLAG_MANDATORY, .iptr=NULL, .defintval=0, TYPE_INT, 0}, \
{ORAN_CONFIG_IO_CORE, "DPDK Core used for IO\n", PARAMFLAG_MANDATORY, .iptr=NULL, .defintval=4, TYPE_INT, 0}, \
{ORAN_CONFIG_WORKER_CORES, "CPU Cores to use for workers\n", PARAMFLAG_MANDATORY, .uptr=NULL, .defintarrayval=NULL,TYPE_UINTARRAY, 0}, \
{ORAN_CONFIG_DU_KEYPAIR, "DU keypair for RU authentication\n", PARAMFLAG_MANDATORY, .strlistptr=NULL, .defstrlistval=NULL, TYPE_STRINGLIST, 0}, \
{ORAN_CONFIG_DU_ADDR, "Ether addr of DU\n", PARAMFLAG_MANDATORY, .strlistptr=NULL, .defstrlistval=NULL, TYPE_STRINGLIST, 0}, \
{ORAN_CONFIG_VLAN_TAG, "VLAN tag\n", PARAMFLAG_MANDATORY, .iptr=NULL, .defintarrayval=0, TYPE_INTARRAY, 0}, \
{ORAN_CONFIG_RU_ADDR, "Ether addr of RU\n", PARAMFLAG_MANDATORY, .strlistptr=NULL, .defstrlistval=NULL, TYPE_STRINGLIST, 0}, \
{ORAN_CONFIG_RU_IP_ADDR, "IP addr of RU\n", PARAMFLAG_MANDATORY, .strlistptr=NULL, .defstrlistval=NULL, TYPE_STRINGLIST, 0}, \
{ORAN_CONFIG_MTU, "MTU of Eth interface\n", 0, .uptr=NULL, .defuintval=1500, TYPE_UINT, 0}, \
{ORAN_CONFIG_FILE_PREFIX, "DPDK file-prefix\n", 0, .strptr=NULL, .defstrval="wls_0", TYPE_STRING, 0}, \
{ORAN_CONFIG_NETHPERPORT, "number of links per port\n", 0, .uptr=NULL, .defuintval=1, TYPE_UINT, 0}, \
@@ -70,6 +78,10 @@
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s5 = { NULL } }, \
{ .s3a = { config_checkstr_assign_integer, \
{"PA", "VA"}, {0, 1}, 2} }, \
{ .s5 = { NULL } }, \
@@ -84,8 +96,6 @@
#define ORAN_FH_CONFIG_T1A_CP_UL "T1a_cp_ul"
#define ORAN_FH_CONFIG_T1A_UP "T1a_up"
#define ORAN_FH_CONFIG_TA4 "Ta4"
#define ORAN_FH_CONFIG_CP_VLAN_TAG "cp_vlan_tag"
#define ORAN_FH_CONFIG_UP_VLAN_TAG "up_vlan_tag"
#define ORAN_FH_HLP_CPLT " parameter of RU in list form (Min&Max, length 2!)\n"
@@ -95,15 +105,13 @@
{ORAN_FH_CONFIG_T1A_CP_UL, "T1a_cp_ul" ORAN_FH_HLP_CPLT, PARAMFLAG_MANDATORY, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_T1A_UP, "T1a_up" ORAN_FH_HLP_CPLT, PARAMFLAG_MANDATORY, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_TA4, "Ta4" ORAN_FH_HLP_CPLT, PARAMFLAG_MANDATORY, .uptr=NULL, .defintarrayval=0, TYPE_UINTARRAY, 0}, \
{ORAN_FH_CONFIG_CP_VLAN_TAG, "not used by xran", 0, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
{ORAN_FH_CONFIG_UP_VLAN_TAG, "not used by xran", 0, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \
}
// clang-format on
#define CONFIG_STRING_ORAN_RU "ru_config"
#define ORAN_RU_CONFIG_IQWIDTH "iq_width"
#define ORAN_RU_CONFIG_IQWIDTH_PRACH "iq_width_prach"
#define ORAN_RU_CONFIG_IQWIDTH "iq_width" // not needed if M-plane used
#define ORAN_RU_CONFIG_IQWIDTH_PRACH "iq_width_prach" // not needed if M-plane used
// clang-format off
#define ORAN_RU_DESC {\
@@ -114,7 +122,7 @@
#define CONFIG_STRING_ORAN_PRACH "prach_config"
#define ORAN_PRACH_CONFIG_EAXC_OFFSET "eAxC_offset"
#define ORAN_PRACH_CONFIG_EAXC_OFFSET "eAxC_offset" // not needed if M-plane used
#define ORAN_PRACH_CONFIG_KBAR "kbar"
// clang-format off
+90 -37
View File
@@ -39,11 +39,17 @@
// line and the use of VERSIONX further below. It is relative to phy/fhi_lib/lib/api
#include "../../app/src/common.h"
#ifdef OAI_MPLANE
#include "mplane/init-mplane.h"
#include "mplane/connect-mplane.h"
#endif
typedef struct {
eth_state_t e;
rru_config_msg_type_t last_msg;
int capabilities_sent;
void *oran_priv;
void *mplane_priv;
} oran_eth_state_t;
notifiedFIFO_t oran_sync_fifo;
@@ -76,6 +82,10 @@ int trx_oran_stop(openair0_device *device)
printf("ORAN: %s\n", __FUNCTION__);
oran_eth_state_t *s = device->priv;
xran_stop(s->oran_priv);
#ifdef OAI_MPLANE
printf("[MPLANE] Stopping M-plane.\n");
disconnect_mplane(s->mplane_priv);
#endif
return (0);
}
@@ -204,11 +214,11 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot)
RU_proc_t *proc = &ru->proc;
int f, sl;
LOG_D(PHY, "Read rxdataF %p,%p\n", ru_info.rxdataF[0], ru_info.rxdataF[1]);
LOG_D(HW, "Read rxdataF %p,%p\n", ru_info.rxdataF[0], ru_info.rxdataF[1]);
start_meas(&ru->rx_fhaul);
int ret = xran_fh_rx_read_slot(&ru_info, &f, &sl);
stop_meas(&ru->rx_fhaul);
LOG_D(PHY, "Read %d.%d rxdataF %p,%p\n", f, sl, ru_info.rxdataF[0], ru_info.rxdataF[1]);
LOG_D(HW, "Read %d.%d rxdataF %p,%p\n", f, sl, ru_info.rxdataF[0], ru_info.rxdataF[1]);
if (ret != 0) {
printf("ORAN: %d.%d ORAN_fh_if4p5_south_in ERROR in RX function \n", f, sl);
}
@@ -221,7 +231,7 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot)
if (proc->first_rx == 0) {
if (proc->tti_rx != *slot) {
LOG_E(PHY,
LOG_E(HW,
"Received Time doesn't correspond to the time we think it is (slot mismatch, received %d.%d, expected %d.%d)\n",
proc->frame_rx,
proc->tti_rx,
@@ -231,7 +241,7 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot)
}
if (proc->frame_rx != *frame) {
LOG_E(PHY,
LOG_E(HW,
"Received Time doesn't correspond to the time we think it is (frame mismatch, %d.%d , expected %d.%d)\n",
proc->frame_rx,
proc->tti_rx,
@@ -241,10 +251,10 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot)
}
} else {
proc->first_rx = 0;
LOG_I(PHY, "before adjusting, OAI: frame=%d slot=%d, XRAN: frame=%d slot=%d\n", *frame, *slot, proc->frame_rx, proc->tti_rx);
LOG_I(HW, "before adjusting, OAI: frame=%d slot=%d, XRAN: frame=%d slot=%d\n", *frame, *slot, proc->frame_rx, proc->tti_rx);
*frame = proc->frame_rx;
*slot = proc->tti_rx;
LOG_I(PHY, "After adjusting, OAI: frame=%d slot=%d, XRAN: frame=%d slot=%d\n", *frame, *slot, proc->frame_rx, proc->tti_rx);
LOG_I(HW, "After adjusting, OAI: frame=%d slot=%d, XRAN: frame=%d slot=%d\n", *frame, *slot, proc->frame_rx, proc->tti_rx);
}
}
@@ -279,7 +289,80 @@ __attribute__((__visibility__("default"))) int transport_init(openair0_device *d
openair0_config_t *openair0_cfg,
eth_params_t *eth_params)
{
oran_eth_state_t *eth;
oran_eth_state_t *eth = calloc_or_fail(1, sizeof(*eth));
struct xran_fh_init fh_init = {0};
struct xran_fh_config fh_config[XRAN_PORTS_NUM] = {0};
bool success = false;
#ifdef OAI_MPLANE
ru_session_list_t ru_session_list = {0};
success = init_mplane(&ru_session_list);
AssertFatal(success, "[MPLANE] Cannot initialize M-plane.\n");
bool ru_configured[ru_session_list.num_rus];
for (size_t i = 0; i < ru_session_list.num_rus; i++) {
ru_session_t *ru_session = &ru_session_list.ru_session[i];
ru_configured[i] = connect_mplane(ru_session);
if (!ru_configured[i]) {
continue;
}
ru_configured[i] = manage_ru(ru_session, openair0_cfg, ru_session_list.num_rus);
}
bool all_ok = true;
bool ru_ready[ru_session_list.num_rus];
for (size_t i = 0; i < ru_session_list.num_rus; i++) {
if (!ru_configured[i]) {
MP_LOG_I("RU with IP %s couldn't be configured.\n", ru_session_list.ru_session[i].ru_ip_add);
all_ok = false;
}
ru_ready[i] = false;
}
if (!all_ok) {
disconnect_mplane((void *)&ru_session_list);
AssertFatal(false, "[MPLANE] Stopping M-plane.\n");
}
while (true) {
sleep(1);
bool all_rus_ready = true;
for (int i = 0; i < ru_session_list.num_rus; i++) {
ru_session_t *ru_session = &ru_session_list.ru_session[i];
if (!ru_ready[i] && ru_session->ru_notif.config_change && ru_session->ru_notif.rx_carrier_state && ru_session->ru_notif.tx_carrier_state) {
MP_LOG_I("RU \"%s\" is now ready.\n", ru_session->ru_ip_add);
ru_ready[i] = true;
} else {
all_rus_ready = false;
break;
}
}
if (all_rus_ready) {
break;
}
}
eth->mplane_priv = (void *)&ru_session_list;
success = get_xran_config((void *)&ru_session_list, openair0_cfg, &fh_init, fh_config);
AssertFatal(success, "[MPLANE] Cannot configure xran with M-plane info.\n");
#else
success = get_xran_config(NULL, openair0_cfg, &fh_init, fh_config);
AssertFatal(success, "cannot get configuration for xran\n");
#endif
LOG_I(HW, "Initializing O-RAN 7.2 FH interface through xran library (compiled against headers of %s)\n", VERSIONX);
eth->oran_priv = oai_oran_initialize(&fh_init, fh_config);
AssertFatal(eth->oran_priv != NULL, "can not initialize fronthaul");
// create message queues for ORAN sync
initNotifiedFIFO(&oran_sync_fifo);
eth->e.flags = ETH_RAW_IF4p5_MODE;
eth->e.compression = NO_COMPRESS;
eth->e.if_name = eth_params->local_if_name;
eth->last_msg = (rru_config_msg_type_t)-1;
device->Mod_id = 0;
device->transp_type = ETHERNET_TP;
@@ -290,43 +373,13 @@ __attribute__((__visibility__("default"))) int transport_init(openair0_device *d
device->trx_stop_func = trx_oran_stop;
device->trx_set_freq_func = trx_oran_set_freq;
device->trx_set_gains_func = trx_oran_set_gains;
device->trx_write_func = trx_oran_write_raw;
device->trx_read_func = trx_oran_read_raw;
device->trx_ctlsend_func = trx_oran_ctlsend;
device->trx_ctlrecv_func = trx_oran_ctlrecv;
device->get_internal_parameter = get_internal_parameter;
eth = (oran_eth_state_t *)calloc(1, sizeof(oran_eth_state_t));
if (eth == NULL) {
AssertFatal(0 == 1, "out of memory\n");
}
eth->e.flags = ETH_RAW_IF4p5_MODE;
eth->e.compression = NO_COMPRESS;
eth->e.if_name = eth_params->local_if_name;
eth->oran_priv = NULL; // define_oran_pointer();
device->priv = eth;
device->openair0_cfg = &openair0_cfg[0];
eth->last_msg = (rru_config_msg_type_t)-1;
LOG_I(HW, "Initializing O-RAN 7.2 FH interface through xran library (compiled against headers of %s)\n", VERSIONX);
initNotifiedFIFO(&oran_sync_fifo);
struct xran_fh_init fh_init = {0};
struct xran_fh_config fh_config[XRAN_PORTS_NUM] = {0};
#ifndef OAI_MPLANE_SUPPORT
bool success = get_xran_config(openair0_cfg, &fh_init, fh_config);
AssertFatal(success, "cannot get configuration for xran\n");
#else
/* TODO: M-plane integration */
#endif
eth->oran_priv = oai_oran_initialize(&fh_init, fh_config);
AssertFatal(eth->oran_priv != NULL, "can not initialize fronthaul");
// create message queues for ORAN sync
return 0;
}
+1 -1
View File
@@ -1004,7 +1004,7 @@ static int rfsimulator_read(openair0_device *device, openair0_timestamp *ptimest
t->nextRxTstamp + nsamps);
flushInput(t, 3, nsamps);
}
if (t->hanging_workaround && loops++ > 10 && t->role == SIMU_ROLE_SERVER) {
if (t->hanging_workaround && loops++ > 10 && t->role == SIMU_ROLE_SERVER && nb_ue > 1) {
// Just start producing samples. The clients will catch up.
have_to_wait = false;
LOG_W(HW,
+4
View File
@@ -0,0 +1,4 @@
add_library(vrtsim MODULE vrtsim.c noise_device.c)
set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor)
add_dependencies(vrtsim generate_T)
+25
View File
@@ -0,0 +1,25 @@
# Overview
This implements a shared memory realtime and near-realtime radio interface.
This is performed using `shm_td_iq_channel` which handles the shared memory
interface.
# Limitations
- Only 1gNB to 1nrUE: gNB as a server, UE as a client.
- Server only accepts connections during device bringup. This means that
restarting the client will not make it reconnect, the server has to be
restarted as well.
# Usage
On the UE and gNB: use `device.name vrtsim` command line argument.
Additionally on gNB use `vrtsim.role server` and optionally
`vrtsim.timescale <timescale>` to set the timescale. Timescale 1.0
is the default and means realtime.
Channel modelling can be enabled by adding `vrtsim.chanmod 1` to the
command line and should work the same as channel modelling in rfsimulator,
see rfsimulator [documentation](../rfsimulator/README.md), provided that your
CPU is fast enough.
+90
View File
@@ -0,0 +1,90 @@
/*
* 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 <pthread.h>
#include <errno.h>
#include "noise_device.h"
// Use block size equal to cache size
#define BLOCK_SIZE (64 / sizeof(float)) // 4 bytes per float
// Use a power of 2 for block size length
#define NUM_BLOCKS (8192)
#define NOISE_VECTOR_LENGTH (BLOCK_SIZE * NUM_BLOCKS)
static struct {
pthread_t noise_device_thread;
float noise[NOISE_VECTOR_LENGTH] __attribute__((aligned(64)));
float noise_power;
bool running;
} state;
static uint32_t jz, jsr = 123456789;
static uint32_t random_uint(void)
{
return SHR3;
}
static void generate_one_block_noise(int write_block_index, float noise_power)
{
// Generate noise vector
float block[BLOCK_SIZE];
for (int i = 0; i < BLOCK_SIZE; i++) {
block[i] = noise_power * gaussZiggurat(0, 1);
}
// Write block to noise vector
memcpy(state.noise + write_block_index * BLOCK_SIZE, block, sizeof(block));
}
void *noise_device_thread_function(void *arg)
{
while (state.running) {
// Generate noise vector
generate_one_block_noise(random_uint() % NUM_BLOCKS, state.noise_power);
usleep(10000);
}
return NULL;
}
void init_noise_device(float noise_power)
{
for (int i = 0; i < NUM_BLOCKS; i++) {
generate_one_block_noise(i, noise_power);
}
int ret = pthread_create(&state.noise_device_thread, NULL, noise_device_thread_function, NULL);
AssertFatal(ret == 0, "pthread_create failed: %d, errno %d (%s)\n", ret, errno, strerror(errno));
}
void free_noise_device(void)
{
state.running = false;
pthread_join(state.noise_device_thread, NULL);
}
void get_noise_vector(float *noise_vector, int length)
{
int start_block = random_uint() % NUM_BLOCKS;
while (length > 0) {
int copy_size = min(length, (NUM_BLOCKS - start_block) * BLOCK_SIZE);
memcpy(noise_vector, &state.noise[start_block * BLOCK_SIZE], copy_size * sizeof(float));
start_block = 0;
length -= copy_size;
noise_vector += copy_size;
}
}
+31
View File
@@ -0,0 +1,31 @@
/*
* 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 NOISE_DEVICE_H
#define NOISE_DEVICE_H
#include "SIMULATION/TOOLS/sim.h"
void init_noise_device(float noise_power);
void free_noise_device(void);
void get_noise_vector(float *noise_vector, int length);
#endif
+598
View File
@@ -0,0 +1,598 @@
/*
* 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 <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdbool.h>
#include <errno.h>
#include <sys/epoll.h>
#include <netdb.h>
#include <pthread.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <common/utils/assertions.h>
#include <common/utils/LOG/log.h>
#include <common/utils/load_module_shlib.h>
#include <common/utils/telnetsrv/telnetsrv.h>
#include <common/config/config_userapi.h>
#include "common_lib.h"
#include "shm_td_iq_channel.h"
#include "SIMULATION/TOOLS/sim.h"
#include "actor.h"
#include "noise_device.h"
#include "simde/x86/avx512.h"
// Simulator role
typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role;
#define NUM_CHANMOD_THREADS 1
#define ROLE_CLIENT_STRING "client"
#define ROLE_SERVER_STRING "server"
#define VRTSIM_SECTION "vrtsim"
#define TIME_SCALE_HLP \
"sample time scale. 1.0 means realtime. Values > 1 mean faster than realtime. Values < 1 mean slower than realtime\n"
// clang-format off
#define VRTSIM_PARAMS_DESC \
{ \
{"channel_name", "shared memory channel name\n", 0, .strptr = &channel_name, .defstrval = "vrtsim_channel", TYPE_STRING, 0}, \
{"role", "either client or server\n", 0, .strptr = &role, .defstrval = ROLE_CLIENT_STRING, TYPE_STRING, 0}, \
{"timescale", TIME_SCALE_HLP, 0, .dblptr = &vrtsim_state->timescale, .defdblval = 1.0, TYPE_DOUBLE, 0}, \
{"chanmod", "Enable channel modelling", 0, .iptr = &vrtsim_state->chanmod, .defintval = 0, TYPE_INT, 0}, \
};
// clang-format on
typedef struct histogram_s {
uint64_t diff[30];
int num_samples;
int min_samples;
double range;
} histogram_t;
// Information about the peer
typedef struct peer_info_s {
int num_rx_antennas;
} peer_info_t;
typedef struct tx_timing_s {
uint64_t tx_samples_late;
uint64_t tx_early;
uint64_t tx_samples_total;
double average_tx_budget;
histogram_t tx_histogram;
} tx_timing_t;
typedef struct {
int role;
char channel_name[40];
ShmTDIQChannel *channel;
uint64_t last_received_sample;
pthread_t timing_thread;
bool run_timing_thread;
double timescale;
double sample_rate;
uint64_t rx_samples_late;
uint64_t rx_early;
uint64_t rx_samples_total;
tx_timing_t *tx_timing;
peer_info_t peer_info;
int chanmod;
double rx_freq;
double tx_bw;
int tx_num_channels;
int rx_num_channels;
channel_desc_t *channel_desc;
Actor_t *channel_modelling_actors;
} vrtsim_state_t;
static void histogram_add(histogram_t *histogram, double diff)
{
histogram->num_samples++;
if (histogram->num_samples >= histogram->min_samples) {
int bin = min(sizeofArray(histogram->diff) - 1, max(0, (int)(diff / histogram->range * sizeofArray(histogram->diff))));
histogram->diff[bin]++;
}
}
static void histogram_print(histogram_t *histogram)
{
LOG_I(HW, "VRTSIM: TX budget histogram: %d samples\n", histogram->num_samples);
float bin_size = histogram->range / sizeofArray(histogram->diff);
float bin_start = 0;
for (int i = 0; i < sizeofArray(histogram->diff); i++) {
LOG_I(HW, "Bin %d\t[%.1f - %.1fuS]:\t\t%lu\n", i, bin_start, bin_start + bin_size, histogram->diff[i]);
bin_start += bin_size;
}
}
static void histogram_merge(histogram_t *dest, histogram_t *src)
{
for (int i = 0; i < sizeofArray(dest->diff); i++) {
dest->diff[i] += src->diff[i];
}
dest->num_samples += src->num_samples;
}
static void load_channel_model(vrtsim_state_t *vrtsim_state)
{
load_channellist(vrtsim_state->tx_num_channels,
vrtsim_state->peer_info.num_rx_antennas,
vrtsim_state->sample_rate,
vrtsim_state->rx_freq,
vrtsim_state->tx_bw);
char *model_name = vrtsim_state->role == ROLE_CLIENT ? "client_tx_channel_model" : "server_tx_channel_model";
vrtsim_state->channel_desc = find_channel_desc_fromname(model_name);
random_channel(vrtsim_state->channel_desc, 0);
AssertFatal(vrtsim_state->channel_desc != NULL, "Could not find channel model %s\n", model_name);
}
static void vrtsim_readconfig(vrtsim_state_t *vrtsim_state)
{
char *channel_name = NULL;
char *role = NULL;
paramdef_t vrtsim_params[] = VRTSIM_PARAMS_DESC;
int ret = config_get(config_get_if(), vrtsim_params, sizeofArray(vrtsim_params), VRTSIM_SECTION);
AssertFatal(ret >= 0, "configuration couldn't be performed\n");
strncpy(vrtsim_state->channel_name, channel_name, sizeof(vrtsim_state->channel_name) - 1);
if (strncmp(role, ROLE_CLIENT_STRING, strlen(ROLE_CLIENT_STRING)) == 0) {
vrtsim_state->role = ROLE_CLIENT;
} else if (strncmp(role, ROLE_SERVER_STRING, strlen(ROLE_SERVER_STRING)) == 0) {
vrtsim_state->role = ROLE_SERVER;
} else {
AssertFatal(false, "Invalid role configuration\n");
}
}
static void *vrtsim_timing_job(void *arg)
{
vrtsim_state_t *vrtsim_state = arg;
struct timespec timestamp;
if (clock_gettime(CLOCK_REALTIME, &timestamp)) {
LOG_E(UTIL, "clock_gettime failed\n");
exit(1);
}
double leftover_samples = 0;
while (vrtsim_state->run_timing_thread) {
struct timespec current_time;
if (clock_gettime(CLOCK_REALTIME, &current_time)) {
LOG_E(UTIL, "clock_gettime failed\n");
exit(1);
}
uint64_t diff = (current_time.tv_sec - timestamp.tv_sec) * 1000000000 + (current_time.tv_nsec - timestamp.tv_nsec);
timestamp = current_time;
double samples_to_produce = vrtsim_state->sample_rate * vrtsim_state->timescale * diff / 1e9;
// Attempt to correct compounding rounding error
leftover_samples += samples_to_produce - (uint64_t)samples_to_produce;
if (leftover_samples > 1.0f) {
samples_to_produce += 1;
leftover_samples -= 1;
}
shm_td_iq_channel_produce_samples(vrtsim_state->channel, samples_to_produce);
usleep(1);
}
return 0;
}
/**
* @brief Exchanges peer information between the server and the client.
*
* This function sets up a Unix domain socket server, waits for a client to connect,
* and exchanges peer information (number of RX antennas) with the client.
*
* @param peer_info The peer information to send to the client.
* @return The peer information received from the client.
*
* @note This function will block until a client connects and the information is exchanged.
*/
static peer_info_t server_exchange_peer_info(peer_info_t peer_info)
{
int server_fd, client_fd;
// Create a Unix domain socket
server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
AssertFatal(server_fd >= 0, "socket() failed: errno: %d, %s\n", errno, strerror(errno));
// Bind the socket to a file path
struct sockaddr_un addr = {.sun_family = AF_UNIX};
strncpy(addr.sun_path, "/tmp/vrtsim_socket", sizeof(addr.sun_path) - 1);
unlink(addr.sun_path); // Ensure the path does not already exist
int ret = bind(server_fd, (struct sockaddr *)&addr, sizeof(addr));
AssertFatal(ret == 0, "bind() failed: errno: %d, %s\n", errno, strerror(errno));
// Listen for incoming connections
ret = listen(server_fd, 1);
AssertFatal(ret == 0, "listen() failed: errno: %d, %s\n", errno, strerror(errno));
// Accept a connection from a client
socklen_t addr_len = sizeof(addr);
client_fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
AssertFatal(client_fd >= 0, "accept() failed: errno: %d, %s\n", errno, strerror(errno));
// Send the antenna info to the server
ssize_t bytes_sent = send(client_fd, &peer_info, sizeof(peer_info), 0);
AssertFatal(bytes_sent == sizeof(peer_info), "send() failed: errno: %d, %s\n", errno, strerror(errno));
ssize_t bytes_received = recv(client_fd, &peer_info, sizeof(peer_info), 0);
AssertFatal(bytes_received == sizeof(peer_info), "recv() failed: errno: %d, %s\n", errno, strerror(errno));
LOG_I(HW, "Received client info: RX %d\n", peer_info.num_rx_antennas);
// Close the client and server sockets
close(client_fd);
close(server_fd);
return peer_info;
}
/**
* @brief Exchanges peer information between the client and the server.
*
* This function connects to a Unix domain socket server, exchanges peer information
* (number of RX antennas) with the server, and returns the peer information received
* from the server.
*
* @param peer_info The peer information to send to the server.
* @return The peer information received from the server.
*
* @note This function will block until the information is exchanged.
*/
static peer_info_t client_exchange_peer_info(peer_info_t peer_info)
{
int client_fd;
// Create a Unix domain socket
client_fd = socket(AF_UNIX, SOCK_STREAM, 0);
AssertFatal(client_fd >= 0, "socket() failed: errno: %d, %s\n", errno, strerror(errno));
// Connect to the server
struct sockaddr_un addr = {.sun_family = AF_UNIX};
strncpy(addr.sun_path, "/tmp/vrtsim_socket", sizeof(addr.sun_path) - 1);
int ret = -1;
int tries = 0;
while (ret != 0 && tries < 10) {
ret = connect(client_fd, (struct sockaddr *)&addr, sizeof(addr));
tries++;
sleep(1);
}
AssertFatal(ret == 0, "connect() failed: errno: %d, %s\n", errno, strerror(errno));
peer_info_t peer_info_received;
ssize_t bytes_received = recv(client_fd, &peer_info_received, sizeof(peer_info_received), 0);
AssertFatal(bytes_received == sizeof(peer_info_received), "recv() failed: errno: %d, %s\n", errno, strerror(errno));
// Send the antenna info to the server
ssize_t bytes_sent = send(client_fd, &peer_info, sizeof(peer_info), 0);
AssertFatal(bytes_sent == sizeof(peer_info), "send() failed: errno: %d, %s\n", errno, strerror(errno));
LOG_I(HW, "Received peer info: RX %d\n", peer_info_received.num_rx_antennas);
// Close the client socket
close(client_fd);
return peer_info_received;
}
static int vrtsim_connect(openair0_device *device)
{
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
// Exchange peer info
peer_info_t peer_info = {.num_rx_antennas = device->openair0_cfg[0].rx_num_channels};
if (vrtsim_state->role == ROLE_SERVER) {
vrtsim_state->peer_info = server_exchange_peer_info(peer_info);
} else {
vrtsim_state->peer_info = client_exchange_peer_info(peer_info);
}
// Handle channel modelling after number of RX antennas are known
int num_tx_stats = 1;
if (vrtsim_state->chanmod) {
vrtsim_state->channel_modelling_actors = calloc_or_fail(vrtsim_state->peer_info.num_rx_antennas, sizeof(Actor_t));
for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) {
init_actor(&vrtsim_state->channel_modelling_actors[i], "chanmod", -1);
}
load_channel_model(vrtsim_state);
num_tx_stats = vrtsim_state->peer_info.num_rx_antennas;
}
vrtsim_state->tx_timing = calloc_or_fail(num_tx_stats, sizeof(tx_timing_t));
for (int i = 0; i < num_tx_stats; i++) {
vrtsim_state->tx_timing[i].tx_histogram.min_samples = 100;
// Set the histogram range to 3000uS. Anything above that is not interesting
vrtsim_state->tx_timing[i].tx_histogram.range = 3000.0;
}
// Setup a shared memory channel
if (vrtsim_state->role == ROLE_SERVER) {
vrtsim_state->channel = shm_td_iq_channel_create(vrtsim_state->channel_name,
vrtsim_state->peer_info.num_rx_antennas,
device->openair0_cfg[0].rx_num_channels);
vrtsim_state->run_timing_thread = true;
while (!shm_td_iq_channel_is_connected(vrtsim_state->channel)) {
LOG_I(HW, "Waiting for client\n");
sleep(1);
}
int ret = pthread_create(&vrtsim_state->timing_thread, NULL, vrtsim_timing_job, vrtsim_state);
AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno));
} else {
vrtsim_state->channel = shm_td_iq_channel_connect(vrtsim_state->channel_name, 10);
}
return 0;
}
static int vrtsim_write_internal(vrtsim_state_t *vrtsim_state,
openair0_timestamp timestamp,
c16_t *samples,
int nsamps,
int aarx,
int flags,
int stats_index)
{
tx_timing_t *tx_timing = &vrtsim_state->tx_timing[stats_index];
uint64_t sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel);
int64_t diff = timestamp - sample;
double budget = diff / (vrtsim_state->sample_rate / 1e6);
tx_timing->average_tx_budget = .05 * budget + .95 * tx_timing->average_tx_budget;
histogram_add(&tx_timing->tx_histogram, budget);
int ret = shm_td_iq_channel_tx(vrtsim_state->channel, timestamp, nsamps, aarx, (sample_t *)samples);
if (ret == CHANNEL_ERROR_TOO_LATE) {
tx_timing->tx_samples_late += nsamps;
} else if (ret == CHANNEL_ERROR_TOO_EARLY) {
tx_timing->tx_early += 1;
}
tx_timing->tx_samples_total += nsamps;
return nsamps;
}
typedef struct {
vrtsim_state_t *vrtsim_state;
openair0_timestamp timestamp;
c16_t *samples[4];
int nsamps;
int nbAnt;
int flags;
int aarx;
} channel_modelling_args_t;
static void perform_channel_modelling(void *arg)
{
channel_modelling_args_t *channel_modelling_args = arg;
int nsamps = channel_modelling_args->nsamps;
int aarx = channel_modelling_args->aarx;
int nb_tx_ant = channel_modelling_args->nbAnt;
c16_t **input_samples = (c16_t **)channel_modelling_args->samples;
int aligned_nsamps = ceil_mod(nsamps, (512 / 8) / sizeof(cf_t));
cf_t samples[aligned_nsamps] __attribute__((aligned(64)));
// Apply noise from global settings
get_noise_vector((float *)samples, nsamps * 2);
channel_desc_t *channel_desc = channel_modelling_args->vrtsim_state->channel_desc;
const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0);
// Convert channel impulse response to float + apply pathloss
cf_t channel_impulse_response[nb_tx_ant][channel_desc->channel_length];
for (int aatx = 0; aatx < nb_tx_ant; aatx++) {
const struct complexd *channelModel = channel_desc->ch[aarx + (aatx * channel_desc->nb_rx)];
for (int i = 0; i < channel_desc->channel_length; i++) {
channel_impulse_response[aatx][i].r = channelModel[i].r * pathloss_linear;
channel_impulse_response[aatx][i].i = channelModel[i].i * pathloss_linear;
}
}
for (int aatx = 0; aatx < nb_tx_ant; aatx++) {
for (int i = 0; i < nsamps; i++) {
cf_t *impulse_response = channel_impulse_response[aatx];
for (int l = 0; l < channel_desc->channel_length; l++) {
int idx = i - l;
// TODO: Use AVX512 for this
// TODO: What are the previously sent samples (for impulse response)
if (idx >= 0) {
c16_t tx_input = input_samples[aatx][idx];
samples[i].r += tx_input.r * impulse_response[l].r - tx_input.i * impulse_response[l].i;
samples[i].i += tx_input.i * impulse_response[l].r + tx_input.r * impulse_response[l].i;
}
}
}
}
// Convert to c16_t
c16_t samples_out[aligned_nsamps] __attribute__((aligned(64)));
#if defined(__AVX512F__)
for (int i = 0; i < aligned_nsamps / 8; i++) {
simde__m512 *in = (simde__m512 *)&samples[i * 8];
simde__m256i *out = (simde__m256i *)&samples_out[i * 8];
*out = simde_mm512_cvtsepi32_epi16(simde_mm512_cvtps_epi32(*in));
}
#elif defined(__AVX2__)
for (int i = 0; i < aligned_nsamps / 4; i++) {
simde__m256 *in = (simde__m256 *)&samples[i * 4];
simde__m128i *out = (simde__m128i *)&samples_out[i * 4];
*out = simde_mm256_cvtsepi32_epi16(simde_mm256_cvtps_epi32(*in));
}
#else
for (int i = 0; i < nsamps; i++) {
samples_out[i].r = lroundf(samples[i].r);
samples_out[i].i = lroundf(samples[i].i);
}
#endif
vrtsim_write_internal(channel_modelling_args->vrtsim_state,
channel_modelling_args->timestamp,
samples_out,
channel_modelling_args->nsamps,
aarx,
channel_modelling_args->flags,
aarx);
}
static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state,
openair0_timestamp timestamp,
void **samplesVoid,
int nsamps,
int nbAnt,
int flags)
{
for (int aarx = 0; aarx < vrtsim_state->peer_info.num_rx_antennas; aarx++) {
notifiedFIFO_elt_t *task = newNotifiedFIFO_elt(sizeof(channel_modelling_args_t), 0, NULL, perform_channel_modelling);
channel_modelling_args_t *args = (channel_modelling_args_t *)NotifiedFifoData(task);
args->vrtsim_state = vrtsim_state;
args->timestamp = timestamp;
args->nsamps = nsamps;
args->nbAnt = nbAnt;
args->flags = flags;
args->aarx = aarx;
for (int i = 0; i < nbAnt; i++) {
args->samples[i] = samplesVoid[i];
}
pushNotifiedFIFO(&vrtsim_state->channel_modelling_actors[aarx].fifo, task);
}
return nsamps;
}
static int vrtsim_write(openair0_device *device, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags)
{
timestamp -= device->openair0_cfg->command_line_sample_advance;
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
return vrtsim_state->chanmod ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags)
: vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0);
}
static int vrtsim_read(openair0_device *device, openair0_timestamp *ptimestamp, void **samplesVoid, int nsamps, int nbAnt)
{
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps);
int ret = shm_td_iq_channel_rx(vrtsim_state->channel, vrtsim_state->last_received_sample, nsamps, 0, samplesVoid[0]);
if (ret == CHANNEL_ERROR_TOO_LATE) {
vrtsim_state->rx_samples_late += nsamps;
} else if (ret == CHANNEL_ERROR_TOO_EARLY) {
vrtsim_state->rx_early += 1;
}
vrtsim_state->rx_samples_total += nsamps;
*ptimestamp = vrtsim_state->last_received_sample;
vrtsim_state->last_received_sample += nsamps;
return nsamps;
}
static void vrtsim_end(openair0_device *device)
{
vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv;
if (vrtsim_state->role == ROLE_SERVER) {
vrtsim_state->run_timing_thread = false;
int ret = pthread_join(vrtsim_state->timing_thread, NULL);
AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno));
}
tx_timing_t *tx_timing = vrtsim_state->tx_timing;
if (vrtsim_state->chanmod) {
for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) {
shutdown_actor(&vrtsim_state->channel_modelling_actors[i]);
}
free(vrtsim_state->channel_modelling_actors);
for (int i = 1; i < vrtsim_state->peer_info.num_rx_antennas; i++) {
histogram_merge(&tx_timing->tx_histogram, &tx_timing[i].tx_histogram);
tx_timing->tx_early += tx_timing[i].tx_early;
tx_timing->tx_samples_late += tx_timing[i].tx_samples_late;
tx_timing->average_tx_budget += tx_timing[i].average_tx_budget;
tx_timing->tx_samples_total += tx_timing[i].tx_samples_total;
}
tx_timing->average_tx_budget /= vrtsim_state->peer_info.num_rx_antennas;
}
// produce 1 second of extra samples so threads can finish
shm_td_iq_channel_produce_samples(vrtsim_state->channel, vrtsim_state->sample_rate);
sleep(1);
shm_td_iq_channel_destroy(vrtsim_state->channel);
LOG_I(HW,
"VRTSIM: Realtime issues: TX %.2f%%, RX %.2f%%\n",
tx_timing->tx_samples_late / (float)tx_timing->tx_samples_total * 100,
vrtsim_state->rx_samples_late / (float)vrtsim_state->rx_samples_total * 100);
LOG_I(HW,
"VRTSIM: Read/write too early (suspected radio implementaton error) TX: %lu, RX: %lu\n",
tx_timing->tx_early,
vrtsim_state->rx_early);
LOG_I(HW, "VRTSIM: Average TX budget %.3lf uS\n", tx_timing->average_tx_budget);
histogram_print(&tx_timing->tx_histogram);
free(vrtsim_state->tx_timing);
}
static int vrtsim_stub(openair0_device *device)
{
return 0;
}
static int vrtsim_stub2(openair0_device *device, openair0_config_t *openair0_cfg)
{
return 0;
}
static int vrtsim_set_freq(openair0_device *device, openair0_config_t *openair0_cfg)
{
vrtsim_state_t *s = device->priv;
s->rx_freq = openair0_cfg->rx_freq[0];
return 0;
}
__attribute__((__visibility__("default"))) int device_init(openair0_device *device, openair0_config_t *openair0_cfg)
{
vrtsim_state_t *vrtsim_state = calloc_or_fail(1, sizeof(vrtsim_state_t));
vrtsim_readconfig(vrtsim_state);
LOG_I(HW,
"Running as %s\n",
vrtsim_state->role == ROLE_SERVER ? "server: waiting for client to connect" : "client: will connect to a vrtsim server");
device->trx_start_func = vrtsim_connect;
device->trx_reset_stats_func = vrtsim_stub;
device->trx_end_func = vrtsim_end;
device->trx_stop_func = vrtsim_stub;
device->trx_set_freq_func = vrtsim_set_freq;
device->trx_set_gains_func = vrtsim_stub2;
device->trx_write_func = vrtsim_write;
device->trx_read_func = vrtsim_read;
device->type = RFSIMULATOR;
device->openair0_cfg = &openair0_cfg[0];
device->priv = vrtsim_state;
device->trx_write_init = vrtsim_stub;
vrtsim_state->last_received_sample = 0;
vrtsim_state->sample_rate = openair0_cfg->sample_rate;
vrtsim_state->rx_freq = openair0_cfg->rx_freq[0];
vrtsim_state->tx_bw = openair0_cfg->tx_bw;
vrtsim_state->tx_num_channels = openair0_cfg->tx_num_channels;
vrtsim_state->rx_num_channels = openair0_cfg->rx_num_channels;
if (vrtsim_state->chanmod) {
init_channelmod();
int noise_power_dBFS = get_noise_power_dBFS();
int16_t noise_power = noise_power_dBFS == INVALID_DBFS_VALUE ? 0 : (int16_t)(32767.0 / powf(10.0, .05 * -noise_power_dBFS));
init_noise_device(noise_power);
}
return 0;
}
@@ -0,0 +1,265 @@
Active_gNBs = ( "gNB-OAI-DU");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_DU_ID = 0xe00;
gNB_name = "gNB-OAI-DU";
// 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; }); });
nr_cellid = 1;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pdsch_AntennaPorts_N1 = 2;
pusch_AntennaPorts = 8;
do_CSIRS = 1;
do_SRS = 0;
sib1_tda = 15;
force_UL256qam_off = 1;
maxMIMO_layers = 2;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# n_TimingAdvanceOffset = 0;
# downlinkConfigCommon
#frequencyInfoDL
# center frequency = 3950.4 MHz
# selected SSB frequency = 3950.4 MHz
absoluteFrequencySSB = 663360;
dl_frequencyBand = 77;
# frequency point A = 3901.26 MHz
dl_absoluteFrequencyPointA = 660084;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 273;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 1099; #38.101-1 Table 5.3.2-1
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 11;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 77;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 273;
pMax = 23;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 1099;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 152;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 0;
preambleReceivedTargetPower = -100;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 8;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 3;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#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;
#one (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,
# this is the offset between the last PRACH preamble power and the Msg3 PUSCH, 2 times the field value in dB
msg3_DeltaPreamble = 2;
p0_NominalWithGrant = -96;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 0;
p0_nominal = -96;
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 = 5;
nrofDownlinkSlots = 3;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 1;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = 10;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "f1";
local_n_if_name = "enp193s0f0";
local_n_address = "172.21.16.51";
remote_n_address = "172.21.6.90";
local_n_portc = 500;
local_n_portd = 2153;
remote_n_portc = 501;
remote_n_portd = 2153;
pusch_TargetSNRx10 = 120;
pucch_TargetSNRx10 = 200;
dl_bler_target_upper = .35;
dl_bler_target_lower = .15;
ul_bler_target_upper = .35;
ul_bler_target_lower = .15;
pusch_FailureThres = 100;
ul_max_mcs = 28;
min_grant_prb = 1;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 100;
pucch0_dtx_threshold = 80;
pusch_dtx_threshold = 10;
max_ldpc_iterations = 10;
tx_amp_backoff_dB = 12; # needs to match O-RU configuration
L1_rx_thread_core = 8;
L1_tx_thread_core = 10; # relevant after merge of l1_tx_thread
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 8;
nb_rx = 8;
att_tx = 0;
att_rx = 0;
bands = [77];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 9;
sl_ahead = 10;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:01:01.0", "0000:01:01.1", "0000:01:01.2", "0000:01:01.3"); # two VFs can be used as well
system_core = 0;
io_core = 1;
worker_cores = (2);
du_key_pair = ("/home/eurecom/.ssh/id_rsa.pub", "/home/eurecom/.ssh/id_rsa");
du_addr = ("00:11:22:33:44:66", "00:11:22:33:44:67", "00:11:22:33:44:68", "00:11:22:33:44:69");
vlan_tag = (5, 5, 6, 6);
ru_ip_addr = ("192.168.80.5", "192.168.80.6");
fh_config = (
# RAN650 #1
{
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
Ta4 = (0, 200);
},
# RAN650 #2
{
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
Ta4 = (0, 200);
});
};
@@ -0,0 +1,273 @@
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; }); });
nr_cellid = 1;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pdsch_AntennaPorts_N1 = 2;
maxMIMO_layers = 2;
pusch_AntennaPorts = 4;
do_CSIRS = 1;
do_SRS = 0;
sib1_tda = 15;
# force_UL256qam_off = 1;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# n_TimingAdvanceOffset = 0;
# downlinkConfigCommon
#frequencyInfoDL
# center frequency = 3350.01 MHz
# selected SSB frequency = 3349.92 MHz
absoluteFrequencySSB = 623328;
dl_frequencyBand = 78;
# frequency point A = 3300.87 MHz
dl_absoluteFrequencyPointA = 620058;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 273;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 1099; #38.101-1 Table 5.3.2-1
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 11;
initialDLBWPsearchSpaceZero = 0;
#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 = 23;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 1099;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 152;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 0;
preambleReceivedTargetPower = -100;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 8;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 3;
#ra_ReponseWindow
#1,2,4,8,10,20,40,80
ra_ResponseWindow = 5;
#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;
#one (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,
# this is the offset between the last PRACH preamble power and the Msg3 PUSCH, 2 times the field value in dB
msg3_DeltaPreamble = 2;
p0_NominalWithGrant = -96;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 0;
p0_nominal = -96;
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 = 0;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "172.21.6.5"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.51";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.51";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 180;
pucch_TargetSNRx10 = 220;
dl_bler_target_upper = .35;
dl_bler_target_lower = .15;
ul_bler_target_upper = .35;
ul_bler_target_lower = .15;
pusch_FailureThres = 1000;
ul_max_mcs = 28;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 100;
pucch0_dtx_threshold = 80;
pusch_dtx_threshold = 10;
max_ldpc_iterations = 15;
tx_amp_backoff_dB = 12; # needs to match O-RU configuration
L1_rx_thread_core = 8;
L1_tx_thread_core = 10;
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 4;
nb_rx = 4;
att_tx = 0;
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 9;
sl_ahead = 10;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
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";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:c1:11.0", "0000:c1:11.1"); # one VF can be used as well
system_core = 0;
io_core = 1;
worker_cores = (2);
du_key_pair = ("/home/eurecom/.ssh/id_rsa.pub", "/home/eurecom/.ssh/id_rsa");
du_addr = ("00:11:22:33:44:68","00:11:22:33:44:69");
vlan_tag = (11, 11);
ru_ip_addr = ("192.168.80.10");
fh_config = ({
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
Ta4 = (0, 200);
});
};
@@ -0,0 +1,271 @@
Active_gNBs = ( "oai-du");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "oai-du";
// 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; }); });
nr_cellid = 1;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pdsch_AntennaPorts_N1 = 2;
maxMIMO_layers = 2;
pusch_AntennaPorts = 4;
do_CSIRS = 1;
do_SRS = 0;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# selected SSB frequency = 3748.80 MHz
absoluteFrequencySSB = 649920;
dl_frequencyBand = 78;
# frequency point A = 3700.86 MHz
dl_absoluteFrequencyPointA = 646724;
#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 = 1099;
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 12;
initialDLBWPsearchSpaceZero = 0;
#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 = 1099;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 151;# testMAC 158; OAI gNB 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 = 5;
#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,
# this is the offset between the last PRACH preamble power and the Msg3 PUSCH, 2 times the field value in dB
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_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 = 5; #6;
nrofDownlinkSlots = 3; #7;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 1; #2;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = 0;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "172.21.6.5"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.51";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.51";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
num_cc = 1;
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 120;
pucch_TargetSNRx10 = 260;
ul_bler_target_upper = .35;
ul_bler_target_lower = .15;
pusch_FailureThres = 100;
ul_max_mcs = 28;
}
);
L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
prach_dtx_threshold = 130;
pucch0_dtx_threshold = 80;
pusch_dtx_threshold = 10;
max_ldpc_iterations = 15;
tx_amp_backoff_dB = 24; # needs to match O-RU configuration
L1_rx_thread_core = 8;
L1_tx_thread_core = 10;
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 4;
nb_rx = 4;
att_tx = 0
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 9;
sl_ahead = 5;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
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";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:c1:11.0", "0000:c1:11.1");
system_core = 0;
io_core = 1;
worker_cores = (2);
ru_addr = ("6c:ad:ad:00:04:dc", "6c:ad:ad:00:04:dc");
mtu = 8000;
fh_config = ({
T1a_cp_dl = (285, 470);
T1a_cp_ul = (285, 429);
T1a_up = (96, 196);
Ta4 = (0, 75);
ru_config = {
iq_width = 9;
iq_width_prach = 9;
};
});
};