diff --git a/.clang-tidy b/.clang-tidy index 874dd51ad..21eb0d83b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -23,6 +23,7 @@ Checks: '*,-fuchsia-*, -google-runtime-references,-google-readability-casting,-google-build-using-namespace, google-default-arguments,-cppcoreguidelines-pro-bounds-pointer-arithmetic, -cert-err58-cpp, + -altera-unroll-loops, -readability-function-cognitive-complexity,-readability-isolate-declaration, -misc-non-private-member-variables-in-classes,-altera-struct-pack-align,-readability-uppercase-literal-suffix, -cppcoreguidelines-non-private-member-variables-in-classes, diff --git a/.gdbinit b/.gdbinit index 6e3a249dd..bb7d55da3 100644 --- a/.gdbinit +++ b/.gdbinit @@ -20,7 +20,7 @@ class BoundedVectorPrinter(object): self.value_type = self.val.type.template_argument(0) def children(self): - start = self.val['buffer'].cast(self.value_type.pointer()) + start = self.val['buffer']['_M_elems'].cast(self.value_type.pointer()) length = int(self.val['size_']) for idx in range(length): yield f'[{idx}]', start[idx] @@ -33,11 +33,10 @@ class BoundedVectorPrinter(object): def display_hint(self): return 'array' - @staticmethod - def make(val): - if str(val.type).startswith('srsran::bounded_vector<'): - return BoundedVectorPrinter(val) +def make_bounded_vector(val): + if 'bounded_vector<' in str(val.type): + return BoundedVectorPrinter(val) -gdb.pretty_printers.append(BoundedVectorPrinter.make) +gdb.pretty_printers.append(make_bounded_vector) end diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 976786810..b2cdc030b 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,6 +1,6 @@ ## Issue Description ## diff --git a/.github/workflows/ccpp.yml b/.github/workflows/ccpp.yml index 9840d5fa5..ae4a09655 100644 --- a/.github/workflows/ccpp.yml +++ b/.github/workflows/ccpp.yml @@ -1,48 +1,44 @@ name: C/C++ CI on: push jobs: - x86_ubuntu18_build: - name: Build and test on x86 Ubuntu 18.04 - strategy: - matrix: - compiler: [gcc, clang] - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v1 - - name: Build srsRAN on x86 Ubuntu 18.04 - run: | - sudo apt update - sudo apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev colordiff ninja-build valgrind - mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja && ctest -T memcheck - x86_ubuntu16_build: - name: Build and test on x86 Ubuntu 16.04 - strategy: - matrix: - compiler: [gcc, clang] - runs-on: ubuntu-16.04 - steps: - - uses: actions/checkout@v1 - - name: Build srsRAN on x86 Ubuntu 16.04 - run: | - sudo apt update - sudo apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev colordiff ninja-build valgrind - mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja && ctest -T memcheck - - aarch64_ubuntu18_build: - runs-on: ubuntu-18.04 - name: Build on aarch64 + x86_ubuntu_build: + name: Build on x86 + runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: + os: [ubuntu-22.04, ubuntu-20.04, ubuntu-18.04] compiler: [gcc, clang] steps: - - uses: actions/checkout@v1 - - name: Build srsRAN on aarch64 + - uses: actions/checkout@v3 + - name: Build srsRAN on x86 ${{ matrix.os }} + run: | + sudo apt update + sudo apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev colordiff ninja-build valgrind + mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja && ctest + + aarch64_ubuntu_build: + name: Build on aarch64 + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, ubuntu-18.04] + compiler: [gcc, clang] + include: + - os: ubuntu-20.04 + distro: ubuntu20.04 + - os: ubuntu-18.04 + distro: ubuntu18.04 + steps: + - uses: actions/checkout@v3 + - name: Build srsRAN on aarch64 ${{ matrix.os }} uses: uraimo/run-on-arch-action@master with: - architecture: aarch64 - distribution: ubuntu18.04 + arch: aarch64 + distro: ${{ matrix.distro }} run: | export CTEST_PARALLEL_LEVEL=$(nproc --all) apt update apt install -y build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev ninja-build - ls -l && pwd && mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja \ No newline at end of file + ls -l && pwd && mkdir build && cd build && cmake -DRF_FOUND=True -GNinja .. && ninja diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..b5d08a1fa --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,68 @@ +name: "CodeQL" + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '38 10 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'cpp' ] + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install dependencies + run: | + sudo apt-get install \ + build-essential \ + cmake \ + libfftw3-dev \ + libmbedtls-dev \ + libpcsclite-dev \ + libboost-program-options-dev \ + libconfig++-dev \ + libsctp-dev \ + libuhd-dev \ + libzmq3-dev + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{matrix.language}}" diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index 3edf9c209..000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,23 +0,0 @@ -extraction: - cpp: - prepare: - packages: - - build-essential - - cmake - - libfftw3-dev - - libmbedtls-dev - - libpcsclite-dev - - libboost-program-options-dev - - libconfig++-dev - - libsctp-dev - - libuhd-dev - - libzmq3-dev - configure: - command: - - mkdir build - - cd build - - cmake .. - index: - build_command: - - cd build - - make diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e53a2a7db..000000000 --- a/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -dist: bionic -sudo: required - -before_script: - - sudo apt-get -qq update - - sudo apt-get install -qq build-essential cmake libfftw3-dev libmbedtls-dev libpcsclite-dev libboost-program-options-dev libconfig++-dev libsctp-dev libczmq-dev libpcsclite-dev rapidjson-dev colordiff ninja-build clang-format-8 - -language: cpp - -compiler: - - gcc - - clang - -script: - - sudo ln -s /usr/bin/clang-format-diff-8 /usr/bin/clang-format-diff - - git remote set-branches --add origin master - - git fetch - - | - if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then - # Run only for PRs because target branch is needed to do the clang-format check - echo "Checking clang-format between TRAVIS_BRANCH=$TRAVIS_BRANCH and TRAVIS_PULL_REQUEST_BRANCH=$TRAVIS_PULL_REQUEST_BRANCH" - ./run-clang-format-diff.sh "$TRAVIS_BRANCH" "$TRAVIS_PULL_REQUEST_BRANCH" - else - echo "Skipping clang-format check" - fi - - mkdir build - - cd build - - cmake -DENABLE_TTCN3=True -DRF_FOUND=True -G Ninja .. - - ninja - - ninja test - - sudo ninja install \ No newline at end of file diff --git a/CHANGELOG b/CHANGELOG index 0569c19bd..cc331753d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,40 @@ Change Log for Releases ======================= +## 22.10 + * Fix DL NAS integrity checks in srsUE + * Remove Travis and LGTM as CI platforms + * Remove polarssl as optional dependency (only mbedTLS used and required for security) + * Allow to specify multiple PLMNs in SIB1 + * Allow non-blocking S1AP connect and expose various other SCTP options + * Add support to broadcast MAC backoff indicator + * Seperate T300/T301 timer in srsENB + * Fix in eMBMS payload buffer handling + * Fix memleak in NR scheduler + +## 22.04.1 + * Various bug fixes in RLC AM and PDCP for NR + * Fix crash when UE attempted to reestablish in SA + * Remove fixed coreset0 index for SSB + * Add support for SIB5 and SIB6 transmission in LTE + +## 22.04 + * Added baseline 5G-SA support to srsUE and srsENB + * Added dynamic loading of RF libraries + * Added RRC Redirect to srsUE + * Added support for A5 measurement events to srsENB + * Added Crest Factor Reduction (CFR) for srsENB downlink and srsUE uplink (4G only) + * Raise C++ standard to C++14 + * Other bug-fixes and improved stability and performance in all parts + +## 21.10 + * Add initial 5G NSA support to srsENB (tested with OnePlus 5G Nord) + * Improved interoperability of srsUE in NSA mode + * Added enhanced instrumentation to file using JSON format + * Fixed stability issues with Ettus N310 + * Added BLER-adaptive MCS scheduling to srsENB + * Other bug-fixes and improved stability and performance in all parts + ## 21.04 * Rename project from srsLTE to srsRAN * Add initial 5G NSA support to srsUE (including x86-optimized FEC and PHY layer) diff --git a/CMakeLists.txt b/CMakeLists.txt index c12fc5a66..85b10d9b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -30,7 +30,7 @@ endif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) ######################################################################## # Project setup ######################################################################## -cmake_minimum_required(VERSION 2.6) +cmake_minimum_required(VERSION 3.5) project( SRSRAN ) message( STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} ) message( STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} ) @@ -67,42 +67,46 @@ option(ENABLE_SRSENB "Build srsENB application" ON) option(ENABLE_SRSEPC "Build srsEPC application" ON) option(DISABLE_SIMD "Disable SIMD instructions" OFF) option(AUTO_DETECT_ISA "Autodetect supported ISA extensions" ON) - + option(ENABLE_GUI "Enable GUI (using srsGUI)" ON) +option(ENABLE_RF_PLUGINS "Enable RF plugins" ON) option(ENABLE_UHD "Enable UHD" ON) option(ENABLE_BLADERF "Enable BladeRF" ON) option(ENABLE_SOAPYSDR "Enable SoapySDR" ON) +option(ENABLE_SKIQ "Enable Sidekiq SDK" ON) option(ENABLE_ZEROMQ "Enable ZeroMQ" ON) option(ENABLE_HARDSIM "Enable support for SIM cards" ON) - + option(ENABLE_TTCN3 "Enable TTCN3 test binaries" OFF) option(ENABLE_ZMQ_TEST "Enable ZMQ based E2E tests" OFF) - + option(BUILD_STATIC "Attempt to statically link external deps" OFF) option(RPATH "Enable RPATH" OFF) option(ENABLE_ASAN "Enable gcc/clang address sanitizer" OFF) -option(ENABLE_GCOV "Enable gcc/clang address sanitizer" OFF) +option(ENABLE_GCOV "Enable gcov" OFF) option(ENABLE_MSAN "Enable clang memory sanitizer" OFF) option(ENABLE_TSAN "Enable clang thread sanitizer" OFF) option(ENABLE_TIDY "Enable clang tidy" OFF) - + option(USE_LTE_RATES "Use standard LTE sampling rates" OFF) option(USE_MKL "Use MKL instead of fftw" OFF) - + option(ENABLE_TIMEPROF "Enable time profiling" ON) - + option(FORCE_32BIT "Add flags to force 32 bit compilation" OFF) option(ENABLE_SRSLOG_TRACING "Enable event tracing using srslog" OFF) option(ASSERTS_ENABLED "Enable srsRAN asserts" ON) option(STOP_ON_WARNING "Interrupt application on warning" OFF) +option(ENABLE_ALL_TEST "Enable all unit/component test" OFF) + # Users that want to try this feature need to make sure the lto plugin is # loaded by bintools (ar, nm, ...). Older versions of bintools will not do # it automatically so it is necessary to use the gcc wrappers of the compiler # (gcc-ar, gcc-nm, ...). option(BUILD_WITH_LTO "Enable LTO (experimental)" OFF) - + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") set(GCC_ARCH armv8-a CACHE STRING "GCC compile for specific architecture.") message(STATUS "Detected aarch64 processor") @@ -176,29 +180,17 @@ else(USE_MKL) endif(USE_MKL) # Crypto -find_package(Polarssl) -if (POLARSSL_FOUND) - set(SEC_INCLUDE_DIRS "${POLARSSL_INCLUDE_DIRS}") +find_package(MbedTLS REQUIRED) +if (MBEDTLS_FOUND) + set(SEC_INCLUDE_DIRS "${MBEDTLS_INCLUDE_DIRS}") if(BUILD_STATIC) - set(SEC_LIBRARIES "${POLARSSL_STATIC_LIBRARIES}") + set(SEC_LIBRARIES "${MBEDTLS_STATIC_LIBRARIES}") else(BUILD_STATIC) - set(SEC_LIBRARIES "${POLARSSL_LIBRARIES}") + set(SEC_LIBRARIES "${MBEDTLS_LIBRARIES}") endif(BUILD_STATIC) - add_definitions(-DHAVE_POLARSSL) -else(POLARSSL_FOUND) - find_package(MbedTLS REQUIRED) - if (MBEDTLS_FOUND) - set(SEC_INCLUDE_DIRS "${MBEDTLS_INCLUDE_DIRS}") - if(BUILD_STATIC) - set(SEC_LIBRARIES "${MBEDTLS_STATIC_LIBRARIES}") - else(BUILD_STATIC) - set(SEC_LIBRARIES "${MBEDTLS_LIBRARIES}") - endif(BUILD_STATIC) - add_definitions(-DHAVE_MBEDTLS) - else(MBEDTLS_FOUND) - message(FATAL_ERROR "Either PolarSSL or mbedTLS are required to build srsRAN") - endif (MBEDTLS_FOUND) -endif(POLARSSL_FOUND) +else(MBEDTLS_FOUND) + message(FATAL_ERROR "mbedTLS is required to build srsRAN") +endif (MBEDTLS_FOUND) # Hard-SIM support if(ENABLE_HARDSIM) @@ -221,6 +213,15 @@ if(ENABLE_UHD) endif(UHD_FOUND) endif(ENABLE_UHD) +# SKIQ +if (ENABLE_SKIQ) + find_package(SKIQ) + if(SKIQ_FOUND) + include_directories(${SKIQ_INCLUDE_DIRS}) + link_directories(${SKIQ_LIBRARY_DIRS}) + endif(SKIQ_FOUND) +endif (ENABLE_SKIQ) + # BladeRF if(ENABLE_BLADERF) find_package(bladeRF) @@ -253,12 +254,12 @@ if(ENABLE_TIMEPROF) add_definitions(-DENABLE_TIMEPROF) endif(ENABLE_TIMEPROF) -if(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND) +if(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND) set(RF_FOUND TRUE CACHE INTERNAL "RF frontend found") -else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND) +else(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND) set(RF_FOUND FALSE CACHE INTERNAL "RF frontend found") add_definitions(-DDISABLE_RF) -endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND) +endif(BLADERF_FOUND OR UHD_FOUND OR SOAPYSDR_FOUND OR ZEROMQ_FOUND OR SKIQ_FOUND) # Boost if(BUILD_STATIC) @@ -268,6 +269,9 @@ endif(BUILD_STATIC) set(BOOST_REQUIRED_COMPONENTS program_options ) +if(UHD_FOUND) # Ubuntu 18.04 requires component 'system' to link UHD + list(APPEND BOOST_REQUIRED_COMPONENTS "system") +endif(UHD_FOUND) if(UNIX AND EXISTS "/usr/lib64") list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix endif(UNIX AND EXISTS "/usr/lib64") @@ -367,22 +371,24 @@ macro(ADD_C_COMPILER_FLAG_IF_AVAILABLE flag have) endmacro(ADD_C_COMPILER_FLAG_IF_AVAILABLE) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-variable -Wtype-limits -std=c++11 -fno-strict-aliasing") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-variable -Wtype-limits -std=c++14 -fno-strict-aliasing") - ADD_C_COMPILER_FLAG_IF_AVAILABLE("-Wno-unused-but-set-variable" HAVE_WNO_UNUSED_BUT_SET_VARIABLE) ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-Wno-unused-but-set-variable" HAVE_WNO_UNUSED_BUT_SET_VARIABLE) if (AUTO_DETECT_ISA) find_package(SSE) endif (AUTO_DETECT_ISA) + ADD_C_COMPILER_FLAG_IF_AVAILABLE("-march=${GCC_ARCH}" HAVE_MARCH_${GCC_ARCH}) + ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-march=${GCC_ARCH}" HAVE_MARCH_${GCC_ARCH}) + if (HAVE_AVX2) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") else (HAVE_AVX2) if(HAVE_AVX) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") elseif(HAVE_SSE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") endif(HAVE_AVX) endif (HAVE_AVX2) @@ -394,8 +400,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") endif(NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") if(FORCE_32BIT) - ADD_C_COMPILER_FLAG_IF_AVAILABLE("-m32" HAVE_WNO_UNUSED_BUT_SET_VARIABLE) - ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-m32" HAVE_WNO_UNUSED_BUT_SET_VARIABLE) + ADD_C_COMPILER_FLAG_IF_AVAILABLE("-m32" HAVE_M32) + ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-m32" HAVE_M32) set(CMAKE_SHARED_LINKER_FLAGS "-m32") endif(FORCE_32BIT) @@ -406,6 +412,7 @@ ADD_C_COMPILER_FLAG_IF_AVAILABLE("-Werror=incompatible-pointer-types" HAVE_ERROR if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Wno-unused-result -Wformat -Wmissing-field-initializers -Wtype-limits -std=c99 -fno-strict-aliasing -D_GNU_SOURCE") + ADD_C_COMPILER_FLAG_IF_AVAILABLE("-Wno-unused-but-set-variable" HAVE_WNO_UNUSED_BUT_SET_VARIABLE) if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb -O0 -DDEBUG_MODE -DBUILD_TYPE_DEBUG") @@ -432,12 +439,12 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") find_package(SSE) endif (AUTO_DETECT_ISA) if (HAVE_AVX2) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx2 -DLV_HAVE_AVX2 -DLV_HAVE_AVX -DLV_HAVE_SSE") else (HAVE_AVX2) if(HAVE_AVX) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -mavx -DLV_HAVE_AVX -DLV_HAVE_SSE") elseif(HAVE_SSE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=${GCC_ARCH} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse -msse4.1 -DLV_HAVE_SSE") endif(HAVE_AVX) endif (HAVE_AVX2) @@ -491,18 +498,22 @@ if(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") endif () if (ENABLE_ASAN) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer") + # Note: When using ASAN, we need to ensure the use of RPATH instead of RUNPATH via "-Wl,--disable-new-dtags" + # While RPATH is default, some systems (e.g. Ubuntu 18.04 and 20.04) use RUNPATH by default, which is non-transitive. + # Since ASAN intercepts dlopen(), by which it replaces the dynamic string token "$ORIGIN" to its own location, + # the RF plugins won't be found by "libsrsran_rf.so" when using ASAN + RUNPATH in the top-level executable. + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -Wl,--disable-new-dtags") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -Wl,--disable-new-dtags") endif (ENABLE_ASAN) if (ENABLE_TSAN) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -DHAVE_TSAN") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") endif (ENABLE_TSAN) if (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fPIE -pie") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins -fPIE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins -fPIE") endif (ENABLE_MSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang") if (ENABLE_GCOV) @@ -519,8 +530,8 @@ endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Add colored output when using the Ninja generator if("Ninja" STREQUAL ${CMAKE_GENERATOR}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color=always") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color=always") + ADD_C_COMPILER_FLAG_IF_AVAILABLE("-fdiagnostics-color=always" HAVE_DIAGNOSTIC_COLOR_C) + ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-fdiagnostics-color=always" HAVE_DIAGNOSTIC_COLOR_CXX) endif() # Add -Werror to C/C++ flags for newer compilers @@ -529,11 +540,22 @@ if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") endif() +# GCC >= 11.2.0: Disable analysis on "maybe-uninitialized" variables because of the high false-positive rate +if(CMAKE_COMPILER_IS_GNUCXX AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.2) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-maybe-uninitialized") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-maybe-uninitialized") +endif() + if(CMAKE_C_COMPILER_ID MATCHES "GNU") # Increase inlining limit to allow gcc compilation on e.g. RPi2 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --param large-function-growth=1600") endif(CMAKE_C_COMPILER_ID MATCHES "GNU") +if (EXTRA_TERM_TIMEOUT_S) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DSRSRAN_TERM_TIMEOUT_S=${EXTRA_TERM_TIMEOUT_S}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSRSRAN_TERM_TIMEOUT_S=${EXTRA_TERM_TIMEOUT_S}") +endif (EXTRA_TERM_TIMEOUT_S) + message(STATUS "CMAKE_C_FLAGS is ${CMAKE_C_FLAGS}") message(STATUS "CMAKE_CXX_FLAGS is ${CMAKE_CXX_FLAGS}") @@ -605,6 +627,18 @@ function(add_nr_test) set_tests_properties(${TNAME} PROPERTIES LABELS "nr;${CTEST_LABELS}") endfunction() +function(add_nr_advanced_test) + if (NOT ${ENABLE_ALL_TEST}) + return() + endif() + add_test(${ARGN}) + set(TNAME ${ARGV0}) + if(${TNAME} STREQUAL NAME) + set(TNAME ${ARGV1}) + endif() + set_tests_properties(${TNAME} PROPERTIES LABELS "nr;${CTEST_LABELS}") +endfunction() + ######################################################################## # Add general includes and dependencies ######################################################################## @@ -638,13 +672,14 @@ if(RF_FOUND) endif(ENABLE_SRSUE) if(ENABLE_SRSENB) - message(STATUS "Building with srsENB") + message(STATUS "Building with srsENB/srsGNB") add_subdirectory(srsenb) + add_subdirectory(srsgnb) else(ENABLE_SRSENB) - message(STATUS "srsENB build disabled") + message(STATUS "srsENB/srsGNB build disabled") endif(ENABLE_SRSENB) else(RF_FOUND) - message(STATUS "srsUE and srsENB builds disabled due to missing RF driver") + message(STATUS "srsUE and srsENB/srsGNB builds disabled due to missing RF driver") endif(RF_FOUND) if(ENABLE_SRSEPC) diff --git a/COPYRIGHT b/COPYRIGHT index 7b6dc37e7..dae09269f 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,5 +1,5 @@ Files: * -Copyright: 2013-2021, Software Radio Systems Limited. +Copyright: 2013-2022, Software Radio Systems Limited. License: diff --git a/CTestConfig.cmake b/CTestConfig.cmake index a868d7957..2eb7b8802 100644 --- a/CTestConfig.cmake +++ b/CTestConfig.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -24,4 +24,4 @@ set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "my.cdash.org") set(CTEST_DROP_LOCATION "/submit.php?project=srsRAN") set(CTEST_DROP_SITE_CDASH TRUE) -set(VALGRIND_COMMAND_OPTIONS "--error-exitcode=1 --trace-children=yes --leak-check=full --show-reachable=yes --vex-guest-max-insns=25") +set(VALGRIND_COMMAND_OPTIONS "--error-exitcode=1 --trace-children=yes --leak-check=full --show-leak-kinds=all --track-origins=yes --show-reachable=yes --vex-guest-max-insns=25") diff --git a/README.md b/README.md index 3ac88667b..1f94b9891 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ srsRAN ====== -[![Build Status](https://travis-ci.com/srsran/srsRAN.svg?branch=master)](https://travis-ci.com/srsran/srsRAN) -[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/srsran/srsRAN.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/srsran/srsRAN/context:cpp) -[![Coverity](https://scan.coverity.com/projects/23048/badge.svg)](https://scan.coverity.com/projects/srsran_agpl) +[![Build Status](https://github.com/srsran/srsRAN/actions/workflows/ccpp.yml/badge.svg?branch=master)](https://github.com/srsran/srsRAN/actions/workflows/ccpp.yml) +[![CodeQL](https://github.com/srsran/srsRAN/actions/workflows/codeql.yml/badge.svg?branch=master)](https://github.com/srsran/srsRAN/actions/workflows/codeql.yml) +[![Coverity](https://scan.coverity.com/projects/23045/badge.svg)](https://scan.coverity.com/projects/srsran) srsRAN is a 4G/5G software radio suite developed by [SRS](http://www.srs.io). See the [srsRAN project pages](https://www.srsran.com) for information, guides and project news. The srsRAN suite includes: - * srsUE - a full-stack SDR 4G/5G-NSA UE application (5G-SA coming soon) - * srsENB - a full-stack SDR 4G eNodeB application (5G-NSA and 5G-SA coming soon) + * srsUE - a full-stack SDR 4G/5G UE application + * srsENB - a full-stack SDR 4G/5G e(g)NodeB application * srsEPC - a light-weight 4G core network implementation with MME, HSS and S/P-GW For application features, build instructions and user guides see the [srsRAN documentation](https://docs.srsran.com). @@ -21,4 +21,4 @@ For license details, see LICENSE file. Support ======= -Mailing list: https://lists.softwareradiosystems.com/mailman/listinfo/srslte-users \ No newline at end of file +Mailing list: https://lists.srsran.com/mailman/listinfo/srsran-users diff --git a/cmake/modules/CheckAtomic.cmake b/cmake/modules/CheckAtomic.cmake index c8168d60d..cc293dbeb 100644 --- a/cmake/modules/CheckAtomic.cmake +++ b/cmake/modules/CheckAtomic.cmake @@ -1,9 +1,21 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # -# By using this file, you agree to the terms and conditions set -# forth in the LICENSE file which can be found at the top level of -# the distribution. +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. # # Adopted from https://github.com/pothosware/SoapyRTLSDR diff --git a/cmake/modules/CheckFunctionExists.c b/cmake/modules/CheckFunctionExists.c index 0cd33af0d..98018febd 100644 --- a/cmake/modules/CheckFunctionExists.c +++ b/cmake/modules/CheckFunctionExists.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/cmake/modules/FindFFTW3F.cmake b/cmake/modules/FindFFTW3F.cmake index c6a3ff727..4a9945ddd 100644 --- a/cmake/modules/FindFFTW3F.cmake +++ b/cmake/modules/FindFFTW3F.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindLibConfig.cmake b/cmake/modules/FindLibConfig.cmake index d109c2117..e23da42d0 100644 --- a/cmake/modules/FindLibConfig.cmake +++ b/cmake/modules/FindLibConfig.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindLimeSDR.cmake b/cmake/modules/FindLimeSDR.cmake index b5f2b35c7..ee5210f73 100644 --- a/cmake/modules/FindLimeSDR.cmake +++ b/cmake/modules/FindLimeSDR.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -23,13 +23,16 @@ if(NOT LIMESDR_FOUND) find_path(LIMESDR_INCLUDE_DIRS NAMES LimeSuite.h + HINTS $ENV{LIMESUITE_DIR}/include PATHS ${LIMESDR_PKG_INCLUDE_DIRS} /usr/include/lime /usr/local/include/lime + $ENV{LIMESUITE_DIR}/include/lime ) find_library(LIMESDR_LIBRARIES NAMES LimeSuite + HINTS $ENV{LIMESUITE_DIR}/lib PATHS ${LIMESDR_PKG_LIBRARY_DIRS} /usr/lib /usr/local/lib diff --git a/cmake/modules/FindMKL.cmake b/cmake/modules/FindMKL.cmake index 22de689e6..3debba566 100644 --- a/cmake/modules/FindMKL.cmake +++ b/cmake/modules/FindMKL.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -28,31 +28,45 @@ find_path(MKL_INCLUDE_DIR NAMES mkl.h HINTS $ENV{MKL_DIR}/include + /opt/intel/oneapi/mkl/latest/include + /opt/intel/mkl/include + /usr/include/mkl PATHS) find_path(MKL_FFTW_INCLUDE_DIR NAMES fftw3.h HINTS $ENV{MKL_DIR}/include/fftw + /opt/intel/oneapi/mkl/latest/include/fftw + /opt/intel/mkl/include/fftw + /usr/include/mkl/fftw PATHS) find_library(MKL_LIBRARIES NAMES mkl_rt HINTS $ENV{MKL_DIR}/lib/intel64 + /opt/intel/oneapi/mkl/latest/lib/intel64 + /opt/intel/mkl/lib/intel64 PATHS) find_library(MKL_CORE NAMES libmkl_core.a HINTS $ENV{MKL_DIR}/lib/intel64 + /opt/intel/oneapi/mkl/latest/lib/intel64/ + /opt/intel/mkl/lib/intel64 PATHS) find_library(MKL_ILP NAMES libmkl_intel_ilp64.a HINTS $ENV{MKL_DIR}/lib/intel64 + /opt/intel/oneapi/mkl/latest/lib/intel64/ + /opt/intel/mkl/lib/intel64 PATHS) find_library(MKL_SEQ NAMES libmkl_sequential.a HINTS $ENV{MKL_DIR}/lib/intel64 + /opt/intel/oneapi/mkl/latest/lib/intel64/ + /opt/intel/mkl/lib/intel64 PATHS) set(MKL_STATIC_LIBRARIES -Wl,--start-group ${MKL_CORE} ${MKL_ILP} ${MKL_SEQ} -Wl,--end-group -lpthread -lm -ldl) @@ -61,7 +75,7 @@ set(MKL_INCLUDE_DIRS ${MKL_INCLUDE_DIR} ${MKL_FFTW_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set MKL_FOUND to TRUE # if all listed variables are TRUE -find_package_handle_standard_args(mkl DEFAULT_MSG +find_package_handle_standard_args(MKL DEFAULT_MSG MKL_LIBRARIES MKL_CORE MKL_ILP MKL_SEQ MKL_INCLUDE_DIRS) if(MKL_FOUND) diff --git a/cmake/modules/FindMbedTLS.cmake b/cmake/modules/FindMbedTLS.cmake index 2cc4508ff..8fcc834e2 100644 --- a/cmake/modules/FindMbedTLS.cmake +++ b/cmake/modules/FindMbedTLS.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindPCSCLite.cmake b/cmake/modules/FindPCSCLite.cmake index 7305baa4b..d31089161 100644 --- a/cmake/modules/FindPCSCLite.cmake +++ b/cmake/modules/FindPCSCLite.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindPolarssl.cmake b/cmake/modules/FindPolarssl.cmake deleted file mode 100644 index e305cacb7..000000000 --- a/cmake/modules/FindPolarssl.cmake +++ /dev/null @@ -1,73 +0,0 @@ -# -# Copyright 2013-2021 Software Radio Systems Limited -# -# This file is part of srsRAN -# -# srsRAN is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of -# the License, or (at your option) any later version. -# -# srsRAN is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# A copy of the GNU Affero General Public License can be found in -# the LICENSE file in the top-level directory of this distribution -# and at http://www.gnu.org/licenses/. -# - -# - Try to find polarssl -# -# Once done this will define -# POLARSSL_FOUND - System has polarssl -# POLARSSL_INCLUDE_DIRS - The polarssl include directories -# POLARSSL_LIBRARIES - The polarssl library - -FIND_PACKAGE(PkgConfig REQUIRED) -PKG_CHECK_MODULES(PC_POLARSSL polarssl) - -FIND_PATH( - POLARSSL_INCLUDE_DIRS - NAMES polarssl/version.h - HINTS $ENV{POLARSSL_DIR}/include - ${PC_POLARSSL_INCLUDEDIR} - ${CMAKE_INSTALL_PREFIX}/include - PATHS /usr/local/include - /usr/include -) - -FIND_LIBRARY( - POLARSSL_LIBRARIES - NAMES polarssl - HINTS $ENV{POLARSSL_DIR}/lib - ${PC_POLARSSL_LIBDIR} - ${CMAKE_INSTALL_PREFIX}/lib - ${CMAKE_INSTALL_PREFIX}/lib64 - PATHS /usr/local/lib - /usr/local/lib64 - /usr/lib - /usr/lib64 -) - -FIND_LIBRARY( - POLARSSL_STATIC_LIBRARIES - NAMES libpolarssl.a - HINTS $ENV{POLARSSL_DIR}/lib - ${PC_POLARSSL_LIBDIR} - ${CMAKE_INSTALL_PREFIX}/lib - ${CMAKE_INSTALL_PREFIX}/lib64 - PATHS /usr/local/lib - /usr/local/lib64 - /usr/lib - /usr/lib64 -) - -message(STATUS "POLARSSL LIBRARIES: " ${POLARSSL_LIBRARIES}) -message(STATUS "POLARSSL STATIC LIBRARIES: " ${POLARSSL_STATIC_LIBRARIES}) -message(STATUS "POLARSSL INCLUDE DIRS: " ${POLARSSL_INCLUDE_DIRS}) - -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Polarssl DEFAULT_MSG POLARSSL_LIBRARIES POLARSSL_INCLUDE_DIRS) -MARK_AS_ADVANCED(POLARSSL_STATIC_LIBRARIES POLARSSL_LIBRARIES POLARSSL_INCLUDE_DIRS) diff --git a/cmake/modules/FindSCTP.cmake b/cmake/modules/FindSCTP.cmake index 37ef92a6a..5584dc5d6 100644 --- a/cmake/modules/FindSCTP.cmake +++ b/cmake/modules/FindSCTP.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindSKIQ.cmake b/cmake/modules/FindSKIQ.cmake new file mode 100644 index 000000000..b6eba07f2 --- /dev/null +++ b/cmake/modules/FindSKIQ.cmake @@ -0,0 +1,89 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +FIND_PACKAGE(PkgConfig REQUIRED) +#PKG_CHECK_MODULES(SKIQ SKIQ) +IF(NOT SKIQ_FOUND) + +FIND_PATH( + SKIQ_INCLUDE_DIRS + NAMES sidekiq_api.h + HINTS $ENV{SKIQ_DIR}/inc + $ENV{SKIQ_DIR}/sidekiq_core/inc + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + SKIQ_LIBRARY + NAMES sidekiq__x86_64.gcc + HINTS $ENV{SKIQ_DIR}/lib + PATHS /usr/local/lib + /usr/lib + /usr/lib/x86_64-linux-gnu + /usr/local/lib64 + /usr/local/lib32 +) + +FIND_LIBRARY( + SKIQ_LIBRARY_GLIB + NAMES libglib-2.0.a + HINTS $ENV{SKIQ_DIR}/lib/support/x86_64.gcc/usr/lib/epiq + PATHS /usr/local/lib + /usr/lib + /usr/lib/epiq + /usr/lib/x86_64-linux-gnu + /usr/local/lib64 + /usr/local/lib32 +) + +FIND_LIBRARY( + SKIQ_LIBRARY_USB + NAMES libusb-1.0.a + HINTS $ENV{SKIQ_DIR}/lib/support/x86_64.gcc/usr/lib/epiq + PATHS /usr/local/lib + /usr/lib + /usr/lib/epiq + /usr/lib/x86_64-linux-gnu + /usr/local/lib64 + /usr/local/lib32 +) + +FIND_LIBRARY( + SKIQ_LIBRARY_TIRPC + NAMES libtirpc.so.3 + PATHS /usr/local/lib + /usr/lib64 + /usr/lib/epiq + /usr/lib/x86_64-linux-gnu + /usr/local/lib64 + /usr/local/lib32 +) + +set(SKIQ_LIBRARIES ${SKIQ_LIBRARY} ${SKIQ_LIBRARY_GLIB} ${SKIQ_LIBRARY_USB} ${SKIQ_LIBRARY_TIRPC}) + +message(STATUS "SKIQ LIBRARIES " ${SKIQ_LIBRARIES}) +message(STATUS "SKIQ INCLUDE DIRS " ${SKIQ_INCLUDE_DIRS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SKIQ DEFAULT_MSG SKIQ_LIBRARIES SKIQ_INCLUDE_DIRS) +MARK_AS_ADVANCED(SKIQ_LIBRARIES SKIQ_INCLUDE_DIRS) + +ENDIF(NOT SKIQ_FOUND) diff --git a/cmake/modules/FindSRSGUI.cmake b/cmake/modules/FindSRSGUI.cmake index e576aa4d4..13410d99a 100644 --- a/cmake/modules/FindSRSGUI.cmake +++ b/cmake/modules/FindSRSGUI.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindSSE.cmake b/cmake/modules/FindSSE.cmake index 6596d371c..06088009a 100644 --- a/cmake/modules/FindSSE.cmake +++ b/cmake/modules/FindSSE.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindSoapySDR.cmake b/cmake/modules/FindSoapySDR.cmake index d4642c99a..af6eba060 100644 --- a/cmake/modules/FindSoapySDR.cmake +++ b/cmake/modules/FindSoapySDR.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -23,14 +23,16 @@ if(NOT SOAPYSDR_FOUND) pkg_check_modules (SOAPYSDR_PKG SoapySDR) find_path(SOAPYSDR_INCLUDE_DIRS - NAMES Device.h + NAMES SoapySDR/Device.h + HINTS $ENV{SOAPY_DIR}/include PATHS ${SOAPYSDR_PKG_INCLUDE_DIRS} - /usr/include/SoapySDR - /usr/local/include/SoapySDR + /usr/include + /usr/local/include ) find_library(SOAPYSDR_LIBRARIES NAMES SoapySDR + HINTS $ENV{SOAPY_DIR}/lib PATHS ${SOAPYSDR_PKG_LIBRARY_DIRS} /usr/lib /usr/local/lib diff --git a/cmake/modules/FindUHD.cmake b/cmake/modules/FindUHD.cmake index 47e9b4ff9..190d38682 100644 --- a/cmake/modules/FindUHD.cmake +++ b/cmake/modules/FindUHD.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindZeroMQ.cmake b/cmake/modules/FindZeroMQ.cmake index 9b0abf37b..7a45ef4ef 100644 --- a/cmake/modules/FindZeroMQ.cmake +++ b/cmake/modules/FindZeroMQ.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/FindbladeRF.cmake b/cmake/modules/FindbladeRF.cmake index f05036ccf..09d6463ba 100644 --- a/cmake/modules/FindbladeRF.cmake +++ b/cmake/modules/FindbladeRF.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -20,18 +20,21 @@ if(NOT BLADERF_FOUND) pkg_check_modules (BLADERF_PKG libbladeRF) - find_path(BLADERF_INCLUDE_DIRS NAMES libbladeRF.h - PATHS - ${BLADERF_PKG_INCLUDE_DIRS} - /usr/include - /usr/local/include + + find_path(BLADERF_INCLUDE_DIRS + NAMES libbladeRF.h + HINTS $ENV{BLADERF_DIR}/include + PATHS ${BLADERF_PKG_INCLUDE_DIRS} + /usr/include + /usr/local/include ) - find_library(BLADERF_LIBRARIES NAMES bladeRF - PATHS - ${BLADERF_PKG_LIBRARY_DIRS} - /usr/lib - /usr/local/lib + find_library(BLADERF_LIBRARIES + NAMES bladeRF + HINTS $ENV{BLADERF_DIR}/lib + PATHS ${BLADERF_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib ) if(BLADERF_INCLUDE_DIRS AND BLADERF_LIBRARIES) diff --git a/cmake/modules/SRSRANPackage.cmake b/cmake/modules/SRSRANPackage.cmake index 571c0e4b2..8fe0b7110 100644 --- a/cmake/modules/SRSRANPackage.cmake +++ b/cmake/modules/SRSRANPackage.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/cmake/modules/SRSRANVersion.cmake b/cmake/modules/SRSRANVersion.cmake index 35f011811..21f67c5a7 100644 --- a/cmake/modules/SRSRANVersion.cmake +++ b/cmake/modules/SRSRANVersion.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,8 +18,8 @@ # and at http://www.gnu.org/licenses/. # -SET(SRSRAN_VERSION_MAJOR 21) -SET(SRSRAN_VERSION_MINOR 04) +SET(SRSRAN_VERSION_MAJOR 22) +SET(SRSRAN_VERSION_MINOR 10) SET(SRSRAN_VERSION_PATCH 0) SET(SRSRAN_VERSION_STRING "${SRSRAN_VERSION_MAJOR}.${SRSRAN_VERSION_MINOR}.${SRSRAN_VERSION_PATCH}") SET(SRSRAN_SOVERSION 0) diff --git a/cmake/modules/SRSRAN_install_configs.sh.in b/cmake/modules/SRSRAN_install_configs.sh.in index aaa642cad..314b25f3b 100755 --- a/cmake/modules/SRSRAN_install_configs.sh.in +++ b/cmake/modules/SRSRAN_install_configs.sh.in @@ -105,7 +105,7 @@ install_file "ue.conf.example" install_file "enb.conf.example" install_file "sib.conf.example" install_file "rr.conf.example" -install_file "drb.conf.example" +install_file "rb.conf.example" install_file "epc.conf.example" install_file "mbms.conf.example" install_file "user_db.csv.example" diff --git a/cmake/modules/SRSRANbuildinfo.cmake.in b/cmake/modules/SRSRANbuildinfo.cmake.in index 8cbea7325..e2ce71273 100644 --- a/cmake/modules/SRSRANbuildinfo.cmake.in +++ b/cmake/modules/SRSRANbuildinfo.cmake.in @@ -1,4 +1,3 @@ -cmake_minimum_required(VERSION 2.6) execute_process( COMMAND git rev-parse --abbrev-ref HEAD diff --git a/debian/packager.sh b/debian/packager.sh index 3f66ff062..ab31ce20e 100755 --- a/debian/packager.sh +++ b/debian/packager.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 29b1f2f85..cbe3df9c8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/examples/CMakeLists.txt b/lib/examples/CMakeLists.txt index 75fc130e8..8befdf20e 100644 --- a/lib/examples/CMakeLists.txt +++ b/lib/examples/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -66,7 +66,7 @@ endif(SRSGUI_FOUND) if (ZEROMQ_FOUND) add_executable(zmq_remote_rx zmq_remote_rx.c) - target_link_libraries(zmq_remote_rx srsran_phy srsran_rf) + target_link_libraries(zmq_remote_rx srsran_phy srsran_rf ${ZEROMQ_LIBRARIES}) endif (ZEROMQ_FOUND) ################################################################# diff --git a/lib/examples/cell_search.c b/lib/examples/cell_search.c index 636022b5e..60f78a62c 100644 --- a/lib/examples/cell_search.c +++ b/lib/examples/cell_search.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -106,7 +106,7 @@ void parse_args(int argc, char** argv) rf_gain = strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/examples/cell_search_nbiot.c b/lib/examples/cell_search_nbiot.c index 75d973b23..c20f4f780 100644 --- a/lib/examples/cell_search_nbiot.c +++ b/lib/examples/cell_search_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -106,7 +106,7 @@ void parse_args(int argc, char** argv) scan_raster_offset = true; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/examples/npdsch_enodeb.c b/lib/examples/npdsch_enodeb.c index 162a58c47..077cc341f 100644 --- a/lib/examples/npdsch_enodeb.c +++ b/lib/examples/npdsch_enodeb.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -170,7 +170,7 @@ void parse_args(int argc, char** argv) cell.nbiot_prb = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -692,10 +692,9 @@ int main(int argc, char** argv) // find the noise spectral density float snr_lin = srsran_convert_dB_to_power(file_snr); float n0 = abs_avg / snr_lin; - float nstd = sqrtf(n0 / 2); // add some noise to the signal - srsran_ch_awgn_c(output_buffer, output_buffer, nstd, sf_n_samples); + srsran_ch_awgn_c(output_buffer, output_buffer, n0, sf_n_samples); } /* send to file or usrp */ diff --git a/lib/examples/npdsch_ue.c b/lib/examples/npdsch_ue.c index 9fd23b883..10a5af7fb 100644 --- a/lib/examples/npdsch_ue.c +++ b/lib/examples/npdsch_ue.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -228,7 +228,7 @@ void parse_args(prog_args_t* args, int argc, char** argv) args->disable_plots_except_constellation = true; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(args, argv[0]); @@ -339,7 +339,7 @@ int main(int argc, char** argv) parse_args(&prog_args, argc, argv); #if HAVE_PCAP - FILE* pcap_file = LTE_PCAP_Open(MAC_LTE_DLT, "/tmp/npdsch.pcap"); + FILE* pcap_file = DLT_PCAP_Open(MAC_LTE_DLT, "/tmp/npdsch.pcap"); #endif sigset_t sigset; @@ -865,7 +865,7 @@ int main(int argc, char** argv) #if HAVE_PCAP printf("Saving PCAP file\n"); - LTE_PCAP_Close(pcap_file); + DLT_PCAP_Close(pcap_file); #endif #ifndef DISABLE_RF diff --git a/lib/examples/npdsch_ue_helper.cc b/lib/examples/npdsch_ue_helper.cc index 24121bc88..06a342878 100644 --- a/lib/examples/npdsch_ue_helper.cc +++ b/lib/examples/npdsch_ue_helper.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/examples/npdsch_ue_helper.h b/lib/examples/npdsch_ue_helper.h index 26b6d725e..7bae0d51d 100644 --- a/lib/examples/npdsch_ue_helper.h +++ b/lib/examples/npdsch_ue_helper.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/examples/pdsch_enodeb.c b/lib/examples/pdsch_enodeb.c index f3880eafa..693a7d5ad 100644 --- a/lib/examples/pdsch_enodeb.c +++ b/lib/examples/pdsch_enodeb.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #include "srsran/common/crash_handler.h" #include "srsran/common/gen_mch_tables.h" #include "srsran/srsran.h" +#include #include #include #include @@ -52,6 +53,12 @@ static char* output_file_name = NULL; #define PAGE_UP 53 #define PAGE_DOWN 54 +#define CFR_THRES_UP_KEY 't' +#define CFR_THRES_DN_KEY 'g' + +#define CFR_THRES_STEP 0.05f +#define CFR_PAPR_STEP 0.1f + static srsran_cell_t cell = { 25, // nof_prb 1, // nof_ports @@ -66,7 +73,7 @@ static int net_port = -1; // -1 generates random dataThat means there is so static uint32_t cfi = 2; static uint32_t mcs_idx = 1, last_mcs_idx = 1; static int nof_frames = -1; -static srsran_tm_t transmission_mode = SRSRAN_TM1; +static srsran_tm_t transmission_mode = SRSRAN_TM1; static uint32_t nof_tb = 1; static uint32_t multiplex_pmi = 0; static uint32_t multiplex_nof_layers = 1; @@ -79,6 +86,27 @@ static bool enable_256qam = false; static float output_file_snr = +INFINITY; static bool use_standard_lte_rate = false; + +// CFR runtime control flags +static bool cfr_thr_inc = false; +static bool cfr_thr_dec = false; + +typedef struct { + int enable; + char* mode; + float manual_thres; + float strength; + float auto_target_papr; + float ema_alpha; +} cfr_args_t; + +static cfr_args_t cfr_args = {.enable = 0, + .mode = "manual", + .manual_thres = 1.0f, + .strength = 1.0f, + .auto_target_papr = 8.0f, + .ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB}; + static bool null_file_sink = false; static srsran_filesink_t fsink; static srsran_ofdm_t ifft[SRSRAN_MAX_PORTS]; @@ -94,6 +122,7 @@ static srsran_softbuffer_tx_t* softbuffers[SRSRAN_MAX_CODEWORDS]; static srsran_regs_t regs; static srsran_dci_dl_t dci_dl; static int rvidx[SRSRAN_MAX_CODEWORDS] = {0, 0}; +static srsran_cfr_cfg_t cfr_config = {}; static cf_t * sf_buffer[SRSRAN_MAX_PORTS] = {NULL}, *output_buffer[SRSRAN_MAX_PORTS] = {NULL}; static uint32_t sf_n_re, sf_n_samples; @@ -143,14 +172,28 @@ static void usage(char* prog) printf("\t-s output file SNR [Default %f]\n", output_file_snr); printf("\t-q Enable/Disable 256QAM modulation (default %s)\n", enable_256qam ? "enabled" : "disabled"); printf("\t-Q Use standard LTE sample rates (default %s)\n", use_standard_lte_rate ? "enabled" : "disabled"); + printf("CFR Options:\n"); + printf("\t--enable_cfr Enable the CFR (default %s)\n", cfr_args.enable ? "enabled" : "disabled"); + printf("\t--cfr_mode CFR mode: manual, auto_cma, auto_ema. (default %s)\n", cfr_args.mode); + printf("\t--cfr_manual_thres CFR manual threshold (default %.2f)\n", cfr_args.manual_thres); + printf("\t--cfr_strength CFR strength (default %.2f)\n", cfr_args.strength); + printf("\t--cfr_auto_papr CFR PAPR target for auto modes (default %.2f)\n", cfr_args.auto_target_papr); + printf("\t--cfr_ema_alpha CFR alpha parameter for EMA mode (default %.2f)\n", cfr_args.ema_alpha); printf("\n"); printf("\t*: See 3GPP 36.212 Table 5.3.3.1.5-4 for more information\n"); } +struct option cfr_opts[] = {{"enable_cfr", no_argument, &cfr_args.enable, 1}, + {"cfr_mode", required_argument, NULL, 'C'}, + {"cfr_manual_thres", required_argument, NULL, 'T'}, + {"cfr_strength", required_argument, NULL, 'S'}, + {"cfr_auto_papr", required_argument, NULL, 'P'}, + {"cfr_ema_alpha", required_argument, NULL, 'e'}, + {0, 0, 0, 0}}; static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "IadglfmoncpqvutxbwMsBQ")) != -1) { + while ((opt = getopt_long(argc, argv, "IadglfmoncpqvutxbwMsBQ", cfr_opts, NULL)) != -1) { switch (opt) { case 'I': rf_dev = argv[optind]; @@ -198,7 +241,7 @@ static void parse_args(int argc, char** argv) mbsfn_area_id = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 's': output_file_snr = strtof(argv[optind], NULL); @@ -212,6 +255,27 @@ static void parse_args(int argc, char** argv) case 'Q': use_standard_lte_rate ^= true; break; + case 'E': + cell.cp = SRSRAN_CP_EXT; + break; + case 'C': + cfr_args.mode = optarg; + break; + case 'T': + cfr_args.manual_thres = strtof(optarg, NULL); + break; + case 'S': + cfr_args.strength = strtof(optarg, NULL); + break; + case 'P': + cfr_args.auto_target_papr = strtof(optarg, NULL); + break; + case 'e': + cfr_args.ema_alpha = strtof(optarg, NULL); + break; + case 0: + /* getopt_long() set a variable, keep going */ + break; default: usage(argv[0]); exit(-1); @@ -225,6 +289,27 @@ static void parse_args(int argc, char** argv) #endif } +static int parse_cfr_args() +{ + cfr_config.cfr_enable = cfr_args.enable; + cfr_config.manual_thr = cfr_args.manual_thres; + cfr_config.max_papr_db = cfr_args.auto_target_papr; + cfr_config.alpha = cfr_args.strength; + cfr_config.ema_alpha = cfr_args.ema_alpha; + + cfr_config.cfr_mode = srsran_cfr_str2mode(cfr_args.mode); + if (cfr_config.cfr_mode == SRSRAN_CFR_THR_INVALID) { + ERROR("CFR mode not recognised"); + return SRSRAN_ERROR; + } + + if (!srsran_cfr_params_valid(&cfr_config)) { + ERROR("Invalid CFR parameters"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + static void base_init() { int i; @@ -316,12 +401,16 @@ static void base_init() /* create ifft object */ for (i = 0; i < cell.nof_ports; i++) { - if (srsran_ofdm_tx_init(&ifft[i], SRSRAN_CP_NORM, sf_buffer[i], output_buffer[i], cell.nof_prb)) { + if (srsran_ofdm_tx_init(&ifft[i], cell.cp, sf_buffer[i], output_buffer[i], cell.nof_prb)) { ERROR("Error creating iFFT object"); exit(-1); } srsran_ofdm_set_normalize(&ifft[i], true); + if (srsran_ofdm_set_cfr(&ifft[i], &cfr_config)) { + ERROR("Error setting CFR object"); + exit(-1); + } } if (srsran_ofdm_tx_init_mbsfn(&ifft_mbsfn, SRSRAN_CP_EXT, sf_buffer[0], output_buffer[0], cell.nof_prb)) { @@ -330,6 +419,10 @@ static void base_init() } srsran_ofdm_set_non_mbsfn_region(&ifft_mbsfn, 2); srsran_ofdm_set_normalize(&ifft_mbsfn, true); + if (srsran_ofdm_set_cfr(&ifft_mbsfn, &cfr_config)) { + ERROR("Error setting CFR object"); + exit(-1); + } if (srsran_pbch_init(&pbch)) { ERROR("Error creating PBCH object"); @@ -480,6 +573,8 @@ static int update_radl() { ZERO_OBJECT(dci_dl); + int ret = SRSRAN_ERROR; + /* Configure cell and PDSCH in function of the transmission mode */ switch (transmission_mode) { case SRSRAN_TM1: @@ -502,7 +597,7 @@ static int update_radl() break; default: ERROR("Transmission mode not implemented."); - exit(-1); + goto exit; } dci_dl.rnti = UE_CRNTI; @@ -523,7 +618,80 @@ static int update_radl() SRSRAN_DCI_TB_DISABLE(dci_dl.tb[1]); } + // Increase the CFR threshold or target PAPR + if (cfr_thr_inc) { + cfr_thr_inc = false; // Reset the flag + if (cfr_config.cfr_enable && cfr_config.cfr_mode == SRSRAN_CFR_THR_MANUAL) { + cfr_config.manual_thr += CFR_THRES_STEP; + for (int i = 0; i < cell.nof_ports; i++) { + if (srsran_cfr_set_threshold(&ifft[i].tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) { + ERROR("Setting the CFR"); + goto exit; + } + } + if (srsran_cfr_set_threshold(&ifft_mbsfn.tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) { + ERROR("Setting the CFR"); + goto exit; + } + printf("CFR Thres. set to %.3f\n", cfr_config.manual_thr); + } else if (cfr_config.cfr_enable && cfr_config.cfr_mode != SRSRAN_CFR_THR_MANUAL) { + cfr_config.max_papr_db += CFR_PAPR_STEP; + for (int i = 0; i < cell.nof_ports; i++) { + if (srsran_cfr_set_papr(&ifft[i].tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) { + ERROR("Setting the CFR"); + goto exit; + } + } + if (srsran_cfr_set_papr(&ifft_mbsfn.tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) { + ERROR("Setting the CFR"); + goto exit; + } + printf("CFR target PAPR set to %.3f\n", cfr_config.max_papr_db); + } + } + + // Decrease the CFR threshold or target PAPR + if (cfr_thr_dec) { + cfr_thr_dec = false; // Reset the flag + if (cfr_config.cfr_enable && cfr_config.cfr_mode == SRSRAN_CFR_THR_MANUAL) { + if (cfr_config.manual_thr - CFR_THRES_STEP >= 0) { + cfr_config.manual_thr -= CFR_THRES_STEP; + for (int i = 0; i < cell.nof_ports; i++) { + if (srsran_cfr_set_threshold(&ifft[i].tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) { + ERROR("Setting the CFR"); + goto exit; + } + } + if (srsran_cfr_set_threshold(&ifft_mbsfn.tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) { + ERROR("Setting the CFR"); + goto exit; + } + printf("CFR Thres. set to %.3f\n", cfr_config.manual_thr); + } + } else if (cfr_config.cfr_enable && cfr_config.cfr_mode != SRSRAN_CFR_THR_MANUAL) { + if (cfr_config.max_papr_db - CFR_PAPR_STEP >= 0) { + cfr_config.max_papr_db -= CFR_PAPR_STEP; + for (int i = 0; i < cell.nof_ports; i++) { + if (srsran_cfr_set_papr(&ifft[i].tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) { + ERROR("Setting the CFR"); + goto exit; + } + } + if (srsran_cfr_set_papr(&ifft_mbsfn.tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) { + ERROR("Setting the CFR"); + goto exit; + } + printf("CFR target PAPR set to %.3f\n", cfr_config.max_papr_db); + } + } + } + srsran_dci_dl_fprint(stdout, &dci_dl, cell.nof_prb); + printf("\nCFR controls:\n"); + printf(" Param | INC | DEC |\n"); + printf("------------+-----+-----+\n"); + printf(" Thres/PAPR | %c | %c |\n", CFR_THRES_UP_KEY, CFR_THRES_DN_KEY); + printf("\n"); if (transmission_mode != SRSRAN_TM1) { printf("\nTransmission mode key table:\n"); printf(" Mode | 1TB | 2TB |\n"); @@ -532,13 +700,15 @@ static int update_radl() printf(" CDD | | z |\n"); printf("Multiplex | q,w,e,r | a,s |\n"); printf("\n"); - printf("Type new MCS index (0-28) or mode key and press Enter: "); + printf("Type new MCS index (0-28) or cfr/mode key and press Enter: "); } else { - printf("Type new MCS index (0-28) and press Enter: "); + printf("Type new MCS index (0-28) or cfr key and press Enter: "); } fflush(stdout); + ret = SRSRAN_SUCCESS; - return 0; +exit: + return ret; } /* Read new MCS from stdin */ @@ -632,6 +802,12 @@ static int update_control() case 'x': transmission_mode = SRSRAN_TM2; break; + case CFR_THRES_UP_KEY: + cfr_thr_inc = true; + break; + case CFR_THRES_DN_KEY: + cfr_thr_dec = true; + break; default: last_mcs_idx = mcs_idx; mcs_idx = strtol(input, NULL, 10); @@ -649,9 +825,9 @@ static int update_control() } else if (n < 0) { // error perror("select"); - return -1; + return SRSRAN_ERROR; } else { - return 0; + return SRSRAN_SUCCESS; } } @@ -725,6 +901,10 @@ int main(int argc, char** argv) #endif parse_args(argc, argv); + if (parse_cfr_args() < SRSRAN_SUCCESS) { + ERROR("Error parsing CFR args"); + exit(-1); + } srsran_use_standard_symbol_size(use_standard_lte_rate); @@ -734,7 +914,7 @@ int main(int argc, char** argv) generate_mcch_table(mch_table, mbsfn_sf_mask); } N_id_2 = cell.id % 3; - sf_n_re = 2 * SRSRAN_CP_NORM_NSYMB * cell.nof_prb * SRSRAN_NRE; + sf_n_re = SRSRAN_SF_LEN_RE(cell.nof_prb, cell.cp); sf_n_samples = 2 * SRSRAN_SLOT_LEN(srsran_symbol_sz(cell.nof_prb)); cell.phich_length = SRSRAN_PHICH_NORM; @@ -850,8 +1030,8 @@ int main(int argc, char** argv) srsran_vec_cf_zero(sf_symbols[0], sf_n_re); if (sf_idx == 0 || sf_idx == 5) { - srsran_pss_put_slot(pss_signal, sf_symbols[0], cell.nof_prb, SRSRAN_CP_NORM); - srsran_sss_put_slot(sf_idx ? sss_signal5 : sss_signal0, sf_symbols[0], cell.nof_prb, SRSRAN_CP_NORM); + srsran_pss_put_slot(pss_signal, sf_symbols[0], cell.nof_prb, cell.cp); + srsran_sss_put_slot(sf_idx ? sss_signal5 : sss_signal0, sf_symbols[0], cell.nof_prb, cell.cp); } /* Copy zeros, SSS, PSS into the rest of antenna ports */ @@ -879,7 +1059,7 @@ int main(int argc, char** argv) srsran_pcfich_encode(&pcfich, &dl_sf, sf_symbols); /* Update DL resource allocation from control port */ - if (update_control()) { + if (update_control() < SRSRAN_SUCCESS) { ERROR("Error updating parameters from control port"); } @@ -998,7 +1178,7 @@ int main(int argc, char** argv) if (!null_file_sink) { /* Apply AWGN */ if (output_file_snr != +INFINITY) { - float var = srsran_convert_dB_to_amplitude(-(output_file_snr + 3.0f)); + float var = srsran_convert_dB_to_power(-output_file_snr); for (int k = 0; k < cell.nof_ports; k++) { srsran_ch_awgn_c(output_buffer[k], output_buffer[k], var, sf_n_samples); } diff --git a/lib/examples/pdsch_ue.c b/lib/examples/pdsch_ue.c index 7d06a8172..cda0b49b4 100644 --- a/lib/examples/pdsch_ue.c +++ b/lib/examples/pdsch_ue.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -286,8 +286,8 @@ void parse_args(prog_args_t* args, int argc, char** argv) args->disable_plots_except_constellation = true; break; case 'v': - srsran_verbose++; - args->verbose = srsran_verbose; + increase_srsran_verbose_level(); + args->verbose = get_srsran_verbose_level(); break; case 'Z': args->decimate = (int)strtol(argv[optind], NULL, 10); @@ -699,16 +699,16 @@ int main(int argc, char** argv) to.tv_usec = 0; /* Set default verbose level */ - srsran_verbose = prog_args.verbose; - int n = select(1, &set, NULL, NULL, &to); + set_srsran_verbose_level(prog_args.verbose); + int n = select(1, &set, NULL, NULL, &to); if (n == 1) { /* If a new line is detected set verbose level to Debug */ if (fgets(input, sizeof(input), stdin)) { - srsran_verbose = SRSRAN_VERBOSE_DEBUG; - pkt_errors = 0; - pkt_total = 0; - nof_detected = 0; - nof_trials = 0; + set_srsran_verbose_level(SRSRAN_VERBOSE_DEBUG); + pkt_errors = 0; + pkt_total = 0; + nof_detected = 0; + nof_trials = 0; } } diff --git a/lib/examples/pssch_ue.c b/lib/examples/pssch_ue.c index cb9b28c68..49030214e 100644 --- a/lib/examples/pssch_ue.c +++ b/lib/examples/pssch_ue.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -188,7 +188,7 @@ void parse_args(prog_args_t* args, int argc, char** argv) args->nof_rx_antennas = (int32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'w': args->disable_plots = true; @@ -232,7 +232,7 @@ int main(int argc, char** argv) parse_args(&prog_args, argc, argv); - FILE* pcap_file = LTE_PCAP_Open(MAC_LTE_DLT, PCAP_FILENAME); + FILE* pcap_file = DLT_PCAP_Open(MAC_LTE_DLT, PCAP_FILENAME); srsran_use_standard_symbol_size(prog_args.use_standard_lte_rates); @@ -303,7 +303,7 @@ int main(int argc, char** argv) cf_t* equalized_sf_buffer = srsran_vec_malloc(sizeof(cf_t) * sf_n_re); // RX - srsran_ofdm_t fft[SRSRAN_MAX_PORTS]; + srsran_ofdm_t fft[SRSRAN_MAX_PORTS] = {}; srsran_ofdm_cfg_t ofdm_cfg = {}; ofdm_cfg.nof_prb = cell_sl.nof_prb; ofdm_cfg.cp = SRSRAN_CP_NORM; @@ -341,7 +341,7 @@ int main(int argc, char** argv) // PSCCH Channel estimation srsran_chest_sl_cfg_t pscch_chest_sl_cfg = {}; srsran_chest_sl_t pscch_chest = {}; - if (srsran_chest_sl_init(&pscch_chest, SRSRAN_SIDELINK_PSCCH, cell_sl, sl_comm_resource_pool) != SRSRAN_SUCCESS) { + if (srsran_chest_sl_init(&pscch_chest, SRSRAN_SIDELINK_PSCCH, cell_sl, &sl_comm_resource_pool) != SRSRAN_SUCCESS) { ERROR("Error in chest PSCCH init"); return SRSRAN_ERROR; } @@ -353,7 +353,7 @@ int main(int argc, char** argv) srsran_chest_sl_cfg_t pssch_chest_sl_cfg = {}; srsran_chest_sl_t pssch_chest = {}; - if (srsran_chest_sl_init(&pssch_chest, SRSRAN_SIDELINK_PSSCH, cell_sl, sl_comm_resource_pool) != SRSRAN_SUCCESS) { + if (srsran_chest_sl_init(&pssch_chest, SRSRAN_SIDELINK_PSSCH, cell_sl, &sl_comm_resource_pool) != SRSRAN_SUCCESS) { ERROR("Error in chest PSSCH init"); return SRSRAN_ERROR; } @@ -546,7 +546,7 @@ clean_exit: if (pcap_file != NULL) { printf("Saving PCAP file to %s\n", PCAP_FILENAME); - LTE_PCAP_Close(pcap_file); + DLT_PCAP_Close(pcap_file); } #ifdef ENABLE_GUI diff --git a/lib/examples/synch_file.c b/lib/examples/synch_file.c index d5160b4d8..6ea3d6e46 100644 --- a/lib/examples/synch_file.c +++ b/lib/examples/synch_file.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -83,7 +83,7 @@ void parse_args(int argc, char** argv) force_cfo = strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/examples/test/CMakeLists.txt b/lib/examples/test/CMakeLists.txt index 3fef66e28..66b4bd11c 100644 --- a/lib/examples/test/CMakeLists.txt +++ b/lib/examples/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/examples/test/iqtests.cmake b/lib/examples/test/iqtests.cmake index 6787908e5..904be55bb 100644 --- a/lib/examples/test/iqtests.cmake +++ b/lib/examples/test/iqtests.cmake @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/examples/usrp_capture.c b/lib/examples/usrp_capture.c index 5b2735492..17ede67fd 100644 --- a/lib/examples/usrp_capture.c +++ b/lib/examples/usrp_capture.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -84,7 +84,7 @@ void parse_args(int argc, char** argv) nof_rx_antennas = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/examples/usrp_capture_sync.c b/lib/examples/usrp_capture_sync.c index 6e509d985..a0fbc6a4d 100644 --- a/lib/examples/usrp_capture_sync.c +++ b/lib/examples/usrp_capture_sync.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -105,7 +105,7 @@ void parse_args(int argc, char** argv) nof_rx_antennas = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/examples/usrp_capture_sync_nbiot.c b/lib/examples/usrp_capture_sync_nbiot.c index d4b4ca8cd..b09faa57f 100644 --- a/lib/examples/usrp_capture_sync_nbiot.c +++ b/lib/examples/usrp_capture_sync_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -77,7 +77,7 @@ void parse_args(int argc, char** argv) nof_subframes = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/examples/usrp_txrx.c b/lib/examples/usrp_txrx.c index a41fa8ec8..9f300b5ac 100644 --- a/lib/examples/usrp_txrx.c +++ b/lib/examples/usrp_txrx.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/examples/zmq_remote_rx.c b/lib/examples/zmq_remote_rx.c index c7458e360..60b4594ff 100644 --- a/lib/examples/zmq_remote_rx.c +++ b/lib/examples/zmq_remote_rx.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -123,7 +123,7 @@ int main(int argc, char** argv) ERROR("Error receiving samples"); exit(-1); } - if (srsran_verbose == SRSRAN_VERBOSE_INFO) { + if (get_srsran_verbose_level() == SRSRAN_VERBOSE_INFO) { printf("Received %d samples from radio\n", n); } @@ -136,7 +136,7 @@ int main(int argc, char** argv) print_cnt = 0; } } else { - if (srsran_verbose == SRSRAN_VERBOSE_INFO) { + if (get_srsran_verbose_level() == SRSRAN_VERBOSE_INFO) { printf("Transmitted %d bytes to ZMQ\n", n); } } @@ -239,7 +239,7 @@ static void parse_args(int argc, char** argv) rf_freq = strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'z': zmq_args = argv[optind]; diff --git a/lib/include/CMakeLists.txt b/lib/include/CMakeLists.txt index 586bda2df..69a857e0e 100644 --- a/lib/include/CMakeLists.txt +++ b/lib/include/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/include/srsran/CMakeLists.txt b/lib/include/srsran/CMakeLists.txt index 666ffb9a4..30578f4b4 100644 --- a/lib/include/srsran/CMakeLists.txt +++ b/lib/include/srsran/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/include/srsran/adt/accumulators.h b/lib/include/srsran/adt/accumulators.h index 50b634416..e5ce9ad16 100644 --- a/lib/include/srsran/adt/accumulators.h +++ b/lib/include/srsran/adt/accumulators.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,6 +39,11 @@ struct rolling_average { } T value() const { return count_ == 0 ? 0 : avg_; } uint32_t count() const { return count_; } + void reset() + { + avg_ = 0; + count_ = 0; + } private: T avg_ = 0; @@ -47,8 +52,10 @@ private: template struct exp_average_fast_start { - exp_average_fast_start(T alpha_, uint32_t start_size = 100) : alpha(alpha_), start_count_size(start_size) + exp_average_fast_start(T alpha_val) : exp_average_fast_start(alpha_val, 1.0 / alpha_val) {} + exp_average_fast_start(T alpha_val, uint32_t start_size) : alpha_(alpha_val), start_count_size(start_size) { + assert(alpha_ < 1); assert(start_size > 0); } void push(T sample) @@ -57,16 +64,18 @@ struct exp_average_fast_start { avg_ += (sample - avg_) / (count + 1); count++; } else { - avg_ = (1 - alpha) * avg_ + alpha * sample; + avg_ = (1 - alpha_) * avg_ + alpha_ * sample; } } - T value() const { return count == 0 ? 0 : avg_; } + T value() const { return count == 0 ? 0 : avg_; } + T alpha() const { return alpha_; } + bool is_exp_average_mode() const { return count >= start_count_size; } private: T avg_ = 0; uint32_t count = 0; uint32_t start_count_size; - T alpha; + T alpha_; }; namespace detail { @@ -121,7 +130,6 @@ private: template struct null_sliding_average { - null_sliding_average(uint32_t N) : window(N, null_value()) {} void push(T sample) { window.push(sample); } void push_hole() { window.push(null_value()); } diff --git a/lib/include/srsran/adt/bounded_bitset.h b/lib/include/srsran/adt/bounded_bitset.h index 86e705ae8..ff1ed063b 100644 --- a/lib/include/srsran/adt/bounded_bitset.h +++ b/lib/include/srsran/adt/bounded_bitset.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,8 +22,8 @@ #ifndef SRSRAN_DYN_BITSET_H #define SRSRAN_DYN_BITSET_H -#include "srsran/common/srsran_assert.h" #include "srsran/srslog/bundled/fmt/format.h" +#include "srsran/support/srsran_assert.h" #include #include #include @@ -372,6 +372,14 @@ public: return get_word_(0); } + void from_uint64(uint64_t v) + { + srsran_assert(nof_words_() == 1, "ERROR: cannot convert bitset of size=%zd to uint64_t", size()); + srsran_assert( + v < (1U << size()), "ERROR: Provided mask=0x%" PRIx64 " does not fit in bitset of size=%zd", v, size()); + buffer[0] = v; + } + template OutputIt to_hex(OutputIt&& mem_buffer) const noexcept { diff --git a/lib/include/srsran/adt/bounded_vector.h b/lib/include/srsran/adt/bounded_vector.h index 417bf9d8d..5aa40abb4 100644 --- a/lib/include/srsran/adt/bounded_vector.h +++ b/lib/include/srsran/adt/bounded_vector.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,7 @@ #define SRSRAN_BOUNDED_VECTOR_H #include "srsran/adt/detail/type_storage.h" -#include "srsran/common/srsran_assert.h" +#include "srsran/support/srsran_assert.h" #include #include #include diff --git a/lib/include/srsran/adt/choice_type.h b/lib/include/srsran/adt/choice_type.h index 39e786a27..469782373 100644 --- a/lib/include/srsran/adt/choice_type.h +++ b/lib/include/srsran/adt/choice_type.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -64,7 +64,7 @@ struct choice_storage_t { } template - void destroy_unsafe() + void destroy_unchecked() { get_unchecked().~U(); }; @@ -80,7 +80,7 @@ struct CopyCtorVisitor { template void operator()(const T& t) { - c->construct_unsafe(t); + c->construct_unchecked(t); } C* c; }; @@ -91,18 +91,18 @@ struct MoveCtorVisitor { template void operator()(T&& t) { - c->construct_unsafe(std::move(t)); + c->construct_unchecked(std::move(t)); } C* c; }; template -struct DtorUnsafeVisitor { - explicit DtorUnsafeVisitor(C* c_) : c(c_) {} +struct DtorUncheckVisitor { + explicit DtorUncheckVisitor(C* c_) : c(c_) {} template void operator()(T& t) { - c->template destroy_unsafe(); + c->template destroy_unchecked(); } C* c; }; @@ -119,12 +119,12 @@ struct tagged_union_t std::size_t type_id; - using base_t::destroy_unsafe; + using base_t::destroy_unchecked; using base_t::get_buffer; using base_t::get_unchecked; template - void construct_emplace_unsafe(Args2&&... args) + void construct_emplace_unchecked(Args2&&... args) { using U2 = typename std::decay::type; static_assert(type_list_contains(), @@ -134,7 +134,7 @@ struct tagged_union_t } template - void construct_unsafe(U&& u) + void construct_unchecked(U&& u) { using U2 = typename std::decay::type; static_assert(type_list_contains(), @@ -143,11 +143,11 @@ struct tagged_union_t new (get_buffer()) U2(std::forward(u)); } - void copy_unsafe(const this_type& other) { visit(CopyCtorVisitor{this}, other); } + void copy_unchecked(const this_type& other) { visit(CopyCtorVisitor{this}, other); } - void move_unsafe(this_type&& other) { visit(MoveCtorVisitor{this}, other); } + void move_unchecked(this_type&& other) { visit(MoveCtorVisitor{this}, other); } - void dtor_unsafe() { visit(choice_details::DtorUnsafeVisitor{this}, *this); } + void dtor_unchecked() { visit(choice_details::DtorUncheckVisitor{this}, *this); } size_t get_type_idx() const { return type_id; } @@ -189,51 +189,51 @@ public: typename = typename std::enable_if::value>::type> explicit choice_t(Args2&&... args) noexcept { - base_t::template construct_emplace_unsafe(std::forward(args)...); + base_t::template construct_emplace_unchecked(std::forward(args)...); } - choice_t(const choice_t& other) noexcept { base_t::copy_unsafe(other); } + choice_t(const choice_t& other) noexcept { base_t::copy_unchecked(other); } - choice_t(choice_t&& other) noexcept { base_t::move_unsafe(std::move(other)); } + choice_t(choice_t&& other) noexcept { base_t::move_unchecked(std::move(other)); } template > choice_t(U&& u) noexcept { - base_t::construct_unsafe(std::forward(u)); + base_t::construct_unchecked(std::forward(u)); } - ~choice_t() { base_t::dtor_unsafe(); } + ~choice_t() { base_t::dtor_unchecked(); } template > choice_t& operator=(U&& u) noexcept { if (not base_t::template is()) { - base_t::dtor_unsafe(); + base_t::dtor_unchecked(); } - base_t::construct_unsafe(std::forward(u)); + base_t::construct_unchecked(std::forward(u)); return *this; } template void emplace(Args2&&... args) noexcept { - base_t::dtor_unsafe(); - base_t::template construct_emplace_unsafe(std::forward(args)...); + base_t::dtor_unchecked(); + base_t::template construct_emplace_unchecked(std::forward(args)...); } choice_t& operator=(const choice_t& other) noexcept { if (this != &other) { - base_t::dtor_unsafe(); - base_t::copy_unsafe(other); + base_t::dtor_unchecked(); + base_t::copy_unchecked(other); } return *this; } choice_t& operator=(choice_t&& other) noexcept { - base_t::dtor_unsafe(); - base_t::move_unsafe(std::move(other)); + base_t::dtor_unchecked(); + base_t::move_unchecked(std::move(other)); return *this; } diff --git a/lib/include/srsran/adt/circular_array.h b/lib/include/srsran/adt/circular_array.h index 6874f48ba..412418228 100644 --- a/lib/include/srsran/adt/circular_array.h +++ b/lib/include/srsran/adt/circular_array.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/circular_buffer.h b/lib/include/srsran/adt/circular_buffer.h index 55c7cee76..c432812b6 100644 --- a/lib/include/srsran/adt/circular_buffer.h +++ b/lib/include/srsran/adt/circular_buffer.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,7 +25,7 @@ #include "srsran/adt/detail/type_storage.h" #include "srsran/adt/expected.h" #include "srsran/adt/pool/pool_utils.h" -#include "srsran/common/srsran_assert.h" +#include "srsran/support/srsran_assert.h" #include #include @@ -291,13 +291,16 @@ public: bool push_blocking(const T& t) { return push_(t, true); } srsran::error_type push_blocking(T&& t) { return push_(std::move(t), true); } bool try_pop(T& obj) { return pop_(obj, false); } - T pop_blocking() + T pop_blocking(bool* success = nullptr) { - T obj{}; - pop_(obj, true); + T obj{}; + bool ret = pop_(obj, true); + if (success != nullptr) { + *success = ret; + } return obj; } - bool pop_wait_until(T& obj, const std::chrono::system_clock::time_point& until) { return pop_(obj, true, &until); } + bool pop_wait_until(T& obj, const std::chrono::steady_clock::time_point& until) { return pop_(obj, true, &until); } void clear() { T obj; @@ -411,7 +414,7 @@ protected: return {}; } - bool pop_(T& obj, bool block, const std::chrono::system_clock::time_point* until = nullptr) + bool pop_(T& obj, bool block, const std::chrono::steady_clock::time_point* until = nullptr) { std::unique_lock lock(mutex); if (not active) { @@ -599,12 +602,6 @@ public: base_t(push_callback, pop_callback, size) {} void set_size(size_t size) { base_t::circ_buffer.set_size(size); } - - template - bool apply_first(const F& func) - { - return base_t::apply_first(func); - } }; } // namespace srsran diff --git a/lib/include/srsran/adt/circular_map.h b/lib/include/srsran/adt/circular_map.h index 9d9b26aee..0e6fa4d94 100644 --- a/lib/include/srsran/adt/circular_map.h +++ b/lib/include/srsran/adt/circular_map.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,7 +24,7 @@ #include "detail/type_storage.h" #include "expected.h" -#include "srsran/common/srsran_assert.h" +#include "srsran/support/srsran_assert.h" #include namespace srsran { @@ -99,7 +99,12 @@ public: { public: const_iterator() = default; - const_iterator(const static_circular_map* map, size_t idx_) : ptr(map), idx(idx_) {} + const_iterator(const static_circular_map* map, size_t idx_) : ptr(map), idx(idx_) + { + if (idx < ptr->capacity() and not ptr->present[idx]) { + ++(*this); + } + } const_iterator& operator++() { diff --git a/lib/include/srsran/adt/detail/index_sequence.h b/lib/include/srsran/adt/detail/index_sequence.h index 9825ab810..6bbe28829 100644 --- a/lib/include/srsran/adt/detail/index_sequence.h +++ b/lib/include/srsran/adt/detail/index_sequence.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/detail/type_storage.h b/lib/include/srsran/adt/detail/type_storage.h index 9f98bef2b..117a57b5c 100644 --- a/lib/include/srsran/adt/detail/type_storage.h +++ b/lib/include/srsran/adt/detail/type_storage.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -79,11 +79,9 @@ void copy_if_present_helper(type_storage& lhs, { if (lhs_present and rhs_present) { lhs.get() = rhs.get(); - } - if (lhs_present) { + } else if (lhs_present) { lhs.destroy(); - } - if (rhs_present) { + } else if (rhs_present) { lhs.copy_ctor(rhs); } } @@ -96,11 +94,9 @@ void move_if_present_helper(type_storage& lhs, { if (lhs_present and rhs_present) { lhs.move_assign(std::move(rhs)); - } - if (lhs_present) { + } else if (lhs_present) { lhs.destroy(); - } - if (rhs_present) { + } else if (rhs_present) { lhs.move_ctor(std::move(rhs)); } } diff --git a/lib/include/srsran/adt/detail/type_utils.h b/lib/include/srsran/adt/detail/type_utils.h index 961bc1ee3..e34a74f1a 100644 --- a/lib/include/srsran/adt/detail/type_utils.h +++ b/lib/include/srsran/adt/detail/type_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/expected.h b/lib/include/srsran/adt/expected.h index 81b7f3be3..af16bafa3 100644 --- a/lib/include/srsran/adt/expected.h +++ b/lib/include/srsran/adt/expected.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,7 +22,7 @@ #ifndef SRSRAN_EXPECTED_H #define SRSRAN_EXPECTED_H -#include "srsran/common/srsran_assert.h" +#include "srsran/support/srsran_assert.h" #include #include diff --git a/lib/include/srsran/adt/fsm.h b/lib/include/srsran/adt/fsm.h index a385cd5ae..bb63801a3 100644 --- a/lib/include/srsran/adt/fsm.h +++ b/lib/include/srsran/adt/fsm.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -341,6 +341,9 @@ public: constexpr static void (Derived::*react_fn)(SrcState&, const Event&) = ReactFn; constexpr static bool (Derived::*guard_fn)(SrcState&, const Event&) = GuardFn; + // ignore warning "never nullptr" for template specialization w/wo defaults for ReactFn or GuardFn + _Pragma("GCC diagnostic push"); // save current diagnostic config + _Pragma("GCC diagnostic ignored \"-Waddress\""); // ignore -Waddress static bool react(derived_view* f, src_state_t& s, const event_t& ev) { if (guard_fn == nullptr or (f->*guard_fn)(s, ev)) { @@ -351,6 +354,7 @@ public: } return false; } + _Pragma("GCC diagnostic pop"); // restore diagnostic config template using is_match = std::is_same, type_list >; @@ -372,6 +376,9 @@ public: constexpr static void (Derived::*react_fn)(const Event&) = ReactFn; constexpr static bool (Derived::*guard_fn)(const Event&) = GuardFn; + // ignore warning "never nullptr" for template specialization w/wo defaults for ReactFn or GuardFn + _Pragma("GCC diagnostic push"); // save current diagnostic config + _Pragma("GCC diagnostic ignored \"-Waddress\""); // ignore -Waddress template static bool react(derived_view* f, SrcState& s, const event_t& ev) { @@ -383,6 +390,7 @@ public: } return false; } + _Pragma("GCC diagnostic pop"); // restore diagnostic config template using is_match = std::is_same; diff --git a/lib/include/srsran/adt/interval.h b/lib/include/srsran/adt/interval.h index def72095c..0e98521df 100644 --- a/lib/include/srsran/adt/interval.h +++ b/lib/include/srsran/adt/interval.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,8 +22,8 @@ #ifndef SRSRAN_INTERVAL_H #define SRSRAN_INTERVAL_H -#include "srsran/common/srsran_assert.h" #include "srsran/srslog/bundled/fmt/format.h" +#include "srsran/support/srsran_assert.h" #include #include #include @@ -86,6 +86,17 @@ public: bool contains(T point) const { return start_ <= point and point < stop_; } + interval& intersect(const interval& other) + { + if (not overlaps(other)) { + *this = interval{}; + } else { + start_ = std::max(start(), other.start()); + stop_ = std::min(stop(), other.stop()); + } + return *this; + } + private: T start_; T stop_; diff --git a/lib/include/srsran/adt/intrusive_list.h b/lib/include/srsran/adt/intrusive_list.h index beb798524..ede8721af 100644 --- a/lib/include/srsran/adt/intrusive_list.h +++ b/lib/include/srsran/adt/intrusive_list.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/move_callback.h b/lib/include/srsran/adt/move_callback.h index 8fdc10a2b..1cc7636ed 100644 --- a/lib/include/srsran/adt/move_callback.h +++ b/lib/include/srsran/adt/move_callback.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,7 @@ #define SRSRAN_MOVE_CALLBACK_H #include "detail/type_storage.h" -#include "srsran/common/srsran_assert.h" +#include "srsran/support/srsran_assert.h" #include #include #include @@ -69,7 +69,7 @@ public: constexpr empty_table_t() = default; R call(void* src, Args... args) const final { - srsran_terminate("ERROR: bad function call (cause: function ptr is empty)"); + srsran_assertion_failure("bad function call (cause: function ptr is empty)"); } void move(void* src, void* dest) const final {} void dtor(void* src) const final {} diff --git a/lib/include/srsran/adt/observer.h b/lib/include/srsran/adt/observer.h index 75a178f83..e6b86601b 100644 --- a/lib/include/srsran/adt/observer.h +++ b/lib/include/srsran/adt/observer.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,8 +29,8 @@ namespace srsran { -using observer_id = std::size_t; -const size_t invalid_observer_id = std::numeric_limits::max(); +using observer_id = std::size_t; +const std::size_t invalid_observer_id = std::numeric_limits::max(); template class observer; @@ -93,7 +93,7 @@ public: template observer_id subscribe(Args2&&... args) { - size_t id = 0; + std::size_t id = 0; for (auto& slot : observers) { if (not static_cast(slot)) { // empty slot found @@ -117,9 +117,9 @@ public: return false; } - size_t nof_observers() const + std::size_t nof_observers() const { - size_t count = 0; + std::size_t count = 0; for (auto& slot : observers) { count += static_cast(slot) ? 1 : 0; } diff --git a/lib/include/srsran/adt/optional.h b/lib/include/srsran/adt/optional.h index 9d3db2d6f..d80c02629 100644 --- a/lib/include/srsran/adt/optional.h +++ b/lib/include/srsran/adt/optional.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,7 @@ #define SRSRAN_OPTIONAL_H #include "detail/type_storage.h" -#include "srsran/common/srsran_assert.h" +#include "srsran/support/srsran_assert.h" namespace srsran { @@ -31,7 +31,9 @@ template class optional { public: - optional() : has_val_(false) {} + using value_type = T; + + optional() : has_val_(false), empty() {} optional(const T& t) : has_val_(true) { storage.emplace(t); } optional(T&& t) : has_val_(true) { storage.emplace(std::move(t)); } optional(const optional& other) : has_val_(other.has_value()) @@ -105,8 +107,11 @@ public: } private: - bool has_val_; - detail::type_storage storage; + bool has_val_; + union { + char empty; + detail::type_storage storage; + }; }; template diff --git a/lib/include/srsran/adt/optional_array.h b/lib/include/srsran/adt/optional_array.h new file mode 100644 index 000000000..5763c03c3 --- /dev/null +++ b/lib/include/srsran/adt/optional_array.h @@ -0,0 +1,429 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_OPTIONAL_ARRAY_H +#define SRSRAN_OPTIONAL_ARRAY_H + +#include "optional.h" +#include "span.h" +#include "srsran/support/srsran_assert.h" +#include + +namespace srsran { + +namespace detail { + +template +class base_optional_span +{ + using base_t = base_optional_span; + using T = typename Vec::value_type::value_type; + +protected: + template + class iterator_impl + { + using It = iterator_impl; + using parent_t = typename std::conditional::value, const base_t, base_t>::type; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = Obj; + using difference_type = std::ptrdiff_t; + using pointer = Obj*; + using reference = Obj&; + + iterator_impl() = default; + iterator_impl(parent_t* parent_, size_t idx_) : parent(parent_), idx(idx_) + { + if (idx < parent->vec.size() and not parent->contains(idx)) { + ++(*this); + } + } + + It& operator++() + { + while (++idx < parent->vec.size() and not parent->contains(idx)) { + } + return *this; + } + It& operator--() + { + while (--idx < parent->vec.size() and not parent->contains(idx)) { + } + return *this; + } + + reference operator*() { return parent->operator[](idx); } + pointer operator->() { return &parent->operator[](idx); } + + bool operator==(const It& other) const { return idx == other.idx and parent == other.parent; } + bool operator!=(const It& other) const { return not(*this == other); } + + private: + friend base_t; + + parent_t* parent = nullptr; + size_t idx = std::numeric_limits::max(); + }; + + size_t nof_elems = 0; + Vec vec; + +public: + using value_type = T; + using iterator = iterator_impl; + using const_iterator = iterator_impl; + + base_optional_span() = default; + base_optional_span(Vec&& v, size_t nof_elems_) : vec(std::move(v)), nof_elems(nof_elems_) {} + base_optional_span(const Vec& v, size_t nof_elems_) : vec(v), nof_elems(nof_elems_) {} + + // Find first position that is empty + size_t find_first_empty(size_t start_guess = 0) + { + if (nof_elems == vec.size()) { + return vec.size(); + } + for (size_t i = start_guess; i < vec.size(); ++i) { + if (not vec[i].has_value()) { + return i; + } + } + return vec.size(); + } + + bool contains(size_t idx) const { return idx < vec.size() and vec[idx].has_value(); } + + T& operator[](size_t idx) { return *vec[idx]; } + const T& operator[](size_t idx) const { return *vec[idx]; } + + bool empty() const { return nof_elems == 0; } + size_t size() const { return nof_elems; } + + iterator begin() { return iterator{this, 0}; } + iterator end() { return iterator{this, vec.size()}; } + const_iterator begin() const { return const_iterator{this, 0}; } + const_iterator end() const { return const_iterator{this, vec.size()}; } + + void clear() + { + this->nof_elems = 0; + for (auto& e : *this) { + e.reset(); + } + } + void erase(size_t idx) + { + srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size()); + if (this->contains(idx)) { + this->nof_elems--; + this->vec[idx].reset(); + } + } + void erase(iterator it) { erase(it.idx); } + + template + void insert(size_t idx, U&& u) + { + srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size()); + this->nof_elems += this->contains(idx) ? 0 : 1; + this->vec[idx] = std::forward(u); + } + + template + void emplace(size_t idx, Args&&... args) + { + srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size()); + if (not this->contains(idx)) { + this->nof_elems++; + } + this->vec[idx].emplace(std::forward(args)...); + } +}; + +template +class base_optional_vector : public base_optional_span +{ + using base_t = base_optional_span; + +public: + using value_type = typename base_optional_span::value_type; + using iterator = typename base_optional_span::iterator; + using const_iterator = typename base_optional_span::const_iterator; + + base_optional_vector() = default; + base_optional_vector(const base_optional_vector&) = default; + base_optional_vector(base_optional_vector&& other) noexcept : base_t(std::move(other.vec), other.size()) + { + other.nof_elems = 0; + } + base_optional_vector& operator=(const base_optional_vector&) = default; + base_optional_vector& operator =(base_optional_vector&& other) noexcept + { + this->vec = std::move(other.vec); + this->nof_elems = other.nof_elems; + other.nof_elems = 0; + return *this; + } +}; + +} // namespace detail + +/** + * Array of optional items. The iteration is in order of indexes and correctly skips non-present items + * Pointer/References/Iterators remain valid throughout the object lifetime + * NOTE: The sorted iteration and pointer validation guarantees add some overhead if the array is very fragmented + * @tparam T type of objects + * @tparam N static size of max nof items + */ +template +class optional_array : public detail::base_optional_vector, N> > +{}; + +/** + * Contrarily to optional_array, this class may allocate and cause pointer/reference/iterator invalidation. + * However, the indexes will remain valid. + * @tparam T + */ +template +class optional_vector : public detail::base_optional_vector > > +{ + using base_t = detail::base_optional_vector > >; + +public: + /// May allocate and cause pointer invalidation + template + void insert(size_t idx, U&& u) + { + if (idx >= this->vec.size()) { + this->vec.resize(idx + 1); + } + base_t::insert(idx, std::forward(u)); + } + + /// May allocate and cause pointer invalidation + template + void emplace(size_t idx, Args&&... args) + { + if (idx >= this->vec.size()) { + this->vec.resize(idx + 1); + } + base_t::emplace(idx, std::forward(args)...); + } +}; + +template +class optional_span : public detail::base_optional_span > > +{ + using base_t = detail::base_optional_span > >; + +public: + template + optional_span(const optional_array& ar) : base_t::vec(ar) + {} + optional_span(const optional_vector& ar) : base_t::vec(ar) {} +}; + +namespace detail { + +template +class base_split_optional_span +{ +protected: + using presence_type = typename std::conditional::value, const bool, bool>::type; + + T* ptr = nullptr; + presence_type* present_ptr = nullptr; + size_t len = 0; + + template + class iterator_impl + { + using It = iterator_impl; + using Parent = typename std:: + conditional::value, const base_split_optional_span, base_split_optional_span >::type; + + public: + using iterator_category = std::forward_iterator_tag; + using value_type = Obj; + using difference_type = std::ptrdiff_t; + using pointer = Obj*; + using reference = Obj&; + + iterator_impl() = default; + iterator_impl(Parent* parent_, size_t idx_) : parent(parent_), idx(idx_) + { + if (idx < parent->len and not parent->contains(idx)) { + ++(*this); + } + } + + It& operator++() + { + while (++idx < parent->len and not parent->contains(idx)) { + } + return *this; + } + It& operator--() + { + while (--idx < parent->len and not parent->contains(idx)) { + } + return *this; + } + + reference operator*() { return parent->operator[](idx); } + pointer operator->() { return &parent->operator[](idx); } + + bool operator==(const It& other) const { return idx == other.idx and parent == other.parent; } + bool operator!=(const It& other) const { return not(*this == other); } + + size_t get_idx() const { return idx; } + + private: + Parent* parent = nullptr; + size_t idx = std::numeric_limits::max(); + }; + +public: + using value_type = T; + using iterator = iterator_impl; + using const_iterator = iterator_impl; + + constexpr base_split_optional_span() = default; + template + constexpr base_split_optional_span(value_type (&arr)[N], presence_type (&present)[N]) noexcept : ptr(arr), + present_ptr(present), + len(N) + {} + constexpr base_split_optional_span(value_type* arr, presence_type* present, size_t N) : + ptr(arr), present_ptr(present), len(N) + {} + + bool contains(size_t idx) const { return idx < len and present_ptr[idx]; } + bool empty() const + { + for (size_t i = 0; i < len; ++i) { + if (present_ptr[i]) { + return false; + } + } + return true; + } + size_t size() const + { + size_t c = 0; + for (size_t i = 0; i < len; ++i) { + c += present_ptr[i] ? 1 : 0; + } + return c; + } + size_t capacity() const { return len; } + + const T& operator[](size_t idx) const { return ptr[idx]; } + T& operator[](size_t idx) { return ptr[idx]; } + const T& at(size_t idx) const + { + srsran_assert(contains(idx), "Access to inexistent element of index=%zd", idx); + return ptr[idx]; + } + T& at(size_t idx) + { + srsran_assert(this->contains(idx), "Access to inexistent element of index=%zd", idx); + return this->ptr[idx]; + } + + const_iterator begin() const { return const_iterator(this, 0); } + const_iterator end() const { return const_iterator(this, len); } + iterator begin() { return iterator(this, 0); } + iterator end() { return iterator(this, this->len); } + + // Find first position that is empty + size_t find_first_empty(size_t start_guess = 0) { return begin().get_idx(); } +}; + +} // namespace detail + +template +class split_optional_span : public detail::base_split_optional_span +{ + using base_t = detail::base_split_optional_span; + +public: + using value_type = T; + using const_iterator = typename base_t::const_iterator; + using iterator = typename base_t::iterator; + + using base_t::base_t; + + template + void insert(size_t idx, U&& u) + { + srsran_assert(idx < this->len, "Out-of-bounds access to array: %zd>=%zd", idx, this->len); + this->present_ptr[idx] = true; + this->ptr[idx] = std::forward(u); + } + void erase(size_t idx) + { + srsran_assert(idx < this->len, "Out-of-bounds access to array: %zd>=%zd", idx, this->len); + this->present_ptr[idx] = false; + } + void erase(iterator it) { erase(it.get_idx()); } + void clear() + { + for (size_t i = 0; i < this->len; ++i) { + this->present_ptr[i] = false; + } + } +}; + +template +class split_optional_span : public detail::base_split_optional_span +{ + using base_t = detail::base_split_optional_span; + using presence_type = typename base_t::presence_type; + +public: + using value_type = const U; + using const_iterator = typename base_t::const_iterator; + + using base_t::base_t; +}; + +template +split_optional_span +make_optional_span(T* array, + typename std::conditional::value, const bool, bool>::type* present, + size_t N) +{ + return split_optional_span(array, present, N); +} +template +split_optional_span + make_optional_span(T (&array)[N], + typename std::conditional::value, const bool, bool>::type (&present)[N]) +{ + return split_optional_span(array, present); +} + +} // namespace srsran + +#endif // SRSRAN_OPTIONAL_ARRAY_H diff --git a/lib/include/srsran/adt/pool/batch_mem_pool.h b/lib/include/srsran/adt/pool/batch_mem_pool.h index dbe7b327e..9acec1208 100644 --- a/lib/include/srsran/adt/pool/batch_mem_pool.h +++ b/lib/include/srsran/adt/pool/batch_mem_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,8 +24,8 @@ #include "memblock_cache.h" #include "pool_utils.h" -#include "srsran/common/srsran_assert.h" #include "srsran/common/thread_pool.h" +#include "srsran/support/srsran_assert.h" #include #include @@ -130,7 +130,7 @@ public: void* node = grow_pool.allocate_node(); if (grow_pool.size() < batch_threshold) { - allocate_batch_in_background_unlocked(); + allocate_batch_in_background_nolock(); } return node; } @@ -155,7 +155,7 @@ public: } private: - void allocate_batch_in_background_unlocked() + void allocate_batch_in_background_nolock() { if (state->dispatched) { // new batch allocation already ongoing diff --git a/lib/include/srsran/adt/pool/cached_alloc.h b/lib/include/srsran/adt/pool/cached_alloc.h new file mode 100644 index 000000000..de2de3e80 --- /dev/null +++ b/lib/include/srsran/adt/pool/cached_alloc.h @@ -0,0 +1,125 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_CACHED_ALLOC_H +#define SRSRAN_CACHED_ALLOC_H + +#include "../intrusive_list.h" +#include "memblock_cache.h" +#include +#include + +namespace srsran { + +/** + * Custom Allocator that caches deallocated memory blocks in a stack to be reused in future allocations. + * This minimizes the number of new/delete calls, when the rate of insertions/removals match (e.g. a queue) + * This allocator is not thread-safe. It assumes the container is being used in a single-threaded environment, + * or being mutexed when altered, which is a reasonable assumption + * @tparam T object type + */ +template +class cached_alloc : public std::allocator +{ + struct memblock_t : public intrusive_double_linked_list_element<> { + explicit memblock_t(size_t sz) : block_size(sz) {} + size_t block_size; + }; + const size_t min_n = (sizeof(memblock_t) + sizeof(T) - 1) / sizeof(T); + +public: + using value_type = T; + + ~cached_alloc() + { + while (not free_list.empty()) { + memblock_t& b = free_list.front(); + free_list.pop_front(); + size_t n = b.block_size; + b.~memblock_t(); + std::allocator::deallocate(reinterpret_cast(&b), n); + } + } + cached_alloc() = default; + cached_alloc(cached_alloc&& other) noexcept = default; + cached_alloc(const cached_alloc& other) noexcept : cached_alloc() {} + template + explicit cached_alloc(const cached_alloc& other) noexcept : cached_alloc() + { + // start empty, as cached blocks cannot be copied + } + cached_alloc& operator=(const cached_alloc& other) noexcept { return *this; } + cached_alloc& operator=(cached_alloc&& other) noexcept = default; + + T* allocate(size_t n, const void* ptr = nullptr) + { + size_t req_n = std::max(n, min_n); + for (memblock_t& b : free_list) { + if (b.block_size == req_n) { + free_list.pop(&b); + b.~memblock_t(); + return reinterpret_cast(&b); + } + } + return std::allocator::allocate(req_n, ptr); + } + void deallocate(T* p, size_t n) noexcept + { + size_t req_n = std::max(n, min_n); + auto* block = reinterpret_cast(p); + new (block) memblock_t(req_n); + free_list.push_front(block); + } + + template + struct rebind { + using other = cached_alloc; + }; + +private: + intrusive_double_linked_list free_list; +}; + +} // namespace srsran + +template +bool operator==(const srsran::cached_alloc& lhs, const srsran::cached_alloc& rhs) noexcept +{ + return &lhs == &rhs; +} + +template +bool operator!=(const srsran::cached_alloc& lhs, const srsran::cached_alloc& rhs) noexcept +{ + return not(lhs == rhs); +} + +namespace srsran { + +template +using deque = std::deque >; + +template +using queue = std::queue >; + +} // namespace srsran + +#endif // SRSRAN_CACHED_ALLOC_H diff --git a/lib/include/srsran/adt/pool/circular_stack_pool.h b/lib/include/srsran/adt/pool/circular_stack_pool.h index b1681a1e5..78775ef87 100644 --- a/lib/include/srsran/adt/pool/circular_stack_pool.h +++ b/lib/include/srsran/adt/pool/circular_stack_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -116,6 +116,22 @@ private: srslog::basic_logger& logger; }; +template +unique_pool_ptr make_pool_obj_with_fallback(circular_stack_pool& pool, size_t key, Args&&... args) +{ + void* block = pool.allocate(key, sizeof(T), alignof(T)); + if (block == nullptr) { + // allocated with "new" as a fallback + return unique_pool_ptr(new T(std::forward(args)...), std::default_delete()); + } + // allocation using memory pool was successful + new (block) T(std::forward(args)...); + return unique_pool_ptr(static_cast(block), [key, &pool](T* ptr) { + ptr->~T(); + pool.deallocate(key, ptr); + }); +} + } // namespace srsran #endif // SRSRAN_CIRCULAR_MAP_STACK_POOL_H diff --git a/lib/include/srsran/adt/pool/fixed_size_pool.h b/lib/include/srsran/adt/pool/fixed_size_pool.h index eecbc3d6c..38b4b52ca 100644 --- a/lib/include/srsran/adt/pool/fixed_size_pool.h +++ b/lib/include/srsran/adt/pool/fixed_size_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/pool/linear_allocator.h b/lib/include/srsran/adt/pool/linear_allocator.h index 960a9edee..23062177b 100644 --- a/lib/include/srsran/adt/pool/linear_allocator.h +++ b/lib/include/srsran/adt/pool/linear_allocator.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,7 @@ #define SRSRAN_LINEAR_ALLOCATOR_H #include "pool_utils.h" -#include "srsran/common/srsran_assert.h" +#include "srsran/support/srsran_assert.h" namespace srsran { diff --git a/lib/include/srsran/adt/pool/mem_pool.h b/lib/include/srsran/adt/pool/mem_pool.h index 2a2cb1cab..7c8a333d4 100644 --- a/lib/include/srsran/adt/pool/mem_pool.h +++ b/lib/include/srsran/adt/pool/mem_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/pool/memblock_cache.h b/lib/include/srsran/adt/pool/memblock_cache.h index 5df93890a..7e9dcd935 100644 --- a/lib/include/srsran/adt/pool/memblock_cache.h +++ b/lib/include/srsran/adt/pool/memblock_cache.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/pool/obj_pool.h b/lib/include/srsran/adt/pool/obj_pool.h index 2d1c0a161..32650e88c 100644 --- a/lib/include/srsran/adt/pool/obj_pool.h +++ b/lib/include/srsran/adt/pool/obj_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/pool/pool_interface.h b/lib/include/srsran/adt/pool/pool_interface.h index 4f842ce1d..623682369 100644 --- a/lib/include/srsran/adt/pool/pool_interface.h +++ b/lib/include/srsran/adt/pool/pool_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/pool/pool_utils.h b/lib/include/srsran/adt/pool/pool_utils.h index f04f781bd..a85737793 100644 --- a/lib/include/srsran/adt/pool/pool_utils.h +++ b/lib/include/srsran/adt/pool/pool_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/scope_exit.h b/lib/include/srsran/adt/scope_exit.h index 50b9a5554..5ad6b6731 100644 --- a/lib/include/srsran/adt/scope_exit.h +++ b/lib/include/srsran/adt/scope_exit.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -69,7 +69,7 @@ detail::scope_exit::type> make_scope_exit(Callable return detail::scope_exit::type>{std::forward(callable)}; } -#define DEFER(FUNC) auto on_exit_call = make_scope_exit([&]() { FUNC }) +#define DEFER(FUNC) auto on_exit_call##__LINE__ = srsran::make_scope_exit([&]() { FUNC }) } // namespace srsran diff --git a/lib/include/srsran/adt/singleton.h b/lib/include/srsran/adt/singleton.h index 9ca4d7820..817609c12 100644 --- a/lib/include/srsran/adt/singleton.h +++ b/lib/include/srsran/adt/singleton.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/adt/span.h b/lib/include/srsran/adt/span.h index f2567980f..bbdf26472 100644 --- a/lib/include/srsran/adt/span.h +++ b/lib/include/srsran/adt/span.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/asn1_utils.h b/lib/include/srsran/asn1/asn1_utils.h index d74108149..40011acb2 100644 --- a/lib/include/srsran/asn1/asn1_utils.h +++ b/lib/include/srsran/asn1/asn1_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,17 +22,14 @@ #ifndef SRSASN_COMMON_UTILS_H #define SRSASN_COMMON_UTILS_H -#include "srsran/common/srsran_assert.h" +#include "srsran/common/buffer_pool.h" #include "srsran/srslog/srslog.h" -#include +#include "srsran/support/srsran_assert.h" #include #include #include -#include #include -#include #include -#include namespace asn1 { @@ -151,6 +148,7 @@ public: int distance() const; int distance_bytes(uint8_t* ref_ptr) const; int distance_bytes() const; + int distance_bytes_end() const; template SRSASN_CODE unpack(T& val, uint32_t n_bits) @@ -204,8 +202,12 @@ public: { size_ = nof_items; cap_ = nof_items; - data_ = new T[cap_]; - std::copy(ptr, ptr + size_, data_); + if (ptr != NULL) { + data_ = new T[cap_]; + std::copy(ptr, ptr + size_, data_); + } else { + data_ = NULL; + } } ~dyn_array() { @@ -235,11 +237,13 @@ public: size_ = new_size; return; } + T* old_data = data_; cap_ = new_size > new_cap ? new_size : new_cap; if (cap_ > 0) { data_ = new T[cap_]; if (old_data != NULL) { + srsran_assert(cap_ > size_, "Old size larger than new capacity in dyn_array\n"); std::copy(&old_data[0], &old_data[size_], data_); } } else { @@ -260,7 +264,7 @@ public: std::copy(it + 1, end(), it); size_--; - return it + 1; + return it; } bool operator==(const dyn_array& other) const { @@ -295,7 +299,8 @@ public: using iterator = T*; using const_iterator = const T*; - explicit bounded_array(uint32_t size_ = 0) : data_(), current_size(size_) {} + bounded_array() : data_(), current_size(0) {} + explicit bounded_array(uint32_t size_) : data_(), current_size(size_) {} static uint32_t capacity() { return MAX_N; } uint32_t size() const { return current_size; } T& operator[](uint32_t idx) { return data_[idx]; } @@ -305,6 +310,7 @@ public: return size() == other.size() and std::equal(data_, data_ + size(), other.data_); } void resize(uint32_t new_size) { current_size = new_size; } + void clear() { resize(0); } void push_back(const T& elem) { if (current_size >= MAX_N) { @@ -605,7 +611,7 @@ public: IntType value; integer() = default; integer(IntType value_) : value(value_) {} - operator IntType() { return value; } + operator IntType() const { return value; } SRSASN_CODE pack(bit_ref& bref) const { return pack_integer(bref, value, lb, ub, has_ext, is_aligned); } SRSASN_CODE unpack(cbit_ref& bref) { return unpack_integer(value, bref, lb, ub, has_ext, is_aligned); } }; @@ -866,7 +872,8 @@ public: static const uint32_t lb = LB, ub = UB; static const bool has_ext = ext, is_aligned = aligned; - explicit bitstring(uint32_t siz_ = lb) { resize(siz_); } + bitstring() { resize(lb); } + explicit bitstring(uint32_t siz_) { resize(siz_); } explicit bitstring(const std::string& s) { resize(s.size()); @@ -929,6 +936,17 @@ public: return *this; } + this_type& from_number(uint64_t val, uint32_t nof_bits) + { + if (nof_bits > UB) { + log_error("The provided bitstring value %ld does not fit the bounds [%d, %d]", val, uint32_t(lb), uint32_t(ub)); + return *this; + } + resize(nof_bits); + bitstring_utils::from_number(data(), val, length()); + return *this; + } + // packers / unpackers SRSASN_CODE pack(bit_ref& bref) const { @@ -1314,11 +1332,13 @@ public: ~varlength_field_pack_guard(); private: - bit_ref brefstart; - // bit_ref bref0; - bit_ref* bref_tracker; - uint8_t buffer[2048]; - bool align; + using byte_array_t = std::array; + using byte_array_ptr = srsran::any_pool_ptr; + + bit_ref brefstart; + bit_ref* bref_tracker; + byte_array_ptr buffer_ptr; + bool align; }; class varlength_field_unpack_guard @@ -1365,6 +1385,33 @@ private: separator_t sep; }; +template +inline auto to_json(json_writer& j, const T& obj) -> decltype(obj.to_json(j)) +{ + obj.to_json(j); +} + +template +inline auto to_json(json_writer& j, const T& obj) -> decltype(j.write_str(obj.to_string())) +{ + j.write_str(obj.to_string()); +} + +template +inline void to_json(json_writer& j, const asn1::dyn_array& lst) +{ + j.start_array(); + for (const auto& o : lst) { + to_json(j, o); + } + j.end_array(); +} + +inline void to_json(json_writer& j, int64_t number) +{ + j.write_int(number); +} + /******************* Test pack/unpack *******************/ @@ -1410,6 +1457,423 @@ int test_pack_unpack_consistency(const Msg& msg) return SRSASN_SUCCESS; } +/************************ + General Layer Types +************************/ + +/// Enumerated used in RRC and RRC NR that distinguishes Release and Setup modes +struct setup_release_opts { + enum options { release, setup, nulltype } value; + + const char* to_string() const + { + static const char* options[] = {"release", "setup"}; + return convert_enum_idx(options, 2, value, "setup_release_c::types"); + } +}; +using setup_release_e = enumerated; + +// SetupRelease{ElementTypeParam} ::= CHOICE +template +struct setup_release_c { + using types_opts = setup_release_opts; + using types = setup_release_e; + + // choice methods + setup_release_c() = default; + void set(typename types::options e = types::nulltype) { type_ = e; } + types type() const { return type_; } + SRSASN_CODE pack(bit_ref& bref) const + { + type_.pack(bref); + switch (type_.value) { + case types::release: + break; + case types::setup: + HANDLE_CODE(c.pack(bref)); + break; + default: + log_invalid_choice_id(type_, "setup_release_c"); + return SRSASN_ERROR_ENCODE_FAIL; + } + return SRSASN_SUCCESS; + } + SRSASN_CODE unpack(cbit_ref& bref) + { + types e; + e.unpack(bref); + set(e); + switch (type_.value) { + case types::release: + break; + case types::setup: + HANDLE_CODE(c.unpack(bref)); + break; + default: + log_invalid_choice_id(type_, "setup_release_c"); + return SRSASN_ERROR_DECODE_FAIL; + } + return SRSASN_SUCCESS; + } + void to_json(json_writer& j) const + { + j.start_obj(); + switch (type_.value) { + case types::release: + break; + case types::setup: + asn1::to_json(j, setup()); + break; + default: + log_invalid_choice_id(type_, "setup_release_c"); + } + j.end_obj(); + } + // getters + bool is_setup() const { return type_.value == setup_release_opts::setup; } + T& setup() + { + assert_choice_type(types::setup, type_, "SetupRelease"); + return c; + } + const T& setup() const + { + assert_choice_type(types::setup, type_, "SetupRelease"); + return c; + } + void set_release() { set(types::release); } + T& set_setup() + { + set(types::setup); + return c; + } + +private: + types type_; + T c; +}; + +// Criticality ::= ENUMERATED +struct crit_opts { + enum options { reject, ignore, notify, nulltype } value; + const char* to_string() const + { + static const char* options[] = {"reject", "ignore", "notify"}; + return convert_enum_idx(options, 3, value, "crit_e"); + } +}; +typedef enumerated crit_e; + +// Presence ::= ENUMERATED +struct presence_opts { + enum options { optional, conditional, mandatory, nulltype } value; + + const char* to_string() const + { + static const char* options[] = {"optional", "conditional", "mandatory"}; + return convert_enum_idx(options, 3, value, "presence_e"); + } +}; +typedef enumerated presence_e; + +namespace detail { + +template +struct ie_field_value_item { + using obj_set_type = IEsSetParam; + using value_type = typename IEsSetParam::value_c; + const char* item_name() const { return "value"; } + void set_item(uint32_t id) { item = IEsSetParam::get_value(id); } + +protected: + value_type item; +}; + +template +struct ie_field_ext_item { + using obj_set_type = ExtensionSetParam; + using value_type = typename ExtensionSetParam::ext_c; + const char* item_name() const { return "extension"; } + void set_item(uint32_t id) { item = ExtensionSetParam::get_ext(id); } + +protected: + value_type item; +}; + +template +struct base_ie_field : public IEItem { + using obj_set_type = typename IEItem::obj_set_type; + using value_type = typename IEItem::value_type; + + uint32_t id() const { return obj_set_type::idx_to_id(value().type().value); } + crit_e crit() const { return obj_set_type::get_crit(id()); } + value_type& value() { return this->item; } + const value_type& value() const { return this->item; } + + value_type* operator->() { return &value(); } + const value_type* operator->() const { return &value(); } + value_type& operator*() { return value(); } + const value_type& operator*() const { return value(); } + + SRSASN_CODE pack(bit_ref& bref) const + { + HANDLE_CODE(pack_integer(bref, id(), (uint32_t)0u, (uint32_t)65535u, false, true)); + HANDLE_CODE(crit().pack(bref)); + HANDLE_CODE(value().pack(bref)); + return SRSASN_SUCCESS; + } + SRSASN_CODE unpack(cbit_ref& bref) + { + uint32_t id_val; + HANDLE_CODE(unpack_integer(id_val, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + this->set_item(id_val); + HANDLE_CODE(crit().unpack(bref)); + HANDLE_CODE(value().unpack(bref)); + return SRSASN_SUCCESS; + } + void to_json(json_writer& j) const + { + j.start_obj(); + j.write_int("id", id()); + j.write_str("criticality", crit().to_string()); + j.write_fieldname(this->item_name()); + asn1::to_json(j, value()); + j.end_obj(); + } + bool load_info_obj(const uint32_t& id_) + { + if (not obj_set_type::is_id_valid(id_)) { + return false; + } + this->set_item(id_); + return value().type().value != obj_set_type::value_c::types_opts::nulltype; + } +}; + +} // namespace detail + +// ProtocolIE-Field{LAYER-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{IEsSetParam}} +template +struct protocol_ie_field_s : public detail::base_ie_field > {}; + +// ProtocolIE-SingleContainer{LAYER-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{IEsSetParam}} +template +struct protocol_ie_single_container_s : public protocol_ie_field_s {}; + +// ProtocolExtensionField{LAYER-PROTOCOL-EXTENSION : ExtensionSetParam} ::= SEQUENCE{{LAYER-PROTOCOL-EXTENSION}} +template +struct protocol_ext_field_s : public detail::base_ie_field > {}; + +namespace detail { + +template +struct ie_value_item { + using value_type = T; + value_type value; + + value_type* operator->() { return &value; } + const value_type* operator->() const { return &value; } + value_type& operator*() { return value; } + const value_type& operator*() const { return value; } + const char* item_name() const { return "value"; } + +protected: + value_type& item() { return value; } + const value_type& item() const { return value; } +}; + +template +struct ie_ext_item { + using value_type = T; + value_type ext; + + value_type* operator->() { return &ext; } + const value_type* operator->() const { return &ext; } + value_type& operator*() { return ext; } + const value_type& operator*() const { return ext; } + const char* item_name() const { return "extension"; } + +protected: + value_type& item() { return ext; } + const value_type& item() const { return ext; } +}; + +template +struct base_ie_container_item : public IEItem { + using value_type = typename IEItem::value_type; + + base_ie_container_item(uint32_t id_, crit_e crit_) : id(id_), crit(crit_) {} + + uint32_t id = 0; + crit_e crit; + + value_type* operator->() { return &this->item(); } + const value_type* operator->() const { return &this->item(); } + value_type& operator*() { return this->item(); } + const value_type& operator*() const { return this->item(); } + + SRSASN_CODE pack(bit_ref& bref) const + { + HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); + HANDLE_CODE(crit.pack(bref)); + { + varlength_field_pack_guard varlen_scope(bref, true); + HANDLE_CODE(this->item().pack(bref)); + } + return SRSASN_SUCCESS; + } + SRSASN_CODE unpack(cbit_ref& bref) + { + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + HANDLE_CODE(crit.unpack(bref)); + { + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(this->item().unpack(bref)); + } + return SRSASN_SUCCESS; + } + void to_json(json_writer& j) const + { + j.start_obj(); + j.write_int("id", id); + j.write_str("criticality", crit.to_string()); + j.write_fieldname(this->item_name()); + asn1::to_json(j, this->item()); + j.end_obj(); + } +}; + +} // namespace detail + +template +struct protocol_ie_container_item_s : public detail::base_ie_container_item > { + using base_type = detail::base_ie_container_item >; + using base_type::base_type; +}; + +template +struct protocol_ext_container_item_s : public detail::base_ie_container_item > { + using base_type = detail::base_ie_container_item >; + using base_type::base_type; +}; + +// ProtocolIE-Container{LAYER-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE (SIZE (0..65535)) OF ProtocolIE-Field +template +using protocol_ie_container_l = dyn_seq_of, 0, 65535, true>; + +// ProtocolExtensionContainer{LAYER-PROTOCOL-EXTENSION : ExtensionSetParam} ::= SEQUENCE (SIZE (1..65535)) OF +// ProtocolExtensionField +template +using protocol_ext_container_l = dyn_seq_of, 1, 65535, true>; + +namespace detail { + +struct empty_obj_set_item_c { + struct types_opts { + enum options { nulltype } value; + const char* to_string() const; + }; + typedef enumerated types; + + // choice methods + types type() const { return types::nulltype; } + SRSASN_CODE pack(bit_ref& bref) const; + SRSASN_CODE unpack(cbit_ref& bref); + void to_json(json_writer& j) const; +}; + +struct base_empty_obj_set { + // members lookup methods + static uint32_t idx_to_id(uint32_t idx); + static bool is_id_valid(const uint32_t& id); + static crit_e get_crit(const uint32_t& id); + static presence_e get_presence(const uint32_t& id); +}; + +} // namespace detail + +/// Empty Protocol IE Object Set +struct protocol_ies_empty_o : public detail::base_empty_obj_set { + using value_c = detail::empty_obj_set_item_c; + + // members lookup methods + static value_c get_value(uint32_t id) { return {}; } +}; + +/// Empty Protocol Extension Object Set +struct protocol_ext_empty_o : public detail::base_empty_obj_set { + using ext_c = detail::empty_obj_set_item_c; + + // members lookup methods + static ext_c get_ext(uint32_t id) { return {}; } +}; + +/// Empty ProtocolExtensionContainer +struct protocol_ie_container_empty_l { + template + using ie_field_s = protocol_ext_container_item_s; + + // sequence methods + SRSASN_CODE pack(bit_ref& bref) const + { + uint32_t nof_ies = 0; + pack_length(bref, nof_ies, 1u, 65535u, true); + return SRSASN_SUCCESS; + } + SRSASN_CODE unpack(cbit_ref& bref) + { + uint32_t nof_ies = 0; + unpack_length(nof_ies, bref, 1u, 65535u, true); + if (nof_ies > 0) { + return SRSASN_ERROR_DECODE_FAIL; + } + return SRSASN_SUCCESS; + } + void to_json(json_writer& j) const + { + j.start_obj(); + j.end_obj(); + } +}; + +using protocol_ext_container_empty_l = protocol_ie_container_empty_l; + +template +class elementary_procedure_option +{ + ProtocolIEs protocol_ies; + +public: + bool ext; + // ... + + ProtocolIEs* operator->() { return &protocol_ies; } + const ProtocolIEs* operator->() const { return &protocol_ies; } + ProtocolIEs& operator*() { return protocol_ies; } + const ProtocolIEs& operator*() const { return protocol_ies; } + + // sequence methods + SRSASN_CODE pack(bit_ref& bref) const + { + bref.pack(ext, 1); + HANDLE_CODE(protocol_ies.pack(bref)); + return SRSASN_SUCCESS; + } + SRSASN_CODE unpack(cbit_ref& bref) + { + bref.unpack(ext, 1); + HANDLE_CODE(protocol_ies.unpack(bref)); + return SRSASN_SUCCESS; + } + void to_json(json_writer& j) const + { + j.start_obj(); + j.write_fieldname("protocolIEs"); + asn1::to_json(j, protocol_ies); + j.end_obj(); + } +}; + } // namespace asn1 #endif // SRSASN_COMMON_UTILS_H diff --git a/lib/include/srsran/asn1/gtpc.h b/lib/include/srsran/asn1/gtpc.h index d26a80f06..185bc4acb 100644 --- a/lib/include/srsran/asn1/gtpc.h +++ b/lib/include/srsran/asn1/gtpc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/gtpc_ies.h b/lib/include/srsran/asn1/gtpc_ies.h index d53a4d217..bb8effb58 100644 --- a/lib/include/srsran/asn1/gtpc_ies.h +++ b/lib/include/srsran/asn1/gtpc_ies.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/gtpc_msg.h b/lib/include/srsran/asn1/gtpc_msg.h index 539b3662f..bc24feb29 100644 --- a/lib/include/srsran/asn1/gtpc_msg.h +++ b/lib/include/srsran/asn1/gtpc_msg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/liblte_mme.h b/lib/include/srsran/asn1/liblte_mme.h index f8ba5b5e1..93d29f348 100644 --- a/lib/include/srsran/asn1/liblte_mme.h +++ b/lib/include/srsran/asn1/liblte_mme.h @@ -2430,6 +2430,7 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_security_protected_nas_msg(LIBLTE_BYTE_MSG_STR #define LIBLTE_MME_EPS_NETWORK_FEATURE_SUPPORT_IEI 0x64 #define LIBLTE_MME_ADDITIONAL_UPDATE_RESULT_IEI 0xF #define LIBLTE_MME_T3412_EXTENDED_VALUE_IEI 0x5E +#define LIBLTE_MME_ADDITIONAL_INFORMATION_IEI 0x65 // Enums // Structs typedef struct { @@ -3649,6 +3650,8 @@ typedef struct { // Functions LIBLTE_ERROR_ENUM liblte_mme_pack_deactivate_eps_bearer_context_request_msg( LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT* deact_eps_bearer_context_req, + uint8 sec_hdr_type, + uint32 count, LIBLTE_BYTE_MSG_STRUCT* msg); LIBLTE_ERROR_ENUM liblte_mme_unpack_deactivate_eps_bearer_context_request_msg( LIBLTE_BYTE_MSG_STRUCT* msg, diff --git a/lib/include/srsran/asn1/nas_5g_ies.h b/lib/include/srsran/asn1/nas_5g_ies.h new file mode 100644 index 000000000..5ef8ee670 --- /dev/null +++ b/lib/include/srsran/asn1/nas_5g_ies.h @@ -0,0 +1,2586 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSRAN_NAS_5G_IES_H +#define SRSRAN_NAS_5G_IES_H +#include "nas_5g_utils.h" + +#include "srsran/asn1/asn1_utils.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/config.h" + +#include +#include +#include + +namespace srsran { +namespace nas_5g { + +// IEs + +// IE: 5GS registration type +// Reference: 9.11.3.7 +class registration_type_5gs_t +{ +public: + struct registration_type_type_ { + enum options { + initial_registration = 0b001, + mobility_registration_updating = 0b010, + periodic_registration_updating = 0b011, + emergency_registration = 0b100, + reserved = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated registration_type_type; + + struct follow_on_request_bit_type_ { + enum options { + no_follow_on_request_pending = 0b0, + follow_on_request_pending = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated follow_on_request_bit_type; + + follow_on_request_bit_type follow_on_request_bit = follow_on_request_bit_type_::options::no_follow_on_request_pending; + registration_type_type registration_type = registration_type_type_::options::initial_registration; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // registration_type_5gs_t + +// IE: key set identifier +// Reference: 9.11.3.32 +class key_set_identifier_t +{ +public: + struct security_context_flag_type_ { + enum options { + native_security_context = 0b0, + mapped_security_context = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated security_context_flag_type; + + struct nas_key_set_identifier_type_ { + enum options { + no_key_is_available_or_reserved = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated nas_key_set_identifier_type; + + security_context_flag_type security_context_flag = security_context_flag_type_::options::native_security_context; + nas_key_set_identifier_type nas_key_set_identifier = + nas_key_set_identifier_type_::options::no_key_is_available_or_reserved; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // key_set_identifier_t + +// IE: 5GS mobile identity +// Reference: 9.11.3.4 +class mobile_identity_5gs_t +{ +public: + struct identity_types_ { + enum options { + no_identity = 0b000, + suci = 0b001, + guti_5g = 0b010, + imei = 0b011, + s_tmsi_5g = 0b100, + imeisv = 0b101, + mac_address = 0b110, + eui_64 = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated identity_types; + + void set(identity_types::options e = identity_types::no_identity) { type_ = e; }; + identity_types type() const { return type_; } + + class suci_s + { + public: + struct supi_format_type_ { + enum options { + imsi = 0b000, + network_specific_identifier = 0b010, + gci = 0b011, + gli = 0b100, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated supi_format_type; + + struct protection_scheme_id_type_ { + enum options { + null_scheme = 0b0000, + ecies_scheme_profile_a = 0b0001, + ecies_scheme_profile_b = 0b0010, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated protection_scheme_id_type; + + supi_format_type supi_format = supi_format_type_::options::imsi; + std::array mcc; + std::array mnc; + std::array routing_indicator; + protection_scheme_id_type protection_scheme_id = protection_scheme_id_type_::options::null_scheme; + uint8_t home_network_public_key_identifier; + std::vector scheme_output; + + SRSASN_CODE pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp); + SRSASN_CODE unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length); + + }; // suci + class guti_5g_s + { + public: + std::array mcc; + std::array mnc; + uint8_t amf_region_id; + uint16_t amf_set_id; + uint8_t amf_pointer; + uint32_t tmsi_5g; + + SRSASN_CODE pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp); + SRSASN_CODE unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length); + + }; // guti_5g + class imei_s + { + public: + bool odd_even_indicator; + std::array imei; + + SRSASN_CODE pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp); + SRSASN_CODE unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length); + + }; // imei + class s_tmsi_5g_s + { + public: + uint16_t amf_set_id; + uint8_t amf_pointer; + uint32_t tmsi_5g; + + SRSASN_CODE pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp); + SRSASN_CODE unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length); + + }; // s_tmsi_5g + class imeisv_s + { + public: + bool odd_even_indicator; + std::array imeisv; + + SRSASN_CODE pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp); + SRSASN_CODE unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length); + + }; // imeisv + class mac_address_s + { + public: + std::array mac_address; + + SRSASN_CODE pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp); + SRSASN_CODE unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length); + + }; // mac_address + class eui_64_s + { + public: + std::array eui_64; + + SRSASN_CODE pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp); + SRSASN_CODE unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length); + + }; // eui_64 + + // Getters + + suci_s& suci() + { + asn1::assert_choice_type(identity_types::suci, type_, "suci"); + return *srslog::detail::any_cast(&choice_container); + } + + guti_5g_s& guti_5g() + { + asn1::assert_choice_type(identity_types::guti_5g, type_, "guti_5g"); + return *srslog::detail::any_cast(&choice_container); + } + + imei_s& imei() + { + asn1::assert_choice_type(identity_types::imei, type_, "imei"); + return *srslog::detail::any_cast(&choice_container); + } + + s_tmsi_5g_s& s_tmsi_5g() + { + asn1::assert_choice_type(identity_types::s_tmsi_5g, type_, "s_tmsi_5g"); + return *srslog::detail::any_cast(&choice_container); + } + + imeisv_s& imeisv() + { + asn1::assert_choice_type(identity_types::imeisv, type_, "imeisv"); + return *srslog::detail::any_cast(&choice_container); + } + + mac_address_s& mac_address() + { + asn1::assert_choice_type(identity_types::mac_address, type_, "mac_address"); + return *srslog::detail::any_cast(&choice_container); + } + + eui_64_s& eui_64() + { + asn1::assert_choice_type(identity_types::eui_64, type_, "eui_64"); + return *srslog::detail::any_cast(&choice_container); + } + + suci_s& set_suci() + { + set(identity_types::suci); + choice_container = srslog::detail::any{suci_s()}; + return *srslog::detail::any_cast(&choice_container); + } + + guti_5g_s& set_guti_5g() + { + set(identity_types::guti_5g); + choice_container = srslog::detail::any{guti_5g_s()}; + return *srslog::detail::any_cast(&choice_container); + } + + imei_s& set_imei() + { + set(identity_types::imei); + choice_container = srslog::detail::any{imei_s()}; + return *srslog::detail::any_cast(&choice_container); + } + + s_tmsi_5g_s& set_s_tmsi_5g() + { + set(identity_types::s_tmsi_5g); + choice_container = srslog::detail::any{s_tmsi_5g_s()}; + return *srslog::detail::any_cast(&choice_container); + } + + imeisv_s& set_imeisv() + { + set(identity_types::imeisv); + choice_container = srslog::detail::any{imeisv_s()}; + return *srslog::detail::any_cast(&choice_container); + } + + mac_address_s& set_mac_address() + { + set(identity_types::mac_address); + choice_container = srslog::detail::any{mac_address_s()}; + return *srslog::detail::any_cast(&choice_container); + } + + eui_64_s& set_eui_64() + { + set(identity_types::eui_64); + choice_container = srslog::detail::any{eui_64_s()}; + return *srslog::detail::any_cast(&choice_container); + } + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +private: + identity_types type_ = identity_types_::options::no_identity; + srslog::detail::any choice_container; + +}; // mobile_identity_5gs_t + +// IE: 5GMM capability +// Reference: 9.11.3.1 +class capability_5gmm_t +{ +public: + bool sgc = false; + bool iphc_cp_c_io_t_5g = false; + bool n3_data = false; + bool cp_c_io_t_5g = false; + bool restrict_ec = false; + bool lpp = false; + bool ho_attach = false; + bool s1_mode = false; + bool racs = false; + bool nssaa = false; + bool lcs_5g = false; + bool v2_xcnpc5 = false; + bool v2_xcepc5 = false; + bool v2_x = false; + bool up_c_io_t_5g = false; + bool srvcc_5g = false; + bool ehc_cp_c_io_t_5g = false; + bool multiple_up = false; + bool wusa = false; + bool cag = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // capability_5gmm_t + +// IE: UE security capability +// Reference: 9.11.3.54 +class ue_security_capability_t +{ +public: + bool ea0_5g_supported = false; + bool ea1_128_5g_supported = false; + bool ea2_128_5g_supported = false; + bool ea3_128_5g_supported = false; + bool ea4_5g_supported = false; + bool ea5_5g_supported = false; + bool ea6_5g_supported = false; + bool ea7_5g_supported = false; + bool ia0_5g_supported = false; + bool ia1_128_5g_supported = false; + bool ia2_128_5g_supported = false; + bool ia3_128_5g_supported = false; + bool ia4_5g_supported = false; + bool ia5_5g_supported = false; + bool ia6_5g_supported = false; + bool ia7_5g_supported = false; + bool eps_caps_present = false; + bool eea0_supported = false; + bool eea1_128_supported = false; + bool eea2_128_supported = false; + bool eea3_128_supported = false; + bool eea4_supported = false; + bool eea5_supported = false; + bool eea6_supported = false; + bool eea7_supported = false; + bool eia0_supported = false; + bool eia1_128_supported = false; + bool eia2_128_supported = false; + bool eia3_128_supported = false; + bool eia4_supported = false; + bool eia5_supported = false; + bool eia6_supported = false; + bool eia7_supported = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ue_security_capability_t + +// IE: S-NSSAI +// Reference: 9.11.2.8 +// IE: S-NSSAI +// Reference: 9.11.2.8 +class s_nssai_t +{ +public: + struct SST_type_ { + enum options { + sst = 0b00000001, + sst_and_mapped_hplmn_sst = 0b00000010, + sst_and_sd = 0b00000100, + sst_sd_and_mapped_hplmn_sst = 0b00000101, + sst_sd_mapped_hplmn_sst_and_mapped_hplmn_sd = 0b00001000, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated SST_type; + + SST_type type; + uint8_t sst; + uint32_t sd; + uint8_t mapped_hplmn_sst; + uint32_t mapped_hplmn_sd; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // s_nssai_t + +// IE: NSSAI +// Reference: 9.11.3.37 +class nssai_t +{ +public: + std::vector s_nssai_list; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // nssai_t + +// IE: 5GS tracking area identity +// Reference: 9.11.3.8 +class tracking_area_identity_5gs_t +{ +public: + std::array mcc; + std::array mnc; + uint32_t tac; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // tracking_area_identity_5gs_t + +// IE: S1 UE network capability +// Reference: 9.11.3.48 +class s1_ue_network_capability_t +{ +public: + bool eea0_supported = false; + bool eea1_128_supported = false; + bool eea2_128_supported = false; + bool eea3_128_supported = false; + bool eea4_supported = false; + bool eea5_supported = false; + bool eea6_supported = false; + bool eea7_supported = false; + bool eia0_supported = false; + bool eia1_128_supported = false; + bool eia2_128_supported = false; + bool eia3_128_supported = false; + bool eia4_supported = false; + bool eia5_supported = false; + bool eia6_supported = false; + bool eia7_supported = false; + bool uea0_supported = false; + bool uea1_128_supported = false; + bool uea2_128_supported = false; + bool uea3_128_supported = false; + bool uea4_supported = false; + bool uea5_supported = false; + bool uea6_supported = false; + bool uea7_supported = false; + bool ucs2_support = false; + bool uia1_128_supported = false; + bool uia2_128_supported = false; + bool uia3_128_supported = false; + bool uia4_supported = false; + bool uia5_supported = false; + bool uia6_supported = false; + bool uia7_supported = false; + bool pro_se_dd_supported = false; + bool pro_se_supported = false; + bool h245_ash_supported = false; + bool acc_csfb_supported = false; + bool llp_supported = false; + bool lcs_supported = false; + bool srvcc_capability_supported = false; + bool nf_capability_supported = false; + bool e_pco_supported = false; + bool hc_cp_c_io_t_supported = false; + bool e_rw_o_pdn_supported = false; + bool s1_u_data_supported = false; + bool up_c_io_t_supported = false; + bool cp_c_io_t_supported = false; + bool pro_se_relay_supported = false; + bool pro_se_dc_supported = false; + bool max_15_eps_bearer_supported = false; + bool sgc_supported = false; + bool n1mode_supported = false; + bool dcnr_supported = false; + bool cp_backoff_supported = false; + bool restrict_ec_supported = false; + bool v2_x_pc5_supported = false; + bool multiple_drb_supported = false; + bool nr_pc5_supported = false; + bool up_mt_edt_supported = false; + bool cp_mt_edt_supported = false; + bool wus_supported = false; + bool racs_supported = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // s1_ue_network_capability_t + +// IE: Uplink data status +// Reference: 9.11.3.57 +class uplink_data_status_t +{ +public: + bool psi_7 = false; + bool psi_6 = false; + bool psi_5 = false; + bool psi_4 = false; + bool psi_3 = false; + bool psi_2 = false; + bool psi_1 = false; + bool psi_0 = false; + bool psi_15 = false; + bool psi_14 = false; + bool psi_13 = false; + bool psi_12 = false; + bool psi_11 = false; + bool psi_10 = false; + bool psi_9 = false; + bool psi_8 = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // uplink_data_status_t + +// IE: PDU session status +// Reference: 9.11.3.44 +class pdu_session_status_t +{ +public: + bool psi_7 = false; + bool psi_6 = false; + bool psi_5 = false; + bool psi_4 = false; + bool psi_3 = false; + bool psi_2 = false; + bool psi_1 = false; + bool psi_0 = false; + bool psi_15 = false; + bool psi_14 = false; + bool psi_13 = false; + bool psi_12 = false; + bool psi_11 = false; + bool psi_10 = false; + bool psi_9 = false; + bool psi_8 = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_status_t + +// IE: MICO indication +// Reference: 9.11.3.31 +class mico_indication_t +{ +public: + bool sprti = false; + bool aai = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // mico_indication_t + +// IE: UE status +// Reference: 9.11.3.56 +class ue_status_t +{ +public: + bool n1_mode_reg = false; + bool s1_mode_reg = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ue_status_t + +// IE: Allowed PDU session status +// Reference: 9.11.3.13 +class allowed_pdu_session_status_t +{ +public: + bool psi_7 = false; + bool psi_6 = false; + bool psi_5 = false; + bool psi_4 = false; + bool psi_3 = false; + bool psi_2 = false; + bool psi_1 = false; + bool psi_0 = false; + bool psi_15 = false; + bool psi_14 = false; + bool psi_13 = false; + bool psi_12 = false; + bool psi_11 = false; + bool psi_10 = false; + bool psi_9 = false; + bool psi_8 = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // allowed_pdu_session_status_t + +// IE: UE usage setting +// Reference: 9.11.3.55 +class ue_usage_setting_t +{ +public: + struct UE_usage_setting_type_ { + enum options { + voice_centric = 0b0, + data_centric = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated UE_usage_setting_type; + + UE_usage_setting_type ue_usage_setting = UE_usage_setting_type_::options::voice_centric; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ue_usage_setting_t + +// IE: 5GS DRX parameters +// Reference: 9.11.3.2A +class drx_parameters_5gs_t +{ +public: + struct drx_value_type_ { + enum options { + drx_value_not_specified = 0b0000, + drx_cycle_parameter_t_32 = 0b0001, + drx_cycle_parameter_t_64 = 0b0010, + drx_cycle_parameter_t_128 = 0b0011, + drx_cycle_parameter_t_256 = 0b0100, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated drx_value_type; + + drx_value_type drx_value = drx_value_type_::options::drx_value_not_specified; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // drx_parameters_5gs_t + +// IE: EPS NAS message container +// Reference: 9.11.3.24 +class eps_nas_message_container_t +{ +public: + std::vector eps_nas_message_container; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // eps_nas_message_container_t + +// IE: DNN +// Reference: 9.11.2.1B +class dnn_t +{ +public: + std::vector dnn_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // dnn_t + +// IE: LADN indication +// Reference: 9.11.3.29 +class ladn_indication_t +{ +public: + std::vector ladn_dnn_values; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ladn_indication_t + +// IE: Payload container type +// Reference: 9.11.3.40 +class payload_container_type_t +{ +public: + struct Payload_container_type_type_ { + enum options { + n1_sm_information = 0b0001, + sms = 0b0010, + lte_positioning_protocol_lpp_message_container = 0b0011, + sor_transparent_container = 0b0100, + ue_policy_container = 0b0101, + ue_parameters_update_transparent_container = 0b0110, + location_services_message_container = 0b0111, + c_io_t_user_data_container = 0b1000, + multiple_payloads = 0b1111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Payload_container_type_type; + + Payload_container_type_type payload_container_type = Payload_container_type_type_::options::n1_sm_information; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // payload_container_type_t + +// IE: Payload container +// Reference: 9.11.3.39 +class payload_container_t +{ +public: + std::vector payload_container_contents; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // payload_container_t + +// IE: Network slicing indication +// Reference: 9.11.3.36 +class network_slicing_indication_t +{ +public: + bool nssci = false; + bool dcni = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // network_slicing_indication_t + +// IE: 5GS update type +// Reference: 9.11.3.9A +class update_type_5gs_t +{ +public: + struct SMS_requested_type_ { + enum options { + sms_over_nas_not_supported = 0b0, + sms_over_nas_supported = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated SMS_requested_type; + + struct NG_RAN_RCU_type_ { + enum options { + ue_radio_capability_update_not_needed = 0b0, + ue_radio_capability_update_needed = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated NG_RAN_RCU_type; + + struct PNB_5GS_CIoT_type_ { + enum options { + no_additional_information = 0b00, + control_plane_c_io_t_5gs_optimization = 0b01, + user_plane_c_io_t_5gs_optimization = 0b10, + reserved = 0b11, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated PNB_5GS_CIoT_type; + + struct PNB_EPS_CIoT_type_ { + enum options { + no_additional_information = 0b00, + control_plane_c_io_t_eps_optimization = 0b01, + user_plane_c_io_t_eps_optimization = 0b10, + reserved = 0b11, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated PNB_EPS_CIoT_type; + + PNB_EPS_CIoT_type pnb_eps_c_io_t = PNB_EPS_CIoT_type_::options::no_additional_information; + PNB_5GS_CIoT_type pnb_5gs_c_io_t = PNB_5GS_CIoT_type_::options::no_additional_information; + NG_RAN_RCU_type ng_ran_rcu = NG_RAN_RCU_type_::options::ue_radio_capability_update_not_needed; + SMS_requested_type sms_requested = SMS_requested_type_::options::sms_over_nas_not_supported; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // update_type_5gs_t + +// IE: Mobile station classmark 2 +// Reference: 9.11.3.31C +class mobile_station_classmark_2_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // mobile_station_classmark_2_t + +// IE: Supported codec list +// Reference: 9.11.3.51A +class supported_codec_list_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // supported_codec_list_t + +// IE: message container +// Reference: 9.11.3.33 +class message_container_t +{ +public: + std::vector nas_message_container; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // message_container_t + +// IE: EPS bearer context status +// Reference: 9.11.3.23A +class eps_bearer_context_status_t +{ +public: + bool ebi_7 = false; + bool ebi_6 = false; + bool ebi_5 = false; + bool ebi_4 = false; + bool ebi_3 = false; + bool ebi_2 = false; + bool ebi_1 = false; + bool ebi_0 = false; + bool ebi_15 = false; + bool ebi_14 = false; + bool ebi_13 = false; + bool ebi_12 = false; + bool ebi_11 = false; + bool ebi_10 = false; + bool ebi_9 = false; + bool ebi_8 = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // eps_bearer_context_status_t + +// IE: Extended DRX parameters +// Reference: 9.11.3.26A +class extended_drx_parameters_t +{ +public: + struct Paging_Time_Window_type_ { + enum options { + seconds_0 = 0b0000, + second_1 = 0b0001, + seconds_2 = 0b0010, + seconds_3 = 0b0011, + seconds_4 = 0b0100, + seconds_5 = 0b0101, + seconds_6 = 0b0110, + seconds_7 = 0b0111, + seconds_8 = 0b1000, + seconds_9 = 0b1001, + seconds_10 = 0b1010, + seconds_12 = 0b1011, + seconds_14 = 0b1100, + seconds_16 = 0b1101, + seconds_18 = 0b1110, + seconds_20 = 0b1111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Paging_Time_Window_type; + + struct eDRX_value_type_ { + enum options { + second_1_28 = 0b0000, + second_2_56 = 0b0001, + second_3_84 = 0b0010, + second_5_12 = 0b0011, + second_6_4 = 0b0100, + second_7_68 = 0b0101, + second_8_96 = 0b0110, + second_10_24 = 0b0111, + second_11_52 = 0b1000, + second_12_8 = 0b1001, + second_14_08 = 0b1010, + second_15_36 = 0b1011, + second_16_64 = 0b1100, + second_17_92 = 0b1101, + second_19_20 = 0b1110, + second_20_48 = 0b1111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated eDRX_value_type; + + Paging_Time_Window_type paging__time__window; + eDRX_value_type e_drx_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // extended_drx_parameters_t + +// IE: GPRS timer 3 +// Reference: 9.11.2.5 +class gprs_timer_3_t +{ +public: + struct Unit_type_ { + enum options { + value_is_incremented_in_multiples_of_10_minutes = 0b000, + value_is_incremented_in_multiples_of_1_hour = 0b001, + value_is_incremented_in_multiples_of_10_hours = 0b010, + value_is_incremented_in_multiples_of_2_seconds = 0b011, + value_is_incremented_in_multiples_of_30_seconds = 0b100, + value_is_incremented_in_multiples_of_1_minute = 0b101, + value_is_incremented_in_multiples_of_320_hours = 0b110, + value_indicates_that_the_timer_is_deactivated = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Unit_type; + + Unit_type unit; + uint8_t timer_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // gprs_timer_3_t + +// IE: UE radio capability ID +// Reference: 9.11.3.68 +class ue_radio_capability_id_t +{ +public: + std::vector ue_radio_capability_id; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ue_radio_capability_id_t + +// IE: Mapped NSSAI +// Reference: 9.11.3.31B +class mapped_nssai_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // mapped_nssai_t + +// IE: Additional information requested +// Reference: 9.11.3.12A +class additional_information_requested_t +{ +public: + bool cipher_key = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // additional_information_requested_t + +// IE: WUS assistance information +// Reference: 9.11.3.71 +class wus_assistance_information_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // wus_assistance_information_t + +// IE: N5GC indication +// Reference: 9.11.3.72 +class n5gc_indication_t +{ +public: + bool n5gcreg = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // n5gc_indication_t + +// IE: NB-N1 mode DRX parameters +// Reference: 9.11.3.73 +class nb_n1_mode_drx_parameters_t +{ +public: + struct nb_n1_mode_drx_value_type_ { + enum options { + drx_value_not_specified = 0b0000, + drx_cycle_parameter_t_32 = 0b0001, + drx_cycle_parameter_t_64 = 0b0010, + drx_cycle_parameter_t_128 = 0b0011, + drx_cycle_parameter_t_256 = 0b0100, + drx_cycle_parameter_t_512 = 0b0101, + drx_cycle_parameter_t_1024 = 0b0111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated nb_n1_mode_drx_value_type; + + nb_n1_mode_drx_value_type nb_n1_mode_drx_value = nb_n1_mode_drx_value_type_::options::drx_value_not_specified; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // nb_n1_mode_drx_parameters_t + +// IE: 5GS registration result +// Reference: 9.11.3.6 +class registration_result_5gs_t +{ +public: + struct Emergency_registered_type_ { + enum options { + not_registered_for_emergency_services = 0b0, + registered_for_emergency_services = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Emergency_registered_type; + + struct NSSAA_to_be_performed_type_ { + enum options { + nssaa_is_not_to_be_performed = 0b0, + nssaa_is_to_be_performed = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated NSSAA_to_be_performed_type; + + struct SMS_allowed_type_ { + enum options { + sms_over_nas_not_allowed = 0b0, + sms_over_nas_allowed = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated SMS_allowed_type; + + struct registration_result_type_ { + enum options { + access_3_gpp = 0b001, + non_3_gpp_access = 0b010, + access_3_gpp_and_non_3_gpp_access = 0b011, + reserved = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated registration_result_type; + + Emergency_registered_type emergency_registered = + Emergency_registered_type_::options::not_registered_for_emergency_services; + NSSAA_to_be_performed_type nssaa_to_be_performed = NSSAA_to_be_performed_type_::options::nssaa_is_to_be_performed; + SMS_allowed_type sms_allowed = SMS_allowed_type_::options::sms_over_nas_not_allowed; + registration_result_type registration_result = registration_result_type_::options::access_3_gpp; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // registration_result_5gs_t + +// IE: PLMN list +// Reference: 9.11.3.45 +class plmn_list_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // plmn_list_t + +// IE: 5GS tracking area identity list +// Reference: 9.11.3.9 +class tracking_area_identity_list_5gs_t +{ +public: + struct type_of_list_type_ { + enum options { + list_of_ta_cs_belonging_to_one_plmn_or_snpn_with_non_consecutive_tac_values = 0b00, + list_of_ta_cs_belonging_to_one_plmn_or_snpn_with_consecutive_tac_values = 0b01, + list_of_ta_is_belonging_to_different_plm_ns = 0b10, + reserved = 0b11, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated type_of_list_type; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // tracking_area_identity_list_5gs_t + +// IE: Rejected NSSAI +// Reference: 9.11.3.46 +class rejected_nssai_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // rejected_nssai_t + +// IE: 5GS network feature support +// Reference: 9.11.3.5 +class network_feature_support_5gs_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // network_feature_support_5gs_t + +// IE: PDU session reactivation result +// Reference: 9.11.3.42 +class pdu_session_reactivation_result_t +{ +public: + bool psi_7 = false; + bool psi_6 = false; + bool psi_5 = false; + bool psi_4 = false; + bool psi_3 = false; + bool psi_2 = false; + bool psi_1 = false; + bool psi_0 = false; + bool psi_15 = false; + bool psi_14 = false; + bool psi_13 = false; + bool psi_12 = false; + bool psi_11 = false; + bool psi_10 = false; + bool psi_9 = false; + bool psi_8 = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_reactivation_result_t + +// IE: PDU session reactivation result error cause +// Reference: 9.11.3.43 +class pdu_session_reactivation_result_error_cause_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_reactivation_result_error_cause_t + +// IE: LADN information +// Reference: 9.11.3.30 +class ladn_information_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ladn_information_t + +// IE: Service area list +// Reference: 9.11.3.49 +class service_area_list_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // service_area_list_t + +// IE: GPRS timer 2 +// Reference: 9.11.2.4 +class gprs_timer_2_t +{ +public: + uint8_t timer_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // gprs_timer_2_t + +// IE: Emergency number list +// Reference: 9.11.3.23 +class emergency_number_list_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // emergency_number_list_t + +// IE: Extended emergency number list +// Reference: 9.11.3.26 +class extended_emergency_number_list_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // extended_emergency_number_list_t + +// IE: SOR transparent container +// Reference: 9.11.3.51 +class sor_transparent_container_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // sor_transparent_container_t + +// IE: EAP message +// Reference: 9.11.2.2 +class eap_message_t +{ +public: + std::vector eap_message; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // eap_message_t + +// IE: NSSAI inclusion mode +// Reference: 9.11.3.37A +class nssai_inclusion_mode_t +{ +public: + struct NSSAI_inclusion_mode_type_ { + enum options { + nssai_inclusion_mode_a = 0b00, + nssai_inclusion_mode_b = 0b01, + nssai_inclusion_mode_c = 0b10, + nssai_inclusion_mode_d = 0b11, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated NSSAI_inclusion_mode_type; + + NSSAI_inclusion_mode_type nssai_inclusion_mode = NSSAI_inclusion_mode_type_::options::nssai_inclusion_mode_a; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // nssai_inclusion_mode_t + +// IE: Operator-defined access category definitions +// Reference: 9.11.3.38 +class operator_defined_access_category_definitions_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // operator_defined_access_category_definitions_t + +// IE: Non-3GPP NW provided policies +// Reference: 9.11.3.36A +class non_3_gpp_nw_provided_policies_t +{ +public: + bool n3_en = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // non_3_gpp_nw_provided_policies_t + +// IE: UE radio capability ID deletion indication +// Reference: 9.11.3.69 +class ue_radio_capability_id_deletion_indication_t +{ +public: + struct Deletion_request_type_ { + enum options { + ue_radio_capability_id_deletion_not_requested = 0b000, + network_assigned_ue_radio_capability_i_ds_deletion_requested = 0b001, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Deletion_request_type; + + Deletion_request_type deletion_request = + Deletion_request_type_::options::ue_radio_capability_id_deletion_not_requested; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ue_radio_capability_id_deletion_indication_t + +// IE: Ciphering key data +// Reference: 9.11.3.18C +class ciphering_key_data_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ciphering_key_data_t + +// IE: CAG information list +// Reference: 9.11.3.18A +class cag_information_list_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // cag_information_list_t + +// IE: Truncated 5G-S-TMSI configuration +// Reference: 9.11.3.70 +class truncated_5g_s_tmsi_configuration_t +{ +public: + uint8_t truncated_amf__set_id_value; + uint8_t truncated_amf__pointer_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // truncated_5g_s_tmsi_configuration_t + +// IE: 5GMM cause +// Reference: 9.11.3.2 +class cause_5gmm_t +{ +public: + struct cause_5gmm_type_ { + enum options { + illegal_ue = 0b00000011, + pei_not_accepted = 0b00000101, + illegal_me = 0b00000110, + services_not_allowed_5gs = 0b00000111, + ue_identity_cannot_be_derived_by_the_network = 0b00001001, + implicitly_de_registered = 0b00001010, + plmn_not_allowed = 0b00001011, + tracking_area_not_allowed = 0b00001100, + roaming_not_allowed_in_this_tracking_area = 0b00001101, + no_suitable_cells_in_tracking_area = 0b00001111, + mac_failure = 0b00010100, + synch_failure = 0b00010101, + congestion = 0b00010110, + ue_security_capabilities_mismatch = 0b00010111, + security_mode_rejected_unspecified = 0b00011000, + non_5g_authentication_unacceptable = 0b00011010, + n1_mode_not_allowed = 0b00011011, + restricted_service_area = 0b00011100, + redirection_to_epc_required = 0b00011111, + ladn_not_available = 0b00101011, + no_network_slices_available = 0b00111110, + maximum_number_of_pdu_sessions_reached_ = 0b01000001, + insufficient_resources_for_specific_slice_and_dnn = 0b01000011, + insufficient_resources_for_specific_slice = 0b01000101, + ng_ksi_already_in_use = 0b01000111, + non_3_gpp_access_to_5gcn_not_allowed = 0b01001000, + serving_network_not_authorized = 0b01001001, + temporarily_not_authorized_for_this_snpn = 0b01001010, + permanently_not_authorized_for_this_snpn = 0b01001011, + not_authorized_for_this_cag_or_authorized_for_cag_cells_only = 0b01001100, + wireline_access_area_not_allowed = 0b01001101, + payload_was_not_forwarded = 0b01011010, + dnn_not_supported_or_not_subscribed_in_the_slice = 0b01011011, + insufficient_user_plane_resources_for_the_pdu_session = 0b01011100, + semantically_incorrect_message = 0b01011111, + invalid_mandatory_information = 0b01100000, + message_type_non_existent_or_not_implemented = 0b01100001, + message_type_not_compatible_with_the_protocol_state = 0b01100010, + information_element_non_existent_or_not_implemented = 0b01100011, + conditional_ie_error = 0b01100100, + message_not_compatible_with_the_protocol_state = 0b01100101, + protocol_error_unspecified = 0b01101111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated cause_5gmm_type; + + cause_5gmm_type cause_5gmm = cause_5gmm_type_::options::illegal_ue; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // cause_5gmm_t + +// IE: De-registration type +// Reference: 9.11.3.20 +class de_registration_type_t +{ +public: + struct switch_off_type_ { + enum options { + normal_de_registration = 0b0, + switch_off = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated switch_off_type; + + struct re_registration_required_type_ { + enum options { + re_registration_not_required = 0b0, + re_registration_required = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated re_registration_required_type; + + struct access_type_type_ { + enum options { + access_3_gpp = 0b01, + non_3_gpp_access = 0b10, + access_3_gpp_and_non_3_gpp_access = 0b11, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated access_type_type; + + switch_off_type switch_off = switch_off_type_::options::normal_de_registration; + re_registration_required_type re_registration_required = + re_registration_required_type_::options::re_registration_not_required; + access_type_type access_type = access_type_type_::options::access_3_gpp; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // de_registration_type_t + +// IE: Spare half octet +// Reference: 9.5 +class spare_half_octet_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // spare_half_octet_t + +// IE: Service type +// Reference: 9.11.3.50 +class service_type_t +{ +public: + struct Service_type_value_type_ { + enum options { + signalling = 0b0000, + data = 0b0001, + mobile_terminated_services = 0b0010, + emergency_services = 0b0011, + emergency_services_fallback = 0b0100, + high_priority_access = 0b0101, + elevated_signalling = 0b0110, + unused_shall_be_interpreted_as_signalling = 0b0111, + unused_shall_be_interpreted_as_signalling_1 = 0b1000, + unused_shall_be_interpreted_as_data = 0b1001, + unused_shall_be_interpreted_as_data_1 = 0b1010, + unused_shall_be_interpreted_as_data_2 = 0b1011, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Service_type_value_type; + + Service_type_value_type service_type_value = Service_type_value_type_::options::signalling; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // service_type_t + +// IE: Configuration update indication +// Reference: 9.11.3.18 +class configuration_update_indication_t +{ +public: + struct control_plane_service_type_value_type_ { + enum options { + mobile_originating_request = 0b000, + mobile_terminating_request = 0b001, + emergency_services = 0b010, + emergency_services_fallback = 0b100, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated control_plane_service_type_value_type; + + control_plane_service_type_value_type control_plane_service_type_value = + control_plane_service_type_value_type_::options::mobile_originating_request; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // configuration_update_indication_t + +// IE: Network name +// Reference: 9.11.3.35 +class network_name_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // network_name_t + +// IE: Time zone +// Reference: 9.11.3.52 +class time_zone_t +{ +public: + uint8_t time_zone; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // time_zone_t + +// IE: Time zone and time +// Reference: 9.11.3.53 +class time_zone_and_time_t +{ +public: + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t time_zone; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // time_zone_and_time_t + +// IE: Daylight saving time +// Reference: 9.11.3.19 +class daylight_saving_time_t +{ +public: + struct value_type_ { + enum options { + no_adjustment_for_daylight_saving_time = 0b00, + hour_1_adjustment_for_daylight_saving_time = 0b01, + hours_2_adjustment_for_daylight_saving_time = 0b10, + reserved = 0b11, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated value_type; + + value_type value = value_type_::options::no_adjustment_for_daylight_saving_time; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // daylight_saving_time_t + +// IE: SMS indication +// Reference: 9.11.3.50A +class sms_indication_t +{ +public: + bool sms_availability_indication = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // sms_indication_t + +// IE: Additional configuration indication +// Reference: 9.11.3.74 +class additional_configuration_indication_t +{ +public: + struct SCMR_type_ { + enum options { + no_additional_information = 0b0, + release_of_n1_nas_signalling_connection_not_required = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated SCMR_type; + + SCMR_type scmr = SCMR_type_::options::no_additional_information; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // additional_configuration_indication_t + +// IE: ABBA +// Reference: 9.11.3.10 +class abba_t +{ +public: + std::vector abba_contents; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // abba_t + +// IE: Authentication parameter RAND +// Reference: 9.11.3.16 +class authentication_parameter_rand_t +{ +public: + std::array rand; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_parameter_rand_t + +// IE: Authentication parameter AUTN +// Reference: 9.11.3.15 +class authentication_parameter_autn_t +{ +public: + std::vector autn; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_parameter_autn_t + +// IE: Authentication response parameter +// Reference: 9.11.3.17 +class authentication_response_parameter_t +{ +public: + std::vector res; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_response_parameter_t + +// IE: Authentication failure parameter +// Reference: 9.11.3.14 +class authentication_failure_parameter_t +{ +public: + std::vector auth_failure; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_failure_parameter_t + +// IE: 5GS identity type +// Reference: 9.11.3.3 +class identity_type_5gs_t +{ +public: + struct identity_types_ { + enum options { + suci = 0b001, + guti_5g = 0b010, + imei = 0b011, + s_tmsi_5g = 0b100, + imeisv = 0b101, + mac_address = 0b110, + eui_64 = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated identity_types; + + identity_types type_of_identity = identity_types_::options::suci; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // identity_type_5gs_t + +// IE: security algorithms +// Reference: 9.11.3.34 +class security_algorithms_t +{ +public: + struct integrity_protection_algorithm_type_ { + enum options { + ia0_5g = 0b0000, + ia1_128_5g = 0b0001, + ia2_128_5g = 0b0010, + ia3_128_5g = 0b0011, + ia4_5g = 0b0100, + ia5_5g = 0b0101, + ia6_5g = 0b0110, + ia7_5g = 0b0111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated integrity_protection_algorithm_type; + + struct ciphering_algorithm_type_ { + enum options { + ea0_5g = 0b0000, + ea1_128_5g = 0b0001, + ea2_128_5g = 0b0010, + ea3_128_5g = 0b0011, + ea4_5g = 0b0100, + ea5_5g = 0b0101, + ea6_5g = 0b0110, + ea7_5g = 0b0111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated ciphering_algorithm_type; + + ciphering_algorithm_type ciphering_algorithm = ciphering_algorithm_type_::options::ea0_5g; + integrity_protection_algorithm_type integrity_protection_algorithm = + integrity_protection_algorithm_type_::options::ia0_5g; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // security_algorithms_t + +// IE: IMEISV request +// Reference: 9.11.3.28 +class imeisv_request_t +{ +public: + struct imeisv_request_type_ { + enum options { + imeisv_not_requested = 0b000, + imeisv_requested = 0b001, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated imeisv_request_type; + + imeisv_request_type imeisv_request = imeisv_request_type_::options::imeisv_not_requested; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // imeisv_request_t + +// IE: EPS NAS security algorithms +// Reference: 9.11.3.25 +class eps_nas_security_algorithms_t +{ +public: + struct integrity_protection_algorithm_type_ { + enum options { + eia0 = 0b000, + eia1_128 = 0b001, + eia2_128 = 0b010, + eia3_128 = 0b011, + eia4 = 0b100, + eia5 = 0b101, + eia6 = 0b110, + eia7 = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated integrity_protection_algorithm_type; + + struct ciphering_algorithm_type_ { + enum options { + eea0 = 0b000, + eea1_128 = 0b001, + eea2_128 = 0b010, + eea3_128 = 0b011, + eea4 = 0b100, + eea5 = 0b101, + eea6 = 0b110, + eea7 = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated ciphering_algorithm_type; + + ciphering_algorithm_type ciphering_algorithm = ciphering_algorithm_type_::options::eea0; + integrity_protection_algorithm_type integrity_protection_algorithm = + integrity_protection_algorithm_type_::options::eia0; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // eps_nas_security_algorithms_t + +// IE: Additional 5G security information +// Reference: 9.11.3.12 +class additional_5g_security_information_t +{ +public: + bool rinmr = false; + bool hdp = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // additional_5g_security_information_t + +// IE: S1 UE security capability +// Reference: 9.11.3.48A +class s1_ue_security_capability_t +{ +public: + bool eea0 = false; + bool eea1_128 = false; + bool eea2_128 = false; + bool eea3_128 = false; + bool eea4 = false; + bool eea5 = false; + bool eea6 = false; + bool eea7 = false; + bool eia0 = false; + bool eia1_128 = false; + bool eia2_128 = false; + bool eia3_128 = false; + bool eia4 = false; + bool eia5 = false; + bool eia6 = false; + bool eia7 = false; + bool uea0 = false; + bool uea1 = false; + bool uea2 = false; + bool uea3 = false; + bool uea4 = false; + bool uea5 = false; + bool uea6 = false; + bool uea7 = false; + bool uia1 = false; + bool uia2 = false; + bool uia3 = false; + bool uia4 = false; + bool uia5 = false; + bool uia6 = false; + bool uia7 = false; + bool gea1 = false; + bool gea2 = false; + bool gea3 = false; + bool gea4 = false; + bool gea5 = false; + bool gea6 = false; + bool gea7 = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // s1_ue_security_capability_t + +// IE: Access type +// Reference: 9.11.2.1A +class access_type_t +{ +public: + struct Access_type_value_type_ { + enum options { + access_3_gpp = 0b01, + non_3_gpp_access = 0b10, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Access_type_value_type; + + Access_type_value_type access_type_value = Access_type_value_type_::options::access_3_gpp; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // access_type_t + +// IE: PDU session identity 2 +// Reference: 9.11.3.41 +class pdu_session_identity_2_t +{ +public: + uint8_t pdu_session_identity_2_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_identity_2_t + +// IE: Request type +// Reference: 9.11.3.47 +class request_type_t +{ +public: + struct Request_type_value_type_ { + enum options { + initial_request = 0b001, + existing_pdu_session = 0b010, + initial_emergency_request = 0b011, + existing_emergency_pdu_session = 0b100, + modification_request = 0b101, + ma_pdu_request = 0b110, + reserved = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Request_type_value_type; + + Request_type_value_type request_type_value = request_type_t::Request_type_value_type_::options::initial_request; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // request_type_t + +// IE: Additional information +// Reference: 9.11.2.1 +class additional_information_t +{ +public: + std::vector additional_information_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // additional_information_t + +// IE: MA PDU session information +// Reference: 9.11.3.31A +class ma_pdu_session_information_t +{ +public: + struct MA_PDU_session_information_value_type_ { + enum options { + ma_pdu_session_network_upgrade_is_allowed = 0b0001, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated MA_PDU_session_information_value_type; + + MA_PDU_session_information_value_type ma_pdu_session_information_value = + MA_PDU_session_information_value_type_::options::ma_pdu_session_network_upgrade_is_allowed; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ma_pdu_session_information_t + +// IE: Release assistance indication +// Reference: 9.11.3.46A +class release_assistance_indication_t +{ +public: + struct Downlink_data_expected_type_ { + enum options { + no_information_regarding_ddx_is_conveyed = 0b00, + no_further_uplink_and_no_further_downlink_data = 0b01, + only_a_single_downlink_data_transmission = 0b10, + reserved = 0b11, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Downlink_data_expected_type; + + Downlink_data_expected_type downlink_data_expected = + Downlink_data_expected_type_::options::no_information_regarding_ddx_is_conveyed; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // release_assistance_indication_t + +// IE: Integrity protection maximum data rate +// Reference: 9.11.4.7 +class integrity_protection_maximum_data_rate_t +{ +public: + struct max_data_rate_UPIP_uplink_type_ { + enum options { + kbps_64 = 0b00000000, + null = 0b00000001, + full_data_rate = 0b11111111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated max_data_rate_UPIP_uplink_type; + + struct max_data_rate_UPIP_downlink_type_ { + enum options { + kbps_64 = 0b00000000, + null = 0b00000001, + full_data_rate = 0b11111111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated max_data_rate_UPIP_downlink_type; + + max_data_rate_UPIP_uplink_type max_data_rate_upip_uplink = max_data_rate_UPIP_uplink_type_::options::kbps_64; + max_data_rate_UPIP_downlink_type max_data_rate_upip_downlink = max_data_rate_UPIP_downlink_type_::options::kbps_64; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // integrity_protection_maximum_data_rate_t + +// IE: PDU session type +// Reference: 9.11.4.11 +class pdu_session_type_t +{ +public: + struct PDU_session_type_value_type_ { + enum options { + ipv4 = 0b001, + ipv6 = 0b010, + ipv4v6 = 0b011, + unstructured = 0b100, + ethernet = 0b101, + reserved = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated PDU_session_type_value_type; + + PDU_session_type_value_type pdu_session_type_value = PDU_session_type_value_type_::options::ipv4; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_type_t + +// IE: SSC mode +// Reference: 9.11.4.16 +class ssc_mode_t +{ +public: + struct SSC_mode_value_type_ { + enum options { + ssc_mode_1 = 0b001, + ssc_mode_2 = 0b010, + ssc_mode_3 = 0b011, + unused_or_ssc_mode_1 = 0b100, + unused_or_ssc_mode_2 = 0b101, + unused_or_ssc_mode_3 = 0b110, + reserved = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated SSC_mode_value_type; + + SSC_mode_value_type ssc_mode_value = ssc_mode_t::SSC_mode_value_type_::options::ssc_mode_1; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ssc_mode_t + +// IE: 5GSM capability +// Reference: 9.11.4.1 +class capability_5gsm_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // capability_5gsm_t + +// IE: Maximum number of supported packet filters +// Reference: 9.11.4.9 +class maximum_number_of_supported_packet_filters_t +{ +public: + uint16_t maximum_number_of_supported_packet_filters; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // maximum_number_of_supported_packet_filters_t + +// IE: Always-on PDU session requested +// Reference: 9.11.4.4 +class always_on_pdu_session_requested_t +{ +public: + bool apsi = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // always_on_pdu_session_requested_t + +// IE: SM PDU DN request container +// Reference: 9.11.4.15 +class sm_pdu_dn_request_container_t +{ +public: + std::vector dn_specific_identity; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // sm_pdu_dn_request_container_t + +// IE: Extended protocol configuration options +// Reference: 9.11.4.6 +class extended_protocol_configuration_options_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // extended_protocol_configuration_options_t + +// IE: IP header compression configuration +// Reference: 9.11.4.24 +class ip_header_compression_configuration_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ip_header_compression_configuration_t + +// IE: DS-TT Ethernet port MAC address +// Reference: 9.11.4.25 +class ds_tt__ethernet_port_mac_address_t +{ +public: + std::array ds_tt__ethernet_port_mac_address_contents; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ds_tt__ethernet_port_mac_address_t + +// IE: UE-DS-TT residence time +// Reference: 9.11.4.26 +class ue_ds_tt_residence_time_t +{ +public: + std::array ue_ds_tt_residence_time_contents; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ue_ds_tt_residence_time_t + +// IE: Port management information container +// Reference: 9.11.4.27 +class port_management_information_container_t +{ +public: + std::vector port_management_information_container; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // port_management_information_container_t + +// IE: Ethernet header compression configuration +// Reference: 9.11.4.28 +class ethernet_header_compression_configuration_t +{ +public: + struct CID_Length_type_ { + enum options { + ethernet_header_compression_not_used = 0b00, + bits_7 = 0b01, + bits_15 = 0b10, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated CID_Length_type; + + CID_Length_type cid__length = CID_Length_type_::options::ethernet_header_compression_not_used; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ethernet_header_compression_configuration_t + +// IE: PDU address +// Reference: 9.11.4.10 +class pdu_address_t +{ +public: + struct PDU_session_type_value_type_ { + enum options { + ipv4 = 0b001, + ipv6 = 0b010, + ipv4v6 = 0b011, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated PDU_session_type_value_type; + + bool si6_lla = false; + PDU_session_type_value_type pdu_session_type_value = PDU_session_type_value_type_::options::ipv4; + std::array ipv4; + std::array ipv6; + std::array smf_i_pv6_link_local_address; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_address_t + +// IE: QoS rules +// Reference: 9.11.4.13 +class qo_s_rules_t +{ +public: + struct qos_rule_t {}; + std::vector qos_rules; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // qo_s_rules_t + +// IE: Session-AMBR +// Reference: 9.11.4.14 +class session_ambr_t +{ +public: + struct unit_session_AMBR_type_ { + enum options { + not_used = 0b00000000, + inc_by_1_kbps = 0b00000001, + inc_by_4_kbps = 0b00000010, + inc_by_16_kbps = 0b00000011, + inc_by_64_kbps = 0b00000100, + inc_by_256_kbps = 0b00000101, + inc_by_1_mbps = 0b00000110, + inc_by_4_mbps = 0b00000111, + inc_by_16_mbps = 0b00001000, + inc_by_64_mbps = 0b00001001, + inc_by_256_mbps = 0b00001010, + inc_by_1_gbps = 0b00001011, + inc_by_4_gbps = 0b00001100, + inc_by_16_gbps = 0b00001101, + inc_by_64_gbps = 0b00001110, + inc_by_256_gbps = 0b00001111, + inc_by_1_tbps = 0b00010000, + inc_by_4_tbps = 0b00010001, + inc_by_16_tbps = 0b00010010, + inc_by_64_tbps = 0b00010011, + inc_by_256_tbps = 0b00010100, + inc_by_1_pbps = 0b00010101, + inc_by_4_pbps = 0b00010110, + inc_by_16_pbps = 0b00010111, + inc_by_64_pbps = 0b00011000, + inc_by_256_pbps = 0b00011001, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated unit_session_AMBR_type; + + unit_session_AMBR_type unit_session_ambr_for_downlink = unit_session_AMBR_type_::options::not_used; + uint16_t session_ambr_for_downlink; + unit_session_AMBR_type unit_session_ambr_for_uplink = unit_session_AMBR_type_::options::not_used; + uint16_t session_ambr_for_uplink; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // session_ambr_t + +// IE: 5GSM cause +// Reference: 9.11.4.2 +class cause_5gsm_t +{ +public: + struct cause_value_type_ { + enum options { + operator_determined_barring = 0b00001000, + insufficient_resources = 0b00011010, + missing_or_unknown_dnn = 0b00011011, + unknown_pdu_session_type = 0b00011100, + user_authentication_or_authorization_failed = 0b00011101, + request_rejected_unspecified = 0b00011111, + service_option_not_supported = 0b00100000, + requested_service_option_not_subscribed = 0b00100001, + pti_already_in_use = 0b00100011, + regular_deactivation = 0b00100100, + network_failure = 0b00100110, + reactivation_requested = 0b00100111, + semantic_error_in_the_tft_operation = 0b00101001, + syntactical_error_in_the_tft_operation = 0b00101010, + invalid_pdu_session_identity = 0b00101011, + semantic_errors_in_packet_filter = 0b00101100, + syntactical_error_in_packet_filter = 0b00101101, + out_of_ladn_service_area = 0b00101110, + pti_mismatch = 0b00101111, + pdu_session_type_i_pv4_only_allowed = 0b00110010, + pdu_session_type_i_pv6_only_allowed = 0b00110011, + pdu_session_does_not_exist = 0b00110110, + pdu_session_type_i_pv4v6_only_allowed = 0b00111001, + pdu_session_type_unstructured_only_allowed = 0b00111010, + unsupported_5_qi_value = 0b00111011, + pdu_session_type_ethernet_only_allowed = 0b00111101, + insufficient_resources_for_specific_slice_and_dnn = 0b01000011, + not_supported_ssc_mode = 0b01000100, + insufficient_resources_for_specific_slice = 0b01000101, + missing_or_unknown_dnn_in_a_slice = 0b01000110, + invalid_pti_value = 0b01010001, + maximum_data_rate_per_ue_for_user_plane_integrity_protection_is_too_low = 0b01010010, + semantic_error_in_the_qo_s_operation = 0b01010011, + syntactical_error_in_the_qo_s_operation = 0b01010100, + invalid_mapped_eps_bearer_identity = 0b01010101, + semantically_incorrect_message = 0b01011111, + invalid_mandatory_information = 0b01100000, + message_type_non_existent_or_not_implemented = 0b01100001, + message_type_not_compatible_with_the_protocol_state = 0b01100010, + information_element_non_existent_or_not_implemented = 0b01100011, + conditional_ie_error = 0b01100100, + message_not_compatible_with_the_protocol_state = 0b01100101, + protocol_error_unspecified = 0b01101111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated cause_value_type; + + cause_value_type cause_value = cause_value_type_::options::operator_determined_barring; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // cause_5gsm_t + +// IE: GPRS timer +// Reference: 9.11.2.3 +class gprs_timer_t +{ +public: + struct Unit_type_ { + enum options { + value_is_incremented_in_multiples_of_2_seconds = 0b000, + value_is_incremented_in_multiples_of_1_minute = 0b001, + value_is_incremented_in_multiples_of_decihours = 0b010, + value_indicates_that_the_timer_is_deactivated = 0b111, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated Unit_type; + + Unit_type unit; + uint8_t timer_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // gprs_timer_t + +// IE: Always-on PDU session indication +// Reference: 9.11.4.3 +class always_on_pdu_session_indication_t +{ +public: + bool apsr = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // always_on_pdu_session_indication_t + +// IE: Mapped EPS bearer contexts +// Reference: 9.11.4.8 +class mapped_eps_bearer_contexts_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // mapped_eps_bearer_contexts_t + +// IE: QoS flow descriptions +// Reference: 9.11.4.12 +class qo_s_flow_descriptions_t +{ +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // qo_s_flow_descriptions_t + +// IE: 5GSM network feature support +// Reference: 9.11.4.18 +class network_feature_support_5gsm_t +{ +public: + struct EPT_S1_type_ { + enum options { + ethernet_pdn_type_in_s1_mode_not_supported = 0b0, + ethernet_pdn_type_in_s1_mode_supported = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated EPT_S1_type; + + EPT_S1_type ept_s1 = EPT_S1_type_::options::ethernet_pdn_type_in_s1_mode_not_supported; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // network_feature_support_5gsm_t + +// IE: Serving PLMN rate control +// Reference: 9.11.4.20 +class serving_plmn_rate_control_t +{ +public: + uint16_t serving_plmn_rate_control_value; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // serving_plmn_rate_control_t + +// IE: ATSSS container +// Reference: 9.11.4.22 +class atsss_container_t +{ +public: + std::vector nas_message_container; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // atsss_container_t + +// IE: Control plane only indication +// Reference: 9.11.4.23 +class control_plane_only_indication_t +{ +public: + bool cpoi = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // control_plane_only_indication_t + +// IE: Allowed SSC mode +// Reference: 9.11.4.5 +class allowed_ssc_mode_t +{ +public: + bool ssc3 = false; + bool ssc2 = false; + bool ssc1 = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // allowed_ssc_mode_t + +// IE: 5GSM congestion re-attempt indicator +// Reference: 9.11.4.21 +class congestion_re_attempt_indicator_5gsm_t +{ +public: + struct abo_type_ { + enum options { + the_back_off_timer_is_applied_in_the_registered_plmn = 0b0, + the_back_off_timer_is_applied_in_all_plm_ns = 0b1, + + } value; + const char* to_string() const; + }; + typedef nas_enumerated abo_type; + + abo_type abo = + congestion_re_attempt_indicator_5gsm_t::abo_type_::options::the_back_off_timer_is_applied_in_the_registered_plmn; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // congestion_re_attempt_indicator_5gsm_t + +// IE: Re-attempt indicator +// Reference: 9.11.4.17 +class re_attempt_indicator_t +{ +public: + bool eplmnc = false; + bool ratc = false; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // re_attempt_indicator_t + +} // namespace nas_5g +} // namespace srsran +#endif diff --git a/lib/include/srsran/asn1/nas_5g_msg.h b/lib/include/srsran/asn1/nas_5g_msg.h new file mode 100644 index 000000000..a5560b9ef --- /dev/null +++ b/lib/include/srsran/asn1/nas_5g_msg.h @@ -0,0 +1,2487 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSRAN_NAS_5G_MSG_H +#define SRSRAN_NAS_5G_MSG_H +#include "nas_5g_ies.h" +#include "nas_5g_utils.h" + +#include "srsran/asn1/asn1_utils.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/config.h" + +#include +#include +#include + +namespace srsran { +namespace nas_5g { + +/* + * Message: Registration request. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class registration_request_t +{ +public: + // Mandatory fields + registration_type_5gs_t registration_type_5gs; + key_set_identifier_t ng_ksi; + mobile_identity_5gs_t mobile_identity_5gs; + + // Optional fields + bool non_current_native_nas_key_set_identifier_present = false; + bool capability_5gmm_present = false; + bool ue_security_capability_present = false; + bool requested_nssai_present = false; + bool last_visited_registered_tai_present = false; + bool s1_ue_network_capability_present = false; + bool uplink_data_status_present = false; + bool pdu_session_status_present = false; + bool mico_indication_present = false; + bool ue_status_present = false; + bool additional_guti_present = false; + bool allowed_pdu_session_status_present = false; + bool ue_usage_setting_present = false; + bool requested_drx_parameters_present = false; + bool eps_nas_message_container_present = false; + bool ladn_indication_present = false; + bool payload_container_type_present = false; + bool payload_container_present = false; + bool network_slicing_indication_present = false; + bool update_type_5gs_present = false; + bool mobile_station_classmark_2_present = false; + bool supported_codecs_present = false; + bool nas_message_container_present = false; + bool eps_bearer_context_status_present = false; + bool requested_extended_drx_parameters_present = false; + bool t3324_value_present = false; + bool ue_radio_capability_id_present = false; + bool requested_mapped_nssai_present = false; + bool additional_information_requested_present = false; + bool requested_wus_assistance_information_present = false; + bool n5gc_indication_present = false; + bool requested_nb_n1_mode_drx_parameters_present = false; + + key_set_identifier_t non_current_native_nas_key_set_identifier; + capability_5gmm_t capability_5gmm; + ue_security_capability_t ue_security_capability; + nssai_t requested_nssai; + tracking_area_identity_5gs_t last_visited_registered_tai; + s1_ue_network_capability_t s1_ue_network_capability; + uplink_data_status_t uplink_data_status; + pdu_session_status_t pdu_session_status; + mico_indication_t mico_indication; + ue_status_t ue_status; + mobile_identity_5gs_t additional_guti; + allowed_pdu_session_status_t allowed_pdu_session_status; + ue_usage_setting_t ue_usage_setting; + drx_parameters_5gs_t requested_drx_parameters; + eps_nas_message_container_t eps_nas_message_container; + ladn_indication_t ladn_indication; + payload_container_type_t payload_container_type; + payload_container_t payload_container; + network_slicing_indication_t network_slicing_indication; + update_type_5gs_t update_type_5gs; + mobile_station_classmark_2_t mobile_station_classmark_2; + supported_codec_list_t supported_codecs; + message_container_t nas_message_container; + eps_bearer_context_status_t eps_bearer_context_status; + extended_drx_parameters_t requested_extended_drx_parameters; + gprs_timer_3_t t3324_value; + ue_radio_capability_id_t ue_radio_capability_id; + mapped_nssai_t requested_mapped_nssai; + additional_information_requested_t additional_information_requested; + wus_assistance_information_t requested_wus_assistance_information; + n5gc_indication_t n5gc_indication; + nb_n1_mode_drx_parameters_t requested_nb_n1_mode_drx_parameters; + + const static uint8_t ie_iei_non_current_native_nas_key_set_identifier = 0xC; + const static uint8_t ie_iei_capability_5gmm = 0x10; + const static uint8_t ie_iei_ue_security_capability = 0x2E; + const static uint8_t ie_iei_requested_nssai = 0x2F; + const static uint8_t ie_iei_last_visited_registered_tai = 0x52; + const static uint8_t ie_iei_s1_ue_network_capability = 0x17; + const static uint8_t ie_iei_uplink_data_status = 0x40; + const static uint8_t ie_iei_pdu_session_status = 0x50; + const static uint8_t ie_iei_mico_indication = 0xB; + const static uint8_t ie_iei_ue_status = 0x2B; + const static uint8_t ie_iei_additional_guti = 0x77; + const static uint8_t ie_iei_allowed_pdu_session_status = 0x25; + const static uint8_t ie_iei_ue_usage_setting = 0x18; + const static uint8_t ie_iei_requested_drx_parameters = 0x51; + const static uint8_t ie_iei_eps_nas_message_container = 0x70; + const static uint8_t ie_iei_ladn_indication = 0x74; + const static uint8_t ie_iei_payload_container_type = 0x8; + const static uint8_t ie_iei_payload_container = 0x7B; + const static uint8_t ie_iei_network_slicing_indication = 0x9; + const static uint8_t ie_iei_update_type_5gs = 0x53; + const static uint8_t ie_iei_mobile_station_classmark_2 = 0x41; + const static uint8_t ie_iei_supported_codecs = 0x42; + const static uint8_t ie_iei_nas_message_container = 0x71; + const static uint8_t ie_iei_eps_bearer_context_status = 0x60; + const static uint8_t ie_iei_requested_extended_drx_parameters = 0x6E; + const static uint8_t ie_iei_t3324_value = 0x6A; + const static uint8_t ie_iei_ue_radio_capability_id = 0x67; + const static uint8_t ie_iei_requested_mapped_nssai = 0x35; + const static uint8_t ie_iei_additional_information_requested = 0x48; + const static uint8_t ie_iei_requested_wus_assistance_information = 0x1A; + const static uint8_t ie_iei_n5gc_indication = 0xA; + const static uint8_t ie_iei_requested_nb_n1_mode_drx_parameters = 0x30; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // registration_request_t + +/* + * Message: Registration accept. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class registration_accept_t +{ +public: + // Mandatory fields + registration_result_5gs_t registration_result_5gs; + + // Optional fields + bool guti_5g_present = false; + bool equivalent_plm_ns_present = false; + bool tai_list_present = false; + bool allowed_nssai_present = false; + bool rejected_nssai_present = false; + bool configured_nssai_present = false; + bool network_feature_support_5gs_present = false; + bool pdu_session_status_present = false; + bool pdu_session_reactivation_result_present = false; + bool pdu_session_reactivation_result_error_cause_present = false; + bool ladn_information_present = false; + bool mico_indication_present = false; + bool network_slicing_indication_present = false; + bool service_area_list_present = false; + bool t3512_value_present = false; + bool non_3_gpp_de_registration_timer_value_present = false; + bool t3502_value_present = false; + bool emergency_number_list_present = false; + bool extended_emergency_number_list_present = false; + bool sor_transparent_container_present = false; + bool eap_message_present = false; + bool nssai_inclusion_mode_present = false; + bool operator_defined_access_category_definitions_present = false; + bool negotiated_drx_parameters_present = false; + bool non_3_gpp_nw_policies_present = false; + bool eps_bearer_context_status_present = false; + bool negotiated_extended_drx_parameters_present = false; + bool t3447_value_present = false; + bool t3448_value_present = false; + bool t3324_value_present = false; + bool ue_radio_capability_id_present = false; + bool ue_radio_capability_id_deletion_indication_present = false; + bool pending_nssai_present = false; + bool ciphering_key_data_present = false; + bool cag_information_list_present = false; + bool truncated_5g_s_tmsi_configuration_present = false; + bool negotiated_wus_assistance_information_present = false; + bool negotiated_nb_n1_mode_drx_parameters_present = false; + + mobile_identity_5gs_t guti_5g; + plmn_list_t equivalent_plm_ns; + tracking_area_identity_list_5gs_t tai_list; + nssai_t allowed_nssai; + rejected_nssai_t rejected_nssai; + nssai_t configured_nssai; + network_feature_support_5gs_t network_feature_support_5gs; + pdu_session_status_t pdu_session_status; + pdu_session_reactivation_result_t pdu_session_reactivation_result; + pdu_session_reactivation_result_error_cause_t pdu_session_reactivation_result_error_cause; + ladn_information_t ladn_information; + mico_indication_t mico_indication; + network_slicing_indication_t network_slicing_indication; + service_area_list_t service_area_list; + gprs_timer_3_t t3512_value; + gprs_timer_2_t non_3_gpp_de_registration_timer_value; + gprs_timer_2_t t3502_value; + emergency_number_list_t emergency_number_list; + extended_emergency_number_list_t extended_emergency_number_list; + sor_transparent_container_t sor_transparent_container; + eap_message_t eap_message; + nssai_inclusion_mode_t nssai_inclusion_mode; + operator_defined_access_category_definitions_t operator_defined_access_category_definitions; + drx_parameters_5gs_t negotiated_drx_parameters; + non_3_gpp_nw_provided_policies_t non_3_gpp_nw_policies; + eps_bearer_context_status_t eps_bearer_context_status; + extended_drx_parameters_t negotiated_extended_drx_parameters; + gprs_timer_3_t t3447_value; + gprs_timer_2_t t3448_value; + gprs_timer_3_t t3324_value; + ue_radio_capability_id_t ue_radio_capability_id; + ue_radio_capability_id_deletion_indication_t ue_radio_capability_id_deletion_indication; + nssai_t pending_nssai; + ciphering_key_data_t ciphering_key_data; + cag_information_list_t cag_information_list; + truncated_5g_s_tmsi_configuration_t truncated_5g_s_tmsi_configuration; + wus_assistance_information_t negotiated_wus_assistance_information; + nb_n1_mode_drx_parameters_t negotiated_nb_n1_mode_drx_parameters; + + const static uint8_t ie_iei_guti_5g = 0x77; + const static uint8_t ie_iei_equivalent_plm_ns = 0x4A; + const static uint8_t ie_iei_tai_list = 0x54; + const static uint8_t ie_iei_allowed_nssai = 0x15; + const static uint8_t ie_iei_rejected_nssai = 0x11; + const static uint8_t ie_iei_configured_nssai = 0x31; + const static uint8_t ie_iei_network_feature_support_5gs = 0x21; + const static uint8_t ie_iei_pdu_session_status = 0x50; + const static uint8_t ie_iei_pdu_session_reactivation_result = 0x26; + const static uint8_t ie_iei_pdu_session_reactivation_result_error_cause = 0x72; + const static uint8_t ie_iei_ladn_information = 0x79; + const static uint8_t ie_iei_mico_indication = 0xB; + const static uint8_t ie_iei_network_slicing_indication = 0x9; + const static uint8_t ie_iei_service_area_list = 0x27; + const static uint8_t ie_iei_t3512_value = 0x5E; + const static uint8_t ie_iei_non_3_gpp_de_registration_timer_value = 0x5D; + const static uint8_t ie_iei_t3502_value = 0x16; + const static uint8_t ie_iei_emergency_number_list = 0x34; + const static uint8_t ie_iei_extended_emergency_number_list = 0x7A; + const static uint8_t ie_iei_sor_transparent_container = 0x73; + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_nssai_inclusion_mode = 0xA; + const static uint8_t ie_iei_operator_defined_access_category_definitions = 0x76; + const static uint8_t ie_iei_negotiated_drx_parameters = 0x51; + const static uint8_t ie_iei_non_3_gpp_nw_policies = 0xD; + const static uint8_t ie_iei_eps_bearer_context_status = 0x60; + const static uint8_t ie_iei_negotiated_extended_drx_parameters = 0x6E; + const static uint8_t ie_iei_t3447_value = 0x6C; + const static uint8_t ie_iei_t3448_value = 0x6B; + const static uint8_t ie_iei_t3324_value = 0x6A; + const static uint8_t ie_iei_ue_radio_capability_id = 0x67; + const static uint8_t ie_iei_ue_radio_capability_id_deletion_indication = 0xE; + const static uint8_t ie_iei_pending_nssai = 0x39; + const static uint8_t ie_iei_ciphering_key_data = 0x74; + const static uint8_t ie_iei_cag_information_list = 0x75; + const static uint8_t ie_iei_truncated_5g_s_tmsi_configuration = 0x1B; + const static uint8_t ie_iei_negotiated_wus_assistance_information = 0x1C; + const static uint8_t ie_iei_negotiated_nb_n1_mode_drx_parameters = 0x29; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // registration_accept_t + +/* + * Message: Registration complete. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class registration_complete_t +{ +public: + // Mandatory fields + + // Optional fields + bool sor_transparent_container_present = false; + + sor_transparent_container_t sor_transparent_container; + + const static uint8_t ie_iei_sor_transparent_container = 0x73; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // registration_complete_t + +/* + * Message: Registration reject. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class registration_reject_t +{ +public: + // Mandatory fields + cause_5gmm_t cause_5gmm; + + // Optional fields + bool t3346_value_present = false; + bool t3502_value_present = false; + bool eap_message_present = false; + bool rejected_nssai_present = false; + bool cag_information_list_present = false; + + gprs_timer_2_t t3346_value; + gprs_timer_2_t t3502_value; + eap_message_t eap_message; + rejected_nssai_t rejected_nssai; + cag_information_list_t cag_information_list; + + const static uint8_t ie_iei_t3346_value = 0x5F; + const static uint8_t ie_iei_t3502_value = 0x16; + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_rejected_nssai = 0x69; + const static uint8_t ie_iei_cag_information_list = 0x75; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // registration_reject_t + +/* + * Message: Deregistration request UE originating. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class deregistration_request_ue_originating_t +{ +public: + // Mandatory fields + de_registration_type_t de_registration_type; + key_set_identifier_t ng_ksi; + mobile_identity_5gs_t mobile_identity_5gs; + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // deregistration_request_ue_originating_t + +/* + * Message: Deregistration accept UE originating. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class deregistration_accept_ue_originating_t +{ +public: + // Mandatory fields + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // deregistration_accept_ue_originating_t + +/* + * Message: Deregistration request UE terminated. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class deregistration_request_ue_terminated_t +{ +public: + // Mandatory fields + de_registration_type_t de_registration_type; + spare_half_octet_t spare_half_octet; + + // Optional fields + bool cause_5gmm_present = false; + bool t3346_value_present = false; + bool rejected_nssai_present = false; + bool cag_information_list_present = false; + + cause_5gmm_t cause_5gmm; + gprs_timer_2_t t3346_value; + rejected_nssai_t rejected_nssai; + cag_information_list_t cag_information_list; + + const static uint8_t ie_iei_cause_5gmm = 0x58; + const static uint8_t ie_iei_t3346_value = 0x5F; + const static uint8_t ie_iei_rejected_nssai = 0x6D; + const static uint8_t ie_iei_cag_information_list = 0x75; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // deregistration_request_ue_terminated_t + +/* + * Message: Deregistration accept UE terminated. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class deregistration_accept_ue_terminated_t +{ +public: + // Mandatory fields + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // deregistration_accept_ue_terminated_t + +/* + * Message: Service request. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class service_request_t +{ +public: + // Mandatory fields + key_set_identifier_t ng_ksi; + service_type_t service_type; + mobile_identity_5gs_t s_tmsi_5g; + + // Optional fields + bool uplink_data_status_present = false; + bool pdu_session_status_present = false; + bool allowed_pdu_session_status_present = false; + bool nas_message_container_present = false; + + uplink_data_status_t uplink_data_status; + pdu_session_status_t pdu_session_status; + allowed_pdu_session_status_t allowed_pdu_session_status; + message_container_t nas_message_container; + + const static uint8_t ie_iei_uplink_data_status = 0x40; + const static uint8_t ie_iei_pdu_session_status = 0x50; + const static uint8_t ie_iei_allowed_pdu_session_status = 0x25; + const static uint8_t ie_iei_nas_message_container = 0x71; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // service_request_t + +/* + * Message: Service reject. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class service_reject_t +{ +public: + // Mandatory fields + + // Optional fields + bool pdu_session_status_present = false; + bool pdu_session_reactivation_result_present = false; + bool pdu_session_reactivation_result_error_cause_present = false; + bool eap_message_present = false; + bool t3448_value_present = false; + + pdu_session_status_t pdu_session_status; + pdu_session_reactivation_result_t pdu_session_reactivation_result; + pdu_session_reactivation_result_error_cause_t pdu_session_reactivation_result_error_cause; + eap_message_t eap_message; + gprs_timer_2_t t3448_value; + + const static uint8_t ie_iei_pdu_session_status = 0x50; + const static uint8_t ie_iei_pdu_session_reactivation_result = 0x26; + const static uint8_t ie_iei_pdu_session_reactivation_result_error_cause = 0x72; + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_t3448_value = 0x6B; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // service_reject_t + +/* + * Message: Service accept. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class service_accept_t +{ +public: + // Mandatory fields + cause_5gmm_t cause_5gmm; + + // Optional fields + bool pdu_session_status_present = false; + bool t3346_value_present = false; + bool eap_message_present = false; + bool t3448_value_present = false; + bool cag_information_list_present = false; + + pdu_session_status_t pdu_session_status; + gprs_timer_2_t t3346_value; + eap_message_t eap_message; + gprs_timer_2_t t3448_value; + cag_information_list_t cag_information_list; + + const static uint8_t ie_iei_pdu_session_status = 0x50; + const static uint8_t ie_iei_t3346_value = 0x5F; + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_t3448_value = 0x6B; + const static uint8_t ie_iei_cag_information_list = 0x75; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // service_accept_t + +/* + * Message: Configuration update command. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class configuration_update_command_t +{ +public: + // Mandatory fields + + // Optional fields + bool configuration_update_indication_present = false; + bool guti_5g_present = false; + bool tai_list_present = false; + bool allowed_nssai_present = false; + bool service_area_list_present = false; + bool full_name_for_network_present = false; + bool short_name_for_network_present = false; + bool local_time_zone_present = false; + bool universal_time_and_local_time_zone_present = false; + bool network_daylight_saving_time_present = false; + bool ladn_information_present = false; + bool mico_indication_present = false; + bool network_slicing_indication_present = false; + bool configured_nssai_present = false; + bool rejected_nssai_present = false; + bool operator_defined_access_category_definitions_present = false; + bool sms_indication_present = false; + bool t3447_value_present = false; + bool cag_information_list_present = false; + bool ue_radio_capability_id_present = false; + bool ue_radio_capability_id_deletion_indication_present = false; + bool registration_result_5gs_present = false; + bool truncated_5g_s_tmsi_configuration_present = false; + bool additional_configuration_indication_present = false; + + configuration_update_indication_t configuration_update_indication; + mobile_identity_5gs_t guti_5g; + tracking_area_identity_list_5gs_t tai_list; + nssai_t allowed_nssai; + service_area_list_t service_area_list; + network_name_t full_name_for_network; + network_name_t short_name_for_network; + time_zone_t local_time_zone; + time_zone_and_time_t universal_time_and_local_time_zone; + daylight_saving_time_t network_daylight_saving_time; + ladn_information_t ladn_information; + mico_indication_t mico_indication; + network_slicing_indication_t network_slicing_indication; + nssai_t configured_nssai; + rejected_nssai_t rejected_nssai; + operator_defined_access_category_definitions_t operator_defined_access_category_definitions; + sms_indication_t sms_indication; + gprs_timer_3_t t3447_value; + cag_information_list_t cag_information_list; + ue_radio_capability_id_t ue_radio_capability_id; + ue_radio_capability_id_deletion_indication_t ue_radio_capability_id_deletion_indication; + registration_result_5gs_t registration_result_5gs; + truncated_5g_s_tmsi_configuration_t truncated_5g_s_tmsi_configuration; + additional_configuration_indication_t additional_configuration_indication; + + const static uint8_t ie_iei_configuration_update_indication = 0xD; + const static uint8_t ie_iei_guti_5g = 0x77; + const static uint8_t ie_iei_tai_list = 0x54; + const static uint8_t ie_iei_allowed_nssai = 0x15; + const static uint8_t ie_iei_service_area_list = 0x27; + const static uint8_t ie_iei_full_name_for_network = 0x43; + const static uint8_t ie_iei_short_name_for_network = 0x45; + const static uint8_t ie_iei_local_time_zone = 0x46; + const static uint8_t ie_iei_universal_time_and_local_time_zone = 0x47; + const static uint8_t ie_iei_network_daylight_saving_time = 0x49; + const static uint8_t ie_iei_ladn_information = 0x79; + const static uint8_t ie_iei_mico_indication = 0xB; + const static uint8_t ie_iei_network_slicing_indication = 0x9; + const static uint8_t ie_iei_configured_nssai = 0x31; + const static uint8_t ie_iei_rejected_nssai = 0x11; + const static uint8_t ie_iei_operator_defined_access_category_definitions = 0x76; + const static uint8_t ie_iei_sms_indication = 0xF; + const static uint8_t ie_iei_t3447_value = 0x6C; + const static uint8_t ie_iei_cag_information_list = 0x75; + const static uint8_t ie_iei_ue_radio_capability_id = 0x67; + const static uint8_t ie_iei_ue_radio_capability_id_deletion_indication = 0xA; + const static uint8_t ie_iei_registration_result_5gs = 0x44; + const static uint8_t ie_iei_truncated_5g_s_tmsi_configuration = 0x1B; + const static uint8_t ie_iei_additional_configuration_indication = 0xC; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // configuration_update_command_t + +/* + * Message: Configuration update complete. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class configuration_update_complete_t +{ +public: + // Mandatory fields + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // configuration_update_complete_t + +/* + * Message: Authentication request. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class authentication_request_t +{ +public: + // Mandatory fields + key_set_identifier_t ng_ksi; + spare_half_octet_t spare_half_octet; + abba_t abba; + + // Optional fields + bool authentication_parameter_rand_present = false; + bool authentication_parameter_autn_present = false; + bool eap_message_present = false; + + authentication_parameter_rand_t authentication_parameter_rand; + authentication_parameter_autn_t authentication_parameter_autn; + eap_message_t eap_message; + + const static uint8_t ie_iei_authentication_parameter_rand = 0x21; + const static uint8_t ie_iei_authentication_parameter_autn = 0x20; + const static uint8_t ie_iei_eap_message = 0x78; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_request_t + +/* + * Message: Authentication response. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class authentication_response_t +{ +public: + // Mandatory fields + + // Optional fields + bool authentication_response_parameter_present = false; + bool eap_message_present = false; + + authentication_response_parameter_t authentication_response_parameter; + eap_message_t eap_message; + + const static uint8_t ie_iei_authentication_response_parameter = 0x2D; + const static uint8_t ie_iei_eap_message = 0x78; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_response_t + +/* + * Message: Authentication reject. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class authentication_reject_t +{ +public: + // Mandatory fields + + // Optional fields + bool eap_message_present = false; + + eap_message_t eap_message; + + const static uint8_t ie_iei_eap_message = 0x78; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_reject_t + +/* + * Message: Authentication failure. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class authentication_failure_t +{ +public: + // Mandatory fields + cause_5gmm_t cause_5gmm; + + // Optional fields + bool authentication_failure_parameter_present = false; + + authentication_failure_parameter_t authentication_failure_parameter; + + const static uint8_t ie_iei_authentication_failure_parameter = 0x30; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_failure_t + +/* + * Message: Authentication result. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class authentication_result_t +{ +public: + // Mandatory fields + key_set_identifier_t ng_ksi; + spare_half_octet_t spare_half_octet; + eap_message_t eap_message; + + // Optional fields + bool abba_present = false; + + abba_t abba; + + const static uint8_t ie_iei_abba = 0x38; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // authentication_result_t + +/* + * Message: Identity request. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class identity_request_t +{ +public: + // Mandatory fields + identity_type_5gs_t identity_type; + spare_half_octet_t spare_half_octet; + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // identity_request_t + +/* + * Message: Identity response. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class identity_response_t +{ +public: + // Mandatory fields + mobile_identity_5gs_t mobile_identity; + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // identity_response_t + +/* + * Message: Security mode command. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class security_mode_command_t +{ +public: + // Mandatory fields + security_algorithms_t selected_nas_security_algorithms; + key_set_identifier_t ng_ksi; + spare_half_octet_t spare_half_octet; + ue_security_capability_t replayed_ue_security_capabilities; + + // Optional fields + bool imeisv_request_present = false; + bool selected_eps_nas_security_algorithms_present = false; + bool additional_5g_security_information_present = false; + bool eap_message_present = false; + bool abba_present = false; + bool replayed_s1_ue_security_capabilities_present = false; + + imeisv_request_t imeisv_request; + eps_nas_security_algorithms_t selected_eps_nas_security_algorithms; + additional_5g_security_information_t additional_5g_security_information; + eap_message_t eap_message; + abba_t abba; + s1_ue_security_capability_t replayed_s1_ue_security_capabilities; + + const static uint8_t ie_iei_imeisv_request = 0xE; + const static uint8_t ie_iei_selected_eps_nas_security_algorithms = 0x57; + const static uint8_t ie_iei_additional_5g_security_information = 0x36; + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_abba = 0x38; + const static uint8_t ie_iei_replayed_s1_ue_security_capabilities = 0x19; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // security_mode_command_t + +/* + * Message: Security mode complete. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class security_mode_complete_t +{ +public: + // Mandatory fields + + // Optional fields + bool imeisv_present = false; + bool nas_message_container_present = false; + bool non_imeisv_pei_present = false; + + mobile_identity_5gs_t imeisv; + message_container_t nas_message_container; + mobile_identity_5gs_t non_imeisv_pei; + + const static uint8_t ie_iei_imeisv = 0x77; + const static uint8_t ie_iei_nas_message_container = 0x71; + const static uint8_t ie_iei_non_imeisv_pei = 0x78; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // security_mode_complete_t + +/* + * Message: Security mode reject. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class security_mode_reject_t +{ +public: + // Mandatory fields + cause_5gmm_t cause_5gmm; + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // security_mode_reject_t + +/* + * Message: Status 5GMM. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class status_5gmm_t +{ +public: + // Mandatory fields + cause_5gmm_t cause_5gmm; + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // status_5gmm_t + +/* + * Message: Notification. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class notification_t +{ +public: + // Mandatory fields + access_type_t access_type; + spare_half_octet_t spare_half_octet; + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // notification_t + +/* + * Message: Notification response. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class notification_response_t +{ +public: + // Mandatory fields + + // Optional fields + bool pdu_session_status_present = false; + + pdu_session_status_t pdu_session_status; + + const static uint8_t ie_iei_pdu_session_status = 0x50; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // notification_response_t + +/* + * Message: UL NAS transport. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class ul_nas_transport_t +{ +public: + // Mandatory fields + payload_container_type_t payload_container_type; + spare_half_octet_t spare_half_octet; + payload_container_t payload_container; + + // Optional fields + bool pdu_session_id_present = false; + bool old_pdu_session_id_present = false; + bool request_type_present = false; + bool s_nssai_present = false; + bool dnn_present = false; + bool additional_information_present = false; + bool ma_pdu_session_information_present = false; + bool release_assistance_indication_present = false; + + pdu_session_identity_2_t pdu_session_id; + pdu_session_identity_2_t old_pdu_session_id; + request_type_t request_type; + s_nssai_t s_nssai; + dnn_t dnn; + additional_information_t additional_information; + ma_pdu_session_information_t ma_pdu_session_information; + release_assistance_indication_t release_assistance_indication; + + const static uint8_t ie_iei_pdu_session_id = 0x12; + const static uint8_t ie_iei_old_pdu_session_id = 0x59; + const static uint8_t ie_iei_request_type = 0x8; + const static uint8_t ie_iei_s_nssai = 0x22; + const static uint8_t ie_iei_dnn = 0x25; + const static uint8_t ie_iei_additional_information = 0x24; + const static uint8_t ie_iei_ma_pdu_session_information = 0xA; + const static uint8_t ie_iei_release_assistance_indication = 0xF; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // ul_nas_transport_t + +/* + * Message: DL NAS transport . + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class dl_nas_transport_t +{ +public: + // Mandatory fields + payload_container_type_t payload_container_type; + spare_half_octet_t spare_half_octet; + payload_container_t payload_container; + + // Optional fields + bool pdu_session_id_present = false; + bool additional_information_present = false; + bool cause_5gmm_present = false; + bool back_off_timer_value_present = false; + + pdu_session_identity_2_t pdu_session_id; + additional_information_t additional_information; + cause_5gmm_t cause_5gmm; + gprs_timer_3_t back_off_timer_value; + + const static uint8_t ie_iei_pdu_session_id = 0x12; + const static uint8_t ie_iei_additional_information = 0x24; + const static uint8_t ie_iei_cause_5gmm = 0x58; + const static uint8_t ie_iei_back_off_timer_value = 0x37; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // dl_nas_transport_t + +/* + * Message: PDU session establishment request. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_establishment_request_t +{ +public: + // Mandatory fields + integrity_protection_maximum_data_rate_t integrity_protection_maximum_data_rate; + + // Optional fields + bool pdu_session_type_present = false; + bool ssc_mode_present = false; + bool capability_5gsm_present = false; + bool maximum_number_of_supported_packet_filters_present = false; + bool always_on_pdu_session_requested_present = false; + bool sm_pdu_dn_request_container_present = false; + bool extended_protocol_configuration_options_present = false; + bool ip_header_compression_configuration_present = false; + bool ds_tt__ethernet_port_mac_address_present = false; + bool ue_ds_tt_residence_time_present = false; + bool port_management_information_container_present = false; + bool ethernet_header_compression_configuration_present = false; + bool suggested_interface_identifier_present = false; + + pdu_session_type_t pdu_session_type; + ssc_mode_t ssc_mode; + capability_5gsm_t capability_5gsm; + maximum_number_of_supported_packet_filters_t maximum_number_of_supported_packet_filters; + always_on_pdu_session_requested_t always_on_pdu_session_requested; + sm_pdu_dn_request_container_t sm_pdu_dn_request_container; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + ip_header_compression_configuration_t ip_header_compression_configuration; + ds_tt__ethernet_port_mac_address_t ds_tt__ethernet_port_mac_address; + ue_ds_tt_residence_time_t ue_ds_tt_residence_time; + port_management_information_container_t port_management_information_container; + ethernet_header_compression_configuration_t ethernet_header_compression_configuration; + pdu_address_t suggested_interface_identifier; + + const static uint8_t ie_iei_pdu_session_type = 0x9; + const static uint8_t ie_iei_ssc_mode = 0xA; + const static uint8_t ie_iei_capability_5gsm = 0x28; + const static uint8_t ie_iei_maximum_number_of_supported_packet_filters = 0x55; + const static uint8_t ie_iei_always_on_pdu_session_requested = 0xB; + const static uint8_t ie_iei_sm_pdu_dn_request_container = 0x39; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + const static uint8_t ie_iei_ip_header_compression_configuration = 0x66; + const static uint8_t ie_iei_ds_tt__ethernet_port_mac_address = 0x6E; + const static uint8_t ie_iei_ue_ds_tt_residence_time = 0x6F; + const static uint8_t ie_iei_port_management_information_container = 0x74; + const static uint8_t ie_iei_ethernet_header_compression_configuration = 0x1F; + const static uint8_t ie_iei_suggested_interface_identifier = 0x29; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_establishment_request_t + +/* + * Message: PDU session establishment accept. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_establishment_accept_t +{ +public: + // Mandatory fields + pdu_session_type_t selected_pdu_session_type; + ssc_mode_t selected_ssc_mode; + qo_s_rules_t authorized__qo_s_rules; + session_ambr_t session_ambr; + + // Optional fields + bool cause_5gsm_present = false; + bool pdu_address_present = false; + bool rq_timer_value_present = false; + bool s_nssai_present = false; + bool always_on_pdu_session_indication_present = false; + bool mapped_eps_bearer_contexts_present = false; + bool eap_message_present = false; + bool authorized__qo_s_flow_descriptions_present = false; + bool extended_protocol_configuration_options_present = false; + bool dnn_present = false; + bool network_feature_support_5gsm_present = false; + bool serving_plmn_rate_control_present = false; + bool atsss_container_present = false; + bool control_plane_only_indication_present = false; + bool ip_header_compression_configuration_present = false; + bool ethernet_header_compression_configuration_present = false; + + cause_5gsm_t cause_5gsm; + pdu_address_t pdu_address; + gprs_timer_t rq_timer_value; + s_nssai_t s_nssai; + always_on_pdu_session_indication_t always_on_pdu_session_indication; + mapped_eps_bearer_contexts_t mapped_eps_bearer_contexts; + eap_message_t eap_message; + qo_s_flow_descriptions_t authorized__qo_s_flow_descriptions; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + dnn_t dnn; + network_feature_support_5gsm_t network_feature_support_5gsm; + serving_plmn_rate_control_t serving_plmn_rate_control; + atsss_container_t atsss_container; + control_plane_only_indication_t control_plane_only_indication; + ip_header_compression_configuration_t ip_header_compression_configuration; + ethernet_header_compression_configuration_t ethernet_header_compression_configuration; + + const static uint8_t ie_iei_cause_5gsm = 0x59; + const static uint8_t ie_iei_pdu_address = 0x29; + const static uint8_t ie_iei_rq_timer_value = 0x56; + const static uint8_t ie_iei_s_nssai = 0x22; + const static uint8_t ie_iei_always_on_pdu_session_indication = 0x8; + const static uint8_t ie_iei_mapped_eps_bearer_contexts = 0x75; + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_authorized__qo_s_flow_descriptions = 0x79; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + const static uint8_t ie_iei_dnn = 0x25; + const static uint8_t ie_iei_network_feature_support_5gsm = 0x17; + const static uint8_t ie_iei_serving_plmn_rate_control = 0x18; + const static uint8_t ie_iei_atsss_container = 0x77; + const static uint8_t ie_iei_control_plane_only_indication = 0xC; + const static uint8_t ie_iei_ip_header_compression_configuration = 0x66; + const static uint8_t ie_iei_ethernet_header_compression_configuration = 0x1F; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_establishment_accept_t + +/* + * Message: PDU session establishment reject. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_establishment_reject_t +{ +public: + // Mandatory fields + cause_5gsm_t cause_5gsm; + + // Optional fields + bool back_off_timer_value_present = false; + bool allowed_ssc_mode_present = false; + bool eap_message_present = false; + bool congestion_re_attempt_indicator_5gsm_present = false; + bool extended_protocol_configuration_options_present = false; + bool re_attempt_indicator_present = false; + + gprs_timer_3_t back_off_timer_value; + allowed_ssc_mode_t allowed_ssc_mode; + eap_message_t eap_message; + congestion_re_attempt_indicator_5gsm_t congestion_re_attempt_indicator_5gsm; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + re_attempt_indicator_t re_attempt_indicator; + + const static uint8_t ie_iei_back_off_timer_value = 0x37; + const static uint8_t ie_iei_allowed_ssc_mode = 0xF; + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_congestion_re_attempt_indicator_5gsm = 0x61; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + const static uint8_t ie_iei_re_attempt_indicator = 0x1D; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_establishment_reject_t + +/* + * Message: PDU session authentication command. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_authentication_command_t +{ +public: + // Mandatory fields + eap_message_t eap_message; + + // Optional fields + bool extended_protocol_configuration_options_present = false; + + extended_protocol_configuration_options_t extended_protocol_configuration_options; + + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_authentication_command_t + +/* + * Message: PDU session authentication complete. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_authentication_complete_t +{ +public: + // Mandatory fields + eap_message_t eap_message; + + // Optional fields + bool extended_protocol_configuration_options_present = false; + + extended_protocol_configuration_options_t extended_protocol_configuration_options; + + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_authentication_complete_t + +/* + * Message: PDU session authentication result. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_authentication_result_t +{ +public: + // Mandatory fields + + // Optional fields + bool eap_message_present = false; + bool extended_protocol_configuration_options_present = false; + + eap_message_t eap_message; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_authentication_result_t + +/* + * Message: PDU session modification request. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_modification_request_t +{ +public: + // Mandatory fields + + // Optional fields + bool capability_5gsm_present = false; + bool cause_5gsm_present = false; + bool maximum_number_of_supported_packet_filters_present = false; + bool always_on_pdu_session_requested_present = false; + bool integrity_protection_maximum_data_rate_present = false; + bool requested__qo_s_rules_present = false; + bool requested__qo_s_flow_descriptions_present = false; + bool mapped_eps_bearer_contexts_present = false; + bool extended_protocol_configuration_options_present = false; + bool port_management_information_container_present = false; + bool ip_header_compression_configuration_present = false; + bool ethernet_header_compression_configuration_present = false; + + capability_5gsm_t capability_5gsm; + cause_5gsm_t cause_5gsm; + maximum_number_of_supported_packet_filters_t maximum_number_of_supported_packet_filters; + always_on_pdu_session_requested_t always_on_pdu_session_requested; + integrity_protection_maximum_data_rate_t integrity_protection_maximum_data_rate; + qo_s_rules_t requested__qo_s_rules; + qo_s_flow_descriptions_t requested__qo_s_flow_descriptions; + mapped_eps_bearer_contexts_t mapped_eps_bearer_contexts; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + port_management_information_container_t port_management_information_container; + ip_header_compression_configuration_t ip_header_compression_configuration; + ethernet_header_compression_configuration_t ethernet_header_compression_configuration; + + const static uint8_t ie_iei_capability_5gsm = 0x28; + const static uint8_t ie_iei_cause_5gsm = 0x59; + const static uint8_t ie_iei_maximum_number_of_supported_packet_filters = 0x55; + const static uint8_t ie_iei_always_on_pdu_session_requested = 0xB; + const static uint8_t ie_iei_integrity_protection_maximum_data_rate = 0x13; + const static uint8_t ie_iei_requested__qo_s_rules = 0x7A; + const static uint8_t ie_iei_requested__qo_s_flow_descriptions = 0x79; + const static uint8_t ie_iei_mapped_eps_bearer_contexts = 0x75; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + const static uint8_t ie_iei_port_management_information_container = 0x74; + const static uint8_t ie_iei_ip_header_compression_configuration = 0x66; + const static uint8_t ie_iei_ethernet_header_compression_configuration = 0x1F; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_modification_request_t + +/* + * Message: PDU session modification reject. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_modification_reject_t +{ +public: + // Mandatory fields + cause_5gsm_t cause_5gsm; + + // Optional fields + bool back_off_timer_value_present = false; + bool congestion_re_attempt_indicator_5gsm_present = false; + bool extended_protocol_configuration_options_present = false; + bool re_attempt_indicator_present = false; + + gprs_timer_3_t back_off_timer_value; + congestion_re_attempt_indicator_5gsm_t congestion_re_attempt_indicator_5gsm; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + re_attempt_indicator_t re_attempt_indicator; + + const static uint8_t ie_iei_back_off_timer_value = 0x37; + const static uint8_t ie_iei_congestion_re_attempt_indicator_5gsm = 0x61; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + const static uint8_t ie_iei_re_attempt_indicator = 0x1D; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_modification_reject_t + +/* + * Message: PDU session modification command. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_modification_command_t +{ +public: + // Mandatory fields + + // Optional fields + bool cause_5gsm_present = false; + bool session_ambr_present = false; + bool rq_timer_value_present = false; + bool always_on_pdu_session_indication_present = false; + bool authorized__qo_s_rules_present = false; + bool mapped_eps_bearer_contexts_present = false; + bool authorized__qo_s_flow_descriptions_present = false; + bool extended_protocol_configuration_options_present = false; + bool atsss_container_present = false; + bool ip_header_compression_configuration_present = false; + bool port_management_information_container_present = false; + bool serving_plmn_rate_control_present = false; + bool ethernet_header_compression_configuration_present = false; + + cause_5gsm_t cause_5gsm; + session_ambr_t session_ambr; + gprs_timer_t rq_timer_value; + always_on_pdu_session_indication_t always_on_pdu_session_indication; + qo_s_rules_t authorized__qo_s_rules; + mapped_eps_bearer_contexts_t mapped_eps_bearer_contexts; + qo_s_flow_descriptions_t authorized__qo_s_flow_descriptions; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + atsss_container_t atsss_container; + ip_header_compression_configuration_t ip_header_compression_configuration; + port_management_information_container_t port_management_information_container; + serving_plmn_rate_control_t serving_plmn_rate_control; + ethernet_header_compression_configuration_t ethernet_header_compression_configuration; + + const static uint8_t ie_iei_cause_5gsm = 0x59; + const static uint8_t ie_iei_session_ambr = 0x2A; + const static uint8_t ie_iei_rq_timer_value = 0x56; + const static uint8_t ie_iei_always_on_pdu_session_indication = 0x8; + const static uint8_t ie_iei_authorized__qo_s_rules = 0x7A; + const static uint8_t ie_iei_mapped_eps_bearer_contexts = 0x75; + const static uint8_t ie_iei_authorized__qo_s_flow_descriptions = 0x79; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + const static uint8_t ie_iei_atsss_container = 0x77; + const static uint8_t ie_iei_ip_header_compression_configuration = 0x66; + const static uint8_t ie_iei_port_management_information_container = 0x74; + const static uint8_t ie_iei_serving_plmn_rate_control = 0x1E; + const static uint8_t ie_iei_ethernet_header_compression_configuration = 0x1F; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_modification_command_t + +/* + * Message: PDU session modification complete. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_modification_complete_t +{ +public: + // Mandatory fields + + // Optional fields + bool extended_protocol_configuration_options_present = false; + bool port_management_information_container_present = false; + + extended_protocol_configuration_options_t extended_protocol_configuration_options; + port_management_information_container_t port_management_information_container; + + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + const static uint8_t ie_iei_port_management_information_container = 0x74; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_modification_complete_t + +/* + * Message: PDU session modification command reject. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_modification_command_reject_t +{ +public: + // Mandatory fields + cause_5gsm_t cause_5gsm; + + // Optional fields + bool extended_protocol_configuration_options_present = false; + + extended_protocol_configuration_options_t extended_protocol_configuration_options; + + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_modification_command_reject_t + +/* + * Message: PDU session release request. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_release_request_t +{ +public: + // Mandatory fields + + // Optional fields + bool cause_5gsm_present = false; + bool extended_protocol_configuration_options_present = false; + + cause_5gsm_t cause_5gsm; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + + const static uint8_t ie_iei_cause_5gsm = 0x59; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_release_request_t + +/* + * Message: PDU session release reject. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_release_reject_t +{ +public: + // Mandatory fields + cause_5gsm_t cause_5gsm; + + // Optional fields + bool extended_protocol_configuration_options_present = false; + + extended_protocol_configuration_options_t extended_protocol_configuration_options; + + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_release_reject_t + +/* + * Message: PDU session release command. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_release_command_t +{ +public: + // Mandatory fields + cause_5gsm_t cause_5gsm; + + // Optional fields + bool back_off_timer_value_present = false; + bool eap_message_present = false; + bool congestion_re_attempt_indicator_5gsm_present = false; + bool extended_protocol_configuration_options_present = false; + bool access_type_present = false; + + gprs_timer_3_t back_off_timer_value; + eap_message_t eap_message; + congestion_re_attempt_indicator_5gsm_t congestion_re_attempt_indicator_5gsm; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + access_type_t access_type; + + const static uint8_t ie_iei_back_off_timer_value = 0x37; + const static uint8_t ie_iei_eap_message = 0x78; + const static uint8_t ie_iei_congestion_re_attempt_indicator_5gsm = 0x61; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + const static uint8_t ie_iei_access_type = 0xD; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_release_command_t + +/* + * Message: PDU session release complete. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class pdu_session_release_complete_t +{ +public: + // Mandatory fields + + // Optional fields + bool cause_5gsm_present = false; + bool extended_protocol_configuration_options_present = false; + + cause_5gsm_t cause_5gsm; + extended_protocol_configuration_options_t extended_protocol_configuration_options; + + const static uint8_t ie_iei_cause_5gsm = 0x59; + const static uint8_t ie_iei_extended_protocol_configuration_options = 0x7B; + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // pdu_session_release_complete_t + +/* + * Message: Status 5GSM. + * Based on 3GPP TS 24.501 v16.7.0 + */ + +class status_5gsm_t +{ +public: + // Mandatory fields + cause_5gsm_t cause_5gsm; + + // Optional fields + +public: + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + +}; // status_5gsm_t +// Include from nas5g/infiles/nas_5g_msg.h.in + +struct msg_opts { + enum options { + registration_request = 0x41, + registration_accept = 0x42, + registration_complete = 0x43, + registration_reject = 0x44, + deregistration_request_ue_originating = 0x45, + deregistration_accept_ue_originating = 0x46, + deregistration_request_ue_terminated = 0x47, + deregistration_accept_ue_terminated = 0x48, + service_request = 0x4c, + service_reject = 0x4d, + service_accept = 0x4e, + configuration_update_command = 0x54, + configuration_update_complete = 0x55, + authentication_request = 0x56, + authentication_response = 0x57, + authentication_reject = 0x58, + authentication_failure = 0x59, + authentication_result = 0x5a, + identity_request = 0x5b, + identity_response = 0x5c, + security_mode_command = 0x5d, + security_mode_complete = 0x5e, + security_mode_reject = 0x5f, + status_5gmm = 0x64, + notification = 0x65, + notification_response = 0x66, + ul_nas_transport = 0x67, + dl_nas_transport = 0x68, + pdu_session_establishment_request = 0xc1, + pdu_session_establishment_accept = 0xc2, + pdu_session_establishment_reject = 0xc3, + pdu_session_authentication_command = 0xc5, + pdu_session_authentication_complete = 0xc6, + pdu_session_authentication_result = 0xc7, + pdu_session_modification_request = 0xc9, + pdu_session_modification_reject = 0xca, + pdu_session_modification_command = 0xcb, + pdu_session_modification_complete = 0xcc, + pdu_session_modification_command_reject = 0xcd, + pdu_session_release_request = 0xd1, + pdu_session_release_reject = 0xd2, + pdu_session_release_command = 0xd3, + pdu_session_release_complete = 0xd4, + status_5gsm = 0xd6, + nulltype = 0xff, + + } value; + const char* to_string() const + { + switch (value) { + case registration_request: + return "Registration request"; + case registration_accept: + return "Registration accept"; + case registration_complete: + return "Registration complete"; + case registration_reject: + return "Registration reject"; + case deregistration_request_ue_originating: + return "Deregistration request UE originating"; + case deregistration_accept_ue_originating: + return "Deregistration accept UE originating"; + case deregistration_request_ue_terminated: + return "Deregistration request UE terminated"; + case deregistration_accept_ue_terminated: + return "Deregistration accept UE terminated"; + case service_request: + return "Service request"; + case service_reject: + return "Service reject"; + case service_accept: + return "Service accept"; + case configuration_update_command: + return "Configuration update command"; + case configuration_update_complete: + return "Configuration update complete"; + case authentication_request: + return "Authentication request"; + case authentication_response: + return "Authentication response"; + case authentication_reject: + return "Authentication reject"; + case authentication_failure: + return "Authentication failure"; + case authentication_result: + return "Authentication result"; + case identity_request: + return "Identity request"; + case identity_response: + return "Identity response"; + case security_mode_command: + return "Security mode command"; + case security_mode_complete: + return "Security mode complete"; + case security_mode_reject: + return "Security mode reject"; + case status_5gmm: + return "Status 5GMM"; + case notification: + return "Notification"; + case notification_response: + return "Notification response"; + case ul_nas_transport: + return "UL NAS transport"; + case dl_nas_transport: + return "DL NAS transport "; + case pdu_session_establishment_request: + return "PDU session establishment request"; + case pdu_session_establishment_accept: + return "PDU session establishment accept"; + case pdu_session_establishment_reject: + return "PDU session establishment reject"; + case pdu_session_authentication_command: + return "PDU session authentication command"; + case pdu_session_authentication_complete: + return "PDU session authentication complete"; + case pdu_session_authentication_result: + return "PDU session authentication result"; + case pdu_session_modification_request: + return "PDU session modification request"; + case pdu_session_modification_reject: + return "PDU session modification reject"; + case pdu_session_modification_command: + return "PDU session modification command"; + case pdu_session_modification_complete: + return "PDU session modification complete"; + case pdu_session_modification_command_reject: + return "PDU session modification command reject"; + case pdu_session_release_request: + return "PDU session release request"; + case pdu_session_release_reject: + return "PDU session release reject"; + case pdu_session_release_command: + return "PDU session release command"; + case pdu_session_release_complete: + return "PDU session release complete"; + case status_5gsm: + return "Status 5GSM"; + default: + return "Error"; + } + } +}; + +typedef asn1::enumerated msg_types; +struct nas_5gs_hdr { + enum security_header_type_opts { + plain_5gs_nas_message, + integrity_protected, + integrity_protected_and_ciphered, + integrity_protected_with_new_5G_nas_context, + integrity_protected_and_ciphered_with_new_5G_nas_context + }; + + enum extended_protocol_discriminator_opts { + extended_protocol_discriminator_5gsm = 0x2e, + extended_protocol_discriminator_5gmm = 0x7e, + }; + + // Outer + extended_protocol_discriminator_opts extended_protocol_discriminator = extended_protocol_discriminator_5gsm; + security_header_type_opts security_header_type = plain_5gs_nas_message; + // Only valid if not plain + uint8_t sequence_number = 0xff; + uint32_t message_authentication_code = 0x00000000; + // Only valid if 5gsm type + uint8_t pdu_session_identity = 0xff; + uint8_t procedure_transaction_identity = 0xff; + // Inner + extended_protocol_discriminator_opts inner_extended_protocol_discriminator = extended_protocol_discriminator_5gsm; + security_header_type_opts inner_security_header_type = plain_5gs_nas_message; + + msg_types message_type = msg_types::options::nulltype; + + SRSASN_CODE pack(asn1::bit_ref& bref); + SRSASN_CODE pack_outer(asn1::bit_ref& bref); + SRSASN_CODE unpack(asn1::cbit_ref& bref); + SRSASN_CODE unpack_outer(asn1::cbit_ref& bref); +}; + +class nas_5gs_msg +{ +public: + nas_5gs_hdr hdr; + + SRSASN_CODE pack(unique_byte_buffer_t& buf); + SRSASN_CODE pack(std::vector& buf); + SRSASN_CODE unpack(const unique_byte_buffer_t& buf); + SRSASN_CODE unpack(const std::vector& buf); + SRSASN_CODE unpack_outer_hdr(const unique_byte_buffer_t& buf); + SRSASN_CODE unpack_outer_hdr(const std::vector& buf); + + void set(msg_types::options e = msg_types::nulltype) { hdr.message_type = e; }; + // Getters + + registration_request_t& registration_request() + { + asn1::assert_choice_type(msg_types::options::registration_request, hdr.message_type, "registration_request"); + return *srslog::detail::any_cast(&msg_container); + } + + registration_accept_t& registration_accept() + { + asn1::assert_choice_type(msg_types::options::registration_accept, hdr.message_type, "registration_accept"); + return *srslog::detail::any_cast(&msg_container); + } + + registration_complete_t& registration_complete() + { + asn1::assert_choice_type(msg_types::options::registration_complete, hdr.message_type, "registration_complete"); + return *srslog::detail::any_cast(&msg_container); + } + + registration_reject_t& registration_reject() + { + asn1::assert_choice_type(msg_types::options::registration_reject, hdr.message_type, "registration_reject"); + return *srslog::detail::any_cast(&msg_container); + } + + deregistration_request_ue_originating_t& deregistration_request_ue_originating() + { + asn1::assert_choice_type(msg_types::options::deregistration_request_ue_originating, + hdr.message_type, + "deregistration_request_ue_originating"); + return *srslog::detail::any_cast(&msg_container); + } + + deregistration_accept_ue_originating_t& deregistration_accept_ue_originating() + { + asn1::assert_choice_type(msg_types::options::deregistration_accept_ue_originating, + hdr.message_type, + "deregistration_accept_ue_originating"); + return *srslog::detail::any_cast(&msg_container); + } + + deregistration_request_ue_terminated_t& deregistration_request_ue_terminated() + { + asn1::assert_choice_type(msg_types::options::deregistration_request_ue_terminated, + hdr.message_type, + "deregistration_request_ue_terminated"); + return *srslog::detail::any_cast(&msg_container); + } + + deregistration_accept_ue_terminated_t& deregistration_accept_ue_terminated() + { + asn1::assert_choice_type(msg_types::options::deregistration_accept_ue_terminated, + hdr.message_type, + "deregistration_accept_ue_terminated"); + return *srslog::detail::any_cast(&msg_container); + } + + service_request_t& service_request() + { + asn1::assert_choice_type(msg_types::options::service_request, hdr.message_type, "service_request"); + return *srslog::detail::any_cast(&msg_container); + } + + service_reject_t& service_reject() + { + asn1::assert_choice_type(msg_types::options::service_reject, hdr.message_type, "service_reject"); + return *srslog::detail::any_cast(&msg_container); + } + + service_accept_t& service_accept() + { + asn1::assert_choice_type(msg_types::options::service_accept, hdr.message_type, "service_accept"); + return *srslog::detail::any_cast(&msg_container); + } + + configuration_update_command_t& configuration_update_command() + { + asn1::assert_choice_type( + msg_types::options::configuration_update_command, hdr.message_type, "configuration_update_command"); + return *srslog::detail::any_cast(&msg_container); + } + + configuration_update_complete_t& configuration_update_complete() + { + asn1::assert_choice_type( + msg_types::options::configuration_update_complete, hdr.message_type, "configuration_update_complete"); + return *srslog::detail::any_cast(&msg_container); + } + + authentication_request_t& authentication_request() + { + asn1::assert_choice_type(msg_types::options::authentication_request, hdr.message_type, "authentication_request"); + return *srslog::detail::any_cast(&msg_container); + } + + authentication_response_t& authentication_response() + { + asn1::assert_choice_type(msg_types::options::authentication_response, hdr.message_type, "authentication_response"); + return *srslog::detail::any_cast(&msg_container); + } + + authentication_reject_t& authentication_reject() + { + asn1::assert_choice_type(msg_types::options::authentication_reject, hdr.message_type, "authentication_reject"); + return *srslog::detail::any_cast(&msg_container); + } + + authentication_failure_t& authentication_failure() + { + asn1::assert_choice_type(msg_types::options::authentication_failure, hdr.message_type, "authentication_failure"); + return *srslog::detail::any_cast(&msg_container); + } + + authentication_result_t& authentication_result() + { + asn1::assert_choice_type(msg_types::options::authentication_result, hdr.message_type, "authentication_result"); + return *srslog::detail::any_cast(&msg_container); + } + + identity_request_t& identity_request() + { + asn1::assert_choice_type(msg_types::options::identity_request, hdr.message_type, "identity_request"); + return *srslog::detail::any_cast(&msg_container); + } + + identity_response_t& identity_response() + { + asn1::assert_choice_type(msg_types::options::identity_response, hdr.message_type, "identity_response"); + return *srslog::detail::any_cast(&msg_container); + } + + security_mode_command_t& security_mode_command() + { + asn1::assert_choice_type(msg_types::options::security_mode_command, hdr.message_type, "security_mode_command"); + return *srslog::detail::any_cast(&msg_container); + } + + security_mode_complete_t& security_mode_complete() + { + asn1::assert_choice_type(msg_types::options::security_mode_complete, hdr.message_type, "security_mode_complete"); + return *srslog::detail::any_cast(&msg_container); + } + + security_mode_reject_t& security_mode_reject() + { + asn1::assert_choice_type(msg_types::options::security_mode_reject, hdr.message_type, "security_mode_reject"); + return *srslog::detail::any_cast(&msg_container); + } + + status_5gmm_t& status_5gmm() + { + asn1::assert_choice_type(msg_types::options::status_5gmm, hdr.message_type, "status_5gmm"); + return *srslog::detail::any_cast(&msg_container); + } + + notification_t& notification() + { + asn1::assert_choice_type(msg_types::options::notification, hdr.message_type, "notification"); + return *srslog::detail::any_cast(&msg_container); + } + + notification_response_t& notification_response() + { + asn1::assert_choice_type(msg_types::options::notification_response, hdr.message_type, "notification_response"); + return *srslog::detail::any_cast(&msg_container); + } + + ul_nas_transport_t& ul_nas_transport() + { + asn1::assert_choice_type(msg_types::options::ul_nas_transport, hdr.message_type, "ul_nas_transport"); + return *srslog::detail::any_cast(&msg_container); + } + + dl_nas_transport_t& dl_nas_transport() + { + asn1::assert_choice_type(msg_types::options::dl_nas_transport, hdr.message_type, "dl_nas_transport"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_establishment_request_t& pdu_session_establishment_request() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_establishment_request, hdr.message_type, "pdu_session_establishment_request"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_establishment_accept_t& pdu_session_establishment_accept() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_establishment_accept, hdr.message_type, "pdu_session_establishment_accept"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_establishment_reject_t& pdu_session_establishment_reject() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_establishment_reject, hdr.message_type, "pdu_session_establishment_reject"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_authentication_command_t& pdu_session_authentication_command() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_authentication_command, hdr.message_type, "pdu_session_authentication_command"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_authentication_complete_t& pdu_session_authentication_complete() + { + asn1::assert_choice_type(msg_types::options::pdu_session_authentication_complete, + hdr.message_type, + "pdu_session_authentication_complete"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_authentication_result_t& pdu_session_authentication_result() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_authentication_result, hdr.message_type, "pdu_session_authentication_result"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_modification_request_t& pdu_session_modification_request() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_modification_request, hdr.message_type, "pdu_session_modification_request"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_modification_reject_t& pdu_session_modification_reject() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_modification_reject, hdr.message_type, "pdu_session_modification_reject"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_modification_command_t& pdu_session_modification_command() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_modification_command, hdr.message_type, "pdu_session_modification_command"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_modification_complete_t& pdu_session_modification_complete() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_modification_complete, hdr.message_type, "pdu_session_modification_complete"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_modification_command_reject_t& pdu_session_modification_command_reject() + { + asn1::assert_choice_type(msg_types::options::pdu_session_modification_command_reject, + hdr.message_type, + "pdu_session_modification_command_reject"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_release_request_t& pdu_session_release_request() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_release_request, hdr.message_type, "pdu_session_release_request"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_release_reject_t& pdu_session_release_reject() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_release_reject, hdr.message_type, "pdu_session_release_reject"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_release_command_t& pdu_session_release_command() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_release_command, hdr.message_type, "pdu_session_release_command"); + return *srslog::detail::any_cast(&msg_container); + } + + pdu_session_release_complete_t& pdu_session_release_complete() + { + asn1::assert_choice_type( + msg_types::options::pdu_session_release_complete, hdr.message_type, "pdu_session_release_complete"); + return *srslog::detail::any_cast(&msg_container); + } + + status_5gsm_t& status_5gsm() + { + asn1::assert_choice_type(msg_types::options::status_5gsm, hdr.message_type, "status_5gsm"); + return *srslog::detail::any_cast(&msg_container); + } + + // Setters + + registration_request_t& set_registration_request() + { + set(msg_types::options::registration_request); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{registration_request_t()}; + return *srslog::detail::any_cast(&msg_container); + } + registration_accept_t& set_registration_accept() + { + set(msg_types::options::registration_accept); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{registration_accept_t()}; + return *srslog::detail::any_cast(&msg_container); + } + registration_complete_t& set_registration_complete() + { + set(msg_types::options::registration_complete); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{registration_complete_t()}; + return *srslog::detail::any_cast(&msg_container); + } + registration_reject_t& set_registration_reject() + { + set(msg_types::options::registration_reject); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{registration_reject_t()}; + return *srslog::detail::any_cast(&msg_container); + } + deregistration_request_ue_originating_t& set_deregistration_request_ue_originating() + { + set(msg_types::options::deregistration_request_ue_originating); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{deregistration_request_ue_originating_t()}; + return *srslog::detail::any_cast(&msg_container); + } + deregistration_accept_ue_originating_t& set_deregistration_accept_ue_originating() + { + set(msg_types::options::deregistration_accept_ue_originating); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{deregistration_accept_ue_originating_t()}; + return *srslog::detail::any_cast(&msg_container); + } + deregistration_request_ue_terminated_t& set_deregistration_request_ue_terminated() + { + set(msg_types::options::deregistration_request_ue_terminated); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{deregistration_request_ue_terminated_t()}; + return *srslog::detail::any_cast(&msg_container); + } + deregistration_accept_ue_terminated_t& set_deregistration_accept_ue_terminated() + { + set(msg_types::options::deregistration_accept_ue_terminated); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{deregistration_accept_ue_terminated_t()}; + return *srslog::detail::any_cast(&msg_container); + } + service_request_t& set_service_request() + { + set(msg_types::options::service_request); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{service_request_t()}; + return *srslog::detail::any_cast(&msg_container); + } + service_reject_t& set_service_reject() + { + set(msg_types::options::service_reject); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{service_reject_t()}; + return *srslog::detail::any_cast(&msg_container); + } + service_accept_t& set_service_accept() + { + set(msg_types::options::service_accept); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{service_accept_t()}; + return *srslog::detail::any_cast(&msg_container); + } + configuration_update_command_t& set_configuration_update_command() + { + set(msg_types::options::configuration_update_command); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{configuration_update_command_t()}; + return *srslog::detail::any_cast(&msg_container); + } + configuration_update_complete_t& set_configuration_update_complete() + { + set(msg_types::options::configuration_update_complete); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{configuration_update_complete_t()}; + return *srslog::detail::any_cast(&msg_container); + } + authentication_request_t& set_authentication_request() + { + set(msg_types::options::authentication_request); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_request_t()}; + return *srslog::detail::any_cast(&msg_container); + } + authentication_response_t& set_authentication_response() + { + set(msg_types::options::authentication_response); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_response_t()}; + return *srslog::detail::any_cast(&msg_container); + } + authentication_reject_t& set_authentication_reject() + { + set(msg_types::options::authentication_reject); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_reject_t()}; + return *srslog::detail::any_cast(&msg_container); + } + authentication_failure_t& set_authentication_failure() + { + set(msg_types::options::authentication_failure); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_failure_t()}; + return *srslog::detail::any_cast(&msg_container); + } + authentication_result_t& set_authentication_result() + { + set(msg_types::options::authentication_result); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{authentication_result_t()}; + return *srslog::detail::any_cast(&msg_container); + } + identity_request_t& set_identity_request() + { + set(msg_types::options::identity_request); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{identity_request_t()}; + return *srslog::detail::any_cast(&msg_container); + } + identity_response_t& set_identity_response() + { + set(msg_types::options::identity_response); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{identity_response_t()}; + return *srslog::detail::any_cast(&msg_container); + } + security_mode_command_t& set_security_mode_command() + { + set(msg_types::options::security_mode_command); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{security_mode_command_t()}; + return *srslog::detail::any_cast(&msg_container); + } + security_mode_complete_t& set_security_mode_complete() + { + set(msg_types::options::security_mode_complete); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{security_mode_complete_t()}; + return *srslog::detail::any_cast(&msg_container); + } + security_mode_reject_t& set_security_mode_reject() + { + set(msg_types::options::security_mode_reject); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{security_mode_reject_t()}; + return *srslog::detail::any_cast(&msg_container); + } + status_5gmm_t& set_status_5gmm() + { + set(msg_types::options::status_5gmm); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{status_5gmm_t()}; + return *srslog::detail::any_cast(&msg_container); + } + notification_t& set_notification() + { + set(msg_types::options::notification); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{notification_t()}; + return *srslog::detail::any_cast(&msg_container); + } + notification_response_t& set_notification_response() + { + set(msg_types::options::notification_response); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{notification_response_t()}; + return *srslog::detail::any_cast(&msg_container); + } + ul_nas_transport_t& set_ul_nas_transport() + { + set(msg_types::options::ul_nas_transport); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{ul_nas_transport_t()}; + return *srslog::detail::any_cast(&msg_container); + } + dl_nas_transport_t& set_dl_nas_transport() + { + set(msg_types::options::dl_nas_transport); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gmm; + msg_container = srslog::detail::any{dl_nas_transport_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_establishment_request_t& set_pdu_session_establishment_request() + { + set(msg_types::options::pdu_session_establishment_request); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_establishment_request_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_establishment_accept_t& set_pdu_session_establishment_accept() + { + set(msg_types::options::pdu_session_establishment_accept); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_establishment_accept_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_establishment_reject_t& set_pdu_session_establishment_reject() + { + set(msg_types::options::pdu_session_establishment_reject); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_establishment_reject_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_authentication_command_t& set_pdu_session_authentication_command() + { + set(msg_types::options::pdu_session_authentication_command); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_authentication_command_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_authentication_complete_t& set_pdu_session_authentication_complete() + { + set(msg_types::options::pdu_session_authentication_complete); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_authentication_complete_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_authentication_result_t& set_pdu_session_authentication_result() + { + set(msg_types::options::pdu_session_authentication_result); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_authentication_result_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_modification_request_t& set_pdu_session_modification_request() + { + set(msg_types::options::pdu_session_modification_request); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_request_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_modification_reject_t& set_pdu_session_modification_reject() + { + set(msg_types::options::pdu_session_modification_reject); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_reject_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_modification_command_t& set_pdu_session_modification_command() + { + set(msg_types::options::pdu_session_modification_command); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_command_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_modification_complete_t& set_pdu_session_modification_complete() + { + set(msg_types::options::pdu_session_modification_complete); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_complete_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_modification_command_reject_t& set_pdu_session_modification_command_reject() + { + set(msg_types::options::pdu_session_modification_command_reject); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_modification_command_reject_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_release_request_t& set_pdu_session_release_request() + { + set(msg_types::options::pdu_session_release_request); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_release_request_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_release_reject_t& set_pdu_session_release_reject() + { + set(msg_types::options::pdu_session_release_reject); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_release_reject_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_release_command_t& set_pdu_session_release_command() + { + set(msg_types::options::pdu_session_release_command); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_release_command_t()}; + return *srslog::detail::any_cast(&msg_container); + } + pdu_session_release_complete_t& set_pdu_session_release_complete() + { + set(msg_types::options::pdu_session_release_complete); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{pdu_session_release_complete_t()}; + return *srslog::detail::any_cast(&msg_container); + } + status_5gsm_t& set_status_5gsm() + { + set(msg_types::options::status_5gsm); + hdr.extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + hdr.inner_extended_protocol_discriminator = nas_5gs_hdr::extended_protocol_discriminator_5gsm; + msg_container = srslog::detail::any{status_5gsm_t()}; + return *srslog::detail::any_cast(&msg_container); + } + +private: + SRSASN_CODE unpack(asn1::cbit_ref& bref); + SRSASN_CODE pack(asn1::bit_ref& bref); + srslog::detail::any msg_container = srslog::detail::any{registration_request_t()}; +}; +} // namespace nas_5g +} // namespace srsran +#endif diff --git a/lib/include/srsran/asn1/nas_5g_utils.h b/lib/include/srsran/asn1/nas_5g_utils.h new file mode 100644 index 000000000..dec443a71 --- /dev/null +++ b/lib/include/srsran/asn1/nas_5g_utils.h @@ -0,0 +1,96 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_NAS_5G_UTILS_H +#define SRSRAN_NAS_5G_UTILS_H + +#include "srsran/asn1/asn1_utils.h" +#include "srsran/common/byte_buffer.h" +#include "srsran/config.h" + +using namespace asn1; +namespace srsran { +namespace nas_5g { + +struct ecies_scheme_profile_a_out { + uint8_t ecc_ephemeral_key[33]; + std::vector ciphertext; + uint8_t mac_tag[8]; +}; + +struct ecies_scheme_profile_b_out { + uint8_t ecc_ephemeral_key[32]; + std::vector ciphertext; + uint8_t mac_tag[8]; +}; + +template +SRSASN_CODE unpack_enum(asn1::cbit_ref& bref, Enum* e) +{ + uint32_t tmp = {}; + HANDLE_CODE(bref.unpack(tmp, bl)); + *e = static_cast(tmp); + return SRSASN_SUCCESS; +} + +template +SRSASN_CODE pack_enum(asn1::bit_ref& bref, Enum e) +{ + uint32_t tmp = static_cast(e); + HANDLE_CODE(bref.pack(tmp, bl)); + return SRSASN_SUCCESS; +} + +template +class nas_enumerated : public EnumType +{ +public: + static const uint32_t bit_length = bit_length_; + + nas_enumerated() {} + nas_enumerated(typename EnumType::options o) { EnumType::value = o; } + SRSASN_CODE pack(asn1::bit_ref& bref) const + { + uint32_t tmp = static_cast(EnumType::value); + HANDLE_CODE(bref.pack(tmp, bit_length)); + return SRSASN_SUCCESS; + } + SRSASN_CODE unpack(asn1::cbit_ref& bref) + { + uint32_t tmp = {}; + HANDLE_CODE(bref.unpack(tmp, bit_length)); + *this = static_cast(tmp); + return SRSASN_SUCCESS; + } + EnumType& operator=(EnumType v) + { + EnumType::value = v; + return *this; + } + operator typename EnumType::options() const { return EnumType::value; } +}; + +SRSASN_CODE unpack_mcc_mnc(uint8_t* mcc_bytes, uint8_t* mnc_bytes, asn1::cbit_ref& bref); +SRSASN_CODE pack_mcc_mnc(uint8_t* mcc_bytes, uint8_t* mnc_bytes, asn1::bit_ref& bref); + +} // namespace nas_5g +} // namespace srsran +#endif // MANUAL_H \ No newline at end of file diff --git a/lib/include/srsran/asn1/ngap.h b/lib/include/srsran/asn1/ngap.h index f90f1a41f..b04f8e46d 100644 --- a/lib/include/srsran/asn1/ngap.h +++ b/lib/include/srsran/asn1/ngap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,393 +21,296 @@ /******************************************************************************* * - * 3GPP TS ASN1 NGAP NR v15.3.0 (2019-03) + * 3GPP TS ASN1 NGAP v15.3.0 (2019-03) * ******************************************************************************/ -#ifndef SRSASN1_NGAP_NR_H -#define SRSASN1_NGAP_NR_H +#ifndef SRSASN1_NGAP_H +#define SRSASN1_NGAP_H #include "asn1_utils.h" #include #include namespace asn1 { -namespace ngap_nr { +namespace ngap { /******************************************************************************* * Constant Definitions ******************************************************************************/ -#define ASN1_NGAP_NR_ID_AMF_CFG_UPD 0 -#define ASN1_NGAP_NR_ID_AMF_STATUS_IND 1 -#define ASN1_NGAP_NR_ID_CELL_TRAFFIC_TRACE 2 -#define ASN1_NGAP_NR_ID_DEACTIV_TRACE 3 -#define ASN1_NGAP_NR_ID_DL_NAS_TRANSPORT 4 -#define ASN1_NGAP_NR_ID_DL_NON_UEASSOCIATED_NRP_PA_TRANSPORT 5 -#define ASN1_NGAP_NR_ID_DL_RAN_CFG_TRANSFER 6 -#define ASN1_NGAP_NR_ID_DL_RAN_STATUS_TRANSFER 7 -#define ASN1_NGAP_NR_ID_DL_UEASSOCIATED_NRP_PA_TRANSPORT 8 -#define ASN1_NGAP_NR_ID_ERROR_IND 9 -#define ASN1_NGAP_NR_ID_HO_CANCEL 10 -#define ASN1_NGAP_NR_ID_HO_NOTIF 11 -#define ASN1_NGAP_NR_ID_HO_PREP 12 -#define ASN1_NGAP_NR_ID_HO_RES_ALLOC 13 -#define ASN1_NGAP_NR_ID_INIT_CONTEXT_SETUP 14 -#define ASN1_NGAP_NR_ID_INIT_UE_MSG 15 -#define ASN1_NGAP_NR_ID_LOCATION_REPORT_CTRL 16 -#define ASN1_NGAP_NR_ID_LOCATION_REPORT_FAIL_IND 17 -#define ASN1_NGAP_NR_ID_LOCATION_REPORT 18 -#define ASN1_NGAP_NR_ID_NAS_NON_DELIVERY_IND 19 -#define ASN1_NGAP_NR_ID_NG_RESET 20 -#define ASN1_NGAP_NR_ID_NG_SETUP 21 -#define ASN1_NGAP_NR_ID_OVERLOAD_START 22 -#define ASN1_NGAP_NR_ID_OVERLOAD_STOP 23 -#define ASN1_NGAP_NR_ID_PAGING 24 -#define ASN1_NGAP_NR_ID_PATH_SWITCH_REQUEST 25 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_MODIFY 26 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_MODIFY_IND 27 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_RELEASE 28 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_SETUP 29 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_NOTIFY 30 -#define ASN1_NGAP_NR_ID_PRIVATE_MSG 31 -#define ASN1_NGAP_NR_ID_PWS_CANCEL 32 -#define ASN1_NGAP_NR_ID_PWS_FAIL_IND 33 -#define ASN1_NGAP_NR_ID_PWS_RESTART_IND 34 -#define ASN1_NGAP_NR_ID_RAN_CFG_UPD 35 -#define ASN1_NGAP_NR_ID_REROUTE_NAS_REQUEST 36 -#define ASN1_NGAP_NR_ID_RRC_INACTIVE_TRANSITION_REPORT 37 -#define ASN1_NGAP_NR_ID_TRACE_FAIL_IND 38 -#define ASN1_NGAP_NR_ID_TRACE_START 39 -#define ASN1_NGAP_NR_ID_UE_CONTEXT_MOD 40 -#define ASN1_NGAP_NR_ID_UE_CONTEXT_RELEASE 41 -#define ASN1_NGAP_NR_ID_UE_CONTEXT_RELEASE_REQUEST 42 -#define ASN1_NGAP_NR_ID_UE_RADIO_CAP_CHECK 43 -#define ASN1_NGAP_NR_ID_UE_RADIO_CAP_INFO_IND 44 -#define ASN1_NGAP_NR_ID_UETNLA_BINDING_RELEASE 45 -#define ASN1_NGAP_NR_ID_UL_NAS_TRANSPORT 46 -#define ASN1_NGAP_NR_ID_UL_NON_UEASSOCIATED_NRP_PA_TRANSPORT 47 -#define ASN1_NGAP_NR_ID_UL_RAN_CFG_TRANSFER 48 -#define ASN1_NGAP_NR_ID_UL_RAN_STATUS_TRANSFER 49 -#define ASN1_NGAP_NR_ID_UL_UEASSOCIATED_NRP_PA_TRANSPORT 50 -#define ASN1_NGAP_NR_ID_WRITE_REPLACE_WARNING 51 -#define ASN1_NGAP_NR_ID_SECONDARY_RAT_DATA_USAGE_REPORT 52 -#define ASN1_NGAP_NR_MAX_PRIVATE_IES 65535 -#define ASN1_NGAP_NR_MAX_PROTOCOL_EXTS 65535 -#define ASN1_NGAP_NR_MAX_PROTOCOL_IES 65535 -#define ASN1_NGAP_NR_MAXNOOF_ALLOWED_AREAS 16 -#define ASN1_NGAP_NR_MAXNOOF_ALLOWED_S_NSSAIS 8 -#define ASN1_NGAP_NR_MAXNOOF_BPLMNS 12 -#define ASN1_NGAP_NR_MAXNOOF_CELL_IDFOR_WARNING 65535 -#define ASN1_NGAP_NR_MAXNOOF_CELLIN_AO_I 256 -#define ASN1_NGAP_NR_MAXNOOF_CELLIN_EAI 65535 -#define ASN1_NGAP_NR_MAXNOOF_CELLIN_TAI 65535 -#define ASN1_NGAP_NR_MAXNOOF_CELLSING_NB 16384 -#define ASN1_NGAP_NR_MAXNOOF_CELLSINNGENB 256 -#define ASN1_NGAP_NR_MAXNOOF_CELLSIN_UE_HISTORY_INFO 16 -#define ASN1_NGAP_NR_MAXNOOF_CELLS_UE_MOVING_TRAJECTORY 16 -#define ASN1_NGAP_NR_MAXNOOF_DRBS 32 -#define ASN1_NGAP_NR_MAXNOOF_EMERGENCY_AREA_ID 65535 -#define ASN1_NGAP_NR_MAXNOOF_EA_IFOR_RESTART 256 -#define ASN1_NGAP_NR_MAXNOOF_EPLMNS 15 -#define ASN1_NGAP_NR_MAXNOOF_EPLMNS_PLUS_ONE 16 -#define ASN1_NGAP_NR_MAXNOOF_ERABS 256 -#define ASN1_NGAP_NR_MAXNOOF_ERRORS 256 -#define ASN1_NGAP_NR_MAXNOOF_FORB_TACS 4096 -#define ASN1_NGAP_NR_MAXNOOF_MULTI_CONNECT 4 -#define ASN1_NGAP_NR_MAXNOOF_MULTI_CONNECT_MINUS_ONE 3 -#define ASN1_NGAP_NR_MAXNOOF_NG_CONNS_TO_RESET 65536 -#define ASN1_NGAP_NR_MAXNOOF_PDU_SESSIONS 256 -#define ASN1_NGAP_NR_MAXNOOF_PLMNS 12 -#define ASN1_NGAP_NR_MAXNOOF_QOS_FLOWS 64 -#define ASN1_NGAP_NR_MAXNOOF_RAN_NODEIN_AO_I 64 -#define ASN1_NGAP_NR_MAXNOOF_RECOMMENDED_CELLS 16 -#define ASN1_NGAP_NR_MAXNOOF_RECOMMENDED_RAN_NODES 16 -#define ASN1_NGAP_NR_MAXNOOF_AO_I 64 -#define ASN1_NGAP_NR_MAXNOOF_SERVED_GUAMIS 256 -#define ASN1_NGAP_NR_MAXNOOF_SLICE_ITEMS 1024 -#define ASN1_NGAP_NR_MAXNOOF_TACS 256 -#define ASN1_NGAP_NR_MAXNOOF_TA_IFOR_INACTIVE 16 -#define ASN1_NGAP_NR_MAXNOOF_TA_IFOR_PAGING 16 -#define ASN1_NGAP_NR_MAXNOOF_TA_IFOR_RESTART 2048 -#define ASN1_NGAP_NR_MAXNOOF_TA_IFOR_WARNING 65535 -#define ASN1_NGAP_NR_MAXNOOF_TA_IIN_AO_I 16 -#define ASN1_NGAP_NR_MAXNOOF_TIME_PERIODS 2 -#define ASN1_NGAP_NR_MAXNOOF_TNLASSOCS 32 -#define ASN1_NGAP_NR_MAXNOOF_XN_EXT_TLAS 2 -#define ASN1_NGAP_NR_MAXNOOF_XN_GTP_TLAS 16 -#define ASN1_NGAP_NR_MAXNOOF_XN_TLAS 16 -#define ASN1_NGAP_NR_ID_ALLOWED_NSSAI 0 -#define ASN1_NGAP_NR_ID_AMF_NAME 1 -#define ASN1_NGAP_NR_ID_AMF_OVERLOAD_RESP 2 -#define ASN1_NGAP_NR_ID_AMF_SET_ID 3 -#define ASN1_NGAP_NR_ID_AMF_TNLASSOC_FAILED_TO_SETUP_LIST 4 -#define ASN1_NGAP_NR_ID_AMF_TNLASSOC_SETUP_LIST 5 -#define ASN1_NGAP_NR_ID_AMF_TNLASSOC_TO_ADD_LIST 6 -#define ASN1_NGAP_NR_ID_AMF_TNLASSOC_TO_REM_LIST 7 -#define ASN1_NGAP_NR_ID_AMF_TNLASSOC_TO_UPD_LIST 8 -#define ASN1_NGAP_NR_ID_AMF_TRAFFIC_LOAD_REDUCTION_IND 9 -#define ASN1_NGAP_NR_ID_AMF_UE_NGAP_ID 10 -#define ASN1_NGAP_NR_ID_ASSIST_DATA_FOR_PAGING 11 -#define ASN1_NGAP_NR_ID_BROADCAST_CANCELLED_AREA_LIST 12 -#define ASN1_NGAP_NR_ID_BROADCAST_COMPLETED_AREA_LIST 13 -#define ASN1_NGAP_NR_ID_CANCEL_ALL_WARNING_MSGS 14 -#define ASN1_NGAP_NR_ID_CAUSE 15 -#define ASN1_NGAP_NR_ID_CELL_ID_LIST_FOR_RESTART 16 -#define ASN1_NGAP_NR_ID_CONCURRENT_WARNING_MSG_IND 17 -#define ASN1_NGAP_NR_ID_CORE_NETWORK_ASSIST_INFO 18 -#define ASN1_NGAP_NR_ID_CRIT_DIAGNOSTICS 19 -#define ASN1_NGAP_NR_ID_DATA_CODING_SCHEME 20 -#define ASN1_NGAP_NR_ID_DEFAULT_PAGING_DRX 21 -#define ASN1_NGAP_NR_ID_DIRECT_FORWARDING_PATH_AVAILABILITY 22 -#define ASN1_NGAP_NR_ID_EMERGENCY_AREA_ID_LIST_FOR_RESTART 23 -#define ASN1_NGAP_NR_ID_EMERGENCY_FALLBACK_IND 24 -#define ASN1_NGAP_NR_ID_EUTRA_CGI 25 -#define ASN1_NGAP_NR_ID_FIVE_G_S_TMSI 26 -#define ASN1_NGAP_NR_ID_GLOBAL_RAN_NODE_ID 27 -#define ASN1_NGAP_NR_ID_GUAMI 28 -#define ASN1_NGAP_NR_ID_HANDOV_TYPE 29 -#define ASN1_NGAP_NR_ID_IMS_VOICE_SUPPORT_IND 30 -#define ASN1_NGAP_NR_ID_IDX_TO_RFSP 31 -#define ASN1_NGAP_NR_ID_INFO_ON_RECOMMENDED_CELLS_AND_RAN_NODES_FOR_PAGING 32 -#define ASN1_NGAP_NR_ID_LOCATION_REPORT_REQUEST_TYPE 33 -#define ASN1_NGAP_NR_ID_MASKED_IMEISV 34 -#define ASN1_NGAP_NR_ID_MSG_ID 35 -#define ASN1_NGAP_NR_ID_MOB_RESTRICT_LIST 36 -#define ASN1_NGAP_NR_ID_NASC 37 -#define ASN1_NGAP_NR_ID_NAS_PDU 38 -#define ASN1_NGAP_NR_ID_NAS_SECURITY_PARAMS_FROM_NGRAN 39 -#define ASN1_NGAP_NR_ID_NEW_AMF_UE_NGAP_ID 40 -#define ASN1_NGAP_NR_ID_NEW_SECURITY_CONTEXT_IND 41 -#define ASN1_NGAP_NR_ID_NGAP_MSG 42 -#define ASN1_NGAP_NR_ID_NGRAN_CGI 43 -#define ASN1_NGAP_NR_ID_NGRAN_TRACE_ID 44 -#define ASN1_NGAP_NR_ID_NR_CGI 45 -#define ASN1_NGAP_NR_ID_NRP_PA_PDU 46 -#define ASN1_NGAP_NR_ID_NOF_BROADCASTS_REQUESTED 47 -#define ASN1_NGAP_NR_ID_OLD_AMF 48 -#define ASN1_NGAP_NR_ID_OVERLOAD_START_NSSAI_LIST 49 -#define ASN1_NGAP_NR_ID_PAGING_DRX 50 -#define ASN1_NGAP_NR_ID_PAGING_ORIGIN 51 -#define ASN1_NGAP_NR_ID_PAGING_PRIO 52 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_ADMITTED_LIST 53 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_FAILED_TO_MODIFY_LIST_MOD_RES 54 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_CXT_RES 55 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_HO_ACK 56 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_PS_REQ 57 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_SU_RES 58 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_HO_LIST 59 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_LIST_CXT_REL_CPL 60 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_LIST_HO_RQD 61 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_MODIFY_LIST_MOD_CFM 62 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_MODIFY_LIST_MOD_IND 63 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_MODIFY_LIST_MOD_REQ 64 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_MODIFY_LIST_MOD_RES 65 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_NOTIFY_LIST 66 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_RELEASED_LIST_NOT 67 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_RELEASED_LIST_PS_ACK 68 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_RELEASED_LIST_PS_FAIL 69 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_RELEASED_LIST_REL_RES 70 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_SETUP_LIST_CXT_REQ 71 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_SETUP_LIST_CXT_RES 72 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_SETUP_LIST_HO_REQ 73 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_SETUP_LIST_SU_REQ 74 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_SETUP_LIST_SU_RES 75 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_TO_BE_SWITCHED_DL_LIST 76 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_SWITCHED_LIST 77 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_TO_RELEASE_LIST_HO_CMD 78 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_TO_RELEASE_LIST_REL_CMD 79 -#define ASN1_NGAP_NR_ID_PLMN_SUPPORT_LIST 80 -#define ASN1_NGAP_NR_ID_PWS_FAILED_CELL_ID_LIST 81 -#define ASN1_NGAP_NR_ID_RAN_NODE_NAME 82 -#define ASN1_NGAP_NR_ID_RAN_PAGING_PRIO 83 -#define ASN1_NGAP_NR_ID_RAN_STATUS_TRANSFER_TRANSPARENT_CONTAINER 84 -#define ASN1_NGAP_NR_ID_RAN_UE_NGAP_ID 85 -#define ASN1_NGAP_NR_ID_RELATIVE_AMF_CAPACITY 86 -#define ASN1_NGAP_NR_ID_REPEAT_PERIOD 87 -#define ASN1_NGAP_NR_ID_RESET_TYPE 88 -#define ASN1_NGAP_NR_ID_ROUTING_ID 89 -#define ASN1_NGAP_NR_ID_RRCESTABLISHMENT_CAUSE 90 -#define ASN1_NGAP_NR_ID_RRC_INACTIVE_TRANSITION_REPORT_REQUEST 91 -#define ASN1_NGAP_NR_ID_RRC_STATE 92 -#define ASN1_NGAP_NR_ID_SECURITY_CONTEXT 93 -#define ASN1_NGAP_NR_ID_SECURITY_KEY 94 -#define ASN1_NGAP_NR_ID_SERIAL_NUM 95 -#define ASN1_NGAP_NR_ID_SERVED_GUAMI_LIST 96 -#define ASN1_NGAP_NR_ID_SLICE_SUPPORT_LIST 97 -#define ASN1_NGAP_NR_ID_SON_CFG_TRANSFER_DL 98 -#define ASN1_NGAP_NR_ID_SON_CFG_TRANSFER_UL 99 -#define ASN1_NGAP_NR_ID_SOURCE_AMF_UE_NGAP_ID 100 -#define ASN1_NGAP_NR_ID_SOURCE_TO_TARGET_TRANSPARENT_CONTAINER 101 -#define ASN1_NGAP_NR_ID_SUPPORTED_TA_LIST 102 -#define ASN1_NGAP_NR_ID_TAI_LIST_FOR_PAGING 103 -#define ASN1_NGAP_NR_ID_TAI_LIST_FOR_RESTART 104 -#define ASN1_NGAP_NR_ID_TARGET_ID 105 -#define ASN1_NGAP_NR_ID_TARGET_TO_SOURCE_TRANSPARENT_CONTAINER 106 -#define ASN1_NGAP_NR_ID_TIME_TO_WAIT 107 -#define ASN1_NGAP_NR_ID_TRACE_ACTIVATION 108 -#define ASN1_NGAP_NR_ID_TRACE_COLLECTION_ENTITY_IP_ADDRESS 109 -#define ASN1_NGAP_NR_ID_UE_AGGREGATE_MAXIMUM_BIT_RATE 110 -#define ASN1_NGAP_NR_ID_UE_ASSOCIATED_LC_NG_CONN_LIST 111 -#define ASN1_NGAP_NR_ID_UE_CONTEXT_REQUEST 112 -#define ASN1_NGAP_NR_ID_UE_NGAP_IDS 114 -#define ASN1_NGAP_NR_ID_UE_PAGING_ID 115 -#define ASN1_NGAP_NR_ID_UE_PRESENCE_IN_AREA_OF_INTEREST_LIST 116 -#define ASN1_NGAP_NR_ID_UE_RADIO_CAP 117 -#define ASN1_NGAP_NR_ID_UE_RADIO_CAP_FOR_PAGING 118 -#define ASN1_NGAP_NR_ID_UE_SECURITY_CAP 119 -#define ASN1_NGAP_NR_ID_UNAVAILABLE_GUAMI_LIST 120 -#define ASN1_NGAP_NR_ID_USER_LOCATION_INFO 121 -#define ASN1_NGAP_NR_ID_WARNING_AREA_LIST 122 -#define ASN1_NGAP_NR_ID_WARNING_MSG_CONTENTS 123 -#define ASN1_NGAP_NR_ID_WARNING_SECURITY_INFO 124 -#define ASN1_NGAP_NR_ID_WARNING_TYPE 125 -#define ASN1_NGAP_NR_ID_ADD_UL_NGU_UP_TNL_INFO 126 -#define ASN1_NGAP_NR_ID_DATA_FORWARDING_NOT_POSSIBLE 127 -#define ASN1_NGAP_NR_ID_DL_NGU_UP_TNL_INFO 128 -#define ASN1_NGAP_NR_ID_NETWORK_INSTANCE 129 -#define ASN1_NGAP_NR_ID_PDU_SESSION_AGGREGATE_MAXIMUM_BIT_RATE 130 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_FAILED_TO_MODIFY_LIST_MOD_CFM 131 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_CXT_FAIL 132 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_LIST_CXT_REL_REQ 133 -#define ASN1_NGAP_NR_ID_PDU_SESSION_TYPE 134 -#define ASN1_NGAP_NR_ID_QOS_FLOW_ADD_OR_MODIFY_REQUEST_LIST 135 -#define ASN1_NGAP_NR_ID_QOS_FLOW_SETUP_REQUEST_LIST 136 -#define ASN1_NGAP_NR_ID_QOS_FLOW_TO_RELEASE_LIST 137 -#define ASN1_NGAP_NR_ID_SECURITY_IND 138 -#define ASN1_NGAP_NR_ID_UL_NGU_UP_TNL_INFO 139 -#define ASN1_NGAP_NR_ID_UL_NGU_UP_TNL_MODIFY_LIST 140 -#define ASN1_NGAP_NR_ID_WARNING_AREA_COORDINATES 141 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_SECONDARY_RATUSAGE_LIST 142 -#define ASN1_NGAP_NR_ID_HO_FLAG 143 -#define ASN1_NGAP_NR_ID_SECONDARY_RATUSAGE_INFO 144 -#define ASN1_NGAP_NR_ID_PDU_SESSION_RES_RELEASE_RESP_TRANSFER 145 -#define ASN1_NGAP_NR_ID_REDIRECTION_VOICE_FALLBACK 146 -#define ASN1_NGAP_NR_ID_UE_RETENTION_INFO 147 -#define ASN1_NGAP_NR_ID_S_NSSAI 148 -#define ASN1_NGAP_NR_ID_PS_CELL_INFO 149 -#define ASN1_NGAP_NR_ID_LAST_EUTRAN_PLMN_ID 150 -#define ASN1_NGAP_NR_ID_MAXIMUM_INTEGRITY_PROTECTED_DATA_RATE_DL 151 -#define ASN1_NGAP_NR_ID_ADD_DL_FORWARDING_UPTNL_INFO 152 -#define ASN1_NGAP_NR_ID_ADD_DLUPTNL_INFO_FOR_HO_LIST 153 -#define ASN1_NGAP_NR_ID_ADD_NGU_UP_TNL_INFO 154 -#define ASN1_NGAP_NR_ID_ADD_DL_QOS_FLOW_PER_TNL_INFO 155 -#define ASN1_NGAP_NR_ID_SECURITY_RESULT 156 -#define ASN1_NGAP_NR_ID_ENDC_SON_CFG_TRANSFER_DL 157 -#define ASN1_NGAP_NR_ID_ENDC_SON_CFG_TRANSFER_UL 158 +#define ASN1_NGAP_ID_AMF_CFG_UPD 0 +#define ASN1_NGAP_ID_AMF_STATUS_IND 1 +#define ASN1_NGAP_ID_CELL_TRAFFIC_TRACE 2 +#define ASN1_NGAP_ID_DEACTIV_TRACE 3 +#define ASN1_NGAP_ID_DL_NAS_TRANSPORT 4 +#define ASN1_NGAP_ID_DL_NON_UEASSOCIATED_NRP_PA_TRANSPORT 5 +#define ASN1_NGAP_ID_DL_RAN_CFG_TRANSFER 6 +#define ASN1_NGAP_ID_DL_RAN_STATUS_TRANSFER 7 +#define ASN1_NGAP_ID_DL_UEASSOCIATED_NRP_PA_TRANSPORT 8 +#define ASN1_NGAP_ID_ERROR_IND 9 +#define ASN1_NGAP_ID_HO_CANCEL 10 +#define ASN1_NGAP_ID_HO_NOTIF 11 +#define ASN1_NGAP_ID_HO_PREP 12 +#define ASN1_NGAP_ID_HO_RES_ALLOC 13 +#define ASN1_NGAP_ID_INIT_CONTEXT_SETUP 14 +#define ASN1_NGAP_ID_INIT_UE_MSG 15 +#define ASN1_NGAP_ID_LOCATION_REPORT_CTRL 16 +#define ASN1_NGAP_ID_LOCATION_REPORT_FAIL_IND 17 +#define ASN1_NGAP_ID_LOCATION_REPORT 18 +#define ASN1_NGAP_ID_NAS_NON_DELIVERY_IND 19 +#define ASN1_NGAP_ID_NG_RESET 20 +#define ASN1_NGAP_ID_NG_SETUP 21 +#define ASN1_NGAP_ID_OVERLOAD_START 22 +#define ASN1_NGAP_ID_OVERLOAD_STOP 23 +#define ASN1_NGAP_ID_PAGING 24 +#define ASN1_NGAP_ID_PATH_SWITCH_REQUEST 25 +#define ASN1_NGAP_ID_PDU_SESSION_RES_MODIFY 26 +#define ASN1_NGAP_ID_PDU_SESSION_RES_MODIFY_IND 27 +#define ASN1_NGAP_ID_PDU_SESSION_RES_RELEASE 28 +#define ASN1_NGAP_ID_PDU_SESSION_RES_SETUP 29 +#define ASN1_NGAP_ID_PDU_SESSION_RES_NOTIFY 30 +#define ASN1_NGAP_ID_PRIVATE_MSG 31 +#define ASN1_NGAP_ID_PWS_CANCEL 32 +#define ASN1_NGAP_ID_PWS_FAIL_IND 33 +#define ASN1_NGAP_ID_PWS_RESTART_IND 34 +#define ASN1_NGAP_ID_RAN_CFG_UPD 35 +#define ASN1_NGAP_ID_REROUTE_NAS_REQUEST 36 +#define ASN1_NGAP_ID_RRC_INACTIVE_TRANSITION_REPORT 37 +#define ASN1_NGAP_ID_TRACE_FAIL_IND 38 +#define ASN1_NGAP_ID_TRACE_START 39 +#define ASN1_NGAP_ID_UE_CONTEXT_MOD 40 +#define ASN1_NGAP_ID_UE_CONTEXT_RELEASE 41 +#define ASN1_NGAP_ID_UE_CONTEXT_RELEASE_REQUEST 42 +#define ASN1_NGAP_ID_UE_RADIO_CAP_CHECK 43 +#define ASN1_NGAP_ID_UE_RADIO_CAP_INFO_IND 44 +#define ASN1_NGAP_ID_UETNLA_BINDING_RELEASE 45 +#define ASN1_NGAP_ID_UL_NAS_TRANSPORT 46 +#define ASN1_NGAP_ID_UL_NON_UEASSOCIATED_NRP_PA_TRANSPORT 47 +#define ASN1_NGAP_ID_UL_RAN_CFG_TRANSFER 48 +#define ASN1_NGAP_ID_UL_RAN_STATUS_TRANSFER 49 +#define ASN1_NGAP_ID_UL_UEASSOCIATED_NRP_PA_TRANSPORT 50 +#define ASN1_NGAP_ID_WRITE_REPLACE_WARNING 51 +#define ASN1_NGAP_ID_SECONDARY_RAT_DATA_USAGE_REPORT 52 +#define ASN1_NGAP_MAX_PRIVATE_IES 65535 +#define ASN1_NGAP_MAX_PROTOCOL_EXTS 65535 +#define ASN1_NGAP_MAX_PROTOCOL_IES 65535 +#define ASN1_NGAP_MAXNOOF_ALLOWED_AREAS 16 +#define ASN1_NGAP_MAXNOOF_ALLOWED_S_NSSAIS 8 +#define ASN1_NGAP_MAXNOOF_BPLMNS 12 +#define ASN1_NGAP_MAXNOOF_CELL_IDFOR_WARNING 65535 +#define ASN1_NGAP_MAXNOOF_CELLIN_AO_I 256 +#define ASN1_NGAP_MAXNOOF_CELLIN_EAI 65535 +#define ASN1_NGAP_MAXNOOF_CELLIN_TAI 65535 +#define ASN1_NGAP_MAXNOOF_CELLSING_NB 16384 +#define ASN1_NGAP_MAXNOOF_CELLSINNGENB 256 +#define ASN1_NGAP_MAXNOOF_CELLSIN_UE_HISTORY_INFO 16 +#define ASN1_NGAP_MAXNOOF_CELLS_UE_MOVING_TRAJECTORY 16 +#define ASN1_NGAP_MAXNOOF_DRBS 32 +#define ASN1_NGAP_MAXNOOF_EMERGENCY_AREA_ID 65535 +#define ASN1_NGAP_MAXNOOF_EA_IFOR_RESTART 256 +#define ASN1_NGAP_MAXNOOF_EPLMNS 15 +#define ASN1_NGAP_MAXNOOF_EPLMNS_PLUS_ONE 16 +#define ASN1_NGAP_MAXNOOF_ERABS 256 +#define ASN1_NGAP_MAXNOOF_ERRORS 256 +#define ASN1_NGAP_MAXNOOF_FORB_TACS 4096 +#define ASN1_NGAP_MAXNOOF_MULTI_CONNECT 4 +#define ASN1_NGAP_MAXNOOF_MULTI_CONNECT_MINUS_ONE 3 +#define ASN1_NGAP_MAXNOOF_NG_CONNS_TO_RESET 65536 +#define ASN1_NGAP_MAXNOOF_PDU_SESSIONS 256 +#define ASN1_NGAP_MAXNOOF_PLMNS 12 +#define ASN1_NGAP_MAXNOOF_QOS_FLOWS 64 +#define ASN1_NGAP_MAXNOOF_RAN_NODEIN_AO_I 64 +#define ASN1_NGAP_MAXNOOF_RECOMMENDED_CELLS 16 +#define ASN1_NGAP_MAXNOOF_RECOMMENDED_RAN_NODES 16 +#define ASN1_NGAP_MAXNOOF_AO_I 64 +#define ASN1_NGAP_MAXNOOF_SERVED_GUAMIS 256 +#define ASN1_NGAP_MAXNOOF_SLICE_ITEMS 1024 +#define ASN1_NGAP_MAXNOOF_TACS 256 +#define ASN1_NGAP_MAXNOOF_TA_IFOR_INACTIVE 16 +#define ASN1_NGAP_MAXNOOF_TA_IFOR_PAGING 16 +#define ASN1_NGAP_MAXNOOF_TA_IFOR_RESTART 2048 +#define ASN1_NGAP_MAXNOOF_TA_IFOR_WARNING 65535 +#define ASN1_NGAP_MAXNOOF_TA_IIN_AO_I 16 +#define ASN1_NGAP_MAXNOOF_TIME_PERIODS 2 +#define ASN1_NGAP_MAXNOOF_TNLASSOCS 32 +#define ASN1_NGAP_MAXNOOF_XN_EXT_TLAS 2 +#define ASN1_NGAP_MAXNOOF_XN_GTP_TLAS 16 +#define ASN1_NGAP_MAXNOOF_XN_TLAS 16 +#define ASN1_NGAP_ID_ALLOWED_NSSAI 0 +#define ASN1_NGAP_ID_AMF_NAME 1 +#define ASN1_NGAP_ID_AMF_OVERLOAD_RESP 2 +#define ASN1_NGAP_ID_AMF_SET_ID 3 +#define ASN1_NGAP_ID_AMF_TNLASSOC_FAILED_TO_SETUP_LIST 4 +#define ASN1_NGAP_ID_AMF_TNLASSOC_SETUP_LIST 5 +#define ASN1_NGAP_ID_AMF_TNLASSOC_TO_ADD_LIST 6 +#define ASN1_NGAP_ID_AMF_TNLASSOC_TO_REM_LIST 7 +#define ASN1_NGAP_ID_AMF_TNLASSOC_TO_UPD_LIST 8 +#define ASN1_NGAP_ID_AMF_TRAFFIC_LOAD_REDUCTION_IND 9 +#define ASN1_NGAP_ID_AMF_UE_NGAP_ID 10 +#define ASN1_NGAP_ID_ASSIST_DATA_FOR_PAGING 11 +#define ASN1_NGAP_ID_BROADCAST_CANCELLED_AREA_LIST 12 +#define ASN1_NGAP_ID_BROADCAST_COMPLETED_AREA_LIST 13 +#define ASN1_NGAP_ID_CANCEL_ALL_WARNING_MSGS 14 +#define ASN1_NGAP_ID_CAUSE 15 +#define ASN1_NGAP_ID_CELL_ID_LIST_FOR_RESTART 16 +#define ASN1_NGAP_ID_CONCURRENT_WARNING_MSG_IND 17 +#define ASN1_NGAP_ID_CORE_NETWORK_ASSIST_INFO 18 +#define ASN1_NGAP_ID_CRIT_DIAGNOSTICS 19 +#define ASN1_NGAP_ID_DATA_CODING_SCHEME 20 +#define ASN1_NGAP_ID_DEFAULT_PAGING_DRX 21 +#define ASN1_NGAP_ID_DIRECT_FORWARDING_PATH_AVAILABILITY 22 +#define ASN1_NGAP_ID_EMERGENCY_AREA_ID_LIST_FOR_RESTART 23 +#define ASN1_NGAP_ID_EMERGENCY_FALLBACK_IND 24 +#define ASN1_NGAP_ID_EUTRA_CGI 25 +#define ASN1_NGAP_ID_FIVE_G_S_TMSI 26 +#define ASN1_NGAP_ID_GLOBAL_RAN_NODE_ID 27 +#define ASN1_NGAP_ID_GUAMI 28 +#define ASN1_NGAP_ID_HANDOV_TYPE 29 +#define ASN1_NGAP_ID_IMS_VOICE_SUPPORT_IND 30 +#define ASN1_NGAP_ID_IDX_TO_RFSP 31 +#define ASN1_NGAP_ID_INFO_ON_RECOMMENDED_CELLS_AND_RAN_NODES_FOR_PAGING 32 +#define ASN1_NGAP_ID_LOCATION_REPORT_REQUEST_TYPE 33 +#define ASN1_NGAP_ID_MASKED_IMEISV 34 +#define ASN1_NGAP_ID_MSG_ID 35 +#define ASN1_NGAP_ID_MOB_RESTRICT_LIST 36 +#define ASN1_NGAP_ID_NASC 37 +#define ASN1_NGAP_ID_NAS_PDU 38 +#define ASN1_NGAP_ID_NAS_SECURITY_PARAMS_FROM_NGRAN 39 +#define ASN1_NGAP_ID_NEW_AMF_UE_NGAP_ID 40 +#define ASN1_NGAP_ID_NEW_SECURITY_CONTEXT_IND 41 +#define ASN1_NGAP_ID_NGAP_MSG 42 +#define ASN1_NGAP_ID_NGRAN_CGI 43 +#define ASN1_NGAP_ID_NGRAN_TRACE_ID 44 +#define ASN1_NGAP_ID_NR_CGI 45 +#define ASN1_NGAP_ID_NRP_PA_PDU 46 +#define ASN1_NGAP_ID_NOF_BROADCASTS_REQUESTED 47 +#define ASN1_NGAP_ID_OLD_AMF 48 +#define ASN1_NGAP_ID_OVERLOAD_START_NSSAI_LIST 49 +#define ASN1_NGAP_ID_PAGING_DRX 50 +#define ASN1_NGAP_ID_PAGING_ORIGIN 51 +#define ASN1_NGAP_ID_PAGING_PRIO 52 +#define ASN1_NGAP_ID_PDU_SESSION_RES_ADMITTED_LIST 53 +#define ASN1_NGAP_ID_PDU_SESSION_RES_FAILED_TO_MODIFY_LIST_MOD_RES 54 +#define ASN1_NGAP_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_CXT_RES 55 +#define ASN1_NGAP_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_HO_ACK 56 +#define ASN1_NGAP_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_PS_REQ 57 +#define ASN1_NGAP_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_SU_RES 58 +#define ASN1_NGAP_ID_PDU_SESSION_RES_HO_LIST 59 +#define ASN1_NGAP_ID_PDU_SESSION_RES_LIST_CXT_REL_CPL 60 +#define ASN1_NGAP_ID_PDU_SESSION_RES_LIST_HO_RQD 61 +#define ASN1_NGAP_ID_PDU_SESSION_RES_MODIFY_LIST_MOD_CFM 62 +#define ASN1_NGAP_ID_PDU_SESSION_RES_MODIFY_LIST_MOD_IND 63 +#define ASN1_NGAP_ID_PDU_SESSION_RES_MODIFY_LIST_MOD_REQ 64 +#define ASN1_NGAP_ID_PDU_SESSION_RES_MODIFY_LIST_MOD_RES 65 +#define ASN1_NGAP_ID_PDU_SESSION_RES_NOTIFY_LIST 66 +#define ASN1_NGAP_ID_PDU_SESSION_RES_RELEASED_LIST_NOT 67 +#define ASN1_NGAP_ID_PDU_SESSION_RES_RELEASED_LIST_PS_ACK 68 +#define ASN1_NGAP_ID_PDU_SESSION_RES_RELEASED_LIST_PS_FAIL 69 +#define ASN1_NGAP_ID_PDU_SESSION_RES_RELEASED_LIST_REL_RES 70 +#define ASN1_NGAP_ID_PDU_SESSION_RES_SETUP_LIST_CXT_REQ 71 +#define ASN1_NGAP_ID_PDU_SESSION_RES_SETUP_LIST_CXT_RES 72 +#define ASN1_NGAP_ID_PDU_SESSION_RES_SETUP_LIST_HO_REQ 73 +#define ASN1_NGAP_ID_PDU_SESSION_RES_SETUP_LIST_SU_REQ 74 +#define ASN1_NGAP_ID_PDU_SESSION_RES_SETUP_LIST_SU_RES 75 +#define ASN1_NGAP_ID_PDU_SESSION_RES_TO_BE_SWITCHED_DL_LIST 76 +#define ASN1_NGAP_ID_PDU_SESSION_RES_SWITCHED_LIST 77 +#define ASN1_NGAP_ID_PDU_SESSION_RES_TO_RELEASE_LIST_HO_CMD 78 +#define ASN1_NGAP_ID_PDU_SESSION_RES_TO_RELEASE_LIST_REL_CMD 79 +#define ASN1_NGAP_ID_PLMN_SUPPORT_LIST 80 +#define ASN1_NGAP_ID_PWS_FAILED_CELL_ID_LIST 81 +#define ASN1_NGAP_ID_RAN_NODE_NAME 82 +#define ASN1_NGAP_ID_RAN_PAGING_PRIO 83 +#define ASN1_NGAP_ID_RAN_STATUS_TRANSFER_TRANSPARENT_CONTAINER 84 +#define ASN1_NGAP_ID_RAN_UE_NGAP_ID 85 +#define ASN1_NGAP_ID_RELATIVE_AMF_CAPACITY 86 +#define ASN1_NGAP_ID_REPEAT_PERIOD 87 +#define ASN1_NGAP_ID_RESET_TYPE 88 +#define ASN1_NGAP_ID_ROUTING_ID 89 +#define ASN1_NGAP_ID_RRCESTABLISHMENT_CAUSE 90 +#define ASN1_NGAP_ID_RRC_INACTIVE_TRANSITION_REPORT_REQUEST 91 +#define ASN1_NGAP_ID_RRC_STATE 92 +#define ASN1_NGAP_ID_SECURITY_CONTEXT 93 +#define ASN1_NGAP_ID_SECURITY_KEY 94 +#define ASN1_NGAP_ID_SERIAL_NUM 95 +#define ASN1_NGAP_ID_SERVED_GUAMI_LIST 96 +#define ASN1_NGAP_ID_SLICE_SUPPORT_LIST 97 +#define ASN1_NGAP_ID_SON_CFG_TRANSFER_DL 98 +#define ASN1_NGAP_ID_SON_CFG_TRANSFER_UL 99 +#define ASN1_NGAP_ID_SOURCE_AMF_UE_NGAP_ID 100 +#define ASN1_NGAP_ID_SOURCE_TO_TARGET_TRANSPARENT_CONTAINER 101 +#define ASN1_NGAP_ID_SUPPORTED_TA_LIST 102 +#define ASN1_NGAP_ID_TAI_LIST_FOR_PAGING 103 +#define ASN1_NGAP_ID_TAI_LIST_FOR_RESTART 104 +#define ASN1_NGAP_ID_TARGET_ID 105 +#define ASN1_NGAP_ID_TARGET_TO_SOURCE_TRANSPARENT_CONTAINER 106 +#define ASN1_NGAP_ID_TIME_TO_WAIT 107 +#define ASN1_NGAP_ID_TRACE_ACTIVATION 108 +#define ASN1_NGAP_ID_TRACE_COLLECTION_ENTITY_IP_ADDRESS 109 +#define ASN1_NGAP_ID_UE_AGGREGATE_MAXIMUM_BIT_RATE 110 +#define ASN1_NGAP_ID_UE_ASSOCIATED_LC_NG_CONN_LIST 111 +#define ASN1_NGAP_ID_UE_CONTEXT_REQUEST 112 +#define ASN1_NGAP_ID_UE_NGAP_IDS 114 +#define ASN1_NGAP_ID_UE_PAGING_ID 115 +#define ASN1_NGAP_ID_UE_PRESENCE_IN_AREA_OF_INTEREST_LIST 116 +#define ASN1_NGAP_ID_UE_RADIO_CAP 117 +#define ASN1_NGAP_ID_UE_RADIO_CAP_FOR_PAGING 118 +#define ASN1_NGAP_ID_UE_SECURITY_CAP 119 +#define ASN1_NGAP_ID_UNAVAILABLE_GUAMI_LIST 120 +#define ASN1_NGAP_ID_USER_LOCATION_INFO 121 +#define ASN1_NGAP_ID_WARNING_AREA_LIST 122 +#define ASN1_NGAP_ID_WARNING_MSG_CONTENTS 123 +#define ASN1_NGAP_ID_WARNING_SECURITY_INFO 124 +#define ASN1_NGAP_ID_WARNING_TYPE 125 +#define ASN1_NGAP_ID_ADD_UL_NGU_UP_TNL_INFO 126 +#define ASN1_NGAP_ID_DATA_FORWARDING_NOT_POSSIBLE 127 +#define ASN1_NGAP_ID_DL_NGU_UP_TNL_INFO 128 +#define ASN1_NGAP_ID_NETWORK_INSTANCE 129 +#define ASN1_NGAP_ID_PDU_SESSION_AGGREGATE_MAXIMUM_BIT_RATE 130 +#define ASN1_NGAP_ID_PDU_SESSION_RES_FAILED_TO_MODIFY_LIST_MOD_CFM 131 +#define ASN1_NGAP_ID_PDU_SESSION_RES_FAILED_TO_SETUP_LIST_CXT_FAIL 132 +#define ASN1_NGAP_ID_PDU_SESSION_RES_LIST_CXT_REL_REQ 133 +#define ASN1_NGAP_ID_PDU_SESSION_TYPE 134 +#define ASN1_NGAP_ID_QOS_FLOW_ADD_OR_MODIFY_REQUEST_LIST 135 +#define ASN1_NGAP_ID_QOS_FLOW_SETUP_REQUEST_LIST 136 +#define ASN1_NGAP_ID_QOS_FLOW_TO_RELEASE_LIST 137 +#define ASN1_NGAP_ID_SECURITY_IND 138 +#define ASN1_NGAP_ID_UL_NGU_UP_TNL_INFO 139 +#define ASN1_NGAP_ID_UL_NGU_UP_TNL_MODIFY_LIST 140 +#define ASN1_NGAP_ID_WARNING_AREA_COORDINATES 141 +#define ASN1_NGAP_ID_PDU_SESSION_RES_SECONDARY_RATUSAGE_LIST 142 +#define ASN1_NGAP_ID_HO_FLAG 143 +#define ASN1_NGAP_ID_SECONDARY_RATUSAGE_INFO 144 +#define ASN1_NGAP_ID_PDU_SESSION_RES_RELEASE_RESP_TRANSFER 145 +#define ASN1_NGAP_ID_REDIRECTION_VOICE_FALLBACK 146 +#define ASN1_NGAP_ID_UE_RETENTION_INFO 147 +#define ASN1_NGAP_ID_S_NSSAI 148 +#define ASN1_NGAP_ID_PS_CELL_INFO 149 +#define ASN1_NGAP_ID_LAST_EUTRAN_PLMN_ID 150 +#define ASN1_NGAP_ID_MAXIMUM_INTEGRITY_PROTECTED_DATA_RATE_DL 151 +#define ASN1_NGAP_ID_ADD_DL_FORWARDING_UPTNL_INFO 152 +#define ASN1_NGAP_ID_ADD_DLUPTNL_INFO_FOR_HO_LIST 153 +#define ASN1_NGAP_ID_ADD_NGU_UP_TNL_INFO 154 +#define ASN1_NGAP_ID_ADD_DL_QOS_FLOW_PER_TNL_INFO 155 +#define ASN1_NGAP_ID_SECURITY_RESULT 156 +#define ASN1_NGAP_ID_ENDC_SON_CFG_TRANSFER_DL 157 +#define ASN1_NGAP_ID_ENDC_SON_CFG_TRANSFER_UL 158 /******************************************************************************* * Struct Definitions ******************************************************************************/ -// Criticality ::= ENUMERATED -struct crit_opts { - enum options { reject, ignore, notify, nulltype } value; +// INTEGER (0..4294967295) ::= INTEGER (0..4294967295) +using ran_ue_ngap_id_t = integer; - const char* to_string() const; -}; -typedef enumerated crit_e; +// INTEGER (0..1099511627775) ::= INTEGER (0..1099511627775) +using amf_ue_ngap_id_t = integer; -// Presence ::= ENUMERATED -struct presence_opts { - enum options { optional, conditional, mandatory, nulltype } value; - - const char* to_string() const; -}; -typedef enumerated presence_e; - -// ProtocolIE-Field{NGAP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{NGAP-PROTOCOL-IES}} -template -struct protocol_ie_field_s { - uint32_t id = 0; - crit_e crit; - typename ies_set_paramT_::value_c value; - - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - bool load_info_obj(const uint32_t& id_); -}; - -struct ngap_protocol_ies_empty_o { - // Value ::= OPEN TYPE - struct value_c { - struct types_opts { - enum options { nulltype } value; - - const char* to_string() const; - }; - typedef enumerated types; - - // choice methods - types type() const { return types::nulltype; } - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - }; - - // members lookup methods - static uint32_t idx_to_id(uint32_t idx); - static bool is_id_valid(const uint32_t& id); - static crit_e get_crit(const uint32_t& id); - static value_c get_value(const uint32_t& id); - static presence_e get_presence(const uint32_t& id); -}; // CPTransportLayerInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using cp_transport_layer_info_ext_ies_o = ngap_protocol_ies_empty_o; +using cp_transport_layer_info_ext_ies_o = protocol_ies_empty_o; -// ProtocolExtensionField{NGAP-PROTOCOL-EXTENSION : ExtensionSetParam} ::= SEQUENCE{{NGAP-PROTOCOL-EXTENSION}} -template -struct protocol_ext_field_s { - uint32_t id = 0; - crit_e crit; - typename ext_set_paramT_::ext_c ext_value; - - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - bool load_info_obj(const uint32_t& id_); -}; - -// ProtocolIE-SingleContainer{NGAP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{NGAP-PROTOCOL-IES}} -template -struct protocol_ie_single_container_s { - uint32_t id = 0; - crit_e crit; - typename ies_set_paramT_::value_c value; - - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - bool load_info_obj(const uint32_t& id_); -}; - -struct ngap_protocol_ext_empty_o { - // Extension ::= OPEN TYPE - struct ext_c { - struct types_opts { - enum options { nulltype } value; - - const char* to_string() const; - }; - typedef enumerated types; - - // choice methods - types type() const { return types::nulltype; } - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - }; - - // members lookup methods - static uint32_t idx_to_id(uint32_t idx); - static bool is_id_valid(const uint32_t& id); - static crit_e get_crit(const uint32_t& id); - static ext_c get_ext(const uint32_t& id); - static presence_e get_presence(const uint32_t& id); -}; // AMF-TNLAssociationSetupItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using amf_tnlassoc_setup_item_ext_ies_o = ngap_protocol_ext_empty_o; +using amf_tnlassoc_setup_item_ext_ies_o = protocol_ext_empty_o; // CPTransportLayerInformation ::= CHOICE struct cp_transport_layer_info_c { @@ -461,33 +364,6 @@ private: void destroy_(); }; -// ProtocolExtensionContainer{NGAP-PROTOCOL-EXTENSION : ExtensionSetParam} ::= SEQUENCE (SIZE (1..65535)) OF -// ProtocolExtensionField -template -using protocol_ext_container_l = dyn_seq_of, 1, 65535, true>; - -template -struct protocol_ext_container_item_s { - uint32_t id = 0; - crit_e crit; - extT_ ext; - - // sequence methods - protocol_ext_container_item_s(uint32_t id_, crit_e crit_); - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; - -struct protocol_ext_container_empty_l { - template - using ie_field_s = protocol_ext_container_item_s; - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; using amf_tnlassoc_setup_item_ext_ies_container = protocol_ext_container_empty_l; // AMF-TNLAssociationSetupItem ::= SEQUENCE @@ -508,7 +384,7 @@ struct amf_tnlassoc_setup_item_s { using amf_tnlassoc_setup_list_l = dyn_array; // AMF-TNLAssociationToAddItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using amf_tnlassoc_to_add_item_ext_ies_o = ngap_protocol_ext_empty_o; +using amf_tnlassoc_to_add_item_ext_ies_o = protocol_ext_empty_o; // TNLAssociationUsage ::= ENUMERATED struct tnlassoc_usage_opts { @@ -541,7 +417,7 @@ struct amf_tnlassoc_to_add_item_s { using amf_tnlassoc_to_add_list_l = dyn_array; // AMF-TNLAssociationToRemoveItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using amf_tnlassoc_to_rem_item_ext_ies_o = ngap_protocol_ext_empty_o; +using amf_tnlassoc_to_rem_item_ext_ies_o = protocol_ext_empty_o; using amf_tnlassoc_to_rem_item_ext_ies_container = protocol_ext_container_empty_l; @@ -563,7 +439,7 @@ struct amf_tnlassoc_to_rem_item_s { using amf_tnlassoc_to_rem_list_l = dyn_array; // AMF-TNLAssociationToUpdateItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using amf_tnlassoc_to_upd_item_ext_ies_o = ngap_protocol_ext_empty_o; +using amf_tnlassoc_to_upd_item_ext_ies_o = protocol_ext_empty_o; using amf_tnlassoc_to_upd_item_ext_ies_container = protocol_ext_container_empty_l; @@ -589,7 +465,7 @@ struct amf_tnlassoc_to_upd_item_s { using amf_tnlassoc_to_upd_list_l = dyn_array; // S-NSSAI-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using s_nssai_ext_ies_o = ngap_protocol_ext_empty_o; +using s_nssai_ext_ies_o = protocol_ext_empty_o; using s_nssai_ext_ies_container = protocol_ext_container_empty_l; @@ -610,10 +486,10 @@ struct s_nssai_s { }; // SliceSupportItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using slice_support_item_ext_ies_o = ngap_protocol_ext_empty_o; +using slice_support_item_ext_ies_o = protocol_ext_empty_o; // GUAMI-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using guami_ext_ies_o = ngap_protocol_ext_empty_o; +using guami_ext_ies_o = protocol_ext_empty_o; using slice_support_item_ext_ies_container = protocol_ext_container_empty_l; @@ -651,10 +527,10 @@ struct guami_s { }; // PLMNSupportItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using plmn_support_item_ext_ies_o = ngap_protocol_ext_empty_o; +using plmn_support_item_ext_ies_o = protocol_ext_empty_o; // ServedGUAMIItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using served_guami_item_ext_ies_o = ngap_protocol_ext_empty_o; +using served_guami_item_ext_ies_o = protocol_ext_empty_o; // SliceSupportList ::= SEQUENCE (SIZE (1..1024)) OF SliceSupportItem using slice_support_list_l = dyn_array; @@ -769,23 +645,6 @@ struct amf_cfg_upd_ies_o { static presence_e get_presence(const uint32_t& id); }; -// ProtocolIE-Container{NGAP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE (SIZE (0..65535)) OF ProtocolIE-Field -template -using protocol_ie_container_l = dyn_seq_of, 0, 65535, true>; - -template -struct protocol_ie_container_item_s { - uint32_t id = 0; - crit_e crit; - valueT_ value; - - // sequence methods - protocol_ie_container_item_s(uint32_t id_, crit_e crit_); - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; - struct amf_cfg_upd_ies_container { template using ie_field_s = protocol_ie_container_item_s; @@ -814,19 +673,10 @@ struct amf_cfg_upd_ies_container { }; // AMFConfigurationUpdate ::= SEQUENCE -struct amf_cfg_upd_s { - bool ext = false; - amf_cfg_upd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using amf_cfg_upd_s = elementary_procedure_option; // Cause-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using cause_ext_ies_o = ngap_protocol_ies_empty_o; +using cause_ext_ies_o = protocol_ies_empty_o; // CauseMisc ::= ENUMERATED struct cause_misc_opts { @@ -938,7 +788,7 @@ struct cause_transport_opts { typedef enumerated cause_transport_e; // CriticalityDiagnostics-IE-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using crit_diagnostics_ie_item_ext_ies_o = ngap_protocol_ext_empty_o; +using crit_diagnostics_ie_item_ext_ies_o = protocol_ext_empty_o; // TypeOfError ::= ENUMERATED struct type_of_error_opts { @@ -1063,10 +913,10 @@ struct crit_diagnostics_ie_item_s { }; // TNLAssociationItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using tnlassoc_item_ext_ies_o = ngap_protocol_ext_empty_o; +using tnlassoc_item_ext_ies_o = protocol_ext_empty_o; // CriticalityDiagnostics-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using crit_diagnostics_ext_ies_o = ngap_protocol_ext_empty_o; +using crit_diagnostics_ext_ies_o = protocol_ext_empty_o; // CriticalityDiagnostics-IE-List ::= SEQUENCE (SIZE (1..256)) OF CriticalityDiagnostics-IE-Item using crit_diagnostics_ie_list_l = dyn_array; @@ -1100,13 +950,12 @@ using crit_diagnostics_ext_ies_container = protocol_ext_container_empty_l; // CriticalityDiagnostics ::= SEQUENCE struct crit_diagnostics_s { - bool ext = false; - bool proc_code_present = false; - bool trigger_msg_present = false; - bool proc_crit_present = false; - bool ies_crit_diagnostics_present = false; - bool ie_exts_present = false; - uint16_t proc_code = 0; + bool ext = false; + bool proc_code_present = false; + bool trigger_msg_present = false; + bool proc_crit_present = false; + bool ie_exts_present = false; + uint16_t proc_code = 0; trigger_msg_e trigger_msg; crit_e proc_crit; crit_diagnostics_ie_list_l ies_crit_diagnostics; @@ -1186,16 +1035,7 @@ struct amf_cfg_upd_ack_ies_container { }; // AMFConfigurationUpdateAcknowledge ::= SEQUENCE -struct amf_cfg_upd_ack_s { - bool ext = false; - amf_cfg_upd_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using amf_cfg_upd_ack_s = elementary_procedure_option; // TimeToWait ::= ENUMERATED struct time_to_wait_opts { @@ -1270,25 +1110,16 @@ struct amf_cfg_upd_fail_ies_container { }; // AMFConfigurationUpdateFailure ::= SEQUENCE -struct amf_cfg_upd_fail_s { - bool ext = false; - amf_cfg_upd_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using amf_cfg_upd_fail_s = elementary_procedure_option; // GNB-ID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using gnb_id_ext_ies_o = ngap_protocol_ies_empty_o; +using gnb_id_ext_ies_o = protocol_ies_empty_o; // N3IWF-ID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using n3_iwf_id_ext_ies_o = ngap_protocol_ies_empty_o; +using n3_iwf_id_ext_ies_o = protocol_ies_empty_o; // NgENB-ID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using ng_enb_id_ext_ies_o = ngap_protocol_ies_empty_o; +using ng_enb_id_ext_ies_o = protocol_ies_empty_o; // GNB-ID ::= CHOICE struct gnb_id_c { @@ -1341,13 +1172,13 @@ private: }; // GlobalGNB-ID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using global_gnb_id_ext_ies_o = ngap_protocol_ext_empty_o; +using global_gnb_id_ext_ies_o = protocol_ext_empty_o; // GlobalN3IWF-ID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using global_n3_iwf_id_ext_ies_o = ngap_protocol_ext_empty_o; +using global_n3_iwf_id_ext_ies_o = protocol_ext_empty_o; // GlobalNgENB-ID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using global_ng_enb_id_ext_ies_o = ngap_protocol_ext_empty_o; +using global_ng_enb_id_ext_ies_o = protocol_ext_empty_o; // N3IWF-ID ::= CHOICE struct n3_iwf_id_c { @@ -1525,13 +1356,13 @@ struct global_ng_enb_id_s { }; // GlobalRANNodeID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using global_ran_node_id_ext_ies_o = ngap_protocol_ies_empty_o; +using global_ran_node_id_ext_ies_o = protocol_ies_empty_o; // TAI-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using tai_ext_ies_o = ngap_protocol_ext_empty_o; +using tai_ext_ies_o = protocol_ext_empty_o; // AMFPagingTarget-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using amf_paging_target_ext_ies_o = ngap_protocol_ies_empty_o; +using amf_paging_target_ext_ies_o = protocol_ies_empty_o; // GlobalRANNodeID ::= CHOICE struct global_ran_node_id_c { @@ -1698,7 +1529,7 @@ struct timer_approach_for_guami_removal_opts { typedef enumerated timer_approach_for_guami_removal_e; // UnavailableGUAMIItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using unavailable_guami_item_ext_ies_o = ngap_protocol_ext_empty_o; +using unavailable_guami_item_ext_ies_o = protocol_ext_empty_o; using unavailable_guami_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1756,16 +1587,7 @@ struct amf_status_ind_ies_o { }; // AMFStatusIndication ::= SEQUENCE -struct amf_status_ind_s { - bool ext = false; - protocol_ie_container_l protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using amf_status_ind_s = elementary_procedure_option >; // DataForwardingAccepted ::= ENUMERATED struct data_forwarding_accepted_opts { @@ -1776,10 +1598,10 @@ struct data_forwarding_accepted_opts { typedef enumerated data_forwarding_accepted_e; // GTPTunnel-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using gtp_tunnel_ext_ies_o = ngap_protocol_ext_empty_o; +using gtp_tunnel_ext_ies_o = protocol_ext_empty_o; // QosFlowItemWithDataForwarding-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_item_with_data_forwarding_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_item_with_data_forwarding_ext_ies_o = protocol_ext_empty_o; using gtp_tunnel_ext_ies_container = protocol_ext_container_empty_l; @@ -1817,10 +1639,10 @@ struct qos_flow_item_with_data_forwarding_s { }; // UPTransportLayerInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using up_transport_layer_info_ext_ies_o = ngap_protocol_ies_empty_o; +using up_transport_layer_info_ext_ies_o = protocol_ies_empty_o; // AdditionalDLUPTNLInformationForHOItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using add_dluptnl_info_for_ho_item_ext_ies_o = ngap_protocol_ext_empty_o; +using add_dluptnl_info_for_ho_item_ext_ies_o = protocol_ext_empty_o; // QosFlowListWithDataForwarding ::= SEQUENCE (SIZE (1..64)) OF QosFlowItemWithDataForwarding using qos_flow_list_with_data_forwarding_l = dyn_array; @@ -1898,7 +1720,7 @@ struct add_dluptnl_info_for_ho_item_s { using add_dluptnl_info_for_ho_list_l = dyn_array; // AllocationAndRetentionPriority-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using alloc_and_retention_prio_ext_ies_o = ngap_protocol_ext_empty_o; +using alloc_and_retention_prio_ext_ies_o = protocol_ext_empty_o; // Pre-emptionCapability ::= ENUMERATED struct pre_emption_cap_opts { @@ -1935,7 +1757,7 @@ struct alloc_and_retention_prio_s { }; // AllowedNSSAI-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using allowed_nssai_item_ext_ies_o = ngap_protocol_ext_empty_o; +using allowed_nssai_item_ext_ies_o = protocol_ext_empty_o; using allowed_nssai_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1960,10 +1782,10 @@ using allowed_nssai_l = dyn_array; using allowed_tacs_l = bounded_array, 16>; // EUTRA-CGI-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using eutra_cgi_ext_ies_o = ngap_protocol_ext_empty_o; +using eutra_cgi_ext_ies_o = protocol_ext_empty_o; // NR-CGI-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using nr_cgi_ext_ies_o = ngap_protocol_ext_empty_o; +using nr_cgi_ext_ies_o = protocol_ext_empty_o; using eutra_cgi_ext_ies_container = protocol_ext_container_empty_l; @@ -1983,7 +1805,7 @@ struct eutra_cgi_s { }; // NGRAN-CGI-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using ngran_cgi_ext_ies_o = ngap_protocol_ies_empty_o; +using ngran_cgi_ext_ies_o = protocol_ies_empty_o; using nr_cgi_ext_ies_container = protocol_ext_container_empty_l; @@ -2003,13 +1825,13 @@ struct nr_cgi_s { }; // AreaOfInterestCellItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using area_of_interest_cell_item_ext_ies_o = ngap_protocol_ext_empty_o; +using area_of_interest_cell_item_ext_ies_o = protocol_ext_empty_o; // AreaOfInterestRANNodeItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using area_of_interest_ran_node_item_ext_ies_o = ngap_protocol_ext_empty_o; +using area_of_interest_ran_node_item_ext_ies_o = protocol_ext_empty_o; // AreaOfInterestTAIItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using area_of_interest_tai_item_ext_ies_o = ngap_protocol_ext_empty_o; +using area_of_interest_tai_item_ext_ies_o = protocol_ext_empty_o; // NGRAN-CGI ::= CHOICE struct ngran_cgi_c { @@ -2121,7 +1943,7 @@ struct area_of_interest_tai_item_s { }; // AreaOfInterest-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using area_of_interest_ext_ies_o = ngap_protocol_ext_empty_o; +using area_of_interest_ext_ies_o = protocol_ext_empty_o; // AreaOfInterestCellList ::= SEQUENCE (SIZE (1..256)) OF AreaOfInterestCellItem using area_of_interest_cell_list_l = dyn_array; @@ -2136,11 +1958,8 @@ using area_of_interest_ext_ies_container = protocol_ext_container_empty_l; // AreaOfInterest ::= SEQUENCE struct area_of_interest_s { - bool ext = false; - bool area_of_interest_tai_list_present = false; - bool area_of_interest_cell_list_present = false; - bool area_of_interest_ran_node_list_present = false; - bool ie_exts_present = false; + bool ext = false; + bool ie_exts_present = false; area_of_interest_tai_list_l area_of_interest_tai_list; area_of_interest_cell_list_l area_of_interest_cell_list; area_of_interest_ran_node_list_l area_of_interest_ran_node_list; @@ -2154,7 +1973,7 @@ struct area_of_interest_s { }; // AreaOfInterestItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using area_of_interest_item_ext_ies_o = ngap_protocol_ext_empty_o; +using area_of_interest_item_ext_ies_o = protocol_ext_empty_o; using area_of_interest_item_ext_ies_container = protocol_ext_container_empty_l; @@ -2177,7 +1996,7 @@ struct area_of_interest_item_s { using area_of_interest_list_l = dyn_array; // RecommendedCellItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using recommended_cell_item_ext_ies_o = ngap_protocol_ext_empty_o; +using recommended_cell_item_ext_ies_o = protocol_ext_empty_o; using recommended_cell_item_ext_ies_container = protocol_ext_container_empty_l; @@ -2201,10 +2020,10 @@ struct recommended_cell_item_s { using recommended_cell_list_l = dyn_array; // RecommendedCellsForPaging-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using recommended_cells_for_paging_ext_ies_o = ngap_protocol_ext_empty_o; +using recommended_cells_for_paging_ext_ies_o = protocol_ext_empty_o; // AssistanceDataForRecommendedCells-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using assist_data_for_recommended_cells_ext_ies_o = ngap_protocol_ext_empty_o; +using assist_data_for_recommended_cells_ext_ies_o = protocol_ext_empty_o; // NextPagingAreaScope ::= ENUMERATED struct next_paging_area_scope_opts { @@ -2215,7 +2034,7 @@ struct next_paging_area_scope_opts { typedef enumerated next_paging_area_scope_e; // PagingAttemptInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using paging_attempt_info_ext_ies_o = ngap_protocol_ext_empty_o; +using paging_attempt_info_ext_ies_o = protocol_ext_empty_o; using recommended_cells_for_paging_ext_ies_container = protocol_ext_container_empty_l; @@ -2234,7 +2053,7 @@ struct recommended_cells_for_paging_s { }; // AssistanceDataForPaging-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using assist_data_for_paging_ext_ies_o = ngap_protocol_ext_empty_o; +using assist_data_for_paging_ext_ies_o = protocol_ext_empty_o; using assist_data_for_recommended_cells_ext_ies_container = protocol_ext_container_empty_l; @@ -2291,7 +2110,7 @@ struct assist_data_for_paging_s { }; // AssociatedQosFlowItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using associated_qos_flow_item_ext_ies_o = ngap_protocol_ext_empty_o; +using associated_qos_flow_item_ext_ies_o = protocol_ext_empty_o; using associated_qos_flow_item_ext_ies_container = protocol_ext_container_empty_l; @@ -2323,16 +2142,16 @@ struct associated_qos_flow_item_s { using associated_qos_flow_list_l = dyn_array; // CancelledCellsInEAI-EUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cancelled_cells_in_eai_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using cancelled_cells_in_eai_eutra_item_ext_ies_o = protocol_ext_empty_o; // CancelledCellsInEAI-NR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cancelled_cells_in_eai_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using cancelled_cells_in_eai_nr_item_ext_ies_o = protocol_ext_empty_o; // CancelledCellsInTAI-EUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cancelled_cells_in_tai_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using cancelled_cells_in_tai_eutra_item_ext_ies_o = protocol_ext_empty_o; // CancelledCellsInTAI-NR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cancelled_cells_in_tai_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using cancelled_cells_in_tai_nr_item_ext_ies_o = protocol_ext_empty_o; using cancelled_cells_in_eai_eutra_item_ext_ies_container = protocol_ext_container_empty_l; @@ -2415,22 +2234,22 @@ using cancelled_cells_in_tai_eutra_l = dyn_array; // CellIDCancelledEUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cell_id_cancelled_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using cell_id_cancelled_eutra_item_ext_ies_o = protocol_ext_empty_o; // CellIDCancelledNR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cell_id_cancelled_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using cell_id_cancelled_nr_item_ext_ies_o = protocol_ext_empty_o; // EmergencyAreaIDCancelledEUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using emergency_area_id_cancelled_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using emergency_area_id_cancelled_eutra_item_ext_ies_o = protocol_ext_empty_o; // EmergencyAreaIDCancelledNR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using emergency_area_id_cancelled_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using emergency_area_id_cancelled_nr_item_ext_ies_o = protocol_ext_empty_o; // TAICancelledEUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using tai_cancelled_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using tai_cancelled_eutra_item_ext_ies_o = protocol_ext_empty_o; // TAICancelledNR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using tai_cancelled_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using tai_cancelled_nr_item_ext_ies_o = protocol_ext_empty_o; using cell_id_cancelled_eutra_item_ext_ies_container = protocol_ext_container_empty_l; @@ -2535,7 +2354,7 @@ struct tai_cancelled_nr_item_s { }; // BroadcastCancelledAreaList-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using broadcast_cancelled_area_list_ext_ies_o = ngap_protocol_ies_empty_o; +using broadcast_cancelled_area_list_ext_ies_o = protocol_ies_empty_o; // CellIDCancelledEUTRA ::= SEQUENCE (SIZE (1..65535)) OF CellIDCancelledEUTRA-Item using cell_id_cancelled_eutra_l = dyn_array; @@ -2677,16 +2496,16 @@ private: }; // CompletedCellsInEAI-EUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using completed_cells_in_eai_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using completed_cells_in_eai_eutra_item_ext_ies_o = protocol_ext_empty_o; // CompletedCellsInEAI-NR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using completed_cells_in_eai_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using completed_cells_in_eai_nr_item_ext_ies_o = protocol_ext_empty_o; // CompletedCellsInTAI-EUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using completed_cells_in_tai_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using completed_cells_in_tai_eutra_item_ext_ies_o = protocol_ext_empty_o; // CompletedCellsInTAI-NR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using completed_cells_in_tai_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using completed_cells_in_tai_nr_item_ext_ies_o = protocol_ext_empty_o; using completed_cells_in_eai_eutra_item_ext_ies_container = protocol_ext_container_empty_l; @@ -2753,10 +2572,10 @@ struct completed_cells_in_tai_nr_item_s { }; // CellIDBroadcastEUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cell_id_broadcast_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using cell_id_broadcast_eutra_item_ext_ies_o = protocol_ext_empty_o; // CellIDBroadcastNR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cell_id_broadcast_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using cell_id_broadcast_nr_item_ext_ies_o = protocol_ext_empty_o; // CompletedCellsInEAI-EUTRA ::= SEQUENCE (SIZE (1..65535)) OF CompletedCellsInEAI-EUTRA-Item using completed_cells_in_eai_eutra_l = dyn_array; @@ -2771,16 +2590,16 @@ using completed_cells_in_tai_eutra_l = dyn_array; // EmergencyAreaIDBroadcastEUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using emergency_area_id_broadcast_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using emergency_area_id_broadcast_eutra_item_ext_ies_o = protocol_ext_empty_o; // EmergencyAreaIDBroadcastNR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using emergency_area_id_broadcast_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using emergency_area_id_broadcast_nr_item_ext_ies_o = protocol_ext_empty_o; // TAIBroadcastEUTRA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using tai_broadcast_eutra_item_ext_ies_o = ngap_protocol_ext_empty_o; +using tai_broadcast_eutra_item_ext_ies_o = protocol_ext_empty_o; // TAIBroadcastNR-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using tai_broadcast_nr_item_ext_ies_o = ngap_protocol_ext_empty_o; +using tai_broadcast_nr_item_ext_ies_o = protocol_ext_empty_o; using cell_id_broadcast_eutra_item_ext_ies_container = protocol_ext_container_empty_l; @@ -2883,7 +2702,7 @@ struct tai_broadcast_nr_item_s { }; // BroadcastCompletedAreaList-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using broadcast_completed_area_list_ext_ies_o = ngap_protocol_ies_empty_o; +using broadcast_completed_area_list_ext_ies_o = protocol_ies_empty_o; // CellIDBroadcastEUTRA ::= SEQUENCE (SIZE (1..65535)) OF CellIDBroadcastEUTRA-Item using cell_id_broadcast_eutra_l = dyn_array; @@ -3025,7 +2844,7 @@ private: }; // BroadcastPLMNItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using broadcast_plmn_item_ext_ies_o = ngap_protocol_ext_empty_o; +using broadcast_plmn_item_ext_ies_o = protocol_ext_empty_o; using broadcast_plmn_item_ext_ies_container = protocol_ext_container_empty_l; @@ -3048,7 +2867,7 @@ struct broadcast_plmn_item_s { using broadcast_plmn_list_l = dyn_array; // COUNTValueForPDCP-SN12-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using count_value_for_pdcp_sn12_ext_ies_o = ngap_protocol_ext_empty_o; +using count_value_for_pdcp_sn12_ext_ies_o = protocol_ext_empty_o; using count_value_for_pdcp_sn12_ext_ies_container = protocol_ext_container_empty_l; @@ -3068,7 +2887,7 @@ struct count_value_for_pdcp_sn12_s { }; // COUNTValueForPDCP-SN18-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using count_value_for_pdcp_sn18_ext_ies_o = ngap_protocol_ext_empty_o; +using count_value_for_pdcp_sn18_ext_ies_o = protocol_ext_empty_o; using count_value_for_pdcp_sn18_ext_ies_container = protocol_ext_container_empty_l; @@ -3088,7 +2907,7 @@ struct count_value_for_pdcp_sn18_s { }; // CellIDListForRestart-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using cell_id_list_for_restart_ext_ies_o = ngap_protocol_ies_empty_o; +using cell_id_list_for_restart_ext_ies_o = protocol_ies_empty_o; // EUTRA-CGIList ::= SEQUENCE (SIZE (1..256)) OF EUTRA-CGI using eutra_cgi_list_l = dyn_array; @@ -3218,11 +3037,11 @@ struct cell_traffic_trace_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > ngran_trace_id; - ie_field_s ngran_cgi; - ie_field_s > trace_collection_entity_ip_address; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > ngran_trace_id; + ie_field_s ngran_cgi; + ie_field_s > trace_collection_entity_ip_address; // sequence methods cell_traffic_trace_ies_container(); @@ -3232,16 +3051,7 @@ struct cell_traffic_trace_ies_container { }; // CellTrafficTrace ::= SEQUENCE -struct cell_traffic_trace_s { - bool ext = false; - cell_traffic_trace_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using cell_traffic_trace_s = elementary_procedure_option; // CellSize ::= ENUMERATED struct cell_size_opts { @@ -3252,7 +3062,7 @@ struct cell_size_opts { typedef enumerated cell_size_e; // CellType-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using cell_type_ext_ies_o = ngap_protocol_ext_empty_o; +using cell_type_ext_ies_o = protocol_ext_empty_o; using cell_type_ext_ies_container = protocol_ext_container_empty_l; @@ -3271,10 +3081,10 @@ struct cell_type_s { }; // ExpectedUEMovingTrajectoryItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using expected_ue_moving_trajectory_item_ext_ies_o = ngap_protocol_ext_empty_o; +using expected_ue_moving_trajectory_item_ext_ies_o = protocol_ext_empty_o; // ExpectedUEActivityBehaviour-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using expected_ue_activity_behaviour_ext_ies_o = ngap_protocol_ext_empty_o; +using expected_ue_activity_behaviour_ext_ies_o = protocol_ext_empty_o; using expected_ue_moving_trajectory_item_ext_ies_container = protocol_ext_container_empty_l; @@ -3303,7 +3113,7 @@ struct source_of_ue_activity_behaviour_info_opts { typedef enumerated source_of_ue_activity_behaviour_info_e; // TAIListForInactiveItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using tai_list_for_inactive_item_ext_ies_o = ngap_protocol_ext_empty_o; +using tai_list_for_inactive_item_ext_ies_o = protocol_ext_empty_o; // ExpectedHOInterval ::= ENUMERATED struct expected_ho_interv_opts { @@ -3337,7 +3147,7 @@ struct expected_ue_activity_behaviour_s { }; // ExpectedUEBehaviour-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using expected_ue_behaviour_ext_ies_o = ngap_protocol_ext_empty_o; +using expected_ue_behaviour_ext_ies_o = protocol_ext_empty_o; // ExpectedUEMobility ::= ENUMERATED struct expected_ue_mob_opts { @@ -3367,10 +3177,10 @@ struct tai_list_for_inactive_item_s { }; // UEIdentityIndexValue-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using ue_id_idx_value_ext_ies_o = ngap_protocol_ies_empty_o; +using ue_id_idx_value_ext_ies_o = protocol_ies_empty_o; // CoreNetworkAssistanceInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using core_network_assist_info_ext_ies_o = ngap_protocol_ext_empty_o; +using core_network_assist_info_ext_ies_o = protocol_ext_empty_o; using expected_ue_behaviour_ext_ies_container = protocol_ext_container_empty_l; @@ -3380,7 +3190,6 @@ struct expected_ue_behaviour_s { bool expected_ue_activity_behaviour_present = false; bool expected_ho_interv_present = false; bool expected_ue_mob_present = false; - bool expected_ue_moving_trajectory_present = false; bool ie_exts_present = false; expected_ue_activity_behaviour_s expected_ue_activity_behaviour; expected_ho_interv_e expected_ho_interv; @@ -3493,13 +3302,13 @@ struct core_network_assist_info_s { }; // DRBStatusDL12-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using drb_status_dl12_ext_ies_o = ngap_protocol_ext_empty_o; +using drb_status_dl12_ext_ies_o = protocol_ext_empty_o; // DRBStatusDL18-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using drb_status_dl18_ext_ies_o = ngap_protocol_ext_empty_o; +using drb_status_dl18_ext_ies_o = protocol_ext_empty_o; // DRBStatusDL-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using drb_status_dl_ext_ies_o = ngap_protocol_ies_empty_o; +using drb_status_dl_ext_ies_o = protocol_ies_empty_o; using drb_status_dl12_ext_ies_container = protocol_ext_container_empty_l; @@ -3597,13 +3406,13 @@ private: }; // DRBStatusUL12-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using drb_status_ul12_ext_ies_o = ngap_protocol_ext_empty_o; +using drb_status_ul12_ext_ies_o = protocol_ext_empty_o; // DRBStatusUL18-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using drb_status_ul18_ext_ies_o = ngap_protocol_ext_empty_o; +using drb_status_ul18_ext_ies_o = protocol_ext_empty_o; // DRBStatusUL-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using drb_status_ul_ext_ies_o = ngap_protocol_ies_empty_o; +using drb_status_ul_ext_ies_o = protocol_ies_empty_o; using drb_status_ul12_ext_ies_container = protocol_ext_container_empty_l; @@ -3705,7 +3514,7 @@ private: }; // DRBsSubjectToStatusTransferItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using drbs_subject_to_status_transfer_item_ext_ies_o = ngap_protocol_ext_empty_o; +using drbs_subject_to_status_transfer_item_ext_ies_o = protocol_ext_empty_o; using drbs_subject_to_status_transfer_item_ext_ies_container = protocol_ext_container_empty_l; @@ -3729,7 +3538,7 @@ struct drbs_subject_to_status_transfer_item_s { using drbs_subject_to_status_transfer_list_l = dyn_array; // DRBsToQosFlowsMappingItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using drbs_to_qos_flows_map_item_ext_ies_o = ngap_protocol_ext_empty_o; +using drbs_to_qos_flows_map_item_ext_ies_o = protocol_ext_empty_o; using drbs_to_qos_flows_map_item_ext_ies_container = protocol_ext_container_empty_l; @@ -3752,7 +3561,7 @@ struct drbs_to_qos_flows_map_item_s { using drbs_to_qos_flows_map_list_l = dyn_array; // DataForwardingResponseDRBItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using data_forwarding_resp_drb_item_ext_ies_o = ngap_protocol_ext_empty_o; +using data_forwarding_resp_drb_item_ext_ies_o = protocol_ext_empty_o; using data_forwarding_resp_drb_item_ext_ies_container = protocol_ext_container_empty_l; @@ -3826,9 +3635,9 @@ struct deactiv_trace_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > ngran_trace_id; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > ngran_trace_id; // sequence methods deactiv_trace_ies_container(); @@ -3838,19 +3647,10 @@ struct deactiv_trace_ies_container { }; // DeactivateTrace ::= SEQUENCE -struct deactiv_trace_s { - bool ext = false; - deactiv_trace_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using deactiv_trace_s = elementary_procedure_option; // ForbiddenAreaInformation-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using forbidden_area_info_item_ext_ies_o = ngap_protocol_ext_empty_o; +using forbidden_area_info_item_ext_ies_o = protocol_ext_empty_o; // ForbiddenTACs ::= SEQUENCE (SIZE (1..4096)) OF OCTET STRING (SIZE (3)) using forbidden_tacs_l = dyn_array >; @@ -3859,10 +3659,10 @@ using forbidden_tacs_l = dyn_array >; using not_allowed_tacs_l = bounded_array, 16>; // RATRestrictions-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using rat_restricts_item_ext_ies_o = ngap_protocol_ext_empty_o; +using rat_restricts_item_ext_ies_o = protocol_ext_empty_o; // ServiceAreaInformation-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using service_area_info_item_ext_ies_o = ngap_protocol_ext_empty_o; +using service_area_info_item_ext_ies_o = protocol_ext_empty_o; using forbidden_area_info_item_ext_ies_container = protocol_ext_container_empty_l; @@ -3902,10 +3702,8 @@ using service_area_info_item_ext_ies_container = protocol_ext_container_empty_l; // ServiceAreaInformation-Item ::= SEQUENCE struct service_area_info_item_s { - bool ext = false; - bool allowed_tacs_present = false; - bool not_allowed_tacs_present = false; - bool ie_exts_present = false; + bool ext = false; + bool ie_exts_present = false; fixed_octstring<3, true> plmn_id; allowed_tacs_l allowed_tacs; not_allowed_tacs_l not_allowed_tacs; @@ -3963,16 +3761,11 @@ using rat_restricts_l = dyn_array; using service_area_info_l = dyn_array; // UEAggregateMaximumBitRate-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ue_aggregate_maximum_bit_rate_ext_ies_o = ngap_protocol_ext_empty_o; +using ue_aggregate_maximum_bit_rate_ext_ies_o = protocol_ext_empty_o; // MobilityRestrictionList ::= SEQUENCE struct mob_restrict_list_s { - bool ext = false; - bool equivalent_plmns_present = false; - bool rat_restricts_present = false; - bool forbidden_area_info_present = false; - bool service_area_info_present = false; - bool ie_exts_present = false; + bool ext = false; fixed_octstring<3, true> serving_plmn; equivalent_plmns_l equivalent_plmns; rat_restricts_l rat_restricts; @@ -4081,21 +3874,21 @@ struct dl_nas_transport_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool old_amf_present = false; - bool ran_paging_prio_present = false; - bool mob_restrict_list_present = false; - bool idx_to_rfsp_present = false; - bool ue_aggregate_maximum_bit_rate_present = false; - bool allowed_nssai_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > old_amf; - ie_field_s > ran_paging_prio; - ie_field_s > nas_pdu; - ie_field_s mob_restrict_list; - ie_field_s > idx_to_rfsp; - ie_field_s ue_aggregate_maximum_bit_rate; - ie_field_s > allowed_nssai; + bool old_amf_present = false; + bool ran_paging_prio_present = false; + bool mob_restrict_list_present = false; + bool idx_to_rfsp_present = false; + bool ue_aggregate_maximum_bit_rate_present = false; + bool allowed_nssai_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > old_amf; + ie_field_s > ran_paging_prio; + ie_field_s > nas_pdu; + ie_field_s mob_restrict_list; + ie_field_s > idx_to_rfsp; + ie_field_s ue_aggregate_maximum_bit_rate; + ie_field_s > allowed_nssai; // sequence methods dl_nas_transport_ies_container(); @@ -4105,16 +3898,7 @@ struct dl_nas_transport_ies_container { }; // DownlinkNASTransport ::= SEQUENCE -struct dl_nas_transport_s { - bool ext = false; - dl_nas_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_nas_transport_s = elementary_procedure_option; // DownlinkNonUEAssociatedNRPPaTransportIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES struct dl_non_ueassociated_nrp_pa_transport_ies_o { @@ -4174,19 +3958,11 @@ struct dl_non_ueassociated_nrp_pa_transport_ies_container { }; // DownlinkNonUEAssociatedNRPPaTransport ::= SEQUENCE -struct dl_non_ueassociated_nrp_pa_transport_s { - bool ext = false; - dl_non_ueassociated_nrp_pa_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_non_ueassociated_nrp_pa_transport_s = + elementary_procedure_option; // XnExtTLA-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using xn_ext_tla_item_ext_ies_o = ngap_protocol_ext_empty_o; +using xn_ext_tla_item_ext_ies_o = protocol_ext_empty_o; // XnGTP-TLAs ::= SEQUENCE (SIZE (1..16)) OF BIT STRING (SIZE (1..160,...)) using xn_gtp_tlas_l = bounded_array, 16>; @@ -4197,7 +3973,6 @@ using xn_ext_tla_item_ext_ies_container = protocol_ext_container_empty_l; struct xn_ext_tla_item_s { bool ext = false; bool ipsec_tla_present = false; - bool gtp_tlas_present = false; bool ie_exts_present = false; bounded_bitstring<1, 160, true, true> ipsec_tla; xn_gtp_tlas_l gtp_tlas; @@ -4217,18 +3992,17 @@ using xn_ext_tlas_l = dyn_array; using xn_tlas_l = bounded_array, 16>; // XnTNLConfigurationInfo-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using xn_tnl_cfg_info_ext_ies_o = ngap_protocol_ext_empty_o; +using xn_tnl_cfg_info_ext_ies_o = protocol_ext_empty_o; // SONInformationReply-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using son_info_reply_ext_ies_o = ngap_protocol_ext_empty_o; +using son_info_reply_ext_ies_o = protocol_ext_empty_o; using xn_tnl_cfg_info_ext_ies_container = protocol_ext_container_empty_l; // XnTNLConfigurationInfo ::= SEQUENCE struct xn_tnl_cfg_info_s { - bool ext = false; - bool xn_extended_transport_layer_addresses_present = false; - bool ie_exts_present = false; + bool ext = false; + bool ie_exts_present = false; xn_tlas_l xn_transport_layer_addresses; xn_ext_tlas_l xn_extended_transport_layer_addresses; xn_tnl_cfg_info_ext_ies_container ie_exts; @@ -4241,7 +4015,7 @@ struct xn_tnl_cfg_info_s { }; // SONInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using son_info_ext_ies_o = ngap_protocol_ies_empty_o; +using son_info_ext_ies_o = protocol_ies_empty_o; using son_info_reply_ext_ies_container = protocol_ext_container_empty_l; @@ -4269,13 +4043,13 @@ struct son_info_request_opts { typedef enumerated son_info_request_e; // SourceRANNodeID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using source_ran_node_id_ext_ies_o = ngap_protocol_ext_empty_o; +using source_ran_node_id_ext_ies_o = protocol_ext_empty_o; // TargetRANNodeID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using target_ran_node_id_ext_ies_o = ngap_protocol_ext_empty_o; +using target_ran_node_id_ext_ies_o = protocol_ext_empty_o; // SONConfigurationTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using son_cfg_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using son_cfg_transfer_ext_ies_o = protocol_ext_empty_o; // SONInformation ::= CHOICE struct son_info_c { @@ -4452,19 +4226,10 @@ struct dl_ran_cfg_transfer_ies_container { }; // DownlinkRANConfigurationTransfer ::= SEQUENCE -struct dl_ran_cfg_transfer_s { - bool ext = false; - dl_ran_cfg_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_ran_cfg_transfer_s = elementary_procedure_option; // RANStatusTransfer-TransparentContainer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ran_status_transfer_transparent_container_ext_ies_o = ngap_protocol_ext_empty_o; +using ran_status_transfer_transparent_container_ext_ies_o = protocol_ext_empty_o; using ran_status_transfer_transparent_container_ext_ies_container = protocol_ext_container_empty_l; @@ -4531,9 +4296,9 @@ struct dl_ran_status_transfer_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s ran_status_transfer_transparent_container; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s ran_status_transfer_transparent_container; // sequence methods dl_ran_status_transfer_ies_container(); @@ -4543,16 +4308,7 @@ struct dl_ran_status_transfer_ies_container { }; // DownlinkRANStatusTransfer ::= SEQUENCE -struct dl_ran_status_transfer_s { - bool ext = false; - dl_ran_status_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_ran_status_transfer_s = elementary_procedure_option; // DownlinkUEAssociatedNRPPaTransportIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES struct dl_ueassociated_nrp_pa_transport_ies_o { @@ -4605,10 +4361,10 @@ struct dl_ueassociated_nrp_pa_transport_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > routing_id; - ie_field_s > nrp_pa_pdu; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > routing_id; + ie_field_s > nrp_pa_pdu; // sequence methods dl_ueassociated_nrp_pa_transport_ies_container(); @@ -4618,19 +4374,10 @@ struct dl_ueassociated_nrp_pa_transport_ies_container { }; // DownlinkUEAssociatedNRPPaTransport ::= SEQUENCE -struct dl_ueassociated_nrp_pa_transport_s { - bool ext = false; - dl_ueassociated_nrp_pa_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_ueassociated_nrp_pa_transport_s = elementary_procedure_option; // PacketErrorRate-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using packet_error_rate_ext_ies_o = ngap_protocol_ext_empty_o; +using packet_error_rate_ext_ies_o = protocol_ext_empty_o; // DelayCritical ::= ENUMERATED struct delay_crit_opts { @@ -4641,7 +4388,7 @@ struct delay_crit_opts { typedef enumerated delay_crit_e; // Dynamic5QIDescriptor-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using dynamic5_qi_descriptor_ext_ies_o = ngap_protocol_ext_empty_o; +using dynamic5_qi_descriptor_ext_ies_o = protocol_ext_empty_o; using packet_error_rate_ext_ies_container = protocol_ext_container_empty_l; @@ -4695,7 +4442,7 @@ struct dl_forwarding_opts { typedef enumerated dl_forwarding_e; // E-RABInformationItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using erab_info_item_ext_ies_o = ngap_protocol_ext_empty_o; +using erab_info_item_ext_ies_o = protocol_ext_empty_o; using erab_info_item_ext_ies_container = protocol_ext_container_empty_l; @@ -4719,7 +4466,7 @@ struct erab_info_item_s { using erab_info_list_l = dyn_array; // EPS-TAI-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using eps_tai_ext_ies_o = ngap_protocol_ext_empty_o; +using eps_tai_ext_ies_o = protocol_ext_empty_o; using eps_tai_ext_ies_container = protocol_ext_container_empty_l; @@ -4748,7 +4495,7 @@ using emergency_area_id_list_l = dyn_array >; using emergency_area_id_list_for_restart_l = dyn_array >; // EmergencyFallbackIndicator-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using emergency_fallback_ind_ext_ies_o = ngap_protocol_ext_empty_o; +using emergency_fallback_ind_ext_ies_o = protocol_ext_empty_o; // EmergencyFallbackRequestIndicator ::= ENUMERATED struct emergency_fallback_request_ind_opts { @@ -4837,14 +4584,14 @@ struct error_ind_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool amf_ue_ngap_id_present = false; - bool ran_ue_ngap_id_present = false; - bool cause_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool amf_ue_ngap_id_present = false; + bool ran_ue_ngap_id_present = false; + bool cause_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods error_ind_ies_container(); @@ -4854,19 +4601,10 @@ struct error_ind_ies_container { }; // ErrorIndication ::= SEQUENCE -struct error_ind_s { - bool ext = false; - error_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using error_ind_s = elementary_procedure_option; // FiveG-S-TMSI-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using five_g_s_tmsi_ext_ies_o = ngap_protocol_ext_empty_o; +using five_g_s_tmsi_ext_ies_o = protocol_ext_empty_o; using five_g_s_tmsi_ext_ies_container = protocol_ext_container_empty_l; @@ -4887,7 +4625,7 @@ struct five_g_s_tmsi_s { }; // GBR-QosInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using gbr_qos_info_ext_ies_o = ngap_protocol_ext_empty_o; +using gbr_qos_info_ext_ies_o = protocol_ext_empty_o; // NotificationControl ::= ENUMERATED struct notif_ctrl_opts { @@ -4971,9 +4709,9 @@ struct ho_cancel_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s cause; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s cause; // sequence methods ho_cancel_ies_container(); @@ -4983,16 +4721,7 @@ struct ho_cancel_ies_container { }; // HandoverCancel ::= SEQUENCE -struct ho_cancel_s { - bool ext = false; - ho_cancel_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_cancel_s = elementary_procedure_option; // HandoverCancelAcknowledgeIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES struct ho_cancel_ack_ies_o { @@ -5043,10 +4772,10 @@ struct ho_cancel_ack_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s crit_diagnostics; // sequence methods ho_cancel_ack_ies_container(); @@ -5056,22 +4785,13 @@ struct ho_cancel_ack_ies_container { }; // HandoverCancelAcknowledge ::= SEQUENCE -struct ho_cancel_ack_s { - bool ext = false; - ho_cancel_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_cancel_ack_s = elementary_procedure_option; // PDUSessionResourceHandoverItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_ho_item_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_ho_item_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceToReleaseItemHOCmd-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_to_release_item_ho_cmd_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_to_release_item_ho_cmd_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_ho_item_ext_ies_container = protocol_ext_container_empty_l; @@ -5194,13 +4914,13 @@ struct ho_cmd_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool nas_security_params_from_ngran_present = false; - bool pdu_session_res_to_release_list_ho_cmd_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s handov_type; - ie_field_s > nas_security_params_from_ngran; + bool nas_security_params_from_ngran_present = false; + bool pdu_session_res_to_release_list_ho_cmd_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s handov_type; + ie_field_s > nas_security_params_from_ngran; ie_field_s > pdu_session_res_ho_list; ie_field_s > pdu_session_res_to_release_list_ho_cmd; @@ -5215,19 +4935,10 @@ struct ho_cmd_ies_container { }; // HandoverCommand ::= SEQUENCE -struct ho_cmd_s { - bool ext = false; - ho_cmd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_cmd_s = elementary_procedure_option; // QosFlowPerTNLInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_per_tnl_info_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_per_tnl_info_ext_ies_o = protocol_ext_empty_o; using qos_flow_per_tnl_info_ext_ies_container = protocol_ext_container_empty_l; @@ -5247,7 +4958,7 @@ struct qos_flow_per_tnl_info_s { }; // QosFlowPerTNLInformationItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_per_tnl_info_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_per_tnl_info_item_ext_ies_o = protocol_ext_empty_o; using qos_flow_per_tnl_info_item_ext_ies_container = protocol_ext_container_empty_l; @@ -5266,7 +4977,7 @@ struct qos_flow_per_tnl_info_item_s { }; // QosFlowToBeForwardedItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_to_be_forwarded_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_to_be_forwarded_item_ext_ies_o = protocol_ext_empty_o; // QosFlowPerTNLInformationList ::= SEQUENCE (SIZE (1..3)) OF QosFlowPerTNLInformationItem using qos_flow_per_tnl_info_list_l = dyn_array; @@ -5324,11 +5035,8 @@ using qos_flow_to_be_forwarded_list_l = dyn_array; // member variables - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods ho_fail_ies_container(); @@ -5405,28 +5113,19 @@ struct ho_fail_ies_container { }; // HandoverFailure ::= SEQUENCE -struct ho_fail_s { - bool ext = false; - ho_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_fail_s = elementary_procedure_option; // UserLocationInformationEUTRA-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using user_location_info_eutra_ext_ies_o = ngap_protocol_ext_empty_o; +using user_location_info_eutra_ext_ies_o = protocol_ext_empty_o; // UserLocationInformationN3IWF-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using user_location_info_n3_iwf_ext_ies_o = ngap_protocol_ext_empty_o; +using user_location_info_n3_iwf_ext_ies_o = protocol_ext_empty_o; // UserLocationInformationNR-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using user_location_info_nr_ext_ies_o = ngap_protocol_ext_empty_o; +using user_location_info_nr_ext_ies_o = protocol_ext_empty_o; // UserLocationInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using user_location_info_ext_ies_o = ngap_protocol_ies_empty_o; +using user_location_info_ext_ies_o = protocol_ies_empty_o; using user_location_info_eutra_ext_ies_container = protocol_ext_container_empty_l; @@ -5616,9 +5315,9 @@ struct ho_notify_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s user_location_info; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s user_location_info; // sequence methods ho_notify_ies_container(); @@ -5628,16 +5327,7 @@ struct ho_notify_ies_container { }; // HandoverNotify ::= SEQUENCE -struct ho_notify_s { - bool ext = false; - ho_notify_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_notify_s = elementary_procedure_option; // HandoverPreparationFailureIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES struct ho_prep_fail_ies_o { @@ -5690,11 +5380,11 @@ struct ho_prep_fail_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods ho_prep_fail_ies_container(); @@ -5704,19 +5394,10 @@ struct ho_prep_fail_ies_container { }; // HandoverPreparationFailure ::= SEQUENCE -struct ho_prep_fail_s { - bool ext = false; - ho_prep_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_prep_fail_s = elementary_procedure_option; // HandoverPreparationUnsuccessfulTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ho_prep_unsuccessful_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using ho_prep_unsuccessful_transfer_ext_ies_o = protocol_ext_empty_o; using ho_prep_unsuccessful_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -5735,7 +5416,7 @@ struct ho_prep_unsuccessful_transfer_s { }; // PDUSessionResourceSetupItemHOReq-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_setup_item_ho_req_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_setup_item_ho_req_ext_ies_o = protocol_ext_empty_o; // EventType ::= ENUMERATED struct event_type_opts { @@ -5755,7 +5436,7 @@ struct event_type_opts { typedef enumerated event_type_e; // LocationReportingRequestType-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using location_report_request_type_ext_ies_o = ngap_protocol_ext_empty_o; +using location_report_request_type_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_setup_item_ho_req_ext_ies_container = protocol_ext_container_empty_l; @@ -5784,10 +5465,10 @@ struct report_area_opts { typedef enumerated report_area_e; // SecurityContext-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using security_context_ext_ies_o = ngap_protocol_ext_empty_o; +using security_context_ext_ies_o = protocol_ext_empty_o; // TraceActivation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using trace_activation_ext_ies_o = ngap_protocol_ext_empty_o; +using trace_activation_ext_ies_o = protocol_ext_empty_o; // TraceDepth ::= ENUMERATED struct trace_depth_opts { @@ -5807,14 +5488,13 @@ struct trace_depth_opts { typedef enumerated trace_depth_e; // UESecurityCapabilities-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ue_security_cap_ext_ies_o = ngap_protocol_ext_empty_o; +using ue_security_cap_ext_ies_o = protocol_ext_empty_o; using location_report_request_type_ext_ies_container = protocol_ext_container_empty_l; // LocationReportingRequestType ::= SEQUENCE struct location_report_request_type_s { bool ext = false; - bool area_of_interest_list_present = false; bool location_report_ref_id_to_be_cancelled_present = false; bool ie_exts_present = false; event_type_e event_type; @@ -6034,24 +5714,24 @@ struct ho_request_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool core_network_assist_info_present = false; - bool new_security_context_ind_present = false; - bool nasc_present = false; - bool trace_activation_present = false; - bool masked_imeisv_present = false; - bool mob_restrict_list_present = false; - bool location_report_request_type_present = false; - bool rrc_inactive_transition_report_request_present = false; - bool redirection_voice_fallback_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s handov_type; - ie_field_s cause; - ie_field_s ue_aggregate_maximum_bit_rate; - ie_field_s core_network_assist_info; - ie_field_s ue_security_cap; - ie_field_s security_context; - ie_field_s new_security_context_ind; - ie_field_s > nasc; + bool core_network_assist_info_present = false; + bool new_security_context_ind_present = false; + bool nasc_present = false; + bool trace_activation_present = false; + bool masked_imeisv_present = false; + bool mob_restrict_list_present = false; + bool location_report_request_type_present = false; + bool rrc_inactive_transition_report_request_present = false; + bool redirection_voice_fallback_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s handov_type; + ie_field_s cause; + ie_field_s ue_aggregate_maximum_bit_rate; + ie_field_s core_network_assist_info; + ie_field_s ue_security_cap; + ie_field_s security_context; + ie_field_s new_security_context_ind; + ie_field_s > nasc; ie_field_s > pdu_session_res_setup_list_ho_req; ie_field_s > allowed_nssai; ie_field_s trace_activation; @@ -6071,22 +5751,13 @@ struct ho_request_ies_container { }; // HandoverRequest ::= SEQUENCE -struct ho_request_s { - bool ext = false; - ho_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_request_s = elementary_procedure_option; // PDUSessionResourceAdmittedItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_admitted_item_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_admitted_item_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceFailedToSetupItemHOAck-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_failed_to_setup_item_ho_ack_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_failed_to_setup_item_ho_ack_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_admitted_item_ext_ies_container = protocol_ext_container_empty_l; @@ -6195,10 +5866,10 @@ struct ho_request_ack_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_failed_to_setup_list_ho_ack_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_failed_to_setup_list_ho_ack_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_admitted_list; ie_field_s > pdu_session_res_failed_to_setup_list_ho_ack; @@ -6213,19 +5884,10 @@ struct ho_request_ack_ies_container { }; // HandoverRequestAcknowledge ::= SEQUENCE -struct ho_request_ack_s { - bool ext = false; - ho_request_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_request_ack_s = elementary_procedure_option; // QosFlowWithCauseItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_with_cause_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_with_cause_item_ext_ies_o = protocol_ext_empty_o; // ConfidentialityProtectionResult ::= ENUMERATED struct confidentiality_protection_result_opts { @@ -6261,7 +5923,7 @@ struct qos_flow_with_cause_item_s { }; // SecurityResult-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using security_result_ext_ies_o = ngap_protocol_ext_empty_o; +using security_result_ext_ies_o = protocol_ext_empty_o; // HandoverRequestAcknowledgeTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION struct ho_request_ack_transfer_ext_ies_o { @@ -6317,12 +5979,9 @@ struct security_result_s { // HandoverRequestAcknowledgeTransfer ::= SEQUENCE struct ho_request_ack_transfer_s { - bool ext = false; - bool dlforwarding_up_tnl_info_present = false; - bool security_result_present = false; - bool qos_flow_failed_to_setup_list_present = false; - bool data_forwarding_resp_drb_list_present = false; - bool ie_exts_present = false; + bool ext = false; + bool dlforwarding_up_tnl_info_present = false; + bool security_result_present = false; up_transport_layer_info_c dl_ngu_up_tnl_info; up_transport_layer_info_c dlforwarding_up_tnl_info; security_result_s security_result; @@ -6339,10 +5998,10 @@ struct ho_request_ack_transfer_s { }; // PDUSessionResourceItemHORqd-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_item_ho_rqd_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_item_ho_rqd_ext_ies_o = protocol_ext_empty_o; // TargeteNB-ID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using targetenb_id_ext_ies_o = ngap_protocol_ext_empty_o; +using targetenb_id_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_item_ho_rqd_ext_ies_container = protocol_ext_container_empty_l; @@ -6362,7 +6021,7 @@ struct pdu_session_res_item_ho_rqd_s { }; // TargetID-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using target_id_ext_ies_o = ngap_protocol_ies_empty_o; +using target_id_ext_ies_o = protocol_ies_empty_o; using targetenb_id_ext_ies_container = protocol_ext_container_empty_l; @@ -6522,13 +6181,13 @@ struct ho_required_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool direct_forwarding_path_availability_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s handov_type; - ie_field_s cause; - ie_field_s target_id; - ie_field_s direct_forwarding_path_availability; + bool direct_forwarding_path_availability_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s handov_type; + ie_field_s cause; + ie_field_s target_id; + ie_field_s direct_forwarding_path_availability; ie_field_s > pdu_session_res_list_ho_rqd; ie_field_s > source_to_target_transparent_container; @@ -6540,19 +6199,10 @@ struct ho_required_ies_container { }; // HandoverRequired ::= SEQUENCE -struct ho_required_s { - bool ext = false; - ho_required_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_required_s = elementary_procedure_option; // HandoverRequiredTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ho_required_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using ho_required_transfer_ext_ies_o = protocol_ext_empty_o; using ho_required_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -6572,7 +6222,7 @@ struct ho_required_transfer_s { }; // HandoverResourceAllocationUnsuccessfulTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ho_res_alloc_unsuccessful_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using ho_res_alloc_unsuccessful_transfer_ext_ies_o = protocol_ext_empty_o; using ho_res_alloc_unsuccessful_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -6593,7 +6243,7 @@ struct ho_res_alloc_unsuccessful_transfer_s { }; // RecommendedRANNodeItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using recommended_ran_node_item_ext_ies_o = ngap_protocol_ext_empty_o; +using recommended_ran_node_item_ext_ies_o = protocol_ext_empty_o; using recommended_ran_node_item_ext_ies_container = protocol_ext_container_empty_l; @@ -6615,10 +6265,10 @@ struct recommended_ran_node_item_s { using recommended_ran_node_list_l = dyn_array; // RecommendedRANNodesForPaging-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using recommended_ran_nodes_for_paging_ext_ies_o = ngap_protocol_ext_empty_o; +using recommended_ran_nodes_for_paging_ext_ies_o = protocol_ext_empty_o; // InfoOnRecommendedCellsAndRANNodesForPaging-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using info_on_recommended_cells_and_ran_nodes_for_paging_ext_ies_o = ngap_protocol_ext_empty_o; +using info_on_recommended_cells_and_ran_nodes_for_paging_ext_ies_o = protocol_ext_empty_o; using recommended_ran_nodes_for_paging_ext_ies_container = protocol_ext_container_empty_l; @@ -6654,7 +6304,7 @@ struct info_on_recommended_cells_and_ran_nodes_for_paging_s { }; // PDUSessionResourceFailedToSetupItemCxtFail-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_failed_to_setup_item_cxt_fail_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_failed_to_setup_item_cxt_fail_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_failed_to_setup_item_cxt_fail_ext_ies_container = protocol_ext_container_empty_l; @@ -6736,10 +6386,10 @@ struct init_context_setup_fail_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_failed_to_setup_list_cxt_fail_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_failed_to_setup_list_cxt_fail_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_failed_to_setup_list_cxt_fail; ie_field_s cause; @@ -6753,26 +6403,16 @@ struct init_context_setup_fail_ies_container { }; // InitialContextSetupFailure ::= SEQUENCE -struct init_context_setup_fail_s { - bool ext = false; - init_context_setup_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using init_context_setup_fail_s = elementary_procedure_option; // PDUSessionResourceSetupItemCxtReq-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_setup_item_cxt_req_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_setup_item_cxt_req_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_setup_item_cxt_req_ext_ies_container = protocol_ext_container_empty_l; // PDUSessionResourceSetupItemCxtReq ::= SEQUENCE struct pdu_session_res_setup_item_cxt_req_s { bool ext = false; - bool nas_pdu_present = false; bool ie_exts_present = false; uint16_t pdu_session_id = 0; unbounded_octstring nas_pdu; @@ -6788,7 +6428,7 @@ struct pdu_session_res_setup_item_cxt_req_s { }; // UERadioCapabilityForPaging-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ue_radio_cap_for_paging_ext_ies_o = ngap_protocol_ext_empty_o; +using ue_radio_cap_for_paging_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceSetupListCxtReq ::= SEQUENCE (SIZE (1..256)) OF PDUSessionResourceSetupItemCxtReq using pdu_session_res_setup_list_cxt_req_l = dyn_array; @@ -6797,10 +6437,8 @@ using ue_radio_cap_for_paging_ext_ies_container = protocol_ext_container_empty_l // UERadioCapabilityForPaging ::= SEQUENCE struct ue_radio_cap_for_paging_s { - bool ext = false; - bool ueradio_cap_for_paging_of_nr_present = false; - bool ueradio_cap_for_paging_of_eutra_present = false; - bool ie_exts_present = false; + bool ext = false; + bool ie_exts_present = false; unbounded_octstring ueradio_cap_for_paging_of_nr; unbounded_octstring ueradio_cap_for_paging_of_eutra; ue_radio_cap_for_paging_ext_ies_container ie_exts; @@ -6930,26 +6568,26 @@ struct init_context_setup_request_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool old_amf_present = false; - bool ue_aggregate_maximum_bit_rate_present = false; - bool core_network_assist_info_present = false; - bool pdu_session_res_setup_list_cxt_req_present = false; - bool trace_activation_present = false; - bool mob_restrict_list_present = false; - bool ue_radio_cap_present = false; - bool idx_to_rfsp_present = false; - bool masked_imeisv_present = false; - bool nas_pdu_present = false; - bool emergency_fallback_ind_present = false; - bool rrc_inactive_transition_report_request_present = false; - bool ue_radio_cap_for_paging_present = false; - bool redirection_voice_fallback_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > old_amf; - ie_field_s ue_aggregate_maximum_bit_rate; - ie_field_s core_network_assist_info; - ie_field_s guami; + bool old_amf_present = false; + bool ue_aggregate_maximum_bit_rate_present = false; + bool core_network_assist_info_present = false; + bool pdu_session_res_setup_list_cxt_req_present = false; + bool trace_activation_present = false; + bool mob_restrict_list_present = false; + bool ue_radio_cap_present = false; + bool idx_to_rfsp_present = false; + bool masked_imeisv_present = false; + bool nas_pdu_present = false; + bool emergency_fallback_ind_present = false; + bool rrc_inactive_transition_report_request_present = false; + bool ue_radio_cap_for_paging_present = false; + bool redirection_voice_fallback_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > old_amf; + ie_field_s ue_aggregate_maximum_bit_rate; + ie_field_s core_network_assist_info; + ie_field_s guami; ie_field_s > pdu_session_res_setup_list_cxt_req; ie_field_s > allowed_nssai; ie_field_s ue_security_cap; @@ -6973,22 +6611,13 @@ struct init_context_setup_request_ies_container { }; // InitialContextSetupRequest ::= SEQUENCE -struct init_context_setup_request_s { - bool ext = false; - init_context_setup_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using init_context_setup_request_s = elementary_procedure_option; // PDUSessionResourceFailedToSetupItemCxtRes-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_failed_to_setup_item_cxt_res_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_failed_to_setup_item_cxt_res_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceSetupItemCxtRes-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_setup_item_cxt_res_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_setup_item_cxt_res_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_failed_to_setup_item_cxt_res_ext_ies_container = protocol_ext_container_empty_l; @@ -7093,11 +6722,11 @@ struct init_context_setup_resp_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_setup_list_cxt_res_present = false; - bool pdu_session_res_failed_to_setup_list_cxt_res_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_setup_list_cxt_res_present = false; + bool pdu_session_res_failed_to_setup_list_cxt_res_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_setup_list_cxt_res; ie_field_s > pdu_session_res_failed_to_setup_list_cxt_res; @@ -7111,16 +6740,7 @@ struct init_context_setup_resp_ies_container { }; // InitialContextSetupResponse ::= SEQUENCE -struct init_context_setup_resp_s { - bool ext = false; - init_context_setup_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using init_context_setup_resp_s = elementary_procedure_option; // RRCEstablishmentCause ::= ENUMERATED struct rrcestablishment_cause_opts { @@ -7228,18 +6848,18 @@ struct init_ue_msg_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool five_g_s_tmsi_present = false; - bool amf_set_id_present = false; - bool ue_context_request_present = false; - bool allowed_nssai_present = false; - ie_field_s > ran_ue_ngap_id; - ie_field_s > nas_pdu; - ie_field_s user_location_info; - ie_field_s rrcestablishment_cause; - ie_field_s five_g_s_tmsi; - ie_field_s > amf_set_id; - ie_field_s ue_context_request; - ie_field_s > allowed_nssai; + bool five_g_s_tmsi_present = false; + bool amf_set_id_present = false; + bool ue_context_request_present = false; + bool allowed_nssai_present = false; + ie_field_s ran_ue_ngap_id; + ie_field_s > nas_pdu; + ie_field_s user_location_info; + ie_field_s rrcestablishment_cause; + ie_field_s five_g_s_tmsi; + ie_field_s > amf_set_id; + ie_field_s ue_context_request; + ie_field_s > allowed_nssai; // sequence methods init_ue_msg_ies_container(); @@ -7249,19 +6869,10 @@ struct init_ue_msg_ies_container { }; // InitialUEMessage ::= SEQUENCE -struct init_ue_msg_s { - bool ext = false; - init_ue_msg_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using init_ue_msg_s = elementary_procedure_option; // SliceOverloadItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using slice_overload_item_ext_ies_o = ngap_protocol_ext_empty_o; +using slice_overload_item_ext_ies_o = protocol_ext_empty_o; // OverloadAction ::= ENUMERATED struct overload_action_opts { @@ -7279,7 +6890,7 @@ struct overload_action_opts { typedef enumerated overload_action_e; // OverloadResponse-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using overload_resp_ext_ies_o = ngap_protocol_ies_empty_o; +using overload_resp_ext_ies_o = protocol_ies_empty_o; using slice_overload_item_ext_ies_container = protocol_ext_container_empty_l; @@ -7298,7 +6909,7 @@ struct slice_overload_item_s { }; // UE-associatedLogicalNG-connectionItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ue_associated_lc_ng_conn_item_ext_ies_o = ngap_protocol_ext_empty_o; +using ue_associated_lc_ng_conn_item_ext_ies_o = protocol_ext_empty_o; // OverloadResponse ::= CHOICE struct overload_resp_c { @@ -7351,19 +6962,19 @@ private: }; // OverloadStartNSSAIItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using overload_start_nssai_item_ext_ies_o = ngap_protocol_ext_empty_o; +using overload_start_nssai_item_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceFailedToModifyItemModCfm-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_failed_to_modify_item_mod_cfm_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_failed_to_modify_item_mod_cfm_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceFailedToModifyItemModRes-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_failed_to_modify_item_mod_res_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_failed_to_modify_item_mod_res_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceFailedToSetupItemPSReq-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_failed_to_setup_item_ps_req_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_failed_to_setup_item_ps_req_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceFailedToSetupItemSURes-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_failed_to_setup_item_su_res_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_failed_to_setup_item_su_res_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceItemCxtRelCpl-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION struct pdu_session_res_item_cxt_rel_cpl_ext_ies_o { @@ -7398,13 +7009,13 @@ struct pdu_session_res_item_cxt_rel_cpl_ext_ies_o { }; // PDUSessionResourceItemCxtRelReq-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_item_cxt_rel_req_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_item_cxt_rel_req_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceModifyItemModCfm-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_modify_item_mod_cfm_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_modify_item_mod_cfm_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceModifyItemModInd-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_modify_item_mod_ind_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_modify_item_mod_ind_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceModifyItemModReq-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION struct pdu_session_res_modify_item_mod_req_ext_ies_o { @@ -7439,40 +7050,40 @@ struct pdu_session_res_modify_item_mod_req_ext_ies_o { }; // PDUSessionResourceModifyItemModRes-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_modify_item_mod_res_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_modify_item_mod_res_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceNotifyItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_notify_item_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_notify_item_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceReleasedItemNot-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_released_item_not_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_released_item_not_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceReleasedItemPSAck-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_released_item_ps_ack_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_released_item_ps_ack_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceReleasedItemPSFail-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_released_item_ps_fail_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_released_item_ps_fail_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceReleasedItemRelRes-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_released_item_rel_res_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_released_item_rel_res_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceSecondaryRATUsageItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_secondary_ratusage_item_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_secondary_ratusage_item_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceSetupItemSUReq-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_setup_item_su_req_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_setup_item_su_req_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceSetupItemSURes-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_setup_item_su_res_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_setup_item_su_res_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceSwitchedItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_switched_item_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_switched_item_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceToBeSwitchedDLItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_to_be_switched_dl_item_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_to_be_switched_dl_item_ext_ies_o = protocol_ext_empty_o; // PDUSessionResourceToReleaseItemRelCmd-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_to_release_item_rel_cmd_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_to_release_item_rel_cmd_ext_ies_o = protocol_ext_empty_o; // PrivateIE-ID ::= CHOICE struct private_ie_id_c { @@ -7513,13 +7124,13 @@ private: using slice_overload_list_l = dyn_array; // SupportedTAItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using supported_ta_item_ext_ies_o = ngap_protocol_ext_empty_o; +using supported_ta_item_ext_ies_o = protocol_ext_empty_o; // TAIListForPagingItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using tai_list_for_paging_item_ext_ies_o = ngap_protocol_ext_empty_o; +using tai_list_for_paging_item_ext_ies_o = protocol_ext_empty_o; // UE-NGAP-ID-pair-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ue_ngap_id_pair_ext_ies_o = ngap_protocol_ext_empty_o; +using ue_ngap_id_pair_ext_ies_o = protocol_ext_empty_o; using ue_associated_lc_ng_conn_item_ext_ies_container = protocol_ext_container_empty_l; @@ -7549,7 +7160,7 @@ struct ue_presence_opts { typedef enumerated ue_presence_e; // UEPresenceInAreaOfInterestItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ue_presence_in_area_of_interest_item_ext_ies_o = ngap_protocol_ext_empty_o; +using ue_presence_in_area_of_interest_item_ext_ies_o = protocol_ext_empty_o; // NR-CGIListForWarning ::= SEQUENCE (SIZE (1..65535)) OF NR-CGI using nr_cgi_list_for_warning_l = dyn_array; @@ -7644,9 +7255,8 @@ struct pdu_session_res_failed_to_setup_item_su_res_s { // PDUSessionResourceItemCxtRelCpl ::= SEQUENCE struct pdu_session_res_item_cxt_rel_cpl_s { - bool ext = false; - bool ie_exts_present = false; - uint16_t pdu_session_id = 0; + bool ext = false; + uint16_t pdu_session_id = 0; protocol_ext_container_l ie_exts; // ... @@ -7708,10 +7318,8 @@ struct pdu_session_res_modify_item_mod_ind_s { // PDUSessionResourceModifyItemModReq ::= SEQUENCE struct pdu_session_res_modify_item_mod_req_s { - bool ext = false; - bool nas_pdu_present = false; - bool ie_exts_present = false; - uint16_t pdu_session_id = 0; + bool ext = false; + uint16_t pdu_session_id = 0; unbounded_octstring nas_pdu; unbounded_octstring pdu_session_res_modify_request_transfer; protocol_ext_container_l ie_exts; @@ -7846,10 +7454,9 @@ using pdu_session_res_setup_item_su_req_ext_ies_container = protocol_ext_contain // PDUSessionResourceSetupItemSUReq ::= SEQUENCE struct pdu_session_res_setup_item_su_req_s { - bool ext = false; - bool pdu_session_nas_pdu_present = false; - bool ie_exts_present = false; - uint16_t pdu_session_id = 0; + bool ext = false; + bool ie_exts_present = false; + uint16_t pdu_session_id = 0; unbounded_octstring pdu_session_nas_pdu; s_nssai_s s_nssai; unbounded_octstring pdu_session_res_setup_request_transfer; @@ -7931,7 +7538,7 @@ struct pdu_session_res_to_release_item_rel_cmd_s { }; // PWSFailedCellIDList-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using pws_failed_cell_id_list_ext_ies_o = ngap_protocol_ies_empty_o; +using pws_failed_cell_id_list_ext_ies_o = protocol_ies_empty_o; // ResetAll ::= ENUMERATED struct reset_all_opts { @@ -7942,7 +7549,7 @@ struct reset_all_opts { typedef enumerated reset_all_e; // ResetType-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using reset_type_ext_ies_o = ngap_protocol_ies_empty_o; +using reset_type_ext_ies_o = protocol_ies_empty_o; using supported_ta_item_ext_ies_container = protocol_ext_container_empty_l; @@ -7998,13 +7605,13 @@ struct ue_ngap_id_pair_s { }; // UE-NGAP-IDs-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using ue_ngap_ids_ext_ies_o = ngap_protocol_ies_empty_o; +using ue_ngap_ids_ext_ies_o = protocol_ies_empty_o; // UE-associatedLogicalNG-connectionList ::= SEQUENCE (SIZE (1..65536)) OF UE-associatedLogicalNG-connectionItem using ue_associated_lc_ng_conn_list_l = dyn_array; // UEPagingIdentity-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using ue_paging_id_ext_ies_o = ngap_protocol_ies_empty_o; +using ue_paging_id_ext_ies_o = protocol_ies_empty_o; using ue_presence_in_area_of_interest_item_ext_ies_container = protocol_ext_container_empty_l; @@ -8024,7 +7631,7 @@ struct ue_presence_in_area_of_interest_item_s { }; // WarningAreaList-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using warning_area_list_ext_ies_o = ngap_protocol_ies_empty_o; +using warning_area_list_ext_ies_o = protocol_ies_empty_o; // CancelAllWarningMessages ::= ENUMERATED struct cancel_all_warning_msgs_opts { @@ -9013,7 +8620,7 @@ struct overload_start_ies_o { }; // OverloadStopIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using overload_stop_ies_o = ngap_protocol_ies_empty_o; +using overload_stop_ies_o = protocol_ies_empty_o; // PDUSessionResourceModifyConfirmIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES struct pdu_session_res_modify_confirm_ies_o { @@ -11187,11 +10794,11 @@ struct location_report_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool ue_presence_in_area_of_interest_list_present = false; - bool ps_cell_info_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s user_location_info; + bool ue_presence_in_area_of_interest_list_present = false; + bool ps_cell_info_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s user_location_info; ie_field_s > ue_presence_in_area_of_interest_list; ie_field_s location_report_request_type; ie_field_s ps_cell_info; @@ -11204,25 +10811,16 @@ struct location_report_ies_container { }; // LocationReport ::= SEQUENCE -struct location_report_s { - bool ext = false; - location_report_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using location_report_s = elementary_procedure_option; struct location_report_ctrl_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s location_report_request_type; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s location_report_request_type; // sequence methods location_report_ctrl_ies_container(); @@ -11232,25 +10830,16 @@ struct location_report_ctrl_ies_container { }; // LocationReportingControl ::= SEQUENCE -struct location_report_ctrl_s { - bool ext = false; - location_report_ctrl_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using location_report_ctrl_s = elementary_procedure_option; struct location_report_fail_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s cause; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s cause; // sequence methods location_report_fail_ind_ies_container(); @@ -11260,26 +10849,17 @@ struct location_report_fail_ind_ies_container { }; // LocationReportingFailureIndication ::= SEQUENCE -struct location_report_fail_ind_s { - bool ext = false; - location_report_fail_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using location_report_fail_ind_s = elementary_procedure_option; struct nas_non_delivery_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > nas_pdu; - ie_field_s cause; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > nas_pdu; + ie_field_s cause; // sequence methods nas_non_delivery_ind_ies_container(); @@ -11289,16 +10869,7 @@ struct nas_non_delivery_ind_ies_container { }; // NASNonDeliveryIndication ::= SEQUENCE -struct nas_non_delivery_ind_s { - bool ext = false; - nas_non_delivery_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using nas_non_delivery_ind_s = elementary_procedure_option; struct ng_reset_ies_container { template @@ -11316,16 +10887,7 @@ struct ng_reset_ies_container { }; // NGReset ::= SEQUENCE -struct ng_reset_s { - bool ext = false; - ng_reset_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ng_reset_s = elementary_procedure_option; struct ng_reset_ack_ies_container { template @@ -11345,16 +10907,7 @@ struct ng_reset_ack_ies_container { }; // NGResetAcknowledge ::= SEQUENCE -struct ng_reset_ack_s { - bool ext = false; - ng_reset_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ng_reset_ack_s = elementary_procedure_option; struct ng_setup_fail_ies_container { template @@ -11375,16 +10928,7 @@ struct ng_setup_fail_ies_container { }; // NGSetupFailure ::= SEQUENCE -struct ng_setup_fail_s { - bool ext = false; - ng_setup_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ng_setup_fail_s = elementary_procedure_option; struct ng_setup_request_ies_container { template @@ -11407,16 +10951,7 @@ struct ng_setup_request_ies_container { }; // NGSetupRequest ::= SEQUENCE -struct ng_setup_request_s { - bool ext = false; - ng_setup_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ng_setup_request_s = elementary_procedure_option; struct ng_setup_resp_ies_container { template @@ -11440,16 +10975,7 @@ struct ng_setup_resp_ies_container { }; // NGSetupResponse ::= SEQUENCE -struct ng_setup_resp_s { - bool ext = false; - ng_setup_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ng_setup_resp_s = elementary_procedure_option; struct overload_start_ies_container { template @@ -11471,49 +10997,22 @@ struct overload_start_ies_container { }; // OverloadStart ::= SEQUENCE -struct overload_start_s { - bool ext = false; - overload_start_ies_container protocol_ies; - // ... +using overload_start_s = elementary_procedure_option; - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; - -struct protocol_ie_container_empty_l { - template - using ie_field_s = protocol_ie_container_item_s; - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; using overload_stop_ies_container = protocol_ie_container_empty_l; // OverloadStop ::= SEQUENCE -struct overload_stop_s { - bool ext = false; - overload_stop_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using overload_stop_s = elementary_procedure_option; struct pdu_session_res_modify_confirm_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_failed_to_modify_list_mod_cfm_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_failed_to_modify_list_mod_cfm_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_modify_list_mod_cfm; ie_field_s > pdu_session_res_failed_to_modify_list_mod_cfm; @@ -11527,24 +11026,15 @@ struct pdu_session_res_modify_confirm_ies_container { }; // PDUSessionResourceModifyConfirm ::= SEQUENCE -struct pdu_session_res_modify_confirm_s { - bool ext = false; - pdu_session_res_modify_confirm_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_modify_confirm_s = elementary_procedure_option; struct pdu_session_res_modify_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_modify_list_mod_ind; // sequence methods @@ -11555,16 +11045,7 @@ struct pdu_session_res_modify_ind_ies_container { }; // PDUSessionResourceModifyIndication ::= SEQUENCE -struct pdu_session_res_modify_ind_s { - bool ext = false; - pdu_session_res_modify_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_modify_ind_s = elementary_procedure_option; struct pdu_session_res_modify_request_ies_container { template @@ -11572,8 +11053,8 @@ struct pdu_session_res_modify_request_ies_container { // member variables bool ran_paging_prio_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > ran_paging_prio; ie_field_s > pdu_session_res_modify_list_mod_req; @@ -11585,28 +11066,19 @@ struct pdu_session_res_modify_request_ies_container { }; // PDUSessionResourceModifyRequest ::= SEQUENCE -struct pdu_session_res_modify_request_s { - bool ext = false; - pdu_session_res_modify_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_modify_request_s = elementary_procedure_option; struct pdu_session_res_modify_resp_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_modify_list_mod_res_present = false; - bool pdu_session_res_failed_to_modify_list_mod_res_present = false; - bool user_location_info_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_modify_list_mod_res_present = false; + bool pdu_session_res_failed_to_modify_list_mod_res_present = false; + bool user_location_info_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_modify_list_mod_res; ie_field_s > pdu_session_res_failed_to_modify_list_mod_res; @@ -11621,27 +11093,18 @@ struct pdu_session_res_modify_resp_ies_container { }; // PDUSessionResourceModifyResponse ::= SEQUENCE -struct pdu_session_res_modify_resp_s { - bool ext = false; - pdu_session_res_modify_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_modify_resp_s = elementary_procedure_option; struct pdu_session_res_notify_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_notify_list_present = false; - bool pdu_session_res_released_list_not_present = false; - bool user_location_info_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_notify_list_present = false; + bool pdu_session_res_released_list_not_present = false; + bool user_location_info_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_notify_list; ie_field_s > pdu_session_res_released_list_not; ie_field_s user_location_info; @@ -11654,28 +11117,19 @@ struct pdu_session_res_notify_ies_container { }; // PDUSessionResourceNotify ::= SEQUENCE -struct pdu_session_res_notify_s { - bool ext = false; - pdu_session_res_notify_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_notify_s = elementary_procedure_option; struct pdu_session_res_release_cmd_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ran_paging_prio_present = false; - bool nas_pdu_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > ran_paging_prio; - ie_field_s > nas_pdu; + bool ran_paging_prio_present = false; + bool nas_pdu_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > ran_paging_prio; + ie_field_s > nas_pdu; ie_field_s > pdu_session_res_to_release_list_rel_cmd; @@ -11687,16 +11141,7 @@ struct pdu_session_res_release_cmd_ies_container { }; // PDUSessionResourceReleaseCommand ::= SEQUENCE -struct pdu_session_res_release_cmd_s { - bool ext = false; - pdu_session_res_release_cmd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_release_cmd_s = elementary_procedure_option; struct pdu_session_res_release_resp_ies_container { template @@ -11705,8 +11150,8 @@ struct pdu_session_res_release_resp_ies_container { // member variables bool user_location_info_present = false; bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_released_list_rel_res; ie_field_s user_location_info; ie_field_s crit_diagnostics; @@ -11719,29 +11164,20 @@ struct pdu_session_res_release_resp_ies_container { }; // PDUSessionResourceReleaseResponse ::= SEQUENCE -struct pdu_session_res_release_resp_s { - bool ext = false; - pdu_session_res_release_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_release_resp_s = elementary_procedure_option; struct pdu_session_res_setup_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ran_paging_prio_present = false; - bool nas_pdu_present = false; - bool ue_aggregate_maximum_bit_rate_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > ran_paging_prio; - ie_field_s > nas_pdu; + bool ran_paging_prio_present = false; + bool nas_pdu_present = false; + bool ue_aggregate_maximum_bit_rate_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > ran_paging_prio; + ie_field_s > nas_pdu; ie_field_s > pdu_session_res_setup_list_su_req; ie_field_s ue_aggregate_maximum_bit_rate; @@ -11753,27 +11189,18 @@ struct pdu_session_res_setup_request_ies_container { }; // PDUSessionResourceSetupRequest ::= SEQUENCE -struct pdu_session_res_setup_request_s { - bool ext = false; - pdu_session_res_setup_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_setup_request_s = elementary_procedure_option; struct pdu_session_res_setup_resp_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_setup_list_su_res_present = false; - bool pdu_session_res_failed_to_setup_list_su_res_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_setup_list_su_res_present = false; + bool pdu_session_res_failed_to_setup_list_su_res_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_setup_list_su_res; ie_field_s > pdu_session_res_failed_to_setup_list_su_res; @@ -11787,16 +11214,7 @@ struct pdu_session_res_setup_resp_ies_container { }; // PDUSessionResourceSetupResponse ::= SEQUENCE -struct pdu_session_res_setup_resp_s { - bool ext = false; - pdu_session_res_setup_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_setup_resp_s = elementary_procedure_option; struct pws_cancel_request_ies_container { template @@ -11818,16 +11236,7 @@ struct pws_cancel_request_ies_container { }; // PWSCancelRequest ::= SEQUENCE -struct pws_cancel_request_s { - bool ext = false; - pws_cancel_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pws_cancel_request_s = elementary_procedure_option; struct pws_cancel_resp_ies_container { template @@ -11849,16 +11258,7 @@ struct pws_cancel_resp_ies_container { }; // PWSCancelResponse ::= SEQUENCE -struct pws_cancel_resp_s { - bool ext = false; - pws_cancel_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pws_cancel_resp_s = elementary_procedure_option; struct pws_fail_ind_ies_container { template @@ -11876,16 +11276,7 @@ struct pws_fail_ind_ies_container { }; // PWSFailureIndication ::= SEQUENCE -struct pws_fail_ind_s { - bool ext = false; - pws_fail_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pws_fail_ind_s = elementary_procedure_option; struct pws_restart_ind_ies_container { template @@ -11906,16 +11297,7 @@ struct pws_restart_ind_ies_container { }; // PWSRestartIndication ::= SEQUENCE -struct pws_restart_ind_s { - bool ext = false; - pws_restart_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pws_restart_ind_s = elementary_procedure_option; struct paging_ies_container { template @@ -11943,24 +11325,15 @@ struct paging_ies_container { }; // Paging ::= SEQUENCE -struct paging_s { - bool ext = false; - paging_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using paging_s = elementary_procedure_option; struct path_switch_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_failed_to_setup_list_ps_req_present = false; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_failed_to_setup_list_ps_req_present = false; + ie_field_s ran_ue_ngap_id; ie_field_s > source_amf_ue_ngap_id; ie_field_s user_location_info; ie_field_s ue_security_cap; @@ -11977,34 +11350,25 @@ struct path_switch_request_ies_container { }; // PathSwitchRequest ::= SEQUENCE -struct path_switch_request_s { - bool ext = false; - path_switch_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using path_switch_request_s = elementary_procedure_option; struct path_switch_request_ack_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ue_security_cap_present = false; - bool new_security_context_ind_present = false; - bool pdu_session_res_released_list_ps_ack_present = false; - bool core_network_assist_info_present = false; - bool rrc_inactive_transition_report_request_present = false; - bool crit_diagnostics_present = false; - bool redirection_voice_fallback_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s ue_security_cap; - ie_field_s security_context; - ie_field_s new_security_context_ind; + bool ue_security_cap_present = false; + bool new_security_context_ind_present = false; + bool pdu_session_res_released_list_ps_ack_present = false; + bool core_network_assist_info_present = false; + bool rrc_inactive_transition_report_request_present = false; + bool crit_diagnostics_present = false; + bool redirection_voice_fallback_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s ue_security_cap; + ie_field_s security_context; + ie_field_s new_security_context_ind; ie_field_s > pdu_session_res_switched_list; ie_field_s > pdu_session_res_released_list_ps_ack; ie_field_s > allowed_nssai; @@ -12021,16 +11385,7 @@ struct path_switch_request_ack_ies_container { }; // PathSwitchRequestAcknowledge ::= SEQUENCE -struct path_switch_request_ack_s { - bool ext = false; - path_switch_request_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using path_switch_request_ack_s = elementary_procedure_option; struct path_switch_request_fail_ies_container { template @@ -12038,8 +11393,8 @@ struct path_switch_request_fail_ies_container { // member variables bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_released_list_ps_fail; ie_field_s crit_diagnostics; @@ -12051,16 +11406,7 @@ struct path_switch_request_fail_ies_container { }; // PathSwitchRequestFailure ::= SEQUENCE -struct path_switch_request_fail_s { - bool ext = false; - path_switch_request_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using path_switch_request_fail_s = elementary_procedure_option; template struct private_ie_container_item_s { @@ -12120,28 +11466,10 @@ struct ran_cfg_upd_ies_container { }; // RANConfigurationUpdate ::= SEQUENCE -struct ran_cfg_upd_s { - bool ext = false; - ran_cfg_upd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ran_cfg_upd_s = elementary_procedure_option; // RANConfigurationUpdateAcknowledge ::= SEQUENCE -struct ran_cfg_upd_ack_s { - bool ext = false; - protocol_ie_container_l protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ran_cfg_upd_ack_s = elementary_procedure_option >; struct ran_cfg_upd_fail_ies_container { template @@ -12162,26 +11490,17 @@ struct ran_cfg_upd_fail_ies_container { }; // RANConfigurationUpdateFailure ::= SEQUENCE -struct ran_cfg_upd_fail_s { - bool ext = false; - ran_cfg_upd_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ran_cfg_upd_fail_s = elementary_procedure_option; struct rrc_inactive_transition_report_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s rrc_state; - ie_field_s user_location_info; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s rrc_state; + ie_field_s user_location_info; // sequence methods rrc_inactive_transition_report_ies_container(); @@ -12191,29 +11510,20 @@ struct rrc_inactive_transition_report_ies_container { }; // RRCInactiveTransitionReport ::= SEQUENCE -struct rrc_inactive_transition_report_s { - bool ext = false; - rrc_inactive_transition_report_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using rrc_inactive_transition_report_s = elementary_procedure_option; struct reroute_nas_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool amf_ue_ngap_id_present = false; - bool allowed_nssai_present = false; - ie_field_s > ran_ue_ngap_id; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ngap_msg; - ie_field_s > amf_set_id; - ie_field_s > allowed_nssai; + bool amf_ue_ngap_id_present = false; + bool allowed_nssai_present = false; + ie_field_s ran_ue_ngap_id; + ie_field_s amf_ue_ngap_id; + ie_field_s > ngap_msg; + ie_field_s > amf_set_id; + ie_field_s > allowed_nssai; // sequence methods reroute_nas_request_ies_container(); @@ -12223,25 +11533,16 @@ struct reroute_nas_request_ies_container { }; // RerouteNASRequest ::= SEQUENCE -struct reroute_nas_request_s { - bool ext = false; - reroute_nas_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using reroute_nas_request_s = elementary_procedure_option; struct secondary_rat_data_usage_report_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ho_flag_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool ho_flag_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_secondary_ratusage_list; ie_field_s ho_flag; @@ -12254,26 +11555,17 @@ struct secondary_rat_data_usage_report_ies_container { }; // SecondaryRATDataUsageReport ::= SEQUENCE -struct secondary_rat_data_usage_report_s { - bool ext = false; - secondary_rat_data_usage_report_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using secondary_rat_data_usage_report_s = elementary_procedure_option; struct trace_fail_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > ngran_trace_id; - ie_field_s cause; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > ngran_trace_id; + ie_field_s cause; // sequence methods trace_fail_ind_ies_container(); @@ -12283,25 +11575,16 @@ struct trace_fail_ind_ies_container { }; // TraceFailureIndication ::= SEQUENCE -struct trace_fail_ind_s { - bool ext = false; - trace_fail_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using trace_fail_ind_s = elementary_procedure_option; struct trace_start_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s trace_activation; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s trace_activation; // sequence methods trace_start_ies_container(); @@ -12311,27 +11594,18 @@ struct trace_start_ies_container { }; // TraceStart ::= SEQUENCE -struct trace_start_s { - bool ext = false; - trace_start_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using trace_start_s = elementary_procedure_option; struct ue_context_mod_fail_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods ue_context_mod_fail_ies_container(); @@ -12341,16 +11615,7 @@ struct ue_context_mod_fail_ies_container { }; // UEContextModificationFailure ::= SEQUENCE -struct ue_context_mod_fail_s { - bool ext = false; - ue_context_mod_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_mod_fail_s = elementary_procedure_option; struct ue_context_mod_request_ies_container { template @@ -12366,8 +11631,8 @@ struct ue_context_mod_request_ies_container { bool emergency_fallback_ind_present = false; bool new_amf_ue_ngap_id_present = false; bool rrc_inactive_transition_report_request_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > ran_paging_prio; ie_field_s > security_key; ie_field_s > idx_to_rfsp; @@ -12386,30 +11651,21 @@ struct ue_context_mod_request_ies_container { }; // UEContextModificationRequest ::= SEQUENCE -struct ue_context_mod_request_s { - bool ext = false; - ue_context_mod_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_mod_request_s = elementary_procedure_option; struct ue_context_mod_resp_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool rrc_state_present = false; - bool user_location_info_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s rrc_state; - ie_field_s user_location_info; - ie_field_s crit_diagnostics; + bool rrc_state_present = false; + bool user_location_info_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s rrc_state; + ie_field_s user_location_info; + ie_field_s crit_diagnostics; // sequence methods ue_context_mod_resp_ies_container(); @@ -12419,16 +11675,7 @@ struct ue_context_mod_resp_ies_container { }; // UEContextModificationResponse ::= SEQUENCE -struct ue_context_mod_resp_s { - bool ext = false; - ue_context_mod_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_mod_resp_s = elementary_procedure_option; struct ue_context_release_cmd_ies_container { template @@ -12446,29 +11693,20 @@ struct ue_context_release_cmd_ies_container { }; // UEContextReleaseCommand ::= SEQUENCE -struct ue_context_release_cmd_s { - bool ext = false; - ue_context_release_cmd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_release_cmd_s = elementary_procedure_option; struct ue_context_release_complete_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool user_location_info_present = false; - bool info_on_recommended_cells_and_ran_nodes_for_paging_present = false; - bool pdu_session_res_list_cxt_rel_cpl_present = false; - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s user_location_info; + bool user_location_info_present = false; + bool info_on_recommended_cells_and_ran_nodes_for_paging_present = false; + bool pdu_session_res_list_cxt_rel_cpl_present = false; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s user_location_info; ie_field_s info_on_recommended_cells_and_ran_nodes_for_paging; ie_field_s > pdu_session_res_list_cxt_rel_cpl; ie_field_s crit_diagnostics; @@ -12481,25 +11719,16 @@ struct ue_context_release_complete_ies_container { }; // UEContextReleaseComplete ::= SEQUENCE -struct ue_context_release_complete_s { - bool ext = false; - ue_context_release_complete_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_release_complete_s = elementary_procedure_option; struct ue_context_release_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool pdu_session_res_list_cxt_rel_req_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + bool pdu_session_res_list_cxt_rel_req_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; ie_field_s > pdu_session_res_list_cxt_rel_req; ie_field_s cause; @@ -12511,26 +11740,17 @@ struct ue_context_release_request_ies_container { }; // UEContextReleaseRequest ::= SEQUENCE -struct ue_context_release_request_s { - bool ext = false; - ue_context_release_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_release_request_s = elementary_procedure_option; struct ue_radio_cap_check_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ue_radio_cap_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > ue_radio_cap; + bool ue_radio_cap_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > ue_radio_cap; // sequence methods ue_radio_cap_check_request_ies_container(); @@ -12540,27 +11760,18 @@ struct ue_radio_cap_check_request_ies_container { }; // UERadioCapabilityCheckRequest ::= SEQUENCE -struct ue_radio_cap_check_request_s { - bool ext = false; - ue_radio_cap_check_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_radio_cap_check_request_s = elementary_procedure_option; struct ue_radio_cap_check_resp_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s ims_voice_support_ind; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s ims_voice_support_ind; + ie_field_s crit_diagnostics; // sequence methods ue_radio_cap_check_resp_ies_container(); @@ -12570,27 +11781,18 @@ struct ue_radio_cap_check_resp_ies_container { }; // UERadioCapabilityCheckResponse ::= SEQUENCE -struct ue_radio_cap_check_resp_s { - bool ext = false; - ue_radio_cap_check_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_radio_cap_check_resp_s = elementary_procedure_option; struct ue_radio_cap_info_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ue_radio_cap_for_paging_present = false; - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > ue_radio_cap; - ie_field_s ue_radio_cap_for_paging; + bool ue_radio_cap_for_paging_present = false; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > ue_radio_cap; + ie_field_s ue_radio_cap_for_paging; // sequence methods ue_radio_cap_info_ind_ies_container(); @@ -12600,24 +11802,15 @@ struct ue_radio_cap_info_ind_ies_container { }; // UERadioCapabilityInfoIndication ::= SEQUENCE -struct ue_radio_cap_info_ind_s { - bool ext = false; - ue_radio_cap_info_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_radio_cap_info_ind_s = elementary_procedure_option; struct uetnla_binding_release_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; // sequence methods uetnla_binding_release_request_ies_container(); @@ -12627,26 +11820,17 @@ struct uetnla_binding_release_request_ies_container { }; // UETNLABindingReleaseRequest ::= SEQUENCE -struct uetnla_binding_release_request_s { - bool ext = false; - uetnla_binding_release_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using uetnla_binding_release_request_s = elementary_procedure_option; struct ul_nas_transport_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > nas_pdu; - ie_field_s user_location_info; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > nas_pdu; + ie_field_s user_location_info; // sequence methods ul_nas_transport_ies_container(); @@ -12656,16 +11840,7 @@ struct ul_nas_transport_ies_container { }; // UplinkNASTransport ::= SEQUENCE -struct ul_nas_transport_s { - bool ext = false; - ul_nas_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_nas_transport_s = elementary_procedure_option; struct ul_non_ueassociated_nrp_pa_transport_ies_container { template @@ -12683,16 +11858,8 @@ struct ul_non_ueassociated_nrp_pa_transport_ies_container { }; // UplinkNonUEAssociatedNRPPaTransport ::= SEQUENCE -struct ul_non_ueassociated_nrp_pa_transport_s { - bool ext = false; - ul_non_ueassociated_nrp_pa_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_non_ueassociated_nrp_pa_transport_s = + elementary_procedure_option; struct ul_ran_cfg_transfer_ies_container { template @@ -12712,25 +11879,16 @@ struct ul_ran_cfg_transfer_ies_container { }; // UplinkRANConfigurationTransfer ::= SEQUENCE -struct ul_ran_cfg_transfer_s { - bool ext = false; - ul_ran_cfg_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_ran_cfg_transfer_s = elementary_procedure_option; struct ul_ran_status_transfer_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s ran_status_transfer_transparent_container; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s ran_status_transfer_transparent_container; // sequence methods ul_ran_status_transfer_ies_container(); @@ -12740,26 +11898,17 @@ struct ul_ran_status_transfer_ies_container { }; // UplinkRANStatusTransfer ::= SEQUENCE -struct ul_ran_status_transfer_s { - bool ext = false; - ul_ran_status_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_ran_status_transfer_s = elementary_procedure_option; struct ul_ueassociated_nrp_pa_transport_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > amf_ue_ngap_id; - ie_field_s > ran_ue_ngap_id; - ie_field_s > routing_id; - ie_field_s > nrp_pa_pdu; + ie_field_s amf_ue_ngap_id; + ie_field_s ran_ue_ngap_id; + ie_field_s > routing_id; + ie_field_s > nrp_pa_pdu; // sequence methods ul_ueassociated_nrp_pa_transport_ies_container(); @@ -12769,16 +11918,7 @@ struct ul_ueassociated_nrp_pa_transport_ies_container { }; // UplinkUEAssociatedNRPPaTransport ::= SEQUENCE -struct ul_ueassociated_nrp_pa_transport_s { - bool ext = false; - ul_ueassociated_nrp_pa_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_ueassociated_nrp_pa_transport_s = elementary_procedure_option; struct write_replace_warning_request_ies_container { template @@ -12812,16 +11952,7 @@ struct write_replace_warning_request_ies_container { }; // WriteReplaceWarningRequest ::= SEQUENCE -struct write_replace_warning_request_s { - bool ext = false; - write_replace_warning_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using write_replace_warning_request_s = elementary_procedure_option; struct write_replace_warning_resp_ies_container { template @@ -12843,16 +11974,7 @@ struct write_replace_warning_resp_ies_container { }; // WriteReplaceWarningResponse ::= SEQUENCE -struct write_replace_warning_resp_s { - bool ext = false; - write_replace_warning_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using write_replace_warning_resp_s = elementary_procedure_option; // NGAP-ELEMENTARY-PROCEDURES ::= OBJECT SET OF NGAP-ELEMENTARY-PROCEDURE struct ngap_elem_procs_o { @@ -13282,10 +12404,10 @@ struct init_msg_s { }; // LastVisitedNGRANCellInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using last_visited_ngran_cell_info_ext_ies_o = ngap_protocol_ext_empty_o; +using last_visited_ngran_cell_info_ext_ies_o = protocol_ext_empty_o; // LastVisitedCellInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using last_visited_cell_info_ext_ies_o = ngap_protocol_ies_empty_o; +using last_visited_cell_info_ext_ies_o = protocol_ies_empty_o; using last_visited_ngran_cell_info_ext_ies_container = protocol_ext_container_empty_l; @@ -13396,7 +12518,7 @@ private: }; // LastVisitedCellItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using last_visited_cell_item_ext_ies_o = ngap_protocol_ext_empty_o; +using last_visited_cell_item_ext_ies_o = protocol_ext_empty_o; using last_visited_cell_item_ext_ies_container = protocol_ext_container_empty_l; @@ -13500,7 +12622,7 @@ private: }; // NonDynamic5QIDescriptor-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using non_dynamic5_qi_descriptor_ext_ies_o = ngap_protocol_ext_empty_o; +using non_dynamic5_qi_descriptor_ext_ies_o = protocol_ext_empty_o; using non_dynamic5_qi_descriptor_ext_ies_container = protocol_ext_container_empty_l; @@ -13525,7 +12647,7 @@ struct non_dynamic5_qi_descriptor_s { }; // PDUSessionAggregateMaximumBitRate-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_aggregate_maximum_bit_rate_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_aggregate_maximum_bit_rate_ext_ies_o = protocol_ext_empty_o; using pdu_session_aggregate_maximum_bit_rate_ext_ies_container = protocol_ext_container_empty_l; @@ -13545,7 +12667,7 @@ struct pdu_session_aggregate_maximum_bit_rate_s { }; // QosFlowInformationItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_info_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_info_item_ext_ies_o = protocol_ext_empty_o; using qos_flow_info_item_ext_ies_container = protocol_ext_container_empty_l; @@ -13566,7 +12688,7 @@ struct qos_flow_info_item_s { }; // PDUSessionResourceInformationItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_info_item_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_info_item_ext_ies_o = protocol_ext_empty_o; // QosFlowInformationList ::= SEQUENCE (SIZE (1..64)) OF QosFlowInformationItem using qos_flow_info_list_l = dyn_array; @@ -13575,10 +12697,9 @@ using pdu_session_res_info_item_ext_ies_container = protocol_ext_container_empty // PDUSessionResourceInformationItem ::= SEQUENCE struct pdu_session_res_info_item_s { - bool ext = false; - bool drbs_to_qos_flows_map_list_present = false; - bool ie_exts_present = false; - uint16_t pdu_session_id = 0; + bool ext = false; + bool ie_exts_present = false; + uint16_t pdu_session_id = 0; qos_flow_info_list_l qos_flow_info_list; drbs_to_qos_flows_map_list_l drbs_to_qos_flows_map_list; pdu_session_res_info_item_ext_ies_container ie_exts; @@ -13594,10 +12715,10 @@ struct pdu_session_res_info_item_s { using pdu_session_res_info_list_l = dyn_array; // QosFlowModifyConfirmItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_modify_confirm_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_modify_confirm_item_ext_ies_o = protocol_ext_empty_o; // UPTransportLayerInformationPairItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using up_transport_layer_info_pair_item_ext_ies_o = ngap_protocol_ext_empty_o; +using up_transport_layer_info_pair_item_ext_ies_o = protocol_ext_empty_o; using qos_flow_modify_confirm_item_ext_ies_container = protocol_ext_container_empty_l; @@ -13633,7 +12754,7 @@ struct up_transport_layer_info_pair_item_s { }; // PDUSessionResourceModifyConfirmTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_modify_confirm_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_modify_confirm_transfer_ext_ies_o = protocol_ext_empty_o; // QosFlowModifyConfirmList ::= SEQUENCE (SIZE (1..64)) OF QosFlowModifyConfirmItem using qos_flow_modify_confirm_list_l = dyn_array; @@ -13645,10 +12766,8 @@ using pdu_session_res_modify_confirm_transfer_ext_ies_container = protocol_ext_c // PDUSessionResourceModifyConfirmTransfer ::= SEQUENCE struct pdu_session_res_modify_confirm_transfer_s { - bool ext = false; - bool add_ng_uuptnl_info_present = false; - bool qos_flow_failed_to_modify_list_present = false; - bool ie_exts_present = false; + bool ext = false; + bool ie_exts_present = false; qos_flow_modify_confirm_list_l qos_flow_modify_confirm_list; up_transport_layer_info_c ulngu_up_tnl_info; up_transport_layer_info_pair_list_l add_ng_uuptnl_info; @@ -13663,7 +12782,7 @@ struct pdu_session_res_modify_confirm_transfer_s { }; // VolumeTimedReport-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using volume_timed_report_item_ext_ies_o = ngap_protocol_ext_empty_o; +using volume_timed_report_item_ext_ies_o = protocol_ext_empty_o; using volume_timed_report_item_ext_ies_container = protocol_ext_container_empty_l; @@ -13685,13 +12804,13 @@ struct volume_timed_report_item_s { }; // QoSFlowsUsageReport-Item-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qo_sflows_usage_report_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qo_sflows_usage_report_item_ext_ies_o = protocol_ext_empty_o; // VolumeTimedReportList ::= SEQUENCE (SIZE (1..2)) OF VolumeTimedReport-Item using volume_timed_report_list_l = dyn_array; // PDUSessionUsageReport-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_usage_report_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_usage_report_ext_ies_o = protocol_ext_empty_o; using qo_sflows_usage_report_item_ext_ies_container = protocol_ext_container_empty_l; @@ -13748,16 +12867,15 @@ struct pdu_session_usage_report_s { using qo_sflows_usage_report_list_l = dyn_array; // SecondaryRATUsageInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using secondary_ratusage_info_ext_ies_o = ngap_protocol_ext_empty_o; +using secondary_ratusage_info_ext_ies_o = protocol_ext_empty_o; using secondary_ratusage_info_ext_ies_container = protocol_ext_container_empty_l; // SecondaryRATUsageInformation ::= SEQUENCE struct secondary_ratusage_info_s { - bool ext = false; - bool pdu_session_usage_report_present = false; - bool qos_flows_usage_report_list_present = false; - bool ie_ext_present = false; + bool ext = false; + bool pdu_session_usage_report_present = false; + bool ie_ext_present = false; pdu_session_usage_report_s pdu_session_usage_report; qo_sflows_usage_report_list_l qos_flows_usage_report_list; secondary_ratusage_info_ext_ies_container ie_ext; @@ -13830,9 +12948,8 @@ struct pdu_session_res_modify_ind_transfer_ext_ies_container { // PDUSessionResourceModifyIndicationTransfer ::= SEQUENCE struct pdu_session_res_modify_ind_transfer_s { - bool ext = false; - bool add_dl_qos_flow_per_tnl_info_present = false; - bool ie_exts_present = false; + bool ext = false; + bool ie_exts_present = false; qos_flow_per_tnl_info_s dlqos_flow_per_tnl_info; qos_flow_per_tnl_info_list_l add_dl_qos_flow_per_tnl_info; pdu_session_res_modify_ind_transfer_ext_ies_container ie_exts; @@ -13845,7 +12962,7 @@ struct pdu_session_res_modify_ind_transfer_s { }; // PDUSessionResourceModifyIndicationUnsuccessfulTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_modify_ind_unsuccessful_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_modify_ind_unsuccessful_transfer_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_modify_ind_unsuccessful_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -13864,7 +12981,7 @@ struct pdu_session_res_modify_ind_unsuccessful_transfer_s { }; // QosCharacteristics-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES -using qos_characteristics_ext_ies_o = ngap_protocol_ies_empty_o; +using qos_characteristics_ext_ies_o = protocol_ies_empty_o; // AdditionalQosFlowInformation ::= ENUMERATED struct add_qos_flow_info_opts { @@ -13939,7 +13056,7 @@ private: }; // QosFlowLevelQosParameters-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_level_qos_params_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_level_qos_params_ext_ies_o = protocol_ext_empty_o; // ReflectiveQosAttribute ::= ENUMERATED struct reflective_qos_attribute_opts { @@ -13950,7 +13067,7 @@ struct reflective_qos_attribute_opts { typedef enumerated reflective_qos_attribute_e; // QosFlowAddOrModifyRequestItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_add_or_modify_request_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_add_or_modify_request_item_ext_ies_o = protocol_ext_empty_o; using qos_flow_level_qos_params_ext_ies_container = protocol_ext_container_empty_l; @@ -13976,10 +13093,10 @@ struct qos_flow_level_qos_params_s { }; // UL-NGU-UP-TNLModifyItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using ul_ngu_up_tnl_modify_item_ext_ies_o = ngap_protocol_ext_empty_o; +using ul_ngu_up_tnl_modify_item_ext_ies_o = protocol_ext_empty_o; // UPTransportLayerInformationItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using up_transport_layer_info_item_ext_ies_o = ngap_protocol_ext_empty_o; +using up_transport_layer_info_item_ext_ies_o = protocol_ext_empty_o; using qos_flow_add_or_modify_request_item_ext_ies_container = protocol_ext_container_empty_l; @@ -14134,19 +13251,11 @@ struct pdu_session_res_modify_request_transfer_ies_container { }; // PDUSessionResourceModifyRequestTransfer ::= SEQUENCE -struct pdu_session_res_modify_request_transfer_s { - bool ext = false; - pdu_session_res_modify_request_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_modify_request_transfer_s = + elementary_procedure_option; // QosFlowAddOrModifyResponseItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_add_or_modify_resp_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_add_or_modify_resp_item_ext_ies_o = protocol_ext_empty_o; using qos_flow_add_or_modify_resp_item_ext_ies_container = protocol_ext_container_empty_l; @@ -14201,18 +13310,14 @@ using qos_flow_add_or_modify_resp_list_l = dyn_array ie_exts; // ... @@ -14223,7 +13328,7 @@ struct pdu_session_res_modify_resp_transfer_s { }; // PDUSessionResourceModifyUnsuccessfulTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_modify_unsuccessful_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_modify_unsuccessful_transfer_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_modify_unsuccessful_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -14277,8 +13382,7 @@ struct pdu_session_res_notify_released_transfer_ext_ies_o { // PDUSessionResourceNotifyReleasedTransfer ::= SEQUENCE struct pdu_session_res_notify_released_transfer_s { - bool ext = false; - bool ie_exts_present = false; + bool ext = false; cause_c cause; protocol_ext_container_l ie_exts; // ... @@ -14298,7 +13402,7 @@ struct notif_cause_opts { typedef enumerated notif_cause_e; // QosFlowNotifyItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_notify_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_notify_item_ext_ies_o = protocol_ext_empty_o; using qos_flow_notify_item_ext_ies_container = protocol_ext_container_empty_l; @@ -14354,10 +13458,7 @@ using qos_flow_notify_list_l = dyn_array; // PDUSessionResourceNotifyTransfer ::= SEQUENCE struct pdu_session_res_notify_transfer_s { - bool ext = false; - bool qos_flow_notify_list_present = false; - bool qos_flow_released_list_present = false; - bool ie_exts_present = false; + bool ext = false; qos_flow_notify_list_l qos_flow_notify_list; qos_flow_list_with_cause_l qos_flow_released_list; protocol_ext_container_l ie_exts; @@ -14370,7 +13471,7 @@ struct pdu_session_res_notify_transfer_s { }; // PDUSessionResourceReleaseCommandTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_release_cmd_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_release_cmd_transfer_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_release_cmd_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -14422,8 +13523,7 @@ struct pdu_session_res_release_resp_transfer_ext_ies_o { // PDUSessionResourceReleaseResponseTransfer ::= SEQUENCE struct pdu_session_res_release_resp_transfer_s { - bool ext = false; - bool ie_exts_present = false; + bool ext = false; protocol_ext_container_l ie_exts; // ... @@ -14444,7 +13544,7 @@ struct maximum_integrity_protected_data_rate_opts { typedef enumerated maximum_integrity_protected_data_rate_e; // QosFlowSetupRequestItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_setup_request_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_setup_request_item_ext_ies_o = protocol_ext_empty_o; // ConfidentialityProtectionIndication ::= ENUMERATED struct confidentiality_protection_ind_opts { @@ -14536,7 +13636,6 @@ using qos_flow_setup_request_list_l = dyn_array; struct security_ind_s { bool ext = false; bool maximum_integrity_protected_data_rate_ul_present = false; - bool ie_exts_present = false; integrity_protection_ind_e integrity_protection_ind; confidentiality_protection_ind_e confidentiality_protection_ind; maximum_integrity_protected_data_rate_e maximum_integrity_protected_data_rate_ul; @@ -14647,29 +13746,19 @@ struct pdu_session_res_setup_request_transfer_ies_container { }; // PDUSessionResourceSetupRequestTransfer ::= SEQUENCE -struct pdu_session_res_setup_request_transfer_s { - bool ext = false; - pdu_session_res_setup_request_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pdu_session_res_setup_request_transfer_s = + elementary_procedure_option; // PDUSessionResourceSetupResponseTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_setup_resp_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_setup_resp_transfer_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_setup_resp_transfer_ext_ies_container = protocol_ext_container_empty_l; // PDUSessionResourceSetupResponseTransfer ::= SEQUENCE struct pdu_session_res_setup_resp_transfer_s { - bool ext = false; - bool add_dl_qos_flow_per_tnl_info_present = false; - bool security_result_present = false; - bool qos_flow_failed_to_setup_list_present = false; - bool ie_exts_present = false; + bool ext = false; + bool security_result_present = false; + bool ie_exts_present = false; qos_flow_per_tnl_info_s dlqos_flow_per_tnl_info; qos_flow_per_tnl_info_list_l add_dl_qos_flow_per_tnl_info; security_result_s security_result; @@ -14684,7 +13773,7 @@ struct pdu_session_res_setup_resp_transfer_s { }; // PDUSessionResourceSetupUnsuccessfulTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using pdu_session_res_setup_unsuccessful_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using pdu_session_res_setup_unsuccessful_transfer_ext_ies_o = protocol_ext_empty_o; using pdu_session_res_setup_unsuccessful_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -14741,7 +13830,6 @@ struct path_switch_request_ack_transfer_s { bool ext = false; bool ul_ngu_up_tnl_info_present = false; bool security_ind_present = false; - bool ie_exts_present = false; up_transport_layer_info_c ul_ngu_up_tnl_info; security_ind_s security_ind; protocol_ext_container_l ie_exts; @@ -14754,7 +13842,7 @@ struct path_switch_request_ack_transfer_s { }; // PathSwitchRequestSetupFailedTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using path_switch_request_setup_failed_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using path_switch_request_setup_failed_transfer_ext_ies_o = protocol_ext_empty_o; using path_switch_request_setup_failed_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -14773,7 +13861,7 @@ struct path_switch_request_setup_failed_transfer_s { }; // QosFlowAcceptedItem-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_accepted_item_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_accepted_item_ext_ies_o = protocol_ext_empty_o; using qos_flow_accepted_item_ext_ies_container = protocol_ext_container_empty_l; @@ -14792,7 +13880,7 @@ struct qos_flow_accepted_item_s { }; // UserPlaneSecurityInformation-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using user_plane_security_info_ext_ies_o = ngap_protocol_ext_empty_o; +using user_plane_security_info_ext_ies_o = protocol_ext_empty_o; // DL-NGU-TNLInformationReused ::= ENUMERATED struct dl_ngu_tnl_info_reused_opts { @@ -14859,7 +13947,6 @@ struct path_switch_request_transfer_s { bool ext = false; bool dl_ngu_tnl_info_reused_present = false; bool user_plane_security_info_present = false; - bool ie_exts_present = false; up_transport_layer_info_c dl_ngu_up_tnl_info; dl_ngu_tnl_info_reused_e dl_ngu_tnl_info_reused; user_plane_security_info_s user_plane_security_info; @@ -14874,7 +13961,7 @@ struct path_switch_request_transfer_s { }; // PathSwitchRequestUnsuccessfulTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using path_switch_request_unsuccessful_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using path_switch_request_unsuccessful_transfer_ext_ies_o = protocol_ext_empty_o; using path_switch_request_unsuccessful_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -14912,7 +13999,7 @@ template using protocol_ie_container_pair_l = dyn_seq_of, 0, 65535, true>; // QosFlowSetupResponseItemSURes-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using qos_flow_setup_resp_item_su_res_ext_ies_o = ngap_protocol_ext_empty_o; +using qos_flow_setup_resp_item_su_res_ext_ies_o = protocol_ext_empty_o; using qos_flow_setup_resp_item_su_res_ext_ies_container = protocol_ext_container_empty_l; @@ -14934,7 +14021,7 @@ struct qos_flow_setup_resp_item_su_res_s { using qos_flow_setup_resp_list_su_res_l = dyn_array; // SecondaryRATDataUsageReportTransfer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using secondary_rat_data_usage_report_transfer_ext_ies_o = ngap_protocol_ext_empty_o; +using secondary_rat_data_usage_report_transfer_ext_ies_o = protocol_ext_empty_o; using secondary_rat_data_usage_report_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -14954,7 +14041,7 @@ struct secondary_rat_data_usage_report_transfer_s { }; // SourceNGRANNode-ToTargetNGRANNode-TransparentContainer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using source_ngran_node_to_target_ngran_node_transparent_container_ext_ies_o = ngap_protocol_ext_empty_o; +using source_ngran_node_to_target_ngran_node_transparent_container_ext_ies_o = protocol_ext_empty_o; // UEHistoryInformation ::= SEQUENCE (SIZE (1..16)) OF LastVisitedCellItem using ue_history_info_l = dyn_array; @@ -14963,17 +14050,15 @@ using source_ngran_node_to_target_ngran_node_transparent_container_ext_ies_conta // SourceNGRANNode-ToTargetNGRANNode-TransparentContainer ::= SEQUENCE struct source_ngran_node_to_target_ngran_node_transparent_container_s { - bool ext = false; - bool pdu_session_res_info_list_present = false; - bool erab_info_list_present = false; - bool idx_to_rfsp_present = false; - bool ie_exts_present = false; - unbounded_octstring rrc_container; - pdu_session_res_info_list_l pdu_session_res_info_list; - erab_info_list_l erab_info_list; - ngran_cgi_c target_cell_id; - uint16_t idx_to_rfsp = 1; - ue_history_info_l uehistory_info; + bool ext = false; + bool idx_to_rfsp_present = false; + bool ie_exts_present = false; + unbounded_octstring rrc_container; + pdu_session_res_info_list_l pdu_session_res_info_list; + erab_info_list_l erab_info_list; + ngran_cgi_c target_cell_id; + uint16_t idx_to_rfsp = 1; + ue_history_info_l uehistory_info; source_ngran_node_to_target_ngran_node_transparent_container_ext_ies_container ie_exts; // ... @@ -14984,7 +14069,7 @@ struct source_ngran_node_to_target_ngran_node_transparent_container_s { }; // TargetNGRANNode-ToSourceNGRANNode-TransparentContainer-ExtIEs ::= OBJECT SET OF NGAP-PROTOCOL-EXTENSION -using target_ngran_node_to_source_ngran_node_transparent_container_ext_ies_o = ngap_protocol_ext_empty_o; +using target_ngran_node_to_source_ngran_node_transparent_container_ext_ies_o = protocol_ext_empty_o; using target_ngran_node_to_source_ngran_node_transparent_container_ext_ies_container = protocol_ext_container_empty_l; @@ -15002,7 +14087,86 @@ struct target_ngran_node_to_source_ngran_node_transparent_container_s { void to_json(json_writer& j) const; }; -} // namespace ngap_nr +} // namespace ngap } // namespace asn1 -#endif // SRSASN1_NGAP_NR_H +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; + +#endif // SRSASN1_NGAP_H diff --git a/lib/include/srsran/asn1/ngap_utils.h b/lib/include/srsran/asn1/ngap_utils.h new file mode 100644 index 000000000..dc862b1c5 --- /dev/null +++ b/lib/include/srsran/asn1/ngap_utils.h @@ -0,0 +1,40 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_NGAP_UTILS_H +#define SRSRAN_NGAP_UTILS_H + +#include "asn1_utils.h" +#include "ngap.h" +/************************ + * Forward declarations + ***********************/ + +namespace asn1 { +namespace ngap { +struct rrcestablishment_cause_opts; +struct cause_radio_network_opts; +using rrcestablishment_cause_e = enumerated; +using cause_radio_network_e = enumerated; +} // namespace ngap +} // namespace asn1 + +#endif // SRSRAN_NGAP_UTILS_H \ No newline at end of file diff --git a/lib/include/srsran/rrc/rrc_cfg_utils.h b/lib/include/srsran/asn1/obj_id_cmp_utils.h similarity index 77% rename from lib/include/srsran/rrc/rrc_cfg_utils.h rename to lib/include/srsran/asn1/obj_id_cmp_utils.h index 69faa5191..3cbd908b8 100644 --- a/lib/include/srsran/rrc/rrc_cfg_utils.h +++ b/lib/include/srsran/asn1/obj_id_cmp_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,18 +19,37 @@ * */ -#ifndef SRSRAN_RRC_CFG_UTILS_H -#define SRSRAN_RRC_CFG_UTILS_H +#ifndef SRSRAN_OBJ_ID_CMP_UTILS_H +#define SRSRAN_OBJ_ID_CMP_UTILS_H -#include "srsran/asn1/rrc_utils.h" #include "srsran/common/common.h" #include #include namespace srsran { -template -using rrc_obj_id_t = decltype(asn1::rrc::get_rrc_obj_id(std::declval())); +using asn1_obj_id_t = uint8_t; + +/// Template function to generically obtain id of asn1 object (e.g. srb_id of srbs, drb_id of drbs, etc.) +template +uint8_t get_asn1_obj_id(const Asn1Obj& obj); + +/// Template function to generically set id of asn1 object (e.g. srb_id of srbs, drb_id of drbs, etc.) +template +void set_asn1_obj_id(Asn1Obj& obj, uint8_t id); + +/// helper macro to help define get_asn1_obj_id and set_asn1_obj_id for specific asn1 objects +#define ASN1_OBJ_ID_DEFINE(Asn1ObjType, member) \ + template <> \ + uint8_t get_asn1_obj_id(const Asn1ObjType& obj) \ + { \ + return obj.member; \ + } \ + template <> \ + void set_asn1_obj_id(Asn1ObjType & obj, uint8_t id) \ + { \ + obj.member = id; \ + } //! Functor to compare RRC config elements (e.g. SRB/measObj/Rep) based on ID struct rrc_obj_id_cmp { @@ -38,27 +57,27 @@ struct rrc_obj_id_cmp { typename std::enable_if::value and not std::is_integral::value, bool>::type operator()(const T& lhs, const U& rhs) const { - return asn1::rrc::get_rrc_obj_id(lhs) < asn1::rrc::get_rrc_obj_id(rhs); + return get_asn1_obj_id(lhs) < get_asn1_obj_id(rhs); } template - bool operator()(const T& lhs, rrc_obj_id_t id) const + bool operator()(const T& lhs, asn1_obj_id_t id) const { - return asn1::rrc::get_rrc_obj_id(lhs) < id; + return get_asn1_obj_id(lhs) < id; } template - bool operator()(rrc_obj_id_t id, const T& rhs) const + bool operator()(asn1_obj_id_t id, const T& rhs) const { - return id < asn1::rrc::get_rrc_obj_id(rhs); + return id < get_asn1_obj_id(rhs); } }; template struct unary_rrc_obj_id { - rrc_obj_id_t id; + asn1_obj_id_t id; template explicit unary_rrc_obj_id(T id_) : id(id_) {} - bool operator()(const typename Container::value_type& e) const { return asn1::rrc::get_rrc_obj_id(e) == id; } + bool operator()(const typename Container::value_type& e) const { return get_asn1_obj_id(e) == id; } }; /// Find rrc object in list based on ID @@ -78,13 +97,13 @@ template typename Container::iterator sorted_find_rrc_obj_id(Container& c, IdType id) { auto it = std::lower_bound(c.begin(), c.end(), id, rrc_obj_id_cmp{}); - return (it == c.end() or asn1::rrc::get_rrc_obj_id(*it) != id) ? c.end() : it; + return (it == c.end() or get_asn1_obj_id(*it) != id) ? c.end() : it; } template typename Container::const_iterator sorted_find_rrc_obj_id(const Container& c, IdType id) { auto it = std::lower_bound(c.begin(), c.end(), id, rrc_obj_id_cmp{}); - return (it == c.end() or asn1::rrc::get_rrc_obj_id(*it) != id) ? c.end() : it; + return (it == c.end() or get_asn1_obj_id(*it) != id) ? c.end() : it; } template @@ -95,7 +114,7 @@ bool equal_rrc_obj_ids(const Container& c, const Container2& c2) c2.begin(), c2.end(), [](const typename Container::value_type& e, const typename Container2::value_type& e2) { - return asn1::rrc::get_rrc_obj_id(e) == asn1::rrc::get_rrc_obj_id(e2); + return get_asn1_obj_id(e) == get_asn1_obj_id(e2); }); } @@ -107,7 +126,7 @@ typename Container::iterator add_rrc_obj_id(Container& c, IdType id) if (it == c.end()) { c.push_back({}); it = c.end() - 1; - asn1::rrc::set_rrc_obj_id(*it, id); + set_asn1_obj_id(*it, id); std::sort(c.begin(), c.end(), rrc_obj_id_cmp{}); it = sorted_find_rrc_obj_id(c, id); } @@ -117,11 +136,11 @@ typename Container::iterator add_rrc_obj_id(Container& c, IdType id) template typename Container::iterator add_rrc_obj(Container& c, const typename Container::value_type& v) { - auto it = sorted_find_rrc_obj_id(c, asn1::rrc::get_rrc_obj_id(v)); + auto it = sorted_find_rrc_obj_id(c, get_asn1_obj_id(v)); if (it == c.end()) { c.push_back(v); std::sort(c.begin(), c.end(), rrc_obj_id_cmp{}); - it = sorted_find_rrc_obj_id(c, asn1::rrc::get_rrc_obj_id(v)); + it = sorted_find_rrc_obj_id(c, get_asn1_obj_id(v)); } else { *it = v; } @@ -145,21 +164,21 @@ bool rem_rrc_obj_id(Container& c, IdType id) * @return id value */ template -auto find_rrc_obj_id_gap(const Container& c) -> decltype(asn1::rrc::get_rrc_obj_id(c[0])) +auto find_rrc_obj_id_gap(const Container& c) -> decltype(get_asn1_obj_id(c[0])) { auto id_cmp_op = rrc_obj_id_cmp{}; assert(std::is_sorted(c.begin(), c.end(), id_cmp_op)); auto prev_it = c.begin(); - if (prev_it != c.end() and asn1::rrc::get_rrc_obj_id(*prev_it) == 1) { + if (prev_it != c.end() and get_asn1_obj_id(*prev_it) == 1) { auto it = prev_it; for (++it; it != c.end(); prev_it = it, ++it) { - if (asn1::rrc::get_rrc_obj_id(*it) > asn1::rrc::get_rrc_obj_id(*prev_it) + 1) { + if (get_asn1_obj_id(*it) > get_asn1_obj_id(*prev_it) + 1) { break; } } } - return (prev_it == c.end()) ? 1 : asn1::rrc::get_rrc_obj_id(*prev_it) + 1; // starts at 1. + return (prev_it == c.end()) ? 1 : get_asn1_obj_id(*prev_it) + 1; // starts at 1. } /** @@ -316,7 +335,7 @@ void compute_cfg_diff(const toAddModList& src_list, } using it_t = typename toAddModList::const_iterator; - auto rem_func = [&rem_diff_list](it_t rem_it) { rem_diff_list.push_back(asn1::rrc::get_rrc_obj_id(*rem_it)); }; + auto rem_func = [&rem_diff_list](it_t rem_it) { rem_diff_list.push_back(get_asn1_obj_id(*rem_it)); }; auto add_func = [&add_diff_list](it_t add_it) { add_diff_list.push_back(*add_it); }; auto mod_func = [&add_diff_list](it_t src_it, it_t target_it) { if (not(*src_it == *target_it)) { @@ -328,4 +347,4 @@ void compute_cfg_diff(const toAddModList& src_list, } // namespace srsran -#endif // SRSRAN_RRC_CFG_UTILS_H +#endif // SRSRAN_OBJ_ID_CMP_UTILS_H diff --git a/lib/include/srsran/asn1/rrc.h b/lib/include/srsran/asn1/rrc.h index 9f6b8ca49..d2653ff8b 100644 --- a/lib/include/srsran/asn1/rrc.h +++ b/lib/include/srsran/asn1/rrc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -178,7 +178,7 @@ struct bcch_dl_sch_msg_mbms_s { }; // ThresholdEUTRA-v1250 ::= INTEGER (0..97) -using thres_eutra_v1250 = uint8_t; +using thres_eutra_v1250 = integer; // MBMS-SessionInfo-r9 ::= SEQUENCE struct mbms_session_info_r9_s { diff --git a/lib/include/srsran/asn1/rrc/bcch_msg.h b/lib/include/srsran/asn1/rrc/bcch_msg.h index 525ec40e6..244a1bdc1 100644 --- a/lib/include/srsran/asn1/rrc/bcch_msg.h +++ b/lib/include/srsran/asn1/rrc/bcch_msg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/common.h b/lib/include/srsran/asn1/rrc/common.h index c8dd034e7..44c5497f7 100644 --- a/lib/include/srsran/asn1/rrc/common.h +++ b/lib/include/srsran/asn1/rrc/common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/common_ext.h b/lib/include/srsran/asn1/rrc/common_ext.h index fb44490e1..ff2c39bc6 100644 --- a/lib/include/srsran/asn1/rrc/common_ext.h +++ b/lib/include/srsran/asn1/rrc/common_ext.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/dl_ccch_msg.h b/lib/include/srsran/asn1/rrc/dl_ccch_msg.h index 7ac072608..f104b2f01 100644 --- a/lib/include/srsran/asn1/rrc/dl_ccch_msg.h +++ b/lib/include/srsran/asn1/rrc/dl_ccch_msg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/dl_dcch_msg.h b/lib/include/srsran/asn1/rrc/dl_dcch_msg.h index fb29422de..44c546922 100644 --- a/lib/include/srsran/asn1/rrc/dl_dcch_msg.h +++ b/lib/include/srsran/asn1/rrc/dl_dcch_msg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/ho_cmd.h b/lib/include/srsran/asn1/rrc/ho_cmd.h index 8665b2e3d..2770f2134 100644 --- a/lib/include/srsran/asn1/rrc/ho_cmd.h +++ b/lib/include/srsran/asn1/rrc/ho_cmd.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/meascfg.h b/lib/include/srsran/asn1/rrc/meascfg.h index 50df4845b..e2832aa56 100644 --- a/lib/include/srsran/asn1/rrc/meascfg.h +++ b/lib/include/srsran/asn1/rrc/meascfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/paging.h b/lib/include/srsran/asn1/rrc/paging.h index f4ec707db..97ddf353f 100644 --- a/lib/include/srsran/asn1/rrc/paging.h +++ b/lib/include/srsran/asn1/rrc/paging.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/phy_ded.h b/lib/include/srsran/asn1/rrc/phy_ded.h index 1d99d1414..444748f16 100644 --- a/lib/include/srsran/asn1/rrc/phy_ded.h +++ b/lib/include/srsran/asn1/rrc/phy_ded.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/rr_common.h b/lib/include/srsran/asn1/rrc/rr_common.h index f7453e150..278f5686f 100644 --- a/lib/include/srsran/asn1/rrc/rr_common.h +++ b/lib/include/srsran/asn1/rrc/rr_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/rr_ded.h b/lib/include/srsran/asn1/rrc/rr_ded.h index 813931f03..a4cfde16f 100644 --- a/lib/include/srsran/asn1/rrc/rr_ded.h +++ b/lib/include/srsran/asn1/rrc/rr_ded.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/rrc_asn1.h b/lib/include/srsran/asn1/rrc/rrc_asn1.h index b27941679..11f7b02d4 100644 --- a/lib/include/srsran/asn1/rrc/rrc_asn1.h +++ b/lib/include/srsran/asn1/rrc/rrc_asn1.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/security.h b/lib/include/srsran/asn1/rrc/security.h index bc17acc5b..667771a26 100644 --- a/lib/include/srsran/asn1/rrc/security.h +++ b/lib/include/srsran/asn1/rrc/security.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/si.h b/lib/include/srsran/asn1/rrc/si.h index 832afc27b..9a77ce688 100644 --- a/lib/include/srsran/asn1/rrc/si.h +++ b/lib/include/srsran/asn1/rrc/si.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/uecap.h b/lib/include/srsran/asn1/rrc/uecap.h index ae9b8aecd..30d4985ff 100644 --- a/lib/include/srsran/asn1/rrc/uecap.h +++ b/lib/include/srsran/asn1/rrc/uecap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/ul_ccch_msg.h b/lib/include/srsran/asn1/rrc/ul_ccch_msg.h index bca30021e..9c823fe7d 100644 --- a/lib/include/srsran/asn1/rrc/ul_ccch_msg.h +++ b/lib/include/srsran/asn1/rrc/ul_ccch_msg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc/ul_dcch_msg.h b/lib/include/srsran/asn1/rrc/ul_dcch_msg.h index d1cdedd75..4a8c6dfb7 100644 --- a/lib/include/srsran/asn1/rrc/ul_dcch_msg.h +++ b/lib/include/srsran/asn1/rrc/ul_dcch_msg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc_nbiot.h b/lib/include/srsran/asn1/rrc_nbiot.h index c0be0ca0a..df74b9802 100644 --- a/lib/include/srsran/asn1/rrc_nbiot.h +++ b/lib/include/srsran/asn1/rrc_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/asn1/rrc_nr.h b/lib/include/srsran/asn1/rrc_nr.h index e39fb0b45..eba90e905 100644 --- a/lib/include/srsran/asn1/rrc_nr.h +++ b/lib/include/srsran/asn1/rrc_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -445,8 +445,7 @@ struct eutra_freq_neigh_cell_info_s { // EUTRA-MultiBandInfo ::= SEQUENCE struct eutra_multi_band_info_s { - bool eutra_ns_pmax_list_present = false; - uint16_t eutra_freq_band_ind = 1; + uint16_t eutra_freq_band_ind = 1; eutra_ns_pmax_list_l eutra_ns_pmax_list; // sequence methods @@ -517,7 +516,6 @@ struct inter_freq_neigh_cell_info_s { // NR-MultiBandInfo ::= SEQUENCE struct nr_multi_band_info_s { bool freq_band_ind_nr_present = false; - bool nr_ns_pmax_list_present = false; uint16_t freq_band_ind_nr = 1; nr_ns_pmax_list_l nr_ns_pmax_list; @@ -689,12 +687,10 @@ struct ctrl_res_set_s { using tci_states_pdcch_to_release_list_l_ = dyn_array; // member variables - bool ext = false; - bool tci_states_pdcch_to_add_list_present = false; - bool tci_states_pdcch_to_release_list_present = false; - bool tci_present_in_dci_present = false; - bool pdcch_dmrs_scrambling_id_present = false; - uint8_t ctrl_res_set_id = 0; + bool ext = false; + bool tci_present_in_dci_present = false; + bool pdcch_dmrs_scrambling_id_present = false; + uint8_t ctrl_res_set_id = 0; fixed_bitstring<45> freq_domain_res; uint8_t dur = 1; cce_reg_map_type_c_ cce_reg_map_type; @@ -1512,13 +1508,10 @@ struct carrier_freq_eutra_s { }; // member variables - bool eutra_multi_band_info_list_present = false; - bool eutra_freq_neigh_cell_list_present = false; - bool eutra_black_cell_list_present = false; - bool cell_resel_prio_present = false; - bool cell_resel_sub_prio_present = false; - bool thresh_x_q_present = false; - uint32_t carrier_freq = 0; + bool cell_resel_prio_present = false; + bool cell_resel_sub_prio_present = false; + bool thresh_x_q_present = false; + uint32_t carrier_freq = 0; eutra_multi_band_info_list_l eutra_multi_band_info_list; eutra_freq_neigh_cell_list_l eutra_freq_neigh_cell_list; eutra_freq_black_cell_list_l eutra_black_cell_list; @@ -1548,8 +1541,6 @@ struct inter_freq_carrier_freq_info_s { // member variables bool ext = false; - bool freq_band_list_present = false; - bool freq_band_list_sul_present = false; bool nrof_ss_blocks_to_average_present = false; bool abs_thresh_ss_blocks_consolidation_present = false; bool smtc_present = false; @@ -1563,8 +1554,6 @@ struct inter_freq_carrier_freq_info_s { bool cell_resel_prio_present = false; bool cell_resel_sub_prio_present = false; bool q_offset_freq_present = false; - bool inter_freq_neigh_cell_list_present = false; - bool inter_freq_black_cell_list_present = false; uint32_t dl_carrier_freq = 0; multi_freq_band_list_nr_sib_l freq_band_list; multi_freq_band_list_nr_sib_l freq_band_list_sul; @@ -1793,7 +1782,6 @@ struct pdcch_cfg_common_s { bool ctrl_res_set_zero_present = false; bool common_ctrl_res_set_present = false; bool search_space_zero_present = false; - bool common_search_space_list_present = false; bool search_space_sib1_present = false; bool search_space_other_sys_info_present = false; bool paging_search_space_present = false; @@ -1818,8 +1806,7 @@ struct pdcch_cfg_common_s { // PDSCH-ConfigCommon ::= SEQUENCE struct pdsch_cfg_common_s { - bool ext = false; - bool pdsch_time_domain_alloc_list_present = false; + bool ext = false; pdsch_time_domain_res_alloc_list_l pdsch_time_domain_alloc_list; // ... @@ -1871,7 +1858,6 @@ struct pucch_cfg_common_s { struct pusch_cfg_common_s { bool ext = false; bool group_hop_enabled_transform_precoding_present = false; - bool pusch_time_domain_alloc_list_present = false; bool msg3_delta_preamb_present = false; bool p0_nominal_with_grant_present = false; pusch_time_domain_res_alloc_list_l pusch_time_domain_alloc_list; @@ -2247,50 +2233,6 @@ struct sib_type_info_s { void to_json(json_writer& j) const; }; -// SetupRelease{ElementTypeParam} ::= CHOICE -template -struct setup_release_c { - struct types_opts { - enum options { release, setup, nulltype } value; - - const char* to_string() const - { - static const char* options[] = {"release", "setup"}; - return convert_enum_idx(options, 2, value, "setup_release_c::types"); - } - }; - typedef enumerated types; - - // choice methods - setup_release_c() = default; - void set(typename types::options e = types::nulltype); - types type() const { return type_; } - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - // getters - elem_type_paramT_& setup() - { - assert_choice_type(types::setup, type_, "SetupRelease"); - return c; - } - const elem_type_paramT_& setup() const - { - assert_choice_type(types::setup, type_, "SetupRelease"); - return c; - } - void set_release() { set(types::release); } - elem_type_paramT_& set_setup() - { - set(types::setup); - return c; - } - -private: - types type_; - elem_type_paramT_ c; -}; - // UAC-BarringPerCat ::= SEQUENCE struct uac_barr_per_cat_s { uint8_t access_category = 1; @@ -2382,7 +2324,6 @@ struct freq_info_ul_sib_s { // member variables bool ext = false; - bool freq_band_list_present = false; bool absolute_freq_point_a_present = false; bool p_max_present = false; bool freq_shift7p5khz_present = false; @@ -2943,22 +2884,20 @@ struct sib2_s { // ... }; struct intra_freq_cell_resel_info_s_ { - bool ext = false; - bool q_rx_lev_min_sul_present = false; - bool q_qual_min_present = false; - bool s_intra_search_q_present = false; - bool freq_band_list_present = false; - bool freq_band_list_sul_present = false; - bool p_max_present = false; - bool smtc_present = false; - bool ss_rssi_meas_present = false; - bool ssb_to_measure_present = false; - int8_t q_rx_lev_min = -70; - int8_t q_rx_lev_min_sul = -70; - int8_t q_qual_min = -43; - uint8_t s_intra_search_p = 0; - uint8_t s_intra_search_q = 0; - uint8_t t_resel_nr = 0; + bool ext = false; + bool q_rx_lev_min_sul_present = false; + bool q_qual_min_present = false; + bool s_intra_search_q_present = false; + bool p_max_present = false; + bool smtc_present = false; + bool ss_rssi_meas_present = false; + bool ssb_to_measure_present = false; + int8_t q_rx_lev_min = -70; + int8_t q_rx_lev_min_sul = -70; + int8_t q_qual_min = -43; + uint8_t s_intra_search_p = 0; + uint8_t s_intra_search_q = 0; + uint8_t t_resel_nr = 0; multi_freq_band_list_nr_sib_l freq_band_list; multi_freq_band_list_nr_sib_l freq_band_list_sul; int8_t p_max = -30; @@ -2991,10 +2930,7 @@ struct sib2_s { // SIB3 ::= SEQUENCE struct sib3_s { - bool ext = false; - bool intra_freq_neigh_cell_list_present = false; - bool intra_freq_black_cell_list_present = false; - bool late_non_crit_ext_present = false; + bool ext = false; intra_freq_neigh_cell_list_l intra_freq_neigh_cell_list; intra_freq_black_cell_list_l intra_freq_black_cell_list; dyn_octstring late_non_crit_ext; @@ -3008,8 +2944,7 @@ struct sib3_s { // SIB4 ::= SEQUENCE struct sib4_s { - bool ext = false; - bool late_non_crit_ext_present = false; + bool ext = false; inter_freq_carrier_freq_list_l inter_freq_carrier_freq_list; dyn_octstring late_non_crit_ext; // ... @@ -3022,10 +2957,8 @@ struct sib4_s { // SIB5 ::= SEQUENCE struct sib5_s { - bool ext = false; - bool carrier_freq_list_eutra_present = false; - bool t_resel_eutra_sf_present = false; - bool late_non_crit_ext_present = false; + bool ext = false; + bool t_resel_eutra_sf_present = false; carrier_freq_list_eutra_l carrier_freq_list_eutra; uint8_t t_resel_eutra = 0; speed_state_scale_factors_s t_resel_eutra_sf; @@ -3040,8 +2973,7 @@ struct sib5_s { // SIB6 ::= SEQUENCE struct sib6_s { - bool ext = false; - bool late_non_crit_ext_present = false; + bool ext = false; fixed_bitstring<16> msg_id; fixed_bitstring<16> serial_num; fixed_octstring<2> warning_type; @@ -3066,7 +2998,6 @@ struct sib7_s { // member variables bool ext = false; bool data_coding_scheme_present = false; - bool late_non_crit_ext_present = false; fixed_bitstring<16> msg_id; fixed_bitstring<16> serial_num; warning_msg_segment_type_e_ warning_msg_segment_type; @@ -3092,10 +3023,8 @@ struct sib8_s { typedef enumerated warning_msg_segment_type_e_; // member variables - bool ext = false; - bool data_coding_scheme_present = false; - bool warning_area_coordinates_segment_present = false; - bool late_non_crit_ext_present = false; + bool ext = false; + bool data_coding_scheme_present = false; fixed_bitstring<16> msg_id; fixed_bitstring<16> serial_num; warning_msg_segment_type_e_ warning_msg_segment_type; @@ -3125,9 +3054,8 @@ struct sib9_s { }; // member variables - bool ext = false; - bool time_info_present = false; - bool late_non_crit_ext_present = false; + bool ext = false; + bool time_info_present = false; time_info_s_ time_info; dyn_octstring late_non_crit_ext; // ... @@ -3528,8 +3456,7 @@ struct sys_info_ies_s { using sib_type_and_info_l_ = dyn_array; // member variables - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; sib_type_and_info_l_ sib_type_and_info; dyn_octstring late_non_crit_ext; @@ -3694,8 +3621,6 @@ struct sib1_s { }; // member variables - bool uac_barr_for_common_present = false; - bool uac_barr_per_plmn_list_present = false; bool uac_access_category1_sel_assist_info_present = false; uac_barr_per_cat_list_l uac_barr_for_common; uac_barr_per_plmn_list_l uac_barr_per_plmn_list; @@ -3713,7 +3638,6 @@ struct sib1_s { bool ue_timers_and_consts_present = false; bool uac_barr_info_present = false; bool use_full_resume_id_present = false; - bool late_non_crit_ext_present = false; bool non_crit_ext_present = false; cell_sel_info_s_ cell_sel_info; cell_access_related_info_s cell_access_related_info; @@ -4199,10 +4123,8 @@ struct sdap_cfg_s { using mapped_qos_flows_to_release_l_ = dyn_array; // member variables - bool ext = false; - bool mapped_qos_flows_to_add_present = false; - bool mapped_qos_flows_to_release_present = false; - uint16_t pdu_session = 0; + bool ext = false; + uint16_t pdu_session = 0; sdap_hdr_dl_e_ sdap_hdr_dl; sdap_hdr_ul_e_ sdap_hdr_ul; bool default_drb = false; @@ -4348,12 +4270,9 @@ struct security_cfg_s { // RadioBearerConfig ::= SEQUENCE struct radio_bearer_cfg_s { - bool ext = false; - bool srb_to_add_mod_list_present = false; - bool srb3_to_release_present = false; - bool drb_to_add_mod_list_present = false; - bool drb_to_release_list_present = false; - bool security_cfg_present = false; + bool ext = false; + bool srb3_to_release_present = false; + bool security_cfg_present = false; srb_to_add_mod_list_l srb_to_add_mod_list; drb_to_add_mod_list_l drb_to_add_mod_list; drb_to_release_list_l drb_to_release_list; @@ -4368,10 +4287,9 @@ struct radio_bearer_cfg_s { // RRCReject-IEs ::= SEQUENCE struct rrc_reject_ies_s { - bool wait_time_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; - uint8_t wait_time = 1; + bool wait_time_present = false; + bool non_crit_ext_present = false; + uint8_t wait_time = 1; dyn_octstring late_non_crit_ext; // sequence methods @@ -4382,8 +4300,7 @@ struct rrc_reject_ies_s { // RRCSetup-IEs ::= SEQUENCE struct rrc_setup_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; radio_bearer_cfg_s radio_bearer_cfg; dyn_octstring master_cell_group; dyn_octstring late_non_crit_ext; @@ -5515,7 +5432,6 @@ struct ran_area_cfg_s { using ran_area_code_list_l_ = bounded_array; // member variables - bool ran_area_code_list_present = false; fixed_bitstring<24> tac; ran_area_code_list_l_ ran_area_code_list; @@ -5608,7 +5524,6 @@ struct ssb_mtc2_s { typedef enumerated periodicity_e_; // member variables - bool pci_list_present = false; pci_list_l_ pci_list; periodicity_e_ periodicity; @@ -5685,13 +5600,9 @@ struct meas_obj_eutra_s { using black_cells_to_add_mod_list_eutran_l_ = dyn_array; // member variables - bool ext = false; - bool cells_to_rem_list_eutran_present = false; - bool cells_to_add_mod_list_eutran_present = false; - bool black_cells_to_rem_list_eutran_present = false; - bool black_cells_to_add_mod_list_eutran_present = false; - bool eutra_q_offset_range_present = false; - uint32_t carrier_freq = 0; + bool ext = false; + bool eutra_q_offset_range_present = false; + uint32_t carrier_freq = 0; eutra_allowed_meas_bw_e allowed_meas_bw; eutra_cell_idx_list_l cells_to_rem_list_eutran; cells_to_add_mod_list_eutran_l_ cells_to_add_mod_list_eutran; @@ -5732,12 +5643,6 @@ struct meas_obj_nr_s { bool abs_thresh_csi_rs_consolidation_present = false; bool nrof_ss_blocks_to_average_present = false; bool nrof_csi_rs_res_to_average_present = false; - bool cells_to_rem_list_present = false; - bool cells_to_add_mod_list_present = false; - bool black_cells_to_rem_list_present = false; - bool black_cells_to_add_mod_list_present = false; - bool white_cells_to_rem_list_present = false; - bool white_cells_to_add_mod_list_present = false; uint32_t ssb_freq = 0; subcarrier_spacing_e ssb_subcarrier_spacing; ssb_mtc_s smtc1; @@ -6223,7 +6128,6 @@ typedef enumerated rat_type_e; // RRCReconfiguration-v1560-IEs ::= SEQUENCE struct rrc_recfg_v1560_ies_s { bool mrdc_secondary_cell_group_cfg_present = false; - bool radio_bearer_cfg2_present = false; bool sk_counter_present = false; bool non_crit_ext_present = false; setup_release_c mrdc_secondary_cell_group_cfg; @@ -6333,7 +6237,6 @@ using freq_prio_list_nr_l = dyn_array; // MasterKeyUpdate ::= SEQUENCE struct master_key_upd_s { bool ext = false; - bool nas_container_present = false; bool key_set_change_ind = false; uint8_t next_hop_chaining_count = 0; dyn_octstring nas_container; @@ -6464,8 +6367,7 @@ struct quant_cfg_s { using quant_cfg_nr_list_l_ = dyn_array; // member variables - bool ext = false; - bool quant_cfg_nr_list_present = false; + bool ext = false; quant_cfg_nr_list_l_ quant_cfg_nr_list; // ... // group 0 @@ -6570,8 +6472,7 @@ using report_cfg_to_rem_list_l = dyn_array; // UE-CapabilityRAT-Request ::= SEQUENCE struct ue_cap_rat_request_s { - bool ext = false; - bool cap_request_filt_present = false; + bool ext = false; rat_type_e rat_type; dyn_octstring cap_request_filt; // ... @@ -6594,10 +6495,8 @@ struct cell_resel_priorities_s { typedef enumerated t320_e_; // member variables - bool ext = false; - bool freq_prio_list_eutra_present = false; - bool freq_prio_list_nr_present = false; - bool t320_present = false; + bool ext = false; + bool t320_present = false; freq_prio_list_eutra_l freq_prio_list_eutra; freq_prio_list_nr_l freq_prio_list_nr; t320_e_ t320; @@ -6664,17 +6563,11 @@ struct meas_cfg_s { }; // member variables - bool ext = false; - bool meas_obj_to_rem_list_present = false; - bool meas_obj_to_add_mod_list_present = false; - bool report_cfg_to_rem_list_present = false; - bool report_cfg_to_add_mod_list_present = false; - bool meas_id_to_rem_list_present = false; - bool meas_id_to_add_mod_list_present = false; - bool s_measure_cfg_present = false; - bool quant_cfg_present = false; - bool meas_gap_cfg_present = false; - bool meas_gap_sharing_cfg_present = false; + bool ext = false; + bool s_measure_cfg_present = false; + bool quant_cfg_present = false; + bool meas_gap_cfg_present = false; + bool meas_gap_sharing_cfg_present = false; meas_obj_to_rem_list_l meas_obj_to_rem_list; meas_obj_to_add_mod_list_l meas_obj_to_add_mod_list; report_cfg_to_rem_list_l report_cfg_to_rem_list; @@ -6698,14 +6591,10 @@ struct rrc_recfg_v1530_ies_s { using ded_nas_msg_list_l_ = bounded_array; // member variables - bool master_cell_group_present = false; - bool full_cfg_present = false; - bool ded_nas_msg_list_present = false; - bool master_key_upd_present = false; - bool ded_sib1_delivery_present = false; - bool ded_sys_info_delivery_present = false; - bool other_cfg_present = false; - bool non_crit_ext_present = false; + bool full_cfg_present = false; + bool master_key_upd_present = false; + bool other_cfg_present = false; + bool non_crit_ext_present = false; dyn_octstring master_cell_group; ded_nas_msg_list_l_ ded_nas_msg_list; master_key_upd_s master_key_upd; @@ -6734,9 +6623,8 @@ struct rrc_release_v1540_ies_s { // RRCResume-v1560-IEs ::= SEQUENCE struct rrc_resume_v1560_ies_s { - bool radio_bearer_cfg2_present = false; - bool sk_counter_present = false; - bool non_crit_ext_present = false; + bool sk_counter_present = false; + bool non_crit_ext_present = false; dyn_octstring radio_bearer_cfg2; uint32_t sk_counter = 0; @@ -6832,8 +6720,7 @@ using ue_cap_rat_request_list_l = dyn_array; // CounterCheck-IEs ::= SEQUENCE struct counter_check_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; drb_count_msb_info_list_l drb_count_msb_info_list; dyn_octstring late_non_crit_ext; @@ -6845,9 +6732,7 @@ struct counter_check_ies_s { // DLInformationTransfer-IEs ::= SEQUENCE struct dl_info_transfer_ies_s { - bool ded_nas_msg_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring ded_nas_msg; dyn_octstring late_non_crit_ext; @@ -6867,9 +6752,7 @@ struct mob_from_nr_cmd_ies_s { typedef enumerated target_rat_type_e_; // member variables - bool nas_security_param_from_nr_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; target_rat_type_e_ target_rat_type; dyn_octstring target_rat_msg_container; dyn_octstring nas_security_param_from_nr; @@ -6883,11 +6766,9 @@ struct mob_from_nr_cmd_ies_s { // RRCReconfiguration-IEs ::= SEQUENCE struct rrc_recfg_ies_s { - bool radio_bearer_cfg_present = false; - bool secondary_cell_group_present = false; - bool meas_cfg_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool radio_bearer_cfg_present = false; + bool meas_cfg_present = false; + bool non_crit_ext_present = false; radio_bearer_cfg_s radio_bearer_cfg; dyn_octstring secondary_cell_group; meas_cfg_s meas_cfg; @@ -6902,9 +6783,8 @@ struct rrc_recfg_ies_s { // RRCReestablishment-IEs ::= SEQUENCE struct rrc_reest_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; - uint8_t next_hop_chaining_count = 0; + bool non_crit_ext_present = false; + uint8_t next_hop_chaining_count = 0; dyn_octstring late_non_crit_ext; // sequence methods @@ -6941,7 +6821,6 @@ struct rrc_release_ies_s { bool cell_resel_priorities_present = false; bool suspend_cfg_present = false; bool depriorit_req_present = false; - bool late_non_crit_ext_present = false; bool non_crit_ext_present = false; redirected_carrier_info_c redirected_carrier_info; cell_resel_priorities_s cell_resel_priorities; @@ -6958,12 +6837,10 @@ struct rrc_release_ies_s { // RRCResume-IEs ::= SEQUENCE struct rrc_resume_ies_s { - bool radio_bearer_cfg_present = false; - bool master_cell_group_present = false; - bool meas_cfg_present = false; - bool full_cfg_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool radio_bearer_cfg_present = false; + bool meas_cfg_present = false; + bool full_cfg_present = false; + bool non_crit_ext_present = false; radio_bearer_cfg_s radio_bearer_cfg; dyn_octstring master_cell_group; meas_cfg_s meas_cfg; @@ -6978,8 +6855,7 @@ struct rrc_resume_ies_s { // SecurityModeCommand-IEs ::= SEQUENCE struct security_mode_cmd_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; security_cfg_smc_s security_cfg_smc; dyn_octstring late_non_crit_ext; @@ -6991,8 +6867,6 @@ struct security_mode_cmd_ies_s { // UECapabilityEnquiry-IEs ::= SEQUENCE struct ue_cap_enquiry_ies_s { - bool late_non_crit_ext_present = false; - bool ue_cap_enquiry_ext_present = false; ue_cap_rat_request_list_l ue_cap_rat_request_list; dyn_octstring late_non_crit_ext; dyn_octstring ue_cap_enquiry_ext; @@ -7693,9 +7567,7 @@ using paging_record_list_l = dyn_array; // Paging ::= SEQUENCE struct paging_s { - bool paging_record_list_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; paging_record_list_l paging_record_list; dyn_octstring late_non_crit_ext; @@ -8414,10 +8286,8 @@ struct cgi_info_nr_s { }; // member variables - bool ext = false; - bool plmn_id_info_list_present = false; - bool freq_band_list_present = false; - bool no_sib1_present = false; + bool ext = false; + bool no_sib1_present = false; plmn_id_info_list_l plmn_id_info_list; multi_freq_band_list_nr_l freq_band_list; no_sib1_s_ no_sib1; @@ -8470,17 +8340,14 @@ struct cgi_info_eutra_s { using cgi_info_epc_list_l_ = dyn_array; // member variables - bool cgi_info_epc_list_present = false; cell_access_related_info_eutra_epc_s cgi_info_epc_legacy; cgi_info_epc_list_l_ cgi_info_epc_list; }; using cgi_info_minus5_gc_l_ = dyn_array; // member variables - bool cgi_info_epc_present = false; - bool cgi_info_minus5_gc_present = false; - bool multi_band_info_list_present = false; - bool freq_band_ind_prio_present = false; + bool cgi_info_epc_present = false; + bool freq_band_ind_prio_present = false; cgi_info_epc_s_ cgi_info_epc; cgi_info_minus5_gc_l_ cgi_info_minus5_gc; uint16_t freq_band_ind = 1; @@ -8517,8 +8384,6 @@ struct meas_result_nr_s { meas_quant_results_s results_csi_rs_cell; }; struct rs_idx_results_s_ { - bool results_ssb_idxes_present = false; - bool results_csi_rs_idxes_present = false; results_per_ssb_idx_list_l results_ssb_idxes; results_per_csi_rs_idx_list_l results_csi_rs_idxes; }; @@ -8625,13 +8490,12 @@ struct meas_result2_eutra_s { // MeasResult2NR ::= SEQUENCE struct meas_result2_nr_s { - bool ext = false; - bool ssb_freq_present = false; - bool ref_freq_csi_rs_present = false; - bool meas_result_serving_cell_present = false; - bool meas_result_neigh_cell_list_nr_present = false; - uint32_t ssb_freq = 0; - uint32_t ref_freq_csi_rs = 0; + bool ext = false; + bool ssb_freq_present = false; + bool ref_freq_csi_rs_present = false; + bool meas_result_serving_cell_present = false; + uint32_t ssb_freq = 0; + uint32_t ref_freq_csi_rs = 0; meas_result_nr_s meas_result_serving_cell; meas_result_list_nr_l meas_result_neigh_cell_list_nr; // ... @@ -8977,9 +8841,7 @@ struct fail_report_scg_s { typedef enumerated fail_type_e_; // member variables - bool ext = false; - bool meas_result_freq_list_present = false; - bool meas_result_scg_fail_present = false; + bool ext = false; fail_type_e_ fail_type; meas_result_freq_list_l meas_result_freq_list; dyn_octstring meas_result_scg_fail; @@ -9013,9 +8875,7 @@ struct fail_report_scg_eutra_s { typedef enumerated fail_type_e_; // member variables - bool ext = false; - bool meas_result_freq_list_mrdc_present = false; - bool meas_result_scg_fail_mrdc_present = false; + bool ext = false; fail_type_e_ fail_type; meas_result_freq_list_fail_mrdc_l meas_result_freq_list_mrdc; dyn_octstring meas_result_scg_fail_mrdc; @@ -9136,8 +8996,7 @@ struct meas_results_s { // RRCReconfigurationComplete-v1530-IEs ::= SEQUENCE struct rrc_recfg_complete_v1530_ies_s { - bool ul_tx_direct_current_list_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; ul_tx_direct_current_list_l ul_tx_direct_current_list; rrc_recfg_complete_v1560_ies_s non_crit_ext; @@ -9211,8 +9070,7 @@ private: // SCGFailureInformation-v1590-IEs ::= SEQUENCE struct scg_fail_info_v1590_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring late_non_crit_ext; // sequence methods @@ -9223,8 +9081,7 @@ struct scg_fail_info_v1590_ies_s { // SCGFailureInformationEUTRA-v1590-IEs ::= SEQUENCE struct scg_fail_info_eutra_v1590_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring late_non_crit_ext; // sequence methods @@ -9250,8 +9107,7 @@ struct ueassist_info_v1540_ies_s { // CounterCheckResponse-IEs ::= SEQUENCE struct counter_check_resp_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; drb_count_info_list_l drb_count_info_list; dyn_octstring late_non_crit_ext; @@ -9264,7 +9120,6 @@ struct counter_check_resp_ies_s { // FailureInformation-IEs ::= SEQUENCE struct fail_info_ies_s { bool fail_info_rlc_bearer_present = false; - bool late_non_crit_ext_present = false; bool non_crit_ext_present = false; fail_info_rlc_bearer_s fail_info_rlc_bearer; dyn_octstring late_non_crit_ext; @@ -9277,8 +9132,7 @@ struct fail_info_ies_s { // LocationMeasurementIndication-IEs ::= SEQUENCE struct location_meas_ind_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; setup_release_c meas_ind; dyn_octstring late_non_crit_ext; @@ -9290,8 +9144,7 @@ struct location_meas_ind_ies_s { // MeasurementReport-IEs ::= SEQUENCE struct meas_report_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; meas_results_s meas_results; dyn_octstring late_non_crit_ext; @@ -9303,8 +9156,7 @@ struct meas_report_ies_s { // RRCReconfigurationComplete-IEs ::= SEQUENCE struct rrc_recfg_complete_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring late_non_crit_ext; rrc_recfg_complete_v1530_ies_s non_crit_ext; @@ -9316,8 +9168,7 @@ struct rrc_recfg_complete_ies_s { // RRCReestablishmentComplete-IEs ::= SEQUENCE struct rrc_reest_complete_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring late_non_crit_ext; // sequence methods @@ -9328,11 +9179,8 @@ struct rrc_reest_complete_ies_s { // RRCResumeComplete-IEs ::= SEQUENCE struct rrc_resume_complete_ies_s { - bool ded_nas_msg_present = false; - bool sel_plmn_id_present = false; - bool ul_tx_direct_current_list_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool sel_plmn_id_present = false; + bool non_crit_ext_present = false; dyn_octstring ded_nas_msg; uint8_t sel_plmn_id = 1; ul_tx_direct_current_list_l ul_tx_direct_current_list; @@ -9405,9 +9253,7 @@ struct rrc_setup_complete_ies_s { // member variables bool registered_amf_present = false; bool guami_type_present = false; - bool s_nssai_list_present = false; bool ng_minus5_g_s_tmsi_value_present = false; - bool late_non_crit_ext_present = false; bool non_crit_ext_present = false; uint8_t sel_plmn_id = 1; registered_amf_s registered_amf; @@ -9451,8 +9297,7 @@ struct scg_fail_info_eutra_ies_s { // SecurityModeComplete-IEs ::= SEQUENCE struct security_mode_complete_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring late_non_crit_ext; // sequence methods @@ -9463,8 +9308,7 @@ struct security_mode_complete_ies_s { // SecurityModeFailure-IEs ::= SEQUENCE struct security_mode_fail_ies_s { - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring late_non_crit_ext; // sequence methods @@ -9476,7 +9320,6 @@ struct security_mode_fail_ies_s { // UEAssistanceInformation-IEs ::= SEQUENCE struct ueassist_info_ies_s { bool delay_budget_report_present = false; - bool late_non_crit_ext_present = false; bool non_crit_ext_present = false; delay_budget_report_c delay_budget_report; dyn_octstring late_non_crit_ext; @@ -9491,7 +9334,6 @@ struct ueassist_info_ies_s { // UECapabilityInformation-IEs ::= SEQUENCE struct ue_cap_info_ies_s { bool ue_cap_rat_container_list_present = false; - bool late_non_crit_ext_present = false; bool non_crit_ext_present = false; ue_cap_rat_container_list_l ue_cap_rat_container_list; dyn_octstring late_non_crit_ext; @@ -9504,9 +9346,7 @@ struct ue_cap_info_ies_s { // ULInformationTransfer-IEs ::= SEQUENCE struct ul_info_transfer_ies_s { - bool ded_nas_msg_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring ded_nas_msg; dyn_octstring late_non_crit_ext; @@ -9518,10 +9358,7 @@ struct ul_info_transfer_ies_s { // ULInformationTransferMRDC-IEs ::= SEQUENCE struct ul_info_transfer_mrdc_ies_s { - bool ul_dcch_msg_nr_present = false; - bool ul_dcch_msg_eutra_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; dyn_octstring ul_dcch_msg_nr; dyn_octstring ul_dcch_msg_eutra; dyn_octstring late_non_crit_ext; @@ -10585,10 +10422,9 @@ struct bfr_csirs_res_s { using ra_occasion_list_l_ = dyn_array; // member variables - bool ext = false; - bool ra_occasion_list_present = false; - bool ra_preamb_idx_present = false; - uint8_t csi_rs = 0; + bool ext = false; + bool ra_preamb_idx_present = false; + uint8_t csi_rs = 0; ra_occasion_list_l_ ra_occasion_list; uint8_t ra_preamb_idx = 0; // ... @@ -11601,15 +11437,11 @@ struct pdcch_cfg_s { using search_spaces_to_release_list_l_ = bounded_array; // member variables - bool ext = false; - bool ctrl_res_set_to_add_mod_list_present = false; - bool ctrl_res_set_to_release_list_present = false; - bool search_spaces_to_add_mod_list_present = false; - bool search_spaces_to_release_list_present = false; - bool dl_preemption_present = false; - bool tpc_pusch_present = false; - bool tpc_pucch_present = false; - bool tpc_srs_present = false; + bool ext = false; + bool dl_preemption_present = false; + bool tpc_pusch_present = false; + bool tpc_pucch_present = false; + bool tpc_srs_present = false; ctrl_res_set_to_add_mod_list_l_ ctrl_res_set_to_add_mod_list; ctrl_res_set_to_release_list_l_ ctrl_res_set_to_release_list; search_spaces_to_add_mod_list_l_ search_spaces_to_add_mod_list; @@ -11772,35 +11604,23 @@ struct pdsch_cfg_s { using sp_zp_csi_rs_res_sets_to_release_list_l_ = bounded_array; // member variables - bool ext = false; - bool data_scrambling_id_pdsch_present = false; - bool dmrs_dl_for_pdsch_map_type_a_present = false; - bool dmrs_dl_for_pdsch_map_type_b_present = false; - bool tci_states_to_add_mod_list_present = false; - bool tci_states_to_release_list_present = false; - bool vrb_to_prb_interleaver_present = false; - bool pdsch_time_domain_alloc_list_present = false; - bool pdsch_aggregation_factor_present = false; - bool rate_match_pattern_to_add_mod_list_present = false; - bool rate_match_pattern_to_release_list_present = false; - bool rate_match_pattern_group1_present = false; - bool rate_match_pattern_group2_present = false; - bool mcs_table_present = false; - bool max_nrof_code_words_sched_by_dci_present = false; - bool zp_csi_rs_res_to_add_mod_list_present = false; - bool zp_csi_rs_res_to_release_list_present = false; - bool aperiodic_zp_csi_rs_res_sets_to_add_mod_list_present = false; - bool aperiodic_zp_csi_rs_res_sets_to_release_list_present = false; - bool sp_zp_csi_rs_res_sets_to_add_mod_list_present = false; - bool sp_zp_csi_rs_res_sets_to_release_list_present = false; - bool p_zp_csi_rs_res_set_present = false; - uint16_t data_scrambling_id_pdsch = 0; - setup_release_c dmrs_dl_for_pdsch_map_type_a; - setup_release_c dmrs_dl_for_pdsch_map_type_b; - tci_states_to_add_mod_list_l_ tci_states_to_add_mod_list; - tci_states_to_release_list_l_ tci_states_to_release_list; - vrb_to_prb_interleaver_e_ vrb_to_prb_interleaver; - res_alloc_e_ res_alloc; + bool ext = false; + bool data_scrambling_id_pdsch_present = false; + bool dmrs_dl_for_pdsch_map_type_a_present = false; + bool dmrs_dl_for_pdsch_map_type_b_present = false; + bool vrb_to_prb_interleaver_present = false; + bool pdsch_time_domain_alloc_list_present = false; + bool pdsch_aggregation_factor_present = false; + bool mcs_table_present = false; + bool max_nrof_code_words_sched_by_dci_present = false; + bool p_zp_csi_rs_res_set_present = false; + uint16_t data_scrambling_id_pdsch = 0; + setup_release_c dmrs_dl_for_pdsch_map_type_a; + setup_release_c dmrs_dl_for_pdsch_map_type_b; + tci_states_to_add_mod_list_l_ tci_states_to_add_mod_list; + tci_states_to_release_list_l_ tci_states_to_release_list; + vrb_to_prb_interleaver_e_ vrb_to_prb_interleaver; + res_alloc_e_ res_alloc; setup_release_c > pdsch_time_domain_alloc_list; pdsch_aggregation_factor_e_ pdsch_aggregation_factor; rate_match_pattern_to_add_mod_list_l_ rate_match_pattern_to_add_mod_list; @@ -11848,11 +11668,9 @@ struct radio_link_monitoring_cfg_s { typedef enumerated beam_fail_detection_timer_e_; // member variables - bool ext = false; - bool fail_detection_res_to_add_mod_list_present = false; - bool fail_detection_res_to_release_list_present = false; - bool beam_fail_instance_max_count_present = false; - bool beam_fail_detection_timer_present = false; + bool ext = false; + bool beam_fail_instance_max_count_present = false; + bool beam_fail_detection_timer_present = false; fail_detection_res_to_add_mod_list_l_ fail_detection_res_to_add_mod_list; fail_detection_res_to_release_list_l_ fail_detection_res_to_release_list; beam_fail_instance_max_count_e_ beam_fail_instance_max_count; @@ -12793,8 +12611,6 @@ struct pucch_pwr_ctrl_s { bool delta_f_pucch_f2_present = false; bool delta_f_pucch_f3_present = false; bool delta_f_pucch_f4_present = false; - bool p0_set_present = false; - bool pathloss_ref_rss_present = false; bool two_pucch_pc_adjustment_states_present = false; int8_t delta_f_pucch_f0 = -16; int8_t delta_f_pucch_f1 = -16; @@ -13025,16 +12841,11 @@ struct pusch_pwr_ctrl_s { using sri_pusch_map_to_release_list_l_ = bounded_array; // member variables - bool tpc_accumulation_present = false; - bool msg3_alpha_present = false; - bool p0_nominal_without_grant_present = false; - bool p0_alpha_sets_present = false; - bool pathloss_ref_rs_to_add_mod_list_present = false; - bool pathloss_ref_rs_to_release_list_present = false; - bool two_pusch_pc_adjustment_states_present = false; - bool delta_mcs_present = false; - bool sri_pusch_map_to_add_mod_list_present = false; - bool sri_pusch_map_to_release_list_present = false; + bool tpc_accumulation_present = false; + bool msg3_alpha_present = false; + bool p0_nominal_without_grant_present = false; + bool two_pusch_pc_adjustment_states_present = false; + bool delta_mcs_present = false; alpha_e msg3_alpha; int16_t p0_nominal_without_grant = -202; p0_alpha_sets_l_ p0_alpha_sets; @@ -13447,7 +13258,6 @@ struct srs_res_set_s { // member variables bool ext = false; - bool srs_res_id_list_present = false; bool alpha_present = false; bool p0_present = false; bool pathloss_ref_rs_present = false; @@ -13758,7 +13568,6 @@ struct beam_fail_recovery_cfg_s { bool root_seq_idx_bfr_present = false; bool rach_cfg_bfr_present = false; bool rsrp_thres_ssb_present = false; - bool candidate_beam_rs_list_present = false; bool ssb_per_rach_occasion_present = false; bool ra_ssb_occasion_mask_idx_present = false; bool recovery_search_space_id_present = false; @@ -13965,22 +13774,12 @@ struct pucch_cfg_s { using spatial_relation_info_to_release_list_l_ = bounded_array; // member variables - bool ext = false; - bool res_set_to_add_mod_list_present = false; - bool res_set_to_release_list_present = false; - bool res_to_add_mod_list_present = false; - bool res_to_release_list_present = false; - bool format1_present = false; - bool format2_present = false; - bool format3_present = false; - bool format4_present = false; - bool sched_request_res_to_add_mod_list_present = false; - bool sched_request_res_to_release_list_present = false; - bool multi_csi_pucch_res_list_present = false; - bool dl_data_to_ul_ack_present = false; - bool spatial_relation_info_to_add_mod_list_present = false; - bool spatial_relation_info_to_release_list_present = false; - bool pucch_pwr_ctrl_present = false; + bool ext = false; + bool format1_present = false; + bool format2_present = false; + bool format3_present = false; + bool format4_present = false; + bool pucch_pwr_ctrl_present = false; res_set_to_add_mod_list_l_ res_set_to_add_mod_list; res_set_to_release_list_l_ res_set_to_release_list; res_to_add_mod_list_l_ res_to_add_mod_list; @@ -14072,7 +13871,6 @@ struct pusch_cfg_s { bool dmrs_ul_for_pusch_map_type_b_present = false; bool pusch_pwr_ctrl_present = false; bool freq_hop_present = false; - bool freq_hop_offset_lists_present = false; bool pusch_time_domain_alloc_list_present = false; bool pusch_aggregation_factor_present = false; bool mcs_table_present = false; @@ -14115,12 +13913,8 @@ struct srs_cfg_s { using srs_res_to_add_mod_list_l_ = dyn_array; // member variables - bool ext = false; - bool srs_res_set_to_release_list_present = false; - bool srs_res_set_to_add_mod_list_present = false; - bool srs_res_to_release_list_present = false; - bool srs_res_to_add_mod_list_present = false; - bool tpc_accumulation_present = false; + bool ext = false; + bool tpc_accumulation_present = false; srs_res_set_to_release_list_l_ srs_res_set_to_release_list; srs_res_set_to_add_mod_list_l_ srs_res_set_to_add_mod_list; srs_res_to_release_list_l_ srs_res_to_release_list; @@ -15854,8 +15648,7 @@ struct csi_associated_report_cfg_info_s { using qcl_info_l_ = bounded_array; // member variables - bool qcl_info_present = false; - uint8_t res_set = 1; + uint8_t res_set = 1; qcl_info_l_ qcl_info; }; struct types_opts { @@ -17652,7 +17445,6 @@ struct csi_report_cfg_s { bool codebook_cfg_present = false; bool dummy_present = false; bool cqi_table_present = false; - bool non_pmi_port_ind_present = false; uint8_t report_cfg_id = 0; uint8_t carrier = 0; uint8_t res_for_ch_meas = 0; @@ -17687,8 +17479,7 @@ struct csi_res_cfg_s { using csi_ssb_res_set_list_l_ = std::array; // member variables - bool nzp_csi_rs_res_set_list_present = false; - bool csi_ssb_res_set_list_present = false; + bool csi_ssb_res_set_list_present = false; nzp_csi_rs_res_set_list_l_ nzp_csi_rs_res_set_list; csi_ssb_res_set_list_l_ csi_ssb_res_set_list; }; @@ -17857,20 +17648,6 @@ struct csi_meas_cfg_s { // member variables bool ext = false; - bool nzp_csi_rs_res_to_add_mod_list_present = false; - bool nzp_csi_rs_res_to_release_list_present = false; - bool nzp_csi_rs_res_set_to_add_mod_list_present = false; - bool nzp_csi_rs_res_set_to_release_list_present = false; - bool csi_im_res_to_add_mod_list_present = false; - bool csi_im_res_to_release_list_present = false; - bool csi_im_res_set_to_add_mod_list_present = false; - bool csi_im_res_set_to_release_list_present = false; - bool csi_ssb_res_set_to_add_mod_list_present = false; - bool csi_ssb_res_set_to_release_list_present = false; - bool csi_res_cfg_to_add_mod_list_present = false; - bool csi_res_cfg_to_release_list_present = false; - bool csi_report_cfg_to_add_mod_list_present = false; - bool csi_report_cfg_to_release_list_present = false; bool report_trigger_size_present = false; bool aperiodic_trigger_state_list_present = false; bool semi_persistent_on_pusch_trigger_state_list_present = false; @@ -18081,7 +17858,6 @@ struct freq_info_ul_s { // member variables bool ext = false; - bool freq_band_list_present = false; bool absolute_freq_point_a_present = false; bool add_spec_emission_present = false; bool p_max_present = false; @@ -18126,7 +17902,6 @@ struct srs_tpc_pdcch_cfg_s { using srs_cc_set_idxlist_l_ = dyn_array; // member variables - bool srs_cc_set_idxlist_present = false; srs_cc_set_idxlist_l_ srs_cc_set_idxlist; // sequence methods @@ -18140,11 +17915,10 @@ struct slot_format_combinations_per_cell_s { using slot_format_combinations_l_ = dyn_array; // member variables - bool ext = false; - bool subcarrier_spacing2_present = false; - bool slot_format_combinations_present = false; - bool position_in_dci_present = false; - uint8_t serving_cell_id = 0; + bool ext = false; + bool subcarrier_spacing2_present = false; + bool position_in_dci_present = false; + uint8_t serving_cell_id = 0; subcarrier_spacing_e subcarrier_spacing; subcarrier_spacing_e subcarrier_spacing2; slot_format_combinations_l_ slot_format_combinations; @@ -18374,8 +18148,7 @@ struct rate_match_pattern_lte_crs_s { typedef enumerated v_shift_e_; // member variables - bool mbsfn_sf_cfg_list_present = false; - uint16_t carrier_freq_dl = 0; + uint16_t carrier_freq_dl = 0; carrier_bw_dl_e_ carrier_bw_dl; eutra_mbsfn_sf_cfg_list_l mbsfn_sf_cfg_list; nrof_crs_ports_e_ nrof_crs_ports; @@ -18470,7 +18243,6 @@ struct srs_carrier_switching_s { bool ext = false; bool srs_switch_from_serv_cell_idx_present = false; bool srs_tpc_pdcch_group_present = false; - bool monitoring_cells_present = false; uint8_t srs_switch_from_serv_cell_idx = 0; srs_switch_from_carrier_e_ srs_switch_from_carrier; srs_tpc_pdcch_group_c_ srs_tpc_pdcch_group; @@ -18489,11 +18261,9 @@ struct slot_format_ind_s { using slot_format_comb_to_release_list_l_ = bounded_array; // member variables - bool ext = false; - bool slot_format_comb_to_add_mod_list_present = false; - bool slot_format_comb_to_release_list_present = false; - uint32_t sfi_rnti = 0; - uint8_t dci_payload_size = 1; + bool ext = false; + uint32_t sfi_rnti = 0; + uint8_t dci_payload_size = 1; slot_format_comb_to_add_mod_list_l_ slot_format_comb_to_add_mod_list; slot_format_comb_to_release_list_l_ slot_format_comb_to_release_list; // ... @@ -19051,20 +18821,18 @@ struct serving_cell_cfg_common_s { using rate_match_pattern_to_release_list_l_ = bounded_array; // member variables - bool ext = false; - bool pci_present = false; - bool dl_cfg_common_present = false; - bool ul_cfg_common_present = false; - bool supplementary_ul_cfg_present = false; - bool n_timing_advance_offset_present = false; - bool ssb_positions_in_burst_present = false; - bool ssb_periodicity_serving_cell_present = false; - bool lte_crs_to_match_around_present = false; - bool rate_match_pattern_to_add_mod_list_present = false; - bool rate_match_pattern_to_release_list_present = false; - bool ssb_subcarrier_spacing_present = false; - bool tdd_ul_dl_cfg_common_present = false; - uint16_t pci = 0; + bool ext = false; + bool pci_present = false; + bool dl_cfg_common_present = false; + bool ul_cfg_common_present = false; + bool supplementary_ul_cfg_present = false; + bool n_timing_advance_offset_present = false; + bool ssb_positions_in_burst_present = false; + bool ssb_periodicity_serving_cell_present = false; + bool lte_crs_to_match_around_present = false; + bool ssb_subcarrier_spacing_present = false; + bool tdd_ul_dl_cfg_common_present = false; + uint16_t pci = 0; dl_cfg_common_s dl_cfg_common; ul_cfg_common_s ul_cfg_common; ul_cfg_common_s supplementary_ul_cfg; @@ -19105,9 +18873,7 @@ struct tdd_ul_dl_cfg_ded_s { using slot_specific_cfgs_to_release_list_l_ = dyn_array; // member variables - bool ext = false; - bool slot_specific_cfgs_to_add_mod_list_present = false; - bool slot_specific_cfgs_to_release_list_present = false; + bool ext = false; slot_specific_cfgs_to_add_mod_list_l_ slot_specific_cfgs_to_add_mod_list; slot_specific_cfgs_to_release_list_l_ slot_specific_cfgs_to_release_list; // ... @@ -19163,8 +18929,6 @@ struct ul_cfg_s { // member variables bool ext = false; bool init_ul_bwp_present = false; - bool ul_bwp_to_release_list_present = false; - bool ul_bwp_to_add_mod_list_present = false; bool first_active_ul_bwp_id_present = false; bool pusch_serving_cell_cfg_present = false; bool carrier_switching_present = false; @@ -19903,8 +19667,6 @@ struct lc_ch_cfg_s { // member variables bool ext = false; - bool allowed_serving_cells_present = false; - bool allowed_scs_list_present = false; bool max_pusch_dur_present = false; bool cfgured_grant_type1_allowed_present = false; bool lc_ch_group_present = false; @@ -20210,8 +19972,6 @@ struct sched_request_cfg_s { using sched_request_to_release_list_l_ = bounded_array; // member variables - bool sched_request_to_add_mod_list_present = false; - bool sched_request_to_release_list_present = false; sched_request_to_add_mod_list_l_ sched_request_to_add_mod_list; sched_request_to_release_list_l_ sched_request_to_release_list; @@ -20306,8 +20066,6 @@ struct serving_cell_cfg_s { bool ext = false; bool tdd_ul_dl_cfg_ded_present = false; bool init_dl_bwp_present = false; - bool dl_bwp_to_release_list_present = false; - bool dl_bwp_to_add_mod_list_present = false; bool first_active_dl_bwp_id_present = false; bool bwp_inactivity_timer_present = false; bool default_dl_bwp_id_present = false; @@ -20357,8 +20115,6 @@ struct tag_cfg_s { using tag_to_add_mod_list_l_ = dyn_array; // member variables - bool tag_to_release_list_present = false; - bool tag_to_add_mod_list_present = false; tag_to_release_list_l_ tag_to_release_list; tag_to_add_mod_list_l_ tag_to_add_mod_list; @@ -20561,15 +20317,11 @@ struct cell_group_cfg_s { using scell_to_release_list_l_ = bounded_array; // member variables - bool ext = false; - bool rlc_bearer_to_add_mod_list_present = false; - bool rlc_bearer_to_release_list_present = false; - bool mac_cell_group_cfg_present = false; - bool phys_cell_group_cfg_present = false; - bool sp_cell_cfg_present = false; - bool scell_to_add_mod_list_present = false; - bool scell_to_release_list_present = false; - uint8_t cell_group_id = 0; + bool ext = false; + bool mac_cell_group_cfg_present = false; + bool phys_cell_group_cfg_present = false; + bool sp_cell_cfg_present = false; + uint8_t cell_group_id = 0; rlc_bearer_to_add_mod_list_l_ rlc_bearer_to_add_mod_list; rlc_bearer_to_release_list_l_ rlc_bearer_to_release_list; mac_cell_group_cfg_s mac_cell_group_cfg; @@ -21102,10 +20854,6 @@ struct feature_set_dl_s { bool time_dur_for_qcl_present = false; bool pdsch_processing_type1_different_tb_per_slot_present = false; bool dummy3_present = false; - bool dummy4_present = false; - bool dummy5_present = false; - bool dummy6_present = false; - bool dummy7_present = false; feature_set_list_per_dl_cc_l_ feature_set_list_per_dl_cc; freq_separation_class_e intra_band_freq_separation_dl; scaling_factor_e_ scaling_factor; @@ -21541,11 +21289,7 @@ struct feature_sets_s { using feature_sets_dl_v15a0_l_ = dyn_array; // member variables - bool ext = false; - bool feature_sets_dl_present = false; - bool feature_sets_dl_per_cc_present = false; - bool feature_sets_ul_present = false; - bool feature_sets_ul_per_cc_present = false; + bool ext = false; feature_sets_dl_l_ feature_sets_dl; feature_sets_dl_per_cc_l_ feature_sets_dl_per_cc; feature_sets_ul_l_ feature_sets_ul; @@ -22022,7 +21766,6 @@ struct nrdc_params_s { bool tdd_add_ue_nrdc_cap_present = false; bool fr1_add_ue_nrdc_cap_present = false; bool fr2_add_ue_nrdc_cap_present = false; - bool late_non_crit_ext_present = false; bool dummy_present = false; meas_and_mob_params_mrdc_s meas_and_mob_params_nrdc; general_params_mrdc_xdd_diff_s general_params_nrdc; @@ -22410,8 +22153,7 @@ struct phy_params_mrdc_s { using naics_cap_list_l_ = dyn_array; // member variables - bool ext = false; - bool naics_cap_list_present = false; + bool ext = false; naics_cap_list_l_ naics_cap_list; // ... // group 0 @@ -22428,9 +22170,7 @@ struct rf_params_s { using supported_band_list_nr_l_ = dyn_array; // member variables - bool ext = false; - bool supported_band_combination_list_present = false; - bool applied_freq_band_list_filt_present = false; + bool ext = false; supported_band_list_nr_l_ supported_band_list_nr; band_combination_list_l supported_band_combination_list; freq_band_list_l applied_freq_band_list_filt; @@ -22452,11 +22192,6 @@ struct rf_params_s { // RF-ParametersMRDC ::= SEQUENCE struct rf_params_mrdc_s { struct supported_band_combination_list_nedc_only_v15a0_s_ { - bool supported_band_combination_list_v1540_present = false; - bool supported_band_combination_list_v1560_present = false; - bool supported_band_combination_list_v1570_present = false; - bool supported_band_combination_list_v1580_present = false; - bool supported_band_combination_list_v1590_present = false; band_combination_list_v1540_l supported_band_combination_list_v1540; band_combination_list_v1560_l supported_band_combination_list_v1560; band_combination_list_v1570_l supported_band_combination_list_v1570; @@ -22465,9 +22200,7 @@ struct rf_params_mrdc_s { }; // member variables - bool ext = false; - bool supported_band_combination_list_present = false; - bool applied_freq_band_list_filt_present = false; + bool ext = false; band_combination_list_l supported_band_combination_list; freq_band_list_l applied_freq_band_list_filt; // ... @@ -22507,8 +22240,7 @@ struct ue_cap_request_filt_nr_v1540_s { // UE-CapabilityRequestFilterNR ::= SEQUENCE struct ue_cap_request_filt_nr_s { - bool freq_band_list_filt_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; freq_band_list_l freq_band_list_filt; ue_cap_request_filt_nr_v1540_s non_crit_ext; @@ -22542,7 +22274,6 @@ struct pdcp_params_mrdc_s { // UE-MRDC-Capability-v1560 ::= SEQUENCE struct ue_mrdc_cap_v1560_s { - bool rx_filts_present = false; bool meas_and_mob_params_mrdc_v1560_present = false; bool fdd_add_ue_mrdc_cap_v1560_present = false; bool tdd_add_ue_mrdc_cap_v1560_present = false; @@ -22570,9 +22301,7 @@ struct ue_mrdc_cap_s { bool tdd_add_ue_mrdc_cap_present = false; bool fr1_add_ue_mrdc_cap_present = false; bool fr2_add_ue_mrdc_cap_present = false; - bool feature_set_combinations_present = false; bool pdcp_params_mrdc_v1530_present = false; - bool late_non_crit_ext_present = false; bool non_crit_ext_present = false; meas_and_mob_params_mrdc_s meas_and_mob_params_mrdc; phy_params_mrdc_s phy_params_mrdc_v1530; @@ -22618,7 +22347,6 @@ struct ue_nr_cap_v1570_s { // UE-NR-Capability-v1560 ::= SEQUENCE struct ue_nr_cap_v1560_s { bool nrdc_params_present = false; - bool rx_filts_present = false; bool non_crit_ext_present = false; nrdc_params_s nrdc_params; dyn_octstring rx_filts; @@ -22833,17 +22561,15 @@ struct ue_nr_cap_s { using feature_set_combinations_l_ = dyn_array; // member variables - bool rlc_params_present = false; - bool mac_params_present = false; - bool meas_and_mob_params_present = false; - bool fdd_add_ue_nr_cap_present = false; - bool tdd_add_ue_nr_cap_present = false; - bool fr1_add_ue_nr_cap_present = false; - bool fr2_add_ue_nr_cap_present = false; - bool feature_sets_present = false; - bool feature_set_combinations_present = false; - bool late_non_crit_ext_present = false; - bool non_crit_ext_present = false; + bool rlc_params_present = false; + bool mac_params_present = false; + bool meas_and_mob_params_present = false; + bool fdd_add_ue_nr_cap_present = false; + bool tdd_add_ue_nr_cap_present = false; + bool fr1_add_ue_nr_cap_present = false; + bool fr2_add_ue_nr_cap_present = false; + bool feature_sets_present = false; + bool non_crit_ext_present = false; access_stratum_release_e access_stratum_release; pdcp_params_s pdcp_params; rlc_params_s rlc_params; @@ -22904,9 +22630,6 @@ struct as_cfg_s { dyn_octstring rrc_recfg; // ... // group 0 - bool source_rb_sn_cfg_present = false; - bool source_scg_nr_cfg_present = false; - bool source_scg_eutra_cfg_present = false; dyn_octstring source_rb_sn_cfg; dyn_octstring source_scg_nr_cfg; dyn_octstring source_scg_eutra_cfg; @@ -22983,7 +22706,6 @@ struct cfg_restrict_info_scg_s { // member variables bool ext = false; - bool allowed_bc_list_mrdc_present = false; bool pwr_coordination_fr1_present = false; bool serv_cell_idx_range_scg_present = false; bool max_meas_freqs_scg_present = false; @@ -23014,8 +22736,7 @@ struct cfg_restrict_info_scg_s { // ReestablishmentInfo ::= SEQUENCE struct reest_info_s { - bool add_reestab_info_list_present = false; - uint16_t source_pci = 0; + uint16_t source_pci = 0; fixed_bitstring<16> target_cell_short_mac_i; reestab_ncell_info_list_l add_reestab_info_list; @@ -23036,7 +22757,6 @@ struct as_context_s { // group 0 copy_ptr ran_notif_area_info; // group 1 - bool ue_assist_info_present = false; dyn_octstring ue_assist_info; // group 2 copy_ptr sel_band_combination_sn; @@ -23077,7 +22797,6 @@ struct affected_carrier_freq_comb_info_mrdc_s { }; typedef enumerated interference_direction_mrdc_e_; struct affected_carrier_freq_comb_mrdc_s_ { - bool affected_carrier_freq_comb_eutra_present = false; affected_carrier_freq_comb_eutra_l affected_carrier_freq_comb_eutra; affected_carrier_freq_comb_nr_l affected_carrier_freq_comb_nr; }; @@ -23122,9 +22841,7 @@ struct cg_cfg_v1590_ies_s { using scell_frequencies_sn_eutra_l_ = bounded_array; // member variables - bool scell_frequencies_sn_nr_present = false; - bool scell_frequencies_sn_eutra_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; scell_frequencies_sn_nr_l_ scell_frequencies_sn_nr; scell_frequencies_sn_eutra_l_ scell_frequencies_sn_eutra; @@ -23166,15 +22883,12 @@ struct cg_cfg_v1560_ies_s { }; // member variables - bool pscell_freq_eutra_present = false; - bool scg_cell_group_cfg_eutra_present = false; - bool candidate_cell_info_list_sn_eutra_present = false; - bool candidate_serving_freq_list_eutra_present = false; - bool need_for_gaps_present = false; - bool drx_cfg_scg_present = false; - bool report_cgi_request_eutra_present = false; - bool non_crit_ext_present = false; - uint32_t pscell_freq_eutra = 0; + bool pscell_freq_eutra_present = false; + bool need_for_gaps_present = false; + bool drx_cfg_scg_present = false; + bool report_cgi_request_eutra_present = false; + bool non_crit_ext_present = false; + uint32_t pscell_freq_eutra = 0; dyn_octstring scg_cell_group_cfg_eutra; dyn_octstring candidate_cell_info_list_sn_eutra; candidate_serving_freq_list_eutra_l candidate_serving_freq_list_eutra; @@ -23241,7 +22955,6 @@ struct cg_cfg_v1540_ies_s { // member variables bool pscell_freq_present = false; bool report_cgi_request_nr_present = false; - bool ph_info_scg_present = false; bool non_crit_ext_present = false; uint32_t pscell_freq = 0; report_cgi_request_nr_s_ report_cgi_request_nr; @@ -23617,8 +23330,7 @@ struct meas_cfg_sn_s { using measured_frequencies_sn_l_ = dyn_array; // member variables - bool ext = false; - bool measured_frequencies_sn_present = false; + bool ext = false; measured_frequencies_sn_l_ measured_frequencies_sn; // ... @@ -23630,16 +23342,11 @@ struct meas_cfg_sn_s { // CG-Config-IEs ::= SEQUENCE struct cg_cfg_ies_s { - bool scg_cell_group_cfg_present = false; - bool scg_rb_cfg_present = false; - bool cfg_restrict_mod_req_present = false; - bool drx_info_scg_present = false; - bool candidate_cell_info_list_sn_present = false; - bool meas_cfg_sn_present = false; - bool sel_band_combination_present = false; - bool fr_info_list_scg_present = false; - bool candidate_serving_freq_list_nr_present = false; - bool non_crit_ext_present = false; + bool cfg_restrict_mod_req_present = false; + bool drx_info_scg_present = false; + bool meas_cfg_sn_present = false; + bool sel_band_combination_present = false; + bool non_crit_ext_present = false; dyn_octstring scg_cell_group_cfg; dyn_octstring scg_rb_cfg; cfg_restrict_mod_req_scg_s cfg_restrict_mod_req; @@ -23744,8 +23451,7 @@ struct cg_cfg_info_v1590_ies_s { using serv_frequencies_mn_nr_l_ = bounded_array; // member variables - bool serv_frequencies_mn_nr_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; serv_frequencies_mn_nr_l_ serv_frequencies_mn_nr; // sequence methods @@ -23784,9 +23490,7 @@ using sftd_freq_list_nr_l = bounded_array; // CG-ConfigInfo-v1570-IEs ::= SEQUENCE struct cg_cfg_info_v1570_ies_s { - bool sftd_freq_list_nr_present = false; - bool sftd_freq_list_eutra_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; sftd_freq_list_nr_l sftd_freq_list_nr; sftd_freq_list_eutra_l sftd_freq_list_eutra; cg_cfg_info_v1590_ies_s non_crit_ext; @@ -23835,15 +23539,10 @@ struct cg_cfg_info_v1560_ies_s { }; // member variables - bool candidate_cell_info_list_mn_eutra_present = false; - bool candidate_cell_info_list_sn_eutra_present = false; - bool source_cfg_scg_eutra_present = false; - bool scg_fail_info_eutra_present = false; - bool drx_cfg_mcg_present = false; - bool meas_result_report_cgi_eutra_present = false; - bool meas_result_cell_list_sftd_eutra_present = false; - bool fr_info_list_mcg_present = false; - bool non_crit_ext_present = false; + bool scg_fail_info_eutra_present = false; + bool drx_cfg_mcg_present = false; + bool meas_result_report_cgi_eutra_present = false; + bool non_crit_ext_present = false; dyn_octstring candidate_cell_info_list_mn_eutra; dyn_octstring candidate_cell_info_list_sn_eutra; dyn_octstring source_cfg_scg_eutra; @@ -23872,7 +23571,6 @@ struct cg_cfg_info_v1540_ies_s { }; // member variables - bool ph_info_mcg_present = false; bool meas_result_report_cgi_present = false; bool non_crit_ext_present = false; ph_type_list_mcg_l ph_info_mcg; @@ -23913,10 +23611,9 @@ struct meas_cfg_mn_s { typedef enumerated gap_purpose_e_; // member variables - bool ext = false; - bool measured_frequencies_mn_present = false; - bool meas_gap_cfg_present = false; - bool gap_purpose_present = false; + bool ext = false; + bool meas_gap_cfg_present = false; + bool gap_purpose_present = false; measured_frequencies_mn_l_ measured_frequencies_mn; setup_release_c meas_gap_cfg; gap_purpose_e_ gap_purpose; @@ -23956,19 +23653,12 @@ struct cg_cfg_info_ies_s { }; // member variables - bool ue_cap_info_present = false; - bool candidate_cell_info_list_mn_present = false; - bool candidate_cell_info_list_sn_present = false; - bool meas_result_cell_list_sftd_nr_present = false; - bool scg_fail_info_present = false; - bool cfg_restrict_info_present = false; - bool drx_info_mcg_present = false; - bool meas_cfg_mn_present = false; - bool source_cfg_scg_present = false; - bool scg_rb_cfg_present = false; - bool mcg_rb_cfg_present = false; - bool mrdc_assist_info_present = false; - bool non_crit_ext_present = false; + bool scg_fail_info_present = false; + bool cfg_restrict_info_present = false; + bool drx_info_mcg_present = false; + bool meas_cfg_mn_present = false; + bool mrdc_assist_info_present = false; + bool non_crit_ext_present = false; dyn_octstring ue_cap_info; meas_result_list2_nr_l candidate_cell_info_list_mn; dyn_octstring candidate_cell_info_list_sn; @@ -24292,9 +23982,8 @@ struct rrm_cfg_s { typedef enumerated ue_inactive_time_e_; // member variables - bool ext = false; - bool ue_inactive_time_present = false; - bool candidate_cell_info_list_present = false; + bool ext = false; + bool ue_inactive_time_present = false; ue_inactive_time_e_ ue_inactive_time; meas_result_list2_nr_l candidate_cell_info_list; // ... @@ -24449,7 +24138,6 @@ struct meas_timing_cfg_v1550_ies_s { // MeasurementTimingConfiguration-IEs ::= SEQUENCE struct meas_timing_cfg_ies_s { - bool meas_timing_present = false; bool non_crit_ext_present = false; meas_timing_list_l meas_timing; meas_timing_cfg_v1550_ies_s non_crit_ext; @@ -24654,8 +24342,7 @@ struct ue_radio_paging_info_ies_s { using supported_band_list_nr_for_paging_l_ = dyn_array; // member variables - bool supported_band_list_nr_for_paging_present = false; - bool non_crit_ext_present = false; + bool non_crit_ext_present = false; supported_band_list_nr_for_paging_l_ supported_band_list_nr_for_paging; // sequence methods @@ -24802,11 +24489,8 @@ struct var_meas_cfg_s { }; // member variables - bool meas_id_list_present = false; - bool meas_obj_list_present = false; - bool report_cfg_list_present = false; - bool quant_cfg_present = false; - bool s_measure_cfg_present = false; + bool quant_cfg_present = false; + bool s_measure_cfg_present = false; meas_id_to_add_mod_list_l meas_id_list; meas_obj_to_add_mod_list_l meas_obj_list; report_cfg_to_add_mod_list_l report_cfg_list; @@ -24821,8 +24505,7 @@ struct var_meas_cfg_s { // VarMeasReport ::= SEQUENCE struct var_meas_report_s { - bool cells_triggered_list_present = false; - uint8_t meas_id = 1; + uint8_t meas_id = 1; cells_triggered_list_l cells_triggered_list; int64_t nof_reports_sent = 0; diff --git a/lib/include/srsran/asn1/rrc_nr_utils.h b/lib/include/srsran/asn1/rrc_nr_utils.h index 7d19d31e4..cd2a85790 100644 --- a/lib/include/srsran/asn1/rrc_nr_utils.h +++ b/lib/include/srsran/asn1/rrc_nr_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,11 +22,10 @@ #ifndef SRSRAN_RRC_NR_UTILS_H #define SRSRAN_RRC_NR_UTILS_H +#include "srsran/common/phy_cfg_nr.h" #include "srsran/interfaces/mac_interface_types.h" #include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/interfaces/rlc_interface_types.h" -#include "srsran/interfaces/rrc_interface_types.h" -#include "srsran/interfaces/sched_interface.h" /************************ * Forward declarations @@ -64,6 +63,19 @@ struct zp_csi_rs_res_s; struct nzp_csi_rs_res_s; struct pdsch_serving_cell_cfg_s; struct freq_info_dl_s; +struct serving_cell_cfg_common_s; +struct serving_cell_cfg_common_sib_s; +struct serving_cell_cfg_s; +struct pdcch_cfg_common_s; +struct pdcch_cfg_s; +struct pdsch_cfg_common_s; +struct pucch_cfg_common_s; +struct pucch_cfg_s; +struct pusch_cfg_common_s; +struct mib_s; + +struct srb_to_add_mod_s; +struct drb_to_add_mod_s; } // namespace rrc_nr } // namespace asn1 @@ -78,13 +90,20 @@ void to_asn1(asn1::rrc_nr::plmn_id_s* asn1_type, const plmn_id_t& cfg); /*************************** * PHY Config **************************/ -bool make_phy_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* prach_cfg); +bool make_phy_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type, + srsran_duplex_mode_t duplex_mode, + srsran_prach_cfg_t* prach_cfg); +bool fill_rach_cfg_common(const srsran_prach_cfg_t& prach_cfg, asn1::rrc_nr::rach_cfg_common_s& asn1_type); bool make_phy_tdd_cfg(const asn1::rrc_nr::tdd_ul_dl_cfg_common_s& tdd_ul_dl_cfg_common, - srsran_tdd_config_nr_t* srsran_tdd_config_nr); + srsran_duplex_config_nr_t* srsran_duplex_config_nr); +bool make_phy_tdd_cfg(const srsran_duplex_config_nr_t& srsran_duplex_config_nr, + srsran_subcarrier_spacing_t scs, + asn1::rrc_nr::tdd_ul_dl_cfg_common_s* tdd_ul_dl_cfg_common); bool make_phy_harq_ack_cfg(const asn1::rrc_nr::phys_cell_group_cfg_s& phys_cell_group_cfg, - srsran_ue_dl_nr_harq_ack_cfg_t* srsran_ue_dl_nr_harq_ack_cfg); + srsran_harq_ack_cfg_hl_t* srsran_ue_dl_nr_harq_ack_cfg); bool make_phy_coreset_cfg(const asn1::rrc_nr::ctrl_res_set_s& ctrl_res_set, srsran_coreset_t* srsran_coreset); +void make_phy_search_space0_cfg(srsran_search_space_t* in_srsran_search_space); bool make_phy_search_space_cfg(const asn1::rrc_nr::search_space_s& search_space, srsran_search_space_t* srsran_search_space); bool make_phy_csi_report(const asn1::rrc_nr::csi_report_cfg_s& csi_report_cfg, @@ -97,6 +116,9 @@ bool make_phy_max_code_rate(const asn1::rrc_nr::pucch_format_cfg_s& pucch_format bool make_phy_res_config(const asn1::rrc_nr::pucch_res_s& pucch_res, uint32_t format_2_max_code_rate, srsran_pucch_nr_resource_t* srsran_pucch_nr_resource); +bool make_phy_res_config(const srsran_pucch_nr_resource_t& in_pucch_res, + asn1::rrc_nr::pucch_res_s& out_pucch_res, + uint32_t pucch_res_id); bool make_phy_sr_resource(const asn1::rrc_nr::sched_request_res_cfg_s& sched_request_res_cfg, srsran_pucch_nr_sr_resource_t* srsran_pucch_nr_sr_resource); bool make_phy_pusch_alloc_type(const asn1::rrc_nr::pusch_cfg_s& pusch_cfg, @@ -115,29 +137,68 @@ bool make_phy_zp_csi_rs_resource(const asn1::rrc_nr::zp_csi_rs_res_s& zp_csi_rs_ bool make_phy_nzp_csi_rs_resource(const asn1::rrc_nr::nzp_csi_rs_res_s& nzp_csi_rs_res, srsran_csi_rs_nzp_resource_t* csi_rs_nzp_resource); bool make_phy_carrier_cfg(const asn1::rrc_nr::freq_info_dl_s& freq_info_dl, srsran_carrier_nr_t* carrier_nr); +bool fill_phy_ssb_cfg(const srsran_carrier_nr_t& carrier, + const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + phy_cfg_nr_t::ssb_cfg_t* out_ssb); +void fill_ssb_pos_in_burst(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& ssb_pos, + phy_cfg_nr_t::ssb_cfg_t* out_ssb); +bool fill_ssb_pattern_scs(const srsran_carrier_nr_t& carrier, + srsran_ssb_pattern_t* pattern, + srsran_subcarrier_spacing_t* ssb_scs); +bool fill_phy_ssb_cfg(const srsran_carrier_nr_t& carrier, + const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + srsran_ssb_cfg_t* out_ssb); +bool make_phy_ssb_cfg(const srsran_carrier_nr_t& carrier, + const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg, + phy_cfg_nr_t::ssb_cfg_t* ssb); +bool make_phy_mib(const asn1::rrc_nr::mib_s& mib_cfg, srsran_mib_nr_t* mib); +bool make_pdsch_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_sch_hl_cfg_nr_t* sch_hl); +bool make_csi_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_csi_hl_cfg_t* csi_hl); +bool make_duplex_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell, + srsran_duplex_config_nr_t* duplex_cfg); +bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch); +bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch); +bool fill_phy_pdsch_cfg_common(const asn1::rrc_nr::pdsch_cfg_common_s& pdsch_cfg, srsran_sch_hl_cfg_nr_t* pdsch); +void fill_phy_pucch_cfg_common(const asn1::rrc_nr::pucch_cfg_common_s& pucch_cfg, srsran_pucch_nr_common_cfg_t* pucch); +bool fill_phy_pucch_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch_nr_hl_cfg_t* pucch); +bool fill_phy_pucch_hl_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch_nr_hl_cfg_t* pucch); +bool fill_phy_pusch_cfg_common(const asn1::rrc_nr::pusch_cfg_common_s& pusch_cfg, srsran_sch_hl_cfg_nr_t* pusch); +void fill_phy_carrier_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + srsran_carrier_nr_t* carrier_nr); +void fill_phy_ssb_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, phy_cfg_nr_t::ssb_cfg_t* ssb); + /*************************** * MAC Config **************************/ logical_channel_config_t make_mac_logical_channel_cfg_t(uint8_t lcid, const asn1::rrc_nr::lc_ch_cfg_s& asn1_type); -rach_nr_cfg_t make_mac_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type); -bool make_mac_phr_cfg_t(const asn1::rrc_nr::phr_cfg_s& asn1_type, phr_cfg_nr_t* phr_cfg_nr); -bool make_mac_dl_harq_cfg_nr_t(const asn1::rrc_nr::pdsch_serving_cell_cfg_s& asn1_type, - dl_harq_cfg_nr_t* out_dl_harq_cfg_nr); +void make_mac_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type, rach_cfg_nr_t* rach_cfg_nr); +bool make_mac_phr_cfg_t(const asn1::rrc_nr::phr_cfg_s& asn1_type, phr_cfg_nr_t* phr_cfg_nr); +bool make_mac_dl_harq_cfg_nr_t(const asn1::rrc_nr::pdsch_serving_cell_cfg_s& asn1_type, + dl_harq_cfg_nr_t* out_dl_harq_cfg_nr); /*************************** * RLC Config **************************/ -rlc_config_t make_rlc_config_t(const asn1::rrc_nr::rlc_cfg_c& asn1_type); +int make_rlc_config_t(const asn1::rrc_nr::rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_t* rlc_config_out); /*************************** * PDCP Config **************************/ +pdcp_config_t make_srb_pdcp_config_t(const uint8_t bearer_id, bool is_ue); +pdcp_config_t make_nr_srb_pdcp_config_t(const uint8_t bearer_id, bool is_ue); pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue, const asn1::rrc_nr::pdcp_cfg_s& pdcp_cfg); } // namespace srsran -namespace srsenb { +/************************ + * ASN1 RRC extensions + ***********************/ +namespace asn1 { -int set_sched_cell_cfg_sib1(srsenb::sched_interface::cell_cfg_t* sched_cfg, const asn1::rrc_nr::sib1_s& sib1); -} +namespace rrc_nr { +bool operator==(const srb_to_add_mod_s& lhs, const srb_to_add_mod_s& rhs); +bool operator==(const drb_to_add_mod_s& lhs, const drb_to_add_mod_s& rhs); +} // namespace rrc_nr + +} // namespace asn1 #endif // SRSRAN_RRC_NR_UTILS_H diff --git a/lib/include/srsran/asn1/rrc_utils.h b/lib/include/srsran/asn1/rrc_utils.h index bc30415de..d010eafc9 100644 --- a/lib/include/srsran/asn1/rrc_utils.h +++ b/lib/include/srsran/asn1/rrc_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,11 +22,11 @@ #ifndef SRSRAN_RRC_UTILS_H #define SRSRAN_RRC_UTILS_H +#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/interfaces/mac_interface_types.h" #include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/interfaces/rlc_interface_types.h" #include "srsran/interfaces/rrc_interface_types.h" -#include "srsran/interfaces/sched_interface.h" /************************ * Forward declarations @@ -150,37 +150,4 @@ sib13_t make_sib13(const asn1::rrc::sib_type13_r9_s& asn1_type); } // namespace srsran -/************************ - * ASN1 RRC extensions - ***********************/ -namespace asn1 { -namespace rrc { - -/************************** - * RRC Obj Id - *************************/ - -uint8_t get_rrc_obj_id(const srb_to_add_mod_s& srb); -uint8_t get_rrc_obj_id(const drb_to_add_mod_s& drb); -uint8_t get_rrc_obj_id(const cells_to_add_mod_s& obj); -uint8_t get_rrc_obj_id(const cells_to_add_mod_nr_r15_s& obj); -uint8_t get_rrc_obj_id(const black_cells_to_add_mod_s& obj); -uint8_t get_rrc_obj_id(const meas_obj_to_add_mod_s& obj); -uint8_t get_rrc_obj_id(const report_cfg_to_add_mod_s& obj); -uint8_t get_rrc_obj_id(const meas_id_to_add_mod_s& obj); -uint8_t get_rrc_obj_id(const scell_to_add_mod_r10_s& obj); - -void set_rrc_obj_id(srb_to_add_mod_s& srb, uint8_t id); -void set_rrc_obj_id(drb_to_add_mod_s& drb, uint8_t id); -void set_rrc_obj_id(cells_to_add_mod_s& obj, uint8_t id); -void set_rrc_obj_id(cells_to_add_mod_nr_r15_s& obj, uint8_t id); -void set_rrc_obj_id(black_cells_to_add_mod_s& obj, uint8_t id); -void set_rrc_obj_id(meas_obj_to_add_mod_s& obj, uint8_t id); -void set_rrc_obj_id(report_cfg_to_add_mod_s& obj, uint8_t id); -void set_rrc_obj_id(meas_id_to_add_mod_s& obj, uint8_t id); -void set_rrc_obj_id(scell_to_add_mod_r10_s& obj, uint8_t id); - -} // namespace rrc -} // namespace asn1 - #endif // SRSRAN_RRC_UTILS_H diff --git a/lib/include/srsran/asn1/s1ap.h b/lib/include/srsran/asn1/s1ap.h index b77f5a704..140b1fac1 100644 --- a/lib/include/srsran/asn1/s1ap.h +++ b/lib/include/srsran/asn1/s1ap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -437,21 +437,11 @@ namespace s1ap { * Struct Definitions ******************************************************************************/ -// Criticality ::= ENUMERATED -struct crit_opts { - enum options { reject, ignore, notify, nulltype } value; +// INTEGER (0..16777215) ::= INTEGER (0..16777215) +using enb_ue_s1ap_id_t = integer; - const char* to_string() const; -}; -typedef enumerated crit_e; - -// Presence ::= ENUMERATED -struct presence_opts { - enum options { optional, conditional, mandatory, nulltype } value; - - const char* to_string() const; -}; -typedef enumerated presence_e; +// INTEGER (0..4294967295) ::= INTEGER (0..4294967295) +using mme_ue_s1ap_id_t = integer; // PrivateIE-ID ::= CHOICE struct private_ie_id_c { @@ -504,54 +494,6 @@ struct private_ie_field_s { template using private_ie_container_l = dyn_seq_of, 1, 65535, true>; -// ProtocolExtensionField{S1AP-PROTOCOL-EXTENSION : ExtensionSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-EXTENSION}} -template -struct protocol_ext_field_s { - uint32_t id = 0; - crit_e crit; - typename ext_set_paramT_::ext_c ext_value; - - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - bool load_info_obj(const uint32_t& id_); -}; - -// ProtocolExtensionContainer{S1AP-PROTOCOL-EXTENSION : ExtensionSetParam} ::= SEQUENCE (SIZE (1..65535)) OF -// ProtocolExtensionField -template -using protocol_ext_container_l = dyn_seq_of, 1, 65535, true>; - -// ProtocolIE-Field{S1AP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-IES}} -template -struct protocol_ie_field_s { - uint32_t id = 0; - crit_e crit; - typename ies_set_paramT_::value_c value; - - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - bool load_info_obj(const uint32_t& id_); -}; - -// ProtocolIE-Container{S1AP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE (SIZE (0..65535)) OF ProtocolIE-Field -template -using protocol_ie_container_l = dyn_seq_of, 0, 65535, true>; - -// ProtocolIE-SingleContainer{S1AP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-IES}} -template -struct protocol_ie_single_container_s { - uint32_t id = 0; - crit_e crit; - typename ies_set_paramT_::value_c value; - - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - bool load_info_obj(const uint32_t& id_); -}; - // ProtocolIE-FieldPair{S1AP-PROTOCOL-IES-PAIR : IEsSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-IES-PAIR}} template struct protocol_ie_field_pair_s { @@ -586,61 +528,15 @@ struct activ_cells_list_item_s { // ActivatedCellsList ::= SEQUENCE (SIZE (0..256)) OF ActivatedCellsList-Item using activ_cells_list_l = dyn_array; -struct s1ap_protocol_ext_empty_o { - // Extension ::= OPEN TYPE - struct ext_c { - struct types_opts { - enum options { nulltype } value; - - const char* to_string() const; - }; - typedef enumerated types; - - // choice methods - types type() const { return types::nulltype; } - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; - }; - - // members lookup methods - static uint32_t idx_to_id(uint32_t idx); - static bool is_id_valid(const uint32_t& id); - static crit_e get_crit(const uint32_t& id); - static ext_c get_ext(const uint32_t& id); - static presence_e get_presence(const uint32_t& id); -}; // GUMMEI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using gummei_ext_ies_o = s1ap_protocol_ext_empty_o; +using gummei_ext_ies_o = protocol_ext_empty_o; // PLMNidentity ::= OCTET STRING using plm_nid = fixed_octstring<3, true>; // Additional-GUTI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using add_guti_ext_ies_o = s1ap_protocol_ext_empty_o; +using add_guti_ext_ies_o = protocol_ext_empty_o; -template -struct protocol_ext_container_item_s { - uint32_t id = 0; - crit_e crit; - extT_ ext; - - // sequence methods - protocol_ext_container_item_s(uint32_t id_, crit_e crit_); - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; - -struct protocol_ext_container_empty_l { - template - using ie_field_s = protocol_ext_container_item_s; - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; using gummei_ext_ies_container = protocol_ext_container_empty_l; // GUMMEI ::= SEQUENCE @@ -677,7 +573,7 @@ struct add_guti_s { }; // AllocationAndRetentionPriority-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using alloc_and_retention_prio_ext_ies_o = s1ap_protocol_ext_empty_o; +using alloc_and_retention_prio_ext_ies_o = protocol_ext_empty_o; // Pre-emptionCapability ::= ENUMERATED struct pre_emption_cap_opts { @@ -714,10 +610,10 @@ struct alloc_and_retention_prio_s { }; // EUTRAN-CGI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using eutran_cgi_ext_ies_o = s1ap_protocol_ext_empty_o; +using eutran_cgi_ext_ies_o = protocol_ext_empty_o; // TAI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using tai_ext_ies_o = s1ap_protocol_ext_empty_o; +using tai_ext_ies_o = protocol_ext_empty_o; using eutran_cgi_ext_ies_container = protocol_ext_container_empty_l; @@ -754,16 +650,16 @@ struct tai_s { }; // CellBasedMDT-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cell_based_mdt_ext_ies_o = s1ap_protocol_ext_empty_o; +using cell_based_mdt_ext_ies_o = protocol_ext_empty_o; // CellIdListforMDT ::= SEQUENCE (SIZE (1..32)) OF EUTRAN-CGI using cell_id_listfor_mdt_l = dyn_array; // TABasedMDT-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ta_based_mdt_ext_ies_o = s1ap_protocol_ext_empty_o; +using ta_based_mdt_ext_ies_o = protocol_ext_empty_o; // TAIBasedMDT-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using tai_based_mdt_ext_ies_o = s1ap_protocol_ext_empty_o; +using tai_based_mdt_ext_ies_o = protocol_ext_empty_o; // TAIListforMDT ::= SEQUENCE (SIZE (1..8)) OF TAI using tai_listfor_mdt_l = dyn_array; @@ -882,22 +778,22 @@ private: }; // CellBasedQMC-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cell_based_qmc_ext_ies_o = s1ap_protocol_ext_empty_o; +using cell_based_qmc_ext_ies_o = protocol_ext_empty_o; // CellIdListforQMC ::= SEQUENCE (SIZE (1..32)) OF EUTRAN-CGI using cell_id_listfor_qmc_l = dyn_array; // PLMNAreaBasedQMC-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using plmn_area_based_qmc_ext_ies_o = s1ap_protocol_ext_empty_o; +using plmn_area_based_qmc_ext_ies_o = protocol_ext_empty_o; // PLMNListforQMC ::= SEQUENCE (SIZE (1..16)) OF OCTET STRING (SIZE (3)) using plmn_listfor_qmc_l = bounded_array, 16>; // TABasedQMC-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ta_based_qmc_ext_ies_o = s1ap_protocol_ext_empty_o; +using ta_based_qmc_ext_ies_o = protocol_ext_empty_o; // TAIBasedQMC-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using tai_based_qmc_ext_ies_o = s1ap_protocol_ext_empty_o; +using tai_based_qmc_ext_ies_o = protocol_ext_empty_o; // TAIListforQMC ::= SEQUENCE (SIZE (1..8)) OF TAI using tai_listfor_qmc_l = dyn_array; @@ -1042,7 +938,7 @@ private: }; // CellIdentifierAndCELevelForCECapableUEs-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cell_id_and_ce_level_for_ce_capable_ues_ext_ies_o = s1ap_protocol_ext_empty_o; +using cell_id_and_ce_level_for_ce_capable_ues_ext_ies_o = protocol_ext_empty_o; using cell_id_and_ce_level_for_ce_capable_ues_ext_ies_container = protocol_ext_container_empty_l; @@ -1062,7 +958,7 @@ struct cell_id_and_ce_level_for_ce_capable_ues_s { }; // InformationForCECapableUEs-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using info_for_ce_capable_ues_ext_ies_o = s1ap_protocol_ext_empty_o; +using info_for_ce_capable_ues_ext_ies_o = protocol_ext_empty_o; using info_for_ce_capable_ues_ext_ies_container = protocol_ext_container_empty_l; @@ -1081,7 +977,7 @@ struct assist_data_for_ce_capable_ues_s { }; // RecommendedCellsForPagingItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using recommended_cells_for_paging_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using recommended_cells_for_paging_item_ext_ies_o = protocol_ext_empty_o; using recommended_cells_for_paging_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1137,10 +1033,10 @@ struct recommended_cell_item_ies_o { using recommended_cell_list_l = bounded_array, 16>; // RecommendedCellsForPaging-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using recommended_cells_for_paging_ext_ies_o = s1ap_protocol_ext_empty_o; +using recommended_cells_for_paging_ext_ies_o = protocol_ext_empty_o; // AssistanceDataForRecommendedCells-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using assist_data_for_recommended_cells_ext_ies_o = s1ap_protocol_ext_empty_o; +using assist_data_for_recommended_cells_ext_ies_o = protocol_ext_empty_o; // NextPagingAreaScope ::= ENUMERATED struct next_paging_area_scope_opts { @@ -1151,7 +1047,7 @@ struct next_paging_area_scope_opts { typedef enumerated next_paging_area_scope_e; // PagingAttemptInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using paging_attempt_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using paging_attempt_info_ext_ies_o = protocol_ext_empty_o; using recommended_cells_for_paging_ext_ies_container = protocol_ext_container_empty_l; @@ -1170,7 +1066,7 @@ struct recommended_cells_for_paging_s { }; // AssistanceDataForPaging-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using assist_data_for_paging_ext_ies_o = s1ap_protocol_ext_empty_o; +using assist_data_for_paging_ext_ies_o = protocol_ext_empty_o; using assist_data_for_recommended_cells_ext_ies_container = protocol_ext_container_empty_l; @@ -1232,10 +1128,10 @@ struct assist_data_for_paging_s { using bplmns_l = bounded_array, 6>; // COUNTValueExtended-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using count_value_extended_ext_ies_o = s1ap_protocol_ext_empty_o; +using count_value_extended_ext_ies_o = protocol_ext_empty_o; // COUNTvaluePDCP-SNlength18-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using coun_tvalue_pdcp_snlen18_ext_ies_o = s1ap_protocol_ext_empty_o; +using coun_tvalue_pdcp_snlen18_ext_ies_o = protocol_ext_empty_o; using count_value_extended_ext_ies_container = protocol_ext_container_empty_l; @@ -1255,7 +1151,7 @@ struct count_value_extended_s { }; // COUNTvalue-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using coun_tvalue_ext_ies_o = s1ap_protocol_ext_empty_o; +using coun_tvalue_ext_ies_o = protocol_ext_empty_o; using coun_tvalue_pdcp_snlen18_ext_ies_container = protocol_ext_container_empty_l; @@ -1445,7 +1341,7 @@ struct bluetooth_meas_cfg_opts { typedef enumerated bluetooth_meas_cfg_e; // BluetoothMeasurementConfiguration-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using bluetooth_meas_cfg_ext_ies_o = s1ap_protocol_ext_empty_o; +using bluetooth_meas_cfg_ext_ies_o = protocol_ext_empty_o; using bluetooth_meas_cfg_ext_ies_container = protocol_ext_container_empty_l; @@ -1476,10 +1372,10 @@ struct bluetooth_meas_cfg_s { }; // CancelledCellinEAI-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cancelled_cellin_eai_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using cancelled_cellin_eai_item_ext_ies_o = protocol_ext_empty_o; // CancelledCellinTAI-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cancelled_cellin_tai_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using cancelled_cellin_tai_item_ext_ies_o = protocol_ext_empty_o; using cancelled_cellin_eai_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1522,13 +1418,13 @@ using cancelled_cellin_eai_l = dyn_array; using cancelled_cellin_tai_l = dyn_array; // CellID-Cancelled-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cell_id_cancelled_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using cell_id_cancelled_item_ext_ies_o = protocol_ext_empty_o; // EmergencyAreaID-Cancelled-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using emergency_area_id_cancelled_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using emergency_area_id_cancelled_item_ext_ies_o = protocol_ext_empty_o; // TAI-Cancelled-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using tai_cancelled_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using tai_cancelled_item_ext_ies_o = protocol_ext_empty_o; using cell_id_cancelled_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1652,10 +1548,10 @@ private: }; // CompletedCellinEAI-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using completed_cellin_eai_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using completed_cellin_eai_item_ext_ies_o = protocol_ext_empty_o; // CompletedCellinTAI-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using completed_cellin_tai_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using completed_cellin_tai_item_ext_ies_o = protocol_ext_empty_o; using completed_cellin_eai_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1690,7 +1586,7 @@ struct completed_cellin_tai_item_s { }; // CellID-Broadcast-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cell_id_broadcast_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using cell_id_broadcast_item_ext_ies_o = protocol_ext_empty_o; // CompletedCellinEAI ::= SEQUENCE (SIZE (1..65535)) OF CompletedCellinEAI-Item using completed_cellin_eai_l = dyn_array; @@ -1699,10 +1595,10 @@ using completed_cellin_eai_l = dyn_array; using completed_cellin_tai_l = dyn_array; // EmergencyAreaID-Broadcast-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using emergency_area_id_broadcast_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using emergency_area_id_broadcast_item_ext_ies_o = protocol_ext_empty_o; // TAI-Broadcast-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using tai_broadcast_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using tai_broadcast_item_ext_ies_o = protocol_ext_empty_o; using cell_id_broadcast_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1825,7 +1721,7 @@ private: }; // CGI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cgi_ext_ies_o = s1ap_protocol_ext_empty_o; +using cgi_ext_ies_o = protocol_ext_empty_o; using cgi_ext_ies_container = protocol_ext_container_empty_l; @@ -1858,7 +1754,7 @@ struct cn_type_opts { typedef enumerated cn_type_e; // CNTypeRestrictions-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cn_type_restricts_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using cn_type_restricts_item_ext_ies_o = protocol_ext_empty_o; using cn_type_restricts_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1881,7 +1777,7 @@ struct cn_type_restricts_item_s { using cn_type_restricts_l = dyn_array; // CSG-IdList-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using csg_id_list_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using csg_id_list_item_ext_ies_o = protocol_ext_empty_o; using csg_id_list_item_ext_ies_container = protocol_ext_container_empty_l; @@ -1903,7 +1799,7 @@ struct csg_id_list_item_s { using csg_id_list_l = dyn_array; // CSGMembershipInfo-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using csg_membership_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using csg_membership_info_ext_ies_o = protocol_ext_empty_o; // CSGMembershipStatus ::= ENUMERATED struct csg_membership_status_opts { @@ -2228,7 +2124,7 @@ private: }; // Cdma2000OneXSRVCCInfo-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cdma2000_one_xsrvcc_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using cdma2000_one_xsrvcc_info_ext_ies_o = protocol_ext_empty_o; using cdma2000_one_xsrvcc_info_ext_ies_container = protocol_ext_container_empty_l; @@ -2501,31 +2397,18 @@ struct cell_traffic_trace_ies_o { static presence_e get_presence(const uint32_t& id); }; -template -struct protocol_ie_container_item_s { - uint32_t id = 0; - crit_e crit; - valueT_ value; - - // sequence methods - protocol_ie_container_item_s(uint32_t id_, crit_e crit_); - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; - struct cell_traffic_trace_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool privacy_ind_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > e_utran_trace_id; - ie_field_s eutran_cgi; - ie_field_s > trace_collection_entity_ip_address; - ie_field_s privacy_ind; + bool privacy_ind_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > e_utran_trace_id; + ie_field_s eutran_cgi; + ie_field_s > trace_collection_entity_ip_address; + ie_field_s privacy_ind; // sequence methods cell_traffic_trace_ies_container(); @@ -2535,16 +2418,7 @@ struct cell_traffic_trace_ies_container { }; // CellTrafficTrace ::= SEQUENCE -struct cell_traffic_trace_s { - bool ext = false; - cell_traffic_trace_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using cell_traffic_trace_s = elementary_procedure_option; // Cell-Size ::= ENUMERATED struct cell_size_opts { @@ -2555,7 +2429,7 @@ struct cell_size_opts { typedef enumerated cell_size_e; // CellType-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using cell_type_ext_ies_o = s1ap_protocol_ext_empty_o; +using cell_type_ext_ies_o = protocol_ext_empty_o; using cell_type_ext_ies_container = protocol_ext_container_empty_l; @@ -2629,7 +2503,7 @@ struct supported_tas_item_s { }; // ConnectedengNBItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using connectedeng_nb_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using connectedeng_nb_item_ext_ies_o = protocol_ext_empty_o; // SupportedTAs ::= SEQUENCE (SIZE (1..256)) OF SupportedTAs-Item using supported_tas_l = dyn_array; @@ -2707,10 +2581,10 @@ struct gbr_qos_info_ext_ies_o { }; // ScheduledCommunicationTime-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using sched_communication_time_ext_ies_o = s1ap_protocol_ext_empty_o; +using sched_communication_time_ext_ies_o = protocol_ext_empty_o; // DL-CP-SecurityInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using dl_cp_security_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using dl_cp_security_info_ext_ies_o = protocol_ext_empty_o; // E-RABQoSParameters-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION struct erab_qos_params_ext_ies_o { @@ -2814,7 +2688,7 @@ struct sched_communication_time_s { }; // Subscription-Based-UE-DifferentiationInfo-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using subscription_based_ue_differentiation_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using subscription_based_ue_differentiation_info_ext_ies_o = protocol_ext_empty_o; // CE-ModeBRestricted ::= ENUMERATED struct ce_mode_brestricted_opts { @@ -3024,22 +2898,22 @@ struct conn_establishment_ind_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool ue_radio_cap_present = false; - bool enhanced_coverage_restricted_present = false; - bool dl_cp_security_info_present = false; - bool ce_mode_brestricted_present = false; - bool end_ind_present = false; - bool subscription_based_ue_differentiation_info_present = false; - bool ue_level_qos_params_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > ue_radio_cap; - ie_field_s enhanced_coverage_restricted; - ie_field_s dl_cp_security_info; - ie_field_s ce_mode_brestricted; - ie_field_s end_ind; - ie_field_s subscription_based_ue_differentiation_info; - ie_field_s ue_level_qos_params; + bool ue_radio_cap_present = false; + bool enhanced_coverage_restricted_present = false; + bool dl_cp_security_info_present = false; + bool ce_mode_brestricted_present = false; + bool end_ind_present = false; + bool subscription_based_ue_differentiation_info_present = false; + bool ue_level_qos_params_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > ue_radio_cap; + ie_field_s enhanced_coverage_restricted; + ie_field_s dl_cp_security_info; + ie_field_s ce_mode_brestricted; + ie_field_s end_ind; + ie_field_s subscription_based_ue_differentiation_info; + ie_field_s ue_level_qos_params; // sequence methods conn_establishment_ind_ies_container(); @@ -3049,16 +2923,7 @@ struct conn_establishment_ind_ies_container { }; // ConnectionEstablishmentIndication ::= SEQUENCE -struct conn_establishment_ind_s { - bool ext = false; - conn_establishment_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using conn_establishment_ind_s = elementary_procedure_option; // ENB-ID ::= CHOICE struct enb_id_c { @@ -3155,13 +3020,13 @@ private: }; // Global-GNB-ID-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using global_gnb_id_ext_ies_o = s1ap_protocol_ext_empty_o; +using global_gnb_id_ext_ies_o = protocol_ext_empty_o; // GlobalENB-ID-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using global_enb_id_ext_ies_o = s1ap_protocol_ext_empty_o; +using global_enb_id_ext_ies_o = protocol_ext_empty_o; // GNB-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using gnb_ext_ies_o = s1ap_protocol_ext_empty_o; +using gnb_ext_ies_o = protocol_ext_empty_o; using global_enb_id_ext_ies_container = protocol_ext_container_empty_l; @@ -3198,7 +3063,7 @@ struct global_gnb_id_s { }; // NG-eNB-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ng_enb_ext_ies_o = s1ap_protocol_ext_empty_o; +using ng_enb_ext_ies_o = protocol_ext_empty_o; using gnb_ext_ies_container = protocol_ext_container_empty_l; @@ -3233,7 +3098,7 @@ struct ng_enb_s { }; // ContextatSource-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using contextat_source_ext_ies_o = s1ap_protocol_ext_empty_o; +using contextat_source_ext_ies_o = protocol_ext_empty_o; // Global-RAN-NODE-ID ::= CHOICE struct global_ran_node_id_c { @@ -3303,7 +3168,7 @@ struct contextat_source_s { }; // CriticalityDiagnostics-IE-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using crit_diagnostics_ie_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using crit_diagnostics_ie_item_ext_ies_o = protocol_ext_empty_o; // TypeOfError ::= ENUMERATED struct type_of_error_opts { @@ -3332,7 +3197,7 @@ struct crit_diagnostics_ie_item_s { }; // CriticalityDiagnostics-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using crit_diagnostics_ext_ies_o = s1ap_protocol_ext_empty_o; +using crit_diagnostics_ext_ies_o = protocol_ext_empty_o; // CriticalityDiagnostics-IE-List ::= SEQUENCE (SIZE (1..256)) OF CriticalityDiagnostics-IE-Item using crit_diagnostics_ie_list_l = dyn_array; @@ -3417,9 +3282,9 @@ struct deactiv_trace_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > e_utran_trace_id; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > e_utran_trace_id; // sequence methods deactiv_trace_ies_container(); @@ -3429,28 +3294,19 @@ struct deactiv_trace_ies_container { }; // DeactivateTrace ::= SEQUENCE -struct deactiv_trace_s { - bool ext = false; - deactiv_trace_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using deactiv_trace_s = elementary_procedure_option; // ForbiddenLACs ::= SEQUENCE (SIZE (1..4096)) OF OCTET STRING (SIZE (2)) using forbidden_lacs_l = dyn_array >; // ForbiddenLAs-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using forbidden_las_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using forbidden_las_item_ext_ies_o = protocol_ext_empty_o; // ForbiddenTACs ::= SEQUENCE (SIZE (1..4096)) OF OCTET STRING (SIZE (2)) using forbidden_tacs_l = dyn_array >; // ForbiddenTAs-Item-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using forbidden_tas_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using forbidden_tas_item_ext_ies_o = protocol_ext_empty_o; using forbidden_las_item_ext_ies_container = protocol_ext_container_empty_l; @@ -3587,7 +3443,7 @@ struct ho_restrict_list_ext_ies_o { }; // NRUESecurityCapabilities-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using nrue_security_cap_ext_ies_o = s1ap_protocol_ext_empty_o; +using nrue_security_cap_ext_ies_o = protocol_ext_empty_o; // DLNASPDUDeliveryAckRequest ::= ENUMERATED struct dlnaspdu_delivery_ack_request_opts { @@ -3781,35 +3637,35 @@ struct dl_nas_transport_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool ho_restrict_list_present = false; - bool subscriber_profile_idfor_rfp_present = false; - bool srvcc_operation_possible_present = false; - bool ue_radio_cap_present = false; - bool dlnaspdu_delivery_ack_request_present = false; - bool enhanced_coverage_restricted_present = false; - bool nrue_security_cap_present = false; - bool ce_mode_brestricted_present = false; - bool ue_cap_info_request_present = false; - bool end_ind_present = false; - bool pending_data_ind_present = false; - bool subscription_based_ue_differentiation_info_present = false; - bool add_rrm_prio_idx_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > nas_pdu; - ie_field_s ho_restrict_list; - ie_field_s > subscriber_profile_idfor_rfp; - ie_field_s srvcc_operation_possible; - ie_field_s > ue_radio_cap; - ie_field_s dlnaspdu_delivery_ack_request; - ie_field_s enhanced_coverage_restricted; - ie_field_s nrue_security_cap; - ie_field_s ce_mode_brestricted; - ie_field_s ue_cap_info_request; - ie_field_s end_ind; - ie_field_s pending_data_ind; - ie_field_s subscription_based_ue_differentiation_info; - ie_field_s > add_rrm_prio_idx; + bool ho_restrict_list_present = false; + bool subscriber_profile_idfor_rfp_present = false; + bool srvcc_operation_possible_present = false; + bool ue_radio_cap_present = false; + bool dlnaspdu_delivery_ack_request_present = false; + bool enhanced_coverage_restricted_present = false; + bool nrue_security_cap_present = false; + bool ce_mode_brestricted_present = false; + bool ue_cap_info_request_present = false; + bool end_ind_present = false; + bool pending_data_ind_present = false; + bool subscription_based_ue_differentiation_info_present = false; + bool add_rrm_prio_idx_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > nas_pdu; + ie_field_s ho_restrict_list; + ie_field_s > subscriber_profile_idfor_rfp; + ie_field_s srvcc_operation_possible; + ie_field_s > ue_radio_cap; + ie_field_s dlnaspdu_delivery_ack_request; + ie_field_s enhanced_coverage_restricted; + ie_field_s nrue_security_cap; + ie_field_s ce_mode_brestricted; + ie_field_s ue_cap_info_request; + ie_field_s end_ind; + ie_field_s pending_data_ind; + ie_field_s subscription_based_ue_differentiation_info; + ie_field_s > add_rrm_prio_idx; // sequence methods dl_nas_transport_ies_container(); @@ -3819,16 +3675,7 @@ struct dl_nas_transport_ies_container { }; // DownlinkNASTransport ::= SEQUENCE -struct dl_nas_transport_s { - bool ext = false; - dl_nas_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_nas_transport_s = elementary_procedure_option; // DownlinkNonUEAssociatedLPPaTransport-IEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct dl_non_ueassociated_lp_pa_transport_ies_o { @@ -3890,19 +3737,11 @@ struct dl_non_ueassociated_lp_pa_transport_ies_container { }; // DownlinkNonUEAssociatedLPPaTransport ::= SEQUENCE -struct dl_non_ueassociated_lp_pa_transport_s { - bool ext = false; - dl_non_ueassociated_lp_pa_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_non_ueassociated_lp_pa_transport_s = + elementary_procedure_option; // E-RABDataForwardingItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_data_forwarding_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_data_forwarding_item_ext_ies_o = protocol_ext_empty_o; using erab_data_forwarding_item_ext_ies_container = protocol_ext_container_empty_l; @@ -4050,10 +3889,10 @@ struct dl_s1cdma2000tunnelling_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_subjectto_data_forwarding_list_present = false; - bool cdma2000_ho_status_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_subjectto_data_forwarding_list_present = false; + bool cdma2000_ho_status_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s > erab_subjectto_data_forwarding_list; ie_field_s cdma2000_ho_status; ie_field_s cdma2000_rat_type; @@ -4067,16 +3906,7 @@ struct dl_s1cdma2000tunnelling_ies_container { }; // DownlinkS1cdma2000tunnelling ::= SEQUENCE -struct dl_s1cdma2000tunnelling_s { - bool ext = false; - dl_s1cdma2000tunnelling_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_s1cdma2000tunnelling_s = elementary_procedure_option; // DownlinkUEAssociatedLPPaTransport-IEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct dl_ueassociated_lp_pa_transport_ies_o { @@ -4129,10 +3959,10 @@ struct dl_ueassociated_lp_pa_transport_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > routing_id; - ie_field_s > lp_pa_pdu; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > routing_id; + ie_field_s > lp_pa_pdu; // sequence methods dl_ueassociated_lp_pa_transport_ies_container(); @@ -4142,16 +3972,7 @@ struct dl_ueassociated_lp_pa_transport_ies_container { }; // DownlinkUEAssociatedLPPaTransport ::= SEQUENCE -struct dl_ueassociated_lp_pa_transport_s { - bool ext = false; - dl_ueassociated_lp_pa_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using dl_ueassociated_lp_pa_transport_s = elementary_procedure_option; // ProtocolIE-ContainerPair-item{S1AP-PROTOCOL-IES-PAIR : IEsSetParam} ::= ProtocolIE-ContainerPair template @@ -4160,11 +3981,10 @@ using protocol_ie_container_pair_item_l = protocol_ie_container_pair_l -using erab_ie_container_pair_list_l = - dyn_seq_of, 0, 65535, true>, 1, 256>; +using erab_ie_container_pair_list_l = dyn_seq_of, 1, 256>; // E-RABAdmittedItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_admitted_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_admitted_item_ext_ies_o = protocol_ext_empty_o; using erab_admitted_item_ext_ies_container = protocol_ext_container_empty_l; @@ -4225,7 +4045,7 @@ struct erab_admitted_item_ies_o { }; // E-RABFailedToResumeItemResumeReq-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_failed_to_resume_item_resume_req_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_failed_to_resume_item_resume_req_ext_ies_o = protocol_ext_empty_o; using erab_failed_to_resume_item_resume_req_ext_ies_container = protocol_ext_container_empty_l; @@ -4277,7 +4097,7 @@ struct erab_failed_to_resume_item_resume_req_ies_o { }; // E-RABFailedToResumeItemResumeRes-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_failed_to_resume_item_resume_res_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_failed_to_resume_item_resume_res_ext_ies_o = protocol_ext_empty_o; using erab_failed_to_resume_item_resume_res_ext_ies_container = protocol_ext_container_empty_l; @@ -4329,7 +4149,7 @@ struct erab_failed_to_resume_item_resume_res_ies_o { }; // E-RABFailedToSetupItemHOReqAckExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_failed_to_setup_item_ho_req_ack_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_failed_to_setup_item_ho_req_ack_ext_ies_o = protocol_ext_empty_o; using erab_failed_to_setup_item_ho_req_ack_ext_ies_container = protocol_ext_container_empty_l; @@ -4389,7 +4209,7 @@ struct dl_forwarding_opts { typedef enumerated dl_forwarding_e; // E-RABInformationListItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_info_list_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_info_list_item_ext_ies_o = protocol_ext_empty_o; using erab_info_list_item_ext_ies_container = protocol_ext_container_empty_l; @@ -4445,7 +4265,7 @@ struct erab_info_list_ies_o { using erab_info_list_l = dyn_array >; // E-RABItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_item_ext_ies_o = protocol_ext_empty_o; using erab_item_ext_ies_container = protocol_ext_container_empty_l; @@ -4500,7 +4320,7 @@ struct erab_item_ies_o { using erab_list_l = dyn_array >; // E-RABModifyItemBearerModConfExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_modify_item_bearer_mod_conf_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_modify_item_bearer_mod_conf_ext_ies_o = protocol_ext_empty_o; using erab_modify_item_bearer_mod_conf_ext_ies_container = protocol_ext_container_empty_l; @@ -4621,13 +4441,13 @@ struct erab_mod_confirm_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_modify_list_bearer_mod_conf_present = false; - bool erab_failed_to_modify_list_bearer_mod_conf_present = false; - bool erab_to_be_released_list_bearer_mod_conf_present = false; - bool crit_diagnostics_present = false; - bool csg_membership_status_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_modify_list_bearer_mod_conf_present = false; + bool erab_failed_to_modify_list_bearer_mod_conf_present = false; + bool erab_to_be_released_list_bearer_mod_conf_present = false; + bool crit_diagnostics_present = false; + bool csg_membership_status_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s, 1, 256, true> > erab_modify_list_bearer_mod_conf; ie_field_s, 1, 256, true> > @@ -4645,19 +4465,10 @@ struct erab_mod_confirm_ies_container { }; // E-RABModificationConfirm ::= SEQUENCE -struct erab_mod_confirm_s { - bool ext = false; - erab_mod_confirm_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_mod_confirm_s = elementary_procedure_option; // E-RABUsageReportItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erabusage_report_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using erabusage_report_item_ext_ies_o = protocol_ext_empty_o; using erabusage_report_item_ext_ies_container = protocol_ext_container_empty_l; @@ -4711,13 +4522,13 @@ struct erabusage_report_item_ies_o { }; // NR-CGI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using nr_cgi_ext_ies_o = s1ap_protocol_ext_empty_o; +using nr_cgi_ext_ies_o = protocol_ext_empty_o; // E-RABNotToBeModifiedItemBearerModInd-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_not_to_be_modified_item_bearer_mod_ind_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_not_to_be_modified_item_bearer_mod_ind_ext_ies_o = protocol_ext_empty_o; // E-RABToBeModifiedItemBearerModInd-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_to_be_modified_item_bearer_mod_ind_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_to_be_modified_item_bearer_mod_ind_ext_ies_o = protocol_ext_empty_o; // E-RABUsageReportList ::= SEQUENCE (SIZE (1..2)) OF ProtocolIE-SingleContainer{S1AP-PROTOCOL-IES : IEsSetParam} using erabusage_report_list_l = bounded_array, 2>; @@ -4740,10 +4551,10 @@ struct nr_cgi_s { }; // PSCellInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ps_cell_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using ps_cell_info_ext_ies_o = protocol_ext_empty_o; // SecondaryRATDataUsageReportItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using secondary_rat_data_usage_report_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using secondary_rat_data_usage_report_item_ext_ies_o = protocol_ext_empty_o; // SecondaryRATType ::= ENUMERATED struct secondary_rat_type_opts { @@ -4923,7 +4734,7 @@ struct secondary_rat_data_usage_report_item_ies_o { }; // Tunnel-Information-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using tunnel_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using tunnel_info_ext_ies_o = protocol_ext_empty_o; // UserLocationInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION struct user_location_info_ext_ies_o { @@ -5073,13 +4884,13 @@ struct erab_mod_ind_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_not_to_be_modified_list_bearer_mod_ind_present = false; - bool csg_membership_info_present = false; - bool tunnel_info_for_bbf_present = false; - bool secondary_rat_data_usage_report_list_present = false; - bool user_location_info_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_not_to_be_modified_list_bearer_mod_ind_present = false; + bool csg_membership_info_present = false; + bool tunnel_info_for_bbf_present = false; + bool secondary_rat_data_usage_report_list_present = false; + bool user_location_info_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s > erab_to_be_modified_list_bearer_mod_ind; ie_field_s > @@ -5098,19 +4909,10 @@ struct erab_mod_ind_ies_container { }; // E-RABModificationIndication ::= SEQUENCE -struct erab_mod_ind_s { - bool ext = false; - erab_mod_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_mod_ind_s = elementary_procedure_option; // E-RABModifyItemBearerModResExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_modify_item_bearer_mod_res_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_modify_item_bearer_mod_res_ext_ies_o = protocol_ext_empty_o; using erab_modify_item_bearer_mod_res_ext_ies_container = protocol_ext_container_empty_l; @@ -5409,11 +5211,11 @@ struct erab_modify_request_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool ueaggregate_maximum_bitrate_present = false; - bool secondary_rat_data_usage_request_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s ueaggregate_maximum_bitrate; + bool ueaggregate_maximum_bitrate_present = false; + bool secondary_rat_data_usage_request_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s ueaggregate_maximum_bitrate; ie_field_s, 1, 256, true> > erab_to_be_modified_list_bearer_mod_req; ie_field_s secondary_rat_data_usage_request; @@ -5426,16 +5228,7 @@ struct erab_modify_request_ies_container { }; // E-RABModifyRequest ::= SEQUENCE -struct erab_modify_request_s { - bool ext = false; - erab_modify_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_modify_request_s = elementary_procedure_option; // E-RABModifyResponseIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct erab_modify_resp_ies_o { @@ -5504,12 +5297,12 @@ struct erab_modify_resp_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_modify_list_bearer_mod_res_present = false; - bool erab_failed_to_modify_list_present = false; - bool crit_diagnostics_present = false; - bool secondary_rat_data_usage_report_list_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_modify_list_bearer_mod_res_present = false; + bool erab_failed_to_modify_list_present = false; + bool crit_diagnostics_present = false; + bool secondary_rat_data_usage_report_list_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s, 1, 256, true> > erab_modify_list_bearer_mod_res; ie_field_s, 1, 256, true> > erab_failed_to_modify_list; @@ -5525,16 +5318,7 @@ struct erab_modify_resp_ies_container { }; // E-RABModifyResponse ::= SEQUENCE -struct erab_modify_resp_s { - bool ext = false; - erab_modify_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_modify_resp_s = elementary_procedure_option; // E-RABReleaseCommandIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct erab_release_cmd_ies_o { @@ -5596,11 +5380,11 @@ struct erab_release_cmd_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool ueaggregate_maximum_bitrate_present = false; - bool nas_pdu_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s ueaggregate_maximum_bitrate; + bool ueaggregate_maximum_bitrate_present = false; + bool nas_pdu_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s ueaggregate_maximum_bitrate; ie_field_s, 1, 256, true> > erab_to_be_released_list; ie_field_s > nas_pdu; @@ -5612,16 +5396,7 @@ struct erab_release_cmd_ies_container { }; // E-RABReleaseCommand ::= SEQUENCE -struct erab_release_cmd_s { - bool ext = false; - erab_release_cmd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_release_cmd_s = elementary_procedure_option; // E-RABReleaseIndicationIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct erab_release_ind_ies_o { @@ -5683,10 +5458,10 @@ struct erab_release_ind_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool user_location_info_present = false; - bool secondary_rat_data_usage_report_list_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool user_location_info_present = false; + bool secondary_rat_data_usage_report_list_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s, 1, 256, true> > erab_released_list; ie_field_s user_location_info; ie_field_s, 1, 256, true> > @@ -5700,19 +5475,10 @@ struct erab_release_ind_ies_container { }; // E-RABReleaseIndication ::= SEQUENCE -struct erab_release_ind_s { - bool ext = false; - erab_release_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_release_ind_s = elementary_procedure_option; // E-RABReleaseItemBearerRelCompExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_release_item_bearer_rel_comp_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_release_item_bearer_rel_comp_ext_ies_o = protocol_ext_empty_o; using erab_release_item_bearer_rel_comp_ext_ies_container = protocol_ext_container_empty_l; @@ -5838,13 +5604,13 @@ struct erab_release_resp_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_release_list_bearer_rel_comp_present = false; - bool erab_failed_to_release_list_present = false; - bool crit_diagnostics_present = false; - bool user_location_info_present = false; - bool secondary_rat_data_usage_report_list_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_release_list_bearer_rel_comp_present = false; + bool erab_failed_to_release_list_present = false; + bool crit_diagnostics_present = false; + bool user_location_info_present = false; + bool secondary_rat_data_usage_report_list_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s, 1, 256, true> > erab_release_list_bearer_rel_comp; ie_field_s, 1, 256, true> > erab_failed_to_release_list; @@ -5861,19 +5627,10 @@ struct erab_release_resp_ies_container { }; // E-RABReleaseResponse ::= SEQUENCE -struct erab_release_resp_s { - bool ext = false; - erab_release_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_release_resp_s = elementary_procedure_option; // E-RABSetupItemBearerSUResExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_setup_item_bearer_su_res_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_setup_item_bearer_su_res_ext_ies_o = protocol_ext_empty_o; using erab_setup_item_bearer_su_res_ext_ies_container = protocol_ext_container_empty_l; @@ -5926,7 +5683,7 @@ struct erab_setup_item_bearer_su_res_ies_o { }; // E-RABSetupItemCtxtSUResExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_setup_item_ctxt_su_res_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_setup_item_ctxt_su_res_ext_ies_o = protocol_ext_empty_o; using erab_setup_item_ctxt_su_res_ext_ies_container = protocol_ext_container_empty_l; @@ -6167,10 +5924,10 @@ struct erab_setup_request_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool ueaggregate_maximum_bitrate_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s ueaggregate_maximum_bitrate; + bool ueaggregate_maximum_bitrate_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s ueaggregate_maximum_bitrate; ie_field_s, 1, 256, true> > erab_to_be_setup_list_bearer_su_req; @@ -6182,16 +5939,7 @@ struct erab_setup_request_ies_container { }; // E-RABSetupRequest ::= SEQUENCE -struct erab_setup_request_s { - bool ext = false; - erab_setup_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_setup_request_s = elementary_procedure_option; // E-RABSetupResponseIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct erab_setup_resp_ies_o { @@ -6253,11 +6001,11 @@ struct erab_setup_resp_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_setup_list_bearer_su_res_present = false; - bool erab_failed_to_setup_list_bearer_su_res_present = false; - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_setup_list_bearer_su_res_present = false; + bool erab_failed_to_setup_list_bearer_su_res_present = false; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s, 1, 256, true> > erab_setup_list_bearer_su_res; ie_field_s, 1, 256, true> > @@ -6272,16 +6020,7 @@ struct erab_setup_resp_ies_container { }; // E-RABSetupResponse ::= SEQUENCE -struct erab_setup_resp_s { - bool ext = false; - erab_setup_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using erab_setup_resp_s = elementary_procedure_option; // E-RABToBeSetupItemCtxtSUReqExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION struct erab_to_be_setup_item_ctxt_su_req_ext_ies_o { @@ -6519,7 +6258,7 @@ using erab_to_be_setup_list_ctxt_su_req_l = dyn_array >; // E-RABToBeSwitchedDLItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_to_be_switched_dl_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_to_be_switched_dl_item_ext_ies_o = protocol_ext_empty_o; using erab_to_be_switched_dl_item_ext_ies_container = protocol_ext_container_empty_l; @@ -6572,7 +6311,7 @@ struct erab_to_be_switched_dl_item_ies_o { }; // E-RABToBeSwitchedULItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using erab_to_be_switched_ul_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using erab_to_be_switched_ul_item_ext_ies_o = protocol_ext_empty_o; using erab_to_be_switched_ul_item_ext_ies_container = protocol_ext_container_empty_l; @@ -6644,7 +6383,7 @@ struct ehrpd_multi_sector_load_report_resp_item_s { }; // ENBX2ExtTLA-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using enbx2_ext_tla_ext_ies_o = s1ap_protocol_ext_empty_o; +using enbx2_ext_tla_ext_ies_o = protocol_ext_empty_o; // ENBX2GTPTLAs ::= SEQUENCE (SIZE (1..16)) OF BIT STRING (SIZE (1..160,...)) using enbx2_gtptlas_l = bounded_array, 16>; @@ -6677,7 +6416,7 @@ struct muting_availability_ind_opts { typedef enumerated muting_availability_ind_e; // RLFReportInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using rlf_report_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using rlf_report_info_ext_ies_o = protocol_ext_empty_o; // ENBIndirectX2TransportLayerAddresses ::= SEQUENCE (SIZE (1..2)) OF BIT STRING (SIZE (1..160,...)) using enb_indirect_x2_transport_layer_addresses_l = bounded_array, 2>; @@ -6686,10 +6425,10 @@ using enb_indirect_x2_transport_layer_addresses_l = bounded_array; // Global-en-gNB-ID-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using global_en_g_nb_id_ext_ies_o = s1ap_protocol_ext_empty_o; +using global_en_g_nb_id_ext_ies_o = protocol_ext_empty_o; // MutingPatternInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using muting_pattern_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using muting_pattern_info_ext_ies_o = protocol_ext_empty_o; using rlf_report_info_ext_ies_container = protocol_ext_container_empty_l; @@ -6750,16 +6489,16 @@ struct time_synchronisation_info_ext_ies_o { }; // EN-DCSONeNBIdentification-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using en_dcso_nenb_identif_ext_ies_o = s1ap_protocol_ext_empty_o; +using en_dcso_nenb_identif_ext_ies_o = protocol_ext_empty_o; // EN-DCSONengNBIdentification-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using en_dcso_neng_nb_identif_ext_ies_o = s1ap_protocol_ext_empty_o; +using en_dcso_neng_nb_identif_ext_ies_o = protocol_ext_empty_o; // ENBX2TLAs ::= SEQUENCE (SIZE (1..2)) OF BIT STRING (SIZE (1..160,...)) using enbx2_tlas_l = bounded_array, 2>; // FiveGSTAI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using five_gstai_ext_ies_o = s1ap_protocol_ext_empty_o; +using five_gstai_ext_ies_o = protocol_ext_empty_o; using global_en_g_nb_id_ext_ies_container = protocol_ext_container_empty_l; @@ -6924,10 +6663,10 @@ struct en_dcso_neng_nb_identif_s { }; // EN-DCTransferTypeReply-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using en_dc_transfer_type_reply_ext_ies_o = s1ap_protocol_ext_empty_o; +using en_dc_transfer_type_reply_ext_ies_o = protocol_ext_empty_o; // EN-DCTransferTypeRequest-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using en_dc_transfer_type_request_ext_ies_o = s1ap_protocol_ext_empty_o; +using en_dc_transfer_type_request_ext_ies_o = protocol_ext_empty_o; using five_gstai_ext_ies_container = protocol_ext_container_empty_l; @@ -7107,7 +6846,7 @@ struct son_info_request_opts { typedef enumerated son_info_request_e; // EN-DCSONConfigurationTransfer-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using en_dcson_cfg_transfer_ext_ies_o = s1ap_protocol_ext_empty_o; +using en_dcson_cfg_transfer_ext_ies_o = protocol_ext_empty_o; // EN-DCSONTransferType ::= CHOICE struct en_dcson_transfer_type_c { @@ -7240,7 +6979,7 @@ struct en_dcson_cfg_transfer_s { }; // ENB-StatusTransfer-TransparentContainer-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using enb_status_transfer_transparent_container_ext_ies_o = s1ap_protocol_ext_empty_o; +using enb_status_transfer_transparent_container_ext_ies_o = protocol_ext_empty_o; using enb_status_transfer_transparent_container_ext_ies_container = protocol_ext_container_empty_l; @@ -7259,10 +6998,10 @@ struct enb_status_transfer_transparent_container_s { }; // S-TMSI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using s_tmsi_ext_ies_o = s1ap_protocol_ext_empty_o; +using s_tmsi_ext_ies_o = protocol_ext_empty_o; // UL-CP-SecurityInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ul_cp_security_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using ul_cp_security_info_ext_ies_o = protocol_ext_empty_o; using s_tmsi_ext_ies_container = protocol_ext_container_empty_l; @@ -7353,11 +7092,11 @@ struct enbcp_relocation_ind_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > enb_ue_s1ap_id; - ie_field_s s_tmsi; - ie_field_s eutran_cgi; - ie_field_s tai; - ie_field_s ul_cp_security_info; + ie_field_s enb_ue_s1ap_id; + ie_field_s s_tmsi; + ie_field_s eutran_cgi; + ie_field_s tai; + ie_field_s ul_cp_security_info; // sequence methods enbcp_relocation_ind_ies_container(); @@ -7367,19 +7106,10 @@ struct enbcp_relocation_ind_ies_container { }; // ENBCPRelocationIndication ::= SEQUENCE -struct enbcp_relocation_ind_s { - bool ext = false; - enbcp_relocation_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using enbcp_relocation_ind_s = elementary_procedure_option; // ListeningSubframePattern-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using listening_sf_pattern_ext_ies_o = s1ap_protocol_ext_empty_o; +using listening_sf_pattern_ext_ies_o = protocol_ext_empty_o; using listening_sf_pattern_ext_ies_container = protocol_ext_container_empty_l; @@ -7409,10 +7139,10 @@ struct listening_sf_pattern_s { }; // SynchronisationInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using synchronisation_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using synchronisation_info_ext_ies_o = protocol_ext_empty_o; // SourceeNB-ID-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using sourceenb_id_ext_ies_o = s1ap_protocol_ext_empty_o; +using sourceenb_id_ext_ies_o = protocol_ext_empty_o; using synchronisation_info_ext_ies_container = protocol_ext_container_empty_l; @@ -7436,7 +7166,7 @@ struct synchronisation_info_s { }; // TargeteNB-ID-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using targetenb_id_ext_ies_o = s1ap_protocol_ext_empty_o; +using targetenb_id_ext_ies_o = protocol_ext_empty_o; // SONConfigurationTransfer-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION struct son_cfg_transfer_ext_ies_o { @@ -7607,16 +7337,7 @@ struct enb_cfg_transfer_ies_container { }; // ENBConfigurationTransfer ::= SEQUENCE -struct enb_cfg_transfer_s { - bool ext = false; - enb_cfg_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using enb_cfg_transfer_s = elementary_procedure_option; // NB-IoT-DefaultPagingDRX ::= ENUMERATED struct nb_io_t_default_paging_drx_opts { @@ -7727,16 +7448,7 @@ struct enb_cfg_upd_ies_container { }; // ENBConfigurationUpdate ::= SEQUENCE -struct enb_cfg_upd_s { - bool ext = false; - enb_cfg_upd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using enb_cfg_upd_s = elementary_procedure_option; // ENBConfigurationUpdateAcknowledgeIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct enb_cfg_upd_ack_ies_o { @@ -7771,16 +7483,7 @@ struct enb_cfg_upd_ack_ies_o { }; // ENBConfigurationUpdateAcknowledge ::= SEQUENCE -struct enb_cfg_upd_ack_s { - bool ext = false; - protocol_ie_container_l protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using enb_cfg_upd_ack_s = elementary_procedure_option >; // TimeToWait ::= ENUMERATED struct time_to_wait_opts { @@ -7855,22 +7558,13 @@ struct enb_cfg_upd_fail_ies_container { }; // ENBConfigurationUpdateFailure ::= SEQUENCE -struct enb_cfg_upd_fail_s { - bool ext = false; - enb_cfg_upd_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using enb_cfg_upd_fail_s = elementary_procedure_option; // LAI-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using lai_ext_ies_o = s1ap_protocol_ext_empty_o; +using lai_ext_ies_o = protocol_ext_empty_o; // GERAN-Cell-ID-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using geran_cell_id_ext_ies_o = s1ap_protocol_ext_empty_o; +using geran_cell_id_ext_ies_o = protocol_ext_empty_o; using lai_ext_ies_container = protocol_ext_container_empty_l; @@ -7890,7 +7584,7 @@ struct lai_s { }; // TargetRNC-ID-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using target_rnc_id_ext_ies_o = s1ap_protocol_ext_empty_o; +using target_rnc_id_ext_ies_o = protocol_ext_empty_o; using geran_cell_id_ext_ies_container = protocol_ext_container_empty_l; @@ -7993,7 +7687,7 @@ private: }; // RIMTransfer-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using rim_transfer_ext_ies_o = s1ap_protocol_ext_empty_o; +using rim_transfer_ext_ies_o = protocol_ext_empty_o; using rim_transfer_ext_ies_container = protocol_ext_container_empty_l; @@ -8068,16 +7762,8 @@ struct enb_direct_info_transfer_ies_o { }; // ENBDirectInformationTransfer ::= SEQUENCE -struct enb_direct_info_transfer_s { - bool ext = false; - protocol_ie_container_l protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using enb_direct_info_transfer_s = + elementary_procedure_option >; // ENBStatusTransferIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct enb_status_transfer_ies_o { @@ -8128,9 +7814,9 @@ struct enb_status_transfer_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s enb_status_transfer_transparent_container; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s enb_status_transfer_transparent_container; // sequence methods enb_status_transfer_ies_container(); @@ -8140,16 +7826,7 @@ struct enb_status_transfer_ies_container { }; // ENBStatusTransfer ::= SEQUENCE -struct enb_status_transfer_s { - bool ext = false; - enb_status_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using enb_status_transfer_s = elementary_procedure_option; // EUTRANResponse ::= SEQUENCE struct eutran_resp_s { @@ -8223,16 +7900,16 @@ struct error_ind_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool mme_ue_s1ap_id_present = false; - bool enb_ue_s1ap_id_present = false; - bool cause_present = false; - bool crit_diagnostics_present = false; - bool s_tmsi_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; - ie_field_s s_tmsi; + bool mme_ue_s1ap_id_present = false; + bool enb_ue_s1ap_id_present = false; + bool cause_present = false; + bool crit_diagnostics_present = false; + bool s_tmsi_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; + ie_field_s s_tmsi; // sequence methods error_ind_ies_container(); @@ -8242,16 +7919,7 @@ struct error_ind_ies_container { }; // ErrorIndication ::= SEQUENCE -struct error_ind_s { - bool ext = false; - error_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using error_ind_s = elementary_procedure_option; // NumberOfMeasurementReportingLevels ::= ENUMERATED struct nof_meas_report_levels_opts { @@ -8298,7 +7966,7 @@ struct event_triggered_cell_load_report_resp_s { }; // ExpectedUEActivityBehaviour-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using expected_ue_activity_behaviour_ext_ies_o = s1ap_protocol_ext_empty_o; +using expected_ue_activity_behaviour_ext_ies_o = protocol_ext_empty_o; // SourceOfUEActivityBehaviourInformation ::= ENUMERATED struct source_of_ue_activity_behaviour_info_opts { @@ -8340,7 +8008,7 @@ struct expected_ho_interv_opts { typedef enumerated expected_ho_interv_e; // ExpectedUEBehaviour-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using expected_ue_behaviour_ext_ies_o = s1ap_protocol_ext_empty_o; +using expected_ue_behaviour_ext_ies_o = protocol_ext_empty_o; using expected_ue_behaviour_ext_ies_container = protocol_ext_container_empty_l; @@ -8485,9 +8153,9 @@ struct ho_cancel_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; // sequence methods ho_cancel_ies_container(); @@ -8497,16 +8165,7 @@ struct ho_cancel_ies_container { }; // HandoverCancel ::= SEQUENCE -struct ho_cancel_s { - bool ext = false; - ho_cancel_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_cancel_s = elementary_procedure_option; // HandoverCancelAcknowledgeIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct ho_cancel_ack_ies_o { @@ -8557,10 +8216,10 @@ struct ho_cancel_ack_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s crit_diagnostics; // sequence methods ho_cancel_ack_ies_container(); @@ -8570,16 +8229,7 @@ struct ho_cancel_ack_ies_container { }; // HandoverCancelAcknowledge ::= SEQUENCE -struct ho_cancel_ack_s { - bool ext = false; - ho_cancel_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_cancel_ack_s = elementary_procedure_option; // HandoverType ::= ENUMERATED struct handov_type_opts { @@ -8676,15 +8326,15 @@ struct ho_cmd_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool nas_security_paramsfrom_e_utran_present = false; - bool erab_subjectto_data_forwarding_list_present = false; - bool erab_to_release_list_ho_cmd_present = false; - bool target_to_source_transparent_container_secondary_present = false; - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s handov_type; - ie_field_s > nas_security_paramsfrom_e_utran; + bool nas_security_paramsfrom_e_utran_present = false; + bool erab_subjectto_data_forwarding_list_present = false; + bool erab_to_release_list_ho_cmd_present = false; + bool target_to_source_transparent_container_secondary_present = false; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s handov_type; + ie_field_s > nas_security_paramsfrom_e_utran; ie_field_s > erab_subjectto_data_forwarding_list; ie_field_s, 1, 256, true> > erab_to_release_list_ho_cmd; ie_field_s > target_to_source_transparent_container; @@ -8699,16 +8349,7 @@ struct ho_cmd_ies_container { }; // HandoverCommand ::= SEQUENCE -struct ho_cmd_s { - bool ext = false; - ho_cmd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_cmd_s = elementary_procedure_option; // HandoverFailureIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct ho_fail_ies_o { @@ -8761,10 +8402,10 @@ struct ho_fail_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods ho_fail_ies_container(); @@ -8774,16 +8415,7 @@ struct ho_fail_ies_container { }; // HandoverFailure ::= SEQUENCE -struct ho_fail_s { - bool ext = false; - ho_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_fail_s = elementary_procedure_option; // HandoverNotifyIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct ho_notify_ies_o { @@ -8851,16 +8483,16 @@ struct ho_notify_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool tunnel_info_for_bbf_present = false; - bool lhn_id_present = false; - bool ps_cell_info_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s eutran_cgi; - ie_field_s tai; - ie_field_s tunnel_info_for_bbf; - ie_field_s > lhn_id; - ie_field_s ps_cell_info; + bool tunnel_info_for_bbf_present = false; + bool lhn_id_present = false; + bool ps_cell_info_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s eutran_cgi; + ie_field_s tai; + ie_field_s tunnel_info_for_bbf; + ie_field_s > lhn_id; + ie_field_s ps_cell_info; // sequence methods ho_notify_ies_container(); @@ -8870,16 +8502,7 @@ struct ho_notify_ies_container { }; // HandoverNotify ::= SEQUENCE -struct ho_notify_s { - bool ext = false; - ho_notify_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_notify_s = elementary_procedure_option; // HandoverPreparationFailureIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct ho_prep_fail_ies_o { @@ -8932,11 +8555,11 @@ struct ho_prep_fail_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods ho_prep_fail_ies_container(); @@ -8946,19 +8569,10 @@ struct ho_prep_fail_ies_container { }; // HandoverPreparationFailure ::= SEQUENCE -struct ho_prep_fail_s { - bool ext = false; - ho_prep_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_prep_fail_s = elementary_procedure_option; // MBSFN-ResultToLogInfo-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using mbsfn_result_to_log_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using mbsfn_result_to_log_info_ext_ies_o = protocol_ext_empty_o; using mbsfn_result_to_log_info_ext_ies_container = protocol_ext_container_empty_l; @@ -8987,7 +8601,7 @@ struct links_to_log_opts { typedef enumerated links_to_log_e; // LoggedMBSFNMDT-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using logged_mbsfnmdt_ext_ies_o = s1ap_protocol_ext_empty_o; +using logged_mbsfnmdt_ext_ies_o = protocol_ext_empty_o; // LoggingDuration ::= ENUMERATED struct logging_dur_opts { @@ -9010,7 +8624,7 @@ struct logging_interv_opts { typedef enumerated logging_interv_e; // M3Configuration-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using m3_cfg_ext_ies_o = s1ap_protocol_ext_empty_o; +using m3_cfg_ext_ies_o = protocol_ext_empty_o; // M3period ::= ENUMERATED struct m3period_opts { @@ -9035,7 +8649,7 @@ struct m3period_opts { typedef enumerated m3period_e; // M4Configuration-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using m4_cfg_ext_ies_o = s1ap_protocol_ext_empty_o; +using m4_cfg_ext_ies_o = protocol_ext_empty_o; // M4period ::= ENUMERATED struct m4period_opts { @@ -9048,7 +8662,7 @@ struct m4period_opts { typedef enumerated m4period_e; // M5Configuration-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using m5_cfg_ext_ies_o = s1ap_protocol_ext_empty_o; +using m5_cfg_ext_ies_o = protocol_ext_empty_o; // M5period ::= ENUMERATED struct m5period_opts { @@ -9061,7 +8675,7 @@ struct m5period_opts { typedef enumerated m5period_e; // M6Configuration-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using m6_cfg_ext_ies_o = s1ap_protocol_ext_empty_o; +using m6_cfg_ext_ies_o = protocol_ext_empty_o; // M6delay-threshold ::= ENUMERATED struct m6delay_thres_opts { @@ -9084,7 +8698,7 @@ struct m6report_interv_opts { typedef enumerated m6report_interv_e; // M7Configuration-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using m7_cfg_ext_ies_o = s1ap_protocol_ext_empty_o; +using m7_cfg_ext_ies_o = protocol_ext_empty_o; // MBSFN-ResultToLog ::= SEQUENCE (SIZE (1..8)) OF MBSFN-ResultToLogInfo using mbsfn_result_to_log_l = dyn_array; @@ -9101,7 +8715,7 @@ typedef enumerated wlan_meas_cfg_e; using wlan_meas_cfg_name_list_l = bounded_array, 4>; // WLANMeasurementConfiguration-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using wlan_meas_cfg_ext_ies_o = s1ap_protocol_ext_empty_o; +using wlan_meas_cfg_ext_ies_o = protocol_ext_empty_o; using logged_mbsfnmdt_ext_ies_container = protocol_ext_container_empty_l; @@ -9123,10 +8737,10 @@ struct logged_mbsfnmdt_s { }; // M1PeriodicReporting-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using m1_periodic_report_ext_ies_o = s1ap_protocol_ext_empty_o; +using m1_periodic_report_ext_ies_o = protocol_ext_empty_o; // M1ThresholdEventA2-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using m1_thres_event_a2_ext_ies_o = s1ap_protocol_ext_empty_o; +using m1_thres_event_a2_ext_ies_o = protocol_ext_empty_o; using m3_cfg_ext_ies_container = protocol_ext_container_empty_l; @@ -9908,7 +9522,7 @@ struct request_type_ext_ies_o { }; // SecurityContext-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using security_context_ext_ies_o = s1ap_protocol_ext_empty_o; +using security_context_ext_ies_o = protocol_ext_empty_o; // TraceActivation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION struct trace_activation_ext_ies_o { @@ -9970,13 +9584,13 @@ struct trace_depth_opts { typedef enumerated trace_depth_e; // UE-Sidelink-Aggregate-MaximumBitrates-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ue_sidelink_aggregate_maximum_bitrates_ext_ies_o = s1ap_protocol_ext_empty_o; +using ue_sidelink_aggregate_maximum_bitrates_ext_ies_o = protocol_ext_empty_o; // UESecurityCapabilities-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ue_security_cap_ext_ies_o = s1ap_protocol_ext_empty_o; +using ue_security_cap_ext_ies_o = protocol_ext_empty_o; // V2XServicesAuthorized-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using v2xservices_authorized_ext_ies_o = s1ap_protocol_ext_empty_o; +using v2xservices_authorized_ext_ies_o = protocol_ext_empty_o; // VehicleUE ::= ENUMERATED struct vehicle_ue_opts { @@ -10304,34 +9918,34 @@ struct ho_request_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool ho_restrict_list_present = false; - bool trace_activation_present = false; - bool request_type_present = false; - bool srvcc_operation_possible_present = false; - bool nas_security_paramsto_e_utran_present = false; - bool csg_id_present = false; - bool csg_membership_status_present = false; - bool gummei_id_present = false; - bool mme_ue_s1ap_id_minus2_present = false; - bool management_based_mdt_allowed_present = false; - bool management_based_mdtplmn_list_present = false; - bool masked_imeisv_present = false; - bool expected_ue_behaviour_present = false; - bool pro_se_authorized_present = false; - bool ueuser_plane_cio_tsupport_ind_present = false; - bool v2xservices_authorized_present = false; - bool ue_sidelink_aggregate_maximum_bitrate_present = false; - bool enhanced_coverage_restricted_present = false; - bool nrue_security_cap_present = false; - bool ce_mode_brestricted_present = false; - bool aerial_uesubscription_info_present = false; - bool pending_data_ind_present = false; - bool subscription_based_ue_differentiation_info_present = false; - bool add_rrm_prio_idx_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s handov_type; - ie_field_s cause; - ie_field_s ueaggregate_maximum_bitrate; + bool ho_restrict_list_present = false; + bool trace_activation_present = false; + bool request_type_present = false; + bool srvcc_operation_possible_present = false; + bool nas_security_paramsto_e_utran_present = false; + bool csg_id_present = false; + bool csg_membership_status_present = false; + bool gummei_id_present = false; + bool mme_ue_s1ap_id_minus2_present = false; + bool management_based_mdt_allowed_present = false; + bool management_based_mdtplmn_list_present = false; + bool masked_imeisv_present = false; + bool expected_ue_behaviour_present = false; + bool pro_se_authorized_present = false; + bool ueuser_plane_cio_tsupport_ind_present = false; + bool v2xservices_authorized_present = false; + bool ue_sidelink_aggregate_maximum_bitrate_present = false; + bool enhanced_coverage_restricted_present = false; + bool nrue_security_cap_present = false; + bool ce_mode_brestricted_present = false; + bool aerial_uesubscription_info_present = false; + bool pending_data_ind_present = false; + bool subscription_based_ue_differentiation_info_present = false; + bool add_rrm_prio_idx_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s handov_type; + ie_field_s cause; + ie_field_s ueaggregate_maximum_bitrate; ie_field_s > erab_to_be_setup_list_ho_req; ie_field_s > source_to_target_transparent_container; ie_field_s ue_security_cap; @@ -10369,16 +9983,7 @@ struct ho_request_ies_container { }; // HandoverRequest ::= SEQUENCE -struct ho_request_s { - bool ext = false; - ho_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_request_s = elementary_procedure_option; // CE-mode-B-SupportIndicator ::= ENUMERATED struct ce_mode_b_support_ind_opts { @@ -10471,8 +10076,8 @@ struct ho_request_ack_ies_container { bool crit_diagnostics_present = false; bool cell_access_mode_present = false; bool ce_mode_b_support_ind_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s > erab_admitted_list; ie_field_s > erab_failed_to_setup_list_ho_req_ack; ie_field_s > target_to_source_transparent_container; @@ -10489,19 +10094,10 @@ struct ho_request_ack_ies_container { }; // HandoverRequestAcknowledge ::= SEQUENCE -struct ho_request_ack_s { - bool ext = false; - ho_request_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_request_ack_s = elementary_procedure_option; // TargetNgRanNode-ID-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using target_ng_ran_node_id_ext_ies_o = s1ap_protocol_ext_empty_o; +using target_ng_ran_node_id_ext_ies_o = protocol_ext_empty_o; using target_ng_ran_node_id_ext_ies_container = protocol_ext_container_empty_l; @@ -10703,28 +10299,28 @@ struct ho_required_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool direct_forwarding_path_availability_present = false; - bool srvccho_ind_present = false; - bool source_to_target_transparent_container_secondary_present = false; - bool ms_classmark2_present = false; - bool ms_classmark3_present = false; - bool csg_id_present = false; - bool cell_access_mode_present = false; - bool ps_service_not_available_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s handov_type; - ie_field_s cause; - ie_field_s target_id; - ie_field_s direct_forwarding_path_availability; - ie_field_s srvccho_ind; - ie_field_s > source_to_target_transparent_container; - ie_field_s > source_to_target_transparent_container_secondary; - ie_field_s > ms_classmark2; - ie_field_s > ms_classmark3; - ie_field_s > csg_id; - ie_field_s cell_access_mode; - ie_field_s ps_service_not_available; + bool direct_forwarding_path_availability_present = false; + bool srvccho_ind_present = false; + bool source_to_target_transparent_container_secondary_present = false; + bool ms_classmark2_present = false; + bool ms_classmark3_present = false; + bool csg_id_present = false; + bool cell_access_mode_present = false; + bool ps_service_not_available_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s handov_type; + ie_field_s cause; + ie_field_s target_id; + ie_field_s direct_forwarding_path_availability; + ie_field_s srvccho_ind; + ie_field_s > source_to_target_transparent_container; + ie_field_s > source_to_target_transparent_container_secondary; + ie_field_s > ms_classmark2; + ie_field_s > ms_classmark3; + ie_field_s > csg_id; + ie_field_s cell_access_mode; + ie_field_s ps_service_not_available; // sequence methods ho_required_ies_container(); @@ -10734,16 +10330,7 @@ struct ho_required_ies_container { }; // HandoverRequired ::= SEQUENCE -struct ho_required_s { - bool ext = false; - ho_required_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ho_required_s = elementary_procedure_option; // MMEPagingTarget ::= CHOICE struct mme_paging_target_c { @@ -10796,7 +10383,7 @@ private: }; // RecommendedENBItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using recommended_enb_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using recommended_enb_item_ext_ies_o = protocol_ext_empty_o; using recommended_enb_item_ext_ies_container = protocol_ext_container_empty_l; @@ -10850,10 +10437,10 @@ struct recommended_enb_item_ies_o { using recommended_enb_list_l = bounded_array, 16>; // RecommendedENBsForPaging-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using recommended_enbs_for_paging_ext_ies_o = s1ap_protocol_ext_empty_o; +using recommended_enbs_for_paging_ext_ies_o = protocol_ext_empty_o; // InformationOnRecommendedCellsAndENBsForPaging-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using info_on_recommended_cells_and_enbs_for_paging_ext_ies_o = s1ap_protocol_ext_empty_o; +using info_on_recommended_cells_and_enbs_for_paging_ext_ies_o = protocol_ext_empty_o; using recommended_enbs_for_paging_ext_ies_container = protocol_ext_container_empty_l; @@ -10939,11 +10526,11 @@ struct init_context_setup_fail_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods init_context_setup_fail_ies_container(); @@ -10953,16 +10540,7 @@ struct init_context_setup_fail_ies_container { }; // InitialContextSetupFailure ::= SEQUENCE -struct init_context_setup_fail_s { - bool ext = false; - init_context_setup_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using init_context_setup_fail_s = elementary_procedure_option; // AdditionalCSFallbackIndicator ::= ENUMERATED struct add_cs_fallback_ind_opts { @@ -11137,35 +10715,35 @@ struct init_context_setup_request_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool trace_activation_present = false; - bool ho_restrict_list_present = false; - bool ue_radio_cap_present = false; - bool subscriber_profile_idfor_rfp_present = false; - bool cs_fallback_ind_present = false; - bool srvcc_operation_possible_present = false; - bool csg_membership_status_present = false; - bool registered_lai_present = false; - bool gummei_id_present = false; - bool mme_ue_s1ap_id_minus2_present = false; - bool management_based_mdt_allowed_present = false; - bool management_based_mdtplmn_list_present = false; - bool add_cs_fallback_ind_present = false; - bool masked_imeisv_present = false; - bool expected_ue_behaviour_present = false; - bool pro_se_authorized_present = false; - bool ueuser_plane_cio_tsupport_ind_present = false; - bool v2xservices_authorized_present = false; - bool ue_sidelink_aggregate_maximum_bitrate_present = false; - bool enhanced_coverage_restricted_present = false; - bool nrue_security_cap_present = false; - bool ce_mode_brestricted_present = false; - bool aerial_uesubscription_info_present = false; - bool pending_data_ind_present = false; - bool subscription_based_ue_differentiation_info_present = false; - bool add_rrm_prio_idx_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s ueaggregate_maximum_bitrate; + bool trace_activation_present = false; + bool ho_restrict_list_present = false; + bool ue_radio_cap_present = false; + bool subscriber_profile_idfor_rfp_present = false; + bool cs_fallback_ind_present = false; + bool srvcc_operation_possible_present = false; + bool csg_membership_status_present = false; + bool registered_lai_present = false; + bool gummei_id_present = false; + bool mme_ue_s1ap_id_minus2_present = false; + bool management_based_mdt_allowed_present = false; + bool management_based_mdtplmn_list_present = false; + bool add_cs_fallback_ind_present = false; + bool masked_imeisv_present = false; + bool expected_ue_behaviour_present = false; + bool pro_se_authorized_present = false; + bool ueuser_plane_cio_tsupport_ind_present = false; + bool v2xservices_authorized_present = false; + bool ue_sidelink_aggregate_maximum_bitrate_present = false; + bool enhanced_coverage_restricted_present = false; + bool nrue_security_cap_present = false; + bool ce_mode_brestricted_present = false; + bool aerial_uesubscription_info_present = false; + bool pending_data_ind_present = false; + bool subscription_based_ue_differentiation_info_present = false; + bool add_rrm_prio_idx_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s ueaggregate_maximum_bitrate; ie_field_s, 1, 256, true> > erab_to_be_setup_list_ctxt_su_req; ie_field_s ue_security_cap; @@ -11205,16 +10783,7 @@ struct init_context_setup_request_ies_container { }; // InitialContextSetupRequest ::= SEQUENCE -struct init_context_setup_request_s { - bool ext = false; - init_context_setup_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using init_context_setup_request_s = elementary_procedure_option; // InitialContextSetupResponseIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES struct init_context_setup_resp_ies_o { @@ -11276,10 +10845,10 @@ struct init_context_setup_resp_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_failed_to_setup_list_ctxt_su_res_present = false; - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_failed_to_setup_list_ctxt_su_res_present = false; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s, 1, 256, true> > erab_setup_list_ctxt_su_res; ie_field_s, 1, 256, true> > @@ -11294,16 +10863,7 @@ struct init_context_setup_resp_ies_container { }; // InitialContextSetupResponse ::= SEQUENCE -struct init_context_setup_resp_s { - bool ext = false; - init_context_setup_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using init_context_setup_resp_s = elementary_procedure_option; // Coverage-Level ::= ENUMERATED struct coverage_level_opts { @@ -11479,45 +11039,45 @@ struct init_ue_msg_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool s_tmsi_present = false; - bool csg_id_present = false; - bool gummei_id_present = false; - bool cell_access_mode_present = false; - bool gw_transport_layer_address_present = false; - bool relay_node_ind_present = false; - bool gummei_type_present = false; - bool tunnel_info_for_bbf_present = false; - bool sipto_l_gw_transport_layer_address_present = false; - bool lhn_id_present = false; - bool mme_group_id_present = false; - bool ue_usage_type_present = false; - bool ce_mode_b_support_ind_present = false; - bool dcn_id_present = false; - bool coverage_level_present = false; - bool ue_application_layer_meas_cap_present = false; - bool edt_session_present = false; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > nas_pdu; - ie_field_s tai; - ie_field_s eutran_cgi; - ie_field_s rrc_establishment_cause; - ie_field_s s_tmsi; - ie_field_s > csg_id; - ie_field_s gummei_id; - ie_field_s cell_access_mode; - ie_field_s > gw_transport_layer_address; - ie_field_s relay_node_ind; - ie_field_s gummei_type; - ie_field_s tunnel_info_for_bbf; - ie_field_s > sipto_l_gw_transport_layer_address; - ie_field_s > lhn_id; - ie_field_s > mme_group_id; - ie_field_s > ue_usage_type; - ie_field_s ce_mode_b_support_ind; - ie_field_s > dcn_id; - ie_field_s coverage_level; - ie_field_s > ue_application_layer_meas_cap; - ie_field_s edt_session; + bool s_tmsi_present = false; + bool csg_id_present = false; + bool gummei_id_present = false; + bool cell_access_mode_present = false; + bool gw_transport_layer_address_present = false; + bool relay_node_ind_present = false; + bool gummei_type_present = false; + bool tunnel_info_for_bbf_present = false; + bool sipto_l_gw_transport_layer_address_present = false; + bool lhn_id_present = false; + bool mme_group_id_present = false; + bool ue_usage_type_present = false; + bool ce_mode_b_support_ind_present = false; + bool dcn_id_present = false; + bool coverage_level_present = false; + bool ue_application_layer_meas_cap_present = false; + bool edt_session_present = false; + ie_field_s enb_ue_s1ap_id; + ie_field_s > nas_pdu; + ie_field_s tai; + ie_field_s eutran_cgi; + ie_field_s rrc_establishment_cause; + ie_field_s s_tmsi; + ie_field_s > csg_id; + ie_field_s gummei_id; + ie_field_s cell_access_mode; + ie_field_s > gw_transport_layer_address; + ie_field_s relay_node_ind; + ie_field_s gummei_type; + ie_field_s tunnel_info_for_bbf; + ie_field_s > sipto_l_gw_transport_layer_address; + ie_field_s > lhn_id; + ie_field_s > mme_group_id; + ie_field_s > ue_usage_type; + ie_field_s ce_mode_b_support_ind; + ie_field_s > dcn_id; + ie_field_s coverage_level; + ie_field_s > ue_application_layer_meas_cap; + ie_field_s edt_session; // sequence methods init_ue_msg_ies_container(); @@ -11527,22 +11087,13 @@ struct init_ue_msg_ies_container { }; // InitialUEMessage ::= SEQUENCE -struct init_ue_msg_s { - bool ext = false; - init_ue_msg_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using init_ue_msg_s = elementary_procedure_option; // UE-associatedLogicalS1-ConnectionItemExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ue_associated_lc_s1_conn_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using ue_associated_lc_s1_conn_item_ext_ies_o = protocol_ext_empty_o; // TAIItemExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using tai_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using tai_item_ext_ies_o = protocol_ext_empty_o; using ue_associated_lc_s1_conn_item_ext_ies_container = protocol_ext_container_empty_l; @@ -11564,7 +11115,7 @@ struct ue_associated_lc_s1_conn_item_s { }; // ServedDCNsItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using served_dcns_item_ext_ies_o = s1ap_protocol_ext_empty_o; +using served_dcns_item_ext_ies_o = protocol_ext_empty_o; // ServedGUMMEIsItem-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION struct served_gummeis_item_ext_ies_o { @@ -11624,7 +11175,7 @@ struct tai_item_s { }; // UE-S1AP-ID-pair-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using ue_s1ap_id_pair_ext_ies_o = s1ap_protocol_ext_empty_o; +using ue_s1ap_id_pair_ext_ies_o = protocol_ext_empty_o; // UE-associatedLogicalS1-ConnectionItemRes ::= OBJECT SET OF S1AP-PROTOCOL-IES struct ue_associated_lc_s1_conn_item_res_o { @@ -11687,7 +11238,7 @@ struct nb_io_t_paging_e_drx_cycle_opts { typedef enumerated nb_io_t_paging_e_drx_cycle_e; // NB-IoT-Paging-eDRXInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using nb_io_t_paging_e_drx_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using nb_io_t_paging_e_drx_info_ext_ies_o = protocol_ext_empty_o; // NB-IoT-PagingTimeWindow ::= ENUMERATED struct nb_io_t_paging_time_win_opts { @@ -11745,7 +11296,7 @@ struct paging_e_drx_cycle_opts { typedef enumerated paging_e_drx_cycle_e; // Paging-eDRXInformation-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using paging_e_drx_info_ext_ies_o = s1ap_protocol_ext_empty_o; +using paging_e_drx_info_ext_ies_o = protocol_ext_empty_o; // PagingTimeWindow ::= ENUMERATED struct paging_time_win_opts { @@ -15235,16 +14786,7 @@ struct kill_request_ies_container { }; // KillRequest ::= SEQUENCE -struct kill_request_s { - bool ext = false; - kill_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using kill_request_s = elementary_procedure_option; struct kill_resp_ies_container { template @@ -15266,29 +14808,20 @@ struct kill_resp_ies_container { }; // KillResponse ::= SEQUENCE -struct kill_resp_s { - bool ext = false; - kill_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using kill_resp_s = elementary_procedure_option; struct location_report_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ps_cell_info_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s eutran_cgi; - ie_field_s tai; - ie_field_s request_type; - ie_field_s ps_cell_info; + bool ps_cell_info_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s eutran_cgi; + ie_field_s tai; + ie_field_s request_type; + ie_field_s ps_cell_info; // sequence methods location_report_ies_container(); @@ -15298,25 +14831,16 @@ struct location_report_ies_container { }; // LocationReport ::= SEQUENCE -struct location_report_s { - bool ext = false; - location_report_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using location_report_s = elementary_procedure_option; struct location_report_ctrl_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s request_type; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s request_type; // sequence methods location_report_ctrl_ies_container(); @@ -15326,25 +14850,16 @@ struct location_report_ctrl_ies_container { }; // LocationReportingControl ::= SEQUENCE -struct location_report_ctrl_s { - bool ext = false; - location_report_ctrl_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using location_report_ctrl_s = elementary_procedure_option; struct location_report_fail_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; // sequence methods location_report_fail_ind_ies_container(); @@ -15354,24 +14869,15 @@ struct location_report_fail_ind_ies_container { }; // LocationReportingFailureIndication ::= SEQUENCE -struct location_report_fail_ind_s { - bool ext = false; - location_report_fail_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using location_report_fail_ind_s = elementary_procedure_option; struct mmecp_relocation_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; // sequence methods mmecp_relocation_ind_ies_container(); @@ -15381,16 +14887,7 @@ struct mmecp_relocation_ind_ies_container { }; // MMECPRelocationIndication ::= SEQUENCE -struct mmecp_relocation_ind_s { - bool ext = false; - mmecp_relocation_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using mmecp_relocation_ind_s = elementary_procedure_option; struct mme_cfg_transfer_ies_container { template @@ -15410,16 +14907,7 @@ struct mme_cfg_transfer_ies_container { }; // MMEConfigurationTransfer ::= SEQUENCE -struct mme_cfg_transfer_s { - bool ext = false; - mme_cfg_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using mme_cfg_transfer_s = elementary_procedure_option; struct mme_cfg_upd_ies_container { template @@ -15443,28 +14931,10 @@ struct mme_cfg_upd_ies_container { }; // MMEConfigurationUpdate ::= SEQUENCE -struct mme_cfg_upd_s { - bool ext = false; - mme_cfg_upd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using mme_cfg_upd_s = elementary_procedure_option; // MMEConfigurationUpdateAcknowledge ::= SEQUENCE -struct mme_cfg_upd_ack_s { - bool ext = false; - protocol_ie_container_l protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using mme_cfg_upd_ack_s = elementary_procedure_option >; struct mme_cfg_upd_fail_ies_container { template @@ -15485,37 +14955,20 @@ struct mme_cfg_upd_fail_ies_container { }; // MMEConfigurationUpdateFailure ::= SEQUENCE -struct mme_cfg_upd_fail_s { - bool ext = false; - mme_cfg_upd_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using mme_cfg_upd_fail_s = elementary_procedure_option; // MMEDirectInformationTransfer ::= SEQUENCE -struct mme_direct_info_transfer_s { - bool ext = false; - protocol_ie_container_l protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using mme_direct_info_transfer_s = + elementary_procedure_option >; struct mme_status_transfer_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s enb_status_transfer_transparent_container; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s enb_status_transfer_transparent_container; // sequence methods mme_status_transfer_ies_container(); @@ -15525,24 +14978,15 @@ struct mme_status_transfer_ies_container { }; // MMEStatusTransfer ::= SEQUENCE -struct mme_status_transfer_s { - bool ext = false; - mme_status_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using mme_status_transfer_s = elementary_procedure_option; struct nas_delivery_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; // sequence methods nas_delivery_ind_ies_container(); @@ -15552,26 +14996,17 @@ struct nas_delivery_ind_ies_container { }; // NASDeliveryIndication ::= SEQUENCE -struct nas_delivery_ind_s { - bool ext = false; - nas_delivery_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using nas_delivery_ind_s = elementary_procedure_option; struct nas_non_delivery_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > nas_pdu; - ie_field_s cause; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > nas_pdu; + ie_field_s cause; // sequence methods nas_non_delivery_ind_ies_container(); @@ -15581,16 +15016,7 @@ struct nas_non_delivery_ind_ies_container { }; // NASNonDeliveryIndication ::= SEQUENCE -struct nas_non_delivery_ind_s { - bool ext = false; - nas_non_delivery_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using nas_non_delivery_ind_s = elementary_procedure_option; struct overload_start_ies_container { template @@ -15611,28 +15037,10 @@ struct overload_start_ies_container { }; // OverloadStart ::= SEQUENCE -struct overload_start_s { - bool ext = false; - overload_start_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using overload_start_s = elementary_procedure_option; // OverloadStop ::= SEQUENCE -struct overload_stop_s { - bool ext = false; - protocol_ie_container_l protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using overload_stop_s = elementary_procedure_option >; struct pws_fail_ind_ies_container { template @@ -15650,16 +15058,7 @@ struct pws_fail_ind_ies_container { }; // PWSFailureIndication ::= SEQUENCE -struct pws_fail_ind_s { - bool ext = false; - pws_fail_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pws_fail_ind_s = elementary_procedure_option; struct pws_restart_ind_ies_container { template @@ -15680,16 +15079,7 @@ struct pws_restart_ind_ies_container { }; // PWSRestartIndication ::= SEQUENCE -struct pws_restart_ind_s { - bool ext = false; - pws_restart_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using pws_restart_ind_s = elementary_procedure_option; struct paging_ies_container { template @@ -15731,16 +15121,7 @@ struct paging_ies_container { }; // Paging ::= SEQUENCE -struct paging_s { - bool ext = false; - paging_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using paging_s = elementary_procedure_option; struct path_switch_request_ies_container { template @@ -15756,7 +15137,7 @@ struct path_switch_request_ies_container { bool rrc_resume_cause_present = false; bool nrue_security_cap_present = false; bool ps_cell_info_present = false; - ie_field_s > enb_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s > erab_to_be_switched_dl_list; ie_field_s > source_mme_ue_s1ap_id; ie_field_s eutran_cgi; @@ -15780,43 +15161,34 @@ struct path_switch_request_ies_container { }; // PathSwitchRequest ::= SEQUENCE -struct path_switch_request_s { - bool ext = false; - path_switch_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using path_switch_request_s = elementary_procedure_option; struct path_switch_request_ack_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ueaggregate_maximum_bitrate_present = false; - bool erab_to_be_switched_ul_list_present = false; - bool erab_to_be_released_list_present = false; - bool crit_diagnostics_present = false; - bool mme_ue_s1ap_id_minus2_present = false; - bool csg_membership_status_present = false; - bool pro_se_authorized_present = false; - bool ueuser_plane_cio_tsupport_ind_present = false; - bool v2xservices_authorized_present = false; - bool ue_sidelink_aggregate_maximum_bitrate_present = false; - bool enhanced_coverage_restricted_present = false; - bool nrue_security_cap_present = false; - bool ce_mode_brestricted_present = false; - bool aerial_uesubscription_info_present = false; - bool pending_data_ind_present = false; - bool subscription_based_ue_differentiation_info_present = false; - bool ho_restrict_list_present = false; - bool add_rrm_prio_idx_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s ueaggregate_maximum_bitrate; + bool ueaggregate_maximum_bitrate_present = false; + bool erab_to_be_switched_ul_list_present = false; + bool erab_to_be_released_list_present = false; + bool crit_diagnostics_present = false; + bool mme_ue_s1ap_id_minus2_present = false; + bool csg_membership_status_present = false; + bool pro_se_authorized_present = false; + bool ueuser_plane_cio_tsupport_ind_present = false; + bool v2xservices_authorized_present = false; + bool ue_sidelink_aggregate_maximum_bitrate_present = false; + bool enhanced_coverage_restricted_present = false; + bool nrue_security_cap_present = false; + bool ce_mode_brestricted_present = false; + bool aerial_uesubscription_info_present = false; + bool pending_data_ind_present = false; + bool subscription_based_ue_differentiation_info_present = false; + bool ho_restrict_list_present = false; + bool add_rrm_prio_idx_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s ueaggregate_maximum_bitrate; ie_field_s > erab_to_be_switched_ul_list; ie_field_s, 1, 256, true> > erab_to_be_released_list; ie_field_s security_context; @@ -15844,27 +15216,18 @@ struct path_switch_request_ack_ies_container { }; // PathSwitchRequestAcknowledge ::= SEQUENCE -struct path_switch_request_ack_s { - bool ext = false; - path_switch_request_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using path_switch_request_ack_s = elementary_procedure_option; struct path_switch_request_fail_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods path_switch_request_fail_ies_container(); @@ -15874,16 +15237,7 @@ struct path_switch_request_fail_ies_container { }; // PathSwitchRequestFailure ::= SEQUENCE -struct path_switch_request_fail_s { - bool ext = false; - path_switch_request_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using path_switch_request_fail_s = elementary_procedure_option; template struct private_ie_container_item_s { @@ -15926,15 +15280,15 @@ struct reroute_nas_request_ies_container { using ie_field_s = protocol_ie_container_item_s; // member variables - bool mme_ue_s1ap_id_present = false; - bool add_guti_present = false; - bool ue_usage_type_present = false; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > s1_msg; - ie_field_s > mme_group_id; - ie_field_s add_guti; - ie_field_s > ue_usage_type; + bool mme_ue_s1ap_id_present = false; + bool add_guti_present = false; + bool ue_usage_type_present = false; + ie_field_s enb_ue_s1ap_id; + ie_field_s mme_ue_s1ap_id; + ie_field_s > s1_msg; + ie_field_s > mme_group_id; + ie_field_s add_guti; + ie_field_s > ue_usage_type; // sequence methods reroute_nas_request_ies_container(); @@ -15944,16 +15298,7 @@ struct reroute_nas_request_ies_container { }; // RerouteNASRequest ::= SEQUENCE -struct reroute_nas_request_s { - bool ext = false; - reroute_nas_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using reroute_nas_request_s = elementary_procedure_option; struct reset_ies_container { template @@ -15971,16 +15316,7 @@ struct reset_ies_container { }; // Reset ::= SEQUENCE -struct reset_s { - bool ext = false; - reset_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using reset_s = elementary_procedure_option; struct reset_ack_ies_container { template @@ -16001,28 +15337,10 @@ struct reset_ack_ies_container { }; // ResetAcknowledge ::= SEQUENCE -struct reset_ack_s { - bool ext = false; - reset_ack_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using reset_ack_s = elementary_procedure_option; // RetrieveUEInformation ::= SEQUENCE -struct retrieve_ue_info_s { - bool ext = false; - protocol_ie_container_l protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using retrieve_ue_info_s = elementary_procedure_option >; struct s1_setup_fail_ies_container { template @@ -16043,16 +15361,7 @@ struct s1_setup_fail_ies_container { }; // S1SetupFailure ::= SEQUENCE -struct s1_setup_fail_s { - bool ext = false; - s1_setup_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using s1_setup_fail_s = elementary_procedure_option; struct s1_setup_request_ies_container { template @@ -16081,16 +15390,7 @@ struct s1_setup_request_ies_container { }; // S1SetupRequest ::= SEQUENCE -struct s1_setup_request_s { - bool ext = false; - s1_setup_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using s1_setup_request_s = elementary_procedure_option; struct s1_setup_resp_ies_container { template @@ -16118,27 +15418,18 @@ struct s1_setup_resp_ies_container { }; // S1SetupResponse ::= SEQUENCE -struct s1_setup_resp_s { - bool ext = false; - s1_setup_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using s1_setup_resp_s = elementary_procedure_option; struct secondary_rat_data_usage_report_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ho_flag_present = false; - bool user_location_info_present = false; - bool time_since_secondary_node_release_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool ho_flag_present = false; + bool user_location_info_present = false; + bool time_since_secondary_node_release_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s, 1, 256, true> > secondary_rat_data_usage_report_list; ie_field_s ho_flag; @@ -16153,26 +15444,17 @@ struct secondary_rat_data_usage_report_ies_container { }; // SecondaryRATDataUsageReport ::= SEQUENCE -struct secondary_rat_data_usage_report_s { - bool ext = false; - secondary_rat_data_usage_report_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using secondary_rat_data_usage_report_s = elementary_procedure_option; struct trace_fail_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > e_utran_trace_id; - ie_field_s cause; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > e_utran_trace_id; + ie_field_s cause; // sequence methods trace_fail_ind_ies_container(); @@ -16182,25 +15464,16 @@ struct trace_fail_ind_ies_container { }; // TraceFailureIndication ::= SEQUENCE -struct trace_fail_ind_s { - bool ext = false; - trace_fail_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using trace_fail_ind_s = elementary_procedure_option; struct trace_start_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s trace_activation; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s trace_activation; // sequence methods trace_start_ies_container(); @@ -16210,31 +15483,22 @@ struct trace_start_ies_container { }; // TraceStart ::= SEQUENCE -struct trace_start_s { - bool ext = false; - trace_start_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using trace_start_s = elementary_procedure_option; struct ue_cap_info_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ue_radio_cap_for_paging_present = false; - bool ue_application_layer_meas_cap_present = false; - bool lte_m_ind_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > ue_radio_cap; - ie_field_s > ue_radio_cap_for_paging; - ie_field_s > ue_application_layer_meas_cap; - ie_field_s lte_m_ind; + bool ue_radio_cap_for_paging_present = false; + bool ue_application_layer_meas_cap_present = false; + bool lte_m_ind_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > ue_radio_cap; + ie_field_s > ue_radio_cap_for_paging; + ie_field_s > ue_application_layer_meas_cap; + ie_field_s lte_m_ind; // sequence methods ue_cap_info_ind_ies_container(); @@ -16244,28 +15508,19 @@ struct ue_cap_info_ind_ies_container { }; // UECapabilityInfoIndication ::= SEQUENCE -struct ue_cap_info_ind_s { - bool ext = false; - ue_cap_info_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_cap_info_ind_s = elementary_procedure_option; struct ue_context_mod_confirm_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool csg_membership_status_present = false; - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s csg_membership_status; - ie_field_s crit_diagnostics; + bool csg_membership_status_present = false; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s csg_membership_status; + ie_field_s crit_diagnostics; // sequence methods ue_context_mod_confirm_ies_container(); @@ -16275,27 +15530,18 @@ struct ue_context_mod_confirm_ies_container { }; // UEContextModificationConfirm ::= SEQUENCE -struct ue_context_mod_confirm_s { - bool ext = false; - ue_context_mod_confirm_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_mod_confirm_s = elementary_procedure_option; struct ue_context_mod_fail_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods ue_context_mod_fail_ies_container(); @@ -16305,26 +15551,17 @@ struct ue_context_mod_fail_ies_container { }; // UEContextModificationFailure ::= SEQUENCE -struct ue_context_mod_fail_s { - bool ext = false; - ue_context_mod_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_mod_fail_s = elementary_procedure_option; struct ue_context_mod_ind_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool csg_membership_info_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s csg_membership_info; + bool csg_membership_info_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s csg_membership_info; // sequence methods ue_context_mod_ind_ies_container(); @@ -16334,56 +15571,47 @@ struct ue_context_mod_ind_ies_container { }; // UEContextModificationIndication ::= SEQUENCE -struct ue_context_mod_ind_s { - bool ext = false; - ue_context_mod_ind_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_mod_ind_s = elementary_procedure_option; struct ue_context_mod_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool security_key_present = false; - bool subscriber_profile_idfor_rfp_present = false; - bool ueaggregate_maximum_bitrate_present = false; - bool cs_fallback_ind_present = false; - bool ue_security_cap_present = false; - bool csg_membership_status_present = false; - bool registered_lai_present = false; - bool add_cs_fallback_ind_present = false; - bool pro_se_authorized_present = false; - bool srvcc_operation_possible_present = false; - bool srvcc_operation_not_possible_present = false; - bool v2xservices_authorized_present = false; - bool ue_sidelink_aggregate_maximum_bitrate_present = false; - bool nrue_security_cap_present = false; - bool aerial_uesubscription_info_present = false; - bool add_rrm_prio_idx_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > security_key; - ie_field_s > subscriber_profile_idfor_rfp; - ie_field_s ueaggregate_maximum_bitrate; - ie_field_s cs_fallback_ind; - ie_field_s ue_security_cap; - ie_field_s csg_membership_status; - ie_field_s registered_lai; - ie_field_s add_cs_fallback_ind; - ie_field_s pro_se_authorized; - ie_field_s srvcc_operation_possible; - ie_field_s srvcc_operation_not_possible; - ie_field_s v2xservices_authorized; - ie_field_s ue_sidelink_aggregate_maximum_bitrate; - ie_field_s nrue_security_cap; - ie_field_s aerial_uesubscription_info; - ie_field_s > add_rrm_prio_idx; + bool security_key_present = false; + bool subscriber_profile_idfor_rfp_present = false; + bool ueaggregate_maximum_bitrate_present = false; + bool cs_fallback_ind_present = false; + bool ue_security_cap_present = false; + bool csg_membership_status_present = false; + bool registered_lai_present = false; + bool add_cs_fallback_ind_present = false; + bool pro_se_authorized_present = false; + bool srvcc_operation_possible_present = false; + bool srvcc_operation_not_possible_present = false; + bool v2xservices_authorized_present = false; + bool ue_sidelink_aggregate_maximum_bitrate_present = false; + bool nrue_security_cap_present = false; + bool aerial_uesubscription_info_present = false; + bool add_rrm_prio_idx_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > security_key; + ie_field_s > subscriber_profile_idfor_rfp; + ie_field_s ueaggregate_maximum_bitrate; + ie_field_s cs_fallback_ind; + ie_field_s ue_security_cap; + ie_field_s csg_membership_status; + ie_field_s registered_lai; + ie_field_s add_cs_fallback_ind; + ie_field_s pro_se_authorized; + ie_field_s srvcc_operation_possible; + ie_field_s srvcc_operation_not_possible; + ie_field_s v2xservices_authorized; + ie_field_s ue_sidelink_aggregate_maximum_bitrate; + ie_field_s nrue_security_cap; + ie_field_s aerial_uesubscription_info; + ie_field_s > add_rrm_prio_idx; // sequence methods ue_context_mod_request_ies_container(); @@ -16393,26 +15621,17 @@ struct ue_context_mod_request_ies_container { }; // UEContextModificationRequest ::= SEQUENCE -struct ue_context_mod_request_s { - bool ext = false; - ue_context_mod_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_mod_request_s = elementary_procedure_option; struct ue_context_mod_resp_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s crit_diagnostics; // sequence methods ue_context_mod_resp_ies_container(); @@ -16422,16 +15641,7 @@ struct ue_context_mod_resp_ies_container { }; // UEContextModificationResponse ::= SEQUENCE -struct ue_context_mod_resp_s { - bool ext = false; - ue_context_mod_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_mod_resp_s = elementary_procedure_option; struct ue_context_release_cmd_ies_container { template @@ -16449,32 +15659,23 @@ struct ue_context_release_cmd_ies_container { }; // UEContextReleaseCommand ::= SEQUENCE -struct ue_context_release_cmd_s { - bool ext = false; - ue_context_release_cmd_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_release_cmd_s = elementary_procedure_option; struct ue_context_release_complete_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - bool user_location_info_present = false; - bool info_on_recommended_cells_and_enbs_for_paging_present = false; - bool cell_id_and_ce_level_for_ce_capable_ues_present = false; - bool secondary_rat_data_usage_report_list_present = false; - bool time_since_secondary_node_release_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s crit_diagnostics; - ie_field_s user_location_info; + bool crit_diagnostics_present = false; + bool user_location_info_present = false; + bool info_on_recommended_cells_and_enbs_for_paging_present = false; + bool cell_id_and_ce_level_for_ce_capable_ues_present = false; + bool secondary_rat_data_usage_report_list_present = false; + bool time_since_secondary_node_release_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s crit_diagnostics; + ie_field_s user_location_info; ie_field_s info_on_recommended_cells_and_enbs_for_paging; ie_field_s cell_id_and_ce_level_for_ce_capable_ues; ie_field_s, 1, 256, true> > @@ -16489,28 +15690,19 @@ struct ue_context_release_complete_ies_container { }; // UEContextReleaseComplete ::= SEQUENCE -struct ue_context_release_complete_s { - bool ext = false; - ue_context_release_complete_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_release_complete_s = elementary_procedure_option; struct ue_context_release_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool gw_context_release_ind_present = false; - bool secondary_rat_data_usage_report_list_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; - ie_field_s gw_context_release_ind; + bool gw_context_release_ind_present = false; + bool secondary_rat_data_usage_report_list_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; + ie_field_s gw_context_release_ind; ie_field_s, 1, 256, true> > secondary_rat_data_usage_report_list; @@ -16522,27 +15714,18 @@ struct ue_context_release_request_ies_container { }; // UEContextReleaseRequest ::= SEQUENCE -struct ue_context_release_request_s { - bool ext = false; - ue_context_release_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_release_request_s = elementary_procedure_option; struct ue_context_resume_fail_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cause; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cause; + ie_field_s crit_diagnostics; // sequence methods ue_context_resume_fail_ies_container(); @@ -16552,26 +15735,17 @@ struct ue_context_resume_fail_ies_container { }; // UEContextResumeFailure ::= SEQUENCE -struct ue_context_resume_fail_s { - bool ext = false; - ue_context_resume_fail_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_resume_fail_s = elementary_procedure_option; struct ue_context_resume_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_failed_to_resume_list_resume_req_present = false; - bool rrc_resume_cause_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_failed_to_resume_list_resume_req_present = false; + bool rrc_resume_cause_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s > erab_failed_to_resume_list_resume_req; ie_field_s rrc_resume_cause; @@ -16584,28 +15758,19 @@ struct ue_context_resume_request_ies_container { }; // UEContextResumeRequest ::= SEQUENCE -struct ue_context_resume_request_s { - bool ext = false; - ue_context_resume_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_resume_request_s = elementary_procedure_option; struct ue_context_resume_resp_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool erab_failed_to_resume_list_resume_res_present = false; - bool crit_diagnostics_present = false; - bool security_context_present = false; - bool pending_data_ind_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool erab_failed_to_resume_list_resume_res_present = false; + bool crit_diagnostics_present = false; + bool security_context_present = false; + bool pending_data_ind_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s > erab_failed_to_resume_list_resume_res; ie_field_s crit_diagnostics; @@ -16620,29 +15785,20 @@ struct ue_context_resume_resp_ies_container { }; // UEContextResumeResponse ::= SEQUENCE -struct ue_context_resume_resp_s { - bool ext = false; - ue_context_resume_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_resume_resp_s = elementary_procedure_option; struct ue_context_suspend_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool info_on_recommended_cells_and_enbs_for_paging_present = false; - bool cell_id_and_ce_level_for_ce_capable_ues_present = false; - bool secondary_rat_data_usage_report_list_present = false; - bool user_location_info_present = false; - bool time_since_secondary_node_release_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; + bool info_on_recommended_cells_and_enbs_for_paging_present = false; + bool cell_id_and_ce_level_for_ce_capable_ues_present = false; + bool secondary_rat_data_usage_report_list_present = false; + bool user_location_info_present = false; + bool time_since_secondary_node_release_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; ie_field_s info_on_recommended_cells_and_enbs_for_paging; ie_field_s cell_id_and_ce_level_for_ce_capable_ues; ie_field_s, 1, 256, true> > @@ -16658,28 +15814,19 @@ struct ue_context_suspend_request_ies_container { }; // UEContextSuspendRequest ::= SEQUENCE -struct ue_context_suspend_request_s { - bool ext = false; - ue_context_suspend_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_suspend_request_s = elementary_procedure_option; struct ue_context_suspend_resp_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - bool security_context_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s crit_diagnostics; - ie_field_s security_context; + bool crit_diagnostics_present = false; + bool security_context_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s crit_diagnostics; + ie_field_s security_context; // sequence methods ue_context_suspend_resp_ies_container(); @@ -16689,16 +15836,7 @@ struct ue_context_suspend_resp_ies_container { }; // UEContextSuspendResponse ::= SEQUENCE -struct ue_context_suspend_resp_s { - bool ext = false; - ue_context_suspend_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_context_suspend_resp_s = elementary_procedure_option; struct ue_info_transfer_ies_container { template @@ -16723,26 +15861,17 @@ struct ue_info_transfer_ies_container { }; // UEInformationTransfer ::= SEQUENCE -struct ue_info_transfer_s { - bool ext = false; - ue_info_transfer_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_info_transfer_s = elementary_procedure_option; struct ue_radio_cap_match_request_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool ue_radio_cap_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > ue_radio_cap; + bool ue_radio_cap_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > ue_radio_cap; // sequence methods ue_radio_cap_match_request_ies_container(); @@ -16752,27 +15881,18 @@ struct ue_radio_cap_match_request_ies_container { }; // UERadioCapabilityMatchRequest ::= SEQUENCE -struct ue_radio_cap_match_request_s { - bool ext = false; - ue_radio_cap_match_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_radio_cap_match_request_s = elementary_procedure_option; struct ue_radio_cap_match_resp_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool crit_diagnostics_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s voice_support_match_ind; - ie_field_s crit_diagnostics; + bool crit_diagnostics_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s voice_support_match_ind; + ie_field_s crit_diagnostics; // sequence methods ue_radio_cap_match_resp_ies_container(); @@ -16782,35 +15902,26 @@ struct ue_radio_cap_match_resp_ies_container { }; // UERadioCapabilityMatchResponse ::= SEQUENCE -struct ue_radio_cap_match_resp_s { - bool ext = false; - ue_radio_cap_match_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ue_radio_cap_match_resp_s = elementary_procedure_option; struct ul_nas_transport_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool gw_transport_layer_address_present = false; - bool sipto_l_gw_transport_layer_address_present = false; - bool lhn_id_present = false; - bool ps_cell_info_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > nas_pdu; - ie_field_s eutran_cgi; - ie_field_s tai; - ie_field_s > gw_transport_layer_address; - ie_field_s > sipto_l_gw_transport_layer_address; - ie_field_s > lhn_id; - ie_field_s ps_cell_info; + bool gw_transport_layer_address_present = false; + bool sipto_l_gw_transport_layer_address_present = false; + bool lhn_id_present = false; + bool ps_cell_info_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > nas_pdu; + ie_field_s eutran_cgi; + ie_field_s tai; + ie_field_s > gw_transport_layer_address; + ie_field_s > sipto_l_gw_transport_layer_address; + ie_field_s > lhn_id; + ie_field_s ps_cell_info; // sequence methods ul_nas_transport_ies_container(); @@ -16820,16 +15931,7 @@ struct ul_nas_transport_ies_container { }; // UplinkNASTransport ::= SEQUENCE -struct ul_nas_transport_s { - bool ext = false; - ul_nas_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_nas_transport_s = elementary_procedure_option; struct ul_non_ueassociated_lp_pa_transport_ies_container { template @@ -16847,35 +15949,27 @@ struct ul_non_ueassociated_lp_pa_transport_ies_container { }; // UplinkNonUEAssociatedLPPaTransport ::= SEQUENCE -struct ul_non_ueassociated_lp_pa_transport_s { - bool ext = false; - ul_non_ueassociated_lp_pa_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_non_ueassociated_lp_pa_transport_s = + elementary_procedure_option; struct ul_s1cdma2000tunnelling_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - bool cdma2000_ho_required_ind_present = false; - bool cdma2000_one_xsrvcc_info_present = false; - bool cdma2000_one_xrand_present = false; - bool eutran_round_trip_delay_estimation_info_present = false; - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s cdma2000_rat_type; - ie_field_s > cdma2000_sector_id; - ie_field_s cdma2000_ho_required_ind; - ie_field_s cdma2000_one_xsrvcc_info; - ie_field_s > cdma2000_one_xrand; - ie_field_s > cdma2000_pdu; - ie_field_s > eutran_round_trip_delay_estimation_info; + bool cdma2000_ho_required_ind_present = false; + bool cdma2000_one_xsrvcc_info_present = false; + bool cdma2000_one_xrand_present = false; + bool eutran_round_trip_delay_estimation_info_present = false; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s cdma2000_rat_type; + ie_field_s > cdma2000_sector_id; + ie_field_s cdma2000_ho_required_ind; + ie_field_s cdma2000_one_xsrvcc_info; + ie_field_s > cdma2000_one_xrand; + ie_field_s > cdma2000_pdu; + ie_field_s > eutran_round_trip_delay_estimation_info; // sequence methods ul_s1cdma2000tunnelling_ies_container(); @@ -16885,26 +15979,17 @@ struct ul_s1cdma2000tunnelling_ies_container { }; // UplinkS1cdma2000tunnelling ::= SEQUENCE -struct ul_s1cdma2000tunnelling_s { - bool ext = false; - ul_s1cdma2000tunnelling_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_s1cdma2000tunnelling_s = elementary_procedure_option; struct ul_ueassociated_lp_pa_transport_ies_container { template using ie_field_s = protocol_ie_container_item_s; // member variables - ie_field_s > mme_ue_s1ap_id; - ie_field_s > enb_ue_s1ap_id; - ie_field_s > routing_id; - ie_field_s > lp_pa_pdu; + ie_field_s mme_ue_s1ap_id; + ie_field_s enb_ue_s1ap_id; + ie_field_s > routing_id; + ie_field_s > lp_pa_pdu; // sequence methods ul_ueassociated_lp_pa_transport_ies_container(); @@ -16914,16 +15999,7 @@ struct ul_ueassociated_lp_pa_transport_ies_container { }; // UplinkUEAssociatedLPPaTransport ::= SEQUENCE -struct ul_ueassociated_lp_pa_transport_s { - bool ext = false; - ul_ueassociated_lp_pa_transport_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using ul_ueassociated_lp_pa_transport_s = elementary_procedure_option; struct write_replace_warning_request_ies_container { template @@ -16959,16 +16035,7 @@ struct write_replace_warning_request_ies_container { }; // WriteReplaceWarningRequest ::= SEQUENCE -struct write_replace_warning_request_s { - bool ext = false; - write_replace_warning_request_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using write_replace_warning_request_s = elementary_procedure_option; struct write_replace_warning_resp_ies_container { template @@ -16990,16 +16057,7 @@ struct write_replace_warning_resp_ies_container { }; // WriteReplaceWarningResponse ::= SEQUENCE -struct write_replace_warning_resp_s { - bool ext = false; - write_replace_warning_resp_ies_container protocol_ies; - // ... - - // sequence methods - SRSASN_CODE pack(bit_ref& bref) const; - SRSASN_CODE unpack(cbit_ref& bref); - void to_json(json_writer& j) const; -}; +using write_replace_warning_resp_s = elementary_procedure_option; // S1AP-ELEMENTARY-PROCEDURES ::= OBJECT SET OF S1AP-ELEMENTARY-PROCEDURE struct s1ap_elem_procs_o { @@ -18216,7 +17274,7 @@ struct sourceenb_to_targetenb_transparent_container_s { }; // TargeteNB-ToSourceeNB-TransparentContainer-ExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION -using targetenb_to_sourceenb_transparent_container_ext_ies_o = s1ap_protocol_ext_empty_o; +using targetenb_to_sourceenb_transparent_container_ext_ies_o = protocol_ext_empty_o; using targetenb_to_sourceenb_transparent_container_ext_ies_container = protocol_ext_container_empty_l; @@ -18237,4 +17295,126 @@ struct targetenb_to_sourceenb_transparent_container_s { } // namespace s1ap } // namespace asn1 +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_single_container_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ie_field_s; +extern template struct asn1::protocol_ext_field_s; +extern template struct asn1::protocol_ext_field_s; + #endif // SRSASN1_S1AP_H diff --git a/lib/include/srsran/asn1/s1ap_utils.h b/lib/include/srsran/asn1/s1ap_utils.h index 9b76bebf0..5b8e03ed7 100644 --- a/lib/include/srsran/asn1/s1ap_utils.h +++ b/lib/include/srsran/asn1/s1ap_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,29 +31,36 @@ namespace asn1 { namespace s1ap { -struct init_context_setup_request_s; -struct ue_context_mod_request_s; -struct erab_setup_request_s; -struct erab_release_cmd_s; -struct erab_modify_request_s; +struct init_context_setup_request_ies_container; +using init_context_setup_request_s = elementary_procedure_option; +struct ue_context_mod_request_ies_container; +using ue_context_mod_request_s = elementary_procedure_option; +struct erab_setup_request_ies_container; +using erab_setup_request_s = elementary_procedure_option; +struct erab_release_cmd_ies_container; +using erab_release_cmd_s = elementary_procedure_option; +struct erab_modify_request_ies_container; +using erab_modify_request_s = elementary_procedure_option; struct ue_paging_id_c; -struct ho_request_s; +struct ho_request_ies_container; +using ho_request_s = elementary_procedure_option; struct sourceenb_to_targetenb_transparent_container_s; -struct init_context_setup_resp_s; -struct erab_setup_resp_s; +struct init_context_setup_resp_ies_container; +using init_context_setup_resp_s = elementary_procedure_option; +struct erab_setup_resp_ies_container; +using erab_setup_resp_s = elementary_procedure_option; struct rrc_establishment_cause_opts; struct cause_radio_network_opts; struct bearers_subject_to_status_transfer_item_ies_o; struct erab_level_qos_params_s; -struct ho_cmd_s; +struct ho_cmd_ies_container; +using ho_cmd_s = elementary_procedure_option; struct erab_admitted_item_s; struct erab_to_be_modified_item_bearer_mod_req_s; struct cause_c; struct erab_item_s; struct ue_aggregate_maximum_bitrate_s; -template -struct protocol_ie_single_container_s; using bearers_subject_to_status_transfer_list_l = dyn_array >; using rrc_establishment_cause_e = enumerated; diff --git a/lib/include/srsran/build_info.h.in b/lib/include/srsran/build_info.h.in index 3d6deebb5..0d6710c76 100644 --- a/lib/include/srsran/build_info.h.in +++ b/lib/include/srsran/build_info.h.in @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/backtrace.h b/lib/include/srsran/common/backtrace.h index 309edce31..b97ea2d8d 100644 --- a/lib/include/srsran/common/backtrace.h +++ b/lib/include/srsran/common/backtrace.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/band_helper.h b/lib/include/srsran/common/band_helper.h index 050c6cbe0..caa230a67 100644 --- a/lib/include/srsran/common/band_helper.h +++ b/lib/include/srsran/common/band_helper.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #ifndef SRSRAN_BAND_HELPER_H #define SRSRAN_BAND_HELPER_H +#include "srsran/phy/common/phy_common_nr.h" #include #include #include @@ -39,6 +40,9 @@ public: // Return frequency of given NR-ARFCN in Hz double nr_arfcn_to_freq(uint32_t nr_arfcn); + // Frequency in Hz to NR-ARFCN + uint32_t freq_to_nr_arfcn(double freq); + // Possible values of delta f_raster in Table 5.4.2.3-1 and Table 5.4.2.3-2 enum delta_f_raster_t { DEFAULT = 0, // for bands with 2 possible values for delta_f_raster (e.g. 15 and 30 kHz), the lower is chosen @@ -53,28 +57,268 @@ public: // For bands with 2 possible raster offsets, delta_f_raster needs to be specified std::vector get_bands_nr(uint32_t nr_arfcn, delta_f_raster_t delta_f_raster = DEFAULT); + /** + * @brief Get the lowest band that includes a given Downlink frequency in Hz + * @param dl_freq_Hz Given frequency in Hz + * @return The band number if the frequency is bounded in a band, UINT16_MAX otherwise + */ + uint16_t get_band_from_dl_freq_Hz(double dl_freq_Hz) const; + + /** + * @brief Get the lowest band that includes a given Downlink ARFCN + * @param arfcn Given ARFCN + * @return The band number if the ARFCN is bounded in a band, UINT16_MAX otherwise + */ + uint16_t get_band_from_dl_arfcn(uint32_t arfcn) const; + + /** + * @brief Get the respective UL ARFCN of a DL ARFCN + * + * For paired spectrum (FDD) the function returns the respective ARFCN in the same band. + * For unparied spectrum (TDD) the function returns the same ARFCN. + * + * @param dl_arfcn The DL ARFCN + * @return uint32_t the UL ARFCN + */ + uint32_t get_ul_arfcn_from_dl_arfcn(uint32_t dl_arfcn) const; + + /** + * @brief Selects the SSB pattern case according to the band number and subcarrier spacing + * @remark Described by TS 38.101-1 Table 5.4.3.3-1: Applicable SS raster entries per operating band + * @param band NR Band number + * @param scs SSB Subcarrier spacing + * @return The SSB pattern case if band and subcarrier spacing match, SRSRAN_SSB_PATTERN_INVALID otherwise + */ + static srsran_ssb_pattern_t get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs); + + /** + * @brief Select the lower SSB subcarrier spacing valid for this band + * @param band NR band number + * @return The SSB subcarrier spacing + */ + srsran_subcarrier_spacing_t get_ssb_scs(uint16_t band) const; + + /** + * @brief gets the NR band duplex mode + * @param band Given band + * @return A valid SRSRAN_DUPLEX_MODE if the band is valid, SRSRAN_DUPLEX_MODE_INVALID otherwise + */ + srsran_duplex_mode_t get_duplex_mode(uint16_t band) const; + + /** + * @brief Compute the center frequency for a NR carrier from its bandwidth and the absolute pointA + * + * @param nof_prb Carrier bandwidth in number of RB + * @param freq_point_a_arfcn Absolute Point A frequency ARFCN + * @return double Frequency in Hz + */ + double get_center_freq_from_abs_freq_point_a(uint32_t nof_prb, uint32_t freq_point_a_arfcn); + + /** + * @brief Compute the absolute pointA for a NR carrier from its bandwidth and the center frequency + * + * @param nof_prb Carrier bandwidth in number of RB + * @param center_freq double Frequency in Hz + * @return Absolute Point A frequency in Hz + */ + double get_abs_freq_point_a_from_center_freq(uint32_t nof_prb, double center_freq); + + /** + * @brief Compute the absolute frequency point A for a arfcn + * + * @param band nr frequency band. + * @param nof_prb Number of PRBs. + * @param arfcn Given ARFCN. + * @return frequency point A in arfcn notation. + */ + uint32_t get_abs_freq_point_a_arfcn(uint32_t nof_prb, uint32_t arfcn); + + /** + * @brief Compute the absolute frequency of the SSB for a DL ARFCN and a band. This selects an SSB center frequency + * following the band SS/PBCH frequency raster provided by TS38.104 table 5.4.3.1-1, which is the upper bound + * of the provided center frequency + * + * @param scs ssb subcarrier spacing. + * @param min_center_freq_hz center frequency above which the SSB absolute frequency must be. + * @return absolute frequency of the SSB in arfcn notation. + */ + uint32_t find_lower_bound_abs_freq_ssb(uint16_t band, srsran_subcarrier_spacing_t scs, uint32_t min_center_freq_hz); + + /** + * @brief Compute the absolute frequency of the SSB for a DL ARFCN and a band. This finds an SSB center frequency + * following the band SS/PBCH frequency raster provided by TS38.104 table 5.4.3.1-1 as close as possible to PointA + * without letting any SS/PBCH subcarrier and CORESET#0 subcarrier (if RB offset is defined) below PointA + * + * @param scs ssb subcarrier spacing. + * @param freq_point_a_arfcn frequency point a in arfcn notation. + * @param coreset0_offset_rb CORESET#0 RB offset. See TS 38.213, Table 13-1,2,3 + * @return absolute frequency of the SSB in arfcn notation. + */ + uint32_t get_abs_freq_ssb_arfcn(uint16_t band, + srsran_subcarrier_spacing_t scs, + uint32_t freq_point_a_arfcn, + uint32_t coreset0_offset_rb = 0); + + /** + * @brief Compute SSB center frequency for NR carrier + * @param carrier Const Reference to a carrier struct including PRB, abs. frequency point A and carrier offset. + * @return double Frequency in Hz + */ + double get_ssb_center_freq(const srsran_carrier_nr_t& carrier); + + class sync_raster_t + { + protected: + sync_raster_t(uint32_t gscn_f, uint32_t gscn_s, uint32_t gscn_l) : + gscn_first(gscn_f), gscn_step(gscn_s), gscn_last(gscn_l), gscn(gscn_f) + { + // see TS38.104 Table 5.4.3.1-1 + if (gscn_last <= 7498) { + N_first = 1; + N_last = 2499; + } else if (7499 <= gscn_last and gscn_last <= 22255) { + N_last = 14756; + } else if (22256 <= gscn_last and gscn_last <= 26639) { + N_last = 4383; + } + + N = N_first; + } + uint32_t gscn; + uint32_t N; + uint32_t M[3] = {1, 3, 5}; + uint32_t M_idx = 0; + + private: + uint32_t gscn_first; + uint32_t gscn_step; + uint32_t gscn_last; + uint32_t N_first = 0; + uint32_t N_last = 0; + + public: + bool valid() const { return gscn_step != 0; } + + void next() + { + if (gscn_last <= 7498 and M_idx < 3) { + M_idx += 1; + if (M_idx == 3 and N <= N_last) { + M_idx = 0; + N += 1; + } + } else if (N <= N_last) { + N += 1; + } + } + + bool end() const { return (N > N_last or gscn_step == 0); } + + void reset() + { + N = N_first; + M_idx = 0; + } + + void gscn_next() + { + if (gscn <= gscn_last) { + gscn += gscn_step; + } + } + + bool gscn_end() const { return (gscn > gscn_last or gscn_step == 0); } + + void gscn_reset() { gscn = gscn_first; } + + double get_frequency() const; + + uint32_t get_gscn() const; + }; + + sync_raster_t get_sync_raster(uint16_t band, srsran_subcarrier_spacing_t scs) const; + private: - // Table 5.4.2.1-1 + // internal helper + + // Elements of TS 38.101-1 Table 5.2-1: NR operating bands in FR1 + struct nr_operating_band { + uint16_t band; + uint32_t F_UL_low; // in MHz + uint32_t F_UL_high; // in MHz + uint32_t F_DL_low; // in MHz + uint32_t F_DL_high; // in MHz + srsran_duplex_mode_t duplex_mode; + }; + static const uint32_t nof_nr_operating_band_fr1 = 32; + static constexpr std::array nr_operating_bands_fr1 = {{ + // clang-format off + {1, 1920, 1080, 2110, 2170, SRSRAN_DUPLEX_MODE_FDD}, + {2, 1850, 1810, 1930, 1990, SRSRAN_DUPLEX_MODE_FDD}, + {3, 1710, 1785, 1805, 1880, SRSRAN_DUPLEX_MODE_FDD}, + {5, 824, 849, 869, 894, SRSRAN_DUPLEX_MODE_FDD}, + {7, 2500, 2570, 2620, 2690, SRSRAN_DUPLEX_MODE_FDD}, + {8, 880, 915, 925, 960, SRSRAN_DUPLEX_MODE_FDD}, + {12, 699, 716, 729, 746, SRSRAN_DUPLEX_MODE_FDD}, + {20, 832, 862, 791, 821, SRSRAN_DUPLEX_MODE_FDD}, + {25, 1850, 1915, 1930, 1995, SRSRAN_DUPLEX_MODE_FDD}, + {28, 703, 748, 758, 803, SRSRAN_DUPLEX_MODE_FDD}, + {34, 2010, 2025, 2010, 2025, SRSRAN_DUPLEX_MODE_TDD}, + {38, 2570, 2620, 2570, 2620, SRSRAN_DUPLEX_MODE_TDD}, + {39, 1880, 1920, 1880, 1920, SRSRAN_DUPLEX_MODE_TDD}, + {40, 2300, 2400, 2300, 2400, SRSRAN_DUPLEX_MODE_TDD}, + {41, 2496, 2690, 2496, 2690, SRSRAN_DUPLEX_MODE_TDD}, + {50, 1432, 1517, 1432, 1517, SRSRAN_DUPLEX_MODE_TDD}, + {51, 1427, 1432, 1427, 1432, SRSRAN_DUPLEX_MODE_TDD}, + {66, 1710, 1780, 2110, 2200, SRSRAN_DUPLEX_MODE_FDD}, + {70, 1695, 1710, 1995, 2020, SRSRAN_DUPLEX_MODE_FDD}, + {71, 663, 698, 617, 652, SRSRAN_DUPLEX_MODE_FDD}, + {74, 1427, 1470, 1475, 1518, SRSRAN_DUPLEX_MODE_FDD}, + {75, 0, 0, 1432, 1517, SRSRAN_DUPLEX_MODE_SDL}, + {76, 0, 0, 1427, 1432, SRSRAN_DUPLEX_MODE_SDL}, + {77, 3300, 4200, 3300, 4200, SRSRAN_DUPLEX_MODE_TDD}, + {78, 3300, 3800, 3300, 3800, SRSRAN_DUPLEX_MODE_TDD}, + {79, 4400, 5000, 4400, 5000, SRSRAN_DUPLEX_MODE_TDD}, + {80, 1710, 1785, 0, 0, SRSRAN_DUPLEX_MODE_SUL}, + {81, 880, 915, 0, 0, SRSRAN_DUPLEX_MODE_SUL}, + {82, 832, 862, 0, 0, SRSRAN_DUPLEX_MODE_SUL}, + {83, 703, 748, 0, 0, SRSRAN_DUPLEX_MODE_SUL}, + {84, 1920, 1980, 0, 0, SRSRAN_DUPLEX_MODE_SUL}, + {86, 1710, 1780, 0, 0, SRSRAN_DUPLEX_MODE_SUL} + // clang-format on + }}; + struct nr_raster_params { + double freq_range_start; + double freq_range_end; double delta_F_global_kHz; double F_REF_Offs_MHz; uint32_t N_REF_Offs; uint32_t N_REF_min; uint32_t N_REF_max; + + bool operator==(const nr_raster_params& rhs) const + { + return freq_range_start == rhs.freq_range_start && freq_range_end == rhs.freq_range_end && + delta_F_global_kHz == rhs.delta_F_global_kHz && F_REF_Offs_MHz == rhs.F_REF_Offs_MHz && + N_REF_Offs == rhs.N_REF_Offs && N_REF_min == rhs.N_REF_min && N_REF_max == rhs.N_REF_max; + } }; // Helper to calculate F_REF according to Table 5.4.2.1-1 nr_raster_params get_raster_params(uint32_t nr_arfcn); + nr_raster_params get_raster_params(double freq); + bool is_valid_raster_param(const nr_raster_params& raster); static const uint32_t max_nr_arfcn = 3279165; static constexpr std::array nr_fr_params = {{ // clang-format off // Frequency range 0 - 3000 MHz - {5, 0.0, 0, 0, 599999}, + {0, 3000, 5, 0.0, 0, 0, 599999}, // Frequency range 3000 - 24250 MHz - {15, 3000.0, 600000, 600000, 2016666}, + {3000, 24250, 15, 3000.0, 600000, 600000, 2016666}, // Frequency range 24250 - 100000 MHz - {60, 24250.08, 2016667, 2016667, max_nr_arfcn} + {24250, 100000, 60, 24250.08, 2016667, 2016667, max_nr_arfcn} // clang-format on }}; @@ -98,22 +342,22 @@ private: {1, KHZ_100, 384000, 20, 396000, 422000, 20, 434000}, {2, KHZ_100, 370000, 20, 382000, 386000, 20, 398000}, {3, KHZ_100, 342000, 20, 357000, 361000, 20, 376000}, - + {5, KHZ_100, 164800, 20, 169800, 173800, 20, 178800}, {7, KHZ_100, 500000, 20, 514000, 524000, 20, 538000}, {8, KHZ_100, 176000, 20, 183000, 185000, 20, 192000}, - + {12, KHZ_100, 139800, 20, 143200, 145800, 20, 149200}, - + {20, KHZ_100, 166400, 20, 172400, 158200, 20, 164200}, {25, KHZ_100, 370000, 20, 383000, 386000, 20, 399000}, {28, KHZ_100, 140600, 20, 149600, 151600, 20, 160600}, - + {34, KHZ_100, 402000, 20, 405000, 402000, 20, 405000}, {38, KHZ_100, 514000, 20, 524000, 514000, 20, 524000}, {39, KHZ_100, 376000, 20, 384000, 376000, 20, 384000}, - + {40, KHZ_100, 460000, 20, 480000, 460000, 20, 480000}, {41, KHZ_15, 499200, 3, 537999, 499200, 3, 537999}, @@ -121,13 +365,13 @@ private: {50, KHZ_100, 286400, 20, 303400, 286400, 20, 303400}, {51, KHZ_100, 285400, 20, 286400, 285400, 20, 286400}, - + {66, KHZ_100, 342000, 20, 356000, 422000, 20, 440000}, - + {70, KHZ_100, 339000, 20, 342000, 399000, 20, 404000}, {71, KHZ_100, 132600, 20, 139600, 123400, 20, 130400}, {74, KHZ_100, 285400, 20, 294000, 295000, 20, 303600}, - + {75, KHZ_100, 0, 0, 0, 286400, 20, 303400}, {76, KHZ_100, 0, 0, 0, 285400, 20, 286400}, @@ -146,25 +390,66 @@ private: {83, KHZ_100, 140600, 20, 149600, 0, 0, 0}, {84, KHZ_100, 384000, 20, 396000, 0, 0, 0}, {86, KHZ_100, 342000, 20, 356000, 0, 0, 0} - // clang-format on + // clang-format on }}; - static const uint32_t nof_nr_bands_fr2 = 36; - static constexpr std::array nr_band_table_fr2 = {{ - {257, KHZ_60, 2054166, 1, 2104165, 2054166, 1, 2104165}, - {257, KHZ_120, 2054167, 2, 2104165, 2054167, 20, 2104165}, - - {258, KHZ_60, 2016667, 1, 2070832, 2016667, 1, 2070832}, - {258, KHZ_120, 2016667, 2, 2070831, 2016667, 2, 2070832}, + static const uint32_t nof_nr_bands_fr2 = 8; + static constexpr std::array nr_band_table_fr2 = { + {{257, KHZ_60, 2054166, 1, 2104165, 2054166, 1, 2104165}, + {257, KHZ_120, 2054167, 2, 2104165, 2054167, 20, 2104165}, - {260, KHZ_60, 2229166, 1, 2279165, 2229166, 1, 2279165}, - {260, KHZ_120, 2229167, 2, 2279165, 2229167, 2, 2279165}, + {258, KHZ_60, 2016667, 1, 2070832, 2016667, 1, 2070832}, + {258, KHZ_120, 2016667, 2, 2070831, 2016667, 2, 2070832}, - {261, KHZ_60, 2070833, 1, 2084999, 2070833, 1, 2084999}, - {261, KHZ_120, 2070833, 2, 2084999, 2070833, 2, 2084999} + {260, KHZ_60, 2229166, 1, 2279165, 2229166, 1, 2279165}, + {260, KHZ_120, 2229167, 2, 2279165, 2229167, 2, 2279165}, + + {261, KHZ_60, 2070833, 1, 2084999, 2070833, 1, 2084999}, + {261, KHZ_120, 2070833, 2, 2084999, 2070833, 2, 2084999}}}; + + // Elements of TS 38.101-1 Table 5.4.3.3-1 : Applicable SS raster entries per operating band + struct nr_band_ss_raster { + uint16_t band; + srsran_subcarrier_spacing_t scs; + srsran_ssb_pattern_t pattern; + uint32_t gscn_first; + uint32_t gscn_step; + uint32_t gscn_last; + }; + static const uint32_t nof_nr_band_ss_raster = 29; + static constexpr std::array nr_band_ss_raster_table = {{ + {1, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 5279, 1, 5419}, + {2, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4829, 1, 4969}, + {3, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4517, 1, 4693}, + {5, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 2177, 1, 2230}, + {5, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_B, 2183, 1, 2224}, + {7, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 6554, 1, 6718}, + {8, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 2318, 1, 2395}, + {12, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 1828, 1, 1858}, + {20, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 1982, 1, 2047}, + {25, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4829, 1, 4981}, + {28, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 1901, 1, 2002}, + {34, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 5030, 1, 5056}, + {38, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 6431, 1, 6544}, + {39, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4706, 1, 4795}, + {40, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 5756, 1, 5995}, + {41, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 6246, 3, 6717}, + {41, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_C, 6252, 3, 6714}, + {50, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3584, 1, 3787}, + {51, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3572, 1, 3574}, + {66, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 5279, 1, 5494}, + {66, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_B, 5285, 1, 5488}, + {70, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 4993, 1, 5044}, + {71, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 1547, 1, 1624}, + {74, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3692, 1, 3790}, + {75, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3584, 1, 3787}, + {76, srsran_subcarrier_spacing_15kHz, SRSRAN_SSB_PATTERN_A, 3572, 1, 3574}, + {77, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_C, 7711, 1, 8329}, + {78, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_C, 7711, 1, 8051}, + {79, srsran_subcarrier_spacing_30kHz, SRSRAN_SSB_PATTERN_C, 8480, 16, 8880}, }}; }; } // namespace srsran -#endif // SRSRAN_BAND_HELPER_H \ No newline at end of file +#endif // SRSRAN_BAND_HELPER_H diff --git a/lib/include/srsran/common/basic_pnf.h b/lib/include/srsran/common/basic_pnf.h deleted file mode 100644 index 4e9550857..000000000 --- a/lib/include/srsran/common/basic_pnf.h +++ /dev/null @@ -1,558 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_BASIC_PNF_H -#define SRSRAN_BASIC_PNF_H - -#include "basic_vnf_api.h" -#include "common.h" -#include "srsran/adt/choice_type.h" -#include "srsran/common/block_queue.h" -#include "srsran/common/buffer_pool.h" -#include "srsran/srslog/srslog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RAND_SEED (12384) -#define RX_TIMEOUT_MS (500) - -#define MIN_TB_LEN (100) // MAX_TB_LEN defined in api.h - -#define PING_REQUEST_PDU 1 - -namespace srsran { - -struct pnf_metrics_t { - uint32_t avg_rtt_us; - uint32_t num_timing_errors; - uint32_t num_pdus; - uint32_t tb_size; -}; - -class srsran_basic_pnf -{ - using msg_header_t = basic_vnf_api::msg_header_t; - const static size_t buffer_size = - srsran::static_max::value; - using msg_buffer_t = std::array; - -public: - srsran_basic_pnf(const std::string& type_, - const std::string& vnf_p5_addr, - const uint16_t& vnf_p5_port, - const uint32_t& sf_interval, - const int32_t& num_sf_, - const uint32_t& tb_len_) : - running(false), - type(type_), - tti(100), ///< Random start TTI - vnf_addr(vnf_p5_addr), - vnf_port(vnf_p5_port), - sf_interval_us(sf_interval), - num_sf(num_sf_), - tb_len(tb_len_), - rand_gen(RAND_SEED), - rand_dist(MIN_TB_LEN, MAX_TB_LEN) - { - logger.set_level(srslog::basic_levels::warning); - logger.set_hex_dump_max_size(-1); - } - - ~srsran_basic_pnf() { stop(); }; - - bool start() - { - // create socket - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) { - perror("socket"); - return false; - } - - int enable = 1; -#if defined(SO_REUSEADDR) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { - perror("setsockopt(SO_REUSEADDR) failed"); - } -#endif -#if defined(SO_REUSEPORT) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) { - perror("setsockopt(SO_REUSEPORT) failed"); - } -#endif - - bzero(&servaddr, sizeof(servaddr)); - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = inet_addr(vnf_addr.c_str()); - servaddr.sin_port = htons(vnf_port); - - // start main thread - running = true; - - if (type == "gnb") { - rx_thread = std::unique_ptr(new std::thread(&srsran_basic_pnf::dl_handler_thread, this)); - tx_thread = std::unique_ptr(new std::thread(&srsran_basic_pnf::ul_handler_thread, this)); - } else { - tx_thread = std::unique_ptr(new std::thread(&srsran_basic_pnf::ue_dl_handler_thread, this)); - } - - return true; - }; - - bool stop() - { - running = false; - - if (rx_thread) { - if (rx_thread->joinable()) { - rx_thread->join(); - } - } - - if (tx_thread) { - if (tx_thread->joinable()) { - tx_thread->join(); - } - } - - return true; - }; - - pnf_metrics_t get_metrics() - { - pnf_metrics_t tmp = metrics; - metrics = {}; - return tmp; - } - - void connect_out_rf_queue(srsran::block_queue* rf_queue_) - { - rf_out_queue = rf_queue_; - policy = bridge; - } - srsran::block_queue* get_in_rf_queue() - { - policy = bridge; - return &rf_in_queue; - } - -private: - //! Waits for DL Config or Tx Request Msg from VNF and forwards to RF - void dl_handler_thread() - { - pthread_setname_np(pthread_self(), rx_thread_name.c_str()); - - // set_rt_prio(); - - struct pollfd fd; - fd.fd = sockfd; - fd.events = POLLIN; - - std::unique_ptr rx_buffer{new msg_buffer_t{}}; - - while (running) { - // receive response - int ret = poll(&fd, 1, RX_TIMEOUT_MS); - switch (ret) { - case -1: - printf("Error occurred.\n"); - running = false; - break; - case 0: - // Timeout - printf("Error: Didn't receive response after %dms\n", RX_TIMEOUT_MS); - break; - default: - int recv_ret = recv(sockfd, rx_buffer->data(), sizeof(*rx_buffer), 0); - handle_msg(rx_buffer->data(), recv_ret); - break; - } - - std::lock_guard lock(mutex); - auto rtt = - std::chrono::duration_cast(std::chrono::steady_clock::now() - tti_start_time) - .count(); - - // TODO: add averaging - metrics.avg_rtt_us = rtt; - } - }; - - void ul_handler_thread() - { - pthread_setname_np(pthread_self(), tx_thread_name.c_str()); - - // set_rt_prio(); - - struct pollfd fd; - fd.fd = sockfd; - fd.events = POLLIN; - - const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message - std::unique_ptr > rx_buffer = - std::unique_ptr >(new std::array); - - int32_t sf_counter = 0; - while (running && (num_sf > 0 ? sf_counter < num_sf : true)) { - { - std::lock_guard lock(mutex); - - // Increase TTI - tti = (tti + 1) % 10240; - - // Take time before sending the SF indication - tti_start_time = std::chrono::steady_clock::now(); - - // Send request - send_sf_ind(tti); - - if (policy == bridge) { - // send_rx_data_ind(tti); - } else { - // provide UL data every 2nd TTI - if (tti % 2 == 0) { - send_rx_data_ind(tti); - } - } - - sf_counter++; - } - - std::this_thread::sleep_for(std::chrono::microseconds(sf_interval_us)); - } - - printf("Leaving Tx thread after %d subframes\n", sf_counter); - }; - - void ue_dl_handler_thread() - { - pthread_setname_np(pthread_self(), tx_thread_name.c_str()); - - // set_rt_prio(); - - struct pollfd fd; - fd.fd = sockfd; - fd.events = POLLIN; - - const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_conf_msg_t) + 32; // larger than biggest message - std::unique_ptr > rx_buffer = - std::unique_ptr >(new std::array); - - int32_t sf_counter = 0; - while (running && (num_sf > 0 ? sf_counter < num_sf : true)) { - { - std::lock_guard lock(mutex); - - // Increase TTI - tti = (tti + 1) % 10240; - - // Take time before sending the SF indication - tti_start_time = std::chrono::steady_clock::now(); - - // Send SF indication - send_sf_ind(tti); - - if (policy == bridge) { - srsran::unique_byte_buffer_t tb; - if (rf_in_queue.try_pop(&tb)) { - send_dl_ind(tti, std::move(tb)); - } - } else { - // provide DL grant every even TTI, and UL grant every odd - if (tti % 2 == 0) { - send_dl_ind(tti); - } else { - send_ul_ind(tti); - } - } - - sf_counter++; - } - - std::this_thread::sleep_for(std::chrono::microseconds(sf_interval_us)); - } - - printf("Leaving Tx thread after %d subframes\n", sf_counter); - }; - - void send_sf_ind(uint32_t tti_) - { - basic_vnf_api::sf_ind_msg_t sf_ind; - bzero(&sf_ind, sizeof(sf_ind)); - sf_ind.header.type = basic_vnf_api::SF_IND; - sf_ind.header.msg_len = sizeof(sf_ind) - sizeof(basic_vnf_api::msg_header_t); - sf_ind.tti = tti_; - sf_ind.t1 = 0; - sf_ind.tb_len = tb_len > 0 ? tb_len : rand_dist(rand_gen); - - int n = 0; - if ((n = sendto(sockfd, &sf_ind, sizeof(sf_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) { - printf("sendto failed, ret=%d\n", n); - } - } - - int handle_msg(const uint8_t* buffer, const uint32_t len) - { - basic_vnf_api::msg_header_t* header = (basic_vnf_api::msg_header_t*)buffer; - - logger.debug("Received %s (%d B) in TTI", basic_vnf_api::msg_type_text[header->type], len); - - switch (header->type) { - case basic_vnf_api::SF_IND: - printf("Error: %s not handled by VNF\n", basic_vnf_api::msg_type_text[header->type]); - break; - case basic_vnf_api::DL_CONFIG: - handle_dl_config((basic_vnf_api::dl_conf_msg_t*)header); - break; - case basic_vnf_api::TX_REQUEST: - handle_tx_request((basic_vnf_api::tx_request_msg_t*)header); - break; - default: - printf("Unknown msg type.\n"); - break; - } - return 0; - } - - int handle_dl_config(basic_vnf_api::dl_conf_msg_t* msg) - { - // printf("Received DL config for TTI=%d\n", msg->tti); - - if (msg->tti != tti) { - metrics.num_timing_errors++; - // printf("Received DL config for TTI=%d but current TTI is %d\n", msg->tti, tti.load()); - return -1; - } - - return 0; - } - - int handle_tx_request(basic_vnf_api::tx_request_msg_t* msg) - { - if (msg->tti != tti) { - metrics.num_timing_errors++; - } - - for (uint32_t i = 0; i < msg->nof_pdus; ++i) { - metrics.tb_size += msg->pdus[i].length; - } - - metrics.num_pdus += msg->nof_pdus; - - if (rf_out_queue != nullptr) { - uint32_t len = sizeof(*msg) - sizeof(msg->pdus->data) + msg->pdus->length; - - srsran::unique_byte_buffer_t tx = srsran::make_byte_buffer(); - if (tx == nullptr) { - return -1; - } - memcpy(tx->msg, msg, len); - rf_out_queue->push(std::move(tx)); - } - - return 0; - } - - void send_rx_data_ind(const uint32_t tti_) - { - // MAC PDU for UL-SCH with IPv6 router solicitation - static uint8_t tv[] = {0x04, 0x38, 0x00, 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x08, 0x3a, 0xff, 0xfe, - 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x44, 0x4b, 0x0f, 0x2c, 0x33, 0x98, 0xf2, - 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x85, 0x00, 0x4b, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x7f, 0x00, 0x00, 0x3f, 0x00}; - - basic_vnf_api::rx_data_ind_msg_t rx_ind = {}; - - rx_ind.header.type = basic_vnf_api::RX_DATA_IND; - rx_ind.header.msg_len = sizeof(rx_ind) - sizeof(basic_vnf_api::msg_header_t); - rx_ind.sfn = tti_; - rx_ind.t1 = 0; - - rx_ind.nof_pdus = 1; - rx_ind.pdus[0].type = basic_vnf_api::PUSCH; - rx_ind.pdus[0].length = tb_len > 0 ? tb_len : rand_dist(rand_gen); - - if (rx_ind.pdus[0].length >= sizeof(tv)) { - // copy TV - memcpy(rx_ind.pdus[0].data, tv, sizeof(tv)); - // set remaining bytes to zero - memset(rx_ind.pdus[0].data + sizeof(tv), 0xaa, rx_ind.pdus[0].length - sizeof(tv)); - } else { - // just fill with dummy bytes - memset(rx_ind.pdus[0].data, 0xab, rx_ind.pdus[0].length); - } - - int n = 0; - if ((n = sendto(sockfd, &rx_ind, sizeof(rx_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) { - printf("sendto failed, ret=%d\n", n); - } - } - - void send_dl_ind(uint32_t tti_, srsran::unique_byte_buffer_t tb = {}) - { -#if PING_REQUEST_PDU - static uint8_t tv[] = { - 0x04, 0x5c, 0x00, 0x80, 0x00, 0x00, 0x45, 0x00, 0x00, 0x54, 0x15, 0x02, 0x40, 0x00, 0x40, 0x01, 0xa2, 0x52, - 0xc0, 0xa8, 0x01, 0x01, 0xc0, 0xa8, 0x01, 0x03, 0x08, 0x00, 0x26, 0x40, 0x5e, 0x8f, 0x00, 0xb3, 0x04, 0x55, - 0xc4, 0x5d, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xf7, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x4f, 0x7f, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -#else - // MAC PDU with a single LCID with padding only - static uint8_t tv[] = { - 0x01, - 0x08, - 0x11, - 0x22, - 0x33, - 0x44, - 0x55, - 0x66, - 0x77, - 0x88, - 0x3f, - }; -#endif // PING_REQUEST_PDU - basic_vnf_api::dl_ind_msg_t dl_ind = {}; - - dl_ind.header.type = basic_vnf_api::DL_IND; - dl_ind.header.msg_len = sizeof(dl_ind) - sizeof(basic_vnf_api::msg_header_t); - dl_ind.tti = tti_; - dl_ind.t1 = 0; - - uint32_t tot_bytes = 0; - uint32_t tb_size = tb_len > 0 ? tb_len : rand_dist(rand_gen); - - if (tb != nullptr) { - auto* header = (basic_vnf_api::msg_header_t*)tb->msg; - - if (header->type == basic_vnf_api::TX_REQUEST) { - auto* tx_req = (basic_vnf_api::tx_request_msg_t*)tb->msg; - dl_ind.nof_pdus = tx_req->nof_pdus; - // dl_ind.tti = tx_req->tti; - for (uint32_t i = 0; i < dl_ind.nof_pdus; ++i) { - dl_ind.pdus[i].length = tx_req->pdus[i].length; - dl_ind.pdus[i].type = tx_req->pdus[i].type; - memcpy(dl_ind.pdus[i].data, tx_req->pdus[i].data, dl_ind.pdus[i].length); - tot_bytes += dl_ind.pdus[i].length; - logger.info( - dl_ind.pdus[i].data, dl_ind.pdus[i].length, "Sending to UE a PDU (%d bytes)", dl_ind.pdus[i].length); - } - } - } else { - uint32_t N_bytes = sizeof(tv); - - // Create default - dl_ind.nof_pdus = 1; - dl_ind.pdus[0].type = basic_vnf_api::PDSCH; - dl_ind.pdus[0].length = tb_size; - - if (dl_ind.pdus[0].length >= N_bytes) { - // copy TV - memcpy(dl_ind.pdus[0].data, tv, N_bytes); - tot_bytes = N_bytes; - } - - logger.info(dl_ind.pdus[0].data, N_bytes, "Sending to UE a TB (%d bytes)", N_bytes); - } - - if (tot_bytes > 0 and tot_bytes < tb_size) { - uint8_t* offset = &dl_ind.pdus[dl_ind.nof_pdus - 1].data[dl_ind.pdus[dl_ind.nof_pdus - 1].length]; - memset(offset, 0xaa, tb_size - tot_bytes); - } else if (tot_bytes == 0) { - // just fill with dummy bytes - dl_ind.nof_pdus = 1; - memset(dl_ind.pdus[0].data, 0xab, tb_size); - } - - int n = 0; - if ((n = sendto(sockfd, &dl_ind, sizeof(dl_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) { - printf("sendto failed, ret=%d\n", n); - } - } - - void send_ul_ind(uint32_t tti_) - { - basic_vnf_api::ul_ind_msg_t ul_ind = {}; - - ul_ind.header.type = basic_vnf_api::UL_IND; - ul_ind.header.msg_len = sizeof(ul_ind) - sizeof(basic_vnf_api::msg_header_t); - ul_ind.tti = tti_; - ul_ind.t1 = 0; - - ul_ind.pdus.type = basic_vnf_api::PUSCH; - ul_ind.pdus.length = tb_len > 0 ? tb_len : rand_dist(rand_gen); - - int n = 0; - if ((n = sendto(sockfd, &ul_ind, sizeof(ul_ind), 0, (struct sockaddr*)&servaddr, sizeof(servaddr))) < 0) { - printf("sendto failed, ret=%d\n", n); - } - } - - std::unique_ptr tx_thread, rx_thread; - std::string tx_thread_name = "TX_PNF", rx_thread_name = "RX_PNF"; - bool running = false; - srslog::basic_logger& logger = srslog::fetch_basic_logger("PNF", false); - - std::mutex mutex; - std::atomic tti; - std::chrono::steady_clock::time_point tti_start_time; - - std::string type; - - std::string vnf_addr; - uint16_t vnf_port = 3333; - - uint32_t sf_interval_us = 1000; - int32_t num_sf = -1; - uint32_t tb_len = 100; - - pnf_metrics_t metrics = {}; - - int sockfd = 0; - struct sockaddr_in servaddr = {}; - - // For random number generation - std::mt19937 rand_gen; - std::uniform_int_distribution rand_dist; - - // two policies possible: dummy packets generated by the PNF class, or bridge between UE and gNB PNFs with TBs - // entering/exiting each PNF via the rf_in_queue/rf_out_queue. - srsran::block_queue* rf_out_queue = nullptr; - srsran::block_queue rf_in_queue; - enum data_policy_t { self_gen, bridge } policy = self_gen; -}; - -} // namespace srsran - -#endif // SRSRAN_BASIC_PNF_H diff --git a/lib/include/srsran/common/basic_vnf.h b/lib/include/srsran/common/basic_vnf.h deleted file mode 100644 index d68952950..000000000 --- a/lib/include/srsran/common/basic_vnf.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_BASIC_VNF_H -#define SRSRAN_BASIC_VNF_H - -#include "basic_vnf_api.h" -#include "common.h" -#include "srsran/common/threads.h" -#include "srsran/interfaces/gnb_interfaces.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include "srsran/srslog/srslog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace srsran { - -class srsran_basic_vnf : public thread -{ -public: - srsran_basic_vnf(const vnf_args_t& args_, stack_interface_phy_nr* stack_); - ~srsran_basic_vnf(); - - bool stop(); - - int dl_config_request(const srsenb::phy_interface_stack_nr::dl_config_request_t& request); - int tx_request(const srsenb::phy_interface_stack_nr::tx_request_t& request); - int tx_request(const srsue::phy_interface_stack_nr::tx_request_t& request); - -private: - void run_thread(); - - // handlers - int handle_msg(const uint8_t* buffer, const uint32_t len); - int handle_sf_ind(basic_vnf_api::sf_ind_msg_t* msg); - int handle_dl_ind(basic_vnf_api::dl_ind_msg_t* msg); - int handle_ul_ind(basic_vnf_api::ul_ind_msg_t* msg); - int handle_rx_data_ind(basic_vnf_api::rx_data_ind_msg_t* msg); - - // senders - int send_dl_config_request(); - - // helpers - uint32_t calc_full_msg_len(const basic_vnf_api::tx_request_msg_t& msg); - - srslog::basic_logger& logger = srslog::fetch_basic_logger("VNF", false); - srsenb::stack_interface_phy_nr* m_gnb_stack = nullptr; - srsue::stack_interface_phy_nr* m_ue_stack = nullptr; - - std::unique_ptr m_tx_req_msg; - - bool running = false; - - vnf_args_t m_args = {}; - - int sockfd = 0; - struct sockaddr_in servaddr = {}, client_addr = {}; - - uint32_t last_sf_indication_time = 0; -}; - -} // namespace srsran - -#endif // SRSRAN_BASIC_VNF_H diff --git a/lib/include/srsran/common/basic_vnf_api.h b/lib/include/srsran/common/basic_vnf_api.h deleted file mode 100644 index 52dc7aedc..000000000 --- a/lib/include/srsran/common/basic_vnf_api.h +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_BASIC_VNF_API_H -#define SRSRAN_BASIC_VNF_API_H - -#include - -namespace basic_vnf_api { - -// PNF (the PHY) VNF (the L2/L3) -// | | -// | | -// | - | -// | \ sf_ind_msg_t -// | \ | -// | \ | -// | \ | -// | \ | -// | \ | -// | \| -// | | -// | | DL_CONFIG.request -// | /| -// | / | -// | / | -// | / | -// | / | -// | / dl_conf_msg_t -// | / | -// |/ | TX.request -// | /| -// | / | -// | / | -// | / | -// | / | -// | / tx_request_msg_t -// | / | -// |/ | -// | | - -// Primitive API messages for basic testing basic VNF/PNF interaction -enum msg_type_t { - SF_IND, ///< To signal start of new subframe (later slot) for both UE and gNB - DL_CONFIG, ///< To configure the DL for gNB - TX_REQUEST, ///< For DL data for gNB - RX_DATA_IND, ///< For UL Data for gNB - DL_IND, ///< For the UE for DL data - UL_IND, ///< For the UE for UL Data - MSG_TYPE_NITEMS -}; -static const char* msg_type_text[MSG_TYPE_NITEMS] = - {"SF Indication", "DL_CONFIG.Request", "TX.Request", "RX_Data.indication", "DL_Indication", "UL_Indication"}; -enum pdu_type_t { MAC_PBCH, PHY_PBCH, PDCCH, PDSCH, PUSCH }; - -struct msg_header_t { - msg_type_t type; - uint32_t msg_len; -}; - -struct sf_ind_msg_t { - msg_header_t header; - uint32_t t1; // Timestamp taken at PNF - uint32_t tti; // TTI of requested subframe - uint32_t tb_len; // Length of the TB -}; - -#define MAX_TB_LEN (16 * 1024) -#define MAX_PDU_SIZE (16 * 1024) -#define MAX_NUM_PDUS (1) - -struct phy_pbch_pdu_t { - uint16_t phy_cell_id; // 0 - 1007 - uint8_t ss_block_index; // 0-63 - uint8_t ssb_sc_offset; // 0-15 - uint8_t dmrs_pos; // 0-1 - uint8_t pdcch_config; // 0-255 -}; - -struct dl_conf_msg_t { - msg_header_t header; - uint32_t t1; // Replayed timestamp - uint32_t t2; // Timestamp taken at VNF - uint32_t tti; // TTI - uint16_t beam_id; // tx beam id for the slot -}; - -struct tx_request_pdu_t { - uint16_t length; - uint16_t index; // index indicated in dl_config - pdu_type_t type; // physical chan of pdu/tb - uint8_t data[MAX_PDU_SIZE]; -}; - -struct tx_request_msg_t { - msg_header_t header; - uint32_t tti; // TTI - uint32_t tb_len; // actual TB len - uint32_t nof_pdus; - tx_request_pdu_t pdus[MAX_NUM_PDUS]; -}; - -struct rx_data_ind_pdu_t { - uint16_t length; - pdu_type_t type; // physical chan of pdu/tb - uint8_t data[MAX_PDU_SIZE]; -}; - -struct rx_data_ind_msg_t { - msg_header_t header; - uint32_t t1; // Timestamp taken at PNF - uint32_t sfn; ///< SFN (0-1023) - uint32_t slot; ///< Slot (0-319) - uint32_t tb_len; ///< actual TB len - uint32_t nof_pdus; // - rx_data_ind_pdu_t pdus[MAX_NUM_PDUS]; -}; - -// UE specific messages -struct dl_ind_pdu_t { - pdu_type_t type; // physical chan of pdu/tb - uint16_t length; - uint8_t data[MAX_PDU_SIZE]; -}; - -struct dl_ind_msg_t { - msg_header_t header; - uint32_t t1; // Timestamp taken at PNF - uint32_t tti; // tti or slot? - uint32_t nof_pdus; - dl_ind_pdu_t pdus[MAX_NUM_PDUS]; -}; - -///< Messages for UL (only one PDU) -struct ul_ind_pdu_t { - pdu_type_t type; // physical chan of pdu/tb - uint16_t length; -}; - -struct ul_ind_msg_t { - msg_header_t header; - uint32_t t1; // Timestamp taken at PNF - uint32_t tti; // tti or slot? - uint32_t rnti; ///< RNTI of this grant - ul_ind_pdu_t pdus; -}; - -} // namespace basic_vnf_api - -#endif // SRSRAN_BASIC_VNF_API_H diff --git a/lib/include/srsran/common/bcd_helpers.h b/lib/include/srsran/common/bcd_helpers.h index 5212e1f1a..43bea9afa 100644 --- a/lib/include/srsran/common/bcd_helpers.h +++ b/lib/include/srsran/common/bcd_helpers.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/bearer_manager.h b/lib/include/srsran/common/bearer_manager.h new file mode 100644 index 000000000..cd1575c02 --- /dev/null +++ b/lib/include/srsran/common/bearer_manager.h @@ -0,0 +1,173 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_BEARER_MANAGER_H +#define SRSRAN_BEARER_MANAGER_H + +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/common/common.h" +#include "srsran/common/rwlock_guard.h" +#include "srsran/srslog/srslog.h" +#include +#include +#include + +namespace srsran { + +namespace detail { + +/** + * @brief Implementation of UE bearer manager internal functionality that is common to both srsue and + * srsenb applications + */ +class ue_bearer_manager_impl +{ +public: + struct radio_bearer_t { + srsran::srsran_rat_t rat; + uint32_t lcid; + uint32_t eps_bearer_id; + uint32_t five_qi = 0; + bool is_valid() const { return rat != srsran_rat_t::nulltype; } + }; + static const radio_bearer_t invalid_rb; + + /// Registers EPS bearer with PDCP RAT type and LCID + bool add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid); + + /// Single EPS bearer is removed from map when the associated DRB is deleted + bool remove_eps_bearer(uint8_t eps_bearer_id); + + void reset(); + + bool has_active_radio_bearer(uint32_t eps_bearer_id); + + radio_bearer_t get_radio_bearer(uint32_t eps_bearer_id) const; + + radio_bearer_t get_eps_bearer_id_for_lcid(uint32_t lcid) const; + + bool set_five_qi(uint32_t eps_bearer_id, uint16_t five_qi); + +private: + using eps_rb_map_t = std::map; + + eps_rb_map_t bearers; + std::map lcid_to_eps_bearer_id; +}; + +} // namespace detail +} // namespace srsran + +namespace srsue { + +/** + * @brief Helper class to manage the mapping between EPS bearer and radio bearer + * + * The class maps EPS bearers that are known to NAS and GW (UE) + * to radio bearer (RB) that are only known to RRC. + * Since the lifetime of a EPS bearer is usually longer than the lifetime of a RB, + * the GW/GTPU needs to query the Stack to check whether a + * given EPS bearer is active, i.e. a DRB is established, or not. + * + * The class also maps between RATs since each LCID can exist on either EUTRA or NR RATs, or both. + * + * Since the access of this class is happening from two different threads (GW+RRC/Stack) + * it's public interface is protected. + * + * The class provides two interfaces to be used with RNTI or without. The version without + * RNTI is used by the UE. The version with RNTI in the interface is intented to be + * used by the eNB. + * + */ +class ue_bearer_manager +{ +public: + using radio_bearer_t = srsran::detail::ue_bearer_manager_impl::radio_bearer_t; + + ue_bearer_manager(); + ue_bearer_manager(const ue_bearer_manager&) = delete; + ue_bearer_manager& operator=(const ue_bearer_manager&) = delete; + ~ue_bearer_manager(); + + // RRC interface + /// Registers EPS bearer with PDCP RAT type and LCID + void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid); + + /// Single EPS bearer is removed from map when the associated DRB is deleted + void remove_eps_bearer(uint8_t eps_bearer_id); + + /// All registered bearer are removed (e.g. after connection release) + void reset(); + + bool has_active_radio_bearer(uint32_t eps_bearer_id) + { + srsran::rwlock_read_guard rw_lock(rwlock); + return impl.has_active_radio_bearer(eps_bearer_id); + } + + radio_bearer_t get_radio_bearer(uint32_t eps_bearer_id) + { + srsran::rwlock_read_guard rw_lock(rwlock); + return impl.get_radio_bearer(eps_bearer_id); + } + + radio_bearer_t get_lcid_bearer(uint32_t lcid) + { + srsran::rwlock_read_guard rw_lock(rwlock); + return impl.get_eps_bearer_id_for_lcid(lcid); + } + +private: + pthread_rwlock_t rwlock = {}; /// RW lock to protect access from RRC/GW threads + srslog::basic_logger& logger; + srsran::detail::ue_bearer_manager_impl impl; +}; + +} // namespace srsue + +namespace srsenb { + +class enb_bearer_manager +{ +public: + using radio_bearer_t = srsran::detail::ue_bearer_manager_impl::radio_bearer_t; + + enb_bearer_manager(); + ~enb_bearer_manager(); + + /// Multi-user interface (see comments above) + void add_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid); + void remove_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id); + void rem_user(uint16_t rnti); + bool has_active_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id); + radio_bearer_t get_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id); + radio_bearer_t get_lcid_bearer(uint16_t rnti, uint32_t lcid) const; + bool set_five_qi(uint16_t rnti, uint32_t eps_bearer_id, uint16_t five_qi); + +private: + srslog::basic_logger& logger; + + std::unordered_map users_map; +}; + +} // namespace srsenb + +#endif // SRSRAN_BEARER_MANAGER_H diff --git a/lib/include/srsran/common/block_queue.h b/lib/include/srsran/common/block_queue.h index d97eaf16a..b1253ce3c 100644 --- a/lib/include/srsran/common/block_queue.h +++ b/lib/include/srsran/common/block_queue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -44,7 +44,6 @@ namespace srsran { template class block_queue { - public: // Callback functions for mutexed operations inside pop/push methods class call_mutexed_itf @@ -105,6 +104,8 @@ public: return value; } + bool timedwait_pop(myobj* value, const struct timespec* abstime) { return pop_(value, true, abstime); } + bool empty() { // queue is empty? pthread_mutex_lock(&mutex); @@ -116,7 +117,7 @@ public: bool full() { // queue is full? pthread_mutex_lock(&mutex); - bool ret = not check_queue_space_unlocked(false); + bool ret = not check_queue_space_nolock(false); pthread_mutex_unlock(&mutex); return ret; } @@ -130,10 +131,17 @@ public: const myobj& front() const { return q.front(); } - size_t size() { return q.size(); } + size_t size() + { + size_t len = 0; + pthread_mutex_lock(&mutex); + len = q.size(); + pthread_mutex_unlock(&mutex); + return len; + } private: - bool pop_(myobj* value, bool block) + bool pop_(myobj* value, bool block, const struct timespec* abstime = nullptr) { if (!enable) { return false; @@ -145,7 +153,13 @@ private: goto exit; } while (q.empty() && enable) { - pthread_cond_wait(&cv_empty, &mutex); + if (abstime == nullptr) { + pthread_cond_wait(&cv_empty, &mutex); + } else { + if (pthread_cond_timedwait(&cv_empty, &mutex, abstime)) { + goto exit; + } + } } if (!enable) { goto exit; @@ -165,7 +179,7 @@ private: return ret; } - bool check_queue_space_unlocked(bool block) + bool check_queue_space_nolock(bool block) { num_threads++; if (capacity > 0) { @@ -192,7 +206,7 @@ private: return std::move(value); } pthread_mutex_lock(&mutex); - bool ret = check_queue_space_unlocked(block); + bool ret = check_queue_space_nolock(block); if (ret) { if (mutexed_callback) { mutexed_callback->pushing(value); @@ -212,7 +226,7 @@ private: return false; } pthread_mutex_lock(&mutex); - bool ret = check_queue_space_unlocked(block); + bool ret = check_queue_space_nolock(block); if (ret) { if (mutexed_callback) { mutexed_callback->pushing(value); diff --git a/lib/include/srsran/common/buffer_pool.h b/lib/include/srsran/common/buffer_pool.h index 5162bc062..90c4c8b5d 100644 --- a/lib/include/srsran/common/buffer_pool.h +++ b/lib/include/srsran/common/buffer_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -89,7 +89,7 @@ public: std::map buffer_cnt; for (uint32_t i = 0; i < pool.size(); i++) { if (std::find(free_list.cbegin(), free_list.cend(), pool[i]) == free_list.cend()) { - buffer_cnt[strlen(used[i]->debug_name) ? pool[i]->debug_name : "Undefined"]++; + buffer_cnt[strlen(pool[i]->debug_name) ? pool[i]->debug_name : "Undefined"]++; } } std::map::iterator it; @@ -166,8 +166,10 @@ private: uint32_t capacity; }; +/// Type of global byte buffer pool using byte_buffer_pool = concurrent_fixed_memory_pool; +/// Function used to generate unique byte buffers inline unique_byte_buffer_t make_byte_buffer() noexcept { return std::unique_ptr(new (std::nothrow) byte_buffer_t()); @@ -187,14 +189,49 @@ inline unique_byte_buffer_t make_byte_buffer(const char* debug_ctxt) noexcept return buffer; } +inline unique_byte_buffer_t make_byte_buffer(const uint8_t* payload, uint32_t len, const char* debug_ctxt) noexcept +{ + std::unique_ptr buffer(new (std::nothrow) byte_buffer_t()); + if (buffer == nullptr) { + srslog::fetch_basic_logger("POOL").error("Failed to allocate byte buffer in %s", debug_ctxt); + } else { + if (buffer->get_tailroom() >= len) { + memcpy(buffer->msg, payload, len); + buffer->N_bytes = len; + } else { + srslog::fetch_basic_logger("POOL").error( + "Failed to create byte buffer in %s. Payload too large (%d > %d)", debug_ctxt, len, buffer->get_tailroom()); + } + } + return buffer; +} + namespace detail { +template struct byte_buffer_pool_deleter { - void operator()(void* ptr) { byte_buffer_pool::get_instance()->deallocate_node(ptr); } + void operator()(T* ptr) const { byte_buffer_pool::get_instance()->deallocate_node(ptr); } }; } // namespace detail +/// Unique ptr to global byte buffer pool +template +using buffer_pool_ptr = std::unique_ptr >; + +/// Method to create unique_ptrs of type T allocated in global byte buffer pool +template +buffer_pool_ptr make_buffer_pool_obj(CtorArgs&&... args) noexcept +{ + static_assert(sizeof(T) <= byte_buffer_pool::BLOCK_SIZE, "pool_bounded_vector does not fit buffer pool block size"); + void* memblock = byte_buffer_pool::get_instance()->allocate_node(sizeof(T)); + if (memblock == nullptr) { + return buffer_pool_ptr(); + } + new (memblock) T(std::forward(args)...); + return buffer_pool_ptr(static_cast(memblock), detail::byte_buffer_pool_deleter()); +} + /** * Class to wrap objects of type T which get allocated/deallocated using the byte_buffer_pool * @tparam T type of the object being allocated @@ -222,21 +259,19 @@ public: template static byte_buffer_pool_ptr make(CtorArgs&&... args) { - void* memblock = byte_buffer_pool::get_instance()->allocate_node(sizeof(T)); - if (memblock == nullptr) { - return byte_buffer_pool_ptr(); - } - new (memblock) T(std::forward(args)...); byte_buffer_pool_ptr ret; - ret.ptr = std::unique_ptr(static_cast(memblock), - detail::byte_buffer_pool_deleter()); + ret.ptr = make_buffer_pool_obj(std::forward(args)...); return ret; }; private: - std::unique_ptr ptr; + buffer_pool_ptr ptr; }; +/// unique_ptr with virtual deleter, so it can be used by any pool +template +using any_pool_ptr = std::unique_ptr >; + } // namespace srsran #endif // SRSRAN_BUFFER_POOL_H diff --git a/lib/include/srsran/common/byte_buffer.h b/lib/include/srsran/common/byte_buffer.h index 435fe29de..0ea44d53a 100644 --- a/lib/include/srsran/common/byte_buffer.h +++ b/lib/include/srsran/common/byte_buffer.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,6 +28,7 @@ #include //#define SRSRAN_BUFFER_POOL_LOG_ENABLED +#define SRSRAN_BUFFER_POOL_LOG_NAME_LEN 128 namespace srsran { @@ -127,7 +128,7 @@ public: // avoid self assignment if (&buf == this) return *this; - msg = &buffer[SRSRAN_BUFFER_HEADER_OFFSET]; + msg = &buffer[buf.msg - &(*buf.buffer)]; N_bytes = buf.N_bytes; md = buf.md; memcpy(msg, buf.msg, N_bytes); diff --git a/lib/include/srsran/common/common.h b/lib/include/srsran/common/common.h index c341b24c3..2f2e87ac4 100644 --- a/lib/include/srsran/common/common.h +++ b/lib/include/srsran/common/common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -89,7 +89,7 @@ inline const char* enum_to_text(const char* const array[], uint32_t nof_types, u } template -ItemType enum_to_number(ItemType* array, uint32_t nof_types, uint32_t enum_val) +constexpr ItemType enum_to_number(ItemType* array, uint32_t nof_types, uint32_t enum_val) { return enum_val >= nof_types ? -1 : array[enum_val]; } diff --git a/lib/include/srsran/common/common_helper.h b/lib/include/srsran/common/common_helper.h index accd15a35..500677806 100644 --- a/lib/include/srsran/common/common_helper.h +++ b/lib/include/srsran/common/common_helper.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/common_lte.h b/lib/include/srsran/common/common_lte.h index 8bd041e5d..e2ef8a04d 100644 --- a/lib/include/srsran/common/common_lte.h +++ b/lib/include/srsran/common/common_lte.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -58,9 +58,10 @@ inline bool is_lte_cell_nof_prb(uint32_t nof_prb) enum class lte_srb { srb0, srb1, srb2, count }; const uint32_t MAX_LTE_SRB_ID = 2; enum class lte_drb { drb1 = 1, drb2, drb3, drb4, drb5, drb6, drb7, drb8, drb9, drb10, drb11, invalid }; -const uint32_t MAX_LTE_DRB_ID = 11; -const uint32_t MAX_LTE_LCID = 10; // logicalChannelIdentity 3..10 in TS 36.331 v15.3 -const uint32_t INVALID_LCID = 99; // random invalid LCID +const uint32_t MAX_LTE_DRB_ID = 11; +const uint32_t MAX_LTE_LCID = 10; // logicalChannelIdentity 3..10 in TS 36.331 v15.3 +const uint32_t INVALID_LCID = 99; // random invalid LCID +const uint32_t INVALID_EPS_BEARER_ID = 99; // random invalid eps bearer id constexpr bool is_lte_rb(uint32_t lcid) { diff --git a/lib/include/srsran/common/common_nr.h b/lib/include/srsran/common/common_nr.h index be914e409..e28360b36 100644 --- a/lib/include/srsran/common/common_nr.h +++ b/lib/include/srsran/common/common_nr.h @@ -1,6 +1,6 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -64,7 +64,7 @@ enum class nr_drb { invalid }; const uint32_t MAX_NR_DRB_ID = 29; -const uint32_t MAX_NR_NOF_BEARERS = MAX_NR_DRB_ID + MAX_NR_SRB_ID; +const uint32_t MAX_NR_NOF_BEARERS = MAX_NR_DRB_ID + MAX_NR_SRB_ID; // 32 constexpr bool is_nr_lcid(uint32_t lcid) { diff --git a/lib/include/srsran/common/config_file.h b/lib/include/srsran/common/config_file.h index 76f6fc06c..aeff7e84a 100644 --- a/lib/include/srsran/common/config_file.h +++ b/lib/include/srsran/common/config_file.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -44,6 +44,7 @@ bool config_exists(std::string& filename, std::string default_name) homedir = "."; } snprintf(full_path, sizeof(full_path), "%s/.config/srsran/%s", homedir, default_name.c_str()); + printf("Couldn't open %s, trying %s\n", filename.c_str(), full_path); filename = std::string(full_path); // try to open again @@ -52,6 +53,7 @@ bool config_exists(std::string& filename, std::string default_name) // Last chance, try to find file in /etc/srsran ZERO_OBJECT(full_path); snprintf(full_path, sizeof(full_path), "/etc/srsran/%s", default_name.c_str()); + printf("Couldn't open %s either, trying %s\n", filename.c_str(), full_path); filename = std::string(full_path); // try to open again diff --git a/lib/include/srsran/common/crash_handler.h b/lib/include/srsran/common/crash_handler.h index d57ffbf2f..68b91ba9d 100644 --- a/lib/include/srsran/common/crash_handler.h +++ b/lib/include/srsran/common/crash_handler.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/enb_events.h b/lib/include/srsran/common/enb_events.h index b851af5d6..69fc973eb 100644 --- a/lib/include/srsran/common/enb_events.h +++ b/lib/include/srsran/common/enb_events.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -40,7 +40,8 @@ public: /// Logs into the underlying log channel any RRC event. virtual void log_rrc_event(uint32_t enb_cc_idx, - const std::string& asn1, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, unsigned type, unsigned additional_info, uint16_t rnti) = 0; @@ -52,16 +53,35 @@ public: virtual void log_s1_ctx_delete(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) = 0; /// Logs into the underlying log channel when a sector has been started. - virtual void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) = 0; + virtual void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id, const std::string &hnb_name) = 0; /// Logs into the underlying log channel when a sector has been stopped. - virtual void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) = 0; + virtual void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id, const std::string &hnb_name) = 0; - /// Logs into the underlying log channel a measurement report event.. - virtual void log_measurement_report(uint32_t enb_cc_idx, const std::string& asn1, uint16_t rnti) = 0; + /// Logs into the underlying log channel a measurement report event. + virtual void log_measurement_report(uint32_t enb_cc_idx, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, + uint16_t rnti) = 0; - /// Logs into the underlying log channel a RLF event. - virtual void log_rlf(uint32_t enb_cc_idx, const std::string& asn1, uint16_t rnti) = 0; + /// Logs into the underlying log channel a RLF report. + virtual void log_rlf_report(uint32_t enb_cc_idx, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, + uint16_t rnti) = 0; + + /// Logs into the underlying log channel a RLF detection event. + virtual void log_rlf_detected(uint32_t enb_cc_idx, const std::string& type, uint16_t rnti) = 0; + + /// Logs into the underlying log channel a handover command event. + virtual void log_handover_command(uint32_t enb_cc_idx, + uint32_t target_pci, + uint32_t target_earfcn, + uint16_t new_ue_rnti, + uint16_t rnti) = 0; + + /// Logs into the underlying log channel a connection resume event. + virtual void log_connection_resume(uint32_t enb_cc_idx, uint16_t resume_rnti, uint16_t rnti) = 0; }; /// Singleton class to provide global access to the event_logger_interface interface. @@ -70,12 +90,15 @@ class event_logger event_logger() = default; public: + /// ASN1 output printing format. + enum class asn1_output_format { text, octets }; + /// Returns the instance of the event logger. static event_logger_interface& get(); /// Uses the specified log channel for event logging. /// NOTE: This method is not thread safe. - static void configure(srslog::log_channel& c); + static void configure(srslog::log_channel& c, asn1_output_format asn1_format); private: static std::unique_ptr pimpl; diff --git a/lib/include/srsran/common/epoll_helper.h b/lib/include/srsran/common/epoll_helper.h index d55807c1f..ea56ec1f3 100644 --- a/lib/include/srsran/common/epoll_helper.h +++ b/lib/include/srsran/common/epoll_helper.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,6 +26,7 @@ #ifndef SRSRAN_EPOLL_HELPER_H #define SRSRAN_EPOLL_HELPER_H +#include #include #include #include @@ -67,7 +68,7 @@ private: class epoll_signal_handler : public epoll_handler { public: - epoll_signal_handler(bool* running_) : running(running_) {} + epoll_signal_handler(std::atomic& running_) : running(running_) {} int handle_event(int fd, epoll_event e, int epoll_fd) { @@ -81,7 +82,7 @@ public: case SIGINT: case SIGHUP: case SIGQUIT: - *running = false; + running = false; break; default: fprintf(stderr, "got signal %d\n", info.ssi_signo); @@ -91,7 +92,7 @@ public: } private: - bool* running = nullptr; + std::atomic& running; }; ///< Create periodic epoll timer every 1ms diff --git a/lib/include/srsran/common/gen_mch_tables.h b/lib/include/srsran/common/gen_mch_tables.h index 3dddc5179..acc588009 100644 --- a/lib/include/srsran/common/gen_mch_tables.h +++ b/lib/include/srsran/common/gen_mch_tables.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/int_helpers.h b/lib/include/srsran/common/int_helpers.h index c792e40b5..d1128a114 100644 --- a/lib/include/srsran/common/int_helpers.h +++ b/lib/include/srsran/common/int_helpers.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/interfaces_common.h b/lib/include/srsran/common/interfaces_common.h index 9e8f7498c..0774ac86b 100644 --- a/lib/include/srsran/common/interfaces_common.h +++ b/lib/include/srsran/common/interfaces_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,19 +29,20 @@ namespace srsran { -typedef struct { +struct phy_log_args_t { std::string phy_level = "none"; std::string phy_lib_level = "none"; + std::string id_preamble = ""; int phy_hex_limit = -1; -} phy_log_args_t; +}; -typedef struct { +struct rf_args_band_t { float min; float max; -} rf_args_band_t; +}; // RF/radio args -typedef struct { +struct rf_args_t { std::string type; std::string log_level; double srate_hz; @@ -65,14 +66,8 @@ typedef struct { std::array ch_rx_bands; std::array ch_tx_bands; -} rf_args_t; - -struct vnf_args_t { - std::string type; - std::string bind_addr; - uint16_t bind_port; - std::string log_level; - int log_hex_limit; + FILE** rx_files; // Array of pre-opened FILE* for rx instead of a real device + FILE** tx_files; // Array of pre-opened FILE* for tx instead of a real device }; class srsran_gw_config_t @@ -86,11 +81,9 @@ public: class read_pdu_interface { public: - virtual int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) = 0; + virtual uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) = 0; }; -class stack_interface_phy_nr -{}; } // namespace srsran diff --git a/lib/include/srsran/common/liblte_security.h b/lib/include/srsran/common/liblte_security.h index 95c6a691e..165d74fb2 100644 --- a/lib/include/srsran/common/liblte_security.h +++ b/lib/include/srsran/common/liblte_security.h @@ -147,6 +147,13 @@ LIBLTE_ERROR_ENUM liblte_security_generate_k_up(uint8* uint8* k_up_int); LIBLTE_ERROR_ENUM liblte_security_generate_sk_gnb(uint8_t* k_enb, uint8_t* sk_gnb, uint16_t scg_counter); +LIBLTE_ERROR_ENUM liblte_security_generate_res_star(uint8_t* ck, + uint8_t* ik, + const char* serving_network_name, + uint8_t* rand, + uint8_t* res, + size_t res_len, + uint8_t* res_star); /********************************************************************* Name: liblte_security_128_eia2 diff --git a/lib/include/srsran/common/log_helper.h b/lib/include/srsran/common/log_helper.h index 910078600..8527349e4 100644 --- a/lib/include/srsran/common/log_helper.h +++ b/lib/include/srsran/common/log_helper.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/mac_pcap.h b/lib/include/srsran/common/mac_pcap.h index 41371fc01..e7fb9b353 100644 --- a/lib/include/srsran/common/mac_pcap.h +++ b/lib/include/srsran/common/mac_pcap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/mac_pcap_base.h b/lib/include/srsran/common/mac_pcap_base.h index eacced47b..15c447c91 100644 --- a/lib/include/srsran/common/mac_pcap_base.h +++ b/lib/include/srsran/common/mac_pcap_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,6 +37,12 @@ class mac_pcap_base : protected srsran::thread { public: mac_pcap_base(); + + mac_pcap_base(const mac_pcap_base& other) = delete; + mac_pcap_base& operator=(const mac_pcap_base& other) = delete; + mac_pcap_base(mac_pcap_base&& other) = delete; + mac_pcap_base& operator=(mac_pcap_base&& other) = delete; + ~mac_pcap_base(); void enable(bool enable); virtual uint32_t close() = 0; @@ -45,7 +51,7 @@ public: // EUTRA void - write_ul_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, uint32_t reTX, uint32_t tti, uint8_t cc_idx); + write_ul_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, uint32_t reTX, uint32_t tti, uint8_t cc_idx); void write_dl_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, bool crc_ok, uint32_t tti, uint8_t cc_idx); void write_dl_ranti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t ranti, bool crc_ok, uint32_t tti, uint8_t cc_idx); @@ -102,11 +108,12 @@ protected: virtual void write_pdu(pcap_pdu_t& pdu) = 0; void run_thread() final; - std::mutex mutex; - srslog::basic_logger& logger; - bool running = false; - static_blocking_queue queue; - uint16_t ue_id = 0; + std::mutex mutex; + srslog::basic_logger& logger; + std::atomic running = {false}; + static_blocking_queue queue; + uint16_t ue_id = 0; + int emergency_handler_id = -1; private: void pack_and_queue(uint8_t* payload, diff --git a/lib/include/srsran/common/mac_pcap_net.h b/lib/include/srsran/common/mac_pcap_net.h index 36d150cbe..11a5e0571 100644 --- a/lib/include/srsran/common/mac_pcap_net.h +++ b/lib/include/srsran/common/mac_pcap_net.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/metrics_hub.h b/lib/include/srsran/common/metrics_hub.h index 36919e535..275d73934 100644 --- a/lib/include/srsran/common/metrics_hub.h +++ b/lib/include/srsran/common/metrics_hub.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/multiqueue.h b/lib/include/srsran/common/multiqueue.h index 8fc87a351..2b7e6317a 100644 --- a/lib/include/srsran/common/multiqueue.h +++ b/lib/include/srsran/common/multiqueue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,6 +28,7 @@ #ifndef SRSRAN_MULTIQUEUE_H #define SRSRAN_MULTIQUEUE_H +#include "srsran/adt/circular_buffer.h" #include "srsran/adt/move_callback.h" #include #include @@ -40,92 +41,206 @@ namespace srsran { #define MULTIQUEUE_DEFAULT_CAPACITY (8192) // Default per-queue capacity +/** + * N-to-1 Message-Passing Broker that manages the creation, destruction of input ports, and popping of messages that + * are pushed to these ports. + * Each port provides a thread-safe push(...) / try_push(...) interface to enqueue messages + * The class will pop from the several created ports in a round-robin fashion. + * The popping() interface is not safe-thread. That means, that it is expected that only one thread will + * be popping tasks. + * @tparam myobj message type + */ template class multiqueue_handler { - class circular_buffer + class input_port_impl { public: - circular_buffer(uint32_t cap) : buffer(cap + 1) {} - circular_buffer(circular_buffer&& other) noexcept + input_port_impl(uint32_t cap, multiqueue_handler* parent_) : buffer(cap), parent(parent_) {} + input_port_impl(const input_port_impl&) = delete; + input_port_impl(input_port_impl&&) = delete; + input_port_impl& operator=(const input_port_impl&) = delete; + input_port_impl& operator=(input_port_impl&&) = delete; + ~input_port_impl() { deactivate_blocking(); } + + size_t capacity() const { return buffer.max_size(); } + size_t size() const { - active = other.active; - other.active = false; - widx = other.widx; - ridx = other.ridx; - buffer = std::move(other.buffer); + std::lock_guard lock(q_mutex); + return buffer.size(); + } + bool active() const + { + std::lock_guard lock(q_mutex); + return active_; + } + void set_active(bool val) + { + std::unique_lock lock(q_mutex); + if (val == active_) { + // no-op + return; + } + active_ = val; + + if (not active_) { + buffer.clear(); + lock.unlock(); + // unlock blocked pushing threads + cv_full.notify_all(); + } } - std::condition_variable cv_full; - bool active = true; + void deactivate_blocking() + { + set_active(false); - bool empty() const { return widx == ridx; } - size_t size() const { return widx >= ridx ? widx - ridx : widx + (buffer.size() - ridx); } - bool full() const { return (ridx > 0) ? widx == ridx - 1 : widx == buffer.size() - 1; } - size_t capacity() const { return buffer.size() - 1; } + // wait for all the pushers to unlock + std::unique_lock lock(q_mutex); + while (nof_waiting > 0) { + cv_exit.wait(lock); + } + } template void push(T&& o) noexcept { - buffer[widx++] = std::forward(o); - if (widx >= buffer.size()) { - widx = 0; - } + push_(&o, true); } - void pop() noexcept + bool try_push(const myobj& o) { return push_(&o, false); } + + srsran::error_type try_push(myobj&& o) { - ridx++; - if (ridx >= buffer.size()) { - ridx = 0; + if (push_(&o, false)) { + return {}; } + return {std::move(o)}; } - myobj& front() noexcept { return buffer[ridx]; } - const myobj& front() const noexcept { return buffer[ridx]; } + bool try_pop(myobj& obj) + { + std::unique_lock lock(q_mutex); + return pop_(lock, obj); + } + + bool try_pop(myobj& obj, bool& try_lock_success) + { + std::unique_lock lock(q_mutex, std::try_to_lock); + try_lock_success = lock.owns_lock(); + return try_lock_success ? pop_(lock, obj) : false; + } private: - std::vector buffer; - size_t widx = 0, ridx = 0; + template + bool push_(T* o, bool blocking) noexcept + { + std::unique_lock lock(q_mutex); + if (not blocking) { + // non-blocking case + if (not active_ or buffer.full()) { + return false; + } + } else { + // blocking case + while (active_ and buffer.full()) { + nof_waiting++; + cv_full.wait(lock); + nof_waiting--; + } + if (not active_) { + lock.unlock(); + cv_exit.notify_one(); + return false; + } + } + buffer.push(std::forward(*o)); + return true; + } + + bool pop_(std::unique_lock& lock, myobj& obj) + { + if (buffer.empty()) { + return false; + } + obj = std::move(buffer.top()); + buffer.pop(); + if (nof_waiting > 0) { + lock.unlock(); + cv_full.notify_one(); + } + return true; + } + + multiqueue_handler* parent = nullptr; + + mutable std::mutex q_mutex; + srsran::dyn_circular_buffer buffer; + std::condition_variable cv_full, cv_exit; + bool active_ = true; + int nof_waiting = 0; }; public: class queue_handle { public: - queue_handle() = default; - queue_handle(multiqueue_handler* parent_, int id) : parent(parent_), queue_id(id) {} + explicit queue_handle(input_port_impl* impl_ = nullptr) : impl(impl_) {} template void push(FwdRef&& value) { - parent->push(queue_id, std::forward(value)); + impl->push(std::forward(value)); } - bool try_push(const myobj& value) { return parent->try_push(queue_id, value); } - std::pair try_push(myobj&& value) { return parent->try_push(queue_id, std::move(value)); } - size_t size() { return parent->size(queue_id); } + bool try_push(const myobj& value) { return impl->try_push(value); } + srsran::error_type try_push(myobj&& value) { return impl->try_push(std::move(value)); } + void reset() + { + if (impl != nullptr) { + impl->deactivate_blocking(); + impl = nullptr; + } + } + + size_t size() { return impl->size(); } + size_t capacity() { return impl->capacity(); } + bool active() const { return impl != nullptr and impl->active(); } + bool empty() const { return impl->size() == 0; } + + bool operator==(const queue_handle& other) const { return impl == other.impl; } + bool operator!=(const queue_handle& other) const { return impl != other.impl; } private: - multiqueue_handler* parent = nullptr; - int queue_id = -1; + struct recycle_op { + void operator()(input_port_impl* p) + { + if (p != nullptr) { + p->deactivate_blocking(); + } + } + }; + std::unique_ptr impl; }; - explicit multiqueue_handler(uint32_t capacity_ = MULTIQUEUE_DEFAULT_CAPACITY) : capacity(capacity_) {} - ~multiqueue_handler() { reset(); } + explicit multiqueue_handler(uint32_t default_capacity_ = MULTIQUEUE_DEFAULT_CAPACITY) : + default_capacity(default_capacity_) + {} + ~multiqueue_handler() { stop(); } - void reset() + void stop() { std::unique_lock lock(mutex); running = false; - while (nof_threads_waiting > 0) { - uint32_t size = queues.size(); - cv_empty.notify_one(); - for (uint32_t i = 0; i < size; ++i) { - queues[i].cv_full.notify_all(); - } - // wait for all threads to unblock + for (auto& q : queues) { + // signal deactivation to pushing threads in a non-blocking way + q.set_active(false); + } + while (consumer_state) { cv_exit.wait(lock); } - queues.clear(); + for (auto& q : queues) { + // ensure the queues are finished being deactivated + q.deactivate_blocking(); + } } /** @@ -133,197 +248,103 @@ public: * @param capacity_ The capacity of the queue. * @return The index of the newly created (or reused) queue within the vector of queues. */ - int add_queue(uint32_t capacity_) + queue_handle add_queue(uint32_t capacity_) { uint32_t qidx = 0; std::lock_guard lock(mutex); if (not running) { - return -1; + return queue_handle(); + } + while (qidx < queues.size() and (queues[qidx].active() or (queues[qidx].capacity() != capacity_))) { + ++qidx; } - for (; qidx < queues.size() and queues[qidx].active; ++qidx) - ; // check if there is a free queue of the required size - if (qidx == queues.size() || queues[qidx].capacity() != capacity_) { + if (qidx == queues.size()) { // create new queue - queues.emplace_back(capacity_); + queues.emplace_back(capacity_, this); qidx = queues.size() - 1; // update qidx to the last element } else { - queues[qidx].active = true; + queues[qidx].set_active(true); } - return (int)qidx; + return queue_handle(&queues[qidx]); } /** * Add queue using the default capacity of the underlying multiqueue * @return The queue index */ - int add_queue() { return add_queue(capacity); } + queue_handle add_queue() { return add_queue(default_capacity); } - int nof_queues() + uint32_t nof_queues() const { std::lock_guard lock(mutex); uint32_t count = 0; for (uint32_t i = 0; i < queues.size(); ++i) { - count += queues[i].active ? 1 : 0; + count += queues[i].active() ? 1 : 0; } return count; } - template - void push(int q_idx, FwdRef&& value) - { - { - std::unique_lock lock(mutex); - while (is_queue_active_(q_idx) and queues[q_idx].full()) { - nof_threads_waiting++; - queues[q_idx].cv_full.wait(lock); - nof_threads_waiting--; - } - if (not is_queue_active_(q_idx)) { - cv_exit.notify_one(); - return; - } - queues[q_idx].push(std::forward(value)); - } - cv_empty.notify_one(); - } - - bool try_push(int q_idx, const myobj& value) - { - { - std::lock_guard lock(mutex); - if (not is_queue_active_(q_idx) or queues[q_idx].full()) { - return false; - } - queues[q_idx].push(value); - } - cv_empty.notify_one(); - return true; - } - - std::pair try_push(int q_idx, myobj&& value) - { - { - std::lock_guard lck(mutex); - if (not is_queue_active_(q_idx) or queues[q_idx].full()) { - return {false, std::move(value)}; - } - queues[q_idx].push(std::move(value)); - } - cv_empty.notify_one(); - return {true, std::move(value)}; - } - - int wait_pop(myobj* value) + bool wait_pop(myobj* value) { std::unique_lock lock(mutex); + consumer_state = true; while (running) { if (round_robin_pop_(value)) { - if (nof_threads_waiting > 0) { - lock.unlock(); - queues[spin_idx].cv_full.notify_one(); - } - return spin_idx; + consumer_state = false; + return true; } - nof_threads_waiting++; - cv_empty.wait(lock); - nof_threads_waiting--; + lock.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + lock.lock(); } + consumer_state = false; + lock.unlock(); cv_exit.notify_one(); - return -1; + return false; } - int try_pop(myobj* value) + bool try_pop(myobj* value) { std::unique_lock lock(mutex); - if (running) { - if (round_robin_pop_(value)) { - if (nof_threads_waiting > 0) { - lock.unlock(); - queues[spin_idx].cv_full.notify_one(); - } - return spin_idx; - } - // didn't find any task - return -1; - } - cv_exit.notify_one(); - return -1; + return running and round_robin_pop_(value); } - bool empty(int qidx) - { - std::lock_guard lck(mutex); - return queues[qidx].empty(); - } - - size_t size(int qidx) - { - std::lock_guard lck(mutex); - return queues[qidx].size(); - } - - size_t max_size(int qidx) - { - std::lock_guard lck(mutex); - return queues[qidx].capacity(); - } - - const myobj& front(int qidx) - { - std::lock_guard lck(mutex); - return queues[qidx].front(); - } - - void erase_queue(int qidx) - { - std::lock_guard lck(mutex); - if (is_queue_active_(qidx)) { - queues[qidx].active = false; - while (not queues[qidx].empty()) { - queues[qidx].pop(); - } - } - } - - bool is_queue_active(int qidx) - { - std::lock_guard lck(mutex); - return is_queue_active_(qidx); - } - - queue_handle get_queue_handler() { return {this, add_queue()}; } - queue_handle get_queue_handler(uint32_t size) { return {this, add_queue(size)}; } - private: - bool is_queue_active_(int qidx) const { return running and queues[qidx].active; } - bool round_robin_pop_(myobj* value) { // Round-robin for all queues - for (const circular_buffer& q : queues) { - spin_idx = (spin_idx + 1) % queues.size(); - if (is_queue_active_(spin_idx) and not queues[spin_idx].empty()) { - if (value) { - *value = std::move(queues[spin_idx].front()); - } - queues[spin_idx].pop(); + auto q_it = queues.begin() + spin_idx; + uint32_t count = 0; + for (; count < queues.size(); ++count, ++q_it) { + if (q_it == queues.end()) { + q_it = queues.begin(); // wrap-around + } + bool try_lock_success = true; + if (q_it->try_pop(*value, try_lock_success)) { + spin_idx = (spin_idx + count + 1) % queues.size(); return true; } + if (not try_lock_success) { + // restart RR search, as there was a collision with a producer + count = 0; + } } return false; } - std::mutex mutex; - std::condition_variable cv_empty, cv_exit; - uint32_t spin_idx = 0; - bool running = true; - std::vector queues; - uint32_t capacity = 0; - uint32_t nof_threads_waiting = 0; + mutable std::mutex mutex; + std::condition_variable cv_exit; + uint32_t spin_idx = 0; + bool running = true, consumer_state = false; + std::deque queues; + uint32_t default_capacity = 0; }; +template +using queue_handle = typename multiqueue_handler::queue_handle; + //! Specialization for tasks using task_multiqueue = multiqueue_handler; using task_queue_handle = task_multiqueue::queue_handle; diff --git a/lib/include/srsran/common/nas_pcap.h b/lib/include/srsran/common/nas_pcap.h index 1079d331e..61b687e8b 100644 --- a/lib/include/srsran/common/nas_pcap.h +++ b/lib/include/srsran/common/nas_pcap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #ifndef SRSRAN_NAS_PCAP_H #define SRSRAN_NAS_PCAP_H +#include "srsran/common/common.h" #include "srsran/common/pcap.h" #include @@ -30,22 +31,19 @@ namespace srsran { class nas_pcap { public: - nas_pcap() - { - enable_write = false; - ue_id = 0; - pcap_file = NULL; - } - void enable(); - uint32_t open(std::string filename_, uint32_t ue_id = 0); - void close(); - void write_nas(uint8_t* pdu, uint32_t pdu_len_bytes); + nas_pcap(); + ~nas_pcap(); + void enable(); + uint32_t open(std::string filename_, uint32_t ue_id = 0, srsran_rat_t rat_type = srsran_rat_t::lte); + void close(); + void write_nas(uint8_t* pdu, uint32_t pdu_len_bytes); private: - bool enable_write; + bool enable_write = false; std::string filename; - FILE* pcap_file; - uint32_t ue_id; + FILE* pcap_file = nullptr; + uint32_t ue_id = 0; + int emergency_handler_id = -1; void pack_and_write(uint8_t* pdu, uint32_t pdu_len_bytes); }; diff --git a/lib/include/srsran/common/netsource_handler.h b/lib/include/srsran/common/netsource_handler.h index 2a8c7e264..cf844ba33 100644 --- a/lib/include/srsran/common/netsource_handler.h +++ b/lib/include/srsran/common/netsource_handler.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/network_utils.h b/lib/include/srsran/common/network_utils.h index eaff437cf..1c581b019 100644 --- a/lib/include/srsran/common/network_utils.h +++ b/lib/include/srsran/common/network_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -83,9 +83,14 @@ public: std::string get_ip() const { return net_utils::get_ip(addr); } net_utils::socket_type get_family() const { return net_utils::get_addr_family(sockfd); } + bool open_socket(net_utils::addr_family ip, net_utils::socket_type socket_type, net_utils::protocol_type protocol); bool bind_addr(const char* bind_addr_str, int port); bool connect_to(const char* dest_addr_str, int dest_port, sockaddr_in* dest_sockaddr = nullptr); - bool open_socket(net_utils::addr_family ip, net_utils::socket_type socket_type, net_utils::protocol_type protocol); + bool start_listen(); + bool reuse_addr(); + bool sctp_subscribe_to_events(); + bool sctp_set_rto_opts(int rto_max); + bool sctp_set_init_msg_opts(int max_init_attempts, int max_init_timeo); int get_socket() const { return sockfd; }; protected: @@ -95,8 +100,7 @@ protected: namespace net_utils { -bool sctp_init_client(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str); -bool sctp_init_server(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port); +bool sctp_init_socket(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int bind_port); } // namespace net_utils @@ -163,7 +167,7 @@ private: // state std::mutex socket_mutex; std::map active_sockets; - bool running = false; + std::atomic running = {false}; int pipefd[2] = {-1, -1}; std::vector rem_fd_tmp_list; std::condition_variable rem_cvar; @@ -196,6 +200,12 @@ make_sctp_sdu_handler(srslog::basic_logger& logger, srsran::task_queue_handle& q socket_manager_itf::recv_callback_t make_sdu_handler(srslog::basic_logger& logger, srsran::task_queue_handle& queue, recvfrom_callback_t rx_callback); +inline socket_manager& get_rx_io_manager() +{ + static socket_manager io; + return io; +} + } // namespace srsran #endif // SRSRAN_RX_SOCKET_HANDLER_H diff --git a/lib/include/srsran/common/ngap_pcap.h b/lib/include/srsran/common/ngap_pcap.h new file mode 100644 index 000000000..772f05c5a --- /dev/null +++ b/lib/include/srsran/common/ngap_pcap.h @@ -0,0 +1,54 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_NGAP_PCAP_H +#define SRSRAN_NGAP_PCAP_H + +#include "srsran/common/pcap.h" +#include + +namespace srsran { + +class ngap_pcap +{ +public: + ngap_pcap(); + ~ngap_pcap(); + ngap_pcap(const ngap_pcap& other) = delete; + ngap_pcap& operator=(const ngap_pcap& other) = delete; + ngap_pcap(ngap_pcap&& other) = delete; + ngap_pcap& operator=(ngap_pcap&& other) = delete; + + void enable(); + void open(const char* filename_); + void close(); + void write_ngap(uint8_t* pdu, uint32_t pdu_len_bytes); + +private: + bool enable_write = false; + std::string filename; + FILE* pcap_file = nullptr; + int emergency_handler_id = -1; +}; + +} // namespace srsran + +#endif // SRSRAN_NGAP_PCAP_H diff --git a/lib/include/srsran/common/pcap.h b/lib/include/srsran/common/pcap.h index a3b337aea..2b732ffff 100644 --- a/lib/include/srsran/common/pcap.h +++ b/lib/include/srsran/common/pcap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,6 +32,8 @@ #define NAS_LTE_DLT 148 #define UDP_DLT 149 // UDP needs to be selected as protocol #define S1AP_LTE_DLT 150 +#define NAS_5G_DLT 151 +#define NGAP_5G_DLT 152 /* This structure gets written to the start of the file */ typedef struct pcap_hdr_s { @@ -188,15 +190,21 @@ typedef struct S1AP_Context_Info_s { unsigned char dummy; } S1AP_Context_Info_t; +/* Context information for every S1AP PDU that will be logged */ +typedef struct NGAP_Context_Info_s { + // No Context yet + unsigned char dummy; +} NGAP_Context_Info_t; + #ifdef __cplusplus extern "C" { #endif /* Open the file and write file header */ -FILE* LTE_PCAP_Open(uint32_t DLT, const char* fileName); +FILE* DLT_PCAP_Open(uint32_t DLT, const char* fileName); /* Close the PCAP file */ -void LTE_PCAP_Close(FILE* fd); +void DLT_PCAP_Close(FILE* fd); /* Write an individual MAC PDU (PCAP packet header + mac-context + mac-pdu) */ int LTE_PCAP_MAC_WritePDU(FILE* fd, MAC_Context_Info_t* context, const unsigned char* PDU, unsigned int length); @@ -212,6 +220,9 @@ int LTE_PCAP_RLC_WritePDU(FILE* fd, RLC_Context_Info_t* context, const unsigned /* Write an individual S1AP PDU (PCAP packet header + s1ap-context + s1ap-pdu) */ int LTE_PCAP_S1AP_WritePDU(FILE* fd, S1AP_Context_Info_t* context, const unsigned char* PDU, unsigned int length); +/* Write an individual S1AP PDU (PCAP packet header + s1ap-context + s1ap-pdu) */ +int LTE_PCAP_NGAP_WritePDU(FILE* fd, NGAP_Context_Info_t* context, const unsigned char* PDU, unsigned int length); + /* Write an individual NR MAC PDU (PCAP packet header + UDP header + nr-mac-context + mac-pdu) */ int NR_PCAP_MAC_UDP_WritePDU(FILE* fd, mac_nr_context_info_t* context, const unsigned char* PDU, unsigned int length); int NR_PCAP_PACK_MAC_CONTEXT_TO_BUFFER(mac_nr_context_info_t* context, uint8_t* buffer, unsigned int length); diff --git a/lib/include/srsran/common/phy_cfg_nr.h b/lib/include/srsran/common/phy_cfg_nr.h new file mode 100644 index 000000000..a1d1a25c2 --- /dev/null +++ b/lib/include/srsran/common/phy_cfg_nr.h @@ -0,0 +1,177 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_PHY_CFG_NR_H +#define SRSRAN_PHY_CFG_NR_H + +#include "srsran/config.h" +#include "srsran/srsran.h" +#include +#include +#include + +namespace srsran { + +/*************************** + * PHY Config + **************************/ + +struct phy_cfg_nr_t { + /** + * SSB configuration + */ + struct ssb_cfg_t { + uint32_t periodicity_ms = 0; + std::array position_in_burst = {}; + srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_30kHz; + srsran_ssb_pattern_t pattern = SRSRAN_SSB_PATTERN_A; + }; + + srsran_duplex_config_nr_t duplex = {}; + srsran_sch_hl_cfg_nr_t pdsch = {}; + srsran_sch_hl_cfg_nr_t pusch = {}; + srsran_pucch_nr_hl_cfg_t pucch = {}; + srsran_prach_cfg_t prach = {}; + srsran_pdcch_cfg_nr_t pdcch = {}; + srsran_harq_ack_cfg_hl_t harq_ack = {}; + srsran_csi_hl_cfg_t csi = {}; + srsran_carrier_nr_t carrier = {}; + ssb_cfg_t ssb = {}; + uint32_t t_offset = 0; ///< n-TimingAdvanceOffset + + phy_cfg_nr_t() {} + + bool carrier_is_equal(const srsran::phy_cfg_nr_t& other) const + { + return srsran_carrier_nr_equal(&carrier, &other.carrier); + } + + /** + * @brief Computes the DCI configuration for the current UE configuration + */ + srsran_dci_cfg_nr_t get_dci_cfg() const; + + /** + * @brief Asserts that the PDCCH configuration is valid for a given Search Space identifier + * @param ss_id Identifier + * @return true if the configuration is valid, false otherwise + */ + bool assert_ss_id(uint32_t ss_id) const; + + /** + * @brief Calculates the DCI location candidates for a given search space and aggregation level + * @param slot_idx Slot index + * @param rnti UE temporal identifier + * @param ss_id Search Space identifier + * @param L Aggregation level + * @param[out] locations DCI candidate locations + * @return true if the configuration is valid, false otherwise + */ + bool get_dci_locations( + const uint32_t& slot_idx, + const uint16_t& rnti, + const uint32_t& ss_id, + const uint32_t& L, + srsran::bounded_vector& locations) const; + + /** + * @brief Selects a valid DCI format for scheduling PDSCH and given Search Space identifier + * @param ss_id Identifier + * @return A valid DCI format if available, SRSRAN_DCI_FORMAT_NR_COUNT otherwise + */ + srsran_dci_format_nr_t get_dci_format_pdsch(uint32_t ss_id) const; + + /** + * @brief Selects a valid DCI format for scheduling PUSCH and given Search Space identifier + * @param ss_id Identifier + * @return A valid DCI format if available, SRSRAN_DCI_FORMAT_NR_COUNT otherwise + */ + srsran_dci_format_nr_t get_dci_format_pusch(uint32_t ss_id) const; + + /** + * @brief Fills PDSCH DCI context for C-RNTI using a search space identifier, DCI candidate location and RNTI + */ + bool get_dci_ctx_pdsch_rnti_c(uint32_t ss_id, + const srsran_dci_location_t& location, + const uint16_t& rnti, + srsran_dci_ctx_t& ctx) const; + + /** + * @brief Fills PUSCH DCI context for C-RNTI using a search space identifier, DCI candidate location and RNTI + */ + bool get_dci_ctx_pusch_rnti_c(uint32_t ss_id, + const srsran_dci_location_t& location, + const uint16_t& rnti, + srsran_dci_ctx_t& ctx) const; + + /** + * @brief Get PDSCH configuration for a given slot and DCI + */ + bool + get_pdsch_cfg(const srsran_slot_cfg_t& slot_cfg, const srsran_dci_dl_nr_t& dci, srsran_sch_cfg_nr_t& pdsch_cfg) const; + + /** + * @brief Get PUSCH configuration for a given slot and DCI + */ + bool + get_pusch_cfg(const srsran_slot_cfg_t& slot_cfg, const srsran_dci_ul_nr_t& dci, srsran_sch_cfg_nr_t& pdsch_cfg) const; + + /** + * @brief Get PDSCH ACK resource for a given PDSCH transmission + */ + bool get_pdsch_ack_resource(const srsran_dci_dl_nr_t& dci_dl, srsran_harq_ack_resource_t& ack_resource) const; + + /** + * @brief Compute UCI configuration for the given slot from the pending PDSCH ACK resources, periodic CSI, + * periodic SR resources and so on. + */ + bool get_uci_cfg(const srsran_slot_cfg_t& slot_cfg, + const srsran_pdsch_ack_nr_t& pdsch_ack, + srsran_uci_cfg_nr_t& uci_cfg) const; + + /** + * @brief Compute UCI configuration for PUCCH for the given slot from the pending PDSCH ACK resources, periodic CSI, + * periodic SR resources and so on. + */ + bool get_pucch_uci_cfg(const srsran_slot_cfg_t& slot_cfg, + const srsran_uci_cfg_nr_t& uci_cfg, + srsran_pucch_nr_common_cfg_t& cfg, + srsran_pucch_nr_resource_t& resource) const; + + /** + * @brief Compute UCI configuration for PUSCH for the given slot from the pending PDSCH ACK resources, periodic CSI, + * periodic SR resources and so on. + */ + bool get_pusch_uci_cfg(const srsran_slot_cfg_t& slot_cfg, + const srsran_uci_cfg_nr_t& uci_cfg, + srsran_sch_cfg_nr_t& pusch_cfg) const; + + /** + * @brief Generate SSB configuration from the overall configuration + * @attention Sampling rate is the only parameter missing + * @return valid SSB configuration + */ + srsran_ssb_cfg_t get_ssb_cfg() const; +}; + +} // namespace srsran + +#endif // SRSRAN_PHY_CFG_NR_H diff --git a/lib/include/srsran/common/phy_cfg_nr_default.h b/lib/include/srsran/common/phy_cfg_nr_default.h new file mode 100644 index 000000000..6dd553f2c --- /dev/null +++ b/lib/include/srsran/common/phy_cfg_nr_default.h @@ -0,0 +1,200 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_PHY_CFG_NR_DEFAULT_H +#define SRSRAN_PHY_CFG_NR_DEFAULT_H + +#include "phy_cfg_nr.h" + +namespace srsran { + +class phy_cfg_nr_default_t : public phy_cfg_nr_t +{ +public: + struct reference_cfg_t { + enum { + /** + * @brief Carrier reference configuration for 10MHz serving cell bandwidth + * - BW: 10 MHZ (52 PRB) + * - PCI: 500 + * - SCS: 15 kHz + * - SSB: 5ms + */ + R_CARRIER_CUSTOM_10MHZ = 0, + /** + * @brief Carrier reference configuration for 10MHz serving cell bandwidth + * - BW: 20 MHZ (106 PRB) + * - PCI: 500 + * - SCS: 15 kHz + * - SSB: 5ms + */ + R_CARRIER_CUSTOM_20MHZ, + R_CARRIER_COUNT + } carrier = R_CARRIER_CUSTOM_10MHZ; + const std::array R_CARRIER_STRING = {{"10MHz", "20MHz"}}; + + enum { + /** + * @brief FDD, all slots for DL and UL + */ + R_DUPLEX_FDD = 0, + + /** + * @brief TDD custom reference 5 slot DL and 5 slot UL + */ + R_DUPLEX_TDD_CUSTOM_6_4, + + /** + * @brief TDD pattern FR1.15-1 defined in TS38.101-4 Table A.1.2-1 + */ + R_DUPLEX_TDD_FR1_15_1, + R_DUPLEX_COUNT, + } duplex = R_DUPLEX_TDD_CUSTOM_6_4; + const std::array R_DUPLEX_STRING = {{"FDD", "6D+4U", "FR1.15-1"}}; + + enum { + /** + * @brief Carrier reference configuration for 10MHz serving cell bandwidth + * - CORESET: all channel, 1 symbol + * - Single common Search Space + * - 2 possible candidate per aggregation level to allow DL and UL grants simultaneously + */ + R_PDCCH_CUSTOM_COMMON_SS = 0, + } pdcch = R_PDCCH_CUSTOM_COMMON_SS; + + enum { + /** + * @brief Custom fallback baseline configuration, designed for component testing + * - Defined single common PDSCH time allocation starting at symbol index 1 and length 13 + * - No DMRS dedicated configuration + */ + R_PDSCH_DEFAULT = 0, + + /** + * @brief PDSCH parameters described in TS 38.101-4 Table 5.2.2.2.1-2 for the test described in table 5.2.2.2.1-3 + */ + R_PDSCH_TS38101_5_2_1, + + /** + * @brief Invalid PDSCH reference channel + */ + R_PDSCH_COUNT + + } pdsch = R_PDSCH_DEFAULT; + const std::array R_PDSCH_STRING = {{"default", "ts38101/5.2-1"}}; + + enum { + /** + * @brief Custom fallback baseline configuration, designed for component testing + * - Single Time resource allocation + * - transmission starts at symbol index 0 for 14 symbols + * - k is 4 slots + * - No DMRS dedicated configuration + */ + R_PUSCH_DEFAULT = 0, + } pusch = R_PUSCH_DEFAULT; + + enum { + /** + * @brief Custom single PUCCH resource per set + * - Format 1 for 1 or 2 bits + * - Format 2 for more than 2 bits + */ + R_PUCCH_CUSTOM_ONE = 0, + } pucch = R_PUCCH_CUSTOM_ONE; + + enum { + /** + * @brief Sets the delay between PDSCH and HARQ feedback timing automatically + * - Dynamic HARQ ACK codebook + * - Guarantees a minimum delay of 4ms + * - Assume 15kHz SCS + * - Assume TDD pattern2 is not enabled + */ + R_HARQ_AUTO = 0, + } harq = R_HARQ_AUTO; + + enum { + /** + * @brief Sets the PRACH configuration to an LTE compatible configuration + * - Configuration index 0 + * - Frequency offset 2 PRB + * - Root sequence 2 + */ + R_PRACH_DEFAULT_LTE, + } prach = R_PRACH_DEFAULT_LTE; + + reference_cfg_t() = default; + explicit reference_cfg_t(const std::string& args); + }; + + phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg); + +private: + /** + * Carrier make helper methods + */ + static void make_carrier_custom_10MHz(srsran_carrier_nr_t& carrier); + static void make_carrier_custom_20MHz(srsran_carrier_nr_t& carrier); + + /** + * TDD make helper methods + */ + static void make_tdd_custom_6_4(srsran_duplex_config_nr_t& duplex); + static void make_tdd_fr1_15_1(srsran_duplex_config_nr_t& duplex); + + /** + * PDCCH make helper methods + */ + static void make_pdcch_custom_common_ss(srsran_pdcch_cfg_nr_t& pdcch, const srsran_carrier_nr_t& carrier); + + /** + * PDSCH make helper methods + */ + static void make_pdsch_default(srsran_sch_hl_cfg_nr_t& pdsch); + static void make_pdsch_2_1_1_tdd(const srsran_carrier_nr_t& carrier, srsran_sch_hl_cfg_nr_t& pdsch); + + /** + * PUSCH make helper methods + */ + static void make_pusch_default(srsran_sch_hl_cfg_nr_t& pusch); + + /** + * PUCCH make helper methods + */ + static void make_pucch_custom_one(srsran_pucch_nr_hl_cfg_t& pucch); + + /** + * HARQ make helper methods + */ + static void make_harq_auto(srsran_harq_ack_cfg_hl_t& harq, + const srsran_carrier_nr_t& carrier, + const srsran_duplex_config_nr_t& duplex_cfg); + + /** + * PRACH make helper methods + */ + static void make_prach_default_lte(srsran_prach_cfg_t& prach); +}; + +} // namespace srsran + +#endif // SRSRAN_PHY_CFG_NR_DEFAULT_H diff --git a/lib/include/srsran/common/rlc_pcap.h b/lib/include/srsran/common/rlc_pcap.h index dc18aff18..766ffabb1 100644 --- a/lib/include/srsran/common/rlc_pcap.h +++ b/lib/include/srsran/common/rlc_pcap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,7 +33,7 @@ class rlc_pcap public: rlc_pcap() {} void enable(bool en); - void open(const char* filename, rlc_config_t config); + void open(const char* filename, const rlc_config_t& config); void close(); void set_ue_id(uint16_t ue_id); diff --git a/lib/include/srsran/common/rwlock_guard.h b/lib/include/srsran/common/rwlock_guard.h index 36bed7c96..ba4fa0410 100644 --- a/lib/include/srsran/common/rwlock_guard.h +++ b/lib/include/srsran/common/rwlock_guard.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/s1ap_pcap.h b/lib/include/srsran/common/s1ap_pcap.h index 3ddb0cfef..4833f61d1 100644 --- a/lib/include/srsran/common/s1ap_pcap.h +++ b/lib/include/srsran/common/s1ap_pcap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,25 +23,30 @@ #define SRSRAN_S1AP_PCAP_H #include "srsran/common/pcap.h" +#include namespace srsran { class s1ap_pcap { public: - s1ap_pcap() - { - enable_write = false; - pcap_file = NULL; - } + s1ap_pcap(); + ~s1ap_pcap(); + s1ap_pcap(const s1ap_pcap& other) = delete; + s1ap_pcap& operator=(const s1ap_pcap& other) = delete; + s1ap_pcap(s1ap_pcap&& other) = delete; + s1ap_pcap& operator=(s1ap_pcap&& other) = delete; + void enable(); - void open(const char* filename); + void open(const char* filename_); void close(); void write_s1ap(uint8_t* pdu, uint32_t pdu_len_bytes); private: - bool enable_write; - FILE* pcap_file; + bool enable_write = false; + std::string filename; + FILE* pcap_file = nullptr; + int emergency_handler_id = -1; }; } // namespace srsran diff --git a/lib/include/srsran/common/s3g.h b/lib/include/srsran/common/s3g.h index c4d37a777..c810c3768 100644 --- a/lib/include/srsran/common/s3g.h +++ b/lib/include/srsran/common/s3g.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/security.h b/lib/include/srsran/common/security.h index f673c3d57..72b8c215d 100644 --- a/lib/include/srsran/common/security.h +++ b/lib/include/srsran/common/security.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,7 +27,21 @@ *****************************************************************************/ #include "srsran/common/common.h" +#include "srsran/srslog/srslog.h" +#include + +#define AKA_RAND_LEN 16 +#define AKA_AUTN_LEN 16 +#define AKA_AUTS_LEN 14 +#define RES_MAX_LEN 16 +#define MAC_LEN 8 +#define IK_LEN 16 +#define CK_LEN 16 +#define AK_LEN 6 +#define SQN_LEN 6 + +#define KEY_LEN 32 namespace srsran { typedef enum { @@ -54,6 +68,28 @@ static const char integrity_algorithm_id_text[INTEGRITY_ALGORITHM_ID_N_ITEMS][20 "128-EIA2", "128-EIA3"}; +typedef enum { + CIPHERING_ALGORITHM_ID_NR_NEA0 = 0, + CIPHERING_ALGORITHM_ID_NR_128_NEA1, + CIPHERING_ALGORITHM_ID_NR_128_NEA2, + CIPHERING_ALGORITHM_ID_NR_128_NEA3, + CIPHERING_ALGORITHM_ID_NR_N_ITEMS, +} CIPHERING_ALGORITHM_ID_NR_ENUM; +static const char ciphering_algorithm_id_nr_text[CIPHERING_ALGORITHM_ID_N_ITEMS][20] = {"NEA0", + "128-NEA1", + "128-NEA2", + "128-NEA3"}; +typedef enum { + INTEGRITY_ALGORITHM_ID_NR_NIA0 = 0, + INTEGRITY_ALGORITHM_ID_NR_128_NIA1, + INTEGRITY_ALGORITHM_ID_NR_128_NIA2, + INTEGRITY_ALGORITHM_ID_NR_128_NIA3, + INTEGRITY_ALGORITHM_ID_NR_N_ITEMS, +} INTEGRITY_ALGORITHM_ID_NR_ENUM; +static const char integrity_algorithm_id_nr_text[INTEGRITY_ALGORITHM_ID_N_ITEMS][20] = {"NIA0", + "128-NIA1", + "128-NIA2", + "128-NIA3"}; typedef enum { SECURITY_DIRECTION_UPLINK = 0, SECURITY_DIRECTION_DOWNLINK = 1, @@ -70,6 +106,7 @@ struct k_enb_context_t { }; struct k_gnb_context_t { + as_key_t k_gnb; as_key_t sk_gnb; }; @@ -82,55 +119,140 @@ struct as_security_config_t { CIPHERING_ALGORITHM_ID_ENUM cipher_algo; }; +struct nr_as_security_config_t { + as_key_t k_nr_rrc_int; + as_key_t k_nr_rrc_enc; + as_key_t k_nr_up_int; + as_key_t k_nr_up_enc; + INTEGRITY_ALGORITHM_ID_NR_ENUM integ_algo; + CIPHERING_ALGORITHM_ID_NR_ENUM cipher_algo; +}; + +template +void log_error(const char* format, Args&&... args) +{ + srslog::fetch_basic_logger("SEC").error(format, std::forward(args)...); +} + +template +void log_warning(const char* format, Args&&... args) +{ + srslog::fetch_basic_logger("SEC").warning(format, std::forward(args)...); +} + +template +void log_info(const char* format, Args&&... args) +{ + srslog::fetch_basic_logger("SEC").info(format, std::forward(args)...); +} + +template +void log_debug(const char* format, Args&&... args) +{ + srslog::fetch_basic_logger("SEC").debug(format, std::forward(args)...); +} + /****************************************************************************** * Key Generation *****************************************************************************/ -uint8_t security_generate_k_asme(uint8_t* ck, - uint8_t* ik, - uint8_t* ak, - uint8_t* sqn, - uint16_t mcc, - uint16_t mnc, - uint8_t* k_asme); -uint8_t security_generate_k_enb(uint8_t* k_asme, uint32_t nas_count, uint8_t* k_enb); +int kdf_common(const uint8_t fc, const std::array& key, const std::vector& P, uint8_t* output); +int kdf_common(const uint8_t fc, + const std::array& key, + const std::vector& P0, + const std::vector& P1, + uint8_t* output); +int kdf_common(const uint8_t fc, + const std::array& key, + const std::vector& P0, + const std::vector& P1, + const std::vector& P3, + uint8_t* output); -uint8_t security_generate_k_enb_star(uint8_t* k_enb, uint32_t pci, uint32_t earfcn, uint8_t* k_enb_star); +uint8_t security_generate_k_asme(const uint8_t* ck, + const uint8_t* ik, + const uint8_t* ak_xor_sqn, + const uint16_t mcc, + const uint16_t mnc, + uint8_t* k_asme); -uint8_t security_generate_nh(uint8_t* k_asme, uint8_t* sync, uint8_t* nh); +uint8_t security_generate_k_ausf(const uint8_t* ck, + const uint8_t* ik, + const uint8_t* ak_xor_sqn, + const char* serving_network_name, + uint8_t* k_ausf); -uint8_t security_generate_k_nas(uint8_t* k_asme, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_nas_enc, - uint8_t* k_nas_int); +uint8_t security_generate_k_amf(const uint8_t* k_seaf, + const char* supi_, + const uint8_t* abba_, + const uint32_t abba_len, + uint8_t* k_amf); -uint8_t security_generate_k_rrc(uint8_t* k_enb, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_rrc_enc, - uint8_t* k_rrc_int); +uint8_t security_generate_k_seaf(const uint8_t* k_ausf, const char* serving_network_name, uint8_t* k_seaf); -uint8_t security_generate_k_up(uint8_t* k_enb, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_up_enc, - uint8_t* k_up_int); +uint8_t security_generate_k_gnb(const as_key_t& k_amf, const uint32_t nas_count, as_key_t& k_gnb); -uint8_t security_generate_k_nr_rrc(uint8_t* k_gnb, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_rrc_enc, - uint8_t* k_rrc_int); +uint8_t security_generate_k_enb(const uint8_t* k_asme, const uint32_t nas_count, uint8_t* k_enb); -uint8_t security_generate_k_nr_up(uint8_t* k_gnb, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_up_enc, - uint8_t* k_up_int); +uint8_t security_generate_k_nb_star_common(uint8_t fc, + const uint8_t* k_enb, + const uint32_t pci_, + const uint32_t earfcn_, + uint8_t* k_enb_star); -uint8_t security_generate_sk_gnb(uint8_t* k_enb, uint8_t* sk_gnb, uint16_t scg_count); +uint8_t +security_generate_k_enb_star(const uint8_t* k_enb, const uint32_t pci, const uint32_t earfcn, uint8_t* k_enb_star); +uint8_t +security_generate_k_gnb_star(const uint8_t* k_gnb, const uint32_t pci_, const uint32_t dl_arfcn_, uint8_t* k_gnb_star); + +uint8_t security_generate_nh(const uint8_t* k_asme, const uint8_t* sync, uint8_t* nh); + +uint8_t security_generate_k_nas(const uint8_t* k_asme, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_nas_enc, + uint8_t* k_nas_int); + +uint8_t security_generate_k_nas_5g(const uint8_t* k_amf, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_nas_enc, + uint8_t* k_nas_int); + +uint8_t security_generate_k_rrc(const uint8_t* k_enb, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_rrc_enc, + uint8_t* k_rrc_int); + +uint8_t security_generate_k_up(const uint8_t* k_enb, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_up_enc, + uint8_t* k_up_int); + +uint8_t security_generate_k_nr_rrc(const uint8_t* k_gnb, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_rrc_enc, + uint8_t* k_rrc_int); + +uint8_t security_generate_k_nr_up(const uint8_t* k_gnb, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_up_enc, + uint8_t* k_up_int); + +uint8_t security_generate_sk_gnb(const uint8_t* k_enb, const uint16_t scg_count, uint8_t* sk_gnb); + +uint8_t security_generate_res_star(const uint8_t* ck, + const uint8_t* ik, + const char* serving_network_name, + const uint8_t* rand, + const uint8_t* res, + const size_t res_len, + uint8_t* res_star); /****************************************************************************** * Integrity Protection *****************************************************************************/ @@ -201,5 +323,8 @@ security_milenage_f2345(uint8_t* k, uint8_t* op, uint8_t* rand, uint8_t* res, ui uint8_t security_milenage_f5_star(uint8_t* k, uint8_t* op, uint8_t* rand, uint8_t* ak); +int security_xor_f2345(uint8_t* k, uint8_t* rand, uint8_t* res, uint8_t* ck, uint8_t* ik, uint8_t* ak); +int security_xor_f1(uint8_t* k, uint8_t* rand, uint8_t* sqn, uint8_t* amf, uint8_t* mac_a); + } // namespace srsran #endif // SRSRAN_SECURITY_H diff --git a/lib/include/srsran/common/slot_point.h b/lib/include/srsran/common/slot_point.h new file mode 100644 index 000000000..21c35b536 --- /dev/null +++ b/lib/include/srsran/common/slot_point.h @@ -0,0 +1,181 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SLOT_POINT_H +#define SRSRAN_SLOT_POINT_H + +#include "srsran/adt/interval.h" +#include "srsran/support/srsran_assert.h" + +namespace srsran { + +class slot_point +{ + uint32_t numerology_ : 3; + uint32_t count_ : 29; + + const static uint8_t NOF_NUMEROLOGIES = 5; + const static uint16_t NOF_SFNS = 1024; + const static uint8_t NOF_SUBFRAMES_PER_FRAME = 10; + + uint32_t nof_slots_per_hf() const { return nof_slots_per_frame() * NOF_SFNS; } + +public: + slot_point() : numerology_(NOF_NUMEROLOGIES), count_(0) {} + slot_point(uint8_t numerology, uint32_t count) : numerology_(numerology), count_(count) + { + srsran_assert(numerology < NOF_NUMEROLOGIES, "Invalid numerology idx=%d passed", (int)numerology); + srsran_assert(count < nof_slots_per_hf(), "Invalid slot count=%d passed", (int)count); + } + slot_point(uint8_t numerology, uint16_t sfn_val, uint8_t slot) : + numerology_(numerology), count_(slot + sfn_val * nof_slots_per_frame()) + { + srsran_assert(numerology < NOF_NUMEROLOGIES, "Invalid numerology idx=%d passed", (int)numerology); + srsran_assert(sfn_val < NOF_SFNS, "Invalid SFN=%d provided", (int)sfn_val); + srsran_assert(slot < nof_slots_per_frame(), + "Slot index=%d exceeds maximum number of slots=%d", + (int)slot, + (int)nof_slots_per_frame()); + } + + bool valid() const { return numerology_ < NOF_NUMEROLOGIES; } + uint8_t nof_slots_per_subframe() const { return 1U << numerology_; } + uint8_t nof_slots_per_frame() const { return nof_slots_per_subframe() * NOF_SUBFRAMES_PER_FRAME; } + + uint16_t sfn() const { return count_ / nof_slots_per_frame(); } + uint16_t subframe_idx() const { return slot_idx() / nof_slots_per_subframe(); } + uint8_t slot_idx() const { return count_ % nof_slots_per_frame(); } + uint8_t numerology_idx() const { return numerology_; } + uint32_t to_uint() const { return count_; } + explicit operator uint32_t() const { return count_; } + + void clear() { numerology_ = NOF_NUMEROLOGIES; } + + // operators + bool operator==(const slot_point& other) const { return other.count_ == count_ and other.numerology_ == numerology_; } + bool operator!=(const slot_point& other) const { return not(*this == other); } + bool operator<(const slot_point& other) const + { + srsran_assert(numerology_idx() == other.numerology_idx(), "Comparing slots of different numerologies"); + int a = static_cast(other.count_) - static_cast(count_); + if (a > 0) { + return (a < (int)nof_slots_per_hf() / 2); + } + return (a < -(int)nof_slots_per_hf() / 2); + } + bool operator<=(const slot_point& other) const { return (*this == other) or (*this < other); } + bool operator>=(const slot_point& other) const { return not(*this < other); } + bool operator>(const slot_point& other) const { return (*this != other) and *this >= other; } + + int32_t operator-(const slot_point& other) const + { + int a = static_cast(count_) - static_cast(other.count_); + if (a >= (int)nof_slots_per_hf() / 2) { + return a - nof_slots_per_hf(); + } + if (a < -(int)nof_slots_per_hf() / 2) { + return a + nof_slots_per_hf(); + } + return a; + } + slot_point& operator++() + { + count_++; + if (count_ == nof_slots_per_hf()) { + count_ = 0; + } + return *this; + } + slot_point operator++(int) + { + slot_point ret{*this}; + this-> operator++(); + return ret; + } + slot_point& operator+=(uint32_t jump) + { + count_ = (count_ + jump) % nof_slots_per_hf(); + return *this; + } + slot_point& operator-=(uint32_t jump) + { + int a = (static_cast(count_) - static_cast(jump)) % static_cast(nof_slots_per_hf()); + if (a < 0) { + a += nof_slots_per_hf(); + } + count_ = a; + return *this; + } + + bool is_in_interval(slot_point begin, slot_point end) const { return (*this >= begin and *this < end); } +}; +inline slot_point operator+(slot_point slot, uint32_t jump) +{ + slot += jump; + return slot; +} +inline slot_point operator+(uint32_t jump, slot_point slot) +{ + slot += jump; + return slot; +} +inline slot_point operator-(slot_point slot, uint32_t jump) +{ + slot -= jump; + return slot; +} +inline slot_point max(slot_point s1, slot_point s2) +{ + return s1 > s2 ? s1 : s2; +} +inline slot_point min(slot_point s1, slot_point s2) +{ + return s1 < s2 ? s1 : s2; +} + +using slot_interval = srsran::interval; + +} // namespace srsran + +namespace fmt { +template <> +struct formatter { + template + auto parse(ParseContext& ctx) -> decltype(ctx.begin()) + { + return ctx.begin(); + } + template + auto format(srsran::slot_point slot, FormatContext& ctx) -> decltype(std::declval().out()) + { + return format_to(ctx.out(), "{}", slot.to_uint()); + } +}; +} // namespace fmt + +namespace srsenb { + +using slot_point = srsran::slot_point; +using slot_interval = srsran::slot_interval; + +} // namespace srsenb + +#endif // SRSRAN_SLOT_POINT_H diff --git a/lib/include/srsran/common/ssl.h b/lib/include/srsran/common/ssl.h index daa558559..db91f9cc1 100644 --- a/lib/include/srsran/common/ssl.h +++ b/lib/include/srsran/common/ssl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,25 +22,6 @@ #ifndef SRSRAN_SSL_H #define SRSRAN_SSL_H -#ifdef HAVE_POLARSSL - -#include "polarssl/aes.h" -#include "polarssl/sha256.h" - -void sha256(const unsigned char* key, - size_t keylen, - const unsigned char* input, - size_t ilen, - unsigned char output[32], - int is224) -{ - sha256_hmac(key, keylen, input, ilen, output, is224); -} - -#endif // HAVE_POLARSSL - -#ifdef HAVE_MBEDTLS - #include "mbedtls/aes.h" #include "mbedtls/md.h" @@ -49,37 +30,35 @@ typedef mbedtls_aes_context aes_context; #define AES_ENCRYPT 1 #define AES_DECRYPT 0 -int aes_setkey_enc(aes_context* ctx, const unsigned char* key, unsigned int keysize) +inline int aes_setkey_enc(aes_context* ctx, const unsigned char* key, unsigned int keysize) { return mbedtls_aes_setkey_enc(ctx, key, keysize); } -int aes_crypt_ecb(aes_context* ctx, int mode, const unsigned char input[16], unsigned char output[16]) +inline int aes_crypt_ecb(aes_context* ctx, int mode, const unsigned char input[16], unsigned char output[16]) { return mbedtls_aes_crypt_ecb(ctx, mode, input, output); } -int aes_crypt_ctr(aes_context* ctx, - size_t length, - size_t* nc_off, - unsigned char nonce_counter[16], - unsigned char stream_block[16], - const unsigned char* input, - unsigned char* output) +inline int aes_crypt_ctr(aes_context* ctx, + size_t length, + size_t* nc_off, + unsigned char nonce_counter[16], + unsigned char stream_block[16], + const unsigned char* input, + unsigned char* output) { return mbedtls_aes_crypt_ctr(ctx, length, nc_off, nonce_counter, stream_block, input, output); } -void sha256(const unsigned char* key, - size_t keylen, - const unsigned char* input, - size_t ilen, - unsigned char output[32], - int is224) +inline void sha256(const unsigned char* key, + size_t keylen, + const unsigned char* input, + size_t ilen, + unsigned char output[32], + int is224) { mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), key, keylen, input, ilen, output); } -#endif // HAVE_MBEDTLS - #endif // SRSRAN_SSL_H diff --git a/lib/include/srsran/common/stack_procedure.h b/lib/include/srsran/common/stack_procedure.h index 362edb459..6b1e11960 100644 --- a/lib/include/srsran/common/stack_procedure.h +++ b/lib/include/srsran/common/stack_procedure.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/standard_streams.h b/lib/include/srsran/common/standard_streams.h index afd8773c9..5c428eb2e 100644 --- a/lib/include/srsran/common/standard_streams.h +++ b/lib/include/srsran/common/standard_streams.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/string_helpers.h b/lib/include/srsran/common/string_helpers.h index 996583cbe..38586dc96 100644 --- a/lib/include/srsran/common/string_helpers.h +++ b/lib/include/srsran/common/string_helpers.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,6 +24,8 @@ #include "srsran/srslog/bundled/fmt/format.h" #include +#include +#include #include #include #include @@ -128,6 +130,49 @@ const char* to_c_str(fmt::basic_memory_buffer& mem_buffer) return mem_buffer.data(); } +static inline bool replace(std::string& str, const std::string& from, const std::string& to) +{ + size_t start_pos = str.find(from); + if (start_pos == std::string::npos) + return false; + str.replace(start_pos, from.length(), to); + return true; +} + +static inline std::vector split_string(const std::string& str, char delimiter) +{ + std::vector tokens; + std::string token; + std::istringstream tokenStream(str); + + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + return tokens; +} + +static inline void get_uint_vec_from_hex_str(const std::string& key_str, uint8_t* key, uint len) +{ + const char* pos = key_str.c_str(); + + for (uint count = 0; count < len; count++) { + sscanf(pos, "%2hhx", &key[count]); + pos += 2; + } + return; +} + +static inline std::string hex_string(uint8_t* hex, int size) +{ + std::stringstream ss; + + ss << std::hex << std::setfill('0'); + for (int i = 0; i < size; i++) { + ss << std::setw(2) << static_cast(hex[i]); + } + return ss.str(); +} + } // namespace srsran #endif // SRSRAN_STRING_HELPERS_H diff --git a/lib/include/srsran/common/task_scheduler.h b/lib/include/srsran/common/task_scheduler.h index 936098deb..5c8a0a532 100644 --- a/lib/include/srsran/common/task_scheduler.h +++ b/lib/include/srsran/common/task_scheduler.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #ifndef SRSRAN_TASK_SCHEDULER_H #define SRSRAN_TASK_SCHEDULER_H +#include "block_queue.h" #include "interfaces_common.h" #include "multiqueue.h" #include "thread_pool.h" @@ -33,22 +34,22 @@ class task_scheduler { public: explicit task_scheduler(uint32_t default_extern_tasks_size = 512, uint32_t nof_timers_prealloc = 100) : - external_tasks{default_extern_tasks_size}, timers{nof_timers_prealloc} + external_tasks{default_extern_tasks_size}, timers{nof_timers_prealloc}, internal_tasks(512) { - background_queue_id = external_tasks.add_queue(); + background_queue = external_tasks.add_queue(); } task_scheduler(const task_scheduler&) = delete; task_scheduler(task_scheduler&&) = delete; task_scheduler& operator=(const task_scheduler&) = delete; task_scheduler& operator=(task_scheduler&&) = delete; - void stop() { external_tasks.reset(); } + void stop() { external_tasks.stop(); } srsran::unique_timer get_unique_timer() { return timers.get_unique_timer(); } //! Creates new queue for tasks coming from external thread - srsran::task_queue_handle make_task_queue() { return external_tasks.get_queue_handler(); } - srsran::task_queue_handle make_task_queue(uint32_t qsize) { return external_tasks.get_queue_handler(qsize); } + srsran::task_queue_handle make_task_queue() { return external_tasks.add_queue(); } + srsran::task_queue_handle make_task_queue(uint32_t qsize) { return external_tasks.add_queue(qsize); } //! Delays a task processing by duration_ms template @@ -58,13 +59,18 @@ public: } //! Enqueues internal task to be run in next tic - void defer_task(srsran::move_task_t func) { internal_tasks.push_back(std::move(func)); } + void defer_task(srsran::move_task_t func) + { + if (not internal_tasks.try_push(std::move(func))) { + srslog::fetch_basic_logger("COMN", false).warning("Couldn't add new internal task"); + } + } //! Defer the handling of the result of a background task to next tic void notify_background_task_result(srsran::move_task_t task) { // run the notification in next tic - external_tasks.push(background_queue_id, std::move(task)); + background_queue.push(std::move(task)); } //! Updates timers, and run any pending internal tasks. @@ -76,7 +82,7 @@ public: bool run_next_task() { srsran::move_task_t task{}; - if (external_tasks.wait_pop(&task) >= 0) { + if (external_tasks.wait_pop(&task)) { task(); run_all_internal_tasks(); return true; @@ -90,7 +96,7 @@ public: { run_all_internal_tasks(); srsran::move_task_t task{}; - while (external_tasks.try_pop(&task) >= 0) { + while (external_tasks.try_pop(&task)) { task(); run_all_internal_tasks(); } @@ -99,21 +105,20 @@ public: srsran::timer_handler* get_timer_handler() { return &timers; } private: + // Perform pending stack deferred tasks void run_all_internal_tasks() { - // Perform pending stack deferred tasks - // Note: Using a deque because tasks can enqueue new tasks, which would lead to - // reference invalidation in case of vector - while (not internal_tasks.empty()) { - internal_tasks.front()(); - internal_tasks.pop_front(); + srsran::move_task_t task{}; + while (internal_tasks.try_pop(task)) { + task(); } } - int background_queue_id = -1; ///< Queue for handling the outcomes of tasks run in the background - srsran::task_multiqueue external_tasks; - srsran::timer_handler timers; - std::deque internal_tasks; ///< enqueues stack tasks from within main thread. Avoids locking + srsran::task_multiqueue external_tasks; + srsran::task_queue_handle background_queue; ///< Queue for handling the outcomes of tasks run in the background + srsran::timer_handler timers; + srsran::dyn_blocking_queue + internal_tasks; ///< enqueues stack tasks from within main thread. Avoids locking }; //! Task scheduler handle given to classes/functions running within the main control thread diff --git a/lib/include/srsran/common/test_common.h b/lib/include/srsran/common/test_common.h index 9c688f017..c5486fb3c 100644 --- a/lib/include/srsran/common/test_common.h +++ b/lib/include/srsran/common/test_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,11 +23,12 @@ #define SRSRAN_TEST_COMMON_H #include "srsran/config.h" +#include "srsran/support/srsran_test.h" #ifdef __cplusplus +#include "srsran/common/buffer_pool.h" #include "srsran/common/crash_handler.h" -#include "srsran/common/srsran_assert.h" #include "srsran/common/standard_streams.h" #include "srsran/srslog/srslog.h" #include @@ -145,31 +146,62 @@ inline void test_init(int argc, char** argv) srsran_debug_handle_crash(argc, argv); srslog::fetch_basic_logger("ALL").set_level(srslog::basic_levels::info); + srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::info); // Start the log backend. srslog::init(); } +inline void copy_msg_to_buffer(unique_byte_buffer_t& pdu, const_byte_span msg) +{ + pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + srslog::fetch_basic_logger("ALL").error("Couldn't allocate PDU in %s().", __FUNCTION__); + return; + } + memcpy(pdu->msg, msg.data(), msg.size()); + pdu->N_bytes = msg.size(); +} + +/** + * Delimits beginning/ending of a test with the following console output: + * ============= [Test ] =============== + * + * ======================================================= + */ +class test_delimit_logger +{ + const size_t delimiter_length = 128; + +public: + template + explicit test_delimit_logger(const char* test_name_fmt, Args&&... args) + { + test_name = fmt::format(test_name_fmt, std::forward(args)...); + std::string name_str = fmt::format("[ Test \"{}\" ]", test_name); + double nof_repeats = (delimiter_length - name_str.size()) / 2.0; + fmt::print("{0:=>{1}}{2}{0:=>{3}}\n", "", (int)floor(nof_repeats), name_str, (int)ceil(nof_repeats)); + } + test_delimit_logger(const test_delimit_logger&) = delete; + test_delimit_logger(test_delimit_logger&&) = delete; + test_delimit_logger& operator=(const test_delimit_logger&) = delete; + test_delimit_logger& operator=(test_delimit_logger&&) = delete; + ~test_delimit_logger() + { + srslog::flush(); + fmt::print("{:=>{}}\n", "", delimiter_length); + } + +private: + std::string test_name; +}; + } // namespace srsran #define CONDERROR(cond, fmt, ...) srsran_assert(not(cond), fmt, ##__VA_ARGS__) #define TESTERROR(fmt, ...) CONDERROR(true, fmt, ##__VA_ARGS__) -#define TESTASSERT(cond) srsran_assert((cond), "Fail at \"%s\"", (#cond)) - -#else // if C - -#include - -#define TESTASSERT(cond) \ - do { \ - if (!(cond)) { \ - printf("[%s][Line %d] Fail at \"%s\"\n", __FUNCTION__, __LINE__, (#cond)); \ - return -1; \ - } \ - } while (0) - #endif // __cplusplus #endif // SRSRAN_TEST_COMMON_H diff --git a/lib/include/srsran/common/test_pcap.h b/lib/include/srsran/common/test_pcap.h new file mode 100644 index 000000000..caf14cd00 --- /dev/null +++ b/lib/include/srsran/common/test_pcap.h @@ -0,0 +1,98 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_TEST_PCAP_H +#define SRSRAN_TEST_PCAP_H + +/** + * @brief Helper class for tests that wish to dump RRC, PDCP or MAC SDUs into PCAP files in order to inspect them with + * Wireshark. + * + * Depending on the layer of interest, the class adds the protocol header for the layers below so that Wireshark can + * disect them. For RRC for example, both PDCP and RLC AM dummy headers are added. + * + * There is an EUTRA and NR version for the helper methods. + * + */ + +#include "srsran/common/mac_pcap.h" +#include "srsran/mac/mac_sch_pdu_nr.h" +static std::unique_ptr pcap_handle = nullptr; +#define PCAP_CRNTI (0x1001) +#define PCAP_TTI (666) + +namespace srsran { + +/** + * @brief Writes a MAC SDU of a gives LCID for NR + * + * @param lcid The logical channel ID of the SDU + * @param payload Pointer to payload + * @param len Length + * @return int + */ +int write_mac_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len) +{ + if (pcap_handle) { + byte_buffer_t tx_buffer; + srsran::mac_sch_pdu_nr tx_pdu; + tx_pdu.init_tx(&tx_buffer, len + 10); + tx_pdu.add_sdu(lcid, payload, len); + tx_pdu.pack(); + pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, 0, PCAP_TTI); + return SRSRAN_SUCCESS; + } + return SRSRAN_ERROR; +} + +/** + * @brief Writes a PDCP SDU (e.g. RRC DL-DCCH PDU) + * + * Both PDCP and RLC AM header (dummy for SN=0) are added. + * + * @param lcid The logical channel ID of the SDU + * @param payload Pointer to payload + * @param len Length + * @return int + */ +int write_pdcp_sdu_nr(const uint32_t lcid, const uint8_t* payload, const uint32_t len) +{ + if (pcap_handle) { + byte_buffer_t mac_sdu; + // Add dummy RLC AM PDU header + mac_sdu.msg[0] = 0x80; + mac_sdu.msg[1] = 0x00; + + // Add dummy PDCP header + mac_sdu.msg[2] = 0x00; + mac_sdu.msg[3] = 0x00; + + mac_sdu.N_bytes = 4; + memcpy(mac_sdu.msg + 4, payload, len); + mac_sdu.N_bytes += len; + return write_mac_sdu_nr(lcid, mac_sdu.msg, mac_sdu.N_bytes); + } + return SRSRAN_ERROR; +} + +} // namespace srsran + +#endif // SRSRAN_TEST_COMMON_H diff --git a/lib/include/srsran/common/thread_pool.h b/lib/include/srsran/common/thread_pool.h index 7eb131623..db0cc3b75 100644 --- a/lib/include/srsran/common/thread_pool.h +++ b/lib/include/srsran/common/thread_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,6 +32,7 @@ #include "srsran/adt/circular_buffer.h" #include "srsran/adt/move_callback.h" #include "srsran/srslog/srslog.h" +#include #include #include #include @@ -54,6 +55,7 @@ public: worker(); ~worker() = default; void setup(uint32_t id, thread_pool* parent, uint32_t prio = 0, uint32_t mask = 255); + void stop(); uint32_t get_id(); void release(); @@ -61,8 +63,9 @@ public: virtual void work_imp() = 0; private: - uint32_t my_id = 0; - thread_pool* my_parent = nullptr; + uint32_t my_id = 0; + thread_pool* my_parent = nullptr; + std::atomic running = {true}; void run_thread(); void wait_to_start(); @@ -70,22 +73,24 @@ public: bool is_stopped() const; }; - thread_pool(uint32_t nof_workers); - void init_worker(uint32_t id, worker*, uint32_t prio = 0, uint32_t mask = 255); - void stop(); - worker* wait_worker_id(uint32_t id); - worker* wait_worker(uint32_t tti); - worker* wait_worker_nb(uint32_t tti); - void start_worker(worker*); - void start_worker(uint32_t id); - worker* get_worker(uint32_t id); - uint32_t get_nof_workers(); + thread_pool(uint32_t nof_workers_, std::string id_ = ""); + void init_worker(uint32_t id, worker*, uint32_t prio = 0, uint32_t mask = 255); + void stop(); + worker* wait_worker_id(uint32_t id); + worker* wait_worker(uint32_t tti); + worker* wait_worker_nb(uint32_t tti); + void start_worker(worker*); + void start_worker(uint32_t id); + worker* get_worker(uint32_t id); + uint32_t get_nof_workers(); + std::string get_id(); private: bool find_finished_worker(uint32_t tti, uint32_t* id); typedef enum { STOP, IDLE, START_WORK, WORKER_READY, WORKING } worker_status; + std::string id; // id is prepended to every worker std::vector workers = {}; uint32_t nof_workers = 0; uint32_t max_workers = 0; @@ -148,6 +153,40 @@ private: bool running = false; }; +/// Class used to create a single worker with an input task queue with a single reader +class task_worker : public thread +{ + using task_t = srsran::move_callback; + +public: + task_worker(std::string thread_name_, + uint32_t queue_size, + bool start_deferred = false, + int32_t prio_ = -1, + uint32_t mask_ = 255); + task_worker(const task_worker&) = delete; + task_worker(task_worker&&) = delete; + task_worker& operator=(const task_worker&) = delete; + task_worker& operator=(task_worker&&) = delete; + ~task_worker(); + + void stop(); + void start(int32_t prio_ = -1, uint32_t mask_ = 255); + + void push_task(task_t&& task); + uint32_t nof_pending_tasks() const; + +private: + void run_thread() override; + + // args + int32_t prio = -1; + uint32_t mask = 255; + srslog::basic_logger& logger; + + srsran::dyn_blocking_queue pending_tasks; +}; + srsran::task_thread_pool& get_background_workers(); } // namespace srsran diff --git a/lib/include/srsran/common/threads.h b/lib/include/srsran/common/threads.h index c2f644b46..0baa5bd2a 100644 --- a/lib/include/srsran/common/threads.h +++ b/lib/include/srsran/common/threads.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -44,6 +44,7 @@ void threads_print_self(); #ifdef __cplusplus } +#include #include namespace srsran { @@ -138,10 +139,10 @@ protected: virtual void run_period() = 0; private: - int wakeups_missed; - int timer_fd; - int period_us; - bool run_enable; + int wakeups_missed = 0; + int timer_fd = 0; + int period_us = 0; + std::atomic run_enable = {false}; void run_thread() { diff --git a/lib/include/srsran/common/time_prof.h b/lib/include/srsran/common/time_prof.h index 404e3b586..153c30446 100644 --- a/lib/include/srsran/common/time_prof.h +++ b/lib/include/srsran/common/time_prof.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/timeout.h b/lib/include/srsran/common/timeout.h index b673b3ec3..c2e227905 100644 --- a/lib/include/srsran/common/timeout.h +++ b/lib/include/srsran/common/timeout.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/timers.h b/lib/include/srsran/common/timers.h index 600478164..dfb51a70c 100644 --- a/lib/include/srsran/common/timers.h +++ b/lib/include/srsran/common/timers.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -46,8 +47,9 @@ public: }; /** - * Class that manages stack timers. It allows creation of unique_timers, with different ids. Each unique_timer duration, + * Class that manages stack timers. It allows creation of unique_timers with different ids. Each unique_timer duration, * and callback can be set via the set(...) method. A timer can be started/stopped via run()/stop() methods. + * The timers access/alteration is thread-safe. Just beware non-atomic uses of its getters. * Internal Data structures: * - timer_list - std::deque that stores timer objects via push_back() to keep pointer/reference validity. * The timer index in the timer_list matches the timer object id field. @@ -62,20 +64,34 @@ public: */ class timer_handler { - using tic_diff_t = uint32_t; - using tic_t = uint32_t; - constexpr static uint32_t INVALID_ID = std::numeric_limits::max(); - constexpr static tic_diff_t INVALID_TIME_DIFF = std::numeric_limits::max(); - constexpr static size_t WHEEL_SHIFT = 16U; - constexpr static size_t WHEEL_SIZE = 1U << WHEEL_SHIFT; - constexpr static size_t WHEEL_MASK = WHEEL_SIZE - 1U; + using tic_diff_t = uint32_t; + using tic_t = uint32_t; + constexpr static uint32_t INVALID_ID = std::numeric_limits::max(); + constexpr static size_t WHEEL_SHIFT = 16U; + constexpr static size_t WHEEL_SIZE = 1U << WHEEL_SHIFT; + constexpr static size_t WHEEL_MASK = WHEEL_SIZE - 1U; + + constexpr static uint64_t STOPPED_FLAG = 0U; + constexpr static uint64_t RUNNING_FLAG = static_cast(1U) << 63U; + constexpr static uint64_t EXPIRED_FLAG = static_cast(1U) << 62U; + constexpr static tic_diff_t MAX_TIMER_DURATION = 0x3FFFFFFFU; + + static bool decode_is_running(uint64_t value) { return (value & RUNNING_FLAG) != 0; } + static bool decode_is_expired(uint64_t value) { return (value & EXPIRED_FLAG) != 0; } + static tic_diff_t decode_duration(uint64_t value) { return (value >> 32U) & MAX_TIMER_DURATION; } + static tic_t decode_timeout(uint64_t value) { return static_cast(value & 0xFFFFFFFFU); } + static uint64_t encode_state(uint64_t mode_flag, uint32_t duration, uint32_t timeout) + { + return mode_flag + (static_cast(duration) << 32U) + timeout; + } struct timer_impl : public intrusive_double_linked_list_element<>, public intrusive_forward_list_element<> { - timer_handler& parent; + // const const uint32_t id; - tic_diff_t duration = INVALID_TIME_DIFF; - tic_t timeout = 0; - enum state_t : int8_t { empty, stopped, running, expired } state = empty; + timer_handler& parent; + // writes protected by backend lock + bool allocated = false; + std::atomic state{0}; ///< read can be without lock, thus writes must be atomic srsran::move_callback callback; explicit timer_impl(timer_handler& parent_, uint32_t id_) : parent(parent_), id(id_) {} @@ -84,32 +100,38 @@ class timer_handler timer_impl& operator=(const timer_impl&) = delete; timer_impl& operator=(timer_impl&&) = delete; - bool is_empty() const { return state == empty; } - bool is_running() const { return state == running; } - bool is_expired() const { return state == expired; } - tic_diff_t time_left() const { return is_running() ? timeout - parent.cur_time : (is_expired() ? 0 : duration); } - uint32_t time_elapsed() const { return duration - time_left(); } - - bool set(uint32_t duration_) + // unprotected + bool is_running_() const { return decode_is_running(state.load(std::memory_order_relaxed)); } + bool is_expired_() const { return decode_is_expired(state.load(std::memory_order_relaxed)); } + uint32_t duration_() const { return decode_duration(state.load(std::memory_order_relaxed)); } + bool is_set_() const { return duration_() > 0; } + tic_diff_t time_elapsed_() const { - duration = std::max(duration_, 1U); // the next step will be one place ahead of current one - if (is_running()) { - // if already running, just extends timer lifetime - run(); - } else { - state = stopped; - timeout = 0; - } - return true; + uint64_t state_snapshot = state.load(std::memory_order_relaxed); + bool running = decode_is_running(state_snapshot), expired = decode_is_expired(state_snapshot); + uint32_t duration = decode_duration(state_snapshot), timeout = decode_timeout(state_snapshot); + return running ? duration - (timeout - parent.cur_time) : (expired ? duration : 0); } - bool set(uint32_t duration_, srsran::move_callback callback_) + void set(uint32_t duration_) { - if (set(duration_)) { - callback = std::move(callback_); - return true; - } - return false; + srsran_assert(duration_ <= MAX_TIMER_DURATION, + "Invalid timer duration=%" PRIu32 ">%" PRIu32, + duration_, + MAX_TIMER_DURATION); + std::lock_guard lock(parent.mutex); + set_(duration_); + } + + void set(uint32_t duration_, srsran::move_callback callback_) + { + srsran_assert(duration_ <= MAX_TIMER_DURATION, + "Invalid timer duration=%" PRIu32 ">%" PRIu32, + duration_, + MAX_TIMER_DURATION); + std::lock_guard lock(parent.mutex); + set_(duration_); + callback = std::move(callback_); } void run() @@ -125,7 +147,25 @@ class timer_handler parent.stop_timer_(*this, false); } - void deallocate() { parent.dealloc_timer(*this); } + void deallocate() + { + std::lock_guard lock(parent.mutex); + parent.dealloc_timer_(*this); + } + + private: + void set_(uint32_t duration_) + { + duration_ = std::max(duration_, 1U); // the next step will be one place ahead of current one + // called in locked context + uint64_t old_state = state.load(std::memory_order_relaxed); + if (decode_is_running(old_state)) { + // if already running, just extends timer lifetime + parent.start_run_(*this, duration_); + } else { + state.store(encode_state(STOPPED_FLAG, duration_, 0), std::memory_order_relaxed); + } + } }; public: @@ -160,17 +200,12 @@ public: handle->set(duration_); } - bool is_set() const { return is_valid() and handle->duration != INVALID_TIME_DIFF; } - - bool is_running() const { return is_valid() and handle->is_running(); } - - bool is_expired() const { return is_valid() and handle->is_expired(); } - - tic_diff_t time_elapsed() const { return is_valid() ? handle->time_elapsed() : INVALID_TIME_DIFF; } - - uint32_t id() const { return is_valid() ? handle->id : INVALID_ID; } - - tic_diff_t duration() const { return is_valid() ? handle->duration : INVALID_TIME_DIFF; } + uint32_t id() const { return is_valid() ? handle->id : INVALID_ID; } + bool is_set() const { return is_valid() and handle->is_set_(); } + bool is_running() const { return is_valid() and handle->is_running_(); } + bool is_expired() const { return is_valid() and handle->is_expired_(); } + tic_diff_t time_elapsed() const { return is_valid() ? handle->time_elapsed_() : 0; } + tic_diff_t duration() const { return is_valid() ? handle->duration_() : 0; } void run() { @@ -214,13 +249,13 @@ public: void step_all() { std::unique_lock lock(mutex); - cur_time++; - auto& wheel_list = time_wheel[cur_time & WHEEL_MASK]; + uint32_t cur_time_local = cur_time.load(std::memory_order_relaxed) + 1; + auto& wheel_list = time_wheel[cur_time_local & WHEEL_MASK]; for (auto it = wheel_list.begin(); it != wheel_list.end();) { timer_impl& timer = timer_list[it->id]; ++it; - if (timer.timeout == cur_time) { + if (decode_timeout(timer.state.load(std::memory_order_relaxed)) == cur_time_local) { // stop timer (callback has to see the timer has already expired) stop_timer_(timer, true); @@ -236,6 +271,8 @@ public: } } } + + cur_time.fetch_add(1, std::memory_order_relaxed); } void stop_all() @@ -261,6 +298,8 @@ public: return nof_timers_running_; } + constexpr static uint32_t max_timer_duration() { return MAX_TIMER_DURATION; } + template void defer_callback(uint32_t duration, const F& func) { @@ -284,7 +323,7 @@ private: timer_impl* t; if (not free_list.empty()) { t = &free_list.front(); - srsran_assert(t->is_empty(), "Invalid timer id=%d state", t->id); + srsran_assert(not t->allocated, "Invalid timer id=%d state", t->id); free_list.pop_front(); nof_free_timers--; } else { @@ -292,63 +331,71 @@ private: timer_list.emplace_back(*this, timer_list.size()); t = &timer_list.back(); } - t->state = timer_impl::stopped; + t->allocated = true; return *t; } - void dealloc_timer(timer_impl& timer) + void dealloc_timer_(timer_impl& timer) { - std::lock_guard lock(mutex); - if (timer.is_empty()) { + if (not timer.allocated) { // already deallocated return; } stop_timer_(timer, false); - timer.state = timer_impl::empty; - timer.duration = INVALID_TIME_DIFF; - timer.timeout = 0; + timer.allocated = false; + timer.state.store(encode_state(STOPPED_FLAG, 0, 0), std::memory_order_relaxed); timer.callback = srsran::move_callback(); free_list.push_front(&timer); nof_free_timers++; // leave id unchanged. } - void start_run_(timer_impl& timer) + void start_run_(timer_impl& timer, uint32_t duration_ = 0) { - uint32_t timeout = cur_time + timer.duration; - size_t new_wheel_pos = timeout & WHEEL_MASK; - if (timer.is_running() and (timer.timeout & WHEEL_MASK) == new_wheel_pos) { + uint64_t timer_old_state = timer.state.load(std::memory_order_relaxed); + duration_ = duration_ == 0 ? decode_duration(timer_old_state) : duration_; + uint32_t new_timeout = cur_time.load(std::memory_order_relaxed) + duration_; + size_t new_wheel_pos = new_timeout & WHEEL_MASK; + + uint32_t old_timeout = decode_timeout(timer_old_state); + bool was_running = decode_is_running(timer_old_state); + if (was_running and (old_timeout & WHEEL_MASK) == new_wheel_pos) { // If no change in timer wheel position. Just update absolute timeout - timer.timeout = timeout; + timer.state.store(encode_state(RUNNING_FLAG, duration_, new_timeout), std::memory_order_relaxed); return; } // Stop timer if it was running, removing it from wheel in the process - stop_timer_(timer, false); + if (was_running) { + time_wheel[old_timeout & WHEEL_MASK].pop(&timer); + nof_timers_running_--; + } // Insert timer in wheel time_wheel[new_wheel_pos].push_front(&timer); - timer.timeout = timeout; - timer.state = timer_impl::running; + timer.state.store(encode_state(RUNNING_FLAG, duration_, new_timeout), std::memory_order_relaxed); nof_timers_running_++; } /// called when user manually stops timer (as an alternative to expiry) void stop_timer_(timer_impl& timer, bool expiry) { - if (not timer.is_running()) { + uint64_t timer_old_state = timer.state.load(std::memory_order_relaxed); + if (not decode_is_running(timer_old_state)) { return; } // If already running, need to disconnect it from previous wheel - time_wheel[timer.timeout & WHEEL_MASK].pop(&timer); - - timer.state = expiry ? timer_impl::expired : timer_impl::stopped; + uint32_t old_timeout = decode_timeout(timer_old_state); + time_wheel[old_timeout & WHEEL_MASK].pop(&timer); + uint64_t new_state = + encode_state(expiry ? EXPIRED_FLAG : STOPPED_FLAG, decode_duration(timer_old_state), old_timeout); + timer.state.store(new_state, std::memory_order_relaxed); nof_timers_running_--; } - tic_t cur_time = 0; - size_t nof_timers_running_ = 0, nof_free_timers = 0; + std::atomic cur_time{0}; + size_t nof_timers_running_ = 0, nof_free_timers = 0; // using a deque to maintain reference validity on emplace_back. Also, this deque will only grow. std::deque timer_list; srsran::intrusive_forward_list free_list; diff --git a/lib/include/srsran/common/trace.h b/lib/include/srsran/common/trace.h index 0e8376239..3941a0ca8 100644 --- a/lib/include/srsran/common/trace.h +++ b/lib/include/srsran/common/trace.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/tsan_options.h b/lib/include/srsran/common/tsan_options.h new file mode 100644 index 000000000..86598261a --- /dev/null +++ b/lib/include/srsran/common/tsan_options.h @@ -0,0 +1,72 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_TSAN_OPTIONS_H +#define SRSRAN_TSAN_OPTIONS_H + +// Options taken from Mozilla project +// abort_on_error=1 - Causes TSan to abort instead of using exit(). +// halt_on_error=1 - Causes TSan to stop on the first race detected. +// +// report_signal_unsafe=0 - Required to avoid TSan deadlocks when +// receiving external signals (e.g. SIGINT manually on console). +// +// allocator_may_return_null=1 - Tell TSan to return NULL when an allocation +// fails instead of aborting the program. This allows us to handle failing +// allocations the same way we would handle them with a regular allocator and +// also uncovers potential bugs that might occur in these situations. + +#ifdef __cplusplus +extern "C" { +#endif + +const char* __tsan_default_options() +{ + return "halt_on_error=1:abort_on_error=1:report_signal_unsafe=0" + ":allocator_may_return_null=1"; +} + +const char* __tsan_default_suppressions() +{ + return + // External uninstrumented libraries + "called_from_lib:libzmq.so\n" + "called_from_lib:libpgm-5.2.so\n" + "called_from_lib:libusb*\n" + "called_from_lib:libuhd*\n" + // Races detected inside uninstrumented libraries. This may hide legit races if any of the libraries appear in the + // backtrace + "race:libusb*\n" + "race:libuhd*\n" + // Lock order inversion issues in these functions, ignore it as it uses rw locks in read mode + "deadlock:srsenb::mac::rlc_buffer_state\n" + "deadlock:srsenb::mac::snr_info\n" + "deadlock:srsenb::mac::ack_info\n" + "deadlock:srsenb::mac::cqi_info\n" + "deadlock:srsenb::rlc::rb_is_um\n" + "deadlock:srsenb::mac::sr_detected\n"; +} + +#ifdef __cplusplus +} +#endif + +#endif // SRSRAN_TSAN_OPTIONS_H diff --git a/lib/include/srsran/common/tti_point.h b/lib/include/srsran/common/tti_point.h index 50400ea34..7c333f0c5 100644 --- a/lib/include/srsran/common/tti_point.h +++ b/lib/include/srsran/common/tti_point.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,8 +24,8 @@ #include "srsran/adt/interval.h" #include "srsran/common/common.h" -#include "srsran/common/srsran_assert.h" #include "srsran/srslog/srslog.h" +#include "srsran/support/srsran_assert.h" #include #include @@ -139,21 +139,22 @@ struct formatter { namespace srsenb { -using tti_point = srsran::tti_point; +using tti_point = srsran::tti_point; +using tti_interval = srsran::tti_interval; -inline srsran::tti_point to_tx_dl(srsran::tti_point t) +inline tti_point to_tx_dl(tti_point t) { return t + TX_ENB_DELAY; } -inline srsran::tti_point to_tx_ul(srsran::tti_point t) +inline tti_point to_tx_ul(tti_point t) { return t + (TX_ENB_DELAY + FDD_HARQ_DELAY_DL_MS); } -inline srsran::tti_point to_tx_dl_ack(srsran::tti_point t) +inline tti_point to_tx_dl_ack(tti_point t) { return to_tx_ul(t); } -inline srsran::tti_point to_tx_ul_ack(srsran::tti_point t) +inline tti_point to_tx_ul_ack(tti_point t) { return to_tx_ul(t) + TX_ENB_DELAY; } diff --git a/lib/include/srsran/common/tti_sempahore.h b/lib/include/srsran/common/tti_sempahore.h index 97552faa2..b317ddf93 100644 --- a/lib/include/srsran/common/tti_sempahore.h +++ b/lib/include/srsran/common/tti_sempahore.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -46,6 +46,8 @@ private: std::deque fifo; ///< Queue to keep order public: + tti_semaphore() = default; + /** * Waits for the first element of the queue match the element identifier provided. * @@ -82,8 +84,10 @@ public: { std::unique_lock lock(mutex); - // Pop first element - fifo.pop_front(); + // If the FIFO is not empty pop first element + if (not fifo.empty()) { + fifo.pop_front(); + } // Notify release cvar.notify_all(); diff --git a/lib/include/srsran/common/tti_sync.h b/lib/include/srsran/common/tti_sync.h index f13c42b90..ac9d3f13d 100644 --- a/lib/include/srsran/common/tti_sync.h +++ b/lib/include/srsran/common/tti_sync.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/tti_sync_cv.h b/lib/include/srsran/common/tti_sync_cv.h index 1b2441c55..5353404c1 100644 --- a/lib/include/srsran/common/tti_sync_cv.h +++ b/lib/include/srsran/common/tti_sync_cv.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/common/zuc.h b/lib/include/srsran/common/zuc.h index 10be8102a..ad78fbe56 100644 --- a/lib/include/srsran/common/zuc.h +++ b/lib/include/srsran/common/zuc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/config.h b/lib/include/srsran/config.h index ffa0650e7..894b472e2 100644 --- a/lib/include/srsran/config.h +++ b/lib/include/srsran/config.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -62,6 +62,7 @@ #define SRSRAN_ERROR_OUT_OF_BOUNDS -5 #define SRSRAN_ERROR_CANT_START -6 #define SRSRAN_ERROR_ALREADY_STARTED -7 +#define SRSRAN_ERROR_RX_EOF -8 // cf_t definition typedef _Complex float cf_t; diff --git a/lib/include/srsran/interfaces/enb_command_interface.h b/lib/include/srsran/interfaces/enb_command_interface.h index bbc581baa..03d1e3beb 100644 --- a/lib/include/srsran/interfaces/enb_command_interface.h +++ b/lib/include/srsran/interfaces/enb_command_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,12 +28,19 @@ namespace srsenb { class enb_command_interface { public: + /** + * Trigger downlink singnal measurements (currently PAPR) + */ + virtual void cmd_cell_measure() = 0; + /** * Sets the relative gain of a cell from it's index (following rr.conf) order. * @param cell_id Provides a cell identifier * @param gain Relative gain */ virtual void cmd_cell_gain(uint32_t cell_id, float gain) = 0; + + virtual void toggle_padding() = 0; }; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/enb_gtpu_interfaces.h b/lib/include/srsran/interfaces/enb_gtpu_interfaces.h index dcbbeb329..a701feb3e 100644 --- a/lib/include/srsran/interfaces/enb_gtpu_interfaces.h +++ b/lib/include/srsran/interfaces/enb_gtpu_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,10 +27,20 @@ namespace srsenb { +struct gtpu_args_t { + std::string gtp_bind_addr; + std::string mme_addr; + std::string embms_m1u_multiaddr; + std::string embms_m1u_if_addr; + bool embms_enable = false; + uint32_t indirect_tunnel_timeout_msec = 0; +}; + // GTPU interface for PDCP class gtpu_interface_pdcp { public: + // PDCP will only know LCIDs, translation to EPS bearer id will be done by gtpu_pdcp_adapter virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; }; @@ -45,12 +55,16 @@ public: uint32_t flush_before_teidin = 0; }; - virtual srsran::expected - add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props = nullptr) = 0; - virtual void set_tunnel_status(uint32_t teidin, bool dl_active) = 0; - virtual void rem_bearer(uint16_t rnti, uint32_t lcid) = 0; - virtual void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) = 0; - virtual void rem_user(uint16_t rnti) = 0; + virtual srsran::expected add_bearer(uint16_t rnti, + uint32_t eps_bearer_id, + uint32_t addr, + uint32_t teid_out, + uint32_t& addr_in, + const bearer_props* props = nullptr) = 0; + virtual void set_tunnel_status(uint32_t teidin, bool dl_active) = 0; + virtual void rem_bearer(uint16_t rnti, uint32_t eps_bearer_id) = 0; + virtual void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) = 0; + virtual void rem_user(uint16_t rnti) = 0; }; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/enb_interfaces.h b/lib/include/srsran/interfaces/enb_interfaces.h index ab31aaf02..c617a2dbb 100644 --- a/lib/include/srsran/interfaces/enb_interfaces.h +++ b/lib/include/srsran/interfaces/enb_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,7 +28,13 @@ namespace srsenb { -class stack_interface_phy_lte; +class stack_interface_rrc +{ +public: + virtual void add_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) = 0; + virtual void remove_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id) = 0; + virtual void remove_eps_bearers(uint16_t rnti) = 0; +}; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/enb_mac_interfaces.h b/lib/include/srsran/interfaces/enb_mac_interfaces.h index c2da852a1..aa6e4e45c 100644 --- a/lib/include/srsran/interfaces/enb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/enb_mac_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,8 +19,8 @@ * */ +#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/interfaces/rrc_interface_types.h" -#include "srsran/interfaces/sched_interface.h" #ifndef SRSRAN_ENB_MAC_INTERFACES_H #define SRSRAN_ENB_MAC_INTERFACES_H @@ -28,11 +28,13 @@ namespace srsenb { struct mac_args_t { - uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells + uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells + uint32_t prach_bi; ///< Backoff Indicator to prevent UE from PRACHing too fast sched_interface::sched_args_t sched; - int nr_tb_size = -1; + int lcid_padding; uint32_t nof_prealloc_ues; ///< Number of UE resources to pre-allocate at eNB startup uint32_t max_nof_kos; + int rlf_min_ul_snr_estim; }; /* Interface PHY -> MAC */ @@ -131,6 +133,18 @@ public: */ virtual int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) = 0; + /** + * PHY callback for giving MAC the Channel Quality information of a given RNTI, TTI, eNb cell/carrier for a specific + * subband + * @param tti the given TTI + * @param rnti the UE identifier in the eNb + * @param cc_idx The eNb Cell/Carrier where the measurement corresponds + * @param sb_idx Index of the Sub-band + * @param cqi_value the corresponding Channel Quality Information + * @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR* if an error occurs + */ + virtual int sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t sb_idx, uint32_t cqi_value) = 0; + typedef enum { PUSCH = 0, PUCCH, SRS } ul_channel_t; /** @@ -178,7 +192,7 @@ public: * @param rnti the UE identifier in the eNb * @param cc_idx the eNb Cell/Carrier identifier * @param nof_bytes the number of grants carrierd by the PUSCH message - * @param crc_res the CRC check, set to true if the message was decoded succesfully + * @param crc_res the CRC check, set to true if the message was decoded successfully * @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR* if an error occurs */ virtual int crc_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) = 0; @@ -190,10 +204,16 @@ public: * @param rnti the UE identifier in the eNb * @param enb_cc_idx the eNb Cell/Carrier identifier * @param nof_bytes the number of grants carrierd by the PUSCH message - * @param crc_res the CRC check, set to true if the message was decoded succesfully + * @param crc_res the CRC check, set to true if the message was decoded successfully + * @param ul_nof_prbs Number of PRBs allocated to grant * @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR* if an error occurs */ - virtual int push_pdu(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) = 0; + virtual int push_pdu(uint32_t tti_rx, + uint16_t rnti, + uint32_t enb_cc_idx, + uint32_t nof_bytes, + bool crc_res, + uint32_t ul_nof_prbs) = 0; virtual int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) = 0; virtual int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) = 0; @@ -211,12 +231,11 @@ class mac_interface_rrc { public: /* Provides cell configuration including SIB periodicity, etc. */ - virtual int cell_cfg(const std::vector& cell_cfg) = 0; - virtual void reset() = 0; + virtual int cell_cfg(const std::vector& cell_cfg) = 0; /* Manages UE configuration context */ - virtual int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) = 0; - virtual int ue_rem(uint16_t rnti) = 0; + virtual int ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t* cfg) = 0; + virtual int ue_rem(uint16_t rnti) = 0; /** * Called after Msg3 reception to set the UE C-RNTI, resolve contention, and alter the UE's configuration in the @@ -226,17 +245,17 @@ public: * @param crnti chosen C-RNTI for the UE * @param cfg new UE scheduler configuration */ - virtual int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, sched_interface::ue_cfg_t* cfg) = 0; + virtual int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, const sched_interface::ue_cfg_t& cfg) = 0; /* Manages UE bearers and associated configuration */ - virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) = 0; - virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0; - virtual void phy_config_enabled(uint16_t rnti, bool enabled) = 0; + virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, mac_lc_ch_cfg_t* cfg) = 0; + virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0; + virtual void phy_config_enabled(uint16_t rnti, bool enabled) = 0; virtual void write_mcch(const srsran::sib2_mbms_t* sib2_, const srsran::sib13_t* sib13_, const srsran::mcch_msg_t* mcch_, const uint8_t* mcch_payload, - const uint8_t mcch_payload_length) = 0; + const uint8_t mcch_payload_length) = 0; /** * Allocate a C-RNTI for a new user, without adding it to the phy layer and scheduler yet @@ -247,10 +266,7 @@ public: // Combined interface for PHY to access stack (MAC and RRC) class stack_interface_phy_lte : public mac_interface_phy_lte -{ -public: - virtual void tti_clock() = 0; -}; +{}; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/enb_metrics_interface.h b/lib/include/srsran/interfaces/enb_metrics_interface.h index e4db5e00d..cd4653a00 100644 --- a/lib/include/srsran/interfaces/enb_metrics_interface.h +++ b/lib/include/srsran/interfaces/enb_metrics_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,14 +26,14 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsenb/hdr/phy/phy_metrics.h" -#include "srsenb/hdr/stack/mac/mac_metrics.h" +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" #include "srsenb/hdr/stack/rrc/rrc_metrics.h" -#include "srsenb/hdr/stack/upper/s1ap_metrics.h" +#include "srsenb/hdr/stack/s1ap/s1ap_metrics.h" #include "srsran/common/metrics_hub.h" #include "srsran/radio/radio_metrics.h" +#include "srsran/rlc/rlc_metrics.h" #include "srsran/system/sys_metrics.h" #include "srsran/upper/pdcp_metrics.h" -#include "srsran/upper/rlc_metrics.h" #include "srsue/hdr/stack/upper/gw_metrics.h" namespace srsenb { @@ -58,6 +58,7 @@ struct enb_metrics_t { srsran::rf_metrics_t rf; std::vector phy; stack_metrics_t stack; + stack_metrics_t nr_stack; srsran::sys_metrics_t sys; bool running; }; diff --git a/lib/include/srsran/interfaces/enb_pdcp_interfaces.h b/lib/include/srsran/interfaces/enb_pdcp_interfaces.h index d28f7cd4c..582d29576 100644 --- a/lib/include/srsran/interfaces/enb_pdcp_interfaces.h +++ b/lib/include/srsran/interfaces/enb_pdcp_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -40,13 +40,14 @@ public: class pdcp_interface_rrc { public: + virtual void set_enabled(uint16_t rnti, uint32_t lcid, bool enable) = 0; virtual void reset(uint16_t rnti) = 0; virtual void add_user(uint16_t rnti) = 0; virtual void rem_user(uint16_t rnti) = 0; virtual void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) = 0; - virtual void add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cnfg) = 0; + virtual void add_bearer(uint16_t rnti, uint32_t lcid, const srsran::pdcp_config_t& cnfg) = 0; virtual void del_bearer(uint16_t rnti, uint32_t lcid) = 0; - virtual void config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t sec_cfg) = 0; + virtual void config_security(uint16_t rnti, uint32_t lcid, const srsran::as_security_config_t& sec_cfg) = 0; virtual void enable_integrity(uint16_t rnti, uint32_t lcid) = 0; virtual void enable_encryption(uint16_t rnti, uint32_t lcid) = 0; virtual void send_status_report(uint16_t rnti) = 0; diff --git a/lib/include/srsran/interfaces/enb_phy_interfaces.h b/lib/include/srsran/interfaces/enb_phy_interfaces.h index 28c996133..f2a9b8ecd 100644 --- a/lib/include/srsran/interfaces/enb_phy_interfaces.h +++ b/lib/include/srsran/interfaces/enb_phy_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/interfaces/enb_rlc_interfaces.h b/lib/include/srsran/interfaces/enb_rlc_interfaces.h index 4eea2d1eb..f49ada1be 100644 --- a/lib/include/srsran/interfaces/enb_rlc_interfaces.h +++ b/lib/include/srsran/interfaces/enb_rlc_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,8 +35,6 @@ public: * Segmentation happens in this function. RLC PDU is stored in payload. */ virtual int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; - virtual void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) = 0; - /* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread. * PDU gets placed into the buffer and higher layer thread gets notified. */ virtual void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; @@ -52,6 +50,7 @@ public: virtual void discard_sdu(uint16_t rnti, uint32_t lcid, uint32_t sn) = 0; virtual bool rb_is_um(uint16_t rnti, uint32_t lcid) = 0; virtual bool sdu_queue_is_full(uint16_t rnti, uint32_t lcid) = 0; + virtual bool is_suspended(uint16_t rnti, uint32_t lcid) = 0; }; // RLC interface for RRC @@ -61,12 +60,13 @@ public: virtual void clear_buffer(uint16_t rnti) = 0; virtual void add_user(uint16_t rnti) = 0; virtual void rem_user(uint16_t rnti) = 0; - virtual void add_bearer(uint16_t rnti, uint32_t lcid, srsran::rlc_config_t cnfg) = 0; + virtual void add_bearer(uint16_t rnti, uint32_t lcid, const srsran::rlc_config_t& cnfg) = 0; virtual void add_bearer_mrb(uint16_t rnti, uint32_t lcid) = 0; virtual void del_bearer(uint16_t rnti, uint32_t lcid) = 0; virtual void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; virtual bool has_bearer(uint16_t rnti, uint32_t lcid) = 0; virtual bool suspend_bearer(uint16_t rnti, uint32_t lcid) = 0; + virtual bool is_suspended(uint16_t rnti, uint32_t lcid) = 0; virtual bool resume_bearer(uint16_t rnti, uint32_t lcid) = 0; virtual void reestablish(uint16_t rnti) = 0; }; diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_mac.h b/lib/include/srsran/interfaces/enb_rrc_interface_mac.h new file mode 100644 index 000000000..67fb1c4b3 --- /dev/null +++ b/lib/include/srsran/interfaces/enb_rrc_interface_mac.h @@ -0,0 +1,48 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_ENB_RRC_INTERFACE_MAC_H +#define SRSRAN_ENB_RRC_INTERFACE_MAC_H + +#include "srsenb/hdr/stack/mac/sched_interface.h" + +namespace srsenb { + +/// RRC interface for MAC +class rrc_interface_mac +{ +public: + /* Radio Link failure */ + virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0; + virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; + virtual void set_activity_user(uint16_t rnti) = 0; + virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0; + virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0; + virtual bool is_paging_opportunity(uint32_t tti_tx_dl, uint32_t* payload_len) = 0; + virtual void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t payload_size) = 0; + + ///< Provide packed SIB to MAC (buffer is managed by RRC) + virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_ENB_RRC_INTERFACE_MAC_H diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_pdcp.h b/lib/include/srsran/interfaces/enb_rrc_interface_pdcp.h new file mode 100644 index 000000000..494c15596 --- /dev/null +++ b/lib/include/srsran/interfaces/enb_rrc_interface_pdcp.h @@ -0,0 +1,39 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_ENB_RRC_INTERFACE_PDCP_H +#define SRSRAN_ENB_RRC_INTERFACE_PDCP_H + +#include "srsran/common/byte_buffer.h" + +namespace srsenb { + +/// RRC interface for PDCP +class rrc_interface_pdcp +{ +public: + virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; + virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_ENB_RRC_INTERFACE_PDCP_H diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_rlc.h b/lib/include/srsran/interfaces/enb_rrc_interface_rlc.h new file mode 100644 index 000000000..98281df27 --- /dev/null +++ b/lib/include/srsran/interfaces/enb_rrc_interface_rlc.h @@ -0,0 +1,40 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_ENB_RRC_INTERFACE_RLC_H +#define SRSRAN_ENB_RRC_INTERFACE_RLC_H + +#include "srsran/common/byte_buffer.h" + +namespace srsenb { + +/// RRC interface for RLC +class rrc_interface_rlc +{ +public: + virtual void max_retx_attempted(uint16_t rnti) = 0; + virtual void protocol_failure(uint16_t rnti) = 0; + virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_ENB_RRC_INTERFACE_RLC_H diff --git a/lib/include/srsran/interfaces/enb_rrc_interfaces.h b/lib/include/srsran/interfaces/enb_rrc_interface_s1ap.h similarity index 77% rename from lib/include/srsran/interfaces/enb_rrc_interfaces.h rename to lib/include/srsran/interfaces/enb_rrc_interface_s1ap.h index e35be3bb0..e4a4b4725 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/enb_rrc_interface_s1ap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,7 +21,6 @@ #include "srsran/asn1/s1ap_utils.h" #include "srsran/interfaces/enb_rrc_interface_types.h" -#include "srsran/interfaces/sched_interface.h" #ifndef SRSRAN_ENB_RRC_INTERFACES_H #define SRSRAN_ENB_RRC_INTERFACES_H @@ -95,38 +94,6 @@ public: virtual void set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_status_transfer_list_l& erabs) = 0; }; -/// RRC interface for RLC -class rrc_interface_rlc -{ -public: - virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0; - virtual void max_retx_attempted(uint16_t rnti) = 0; - virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; -}; - -/// RRC interface for MAC -class rrc_interface_mac -{ -public: - /* Radio Link failure */ - virtual int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& init_ue_cfg) = 0; - virtual void upd_user(uint16_t new_rnti, uint16_t old_rnti) = 0; - virtual void set_activity_user(uint16_t rnti) = 0; - virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0; - virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0; - virtual bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) = 0; - - ///< Provide packed SIB to MAC (buffer is managed by RRC) - virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0; -}; - -/// RRC interface for PDCP -class rrc_interface_pdcp -{ -public: - virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; -}; - } // namespace srsenb #endif // SRSRAN_ENB_RRC_INTERFACES_H diff --git a/lib/include/srsran/interfaces/enb_rrc_interface_types.h b/lib/include/srsran/interfaces/enb_rrc_interface_types.h index 80f4e4c8c..974b42e9d 100644 --- a/lib/include/srsran/interfaces/enb_rrc_interface_types.h +++ b/lib/include/srsran/interfaces/enb_rrc_interface_types.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,12 +37,13 @@ struct scell_cfg_t { // Cell to measure for Handover struct meas_cell_cfg_t { - uint32_t earfcn; - uint16_t pci; - uint32_t eci; - float q_offset; - uint32_t allowed_meas_bw; - bool direct_forward_path_available; + uint32_t earfcn; + uint16_t pci; + uint32_t eci; + asn1::rrc::q_offset_range_e cell_individual_offset; + uint32_t allowed_meas_bw; + bool direct_forward_path_available; + int tac; }; // neigh measurement Cell info @@ -51,26 +52,29 @@ struct rrc_meas_cfg_t { std::vector meas_reports; asn1::rrc::quant_cfg_eutra_s quant_cfg; uint32_t meas_gap_period; + std::vector meas_gap_offset_subframe; uint32_t allowed_meas_bw; }; // Cell/Sector configuration struct cell_cfg_t { - uint32_t rf_port; - uint32_t cell_id; - uint16_t tac; - uint32_t pci; - uint16_t root_seq_idx; - uint32_t dl_earfcn; - double dl_freq_hz; - uint32_t ul_earfcn; - double ul_freq_hz; - int target_pucch_sinr_db; - int target_pusch_sinr_db; - uint32_t initial_dl_cqi; - bool enable_phr_handling; - std::vector scell_list; - rrc_meas_cfg_t meas_cfg; + uint32_t rf_port; + uint32_t cell_id; + uint16_t tac; + uint32_t pci; + double tx_gain; + uint16_t root_seq_idx; + uint32_t dl_earfcn; + double dl_freq_hz; + uint32_t ul_earfcn; + double ul_freq_hz; + int target_pucch_sinr_db; + int target_pusch_sinr_db; + bool enable_phr_handling; + int min_phr_thres; + asn1::rrc::mob_ctrl_info_s::t304_e_ t304; + std::vector scell_list; + rrc_meas_cfg_t meas_cfg; }; typedef std::vector cell_list_t; diff --git a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h index f911ffe7b..e1d0457e8 100644 --- a/lib/include/srsran/interfaces/enb_s1ap_interfaces.h +++ b/lib/include/srsran/interfaces/enb_s1ap_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,16 @@ struct s1ap_args_t { std::string gtp_bind_addr; std::string gtp_advertise_addr; std::string s1c_bind_addr; + uint16_t s1c_bind_port; std::string enb_name; + uint32_t ts1_reloc_prep_timeout; + uint32_t ts1_reloc_overall_timeout; + int32_t max_s1_setup_retries; + uint32_t s1_connect_timer; + bool sctp_reuse_addr; + int32_t sctp_rto_max; + int32_t sctp_init_max_attempts; + int32_t sctp_max_init_timeo; }; // S1AP interface for RRC @@ -68,8 +77,9 @@ public: virtual bool user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_radio) = 0; virtual bool is_mme_connected() = 0; - /// TS 36.413, 8.3.1 - Initial Context Setup - virtual void ue_ctxt_setup_complete(uint16_t rnti) = 0; + // Notify S1AP of RRC reconfiguration successful finish. + // Many S1AP procedures use this notification to indicate successful end (e.g InitialContextSetupRequest) + virtual void notify_rrc_reconf_complete(uint16_t rnti) = 0; /** * Command the s1ap to transmit a HandoverRequired message to MME. @@ -84,6 +94,7 @@ public: */ virtual bool send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, diff --git a/lib/include/srsran/interfaces/enb_time_interface.h b/lib/include/srsran/interfaces/enb_time_interface.h new file mode 100644 index 000000000..1f6f5d1d6 --- /dev/null +++ b/lib/include/srsran/interfaces/enb_time_interface.h @@ -0,0 +1,40 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_ENB_TIME_INTERFACE_H +#define SRSRAN_ENB_TIME_INTERFACE_H + +namespace srsenb { + +// RAT-agnostic interface to provide timing information to upper layers +class enb_time_interface +{ +public: + /** + * @brief Called for every tick (currently every ms) + * + */ + virtual void tti_clock() = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_ENB_TIME_INTERFACE_H diff --git a/lib/include/srsran/interfaces/enb_x2_interfaces.h b/lib/include/srsran/interfaces/enb_x2_interfaces.h new file mode 100644 index 000000000..faab81d44 --- /dev/null +++ b/lib/include/srsran/interfaces/enb_x2_interfaces.h @@ -0,0 +1,132 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/interfaces/enb_gtpu_interfaces.h" +#include "srsran/interfaces/enb_pdcp_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_types.h" + +#ifndef SRSRAN_ENB_X2_INTERFACES_H +#define SRSRAN_ENB_X2_INTERFACES_H + +namespace srsenb { + +/** + * @brief Set of X2AP inspired interfaces to support 5G NSA + * + */ + +/// X2AP inspired interface to allow EUTRA RRC to call NR RRC +class rrc_nr_interface_rrc +{ +public: + struct sgnb_addition_req_params_t { + uint32_t eps_bearer_id; + uint32_t five_qi; + // add configuration check + // E-RAB Parameters, Tunnel address (IP address, TEID) + // QCI, security, etc + }; + + /// Request addition of NR carrier for UE + virtual void sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) = 0; + + /// Provide information whether the requested configuration was applied successfully by the UE + virtual void sgnb_reconfiguration_complete(uint16_t eutra_rnti, const asn1::dyn_octstring& reconfig_response) = 0; + + /// Trigger release for specific UE + virtual void sgnb_release_request(uint16_t nr_rnti) = 0; +}; + +/// X2AP inspired interface for response from NR RRC to EUTRA RRC +class rrc_eutra_interface_rrc_nr +{ +public: + /** + * @brief List of parameters included in the SgNB addition Ack message + * @param nr_secondary_cell_group_cfg_r15 Encoded part of the RRC Reconfiguration + * @param nr_radio_bearer_cfg1_r15 Encoded part of the RRC Reconfiguration + * @param eps_bearer_id ID of the transfered bearer + */ + struct sgnb_addition_ack_params_t { + uint16_t nr_rnti = SRSRAN_INVALID_RNTI; // RNTI that was assigned to the UE + asn1::dyn_octstring nr_secondary_cell_group_cfg_r15; + asn1::dyn_octstring nr_radio_bearer_cfg1_r15; + uint32_t eps_bearer_id = 0; // (list of) successfully transfered EPS bearers + }; + + /** + * @brief Signal successful addition of UE + * + * @param eutra_rnti The RNTI that the EUTRA RRC used to request the SgNB addition + * @param params Parameter list + */ + virtual void sgnb_addition_ack(uint16_t eutra_rnti, sgnb_addition_ack_params_t params) = 0; + + /** + * @brief Signal unsuccessful SgNB addition + * + * @param eutra_rnti The RNTI that the EUTRA RRC used to request the SgNB addition + */ + virtual void sgnb_addition_reject(uint16_t eutra_rnti) = 0; + + /** + * @brief Signal completion of SgNB addition after UE (with new NR identity) has attached + * + * @param eutra_rnti The RNTI that the EUTRA RRC used to request the SgNB addition + * @param nr_rnti The RNTI that has been assigned to the UE on the SgNB + */ + virtual void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) = 0; + + /** + * @brief Signal timeout for inactivity or MSG5 timers + * + * @param eutra_rnti The RNTI that the EUTRA RRC used to request the SgNB addition + */ + virtual void sgnb_inactivity_timeout(uint16_t eutra_rnti) = 0; + + /** + * @brief Signal release of all UE resources on the NR cell + * + * @param eutra_rnti The RNTI that the EUTRA RRC used to request the SgNB addition + */ + virtual void sgnb_release_ack(uint16_t eutra_rnti) = 0; + + /** + * @brief Signal user activity (i.e. DL/UL traffic) for given RNTI + * + * @param eutra_rnti The RNTI that the EUTRA RRC uses + */ + virtual void set_activity_user(uint16_t eutra_rnti) = 0; +}; + +// combined interface used by X2 adapter +class x2_interface : public rrc_nr_interface_rrc, + public rrc_eutra_interface_rrc_nr, + public pdcp_interface_gtpu, // allow GTPU to access PDCP in DL direction + public gtpu_interface_pdcp // allow PDCP to access GTPU in UL direction +{ +public: + virtual ~x2_interface() = default; +}; + +} // namespace srsenb + +#endif // SRSRAN_ENB_X2_INTERFACES_H diff --git a/lib/include/srsran/interfaces/epc_interfaces.h b/lib/include/srsran/interfaces/epc_interfaces.h index fb8dfbf31..a4b69278d 100644 --- a/lib/include/srsran/interfaces/epc_interfaces.h +++ b/lib/include/srsran/interfaces/epc_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index a86d704cf..d05a9b25e 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,26 +28,13 @@ #include "srsran/common/security.h" #include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/interfaces/rlc_interface_types.h" -#include "srsran/interfaces/rrc_interface_types.h" -#include "srsran/interfaces/sched_interface.h" +// EUTRA interfaces that are used unmodified +#include "srsran/interfaces/enb_rrc_interface_pdcp.h" +#include "srsran/interfaces/enb_rrc_interface_rlc.h" namespace srsenb { -/***************************** - * MAC INTERFACES - ****************************/ -class mac_interface_rrc_nr -{ -public: - // Provides cell configuration including SIB periodicity, etc. - virtual int cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg) = 0; -}; - -class mac_interface_rlc_nr -{ -public: - virtual int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) = 0; -}; +struct sched_nr_ue_cfg_t; /***************************** * RLC INTERFACES @@ -99,14 +86,14 @@ public: class pdcp_interface_rrc_nr { public: - virtual void reset(uint16_t rnti) = 0; - virtual void add_user(uint16_t rnti) = 0; - virtual void rem_user(uint16_t rnti) = 0; - virtual void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; - virtual void add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cnfg) = 0; - virtual void config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t sec_cfg) = 0; - virtual void enable_integrity(uint16_t rnti, uint32_t lcid) = 0; - virtual void enable_encryption(uint16_t rnti, uint32_t lcid) = 0; + virtual void reset(uint16_t rnti) = 0; + virtual void add_user(uint16_t rnti) = 0; + virtual void rem_user(uint16_t rnti) = 0; + virtual void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; + virtual void add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cnfg) = 0; + virtual void config_security(uint16_t rnti, uint32_t lcid, const srsran::as_security_config_t& sec_cfg) = 0; + virtual void enable_integrity(uint16_t rnti, uint32_t lcid) = 0; + virtual void enable_encryption(uint16_t rnti, uint32_t lcid) = 0; }; class pdcp_interface_sdap_nr @@ -143,6 +130,17 @@ public: virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; }; +/***************************** + * MAC internal INTERFACES + ****************************/ + +class mac_interface_pdu_demux_nr +{ +public: + // Called by PDU handler from Stack thread to store Msg3 content (According to O-RAN WG8 v3.0, Sec. 9.2.2.3.5 MAC) + virtual void store_msg3(uint16_t rnti, srsran::unique_byte_buffer_t pdu) = 0; +}; + /***************************** * RRC INTERFACES ****************************/ @@ -152,71 +150,55 @@ class rrc_interface_mac_nr { public: // Provides MIB packed message - virtual int read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) = 0; - virtual int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) = 0; -}; -class rrc_interface_rlc_nr -{ -public: - virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0; - virtual void max_retx_attempted(uint16_t rnti) = 0; - virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; - virtual const char* get_rb_name(uint32_t lcid) = 0; -}; -class rrc_interface_pdcp_nr -{ -public: - virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; -}; -class rrc_interface_ngap_nr -{ -public: + virtual int read_pdu_bcch_bch(const uint32_t tti, srsran::byte_buffer_t& buffer) = 0; + virtual int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::byte_buffer_t& buffer) = 0; + + /// User management + virtual int add_user(uint16_t rnti, uint32_t pcell_cc_idx) = 0; + virtual int update_user(uint16_t new_rnti, uint16_t old_rnti) = 0; + virtual void set_activity_user(uint16_t rnti) = 0; }; -/***************************** - * NGAP INTERFACES - ****************************/ -class ngap_interface_rrc_nr +// NR interface is almost identical to EUTRA version +class rrc_interface_rlc_nr : public rrc_interface_rlc { public: + virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0; + virtual const char* get_rb_name(uint32_t lcid) = 0; }; -class phy_interface_stack_nr +// NR interface identical to EUTRA version +class rrc_interface_pdcp_nr : public rrc_interface_pdcp +{}; + +class phy_interface_rrc_nr { public: - const static int MAX_DL_GRANTS = 4; + /** + * @brief Describes physical layer configuration common among all the UEs for a given cell + */ + struct common_cfg_t { + srsran_carrier_nr_t carrier; + srsran_pdcch_cfg_nr_t pdcch; + srsran_prach_cfg_t prach; + srsran_ssb_cfg_t ssb; + srsran_duplex_mode_t duplex_mode; + }; - typedef struct { - // TODO: include NR related fields - } dl_sched_grant_t; + virtual int set_common_cfg(const common_cfg_t& common_cfg) = 0; +}; - typedef struct { - bool mib_present; - } bch_sched_t; +class phy_interface_mac_nr +{ +public: + // TBD +}; - typedef struct { - uint32_t tti; - uint32_t nof_grants; - dl_sched_grant_t pdsch[MAX_DL_GRANTS]; - int beam_id; - } dl_config_request_t; - - typedef struct { - bch_sched_t pbch; - uint16_t length; - uint16_t index; // index indicated in dl_config - uint8_t* data[SRSRAN_MAX_TB]; // always a pointer in our case - } tx_request_pdu_t; - - typedef struct { - uint32_t tti; - uint32_t tb_len; - uint32_t nof_pdus; - tx_request_pdu_t pdus[MAX_DL_GRANTS]; - } tx_request_t; - - virtual int dl_config_request(const dl_config_request_t& request) = 0; - virtual int tx_request(const tx_request_t& request) = 0; +// Combined interface for stack (MAC and RRC) to access PHY +class phy_interface_stack_nr : public phy_interface_rrc_nr, public phy_interface_mac_nr +{ +public: + // TBD }; class stack_interface_mac @@ -229,44 +211,103 @@ public: class mac_interface_phy_nr { public: - const static int MAX_GRANTS = 64; + const static int MAX_SSB = 4; + const static int MAX_GRANTS = 4; + const static int MAX_PUCCH_MSG = 64; + const static int MAX_PUCCH_CANDIDATES = 2; + const static int MAX_NZP_CSI_RS = 4; - /** - * DL grant structure per UE - */ - struct dl_sched_grant_t { - srsran_dci_dl_nr_t dci = {}; - uint8_t* data[SRSRAN_MAX_TB] = {}; - srsran_softbuffer_tx_t* softbuffer_tx[SRSRAN_MAX_TB] = {}; + struct pdcch_dl_t { + srsran_dci_cfg_nr_t dci_cfg = {}; + srsran_dci_dl_nr_t dci = {}; + }; + + struct pdcch_ul_t { + srsran_dci_cfg_nr_t dci_cfg = {}; + srsran_dci_ul_nr_t dci = {}; + }; + + struct pdsch_t { + srsran_sch_cfg_nr_t sch = {}; ///< PDSCH configuration + std::array data = {}; ///< Data pointer + }; + + struct ssb_t { + srsran_pbch_msg_nr_t pbch_msg = {}; + }; + + struct dl_sched_t { + srsran::bounded_vector ssb; + srsran::bounded_vector pdcch_dl; + srsran::bounded_vector pdcch_ul; + srsran::bounded_vector pdsch; + srsran::bounded_vector nzp_csi_rs; + }; + + struct pusch_t { + uint32_t pid = 0; ///< HARQ process ID + srsran_sch_cfg_nr_t sch = {}; ///< PUSCH configuration }; /** - * DL Scheduling result per cell/carrier + * @brief Describes a possible PUCCH candidate transmission + * @note The physical layer shall try decoding all the possible PUCCH candidates and report back to the stack the + * strongest of the candidates. This is thought to be used in the case of SR opportunities in which the UE could + * transmit HARQ-ACK in two possible resources. */ - typedef struct { - dl_sched_grant_t pdsch[MAX_GRANTS]; //< DL Grants - uint32_t nof_grants; //< Number of DL grants - } dl_sched_t; - - /** - * List of DL scheduling results, one entry per cell/carrier - */ - typedef std::vector dl_sched_list_t; -}; - -class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr -{ -public: - struct rx_data_ind_t { - uint32_t tti; - uint16_t rnti; - srsran::unique_byte_buffer_t tb; + struct pucch_candidate_t { + srsran_uci_cfg_nr_t uci_cfg; ///< UCI configuration for the opportunity + srsran_pucch_nr_resource_t resource; ///< PUCCH resource to use }; - virtual int sf_indication(const uint32_t tti) = 0; - virtual int rx_data_indication(rx_data_ind_t& grant) = 0; + struct pucch_t { + srsran_pucch_nr_common_cfg_t pucch_cfg; ///< UE dedicated PUCCH configuration + srsran::bounded_vector candidates; ///< PUCCH candidates to decode + }; + + struct ul_sched_t { + srsran::bounded_vector pusch; + srsran::bounded_vector pucch; + }; + + struct pucch_info_t { + srsran_uci_data_nr_t uci_data; ///< RNTI is available under cfg->pucch->rnti + srsran_csi_trs_measurements_t csi; ///< DMRS based signal Channel State Information (CSI) + }; + + struct pusch_info_t { + // Context + uint16_t rnti; ///< UE temporal identifier + uint32_t pid = 0; ///< HARQ process ID + + // SCH and UCI payload information + srsran_pusch_res_nr_t pusch_data; + srsran_uci_cfg_nr_t uci_cfg; ///< Provides UCI configuration, so stack does not need to keep the pending state + + // Actual SCH PDU + srsran::unique_byte_buffer_t pdu = nullptr; + + // PUSCH signal measurements + srsran_csi_trs_measurements_t csi; ///< DMRS based signal Channel State Information (CSI) + }; + + struct rach_info_t { + uint32_t slot_index; + uint32_t preamble; + uint32_t time_adv; + }; + + virtual int slot_indication(const srsran_slot_cfg_t& slot_cfg) = 0; + virtual dl_sched_t* get_dl_sched(const srsran_slot_cfg_t& slot_cfg) = 0; + virtual ul_sched_t* get_ul_sched(const srsran_slot_cfg_t& slot_cfg) = 0; + virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0; + virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) = 0; + virtual void rach_detected(const rach_info_t& rach_info) = 0; }; +class stack_interface_phy_nr : public mac_interface_phy_nr +{}; + } // namespace srsenb #endif // SRSRAN_GNB_INTERFACES_H diff --git a/lib/include/srsran/interfaces/gnb_mac_interfaces.h b/lib/include/srsran/interfaces/gnb_mac_interfaces.h new file mode 100644 index 000000000..d5a325635 --- /dev/null +++ b/lib/include/srsran/interfaces/gnb_mac_interfaces.h @@ -0,0 +1,50 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_GNB_MAC_INTERFACES_H +#define SRSRAN_GNB_MAC_INTERFACES_H + +#include "srsgnb/hdr/stack/mac/sched_nr_interface.h" +#include "srsran/interfaces/enb_mac_interfaces.h" + +namespace srsenb { + +class mac_interface_rrc_nr +{ +public: + // Provides cell configuration including SIB periodicity, etc. + virtual int cell_cfg(const std::vector& nr_cells) = 0; + + /// Allocates a new user/RNTI at MAC. Returns RNTI on success or SRSRAN_INVALID_RNTI otherwise. + virtual uint16_t reserve_rnti(uint32_t enb_cc_idx, const sched_nr_interface::ue_cfg_t& uecfg) = 0; + + virtual int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) = 0; + + virtual int remove_ue(uint16_t rnti) = 0; +}; + +// NR interface is identical to EUTRA interface +class mac_interface_rlc_nr : public mac_interface_rlc +{}; + +} // namespace srsenb + +#endif // SRSRAN_GNB_MAC_INTERFACES_H diff --git a/lib/include/srsran/interfaces/gnb_ngap_interfaces.h b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h new file mode 100644 index 000000000..b8a5d649d --- /dev/null +++ b/lib/include/srsran/interfaces/gnb_ngap_interfaces.h @@ -0,0 +1,66 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_GNB_NGAP_INTERFACES_H +#define SRSRAN_GNB_NGAP_INTERFACES_H + +#include "srsran/asn1/ngap_utils.h" + +namespace srsenb { + +struct ngap_args_t { + uint32_t gnb_id = 0; // 20-bit id (lsb bits) + uint8_t cell_id = 0; // 8-bit cell id + uint16_t tac = 0; // 16-bit tac + uint16_t mcc = 0; // BCD-coded with 0xF filler + uint16_t mnc = 0; // BCD-coded with 0xF filler + std::string amf_addr = ""; + std::string gtp_bind_addr = ""; + std::string gtp_advertise_addr = ""; + std::string ngc_bind_addr = ""; + std::string gnb_name = ""; +}; + +// NGAP interface for RRC +class ngap_interface_rrc_nr +{ +public: + virtual void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu) = 0; + virtual void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu, + uint32_t m_tmsi) = 0; + + virtual void write_pdu(uint16_t rnti, srsran::const_byte_span pdu) = 0; + virtual bool user_exists(uint16_t rnti) = 0; + virtual void user_mod(uint16_t old_rnti, uint16_t new_rnti) = 0; + virtual void user_release_request(uint16_t rnti, asn1::ngap::cause_radio_network_e cause_radio) = 0; + virtual bool is_amf_connected() = 0; + virtual void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_GNB_NGAP_INTERFACES_H diff --git a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h new file mode 100644 index 000000000..485f0d57d --- /dev/null +++ b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h @@ -0,0 +1,50 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSRAN_GNB_RRC_NR_INTERFACES_H +#define SRSRAN_GNB_RRC_NR_INTERFACES_H + +#include "srsran/asn1/ngap.h" +#include "srsran/common/byte_buffer.h" + +namespace srsenb { + +class rrc_interface_ngap_nr +{ +public: + virtual int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) = 0; + virtual int ue_set_bitrates(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0; + virtual int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) = 0; + virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) = 0; + virtual int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) = 0; + virtual int establish_rrc_bearer(uint16_t rnti, + uint16_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) = 0; + virtual int allocate_lcid(uint16_t rnti) = 0; + virtual int release_bearers(uint16_t rnti) = 0; + virtual void release_user(uint16_t rnti) = 0; + virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_GNB_RRC_NR_INTERFACES_H diff --git a/lib/include/srsran/interfaces/mac_interface_types.h b/lib/include/srsran/interfaces/mac_interface_types.h index a648ddaaf..90a4c91a9 100644 --- a/lib/include/srsran/interfaces/mac_interface_types.h +++ b/lib/include/srsran/interfaces/mac_interface_types.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -133,7 +133,7 @@ struct rach_cfg_t { }; // 38.321 5.1.1 Not complete yet -struct rach_nr_cfg_t { +struct rach_cfg_nr_t { uint32_t prach_ConfigurationIndex; int PreambleReceivedTargetPower; uint32_t preambleTransMax; @@ -141,7 +141,7 @@ struct rach_nr_cfg_t { uint32_t ra_responseWindow; uint32_t ra_ContentionResolutionTimer; - rach_nr_cfg_t() { reset(); } + rach_cfg_nr_t() { reset(); } void reset() { prach_ConfigurationIndex = 0; @@ -224,6 +224,33 @@ struct mac_cfg_t { int time_alignment_timer = -1; }; +struct mac_cfg_nr_t { + // Default constructor with default values as in 36.331 9.2.2 + mac_cfg_nr_t() { set_defaults(); } + + void set_defaults() + { + rach_cfg.reset(); + sr_cfg.reset(); + set_mac_main_cfg_default(); + } + + void set_mac_main_cfg_default() + { + bsr_cfg.reset(); + phr_cfg.reset(); + harq_cfg.reset(); + time_alignment_timer = -1; + } + + bsr_cfg_t bsr_cfg; + phr_cfg_nr_t phr_cfg; + sr_cfg_t sr_cfg; + rach_cfg_nr_t rach_cfg; + ul_harq_cfg_t harq_cfg; + int time_alignment_timer = -1; +}; + } // namespace srsran #endif // SRSRAN_MAC_INTERFACE_TYPES_H diff --git a/lib/include/srsran/interfaces/pdcp_interface_types.h b/lib/include/srsran/interfaces/pdcp_interface_types.h index 979959d5f..962df1b46 100644 --- a/lib/include/srsran/interfaces/pdcp_interface_types.h +++ b/lib/include/srsran/interfaces/pdcp_interface_types.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -67,42 +67,43 @@ static const char* pdcp_pdu_type_text[PDCP_PDU_TYPE_N_ITEMS] = {"PDCP Report PDU // Taken from PDCP-Config (TS 38.331 version 15.2.1) enum class pdcp_t_reordering_t { - ms0 = 0, - ms1 = 1, - ms2 = 2, - ms4 = 4, - ms5 = 5, - ms8 = 8, - ms10 = 10, - ms15 = 15, - ms20 = 20, - ms30 = 30, - ms40 = 40, - ms50 = 50, - ms60 = 60, - ms80 = 80, - ms100 = 100, - ms120 = 120, - ms140 = 140, - ms160 = 160, - ms180 = 180, - ms200 = 200, - ms220 = 220, - ms240 = 240, - ms260 = 260, - ms280 = 280, - ms300 = 300, - ms500 = 500, - ms750 = 750, - ms1000 = 1000, - ms1250 = 1250, - ms1500 = 1500, - ms1750 = 1750, - ms2000 = 2000, - ms2250 = 2250, - ms2500 = 2500, - ms2750 = 2750, - ms3000 = 3000 + ms0 = 0, + ms1 = 1, + ms2 = 2, + ms4 = 4, + ms5 = 5, + ms8 = 8, + ms10 = 10, + ms15 = 15, + ms20 = 20, + ms30 = 30, + ms40 = 40, + ms50 = 50, + ms60 = 60, + ms80 = 80, + ms100 = 100, + ms120 = 120, + ms140 = 140, + ms160 = 160, + ms180 = 180, + ms200 = 200, + ms220 = 220, + ms240 = 240, + ms260 = 260, + ms280 = 280, + ms300 = 300, + ms500 = 500, + ms750 = 750, + ms1000 = 1000, + ms1250 = 1250, + ms1500 = 1500, + ms1750 = 1750, + ms2000 = 2000, + ms2250 = 2250, + ms2500 = 2500, + ms2750 = 2750, + ms3000 = 3000, + infinity = -1 }; // Taken from PDCP-Config (TS 38.331 version 15.2.1) @@ -122,7 +123,7 @@ enum class pdcp_discard_timer_t { ms500 = 500, ms750 = 750, ms1500 = 1500, - infinity = 0 + infinity = -1 }; class pdcp_config_t @@ -165,6 +166,17 @@ public: // TODO: Support the following configurations // bool do_rohc; + + bool operator==(const pdcp_config_t& other) const + { + return bearer_id == other.bearer_id and rb_type == other.rb_type and tx_direction == other.tx_direction and + rx_direction == other.rx_direction and sn_len == other.sn_len and hdr_len_bytes == other.hdr_len_bytes and + t_reordering == other.t_reordering and discard_timer == other.discard_timer and rat == other.rat and + status_report_required == other.status_report_required; + } + bool operator!=(const pdcp_config_t& other) const { return not(*this == other); } + + std::string get_rb_name() const { return (rb_type == PDCP_RB_IS_DRB ? "DRB" : "SRB") + std::to_string(bearer_id); } }; // Specifies in which direction security (integrity and ciphering) are enabled for PDCP diff --git a/lib/include/srsran/interfaces/phy_common_interface.h b/lib/include/srsran/interfaces/phy_common_interface.h new file mode 100644 index 000000000..4c9e37726 --- /dev/null +++ b/lib/include/srsran/interfaces/phy_common_interface.h @@ -0,0 +1,101 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_PHY_COMMON_INTERFACE_H +#define SRSRAN_PHY_COMMON_INTERFACE_H + +#include "../radio/rf_buffer.h" +#include "../radio/rf_timestamp.h" + +namespace srsran { + +/** + * @brief Describes a physical layer common interface + */ +class phy_common_interface +{ +private: + std::mutex tx_mutex; ///< Protect Tx attributes + std::condition_variable tx_cvar; ///< Tx condition variable + bool tx_hold = false; ///< Hold threads until the signal is transmitted + +protected: + void reset_last_worker() + { + std::unique_lock lock(tx_mutex); + tx_hold = true; + } + /** + * @brief Waits for the last worker to call `last_worker()` to prevent that the current SF worker is released and + * overwrites the transmit signal prior transmission + */ + void wait_last_worker() + { + std::unique_lock lock(tx_mutex); + while (tx_hold) { + tx_cvar.wait(lock); + } + } + + /** + * @brief Notifies the last SF worker transmitted the baseband and all the workers waiting are released + */ + void last_worker() + { + std::unique_lock lock(tx_mutex); + tx_hold = false; + tx_cvar.notify_all(); + } + +public: + /** + * @brief Describes a worker context + */ + struct worker_context_t { + uint32_t sf_idx = 0; ///< Subframe index + void* worker_ptr = nullptr; ///< Worker pointer for wait/release semaphore + bool last = false; ///< Indicates this worker is the last one in the sub-frame processing + srsran::rf_timestamp_t tx_time = {}; ///< Transmit time, used only by last worker + + void copy(const worker_context_t& other) + { + sf_idx = other.sf_idx; + worker_ptr = other.worker_ptr; + last = other.last; + tx_time.copy(other.tx_time); + } + + worker_context_t() = default; + worker_context_t(const worker_context_t& other) { copy(other); } + }; + + /** + * @brief Common PHY interface for workers to indicate they ended + * @param w_ctx Worker context + * @param tx_enable Indicates whether the buffer has baseband samples to transmit + * @param buffer Baseband buffer + */ + virtual void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) = 0; +}; + +} // namespace srsran + +#endif // SRSRAN_PHY_COMMON_INTERFACE_H diff --git a/lib/include/srsran/interfaces/phy_interface_types.h b/lib/include/srsran/interfaces/phy_interface_types.h index 57516b7e1..0c5f74d5e 100644 --- a/lib/include/srsran/interfaces/phy_interface_types.h +++ b/lib/include/srsran/interfaces/phy_interface_types.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #ifndef SRSRAN_PHY_INTERFACE_TYPES_H #define SRSRAN_PHY_INTERFACE_TYPES_H +#include "srsran/common/common.h" #include "srsran/srsran.h" /// Common types defined by the PHY layer. @@ -61,11 +62,12 @@ struct phy_meas_nr_t { }; struct phy_meas_t { - float rsrp; - float rsrq; - float cfo_hz; - uint32_t earfcn; - uint32_t pci; + srsran::srsran_rat_t rat; ///< LTE or NR + float rsrp; + float rsrq; + float cfo_hz; + uint32_t earfcn; + uint32_t pci; }; struct phy_cell_t { diff --git a/lib/include/srsran/interfaces/radio_interfaces.h b/lib/include/srsran/interfaces/radio_interfaces.h index 3b492d805..3db1acce5 100644 --- a/lib/include/srsran/interfaces/radio_interfaces.h +++ b/lib/include/srsran/interfaces/radio_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/interfaces/rlc_interface_types.h b/lib/include/srsran/interfaces/rlc_interface_types.h index a8f317f62..18fa1239f 100644 --- a/lib/include/srsran/interfaces/rlc_interface_types.h +++ b/lib/include/srsran/interfaces/rlc_interface_types.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -73,11 +73,28 @@ inline std::string to_string(const rlc_am_nr_sn_size_t& sn_size) constexpr static const char* options[] = {"12 bits", "18 bits"}; return enum_to_text(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size); } -inline uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size) +constexpr uint16_t to_number(const rlc_am_nr_sn_size_t& sn_size) { - constexpr static uint16_t options[] = {12, 18}; + constexpr uint16_t options[] = {12, 18}; return enum_to_number(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size); } +/** + * @brief Value range of the serial numbers + * @param sn_size Length of the serial number field in bits + * @return cardianlity + */ +constexpr uint32_t cardinality(const rlc_am_nr_sn_size_t& sn_size) +{ + return (1 << to_number(sn_size)); +} +/**************************************************************************** + * Tx constants + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2 + ***************************************************************************/ +constexpr uint32_t am_window_size(const rlc_am_nr_sn_size_t& sn_size) +{ + return cardinality(sn_size) / 2; +} struct rlc_am_config_t { /**************************************************************************** @@ -96,6 +113,26 @@ struct rlc_am_config_t { int32_t t_status_prohibit; // Timer used by rx to prohibit tx of status PDU (ms) }; +struct rlc_am_nr_config_t { + /**************************************************************************** + * Configurable parameters + * Ref: 3GPP TS 38.322 Section 7 + ***************************************************************************/ + + rlc_am_nr_sn_size_t tx_sn_field_length; // Number of bits used for tx (UL) sequence number + rlc_am_nr_sn_size_t rx_sn_field_length; // Number of bits used for rx (DL) sequence number + + // Timers Ref: 3GPP TS 38.322 Section 7.3 + int32_t t_poll_retx; // Poll retx timeout (ms) + int32_t t_reassembly; // Timer used by rx to detect PDU loss (ms) + int32_t t_status_prohibit; // Timer used by rx to prohibit tx of status PDU (ms) + + // Configurable Parameters. Ref: 3GPP TS 38.322 Section 7.4 + uint32_t max_retx_thresh; // Max number of retx + int32_t poll_pdu; // Insert poll bit after this many PDUs + int32_t poll_byte; // Insert poll bit after this much data (KB) +}; + struct rlc_um_config_t { /**************************************************************************** * Configurable parameters @@ -119,9 +156,8 @@ struct rlc_um_nr_config_t { ***************************************************************************/ rlc_um_nr_sn_size_t sn_field_length; // Number of bits used for sequence number - uint32_t UM_Window_Size; - uint32_t mod; // Rx/Tx counter modulus int32_t t_reassembly_ms; // Timer used by rx to detect PDU loss (ms) + uint8_t bearer_id; // This is not in the 3GPP TS 38.322 }; #define RLC_TX_QUEUE_LEN (256) @@ -132,12 +168,13 @@ public: srsran_rat_t rat; rlc_mode_t rlc_mode; rlc_am_config_t am; + rlc_am_nr_config_t am_nr; rlc_um_config_t um; rlc_um_nr_config_t um_nr; uint32_t tx_queue_length; rlc_config_t() : - rat(srsran_rat_t::lte), rlc_mode(rlc_mode_t::tm), am(), um(), um_nr(), tx_queue_length(RLC_TX_QUEUE_LEN){}; + rat(srsran_rat_t::lte), rlc_mode(rlc_mode_t::tm), am(), am_nr(), um(), um_nr(), tx_queue_length(RLC_TX_QUEUE_LEN){}; // Factory for MCH static rlc_config_t mch_config() @@ -208,6 +245,27 @@ public: rlc_cnfg.am.t_poll_retx = 5; return rlc_cnfg; } + static rlc_config_t default_rlc_am_nr_config(uint32_t sn_size = 12) + { + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + if (sn_size == 12) { + rlc_cnfg.am_nr.tx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size12bits; + rlc_cnfg.am_nr.rx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size12bits; + } else if (sn_size == 18) { + rlc_cnfg.am_nr.tx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size18bits; + rlc_cnfg.am_nr.rx_sn_field_length = srsran::rlc_am_nr_sn_size_t::size18bits; + } else { + return {}; + } + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 4; + rlc_cnfg.am_nr.t_reassembly = 35; + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.t_poll_retx = 45; + return rlc_cnfg; + } static rlc_config_t default_rlc_um_nr_config(uint32_t sn_size = 6) { rlc_config_t cnfg = {}; @@ -215,12 +273,8 @@ public: cnfg.rlc_mode = rlc_mode_t::um; if (sn_size == 6) { cnfg.um_nr.sn_field_length = rlc_um_nr_sn_size_t::size6bits; - cnfg.um_nr.UM_Window_Size = 32; - cnfg.um_nr.mod = 64; } else if (sn_size == 12) { cnfg.um_nr.sn_field_length = rlc_um_nr_sn_size_t::size12bits; - cnfg.um_nr.UM_Window_Size = 2048; - cnfg.um_nr.mod = 4096; } else { return {}; } diff --git a/lib/include/srsran/interfaces/rrc_interface_types.h b/lib/include/srsran/interfaces/rrc_interface_types.h index 0dd75ace6..21d713037 100644 --- a/lib/include/srsran/interfaces/rrc_interface_types.h +++ b/lib/include/srsran/interfaces/rrc_interface_types.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -109,6 +109,24 @@ struct plmn_id_t { mcc_to_string(mcc_num, &mcc_str); return mcc_str + mnc_str; } + + std::string to_serving_network_name_string() const + { + char buff[50]; + std::string mcc_str, mnc_str; + uint16_t mnc_num, mcc_num; + bytes_to_mnc(&mnc[0], &mnc_num, nof_mnc_digits); + bytes_to_mcc(&mcc[0], &mcc_num); + mnc_to_string(mnc_num, &mnc_str); + mcc_to_string(mcc_num, &mcc_str); + if (mnc_str.size() == 2) { + mnc_str = "0" + mnc_str; + } + snprintf(buff, sizeof(buff), "5G:mnc%s.mcc%s.3gppnetwork.org", mnc_str.c_str(), mcc_str.c_str()); + std::string ssn_s = buff; + return ssn_s; + } + bool operator==(const plmn_id_t& other) const { return std::equal(&mcc[0], &mcc[3], &other.mcc[0]) and nof_mnc_digits == other.nof_mnc_digits and @@ -176,6 +194,49 @@ inline std::string to_string(const scg_failure_cause_t& cause) "nulltype"}; return enum_to_text(options, (uint32_t)scg_failure_cause_t::nulltype, (uint32_t)cause); } + +enum class nr_establishment_cause_t { + emergency, + highPriorityAccess, + mt_Access, + mo_Signalling, + mo_Data, + mo_VoiceCall, + mo_VideoCall, + mo_SMS, + mps_PriorityAccess, + mcs_PriorityAccess, + spare6, + spare5, + spare4, + spare3, + spare2, + spare1, + nulltype +}; +inline std::string to_string(const nr_establishment_cause_t& cause) +{ + constexpr static const char* options[] = { + "emergency", + "highPriorityAccess", + "mt_Access", + "mo_Signalling", + "mo_Data", + "mo_VoiceCall", + "mo_VideoCall", + "mo_SMS", + "mps_PriorityAccess", + "mcs_PriorityAccess", + "spare6", + "spare5", + "spare4", + "spare3", + "spare2", + "spare1", + }; + return enum_to_text(options, (uint32_t)nr_establishment_cause_t::nulltype, (uint32_t)cause); +} + /*************************** * PHY Config **************************/ @@ -346,7 +407,7 @@ inline uint16_t enum_to_number(const pmch_info_t::mch_sched_period_t& mch_period struct mcch_msg_t { uint32_t nof_common_sf_alloc = 0; - mbsfn_sf_cfg_t common_sf_alloc[8]; + mbsfn_sf_cfg_t common_sf_alloc[8] = {}; enum class common_sf_alloc_period_t { rf4, rf8, rf16, rf32, rf64, rf128, rf256, nulltype } common_sf_alloc_period; uint32_t nof_pmch_info; pmch_info_t pmch_info_list[15]; diff --git a/lib/include/srsran/interfaces/rrc_nr_interface_types.h b/lib/include/srsran/interfaces/rrc_nr_interface_types.h deleted file mode 100644 index dafe4debf..000000000 --- a/lib/include/srsran/interfaces/rrc_nr_interface_types.h +++ /dev/null @@ -1,136 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_RRC_NR_INTERFACE_TYPES_H -#define SRSRAN_RRC_NR_INTERFACE_TYPES_H - -#include "srsran/config.h" -#include "srsran/srsran.h" -#include - -namespace srsran { - -/*************************** - * PHY Config - **************************/ - -struct phy_cfg_nr_t { - srsran_tdd_config_nr_t tdd = {}; - srsran_sch_hl_cfg_nr_t pdsch = {}; - srsran_sch_hl_cfg_nr_t pusch = {}; - srsran_pucch_nr_hl_cfg_t pucch = {}; - srsran_prach_cfg_t prach = {}; - srsran_pdcch_cfg_nr_t pdcch = {}; - srsran_ue_dl_nr_harq_ack_cfg_t harq_ack = {}; - srsran_csi_hl_cfg_t csi = {}; - srsran_carrier_nr_t carrier = {}; - - phy_cfg_nr_t() {} - - /** - * @param carrier - */ - srsran_dci_cfg_nr_t get_dci_cfg() const - { - srsran_dci_cfg_nr_t dci_cfg = {}; - - // Assume BWP bandwidth equals full channel bandwidth - dci_cfg.coreset0_bw = pdcch.coreset_present[0] ? srsran_coreset_get_bw(&pdcch.coreset[0]) : 0; - dci_cfg.bwp_dl_initial_bw = carrier.nof_prb; - dci_cfg.bwp_dl_active_bw = carrier.nof_prb; - dci_cfg.bwp_ul_initial_bw = carrier.nof_prb; - dci_cfg.bwp_ul_active_bw = carrier.nof_prb; - - // Iterate over all SS to select monitoring options - for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; i++) { - // Skip not configured SS - if (not pdcch.search_space_present[i]) { - continue; - } - - // Iterate all configured formats - for (uint32_t j = 0; j < pdcch.search_space[i].nof_formats; j++) { - if (pdcch.search_space[i].type == srsran_search_space_type_common_3 && - pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_0) { - dci_cfg.monitor_common_0_0 = true; - } else if (pdcch.search_space[i].type == srsran_search_space_type_ue && - pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_0) { - dci_cfg.monitor_0_0_and_1_0 = true; - } else if (pdcch.search_space[i].type == srsran_search_space_type_ue && - pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_1) { - dci_cfg.monitor_0_1_and_1_1 = true; - } - } - } - - // Set PUSCH parameters - dci_cfg.enable_sul = false; - dci_cfg.enable_hopping = false; - - // Set Format 0_1 and 1_1 parameters - dci_cfg.carrier_indicator_size = 0; - dci_cfg.harq_ack_codebok = harq_ack.harq_ack_codebook; - dci_cfg.nof_rb_groups = 0; - - // Format 0_1 specific configuration (for PUSCH only) - dci_cfg.nof_ul_bwp = 0; - dci_cfg.nof_ul_time_res = (pusch.nof_dedicated_time_ra > 0) - ? pusch.nof_dedicated_time_ra - : (pusch.nof_common_time_ra > 0) ? pusch.nof_common_time_ra : SRSRAN_MAX_NOF_TIME_RA; - dci_cfg.nof_srs = 1; - dci_cfg.nof_ul_layers = 1; - dci_cfg.pusch_nof_cbg = 0; - dci_cfg.report_trigger_size = 0; - dci_cfg.enable_transform_precoding = false; - dci_cfg.dynamic_dual_harq_ack_codebook = false; - dci_cfg.pusch_tx_config_non_codebook = false; - dci_cfg.pusch_dmrs_type2 = false; - dci_cfg.pusch_dmrs_double = false; - dci_cfg.pusch_ptrs = false; - dci_cfg.pusch_dynamic_betas = false; - dci_cfg.pusch_alloc_type = pusch.alloc; - - // Format 1_1 specific configuration (for PDSCH only) - dci_cfg.nof_dl_bwp = 0; - dci_cfg.nof_dl_time_res = (pdsch.nof_dedicated_time_ra > 0) - ? pdsch.nof_dedicated_time_ra - : (pdsch.nof_common_time_ra > 0) ? pdsch.nof_common_time_ra : SRSRAN_MAX_NOF_TIME_RA; - dci_cfg.nof_aperiodic_zp = 0; - dci_cfg.pdsch_nof_cbg = 0; - dci_cfg.nof_dl_to_ul_ack = harq_ack.nof_dl_data_to_ul_ack; - dci_cfg.pdsch_inter_prb_to_prb = false; - dci_cfg.pdsch_rm_pattern1 = false; - dci_cfg.pdsch_rm_pattern2 = false; - dci_cfg.pdsch_2cw = false; - dci_cfg.multiple_scell = false; - dci_cfg.pdsch_dmrs_type2 = false; - dci_cfg.pdsch_dmrs_double = false; - dci_cfg.pdsch_tci = false; - dci_cfg.pdsch_cbg_flush = false; - dci_cfg.pdsch_dynamic_bundling = false; - dci_cfg.pdsch_alloc_type = pdsch.alloc; - - return dci_cfg; - }; -}; -} // namespace srsran - -#endif // SRSRAN_RRC_NR_INTERFACE_TYPES_H diff --git a/lib/include/srsran/interfaces/ue_gw_interfaces.h b/lib/include/srsran/interfaces/ue_gw_interfaces.h index 363c46745..baf10cd03 100644 --- a/lib/include/srsran/interfaces/ue_gw_interfaces.h +++ b/lib/include/srsran/interfaces/ue_gw_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -30,14 +30,24 @@ namespace srsue { class gw_interface_nas { public: - virtual int setup_if_addr(uint32_t eps_bearer_id, - uint32_t lcid, - uint8_t pdn_type, - uint32_t ip_addr, - uint8_t* ipv6_if_id, - char* err_str) = 0; + /** + * Informs GW about new EPS default bearer being created after attach accept + * along with the assigned IP address. + */ + virtual int + setup_if_addr(uint32_t eps_bearer_id, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) = 0; + + /** + * Inform GW about the deactivation of a EPS bearer, e.g. during + * detach + */ + virtual int deactivate_eps_bearer(const uint32_t eps_bearer_id) = 0; + + /** + * Informs GW about new traffic flow templates and their associated EPS bearer ID + * All TFT are applied to a dedicated EPS bearer that has a linked default bearer + */ virtual int apply_traffic_flow_template(const uint8_t& eps_bearer_id, - const uint8_t& lcid, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) = 0; typedef enum { @@ -59,7 +69,6 @@ class gw_interface_rrc { public: virtual void add_mch_port(uint32_t lcid, uint32_t port) = 0; - virtual int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid) = 0; virtual bool is_running() = 0; }; diff --git a/lib/include/srsran/interfaces/ue_interfaces.h b/lib/include/srsran/interfaces/ue_interfaces.h index fe77a5d6d..e121886bd 100644 --- a/lib/include/srsran/interfaces/ue_interfaces.h +++ b/lib/include/srsran/interfaces/ue_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,10 @@ namespace srsue { class stack_interface_rrc { public: - virtual srsran::tti_point get_current_tti() = 0; + virtual srsran::tti_point get_current_tti() = 0; + virtual void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) = 0; + virtual void remove_eps_bearer(uint8_t eps_bearer_id) = 0; + virtual void reset_eps_bearers() = 0; }; // Combined interface for PHY to access stack (MAC and RRC) diff --git a/lib/include/srsran/interfaces/ue_mac_interfaces.h b/lib/include/srsran/interfaces/ue_mac_interfaces.h index ad6c6062d..0ba7ac57b 100644 --- a/lib/include/srsran/interfaces/ue_mac_interfaces.h +++ b/lib/include/srsran/interfaces/ue_mac_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,6 +45,9 @@ public: uint32_t pid; uint16_t rnti; bool is_sps_release; + bool is_pdcch_order; + uint32_t preamble_idx; + uint32_t prach_mask_idx; uint32_t tti; } mac_grant_dl_t; @@ -100,30 +103,13 @@ public: virtual void bch_decoded_ok(uint32_t cc_idx, uint8_t* payload, uint32_t len) = 0; /* Indicate successful decoding of MCH TB through PMCH */ - virtual void mch_decoded(uint32_t len, bool crc) = 0; - - /* Obtain action for a new MCH subframe. */ - virtual void new_mch_dl(const srsran_pdsch_grant_t& phy_grant, tb_action_dl_t* action) = 0; + virtual void mch_decoded(uint32_t len, bool crc, uint8_t* payload) = 0; /* Communicate the number of mbsfn services available */ virtual void set_mbsfn_config(uint32_t nof_mbsfn_services) = 0; }; -class mac_interface_rrc_common -{ -public: - // Class to handle UE specific RNTIs between RRC and MAC - typedef struct { - uint16_t crnti; - uint16_t rar_rnti; - uint16_t temp_rnti; - uint16_t tpc_rnti; - uint16_t sps_rnti; - uint64_t contention_id; - } ue_rnti_t; -}; - -class mac_interface_rrc : public mac_interface_rrc_common +class mac_interface_rrc { public: /* Instructs the MAC to start receiving BCCH */ @@ -147,9 +133,9 @@ public: virtual void set_rach_ded_cfg(uint32_t preamble_index, uint32_t prach_mask) = 0; - virtual void get_rntis(ue_rnti_t* rntis) = 0; - virtual void set_contention_id(uint64_t uecri) = 0; - virtual void set_ho_rnti(uint16_t crnti, uint16_t target_pci) = 0; + virtual uint16_t get_crnti() = 0; + virtual void set_contention_id(uint64_t uecri) = 0; + virtual void set_ho_rnti(uint16_t crnti, uint16_t target_pci) = 0; virtual void reconfiguration(const uint32_t& cc_idx, const bool& enable) = 0; virtual void reset() = 0; diff --git a/lib/include/srsran/interfaces/ue_nas_interfaces.h b/lib/include/srsran/interfaces/ue_nas_interfaces.h index 15f02819d..133fef6ca 100644 --- a/lib/include/srsran/interfaces/ue_nas_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nas_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,10 +22,26 @@ #ifndef SRSRAN_UE_NAS_INTERFACES_H #define SRSRAN_UE_NAS_INTERFACES_H +#include "srsran/asn1/nas_5g_ies.h" #include "srsran/interfaces/rrc_interface_types.h" namespace srsue { +enum apn_types { + ipv4 = 0b001, + ipv6 = 0b010, + ipv4v6 = 0b011, + unstructured = 0b100, + ethernet = 0b101, +}; +class pdu_session_cfg_t +{ +public: + std::string apn_name; + apn_types apn_type = ipv4; + std::string apn_user; + std::string apn_pass; +}; class nas_interface_rrc { public: @@ -48,6 +64,27 @@ public: virtual bool connection_request_completed(bool outcome) = 0; }; +class nas_5g_interface_rrc_nr +{ +public: + virtual int write_pdu(srsran::unique_byte_buffer_t pdu) = 0; + virtual int get_k_amf(srsran::as_key_t& k_amf) = 0; + virtual uint32_t get_ul_nas_count() = 0; +}; + +class nas_5g_interface_procedures +{ +public: + virtual int send_registration_request() = 0; + virtual int send_pdu_session_establishment_request(uint32_t transaction_identity, + uint16_t pdu_session_id, + const pdu_session_cfg_t& pdu_session) = 0; + virtual int + add_pdu_session(uint16_t pdu_session_id, uint16_t pdu_session_type, srsran::nas_5g::pdu_address_t pdu_address) = 0; + + virtual uint32_t allocate_next_proc_trans_id() = 0; +}; + } // namespace srsue #endif // SRSRAN_UE_NAS_INTERFACES_H diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index 29bc73f67..0b8736fac 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,8 +23,8 @@ #define SRSRAN_UE_NR_INTERFACES_H #include "srsran/common/interfaces_common.h" +#include "srsran/common/phy_cfg_nr.h" #include "srsran/interfaces/mac_interface_types.h" -#include "srsran/interfaces/rrc_nr_interface_types.h" #include #include #include @@ -34,9 +34,43 @@ namespace srsue { class rrc_interface_phy_nr { public: - virtual void in_sync() = 0; - virtual void out_of_sync() = 0; - virtual void run_tti(const uint32_t tti) = 0; + virtual void in_sync() = 0; + virtual void out_of_sync() = 0; + virtual void set_phy_config_complete(bool status) = 0; + + /** + * @brief Describes a cell search result + */ + struct cell_search_result_t { + bool cell_found = false; + uint32_t ssb_arfcn = 0; ///< SSB center ARFCN + uint32_t pci = 0; ///< Physical Cell Identifier + srsran_pbch_msg_nr_t pbch_msg = {}; ///< Packed PBCH message for the upper layers + srsran_csi_trs_measurements_t measurements = {}; ///< Measurements from SSB block + }; + + /** + * @brief Informs RRC about cell search process completion + * @param result Cell search result completion + */ + virtual void cell_search_found_cell(const cell_search_result_t& result) = 0; + + /** + * @brief Describes a cell select result + */ + struct cell_select_result_t { + enum { + ERROR = 0, ///< The cell selection procedure failed due a to an invalid configuration + UNSUCCESSFUL, ///< The cell selection failed to find and synchronise the SSB + SUCCESSFUL, ///< The cell selection was successful, resulting in a camping state + } status; + }; + + /** + * @brief Informs RRC about cell select process completion + * @param result Cell select result completion + */ + virtual void cell_select_completed(const cell_select_result_t& result) = 0; }; class mac_interface_phy_nr @@ -63,8 +97,9 @@ public: } mac_nr_grant_dl_t; typedef struct { - srsran::unique_byte_buffer_t payload; // TB when decoded successfully, nullptr otherwise - bool ack; // HARQ information + uint32_t rx_slot_idx; // Slot when DL TB has been decoded + srsran::unique_byte_buffer_t payload; // TB when decoded successfully, nullptr otherwise + bool ack; // HARQ information } tb_action_dl_result_t; // UL grant as conveyed between PHY and MAC @@ -91,8 +126,6 @@ public: tb_ul_t tb; // only single TB in UL } tb_action_ul_t; - virtual int sf_indication(const uint32_t tti) = 0; ///< TODO: rename to slot indication - // Query the MAC for the current RNTI to look for struct sched_rnti_t { uint16_t id; @@ -154,12 +187,13 @@ public: class mac_interface_rrc_nr { public: + virtual void reset() = 0; // Config calls that return SRSRAN_SUCCESS or SRSRAN_ERROR virtual int setup_lcid(const srsran::logical_channel_config_t& config) = 0; virtual int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) = 0; virtual int set_config(const srsran::sr_cfg_nr_t& sr_cfg) = 0; virtual int set_config(const srsran::dl_harq_cfg_nr_t& dl_hrq_cfg) = 0; - virtual void set_config(const srsran::rach_nr_cfg_t& rach_cfg) = 0; + virtual void set_config(const srsran::rach_cfg_nr_t& rach_cfg_nr) = 0; virtual int add_tag_config(const srsran::tag_cfg_nr_t& tag_cfg) = 0; virtual int set_config(const srsran::phr_cfg_nr_t& phr_cfg) = 0; virtual int remove_tag_config(const uint32_t tag_id) = 0; @@ -172,18 +206,31 @@ public: // RRC informs MAC about new UE identity for contention-free RA virtual bool set_crnti(const uint16_t crnti) = 0; + + // RRC informs MAC to start/stop search for BCCH messages + virtual void bcch_search(bool enabled) = 0; }; struct phy_args_nr_t { - uint32_t nof_carriers = 1; - uint32_t max_nof_prb = 106; - uint32_t nof_phy_threads = 3; - uint32_t worker_cpu_mask = 0; - srsran::phy_log_args_t log = {}; - srsran_ue_dl_nr_args_t dl = {}; - srsran_ue_ul_nr_args_t ul = {}; - std::set fixed_sr = {1}; - uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report + uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill + uint32_t nof_carriers = 1; + uint32_t max_nof_prb = 106; + double srate_hz = 23.04e6; + uint32_t nof_phy_threads = 3; + uint32_t worker_cpu_mask = 0; + int slot_recv_thread_prio = 0; /// Specifies the slot receive thread priority, RT by default + int workers_thread_prio = 2; /// Specifies the workers thread priority, RT by default + srsran::phy_log_args_t log = {}; + srsran_ue_dl_nr_args_t dl = {}; + srsran_ue_ul_nr_args_t ul = {}; + std::set fixed_sr = {1}; + uint32_t fix_wideband_cqi = 15; ///< Set to a non-zero value for fixing the wide-band CQI report + bool store_pdsch_ko = false; + float trs_epre_ema_alpha = 0.1f; ///< EPRE measurement exponential average alpha + float trs_rsrp_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha + float trs_sinr_ema_alpha = 0.1f; ///< SINR measurement exponential average alpha + float trs_cfo_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha + bool enable_worker_cfo = true; ///< Enable/Disable open loop CFO correction at the workers phy_args_nr_t() { @@ -208,19 +255,11 @@ struct phy_args_nr_t { class phy_interface_mac_nr { public: - typedef struct { - uint32_t tti; - uint32_t tb_len; - uint8_t* data; // always a pointer in our case - } tx_request_t; - // MAC informs PHY about UL grant included in RAR PDU - virtual int set_ul_grant(std::array packed_ul_grant, - uint16_t rnti, - srsran_rnti_type_t rnti_type) = 0; - - // MAC instructs PHY to transmit MAC TB at the given TTI - virtual int tx_request(const tx_request_t& request) = 0; + virtual int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) = 0; /// Instruct PHY to send PRACH in the next occasion. virtual void send_prach(const uint32_t prach_occasion, @@ -228,6 +267,12 @@ public: const float preamble_received_target_power, const float ta_base_sec = 0.0f) = 0; + /// Apply TA command after RAR + virtual void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) = 0; + + /// Apply TA command after MAC CE + virtual void set_timeadv(uint32_t tti, uint32_t ta_cmd) = 0; + /** * @brief Query PHY if there is a valid PUCCH SR resource configured for a given SR identifier * @param sr_id SR identifier @@ -245,13 +290,69 @@ class phy_interface_rrc_nr { public: virtual bool set_config(const srsran::phy_cfg_nr_t& cfg) = 0; + + /** + * @brief Describe the possible NR standalone physical layer possible states + */ + typedef enum { + PHY_NR_STATE_IDLE = 0, ///< There is no process going on + PHY_NR_STATE_CELL_SEARCH, ///< Cell search is currently in progress + PHY_NR_STATE_CELL_SELECT, ///< Cell selection is in progress or it is camped on a cell + PHY_NR_STATE_CAMPING + } phy_nr_state_t; + + /** + * @brief Retrieves the physical layer state + * @return + */ + virtual phy_nr_state_t get_state() = 0; + + /** + * @brief Stops the ongoing process and transitions to IDLE + */ + virtual void reset_nr() = 0; + + /** + * @brief Describes cell search arguments + */ + struct cell_search_args_t { + double center_freq_hz; + double ssb_freq_hz; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_pattern_t ssb_pattern; + srsran_duplex_mode_t duplex_mode; + }; + + /** + * @brief Start cell search + * @param args Cell Search arguments + * @return true if the physical layer started successfully the cell search process + */ + virtual bool start_cell_search(const cell_search_args_t& req) = 0; + + /** + * @brief Describes cell select arguments + */ + struct cell_select_args_t { + srsran_ssb_cfg_t ssb_cfg; + srsran_carrier_nr_t carrier; + }; + + /** + * @brief Start cell search + * @param args Cell Search arguments + * @return true if the physical layer started successfully the cell search process + */ + virtual bool start_cell_select(const cell_select_args_t& req) = 0; }; // Combined interface for PHY to access stack (MAC and RRC) -class stack_interface_phy_nr : public mac_interface_phy_nr, - public rrc_interface_phy_nr, - public srsran::stack_interface_phy_nr -{}; +class stack_interface_phy_nr : public mac_interface_phy_nr, public rrc_interface_phy_nr +{ +public: + /* Indicate new TTI */ + virtual void run_tti(const uint32_t tti, const uint32_t tti_jump) = 0; +}; // Combined interface for stack (MAC and RRC) to access PHY class phy_interface_stack_nr : public phy_interface_mac_nr, public phy_interface_rrc_nr diff --git a/lib/include/srsran/interfaces/ue_pdcp_interfaces.h b/lib/include/srsran/interfaces/ue_pdcp_interfaces.h index 5a8979e7f..c9f581dea 100644 --- a/lib/include/srsran/interfaces/ue_pdcp_interfaces.h +++ b/lib/include/srsran/interfaces/ue_pdcp_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,8 +33,9 @@ public: virtual void reestablish() = 0; virtual void reestablish(uint32_t lcid) = 0; virtual void reset() = 0; + virtual void set_enabled(uint32_t lcid, bool enabled) = 0; virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu, int sn = -1) = 0; - virtual void add_bearer(uint32_t lcid, srsran::pdcp_config_t cnfg) = 0; + virtual int add_bearer(uint32_t lcid, const srsran::pdcp_config_t& cnfg) = 0; virtual void del_bearer(uint32_t lcid) = 0; virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0; virtual void config_security(uint32_t lcid, const srsran::as_security_config_t& sec_cfg) = 0; @@ -59,19 +60,30 @@ public: virtual void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn) = 0; }; -class pdcp_interface_gw +// Data-plane interface for Stack after EPS bearer to LCID conversion +class pdcp_interface_stack { public: - virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; - virtual bool is_lcid_enabled(uint32_t lcid) = 0; + virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu, int sn = -1) = 0; + virtual bool is_lcid_enabled(uint32_t lcid) = 0; }; -// STACK interface for GW -class stack_interface_gw : public pdcp_interface_gw +// SDAP interface +class pdcp_interface_sdap_nr +{ +public: + virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; +}; + +// STACK interface for GW (based on EPS-bearer IDs) +class stack_interface_gw { public: virtual bool is_registered() = 0; virtual bool start_service_request() = 0; + virtual void write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu) = 0; + ///< Allow GW to query if a radio bearer for a given EPS bearer ID is currently active + virtual bool has_active_radio_bearer(uint32_t eps_bearer_id) = 0; }; } // namespace srsue diff --git a/lib/include/srsran/interfaces/ue_phy_interfaces.h b/lib/include/srsran/interfaces/ue_phy_interfaces.h index b578bfa0e..daf33ba46 100644 --- a/lib/include/srsran/interfaces/ue_phy_interfaces.h +++ b/lib/include/srsran/interfaces/ue_phy_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,6 +35,15 @@ namespace srsue { +struct cfr_args_t { + bool enable = false; + srsran_cfr_mode_t mode = SRSRAN_CFR_THR_MANUAL; + float manual_thres = 2.0f; + float strength = 1.0f; + float auto_target_papr = 7.0f; + float ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB; +}; + struct phy_args_t { std::string type = "lte"; srsran::phy_log_args_t log; @@ -45,6 +54,7 @@ struct phy_args_t { std::map ul_earfcn_map; // Map linking DL EARFCN and UL EARFCN int force_N_id_2 = -1; // Cell identity within the identity group (PSS) to filter. + int force_N_id_1 = -1; // Cell identity group (SSS) to filter. float dl_freq = -1.0f; float ul_freq = -1.0f; @@ -60,7 +70,7 @@ struct phy_args_t { uint32_t nof_lte_carriers = 1; uint32_t nof_nr_carriers = 0; - uint32_t nr_max_nof_prb = 106; + uint32_t nr_max_nof_prb = 52; uint32_t nof_rx_ant = 1; std::string equalizer_mode = "mmse"; int cqi_max = 15; @@ -93,6 +103,9 @@ struct phy_args_t { uint32_t intra_freq_meas_len_ms = 20; uint32_t intra_freq_meas_period_ms = 200; float force_ul_amplitude = 0.0f; + bool detect_cp = false; + + bool nr_store_pdsch_ko = false; float in_sync_rsrp_dbm_th = -130.0f; float in_sync_snr_db_th = 1.0f; @@ -102,7 +115,7 @@ struct phy_args_t { srsran::channel::args_t dl_channel_args; srsran::channel::args_t ul_channel_args; - srsran::vnf_args_t vnf_args; + cfr_args_t cfr_args; ///< Stores user-defined CFR configuration }; /* RAT agnostic Interface MAC -> PHY */ @@ -137,8 +150,8 @@ public: } prach_info_t; virtual void - prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec = 0.0f) = 0; - virtual prach_info_t prach_get_info() = 0; + prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec = 0.0f) = 0; + virtual prach_info_t prach_get_info() = 0; /* Indicates the transmission of a SR signal in the next opportunity */ virtual void sr_send() = 0; @@ -164,11 +177,9 @@ public: virtual void meas_stop() = 0; /* Cell search and selection procedures */ - virtual bool cell_search() = 0; + virtual bool cell_search(int earfcn) = 0; virtual bool cell_select(phy_cell_t cell) = 0; virtual bool cell_is_camping() = 0; - - virtual void enable_pregen_signals(bool enable) = 0; }; // Combined interface for stack (MAC and RRC) to access PHY diff --git a/lib/include/srsran/interfaces/ue_rlc_interfaces.h b/lib/include/srsran/interfaces/ue_rlc_interfaces.h index 42afff8bf..ef886496d 100644 --- a/lib/include/srsran/interfaces/ue_rlc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rlc_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,8 +33,8 @@ public: virtual void reset() = 0; virtual void reestablish() = 0; virtual void reestablish(uint32_t lcid) = 0; - virtual void add_bearer(uint32_t lcid, const srsran::rlc_config_t& cnfg) = 0; - virtual void add_bearer_mrb(uint32_t lcid) = 0; + virtual int add_bearer(uint32_t lcid, const srsran::rlc_config_t& cnfg) = 0; + virtual int add_bearer_mrb(uint32_t lcid) = 0; virtual void del_bearer(uint32_t lcid) = 0; virtual void suspend_bearer(uint32_t lcid) = 0; virtual void resume_bearer(uint32_t lcid) = 0; @@ -60,6 +60,8 @@ public: ///< Allow PDCP to query SDU queue status virtual bool sdu_queue_is_full(uint32_t lcid) = 0; + + virtual bool is_suspended(const uint32_t lcid) = 0; }; class rlc_interface_mac : public srsran::read_pdu_interface @@ -76,7 +78,7 @@ public: /* MAC calls RLC to get RLC segment of nof_bytes length. * Segmentation happens in this function. RLC PDU is stored in payload. */ - virtual int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; + virtual uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; /* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread. * PDU gets placed into the buffer and higher layer thread gets notified. */ diff --git a/lib/include/srsran/interfaces/ue_rrc_interfaces.h b/lib/include/srsran/interfaces/ue_rrc_interfaces.h index d2923c2b8..34026b027 100644 --- a/lib/include/srsran/interfaces/ue_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rrc_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,6 +25,7 @@ #include "phy_interface_types.h" #include "rrc_interface_types.h" #include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/rrc_nr.h" #include "srsran/common/byte_buffer.h" #include "srsran/common/tti_point.h" @@ -81,7 +82,6 @@ public: virtual bool is_connected() = 0; virtual void paging_completed(bool outcome) = 0; virtual const char* get_rb_name(uint32_t lcid) = 0; - virtual uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) = 0; virtual bool has_nr_dc() = 0; }; @@ -93,6 +93,7 @@ public: virtual void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) = 0; virtual void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) = 0; virtual void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; + virtual void notify_pdcp_integrity_error(uint32_t lcid) = 0; virtual const char* get_rb_name(uint32_t lcid) = 0; }; @@ -100,6 +101,7 @@ class rrc_interface_rlc { public: virtual void max_retx_attempted() = 0; + virtual void protocol_failure() = 0; virtual const char* get_rb_name(uint32_t lcid) = 0; virtual void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; }; @@ -107,20 +109,25 @@ public: class rrc_nr_interface_rrc { public: - virtual void get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps) = 0; - virtual void get_nr_capabilities(srsran::byte_buffer_t* nr_cap) = 0; - virtual void phy_set_cells_to_meas(uint32_t carrier_freq_r15) = 0; - virtual void phy_meas_stop() = 0; - virtual bool rrc_reconfiguration(bool endc_release_and_add_r15, - bool nr_secondary_cell_group_cfg_r15_present, - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - bool sk_counter_r15_present, - uint32_t sk_counter_r15, - bool nr_radio_bearer_cfg1_r15_present, - asn1::dyn_octstring nr_radio_bearer_cfg1_r15) = 0; - virtual bool is_config_pending() = 0; + virtual int get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps) = 0; + virtual int get_nr_capabilities(srsran::byte_buffer_t* nr_cap) = 0; + virtual void phy_set_cells_to_meas(uint32_t carrier_freq_r15) = 0; + virtual void phy_meas_stop() = 0; + virtual bool rrc_reconfiguration(bool endc_release_and_add_r15, const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf) = 0; + virtual void rrc_release() = 0; + virtual bool is_config_pending() = 0; }; +class rrc_nr_interface_nas_5g +{ +public: + virtual ~rrc_nr_interface_nas_5g() = default; + virtual int write_sdu(srsran::unique_byte_buffer_t sdu) = 0; + virtual bool is_connected() = 0; + virtual int connection_request(srsran::nr_establishment_cause_t cause, srsran::unique_byte_buffer_t sdu) = 0; + virtual uint16_t get_mcc() = 0; + virtual uint16_t get_mnc() = 0; +}; } // namespace srsue #endif // SRSRAN_UE_RRC_INTERFACES_H diff --git a/lib/include/srsran/interfaces/ue_sdap_interfaces.h b/lib/include/srsran/interfaces/ue_sdap_interfaces.h new file mode 100644 index 000000000..2dd858cc4 --- /dev/null +++ b/lib/include/srsran/interfaces/ue_sdap_interfaces.h @@ -0,0 +1,56 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/****************************************************************************** + * File: ue_sdap_interfaces.h + * Description: Abstract base class interfaces for SDAP layer + *****************************************************************************/ + +#ifndef SRSRAN_UE_SDAP_INTERFACES_H +#define SRSRAN_UE_SDAP_INTERFACES_H + +/***************************** + * SDAP INTERFACES + ****************************/ +class sdap_interface_pdcp_nr +{ +public: + virtual void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; +}; +class sdap_interface_gw_nr +{ +public: + virtual void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) = 0; +}; + +class sdap_interface_rrc +{ +public: + struct bearer_cfg_t { + bool is_data; + bool add_downlink_header; + bool add_uplink_header; + uint32_t qfi; + }; + virtual bool set_bearer_cfg(uint32_t lcid, const bearer_cfg_t& cfg) = 0; +}; + +#endif // SRSRAN_UE_SDAP_INTERFACES_H diff --git a/lib/include/srsran/interfaces/ue_usim_interfaces.h b/lib/include/srsran/interfaces/ue_usim_interfaces.h index 30eeabaa0..44e233762 100644 --- a/lib/include/srsran/interfaces/ue_usim_interfaces.h +++ b/lib/include/srsran/interfaces/ue_usim_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,23 +33,44 @@ enum auth_result_t { AUTH_OK, AUTH_FAILED, AUTH_SYNCH_FAILURE }; class usim_interface_nas { public: - virtual std::string get_imsi_str() = 0; - virtual std::string get_imei_str() = 0; - virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0; - virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0; - virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0; + virtual std::string get_imsi_str() = 0; + virtual std::string get_imei_str() = 0; + virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0; + virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0; + virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0; + // Get the home mcc as bytes array + virtual bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) = 0; + // Get the home mnc as byte array + virtual bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) = 0; + // Get the home msin in bytes array encoded as bcd + virtual bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) = 0; virtual auth_result_t generate_authentication_response(uint8_t* rand, uint8_t* autn_enb, uint16_t mcc, uint16_t mnc, uint8_t* res, int* res_len, - uint8_t* k_asme) = 0; - virtual void generate_nas_keys(uint8_t* k_asme, - uint8_t* k_nas_enc, - uint8_t* k_nas_int, - srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, - srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; + uint8_t* k_asme) = 0; + + virtual auth_result_t generate_authentication_response_5g(uint8_t* rand, + uint8_t* autn_enb, + const char* serving_network_name, + uint8_t* abba, + uint32_t abba_len, + uint8_t* res_star, + uint8_t* k_amf) = 0; + + virtual void generate_nas_keys(uint8_t* k_asme, + uint8_t* k_nas_enc, + uint8_t* k_nas_int, + srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; + + virtual bool generate_nas_keys_5g(uint8_t* k_amf, + uint8_t* k_nas_enc, + uint8_t* k_nas_int, + srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) = 0; }; // USIM interface for RRC @@ -65,8 +86,10 @@ public: class usim_interface_rrc_nr { public: - virtual bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) = 0; - virtual bool update_nr_context(srsran::as_security_config_t* sec_cfg) = 0; + virtual void + generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) = 0; + virtual bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) = 0; + virtual bool update_nr_context(srsran::as_security_config_t* sec_cfg) = 0; }; } // namespace srsue diff --git a/lib/include/srsran/mac/bsr_nr.h b/lib/include/srsran/mac/bsr_nr.h new file mode 100644 index 000000000..1df1326f7 --- /dev/null +++ b/lib/include/srsran/mac/bsr_nr.h @@ -0,0 +1,102 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_BSR_NR_H +#define SRSRAN_BSR_NR_H + +#include + +/** + * @brief Buffer size format definition and levels according to 3GPP TS 38.321 version 15.3.0 + * + * Shared between UE and gNB MAC layers + */ + +namespace srsran { + +// TS 38.321 Sec 6.1.3.1 +typedef enum { SHORT_BSR, LONG_BSR, SHORT_TRUNC_BSR, LONG_TRUNC_BSR } bsr_format_nr_t; + +// TS 38.321, Table 6.1.3.1-1 Buffer size levels (in bytes) for 5-bit Buffer Size field, all values <= except marked +static const uint32_t buffer_size_levels_5bit_max_idx = 31; +static uint32_t buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx + 1] = { + /* == */ 0, 10, 14, 20, 28, 38, 53, 74, 102, 142, 198, + 276, 384, 535, 745, 1038, 1446, 2014, 2806, 3909, 5446, 7587, + 10570, 14726, 20516, 28581, 39818, 55474, 77284, 107669, 150000, /* > */ 150000}; + +// TS 38.321, Table 6.1.3.1-2: Buffer size levels (in bytes) for 8-bit Buffer Size field, all values <= except marked +static const uint32_t buffer_size_levels_8bit_max_idx = 254; +static uint32_t buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx + 1] = { + /* == */ 0, 10, 11, 12, 13, + 14, 15, 16, 17, 18, + 19, 20, 22, 23, 25, + 26, 28, 30, 32, 34, + 36, 38, 40, 43, 46, + 49, 52, 55, 59, 62, + 66, 71, 75, 80, 85, + 91, 97, 103, 110, 117, + 124, 132, 141, 150, 160, + 170, 181, 193, 205, 218, + 233, 248, 264, 281, 299, + 318, 339, 361, 384, 409, + 436, 464, 494, 526, 560, + 597, 635, 677, 720, 767, + 817, 870, 926, 987, 1051, + 1119, 1191, 1269, 1351, 1439, + 1532, 1631, 1737, 1850, 1970, + 2098, 2234, 2379, 2533, 2698, + 2873, 3059, 3258, 3469, 3694, + 3934, 4189, 4461, 4751, 5059, + 5387, 5737, 6109, 6506, 6928, + 7378, 7857, 8367, 8910, 9488, + 10104, 10760, 11458, 12202, 12994, + 13838, 14736, 15692, 16711, 17795, + 18951, 20181, 21491, 22885, 24371, + 25953, 27638, 29431, 31342, 33376, + 35543, 37850, 40307, 42923, 45709, + 48676, 51836, 55200, 58784, 62599, + 66663, 70990, 75598, 80505, 85730, + 91295, 97221, 103532, 110252, 117409, + 125030, 133146, 141789, 150992, 160793, + 171231, 182345, 194182, 206786, 220209, + 234503, 249725, 265935, 283197, 301579, + 321155, 342002, 364202, 387842, 413018, + 439827, 468377, 498780, 531156, 565634, + 602350, 641449, 683087, 727427, 774645, + 824928, 878475, 935498, 996222, 1060888, + 1129752, 1203085, 1281179, 1364342, 1452903, + 1547213, 1647644, 1754595, 1868488, 1989774, + 2118933, 2256475, 2402946, 2558924, 2725027, + 2901912, 3090279, 3290873, 3504487, 3731968, + 3974215, 4232186, 4506902, 4799451, 5110989, + 5442750, 5796046, 6172275, 6572925, 6999582, + 7453933, 7937777, 8453028, 9001725, 9586039, + 10208280, 10870913, 11576557, 12328006, 13128233, + 13980403, 14887889, 15854280, 16883401, 17979324, + 19146385, 20389201, 21712690, 23122088, 24622972, + 26221280, 27923336, 29735875, 31666069, 33721553, + 35910462, 38241455, 40723756, 43367187, 46182206, + 49179951, 52372284, 55771835, 59392055, 63247269, + 67352729, 71724679, 76380419, 81338368, /* > */ 81338368}; + +} // namespace srsran + +#endif // SRSRAN_BSR_NR_H diff --git a/lib/include/srsran/mac/mac_rar_pdu_nr.h b/lib/include/srsran/mac/mac_rar_pdu_nr.h index 38315ff63..1a0a0572e 100644 --- a/lib/include/srsran/mac/mac_rar_pdu_nr.h +++ b/lib/include/srsran/mac/mac_rar_pdu_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #ifndef SRSRAN_MAC_RAR_PDU_NR_H #define SRSRAN_MAC_RAR_PDU_NR_H +#include "srsran/common/byte_buffer.h" #include "srsran/common/common.h" #include "srsran/config.h" #include "srsran/phy/common/phy_common_nr.h" @@ -60,12 +61,19 @@ public: uint8_t get_backoff() const; // setter - uint32_t write_subpdu(const uint8_t* start_); - void set_backoff(const uint8_t backoff_indicator_); + void write_subpdu(const uint8_t* start_); + void set_backoff(const uint8_t backoff_indicator_); + void set_ta(const uint32_t ta_); + void set_temp_crnti(const uint16_t temp_crnti_); + void set_rapid(const uint8_t rapid_); + void set_ul_grant(std::array ul_grant_); + void set_is_last_subpdu(); - std::string to_string(); + void to_string(fmt::memory_buffer& buffer); private: + const uint32_t MAC_RAR_NBYTES = 7; // see TS 38.321 Sec 6.2.3 + int header_length = 1; // RAR PDU subheader is always 1 B int payload_length = 0; // only used if MAC RAR is included @@ -75,7 +83,7 @@ private: uint16_t rapid = 0; uint8_t backoff_indicator = 0; rar_subh_type_t type = BACKOFF; - bool E_bit = 0; + bool E_bit = true; srslog::basic_logger& logger; @@ -88,7 +96,8 @@ public: mac_rar_pdu_nr(); ~mac_rar_pdu_nr() = default; - bool pack(); + int init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_); + int pack(); bool unpack(const uint8_t* payload, const uint32_t& len); uint32_t get_num_subpdus(); // Returns reference to a single subPDU @@ -101,13 +110,20 @@ public: void set_si_rapid(uint16_t si_rapid_); // configured through SIB1 for on-demand SI request (See 38.331 Sec 5.2.1) bool has_si_rapid(); - std::string to_string(); + // returns reference to added RAR subPDU + mac_rar_subpdu_nr& add_subpdu(); + + void to_string(fmt::memory_buffer& buffer); private: std::vector subpdus; + uint32_t pdu_len = 0; uint32_t remaining_len = 0; + uint16_t si_rapid = 0; bool si_rapid_set = false; + + byte_buffer_t* buffer = nullptr; srslog::basic_logger& logger; }; diff --git a/lib/include/srsran/mac/mac_sch_pdu_nr.h b/lib/include/srsran/mac/mac_sch_pdu_nr.h index 9f6f2f087..18c0ef675 100644 --- a/lib/include/srsran/mac/mac_sch_pdu_nr.h +++ b/lib/include/srsran/mac/mac_sch_pdu_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,7 +38,7 @@ class mac_sch_subpdu_nr { public: // 3GPP 38.321 v15.3.0 Combined Tables 6.2.1-1, 6.2.1-2 - typedef enum { + enum nr_lcid_sch_t { // Values for DL-SCH CCCH = 0b000000, DRX_CMD = 0b111100, @@ -58,7 +58,7 @@ public: // Common PADDING = 0b111111, - } nr_lcid_sch_t; + }; // SDUs up to 256 B can use the short 8-bit L field static const int32_t MAC_SUBHEADER_LEN_THRESHOLD = 256; @@ -66,17 +66,18 @@ public: mac_sch_subpdu_nr(mac_sch_pdu_nr* parent_) : parent(parent_), logger(&srslog::fetch_basic_logger("MAC-NR")){}; nr_lcid_sch_t get_type(); - bool is_sdu(); + bool is_sdu() const; bool is_valid_lcid(); - bool is_var_len_ce(); - bool is_ul_ccch(); + bool is_var_len_ce(uint32_t lcid); + bool is_ul_ccch() const; - int32_t read_subheader(const uint8_t* ptr); - uint32_t get_total_length(); - uint32_t get_sdu_length(); - uint32_t get_lcid(); - uint8_t* get_sdu(); - uint16_t get_c_rnti(); + int32_t read_subheader(const uint8_t* ptr); + uint32_t get_total_length() const; + uint32_t get_sdu_length() const; + uint32_t get_lcid() const; + uint8_t* get_sdu(); + const uint8_t* get_sdu() const; + uint16_t get_c_rnti() const; // both return the reported values as per TS 38.321, mapping to dB according to TS 38.133 Sec 10.1.17 not done here uint8_t get_phr(); @@ -87,9 +88,13 @@ public: uint8_t lcg_id; uint8_t buffer_size; }; - lcg_bsr_t get_sbsr(); - static const uint8_t max_num_lcg_lbsr = 8; - std::array get_lbsr(); + lcg_bsr_t get_sbsr() const; + static const uint8_t max_num_lcg_lbsr = 8; + struct lbsr_t { + uint8_t bitmap; // the first octet of LBSR and Long Trunc BSR + std::vector list; // one entry for each reported LCG + }; + lbsr_t get_lbsr() const; // TA struct ta_t { @@ -98,6 +103,12 @@ public: }; ta_t get_ta(); + // UE contention resolution identity CE + static const uint8_t ue_con_res_id_len = 6; + typedef std::array ue_con_res_id_t; + ue_con_res_id_t get_ue_con_res_id_ce(); + uint64_t get_ue_con_res_id_ce_packed(); + // setters void set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_); void set_padding(const uint32_t len_); @@ -105,23 +116,83 @@ public: void set_se_phr(const uint8_t phr_, const uint8_t pcmax_); void set_sbsr(const lcg_bsr_t bsr_); void set_lbsr(const std::array bsr_); + void set_ue_con_res_id_ce(const ue_con_res_id_t id); uint32_t write_subpdu(const uint8_t* start_); // Used by BSR procedure to determine size of BSR types static uint32_t sizeof_ce(uint32_t lcid, bool is_ul); + void to_string(fmt::memory_buffer& buffer); + private: srslog::basic_logger* logger; + // internal helpers + bool has_length_field(); + uint32_t lcid = 0; int header_length = 0; int sdu_length = 0; bool F_bit = false; - uint8_t* sdu = nullptr; - static const uint8_t mac_ce_payload_len = 8 + 1; // Long BSR has max. 9 octets (see sizeof_ce() too) - std::array ce_write_buffer; // Buffer for CE payload + /// This helper class manages a SDU pointer that can point to either a user provided external buffer or to a small + /// internal buffer, useful for storing very short SDUs. + class sdu_buffer + { + static const uint8_t mac_ce_payload_len = 8 + 1; // Long BSR has max. 9 octets (see sizeof_ce() too) + std::array ce_write_buffer = {}; // Buffer for CE payload + uint8_t* sdu = nullptr; + + public: + sdu_buffer() = default; + + sdu_buffer(const sdu_buffer& other) : ce_write_buffer(other.ce_write_buffer) + { + // First check if we need to use internal storage. + if (other.sdu == other.ce_write_buffer.data()) { + sdu = ce_write_buffer.data(); + return; + } + sdu = other.sdu; + } + + sdu_buffer& operator=(const sdu_buffer& other) + { + if (this == &other) { + return *this; + } + ce_write_buffer = other.ce_write_buffer; + if (other.sdu == other.ce_write_buffer.data()) { + sdu = ce_write_buffer.data(); + return *this; + } + sdu = other.sdu; + return *this; + } + + explicit operator bool() const { return sdu; } + + /// Set the SDU pointer to use the internal buffer. + uint8_t* use_internal_storage() + { + sdu = ce_write_buffer.data(); + return sdu; + } + + /// Set the SDU pointer to point to the provided buffer. + uint8_t* set_storage_to(uint8_t* p) + { + sdu = p; + return sdu; + } + + /// Returns the SDU pointer. + const uint8_t* ptr() const { return sdu; } + uint8_t* ptr() { return sdu; } + }; + + sdu_buffer sdu; mac_sch_pdu_nr* parent = nullptr; }; @@ -133,11 +204,12 @@ public: void pack(); int unpack(const uint8_t* payload, const uint32_t& len); - uint32_t get_num_subpdus(); - const mac_sch_subpdu_nr& get_subpdu(const uint32_t& index); + uint32_t get_num_subpdus() const { return subpdus.size(); } + const mac_sch_subpdu_nr& get_subpdu(const uint32_t& index) const; + mac_sch_subpdu_nr& get_subpdu(uint32_t index); bool is_ulsch(); - void init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool is_ulsch_ = false); + int init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool is_ulsch_ = false); void init_rx(bool ulsch_ = false); // Add SDU or CEs to PDU @@ -147,11 +219,15 @@ public: uint32_t add_se_phr_ce(const uint8_t phr_, const uint8_t pcmax_); uint32_t add_sbsr_ce(const mac_sch_subpdu_nr::lcg_bsr_t bsr_); uint32_t add_lbsr_ce(const std::array bsr_); + uint32_t add_ue_con_res_id_ce(const mac_sch_subpdu_nr::ue_con_res_id_t id); uint32_t get_remaing_len(); -private: + void to_string(fmt::memory_buffer& buffer); + uint32_t size_header_sdu(const uint32_t lcid_, const uint32_t nbytes); + +private: /// Private helper that adds a subPDU to the MAC PDU uint32_t add_sudpdu(mac_sch_subpdu_nr& subpdu); diff --git a/lib/include/srsran/mac/pdu.h b/lib/include/srsran/mac/pdu.h index df82b95ac..2762ae142 100644 --- a/lib/include/srsran/mac/pdu.h +++ b/lib/include/srsran/mac/pdu.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/mac/pdu_queue.h b/lib/include/srsran/mac/pdu_queue.h index 09e9b2c84..6ebdd5449 100644 --- a/lib/include/srsran/mac/pdu_queue.h +++ b/lib/include/srsran/mac/pdu_queue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,7 +39,7 @@ public: class process_callback { public: - virtual void process_pdu(uint8_t* buff, uint32_t len, channel_t channel) = 0; + virtual void process_pdu(uint8_t* buff, uint32_t len, channel_t channel, int ul_nof_prbs = -1) = 0; }; pdu_queue(srslog::basic_logger& logger) : pool(DEFAULT_POOL_SIZE), callback(NULL), logger(logger) {} @@ -47,7 +47,7 @@ public: uint8_t* request(uint32_t len); void deallocate(const uint8_t* pdu); - void push(const uint8_t* ptr, uint32_t len, channel_t channel = DCH); + void push(const uint8_t* ptr, uint32_t len, channel_t channel = DCH, int ul_nof_prbs = -1); bool process_pdus(); @@ -61,6 +61,7 @@ private: uint8_t ptr[MAX_PDU_LEN]; uint32_t len; channel_t channel; + int grant_nof_prbs; #ifdef SRSRAN_BUFFER_POOL_LOG_ENABLED char debug_name[128]; #endif diff --git a/lib/include/srsran/phy/agc/agc.h b/lib/include/srsran/phy/agc/agc.h index c9a1252e3..a853a729d 100644 --- a/lib/include/srsran/phy/agc/agc.h +++ b/lib/include/srsran/phy/agc/agc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/cfr/cfr.h b/lib/include/srsran/phy/cfr/cfr.h new file mode 100644 index 000000000..0e20e25e3 --- /dev/null +++ b/lib/include/srsran/phy/cfr/cfr.h @@ -0,0 +1,139 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_CFR_H +#define SRSRAN_CFR_H + +#include "srsran/config.h" +#include "srsran/phy/common/phy_common.h" +#include "srsran/phy/dft/dft.h" + +#define CFR_EMA_INIT_AVG_PWR 0.1 + +/** + * @brief CFR manual threshold or PAPR limiting with CMA or EMA power averaging + */ +typedef enum SRSRAN_API { + SRSRAN_CFR_THR_INVALID, + SRSRAN_CFR_THR_MANUAL, + SRSRAN_CFR_THR_AUTO_CMA, + SRSRAN_CFR_THR_AUTO_EMA, + SRSRAN_CFR_NOF_MODES +} srsran_cfr_mode_t; + +/** + * @brief CFR module configuration arguments + */ +typedef struct SRSRAN_API { + bool cfr_enable; + srsran_cfr_mode_t cfr_mode; + // always used (mandatory) + uint32_t symbol_bw; ///< OFDM symbol bandwidth, in FFT bins + uint32_t symbol_sz; ///< OFDM symbol size (in samples). This is the FFT size + float alpha; ///< Alpha parameter of the clipping algorithm + bool dc_sc; ///< Take into account the DC subcarrier for the filter BW + + // SRSRAN_CFR_THR_MANUAL mode parameters + float manual_thr; ///< Fixed threshold used in SRSRAN_CFR_THR_MANUAL mode + + // SRSRAN_CFR_THR_AUTO_CMA and SRSRAN_CFR_THR_AUTO_EMA mode parameters + bool measure_out_papr; ///< Enable / disable output PAPR measurement + float max_papr_db; ///< Input PAPR threshold used in SRSRAN_CFR_THR_AUTO_CMA and SRSRAN_CFR_THR_AUTO_EMA modes + float ema_alpha; ///< EMA alpha parameter for avg power calculation, used in SRSRAN_CFR_THR_AUTO_EMA mode +} srsran_cfr_cfg_t; + +typedef struct SRSRAN_API { + srsran_cfr_cfg_t cfg; + float max_papr_lin; + + srsran_dft_plan_t fft_plan; + srsran_dft_plan_t ifft_plan; + float* lpf_spectrum; ///< FFT filter spectrum + uint32_t lpf_bw; ///< Bandwidth of the LPF + + float* abs_buffer_in; ///< Store the input absolute value + float* abs_buffer_out; ///< Store the output absolute value + cf_t* peak_buffer; + + float pwr_avg_in; ///< store the avg. input power with MA or EMA averaging + float pwr_avg_out; ///< store the avg. output power with MA or EMA averaging + + // Power average buffers, used in SRSRAN_CFR_THR_AUTO_CMA mode + uint64_t cma_n; +} srsran_cfr_t; + +SRSRAN_API int srsran_cfr_init(srsran_cfr_t* q, srsran_cfr_cfg_t* cfg); + +/** + * @brief Applies the CFR algorithm to the time domain OFDM symbols + * + * @attention This function must be called once per symbol, and it will process q->symbol_sz samples + * + * @param[in] q The CFR object and configuration + * @param[in] in Input buffer containing the time domain OFDM symbol without CP + * @param[out] out Output buffer with the processed OFDM symbol + * @return SRSRAN_SUCCESS if the CFR object is initialised, otherwise SRSRAN_ERROR + */ +SRSRAN_API void srsran_cfr_process(srsran_cfr_t* q, cf_t* in, cf_t* out); + +SRSRAN_API void srsran_cfr_free(srsran_cfr_t* q); + +/** + * @brief Checks the validity of the CFR algorithm parameters. + * + * @attention Does not check symbol size and bandwidth + * + * @param[in] cfr_conf the CFR configuration + * @return true if the configuration is valid, false otherwise + */ +SRSRAN_API bool srsran_cfr_params_valid(srsran_cfr_cfg_t* cfr_conf); + +/** + * @brief Sets the manual threshold of the CFR (used in manual mode). + * + * @attention this is not thread-safe + * + * @param[in] q the CFR object + * @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR or SRSRAN_ERROR_INVALID_INPUTS otherwise + */ +SRSRAN_API int srsran_cfr_set_threshold(srsran_cfr_t* q, float thres); + +/** + * @brief Sets the papr target of the CFR (used in auto modes). + * + * @attention this is not thread-safe + * + * @param[in] q the CFR object + * @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR or SRSRAN_ERROR_INVALID_INPUTS otherwise + */ +SRSRAN_API int srsran_cfr_set_papr(srsran_cfr_t* q, float papr); + +/** + * @brief Converts a string representing a CFR mode from the config files into srsran_cfr_mode_t type + * + * @param[in] mode_str the cfr.mode string coming from the config file + * @return SRSRAN_CFR_THR_INVALID if mode_str is empty, + * SRSRAN_CFR_THR_INVALID if mode_str is not recognised, + * otherwise it returns the corresponding srsran_cfr_mode_t value. + */ +SRSRAN_API srsran_cfr_mode_t srsran_cfr_str2mode(const char* mode_str); + +#endif // SRSRAN_CFR_H diff --git a/lib/include/srsran/phy/ch_estimation/chest_common.h b/lib/include/srsran/phy/ch_estimation/chest_common.h index a054eb57a..8d9357573 100644 --- a/lib/include/srsran/phy/ch_estimation/chest_common.h +++ b/lib/include/srsran/phy/ch_estimation/chest_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ch_estimation/chest_dl.h b/lib/include/srsran/phy/ch_estimation/chest_dl.h index cc537450f..4530e0b4d 100644 --- a/lib/include/srsran/phy/ch_estimation/chest_dl.h +++ b/lib/include/srsran/phy/ch_estimation/chest_dl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ch_estimation/chest_dl_nbiot.h b/lib/include/srsran/phy/ch_estimation/chest_dl_nbiot.h index b819f2a97..bb9cbbed0 100644 --- a/lib/include/srsran/phy/ch_estimation/chest_dl_nbiot.h +++ b/lib/include/srsran/phy/ch_estimation/chest_dl_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ch_estimation/chest_sl.h b/lib/include/srsran/phy/ch_estimation/chest_sl.h index 794c85025..9d2c38a38 100644 --- a/lib/include/srsran/phy/ch_estimation/chest_sl.h +++ b/lib/include/srsran/phy/ch_estimation/chest_sl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -85,10 +85,10 @@ typedef struct SRSRAN_API { } srsran_chest_sl_t; -SRSRAN_API int srsran_chest_sl_init(srsran_chest_sl_t* q, - srsran_sl_channels_t channel, - srsran_cell_sl_t cell, - srsran_sl_comm_resource_pool_t sl_comm_resource_pool); +SRSRAN_API int srsran_chest_sl_init(srsran_chest_sl_t* q, + srsran_sl_channels_t channel, + srsran_cell_sl_t cell, + const srsran_sl_comm_resource_pool_t* sl_comm_resource_pool); SRSRAN_API int srsran_chest_sl_set_cell(srsran_chest_sl_t* q, srsran_cell_sl_t cell); diff --git a/lib/include/srsran/phy/ch_estimation/chest_ul.h b/lib/include/srsran/phy/ch_estimation/chest_ul.h index cca034e17..e74ff6845 100644 --- a/lib/include/srsran/phy/ch_estimation/chest_ul.h +++ b/lib/include/srsran/phy/ch_estimation/chest_ul.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -49,7 +49,7 @@ typedef struct SRSRAN_API { cf_t* ce; uint32_t nof_re; float noise_estimate; - float noise_estimate_dbm; + float noise_estimate_dbFs; float rsrp; float rsrp_dBfs; float epre; diff --git a/lib/include/srsran/phy/ch_estimation/csi_rs.h b/lib/include/srsran/phy/ch_estimation/csi_rs.h index 41c2752fb..281835a53 100644 --- a/lib/include/srsran/phy/ch_estimation/csi_rs.h +++ b/lib/include/srsran/phy/ch_estimation/csi_rs.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,20 +48,6 @@ */ #define SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER 6 -/** - * @brief Measurement structure - */ -typedef struct SRSRAN_API { - float rsrp; - float rsrp_dB; - float epre; - float epre_dB; - float n0; - float n0_dB; - float snr_dB; - uint32_t nof_re; -} srsran_csi_rs_measure_t; - /** * @brief Calculates if the given periodicity implies a CSI-RS transmission in the given slot * @remark Described in TS 36.211 section 7.4.1.5.3 Mapping to physical resources @@ -76,7 +62,7 @@ SRSRAN_API bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* peri * @brief Adds to a RE pattern list the RE used in a CSI-RS resource for all CDM grops. This is intended for generating * reserved RE pattern for PDSCH transmission. * @param carrier Provides carrier configuration - * @param resource Provides a CSI-RS resource + * @param resource Provides any CSI-RS resource mapping * @param nof_resources Provides the number of ZP-CSI-RS resources * @param l Symbol index in the slot * @param[out] rvd_mask Provides the reserved mask @@ -86,17 +72,123 @@ SRSRAN_API int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_ const srsran_csi_rs_resource_mapping_t* resource, srsran_re_pattern_list_t* re_pattern_list); -SRSRAN_API int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - cf_t* grid); +/** + * @brief Puts in the provided resource grid NZP-CSI-RS signals given by a NZP-CSI-RS resource + * + * @note it does not check if the provided slot matches with the periodicity of the provided NZP-CSI-RS resource + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot configuration + * @param resource Provides a NZP-CSI-RS resource + * @param[out] grid Resource grid + * @return SRSRAN_SUCCESS if the arguments and the resource are valid. SRSRAN_ERROR code otherwise. + */ +SRSRAN_API int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + cf_t* grid); +/** + * @brief Puts in the provided resource grid NZP-CSI-RS signals given by a NZP-CSI-RS resource set if their periodicity + * configuration matches with the provided slot + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot configuration + * @param set Provides a NZP-CSI-RS resource set + * @param[out] grid Resource grid + * @return The number of NZP-CSI-RS resources that have been scheduled for this slot if the arguments and the resource + * are valid. SRSRAN_ERROR code otherwise. + */ +SRSRAN_API int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + cf_t* grid); SRSRAN_API int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, const srsran_slot_cfg_t* slot_cfg, const srsran_csi_rs_nzp_resource_t* resource, const cf_t* grid, - srsran_csi_rs_measure_t* measure); + srsran_csi_trs_measurements_t* measure); -SRSRAN_API uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_measure_t* measure, char* str, uint32_t str_len); +/** + * @brief Performs measurements of NZP-CSI-RS resource set flagged as TRS + * + * @attention It expects: + * - The NZP-CSI-RS resource set shall be flagged as TRS; and + * - at least a pair of active NZP-CSR-RS per measurement opportunity with their first transmission symbol in ascending + * order. + * + * @note It performs the following wideband measurements: + * - RSRP (linear and dB), + * - EPRE (linear and dB), + * - Noise (linear and dB), + * - SNR (dB), + * - average delay (microseconds), and + * - CFO (Hz) + * + * @note It is intended for fine tracking of synchronization error (average delay) and carrier frequency error + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot + * @param set Provides NZP-CSI-RS resource + * @param grid Resource grid + * @param measure Provides measurement + * @return The number of NZP-CSI-RS resources scheduled for this TTI if the configuration is right, SRSRAN_ERROR code if + * the configuration is invalid + */ +SRSRAN_API int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_trs_measurements_t* measure); + +SRSRAN_API uint32_t srsran_csi_rs_measure_info(const srsran_csi_trs_measurements_t* measure, + char* str, + uint32_t str_len); + +/** + * @brief Performs channel measurements of NZP-CSI-RS resource set for CSI reports + * + * @note It performs the following wideband measurements: + * - RSRP (dB), + * - EPRE (dB), + * - SNR (dB), + * + * @note It is intended for generating CSI wideband measurements that are used for generating CSI reporting + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot + * @param set Provides NZP-CSI-RS resource + * @param grid Resource grid + * @param measure Provides CSI measurement + * @return The number of NZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSRAN_ERROR code + * if the configuration is invalid + */ +SRSRAN_API int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_channel_measurements_t* measure); + +/** + * @brief Performs measurements of ZP-CSI-RS resource set for CSI reports + * + * @note It performs the following wideband measurememnts: + * - EPRE (dB) + * + * @note It is intended for measuring interference + * + * @param carrier Provides carrier configuration + * @param slot_cfg Provides current slot + * @param set Provides ZP-CSI-RS resource + * @param grid Resource grid + * @param measure Provides CSI measurement + * @return The number of ZP-CSI-RS resources scheduled for this slot if the configuration is right, SRSRAN_ERROR code if + * the configuration is invalid + */ +SRSRAN_API int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_set_t* set, + const cf_t* grid, + srsran_csi_channel_measurements_t* measure); #endif // SRSRAN_CSI_RS_H_ diff --git a/lib/include/srsran/phy/ch_estimation/csi_rs_cfg.h b/lib/include/srsran/phy/ch_estimation/csi_rs_cfg.h index b3d4fc3f6..31da17b21 100644 --- a/lib/include/srsran/phy/ch_estimation/csi_rs_cfg.h +++ b/lib/include/srsran/phy/ch_estimation/csi_rs_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -137,4 +137,10 @@ typedef struct SRSRAN_API { uint32_t count; ///< Number of resources in the set } srsran_csi_rs_zp_set_t; +SRSRAN_API bool srsran_csi_rs_resource_mapping_is_valid(const srsran_csi_rs_resource_mapping_t* res); + +SRSRAN_API uint32_t srsran_csi_rs_resource_mapping_info(const srsran_csi_rs_resource_mapping_t* res, + char* str, + uint32_t str_len); + #endif // SRSRAN_CSI_RS_CFG_H diff --git a/lib/include/srsran/phy/ch_estimation/dmrs_pbch.h b/lib/include/srsran/phy/ch_estimation/dmrs_pbch.h new file mode 100644 index 000000000..3a4686587 --- /dev/null +++ b/lib/include/srsran/phy/ch_estimation/dmrs_pbch.h @@ -0,0 +1,80 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_DMRS_PBCH_H +#define SRSRAN_DMRS_PBCH_H + +#include "srsran/phy/common/phy_common_nr.h" + +/** + * @brief Describes the DeModulation Reference Signals (DMRS) for NR PBCH configuration + */ +typedef struct SRSRAN_API { + uint32_t N_id; ///< Physical cell identifier + uint32_t n_hf; ///< Number of half radio frame, 0 or 1 + uint32_t ssb_idx; ///< SSB candidate index, up to 3 LSB are significant + uint32_t L_max; ///< Number of SSB opportunities in half radio frame + float beta; ///< Power allocation specified in TS 38.213 + srsran_subcarrier_spacing_t scs; ///< SSB configured subcarrier spacing +} srsran_dmrs_pbch_cfg_t; + +/** + * @brief Describes an NR PBCH DMRS based measurement + */ +typedef struct SRSRAN_API { + float corr; ///< Normalised correlation + float epre; ///< Linear energy per resource element + float rsrp; ///< Linear RSRP + float cfo_hz; ///< CFO in Hz + float avg_delay_us; ///< Average delay in us +} srsran_dmrs_pbch_meas_t; + +/** + * @brief Put NR PBCH DMRS in the SSB resource grid + * @param cfg PBCH DMRS configuration + * @param[out] ssb_grid SSB resource grid + * @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]); + +/** + * @brief Measures NR PBCH DMRS + * @param cfg PBCH DMRS configuration + * @param ssb_grid SSB resource grid + * @param[out] meas Measurement + * @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_dmrs_pbch_measure(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + srsran_dmrs_pbch_meas_t* meas); + +/** + * @brief Estimates NR PBCH DMRS + * @param cfg PBCH DMRS configuration + * @param ssb_grid Demodulated SSB resource grid + * @param[out] ce Estimated channel + * @return SRSRAN_SUCCESS if the inputs and configuration are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + cf_t ce[SRSRAN_SSB_NOF_RE]); + +#endif // SRSRAN_DMRS_PBCH_H diff --git a/lib/include/srsran/phy/ch_estimation/dmrs_pdcch.h b/lib/include/srsran/phy/ch_estimation/dmrs_pdcch.h index edb73b6ee..1f223d9ce 100644 --- a/lib/include/srsran/phy/ch_estimation/dmrs_pdcch.h +++ b/lib/include/srsran/phy/ch_estimation/dmrs_pdcch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ch_estimation/dmrs_pucch.h b/lib/include/srsran/phy/ch_estimation/dmrs_pucch.h index 1cf55aa24..d39e3e197 100644 --- a/lib/include/srsran/phy/ch_estimation/dmrs_pucch.h +++ b/lib/include/srsran/phy/ch_estimation/dmrs_pucch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -58,7 +58,6 @@ SRSRAN_API int srsran_dmrs_pucch_format1_put(const srsran_pucch_nr_t* /** * @brief Estimates NR-PUCCH format 1 resource elements from their DMRS in the provided resource grid * @param[in] q NR-PUCCH encoder/decoder object - * @param[in] carrier Carrier configuration * @param[in] cfg PUCCH common configuration * @param[in] slot slot configuration * @param[in] resource PUCCH format 1 resource @@ -67,7 +66,6 @@ SRSRAN_API int srsran_dmrs_pucch_format1_put(const srsran_pucch_nr_t* * @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR code otherwise */ SRSRAN_API int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, - const srsran_carrier_nr_t* carrier, const srsran_pucch_nr_common_cfg_t* cfg, const srsran_slot_cfg_t* slot, const srsran_pucch_nr_resource_t* resource, @@ -94,7 +92,6 @@ int srsran_dmrs_pucch_format2_put(const srsran_pucch_nr_t* q, /** * @brief Estimates NR-PUCCH format 2 resource elements from their DMRS in the provided resource grid * @param[in] q NR-PUCCH encoder/decoder object - * @param[in] carrier Carrier configuration * @param[in] cfg PUCCH common configuration * @param[in] slot slot configuration * @param[in] resource PUCCH format 2 resource @@ -103,7 +100,6 @@ int srsran_dmrs_pucch_format2_put(const srsran_pucch_nr_t* q, * @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR code otherwise */ int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q, - const srsran_carrier_nr_t* carrier, const srsran_pucch_nr_common_cfg_t* cfg, const srsran_slot_cfg_t* slot, const srsran_pucch_nr_resource_t* resource, diff --git a/lib/include/srsran/phy/ch_estimation/dmrs_sch.h b/lib/include/srsran/phy/ch_estimation/dmrs_sch.h index ef797d773..110455aa2 100644 --- a/lib/include/srsran/phy/ch_estimation/dmrs_sch.h +++ b/lib/include/srsran/phy/ch_estimation/dmrs_sch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -59,6 +59,8 @@ typedef struct { cf_t* temp; /// Temporal data vector of size SRSRAN_NRE * carrier.nof_prb float* filter; ///< Smoothing filter + + srsran_csi_trs_measurements_t csi; ///< Last estimated channel state information } srsran_dmrs_sch_t; /** diff --git a/lib/include/srsran/phy/ch_estimation/refsignal_dl.h b/lib/include/srsran/phy/ch_estimation/refsignal_dl.h index 48086f27e..1d0cec5ed 100644 --- a/lib/include/srsran/phy/ch_estimation/refsignal_dl.h +++ b/lib/include/srsran/phy/ch_estimation/refsignal_dl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ch_estimation/refsignal_dl_nbiot.h b/lib/include/srsran/phy/ch_estimation/refsignal_dl_nbiot.h index ab77713bc..21380d416 100644 --- a/lib/include/srsran/phy/ch_estimation/refsignal_dl_nbiot.h +++ b/lib/include/srsran/phy/ch_estimation/refsignal_dl_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ch_estimation/refsignal_ul.h b/lib/include/srsran/phy/ch_estimation/refsignal_ul.h index 2d818335c..53e7f741a 100644 --- a/lib/include/srsran/phy/ch_estimation/refsignal_ul.h +++ b/lib/include/srsran/phy/ch_estimation/refsignal_ul.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ch_estimation/wiener_dl.h b/lib/include/srsran/phy/ch_estimation/wiener_dl.h index 7fad9dfbf..1cb41028d 100644 --- a/lib/include/srsran/phy/ch_estimation/wiener_dl.h +++ b/lib/include/srsran/phy/ch_estimation/wiener_dl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/channel/ch_awgn.h b/lib/include/srsran/phy/channel/ch_awgn.h index eff5e5e80..f85706d17 100644 --- a/lib/include/srsran/phy/channel/ch_awgn.h +++ b/lib/include/srsran/phy/channel/ch_awgn.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,13 +19,11 @@ * */ -/********************************************************************************************** - * File: ch_awgn.h +/** + * \file ch_awgn.h + * \brief Additive white Gaussian noise channel object * - * Description: Additive white gaussian noise channel object - * - * Reference: - *********************************************************************************************/ + */ #include "srsran/config.h" #include @@ -38,7 +36,7 @@ extern "C" { #endif /** - * The srsRAN channel AWGN implements an efficient Box-Muller Method accelerated with SIMD. + * \brief srsRAN channel AWGN implements an efficient Box-Muller Method accelerated with SIMD. */ typedef struct { float* table_cos; @@ -48,7 +46,7 @@ typedef struct { } srsran_channel_awgn_t; /** - * Initialization function of the channel AWGN object + * \brief function of the channel AWGN object * * @param q AWGN channel object * @param seed random generator seed @@ -56,7 +54,7 @@ typedef struct { SRSRAN_API int srsran_channel_awgn_init(srsran_channel_awgn_t* q, uint32_t seed); /** - * Sets the noise level N0 in decibels full scale (dBfs) + * \brief the noise level N0 in decibels full scale (dBfs) * * @param q AWGN channel object * @param n0_dBfs noise level @@ -64,7 +62,7 @@ SRSRAN_API int srsran_channel_awgn_init(srsran_channel_awgn_t* q, uint32_t seed) SRSRAN_API int srsran_channel_awgn_set_n0(srsran_channel_awgn_t* q, float n0_dBfs); /** - * Runs the complex AWGN channel + * \brief the complex AWGN channel * * @param q AWGN channel object * @param in complex input array @@ -74,7 +72,7 @@ SRSRAN_API int srsran_channel_awgn_set_n0(srsran_channel_awgn_t* q, float n0_dBf SRSRAN_API void srsran_channel_awgn_run_c(srsran_channel_awgn_t* q, const cf_t* in, cf_t* out, uint32_t length); /** - * Runs the real AWGN channel + * \brief the real AWGN channel * * @param q AWGN channel object * @param in real input array @@ -84,15 +82,31 @@ SRSRAN_API void srsran_channel_awgn_run_c(srsran_channel_awgn_t* q, const cf_t* SRSRAN_API void srsran_channel_awgn_run_f(srsran_channel_awgn_t* q, const float* in, float* out, uint32_t length); /** - * Free AWGN channel generator data + * \brief AWGN channel generator data * * @param q AWGN channel object */ SRSRAN_API void srsran_channel_awgn_free(srsran_channel_awgn_t* q); +/** + * \brief signal \p input with AWGN to obtain signal \p output (complex case). + * + * @param[in] input Input signal + * @param[out] output Output signal + * @param[in] variance Noise variance + * @param[in] len Number of samples + */ SRSRAN_API void srsran_ch_awgn_c(const cf_t* input, cf_t* output, float variance, uint32_t len); -SRSRAN_API void srsran_ch_awgn_f(const float* x, float* y, float variance, uint32_t len); +/** + * \brief Perturb signal \p input with AWGN to obtain signal \p output (real case). + * + * @param[in] input Input signal + * @param[out] output Output signal + * @param[in] variance Noise variance + * @param[in] len Number of samples + */ +SRSRAN_API void srsran_ch_awgn_f(const float* input, float* output, float variance, uint32_t len); SRSRAN_API float srsran_ch_awgn_get_variance(float ebno_db, float rate); diff --git a/lib/include/srsran/phy/channel/channel.h b/lib/include/srsran/phy/channel/channel.h index a58b206f9..983e601f2 100644 --- a/lib/include/srsran/phy/channel/channel.h +++ b/lib/include/srsran/phy/channel/channel.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,7 @@ namespace srsran { class channel { public: - typedef struct { + struct args_t { // General bool enable = false; @@ -67,7 +67,7 @@ public: bool rlf_enable = false; uint32_t rlf_t_on_ms = 10000; uint32_t rlf_t_off_ms = 2000; - } args_t; + }; channel(const args_t& channel_args, uint32_t _nof_channels, srslog::basic_logger& logger); ~channel(); diff --git a/lib/include/srsran/phy/channel/delay.h b/lib/include/srsran/phy/channel/delay.h index d58a88f77..aa6452b73 100644 --- a/lib/include/srsran/phy/channel/delay.h +++ b/lib/include/srsran/phy/channel/delay.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/channel/fading.h b/lib/include/srsran/phy/channel/fading.h index ccc2b6321..0bbe532aa 100644 --- a/lib/include/srsran/phy/channel/fading.h +++ b/lib/include/srsran/phy/channel/fading.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/channel/hst.h b/lib/include/srsran/phy/channel/hst.h index de88e5cea..5305c15e1 100644 --- a/lib/include/srsran/phy/channel/hst.h +++ b/lib/include/srsran/phy/channel/hst.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/channel/rlf.h b/lib/include/srsran/phy/channel/rlf.h index 782e3ee32..c8b6ff0fb 100644 --- a/lib/include/srsran/phy/channel/rlf.h +++ b/lib/include/srsran/phy/channel/rlf.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/common/phy_common.h b/lib/include/srsran/phy/common/phy_common.h index 900bdbc7d..f19b2ce87 100644 --- a/lib/include/srsran/phy/common/phy_common.h +++ b/lib/include/srsran/phy/common/phy_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -507,7 +507,7 @@ SRSRAN_API char* srsran_nbiot_mode_string(srsran_nbiot_mode_t mode); * Returns a constant string pointer with the ACK/NACK feedback mode * * @param ack_nack_feedback_mode Mode - * @return Returns constant pointer with the text of the mode if succesful, `error` otherwise + * @return Returns constant pointer with the text of the mode if successful, `error` otherwise */ SRSRAN_API const char* srsran_ack_nack_feedback_mode_string(srsran_ack_nack_feedback_mode_t ack_nack_feedback_mode); @@ -515,7 +515,7 @@ SRSRAN_API const char* srsran_ack_nack_feedback_mode_string(srsran_ack_nack_feed * Returns a constant string pointer with the ACK/NACK feedback mode * * @param ack_nack_feedback_mode Mode - * @return Returns constant pointer with the text of the mode if succesful, `error` otherwise + * @return Returns constant pointer with the text of the mode if successful, `error` otherwise */ SRSRAN_API srsran_ack_nack_feedback_mode_t srsran_string_ack_nack_feedback_mode(const char* str); diff --git a/lib/include/srsran/phy/common/phy_common_nr.h b/lib/include/srsran/phy/common/phy_common_nr.h index b8ad1406a..8a8d543c1 100644 --- a/lib/include/srsran/phy/common/phy_common_nr.h +++ b/lib/include/srsran/phy/common/phy_common_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,7 +45,6 @@ extern "C" { #define SRSRAN_SF_LEN_PRB_NR(nof_prb) (srsran_min_symbol_sz_rb(nof_prb) * 15) #define SRSRAN_SLOT_MAX_LEN_RE_NR (SRSRAN_SLOT_LEN_RE_NR(SRSRAN_MAX_PRB_NR)) -#define SRSRAN_SLOT_MAX_NOF_BITS_NR (SRSRAN_SLOT_MAX_LEN_RE_NR * SRSRAN_MAX_QM) #define SRSRAN_MAX_LAYERS_NR 8 /** @@ -56,7 +55,7 @@ extern "C" { /** * @brief Defines the symbol duration, including cyclic prefix */ -#define SRSRAN_SUBC_SPACING_NR(NUM) (15000U << (NUM)) +#define SRSRAN_SUBC_SPACING_NR(NUM) (15000U << (uint32_t)(NUM)) /** * @brief Defines the number of slots per SF. Defined by TS 38.211 v15.8.0 Table 4.3.2-1. @@ -123,6 +122,11 @@ extern "C" { */ #define SRSRAN_PDSCH_MAX_RE_NR (SRSRAN_MAX_NRE_NR * SRSRAN_MAX_PRB_NR) +/** + * @brief defines the maximum number of bits that can be transmitted in a slot + */ +#define SRSRAN_SLOT_MAX_NOF_BITS_NR (SRSRAN_PDSCH_MAX_RE_NR * SRSRAN_MAX_QM) + /** * @brief Maximum number of PDSCH time domain resource allocations. This is defined by TS 38.331 v15.10.0 * as maxNrofDL-Allocations @@ -149,6 +153,63 @@ extern "C" { */ #define SRSRAN_MAX_HARQ_PROC_UL_NR 16 // 3GPP TS 38.214 version 15.3.0 Sec. 6.1 +/** + * @brief SSB bandwidth in subcarriers, described in TS 38.211 section 7.4.3.1 Time-frequency structure of an SS/PBCH + * block + */ +#define SRSRAN_SSB_BW_SUBC 240 + +/** + * @brief SSB duration in symbols, described in TS 38.211 section 7.4.3.1 Time-frequency structure of an SS/PBCH block + */ +#define SRSRAN_SSB_DURATION_NSYMB 4 + +/** + * @brief Number of NR N_id_1 Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 Physical-layer + * cell identities + */ +#define SRSRAN_NOF_NID_1_NR 336 + +/** + * @brief Number of NR N_id_2 Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 Physical-layer + * cell identities + */ +#define SRSRAN_NOF_NID_2_NR 3 + +/** + * @brief Number of NR N_id Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 Physical-layer + * cell identities + */ +#define SRSRAN_NOF_NID_NR (SRSRAN_NOF_NID_1_NR * SRSRAN_NOF_NID_2_NR) + +/** + * @brief Compute N_id_1 from the Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 + * Physical-layer cell identities + */ +#define SRSRAN_NID_1_NR(N_ID) ((N_ID) / SRSRAN_NOF_NID_2_NR) + +/** + * @brief Compute N_id_2 from the Physical Cell Identifier (PCI) as described in TS 38.211 section 7.4.2.1 + * Physical-layer cell identities + */ +#define SRSRAN_NID_2_NR(N_ID) ((N_ID) % SRSRAN_NOF_NID_2_NR) + +/** + * @brief Compute Physical Cell Identifier (PCI) N_id from N_id_1 and N_id_2 + */ +#define SRSRAN_NID_NR(NID_1, NID_2) (SRSRAN_NOF_NID_2_NR * (NID_1) + (NID_2)) + +/** + * @brief SSB number of resource elements, described in TS 38.211 section 7.4.3.1 Time-frequency structure of an SS/PBCH + * block + */ +#define SRSRAN_SSB_NOF_RE (SRSRAN_SSB_BW_SUBC * SRSRAN_SSB_DURATION_NSYMB) + +/** + * @brief Symbol index with extended CP + */ +#define SRSRAN_EXT_CP_SYMBOL(SCS) (7U << (uint32_t)(SCS)) + typedef enum SRSRAN_API { srsran_coreset_mapping_type_non_interleaved = 0, srsran_coreset_mapping_type_interleaved, @@ -160,6 +221,8 @@ typedef enum SRSRAN_API { srsran_coreset_bundle_size_n6, } srsran_coreset_bundle_size_t; +uint32_t pdcch_nr_bundle_size(srsran_coreset_bundle_size_t x); + typedef enum SRSRAN_API { srsran_coreset_precoder_granularity_contiguous = 0, srsran_coreset_precoder_granularity_reg_bundle @@ -285,15 +348,42 @@ typedef enum SRSRAN_API { srsran_subcarrier_spacing_60kHz, srsran_subcarrier_spacing_120kHz, srsran_subcarrier_spacing_240kHz, + srsran_subcarrier_spacing_invalid } srsran_subcarrier_spacing_t; +typedef enum SRSRAN_API { + SRSRAN_SSB_PATTERN_A = 0, // FR1, 15 kHz SCS + SRSRAN_SSB_PATTERN_B, // FR1, 30 kHz SCS + SRSRAN_SSB_PATTERN_C, // FR1, 30 kHz SCS + SRSRAN_SSB_PATTERN_D, // FR2, 120 kHz SCS + SRSRAN_SSB_PATTERN_E, // FR2, 240 kHz SCS + SRSRAN_SSB_PATTERN_INVALID, +} srsran_ssb_pattern_t; + +typedef enum SRSRAN_API { + SRSRAN_DUPLEX_MODE_FDD = 0, // Paired + SRSRAN_DUPLEX_MODE_TDD, // Unpaired + SRSRAN_DUPLEX_MODE_SDL, // Supplementary DownLink + SRSRAN_DUPLEX_MODE_SUL, // Supplementary UpLink + SRSRAN_DUPLEX_MODE_INVALID +} srsran_duplex_mode_t; + +/** + * @brief Determines whether the first DMRS goes into symbol index 2 or 3 + */ +typedef enum { + srsran_dmrs_sch_typeA_pos_2 = 0, // Start in slot symbol index 2 (default) + srsran_dmrs_sch_typeA_pos_3 // Start in slot symbol index 3 +} srsran_dmrs_sch_typeA_pos_t; + /** * @brief NR carrier parameters. It is a combination of fixed cell and bandwidth-part (BWP) */ typedef struct SRSRAN_API { uint32_t pci; - uint32_t absolute_frequency_ssb; - uint32_t absolute_frequency_point_a; + double dl_center_frequency_hz; ///< Absolute baseband center frequency in Hz for DL grid + double ul_center_frequency_hz; ///< Absolute baseband center frequency in Hz for UL grid + double ssb_center_freq_hz; ///< SS/PBCH Block center frequency in Hz. Set to 0 if not present uint32_t offset_to_carrier; ///< Offset between point A and the lowest subcarrier of the lowest RB srsran_subcarrier_spacing_t scs; uint32_t nof_prb; ///< @brief See TS 38.101-1 Table 5.3.2-1 for more details @@ -304,6 +394,13 @@ typedef struct SRSRAN_API { ///< TS 38.212 [17], clause 5.4.2.1) } srsran_carrier_nr_t; +#define SRSRAN_DEFAULT_CARRIER_NR \ + { \ + .pci = 500, .dl_center_frequency_hz = 117000 * 30e3, .ul_center_frequency_hz = 117000 * 30e3, \ + .ssb_center_freq_hz = 3.5e9, .offset_to_carrier = 0, .scs = srsran_subcarrier_spacing_15kHz, .nof_prb = 52, \ + .start = 0, .max_mimo_layers = 1 \ + } + /** * @brief NR Slot parameters. It contains parameters that change in a slot basis. */ @@ -339,17 +436,19 @@ typedef struct SRSRAN_API { * @brief CORESET parameters as defined in TS 38.331 V15.10.0 - ControlResourceSet */ typedef struct SRSRAN_API { - uint32_t id; - srsran_coreset_mapping_type_t mapping_type; - uint32_t duration; - bool freq_resources[SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE]; - srsran_coreset_bundle_size_t interleaver_size; - + uint32_t id; + srsran_coreset_mapping_type_t mapping_type; + uint32_t duration; + bool freq_resources[SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE]; bool dmrs_scrambling_id_present; uint32_t dmrs_scrambling_id; srsran_coreset_precoder_granularity_t precoder_granularity; - srsran_coreset_bundle_size_t reg_bundle_size; + srsran_coreset_bundle_size_t interleaver_size; ///< Referenced in TS 38.211 section 7.3.2.2 as R + srsran_coreset_bundle_size_t reg_bundle_size; ///< Referenced in TS 38.211 section 7.3.2.2 as L uint32_t shift_index; + uint32_t offset_rb; ///< Integer offset in resource blocks from the pointA (lowest subcarrier of resource grid) to the + ///< lowest resource block of the CORESET region (used by CORESET Zero only) + /** Missing TCI parameters */ } srsran_coreset_t; @@ -385,6 +484,37 @@ typedef struct SRSRAN_API { srsran_tdd_pattern_t pattern2; } srsran_tdd_config_nr_t; +/** + * @brief Describes duplex configuration + */ +typedef struct SRSRAN_API { + srsran_duplex_mode_t mode; + union { + srsran_tdd_config_nr_t tdd; ///< TDD configuration + // ... add here other mode parameters + }; +} srsran_duplex_config_nr_t; + +/** + * @brief Describes a measurement based on NZP-CSI-RS or SSB-CSI + * @note Used for tracking RSRP, SNR, CFO, SFO, and so on + * @note srsran_csi_channel_measurements_t is used for CSI report generation + */ +typedef struct SRSRAN_API { + float rsrp; ///< Linear scale RSRP + float rsrp_dB; ///< Logarithm scale RSRP relative to full-scale + float epre; ///< Linear scale EPRE + float epre_dB; ///< Logarithm scale EPRE relative to full-scale + float n0; ///< Linear noise level + float n0_dB; ///< Logarithm scale noise level relative to full-scale + float snr_dB; ///< Signal to noise ratio in decibels + float cfo_hz; ///< Carrier frequency offset in Hz. Only set if more than 2 symbols are available in a TRS set + float cfo_hz_max; ///< Maximum CFO in Hz that can be measured. It is set to 0 if CFO cannot be estimated + float delay_us; ///< Average measured delay in microseconds + uint32_t nof_re; ///< Number of available RE for the measurement, it can be used for weighting among different + ///< measurements +} srsran_csi_trs_measurements_t; + /** * @brief Get the RNTI type name for NR * @param rnti_type RNTI type name @@ -392,6 +522,20 @@ typedef struct SRSRAN_API { */ SRSRAN_API const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type); +/** + * @brief Get the short RNTI type name for NR + * @param rnti_type RNTI type name + * @return Constant string with the short RNTI type name + */ +SRSRAN_API const char* srsran_rnti_type_str_short(srsran_rnti_type_t rnti_type); + +/** + * @brief Get the Search Space Type string for a given type + * @param ss_type The given Search Space Type + * @return The string describing the SS Type + */ +SRSRAN_API const char* srsran_ss_type_str(srsran_search_space_type_t ss_type); + /** * @brief Get the RNTI type name for NR * @param rnti_type RNTI type name @@ -423,6 +567,18 @@ SRSRAN_API uint32_t srsran_coreset_get_bw(const srsran_coreset_t* coreset); */ SRSRAN_API uint32_t srsran_coreset_get_sz(const srsran_coreset_t* coreset); +/** + * @brief Calculates the starting resource block index in the resource grid + * + * @remark Intended to be used for common search space as specifies the lat clause in TS 38.214 section 5.1.2.2 Resource + * allocation in frequency domain + * + * @param coreset provides the given CORESET configuration + * @return The index of the lowest resource block in the resource grid used by the given CORESET if the CORESET + * configuration is valid; Otherwise, 0. + */ +SRSRAN_API uint32_t srsran_coreset_start_rb(const srsran_coreset_t* coreset); + /** * @brief Get the NR PDSCH mapping type in string * @param mapping_type Mapping type @@ -452,37 +608,156 @@ SRSRAN_API srsran_mcs_table_t srsran_mcs_table_from_str(const char* str); */ SRSRAN_API uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb); +/** + * @brief Computes the minimum valid symbol size for a given amount of PRB + * @attention The valid FFT sizes are radix 2 and radix 3 between 128 to 4096 points. + * @param nof_prb Number of PRB + * @return The minimum valid FFT size if the number of PRB is in range, 0 otherwise + */ +SRSRAN_API int srsran_symbol_sz_from_srate(double srate_hz, srsran_subcarrier_spacing_t scs); + +/** + * @brief Computes the time in seconds between the beginning of the slot and the given symbol + * @remark All symbol size reference and values are taken from TS 38.211 section 5.3 OFDM baseband signal generation + * @param l Given symbol index + * @param scs Subcarrier spacing + * @return Returns the symbol time offset in seconds + */ +SRSRAN_API float srsran_symbol_offset_s(uint32_t l, srsran_subcarrier_spacing_t scs); + /** * @brief Computes the time in seconds between two symbols in a slot * @note l0 is expected to be smaller than l1 * @remark All symbol size reference and values are taken from TS 38.211 section 5.3 OFDM baseband signal generation * @param l0 First symbol index within the slot * @param l1 Second symbol index within the slot - * @param numerology NR Carrier numerology + * @param scs Subcarrier spacing * @return Returns the time in seconds between the two symbols if the condition above is satisfied, 0 seconds otherwise */ -SRSRAN_API float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology); +SRSRAN_API float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, srsran_subcarrier_spacing_t scs); /** * @brief Decides whether a given slot is configured as Downlink - * @param cfg Provides TDD configuration + * @param cfg Provides the carrier duplex configuration * @param numerology Provides BWP numerology * @param slot_idx Slot index in the frame for the given numerology * @return true if the provided slot index is configured for Downlink */ -SRSRAN_API bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx); +SRSRAN_API bool srsran_duplex_nr_is_dl(const srsran_duplex_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx); /** * @brief Decides whether a given slot is configured as Uplink - * @param cfg Provides TDD configuration + * @param cfg Provides the carrier duplex configuration * @param numerology Provides BWP numerology * @param slot_idx Slot index in the frame for the given numerology * @return true if the provided slot index is configured for Uplink */ -SRSRAN_API bool srsran_tdd_nr_is_ul(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx); +SRSRAN_API bool srsran_duplex_nr_is_ul(const srsran_duplex_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx); SRSRAN_API int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran_cell_t* cell); +/** + * @brief Writes detailed Channel State Information measurement into a string + * @param meas Provides the measurement + * @param str Provides string + * @param str_len Maximum string length + * @return The number of writen characters + */ +SRSRAN_API uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len); + +/** + * @brief Writes short Channel State Information measurement into a string + * @param meas Provides the measurement + * @param str Provides string + * @param str_len Maximum string length + * @return The number of writen characters + */ +SRSRAN_API uint32_t srsran_csi_meas_info_short(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len); + +/** + * @brief Converts a given string into a subcarrier spacing + * @param str Provides the string + * @return A valid subcarrier if the string is valid, srsran_subcarrier_spacing_invalid otherwise + */ +SRSRAN_API srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str); + +/** + * @brief Converts a given subcarrier spacing to string + * @param scs Subcarrier spacing + * @return A constant string pointer + */ +SRSRAN_API const char* srsran_subcarrier_spacing_to_str(srsran_subcarrier_spacing_t scs); + +/** + * @brief Combine Channel State Information from Tracking Reference Signals (CSI-TRS) + * @param[in] a CSI-RS measurement + * @param[in] b CSI-RS measurement + * @param[out] dst Destination of the combined + */ +SRSRAN_API void srsran_combine_csi_trs_measurements(const srsran_csi_trs_measurements_t* a, + const srsran_csi_trs_measurements_t* b, + srsran_csi_trs_measurements_t* dst); + +/** + * @brief Setup CORESET Zero from a configuration index + * @remark Defined by TS 38.213 tables 13-1, 13-2, 13-3, 13-4, 13-5, 13-6, 13-7, 13-8, 13-9, 13-10 + * @param n_cell_id Physical Cell identifier + * @param ssb_pointA_freq_offset_Hz Integer frequency offset in Hz between the SS/PBCH block center and pointA + * @param ssb_scs SS/PBCH block subcarrier spacing + * @param pdcch_scs PDCCH subcarrier spacing + * @param idx CORESET Zero configuration index + * @param[out] coreset Points to the resultant CORESET + * @return SRSRAN_SUCCESS if the given inputs lead to a valid CORESET configuration, otherise SRSRAN_ERROR code + */ +SRSRAN_API int srsran_coreset_zero(uint32_t n_cell_id, + uint32_t ssb_pointA_freq_offset_Hz, + srsran_subcarrier_spacing_t ssb_scs, + srsran_subcarrier_spacing_t pdcch_scs, + uint32_t idx, + srsran_coreset_t* coreset); + +/** + * @brief Obtain offset in RBs between CoresetZero and SSB. See TS 38.213, Tables 13-{1,...,10} + * @param idx Index of 13-{1,...10} table + * @param ssb_scs SS/PBCH block subcarrier spacing + * @param pdcch_scs PDCCH subcarrier spacing + * @return offset in RBs, or -1 in case of invalid inputs + */ +SRSRAN_API int +srsran_coreset0_ssb_offset(uint32_t idx, srsran_subcarrier_spacing_t ssb_scs, srsran_subcarrier_spacing_t pdcch_scs); + +/** + * @brief Convert Coreset to string + * + * @param coreset The coreset structure as input + * @param str The string to write to + * @param str_len Maximum string length + * @return SRSRAN_API + */ +SRSRAN_API int srsran_coreset_to_str(srsran_coreset_t* coreset, char* str, uint32_t str_len); + +/** + * @brief Convert SSB pattern to string + * @param pattern + * @return a string describing the SSB pattern + */ +SRSRAN_API const char* srsran_ssb_pattern_to_str(srsran_ssb_pattern_t pattern); + +/** + * @brief Convert string to SSB pattern + * @param str String to convert + * @return The pattern, SRSRAN_SSB_PATTERN_INVALID if string is invalid + */ +SRSRAN_API srsran_ssb_pattern_t srsran_ssb_pattern_fom_str(const char* str); + +/** + * @brief Compares if two NR carrier structures are equal + * @param a First carrier to compare + * @param b Second carrier to compare + * @return True if all the carrier structure fields are equal, otherwise false + */ +SRSRAN_API bool srsran_carrier_nr_equal(const srsran_carrier_nr_t* a, const srsran_carrier_nr_t* b); + #ifdef __cplusplus } #endif diff --git a/lib/include/srsran/phy/common/phy_common_sl.h b/lib/include/srsran/phy/common/phy_common_sl.h index 3883b54e8..4a1380be2 100644 --- a/lib/include/srsran/phy/common/phy_common_sl.h +++ b/lib/include/srsran/phy/common/phy_common_sl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/common/sequence.h b/lib/include/srsran/phy/common/sequence.h index 944271edd..a956fc3d0 100644 --- a/lib/include/srsran/phy/common/sequence.h +++ b/lib/include/srsran/phy/common/sequence.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,6 +45,14 @@ SRSRAN_API void srsran_sequence_state_init(srsran_sequence_state_t* s, uint32_t SRSRAN_API void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* out, uint32_t length); +SRSRAN_API void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length); + +SRSRAN_API void +srsran_sequence_state_apply_c(srsran_sequence_state_t* s, const int8_t* in, int8_t* out, uint32_t length); + +SRSRAN_API +void srsran_sequence_state_apply_bit(srsran_sequence_state_t* s, const uint8_t* in, uint8_t* out, uint32_t length); + SRSRAN_API void srsran_sequence_state_advance(srsran_sequence_state_t* s, uint32_t length); typedef struct SRSRAN_API { diff --git a/lib/include/srsran/phy/common/sliv.h b/lib/include/srsran/phy/common/sliv.h new file mode 100644 index 000000000..e215394bc --- /dev/null +++ b/lib/include/srsran/phy/common/sliv.h @@ -0,0 +1,32 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SLIV_H +#define SRSRAN_SLIV_H + +#include "srsran/config.h" +#include + +SRSRAN_API void srsran_sliv_to_s_and_l(uint32_t N, uint32_t v, uint32_t* S, uint32_t* L); + +SRSRAN_API uint32_t srsran_sliv_from_s_and_l(uint32_t N, uint32_t S, uint32_t L); + +#endif // SRSRAN_SLIV_H diff --git a/lib/include/srsran/phy/common/timestamp.h b/lib/include/srsran/phy/common/timestamp.h index 4c150fb12..66acdd3ff 100644 --- a/lib/include/srsran/phy/common/timestamp.h +++ b/lib/include/srsran/phy/common/timestamp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/common/zc_sequence.h b/lib/include/srsran/phy/common/zc_sequence.h index 2d572ef54..fc274ab7a 100644 --- a/lib/include/srsran/phy/common/zc_sequence.h +++ b/lib/include/srsran/phy/common/zc_sequence.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/dft/dft.h b/lib/include/srsran/phy/dft/dft.h index fe2d19be8..a7600377c 100644 --- a/lib/include/srsran/phy/dft/dft.h +++ b/lib/include/srsran/phy/dft/dft.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/dft/dft_precoding.h b/lib/include/srsran/phy/dft/dft_precoding.h index 93dcc2c90..f7981a8c5 100644 --- a/lib/include/srsran/phy/dft/dft_precoding.h +++ b/lib/include/srsran/phy/dft/dft_precoding.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/dft/ofdm.h b/lib/include/srsran/phy/dft/ofdm.h index ebb73e7ad..5e2448081 100644 --- a/lib/include/srsran/phy/dft/ofdm.h +++ b/lib/include/srsran/phy/dft/ofdm.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,6 +34,7 @@ #include #include "srsran/config.h" +#include "srsran/phy/cfr/cfr.h" #include "srsran/phy/common/phy_common.h" #include "srsran/phy/dft/dft.h" @@ -48,17 +49,19 @@ typedef struct SRSRAN_API { // Compulsory parameters uint32_t nof_prb; ///< Number of Resource Block - cf_t* in_buffer; ///< Input bnuffer pointer + cf_t* in_buffer; ///< Input buffer pointer cf_t* out_buffer; ///< Output buffer pointer srsran_cp_t cp; ///< Cyclic prefix type // Optional parameters - srsran_sf_t sf_type; ///< Subframe type, normal or MBSFN - bool normalize; ///< Normalization flag, it divides the output by square root of the symbol size - float freq_shift_f; ///< Frequency shift, normalised by sampling rate (used in UL) - float rx_window_offset; ///< DFT Window offset in CP portion (0-1), RX only - uint32_t symbol_sz; ///< Symbol size, forces a given symbol size for the number of PRB - bool keep_dc; ///< If true, it does not remove the DC + srsran_sf_t sf_type; ///< Subframe type, normal or MBSFN + bool normalize; ///< Normalization flag, it divides the output by square root of the symbol size + float freq_shift_f; ///< Frequency shift, normalised by sampling rate (used in UL) + float rx_window_offset; ///< DFT Window offset in CP portion (0-1), RX only + uint32_t symbol_sz; ///< Symbol size, forces a given symbol size for the number of PRB + bool keep_dc; ///< If true, it does not remove the DC + double phase_compensation_hz; ///< Carrier frequency in Hz for phase compensation, set to 0 to disable + srsran_cfr_cfg_t cfr_tx_cfg; ///< Tx CFR configuration } srsran_ofdm_cfg_t; /** @@ -83,6 +86,8 @@ typedef struct SRSRAN_API { uint32_t window_offset_n; cf_t* shift_buffer; cf_t* window_offset_buffer; + cf_t phase_compensation[SRSRAN_MAX_NSYMB * SRSRAN_NOF_SLOTS_PER_SF]; + srsran_cfr_t tx_cfr; ///< Tx CFR object } srsran_ofdm_t; /** @@ -139,6 +144,10 @@ SRSRAN_API int srsran_ofdm_set_freq_shift(srsran_ofdm_t* q, float freq_shift); SRSRAN_API void srsran_ofdm_set_normalize(srsran_ofdm_t* q, bool normalize_enable); +SRSRAN_API int srsran_ofdm_set_phase_compensation(srsran_ofdm_t* q, double center_freq_hz); + SRSRAN_API void srsran_ofdm_set_non_mbsfn_region(srsran_ofdm_t* q, uint8_t non_mbsfn_region); +SRSRAN_API int srsran_ofdm_set_cfr(srsran_ofdm_t* q, srsran_cfr_cfg_t* cfr); + #endif // SRSRAN_OFDM_H diff --git a/lib/include/srsran/phy/enb/enb_dl.h b/lib/include/srsran/phy/enb/enb_dl.h index d3eac4d0c..a3f557692 100644 --- a/lib/include/srsran/phy/enb/enb_dl.h +++ b/lib/include/srsran/phy/enb/enb_dl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -64,8 +64,10 @@ typedef struct SRSRAN_API { srsran_dl_sf_cfg_t dl_sf; - cf_t* sf_symbols[SRSRAN_MAX_PORTS]; + srsran_cfr_cfg_t cfr_config; + cf_t* sf_symbols[SRSRAN_MAX_PORTS]; + cf_t* out_buffer[SRSRAN_MAX_PORTS]; srsran_ofdm_t ifft[SRSRAN_MAX_PORTS]; srsran_ofdm_t ifft_mbsfn; @@ -102,7 +104,9 @@ SRSRAN_API void srsran_enb_dl_free(srsran_enb_dl_t* q); SRSRAN_API int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell); -SRSRAN_API bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, uint32_t ncce); +SRSRAN_API int srsran_enb_dl_set_cfr(srsran_enb_dl_t* q, const srsran_cfr_cfg_t* cfr); + +SRSRAN_API bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, const srsran_dci_location_t* loc); SRSRAN_API void srsran_enb_dl_put_base(srsran_enb_dl_t* q, srsran_dl_sf_cfg_t* dl_sf); diff --git a/lib/include/srsran/phy/enb/enb_dl_nr.h b/lib/include/srsran/phy/enb/enb_dl_nr.h deleted file mode 100644 index 002c3cf62..000000000 --- a/lib/include/srsran/phy/enb/enb_dl_nr.h +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_ENB_DL_NR_H -#define SRSRAN_ENB_DL_NR_H - -#include "srsran/phy/common/phy_common_nr.h" -#include "srsran/phy/dft/ofdm.h" -#include "srsran/phy/phch/pdcch_cfg_nr.h" -#include "srsran/phy/phch/pdcch_nr.h" -#include "srsran/phy/phch/pdsch_nr.h" - -typedef struct SRSRAN_API { - srsran_pdsch_nr_args_t pdsch; - srsran_pdcch_nr_args_t pdcch; - uint32_t nof_tx_antennas; - uint32_t nof_max_prb; -} srsran_enb_dl_nr_args_t; - -typedef struct SRSRAN_API { - uint32_t max_prb; - uint32_t nof_tx_antennas; - srsran_carrier_nr_t carrier; - srsran_pdcch_cfg_nr_t pdcch_cfg; - - srsran_ofdm_t fft[SRSRAN_MAX_PORTS]; - - cf_t* sf_symbols[SRSRAN_MAX_PORTS]; - srsran_pdsch_nr_t pdsch; - srsran_dmrs_sch_t dmrs; - - srsran_dci_nr_t dci; ///< Stores DCI configuration - srsran_pdcch_nr_t pdcch; -} srsran_enb_dl_nr_t; - -SRSRAN_API int -srsran_enb_dl_nr_init(srsran_enb_dl_nr_t* q, cf_t* output[SRSRAN_MAX_PORTS], const srsran_enb_dl_nr_args_t* args); - -SRSRAN_API int srsran_enb_dl_nr_set_carrier(srsran_enb_dl_nr_t* q, const srsran_carrier_nr_t* carrier); - -SRSRAN_API int srsran_enb_dl_nr_set_pdcch_config(srsran_enb_dl_nr_t* q, - const srsran_pdcch_cfg_nr_t* cfg, - const srsran_dci_cfg_nr_t* dci_cfg); - -SRSRAN_API void srsran_enb_dl_nr_free(srsran_enb_dl_nr_t* q); - -SRSRAN_API int srsran_enb_dl_nr_base_zero(srsran_enb_dl_nr_t* q); - -SRSRAN_API void srsran_enb_dl_nr_gen_signal(srsran_enb_dl_nr_t* q); - -SRSRAN_API int -srsran_enb_dl_nr_pdcch_put(srsran_enb_dl_nr_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_dl_nr_t* dci_dl); - -SRSRAN_API int srsran_enb_dl_nr_pdsch_put(srsran_enb_dl_nr_t* q, - const srsran_slot_cfg_t* slot, - const srsran_sch_cfg_nr_t* cfg, - uint8_t* data[SRSRAN_MAX_TB]); - -SRSRAN_API int -srsran_enb_dl_nr_pdsch_info(const srsran_enb_dl_nr_t* q, const srsran_sch_cfg_nr_t* cfg, char* str, uint32_t str_len); - -#endif // SRSRAN_ENB_DL_NR_H diff --git a/lib/include/srsran/phy/enb/enb_ul.h b/lib/include/srsran/phy/enb/enb_ul.h index 03c7e776b..fafc2609e 100644 --- a/lib/include/srsran/phy/enb/enb_ul.h +++ b/lib/include/srsran/phy/enb/enb_ul.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -53,6 +53,7 @@ typedef struct SRSRAN_API { srsran_cell_t cell; cf_t* sf_symbols; + cf_t* in_buffer; srsran_chest_ul_res_t chest_res; srsran_ofdm_t fft; diff --git a/lib/include/srsran/phy/fec/block/block.h b/lib/include/srsran/phy/fec/block/block.h index 8b88e1f54..953c238ce 100644 --- a/lib/include/srsran/phy/fec/block/block.h +++ b/lib/include/srsran/phy/fec/block/block.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/cbsegm.h b/lib/include/srsran/phy/fec/cbsegm.h index be01ed508..47fbc12ac 100644 --- a/lib/include/srsran/phy/fec/cbsegm.h +++ b/lib/include/srsran/phy/fec/cbsegm.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/convolutional/convcoder.h b/lib/include/srsran/phy/fec/convolutional/convcoder.h index 75ab8d04f..6c17ef173 100644 --- a/lib/include/srsran/phy/fec/convolutional/convcoder.h +++ b/lib/include/srsran/phy/fec/convolutional/convcoder.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/convolutional/rm_conv.h b/lib/include/srsran/phy/fec/convolutional/rm_conv.h index fb504edc9..a9203b7c8 100644 --- a/lib/include/srsran/phy/fec/convolutional/rm_conv.h +++ b/lib/include/srsran/phy/fec/convolutional/rm_conv.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/convolutional/viterbi.h b/lib/include/srsran/phy/fec/convolutional/viterbi.h index 043287fb6..1b3b4c808 100644 --- a/lib/include/srsran/phy/fec/convolutional/viterbi.h +++ b/lib/include/srsran/phy/fec/convolutional/viterbi.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/crc.h b/lib/include/srsran/phy/fec/crc.h index 30e225b69..23520e99f 100644 --- a/lib/include/srsran/phy/fec/crc.h +++ b/lib/include/srsran/phy/fec/crc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -56,7 +56,6 @@ SRSRAN_API uint32_t srsran_crc_attach_byte(srsran_crc_t* h, uint8_t* data, int l static inline void srsran_crc_checksum_put_byte(srsran_crc_t* h, uint8_t byte) { - uint64_t crc = h->crcinit; uint32_t idx; @@ -83,6 +82,8 @@ SRSRAN_API uint32_t srsran_crc_checksum_byte(srsran_crc_t* h, const uint8_t* dat SRSRAN_API uint32_t srsran_crc_checksum(srsran_crc_t* h, uint8_t* data, int len); +SRSRAN_API bool srsran_crc_match_byte(srsran_crc_t* h, uint8_t* data, int len); + SRSRAN_API bool srsran_crc_match(srsran_crc_t* h, uint8_t* data, int len); #endif // SRSRAN_CRC_H diff --git a/lib/include/srsran/phy/fec/ldpc/base_graph.h b/lib/include/srsran/phy/fec/ldpc/base_graph.h index 244b762fc..75184fa5b 100644 --- a/lib/include/srsran/phy/fec/ldpc/base_graph.h +++ b/lib/include/srsran/phy/fec/ldpc/base_graph.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -41,11 +41,9 @@ #include -#define SRSRAN_LDPC_BG1_MAX_LEN_CB 8448 /*!< \brief Maximum code block size for LDPC BG1 */ -#define SRSRAN_LDPC_BG2_MAX_LEN_CB 3840 /*!< \brief Maximum code block size for LDPC BG2 */ -#define SRSRAN_LDPC_MAX_LEN_CB \ - SRSRAN_MAX(SRSRAN_LDPC_BG1_MAX_LEN_CB, \ - SRSRAN_LDPC_BG2_MAX_LEN_CB) /*!< \brief Maximum code block size for LDPC BG1 or BG2 */ +#define SRSRAN_LDPC_BG1_MAX_LEN_CB 8448 /*!< \brief Maximum code block size for LDPC BG1 */ +#define SRSRAN_LDPC_BG2_MAX_LEN_CB 3840 /*!< \brief Maximum code block size for LDPC BG2 */ +#define SRSRAN_LDPC_MAX_LEN_CB SRSRAN_LDPC_BG1_MAX_LEN_CB /*!< \brief Maximum code block size for LDPC BG1 or BG2 */ #define BG1Nfull 68 /*!< \brief Number of variable nodes in BG1. */ #define BG1N 66 /*!< \brief Number of variable nodes in BG1 after puncturing. */ diff --git a/lib/include/srsran/phy/fec/ldpc/ldpc_common.h b/lib/include/srsran/phy/fec/ldpc/ldpc_common.h index c1b6d6ab7..4a54dc7a3 100644 --- a/lib/include/srsran/phy/fec/ldpc/ldpc_common.h +++ b/lib/include/srsran/phy/fec/ldpc/ldpc_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/ldpc/ldpc_decoder.h b/lib/include/srsran/phy/fec/ldpc/ldpc_decoder.h index c08ac2fc0..769b1c32c 100644 --- a/lib/include/srsran/phy/fec/ldpc/ldpc_decoder.h +++ b/lib/include/srsran/phy/fec/ldpc/ldpc_decoder.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/ldpc/ldpc_encoder.h b/lib/include/srsran/phy/fec/ldpc/ldpc_encoder.h index 79022c6be..f5e8245b2 100644 --- a/lib/include/srsran/phy/fec/ldpc/ldpc_encoder.h +++ b/lib/include/srsran/phy/fec/ldpc/ldpc_encoder.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/ldpc/ldpc_rm.h b/lib/include/srsran/phy/fec/ldpc/ldpc_rm.h index a7e349cc8..d67a16e7f 100644 --- a/lib/include/srsran/phy/fec/ldpc/ldpc_rm.h +++ b/lib/include/srsran/phy/fec/ldpc/ldpc_rm.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/polar/polar_chanalloc.h b/lib/include/srsran/phy/fec/polar/polar_chanalloc.h index db22c7b01..abdd10881 100644 --- a/lib/include/srsran/phy/fec/polar/polar_chanalloc.h +++ b/lib/include/srsran/phy/fec/polar/polar_chanalloc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/polar/polar_code.h b/lib/include/srsran/phy/fec/polar/polar_code.h index 65049f709..31a1b08ac 100644 --- a/lib/include/srsran/phy/fec/polar/polar_code.h +++ b/lib/include/srsran/phy/fec/polar/polar_code.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/polar/polar_decoder.h b/lib/include/srsran/phy/fec/polar/polar_decoder.h index 52f2e22b4..69a8a943a 100644 --- a/lib/include/srsran/phy/fec/polar/polar_decoder.h +++ b/lib/include/srsran/phy/fec/polar/polar_decoder.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/polar/polar_encoder.h b/lib/include/srsran/phy/fec/polar/polar_encoder.h index 205ce93da..c1f8442b0 100644 --- a/lib/include/srsran/phy/fec/polar/polar_encoder.h +++ b/lib/include/srsran/phy/fec/polar/polar_encoder.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/polar/polar_interleaver.h b/lib/include/srsran/phy/fec/polar/polar_interleaver.h index 8cc1fd5dd..46a634fde 100644 --- a/lib/include/srsran/phy/fec/polar/polar_interleaver.h +++ b/lib/include/srsran/phy/fec/polar/polar_interleaver.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/polar/polar_rm.h b/lib/include/srsran/phy/fec/polar/polar_rm.h index 9d1afe2ac..618939a3f 100644 --- a/lib/include/srsran/phy/fec/polar/polar_rm.h +++ b/lib/include/srsran/phy/fec/polar/polar_rm.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/polar/test/polar_sets.h b/lib/include/srsran/phy/fec/polar/test/polar_sets.h index ccdbc8c4b..31e1f1414 100644 --- a/lib/include/srsran/phy/fec/polar/test/polar_sets.h +++ b/lib/include/srsran/phy/fec/polar/test/polar_sets.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/polar/test/subchannel_allocation.h b/lib/include/srsran/phy/fec/polar/test/subchannel_allocation.h index eaccab5b8..452d996ba 100644 --- a/lib/include/srsran/phy/fec/polar/test/subchannel_allocation.h +++ b/lib/include/srsran/phy/fec/polar/test/subchannel_allocation.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/softbuffer.h b/lib/include/srsran/phy/fec/softbuffer.h index 6f502aef1..a00623f48 100644 --- a/lib/include/srsran/phy/fec/softbuffer.h +++ b/lib/include/srsran/phy/fec/softbuffer.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -62,7 +62,8 @@ SRSRAN_API int srsran_softbuffer_rx_init(srsran_softbuffer_rx_t* q, uint32_t nof * @param q The Rx soft-buffer pointer * @param max_cb The maximum number of code blocks to allocate * @param max_cb_size The code block size to allocate - * @return It returns SRSRAN_SUCCESS if it allocates the soft-buffer succesfully, otherwise it returns SRSRAN_ERROR code + * @return It returns SRSRAN_SUCCESS if it allocates the soft-buffer successfully, otherwise it returns SRSRAN_ERROR + * code */ SRSRAN_API int srsran_softbuffer_rx_init_guru(srsran_softbuffer_rx_t* q, uint32_t max_cb, uint32_t max_cb_size); @@ -74,6 +75,15 @@ SRSRAN_API void srsran_softbuffer_rx_reset_cb(srsran_softbuffer_rx_t* q, uint32_ SRSRAN_API void srsran_softbuffer_rx_free(srsran_softbuffer_rx_t* p); +/** + * @brief Resets a number of CB CRCs + * @note This function is intended to be used if all CB CRC have matched but the TB CRC failed. In this case, all CB + * should be decoded again + * @param q Rx soft-buffer object + * @param nof_cb Number of CB to reset + */ +SRSRAN_API void srsran_softbuffer_rx_reset_cb_crc(srsran_softbuffer_rx_t* q, uint32_t nof_cb); + SRSRAN_API int srsran_softbuffer_tx_init(srsran_softbuffer_tx_t* q, uint32_t nof_prb); /** @@ -81,7 +91,8 @@ SRSRAN_API int srsran_softbuffer_tx_init(srsran_softbuffer_tx_t* q, uint32_t nof * @param q The Tx soft-buffer pointer * @param max_cb The maximum number of code blocks to allocate * @param max_cb_size The code block size to allocate - * @return It returns SRSRAN_SUCCESS if it allocates the soft-buffer succesfully, otherwise it returns SRSRAN_ERROR code + * @return It returns SRSRAN_SUCCESS if it allocates the soft-buffer successfully, otherwise it returns SRSRAN_ERROR + * code */ SRSRAN_API int srsran_softbuffer_tx_init_guru(srsran_softbuffer_tx_t* q, uint32_t max_cb, uint32_t max_cb_size); diff --git a/lib/include/srsran/phy/fec/turbo/rm_turbo.h b/lib/include/srsran/phy/fec/turbo/rm_turbo.h index 5f7b55d2d..ccfa24a7a 100644 --- a/lib/include/srsran/phy/fec/turbo/rm_turbo.h +++ b/lib/include/srsran/phy/fec/turbo/rm_turbo.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/turbo/tc_interl.h b/lib/include/srsran/phy/fec/turbo/tc_interl.h index aa61ebf81..1e79e7ebf 100644 --- a/lib/include/srsran/phy/fec/turbo/tc_interl.h +++ b/lib/include/srsran/phy/fec/turbo/tc_interl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/turbo/turbocoder.h b/lib/include/srsran/phy/fec/turbo/turbocoder.h index b0047c452..9b0c6c925 100644 --- a/lib/include/srsran/phy/fec/turbo/turbocoder.h +++ b/lib/include/srsran/phy/fec/turbo/turbocoder.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/turbo/turbodecoder.h b/lib/include/srsran/phy/fec/turbo/turbodecoder.h index f068edff1..d5eb4100d 100644 --- a/lib/include/srsran/phy/fec/turbo/turbodecoder.h +++ b/lib/include/srsran/phy/fec/turbo/turbodecoder.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/turbo/turbodecoder_gen.h b/lib/include/srsran/phy/fec/turbo/turbodecoder_gen.h index c837b32cf..13aa30695 100644 --- a/lib/include/srsran/phy/fec/turbo/turbodecoder_gen.h +++ b/lib/include/srsran/phy/fec/turbo/turbodecoder_gen.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/turbo/turbodecoder_impl.h b/lib/include/srsran/phy/fec/turbo/turbodecoder_impl.h index c3c757f91..46464d2de 100644 --- a/lib/include/srsran/phy/fec/turbo/turbodecoder_impl.h +++ b/lib/include/srsran/phy/fec/turbo/turbodecoder_impl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/turbo/turbodecoder_iter.h b/lib/include/srsran/phy/fec/turbo/turbodecoder_iter.h index b6deae8b0..bcc9bf922 100644 --- a/lib/include/srsran/phy/fec/turbo/turbodecoder_iter.h +++ b/lib/include/srsran/phy/fec/turbo/turbodecoder_iter.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/turbo/turbodecoder_sse.h b/lib/include/srsran/phy/fec/turbo/turbodecoder_sse.h index 8018e94fe..d3c695d14 100644 --- a/lib/include/srsran/phy/fec/turbo/turbodecoder_sse.h +++ b/lib/include/srsran/phy/fec/turbo/turbodecoder_sse.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/fec/turbo/turbodecoder_win.h b/lib/include/srsran/phy/fec/turbo/turbodecoder_win.h index eee2d787a..cd73133c3 100644 --- a/lib/include/srsran/phy/fec/turbo/turbodecoder_win.h +++ b/lib/include/srsran/phy/fec/turbo/turbodecoder_win.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/gnb/gnb_dl.h b/lib/include/srsran/phy/gnb/gnb_dl.h new file mode 100644 index 000000000..d18d8bb1b --- /dev/null +++ b/lib/include/srsran/phy/gnb/gnb_dl.h @@ -0,0 +1,105 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_GNB_DL_H +#define SRSRAN_GNB_DL_H + +#include "srsran/phy/ch_estimation/csi_rs.h" +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/dft/ofdm.h" +#include "srsran/phy/phch/pdcch_cfg_nr.h" +#include "srsran/phy/phch/pdcch_nr.h" +#include "srsran/phy/phch/pdsch_nr.h" +#include "srsran/phy/sync/ssb.h" + +typedef struct SRSRAN_API { + srsran_pdsch_nr_args_t pdsch; + srsran_pdcch_nr_args_t pdcch; + uint32_t nof_tx_antennas; + uint32_t nof_max_prb; ///< Maximum number of allocated RB + double srate_hz; ///< Fix sampling rate, set to 0 for minimum to fit nof_max_prb + srsran_subcarrier_spacing_t scs; +} srsran_gnb_dl_args_t; + +typedef struct SRSRAN_API { + float srate_hz; + uint32_t symbol_sz; + uint32_t max_prb; + uint32_t nof_tx_antennas; + srsran_carrier_nr_t carrier; + srsran_pdcch_cfg_nr_t pdcch_cfg; + + srsran_ofdm_t fft[SRSRAN_MAX_PORTS]; + + cf_t* sf_symbols[SRSRAN_MAX_PORTS]; + srsran_pdsch_nr_t pdsch; + srsran_dmrs_sch_t dmrs; + + srsran_dci_nr_t dci; ///< Stores DCI configuration + srsran_pdcch_nr_t pdcch; + srsran_ssb_t ssb; +} srsran_gnb_dl_t; + +SRSRAN_API int srsran_gnb_dl_init(srsran_gnb_dl_t* q, cf_t* output[SRSRAN_MAX_PORTS], const srsran_gnb_dl_args_t* args); + +SRSRAN_API int srsran_gnb_dl_set_carrier(srsran_gnb_dl_t* q, const srsran_carrier_nr_t* carrier); + +SRSRAN_API int srsran_gnb_dl_set_ssb_config(srsran_gnb_dl_t* q, const srsran_ssb_cfg_t* ssb); + +SRSRAN_API int srsran_gnb_dl_set_pdcch_config(srsran_gnb_dl_t* q, + const srsran_pdcch_cfg_nr_t* cfg, + const srsran_dci_cfg_nr_t* dci_cfg); + +SRSRAN_API void srsran_gnb_dl_free(srsran_gnb_dl_t* q); + +SRSRAN_API int srsran_gnb_dl_base_zero(srsran_gnb_dl_t* q); + +SRSRAN_API void srsran_gnb_dl_gen_signal(srsran_gnb_dl_t* q); + +SRSRAN_API int srsran_gnb_dl_add_ssb(srsran_gnb_dl_t* q, const srsran_pbch_msg_nr_t* pbch_msg, uint32_t sf_idx); + +SRSRAN_API int +srsran_gnb_dl_pdcch_put_dl(srsran_gnb_dl_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_dl_nr_t* dci_dl); + +SRSRAN_API int +srsran_gnb_dl_pdcch_put_ul(srsran_gnb_dl_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_ul_nr_t* dci_ul); + +SRSRAN_API int srsran_gnb_dl_pdsch_put(srsran_gnb_dl_t* q, + const srsran_slot_cfg_t* slot, + const srsran_sch_cfg_nr_t* cfg, + uint8_t* data[SRSRAN_MAX_TB]); + +SRSRAN_API float srsran_gnb_dl_get_maximum_signal_power_dBfs(uint32_t nof_prb); + +SRSRAN_API int +srsran_gnb_dl_pdsch_info(const srsran_gnb_dl_t* q, const srsran_sch_cfg_nr_t* cfg, char* str, uint32_t str_len); + +SRSRAN_API int +srsran_gnb_dl_pdcch_dl_info(const srsran_gnb_dl_t* q, const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len); + +SRSRAN_API int +srsran_gnb_dl_pdcch_ul_info(const srsran_gnb_dl_t* q, const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len); + +SRSRAN_API int srsran_gnb_dl_nzp_csi_rs_put(srsran_gnb_dl_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource); + +#endif // SRSRAN_GNB_DL_H diff --git a/lib/include/srsran/phy/gnb/gnb_ul.h b/lib/include/srsran/phy/gnb/gnb_ul.h new file mode 100644 index 000000000..d86c833b8 --- /dev/null +++ b/lib/include/srsran/phy/gnb/gnb_ul.h @@ -0,0 +1,87 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_GNB_UL_H +#define SRSRAN_GNB_UL_H + +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/dft/ofdm.h" +#include "srsran/phy/phch/pucch_nr.h" +#include "srsran/phy/phch/pusch_nr.h" + +typedef struct SRSRAN_API { + srsran_pusch_nr_args_t pusch; + srsran_pucch_nr_args_t pucch; + float pusch_min_snr_dB; ///< Minimum SNR threshold to decode PUSCH, set to 0 for default value + uint32_t nof_max_prb; +} srsran_gnb_ul_args_t; + +typedef struct SRSRAN_API { + uint32_t max_prb; + srsran_carrier_nr_t carrier; + + srsran_ofdm_t fft; + + cf_t* sf_symbols[SRSRAN_MAX_PORTS]; + srsran_pusch_nr_t pusch; + srsran_pucch_nr_t pucch; + srsran_dmrs_sch_t dmrs; + srsran_chest_dl_res_t chest_pusch; + srsran_chest_ul_res_t chest_pucch; + float pusch_min_snr_dB; ///< Minimum measured DMRS SNR, below this threshold PUSCH is not decoded +} srsran_gnb_ul_t; + +SRSRAN_API int srsran_gnb_ul_init(srsran_gnb_ul_t* q, cf_t* input, const srsran_gnb_ul_args_t* args); + +SRSRAN_API void srsran_gnb_ul_free(srsran_gnb_ul_t* q); + +SRSRAN_API int srsran_gnb_ul_set_carrier(srsran_gnb_ul_t* q, const srsran_carrier_nr_t* carrier); + +SRSRAN_API int srsran_gnb_ul_fft(srsran_gnb_ul_t* q); + +SRSRAN_API int srsran_gnb_ul_get_pusch(srsran_gnb_ul_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_sch_cfg_nr_t* cfg, + const srsran_sch_grant_nr_t* grant, + srsran_pusch_res_nr_t* data); + +SRSRAN_API int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_pucch_nr_common_cfg_t* cfg, + const srsran_pucch_nr_resource_t* resource, + const srsran_uci_cfg_nr_t* uci_cfg, + srsran_uci_value_nr_t* uci_value, + srsran_csi_trs_measurements_t* meas); + +SRSRAN_API uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, + const srsran_pucch_nr_resource_t* resource, + const srsran_uci_data_nr_t* uci_data, + const srsran_csi_trs_measurements_t* csi, + char* str, + uint32_t str_len); + +SRSRAN_API uint32_t srsran_gnb_ul_pusch_info(srsran_gnb_ul_t* q, + const srsran_sch_cfg_nr_t* cfg, + const srsran_pusch_res_nr_t* res, + char* str, + uint32_t str_len); + +#endif // SRSRAN_GNB_UL_H diff --git a/lib/include/srsran/phy/io/binsource.h b/lib/include/srsran/phy/io/binsource.h index 1b5da7a6a..0b26d1ce5 100644 --- a/lib/include/srsran/phy/io/binsource.h +++ b/lib/include/srsran/phy/io/binsource.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/io/filesink.h b/lib/include/srsran/phy/io/filesink.h index 1950f8ebe..62ba064c1 100644 --- a/lib/include/srsran/phy/io/filesink.h +++ b/lib/include/srsran/phy/io/filesink.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,7 +45,7 @@ typedef struct SRSRAN_API { srsran_datatype_t type; } srsran_filesink_t; -SRSRAN_API int srsran_filesink_init(srsran_filesink_t* q, char* filename, srsran_datatype_t type); +SRSRAN_API int srsran_filesink_init(srsran_filesink_t* q, const char* filename, srsran_datatype_t type); SRSRAN_API void srsran_filesink_free(srsran_filesink_t* q); diff --git a/lib/include/srsran/phy/io/filesource.h b/lib/include/srsran/phy/io/filesource.h index 0e008a385..ddc88a776 100644 --- a/lib/include/srsran/phy/io/filesource.h +++ b/lib/include/srsran/phy/io/filesource.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,7 +33,7 @@ #define SRSRAN_FILESOURCE_H #include -#include +#include #include "srsran/config.h" #include "srsran/phy/io/format.h" @@ -44,7 +44,7 @@ typedef struct SRSRAN_API { srsran_datatype_t type; } srsran_filesource_t; -SRSRAN_API int srsran_filesource_init(srsran_filesource_t* q, char* filename, srsran_datatype_t type); +SRSRAN_API int srsran_filesource_init(srsran_filesource_t* q, const char* filename, srsran_datatype_t type); SRSRAN_API void srsran_filesource_free(srsran_filesource_t* q); diff --git a/lib/include/srsran/phy/io/format.h b/lib/include/srsran/phy/io/format.h index cbd9b6d8d..a7e6d7d43 100644 --- a/lib/include/srsran/phy/io/format.h +++ b/lib/include/srsran/phy/io/format.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/io/netsink.h b/lib/include/srsran/phy/io/netsink.h index d334c899c..8d75f2a29 100644 --- a/lib/include/srsran/phy/io/netsink.h +++ b/lib/include/srsran/phy/io/netsink.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/io/netsource.h b/lib/include/srsran/phy/io/netsource.h index 9a1b9ea3a..48f29ac34 100644 --- a/lib/include/srsran/phy/io/netsource.h +++ b/lib/include/srsran/phy/io/netsource.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/mimo/layermap.h b/lib/include/srsran/phy/mimo/layermap.h index bab2c655e..40b1c7746 100644 --- a/lib/include/srsran/phy/mimo/layermap.h +++ b/lib/include/srsran/phy/mimo/layermap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/mimo/precoding.h b/lib/include/srsran/phy/mimo/precoding.h index a00ead41d..6b840b70a 100644 --- a/lib/include/srsran/phy/mimo/precoding.h +++ b/lib/include/srsran/phy/mimo/precoding.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -89,7 +89,7 @@ SRSRAN_API int srsran_predecoding_diversity(cf_t* y, SRSRAN_API int srsran_predecoding_diversity_multi(cf_t* y[SRSRAN_MAX_PORTS], cf_t* h[SRSRAN_MAX_PORTS][SRSRAN_MAX_PORTS], cf_t* x[SRSRAN_MAX_LAYERS], - float* csi[SRSRAN_MAX_LAYERS], + float* csi[SRSRAN_MAX_CODEWORDS], int nof_rxant, int nof_ports, int nof_symbols, diff --git a/lib/include/srsran/phy/modem/demod_hard.h b/lib/include/srsran/phy/modem/demod_hard.h index c466f100d..14f0bb8ee 100644 --- a/lib/include/srsran/phy/modem/demod_hard.h +++ b/lib/include/srsran/phy/modem/demod_hard.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/modem/demod_soft.h b/lib/include/srsran/phy/modem/demod_soft.h index 052e94319..62f6b6b03 100644 --- a/lib/include/srsran/phy/modem/demod_soft.h +++ b/lib/include/srsran/phy/modem/demod_soft.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/modem/evm.h b/lib/include/srsran/phy/modem/evm.h index 2f464ee00..7d5452f53 100644 --- a/lib/include/srsran/phy/modem/evm.h +++ b/lib/include/srsran/phy/modem/evm.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/modem/mod.h b/lib/include/srsran/phy/modem/mod.h index 22aa05873..f9e8359c0 100644 --- a/lib/include/srsran/phy/modem/mod.h +++ b/lib/include/srsran/phy/modem/mod.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/modem/modem_table.h b/lib/include/srsran/phy/modem/modem_table.h index c6f0e13e9..bf3945fbf 100644 --- a/lib/include/srsran/phy/modem/modem_table.h +++ b/lib/include/srsran/phy/modem/modem_table.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/cqi.h b/lib/include/srsran/phy/phch/cqi.h index ce01c6ccc..f2ddd0d90 100644 --- a/lib/include/srsran/phy/phch/cqi.h +++ b/lib/include/srsran/phy/phch/cqi.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -59,7 +59,7 @@ typedef struct { uint32_t ri_idx; bool ri_idx_present; bool format_is_subband; - uint32_t subband_size; + uint8_t subband_wideband_ratio; ///< K value in TS 36.331. 0 for wideband reporting, (1..4) otherwise srsran_cqi_report_mode_t periodic_mode; srsran_cqi_report_mode_t aperiodic_mode; } srsran_cqi_report_cfg_t; @@ -87,7 +87,7 @@ typedef struct SRSRAN_API { uint8_t wideband_cqi; // 4-bit width uint8_t subband_diff_cqi; // 2-bit width uint32_t position_subband; // L-bit width -} srsran_cqi_ue_subband_t; +} srsran_cqi_ue_diff_subband_t; /* Table 5.2.3.3.1-1: Fields for channel quality information feedback for wideband CQI reports (transmission mode 1, transmission mode 2, transmission mode 3, transmission mode 7 and @@ -109,12 +109,12 @@ typedef struct SRSRAN_API { typedef struct SRSRAN_API { uint8_t subband_cqi; // 4-bit width uint8_t subband_label; // 1- or 2-bit width -} srsran_cqi_format2_subband_t; +} srsran_cqi_ue_subband_t; typedef enum { SRSRAN_CQI_TYPE_WIDEBAND = 0, - SRSRAN_CQI_TYPE_SUBBAND, SRSRAN_CQI_TYPE_SUBBAND_UE, + SRSRAN_CQI_TYPE_SUBBAND_UE_DIFF, SRSRAN_CQI_TYPE_SUBBAND_HL } srsran_cqi_type_t; @@ -127,6 +127,7 @@ typedef struct SRSRAN_API { uint32_t scell_index; ///< Indicates the cell/carrier the measurement belongs, use 0 for PCell uint32_t L; uint32_t N; + uint32_t sb_idx; srsran_cqi_type_t type; uint32_t ri_len; } srsran_cqi_cfg_t; @@ -134,8 +135,8 @@ typedef struct SRSRAN_API { typedef struct { union { srsran_cqi_format2_wideband_t wideband; - srsran_cqi_format2_subband_t subband; srsran_cqi_ue_subband_t subband_ue; + srsran_cqi_ue_diff_subband_t subband_ue_diff; srsran_cqi_hl_subband_t subband_hl; }; bool data_crc; @@ -143,7 +144,8 @@ typedef struct { SRSRAN_API int srsran_cqi_size(srsran_cqi_cfg_t* cfg); -SRSRAN_API int srsran_cqi_value_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, uint8_t* buff); +SRSRAN_API int +srsran_cqi_value_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, uint8_t buff[SRSRAN_CQI_MAX_BITS]); SRSRAN_API int srsran_cqi_value_unpack(srsran_cqi_cfg_t* cfg, uint8_t buff[SRSRAN_CQI_MAX_BITS], srsran_cqi_value_t* value); @@ -154,11 +156,35 @@ srsran_cqi_value_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, char SRSRAN_API bool srsran_cqi_periodic_send(const srsran_cqi_report_cfg_t* periodic_cfg, uint32_t tti, srsran_frame_type_t frame_type); +SRSRAN_API bool srsran_cqi_periodic_is_subband(const srsran_cqi_report_cfg_t* cfg, + uint32_t tti, + uint32_t nof_prb, + srsran_frame_type_t frame_type); + SRSRAN_API bool srsran_cqi_periodic_ri_send(const srsran_cqi_report_cfg_t* periodic_cfg, uint32_t tti, srsran_frame_type_t frame_type); +SRSRAN_API uint32_t srsran_cqi_periodic_sb_bw_part_idx(const srsran_cqi_report_cfg_t* cfg, + uint32_t tti, + uint32_t nof_prb, + srsran_frame_type_t frame_type); + SRSRAN_API int srsran_cqi_hl_get_no_subbands(int nof_prb); +/** + * @brief Returns the number of bits to index a bandwidth part (L) + * + * @remark Implemented according to TS 38.213 section 7.2.2 Periodic CSI Reporting using PUCCH, paragraph that refers to + * `L-bit label indexed in the order of increasing frequency, where L = ceil(log2(nof_prb/k/J))` + * + */ +SRSRAN_API int srsran_cqi_hl_get_L(int nof_prb); + +SRSRAN_API uint32_t srsran_cqi_get_sb_idx(uint32_t tti, + uint32_t subband_label, + const srsran_cqi_report_cfg_t* cqi_report_cfg, + const srsran_cell_t* cell); + SRSRAN_API uint8_t srsran_cqi_from_snr(float snr); SRSRAN_API float srsran_cqi_to_coderate(uint32_t cqi, bool use_alt_table); diff --git a/lib/include/srsran/phy/phch/csi.h b/lib/include/srsran/phy/phch/csi.h index 342c45cc5..d1b6783b0 100644 --- a/lib/include/srsran/phy/phch/csi.h +++ b/lib/include/srsran/phy/phch/csi.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,18 +25,42 @@ #include "uci_cfg_nr.h" /** - * @brief Fills Uplink Control Information data with triggered reports for the given slot - * @param cfg CSI report configuration - * @param slot_idx Slot index within the radio frame - * @param measurements CSI measurements - * @param[out] uci_data Uplink Control Information data + * @brief Processes a new NZP-CSI-RS channel measurement, it maps the given measurement into the current measurements + * applying an exponential moving average filter + * @param csi_resources CSI Resource configuration, links NZP-CSI-RS resources with CSI Measurements + * @param measurements Current CSI measurements + * @param new_measure New NZP-CSI-RS channel measurement + * @param nzp_csi_rs_id NZP-CSI-RS resource set identifier + * @return SRSRAN_SUCCESS if the provided information is valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int +srsran_csi_new_nzp_csi_rs_measurement(const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + const srsran_csi_channel_measurements_t* new_measure, + uint32_t nzp_csi_rs_id); + +/** + * @brief Generates CSI report configuration from the higher layer configuration for a given slot + * @param cfg Higher layer report configuration + * @param slot_cfg Current slot configuration + * @param[out] report_cfg Report configuration for the given slot * @return The number CSI reports for transmission if the provided data is valid, SRSRAN_ERROR code otherwise */ -SRSRAN_API int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, - uint32_t slot_idx, - const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], - srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], - srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]); +SRSRAN_API int srsran_csi_reports_generate(const srsran_csi_hl_cfg_t* cfg, + const srsran_slot_cfg_t* slot_cfg, + srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_SLOT_MAX_NOF_REPORT]); + +/** + * @brief Quantifies a given set of CSI reports from the given set of measurements + * @param reports Set of report configuration + * @param measurements Set of measurements to quantify + * @param report_value Set of report values + * @return The number CSI reports for transmission if the provided data is valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int +srsran_csi_reports_quantify(const srsran_csi_report_cfg_t reports[SRSRAN_CSI_SLOT_MAX_NOF_REPORT], + const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_report_value_t report_value[SRSRAN_CSI_SLOT_MAX_NOF_REPORT]); /** * @brief Compute number of CSI bits necessary to transmit all the CSI reports for a PUCCH transmission diff --git a/lib/include/srsran/phy/phch/csi_cfg.h b/lib/include/srsran/phy/phch/csi_cfg.h index da65dd460..89c7b5502 100644 --- a/lib/include/srsran/phy/phch/csi_cfg.h +++ b/lib/include/srsran/phy/phch/csi_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,11 +31,32 @@ */ #define SRSRAN_CSI_MAX_NOF_REPORT 48 +/** + * @brief Maximum number of supported simultaneous CSI reports in a single slot transmission + */ +#define SRSRAN_CSI_SLOT_MAX_NOF_REPORT 2 + /** * @brief Maximum number of CSI-RS resources defined in TS 38.331 maxNrofCSI-ResourceConfigurations */ #define SRSRAN_CSI_MAX_NOF_RESOURCES 112 +/** + * @brief Maximum number of NZP-CSI-RS resources sets per config, defined in TS 38.331 + * maxNrofNZP-CSI-RS-ResourceSetsPerConfig + */ +#define SRSRAN_CSI_MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_X_CONFIG 16 + +/** + * @brief Maximum number of CSI-SSB resources sets per config, defined in TS 38.331 maxNrofCSI-SSB-ResourceSetsPerConfig + */ +#define SRSRAN_CSI_MAX_NOF_CSI_SSB_RESOURCE_SETS_X_CONFIG 1 + +/** + * @brief Maximum number of CSI-SSB resources sets per config, defined in TS 38.331 maxNrofCSI-IM-ResourceSetsPerConfig + */ +#define SRSRAN_CSI_MAX_NOF_CSI_IM_RESOURCE_SETS_X_CONFIG 12 + /** * @brief CSI report types defined in TS 38.331 CSI-ReportConfig */ @@ -106,16 +127,41 @@ typedef struct SRSRAN_API { srsran_csi_report_freq_t freq_cfg; ///< Determine whether it is wideband or subband } srsran_csi_hl_report_cfg_t; +/** + * @brief CSI Resource configuration + */ +typedef struct SRSRAN_API { + enum { + SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NONE = 0, + SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NZP_CSI_RS_SSB, + SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_IM + } type; + union { + struct { + uint32_t nzp_csi_rs_resource_set_id_list[SRSRAN_CSI_MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_X_CONFIG]; + uint32_t nzp_csi_rs_resource_set_id_list_count; + uint32_t csi_ssb_rs_resource_set_id_list[SRSRAN_CSI_MAX_NOF_CSI_SSB_RESOURCE_SETS_X_CONFIG]; + uint32_t csi_ssb_rs_resource_set_id_list_count; + } nzp_csi_rs_ssb; + struct { + uint32_t resource_set_id_list[SRSRAN_CSI_MAX_NOF_CSI_IM_RESOURCE_SETS_X_CONFIG]; + uint32_t resource_set_id_list_count; + } csi_im; + }; +} srsran_csi_hl_resource_cfg_t; + /** * @brief General CSI configuration provided by higher layers */ typedef struct SRSRAN_API { - srsran_csi_hl_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration - // ... add here physical CSI measurement sets + srsran_csi_hl_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration indexed by + ///< reportConfigId + srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES]; ///< Configured CSI resource settings, + ///< indexed by csi-ResourceConfigId } srsran_csi_hl_cfg_t; /** - * @brief Generic measurement structure + * @brief Generic CSI measurement structure, used for generating CSI reports */ typedef struct SRSRAN_API { uint32_t cri; ///< CSI-RS Resource Indicator @@ -126,16 +172,14 @@ typedef struct SRSRAN_API { // Resource set context uint32_t nof_ports; ///< Number of antenna ports uint32_t K_csi_rs; ///< Number of CSI-RS in the corresponding resource set -} srsran_csi_measurements_t; +} srsran_csi_channel_measurements_t; /** * @brief CSI report configuration + * @note An unset report is marked with `cfg.type = SRSRAN_CSI_REPORT_TYPE_NONE` */ typedef struct SRSRAN_API { - srsran_csi_report_type_t type; ///< CSI report type (none, periodic, semiPersistentOnPUCCH, ...) - srsran_csi_report_quantity_t quantity; ///< Report quantity - srsran_pucch_nr_resource_t pucch_resource; ///< PUCCH resource to use for periodic reporting - srsran_csi_report_freq_t freq_cfg; ///< Determine whether it is wideband or subband + srsran_csi_hl_report_cfg_t cfg; ///< Higher layer CSI report configuration // Resource set context uint32_t nof_ports; ///< Number of antenna ports @@ -161,16 +205,6 @@ typedef struct SRSRAN_API { void* none; srsran_csi_report_wideband_cri_ri_pmi_cqi_t wideband_cri_ri_pmi_cqi; }; - bool valid; ///< Used by receiver only } srsran_csi_report_value_t; -/** - * @brief Complete report configuration and value - */ -typedef struct SRSRAN_API { - srsran_csi_report_cfg_t cfg[SRSRAN_CSI_MAX_NOF_REPORT]; ///< Configuration ready for encoding - srsran_csi_report_value_t value[SRSRAN_CSI_MAX_NOF_REPORT]; ///< Quantified values - uint32_t nof_reports; ///< Total number of reports to transmit -} srsran_csi_reports_t; - #endif // SRSRAN_CSI_CFG_H diff --git a/lib/include/srsran/phy/phch/dci.h b/lib/include/srsran/phy/phch/dci.h index 1763224fc..9ee66d19b 100644 --- a/lib/include/srsran/phy/phch/dci.h +++ b/lib/include/srsran/phy/phch/dci.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -79,7 +79,6 @@ typedef struct SRSRAN_API { } srsran_dci_tb_t; typedef struct SRSRAN_API { - uint16_t rnti; srsran_dci_format_t format; srsran_dci_location_t location; @@ -103,10 +102,10 @@ typedef struct SRSRAN_API { bool power_offset; uint8_t tpc_pucch; - // RA order - bool is_ra_order; - uint32_t ra_preamble; - uint32_t ra_mask_idx; + // PDCCH order + bool is_pdcch_order; + uint32_t preamble_idx; + uint32_t prach_mask_idx; // Release 10 uint32_t cif; @@ -130,7 +129,6 @@ typedef struct SRSRAN_API { /** Unpacked DCI Format0 message */ typedef struct SRSRAN_API { - uint16_t rnti; srsran_dci_format_t format; srsran_dci_location_t location; @@ -238,8 +236,9 @@ SRSRAN_API char* srsran_dci_format_string_short(srsran_dci_format_t format); SRSRAN_API bool srsran_location_find(const srsran_dci_location_t* locations, uint32_t nof_locations, srsran_dci_location_t x); -SRSRAN_API bool -srsran_location_find_ncce(const srsran_dci_location_t* locations, uint32_t nof_locations, uint32_t ncce); +SRSRAN_API bool srsran_location_find_location(const srsran_dci_location_t* locations, + uint32_t nof_locations, + const srsran_dci_location_t* location); SRSRAN_API int srsran_dci_location_set(srsran_dci_location_t* c, uint32_t L, uint32_t nCCE); diff --git a/lib/include/srsran/phy/phch/dci_nbiot.h b/lib/include/srsran/phy/phch/dci_nbiot.h index 12798e8c7..d25d33a35 100644 --- a/lib/include/srsran/phy/phch/dci_nbiot.h +++ b/lib/include/srsran/phy/phch/dci_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/dci_nr.h b/lib/include/srsran/phy/phch/dci_nr.h index c48d0209a..79f0a917e 100644 --- a/lib/include/srsran/phy/phch/dci_nr.h +++ b/lib/include/srsran/phy/phch/dci_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,11 +66,11 @@ typedef struct SRSRAN_API { bool enable_transform_precoding; ///< Set to true if PUSCH transform precoding is enabled bool dynamic_dual_harq_ack_codebook; ///< Set to true if HARQ-ACK codebook is set to dynamic with 2 sub-codebooks bool pusch_tx_config_non_codebook; ///< Set to true if PUSCH txConfig is set to non-codebook - bool pusch_dmrs_type2; ///< Set to true if PUSCH DMRS are type 2 - bool pusch_dmrs_double; ///< Set to true if PUSCH DMRS are 2 symbol long bool pusch_ptrs; ///< Set to true if PT-RS are enabled for PUSCH transmission bool pusch_dynamic_betas; ///< Set to true if beta offsets operation is not semi-static srsran_resource_alloc_t pusch_alloc_type; ///< PUSCH resource allocation type + srsran_dmrs_sch_type_t pusch_dmrs_type; ///< PUSCH DMRS type + srsran_dmrs_sch_len_t pusch_dmrs_max_len; ///< PUSCH DMRS maximum length /// Format 1_1 specific configuration (for PDSCH only) uint32_t nof_dl_bwp; ///< Number of DL BWPs excluding the initial UL BWP, mentioned in the TS as N_BWP_RRC @@ -83,13 +83,12 @@ typedef struct SRSRAN_API { bool pdsch_rm_pattern2; ///< Set to true if rateMatchPatternGroup2 is configured bool pdsch_2cw; ///< Set to true if maxNrofCodeWordsScheduledByDCI is set to 2 in any BWP bool multiple_scell; ///< Set to true if configured with multiple serving cell - bool pdsch_dmrs_type2; ///< Set to true if PDSCH DMRS are type 2 - bool pdsch_dmrs_double; ///< Set to true if PDSCH DMRS are 2 symbol long bool pdsch_tci; ///< Set to true if tci-PresentInDCI is enabled bool pdsch_cbg_flush; ///< Set to true if codeBlockGroupFlushIndicator is true bool pdsch_dynamic_bundling; ///< Set to true if prb-BundlingType is set to dynamicBundling - srsran_resource_alloc_t pdsch_alloc_type; ///< PDSCH resource allocation type, set to 0 for default - + srsran_resource_alloc_t pdsch_alloc_type; ///< PDSCH resource allocation type, set to 0 for default + srsran_dmrs_sch_type_t pdsch_dmrs_type; ///< PDSCH DMRS type + srsran_dmrs_sch_len_t pdsch_dmrs_max_len; ///< PDSCH DMRS maximum length } srsran_dci_cfg_nr_t; /** @@ -121,12 +120,13 @@ typedef struct SRSRAN_API { * @brief Describes the NR DCI search context */ typedef struct SRSRAN_API { - srsran_dci_location_t location; ///< DCI location - srsran_search_space_type_t ss_type; ///< Search space type - uint32_t coreset_id; ///< CORESET identifier - srsran_rnti_type_t rnti_type; ///< RNTI type - srsran_dci_format_nr_t format; ///< DCI format - uint16_t rnti; ///< UE temporal RNTI + srsran_dci_location_t location; ///< DCI location + srsran_search_space_type_t ss_type; ///< Search space type + uint32_t coreset_id; ///< CORESET identifier + uint32_t coreset_start_rb; ///< CORESET lowest RB index in the resource grid + srsran_rnti_type_t rnti_type; ///< RNTI type + srsran_dci_format_nr_t format; ///< DCI format + uint16_t rnti; ///< UE temporal RNTI } srsran_dci_ctx_t; /** @@ -157,8 +157,14 @@ typedef struct SRSRAN_API { uint32_t pid; ///< HARQ process number uint32_t dai; ///< Downlink assignment index uint32_t tpc; ///< TPC command for scheduled PUCCH - uint32_t pucch_resource; ///< PUCCH resource indicator + uint32_t pucch_resource; ///< PUCCH resource indicator for HARQ feedback + ///< @note PUCCH resource is selected from PUCCH-ResourceSet if available, otherwise the UE + ///< shall pick a pucch-ResourceCommon from Table 9.2.1-1. uint32_t harq_feedback; ///< PDSCH-to-HARQ_feedback timing indicator + ///< @note harq_feedback for format 1_0 indicates the delay between the PDSCH reception and + ///< the UL transmission timing + ///< @note harq_feedback for format 1_1 is index of the delay indicated in DL data to UL ACK + ///< dedicated configuration table // P-RNTI specific fields uint32_t smi; ///< Short Messages Indicator @@ -186,6 +192,9 @@ typedef struct SRSRAN_API { uint32_t cbg_flush; ///< CBG flushing out information (CBGFI) uint32_t dmrs_id; ///< DMRS sequence initialization + // DL context from unpacking. Required for resource allocation + uint32_t coreset0_bw; ///< CORESET0 size used for frequency resource allocation + } srsran_dci_dl_nr_t; /** @@ -281,7 +290,7 @@ SRSRAN_API int srsran_dci_nr_dl_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_ /** * @brief Packs an UL NR DCI into a DCI message - * @param q NR DCI object with precomputed DCI parameters + * @param q NR DCI object with precomputed DCI parameters (not required for RAR type, set to NULL) * @param dci UL NR DCI to pack (serialize) * @param[out] msg resultant DCI message * @return SRSRAN_SUCCESS if provided arguments are valid, SRSRAN_ERROR code otherwise @@ -290,7 +299,7 @@ SRSRAN_API int srsran_dci_nr_ul_pack(const srsran_dci_nr_t* q, const srsran_dci_ /** * @brief Unpacks an NR DCI message into an UL NR DCI - * @param q NR DCI object with precomputed DCI parameters + * @param q NR DCI object with precomputed DCI parameters (not required for RAR type, set to NULL) * @param msg DCI message to unpack (deserialize) * @param[out] dci Resultant unpacked UL DCI * @return SRSRAN_SUCCESS if provided arguments are valid, SRSRAN_ERROR code otherwise @@ -304,7 +313,7 @@ SRSRAN_API int srsran_dci_nr_ul_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_ * @param str_len Destination string length * @return The number of written characters */ -SRSRAN_API int srsran_dci_ctx_to_str(const srsran_dci_ctx_t* ctx, char* str, uint32_t str_len); +SRSRAN_API uint32_t srsran_dci_ctx_to_str(const srsran_dci_ctx_t* ctx, char* str, uint32_t str_len); /** * @brief Stringifies a DL NR DCI structure @@ -314,8 +323,10 @@ SRSRAN_API int srsran_dci_ctx_to_str(const srsran_dci_ctx_t* ctx, char* str, uin * @param str_len Destination string length * @return The number of written characters */ -SRSRAN_API int -srsran_dci_dl_nr_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len); +SRSRAN_API uint32_t srsran_dci_dl_nr_to_str(const srsran_dci_nr_t* q, + const srsran_dci_dl_nr_t* dci, + char* str, + uint32_t str_len); /** * @brief Stringifies an UL NR DCI structure @@ -325,7 +336,9 @@ srsran_dci_dl_nr_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, * @param str_len Destination string length * @return The number of written characters */ -SRSRAN_API int -srsran_dci_ul_nr_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len); +SRSRAN_API uint32_t srsran_dci_ul_nr_to_str(const srsran_dci_nr_t* q, + const srsran_dci_ul_nr_t* dci, + char* str, + uint32_t str_len); #endif // SRSRAN_DCI_NR_H diff --git a/lib/include/srsran/phy/phch/harq_ack.h b/lib/include/srsran/phy/phch/harq_ack.h new file mode 100644 index 000000000..f325ec7fe --- /dev/null +++ b/lib/include/srsran/phy/phch/harq_ack.h @@ -0,0 +1,49 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_HARQ_ACK_H +#define SRSRAN_HARQ_ACK_H + +#include "srsran/phy/phch/dci_nr.h" +#include "srsran/phy/phch/harq_ack_cfg.h" +#include "srsran/phy/phch/uci_cfg_nr.h" + +SRSRAN_API int srsran_harq_ack_resource(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_dci_dl_nr_t* dci_dl, + srsran_harq_ack_resource_t* pdsch_ack_resource); + +SRSRAN_API int srsran_harq_ack_gen_uci_cfg(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_pdsch_ack_nr_t* ack_info, + srsran_uci_cfg_nr_t* uci_cfg); + +SRSRAN_API int srsran_harq_ack_pack(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_pdsch_ack_nr_t* ack_info, + srsran_uci_data_nr_t* uci_data); + +SRSRAN_API int srsran_harq_ack_unpack(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_uci_data_nr_t* uci_data, + srsran_pdsch_ack_nr_t* ack_info); + +SRSRAN_API int srsran_harq_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, const srsran_harq_ack_m_t* m); + +SRSRAN_API uint32_t srsran_harq_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len); + +#endif // SRSRAN_HARQ_ACK_H diff --git a/lib/include/srsran/phy/phch/harq_ack_cfg.h b/lib/include/srsran/phy/phch/harq_ack_cfg.h new file mode 100644 index 000000000..9d56f3c35 --- /dev/null +++ b/lib/include/srsran/phy/phch/harq_ack_cfg.h @@ -0,0 +1,117 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_HARQ_ACK_CFG_H +#define SRSRAN_HARQ_ACK_CFG_H + +#include "srsran/phy/common/phy_common_nr.h" +#include +#include + +/** + * @brief Maximum number of HARQ ACK feedback bits that can be carried in Uplink Control Information (UCI) message + */ +#define SRSRAN_HARQ_ACK_MAX_NOF_BITS 360 + +/** + * @brief Describes a HARQ ACK resource configuration + */ +typedef struct { + uint32_t scell_idx; ///< Serving cell index + uint32_t v_dai_dl; ///< Downlink Assigment Index + bool dci_format_1_1; ///< Set to true if the PDSCH transmission is triggered by a PDCCH DCI format 1_1 transmission + uint32_t k1; ///< HARQ feedback timing + uint32_t pid; ///< HARQ process identifier + uint16_t rnti; + uint32_t pucch_resource_id; + uint32_t n_cce; +} srsran_harq_ack_resource_t; + +/** + * @brief Describes a single PDSCH transmission HARQ ACK feedback + */ +typedef struct { + srsran_harq_ack_resource_t resource; + uint8_t value[SRSRAN_MAX_CODEWORDS]; // 0/1 or 2 for DTX + bool present; // set to true if there is a PDSCH on serving cell c associated with PDCCH in PDCCH monitoring occasion + // m, or there is a PDCCH indicating SPS PDSCH release on serving cell c + bool dl_bwp_changed; // set to true if PDCCH monitoring occasion m is before an active DL BWP change on serving cell c + bool ul_bwp_changed; // set to true if an active UL BWP change on the PCell and an active DL BWP change is not + // triggered by a DCI format 1_1 in PDCCH monitoring occasion m + bool second_tb_present; // set to true if two TB were detected in the PDCCH monitoring occasion m +} srsran_harq_ack_m_t; + +#define SRSRAN_UCI_NR_MAX_M 10 + +/** + * @brief Describes a collection of PDSCH HARQ ACK feedback for a number of PDSCH transmissions within a component + * carrier + */ +typedef struct { + uint32_t M; + srsran_harq_ack_m_t m[SRSRAN_UCI_NR_MAX_M]; +} srsran_harq_ack_cc_t; + +/** + * @brief Describes a collection of PDSCH HARQ ACK feedback for a number of component carriers + */ +typedef struct { + srsran_harq_ack_cc_t cc[SRSRAN_MAX_CARRIERS]; + uint32_t nof_cc; + bool use_pusch; // Set to true, if UCI bits are carried by PUSCH +} srsran_pdsch_ack_nr_t; + +/** + * @brief Describes higher layer HARQ ACK feedback for PDSCH configuration + */ +typedef struct SRSRAN_API { + bool harq_ack_spatial_bundling_pucch; ///< Param harq-ACK-SpatialBundlingPUCCH, set to true if provided + bool harq_ack_spatial_bundling_pusch; ///< Param harq-ACK-SpatialBundlingPUSCH, set to true if provided + srsran_harq_ack_codebook_t harq_ack_codebook; ///< pdsch-HARQ-ACK-Codebook configuration + bool max_cw_sched_dci_is_2; ///< Param maxNrofCodeWordsScheduledByDCI, set to true if present and equal to 2 + + uint32_t dl_data_to_ul_ack[SRSRAN_MAX_NOF_DL_DATA_TO_UL]; + uint32_t nof_dl_data_to_ul_ack; +} srsran_harq_ack_cfg_hl_t; + +/** + * @brief Describes HARQ ACK bit configuration for UCI encoding/decoding + * @note if tb0 and tb1 are false, the HARQ ACK bit is not used + * @note if tb0 and tb1 are true, the HARQ ACK feedback are bundled, the actual HARQ ACK feedback bit is the result of + * the AND operation + */ +typedef struct SRSRAN_API { + bool tb0; ///< Set to true if this ACK bit is mapped into TB index 0 + bool tb1; ///< Set to true if this ACK bit is mapped into TB index 1 + uint32_t cc_idx; ///< Component carrier index + uint32_t m_idx; ///< M resource index for packing/unpacking + uint32_t pid; ///< HARQ process identifier +} srsran_harq_ack_bit_t; + +/** + * @brief Describes HARQ ACK feedback configuration for UCI encoding/decoding + */ +typedef struct SRSRAN_API { + srsran_harq_ack_bit_t bits[SRSRAN_HARQ_ACK_MAX_NOF_BITS]; ///< HARQ-ACK feedback bits + uint32_t count; ///< Number of ACK bits +} srsran_harq_ack_cfg_t; + +#endif // SRSRAN_HARQ_ACK_CFG_H diff --git a/lib/include/srsran/phy/phch/mib_sl.h b/lib/include/srsran/phy/phch/mib_sl.h index 4f174c365..216c99712 100644 --- a/lib/include/srsran/phy/phch/mib_sl.h +++ b/lib/include/srsran/phy/phch/mib_sl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/npbch.h b/lib/include/srsran/phy/phch/npbch.h index d517d3dda..9801d4197 100644 --- a/lib/include/srsran/phy/phch/npbch.h +++ b/lib/include/srsran/phy/phch/npbch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -58,7 +58,7 @@ typedef struct { * * Reference: 3GPP TS 36.211 version 13.2.0 Release 13 Sec. 10.2.4 */ -typedef struct SRS_API { +typedef struct SRSRAN_API { srsran_nbiot_cell_t cell; uint32_t frame_idx; diff --git a/lib/include/srsran/phy/phch/npdcch.h b/lib/include/srsran/phy/phch/npdcch.h index 0554f7d3e..70731f850 100644 --- a/lib/include/srsran/phy/phch/npdcch.h +++ b/lib/include/srsran/phy/phch/npdcch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/npdsch.h b/lib/include/srsran/phy/phch/npdsch.h index 7e9fdaad5..a3e86dc58 100644 --- a/lib/include/srsran/phy/phch/npdsch.h +++ b/lib/include/srsran/phy/phch/npdsch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/npdsch_cfg.h b/lib/include/srsran/phy/phch/npdsch_cfg.h index f4d6f6d2b..1854876ce 100644 --- a/lib/include/srsran/phy/phch/npdsch_cfg.h +++ b/lib/include/srsran/phy/phch/npdsch_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pbch.h b/lib/include/srsran/phy/phch/pbch.h index 0e70ea735..45c5a393d 100644 --- a/lib/include/srsran/phy/phch/pbch.h +++ b/lib/include/srsran/phy/phch/pbch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pbch_msg_nr.h b/lib/include/srsran/phy/phch/pbch_msg_nr.h new file mode 100644 index 000000000..9fe86c763 --- /dev/null +++ b/lib/include/srsran/phy/phch/pbch_msg_nr.h @@ -0,0 +1,76 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_PBCH_MSG_NR_H +#define SRSRAN_PBCH_MSG_NR_H + +#include "srsran/config.h" +#include "srsran/phy/common/phy_common_nr.h" +#include +#include + +/** + * @brief NR PBCH payload size generated by higher layers, deduced from TS 38.331 MIB description + */ +#define SRSRAN_PBCH_MSG_NR_SZ 24 + +/** + * @brief Define the payload buffer for SRSRAN_PBCH_MSG_NR_SZ to be 32 for alignment purposes + */ +#define SRSRAN_PBCH_MSG_NR_MAX_SZ 32 + +/** + * @brief Describes the NR PBCH message + */ +typedef struct SRSRAN_API { + uint8_t payload[SRSRAN_PBCH_MSG_NR_MAX_SZ]; ///< Actual PBCH payload provided by higher layers + uint8_t sfn_4lsb; ///< SFN 4 LSB + uint8_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1 + uint8_t k_ssb_msb; ///< Subcarrier offset MSB described in TS 38.211 7.4.3.1 + bool hrf; ///< Half Radio Frame bit + bool crc; ///< Decoder only, it is true only if the received CRC matches +} srsran_pbch_msg_nr_t; + +typedef struct SRSRAN_API { + uint32_t sfn; ///< System frame number + uint8_t ssb_idx; ///< SS/PBCH blocks index described in TS 38.213 4.1 + bool hrf; ///< Half Radio Frame bit + srsran_subcarrier_spacing_t scs_common; ///< Subcarrier spacing common + uint32_t ssb_offset; ///< SSB subcarrier offset + srsran_dmrs_sch_typeA_pos_t dmrs_typeA_pos; ///< DMRS typeA position + uint32_t coreset0_idx; ///< CORESET Zero configuration index (0-15) + uint32_t ss0_idx; ///< SearchSpace Zero configuration index (0-15) + bool cell_barred; ///< Set to true if the cell is barred + bool intra_freq_reselection; ///< Set to true if allowed + uint32_t spare; ///< Unused bits +} srsran_mib_nr_t; + +SRSRAN_API bool srsran_pbch_msg_nr_is_mib(const srsran_pbch_msg_nr_t* msg); + +SRSRAN_API int srsran_pbch_msg_nr_mib_pack(const srsran_mib_nr_t* mib, srsran_pbch_msg_nr_t* msg); + +SRSRAN_API int srsran_pbch_msg_nr_mib_unpack(const srsran_pbch_msg_nr_t* pbch_msg, srsran_mib_nr_t* mib); + +SRSRAN_API uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len); + +SRSRAN_API uint32_t srsran_pbch_msg_nr_mib_info(const srsran_mib_nr_t* mib, char* str, uint32_t str_len); + +#endif // SRSRAN_PBCH_MSG_NR_H diff --git a/lib/include/srsran/phy/phch/pbch_nr.h b/lib/include/srsran/phy/phch/pbch_nr.h new file mode 100644 index 000000000..817dc52f5 --- /dev/null +++ b/lib/include/srsran/phy/phch/pbch_nr.h @@ -0,0 +1,108 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_PBCH_NR_H +#define SRSRAN_PBCH_NR_H + +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/fec/crc.h" +#include "srsran/phy/fec/polar/polar_code.h" +#include "srsran/phy/fec/polar/polar_decoder.h" +#include "srsran/phy/fec/polar/polar_encoder.h" +#include "srsran/phy/fec/polar/polar_rm.h" +#include "srsran/phy/modem/modem_table.h" +#include "srsran/phy/phch/pbch_msg_nr.h" + +/** + * @brief Describes the NR PBCH object initialisation arguments + */ +typedef struct SRSRAN_API { + bool enable_encode; ///< Enable encoder + bool enable_decode; ///< Enable decoder + bool disable_simd; ///< Disable SIMD polar encoder/decoder +} srsran_pbch_nr_args_t; + +/** + * @brief Describes the NR PBCH configuration + */ +typedef struct SRSRAN_API { + uint32_t N_id; ///< Physical cell identifier + uint32_t n_hf; ///< Number of half radio frame, 0 or 1 + uint32_t ssb_idx; ///< SSB candidate index, up to 4 LSB significant + uint32_t Lmax; ///< Number of SSB opportunities, described in TS 38.213 4.1 ... + float beta; ///< Scaling factor for PBCH symbols, set to zero for default +} srsran_pbch_nr_cfg_t; + +/** + * @brief Describes the NR PBCH object initialisation arguments + */ +typedef struct SRSRAN_API { + srsran_polar_code_t code; + srsran_polar_encoder_t polar_encoder; + srsran_polar_decoder_t polar_decoder; + srsran_polar_rm_t polar_rm_tx; + srsran_polar_rm_t polar_rm_rx; + srsran_crc_t crc; + srsran_modem_table_t qpsk; +} srsran_pbch_nr_t; + +/** + * @brief Initialises an NR PBCH object with the provided arguments + * @param q NR PBCH object + * @param args Arguments providing the desired configuration + * @return SRSRAN_SUCCESS if initialization is successful, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pbch_nr_init(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args); + +/** + * @brief Deallocates an NR PBCH object + * @param q NR PBCH object + */ +SRSRAN_API void srsran_pbch_nr_free(srsran_pbch_nr_t* q); + +/** + * @brief Encodes an NR PBCH message into a SSB resource grid + * @param q NR PBCH object + * @param cfg NR PBCH configuration + * @param msg NR PBCH message to transmit + * @param[out] ssb_grid SSB resource grid + * @return SRSRAN_SUCCESS if encoding is successful, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pbch_nr_encode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + const srsran_pbch_msg_nr_t* msg, + cf_t ssb_grid[SRSRAN_SSB_NOF_RE]); + +/** + * @brief Decodes an NR PBCH message in the SSB resource grid + * @param q NR PBCH object + * @param cfg NR PBCH configuration + * @param[in] ce Channel estimates for the SSB resource grid + * @param msg NR PBCH message received + * @return SRSRAN_SUCCESS if decoding is successful, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pbch_nr_decode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + const cf_t ce[SRSRAN_SSB_NOF_RE], + srsran_pbch_msg_nr_t* msg); + +#endif // SRSRAN_PBCH_NR_H diff --git a/lib/include/srsran/phy/phch/pcfich.h b/lib/include/srsran/phy/phch/pcfich.h index 57d77f442..15d310f69 100644 --- a/lib/include/srsran/phy/phch/pcfich.h +++ b/lib/include/srsran/phy/phch/pcfich.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pdcch.h b/lib/include/srsran/phy/phch/pdcch.h index 364e907b5..4ca2b22cc 100644 --- a/lib/include/srsran/phy/phch/pdcch.h +++ b/lib/include/srsran/phy/phch/pdcch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -104,6 +104,14 @@ SRSRAN_API int srsran_pdcch_extract_llr(srsran_pdcch_t* q, SRSRAN_API int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dci_cfg_t* dci_cfg, srsran_dci_msg_t* msg); +/** + * @brief Computes decoded DCI correlation. It encodes the given DCI message and compares it with the received LLRs + * @param q PDCCH object + * @param msg Previously decoded DCI message + * @return The normalized correlation between the restored symbols and the received LLRs + */ +SRSRAN_API float srsran_pdcch_msg_corr(srsran_pdcch_t* q, srsran_dci_msg_t* msg); + SRSRAN_API int srsran_pdcch_dci_decode(srsran_pdcch_t* q, float* e, uint8_t* data, uint32_t E, uint32_t nof_bits, uint16_t* crc); diff --git a/lib/include/srsran/phy/phch/pdcch_cfg_nr.h b/lib/include/srsran/phy/phch/pdcch_cfg_nr.h index b4e80e9cd..c553591b4 100644 --- a/lib/include/srsran/phy/phch/pdcch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/pdcch_cfg_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pdcch_nr.h b/lib/include/srsran/phy/phch/pdcch_nr.h index 9d027e753..d6405fc8b 100644 --- a/lib/include/srsran/phy/phch/pdcch_nr.h +++ b/lib/include/srsran/phy/phch/pdcch_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -107,6 +107,17 @@ int srsran_pdcch_nr_locations_coreset(const srsran_coreset_t* coreset, SRSRAN_API int srsran_pdcch_nr_max_candidates_coreset(const srsran_coreset_t* coreset, uint32_t aggregation_level); +/** + * @brief Function for generating a RB mask indicating the CCE-to-REG mapping + * @param coreset A given CORESET + * @param dci_location The DCI location for the PDCCH transmission + * @param[out] rb_mask The resultant mask indicating the locations of PDCCH payload + * @return SRSRAN_SUCCESS if the provided parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pdcch_nr_cce_to_reg_mapping(const srsran_coreset_t* coreset, + const srsran_dci_location_t* dci_location, + bool rb_mask[SRSRAN_MAX_PRB_NR]); + SRSRAN_API int srsran_pdcch_nr_init_tx(srsran_pdcch_nr_t* q, const srsran_pdcch_nr_args_t* args); SRSRAN_API int srsran_pdcch_nr_init_rx(srsran_pdcch_nr_t* q, const srsran_pdcch_nr_args_t* args); diff --git a/lib/include/srsran/phy/phch/pdsch.h b/lib/include/srsran/phy/phch/pdsch.h index 9f037e4bf..b6dc59ae3 100644 --- a/lib/include/srsran/phy/phch/pdsch.h +++ b/lib/include/srsran/phy/phch/pdsch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pdsch_cfg.h b/lib/include/srsran/phy/phch/pdsch_cfg.h index af7a6efe2..2df9b3521 100644 --- a/lib/include/srsran/phy/phch/pdsch_cfg.h +++ b/lib/include/srsran/phy/phch/pdsch_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pdsch_nr.h b/lib/include/srsran/phy/phch/pdsch_nr.h index 7e443a4ba..6c9bd6cc3 100644 --- a/lib/include/srsran/phy/phch/pdsch_nr.h +++ b/lib/include/srsran/phy/phch/pdsch_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/phch_cfg_nr.h b/lib/include/srsran/phy/phch/phch_cfg_nr.h index 6346b9038..bc754e7b8 100644 --- a/lib/include/srsran/phy/phch/phch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/phch_cfg_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -58,14 +58,6 @@ typedef enum { srsran_dmrs_sch_len_2 // double, 2 symbol long } srsran_dmrs_sch_len_t; -/** - * @brief Determines whether the first pilot goes into symbol index 2 or 3 - */ -typedef enum { - srsran_dmrs_sch_typeA_pos_2 = 0, // Start in slot symbol index 2 (default) - srsran_dmrs_sch_typeA_pos_3 // Start in slot symbol index 3 -} srsran_dmrs_sch_typeA_pos_t; - /** * @brief Determines additional symbols if possible to be added */ @@ -94,6 +86,8 @@ typedef struct { srsran_dmrs_sch_typeA_pos_t typeA_pos; bool lte_CRS_to_match_around; + uint32_t reference_point_k_rb; + /// Parameters provided by FeatureSetDownlink-v1540 bool additional_DMRS_DL_Alt; @@ -193,10 +187,11 @@ typedef struct SRSRAN_API { bool scrambling_id_present; uint32_t scambling_id; // Identifier used to initialize data scrambling (0-1023) + srsran_mcs_table_t mcs_table; + srsran_dmrs_sch_type_t dmrs_type; + srsran_dmrs_sch_len_t dmrs_max_length; struct { - srsran_dmrs_sch_type_t type; srsran_dmrs_sch_add_pos_t additional_pos; - srsran_dmrs_sch_len_t length; bool scrambling_id0_present; uint32_t scrambling_id0; bool scrambling_id1_present; @@ -205,9 +200,7 @@ typedef struct SRSRAN_API { } dmrs_typeA; struct { - srsran_dmrs_sch_type_t type; srsran_dmrs_sch_add_pos_t additional_pos; - srsran_dmrs_sch_len_t length; bool scrambling_id0_present; uint32_t scrambling_id0; bool scrambling_id1_present; @@ -233,7 +226,8 @@ typedef struct SRSRAN_API { srsran_csi_rs_nzp_set_t nzp_csi_rs_sets[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS]; /// PUSCH only - srsran_beta_offsets_t beta_offsets; /// Semi-static only. + srsran_beta_offsets_t beta_offsets; /// Semi-static only. + bool enable_transform_precoder; /// Enables transform precoding float scaling; /// Indicates a scaling factor to limit the number of resource elements assigned to UCI on PUSCH. } srsran_sch_hl_cfg_nr_t; diff --git a/lib/include/srsran/phy/phch/phich.h b/lib/include/srsran/phy/phch/phich.h index 46ca69ae3..72c16f855 100644 --- a/lib/include/srsran/phy/phch/phich.h +++ b/lib/include/srsran/phy/phch/phich.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pmch.h b/lib/include/srsran/phy/phch/pmch.h index f367b25c6..d84977dad 100644 --- a/lib/include/srsran/phy/phch/pmch.h +++ b/lib/include/srsran/phy/phch/pmch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/prach.h b/lib/include/srsran/phy/phch/prach.h index 3498cb4a4..5043a8b96 100644 --- a/lib/include/srsran/phy/phch/prach.h +++ b/lib/include/srsran/phy/phch/prach.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,6 +32,7 @@ #include "srsran/config.h" #include "srsran/phy/common/phy_common.h" +#include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/dft/dft.h" #include #include @@ -126,7 +127,7 @@ typedef struct SRSRAN_API { } srsran_prach_sf_config_t; ///@brief Maximum number of subframe number candidates for PRACH NR configuration -#define PRACH_NR_CFG_MAX_NOF_SF 5 +#define PRACH_NR_CFG_MAX_NOF_SF 10 /** * @brief PRACH configuration for NR as described in TS 38.211 Tables 6.3.3.2-2, 6.3.3.2-3 and 6.3.3.2-4 @@ -179,16 +180,24 @@ SRSRAN_API bool srsran_prach_tti_opportunity(srsran_prach_t* p, uint32_t current SRSRAN_API bool srsran_prach_tti_opportunity_config_fdd(uint32_t config_idx, uint32_t current_tti, int allowed_subframe); +SRSRAN_API bool srsran_prach_in_window_config_fdd(uint32_t config_idx, uint32_t current_tti, int allowed_subframe); + SRSRAN_API bool srsran_prach_tti_opportunity_config_tdd(uint32_t config_idx, uint32_t tdd_ul_dl_config, uint32_t current_tti, uint32_t* prach_idx); +SRSRAN_API const prach_nr_config_t* srsran_prach_nr_get_cfg_fr1_paired(uint32_t config_idx); + +SRSRAN_API bool srsran_prach_nr_tti_opportunity_fr1_paired(uint32_t config_idx, uint32_t current_tti); + +SRSRAN_API uint32_t srsran_prach_nr_start_symbol_fr1_paired(uint32_t config_idx); + SRSRAN_API const prach_nr_config_t* srsran_prach_nr_get_cfg_fr1_unpaired(uint32_t config_idx); SRSRAN_API bool srsran_prach_nr_tti_opportunity_fr1_unpaired(uint32_t config_idx, uint32_t current_tti); -SRSRAN_API uint32_t srsran_prach_nr_start_symbol_fr1_unpaired(uint32_t config_idx); +SRSRAN_API uint32_t srsran_prach_nr_start_symbol(uint32_t config_idx, srsran_duplex_mode_t duplex_mode); SRSRAN_API uint32_t srsran_prach_f_ra_tdd(uint32_t config_idx, uint32_t tdd_ul_dl_config, diff --git a/lib/include/srsran/phy/phch/psbch.h b/lib/include/srsran/phy/phch/psbch.h index 669da20a2..08f5a2377 100644 --- a/lib/include/srsran/phy/phch/psbch.h +++ b/lib/include/srsran/phy/phch/psbch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pscch.h b/lib/include/srsran/phy/phch/pscch.h index c0945d90e..bfd11a820 100644 --- a/lib/include/srsran/phy/phch/pscch.h +++ b/lib/include/srsran/phy/phch/pscch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pssch.h b/lib/include/srsran/phy/phch/pssch.h index 2683c7cd4..15983257c 100644 --- a/lib/include/srsran/phy/phch/pssch.h +++ b/lib/include/srsran/phy/phch/pssch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pucch.h b/lib/include/srsran/phy/phch/pucch.h index 711ec0bc1..b9811bd16 100644 --- a/lib/include/srsran/phy/phch/pucch.h +++ b/lib/include/srsran/phy/phch/pucch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -84,6 +84,8 @@ typedef struct SRSRAN_API { srsran_uci_value_t uci_data; float dmrs_correlation; float snr_db; + float rssi_dbFs; + float ni_dbFs; float correlation; bool detected; diff --git a/lib/include/srsran/phy/phch/pucch_cfg.h b/lib/include/srsran/phy/phch/pucch_cfg.h index ac752c9da..4af55a445 100644 --- a/lib/include/srsran/phy/phch/pucch_cfg.h +++ b/lib/include/srsran/phy/phch/pucch_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pucch_cfg_nr.h b/lib/include/srsran/phy/phch/pucch_cfg_nr.h index 04cae4789..8914653c8 100644 --- a/lib/include/srsran/phy/phch/pucch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/pucch_cfg_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pucch_nr.h b/lib/include/srsran/phy/phch/pucch_nr.h index 29e0e3da2..b58079520 100644 --- a/lib/include/srsran/phy/phch/pucch_nr.h +++ b/lib/include/srsran/phy/phch/pucch_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -197,6 +197,7 @@ SRSRAN_API int srsran_pucch_nr_format1_encode(const srsran_pucch_nr_t* * @param[in] slot_symbols Resource grid of the given slot * @param[out] b Bits to decode * @param[in] nof_bits Number of bits to decode in the message + * @param[out] norm_corr Normalised correlation * @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR code otherwise */ SRSRAN_API int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* q, @@ -206,7 +207,8 @@ SRSRAN_API int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* srsran_chest_ul_res_t* chest_res, cf_t* slot_symbols, uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS], - uint32_t nof_bits); + uint32_t nof_bits, + float* norm_corr); /** * @brief Encoder NR-PUCCH formats 2, 3 and 4. The NR-PUCCH format is selected by resource->format. @@ -248,9 +250,9 @@ SRSRAN_API int srsran_pucch_nr_format_2_3_4_decode(srsran_pucch_nr_t* cf_t* slot_symbols, srsran_uci_value_nr_t* uci_value); -SRSRAN_API uint32_t srsran_pucch_nr_tx_info(const srsran_pucch_nr_resource_t* resource, - const srsran_uci_data_nr_t* uci_data, - char* str, - uint32_t str_len); +SRSRAN_API uint32_t srsran_pucch_nr_info(const srsran_pucch_nr_resource_t* resource, + const srsran_uci_data_nr_t* uci_data, + char* str, + uint32_t str_len); #endif // SRSRAN_PUCCH_NR_H diff --git a/lib/include/srsran/phy/phch/pucch_proc.h b/lib/include/srsran/phy/phch/pucch_proc.h index fbecd8fd6..c075d977c 100644 --- a/lib/include/srsran/phy/phch/pucch_proc.h +++ b/lib/include/srsran/phy/phch/pucch_proc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -87,7 +87,7 @@ SRSRAN_API uint32_t srsran_pucch_proc_get_npucch(const srsran_cell_t* cell, * @param uci_cfg uplink control information configuration * @param j selected channel * @param b received bits - * @return Returns SRSRAN_SUCCESS if it can decode it succesfully, SRSRAN_ERROR code otherwise + * @return Returns SRSRAN_SUCCESS if it can decode it successfully, SRSRAN_ERROR code otherwise */ SRSRAN_API int srsran_pucch_cs_get_ack(const srsran_pucch_cfg_t* cfg, const srsran_uci_cfg_t* uci_cfg, diff --git a/lib/include/srsran/phy/phch/pusch.h b/lib/include/srsran/phy/phch/pusch.h index 8ebde349b..b6ee6d681 100644 --- a/lib/include/srsran/phy/phch/pusch.h +++ b/lib/include/srsran/phy/phch/pusch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pusch_cfg.h b/lib/include/srsran/phy/phch/pusch_cfg.h index 11f4f19bb..eae96c5f3 100644 --- a/lib/include/srsran/phy/phch/pusch_cfg.h +++ b/lib/include/srsran/phy/phch/pusch_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/pusch_nr.h b/lib/include/srsran/phy/phch/pusch_nr.h index 1cda3fb23..ca7352375 100644 --- a/lib/include/srsran/phy/phch/pusch_nr.h +++ b/lib/include/srsran/phy/phch/pusch_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -113,7 +113,7 @@ SRSRAN_API int srsran_pusch_nr_decode(srsran_pusch_nr_t* q, const srsran_sch_grant_nr_t* grant, srsran_chest_dl_res_t* channel, cf_t* sf_symbols[SRSRAN_MAX_PORTS], - srsran_pusch_res_nr_t data[SRSRAN_MAX_TB]); + srsran_pusch_res_nr_t* data); SRSRAN_API uint32_t srsran_pusch_nr_rx_info(const srsran_pusch_nr_t* q, const srsran_sch_cfg_nr_t* cfg, diff --git a/lib/include/srsran/phy/phch/ra.h b/lib/include/srsran/phy/phch/ra.h index fd3165401..c016745ab 100644 --- a/lib/include/srsran/phy/phch/ra.h +++ b/lib/include/srsran/phy/phch/ra.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/ra_dl.h b/lib/include/srsran/phy/phch/ra_dl.h index 89109551c..df6dc86de 100644 --- a/lib/include/srsran/phy/phch/ra_dl.h +++ b/lib/include/srsran/phy/phch/ra_dl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/ra_dl_nr.h b/lib/include/srsran/phy/phch/ra_dl_nr.h index 5b09e9f89..13e4c8efc 100644 --- a/lib/include/srsran/phy/phch/ra_dl_nr.h +++ b/lib/include/srsran/phy/phch/ra_dl_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -71,17 +71,32 @@ SRSRAN_API int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, */ SRSRAN_API int srsran_ra_dl_nr_time_default_A(uint32_t m, srsran_dmrs_sch_typeA_pos_t dmrs_typeA_pos, srsran_sch_grant_nr_t* grant); + /** - * @brief Calculates the number of PDSCH-DMRS CDM groups without data for DCI format 1_0 + * @brief Calculates the number of front load symbols * - * @remark Defined by TS 38.214 V15.10.0 5.1.6.1.3 CSI-RS for mobility - * - * @param cfg PDSCH-DMRS NR configuration by upper layers - * @param[out] grant Provides grant pointer to fill - * @return Returns SRSRAN_SUCCESS if the provided data is valid, otherwise it returns SRSRAN_ERROR code + * @param cfg PDSCH NR configuration by upper layers + * @param dci Provides PDSCH NR DCI + * @param[out] dmrs_duration + * @return SRSRAN_SUCCESS if provided arguments are valid, SRSRAN_ERROR code otherwise */ -SRSRAN_API int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(const srsran_dmrs_sch_cfg_t* cfg, - srsran_sch_grant_nr_t* grant); +SRSRAN_API int srsran_ra_dl_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_dl_nr_t* dci, + srsran_dmrs_sch_len_t* dmrs_duration); + +/** + * @brief Calculates the number of DMRS CDM groups without data + * + * @remark Defined by TS 38.214 V15.10.0 5.1.6.2 DM-RS reception procedure + * + * @param cfg PDSCH NR configuration by upper layers + * @param dci Provides PUSCH NR DCI + * @return The number of DMRS CDM groups without data if the provided data is valid, otherwise it returns SRSRAN_ERROR + * code + */ +SRSRAN_API int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_dl_nr_t* dci, + uint32_t L); /** * @brief Calculates the PDSCH frequency resource allocation and stores it in the provided PDSCH NR grant. diff --git a/lib/include/srsran/phy/phch/ra_nbiot.h b/lib/include/srsran/phy/phch/ra_nbiot.h index 82786bfb9..838125a3a 100644 --- a/lib/include/srsran/phy/phch/ra_nbiot.h +++ b/lib/include/srsran/phy/phch/ra_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/ra_nr.h b/lib/include/srsran/phy/phch/ra_nr.h index 574ac8e28..6dd67fdab 100644 --- a/lib/include/srsran/phy/phch/ra_nr.h +++ b/lib/include/srsran/phy/phch/ra_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -120,6 +120,7 @@ SRSRAN_API int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrie * Note: Only TypeA PUSCH mapping type is supported * * @param carrier Carrier information struct + * @param slot Slot configuration * @param pusch_hl_cfg PUSCH configuration provided by higher layers * @param dci_ul DCI uplink (format 0_0 or 0_1) * @param pusch_cfg PUSCH configuration after applying the procedure @@ -127,6 +128,7 @@ SRSRAN_API int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrie * @return 0 on success, -1 on error */ SRSRAN_API int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot, const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, const srsran_dci_ul_nr_t* dci_ul, srsran_sch_cfg_nr_t* pusch_cfg, @@ -148,4 +150,66 @@ SRSRAN_API int srsran_ra_ul_set_grant_uci_nr(const srsran_carrier_nr_t* carri const srsran_uci_cfg_nr_t* uci_cfg, srsran_sch_cfg_nr_t* pusch_cfg); +/** + * @brief Calculates frequency allocation type 1 RIV field + * @param N_rb Number of resource blocks + * @param start_rb Start resource block index + * @param length_rb Number of resource blocks + * @return The RIV field with the encoded value + */ +SRSRAN_API uint32_t srsran_ra_nr_type1_riv(uint32_t N_rb, uint32_t start_rb, uint32_t length_rb); + +/** + * @brief Returns the MCS corresponding to CQI + * + * Mapping is performed as: return the MCS that has the closest spectral efficiency to that of the CQI + * + * @remark Implements mapping based on TS 38.214, MCS Tables 5.1.3.1-1, 5.1.3.1-2, 5.1.3.1-3 and CQI Tables 5.2.2.1-2, + * Table 5.2.2.1-3, 5.2.2.1-4 + * @param cqi CQI value + * @param cqi_table_idx CQI table index + * @param mcs_table MCS table parameter + * @param dci_format DCI format + * @param search_space_type Seach Space type + * @param rnti_type RNTI type + * @return The MCS index + */ +SRSRAN_API int srsran_ra_nr_cqi_to_mcs(uint8_t cqi, + srsran_csi_cqi_table_t cqi_table_idx, + srsran_mcs_table_t mcs_table, + srsran_dci_format_nr_t dci_format, + srsran_search_space_type_t search_space_type, + srsran_rnti_type_t rnti_type); + +/** + * @brief Returns the Spectral Efficiency corresponding to CQI + * + * Mapping is performed as: return the MCS that has the closest spectral efficiency to that of the CQI + * + * @remark Implements mapping based on TS 38.214, CQI Tables 5.2.2.1-2, Table 5.2.2.1-3, 5.2.2.1-4 + * @param cqi CQI value + * @param cqi_table_idx CQI table index + * @return The Spectral Efficiency + */ +SRSRAN_API double srsran_ra_nr_cqi_to_se(uint8_t cqi, srsran_csi_cqi_table_t cqi_table_idx); + +/** + * @brief Returns the MCS corresponding to Spectral Efficiency + * + * Mapping is performed as: return the greatest MCS with an SE lower than or equal to target SE + * + * @remark Implements mapping based on TS 38.214, MCS Tables 5.1.3.1-1, 5.1.3.1-2, 5.1.3.1-3 + * @param se_target Target Spectral efficiency to be mapped into MCS + * @param mcs_table MCS table parameter + * @param dci_format DCI format + * @param search_space_type Seach Space type + * @param rnti_type RNTI type + * @return The MCS index + */ +SRSRAN_API int srsran_ra_nr_se_to_mcs(double se_target, + srsran_mcs_table_t mcs_table, + srsran_dci_format_nr_t dci_format, + srsran_search_space_type_t search_space_type, + srsran_rnti_type_t rnti_type); + #endif // SRSRAN_RA_NR_H diff --git a/lib/include/srsran/phy/phch/ra_sl.h b/lib/include/srsran/phy/phch/ra_sl.h index ddb81fa25..9f67d557f 100644 --- a/lib/include/srsran/phy/phch/ra_sl.h +++ b/lib/include/srsran/phy/phch/ra_sl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/ra_ul.h b/lib/include/srsran/phy/phch/ra_ul.h index 3c9e78218..76fb23b94 100644 --- a/lib/include/srsran/phy/phch/ra_ul.h +++ b/lib/include/srsran/phy/phch/ra_ul.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/ra_ul_nr.h b/lib/include/srsran/phy/phch/ra_ul_nr.h index df98b2697..f87f8865d 100644 --- a/lib/include/srsran/phy/phch/ra_ul_nr.h +++ b/lib/include/srsran/phy/phch/ra_ul_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -64,16 +64,30 @@ SRSRAN_API int srsran_ra_ul_nr_pusch_time_resource_default_A(uint32_t scs_cfg, uint32_t m, srsran_sch_grant_nr_t* grant); /** - * @brief Calculates the number of PUSCH-DMRS CDM groups without data for DCI format 0_0 + * @brief Calculates the number of front load symbols + * + * @param cfg PUSCH NR configuration by upper layers + * @param dci Provides PUSCH NR DCI + * @param[out] dmrs_duration + * @return SRSRAN_SUCCESS if provided arguments are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ra_ul_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_ul_nr_t* dci, + srsran_dmrs_sch_len_t* dmrs_duration); + +/** + * @brief Calculates the number of DMRS CDM groups without data * * @remark Defined by TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure * * @param cfg PUSCH NR configuration by upper layers - * @param[out] grant Provides grant pointer to fill - * @return Returns SRSRAN_SUCCESS if the provided data is valid, otherwise it returns SRSRAN_ERROR code + * @param dci Provides PUSCH NR DCI + * @return The number of DMRS CDM groups without data if the provided data is valid, otherwise it returns SRSRAN_ERROR + * code */ -SRSRAN_API int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srsran_sch_cfg_nr_t* cfg, - srsran_sch_grant_nr_t* grant); +SRSRAN_API int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_ul_nr_t* dci, + uint32_t L); /** * @brief Calculates the ratio of PUSCH EPRE to DM-RS EPRE @@ -96,7 +110,9 @@ SRSRAN_API int srsran_ra_ul_nr_pucch_format_2_3_min_prb(const srsran_pucch_nr_re /** * @brief Calculates the PUSCH frequency resource allocation and stores it in the provided PUSCH NR grant. * - * @remark Defined by TS 38.214 V15.10.0 section 5.1.2.2 + * @remark Defined by TS 38.214 V15.10.0 section 6.1.2.2 Resource allocation in frequency domain (for DCI formats 0_0 + * and 0_1) + * @remark Defined by TS 38.213 V15.10.0 section 8.3 for PUSCH scheduled by RAR UL grant * @param carrier Carrier information * @param cfg PDSCH NR configuration by upper layers * @param dci_dl Unpacked DCI used to schedule the PDSCH grant @@ -112,11 +128,13 @@ SRSRAN_API int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier, * @brief Selects a valid PUCCH resource for transmission * @param pucch_cfg PUCCH configuration from upper layers * @param uci_cfg Uplink Control information configuration (and PDCCH context) + * @param N_bwp_sz Uplink BWP size in nof_prb * @param[out] resource Selected resource for transmitting PUCCH * @return SRSRAN_SUCCESS if provided configuration is valid, SRSRAN_ERROR code otherwise */ SRSRAN_API int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, const srsran_uci_cfg_nr_t* uci_cfg, + uint32_t N_bwp_sz, srsran_pucch_nr_resource_t* resource); /** diff --git a/lib/include/srsran/phy/phch/regs.h b/lib/include/srsran/phy/phch/regs.h index 3e56d7ac1..116043f3a 100644 --- a/lib/include/srsran/phy/phch/regs.h +++ b/lib/include/srsran/phy/phch/regs.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/sch.h b/lib/include/srsran/phy/phch/sch.h index 3ecd799a3..b938f5e99 100644 --- a/lib/include/srsran/phy/phch/sch.h +++ b/lib/include/srsran/phy/phch/sch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/sch_cfg_nr.h b/lib/include/srsran/phy/phch/sch_cfg_nr.h index 69d1f7343..f1d5d8ef9 100644 --- a/lib/include/srsran/phy/phch/sch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/sch_cfg_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,8 +28,11 @@ typedef struct SRSRAN_API { srsran_mcs_table_t mcs_table; ///< @brief Indicates the MCS table the UE shall use for PDSCH and/or PUSCH without ///< transform precoding - srsran_xoverhead_t xoverhead; ///< Accounts for overhead from CSI-RS, CORESET, etc. If the field is absent, the UE - ///< applies value xOh0 (see TS 38.214 [19], clause 5.1.3.2). + srsran_xoverhead_t xoverhead; ///< @brief Accounts for overhead from CSI-RS, CORESET, etc. If the field is absent, the + ///< UE applies value xOh0 (see TS 38.214 [19], clause 5.1.3.2). + + bool limited_buffer_rm; ///< @brief Enables LBRM (Limited buffer rate-matching). Given by rateMatching parameter in + ///< PUSCH-ServingCellConfig or PDSCH-ServingCellConfig ASN1 sequences } srsran_sch_cfg_t; typedef struct SRSRAN_API { @@ -37,7 +40,8 @@ typedef struct SRSRAN_API { uint32_t N_L; ///< the number of transmission layers that the transport block is mapped onto uint32_t mcs; ///< Modulation Code Scheme (MCS) for debug and trace purpose int tbs; ///< Payload size, TS 38.212 refers to it as A - double R; ///< Target LDPC rate + double R; ///< Target code rate + double R_prime; ///< Actual code rate int rv; ///< Redundancy version int ndi; ///< New Data Indicator uint32_t nof_re; ///< Number of available resource elements to transmit ULSCH (data) and UCI (control) diff --git a/lib/include/srsran/phy/phch/sch_nr.h b/lib/include/srsran/phy/phch/sch_nr.h index a2ae5ec8a..455316ddb 100644 --- a/lib/include/srsran/phy/phch/sch_nr.h +++ b/lib/include/srsran/phy/phch/sch_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,8 +38,12 @@ #include "srsran/phy/fec/ldpc/ldpc_rm.h" #include "srsran/phy/phch/phch_cfg_nr.h" +/** + * @brief Maximum number of codeblocks for a NR shared channel transmission. It assumes a rate of 1.0 for the maximum + * amount of bits a resource grid can fit + */ #define SRSRAN_SCH_NR_MAX_NOF_CB_LDPC \ - ((SRSRAN_SLOT_MAX_NOF_BITS_NR + (SRSRAN_LDPC_BG2_MAX_LEN_CB - 1)) / SRSRAN_LDPC_BG2_MAX_LEN_CB) + ((SRSRAN_SLOT_MAX_NOF_BITS_NR + (SRSRAN_LDPC_MAX_LEN_CB - 1)) / SRSRAN_LDPC_MAX_LEN_CB) /** * @brief Groups NR-PUSCH data for reception diff --git a/lib/include/srsran/phy/phch/sci.h b/lib/include/srsran/phy/phch/sci.h index a08238d53..8395572f5 100644 --- a/lib/include/srsran/phy/phch/sci.h +++ b/lib/include/srsran/phy/phch/sci.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/uci.h b/lib/include/srsran/phy/phch/uci.h index f9adcca2b..0e11a9cd5 100644 --- a/lib/include/srsran/phy/phch/uci.h +++ b/lib/include/srsran/phy/phch/uci.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/uci_cfg.h b/lib/include/srsran/phy/phch/uci_cfg.h index c01a64ada..44cf61937 100644 --- a/lib/include/srsran/phy/phch/uci_cfg.h +++ b/lib/include/srsran/phy/phch/uci_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/phch/uci_cfg_nr.h b/lib/include/srsran/phy/phch/uci_cfg_nr.h index cbf1ac732..419233e65 100644 --- a/lib/include/srsran/phy/phch/uci_cfg_nr.h +++ b/lib/include/srsran/phy/phch/uci_cfg_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,6 +23,7 @@ #define SRSRAN_UCI_CFG_NR_H #include "csi_cfg.h" +#include "harq_ack_cfg.h" #include "srsran/phy/common/phy_common_nr.h" #include #include @@ -33,11 +34,6 @@ */ #define SRSRAN_UCI_NR_MAX_NOF_BITS 1706U -/** - * @brief Maximum number of HARQ ACK feedback bits that can be carried in Uplink Control Information (UCI) message - */ -#define SRSRAN_UCI_NR_MAX_ACK_BITS 360 - /** * @brief Maximum number of Channel State Information part 1 (CSI1) bits that can be carried in Uplink Control * Information (UCI) message @@ -48,16 +44,16 @@ * @brief Uplink Control Information bits configuration for PUCCH transmission */ typedef struct { - uint16_t rnti; ///< RNTI - uint32_t resource_id; ///< PUCCH resource indicator field in the DCI format 1_0 or DCI format 1_1 - uint32_t n_cce_0; ///< index of a first CCE for the PDCCH reception - uint32_t N_cce; ///< number of CCEs in a CORESET of a PDCCH reception with DCI format 1_0 or 1_1 - uint32_t sr_resource_id; ///< Scheduling request resource identifier, only valid if positive SR - bool sr_positive_present; ///< Set to true if there is at least one positive SR + uint16_t rnti; ///< RNTI + uint32_t resource_id; ///< PUCCH resource indicator field in the DCI format 1_0 or DCI format 1_1 + uint32_t n_cce_0; ///< index of a first CCE for the PDCCH reception + uint32_t N_cce; ///< number of CCEs in a CORESET of a PDCCH reception with DCI format 1_0 or 1_1 + uint32_t sr_resource_id; ///< Scheduling request resource identifier, only valid if positive SR } srsran_uci_nr_pucch_cfg_t; /** * @brief Uplink Control Information bits configuration for PUSCH transmission + * @attention Set nof_layers, nof_re or R to 0 to indicate this structure is not initialised. */ typedef struct { uint32_t l0; ///< First OFDM symbol that does not carry DMRS of the PUSCH, after the first DMRS symbol(s) @@ -67,12 +63,12 @@ typedef struct { uint32_t K_sum; ///< Sum of UL-SCH code block sizes, set to zero if no UL-SCH srsran_mod_t modulation; ///< Modulation for the PUSCH uint32_t nof_layers; ///< Number of layers for PUSCH + uint32_t nof_re; ///< Total number of resource elements allocated for the grant float R; ///< Code rate of the PUSCH float alpha; ///< Higher layer parameter scaling float beta_harq_ack_offset; float beta_csi1_offset; float beta_csi2_offset; - uint32_t nof_re; bool csi_part2_present; } srsran_uci_nr_pusch_cfg_t; @@ -81,10 +77,11 @@ typedef struct { */ typedef struct SRSRAN_API { /// Common Parameters - uint32_t o_ack; ///< Number of HARQ-ACK bits - uint32_t o_sr; ///< Number of SR bits - srsran_csi_report_cfg_t csi[SRSRAN_CSI_MAX_NOF_REPORT]; ///< CSI report configuration - uint32_t nof_csi; ///< Number of CSI reports + srsran_harq_ack_cfg_t ack; ///< HARQ-ACK configuration + uint32_t o_sr; ///< Number of SR bits + bool sr_positive_present; ///< Set to true if there is at least one positive SR + srsran_csi_report_cfg_t csi[SRSRAN_CSI_SLOT_MAX_NOF_REPORT]; ///< CSI report configuration + uint32_t nof_csi; ///< Number of CSI reports union { srsran_uci_nr_pucch_cfg_t pucch; ///< Configuration for transmission in PUCCH srsran_uci_nr_pusch_cfg_t pusch; ///< Configuration for transmission in PUSCH @@ -95,9 +92,9 @@ typedef struct SRSRAN_API { * @brief Uplink Control Information (UCI) message packed information */ typedef struct SRSRAN_API { - uint8_t ack[SRSRAN_UCI_NR_MAX_ACK_BITS]; ///< HARQ ACK feedback bits - uint32_t sr; ///< Number of positive SR - srsran_csi_report_value_t csi[SRSRAN_CSI_MAX_NOF_REPORT]; ///< Packed CSI report values + uint8_t ack[SRSRAN_HARQ_ACK_MAX_NOF_BITS]; ///< HARQ ACK feedback bits + uint32_t sr; ///< Number of positive SR + srsran_csi_report_value_t csi[SRSRAN_CSI_SLOT_MAX_NOF_REPORT]; ///< Packed CSI report values bool valid; ///< Indicates whether the message has been decoded successfully, ignored in the transmitter } srsran_uci_value_nr_t; diff --git a/lib/include/srsran/phy/phch/uci_nr.h b/lib/include/srsran/phy/phch/uci_nr.h index a4fb05e40..66f1ebb35 100644 --- a/lib/include/srsran/phy/phch/uci_nr.h +++ b/lib/include/srsran/phy/phch/uci_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/resampling/decim.h b/lib/include/srsran/phy/resampling/decim.h index e67a27034..eb1e1b047 100644 --- a/lib/include/srsran/phy/resampling/decim.h +++ b/lib/include/srsran/phy/resampling/decim.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/resampling/interp.h b/lib/include/srsran/phy/resampling/interp.h index 2623a6485..35b5b4568 100644 --- a/lib/include/srsran/phy/resampling/interp.h +++ b/lib/include/srsran/phy/resampling/interp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/resampling/resample_arb.h b/lib/include/srsran/phy/resampling/resample_arb.h index ef7f67b0f..8b301902e 100644 --- a/lib/include/srsran/phy/resampling/resample_arb.h +++ b/lib/include/srsran/phy/resampling/resample_arb.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/resampling/resampler.h b/lib/include/srsran/phy/resampling/resampler.h index 571e5be76..e846fd481 100644 --- a/lib/include/srsran/phy/resampling/resampler.h +++ b/lib/include/srsran/phy/resampling/resampler.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -49,19 +49,20 @@ typedef enum { } srsran_resampler_mode_t; /** - * Resampler internal buffers and subcomponents + * @brief Resampler internal buffers and subcomponents */ typedef struct { - srsran_resampler_mode_t mode; - uint32_t ratio; - uint32_t window_sz; - srsran_dft_plan_t fft; - srsran_dft_plan_t ifft; - uint32_t state_len; - cf_t* in_buffer; - cf_t* out_buffer; - cf_t* state; - cf_t* filter; + srsran_resampler_mode_t mode; ///< Interpolate or decimate mode + uint32_t ratio; ///< Decimation/Interpolation ratio + uint32_t window_sz; ///< Maximum number of processed samples + uint32_t delay; ///< Filter delay in samples + srsran_dft_plan_t fft; ///< Forward DFT + srsran_dft_plan_t ifft; ///< Backward DFT + uint32_t state_len; ///< Number of acccumulated samples in the internal state + cf_t* in_buffer; ///< DFT input buffer + cf_t* out_buffer; ///< DFT output buffer + cf_t* state; ///< Filter state + cf_t* filter; ///< Frequency domain filter } srsran_resampler_fft_t; /** diff --git a/lib/include/srsran/phy/rf/rf.h b/lib/include/srsran/phy/rf/rf.h index 64150574e..1acb319f4 100644 --- a/lib/include/srsran/phy/rf/rf.h +++ b/lib/include/srsran/phy/rf/rf.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "srsran/config.h" @@ -71,12 +72,91 @@ typedef struct { typedef void (*srsran_rf_error_handler_t)(void* arg, srsran_rf_error_t error); +/* RF frontend API */ +typedef struct { + const char* name; + const char* (*srsran_rf_devname)(void* h); + int (*srsran_rf_start_rx_stream)(void* h, bool now); + int (*srsran_rf_stop_rx_stream)(void* h); + void (*srsran_rf_flush_buffer)(void* h); + bool (*srsran_rf_has_rssi)(void* h); + float (*srsran_rf_get_rssi)(void* h); + void (*srsran_rf_suppress_stdout)(void* h); + void (*srsran_rf_register_error_handler)(void* h, srsran_rf_error_handler_t error_handler, void* arg); + int (*srsran_rf_open)(char* args, void** h); + int (*srsran_rf_open_multi)(char* args, void** h, uint32_t nof_channels); + int (*srsran_rf_close)(void* h); + double (*srsran_rf_set_rx_srate)(void* h, double freq); + int (*srsran_rf_set_rx_gain)(void* h, double gain); + int (*srsran_rf_set_rx_gain_ch)(void* h, uint32_t ch, double gain); + int (*srsran_rf_set_tx_gain)(void* h, double gain); + int (*srsran_rf_set_tx_gain_ch)(void* h, uint32_t ch, double gain); + double (*srsran_rf_get_rx_gain)(void* h); + double (*srsran_rf_get_tx_gain)(void* h); + srsran_rf_info_t* (*srsran_rf_get_info)(void* h); + double (*srsran_rf_set_rx_freq)(void* h, uint32_t ch, double freq); + double (*srsran_rf_set_tx_srate)(void* h, double freq); + double (*srsran_rf_set_tx_freq)(void* h, uint32_t ch, double freq); + void (*srsran_rf_get_time)(void* h, time_t* secs, double* frac_secs); + void (*srsran_rf_sync_pps)(void* h); + int (*srsran_rf_recv_with_time)(void* h, + void* data, + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs); + int (*srsran_rf_recv_with_time_multi)(void* h, + void** data, + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs); + int (*srsran_rf_send_timed)(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + int (*srsran_rf_send_timed_multi)(void* h, + void** data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); +} rf_dev_t; + +typedef struct { + const char* plugin_name; + void* dl_handle; + rf_dev_t* rf_api; +} srsran_rf_plugin_t; + +SRSRAN_API int srsran_rf_load_plugins(); + SRSRAN_API int srsran_rf_open(srsran_rf_t* h, char* args); SRSRAN_API int srsran_rf_open_multi(srsran_rf_t* h, char* args, uint32_t nof_channels); SRSRAN_API int srsran_rf_open_devname(srsran_rf_t* h, const char* devname, char* args, uint32_t nof_channels); +/** + * @brief Opens a file-based RF abstraction + * @param[out] rf Device handle + * @param[in] rx_files List of pre-opened FILE* for each RX channel; NULL to disable + * @param[in] tx_files List of pre-opened FILE* for each TX channel; NULL to disable + * @param[in] nof_channels Number of channels per direction + * @param[in] base_srate Sample rate of RX and TX files + * @return SRSRAN_SUCCESS on success, otherwise error code + */ +SRSRAN_API int +srsran_rf_open_file(srsran_rf_t* rf, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate); + SRSRAN_API const char* srsran_rf_name(srsran_rf_t* h); SRSRAN_API int srsran_rf_start_gain_thread(srsran_rf_t* rf, bool tx_gain_same_rx); diff --git a/lib/include/srsran/phy/rf/rf_utils.h b/lib/include/srsran/phy/rf/rf_utils.h index 0e7fe0f75..ba3e0679f 100644 --- a/lib/include/srsran/phy/rf/rf_utils.h +++ b/lib/include/srsran/phy/rf/rf_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/scrambling/scrambling.h b/lib/include/srsran/phy/scrambling/scrambling.h index 140a4caef..1a6ae0c56 100644 --- a/lib/include/srsran/phy/scrambling/scrambling.h +++ b/lib/include/srsran/phy/scrambling/scrambling.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/cfo.h b/lib/include/srsran/phy/sync/cfo.h index b1874e791..818a3926d 100644 --- a/lib/include/srsran/phy/sync/cfo.h +++ b/lib/include/srsran/phy/sync/cfo.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/cp.h b/lib/include/srsran/phy/sync/cp.h index 41cd61600..88487956b 100644 --- a/lib/include/srsran/phy/sync/cp.h +++ b/lib/include/srsran/phy/sync/cp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/npss.h b/lib/include/srsran/phy/sync/npss.h index 8402ee2f1..3b62a4805 100644 --- a/lib/include/srsran/phy/sync/npss.h +++ b/lib/include/srsran/phy/sync/npss.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/nsss.h b/lib/include/srsran/phy/sync/nsss.h index 1d31e7f85..41cff6440 100644 --- a/lib/include/srsran/phy/sync/nsss.h +++ b/lib/include/srsran/phy/sync/nsss.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/pss.h b/lib/include/srsran/phy/sync/pss.h index 2d665c9d6..e3233e340 100644 --- a/lib/include/srsran/phy/sync/pss.h +++ b/lib/include/srsran/phy/sync/pss.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/pss_nr.h b/lib/include/srsran/phy/sync/pss_nr.h new file mode 100644 index 000000000..b7f28d4f7 --- /dev/null +++ b/lib/include/srsran/phy/sync/pss_nr.h @@ -0,0 +1,68 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_PSS_NR_H +#define SRSRAN_PSS_NR_H + +#include "srsran/config.h" +#include "srsran/phy/common/phy_common_nr.h" +#include + +/** + * @brief NR PSS sequence length in frequency domain + */ +#define SRSRAN_PSS_NR_LEN 127 + +/** + * @brief NR PSS Symbol number + */ +#define SRSRAN_PSS_NR_SYMBOL_IDX 0 + +/** + * @brief Put NR PSS sequence in the SSB grid + * @remark Described in TS 38.211 section 7.4.2.2 Primary synchronization signal + * @param ssb_grid SSB resource grid + * @param N_id_2 Physical cell ID 2 + * @param beta PSS power allocation + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float beta); + +/** + * @brief Extracts the NR PSS Least Square Estimates (LSE) from the SSB grid + * @param ssb_grid received SSB resource grid + * @param N_id_2 Physical cell ID 2 + * @param lse Provides LSE pointer + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_2, cf_t lse[SRSRAN_PSS_NR_LEN]); + +/** + * @brief Find the best PSS sequence given the SSB resource grid + * @attention Assumes the SSB is synchronized and the average delay is pre-compensated + * @param ssb_grid The SSB resource grid to search + * @param norm_corr Normalised correlation of the best found sequence + * @param found_N_id_2 The N_id_2 of the best sequence + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_pss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], float* norm_corr, uint32_t* found_N_id_2); + +#endif // SRSRAN_PSS_NR_H diff --git a/lib/include/srsran/phy/sync/psss.h b/lib/include/srsran/phy/sync/psss.h index c7b827b96..3ef08d892 100644 --- a/lib/include/srsran/phy/sync/psss.h +++ b/lib/include/srsran/phy/sync/psss.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/refsignal_dl_sync.h b/lib/include/srsran/phy/sync/refsignal_dl_sync.h index 874c93651..5b6fb2711 100644 --- a/lib/include/srsran/phy/sync/refsignal_dl_sync.h +++ b/lib/include/srsran/phy/sync/refsignal_dl_sync.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -44,13 +44,13 @@ typedef struct { uint32_t peak_index; } srsran_refsignal_dl_sync_t; -SRSRAN_API int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q); +SRSRAN_API int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q, srsran_cp_t cp); SRSRAN_API int srsran_refsignal_dl_sync_set_cell(srsran_refsignal_dl_sync_t* q, srsran_cell_t cell); SRSRAN_API void srsran_refsignal_dl_sync_free(srsran_refsignal_dl_sync_t* q); -SRSRAN_API void srsran_refsignal_dl_sync_run(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples); +SRSRAN_API int srsran_refsignal_dl_sync_run(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples); SRSRAN_API void srsran_refsignal_dl_sync_measure_sf(srsran_refsignal_dl_sync_t* q, cf_t* buffer, diff --git a/lib/include/srsran/phy/sync/sfo.h b/lib/include/srsran/phy/sync/sfo.h index a5bb4729b..3bb8eb82f 100644 --- a/lib/include/srsran/phy/sync/sfo.h +++ b/lib/include/srsran/phy/sync/sfo.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/ssb.h b/lib/include/srsran/phy/sync/ssb.h new file mode 100644 index 000000000..3f99176a0 --- /dev/null +++ b/lib/include/srsran/phy/sync/ssb.h @@ -0,0 +1,317 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SSB_H +#define SRSRAN_SSB_H + +#include "srsran/config.h" +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/dft/dft.h" +#include "srsran/phy/phch/pbch_nr.h" +#include + +/** + * @brief Default SSB maximum sampling rate + */ +#define SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ 61.44e6 + +/** + * @brief Default SSB minimum subcarrier spacing + */ +#define SRSRAN_SSB_DEFAULT_MIN_SCS srsran_subcarrier_spacing_15kHz + +/** + * @brief Default beta value, used in case they are set to zero + */ +#define SRSRAN_SSB_DEFAULT_BETA 1.0f + +/** + * @brief Maximum number of SSB positions in burst. Defined in TS 38.331 ServingCellConfigCommon, ssb-PositionsInBurst + */ +#define SRSRAN_SSB_NOF_CANDIDATES 64 + +/** + * @brief Describes SSB object initialization arguments + */ +typedef struct SRSRAN_API { + double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default + srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing + bool enable_search; ///< Enables PSS/SSS blind search + bool enable_measure; ///< Enables PSS/SSS CSI measurements and frequency domain search + bool enable_encode; ///< Enables PBCH Encoder + bool enable_decode; ///< Enables PBCH Decoder + bool disable_polar_simd; ///< Disables polar encoder/decoder SIMD acceleration + float pbch_dmrs_thr; ///< NR-PBCH DMRS threshold for blind decoding, set to 0 for default +} srsran_ssb_args_t; + +/** + * @brief Describes SSB configuration arguments + */ +typedef struct SRSRAN_API { + double srate_hz; ///< Current sampling rate in Hz + double center_freq_hz; ///< Base-band center frequency in Hz + double ssb_freq_hz; ///< SSB center frequency + srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing + srsran_ssb_pattern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search + srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD) + uint32_t periodicity_ms; ///< SSB periodicity in ms + float beta_pss; ///< PSS power allocation + float beta_sss; ///< SSS power allocation + float beta_pbch; ///< PBCH power allocation + float beta_pbch_dmrs; ///< PBCH DMRS power allocation + float scaling; ///< IFFT scaling (used for modulation), set to 0 for default +} srsran_ssb_cfg_t; + +/** + * @brief Describes SSB object + */ +typedef struct SRSRAN_API { + srsran_ssb_args_t args; ///< Stores initialization arguments + srsran_ssb_cfg_t cfg; ///< Stores last configuration + + /// Sampling rate dependent parameters + float scs_hz; ///< Subcarrier spacing in Hz + uint32_t max_sf_sz; ///< Maximum subframe size at the specified sampling rate + uint32_t max_symbol_sz; ///< Maximum symbol size given the minimum supported SCS and sampling rate + uint32_t max_corr_sz; ///< Maximum correlation size + uint32_t max_ssb_sz; ///< Maximum SSB size in samples at the configured sampling rate + uint32_t sf_sz; ///< Current subframe size at the specified sampling rate + uint32_t symbol_sz; ///< Current SSB symbol size (for the given base-band sampling rate) + uint32_t corr_sz; ///< Correlation size + uint32_t corr_window; ///< Correlation window length + uint32_t ssb_sz; ///< SSB size in samples at the configured sampling rate + int32_t f_offset; ///< SSB integer frequency offset (multiple of SCS) between DC and the SSB center + uint32_t cp_sz; ///< CP length for the given symbol size + + /// Other parameters + uint32_t l_first[SRSRAN_SSB_NOF_CANDIDATES]; ///< Start symbol for each SSB candidate in half radio frame + uint32_t Lmax; ///< Number of SSB candidates + + /// Internal Objects + srsran_dft_plan_t ifft; ///< IFFT object for modulating the SSB + srsran_dft_plan_t fft; ///< FFT object for demodulate the SSB. + srsran_dft_plan_t fft_corr; ///< FFT for correlation + srsran_dft_plan_t ifft_corr; ///< IFFT for correlation + srsran_pbch_nr_t pbch; ///< PBCH encoder and decoder + + /// Frequency/Time domain temporal data + cf_t* tmp_freq; ///< Temporal frequency domain buffer + cf_t* tmp_time; ///< Temporal time domain buffer + cf_t* tmp_corr; ///< Temporal correlation frequency domain buffer + cf_t* sf_buffer; ///< subframe buffer + cf_t* pss_seq[SRSRAN_NOF_NID_2_NR]; ///< Possible frequency domain PSS for find +} srsran_ssb_t; + +/** + * @brief Describes an SSB search result + * @note if pbch.crc is true, SSB transmission is found and decoded. Otherwise, no SSB transmission has been decoded + */ +typedef struct { + uint32_t N_id; ///< Most suitable physical cell identifier + uint32_t t_offset; ///< Time offset in the input samples + srsran_pbch_msg_nr_t pbch_msg; ///< Physical broadcast channel message of the most suitable SSB candidate + srsran_csi_trs_measurements_t measurements; ///< Measurements +} srsran_ssb_search_res_t; + +/** + * @brief Initialises configures NR SSB with the given arguments + * @param q SSB object + * @param args NR PSS initialization arguments + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args); + +/** + * @brief Frees NR SSB object + * @param q SSB object + */ +SRSRAN_API void srsran_ssb_free(srsran_ssb_t* q); + +/** + * @brief Sets SSB configuration with the current SSB configuration + * @param q SSB object + * @param cfg Current SSB configuration + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg); + +/** + * @brief Searches for an SSB transmission and decodes the PBCH message + * @param q SSB object + * @param in Input baseband buffer + * @param nof_samples Number of samples available in the buffer + * @param res SSB Search result + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srsran_ssb_search_res_t* res); + +/** + * @brief Decides if the SSB object is configured and a given subframe is configured for SSB transmission + * @param q SSB object + * @param sf_idx Subframe index within the radio frame + * @return true if the SSB object is configured and SSB is transmitted, false otherwise + */ +SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx); + +/** + * + * @param q SSB object + * @param N_id Physical Cell Identifier + * @param msg NR PBCH message to transmit + * @param re_grid Resource grid pointer + * @param grid_sz_rb Resource grid bandwidth in subcarriers + * @return SRSRAN_SUCCES if inputs and data are correct, otherwise SRSRAN_ERROR code + */ +SRSRAN_API int srsran_ssb_put_grid(srsran_ssb_t* q, + uint32_t N_id, + const srsran_pbch_msg_nr_t* msg, + cf_t* re_grid, + uint32_t grid_bw_sc); + +/** + * @brief Adds SSB to a given signal in time domain + * @param q SSB object + * @param N_id Physical Cell Identifier + * @param msg NR PBCH message to transmit + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int +srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out); + +/** + * @brief Decodes PBCH in the given time domain signal + * @note It currently expects an input buffer of half radio frame + * @param q SSB object + * @param N_id Physical Cell Identifier + * @param n_hf Number of hald radio frame, 0 or 1 + * @param ssb_idx SSB candidate index + * @param in Input baseband buffer + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q, + uint32_t N_id, + uint32_t n_hf, + uint32_t ssb_idx, + const cf_t* grid, + srsran_pbch_msg_nr_t* msg); + +/** + * @brief Decodes PBCH in the given resource grid + * @param q SSB object + * @param N_id Physical Cell Identifier + * @param n_hf Number of hald radio frame, 0 or 1 + * @param ssb_idx SSB candidate index + * @param grid Input resource grid buffer + * @param grid_sz_rb Resource grid bandwidth in subcarriers + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_decode_grid(srsran_ssb_t* q, + uint32_t N_id, + uint32_t n_hf, + uint32_t ssb_idx, + const cf_t* in, + uint32_t grid_sz_rb, + srsran_pbch_msg_nr_t* msg); + +/** + * @brief Perform cell search and measurement + * @note This function assumes the SSB transmission is aligned with the input base-band signal + * @param q SSB object + * @param in Base-band signal buffer + * @param N_id Physical Cell Identifier of the most suitable cell identifier + * @param meas SSB-based CSI measurement of the most suitable cell identifier + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_csi_search(srsran_ssb_t* q, + const cf_t* in, + uint32_t nof_samples, + uint32_t* N_id, + srsran_csi_trs_measurements_t* meas); + +/** + * @brief Perform Channel State Information (CSI) measurement from the SSB + * @param q SSB object + * @param N_id Physical Cell Identifier + * @param ssb_idx SSB candidate index + * @param in Base-band signal + * @param meas SSB-based CSI measurement + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_csi_measure(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const cf_t* in, + srsran_csi_trs_measurements_t* meas); + +/** + * @brief Find SSB signal in a given time domain subframe buffer + * @param q SSB object + * @param sf_buffer subframe buffer with 1ms worth of samples + * @param N_id Physical cell identifier to find + * @param meas Measurements performed on the found peak + * @param pbch_msg PBCH decoded message + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_find(srsran_ssb_t* q, + const cf_t* sf_buffer, + uint32_t N_id, + srsran_csi_trs_measurements_t* meas, + srsran_pbch_msg_nr_t* pbch_msg); + +/** + * @brief Track SSB by performing measurements and decoding PBCH + * @param q SSB object + * @param sf_buffer subframe buffer with 1ms worth of samples + * @param N_id Physical cell identifier to find + * @param ssb_idx SSB candidate index + * @param n_hf Number of half frame + * @param meas Measurements perform + * @param pbch_msg PBCH decoded message + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ssb_track(srsran_ssb_t* q, + const cf_t* sf_buffer, + uint32_t N_id, + uint32_t ssb_idx, + uint32_t n_hf, + srsran_csi_trs_measurements_t* meas, + srsran_pbch_msg_nr_t* pbch_msg); + +/** + * @brief Calculates the subframe index within the radio frame of a given SSB candidate for the SSB object + * @param q SSB object + * @param ssb_idx SSB candidate index + * @param half_frame Indicates whether it is half frame + * @return The subframe index + */ +SRSRAN_API uint32_t srsran_ssb_candidate_sf_idx(const srsran_ssb_t* q, uint32_t ssb_idx, bool half_frame); + +/** + * @brief Calculates the SSB offset within the subframe of a given SSB candidate for the SSB object + * @param q SSB object + * @param ssb_idx SSB candidate index + * @return The sample offset within the subframe + */ +SRSRAN_API uint32_t srsran_ssb_candidate_sf_offset(const srsran_ssb_t* q, uint32_t ssb_idx); + +SRSRAN_API uint32_t srsran_ssb_cfg_to_str(const srsran_ssb_cfg_t* cfg, char* str, uint32_t str_len); + +#endif // SRSRAN_SSB_H diff --git a/lib/include/srsran/phy/sync/sss.h b/lib/include/srsran/phy/sync/sss.h index bbc485189..b66020386 100644 --- a/lib/include/srsran/phy/sync/sss.h +++ b/lib/include/srsran/phy/sync/sss.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/sss_nr.h b/lib/include/srsran/phy/sync/sss_nr.h new file mode 100644 index 000000000..5208dad43 --- /dev/null +++ b/lib/include/srsran/phy/sync/sss_nr.h @@ -0,0 +1,73 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SSS_NR_H +#define SRSRAN_SSS_NR_H + +#include "srsran/config.h" +#include "srsran/phy/common/phy_common_nr.h" +#include + +/** + * @brief NR SSS sequence length in frequency domain + */ +#define SRSRAN_SSS_NR_LEN 127 + +/** + * @brief NR SSS Symbol number + */ +#define SRSRAN_SSS_NR_SYMBOL_IDX 2 + +/** + * @brief Put NR SSS sequence in the SSB grid + * @remark Described in TS 38.211 section 7.4.2.3 Secondary synchronization signal + * @param ssb_grid SSB resource grid + * @param N_id_1 Physical cell ID 1 + * @param N_id_2 Physical cell ID 2 + * @param beta SSS power allocation + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_sss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_1, uint32_t N_id_2, float beta); + +/** + * @brief Extracts the NR SSS Least Square Estimates (LSE) from the SSB grid + * @param ssb_grid received SSB resource grid + * @param N_id_1 Physical cell ID 1 + * @param N_id_2 Physical cell ID 2 + * @param lse Provides LSE pointer + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int +srsran_sss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_1, uint32_t N_id_2, cf_t lse[SRSRAN_SSS_NR_LEN]); + +/** + * @brief Find the best SSS sequence given the N_id_2 and the SSB resource grid + * @attention Assumes the SSB is synchronized and the average delay is pre-compensated + * @param ssb_grid The SSB resource grid to search + * @param N_id_2 Fix N_id_2 to search, it reduces the search space 1/3 + * @param norm_corr Normalised correlation of the best found sequence + * @param found_N_id_1 The N_id_1 of the best sequence + * @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int +srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float* norm_corr, uint32_t* found_N_id_1); + +#endif // SRSRAN_SSS_NR_H diff --git a/lib/include/srsran/phy/sync/ssss.h b/lib/include/srsran/phy/sync/ssss.h index c1e306743..40a20afc2 100644 --- a/lib/include/srsran/phy/sync/ssss.h +++ b/lib/include/srsran/phy/sync/ssss.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/sync.h b/lib/include/srsran/phy/sync/sync.h index 4c74c792b..95b482525 100644 --- a/lib/include/srsran/phy/sync/sync.h +++ b/lib/include/srsran/phy/sync/sync.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/sync/sync_nbiot.h b/lib/include/srsran/phy/sync/sync_nbiot.h index e49719de2..15baebe7f 100644 --- a/lib/include/srsran/phy/sync/sync_nbiot.h +++ b/lib/include/srsran/phy/sync/sync_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ue/ue_cell_search.h b/lib/include/srsran/phy/ue/ue_cell_search.h index 05e2a5eb3..971245012 100644 --- a/lib/include/srsran/phy/ue/ue_cell_search.h +++ b/lib/include/srsran/phy/ue/ue_cell_search.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -99,5 +99,7 @@ SRSRAN_API int srsran_ue_cellsearch_scan(srsran_ue_cellsearch_t* q, SRSRAN_API int srsran_ue_cellsearch_set_nof_valid_frames(srsran_ue_cellsearch_t* q, uint32_t nof_frames); +SRSRAN_API void srsran_set_detect_cp(srsran_ue_cellsearch_t* q, bool enable); + #endif // SRSRAN_UE_CELL_SEARCH_H diff --git a/lib/include/srsran/phy/ue/ue_cell_search_nbiot.h b/lib/include/srsran/phy/ue/ue_cell_search_nbiot.h index bfaac0daf..48a1b1a50 100644 --- a/lib/include/srsran/phy/ue/ue_cell_search_nbiot.h +++ b/lib/include/srsran/phy/ue/ue_cell_search_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ue/ue_dl.h b/lib/include/srsran/phy/ue/ue_dl.h index 360bffd59..28f88a5cd 100644 --- a/lib/include/srsran/phy/ue/ue_dl.h +++ b/lib/include/srsran/phy/ue/ue_dl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -208,7 +208,7 @@ SRSRAN_API int srsran_ue_dl_decode_pdsch(srsran_ue_dl_t* q, SRSRAN_API int srsran_ue_dl_decode_pmch(srsran_ue_dl_t* q, srsran_dl_sf_cfg_t* sf, srsran_pmch_cfg_t* pmch_cfg, - srsran_pdsch_res_t* data); + srsran_pdsch_res_t data[SRSRAN_MAX_CODEWORDS]); SRSRAN_API int srsran_ue_dl_decode_phich(srsran_ue_dl_t* q, srsran_dl_sf_cfg_t* sf, diff --git a/lib/include/srsran/phy/ue/ue_dl_nbiot.h b/lib/include/srsran/phy/ue/ue_dl_nbiot.h index 3a561d8d2..d2310f16b 100644 --- a/lib/include/srsran/phy/ue/ue_dl_nbiot.h +++ b/lib/include/srsran/phy/ue/ue_dl_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ue/ue_dl_nr.h b/lib/include/srsran/phy/ue/ue_dl_nr.h index 7843d9e4a..1f07544e3 100644 --- a/lib/include/srsran/phy/ue/ue_dl_nr.h +++ b/lib/include/srsran/phy/ue/ue_dl_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,9 +22,11 @@ #ifndef SRSRAN_UE_DL_NR_H #define SRSRAN_UE_DL_NR_H +#include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/ch_estimation/dmrs_pdcch.h" #include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/dft/ofdm.h" +#include "srsran/phy/phch/csi.h" #include "srsran/phy/phch/dci_nr.h" #include "srsran/phy/phch/pdcch_cfg_nr.h" #include "srsran/phy/phch/pdcch_nr.h" @@ -45,49 +47,6 @@ typedef struct SRSRAN_API { float pdcch_dmrs_epre_thr; } srsran_ue_dl_nr_args_t; -typedef struct { - uint32_t scell_idx; ///< Serving cell index - uint32_t v_dai_dl; ///< Downlink Assigment Index - bool dci_format_1_1; ///< Set to true if the PDSCH transmission is triggered by a PDCCH DCI format 1_1 transmission - uint32_t k1; ///< HARQ feedback timing - uint16_t rnti; - uint32_t pucch_resource_id; -} srsran_pdsch_ack_resource_nr_t; - -typedef struct { - srsran_pdsch_ack_resource_nr_t resource; - uint8_t value[SRSRAN_MAX_CODEWORDS]; // 0/1 or 2 for DTX - bool present; // set to true if there is a PDSCH on serving cell c associated with PDCCH in PDCCH monitoring occasion - // m, or there is a PDCCH indicating SPS PDSCH release on serving cell c - bool dl_bwp_changed; // set to true if PDCCH monitoring occasion m is before an active DL BWP change on serving cell c - bool ul_bwp_changed; // set to true if an active UL BWP change on the PCell and an active DL BWP change is not - // triggered by a DCI format 1_1 in PDCCH monitoring occasion m - bool second_tb_present; // set to true if two TB were detected in the PDCCH monitoring occasion m -} srsran_pdsch_ack_m_nr_t; - -#define SRSRAN_UCI_NR_MAX_M 10 - -typedef struct { - uint32_t M; - srsran_pdsch_ack_m_nr_t m[SRSRAN_UCI_NR_MAX_M]; -} srsran_pdsch_ack_cc_nr_t; - -typedef struct { - srsran_pdsch_ack_cc_nr_t cc[SRSRAN_MAX_CARRIERS]; - uint32_t nof_cc; - bool use_pusch; // Set to true, if UCI bits are carried by PUSCH -} srsran_pdsch_ack_nr_t; - -typedef struct SRSRAN_API { - bool harq_ack_spatial_bundling_pucch; ///< Param harq-ACK-SpatialBundlingPUCCH, set to true if provided - bool harq_ack_spatial_bundling_pusch; ///< Param harq-ACK-SpatialBundlingPUSCH, set to true if provided - srsran_harq_ack_codebook_t harq_ack_codebook; ///< pdsch-HARQ-ACK-Codebook configuration - bool max_cw_sched_dci_is_2; ///< Param maxNrofCodeWordsScheduledByDCI, set to true if present and equal to 2 - - uint32_t dl_data_to_ul_ack[SRSRAN_MAX_NOF_DL_DATA_TO_UL]; - uint32_t nof_dl_data_to_ul_ack; -} srsran_ue_dl_nr_harq_ack_cfg_t; - typedef struct SRSRAN_API { srsran_dci_ctx_t dci_ctx; srsran_dmrs_pdcch_measure_t measure; @@ -162,22 +121,22 @@ SRSRAN_API int srsran_ue_dl_nr_decode_pdsch(srsran_ue_dl_nr_t* q, const srsran_sch_cfg_nr_t* cfg, srsran_pdsch_res_nr_t* res); -SRSRAN_API int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, - const srsran_sch_cfg_nr_t* cfg, - const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS], - char* str, - uint32_t str_len); +SRSRAN_API uint32_t srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, + const srsran_sch_cfg_nr_t* cfg, + const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS], + char* str, + uint32_t str_len); -SRSRAN_API int srsran_ue_dl_nr_pdsch_ack_resource(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, - const srsran_dci_dl_nr_t* dci_dl, - srsran_pdsch_ack_resource_nr_t* pdsch_ack_resource); +SRSRAN_API +int srsran_ue_dl_nr_csi_measure_trs(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_trs_measurements_t* measurement); -SRSRAN_API int srsran_ue_dl_nr_gen_ack(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, - const srsran_pdsch_ack_nr_t* ack_info, - srsran_uci_data_nr_t* uci_data); - -SRSRAN_API int srsran_ue_dl_nr_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, srsran_pdsch_ack_m_nr_t* m); - -SRSRAN_API uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len); +SRSRAN_API +int srsran_ue_dl_nr_csi_measure_channel(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_channel_measurements_t* measurement); #endif // SRSRAN_UE_DL_NR_H diff --git a/lib/include/srsran/phy/ue/ue_mib.h b/lib/include/srsran/phy/ue/ue_mib.h index 74c8c01b5..81f2def37 100644 --- a/lib/include/srsran/phy/ue/ue_mib.h +++ b/lib/include/srsran/phy/ue/ue_mib.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ue/ue_mib_nbiot.h b/lib/include/srsran/phy/ue/ue_mib_nbiot.h index 84b985089..78762ee06 100644 --- a/lib/include/srsran/phy/ue/ue_mib_nbiot.h +++ b/lib/include/srsran/phy/ue/ue_mib_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ue/ue_mib_sl.h b/lib/include/srsran/phy/ue/ue_mib_sl.h index 7fbf2fa7b..25a66b59c 100644 --- a/lib/include/srsran/phy/ue/ue_mib_sl.h +++ b/lib/include/srsran/phy/ue/ue_mib_sl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ue/ue_phy.h b/lib/include/srsran/phy/ue/ue_phy.h index ba50a0e08..a5c907c52 100644 --- a/lib/include/srsran/phy/ue/ue_phy.h +++ b/lib/include/srsran/phy/ue/ue_phy.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ue/ue_sync.h b/lib/include/srsran/phy/ue/ue_sync.h index 67c36cd73..d82e9bbb5 100644 --- a/lib/include/srsran/phy/ue/ue_sync.h +++ b/lib/include/srsran/phy/ue/ue_sync.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -247,6 +247,8 @@ SRSRAN_API uint32_t srsran_ue_sync_get_sfidx(srsran_ue_sync_t* q); SRSRAN_API float srsran_ue_sync_get_cfo(srsran_ue_sync_t* q); +SRSRAN_API void srsran_ue_sync_cp_en(srsran_ue_sync_t* q, bool enabled); + SRSRAN_API float srsran_ue_sync_get_sfo(srsran_ue_sync_t* q); SRSRAN_API int srsran_ue_sync_get_last_sample_offset(srsran_ue_sync_t* q); diff --git a/lib/include/srsran/phy/ue/ue_sync_nbiot.h b/lib/include/srsran/phy/ue/ue_sync_nbiot.h index ee8cb0c2f..c4099e2d3 100644 --- a/lib/include/srsran/phy/ue/ue_sync_nbiot.h +++ b/lib/include/srsran/phy/ue/ue_sync_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/ue/ue_sync_nr.h b/lib/include/srsran/phy/ue/ue_sync_nr.h new file mode 100644 index 000000000..f8e340dcd --- /dev/null +++ b/lib/include/srsran/phy/ue/ue_sync_nr.h @@ -0,0 +1,153 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_UE_SYNC_NR_H +#define SRSRAN_UE_SYNC_NR_H + +#include "srsran/phy/common/timestamp.h" +#include "srsran/phy/sync/ssb.h" + +#define SRSRAN_RECV_CALLBACK_TEMPLATE(NAME) int (*NAME)(void*, cf_t**, uint32_t, srsran_timestamp_t*) + +/** + * @brief Describes NR UE synchronization object internal states + */ +typedef enum SRSRAN_API { + SRSRAN_UE_SYNC_NR_STATE_IDLE = 0, ///< Initial state, the object has no configuration + SRSRAN_UE_SYNC_NR_STATE_FIND, ///< State just after configuring, baseband is not aligned + SRSRAN_UE_SYNC_NR_STATE_TRACK ///< Baseband is aligned with subframes +} srsran_ue_sync_nr_state_t; + +/** + * @brief Describes a UE sync NR object arguments + */ +typedef struct SRSRAN_API { + // Memory allocation constraints + double max_srate_hz; ///< Maximum sampling rate in Hz, set to zero to use default + srsran_subcarrier_spacing_t min_scs; ///< Minimum subcarrier spacing + uint32_t nof_rx_channels; ///< Number of receive channels, set to 0 for 1 + + // Enable/Disable features + bool disable_cfo; ///< Set to true for disabling the CFO compensation close loop + + // Signal detection thresholds and averaging coefficients + float pbch_dmrs_thr; ///< NR-PBCH DMRS threshold for blind decoding, set to 0 for default + float cfo_alpha; ///< Exponential Moving Average (EMA) alpha coefficient for CFO + + // Receive callback + void* recv_obj; ///< Receive object + SRSRAN_RECV_CALLBACK_TEMPLATE(recv_callback); ///< Receive callback +} srsran_ue_sync_nr_args_t; + +/** + * @brief Describes a UE sync NR object configuration + */ +typedef struct SRSRAN_API { + srsran_ssb_cfg_t ssb; ///< SSB configuration + uint32_t N_id; ///< Physical cell identifier +} srsran_ue_sync_nr_cfg_t; + +/** + * @brief Describes a UE sync NR object + */ +typedef struct SRSRAN_API { + // State + srsran_ue_sync_nr_state_t state; ///< Internal state + int32_t next_rf_sample_offset; ///< Next RF sample offset + uint32_t ssb_idx; ///< Tracking SSB candidate index + uint32_t sf_idx; ///< Current subframe index (0-9) + uint32_t sfn; ///< Current system frame number (0-1023) + srsran_csi_trs_measurements_t feedback; ///< Feedback measurements + + // Components + srsran_ssb_t ssb; ///< SSB internal object + cf_t** tmp_buffer; ///< Temporal buffer pointers + + // Initialised arguments + uint32_t nof_rx_channels; ///< Number of receive channels + bool disable_cfo; ///< Set to true for disabling the CFO compensation close loop + float cfo_alpha; ///< Exponential Moving Average (EMA) alpha coefficient for CFO + void* recv_obj; ///< Receive object + SRSRAN_RECV_CALLBACK_TEMPLATE(recv_callback); ///< Receive callback + + // Current configuration + uint32_t N_id; ///< Current physical cell identifier + double srate_hz; ///< Current sampling rate in Hz + uint32_t sf_sz; ///< Current subframe size + + // Metrics + float cfo_hz; ///< Current CFO in Hz + float avg_delay_us; ///< Current average delay +} srsran_ue_sync_nr_t; + +/** + * @brief Describes a UE sync NR zerocopy outcome + */ +typedef struct SRSRAN_API { + bool in_sync; ///< Indicates whether the received baseband is synchronized + uint32_t sf_idx; ///< Subframe index + uint32_t sfn; ///< System Frame Number + srsran_timestamp_t timestamp; ///< Last received timestamp + float cfo_hz; ///< Current CFO in Hz + float delay_us; ///< Current average delay in microseconds +} srsran_ue_sync_nr_outcome_t; + +/** + * @brief Initialises a UE sync NR object + * @param q NR UE synchronization object + * @param[in] args NR UE synchronization initialization arguments + * @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ue_sync_nr_init(srsran_ue_sync_nr_t* q, const srsran_ue_sync_nr_args_t* args); + +/** + * @brief Deallocate an NR UE synchronization object + * @param q NR UE synchronization object + */ +SRSRAN_API void srsran_ue_sync_nr_free(srsran_ue_sync_nr_t* q); + +/** + * @brief Configures a UE sync NR object + * @param q NR UE synchronization object + * @param[in] cfg NR UE synchronization configuration + * @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ue_sync_nr_set_cfg(srsran_ue_sync_nr_t* q, const srsran_ue_sync_nr_cfg_t* cfg); + +/** + * @brief Runs the NR UE synchronization object, tries to find and track the configured SSB leaving in buffer the + * received baseband subframe + * @param q NR UE synchronization object + * @param buffer 2D complex buffer + * @param outcome zerocopy outcome + * @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ue_sync_nr_zerocopy(srsran_ue_sync_nr_t* q, cf_t** buffer, srsran_ue_sync_nr_outcome_t* outcome); + +/** + * @brief Feedback Channel State Information from Tracking Reference Signals into a UE synchronization object + * @param q NR UE synchronization object + * @param measurements CSI-TRS feedback measurement + * @return SRSRAN_SUCCESS if no error occurs, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_ue_sync_nr_feedback(srsran_ue_sync_nr_t* q, const srsran_csi_trs_measurements_t* measurements); + +#endif // SRSRAN_UE_SYNC_NR_H diff --git a/lib/include/srsran/phy/ue/ue_ul.h b/lib/include/srsran/phy/ue/ue_ul.h index 03f883d6e..a694e59c6 100644 --- a/lib/include/srsran/phy/ue/ue_ul.h +++ b/lib/include/srsran/phy/ue/ue_ul.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -108,6 +108,8 @@ typedef struct SRSRAN_API { srsran_ra_ul_pusch_hopping_t hopping; + srsran_cfr_cfg_t cfr_config; + cf_t* out_buffer; cf_t* refsignal; cf_t* srs_signal; @@ -121,6 +123,8 @@ SRSRAN_API void srsran_ue_ul_free(srsran_ue_ul_t* q); SRSRAN_API int srsran_ue_ul_set_cell(srsran_ue_ul_t* q, srsran_cell_t cell); +SRSRAN_API int srsran_ue_ul_set_cfr(srsran_ue_ul_t* q, const srsran_cfr_cfg_t* cfr); + SRSRAN_API int srsran_ue_ul_pregen_signals(srsran_ue_ul_t* q, srsran_ue_ul_cfg_t* cfg); SRSRAN_API int srsran_ue_ul_dci_to_pusch_grant(srsran_ue_ul_t* q, diff --git a/lib/include/srsran/phy/ue/ue_ul_nr.h b/lib/include/srsran/phy/ue/ue_ul_nr.h index 6171ba77e..c63c63fa4 100644 --- a/lib/include/srsran/phy/ue/ue_ul_nr.h +++ b/lib/include/srsran/phy/ue/ue_ul_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -57,12 +57,16 @@ typedef struct SRSRAN_API { srsran_pusch_nr_t pusch; srsran_pucch_nr_t pucch; srsran_dmrs_sch_t dmrs; + + float freq_offset_hz; } srsran_ue_ul_nr_t; SRSRAN_API int srsran_ue_ul_nr_init(srsran_ue_ul_nr_t* q, cf_t* output, const srsran_ue_ul_nr_args_t* args); SRSRAN_API int srsran_ue_ul_nr_set_carrier(srsran_ue_ul_nr_t* q, const srsran_carrier_nr_t* carrier); +SRSRAN_API void srsran_ue_ul_nr_set_freq_offset(srsran_ue_ul_nr_t* q, float freq_offset_hz); + SRSRAN_API int srsran_ue_ul_nr_encode_pusch(srsran_ue_ul_nr_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_sch_cfg_nr_t* pusch_cfg, diff --git a/lib/include/srsran/phy/utils/bit.h b/lib/include/srsran/phy/utils/bit.h index 06dc1d229..7a5720652 100644 --- a/lib/include/srsran/phy/utils/bit.h +++ b/lib/include/srsran/phy/utils/bit.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,6 +35,10 @@ #include "srsran/config.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { uint32_t nof_bits; uint16_t* interleaver; @@ -81,10 +85,16 @@ SRSRAN_API void srsran_bit_unpack_l(uint64_t value, uint8_t** bits, int nof_bits SRSRAN_API void srsran_bit_unpack(uint32_t value, uint8_t** bits, int nof_bits); +SRSRAN_API void srsran_bit_unpack_lsb(uint32_t value, uint8_t** bits, int nof_bits); + SRSRAN_API void srsran_bit_fprint(FILE* stream, uint8_t* bits, int nof_bits); SRSRAN_API uint32_t srsran_bit_diff(const uint8_t* x, const uint8_t* y, int nbits); SRSRAN_API uint32_t srsran_bit_count(uint32_t n); +#ifdef __cplusplus +} +#endif + #endif // SRSRAN_BIT_H diff --git a/lib/include/srsran/phy/utils/cexptab.h b/lib/include/srsran/phy/utils/cexptab.h index 57e614e0f..86131fe0b 100644 --- a/lib/include/srsran/phy/utils/cexptab.h +++ b/lib/include/srsran/phy/utils/cexptab.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/utils/convolution.h b/lib/include/srsran/phy/utils/convolution.h index fb93ebee6..51b8470f8 100644 --- a/lib/include/srsran/phy/utils/convolution.h +++ b/lib/include/srsran/phy/utils/convolution.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/utils/debug.h b/lib/include/srsran/phy/utils/debug.h index 3d668c755..c18541a3f 100644 --- a/lib/include/srsran/phy/utils/debug.h +++ b/lib/include/srsran/phy/utils/debug.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,30 +33,38 @@ #include "phy_logger.h" #include "srsran/config.h" #include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus #define SRSRAN_VERBOSE_DEBUG 2 #define SRSRAN_VERBOSE_INFO 1 #define SRSRAN_VERBOSE_NONE 0 -#include SRSRAN_API void get_time_interval(struct timeval* tdata); #define SRSRAN_DEBUG_ENABLED 1 -SRSRAN_API extern int srsran_verbose; -SRSRAN_API extern int handler_registered; +SRSRAN_API int get_srsran_verbose_level(void); +SRSRAN_API void set_srsran_verbose_level(int level); +SRSRAN_API void increase_srsran_verbose_level(void); -#define SRSRAN_VERBOSE_ISINFO() (srsran_verbose >= SRSRAN_VERBOSE_INFO) -#define SRSRAN_VERBOSE_ISDEBUG() (srsran_verbose >= SRSRAN_VERBOSE_DEBUG) -#define SRSRAN_VERBOSE_ISNONE() (srsran_verbose == SRSRAN_VERBOSE_NONE) +SRSRAN_API bool is_handler_registered(void); +SRSRAN_API void set_handler_enabled(bool enable); -#define PRINT_DEBUG srsran_verbose = SRSRAN_VERBOSE_DEBUG -#define PRINT_INFO srsran_verbose = SRSRAN_VERBOSE_INFO -#define PRINT_NONE srsran_verbose = SRSRAN_VERBOSE_NONE +#define SRSRAN_VERBOSE_ISINFO() (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) +#define SRSRAN_VERBOSE_ISDEBUG() (get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG) +#define SRSRAN_VERBOSE_ISNONE() (get_srsran_verbose_level() == SRSRAN_VERBOSE_NONE) + +#define PRINT_DEBUG set_srsran_verbose_level(SRSRAN_VERBOSE_DEBUG) +#define PRINT_INFO set_srsran_verbose_level(SRSRAN_VERBOSE_INFO) +#define PRINT_NONE set_srsran_verbose_level(SRSRAN_VERBOSE_NONE) #define DEBUG(_fmt, ...) \ do { \ - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { \ + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { \ fprintf(stdout, "[DEBUG]: " _fmt "\n", ##__VA_ARGS__); \ } else { \ srsran_phy_log_print(LOG_LEVEL_DEBUG_S, _fmt, ##__VA_ARGS__); \ @@ -65,7 +73,7 @@ SRSRAN_API extern int handler_registered; #define INFO(_fmt, ...) \ do { \ - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { \ + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { \ fprintf(stdout, "[INFO]: " _fmt "\n", ##__VA_ARGS__); \ } else { \ srsran_phy_log_print(LOG_LEVEL_INFO_S, _fmt, ##__VA_ARGS__); \ @@ -76,19 +84,23 @@ SRSRAN_API extern int handler_registered; /* In debug mode, it prints out the */ #define ERROR(_fmt, ...) \ do { \ - if (!handler_registered) { \ - fprintf(stderr, "\e[31m%s.%d: " _fmt "\e[0m\n", __FILE__, __LINE__, ##__VA_ARGS__); \ + if (!is_handler_registered()) { \ + fprintf(stderr, "\e[31m%s:%d: " _fmt "\e[0m\n", __FILE__, __LINE__, ##__VA_ARGS__); \ } else { \ srsran_phy_log_print(LOG_LEVEL_ERROR_S, _fmt, ##__VA_ARGS__); \ } \ } while (0) #else #define ERROR(_fmt, ...) \ - if (!handler_registered) { \ + if (!is_handler_registered()) { \ fprintf(stderr, "[ERROR in %s]:" _fmt "\n", __FUNCTION__, ##__VA_ARGS__); \ } else { \ srsran_phy_log_print(LOG_LEVEL_ERROR, _fmt, ##__VA_ARGS__); \ } // #endif /* CMAKE_BUILD_TYPE==Debug */ +#ifdef __cplusplus +} +#endif // __cplusplus + #endif // SRSRAN_DEBUG_H diff --git a/lib/include/srsran/phy/utils/filter.h b/lib/include/srsran/phy/utils/filter.h index 854390d8d..be098b845 100644 --- a/lib/include/srsran/phy/utils/filter.h +++ b/lib/include/srsran/phy/utils/filter.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/utils/mat.h b/lib/include/srsran/phy/utils/mat.h index 72e409f63..ed3e9bda8 100644 --- a/lib/include/srsran/phy/utils/mat.h +++ b/lib/include/srsran/phy/utils/mat.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/utils/phy_logger.h b/lib/include/srsran/phy/utils/phy_logger.h index cd2d8b702..a493887d1 100644 --- a/lib/include/srsran/phy/utils/phy_logger.h +++ b/lib/include/srsran/phy/utils/phy_logger.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/utils/primes.h b/lib/include/srsran/phy/utils/primes.h index 81fca7aa3..b67d4090b 100644 --- a/lib/include/srsran/phy/utils/primes.h +++ b/lib/include/srsran/phy/utils/primes.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/utils/random.h b/lib/include/srsran/phy/utils/random.h index c06799fb6..3af668054 100644 --- a/lib/include/srsran/phy/utils/random.h +++ b/lib/include/srsran/phy/utils/random.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,6 +48,10 @@ SRSRAN_API float srsran_random_gauss_dist(srsran_random_t q, float std_dev); SRSRAN_API bool srsran_random_bool(srsran_random_t q, float prob_true); +SRSRAN_API void srsran_random_byte_vector(srsran_random_t q, uint8_t* c, uint32_t nsamples); + +SRSRAN_API void srsran_random_bit_vector(srsran_random_t q, uint8_t* c, uint32_t nsamples); + SRSRAN_API void srsran_random_free(srsran_random_t q); #ifdef __cplusplus diff --git a/lib/include/srsran/phy/utils/re_pattern.h b/lib/include/srsran/phy/utils/re_pattern.h index 1fd410dcc..c8140438a 100644 --- a/lib/include/srsran/phy/utils/re_pattern.h +++ b/lib/include/srsran/phy/utils/re_pattern.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/utils/ringbuffer.h b/lib/include/srsran/phy/utils/ringbuffer.h index a822b47f1..2b4135391 100644 --- a/lib/include/srsran/phy/utils/ringbuffer.h +++ b/lib/include/srsran/phy/utils/ringbuffer.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/phy/utils/simd.h b/lib/include/srsran/phy/utils/simd.h index fde7f4595..02abc9278 100644 --- a/lib/include/srsran/phy/utils/simd.h +++ b/lib/include/srsran/phy/utils/simd.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -1177,7 +1177,7 @@ typedef __m128 simd_sel_t; #else /* LV_HAVE_AVX2 */ #ifdef HAVE_NEON typedef int32x4_t simd_i_t; -typedef int32x4_t simd_sel_t; +typedef uint32x4_t simd_sel_t; #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ @@ -1309,7 +1309,7 @@ static inline simd_sel_t srsran_simd_f_max(simd_f_t a, simd_f_t b) return (simd_sel_t)_mm_cmpgt_ps(a, b); #else /* LV_HAVE_SSE */ #ifdef HAVE_NEON - return (simd_sel_t)vcgtq_f32(a, b); + return vcgtq_f32(a, b); #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ @@ -1328,7 +1328,7 @@ static inline simd_sel_t srsran_simd_f_min(simd_f_t a, simd_f_t b) return (simd_sel_t)_mm_cmplt_ps(a, b); #else /* LV_HAVE_SSE */ #ifdef HAVE_NEON - return (simd_sel_t)vcltq_f32(a, b); + return vcltq_f32(a, b); #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ @@ -1346,20 +1346,8 @@ static inline simd_f_t srsran_simd_f_select(simd_f_t a, simd_f_t b, simd_sel_t s #ifdef LV_HAVE_SSE return _mm_blendv_ps(a, b, selector); #else /* LV_HAVE_SSE */ -#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON - float* a_ptr = (float*)&a; - float* b_ptr = (float*)&b; - simd_f_t ret; - int* sel = (int*)&selector; - float* c_ptr = (float*)&ret; - for (int i = 0; i < 4; i++) { - if (sel[i] == -1) { - c_ptr[i] = b_ptr[i]; - } else { - c_ptr[i] = a_ptr[i]; - } - } - return ret; +#ifdef HAVE_NEON + return vbslq_f32(selector, b, a); #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ @@ -1377,20 +1365,8 @@ static inline simd_i_t srsran_simd_i_select(simd_i_t a, simd_i_t b, simd_sel_t s #ifdef LV_HAVE_SSE return (__m128i)_mm_blendv_ps((__m128)a, (__m128)b, selector); #else /* LV_HAVE_SSE */ -#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON - int* a_ptr = (int*)&a; - int* b_ptr = (int*)&b; - simd_i_t ret; - int* sel = (int*)&selector; - int* c_ptr = (int*)&ret; - for (int i = 0; i < 4; i++) { - if (sel[i] == -1) { - c_ptr[i] = b_ptr[i]; - } else { - c_ptr[i] = a_ptr[i]; - } - } - return ret; +#ifdef HAVE_NEON + return vbslq_s32(selector, b, a); #endif /* HAVE_NEON */ #endif /* LV_HAVE_SSE */ #endif /* LV_HAVE_AVX2 */ diff --git a/lib/include/srsran/phy/utils/vector.h b/lib/include/srsran/phy/utils/vector.h index a12ff1184..c3bfae41c 100644 --- a/lib/include/srsran/phy/utils/vector.h +++ b/lib/include/srsran/phy/utils/vector.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -52,16 +52,44 @@ extern "C" { #define SRSRAN_CEIL(NUM, DEN) (((NUM) + ((DEN)-1)) / (DEN)) #define SRSRAN_FLOOR(NUM, DEN) ((NUM) / (DEN)) #define SRSRAN_ROUND(NUM, DEN) ((uint32_t)round((double)(NUM) / (double)(DEN))) +#define SRSRAN_CEIL_LOG2(N) (((N) == 0) ? 0 : ceil(log2((double)(N)))) + +// Complex squared absolute value +#define SRSRAN_CSQABS(X) (__real__(X) * __real__(X) + __imag__(X) * __imag__(X)) // Cumulative moving average #define SRSRAN_VEC_CMA(data, average, n) ((average) + ((data) - (average)) / ((n) + 1)) +// Cumulative moving average +#ifdef __cplusplus +#define SRSRAN_VEC_SAFE_CMA(data, average, n) (std::isnormal(average) ? SRSRAN_VEC_CMA(data, average, n) : (data)) +#else +#define SRSRAN_VEC_SAFE_CMA(data, average, n) (isnormal(average) ? SRSRAN_VEC_CMA(data, average, n) : (data)) +#endif + // Proportional moving average #define SRSRAN_VEC_PMA(average1, n1, average2, n2) (((average1) * (n1) + (average2) * (n2)) / ((n1) + (n2))) +// Safe Proportional moving average +#ifdef __cplusplus +#define SRSRAN_VEC_SAFE_PMA(average1, n1, average2, n2) \ + (std::isnormal((n1) + (n2)) ? SRSRAN_VEC_PMA(average1, n1, average2, n2) : (0)) +#else +#define SRSRAN_VEC_SAFE_PMA(average1, n1, average2, n2) \ + (isnormal((n1) + (n2)) ? SRSRAN_VEC_PMA(average1, n1, average2, n2) : (0)) +#endif + // Exponential moving average #define SRSRAN_VEC_EMA(data, average, alpha) ((alpha) * (data) + (1 - alpha) * (average)) +// Safe exponential moving average +#ifdef __cplusplus +#define SRSRAN_VEC_SAFE_EMA(data, average, alpha) \ + (std::isnormal(average) ? SRSRAN_VEC_EMA(data, average, alpha) : (data)) +#else +#define SRSRAN_VEC_SAFE_EMA(data, average, alpha) (isnormal(average) ? SRSRAN_VEC_EMA(data, average, alpha) : (data)) +#endif + static inline float srsran_convert_amplitude_to_dB(float v) { return 20.0f * log10f(v); @@ -126,16 +154,16 @@ SRSRAN_API void srsran_vec_u16_copy(uint16_t* dst, const uint16_t* src, uint32_t SRSRAN_API void srsran_vec_i16_copy(int16_t* dst, const int16_t* src, uint32_t len); /* print vectors */ -SRSRAN_API void srsran_vec_fprint_c(FILE* stream, const cf_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_f(FILE* stream, const float* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_b(FILE* stream, const uint8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_bs(FILE* stream, const int8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_byte(FILE* stream, const uint8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_i(FILE* stream, const int* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_s(FILE* stream, const int16_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_fprint_hex(FILE* stream, uint8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len); -SRSRAN_API void srsran_vec_sprint_bin(char* str, const uint32_t max_str_len, const uint8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_c(FILE* stream, const cf_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_f(FILE* stream, const float* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_b(FILE* stream, const uint8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_bs(FILE* stream, const int8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_byte(FILE* stream, const uint8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_i(FILE* stream, const int* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_s(FILE* stream, const int16_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_fprint_hex(FILE* stream, uint8_t* x, const uint32_t len); +SRSRAN_API uint32_t srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len); +SRSRAN_API void srsran_vec_sprint_bin(char* str, const uint32_t max_str_len, const uint8_t* x, const uint32_t len); /* Saves/loads a vector to a file */ SRSRAN_API void srsran_vec_save_file(char* filename, const void* buffer, const uint32_t len); @@ -152,6 +180,9 @@ SRSRAN_API void srsran_vec_sub_ccc(const cf_t* x, const cf_t* y, cf_t* z, const SRSRAN_API void srsran_vec_sub_sss(const int16_t* x, const int16_t* y, int16_t* z, const uint32_t len); SRSRAN_API void srsran_vec_sub_bbb(const int8_t* x, const int8_t* y, int8_t* z, const uint32_t len); +/* sum a scalar to all elements of a vector */ +SRSRAN_API void srsran_vec_sc_sum_fff(const float* x, float h, float* z, uint32_t len); + /* scalar product */ SRSRAN_API void srsran_vec_sc_prod_cfc(const cf_t* x, const float h, cf_t* z, const uint32_t len); SRSRAN_API void srsran_vec_sc_prod_fcc(const float* x, const cf_t h, cf_t* z, const uint32_t len); @@ -211,6 +242,7 @@ SRSRAN_API void srsran_vec_conj_cc(const cf_t* x, cf_t* y, const uint32_t len); SRSRAN_API float srsran_vec_avg_power_cf(const cf_t* x, const uint32_t len); SRSRAN_API float srsran_vec_avg_power_sf(const int16_t* x, const uint32_t len); SRSRAN_API float srsran_vec_avg_power_bf(const int8_t* x, const uint32_t len); +SRSRAN_API float srsran_vec_avg_power_ff(const float* x, const uint32_t len); /* Correlation between complex vectors x and y */ SRSRAN_API float srsran_vec_corr_ccc(const cf_t* x, cf_t* y, const uint32_t len); @@ -333,12 +365,42 @@ SRSRAN_API void srsran_vec_interleave(const cf_t* x, const cf_t* y, cf_t* z, con SRSRAN_API void srsran_vec_interleave_add(const cf_t* x, const cf_t* y, cf_t* z, const int len); -SRSRAN_API void srsran_vec_gen_sine(cf_t amplitude, float freq, cf_t* z, int len); +SRSRAN_API cf_t srsran_vec_gen_sine(cf_t amplitude, float freq, cf_t* z, int len); SRSRAN_API void srsran_vec_apply_cfo(const cf_t* x, float cfo, cf_t* z, int len); SRSRAN_API float srsran_vec_estimate_frequency(const cf_t* x, int len); +/*! + * @brief Generates an amplitude envelope that, multiplied point-wise with a vector, results in clipping + * by a specified amplitude threshold. + * @param[in] x_abs Absolute value vector of the signal to be clipped + * @param[in] thres Clipping threshold + * @param[out] clip_env The generated clipping envelope + * @param[in] len Length of the vector. + */ +SRSRAN_API void +srsran_vec_gen_clip_env(const float* x_abs, const float thres, const float alpha, float* env, const int len); + +/*! + * @brief Calculates the PAPR of a complex vector + * @param[in] in Input vector + * @param[in] len Vector length. + */ +SRSRAN_API float srsran_vec_papr_c(const cf_t* in, const int len); + +/*! + * @brief Calculates the ACPR of a signal using its baseband spectrum + * @attention The spectrum passed by x_f needs to be in FFT form + * @param[in] x_f Spectrum of the signal + * @param[in] win_pos_len Channel frequency window for the positive side of the spectrum + * @param[in] win_neg_len Channel frequency window for the negative side of the spectrum + * @param[in] len Length of the x_f vector + * @returns The ACPR in linear form + */ +SRSRAN_API float +srsran_vec_acpr_c(const cf_t* x_f, const uint32_t win_pos_len, const uint32_t win_neg_len, const uint32_t len); + #ifdef __cplusplus } #endif diff --git a/lib/include/srsran/phy/utils/vector_simd.h b/lib/include/srsran/phy/utils/vector_simd.h index ccfd27024..22e2980c8 100644 --- a/lib/include/srsran/phy/utils/vector_simd.h +++ b/lib/include/srsran/phy/utils/vector_simd.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,6 +48,8 @@ SRSRAN_API void srsran_vec_add_fff_simd(const float* x, const float* y, float* z SRSRAN_API void srsran_vec_sub_fff_simd(const float* x, const float* y, float* z, int len); +SRSRAN_API void srsran_vec_sc_sum_fff_simd(const float* x, float h, float* z, int len); + /* SIMD Vector Scalar Product */ SRSRAN_API void srsran_vec_sc_prod_cfc_simd(const cf_t* x, const float h, cf_t* y, const int len); @@ -130,7 +132,7 @@ SRSRAN_API void srsran_vec_interleave_simd(const cf_t* x, const cf_t* y, cf_t* z SRSRAN_API void srsran_vec_interleave_add_simd(const cf_t* x, const cf_t* y, cf_t* z, const int len); -SRSRAN_API void srsran_vec_gen_sine_simd(cf_t amplitude, float freq, cf_t* z, int len); +SRSRAN_API cf_t srsran_vec_gen_sine_simd(cf_t amplitude, float freq, cf_t* z, int len); SRSRAN_API void srsran_vec_apply_cfo_simd(const cf_t* x, float cfo, cf_t* z, int len); diff --git a/lib/include/srsran/radio/channel_mapping.h b/lib/include/srsran/radio/channel_mapping.h index 218e1dd7c..23d718293 100644 --- a/lib/include/srsran/radio/channel_mapping.h +++ b/lib/include/srsran/radio/channel_mapping.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -102,10 +102,10 @@ public: void set_channels(const std::list& channels_) { available_channels = channels_; } /** - * Finds an unused physical channel that supports the provided frequency and assigns it to logical channel - * logical_ch + * It deallocates the logical channel if it has been already allocated and it is not suitable, then finds an unused + * physical channel that supports the provided frequency and assigns it to logical channel logical_ch * @param logical_ch logical channel index - * @param freq Frequency (in Hz) that we want to receive/transmitt + * @param freq Frequency (in Hz) that we want to receive/transmit * @return true if a physical channel supporting this frequency was found or false otherwise */ bool allocate_freq(const uint32_t& logical_ch, const float& freq); @@ -147,6 +147,8 @@ private: mutable std::mutex mutex = {}; uint32_t nof_antennas = 1; uint32_t nof_channels_x_dev = 1; + + void release_freq_(const uint32_t& logical_ch); }; } // namespace srsran diff --git a/lib/include/srsran/radio/radio.h b/lib/include/srsran/radio/radio.h index d4a3859ab..5b2b98bcf 100644 --- a/lib/include/srsran/radio/radio.h +++ b/lib/include/srsran/radio/radio.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -99,35 +99,36 @@ private: std::vector rf_info = {}; std::vector rx_offset_n = {}; rf_metrics_t rf_metrics = {}; - srslog::basic_logger& logger = srslog::fetch_basic_logger("RF", false); - phy_interface_radio* phy = nullptr; - cf_t* zeros = nullptr; - std::array dummy_buffers; + std::mutex metrics_mutex; + srslog::basic_logger& logger = srslog::fetch_basic_logger("RF", false); + phy_interface_radio* phy = nullptr; + std::vector zeros; + std::array, SRSRAN_MAX_CHANNELS> dummy_buffers; std::mutex tx_mutex; std::mutex rx_mutex; std::array, SRSRAN_MAX_CHANNELS> tx_buffer; std::array, SRSRAN_MAX_CHANNELS> rx_buffer; std::array interpolators = {}; std::array decimators = {}; - bool decimator_busy = false; ///< Indicates the decimator is changing the rate + std::atomic decimator_busy = {false}; ///< Indicates the decimator is changing the rate - rf_timestamp_t end_of_burst_time = {}; - bool is_start_of_burst = false; - uint32_t tx_adv_nsamples = 0; - double tx_adv_sec = 0.0; // Transmission time advance to compensate for antenna->timestamp delay - bool tx_adv_auto = false; - bool tx_adv_negative = false; - bool is_initialized = false; - bool radio_is_streaming = false; - bool continuous_tx = false; - double freq_offset = 0.0; - double cur_tx_srate = 0.0; - double cur_rx_srate = 0.0; - double fix_srate_hz = 0.0; - uint32_t nof_antennas = 0; - uint32_t nof_channels = 0; - uint32_t nof_channels_x_dev = 0; - uint32_t nof_carriers = 0; + rf_timestamp_t end_of_burst_time = {}; + std::atomic is_start_of_burst{false}; + uint32_t tx_adv_nsamples = 0; + double tx_adv_sec = 0.0; // Transmission time advance to compensate for antenna->timestamp delay + bool tx_adv_auto = false; + bool tx_adv_negative = false; + bool is_initialized = false; + bool radio_is_streaming = false; + bool continuous_tx = false; + double freq_offset = 0.0; + double cur_tx_srate = 0.0; + double cur_rx_srate = 0.0; + double fix_srate_hz = 0.0; + uint32_t nof_antennas = 0; + uint32_t nof_channels = 0; + uint32_t nof_channels_x_dev = 0; + uint32_t nof_carriers = 0; std::vector cur_tx_freqs = {}; std::vector cur_rx_freqs = {}; @@ -166,6 +167,19 @@ private: */ bool open_dev(const uint32_t& device_idx, const std::string& device_name, const std::string& devive_args); + /** + * Helper method for opening a file-based RF device abstraction + * + * @param device_idx Device index + * @param rx_files Array of pre-opened FILE* for rx + * @param tx_files Array of pre-opened FILE* for tx + * @param nof_channels Number of elements in each array @p rx_files and @p tx_files + * @param base_srate Sampling rate in Hz + * @return it returns true if the device was opened successful, otherwise it returns false + */ + bool + open_dev(const uint32_t& device_idx, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate); + /** * Helper method for transmitting over a single RF device. This function maps automatically the logical transmit * buffers to the physical RF buffers for the given device. @@ -181,6 +195,9 @@ private: */ bool tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, const srsran_timestamp_t& tx_time_); + // private unprotected tx_end implementation + void tx_end_nolock(); + /** * Helper method for receiving over a single RF device. This function maps automatically the logical receive buffers * to the physical RF buffers for the given device. diff --git a/lib/include/srsran/radio/radio_base.h b/lib/include/srsran/radio/radio_base.h index 6b7c01ecc..a69d0de8b 100644 --- a/lib/include/srsran/radio/radio_base.h +++ b/lib/include/srsran/radio/radio_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/radio/radio_dummy.h b/lib/include/srsran/radio/radio_dummy.h new file mode 100644 index 000000000..e9d31f994 --- /dev/null +++ b/lib/include/srsran/radio/radio_dummy.h @@ -0,0 +1,306 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "channel_mapping.h" +#include "radio_metrics.h" +#include "rf_buffer.h" +#include "rf_timestamp.h" +#include "srsran/common/interfaces_common.h" +#include "srsran/interfaces/radio_interfaces.h" +#include "srsran/phy/resampling/resampler.h" +#include "srsran/phy/rf/rf.h" +#include "srsran/radio/radio_base.h" +#include "srsran/srslog/srslog.h" +#include "srsran/srsran.h" + +#include +#include +#include + +#ifndef SRSRAN_RADIO_DUMMY_H +#define SRSRAN_RADIO_DUMMY_H + +namespace srsran { + +/** + * Implementation of radio dummy for the PHY testing + * + * It uses ringbuffers from srsRAN library to emulate baseband transmission and reception. The current implementation + * does not support dynamic sampling rates, gains and frequencies. + */ +class radio_dummy : public srsran::radio_base, public srsran::radio_interface_phy +{ +private: + static const uint32_t TEMP_BUFFER_SZ = SRSRAN_SF_LEN_MAX * SRSRAN_NOF_SF_X_FRAME; + srslog::basic_logger& logger; + std::vector rx_ring_buffers; + std::vector tx_ring_buffers; + std::mutex tx_mutex; + std::atomic srate_hz = {0.0f}; + std::atomic rx_gain = {1.0f}; + std::atomic tx_gain = {1.0f}; + cf_t* temp_buffer = nullptr; + uint64_t rx_timestamp = 0; + uint64_t tx_timestamp = 0; + srsran_rf_info_t rf_info = {}; + std::atomic is_initialised = {false}; + std::atomic quit = {false}; + + void write_ring_buffers(std::vector& buffers, cf_t** buffer, uint32_t nsamples) + { + for (uint32_t i = 0; i < buffers.size(); i++) { + int ret = SRSRAN_SUCCESS; + do { + if (ret != SRSRAN_SUCCESS) { + logger.error("Ring buffer write failed (full). Trying again."); + } + ret = srsran_ringbuffer_write_timed(&buffers[i], buffer[i], (int)(sizeof(cf_t) * nsamples), 1000); + } while (ret == SRSRAN_ERROR_TIMEOUT and not quit); + } + } + + void read_ring_buffers(std::vector& buffers, cf_t** buffer, uint32_t nsamples) + { + for (uint32_t i = 0; i < buffers.size(); i++) { + int ret = SRSRAN_SUCCESS; + do { + if (ret != SRSRAN_SUCCESS) { + logger.error("Ring buffer read failed. Trying again."); + } + ret = srsran_ringbuffer_read_timed(&buffers[i], buffer[i], (int)(sizeof(cf_t) * nsamples), 1000); + } while (ret == SRSRAN_ERROR_TIMEOUT and not quit); + } + } + + void write_zeros_ring_buffers(std::vector& buffers, uint32_t nsamples) + { + uint32_t n = SRSRAN_MIN(nsamples, TEMP_BUFFER_SZ); + srsran_vec_cf_zero(temp_buffer, n); + + std::array zero_buffer_pointers = {}; + for (cf_t*& ptr : zero_buffer_pointers) { + ptr = temp_buffer; + } + + while (nsamples > 0) { + // Get new number of samples + n = SRSRAN_MIN(nsamples, TEMP_BUFFER_SZ); + + // Write zeros in the buffers + write_ring_buffers(buffers, zero_buffer_pointers.data(), n); + + nsamples -= n; + } + } + + void advance_tx_timestamp(uint64_t ts, bool round_sf = false) + { + std::lock_guard lock(tx_mutex); + + // Make sure new timestamp has not passed + if (ts < tx_timestamp) { + return; + } + + // Calculate transmission gap + uint32_t tx_gap = (uint32_t)(ts - tx_timestamp); + + // Round gap to subframe size + if (round_sf) { + uint64_t sf_sz = (uint64_t)(srate_hz / 1e3); + tx_gap = sf_sz * SRSRAN_CEIL(tx_gap, sf_sz); + } + + // Skip zeros if there is no gap + if (tx_gap == 0) { + return; + } + + // Write zeros in tx ring buffer + write_zeros_ring_buffers(tx_ring_buffers, tx_gap); + + // Update new transmit timestamp + tx_timestamp += tx_gap; + } + +public: + radio_dummy() : logger(srslog::fetch_basic_logger("RF", false)) {} + + ~radio_dummy() + { + for (auto& rb : rx_ring_buffers) { + srsran_ringbuffer_free(&rb); + } + for (auto& rb : tx_ring_buffers) { + srsran_ringbuffer_free(&rb); + } + if (temp_buffer) { + free(temp_buffer); + } + } + + std::string get_type() override { return "dummy"; } + int init(const rf_args_t& args_, phy_interface_radio* phy_) override + { + // Set logger level + logger.set_level(srslog::str_to_basic_level(args_.log_level)); + + // Get base sampling rate and assert the value is valid + srate_hz = args_.srate_hz; + if (not std::isnormal(srate_hz)) { + logger.error("A valid sampling rate is missing"); + return SRSRAN_ERROR; + } + + // Create receiver ring buffers + rx_ring_buffers.resize(args_.nof_carriers * (size_t)args_.nof_antennas); + for (auto& rb : rx_ring_buffers) { + if (srsran_ringbuffer_init(&rb, (int)sizeof(cf_t) * TEMP_BUFFER_SZ) != SRSRAN_SUCCESS) { + perror("init softbuffer"); + } + } + + // Create transmitter ring buffers + tx_ring_buffers.resize(args_.nof_carriers * (size_t)args_.nof_antennas); + for (auto& rb : tx_ring_buffers) { + if (srsran_ringbuffer_init(&rb, (int)sizeof(cf_t) * TEMP_BUFFER_SZ) != SRSRAN_SUCCESS) { + perror("init softbuffer"); + } + } + + // Create temporal buffer + temp_buffer = srsran_vec_cf_malloc(TEMP_BUFFER_SZ); + if (!temp_buffer) { + perror("malloc"); + } + + // Set RF Info (in dB) + rf_info.min_rx_gain = 0.0f; + rf_info.max_rx_gain = 90.0f; + rf_info.min_tx_gain = 0.0f; + rf_info.max_tx_gain = 90.0f; + + // Finally, the radio is initialised + is_initialised = true; + + return SRSRAN_SUCCESS; + } + void stop() override { quit = true; } + bool get_metrics(rf_metrics_t* metrics) override { return false; } + + void set_loglevel(std::string& str) { logger.set_level(srslog::str_to_basic_level(str)); } + + void write_rx(cf_t** buffer, uint32_t nsamples) { write_ring_buffers(rx_ring_buffers, buffer, nsamples); } + + void read_tx(cf_t** buffer, uint32_t nsamples) { read_ring_buffers(tx_ring_buffers, buffer, nsamples); } + + bool tx(srsran::rf_buffer_interface& buffer, const srsran::rf_timestamp_interface& tx_time) override + { + bool ret = true; + + // Convert timestamp to samples + uint64_t tx_time_n = srsran_timestamp_uint64(&tx_time.get(0), srate_hz); + + // Check if the transmission is in the past + { + std::lock_guard lock(tx_mutex); + if (tx_time_n < tx_timestamp) { + logger.error("Error transmission in the past for %d samples", (int)(tx_timestamp - tx_time_n)); + return false; + } + } + + // Advance TX to timestamp + advance_tx_timestamp(tx_time_n); + + // From now on, protect buffers + std::lock_guard lock(tx_mutex); + + // Write transmission buffers into the ring buffer + write_ring_buffers(tx_ring_buffers, buffer.to_cf_t(), buffer.get_nof_samples()); + + // Increment transmit timestamp + tx_timestamp += buffer.get_nof_samples(); + + return ret; + } + void release_freq(const uint32_t& carrier_idx) override{}; + void tx_end() override {} + bool rx_now(srsran::rf_buffer_interface& buffer, srsran::rf_timestamp_interface& rxd_time) override + { + // Advance Tx buffer + advance_tx_timestamp(rx_timestamp + buffer.get_nof_samples(), true); + + // Read samples + read_ring_buffers(rx_ring_buffers, buffer.to_cf_t(), buffer.get_nof_samples()); + + // Apply Rx gain + for (uint32_t i = 0; i < rx_ring_buffers.size(); i++) { + cf_t* ptr = buffer.get(i); + srsran_vec_sc_prod_cfc(ptr, rx_gain, ptr, buffer.get_nof_samples()); + } + + // Set Rx timestamp + srsran_timestamp_init_uint64(rxd_time.get_ptr(0), rx_timestamp, (double)srate_hz); + + // Advance timestamp + rx_timestamp += buffer.get_nof_samples(); + + return true; + } + void set_tx_freq(const uint32_t& channel_idx, const double& freq) override + { + logger.info("Set Tx freq to %+.0f MHz.", freq * 1.0e-6); + } + void set_rx_freq(const uint32_t& channel_idx, const double& freq) override + { + logger.info("Set Rx freq to %+.0f MHz.", freq * 1.0e-6); + } + void set_rx_gain_th(const float& gain) override + { + rx_gain = srsran_convert_dB_to_amplitude(gain); + logger.info("Set Rx gain-th to %+.1f dB (%.6f).", gain, rx_gain.load()); + } + void set_tx_gain(const float& gain) override + { + tx_gain = srsran_convert_dB_to_amplitude(gain); + logger.info("Set Tx gain to %+.1f dB (%.6f).", gain, tx_gain.load()); + } + void set_rx_gain(const float& gain) override + { + rx_gain = srsran_convert_dB_to_amplitude(gain); + logger.info("Set Rx gain to %+.1f dB (%.6f).", gain, rx_gain.load()); + } + void set_tx_srate(const double& srate) override { logger.info("Set Tx sampling rate to %+.3f MHz.", srate * 1.0e-6); } + void set_rx_srate(const double& srate) override { logger.info("Set Rx sampling rate to %+.3f MHz.", srate * 1.0e-6); } + void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) override{}; + float get_rx_gain() override { return srsran_convert_amplitude_to_dB(rx_gain); } + double get_freq_offset() override { return 0; } + bool is_continuous_tx() override { return false; } + bool get_is_start_of_burst() override { return false; } + bool is_init() override { return is_initialised; } + void reset() override {} + srsran_rf_info_t* get_info() override { return &rf_info; } +}; + +} // namespace srsran + +#endif // SRSRAN_RADIO_DUMMY_H diff --git a/lib/include/srsran/radio/radio_metrics.h b/lib/include/srsran/radio/radio_metrics.h index 8be71fee7..d08c46102 100644 --- a/lib/include/srsran/radio/radio_metrics.h +++ b/lib/include/srsran/radio/radio_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/radio/radio_null.h b/lib/include/srsran/radio/radio_null.h index f832fecaa..95e2f53e8 100644 --- a/lib/include/srsran/radio/radio_null.h +++ b/lib/include/srsran/radio/radio_null.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/radio/rf_buffer.h b/lib/include/srsran/radio/rf_buffer.h index 9c3ec63de..3ab492c6b 100644 --- a/lib/include/srsran/radio/rf_buffer.h +++ b/lib/include/srsran/radio/rf_buffer.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -121,6 +121,26 @@ public: { sample_buffer.at(logical_ch * nof_antennas + port_idx) = ptr; } + void set_combine(const uint32_t& channel_idx, cf_t* ptr) + { + if (sample_buffer.at(channel_idx) == nullptr) { + sample_buffer.at(channel_idx) = ptr; + } else if (ptr != nullptr) { + srsran_vec_sum_ccc(ptr, sample_buffer.at(channel_idx), sample_buffer.at(channel_idx), nof_samples); + } + } + void set_combine(const uint32_t& logical_ch, const uint32_t& port_idx, const uint32_t& nof_antennas, cf_t* ptr) + { + set_combine(logical_ch * nof_antennas + port_idx, ptr); + } + void set_combine(const rf_buffer_interface& other) + { + // Take the other number of samples always + set_nof_samples(other.get_nof_samples()); + for (uint32_t ch = 0; ch < SRSRAN_MAX_CHANNELS; ch++) { + set_combine(ch, other.get(ch)); + } + } void** to_void() override { return (void**)sample_buffer.data(); } cf_t** to_cf_t() override { return sample_buffer.data(); } uint32_t size() override { return nof_subframes * SRSRAN_SF_LEN_MAX; } diff --git a/lib/include/srsran/radio/rf_timestamp.h b/lib/include/srsran/radio/rf_timestamp.h index c4db90c17..fb5da37a9 100644 --- a/lib/include/srsran/radio/rf_timestamp.h +++ b/lib/include/srsran/radio/rf_timestamp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/upper/bearer_mem_pool.h b/lib/include/srsran/rlc/bearer_mem_pool.h similarity index 95% rename from lib/include/srsran/upper/bearer_mem_pool.h rename to lib/include/srsran/rlc/bearer_mem_pool.h index 6b0a588db..1d1a6043d 100644 --- a/lib/include/srsran/upper/bearer_mem_pool.h +++ b/lib/include/srsran/rlc/bearer_mem_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/upper/rlc.h b/lib/include/srsran/rlc/rlc.h similarity index 84% rename from lib/include/srsran/upper/rlc.h rename to lib/include/srsran/rlc/rlc.h index 8c1829fdc..827908f29 100644 --- a/lib/include/srsran/upper/rlc.h +++ b/lib/include/srsran/rlc/rlc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,8 +28,8 @@ #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/interfaces/ue_rlc_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h" -#include "srsran/upper/rlc_common.h" -#include "srsran/upper/rlc_metrics.h" +#include "srsran/rlc/rlc_common.h" +#include "srsran/rlc/rlc_metrics.h" namespace srsran { @@ -49,12 +49,6 @@ public: srsran::timer_handler* timers_, uint32_t lcid_); - void init(srsue::pdcp_interface_rlc* pdcp_, - srsue::rrc_interface_rlc* rrc_, - srsue::rrc_interface_rlc* rrc_nr_, - srsran::timer_handler* timers_, - uint32_t lcid_); - void init(srsue::pdcp_interface_rlc* pdcp_, srsue::rrc_interface_rlc* rrc_, srsran::timer_handler* timers_, @@ -75,8 +69,8 @@ public: bool has_data_locked(const uint32_t lcid); uint32_t get_buffer_state(const uint32_t lcid); uint32_t get_total_mch_buffer_state(uint32_t lcid); - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); - int read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); + uint32_t read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); int get_increment_sequence_num(); void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); void write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu); @@ -91,8 +85,8 @@ public: void reestablish(uint32_t lcid); void reset(); void empty_queue(); - void add_bearer(uint32_t lcid, const rlc_config_t& cnfg); - void add_bearer_mrb(uint32_t lcid); + int add_bearer(uint32_t lcid, const rlc_config_t& cnfg); + int add_bearer_mrb(uint32_t lcid); void del_bearer(uint32_t lcid); void del_bearer_mrb(uint32_t lcid); void suspend_bearer(uint32_t lcid); @@ -103,15 +97,16 @@ public: private: void reset_metrics(); + void get_buffer_state(uint32_t lcid, uint32_t& tx_queue, uint32_t& prio_tx_queue); + srslog::basic_logger& logger; byte_buffer_pool* pool = nullptr; srsue::pdcp_interface_rlc* pdcp = nullptr; srsue::rrc_interface_rlc* rrc = nullptr; - srsue::rrc_interface_rlc* rrc_nr = nullptr; srsran::timer_handler* timers = nullptr; - typedef std::map rlc_map_t; - typedef std::pair rlc_map_pair_t; + typedef std::map > rlc_map_t; + typedef std::pair > rlc_map_pair_t; rlc_map_t rlc_array, rlc_array_mrb; pthread_rwlock_t rwlock; diff --git a/lib/include/srsran/rlc/rlc_am_base.h b/lib/include/srsran/rlc/rlc_am_base.h new file mode 100644 index 000000000..1017878d6 --- /dev/null +++ b/lib/include/srsran/rlc/rlc_am_base.h @@ -0,0 +1,211 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RLC_AM_BASE_H +#define SRSRAN_RLC_AM_BASE_H + +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/timers.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/rlc/rlc_common.h" +#include "srsran/upper/byte_buffer_queue.h" +#include +#include +#include +#include + +namespace srsue { + +class pdcp_interface_rlc; +class rrc_interface_rlc; + +} // namespace srsue + +namespace srsran { + +bool rlc_am_is_control_pdu(uint8_t* payload); +bool rlc_am_is_control_pdu(byte_buffer_t* pdu); + +/******************************************************* + * RLC AM entity + * This entity is common between LTE and NR + * and only the TX/RX entities change between them + *******************************************************/ +class rlc_am : public rlc_common +{ +public: + class rlc_am_base_tx; + class rlc_am_base_rx; + + friend class rlc_am_lte_tx; + friend class rlc_am_lte_rx; + friend class rlc_am_nr_tx; + friend class rlc_am_nr_rx; + + rlc_am(srsran_rat_t rat, + srslog::basic_logger& logger, + uint32_t lcid_, + srsue::pdcp_interface_rlc* pdcp_, + srsue::rrc_interface_rlc* rrc_, + srsran::timer_handler* timers_); + + bool configure(const rlc_config_t& cfg_) final; + + void reestablish() final; + + void stop() final; + + void empty_queue() final { tx_base->empty_queue(); } + + rlc_mode_t get_mode() final { return rlc_mode_t::am; } + + uint32_t get_lcid() final { return lcid; } + + /**************************************************************************** + * PDCP interface + ***************************************************************************/ + void write_sdu(unique_byte_buffer_t sdu) final; + + void discard_sdu(uint32_t discard_sn) final; + + bool sdu_queue_is_full() final; + + /**************************************************************************** + * MAC interface + ***************************************************************************/ + bool has_data() final; + uint32_t get_buffer_state() final; + void get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio) final; + + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final; + + void write_pdu(uint8_t* payload, uint32_t nof_bytes) final; + + /**************************************************************************** + * Metrics + ***************************************************************************/ + rlc_bearer_metrics_t get_metrics() final; + void reset_metrics() final; + + /**************************************************************************** + * BSR Callback + ***************************************************************************/ + void set_bsr_callback(bsr_callback_t callback) final; + +protected: + // Common variables needed/provided by parent class + srsran::timer_handler* timers = nullptr; + uint32_t lcid = 0; + rlc_config_t cfg = {}; + + static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested + + std::mutex metrics_mutex; + rlc_bearer_metrics_t metrics = {}; + + srsue::rrc_interface_rlc* rrc = nullptr; + srsue::pdcp_interface_rlc* pdcp = nullptr; + + /******************************************************* + * RLC AM TX entity + * This class is used for common code between the + * LTE and NR TX entities + *******************************************************/ +public: + class rlc_am_base_tx + { + public: + explicit rlc_am_base_tx(srslog::basic_logger& logger_) : logger(logger_) {} + virtual ~rlc_am_base_tx() = default; + + virtual bool configure(const rlc_config_t& cfg_) = 0; + virtual void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; + virtual uint32_t get_buffer_state() = 0; + virtual void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) = 0; + virtual void reestablish() = 0; + virtual void empty_queue() = 0; + virtual bool has_data() = 0; + virtual void stop() = 0; + + void set_bsr_callback(bsr_callback_t callback); + + int write_sdu(unique_byte_buffer_t sdu); + bool sdu_queue_is_full(); + virtual void discard_sdu(uint32_t pdcp_sn); + virtual uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; + + bool tx_enabled = false; + byte_buffer_pool* pool = nullptr; + srslog::basic_logger& logger; + std::string rb_name; + + bsr_callback_t bsr_callback; + + // Tx SDU buffers + byte_buffer_queue tx_sdu_queue; + + // Mutexes + std::mutex mutex; + }; + + /******************************************************* + * RLC AM RX entity + * This class is used for common code between the + * LTE and NR RX entities + *******************************************************/ + class rlc_am_base_rx + { + public: + explicit rlc_am_base_rx(rlc_am* parent_, srslog::basic_logger& logger_) : parent(parent_), logger(logger_) {} + virtual ~rlc_am_base_rx() = default; + + virtual bool configure(const rlc_config_t& cfg_) = 0; + virtual void handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; + virtual void reestablish() = 0; + virtual void stop() = 0; + virtual uint32_t get_sdu_rx_latency_ms() = 0; + virtual uint32_t get_rx_buffered_bytes() = 0; + + void write_pdu(uint8_t* payload, uint32_t nof_bytes); + + srslog::basic_logger& logger; + byte_buffer_pool* pool = nullptr; + rlc_am* parent = nullptr; + std::string rb_name; + + protected: + std::atomic do_status = {false}; // light-weight access from Tx entity + }; + +protected: + std::unique_ptr tx_base = {}; + std::unique_ptr rx_base = {}; + +public: + // Getters for TX/RX entities. Useful for testing. + rlc_am_base_rx* get_rx() { return rx_base.get(); } + rlc_am_base_tx* get_tx() { return tx_base.get(); } +}; + +} // namespace srsran + +#endif // SRSRAN_RLC_AM_BASE_H diff --git a/lib/include/srsran/rlc/rlc_am_data_structs.h b/lib/include/srsran/rlc/rlc_am_data_structs.h new file mode 100644 index 000000000..b3193c654 --- /dev/null +++ b/lib/include/srsran/rlc/rlc_am_data_structs.h @@ -0,0 +1,547 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RLC_AM_DATA_STRUCTS_H +#define SRSRAN_RLC_AM_DATA_STRUCTS_H + +#include "srsran/adt/circular_buffer.h" +#include "srsran/adt/circular_map.h" +#include "srsran/adt/intrusive_list.h" +#include "srsran/common/buffer_pool.h" +#include +#include +#include + +namespace srsran { + +template +class rlc_amd_tx_pdu; +template +class pdcp_pdu_info; + +/// Pool that manages the allocation of RLC AM PDU Segments to RLC PDUs and tracking of segments ACK state +template +struct rlc_am_pdu_segment_pool { + const static size_t MAX_POOL_SIZE = 16384; + + /// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state + using rlc_list_tag = default_intrusive_tag; + struct free_list_tag {}; + struct segment_resource : public intrusive_forward_list_element, + public intrusive_forward_list_element, + public intrusive_double_linked_list_element<> { + const static uint32_t invalid_rlc_sn = std::numeric_limits::max(); + const static uint32_t invalid_pdcp_sn = std::numeric_limits::max() - 1; // -1 for Status Report + + int id() const { return std::distance(parent_pool->segments.cbegin(), this); } + + void release_pdcp_sn() + { + pdcp_sn_ = invalid_pdcp_sn; + if (empty()) { + parent_pool->free_list.push_front(this); + } + } + + void release_rlc_sn() + { + rlc_sn_ = invalid_rlc_sn; + if (empty()) { + parent_pool->free_list.push_front(this); + } + } + + uint32_t rlc_sn() const { return rlc_sn_; } + uint32_t pdcp_sn() const { return pdcp_sn_; } + bool empty() const { return rlc_sn_ == invalid_rlc_sn and pdcp_sn_ == invalid_pdcp_sn; } + + private: + friend struct rlc_am_pdu_segment_pool; + uint32_t rlc_sn_ = invalid_rlc_sn; + uint32_t pdcp_sn_ = invalid_pdcp_sn; + rlc_am_pdu_segment_pool* parent_pool = nullptr; + }; + + rlc_am_pdu_segment_pool() + { + for (segment_resource& s : segments) { + s.parent_pool = this; + free_list.push_front(&s); + } + } + rlc_am_pdu_segment_pool(const rlc_am_pdu_segment_pool&) = delete; + rlc_am_pdu_segment_pool(rlc_am_pdu_segment_pool&&) = delete; + rlc_am_pdu_segment_pool& operator=(const rlc_am_pdu_segment_pool&) = delete; + rlc_am_pdu_segment_pool& operator=(rlc_am_pdu_segment_pool&&) = delete; + + bool has_segments() const { return not free_list.empty(); } + bool make_segment(rlc_amd_tx_pdu& rlc_list, pdcp_pdu_info& pdcp_list) + { + if (not has_segments()) { + return false; + } + segment_resource* segment = free_list.pop_front(); + segment->rlc_sn_ = rlc_list.rlc_sn; + segment->pdcp_sn_ = pdcp_list.sn; + rlc_list.add_segment(*segment); + pdcp_list.add_segment(*segment); + return true; + } + +private: + intrusive_forward_list::segment_resource, free_list_tag> free_list; + std::array::segment_resource, MAX_POOL_SIZE> segments; +}; + +/// Class that contains the parameters and state (e.g. segments) of a RLC PDU +template +class rlc_amd_tx_pdu +{ + using rlc_am_pdu_segment = typename rlc_am_pdu_segment_pool::segment_resource; + using list_type = intrusive_forward_list; + const static uint32_t invalid_rlc_sn = std::numeric_limits::max(); + + list_type list; + +public: + using iterator = typename list_type::iterator; + using const_iterator = typename list_type::const_iterator; + + const uint32_t rlc_sn = invalid_rlc_sn; + uint32_t retx_count = 0; + HeaderType header = {}; + unique_byte_buffer_t buf = nullptr; + + explicit rlc_amd_tx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} + rlc_amd_tx_pdu(const rlc_amd_tx_pdu&) = delete; + rlc_amd_tx_pdu(rlc_amd_tx_pdu&& other) noexcept = default; + rlc_amd_tx_pdu& operator=(const rlc_amd_tx_pdu& other) = delete; + rlc_amd_tx_pdu& operator=(rlc_amd_tx_pdu&& other) = delete; + ~rlc_amd_tx_pdu() + { + while (not list.empty()) { + // remove from list + rlc_am_pdu_segment* segment = list.pop_front(); + // deallocate if also removed from PDCP + segment->release_rlc_sn(); + } + } + + // Segment List Interface + void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); } + const_iterator begin() const { return list.begin(); } + const_iterator end() const { return list.end(); } + iterator begin() { return list.begin(); } + iterator end() { return list.end(); } +}; + +/// Class that contains the parameters and state (e.g. unACKed segments) of a PDCP PDU +template +class pdcp_pdu_info +{ + using rlc_am_pdu_segment = typename rlc_am_pdu_segment_pool::segment_resource; + using list_type = intrusive_double_linked_list; + + list_type list; // List of unACKed RLC PDUs that contain segments that belong to the PDCP PDU. + +public: + const static uint32_t status_report_sn = std::numeric_limits::max(); + const static uint32_t invalid_pdcp_sn = std::numeric_limits::max() - 1; + + using iterator = typename list_type::iterator; + using const_iterator = typename list_type::const_iterator; + + // Copy is forbidden to avoid multiple PDCP SN references to the same segment + pdcp_pdu_info() = default; + pdcp_pdu_info(pdcp_pdu_info&&) noexcept = default; + pdcp_pdu_info(const pdcp_pdu_info&) noexcept = delete; + pdcp_pdu_info& operator=(const pdcp_pdu_info&) noexcept = delete; + pdcp_pdu_info& operator=(pdcp_pdu_info&&) noexcept = default; + ~pdcp_pdu_info() { clear(); } + + uint32_t sn = invalid_pdcp_sn; + bool fully_txed = false; // Boolean indicating if the SDU is fully transmitted. + + bool fully_acked() const { return fully_txed and list.empty(); } + bool valid() const { return sn != invalid_pdcp_sn; } + + // Interface for list of unACKed RLC segments of the PDCP PDU + void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); } + void ack_segment(rlc_am_pdu_segment& segment) + { + // remove from list + list.pop(&segment); + // signal pool that the pdcp handle is released + segment.release_pdcp_sn(); + } + void clear() + { + sn = invalid_pdcp_sn; + fully_txed = false; + while (not list.empty()) { + ack_segment(list.front()); + } + } + + const_iterator begin() const { return list.begin(); } + const_iterator end() const { return list.end(); } +}; + +template +struct rlc_ringbuffer_base { + virtual ~rlc_ringbuffer_base() = default; + virtual T& add_pdu(size_t sn) = 0; + virtual void remove_pdu(size_t sn) = 0; + virtual T& operator[](size_t sn) = 0; + virtual size_t size() const = 0; + virtual bool empty() const = 0; + virtual bool full() const = 0; + virtual void clear() = 0; + virtual bool has_sn(uint32_t sn) const = 0; +}; + +template +struct rlc_ringbuffer_t : public rlc_ringbuffer_base { + ~rlc_ringbuffer_t() = default; + + T& add_pdu(size_t sn) override + { + srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn); + window.overwrite(sn, T(sn)); + return window[sn]; + } + void remove_pdu(size_t sn) override + { + srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn); + window.erase(sn); + } + T& operator[](size_t sn) override { return window[sn]; } + size_t size() const override { return window.size(); } + bool full() const override { return window.full(); } + bool empty() const override { return window.empty(); } + void clear() override { window.clear(); } + + bool has_sn(uint32_t sn) const override { return window.contains(sn); } + + // Return the sum data bytes of all active PDUs (check PDU is non-null) + uint32_t get_buffered_bytes() + { + uint32_t buff_size = 0; + for (const auto& pdu : window) { + if (pdu.second.buf != nullptr) { + buff_size += pdu.second.buf->N_bytes; + } + } + return buff_size; + } + +private: + srsran::static_circular_map window; +}; + +template +struct buffered_pdcp_pdu_list { +public: + explicit buffered_pdcp_pdu_list() : buffered_pdus(buffered_pdcp_pdu_list::buffer_size) { clear(); } + + void clear() + { + count = 0; + for (pdcp_pdu_info& b : buffered_pdus) { + b.clear(); + } + } + + void add_pdcp_sdu(uint32_t sn) + { + srsran_expect(sn <= max_pdcp_sn or sn == status_report_sn, "Invalid PDCP SN=%d", sn); + srsran_assert(not has_pdcp_sn(sn), "Cannot re-add same PDCP SN twice"); + pdcp_pdu_info& pdu = get_pdu_(sn); + if (pdu.valid()) { + pdu.clear(); + count--; + } + pdu.sn = sn; + count++; + } + + void clear_pdcp_sdu(uint32_t sn) + { + pdcp_pdu_info& pdu = get_pdu_(sn); + if (not pdu.valid()) { + return; + } + pdu.clear(); + count--; + } + + pdcp_pdu_info& operator[](uint32_t sn) + { + srsran_expect(has_pdcp_sn(sn), "Invalid access to non-existent PDCP SN=%d", sn); + return get_pdu_(sn); + } + + bool has_pdcp_sn(uint32_t pdcp_sn) const + { + srsran_expect(pdcp_sn <= max_pdcp_sn or pdcp_sn == status_report_sn, "Invalid PDCP SN=%d", pdcp_sn); + return get_pdu_(pdcp_sn).sn == pdcp_sn; + } + uint32_t nof_sdus() const { return count; } + +private: + const static size_t max_pdcp_sn = 262143u; + const static size_t buffer_size = 4096u; + const static uint32_t status_report_sn = pdcp_pdu_info::status_report_sn; + + pdcp_pdu_info& get_pdu_(uint32_t sn) + { + return (sn == status_report_sn) ? status_report_pdu : buffered_pdus[static_cast(sn % buffer_size)]; + } + const pdcp_pdu_info& get_pdu_(uint32_t sn) const + { + return (sn == status_report_sn) ? status_report_pdu : buffered_pdus[static_cast(sn % buffer_size)]; + } + + // size equal to buffer_size + std::vector > buffered_pdus; + pdcp_pdu_info status_report_pdu; + uint32_t count = 0; +}; + +struct rlc_amd_retx_base_t { + const static uint32_t invalid_rlc_sn = std::numeric_limits::max(); + + uint32_t sn; ///< sequence number + bool is_segment; ///< flag whether this is a segment or not + uint32_t so_start; ///< offset to first byte of this segment + // so_end or segment_length are different for LTE and NR, hence are defined in subclasses + uint32_t current_so; ///< stores progressing SO during segmentation of this object + + rlc_amd_retx_base_t() : sn(invalid_rlc_sn), is_segment(false), so_start(0), current_so(0) {} + virtual ~rlc_amd_retx_base_t() = default; + + /** + * @brief overlaps implements a check whether the range of this retransmission object includes + * the given segment offset + * @param so the segment offset to check + * @return true if the segment offset is covered by the retransmission object. Otherwise false + */ + virtual bool overlaps(uint32_t so) const = 0; +}; + +struct rlc_amd_retx_lte_t : public rlc_amd_retx_base_t { + uint32_t so_end; ///< offset to first byte beyond the end of this segment + + rlc_amd_retx_lte_t() : rlc_amd_retx_base_t(), so_end(0) {} + bool overlaps(uint32_t segment_offset) const override + { + return (segment_offset >= so_start) && (segment_offset < so_end); + } +}; + +struct rlc_amd_retx_nr_t : public rlc_amd_retx_base_t { + uint32_t segment_length; ///< number of bytes contained in this segment + + rlc_amd_retx_nr_t() : rlc_amd_retx_base_t(), segment_length(0) {} + bool overlaps(uint32_t segment_offset) const override + { + return (segment_offset >= so_start) && (segment_offset < current_so + segment_length); + } +}; + +template +class pdu_retx_queue_base +{ +public: + virtual ~pdu_retx_queue_base() = default; + virtual T& push() = 0; + virtual void pop() = 0; + virtual T& front() = 0; + virtual void clear() = 0; + virtual size_t size() const = 0; + virtual bool empty() const = 0; + virtual bool full() const = 0; + + virtual T& operator[](size_t idx) = 0; + virtual const T& operator[](size_t idx) const = 0; + + virtual bool has_sn(uint32_t sn) const = 0; + virtual bool has_sn(uint32_t sn, uint32_t so) const = 0; +}; + +template +class pdu_retx_queue : public pdu_retx_queue_base +{ +public: + ~pdu_retx_queue() = default; + + T& push() override + { + assert(not full()); + T& p = buffer[wpos]; + wpos = (wpos + 1) % WINDOW_SIZE; + return p; + } + + void pop() override { rpos = (rpos + 1) % WINDOW_SIZE; } + + T& front() override + { + assert(not empty()); + return buffer[rpos]; + } + + T& operator[](size_t idx) override + { + srsran_assert(idx < size(), "Out-of-bounds access to element idx=%zd", idx); + return buffer[(rpos + idx) % WINDOW_SIZE]; + } + + const T& operator[](size_t idx) const override + { + srsran_assert(idx < size(), "Out-of-bounds access to element idx=%zd", idx); + return buffer[(rpos + idx) % WINDOW_SIZE]; + } + + void clear() override + { + wpos = 0; + rpos = 0; + } + + bool has_sn(uint32_t sn) const override + { + for (size_t i = rpos; i != wpos; i = (i + 1) % WINDOW_SIZE) { + if (buffer[i].sn == sn) { + return true; + } + } + return false; + } + + bool has_sn(uint32_t sn, uint32_t so) const override + { + for (size_t i = rpos; i != wpos; i = (i + 1) % WINDOW_SIZE) { + if (buffer[i].sn == sn) { + if (buffer[i].overlaps(so)) { + return true; + } + } + } + return false; + } + + size_t size() const override { return (wpos >= rpos) ? wpos - rpos : WINDOW_SIZE + wpos - rpos; } + bool empty() const override { return wpos == rpos; } + bool full() const override { return size() == WINDOW_SIZE - 1; } + +private: + std::array buffer; + size_t wpos = 0; + size_t rpos = 0; +}; + +template +class pdu_retx_queue_list +{ + std::list queue; + +public: + ~pdu_retx_queue_list() = default; + T& push() + { + queue.emplace_back(); + return queue.back(); + } + + void pop() + { + if (not queue.empty()) { + queue.pop_front(); + } + } + + T& front() + { + assert(not queue.empty()); + return queue.front(); + } + + const std::list& get_inner_queue() const { return queue; } + + void clear() { queue.clear(); } + size_t size() const { return queue.size(); } + bool empty() const { return queue.empty(); } + + bool has_sn(uint32_t sn) const + { + if (queue.empty()) { + return false; + } + for (auto elem : queue) { + if (elem.sn == sn) { + return true; + } + } + return false; + }; + + bool has_sn(uint32_t sn, uint32_t so) const + { + if (queue.empty()) { + return false; + } + for (auto elem : queue) { + if (elem.sn == sn) { + if (elem.overlaps(so)) { + return true; + } + } + } + return false; + }; + + /** + * @brief remove_sn removes SN from queue and returns after first match + * @param sn sequence number to be removed from queue + * @return true if one element was removed, false if no element to remove was found + */ + bool remove_sn(uint32_t sn) + { + if (queue.empty()) { + return false; + } + auto iter = queue.begin(); + while (iter != queue.end()) { + if (iter->sn == sn) { + iter = queue.erase(iter); + return true; + } else { + ++iter; + } + } + return false; + } +}; + +} // namespace srsran + +#endif // SRSRAN_RLC_AM_DATA_STRUCTS_H diff --git a/lib/include/srsran/rlc/rlc_am_lte.h b/lib/include/srsran/rlc/rlc_am_lte.h new file mode 100644 index 000000000..ca60af86c --- /dev/null +++ b/lib/include/srsran/rlc/rlc_am_lte.h @@ -0,0 +1,242 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RLC_AM_LTE_H +#define SRSRAN_RLC_AM_LTE_H + +#include "srsran/adt/accumulators.h" +#include "srsran/adt/circular_array.h" +#include "srsran/adt/circular_map.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/common/timeout.h" +#include "srsran/interfaces/pdcp_interface_types.h" +#include "srsran/rlc/rlc_am_base.h" +#include "srsran/rlc/rlc_am_data_structs.h" +#include "srsran/rlc/rlc_am_lte_packing.h" +#include "srsran/rlc/rlc_common.h" +#include "srsran/support/srsran_assert.h" +#include "srsran/upper/byte_buffer_queue.h" +#include +#include +#include + +namespace srsran { + +#undef RLC_AM_BUFFER_DEBUG + +/****************************** + * + * RLC AM LTE entity + * + *****************************/ + +/****************************** + * RLC AM LTE TX entity + *****************************/ +class rlc_am_lte_tx; +class rlc_am_lte_rx; +class rlc_am_lte_tx : public rlc_am::rlc_am_base_tx, timer_callback +{ +public: + explicit rlc_am_lte_tx(rlc_am* parent_); + ~rlc_am_lte_tx() = default; + + void set_rx(rlc_am_lte_rx* rx_) { rx = rx_; }; + bool configure(const rlc_config_t& cfg_); + void empty_queue(); + void reestablish(); + void stop(); + + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes); + + bool has_data(); + uint32_t get_buffer_state(); + void get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio); + + void empty_queue_nolock(); + void debug_state(); + + // Timeout callback interface + void timer_expired(uint32_t timeout_id) final; + + // Interface for Rx subclass + void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes); + +private: + void stop_nolock(); + + int build_status_pdu(uint8_t* payload, uint32_t nof_bytes); + int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes); + int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_lte_t retx); + int build_data_pdu(uint8_t* payload, uint32_t nof_bytes); + void update_notification_ack_info(uint32_t rlc_sn); + + int required_buffer_size(const rlc_amd_retx_lte_t& retx); + void retransmit_pdu(uint32_t sn); + + // Helpers + bool window_full(); + bool poll_required(); + bool do_status(); + void check_sn_reached_max_retx(uint32_t sn); + void get_buffer_state_nolock(uint32_t& new_tx, uint32_t& prio_tx); + + rlc_am* parent = nullptr; + rlc_am_lte_rx* rx = nullptr; + byte_buffer_pool* pool = nullptr; + rlc_am_pdu_segment_pool segment_pool; + + /**************************************************************************** + * Configurable parameters + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + rlc_am_config_t cfg = {}; + + // TX SDU buffers + unique_byte_buffer_t tx_sdu; + + /**************************************************************************** + * State variables and counters + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + // Tx state variables + uint32_t vt_a = 0; // ACK state. SN of next PDU in sequence to be ACKed. Low edge of tx window. + uint32_t vt_ms = RLC_AM_WINDOW_SIZE; // Max send state. High edge of tx window. vt_a + window_size. + uint32_t vt_s = 0; // Send state. SN to be assigned for next PDU. + uint32_t poll_sn = 0; // Poll send state. SN of most recent PDU txed with poll bit set. + + // Tx counters + uint32_t pdu_without_poll = 0; + uint32_t byte_without_poll = 0; + + rlc_status_pdu_t tx_status; + + /**************************************************************************** + * Timers + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + srsran::timer_handler::unique_timer poll_retx_timer; + srsran::timer_handler::unique_timer status_prohibit_timer; + + // SDU info for PDCP notifications + buffered_pdcp_pdu_list undelivered_sdu_info_queue; + + // Tx windows + rlc_ringbuffer_t, RLC_AM_WINDOW_SIZE> tx_window; + pdu_retx_queue retx_queue; + pdcp_sn_vector_t notify_info_vec; + + // Mutexes + std::mutex mutex; + + // default to RLC SDU queue length + const uint32_t MAX_SDUS_PER_RLC_PDU = RLC_TX_QUEUE_LEN; +}; + +/****************************** + * RLC AM LTE RX entity + *****************************/ +class rlc_am_lte_rx : public rlc_am::rlc_am_base_rx, public timer_callback +{ +public: + explicit rlc_am_lte_rx(rlc_am* parent_); + ~rlc_am_lte_rx() = default; + + void set_tx(rlc_am_lte_tx* tx_) { tx = tx_; }; + bool configure(const rlc_config_t& cfg_) final; + void reestablish() final; + void stop() final; + + uint32_t get_rx_buffered_bytes() final; // returns sum of PDUs in rx_window + uint32_t get_sdu_rx_latency_ms() final; + + // Timeout callback interface + void timer_expired(uint32_t timeout_id) final; + + // Functions needed by Tx subclass to query rx state + int get_status_pdu_length(); + int get_status_pdu(rlc_status_pdu_t* status, uint32_t nof_bytes); + bool get_do_status(); + +private: + void handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) final; + void handle_data_pdu_full(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header); + void handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header); + void reassemble_rx_sdus(); + bool inside_rx_window(const int16_t sn); + void debug_state(); + void print_rx_segments(); + bool add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment); + void reset_status(); + + rlc_am* parent = nullptr; + rlc_am_lte_tx* tx = nullptr; + byte_buffer_pool* pool = nullptr; + + /**************************************************************************** + * Configurable parameters + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + rlc_am_config_t cfg = {}; + + // RX SDU buffers + unique_byte_buffer_t rx_sdu; + + /**************************************************************************** + * State variables and counters + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + // Rx state variables + uint32_t vr_r = 0; // Receive state. SN following last in-sequence received PDU. Low edge of rx window + uint32_t vr_mr = RLC_AM_WINDOW_SIZE; // Max acceptable receive state. High edge of rx window. vr_r + window size. + uint32_t vr_x = 0; // t_reordering state. SN following PDU which triggered t_reordering. + uint32_t vr_ms = 0; // Max status tx state. Highest possible value of SN for ACK_SN in status PDU. + uint32_t vr_h = 0; // Highest rx state. SN following PDU with highest SN among rxed PDUs. + + // Mutex to protect members + std::mutex mutex; + + // Rx windows + rlc_ringbuffer_t rx_window; + std::map rx_segments; + + bool poll_received = false; + std::atomic do_status = {false}; // light-weight access from Tx entity + + /**************************************************************************** + * Timers + * Ref: 3GPP TS 36.322 v10.0.0 Section 7 + ***************************************************************************/ + + srsran::timer_handler::unique_timer reordering_timer; + + srsran::rolling_average sdu_rx_latency_ms; +}; + +} // namespace srsran + +#endif // SRSRAN_RLC_AM_LTE_H diff --git a/lib/include/srsran/rlc/rlc_am_lte_packing.h b/lib/include/srsran/rlc/rlc_am_lte_packing.h new file mode 100644 index 000000000..93f5518c9 --- /dev/null +++ b/lib/include/srsran/rlc/rlc_am_lte_packing.h @@ -0,0 +1,137 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RLC_AM_LTE_PACKING_H +#define SRSRAN_RLC_AM_LTE_PACKING_H + +#include "srsran/common/string_helpers.h" +#include "srsran/rlc/rlc_am_base.h" +#include "srsran/rlc/rlc_am_data_structs.h" // required for rlc_am_pdu_segment +#include + +namespace srsran { + +struct rlc_sn_info_t { + uint32_t sn; + bool is_acked; +}; + +struct rlc_amd_rx_pdu { + rlc_amd_pdu_header_t header; + unique_byte_buffer_t buf; + uint32_t rlc_sn = 0; + + rlc_amd_rx_pdu() = default; + explicit rlc_amd_rx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} +}; + +struct rlc_amd_rx_pdu_segments_t { + std::list segments; +}; + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ +void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header); +void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header); +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu); +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload); +void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status); +void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status); +void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu); +int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload); + +uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header); +uint32_t rlc_am_packed_length(rlc_status_pdu_t* status); +uint32_t rlc_am_packed_length(rlc_amd_retx_lte_t retx); +bool rlc_am_is_pdu_segment(uint8_t* payload); +bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min = 0); +bool rlc_am_start_aligned(const uint8_t fi); +bool rlc_am_end_aligned(const uint8_t fi); +bool rlc_am_is_unaligned(const uint8_t fi); +bool rlc_am_not_start_aligned(const uint8_t fi); +std::string +rlc_am_undelivered_sdu_info_to_string(const std::map >& info_queue); + +template +void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, + const std::string& rb_name, + const char* fmt_str, + const rlc_amd_pdu_header_t& header, + Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + + fmt::memory_buffer buffer; + fmt::format_to(buffer, + "{}: [{}, RF={}, P={}, FI={}, SN={}, LSF={}, SO={}, N_li={}", + rb_name, + rlc_dc_field_text[header.dc], + (header.rf ? "1" : "0"), + (header.p ? "1" : "0"), + (header.fi ? "1" : "0"), + header.sn, + (header.lsf ? "1" : "0"), + header.so, + header.N_li); + if (header.N_li > 0) { + fmt::format_to(buffer, " ({}", header.li[0]); + for (uint32_t i = 1; i < header.N_li; ++i) { + fmt::format_to(buffer, ", {}", header.li[i]); + } + fmt::format_to(buffer, ")"); + } + fmt::format_to(buffer, "]"); + + log_ch(fmt_str, std::forward(args)..., to_c_str(buffer)); +} + +template +void log_rlc_am_status_pdu_to_string(srslog::log_channel& log_ch, + const std::string& rb_name, + const char* fmt_str, + rlc_status_pdu_t* status, + Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + fmt::memory_buffer buffer; + fmt::format_to(buffer, "{}: ACK_SN = {}, N_nack = {}", rb_name, status->ack_sn, status->N_nack); + if (status->N_nack > 0) { + fmt::format_to(buffer, ", NACK_SN = "); + for (uint32_t i = 0; i < status->N_nack; ++i) { + if (status->nacks[i].has_so) { + fmt::format_to( + buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end); + } else { + fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn); + } + } + } + log_ch(fmt_str, std::forward(args)..., to_c_str(buffer)); +} +} // namespace srsran + +#endif // SRSRAN_RLC_AM_LTE_PACKING_H diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h new file mode 100644 index 000000000..7b24f8299 --- /dev/null +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -0,0 +1,321 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RLC_AM_NR_H +#define SRSRAN_RLC_AM_NR_H + +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/timers.h" +#include "srsran/interfaces/pdcp_interface_types.h" +#include "srsran/rlc/rlc_am_base.h" +#include "srsran/rlc/rlc_am_data_structs.h" +#include "srsran/rlc/rlc_am_nr_packing.h" +#include "srsran/upper/byte_buffer_queue.h" +#include +#include +#include +#include + +namespace srsran { + +/****************************** + * + * RLC AM NR entity + * + *****************************/ +class rlc_am_nr_tx; +class rlc_am_nr_rx; + +/**************************************************************************** + * Tx state variables + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 + ***************************************************************************/ +struct rlc_am_nr_tx_state_t { + /* + * TX_Next_Ack: This state variable holds the value of the SN of the next RLC SDU for which a positive + * acknowledgment is to be received in-sequence, and it serves as the lower edge of the transmitting window. It is + * initially set to 0, and is updated whenever the AM RLC entity receives a positive acknowledgment for an RLC SDU + * with SN = TX_Next_Ack. + */ + uint32_t tx_next_ack; + /* + * TX_Next: This state variable holds the value of the SN to be assigned for the next newly generated AMD PDU. It is + * initially set to 0, and is updated whenever the AM RLC entity constructs an AMD PDU with SN = TX_Next and + * contains an RLC SDU or the last segment of a RLC SDU. + */ + uint32_t tx_next; + /* + * POLL_SN: This state variable holds the value of the highest SN of the AMD PDU among the AMD PDUs submitted to + * lower layer when POLL_SN is set according to sub clause 5.3.3.2. It is initially set to 0. + */ + uint32_t poll_sn; + /* + * PDU_WITHOUT_POLL: This counter is initially set to 0. It counts the number of AMD PDUs sent since the most recent + * poll bit was transmitted. + */ + uint32_t pdu_without_poll; + /* + * BYTE_WITHOUT_POLL: This counter is initially set to 0. It counts the number of data bytes sent since the most + * recent poll bit was transmitted. + */ + uint32_t byte_without_poll; +}; + +struct rlc_amd_tx_pdu_nr { + const uint32_t rlc_sn = INVALID_RLC_SN; + uint32_t pdcp_sn = INVALID_RLC_SN; + rlc_am_nr_pdu_header_t header = {}; + unique_byte_buffer_t sdu_buf = nullptr; + uint32_t retx_count = RETX_COUNT_NOT_STARTED; + struct pdu_segment { + uint32_t so = 0; + uint32_t payload_len = 0; + }; + std::list segment_list; + explicit rlc_amd_tx_pdu_nr(uint32_t sn) : rlc_sn(sn) {} +}; + +class rlc_am_nr_tx : public rlc_am::rlc_am_base_tx +{ +public: + explicit rlc_am_nr_tx(rlc_am* parent_); + ~rlc_am_nr_tx() = default; + + void set_rx(rlc_am_nr_rx* rx_) { rx = rx_; } + bool configure(const rlc_config_t& cfg_) final; + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final; + void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) final; + void handle_nack(const rlc_status_nack_t& nack, std::set& retx_sn_set); + + void reestablish() final; + void stop() final; + + int write_sdu(unique_byte_buffer_t sdu); + void empty_queue() final; + void empty_queue_no_lock(); + + // Data PDU helpers + uint32_t build_new_pdu(uint8_t* payload, uint32_t nof_bytes); + uint32_t build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_retx_pdu(uint8_t* payload, uint32_t nof_bytes); + uint32_t build_retx_pdu_without_segmentation(rlc_amd_retx_nr_t retx, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes); + bool is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx, uint32_t nof_bytes); + uint32_t get_retx_expected_hdr_len(const rlc_amd_retx_nr_t& retx); + + // Buffer State + bool has_data() final; + uint32_t get_buffer_state() final; + void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) final; + + // Status PDU + bool do_status(); + uint32_t build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes); + + // Polling + uint8_t get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes); + + // Timers + void timer_expired(uint32_t timeout_id); + + // Window helpers + bool inside_tx_window(uint32_t sn) const; + bool valid_ack_sn(uint32_t sn) const; + +private: + rlc_am* parent = nullptr; + rlc_am_nr_rx* rx = nullptr; + + uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t()); + inline uint32_t tx_mod_base_nr(uint32_t sn) const; + void check_sn_reached_max_retx(uint32_t sn); + + /**************************************************************************** + * Configurable parameters + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.4 + ***************************************************************************/ + rlc_am_nr_config_t cfg = {}; + + /**************************************************************************** + * Tx state variables + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 + ***************************************************************************/ + struct rlc_am_nr_tx_state_t st = {}; + std::unique_ptr > tx_window; + + // Queues, buffers and container + pdu_retx_queue_list retx_queue; + uint32_t sdu_under_segmentation_sn = INVALID_RLC_SN; // SN of the SDU currently being segmented. + pdcp_sn_vector_t notify_info_vec; + + // Helper constants + uint32_t min_hdr_size = 2; // Pre-initialized for 12 bit SN, updated by configure() + uint32_t so_size = 2; + uint32_t max_hdr_size = 4; // Pre-initialized for 12 bit SN, updated by configure() + + /**************************************************************************** + * Tx constants + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2 + ***************************************************************************/ + inline uint32_t tx_window_size() const; + + /**************************************************************************** + * TX timers + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.3 + ***************************************************************************/ + srsran::timer_handler::unique_timer poll_retransmit_timer; + +public: + // Getters/Setters + void set_tx_state(const rlc_am_nr_tx_state_t& st_) { st = st_; } // This should only be used for testing. + rlc_am_nr_tx_state_t get_tx_state() { return st; } // This should only be used for testing. + uint32_t get_tx_window_utilization() { return tx_window->size(); } // This should only be used for testing. + size_t get_retx_queue_size() const { return retx_queue.size(); } // This should only be used for testing. + + // Debug Helpers + void debug_state() const; + void info_state() const; + void debug_window() const; +}; + +/**************************************************************************** + * State Variables + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 + ***************************************************************************/ +struct rlc_am_nr_rx_state_t { + /* + * RX_Next: This state variable holds the value of the SN following the last in-sequence completely received RLC + * SDU, and it serves as the lower edge of the receiving window. It is initially set to 0, and is updated whenever + * the AM RLC entity receives an RLC SDU with SN = RX_Next. + */ + uint32_t rx_next = 0; + /* + * RX_Next_Status_Trigger: This state variable holds the value of the SN following the SN of the RLC SDU which + * triggered t-Reassembly. + */ + uint32_t rx_next_status_trigger = 0; + /* + * RX_Next_Highest: This state variable holds the highest possible value of the SN which can be indicated by + *"ACK_SN" when a STATUS PDU needs to be constructed. It is initially set to 0. + */ + uint32_t rx_highest_status = 0; + /* + * RX_Next_Highest: This state variable holds the value of the SN following the SN of the RLC SDU with the + * highest SN among received RLC SDUs. It is initially set to 0. + */ + uint32_t rx_next_highest = 0; +}; + +// Receiver sub-class +class rlc_am_nr_rx : public rlc_am::rlc_am_base_rx +{ +public: + explicit rlc_am_nr_rx(rlc_am* parent_); + ~rlc_am_nr_rx() = default; + + void set_tx(rlc_am_nr_tx* tx_) { tx = tx_; } + bool configure(const rlc_config_t& cfg_) final; + + void handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) final; + + void reestablish() final; + void stop() final; + + // Status PDU + bool get_do_status(); + uint32_t get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t len); + uint32_t get_status_pdu_length(); + + // Data handling methods + int handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, const uint8_t* payload, uint32_t nof_bytes); + int handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header, const uint8_t* payload, uint32_t nof_bytes); + bool inside_rx_window(uint32_t sn) const; + bool valid_ack_sn(uint32_t sn) const; + void write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu); + void insert_received_segment(rlc_amd_rx_pdu_nr segment, rlc_amd_rx_sdu_nr_t::segment_list_t& segment_list) const; + /** + * @brief update_segment_inventory This function updates the flags has_gap and fully_received of an SDU + * according to the current inventory of received SDU segments + * @param rx_sdu The SDU to operate on + */ + void update_segment_inventory(rlc_amd_rx_sdu_nr_t& rx_sdu) const; + + // Metrics + uint32_t get_sdu_rx_latency_ms() final; + uint32_t get_rx_buffered_bytes() final; + + // Timers + void timer_expired(uint32_t timeout_id); + + // Helpers + void debug_state() const; + void debug_window() const; + +private: + rlc_am* parent = nullptr; + rlc_am_nr_tx* tx = nullptr; + byte_buffer_pool* pool = nullptr; + + uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t()); + uint32_t rx_mod_base_nr(uint32_t sn) const; + + // RX Window + std::unique_ptr > rx_window; + + // Mutexes + std::mutex mutex; + + /**************************************************************************** + * Rx timers + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.3 + ***************************************************************************/ + srsran::timer_handler::unique_timer status_prohibit_timer; + srsran::timer_handler::unique_timer reassembly_timer; + + /**************************************************************************** + * Configurable parameters + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.4 + ***************************************************************************/ + rlc_am_nr_config_t cfg = {}; + + /**************************************************************************** + * Rx state variables + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.1 + ***************************************************************************/ + struct rlc_am_nr_rx_state_t st = {}; + + /**************************************************************************** + * Rx constants + * Ref: 3GPP TS 38.322 version 16.2.0 Section 7.2 + ***************************************************************************/ + inline uint32_t rx_window_size() const; + +public: + // Getters/Setters + void set_rx_state(const rlc_am_nr_rx_state_t& st_) { st = st_; } // This should only be used for testing. + rlc_am_nr_rx_state_t get_rx_state() { return st; } // This should only be used for testing. + uint32_t get_rx_window_size() { return rx_window->size(); } // This should only be used for testing. +}; + +} // namespace srsran +#endif // SRSRAN_RLC_AM_NR_H diff --git a/lib/include/srsran/rlc/rlc_am_nr_packing.h b/lib/include/srsran/rlc/rlc_am_nr_packing.h new file mode 100644 index 000000000..aec5d0aee --- /dev/null +++ b/lib/include/srsran/rlc/rlc_am_nr_packing.h @@ -0,0 +1,239 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RLC_AM_NR_PACKING_H +#define SRSRAN_RLC_AM_NR_PACKING_H + +#include "srsran/common/string_helpers.h" +#include "srsran/rlc/rlc_am_base.h" +#include + +namespace srsran { + +const uint32_t INVALID_RLC_SN = 0xFFFFFFFF; +const uint32_t RETX_COUNT_NOT_STARTED = 0xFFFFFFFF; + +///< AM NR PDU header +struct rlc_am_nr_pdu_header_t { + rlc_am_nr_pdu_header_t() = default; + rlc_am_nr_pdu_header_t(const rlc_am_nr_pdu_header_t& h) = default; + rlc_am_nr_pdu_header_t& operator=(const rlc_am_nr_pdu_header_t&) = default; + rlc_am_nr_pdu_header_t(rlc_am_nr_pdu_header_t&& h) = default; + ~rlc_am_nr_pdu_header_t() = default; + + rlc_am_nr_pdu_header_t& operator=(rlc_am_nr_pdu_header_t&& h) = default; + + rlc_dc_field_t dc = {}; ///< Data/Control (D/C) field + uint8_t p = {}; ///< Polling bit + rlc_nr_si_field_t si = {}; ///< Segmentation info + rlc_am_nr_sn_size_t sn_size = {}; ///< Sequence number size (12 or 18 bits) + uint32_t sn = {}; ///< Sequence number + uint16_t so = {}; ///< Sequence offset +}; + +struct rlc_amd_pdu_nr_t { + rlc_am_nr_pdu_header_t header; + unique_byte_buffer_t buf; +}; + +struct rlc_amd_rx_pdu_nr { + rlc_am_nr_pdu_header_t header = {}; + unique_byte_buffer_t buf = nullptr; + uint32_t rlc_sn = {}; + + rlc_amd_rx_pdu_nr() = default; + explicit rlc_amd_rx_pdu_nr(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} +}; + +struct rlc_amd_rx_pdu_nr_cmp { + bool operator()(const rlc_amd_rx_pdu_nr& a, const rlc_amd_rx_pdu_nr& b) const { return a.header.so < b.header.so; } +}; + +struct rlc_amd_rx_sdu_nr_t { + uint32_t rlc_sn = 0; + bool fully_received = false; + bool has_gap = false; + unique_byte_buffer_t buf; + using segment_list_t = std::set; + segment_list_t segments; + + rlc_amd_rx_sdu_nr_t() = default; + explicit rlc_amd_rx_sdu_nr_t(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} +}; + +struct rlc_amd_tx_sdu_nr_t { + uint32_t rlc_sn = INVALID_RLC_SN; + unique_byte_buffer_t buf; + + rlc_amd_tx_sdu_nr_t() = default; + explicit rlc_amd_tx_sdu_nr_t(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} +}; + +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_header_ack_sn = 3; ///< header fixed part and ACK SN +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn = 2; ///< NACK SN and extension fields (12 bit SN) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn = 3; ///< NACK SN and extension fields (18 bit SN) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_so = 4; ///< NACK segment offsets (start and end) +constexpr uint32_t rlc_am_nr_status_pdu_sizeof_nack_range = 1; ///< NACK range (nof consecutively lost SDUs) + +/// AM NR Status PDU header +class rlc_am_nr_status_pdu_t +{ +private: + /// Stored SN size required to compute the packed size + rlc_am_nr_sn_size_t sn_size = rlc_am_nr_sn_size_t::nulltype; + /// Stored modulus to determine continuous sequences across SN overflows + uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t::nulltype); + /// Internal NACK container; keep in sync with packed_size_ + std::vector nacks_ = {}; + /// Stores the current packed size; sync on each change of nacks_ + uint32_t packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn; + + void refresh_packed_size(); + uint32_t nack_size(const rlc_status_nack_t& nack) const; + +public: + /// CPT header + rlc_am_nr_control_pdu_type_t cpt = rlc_am_nr_control_pdu_type_t::status_pdu; + /// SN of the next not received RLC Data PDU + uint32_t ack_sn = INVALID_RLC_SN; + /// Read-only reference to NACKs + const std::vector& nacks = nacks_; + /// Read-only reference to packed size + const uint32_t& packed_size = packed_size_; + + rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size); + void reset(); + bool is_continuous_sequence(const rlc_status_nack_t& left, const rlc_status_nack_t& right) const; + void push_nack(const rlc_status_nack_t& nack); + const std::vector& get_nacks() const { return nacks_; } + uint32_t get_packed_size() const { return packed_size; } + bool trim(uint32_t max_packed_size); +}; + +/**************************************************************************** + * Header pack/unpack helper functions for NR + * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.3 + ***************************************************************************/ +uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_pdu_header_t* header); + +uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload, + const uint32_t nof_bytes, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_pdu_header_t* header); + +uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, uint8_t* payload); +uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu); + +uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header); + +/**************************************************************************** + * Status PDU pack/unpack helper functions for NR + * Ref: 3GPP TS 38.322 v16.2.0 Section 6.2.2.5 + ***************************************************************************/ +uint32_t +rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status); + +uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, + const uint32_t nof_bytes, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_status_pdu_t* status); +uint32_t +rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status); +uint32_t +rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status); + +int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, + const rlc_am_nr_sn_size_t sn_size, + byte_buffer_t* pdu); +int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu); +int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu); + +/** + * Logs Status PDU into provided log channel, using fmt_str as format string + */ +template +void log_rlc_am_nr_status_pdu_to_string(srslog::log_channel& log_ch, + const char* fmt_str, + rlc_am_nr_status_pdu_t* status, + const std::string& rb_name, + Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + fmt::memory_buffer buffer; + fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->nacks.size()); + if (status->nacks.size() > 0) { + fmt::format_to(buffer, ", NACK_SN = "); + for (uint32_t i = 0; i < status->nacks.size(); ++i) { + if (status->nacks[i].has_nack_range) { + if (status->nacks[i].has_so) { + fmt::format_to(buffer, + "[{} {}:{} r{}]", + status->nacks[i].nack_sn, + status->nacks[i].so_start, + status->nacks[i].so_end, + status->nacks[i].nack_range); + } else { + fmt::format_to(buffer, "[{} r{}]", status->nacks[i].nack_sn, status->nacks[i].nack_range); + } + } else { + if (status->nacks[i].has_so) { + fmt::format_to( + buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end); + } else { + fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn); + } + } + } + } + log_ch(fmt_str, std::forward(args)..., to_c_str(buffer)); +} + +/* + * Log NR AMD PDUs + */ +inline void log_rlc_am_nr_pdu_header_to_string(srslog::log_channel& log_ch, + const rlc_am_nr_pdu_header_t& header, + const std::string& rb_name) +{ + if (not log_ch.enabled()) { + return; + } + fmt::memory_buffer buffer; + fmt::format_to(buffer, + "{}: [{}, P={}, SI={}, SN_SIZE={}, SN={}, SO={}", + rb_name, + rlc_dc_field_text[header.dc], + (header.p ? "1" : "0"), + to_string_short(header.si), + to_string(header.sn_size), + header.sn, + header.so); + fmt::format_to(buffer, "]"); + + log_ch("%s", to_c_str(buffer)); +} +} // namespace srsran + +#endif // SRSRAN_RLC_AM_NR_PACKING_H diff --git a/lib/include/srsran/upper/rlc_common.h b/lib/include/srsran/rlc/rlc_common.h similarity index 71% rename from lib/include/srsran/upper/rlc_common.h rename to lib/include/srsran/rlc/rlc_common.h index 45c33df65..e4c82fc42 100644 --- a/lib/include/srsran/upper/rlc_common.h +++ b/lib/include/srsran/rlc/rlc_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,10 +23,13 @@ #define SRSRAN_RLC_COMMON_H #include "srsran/adt/circular_buffer.h" +#include "srsran/adt/circular_map.h" +#include "srsran/adt/intrusive_list.h" #include "srsran/interfaces/rlc_interface_types.h" -#include "srsran/upper/bearer_mem_pool.h" -#include "srsran/upper/rlc_metrics.h" -#include +#include "srsran/rlc/bearer_mem_pool.h" +#include "srsran/rlc/rlc_metrics.h" +#include +#include namespace srsran { @@ -35,10 +38,24 @@ namespace srsran { * Ref: 3GPP TS 36.322 v10.0.0 ***************************************************************************/ +#define MOD 1024 #define RLC_AM_WINDOW_SIZE 512 #define RLC_MAX_SDU_SIZE ((1 << 11) - 1) // Length of LI field is 11bits #define RLC_AM_MIN_DATA_PDU_SIZE (3) // AMD PDU with 10 bit SN (length of LI field is 11 bits) (No LI) +#define RLC_AM_NR_TYP_NACKS 512 // Expected number of NACKs in status PDU before expanding space by alloc +#define RLC_AM_NR_MAX_NACKS 2048 // Maximum number of NACKs in status PDU + +#define RlcDebug(fmt, ...) logger.debug("%s: " fmt, rb_name, ##__VA_ARGS__) +#define RlcInfo(fmt, ...) logger.info("%s: " fmt, rb_name, ##__VA_ARGS__) +#define RlcWarning(fmt, ...) logger.warning("%s: " fmt, rb_name, ##__VA_ARGS__) +#define RlcError(fmt, ...) logger.error("%s: " fmt, rb_name, ##__VA_ARGS__) + +#define RlcHexDebug(msg, bytes, fmt, ...) logger.debug(msg, bytes, "%s: " fmt, rb_name, ##__VA_ARGS__) +#define RlcHexInfo(msg, bytes, fmt, ...) logger.info(msg, bytes, "%s: " fmt, rb_name, ##__VA_ARGS__) +#define RlcHexWarning(msg, bytes, fmt, ...) logger.warning(msg, bytes, "%s: " fmt, rb_name, ##__VA_ARGS__) +#define RlcHexError(msg, bytes, fmt, ...) logger.error(msg, bytes, "%s: " fmt, rb_name, ##__VA_ARGS__) + typedef enum { RLC_FI_FIELD_START_AND_END_ALIGNED = 0, RLC_FI_FIELD_NOT_END_ALIGNED, @@ -157,18 +174,33 @@ public: // NACK helper (for LTE and NR) struct rlc_status_nack_t { - uint32_t nack_sn; - bool has_so; - uint16_t so_start; - uint16_t so_end; + const static uint16_t so_end_of_sdu = 0xFFFF; + + uint32_t nack_sn; // Sequence Number (SN) of first missing SDU + bool has_so; // NACKs continuous sequence of bytes [so_start..so_end] + uint16_t so_start; // First missing byte in SDU with SN=nack_sn + uint16_t so_end; // Last missing byte in SDU with SN=nack_sn or SN=nack_sn+nack_range-1 if has_nack_range. + bool has_nack_range; // NACKs continuous sequence of SDUs + uint8_t nack_range; // Number of SDUs being NACKed (including SN=nack_sn) rlc_status_nack_t() { - has_so = false; - nack_sn = 0; - so_start = 0; - so_end = 0; + has_so = false; + nack_sn = 0; + so_start = 0; + so_end = so_end_of_sdu; + has_nack_range = false; + nack_range = 0; } + + bool equals(const rlc_status_nack_t& other) const + { + return nack_sn == other.nack_sn && has_so == other.has_so && so_start == other.so_start && so_end == other.so_end && + has_nack_range == other.has_nack_range && nack_range == other.nack_range; + } + + bool operator==(const rlc_status_nack_t& other) const { return equals(other); } + bool operator!=(const rlc_status_nack_t& other) const { return not equals(other); } }; // STATUS PDU @@ -184,27 +216,6 @@ struct rlc_status_pdu_t { } }; -/** RLC AM NR structs */ - -///< AM NR PDU header -typedef struct { - rlc_dc_field_t dc; ///< Data/Control (D/C) field - uint8_t p; ///< Polling bit - rlc_nr_si_field_t si; ///< Segmentation info - rlc_am_nr_sn_size_t sn_size; ///< Sequence number size (12 or 18 bits) - uint32_t sn; ///< Sequence number - uint16_t so; ///< Sequence offset -} rlc_am_nr_pdu_header_t; - -///< AM NR Status PDU header (perhaps merge with LTE version) -typedef struct { - rlc_am_nr_control_pdu_type_t cpt; - uint32_t ack_sn; ///< SN of the next not received RLC Data PDU - uint16_t N_nack; ///< number of NACKs - uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN - rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE]; -} rlc_am_nr_status_pdu_t; - typedef std::function bsr_callback_t; /**************************************************************************** @@ -214,6 +225,7 @@ typedef std::function bsr_callback_t; class rlc_common { public: + explicit rlc_common(srslog::basic_logger& logger_) : logger(logger_) {} virtual ~rlc_common() = default; virtual bool configure(const rlc_config_t& cnfg) = 0; virtual void stop() = 0; @@ -235,16 +247,15 @@ public: if (!suspended) { return false; } - pdu_t p; + unique_byte_buffer_t rx_pdu; // Do not block - while (rx_pdu_resume_queue.try_pop(p)) { - write_pdu(p.payload, p.nof_bytes); - free(p.payload); + while (rx_pdu_resume_queue.try_pop(rx_pdu)) { + write_pdu(rx_pdu->msg, rx_pdu->N_bytes); } - unique_byte_buffer_t s; - while (tx_sdu_resume_queue.try_pop(s)) { - write_sdu(std::move(s)); + unique_byte_buffer_t tx_sdu; + while (tx_sdu_resume_queue.try_pop(tx_sdu)) { + write_sdu(std::move(tx_sdu)); } suspended = false; return true; @@ -268,8 +279,8 @@ public: } } - virtual rlc_mode_t get_mode() = 0; - virtual uint32_t get_bearer() = 0; + virtual rlc_mode_t get_mode() = 0; + virtual uint32_t get_lcid() = 0; virtual rlc_bearer_metrics_t get_metrics() = 0; virtual void reset_metrics() = 0; @@ -282,28 +293,42 @@ public: // MAC interface virtual bool has_data() = 0; bool is_suspended() { return suspended; }; - virtual uint32_t get_buffer_state() = 0; - virtual int read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; - virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; + virtual uint32_t get_buffer_state() = 0; + virtual void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) = 0; + virtual uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; + virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0; virtual void set_bsr_callback(bsr_callback_t callback) = 0; void* operator new(size_t sz) { return allocate_rlc_bearer(sz); } void operator delete(void* p) { return deallocate_rlc_bearer(p); } +protected: + std::string rb_name = {}; + srslog::basic_logger& logger; + private: bool suspended = false; // Enqueues the Rx PDU in the resume queue void queue_rx_pdu(uint8_t* payload, uint32_t nof_bytes) { - pdu_t p = {}; - p.nof_bytes = nof_bytes; - p.payload = (uint8_t*)malloc(nof_bytes); - memcpy(p.payload, payload, nof_bytes); + unique_byte_buffer_t rx_pdu = srsran::make_byte_buffer(); + if (rx_pdu == nullptr) { + srslog::fetch_basic_logger("RLC").warning("Couldn't allocate PDU in %s().", __FUNCTION__); + return; + } + + if (rx_pdu->get_tailroom() < nof_bytes) { + srslog::fetch_basic_logger("RLC").warning("Not enough space to store PDU."); + return; + } + + memcpy(rx_pdu->msg, payload, nof_bytes); + rx_pdu->N_bytes = nof_bytes; // Do not block ever - if (!rx_pdu_resume_queue.try_push(p)) { + if (!rx_pdu_resume_queue.try_push(std::move(rx_pdu))) { srslog::fetch_basic_logger("RLC").warning("Dropping SDUs while bearer suspended."); return; } @@ -319,14 +344,10 @@ private: } } - typedef struct { - uint8_t* payload; - uint32_t nof_bytes; - } pdu_t; - - static_blocking_queue rx_pdu_resume_queue; + static_blocking_queue rx_pdu_resume_queue; static_blocking_queue tx_sdu_resume_queue; }; } // namespace srsran + #endif // SRSRAN_RLC_COMMON_H diff --git a/lib/include/srsran/upper/rlc_metrics.h b/lib/include/srsran/rlc/rlc_metrics.h similarity index 96% rename from lib/include/srsran/upper/rlc_metrics.h rename to lib/include/srsran/rlc/rlc_metrics.h index 6c0f23a23..2773f0cee 100644 --- a/lib/include/srsran/upper/rlc_metrics.h +++ b/lib/include/srsran/rlc/rlc_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/upper/rlc_tm.h b/lib/include/srsran/rlc/rlc_tm.h similarity index 82% rename from lib/include/srsran/upper/rlc_tm.h rename to lib/include/srsran/rlc/rlc_tm.h index 246ea3e4c..6db8ea430 100644 --- a/lib/include/srsran/upper/rlc_tm.h +++ b/lib/include/srsran/rlc/rlc_tm.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,8 +24,8 @@ #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" +#include "srsran/rlc/rlc_common.h" #include "srsran/upper/byte_buffer_queue.h" -#include "srsran/upper/rlc_common.h" namespace srsue { @@ -50,7 +50,7 @@ public: void empty_queue() override; rlc_mode_t get_mode() override; - uint32_t get_bearer() override; + uint32_t get_lcid() override; rlc_bearer_metrics_t get_metrics() override; void reset_metrics() override; @@ -63,20 +63,24 @@ public: // MAC interface bool has_data() override; uint32_t get_buffer_state() override; - int read_pdu(uint8_t* payload, uint32_t nof_bytes) override; + void get_buffer_state(uint32_t& newtx_queue, uint32_t& prio_tx_queue) override; + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) override; void write_pdu(uint8_t* payload, uint32_t nof_bytes) override; - void set_bsr_callback(bsr_callback_t callback) override {} + void set_bsr_callback(bsr_callback_t callback) override; private: byte_buffer_pool* pool = nullptr; - srslog::basic_logger& logger; uint32_t lcid = 0; srsue::pdcp_interface_rlc* pdcp = nullptr; srsue::rrc_interface_rlc* rrc = nullptr; - bool tx_enabled = true; + std::mutex bsr_callback_mutex; + bsr_callback_t bsr_callback; + std::atomic tx_enabled = {true}; + + std::mutex metrics_mutex; rlc_bearer_metrics_t metrics = {}; // Thread-safe queues for MAC messages diff --git a/lib/include/srsran/upper/rlc_um_base.h b/lib/include/srsran/rlc/rlc_um_base.h similarity index 85% rename from lib/include/srsran/upper/rlc_um_base.h rename to lib/include/srsran/rlc/rlc_um_base.h index adf6fc0b9..6517f6b33 100644 --- a/lib/include/srsran/upper/rlc_um_base.h +++ b/lib/include/srsran/rlc/rlc_um_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,8 +26,8 @@ #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" #include "srsran/common/task_scheduler.h" +#include "srsran/rlc/rlc_common.h" #include "srsran/upper/byte_buffer_queue.h" -#include "srsran/upper/rlc_common.h" #include #include #include @@ -57,7 +57,7 @@ public: bool is_mrb(); rlc_mode_t get_mode(); - uint32_t get_bearer(); + uint32_t get_lcid() final; // PDCP interface void write_sdu(unique_byte_buffer_t sdu); @@ -67,14 +67,17 @@ public: // MAC interface bool has_data(); uint32_t get_buffer_state(); - int read_pdu(uint8_t* payload, uint32_t nof_bytes); + void get_buffer_state(uint32_t& newtx_queue, uint32_t& prio_tx_queue); + uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes); void write_pdu(uint8_t* payload, uint32_t nof_bytes); int get_increment_sequence_num(); rlc_bearer_metrics_t get_metrics(); void reset_metrics(); - void set_bsr_callback(bsr_callback_t callback) {} + void set_bsr_callback(bsr_callback_t callback); + + uint32_t get_lcid() const { return lcid; } protected: // Transmitter sub-class base @@ -84,7 +87,7 @@ protected: rlc_um_base_tx(rlc_um_base* parent_); virtual ~rlc_um_base_tx(); virtual bool configure(const rlc_config_t& cfg, std::string rb_name) = 0; - int build_data_pdu(uint8_t* payload, uint32_t nof_bytes); + uint32_t build_data_pdu(uint8_t* payload, uint32_t nof_bytes); void stop(); void reestablish(); void empty_queue(); @@ -96,11 +99,14 @@ protected: bool has_data(); virtual uint32_t get_buffer_state() = 0; + void set_bsr_callback(bsr_callback_t callback); + protected: byte_buffer_pool* pool = nullptr; srslog::basic_logger& logger; std::string rb_name; rlc_um_base* parent = nullptr; + bsr_callback_t bsr_callback; rlc_config_t cfg = {}; @@ -116,7 +122,7 @@ protected: srsran::rolling_average mean_pdu_latency_us; #endif - virtual int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) = 0; + virtual uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) = 0; // helper functions virtual void debug_state() = 0; @@ -157,14 +163,12 @@ protected: }; // Common variables needed by parent class - srsue::rrc_interface_rlc* rrc = nullptr; - srsue::pdcp_interface_rlc* pdcp = nullptr; - srslog::basic_logger& logger; + srsue::rrc_interface_rlc* rrc = nullptr; + srsue::pdcp_interface_rlc* pdcp = nullptr; srsran::timer_handler* timers = nullptr; uint32_t lcid = 0; rlc_config_t cfg = {}; - std::string rb_name; - byte_buffer_pool* pool = nullptr; + byte_buffer_pool* pool = nullptr; std::string get_rb_name(srsue::rrc_interface_rlc* rrc, uint32_t lcid, bool is_mrb); // Rx and Tx objects @@ -174,6 +178,7 @@ protected: bool tx_enabled = false; bool rx_enabled = false; + std::mutex metrics_mutex; rlc_bearer_metrics_t metrics = {}; }; diff --git a/lib/include/srsran/upper/rlc_um_lte.h b/lib/include/srsran/rlc/rlc_um_lte.h similarity index 95% rename from lib/include/srsran/upper/rlc_um_lte.h rename to lib/include/srsran/rlc/rlc_um_lte.h index 48956a896..d7f3790aa 100644 --- a/lib/include/srsran/upper/rlc_um_lte.h +++ b/lib/include/srsran/rlc/rlc_um_lte.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,8 +24,8 @@ #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" +#include "srsran/rlc/rlc_um_base.h" #include "srsran/upper/byte_buffer_queue.h" -#include "srsran/upper/rlc_um_base.h" #include #include #include @@ -57,7 +57,8 @@ private: rlc_um_lte_tx(rlc_um_base* parent_); bool configure(const rlc_config_t& cfg, std::string rb_name); - int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); + void discard_sdu(uint32_t discard_sn); uint32_t get_buffer_state(); bool sdu_queue_is_full(); diff --git a/lib/include/srsran/upper/rlc_um_nr.h b/lib/include/srsran/rlc/rlc_um_nr.h similarity index 92% rename from lib/include/srsran/upper/rlc_um_nr.h rename to lib/include/srsran/rlc/rlc_um_nr.h index 1fc3ad431..5ec2f936f 100644 --- a/lib/include/srsran/upper/rlc_um_nr.h +++ b/lib/include/srsran/rlc/rlc_um_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,8 +25,8 @@ #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" #include "srsran/interfaces/ue_interfaces.h" +#include "srsran/rlc/rlc_um_base.h" #include "srsran/upper/byte_buffer_queue.h" -#include "srsran/upper/rlc_um_base.h" #include #include #include @@ -50,6 +50,9 @@ public: ~rlc_um_nr(); bool configure(const rlc_config_t& cnfg); + // logging helpers + std::string get_rb_name() const; + private: // Transmitter sub-class for NR class rlc_um_nr_tx : public rlc_um_base_tx @@ -58,7 +61,8 @@ private: rlc_um_nr_tx(rlc_um_base* parent_); bool configure(const rlc_config_t& cfg, std::string rb_name); - int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); + uint32_t build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes); + void discard_sdu(uint32_t discard_sn); uint32_t get_buffer_state(); private: @@ -71,6 +75,9 @@ private: uint32_t next_so = 0; // The segment offset for the next generated PDU + uint32_t UM_Window_Size; + uint32_t mod; // Rx counter modulus + static constexpr uint32_t head_len_full = 1; // full SDU header size is always uint32_t head_len_first = 0, head_len_segment = 0; // are computed during configure based on SN length @@ -105,6 +112,9 @@ private: uint32_t RX_Next_Highest = 0; // the SN following the SN of the UMD PDU with the highest SN among // received UMD PDUs. It serves as the higher edge of the reassembly window. + uint32_t UM_Window_Size = 0; + uint32_t mod = 0; // Rx counter modulus + // Rx window typedef struct { std::map segments; // Map of segments with SO as key diff --git a/srsue/hdr/stack/rrc/rrc_common.h b/lib/include/srsran/rrc/rrc_common.h similarity index 72% rename from srsue/hdr/stack/rrc/rrc_common.h rename to lib/include/srsran/rrc/rrc_common.h index 3eba201e0..b80b29d2f 100644 --- a/srsue/hdr/stack/rrc/rrc_common.h +++ b/lib/include/srsran/rrc/rrc_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,16 +22,15 @@ #ifndef SRSUE_RRC_COMMON_H #define SRSUE_RRC_COMMON_H -namespace srsue { +#include -// RRC states (3GPP 36.331 v10.0.0) -typedef enum { - RRC_STATE_IDLE = 0, - RRC_STATE_CONNECTED, - RRC_STATE_N_ITEMS, -} rrc_state_t; -static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", "CONNECTED"}; +namespace srsran { -} // namespace srsue +enum quant_s { quant_rsrp, quant_rsrq }; + +uint8_t rrc_value_to_range(quant_s quant, const float value); +float rrc_range_to_value(quant_s quant, const uint8_t range); + +} // namespace srsran #endif // SRSUE_RRC_COMMON_H diff --git a/lib/include/srsran/srslog/bundled/fmt/chrono.h b/lib/include/srsran/srslog/bundled/fmt/chrono.h index 72b5af0ac..93cf376ed 100644 --- a/lib/include/srsran/srslog/bundled/fmt/chrono.h +++ b/lib/include/srsran/srslog/bundled/fmt/chrono.h @@ -318,7 +318,7 @@ inline null<> gmtime_s(...) { return null<>(); } inline std::tm localtime(std::time_t time) { struct dispatcher { std::time_t time_; - std::tm tm_; + std::tm tm_{}; dispatcher(std::time_t t) : time_(t) {} diff --git a/lib/include/srsran/srslog/bundled/fmt/core.h b/lib/include/srsran/srslog/bundled/fmt/core.h index d676f27e5..04a85f295 100644 --- a/lib/include/srsran/srslog/bundled/fmt/core.h +++ b/lib/include/srsran/srslog/bundled/fmt/core.h @@ -8,7 +8,8 @@ #ifndef FMT_CORE_H_ #define FMT_CORE_H_ -#include // std::FILE +#include +#include // std::FILE #include #include #include diff --git a/lib/include/srsran/srslog/context.h b/lib/include/srsran/srslog/context.h index d9ed6aa2e..6cb03b1ee 100644 --- a/lib/include/srsran/srslog/context.h +++ b/lib/include/srsran/srslog/context.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/detail/log_backend.h b/lib/include/srsran/srslog/detail/log_backend.h index 104b2e504..7c7810d9f 100644 --- a/lib/include/srsran/srslog/detail/log_backend.h +++ b/lib/include/srsran/srslog/detail/log_backend.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,6 +23,7 @@ #define SRSLOG_DETAIL_LOG_BACKEND_H #include "srsran/srslog/bundled/fmt/printf.h" +#include "srsran/srslog/shared_types.h" namespace srslog { @@ -40,7 +41,7 @@ public: /// Starts the processing of incoming log entries. /// NOTE: Calling this function more than once has no side effects. - virtual void start() = 0; + virtual void start(backend_priority priority = backend_priority::normal) = 0; /// Allocates a dyn_arg_store and returns a pointer to it on success, otherwise returns nullptr. virtual fmt::dynamic_format_arg_store* alloc_arg_store() = 0; diff --git a/lib/include/srsran/srslog/detail/log_entry.h b/lib/include/srsran/srslog/detail/log_entry.h index f3a90a1d0..0cc21cbd0 100644 --- a/lib/include/srsran/srslog/detail/log_entry.h +++ b/lib/include/srsran/srslog/detail/log_entry.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/detail/log_entry_metadata.h b/lib/include/srsran/srslog/detail/log_entry_metadata.h index ad830a7bc..f7ee1d10a 100644 --- a/lib/include/srsran/srslog/detail/log_entry_metadata.h +++ b/lib/include/srsran/srslog/detail/log_entry_metadata.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,9 +27,6 @@ namespace srslog { -/// This type is used to store small strings without doing any memory allocation. -using small_str_buffer = fmt::basic_memory_buffer; - namespace detail { /// This structure gives the user a way to log generic information as a context. @@ -49,7 +46,6 @@ struct log_entry_metadata { fmt::dynamic_format_arg_store* store; std::string log_name; char log_tag; - small_str_buffer small_str; std::vector hex_dump; }; diff --git a/lib/include/srsran/srslog/detail/support/any.h b/lib/include/srsran/srslog/detail/support/any.h index db8ae9504..6b0091aae 100644 --- a/lib/include/srsran/srslog/detail/support/any.h +++ b/lib/include/srsran/srslog/detail/support/any.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/detail/support/backend_capacity.h b/lib/include/srsran/srslog/detail/support/backend_capacity.h index a0420850b..d30420180 100644 --- a/lib/include/srsran/srslog/detail/support/backend_capacity.h +++ b/lib/include/srsran/srslog/detail/support/backend_capacity.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/detail/support/dyn_arg_store_pool.h b/lib/include/srsran/srslog/detail/support/dyn_arg_store_pool.h index 03adff10f..4bf3458f6 100644 --- a/lib/include/srsran/srslog/detail/support/dyn_arg_store_pool.h +++ b/lib/include/srsran/srslog/detail/support/dyn_arg_store_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -69,8 +69,8 @@ public: return; } - scoped_lock lock(m); p->clear(); + scoped_lock lock(m); free_list.push_back(p); } diff --git a/lib/include/srsran/srslog/detail/support/error_string.h b/lib/include/srsran/srslog/detail/support/error_string.h index 51b6142e4..20c00e8c7 100644 --- a/lib/include/srsran/srslog/detail/support/error_string.h +++ b/lib/include/srsran/srslog/detail/support/error_string.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/detail/support/memory_buffer.h b/lib/include/srsran/srslog/detail/support/memory_buffer.h index 6d28e098b..59adc6e8f 100644 --- a/lib/include/srsran/srslog/detail/support/memory_buffer.h +++ b/lib/include/srsran/srslog/detail/support/memory_buffer.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/detail/support/thread_utils.h b/lib/include/srsran/srslog/detail/support/thread_utils.h index 03d133a90..031fb6399 100644 --- a/lib/include/srsran/srslog/detail/support/thread_utils.h +++ b/lib/include/srsran/srslog/detail/support/thread_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,14 @@ public: mutex(const mutex&) = delete; mutex& operator=(const mutex&) = delete; - mutex() { ::pthread_mutex_init(&m, nullptr); } + mutex() + { + ::pthread_mutexattr_t mutex_attr; + ::pthread_mutexattr_init(&mutex_attr); + ::pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT); + ::pthread_mutex_init(&m, &mutex_attr); + } + ~mutex() { ::pthread_mutex_destroy(&m); } /// Mutex lock. diff --git a/lib/include/srsran/srslog/detail/support/tmp_utils.h b/lib/include/srsran/srslog/detail/support/tmp_utils.h index 24a9f2fd4..d262c9811 100644 --- a/lib/include/srsran/srslog/detail/support/tmp_utils.h +++ b/lib/include/srsran/srslog/detail/support/tmp_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/detail/support/work_queue.h b/lib/include/srsran/srslog/detail/support/work_queue.h index 1da2cefcb..2a60f7a68 100644 --- a/lib/include/srsran/srslog/detail/support/work_queue.h +++ b/lib/include/srsran/srslog/detail/support/work_queue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,7 @@ template class work_queue { srsran::dyn_circular_buffer queue; - mutable condition_variable cond_var; + mutable mutex m; static constexpr size_t threshold = capacity * 0.98; public: @@ -50,15 +50,14 @@ public: /// queue is full, otherwise true. bool push(const T& value) { - cond_var.lock(); + m.lock(); // Discard the new element if we reach the maximum capacity. if (queue.full()) { - cond_var.unlock(); + m.unlock(); return false; } queue.push(value); - cond_var.unlock(); - cond_var.signal(); + m.unlock(); return true; } @@ -67,56 +66,26 @@ public: /// queue is full, otherwise true. bool push(T&& value) { - cond_var.lock(); + m.lock(); // Discard the new element if we reach the maximum capacity. if (queue.full()) { - cond_var.unlock(); + m.unlock(); return false; } queue.push(std::move(value)); - cond_var.unlock(); - cond_var.signal(); + m.unlock(); return true; } - /// Extracts the top most element from the queue. - /// NOTE: This method blocks while the queue is empty. - T pop() + /// Extracts the top most element from the queue if it exists. + /// Returns a pair with a bool indicating if the pop has been successful. + std::pair try_pop() { - cond_var.lock(); + m.lock(); - while (queue.empty()) { - cond_var.wait(); - } - - T elem = std::move(queue.top()); - queue.pop(); - - cond_var.unlock(); - - return elem; - } - - /// Extracts the top most element from the queue. - /// NOTE: This method blocks while the queue is empty or or until the - /// programmed timeout expires. Returns a pair with a bool indicating if the - /// pop has been successful. - std::pair timed_pop(unsigned timeout_ms) - { - // Build an absolute time reference for the expiration time. - timespec ts = condition_variable::build_timeout(timeout_ms); - - cond_var.lock(); - - bool timedout = false; - while (queue.empty() && !timedout) { - timedout = cond_var.wait(ts); - } - - // Did we wake up on timeout? - if (timedout && queue.empty()) { - cond_var.unlock(); + if (queue.empty()) { + m.unlock(); return {false, T()}; } @@ -124,7 +93,7 @@ public: T Item = std::move(queue.top()); queue.pop(); - cond_var.unlock(); + m.unlock(); return {true, std::move(Item)}; } @@ -135,7 +104,7 @@ public: /// Returns true when the queue is almost full, otherwise returns false. bool is_almost_full() const { - cond_var_scoped_lock lock(cond_var); + scoped_lock lock(m); return queue.size() > threshold; } diff --git a/lib/include/srsran/srslog/event_trace.h b/lib/include/srsran/srslog/event_trace.h index c5525f521..951f58414 100644 --- a/lib/include/srsran/srslog/event_trace.h +++ b/lib/include/srsran/srslog/event_trace.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/formatter.h b/lib/include/srsran/srslog/formatter.h index a58e37f9c..c81cb2d40 100644 --- a/lib/include/srsran/srslog/formatter.h +++ b/lib/include/srsran/srslog/formatter.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/log_channel.h b/lib/include/srsran/srslog/log_channel.h index d515b43ae..0dd8bb158 100644 --- a/lib/include/srsran/srslog/log_channel.h +++ b/lib/include/srsran/srslog/log_channel.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,6 +25,7 @@ #include "srsran/srslog/detail/log_backend.h" #include "srsran/srslog/detail/log_entry.h" #include "srsran/srslog/sink.h" +#include namespace srslog { @@ -74,11 +75,11 @@ public: log_channel& operator=(const log_channel& other) = delete; /// Controls when the channel accepts incoming log entries. - void set_enabled(bool enabled) { is_enabled = enabled; } + void set_enabled(bool enabled) { is_enabled.store(enabled, std::memory_order_relaxed); } /// Returns true if the channel is accepting incoming log entries, otherwise /// false. - bool enabled() const { return is_enabled; } + bool enabled() const { return is_enabled.load(std::memory_order_relaxed); } /// Returns the id string of the channel. const std::string& id() const { return log_id; } @@ -117,32 +118,7 @@ public: fmtstr, store, log_name, - log_tag, - small_str_buffer()}}; - backend.push(std::move(entry)); - } - - /// Builds the provided log entry and passes it to the backend. When the - /// channel is disabled the log entry will be discarded. - void operator()(small_str_buffer&& str) - { - if (!enabled()) { - return; - } - - // Send the log entry to the backend. - log_formatter& formatter = log_sink.get_formatter(); - detail::log_entry entry = {&log_sink, - [&formatter](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) { - formatter.format(std::move(metadata), buffer); - }, - {std::chrono::high_resolution_clock::now(), - {ctx_value, should_print_context}, - nullptr, - nullptr, - log_name, - log_tag, - std::move(str)}}; + log_tag}}; backend.push(std::move(entry)); } @@ -179,7 +155,6 @@ public: store, log_name, log_tag, - small_str_buffer(), std::vector(buffer, buffer + len)}}; backend.push(std::move(entry)); } @@ -204,8 +179,7 @@ public: nullptr, nullptr, log_name, - log_tag, - small_str_buffer()}}; + log_tag}}; backend.push(std::move(entry)); } @@ -236,21 +210,20 @@ public: fmtstr, store, log_name, - log_tag, - small_str_buffer()}}; + log_tag}}; backend.push(std::move(entry)); } private: - const std::string log_id; - sink& log_sink; - detail::log_backend& backend; - const std::string log_name; - const char log_tag; - const bool should_print_context; - detail::shared_variable ctx_value; - detail::shared_variable hex_max_size; - detail::shared_variable is_enabled; + const std::string log_id; + sink& log_sink; + detail::log_backend& backend; + const std::string log_name; + const char log_tag; + const bool should_print_context; + std::atomic ctx_value; + std::atomic hex_max_size; + std::atomic is_enabled; }; } // namespace srslog diff --git a/lib/include/srsran/srslog/logger.h b/lib/include/srsran/srslog/logger.h index cac34908f..dda5caeee 100644 --- a/lib/include/srsran/srslog/logger.h +++ b/lib/include/srsran/srslog/logger.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/shared_types.h b/lib/include/srsran/srslog/shared_types.h index b173c413b..0ae0b012a 100644 --- a/lib/include/srsran/srslog/shared_types.h +++ b/lib/include/srsran/srslog/shared_types.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,12 +23,35 @@ #define SRSLOG_SHARED_TYPES_H #include +#include namespace srslog { /// Generic error handler callback. using error_handler = std::function; +/// Backend priority levels. +enum class backend_priority { + /// Default priority of the operating system. + normal, + /// Thread will be given a high priority. + high, + /// Thread will be given a very high priority. + very_high +}; + +/// syslog log local types +enum class syslog_local_type { + local0, + local1, + local2, + local3, + local4, + local5, + local6, + local7, +}; + } // namespace srslog #endif // SRSLOG_SHARED_TYPES_H diff --git a/lib/include/srsran/srslog/sink.h b/lib/include/srsran/srslog/sink.h index 342d8d001..3578815af 100644 --- a/lib/include/srsran/srslog/sink.h +++ b/lib/include/srsran/srslog/sink.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/srslog/srslog.h b/lib/include/srsran/srslog/srslog.h index 323a7c019..14037aa0c 100644 --- a/lib/include/srsran/srslog/srslog.h +++ b/lib/include/srsran/srslog/srslog.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -188,10 +188,20 @@ sink& fetch_stderr_sink(const std::string& id = "stderr", /// Specifying a max_size value different to zero will make the sink create a /// new file each time the current file exceeds this value. The units of /// max_size are bytes. +/// Setting force_flush to true will flush the sink after every write. /// NOTE: Any '#' characters in the path will get removed. sink& fetch_file_sink(const std::string& path, - size_t max_size = 0, - std::unique_ptr f = get_default_log_formatter()); + size_t max_size = 0, + bool force_flush = false, + std::unique_ptr f = get_default_log_formatter()); + +/// Returns an instance of a sink that writes into syslog +/// preamble: The string prepended to every message, If ident is "", the program name is used. +/// log_local: custom unused facilities that syslog provides which can be used by the user +/// NOTE: Any '#' characters in the path will get removed. +sink& fetch_syslog_sink(const std::string& preamble_ = "", + syslog_local_type log_local_ = syslog_local_type::local0, + std::unique_ptr f = get_default_log_formatter()); /// Installs a custom user defined sink in the framework getting associated to /// the specified id. Returns true on success, otherwise false. @@ -232,7 +242,7 @@ sink* create_stderr_sink(const std::string& name = "stderr"); /// This function initializes the logging framework. It must be called before /// any log entry is generated. /// NOTE: Calling this function more than once has no side effects. -void init(); +void init(backend_priority priority = backend_priority::normal); /// Flushes the contents of all the registered sinks. The caller thread will /// block until the operation is completed. diff --git a/lib/include/srsran/srslog/srslog_c.h b/lib/include/srsran/srslog/srslog_c.h index 7e9985e47..433c21642 100644 --- a/lib/include/srsran/srslog/srslog_c.h +++ b/lib/include/srsran/srslog/srslog_c.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -157,9 +157,10 @@ srslog_sink* srslog_fetch_stderr_sink(void); * Specifying a max_size value different to zero will make the sink create a * new file each time the current file exceeds this value. The units of * max_size are bytes. + * Setting force_flush to true will flush the sink after every write. * NOTE: Any '#' characters in the id will get removed. */ -srslog_sink* srslog_fetch_file_sink(const char* path, size_t max_size); +srslog_sink* srslog_fetch_file_sink(const char* path, size_t max_size, srslog_bool force_flush); #ifdef __cplusplus } diff --git a/lib/include/srsran/srsran.h b/lib/include/srsran/srsran.h index 8da90bd73..2e86d5dbb 100644 --- a/lib/include/srsran/srsran.h +++ b/lib/include/srsran/srsran.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,6 +48,7 @@ extern "C" { #include "srsran/phy/ch_estimation/chest_dl.h" #include "srsran/phy/ch_estimation/chest_ul.h" +#include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/ch_estimation/dmrs_pdcch.h" #include "srsran/phy/ch_estimation/dmrs_sch.h" #include "srsran/phy/ch_estimation/refsignal_dl.h" @@ -60,6 +61,7 @@ extern "C" { #include "srsran/phy/channel/ch_awgn.h" +#include "srsran/phy/cfr/cfr.h" #include "srsran/phy/dft/dft.h" #include "srsran/phy/dft/dft_precoding.h" #include "srsran/phy/dft/ofdm.h" @@ -93,7 +95,9 @@ extern "C" { #include "srsran/phy/phch/csi.h" #include "srsran/phy/phch/dci.h" #include "srsran/phy/phch/dci_nr.h" +#include "srsran/phy/phch/harq_ack.h" #include "srsran/phy/phch/pbch.h" +#include "srsran/phy/phch/pbch_nr.h" #include "srsran/phy/phch/pcfich.h" #include "srsran/phy/phch/pdcch.h" #include "srsran/phy/phch/pdcch_nr.h" @@ -119,12 +123,14 @@ extern "C" { #include "srsran/phy/ue/ue_dl_nr.h" #include "srsran/phy/ue/ue_mib.h" #include "srsran/phy/ue/ue_sync.h" +#include "srsran/phy/ue/ue_sync_nr.h" #include "srsran/phy/ue/ue_ul.h" #include "srsran/phy/ue/ue_ul_nr.h" #include "srsran/phy/enb/enb_dl.h" -#include "srsran/phy/enb/enb_dl_nr.h" #include "srsran/phy/enb/enb_ul.h" +#include "srsran/phy/gnb/gnb_dl.h" +#include "srsran/phy/gnb/gnb_ul.h" #include "srsran/phy/scrambling/scrambling.h" @@ -133,6 +139,7 @@ extern "C" { #include "srsran/phy/sync/pss.h" #include "srsran/phy/sync/refsignal_dl_sync.h" #include "srsran/phy/sync/sfo.h" +#include "srsran/phy/sync/ssb.h" #include "srsran/phy/sync/sss.h" #include "srsran/phy/sync/sync.h" diff --git a/lib/include/srsran/support/emergency_handlers.h b/lib/include/srsran/support/emergency_handlers.h new file mode 100644 index 000000000..8fcdfa069 --- /dev/null +++ b/lib/include/srsran/support/emergency_handlers.h @@ -0,0 +1,38 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_EMERGENCY_HANDLERS_H +#define SRSRAN_EMERGENCY_HANDLERS_H + +using emergency_cleanup_callback = void (*)(void*); + +// Add a cleanup function to be called when a kill signal is about to be delivered to the process. The handler may +// optionally pass a pointer to identify what instance of the handler is being called. +// Returns the id of the handler as a positive value, otherwise returns -1 on error. +int add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data); + +// Removes the emergency handler with the specified id. +void remove_emergency_cleanup_handler(int id); + +// Executes all registered emergency cleanup handlers. +void execute_emergency_cleanup_handlers(); + +#endif // SRSRAN_EMERGENCY_HANDLERS_H diff --git a/lib/include/srsran/support/signal_handler.h b/lib/include/srsran/support/signal_handler.h new file mode 100644 index 000000000..efc225a23 --- /dev/null +++ b/lib/include/srsran/support/signal_handler.h @@ -0,0 +1,36 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/** + * @file signal_handler.h + * @brief Common signal handling methods for all srsRAN applications. + */ + +#ifndef SRSRAN_SIGNAL_HANDLER_H +#define SRSRAN_SIGNAL_HANDLER_H + +using srsran_signal_hanlder = void (*)(); + +/// Registers the specified function to be called when the user interrupts the program execution (eg: via Ctrl+C). +/// Passing a null function pointer disables the current installed handler. +void srsran_register_signal_handler(srsran_signal_hanlder handler); + +#endif // SRSRAN_SIGNAL_HANDLER_H diff --git a/lib/include/srsran/common/srsran_assert.h b/lib/include/srsran/support/srsran_assert.h similarity index 54% rename from lib/include/srsran/common/srsran_assert.h rename to lib/include/srsran/support/srsran_assert.h index 9139f87c4..c069d0ad7 100644 --- a/lib/include/srsran/common/srsran_assert.h +++ b/lib/include/srsran/support/srsran_assert.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,38 +22,63 @@ #ifndef SRSRAN_ASSERT_H #define SRSRAN_ASSERT_H +#ifdef __cplusplus #include "srsran/srslog/srslog.h" #include +#include #define srsran_unlikely(expr) __builtin_expect(!!(expr), 0) -#define srsran_terminate(fmt, ...) \ - std::fprintf(stderr, "%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \ - srslog::flush(); \ - std::abort() +/** + * Command to terminate srsRAN application with an error message, ensuring first that the log is flushed + */ +[[gnu::noinline, noreturn]] inline bool srsran_terminate(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + srslog::flush(); + vfprintf(stderr, fmt, args); + va_end(args); + std::abort(); +} -#ifdef ASSERTS_ENABLED +#define srsran_assertion_failure(fmt, ...) \ + srsran_terminate("%s:%d: Assertion Failure: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) /** - * Macro that asserts condition is true. If false, it logs the remaining parameters, prints the backtrace and closes - * the application + * Macro that asserts condition is true. If false, it logs the remaining macro args, flushes the log, + * prints the backtrace (if it was activated) and closes the application. */ -#define srsran_assert(condition, fmt, ...) \ - do { \ - if (srsran_unlikely(not(condition))) { \ - srsran_terminate(fmt, ##__VA_ARGS__); \ - } \ - } while (0) +#define srsran_always_assert(condition, fmt, ...) (void)((condition) || srsran_assertion_failure(fmt, ##__VA_ARGS__)) + +#define SRSRAN_IS_DEFINED(x) SRSRAN_IS_DEFINED2(x) +#define SRSRAN_IS_DEFINED2(x) (#x[0] == 0 || (#x[0] >= '1' && #x[0] <= '9')) + +/** + * Same as "srsran_always_assert" but it is only active when "enable_check" flag is defined + */ +#define srsran_assert_ifdef(enable_check, condition, fmt, ...) \ + (void)((not SRSRAN_IS_DEFINED(enable_check)) || (srsran_always_assert(condition, fmt, ##__VA_ARGS__), 0)) + +/** + * Specialization of "srsran_assert_ifdef" for the ASSERTS_ENABLED flag + */ +#define srsran_assert(condition, fmt, ...) srsran_assert_ifdef(ASSERTS_ENABLED, condition, fmt, ##__VA_ARGS__) + +/** + * Specialization of "srsran_assert_ifdef" for the SANITY_CHECKS_ENABLED flag + */ +#ifndef NDEBUG +#define SANITY_CHECKS_ENABLED +#endif +#define srsran_sanity_check(condition, fmt, ...) \ + srsran_assert_ifdef(SANITY_CHECKS_ENABLED, condition, fmt, ##__VA_ARGS__) #ifdef STOP_ON_WARNING -/** - * Macro that verifies if condition is true. If false, and STOP_ON_WARNING is true, it behaves like srsran_assert. - * If STOP_ON_WARNING is false, it logs a warning. - */ #define srsran_expect(condition, fmt, ...) srsran_assert(condition, fmt, ##__VA_ARGS__) -#else // STOP_ON_WARNING +#else #define srsran_expect(condition, fmt, ...) \ do { \ @@ -62,16 +87,20 @@ } \ } while (0) -#endif // STOP_ON_WARNING +#endif -#else // ASSERTS_ENABLED +#else // __ifcplusplus +#include + +#ifdef ASSERTS_ENABLED +#define srsran_assert(condition, fmt, ...) (void)((condition) || (__assert(#condition, __FILE__, __FLAG__), 0)) +#else #define srsran_assert(condition, fmt, ...) \ do { \ } while (0) - -#define srsran_expect(condition, fmt, ...) srsran_assert(condition, fmt, ##__VA_ARGS__) - #endif +#endif // __ifcplusplus + #endif // SRSRAN_ASSERT_H diff --git a/lib/include/srsran/support/srsran_test.h b/lib/include/srsran/support/srsran_test.h new file mode 100644 index 000000000..2c43ffd10 --- /dev/null +++ b/lib/include/srsran/support/srsran_test.h @@ -0,0 +1,57 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SRSRAN_TEST_H +#define SRSRAN_SRSRAN_TEST_H + +#ifdef __cplusplus + +#include "srsran_assert.h" + +#define TESTASSERT_EQ(EXPECTED, ACTUAL) \ + (void)((EXPECTED == ACTUAL) || \ + (srsran_assertion_failure( \ + "%s", fmt::format("Actual value '{}' differs from expected '{}'", ACTUAL, EXPECTED).c_str()), \ + 0)) + +#define TESTASSERT_NEQ(EXPECTED, ACTUAL) \ + (void)((EXPECTED != ACTUAL) || \ + (srsran_assertion_failure("%s", fmt::format("Value should not be equal to '{}'", ACTUAL).c_str()), 0)) + +#define TESTASSERT(cond) srsran_assert((cond), "Fail at \"%s\"", (#cond)) + +#define TESTASSERT_SUCCESS(cond) srsran_assert((cond == SRSRAN_SUCCESS), "Operation \"%s\" was not successful", (#cond)) + +#else // __cplusplus + +#include + +#define TESTASSERT(cond) \ + do { \ + if (!(cond)) { \ + fprintf(stderr, "%s:%d: Assertion Failure: \"%s\"\n", __FUNCTION__, __LINE__, (#cond)); \ + return -1; \ + } \ + } while (0) + +#endif // __cplusplus + +#endif // SRSRAN_SRSRAN_TEST_H diff --git a/lib/include/srsran/system/sys_metrics.h b/lib/include/srsran/system/sys_metrics.h index 1be398883..98fcc6e99 100644 --- a/lib/include/srsran/system/sys_metrics.h +++ b/lib/include/srsran/system/sys_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #ifndef SRSRAN_SYS_METRICS_H #define SRSRAN_SYS_METRICS_H +#include #include namespace srsran { @@ -30,14 +31,14 @@ constexpr uint32_t metrics_max_supported_cpu = 32u; /// Metrics of cpu usage, memory consumption and number of thread used by the process. struct sys_metrics_t { - uint32_t process_realmem_kB = 0; - uint32_t process_virtualmem_kB = 0; - float process_realmem = 0.f; - uint32_t thread_count = 0; - float process_cpu_usage = 0.f; - float system_mem = 0.f; - uint32_t cpu_count = 0; - float cpu_load[metrics_max_supported_cpu]; + uint32_t process_realmem_kB = 0; + uint32_t process_virtualmem_kB = 0; + float process_realmem = 0.f; + uint32_t thread_count = 0; + float process_cpu_usage = 0.f; + float system_mem = 0.f; + uint32_t cpu_count = 0; + std::array cpu_load = {}; }; } // namespace srsran diff --git a/lib/include/srsran/system/sys_metrics_processor.h b/lib/include/srsran/system/sys_metrics_processor.h index 507d05034..914926a66 100644 --- a/lib/include/srsran/system/sys_metrics_processor.h +++ b/lib/include/srsran/system/sys_metrics_processor.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/test/ue_test_interfaces.h b/lib/include/srsran/test/ue_test_interfaces.h index 6c50d3d0b..37bbe7795 100644 --- a/lib/include/srsran/test/ue_test_interfaces.h +++ b/lib/include/srsran/test/ue_test_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -36,6 +36,9 @@ public: stack_test_dummy() {} srsran::tti_point get_current_tti() override { return srsran::tti_point{tti % 10240}; } + void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final{}; + void remove_eps_bearer(uint8_t eps_bearer_id) final{}; + void reset_eps_bearers() final{}; // Testing utility functions void run_tti() @@ -59,7 +62,7 @@ class rlc_dummy_interface : public rlc_interface_mac public: bool has_data_locked(const uint32_t lcid) override { return false; } uint32_t get_buffer_state(const uint32_t lcid) override { return 0; } - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override { return 0; } + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override { return 0; } void write_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override {} void write_pdu_bcch_bch(srsran::unique_byte_buffer_t payload) override {} void write_pdu_bcch_dlsch(uint8_t* payload, uint32_t nof_bytes) override {} @@ -82,11 +85,9 @@ class phy_dummy_interface : public phy_interface_rrc_lte void meas_stop() override {} /* Cell search and selection procedures */ - bool cell_search() override { return true; } + bool cell_search(int earfcn) override { return true; } bool cell_select(phy_cell_t cell) override { return true; } bool cell_is_camping() override { return false; } - - void enable_pregen_signals(bool enable) override {} }; } // namespace srsue diff --git a/lib/include/srsran/upper/byte_buffer_queue.h b/lib/include/srsran/upper/byte_buffer_queue.h index a7d5179aa..8f59abea8 100644 --- a/lib/include/srsran/upper/byte_buffer_queue.h +++ b/lib/include/srsran/upper/byte_buffer_queue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -89,33 +89,37 @@ public: private: struct push_callback { - explicit push_callback(uint32_t& unread_bytes_, uint32_t& n_sdus_) : unread_bytes(&unread_bytes_), n_sdus(&n_sdus_) + explicit push_callback(std::atomic& unread_bytes_, std::atomic& n_sdus_) : + unread_bytes(unread_bytes_), n_sdus(n_sdus_) {} void operator()(const unique_byte_buffer_t& msg) { - *unread_bytes += msg->N_bytes; - (*n_sdus)++; + unread_bytes.fetch_add(msg->N_bytes, std::memory_order_relaxed); + n_sdus.fetch_add(1, std::memory_order_relaxed); } - uint32_t* unread_bytes; - uint32_t* n_sdus; + std::atomic& unread_bytes; + std::atomic& n_sdus; }; struct pop_callback { - explicit pop_callback(uint32_t& unread_bytes_, uint32_t& n_sdus_) : unread_bytes(&unread_bytes_), n_sdus(&n_sdus_) + explicit pop_callback(std::atomic& unread_bytes_, std::atomic& n_sdus_) : + unread_bytes(unread_bytes_), n_sdus(n_sdus_) {} void operator()(const unique_byte_buffer_t& msg) { if (msg == nullptr) { return; } - *unread_bytes -= std::min(msg->N_bytes, *unread_bytes); - *n_sdus = std::max(0, (int32_t)(*n_sdus) - 1); + // non-atomic update of both state variables + unread_bytes.fetch_sub(std::min(msg->N_bytes, unread_bytes.load(std::memory_order_relaxed)), + std::memory_order_relaxed); + n_sdus.store(std::max(0, (int32_t)(n_sdus.load(std::memory_order_relaxed)) - 1), std::memory_order_relaxed); } - uint32_t* unread_bytes; - uint32_t* n_sdus; + std::atomic& unread_bytes; + std::atomic& n_sdus; }; - uint32_t unread_bytes = 0; - uint32_t n_sdus = 0; + std::atomic unread_bytes = {0}; + std::atomic n_sdus = {0}; public: dyn_blocking_queue queue; diff --git a/lib/include/srsran/upper/gtpu.h b/lib/include/srsran/upper/gtpu.h index 00183e21f..c1c0df566 100644 --- a/lib/include/srsran/upper/gtpu.h +++ b/lib/include/srsran/upper/gtpu.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -67,8 +67,11 @@ namespace srsran { #define GTPU_MSG_END_MARKER 254 #define GTPU_MSG_DATA_PDU 255 +#define GTPU_EXT_NO_MORE_EXTENSION_HEADERS 0x00 #define GTPU_EXT_HEADER_PDCP_PDU_NUMBER 0b11000000 +#define GTPU_EXT_HEADER_PDU_SESSION_CONTAINER 0x85 +#define GTPU_EXT_HEADER_PDU_SESSION_CONTAINER_LEN 4 struct gtpu_header_t { uint8_t flags = 0; uint8_t message_type = 0; diff --git a/lib/include/srsran/upper/ipv6.h b/lib/include/srsran/upper/ipv6.h index 3fa39b5eb..083ea640d 100644 --- a/lib/include/srsran/upper/ipv6.h +++ b/lib/include/srsran/upper/ipv6.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/upper/pdcp.h b/lib/include/srsran/upper/pdcp.h index 88a703f19..c90145fcd 100644 --- a/lib/include/srsran/upper/pdcp.h +++ b/lib/include/srsran/upper/pdcp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,24 +35,21 @@ class pdcp : public srsue::pdcp_interface_rlc, public srsue::pdcp_interface_rrc public: pdcp(srsran::task_sched_handle task_sched_, const char* logname); virtual ~pdcp(); - void init(srsue::rlc_interface_pdcp* rlc_, - srsue::rrc_interface_pdcp* rrc_, - srsue::rrc_interface_pdcp* rrc_nr_, - srsue::gw_interface_pdcp* gw_); void init(srsue::rlc_interface_pdcp* rlc_, srsue::rrc_interface_pdcp* rrc_, srsue::gw_interface_pdcp* gw_); void stop(); - // GW interface + // Stack interface bool is_lcid_enabled(uint32_t lcid); // RRC interface void reestablish() override; void reestablish(uint32_t lcid) override; void reset() override; + void set_enabled(uint32_t lcid, bool enabled) override; void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, int sn = -1) override; void write_sdu_mch(uint32_t lcid, unique_byte_buffer_t sdu); - void add_bearer(uint32_t lcid, pdcp_config_t cnfg) override; - void add_bearer_mrb(uint32_t lcid, pdcp_config_t cnfg); + int add_bearer(uint32_t lcid, const pdcp_config_t& cnfg) override; + void add_bearer_mrb(uint32_t lcid, const pdcp_config_t& cnfg); void del_bearer(uint32_t lcid) override; void change_lcid(uint32_t old_lcid, uint32_t new_lcid) override; void config_security(uint32_t lcid, const as_security_config_t& sec_cfg) override; @@ -84,7 +81,6 @@ public: private: srsue::rlc_interface_pdcp* rlc = nullptr; srsue::rrc_interface_pdcp* rrc = nullptr; - srsue::rrc_interface_pdcp* rrc_nr = nullptr; srsue::gw_interface_pdcp* gw = nullptr; srsran::task_sched_handle task_sched; srslog::basic_logger& logger; @@ -104,4 +100,5 @@ private: }; } // namespace srsran + #endif // SRSRAN_PDCP_H diff --git a/lib/include/srsran/upper/pdcp_entity_base.h b/lib/include/srsran/upper/pdcp_entity_base.h index 46cd20f75..10f173696 100644 --- a/lib/include/srsran/upper/pdcp_entity_base.h +++ b/lib/include/srsran/upper/pdcp_entity_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -69,6 +69,7 @@ public: virtual void reset() = 0; virtual void reestablish() = 0; + void set_enabled(bool enabled) { active = enabled; } bool is_active() { return active; } bool is_srb() { return cfg.rb_type == PDCP_RB_IS_SRB; } bool is_drb() { return cfg.rb_type == PDCP_RB_IS_DRB; } @@ -84,7 +85,7 @@ public: } else { integrity_direction = direction; } - logger.debug("LCID=%d, integrity=%s", lcid, srsran_direction_text[integrity_direction]); + logger.debug("Enabled integrity. LCID=%d, integrity=%s", lcid, srsran_direction_text[integrity_direction]); } void enable_encryption(srsran_direction_t direction = DIRECTION_TXRX) @@ -97,7 +98,7 @@ public: } else { encryption_direction = direction; } - logger.debug("LCID=%d encryption=%s", lcid, srsran_direction_text[integrity_direction]); + logger.debug("Enabled encryption. LCID=%d, encryption=%s", lcid, srsran_direction_text[integrity_direction]); } void enable_security_timed(srsran_direction_t direction, uint32_t sn) @@ -141,6 +142,8 @@ public: virtual pdcp_bearer_metrics_t get_metrics() = 0; virtual void reset_metrics() = 0; + const char* get_rb_name() const { return rb_name.c_str(); } + protected: srslog::basic_logger& logger; srsran::task_sched_handle task_sched; @@ -162,6 +165,7 @@ protected: pdcp_discard_timer_t::infinity, false, srsran_rat_t::lte}; + std::string rb_name; srsran::as_security_config_t sec_cfg = {}; diff --git a/lib/include/srsran/upper/pdcp_entity_lte.h b/lib/include/srsran/upper/pdcp_entity_lte.h index 4cb0a6d1d..f35e62879 100644 --- a/lib/include/srsran/upper/pdcp_entity_lte.h +++ b/lib/include/srsran/upper/pdcp_entity_lte.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -42,7 +42,7 @@ namespace srsran { class undelivered_sdus_queue { public: - explicit undelivered_sdus_queue(srsran::task_sched_handle task_sched); + explicit undelivered_sdus_queue(srsran::task_sched_handle task_sched, uint32_t sn_mod); bool empty() const { return count == 0; } bool is_full() const { return count >= capacity; } @@ -82,8 +82,9 @@ public: private: const static uint32_t capacity = 4096; const static uint32_t invalid_sn = -1; + uint32_t sn_mod = 0; - static uint32_t increment_sn(uint32_t sn) { return (sn + 1) % capacity; } + uint32_t increment_sn(uint32_t sn) { return (sn + 1) % sn_mod; } struct sdu_data { srsran::unique_byte_buffer_t sdu; diff --git a/lib/include/srsran/upper/pdcp_entity_nr.h b/lib/include/srsran/upper/pdcp_entity_nr.h index d8de91ce3..f82496743 100644 --- a/lib/include/srsran/upper/pdcp_entity_nr.h +++ b/lib/include/srsran/upper/pdcp_entity_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,7 +35,6 @@ #include namespace srsran { - /**************************************************************************** * NR PDCP Entity * PDCP entity for 5G NR @@ -49,7 +48,7 @@ public: srsran::task_sched_handle task_sched_, srslog::basic_logger& logger, uint32_t lcid); - ~pdcp_entity_nr() final; + ~pdcp_entity_nr() final = default; bool configure(const pdcp_config_t& cnfg_) final; void reset() final; void reestablish() final; @@ -62,12 +61,6 @@ public: void notify_delivery(const pdcp_sn_vector_t& pdcp_sns) final; void notify_failure(const pdcp_sn_vector_t& pdcp_sns) final; - // State variable setters (should be used only for testing) - void set_tx_next(uint32_t tx_next_) { tx_next = tx_next_; } - void set_rx_next(uint32_t rx_next_) { rx_next = rx_next_; } - void set_rx_deliv(uint32_t rx_deliv_) { rx_deliv = rx_deliv_; } - void set_rx_reord(uint32_t rx_reord_) { rx_reord = rx_reord_; } - void get_bearer_state(pdcp_lte_state_t* state) override; void set_bearer_state(const pdcp_lte_state_t& state, bool set_fmc) override; @@ -79,6 +72,17 @@ public: // State variable getters (useful for testing) uint32_t nof_discard_timers() { return discard_timers_map.size(); } + bool is_reordering_timer_running() { return reordering_timer.is_running(); } + + // State variable setters (should be used only for testing) + void set_tx_next(uint32_t tx_next_) { tx_next = tx_next_; } + void set_rx_next(uint32_t rx_next_) { rx_next = rx_next_; } + void set_rx_deliv(uint32_t rx_deliv_) { rx_deliv = rx_deliv_; } + void set_rx_reord(uint32_t rx_reord_) { rx_reord = rx_reord_; } + uint32_t get_tx_next() const { return tx_next; } + uint32_t get_rx_next() const { return rx_next; } + uint32_t get_rx_deliv() const { return rx_deliv; } + uint32_t get_rx_reord() const { return rx_reord; } private: srsue::rlc_interface_pdcp* rlc = nullptr; @@ -113,6 +117,11 @@ private: // COUNT overflow protection bool tx_overflow = false; bool rx_overflow = false; + + enum class rlc_mode_t { + UM, + AM, + } rlc_mode; }; /* diff --git a/lib/include/srsran/upper/pdcp_metrics.h b/lib/include/srsran/upper/pdcp_metrics.h index af6a5ff0f..2a6698ebb 100644 --- a/lib/include/srsran/upper/pdcp_metrics.h +++ b/lib/include/srsran/upper/pdcp_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/include/srsran/upper/rlc_am_lte.h b/lib/include/srsran/upper/rlc_am_lte.h deleted file mode 100644 index 02611eeae..000000000 --- a/lib/include/srsran/upper/rlc_am_lte.h +++ /dev/null @@ -1,597 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_RLC_AM_LTE_H -#define SRSRAN_RLC_AM_LTE_H - -#include "srsran/adt/accumulators.h" -#include "srsran/adt/circular_array.h" -#include "srsran/adt/circular_map.h" -#include "srsran/adt/intrusive_list.h" -#include "srsran/common/buffer_pool.h" -#include "srsran/common/common.h" -#include "srsran/common/srsran_assert.h" -#include "srsran/common/task_scheduler.h" -#include "srsran/common/timeout.h" -#include "srsran/interfaces/pdcp_interface_types.h" -#include "srsran/upper/byte_buffer_queue.h" -#include "srsran/upper/rlc_am_base.h" -#include "srsran/upper/rlc_common.h" -#include -#include -#include - -namespace srsran { - -#undef RLC_AM_BUFFER_DEBUG - -class rlc_amd_tx_pdu; -class pdcp_pdu_info; - -/// Pool that manages the allocation of RLC AM PDU Segments to RLC PDUs and tracking of segments ACK state -struct rlc_am_pdu_segment_pool { - const static size_t MAX_POOL_SIZE = 16384; - using rlc_list_tag = default_intrusive_tag; - struct free_list_tag {}; - - /// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state - struct segment_resource : public intrusive_forward_list_element, - public intrusive_forward_list_element, - public intrusive_double_linked_list_element<> { - const static uint32_t invalid_rlc_sn = std::numeric_limits::max(); - const static uint32_t invalid_pdcp_sn = std::numeric_limits::max() - 1; // -1 for Status Report - - int id() const; - void release_pdcp_sn(); - void release_rlc_sn(); - uint32_t rlc_sn() const { return rlc_sn_; } - uint32_t pdcp_sn() const { return pdcp_sn_; } - bool empty() const { return rlc_sn_ == invalid_rlc_sn and pdcp_sn_ == invalid_pdcp_sn; } - - private: - friend struct rlc_am_pdu_segment_pool; - uint32_t rlc_sn_ = invalid_rlc_sn; - uint32_t pdcp_sn_ = invalid_pdcp_sn; - rlc_am_pdu_segment_pool* parent_pool = nullptr; - }; - - rlc_am_pdu_segment_pool(); - rlc_am_pdu_segment_pool(const rlc_am_pdu_segment_pool&) = delete; - rlc_am_pdu_segment_pool(rlc_am_pdu_segment_pool&&) = delete; - rlc_am_pdu_segment_pool& operator=(const rlc_am_pdu_segment_pool&) = delete; - rlc_am_pdu_segment_pool& operator=(rlc_am_pdu_segment_pool&&) = delete; - bool has_segments() const { return not free_list.empty(); } - bool make_segment(rlc_amd_tx_pdu& rlc_list, pdcp_pdu_info& pdcp_info); - -private: - intrusive_forward_list free_list; - std::array segments; -}; - -/// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state -using rlc_am_pdu_segment = rlc_am_pdu_segment_pool::segment_resource; - -struct rlc_amd_rx_pdu { - rlc_amd_pdu_header_t header; - unique_byte_buffer_t buf; - uint32_t rlc_sn; - - rlc_amd_rx_pdu() = default; - explicit rlc_amd_rx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} -}; - -struct rlc_amd_rx_pdu_segments_t { - std::list segments; -}; - -/// Class that contains the parameters and state (e.g. segments) of a RLC PDU -class rlc_amd_tx_pdu -{ - using list_type = intrusive_forward_list; - const static uint32_t invalid_rlc_sn = std::numeric_limits::max(); - - list_type list; - -public: - using iterator = typename list_type::iterator; - using const_iterator = typename list_type::const_iterator; - - const uint32_t rlc_sn = invalid_rlc_sn; - uint32_t retx_count = 0; - rlc_amd_pdu_header_t header; - unique_byte_buffer_t buf; - - explicit rlc_amd_tx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {} - rlc_amd_tx_pdu(const rlc_amd_tx_pdu&) = delete; - rlc_amd_tx_pdu(rlc_amd_tx_pdu&& other) noexcept = default; - rlc_amd_tx_pdu& operator=(const rlc_amd_tx_pdu& other) = delete; - rlc_amd_tx_pdu& operator=(rlc_amd_tx_pdu&& other) = delete; - ~rlc_amd_tx_pdu(); - - // Segment List Interface - void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); } - const_iterator begin() const { return list.begin(); } - const_iterator end() const { return list.end(); } - iterator begin() { return list.begin(); } - iterator end() { return list.end(); } -}; - -struct rlc_amd_retx_t { - uint32_t sn; - bool is_segment; - uint32_t so_start; - uint32_t so_end; -}; - -struct rlc_sn_info_t { - uint32_t sn; - bool is_acked; -}; - -/// Class that contains the parameters and state (e.g. unACKed segments) of a PDCP PDU -class pdcp_pdu_info -{ - using list_type = intrusive_double_linked_list; - - list_type list; // List of unACKed RLC PDUs that contain segments that belong to the PDCP PDU. - -public: - const static uint32_t status_report_sn = std::numeric_limits::max(); - const static uint32_t invalid_pdcp_sn = std::numeric_limits::max() - 1; - - using iterator = typename list_type::iterator; - using const_iterator = typename list_type::const_iterator; - - // Copy is forbidden to avoid multiple PDCP SN references to the same segment - pdcp_pdu_info() = default; - pdcp_pdu_info(pdcp_pdu_info&&) noexcept = default; - pdcp_pdu_info(const pdcp_pdu_info&) noexcept = delete; - pdcp_pdu_info& operator=(const pdcp_pdu_info&) noexcept = delete; - pdcp_pdu_info& operator=(pdcp_pdu_info&&) noexcept = default; - ~pdcp_pdu_info() { clear(); } - - uint32_t sn = invalid_pdcp_sn; - bool fully_txed = false; // Boolean indicating if the SDU is fully transmitted. - - bool fully_acked() const { return fully_txed and list.empty(); } - bool valid() const { return sn != invalid_pdcp_sn; } - - // Interface for list of unACKed RLC segments of the PDCP PDU - void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); } - void ack_segment(rlc_am_pdu_segment& segment); - void clear() - { - sn = invalid_pdcp_sn; - fully_txed = false; - while (not list.empty()) { - ack_segment(list.front()); - } - } - const_iterator begin() const { return list.begin(); } - const_iterator end() const { return list.end(); } -}; - -template -struct rlc_ringbuffer_t { - T& add_pdu(size_t sn) - { - srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn); - window.overwrite(sn, T(sn)); - return window[sn]; - } - void remove_pdu(size_t sn) - { - srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn); - window.erase(sn); - } - T& operator[](size_t sn) { return window[sn]; } - size_t size() const { return window.size(); } - bool empty() const { return window.empty(); } - void clear() { window.clear(); } - - bool has_sn(uint32_t sn) const { return window.contains(sn); } - - // Return the sum data bytes of all active PDUs (check PDU is non-null) - uint32_t get_buffered_bytes() - { - uint32_t buff_size = 0; - for (const auto& pdu : window) { - if (pdu.second.buf != nullptr) { - buff_size += pdu.second.buf->N_bytes; - } - } - return buff_size; - } - -private: - srsran::static_circular_map window; -}; - -struct buffered_pdcp_pdu_list { -public: - explicit buffered_pdcp_pdu_list(); - void clear(); - - void add_pdcp_sdu(uint32_t sn) - { - srsran_expect(sn <= max_pdcp_sn or sn == status_report_sn, "Invalid PDCP SN=%d", sn); - srsran_assert(not has_pdcp_sn(sn), "Cannot re-add same PDCP SN twice"); - pdcp_pdu_info& pdu = get_pdu_(sn); - if (pdu.valid()) { - pdu.clear(); - count--; - } - pdu.sn = sn; - count++; - } - void clear_pdcp_sdu(uint32_t sn) - { - pdcp_pdu_info& pdu = get_pdu_(sn); - if (not pdu.valid()) { - return; - } - pdu.clear(); - count--; - } - - pdcp_pdu_info& operator[](uint32_t sn) - { - srsran_expect(has_pdcp_sn(sn), "Invalid access to non-existent PDCP SN=%d", sn); - return get_pdu_(sn); - } - bool has_pdcp_sn(uint32_t pdcp_sn) const - { - srsran_expect(pdcp_sn <= max_pdcp_sn or pdcp_sn == status_report_sn, "Invalid PDCP SN=%d", pdcp_sn); - return get_pdu_(pdcp_sn).sn == pdcp_sn; - } - uint32_t nof_sdus() const { return count; } - -private: - const static size_t max_pdcp_sn = 262143u; - const static size_t buffer_size = 4096u; - const static uint32_t status_report_sn = pdcp_pdu_info::status_report_sn; - - pdcp_pdu_info& get_pdu_(uint32_t sn) - { - return (sn == status_report_sn) ? status_report_pdu : buffered_pdus[static_cast(sn % buffer_size)]; - } - const pdcp_pdu_info& get_pdu_(uint32_t sn) const - { - return (sn == status_report_sn) ? status_report_pdu : buffered_pdus[static_cast(sn % buffer_size)]; - } - - // size equal to buffer_size - std::vector buffered_pdus; - pdcp_pdu_info status_report_pdu; - uint32_t count = 0; -}; - -class pdu_retx_queue -{ -public: - rlc_amd_retx_t& push() - { - assert(not full()); - rlc_amd_retx_t& p = buffer[wpos]; - wpos = (wpos + 1) % RLC_AM_WINDOW_SIZE; - return p; - } - - void pop() { rpos = (rpos + 1) % RLC_AM_WINDOW_SIZE; } - - rlc_amd_retx_t& front() - { - assert(not empty()); - return buffer[rpos]; - } - - void clear() - { - wpos = 0; - rpos = 0; - } - - bool has_sn(uint32_t sn) const - { - for (size_t i = rpos; i != wpos; i = (i + 1) % RLC_AM_WINDOW_SIZE) { - if (buffer[i].sn == sn) { - return true; - } - } - return false; - } - - size_t size() const { return (wpos >= rpos) ? wpos - rpos : RLC_AM_WINDOW_SIZE + wpos - rpos; } - bool empty() const { return wpos == rpos; } - bool full() const { return size() == RLC_AM_WINDOW_SIZE - 1; } - -private: - std::array buffer; - size_t wpos = 0; - size_t rpos = 0; -}; - -class rlc_am_lte : public rlc_common -{ -public: - rlc_am_lte(srslog::basic_logger& logger, - uint32_t lcid_, - srsue::pdcp_interface_rlc* pdcp_, - srsue::rrc_interface_rlc* rrc_, - srsran::timer_handler* timers_); - bool configure(const rlc_config_t& cfg_); - void reestablish(); - void stop(); - - void empty_queue(); - - rlc_mode_t get_mode(); - uint32_t get_bearer(); - - // PDCP interface - void write_sdu(unique_byte_buffer_t sdu); - void discard_sdu(uint32_t pdcp_sn); - bool sdu_queue_is_full(); - - // MAC interface - bool has_data(); - uint32_t get_buffer_state(); - int read_pdu(uint8_t* payload, uint32_t nof_bytes); - void write_pdu(uint8_t* payload, uint32_t nof_bytes); - - rlc_bearer_metrics_t get_metrics(); - void reset_metrics(); - - void set_bsr_callback(bsr_callback_t callback); - -private: - // Transmitter sub-class - class rlc_am_lte_tx : public timer_callback - { - public: - rlc_am_lte_tx(rlc_am_lte* parent_); - ~rlc_am_lte_tx(); - - bool configure(const rlc_config_t& cfg_); - - void empty_queue(); - void reestablish(); - void stop(); - - int write_sdu(unique_byte_buffer_t sdu); - int read_pdu(uint8_t* payload, uint32_t nof_bytes); - void discard_sdu(uint32_t discard_sn); - bool sdu_queue_is_full(); - - bool has_data(); - uint32_t get_buffer_state(); - - // Timeout callback interface - void timer_expired(uint32_t timeout_id); - - // Interface for Rx subclass - void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes); - - void set_bsr_callback(bsr_callback_t callback); - - private: - int build_status_pdu(uint8_t* payload, uint32_t nof_bytes); - int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes); - int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx); - int build_data_pdu(uint8_t* payload, uint32_t nof_bytes); - void update_notification_ack_info(uint32_t rlc_sn); - - void debug_state(); - - int required_buffer_size(rlc_amd_retx_t retx); - void retransmit_pdu(); - - // Helpers - bool poll_required(); - bool do_status(); - void check_sn_reached_max_retx(uint32_t sn); - - rlc_am_lte* parent = nullptr; - byte_buffer_pool* pool = nullptr; - srslog::basic_logger& logger; - rlc_am_pdu_segment_pool segment_pool; - - /**************************************************************************** - * Configurable parameters - * Ref: 3GPP TS 36.322 v10.0.0 Section 7 - ***************************************************************************/ - - rlc_am_config_t cfg = {}; - - // TX SDU buffers - byte_buffer_queue tx_sdu_queue; - unique_byte_buffer_t tx_sdu; - - bool tx_enabled = false; - - /**************************************************************************** - * State variables and counters - * Ref: 3GPP TS 36.322 v10.0.0 Section 7 - ***************************************************************************/ - - // Tx state variables - uint32_t vt_a = 0; // ACK state. SN of next PDU in sequence to be ACKed. Low edge of tx window. - uint32_t vt_ms = RLC_AM_WINDOW_SIZE; // Max send state. High edge of tx window. vt_a + window_size. - uint32_t vt_s = 0; // Send state. SN to be assigned for next PDU. - uint32_t poll_sn = 0; // Poll send state. SN of most recent PDU txed with poll bit set. - - // Tx counters - uint32_t pdu_without_poll = 0; - uint32_t byte_without_poll = 0; - - rlc_status_pdu_t tx_status; - - /**************************************************************************** - * Timers - * Ref: 3GPP TS 36.322 v10.0.0 Section 7 - ***************************************************************************/ - - srsran::timer_handler::unique_timer poll_retx_timer; - srsran::timer_handler::unique_timer status_prohibit_timer; - - // SDU info for PDCP notifications - buffered_pdcp_pdu_list undelivered_sdu_info_queue; - - // Callback function for buffer status report - bsr_callback_t bsr_callback; - - // Tx windows - rlc_ringbuffer_t tx_window; - pdu_retx_queue retx_queue; - pdcp_sn_vector_t notify_info_vec; - - // Mutexes - std::mutex mutex; - - // default to RLC SDU queue length - const uint32_t MAX_SDUS_PER_RLC_PDU = RLC_TX_QUEUE_LEN; - }; - - // Receiver sub-class - class rlc_am_lte_rx : public timer_callback - { - public: - rlc_am_lte_rx(rlc_am_lte* parent_); - ~rlc_am_lte_rx(); - - bool configure(rlc_am_config_t cfg_); - void reestablish(); - void stop(); - - void write_pdu(uint8_t* payload, uint32_t nof_bytes); - - uint32_t get_rx_buffered_bytes(); // returns sum of PDUs in rx_window - uint32_t get_sdu_rx_latency_ms(); - - // Timeout callback interface - void timer_expired(uint32_t timeout_id); - - // Functions needed by Tx subclass to query rx state - int get_status_pdu_length(); - int get_status_pdu(rlc_status_pdu_t* status, const uint32_t nof_bytes); - bool get_do_status(); - void reset_status(); // called when status PDU has been sent - - private: - void handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header); - void handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header); - void reassemble_rx_sdus(); - bool inside_rx_window(const int16_t sn); - void debug_state(); - void print_rx_segments(); - bool add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment); - - rlc_am_lte* parent = nullptr; - byte_buffer_pool* pool = nullptr; - srslog::basic_logger& logger; - - /**************************************************************************** - * Configurable parameters - * Ref: 3GPP TS 36.322 v10.0.0 Section 7 - ***************************************************************************/ - rlc_am_config_t cfg = {}; - - // RX SDU buffers - unique_byte_buffer_t rx_sdu; - - /**************************************************************************** - * State variables and counters - * Ref: 3GPP TS 36.322 v10.0.0 Section 7 - ***************************************************************************/ - - // Rx state variables - uint32_t vr_r = 0; // Receive state. SN following last in-sequence received PDU. Low edge of rx window - uint32_t vr_mr = RLC_AM_WINDOW_SIZE; // Max acceptable receive state. High edge of rx window. vr_r + window size. - uint32_t vr_x = 0; // t_reordering state. SN following PDU which triggered t_reordering. - uint32_t vr_ms = 0; // Max status tx state. Highest possible value of SN for ACK_SN in status PDU. - uint32_t vr_h = 0; // Highest rx state. SN following PDU with highest SN among rxed PDUs. - - // Mutex to protect members - std::mutex mutex; - - // Rx windows - rlc_ringbuffer_t rx_window; - std::map rx_segments; - - bool poll_received = false; - bool do_status = false; - - /**************************************************************************** - * Timers - * Ref: 3GPP TS 36.322 v10.0.0 Section 7 - ***************************************************************************/ - - srsran::timer_handler::unique_timer reordering_timer; - - srsran::rolling_average sdu_rx_latency_ms; - }; - - // Common variables needed/provided by parent class - srsue::rrc_interface_rlc* rrc = nullptr; - srslog::basic_logger& logger; - srsue::pdcp_interface_rlc* pdcp = nullptr; - srsran::timer_handler* timers = nullptr; - uint32_t lcid = 0; - rlc_config_t cfg = {}; - std::string rb_name; - - static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested - - // Rx and Tx objects - rlc_am_lte_tx tx; - rlc_am_lte_rx rx; - - rlc_bearer_metrics_t metrics = {}; -}; - -/**************************************************************************** - * Header pack/unpack helper functions - * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 - ***************************************************************************/ -void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header); -void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header); -void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu); -void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload); -void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status); -void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status); -void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu); -int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload); - -uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header); -uint32_t rlc_am_packed_length(rlc_status_pdu_t* status); -uint32_t rlc_am_packed_length(rlc_amd_retx_t retx); -bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status); -bool rlc_am_is_pdu_segment(uint8_t* payload); -std::string rlc_am_undelivered_sdu_info_to_string(const std::map& info_queue); -void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header); -bool rlc_am_start_aligned(const uint8_t fi); -bool rlc_am_end_aligned(const uint8_t fi); -bool rlc_am_is_unaligned(const uint8_t fi); -bool rlc_am_not_start_aligned(const uint8_t fi); - -} // namespace srsran - -#endif // SRSRAN_RLC_AM_LTE_H diff --git a/lib/include/srsran/upper/rlc_am_nr.h b/lib/include/srsran/upper/rlc_am_nr.h deleted file mode 100644 index 996e18b66..000000000 --- a/lib/include/srsran/upper/rlc_am_nr.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_RLC_AM_NR_H -#define SRSRAN_RLC_AM_NR_H - -#include "srsran/common/buffer_pool.h" -#include "srsran/common/common.h" -#include "srsran/upper/byte_buffer_queue.h" -#include "srsran/upper/rlc_am_base.h" -#include -#include -#include -#include - -namespace srsran { - -typedef struct { - rlc_am_nr_pdu_header_t header; - unique_byte_buffer_t buf; -} rlc_amd_pdu_nr_t; - -///< add class here - -/**************************************************************************** - * Header pack/unpack helper functions for NR - * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.3 - ***************************************************************************/ -uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_pdu_header_t* header); - -uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload, - const uint32_t nof_bytes, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_pdu_header_t* header); - -uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu); - -uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header); - -uint32_t -rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status); - -uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, - const uint32_t nof_bytes, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_status_pdu_t* status); - -int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, - const rlc_am_nr_sn_size_t sn_size, - byte_buffer_t* pdu); - -} // namespace srsran - -#endif // SRSRAN_RLC_AM_NR_H diff --git a/lib/include/srsran/version.h.in b/lib/include/srsran/version.h.in index dca66c6cc..278dc0c54 100644 --- a/lib/include/srsran/version.h.in +++ b/lib/include/srsran/version.h.in @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/CMakeLists.txt b/lib/src/CMakeLists.txt index 5c05359ac..e139062c1 100644 --- a/lib/src/CMakeLists.txt +++ b/lib/src/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -23,6 +23,9 @@ add_subdirectory(common) add_subdirectory(mac) add_subdirectory(phy) add_subdirectory(radio) +add_subdirectory(rlc) +add_subdirectory(pdcp) +add_subdirectory(gtpu) add_subdirectory(srslog) +add_subdirectory(support) add_subdirectory(system) -add_subdirectory(upper) diff --git a/lib/src/asn1/CMakeLists.txt b/lib/src/asn1/CMakeLists.txt index f8371811a..d38cc3246 100644 --- a/lib/src/asn1/CMakeLists.txt +++ b/lib/src/asn1/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -28,7 +28,7 @@ add_library(srsran_asn1 STATIC # ASN1 utils add_library(asn1_utils STATIC asn1_utils.cc) target_link_libraries(asn1_utils srsran_common) -INSTALL(TARGETS asn1_utils DESTINATION ${LIBRARY_DIR}) +install(TARGETS asn1_utils DESTINATION ${LIBRARY_DIR} OPTIONAL) # RRC ASN1 lib add_library(rrc_asn1 STATIC @@ -54,24 +54,29 @@ add_library(rrc_asn1 STATIC # Compile RRC ASN1 optimized for size target_compile_options(rrc_asn1 PRIVATE "-Os") target_link_libraries(rrc_asn1 asn1_utils srsran_common) -INSTALL(TARGETS rrc_asn1 DESTINATION ${LIBRARY_DIR}) +install(TARGETS rrc_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL) # S1AP ASN1 lib add_library(s1ap_asn1 STATIC s1ap.cc s1ap_utils.cc) target_compile_options(s1ap_asn1 PRIVATE "-Os") target_link_libraries(s1ap_asn1 asn1_utils srsran_common) -INSTALL(TARGETS s1ap_asn1 DESTINATION ${LIBRARY_DIR}) +install(TARGETS s1ap_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL) # RRC NR ASN1 add_library(rrc_nr_asn1 STATIC rrc_nr.cc rrc_nr_utils.cc) target_compile_options(rrc_nr_asn1 PRIVATE "-Os") target_link_libraries(rrc_nr_asn1 asn1_utils srsran_common) -INSTALL(TARGETS rrc_nr_asn1 DESTINATION ${LIBRARY_DIR}) +install(TARGETS rrc_nr_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL) # NGAP ASN1 add_library(ngap_nr_asn1 STATIC ngap.cc) target_compile_options(ngap_nr_asn1 PRIVATE "-Os") target_link_libraries(ngap_nr_asn1 asn1_utils srsran_common) -INSTALL(TARGETS ngap_nr_asn1 DESTINATION ${LIBRARY_DIR}) +install(TARGETS ngap_nr_asn1 DESTINATION ${LIBRARY_DIR} OPTIONAL) +# NAS 5G +add_library(nas_5g_msg STATIC nas_5g_msg.cc nas_5g_ies.cc nas_5g_utils.cc) +target_compile_options(nas_5g_msg PRIVATE "-Os") +target_link_libraries(nas_5g_msg asn1_utils srsran_common) +install(TARGETS nas_5g_msg DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/asn1/asn1_utils.cc b/lib/src/asn1/asn1_utils.cc index 85427f2a1..b40818df5 100644 --- a/lib/src/asn1/asn1_utils.cc +++ b/lib/src/asn1/asn1_utils.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -138,6 +138,11 @@ int bit_ref_impl::distance_bytes() const { return ((int)(ptr - start_ptr)) + ((offset) ? 1 : 0); } +template +int bit_ref_impl::distance_bytes_end() const +{ + return ((int)(max_ptr - ptr)) - ((offset) ? 1 : 0); +} SRSASN_CODE bit_ref::pack(uint64_t val, uint32_t n_bits) { @@ -148,7 +153,7 @@ SRSASN_CODE bit_ref::pack(uint64_t val, uint32_t n_bits) uint64_t mask; while (n_bits > 0) { if (ptr >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("pack: Buffer size limit was achieved"); return SRSASN_ERROR_ENCODE_FAIL; } mask = ((1ul << n_bits) - 1ul); @@ -180,7 +185,7 @@ SRSASN_CODE unpack_bits(T& val, Ptr& ptr, uint8_t& offset, const uint8_t* max_pt val = 0; while (n_bits > 0) { if (ptr >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("unpack_bits: Buffer size limit was achieved"); return SRSASN_ERROR_DECODE_FAIL; } if ((uint32_t)(8 - offset) > n_bits) { @@ -190,7 +195,7 @@ SRSASN_CODE unpack_bits(T& val, Ptr& ptr, uint8_t& offset, const uint8_t* max_pt n_bits = 0; } else { auto mask = static_cast((1u << (8u - offset)) - 1u); - val += ((uint32_t)((*ptr) & mask)) << (n_bits - 8 + offset); + val += static_cast((*ptr) & mask) << (n_bits - 8 + offset); n_bits -= 8 - offset; offset = 0; ptr++; @@ -241,15 +246,20 @@ SRSASN_CODE bit_ref_impl::unpack_bytes(uint8_t* buf, uint32_t n_bytes) if (n_bytes == 0) { return SRSASN_SUCCESS; } - if (ptr + n_bytes >= max_ptr) { - log_error("Buffer size limit was achieved"); - return SRSASN_ERROR_DECODE_FAIL; - } if (offset == 0) { // Aligned case + if (ptr + n_bytes > max_ptr) { + log_error("unpack_bytes (aligned): Buffer size limit was achieved"); + return SRSASN_ERROR_DECODE_FAIL; + } memcpy(buf, ptr, n_bytes); ptr += n_bytes; } else { + // Unaligned case + if (ptr + n_bytes >= max_ptr) { + log_error("unpack_bytes (unaligned): Buffer size limit was achieved"); + return SRSASN_ERROR_DECODE_FAIL; + } for (uint32_t i = 0; i < n_bytes; ++i) { HANDLE_CODE(unpack(buf[i], 8)); } @@ -263,7 +273,7 @@ SRSASN_CODE bit_ref_impl::align_bytes() if (offset == 0) return SRSASN_SUCCESS; if (ptr >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("align_bytes: Buffer size limit was achieved"); return SRSASN_ERROR_DECODE_FAIL; } offset = 0; @@ -278,8 +288,8 @@ SRSASN_CODE bit_ref_impl::advance_bits(uint32_t n_bits) uint32_t bytes_required = ceilf((offset + n_bits) / 8.0f); uint32_t bytes_offset = floorf((offset + n_bits) / 8.0f); - if (ptr + bytes_required >= max_ptr) { - log_error("Buffer size limit was achieved"); + if (ptr + bytes_required > max_ptr) { + log_error("advance_bytes: Buffer size limit was achieved"); return SRSASN_ERROR_DECODE_FAIL; } ptr += bytes_offset; @@ -305,7 +315,7 @@ SRSASN_CODE bit_ref::pack_bytes(const uint8_t* buf, uint32_t n_bytes) return SRSASN_SUCCESS; } if (ptr + n_bytes >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("pack_bytes: Buffer size limit was achieved"); return SRSASN_ERROR_ENCODE_FAIL; } if (offset == 0) { @@ -325,7 +335,7 @@ SRSASN_CODE bit_ref::align_bytes_zero() if (offset == 0) return SRSASN_SUCCESS; if (ptr >= max_ptr) { - log_error("Buffer size limit was achieved"); + log_error("align_bytes_zero: Buffer size limit was achieved"); return SRSASN_ERROR_ENCODE_FAIL; } auto mask = static_cast(256u - (1u << (8u - offset))); @@ -1411,10 +1421,15 @@ SRSASN_CODE ext_groups_unpacker_guard::unpack(cbit_ref& bref) Open Field *********************/ -varlength_field_pack_guard::varlength_field_pack_guard(bit_ref& bref, bool align_) +varlength_field_pack_guard::varlength_field_pack_guard(bit_ref& bref, bool align_) : + buffer_ptr(srsran::make_buffer_pool_obj()) { + if (buffer_ptr == nullptr) { + // failed to allocate from global byte buffer pool. Fallback to malloc + buffer_ptr = std::unique_ptr(new byte_array_t()); + } brefstart = bref; - bref = bit_ref(&buffer[0], sizeof(buffer)); + bref = bit_ref(buffer_ptr->data(), buffer_ptr->size()); bref_tracker = &bref; align = align_; } @@ -1422,16 +1437,15 @@ varlength_field_pack_guard::varlength_field_pack_guard(bit_ref& bref, bool align varlength_field_pack_guard::~varlength_field_pack_guard() { // fill the spare bits - const bit_ref bref0 = bit_ref(&buffer[0], sizeof(buffer)); - uint32_t leftover = 7 - ((bref_tracker->distance(bref0) - (uint32_t)1) % (uint32_t)8); + uint32_t leftover = 7 - ((bref_tracker->distance() - (uint32_t)1) % (uint32_t)8); bref_tracker->pack(0, leftover); // check how many bytes were written in total - uint32_t nof_bytes = bref_tracker->distance(bref0) / (uint32_t)8; - if (nof_bytes > sizeof(buffer)) { + uint32_t nof_bytes = bref_tracker->distance() / (uint32_t)8; + if (nof_bytes > buffer_ptr->size()) { log_error("The packed variable sized field is too long for the reserved buffer (%zd > %zd)", (size_t)nof_bytes, - sizeof(buffer)); + buffer_ptr->size()); } // go back in time to pack length @@ -1439,7 +1453,7 @@ varlength_field_pack_guard::~varlength_field_pack_guard() // pack encoded bytes for (uint32_t i = 0; i < nof_bytes; ++i) { - brefstart.pack(buffer[i], 8); + brefstart.pack((*buffer_ptr)[i], 8); } *bref_tracker = brefstart; } @@ -1551,4 +1565,49 @@ std::string json_writer::to_string() const return std::string(buffer.data(), buffer.size()); } +/************************ + General Layer Types +************************/ + +uint32_t detail::base_empty_obj_set::idx_to_id(uint32_t idx) +{ + asn1::log_error("object set is empty\n"); + return 0; +} +bool detail::base_empty_obj_set::is_id_valid(const uint32_t& id) +{ + asn1::log_error("object set is empty\n"); + return false; +} +crit_e detail::base_empty_obj_set::get_crit(const uint32_t& id) +{ + return {}; +} +presence_e detail::base_empty_obj_set::get_presence(const uint32_t& id) +{ + return {}; +} + +void detail::empty_obj_set_item_c::to_json(json_writer& j) const +{ + j.start_obj(); + j.end_obj(); +} +SRSASN_CODE detail::empty_obj_set_item_c::pack(bit_ref& bref) const +{ + varlength_field_pack_guard varlen_scope(bref, true); + return SRSASN_SUCCESS; +} +SRSASN_CODE detail::empty_obj_set_item_c::unpack(cbit_ref& bref) +{ + varlength_field_unpack_guard varlen_scope(bref, true); + return SRSASN_SUCCESS; +} + +const char* detail::empty_obj_set_item_c::types_opts::to_string() const +{ + log_error("The enum value=0 of type protocol_ies_empty_o::value_c::types is not valid."); + return ""; +} + } // namespace asn1 diff --git a/lib/src/asn1/gtpc.cc b/lib/src/asn1/gtpc.cc index da184536e..4bd6d3358 100644 --- a/lib/src/asn1/gtpc.cc +++ b/lib/src/asn1/gtpc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/liblte_mme.cc b/lib/src/asn1/liblte_mme.cc index 6fb1e41eb..48f3f9817 100644 --- a/lib/src/asn1/liblte_mme.cc +++ b/lib/src/asn1/liblte_mme.cc @@ -319,6 +319,7 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_mobile_id_ie(uint8** ie_ptr, LIBLTE_MME_MOBI { LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; uint8* id; + uint32* id32; uint32 length; uint32 i; bool odd = false; @@ -338,22 +339,35 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_mobile_id_ie(uint8** ie_ptr, LIBLTE_MME_MOBI } else if (LIBLTE_MME_MOBILE_ID_TYPE_IMEISV == mobile_id->type_of_id) { id = mobile_id->imeisv; odd = false; + } else if (LIBLTE_MME_MOBILE_ID_TYPE_TMSI == mobile_id->type_of_id) { + id32 = &mobile_id->tmsi; + odd = false; } else { // TODO: Not handling these IDs return (err); } - id[0] = **ie_ptr >> 4; - *ie_ptr += 1; - for (i = 0; i < 7; i++) { - id[i * 2 + 1] = (*ie_ptr)[i] & 0x0F; - id[i * 2 + 2] = (*ie_ptr)[i] >> 4; - } - if (odd) { - *ie_ptr += 7; + if (mobile_id->type_of_id != LIBLTE_MME_MOBILE_ID_TYPE_TMSI) { + id[0] = **ie_ptr >> 4; + *ie_ptr += 1; + for (i = 0; i < 7; i++) { + id[i * 2 + 1] = (*ie_ptr)[i] & 0x0F; + id[i * 2 + 2] = (*ie_ptr)[i] >> 4; + } + if (odd) { + *ie_ptr += 7; + } else { + id[i * 2 + 1] = (*ie_ptr)[i] & 0xF; + *ie_ptr += 8; + } } else { - id[i * 2 + 1] = (*ie_ptr)[i] & 0xF; - *ie_ptr += 8; + *ie_ptr += 1; + uint32 tmsi = 0; + for (i = 0; i < 4; i++) { + tmsi += ((*ie_ptr)[i] & 0xFF) << ((3 - i) * 8); + } + *id32 = tmsi; + *ie_ptr += 4; } err = LIBLTE_SUCCESS; @@ -1380,12 +1394,13 @@ liblte_mme_unpack_eps_network_feature_support_ie(uint8** ie_ptr, LIBLTE_MME_EPS_ LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; if (ie_ptr != NULL && eps_nfs != NULL) { + int ie_len = *ie_ptr[0]; eps_nfs->esrps = ((*ie_ptr)[1] >> 5) & 0x01; eps_nfs->cs_lcs = (LIBLTE_MME_CS_LCS_ENUM)(((*ie_ptr)[1] >> 3) & 0x03); eps_nfs->epc_lcs = ((*ie_ptr)[1] >> 2) & 0x01; eps_nfs->emc_bs = ((*ie_ptr)[1] >> 1) & 0x01; eps_nfs->ims_vops = (*ie_ptr)[1] & 0x01; - *ie_ptr += 2; + *ie_ptr += (ie_len + 1); err = LIBLTE_SUCCESS; } @@ -2092,10 +2107,8 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_network_name_ie(uint8** ie_ptr, LIBLTE_MME_N if (tmp_char == 0x0A || tmp_char == 0x0D || (tmp_char >= 0x20 && tmp_char <= 0x3F) || (tmp_char >= 0x41 && tmp_char <= 0x5A) || (tmp_char >= 0x61 && tmp_char <= 0x7A)) { - if (str_cnt < LIBLTE_STRING_LEN) { - net_name->name[str_cnt] = tmp_char; - str_cnt++; - } + net_name->name[str_cnt] = tmp_char; + str_cnt++; } } @@ -3006,9 +3019,13 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_emergency_number_list_ie(uint8** emerg_num_list->N_emerg_nums = 0; while (length < sent_length) { idx = emerg_num_list->N_emerg_nums; + //add length check on emergency number list + if (idx >= LIBLTE_MME_EMERGENCY_NUMBER_LIST_MAX_SIZE) { + return (err); + } emerg_num_list->emerg_num[idx].N_emerg_num_digits = ((*ie_ptr)[length++] - 1) * 2; if (emerg_num_list->emerg_num[idx].N_emerg_num_digits > LIBLTE_MME_EMERGENCY_NUMBER_MAX_NUM_DIGITS) { - return err; + return (err); } emerg_num_list->emerg_num[idx].emerg_service_cat = @@ -5027,7 +5044,6 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_attach_request_msg(LIBLTE_MME_ATTACH_REQUEST_M if (attach_req->ms_cm3_present) { *msg_ptr = LIBLTE_MME_MS_CLASSMARK_3_IEI; msg_ptr++; - liblte_mme_pack_mobile_station_classmark_3_ie(&attach_req->ms_cm3, &msg_ptr); } // Supported Codecs @@ -5219,7 +5235,6 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_attach_request_msg(LIBLTE_BYTE_MSG_STRUCT* // Mobile Station Classmark 3 if (LIBLTE_MME_MS_CLASSMARK_3_IEI == *msg_ptr) { msg_ptr++; - liblte_mme_unpack_mobile_station_classmark_3_ie(&msg_ptr, &attach_req->ms_cm3); attach_req->ms_cm3_present = true; } else { attach_req->ms_cm3_present = false; @@ -5909,72 +5924,71 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_emm_information_msg(LIBLTE_MME_EMM_INFORMATION LIBLTE_ERROR_ENUM liblte_mme_unpack_emm_information_msg(LIBLTE_BYTE_MSG_STRUCT* msg, LIBLTE_MME_EMM_INFORMATION_MSG_STRUCT* emm_info) { - LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; - uint8* msg_ptr = msg->msg; - uint8* msg_end = msg->msg + msg->N_bytes; - uint8 sec_hdr_type; - - if (msg != NULL && emm_info != NULL) { - // Security Header Type - sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; - if (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) { - msg_ptr++; - } else { - msg_ptr += 7; - } - - // Skip Message Type - msg_ptr++; - - // Full Name For Network - if (LIBLTE_MME_FULL_NAME_FOR_NETWORK_IEI == *msg_ptr) { - msg_ptr++; - liblte_mme_unpack_network_name_ie(&msg_ptr, &emm_info->full_net_name); - emm_info->full_net_name_present = true; - } else { - emm_info->full_net_name_present = false; - } - - // Short Name For Network - if (msg_ptr < msg_end && LIBLTE_MME_SHORT_NAME_FOR_NETWORK_IEI == *msg_ptr) { - msg_ptr++; - liblte_mme_unpack_network_name_ie(&msg_ptr, &emm_info->short_net_name); - emm_info->short_net_name_present = true; - } else { - emm_info->short_net_name_present = false; - } - - // Local Time Zone - if (msg_ptr < msg_end && LIBLTE_MME_LOCAL_TIME_ZONE_IEI == *msg_ptr) { - msg_ptr++; - liblte_mme_unpack_time_zone_ie(&msg_ptr, &emm_info->local_time_zone); - emm_info->local_time_zone_present = true; - } else { - emm_info->local_time_zone_present = false; - } - - // Universal Time And Local Time Zone - if (msg_ptr < msg_end && LIBLTE_MME_UNIVERSAL_TIME_AND_LOCAL_TIME_ZONE_IEI == *msg_ptr) { - msg_ptr++; - liblte_mme_unpack_time_zone_and_time_ie(&msg_ptr, &emm_info->utc_and_local_time_zone); - emm_info->utc_and_local_time_zone_present = true; - } else { - emm_info->utc_and_local_time_zone_present = false; - } - - // Network Daylight Saving Time - if (msg_ptr < msg_end && LIBLTE_MME_NETWORK_DAYLIGHT_SAVING_TIME_IEI == *msg_ptr) { - msg_ptr++; - liblte_mme_unpack_daylight_saving_time_ie(&msg_ptr, &emm_info->net_dst); - emm_info->net_dst_present = true; - } else { - emm_info->net_dst_present = false; - } - - err = LIBLTE_SUCCESS; + if (!msg || !emm_info) { + return LIBLTE_ERROR_INVALID_INPUTS; } - return (err); + uint8* msg_ptr = msg->msg; + uint8* msg_end = msg->msg + msg->N_bytes; + uint8 sec_hdr_type; + + // Security Header Type + sec_hdr_type = (msg->msg[0] & 0xF0) >> 4; + if (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS == sec_hdr_type) { + msg_ptr++; + } else { + msg_ptr += 7; + } + + // Skip Message Type + msg_ptr++; + + // Full Name For Network + if (LIBLTE_MME_FULL_NAME_FOR_NETWORK_IEI == *msg_ptr) { + msg_ptr++; + liblte_mme_unpack_network_name_ie(&msg_ptr, &emm_info->full_net_name); + emm_info->full_net_name_present = true; + } else { + emm_info->full_net_name_present = false; + } + + // Short Name For Network + if (msg_ptr < msg_end && LIBLTE_MME_SHORT_NAME_FOR_NETWORK_IEI == *msg_ptr) { + msg_ptr++; + liblte_mme_unpack_network_name_ie(&msg_ptr, &emm_info->short_net_name); + emm_info->short_net_name_present = true; + } else { + emm_info->short_net_name_present = false; + } + + // Local Time Zone + if (msg_ptr < msg_end && LIBLTE_MME_LOCAL_TIME_ZONE_IEI == *msg_ptr) { + msg_ptr++; + liblte_mme_unpack_time_zone_ie(&msg_ptr, &emm_info->local_time_zone); + emm_info->local_time_zone_present = true; + } else { + emm_info->local_time_zone_present = false; + } + + // Universal Time And Local Time Zone + if (msg_ptr < msg_end && LIBLTE_MME_UNIVERSAL_TIME_AND_LOCAL_TIME_ZONE_IEI == *msg_ptr) { + msg_ptr++; + liblte_mme_unpack_time_zone_and_time_ie(&msg_ptr, &emm_info->utc_and_local_time_zone); + emm_info->utc_and_local_time_zone_present = true; + } else { + emm_info->utc_and_local_time_zone_present = false; + } + + // Network Daylight Saving Time + if (msg_ptr < msg_end && LIBLTE_MME_NETWORK_DAYLIGHT_SAVING_TIME_IEI == *msg_ptr) { + msg_ptr++; + liblte_mme_unpack_daylight_saving_time_ie(&msg_ptr, &emm_info->net_dst); + emm_info->net_dst_present = true; + } else { + emm_info->net_dst_present = false; + } + + return LIBLTE_SUCCESS; } /********************************************************************* @@ -7590,7 +7604,11 @@ LIBLTE_ERROR_ENUM liblte_mme_pack_downlink_generic_nas_transport_msg( liblte_mme_pack_generic_message_container_ie(&dl_generic_nas_transport->generic_msg_cont, &msg_ptr); // Additional Information - liblte_mme_pack_additional_information_ie(&dl_generic_nas_transport->add_info, &msg_ptr); + if (dl_generic_nas_transport->add_info_present) { + *msg_ptr = LIBLTE_MME_ADDITIONAL_INFORMATION_IEI; + msg_ptr++; + liblte_mme_pack_additional_information_ie(&dl_generic_nas_transport->add_info, &msg_ptr); + } // Fill in the number of bytes used msg->N_bytes = msg_ptr - msg->msg; @@ -7627,8 +7645,13 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_downlink_generic_nas_transport_msg( liblte_mme_unpack_generic_message_container_ie(&msg_ptr, &dl_generic_nas_transport->generic_msg_cont); // Additional Information - liblte_mme_unpack_additional_information_ie(&msg_ptr, &dl_generic_nas_transport->add_info); - + if (LIBLTE_MME_ADDITIONAL_INFORMATION_IEI == *msg_ptr) { + msg_ptr++; + liblte_mme_unpack_additional_information_ie(&msg_ptr, &dl_generic_nas_transport->add_info); + dl_generic_nas_transport->add_info_present = true; + } else { + dl_generic_nas_transport->add_info_present = false; + } err = LIBLTE_SUCCESS; } @@ -9023,12 +9046,27 @@ LIBLTE_ERROR_ENUM liblte_mme_unpack_deactivate_eps_bearer_context_accept_msg( *********************************************************************/ LIBLTE_ERROR_ENUM liblte_mme_pack_deactivate_eps_bearer_context_request_msg( LIBLTE_MME_DEACTIVATE_EPS_BEARER_CONTEXT_REQUEST_MSG_STRUCT* deact_eps_bearer_context_req, + uint8 sec_hdr_type, + uint32 count, LIBLTE_BYTE_MSG_STRUCT* msg) { LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; uint8* msg_ptr = msg->msg; if (deact_eps_bearer_context_req != NULL && msg != NULL) { + if (LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS != sec_hdr_type) { + // Protocol Discriminator and Security Header Type + *msg_ptr = (sec_hdr_type << 4) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); + msg_ptr++; + + // MAC will be filled in later + msg_ptr += 4; + + // Sequence Number + *msg_ptr = count & 0xFF; + msg_ptr++; + } + // Protocol Discriminator and EPS Bearer ID *msg_ptr = (deact_eps_bearer_context_req->eps_bearer_id << 4) | (LIBLTE_MME_PD_EPS_SESSION_MANAGEMENT); msg_ptr++; diff --git a/lib/src/asn1/nas_5g_ies.cc b/lib/src/asn1/nas_5g_ies.cc new file mode 100644 index 000000000..67fc587bc --- /dev/null +++ b/lib/src/asn1/nas_5g_ies.cc @@ -0,0 +1,5482 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsran/asn1/nas_5g_ies.h" + +#include "srsran/asn1/asn1_utils.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/config.h" + +#include +#include +#include + +namespace srsran { +namespace nas_5g { + +using namespace asn1; +// IE: 5GS registration type +// Reference: 9.11.3.7 +SRSASN_CODE registration_type_5gs_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(follow_on_request_bit.pack(bref)); + HANDLE_CODE(registration_type.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: 5GS registration type +// Reference: 9.11.3.7 +SRSASN_CODE registration_type_5gs_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(follow_on_request_bit.unpack(bref)); + HANDLE_CODE(registration_type.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* registration_type_5gs_t::registration_type_type_::to_string() const +{ + switch (value) { + case registration_type_type_::initial_registration: + return "Initial Registration"; + case registration_type_type_::mobility_registration_updating: + return "Mobility Registration Updating"; + case registration_type_type_::periodic_registration_updating: + return "Periodic Registration Updating"; + case registration_type_type_::emergency_registration: + return "Emergency Registration"; + case registration_type_type_::reserved: + return "Reserved"; + default: + return "Invalid Choice"; + } +} +const char* registration_type_5gs_t::follow_on_request_bit_type_::to_string() const +{ + switch (value) { + case follow_on_request_bit_type_::no_follow_on_request_pending: + return "no_follow_on_request_pending"; + case follow_on_request_bit_type_::follow_on_request_pending: + return "follow_on_request_pending"; + default: + return "Invalid Choice"; + } +} +// IE: key set identifier +// Reference: 9.11.3.32 +SRSASN_CODE key_set_identifier_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(security_context_flag.pack(bref)); + HANDLE_CODE(nas_key_set_identifier.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: key set identifier +// Reference: 9.11.3.32 +SRSASN_CODE key_set_identifier_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(security_context_flag.unpack(bref)); + HANDLE_CODE(nas_key_set_identifier.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* key_set_identifier_t::security_context_flag_type_::to_string() const +{ + switch (value) { + case security_context_flag_type_::native_security_context: + return "native security context"; + case security_context_flag_type_::mapped_security_context: + return "mapped security context"; + default: + return "Invalid Choice"; + } +} +const char* key_set_identifier_t::nas_key_set_identifier_type_::to_string() const +{ + switch (value) { + case nas_key_set_identifier_type_::no_key_is_available_or_reserved: + return "no key is available or reserved"; + default: + return "Invalid Choice"; + } +} +// IE: 5GS mobile identity +// Reference: 9.11.3.4 +SRSASN_CODE mobile_identity_5gs_t::pack(asn1::bit_ref& bref) +{ + // Length + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + asn1::bit_ref bref_tmp = bref; + switch (type_) { + case identity_types::no_identity: + HANDLE_CODE(type_.pack(bref)); + break; + case identity_types::suci: { + suci_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->pack(bref, bref_tmp)); + break; + } + case identity_types::guti_5g: { + guti_5g_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->pack(bref, bref_tmp)); + break; + } + case identity_types::imei: { + imei_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->pack(bref, bref_tmp)); + break; + } + case identity_types::s_tmsi_5g: { + s_tmsi_5g_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->pack(bref, bref_tmp)); + break; + } + case identity_types::imeisv: { + imeisv_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->pack(bref, bref_tmp)); + break; + } + case identity_types::mac_address: { + mac_address_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->pack(bref, bref_tmp)); + break; + } + case identity_types::eui_64: { + eui_64_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->pack(bref, bref_tmp)); + break; + } + default: + log_invalid_choice_id(type_, "5G NAS ID TYPE"); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + uint16_t length = (uint16_t)((bref.distance(bref_length) / 8) - 2); + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: 5GS mobile identity +// Reference: 9.11.3.4 +SRSASN_CODE mobile_identity_5gs_t::unpack(asn1::cbit_ref& bref) +{ + // Length + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + uint8_t tmp; + HANDLE_CODE(bref.unpack(tmp, 5)); + identity_types e = identity_types::no_identity; + HANDLE_CODE(e.unpack(bref)); + set(e); + switch (type_) { + case identity_types::no_identity: + break; + case identity_types::suci: { + choice_container = srslog::detail::any{suci_s()}; + suci_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->unpack(bref, tmp, length)); + break; + } + case identity_types::guti_5g: { + choice_container = srslog::detail::any{guti_5g_s()}; + guti_5g_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->unpack(bref, tmp, length)); + break; + } + case identity_types::imei: { + choice_container = srslog::detail::any{imei_s()}; + imei_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->unpack(bref, tmp, length)); + break; + } + case identity_types::s_tmsi_5g: { + choice_container = srslog::detail::any{s_tmsi_5g_s()}; + s_tmsi_5g_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->unpack(bref, tmp, length)); + break; + } + case identity_types::imeisv: { + choice_container = srslog::detail::any{imeisv_s()}; + imeisv_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->unpack(bref, tmp, length)); + break; + } + case identity_types::mac_address: { + choice_container = srslog::detail::any{mac_address_s()}; + mac_address_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->unpack(bref, tmp, length)); + break; + } + case identity_types::eui_64: { + choice_container = srslog::detail::any{eui_64_s()}; + eui_64_s* choice = srslog::detail::any_cast(&choice_container); + HANDLE_CODE(choice->unpack(bref, tmp, length)); + break; + } + default: + log_invalid_choice_id(type_, "5G NAS ID TYPE"); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + return SRSASN_SUCCESS; +} + +const char* mobile_identity_5gs_t::identity_types_::to_string() const +{ + switch (value) { + case identity_types_::no_identity: + return "No identity"; + case identity_types_::suci: + return "SUCI"; + case identity_types_::guti_5g: + return "5G-GUTI"; + case identity_types_::imei: + return "IMEI"; + case identity_types_::s_tmsi_5g: + return "5G-S-TMSI"; + case identity_types_::imeisv: + return "IMEISV"; + case identity_types_::mac_address: + return "MAC address"; + case identity_types_::eui_64: + return "EUI-64"; + default: + return "Invalid Choice"; + } +} +SRSASN_CODE mobile_identity_5gs_t::suci_s::pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp) +{ + HANDLE_CODE(bref.advance_bits(1)); + HANDLE_CODE(supi_format.pack(bref)); + HANDLE_CODE(bref.advance_bits(1)); + + // Pack Type of identity + uint8_t type = static_cast(mobile_identity_5gs_t::identity_types_::options::suci); + HANDLE_CODE(bref.pack(type, 3)); + + HANDLE_CODE(pack_mcc_mnc(mcc.data(), mnc.data(), bref)); + HANDLE_CODE(bref.pack(routing_indicator[1], 4)); + HANDLE_CODE(bref.pack(routing_indicator[0], 4)); + HANDLE_CODE(bref.pack(routing_indicator[3], 4)); + HANDLE_CODE(bref.pack(routing_indicator[2], 4)); + // Spare + HANDLE_CODE(bref.advance_bits(4)); + HANDLE_CODE(protection_scheme_id.pack(bref)); + HANDLE_CODE(bref.pack(home_network_public_key_identifier, 8)); + HANDLE_CODE(bref.pack_bytes(scheme_output.data(), scheme_output.size())); + return SRSASN_SUCCESS; +} +SRSASN_CODE mobile_identity_5gs_t::suci_s::unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length) +{ + supi_format = static_cast(tmp >> 1 & 0b111); + HANDLE_CODE(unpack_mcc_mnc(mcc.data(), mnc.data(), bref)); + HANDLE_CODE(bref.unpack(routing_indicator[1], 4)); + HANDLE_CODE(bref.unpack(routing_indicator[0], 4)); + HANDLE_CODE(bref.unpack(routing_indicator[3], 4)); + HANDLE_CODE(bref.unpack(routing_indicator[2], 4)); + // Spare + HANDLE_CODE(bref.advance_bits(4)); + HANDLE_CODE(protection_scheme_id.unpack(bref)); + HANDLE_CODE(bref.unpack(home_network_public_key_identifier, 8)); + scheme_output.resize(length - 8); + HANDLE_CODE(bref.unpack_bytes(scheme_output.data(), length - 8)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE mobile_identity_5gs_t::guti_5g_s::pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp) +{ + HANDLE_CODE(bref.pack(0x0, 5)); + // Pack Type of identity + uint8_t type = static_cast(mobile_identity_5gs_t::identity_types_::options::guti_5g); + HANDLE_CODE(bref.pack(type, 3)); + + HANDLE_CODE(pack_mcc_mnc(mcc.data(), mnc.data(), bref)); + HANDLE_CODE(bref.pack(amf_region_id, 8)); + HANDLE_CODE(bref.pack(amf_set_id, 10)); + HANDLE_CODE(bref.pack(amf_pointer, 6)); + HANDLE_CODE(bref.pack(tmsi_5g, 4 * 8)); + return SRSASN_SUCCESS; +} +SRSASN_CODE mobile_identity_5gs_t::guti_5g_s::unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length) +{ + HANDLE_CODE(unpack_mcc_mnc(mcc.data(), mnc.data(), bref)); + HANDLE_CODE(bref.unpack(amf_region_id, 8)); + HANDLE_CODE(bref.unpack(amf_set_id, 10)); + HANDLE_CODE(bref.unpack(amf_pointer, 6)); + HANDLE_CODE(bref.unpack(tmsi_5g, 4 * 8)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE mobile_identity_5gs_t::imei_s::pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp) +{ + HANDLE_CODE(bref.pack(imei[0], 4)); + HANDLE_CODE(bref.pack(odd_even_indicator, 1)); + + // Pack Type of identity + uint8_t type = static_cast(mobile_identity_5gs_t::identity_types_::options::imei); + HANDLE_CODE(bref.pack(type, 3)); + + HANDLE_CODE(bref.pack(imei[2], 4)); + HANDLE_CODE(bref.pack(imei[1], 4)); + HANDLE_CODE(bref.pack(imei[4], 4)); + HANDLE_CODE(bref.pack(imei[3], 4)); + HANDLE_CODE(bref.pack(imei[6], 4)); + HANDLE_CODE(bref.pack(imei[5], 4)); + HANDLE_CODE(bref.pack(imei[8], 4)); + HANDLE_CODE(bref.pack(imei[7], 4)); + HANDLE_CODE(bref.pack(imei[10], 4)); + HANDLE_CODE(bref.pack(imei[9], 4)); + HANDLE_CODE(bref.pack(imei[12], 4)); + HANDLE_CODE(bref.pack(imei[11], 4)); + HANDLE_CODE(bref.pack(imei[14], 4)); + HANDLE_CODE(bref.pack(imei[13], 4)); + return SRSASN_SUCCESS; +} +SRSASN_CODE mobile_identity_5gs_t::imei_s::unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length) +{ + imei[0] = (uint8_t)(tmp >> 1) & 0x0f; + odd_even_indicator = (bool)(tmp & 0x01); // true = odd number + HANDLE_CODE(bref.unpack(imei[2], 4)); + HANDLE_CODE(bref.unpack(imei[1], 4)); + HANDLE_CODE(bref.unpack(imei[4], 4)); + HANDLE_CODE(bref.unpack(imei[3], 4)); + HANDLE_CODE(bref.unpack(imei[6], 4)); + HANDLE_CODE(bref.unpack(imei[5], 4)); + HANDLE_CODE(bref.unpack(imei[8], 4)); + HANDLE_CODE(bref.unpack(imei[7], 4)); + HANDLE_CODE(bref.unpack(imei[10], 4)); + HANDLE_CODE(bref.unpack(imei[9], 4)); + HANDLE_CODE(bref.unpack(imei[12], 4)); + HANDLE_CODE(bref.unpack(imei[11], 4)); + HANDLE_CODE(bref.unpack(imei[14], 4)); + HANDLE_CODE(bref.unpack(imei[13], 4)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE mobile_identity_5gs_t::s_tmsi_5g_s::pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp) +{ + // Pack Type of identity + uint8_t type = static_cast(mobile_identity_5gs_t::identity_types_::options::s_tmsi_5g); + HANDLE_CODE(bref.pack(type, 3)); + + HANDLE_CODE(bref.unpack(amf_set_id, 10)); + HANDLE_CODE(bref.unpack(amf_pointer, 6)); + HANDLE_CODE(bref.unpack(tmsi_5g, 4 * 8)); + return SRSASN_SUCCESS; +} +SRSASN_CODE mobile_identity_5gs_t::s_tmsi_5g_s::unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length) +{ + HANDLE_CODE(bref.unpack(amf_set_id, 10)); + HANDLE_CODE(bref.unpack(amf_pointer, 6)); + HANDLE_CODE(bref.unpack(tmsi_5g, 4 * 8)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE mobile_identity_5gs_t::imeisv_s::pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp) +{ + HANDLE_CODE(bref.pack(imeisv[0], 4)); + HANDLE_CODE(bref.pack(odd_even_indicator, 1)); + + // Pack Type of identity + uint8_t type = static_cast(mobile_identity_5gs_t::identity_types_::options::imeisv); + HANDLE_CODE(bref.pack(type, 3)); + + HANDLE_CODE(bref.pack(imeisv[2], 4)); + HANDLE_CODE(bref.pack(imeisv[1], 4)); + HANDLE_CODE(bref.pack(imeisv[4], 4)); + HANDLE_CODE(bref.pack(imeisv[3], 4)); + HANDLE_CODE(bref.pack(imeisv[6], 4)); + HANDLE_CODE(bref.pack(imeisv[5], 4)); + HANDLE_CODE(bref.pack(imeisv[8], 4)); + HANDLE_CODE(bref.pack(imeisv[7], 4)); + HANDLE_CODE(bref.pack(imeisv[10], 4)); + HANDLE_CODE(bref.pack(imeisv[9], 4)); + HANDLE_CODE(bref.pack(imeisv[12], 4)); + HANDLE_CODE(bref.pack(imeisv[11], 4)); + HANDLE_CODE(bref.pack(imeisv[14], 4)); + HANDLE_CODE(bref.pack(imeisv[13], 4)); + HANDLE_CODE(bref.pack(0xf, 4)); + HANDLE_CODE(bref.pack(imeisv[15], 4)); + + return SRSASN_SUCCESS; +} +SRSASN_CODE mobile_identity_5gs_t::imeisv_s::unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length) +{ + imeisv[0] = (uint8_t)(tmp >> 1) & 0x0f; + odd_even_indicator = (bool)(tmp & 0x01); // true = odd number + HANDLE_CODE(bref.unpack(imeisv[2], 4)); + HANDLE_CODE(bref.unpack(imeisv[1], 4)); + HANDLE_CODE(bref.unpack(imeisv[4], 4)); + HANDLE_CODE(bref.unpack(imeisv[3], 4)); + HANDLE_CODE(bref.unpack(imeisv[6], 4)); + HANDLE_CODE(bref.unpack(imeisv[5], 4)); + HANDLE_CODE(bref.unpack(imeisv[8], 4)); + HANDLE_CODE(bref.unpack(imeisv[7], 4)); + HANDLE_CODE(bref.unpack(imeisv[10], 4)); + HANDLE_CODE(bref.unpack(imeisv[9], 4)); + HANDLE_CODE(bref.unpack(imeisv[12], 4)); + HANDLE_CODE(bref.unpack(imeisv[11], 4)); + HANDLE_CODE(bref.unpack(imeisv[14], 4)); + HANDLE_CODE(bref.unpack(imeisv[13], 4)); + HANDLE_CODE(bref.advance_bits(4)); + HANDLE_CODE(bref.unpack(imeisv[15], 4)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE mobile_identity_5gs_t::mac_address_s::pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp) +{ + return SRSASN_SUCCESS; +} +SRSASN_CODE mobile_identity_5gs_t::mac_address_s::unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length) +{ + return SRSASN_SUCCESS; +} + +SRSASN_CODE mobile_identity_5gs_t::eui_64_s::pack(asn1::bit_ref& bref, asn1::bit_ref& bref_tmp) +{ + return SRSASN_SUCCESS; +} +SRSASN_CODE mobile_identity_5gs_t::eui_64_s::unpack(asn1::cbit_ref& bref, uint8_t tmp, uint32_t length) +{ + return SRSASN_SUCCESS; +} + +// IE: 5GMM capability +// Reference: 9.11.3.1 +SRSASN_CODE capability_5gmm_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(sgc, 1)); + HANDLE_CODE(bref.pack(iphc_cp_c_io_t_5g, 1)); + HANDLE_CODE(bref.pack(n3_data, 1)); + HANDLE_CODE(bref.pack(cp_c_io_t_5g, 1)); + HANDLE_CODE(bref.pack(restrict_ec, 1)); + HANDLE_CODE(bref.pack(lpp, 1)); + HANDLE_CODE(bref.pack(ho_attach, 1)); + HANDLE_CODE(bref.pack(s1_mode, 1)); + + HANDLE_CODE(bref.pack(racs, 1)); + HANDLE_CODE(bref.pack(nssaa, 1)); + HANDLE_CODE(bref.pack(lcs_5g, 1)); + HANDLE_CODE(bref.pack(v2_xcnpc5, 1)); + HANDLE_CODE(bref.pack(v2_xcepc5, 1)); + HANDLE_CODE(bref.pack(v2_x, 1)); + HANDLE_CODE(bref.pack(up_c_io_t_5g, 1)); + HANDLE_CODE(bref.pack(srvcc_5g, 1)); + + HANDLE_CODE(bref.advance_bits(4)); + HANDLE_CODE(bref.pack(ehc_cp_c_io_t_5g, 1)); + HANDLE_CODE(bref.pack(multiple_up, 1)); + HANDLE_CODE(bref.pack(wusa, 1)); + HANDLE_CODE(bref.pack(cag, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1 || length > 13) { + asn1::log_error("Encoding Failed (5GMM capability): Packed length (%d) is not in range of min: 1 and max 13 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GMM capability +// Reference: 9.11.3.1 +SRSASN_CODE capability_5gmm_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1 || length > 13) { + asn1::log_error("Decoding Failed (5GMM capability): Length (%d) is not in range of min: 1 and max 13 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(sgc, 1)); + HANDLE_CODE(bref.unpack(iphc_cp_c_io_t_5g, 1)); + HANDLE_CODE(bref.unpack(n3_data, 1)); + HANDLE_CODE(bref.unpack(cp_c_io_t_5g, 1)); + HANDLE_CODE(bref.unpack(restrict_ec, 1)); + HANDLE_CODE(bref.unpack(lpp, 1)); + HANDLE_CODE(bref.unpack(ho_attach, 1)); + HANDLE_CODE(bref.unpack(s1_mode, 1)); + + if (length < 2) { + return SRSASN_SUCCESS; + } + + HANDLE_CODE(bref.unpack(racs, 1)); + HANDLE_CODE(bref.unpack(nssaa, 1)); + HANDLE_CODE(bref.unpack(lcs_5g, 1)); + HANDLE_CODE(bref.unpack(v2_xcnpc5, 1)); + HANDLE_CODE(bref.unpack(v2_xcepc5, 1)); + HANDLE_CODE(bref.unpack(v2_x, 1)); + HANDLE_CODE(bref.unpack(up_c_io_t_5g, 1)); + HANDLE_CODE(bref.unpack(srvcc_5g, 1)); + + if (length < 3) { + return SRSASN_SUCCESS; + } + + HANDLE_CODE(bref.advance_bits(4)); + HANDLE_CODE(bref.unpack(ehc_cp_c_io_t_5g, 1)); + HANDLE_CODE(bref.unpack(multiple_up, 1)); + HANDLE_CODE(bref.unpack(wusa, 1)); + HANDLE_CODE(bref.unpack(cag, 1)); + return SRSASN_SUCCESS; +} + +// IE: UE security capability +// Reference: 9.11.3.54 +SRSASN_CODE ue_security_capability_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(ea0_5g_supported, 1)); + HANDLE_CODE(bref.pack(ea1_128_5g_supported, 1)); + HANDLE_CODE(bref.pack(ea2_128_5g_supported, 1)); + HANDLE_CODE(bref.pack(ea3_128_5g_supported, 1)); + HANDLE_CODE(bref.pack(ea4_5g_supported, 1)); + HANDLE_CODE(bref.pack(ea5_5g_supported, 1)); + HANDLE_CODE(bref.pack(ea6_5g_supported, 1)); + HANDLE_CODE(bref.pack(ea7_5g_supported, 1)); + HANDLE_CODE(bref.pack(ia0_5g_supported, 1)); + + HANDLE_CODE(bref.pack(ia1_128_5g_supported, 1)); + HANDLE_CODE(bref.pack(ia2_128_5g_supported, 1)); + HANDLE_CODE(bref.pack(ia3_128_5g_supported, 1)); + HANDLE_CODE(bref.pack(ia4_5g_supported, 1)); + HANDLE_CODE(bref.pack(ia5_5g_supported, 1)); + HANDLE_CODE(bref.pack(ia6_5g_supported, 1)); + HANDLE_CODE(bref.pack(ia7_5g_supported, 1)); + + if (eps_caps_present == true) { + HANDLE_CODE(bref.pack(eea0_supported, 1)); + HANDLE_CODE(bref.pack(eea1_128_supported, 1)); + HANDLE_CODE(bref.pack(eea2_128_supported, 1)); + HANDLE_CODE(bref.pack(eea3_128_supported, 1)); + HANDLE_CODE(bref.pack(eea4_supported, 1)); + HANDLE_CODE(bref.pack(eea5_supported, 1)); + HANDLE_CODE(bref.pack(eea6_supported, 1)); + HANDLE_CODE(bref.pack(eea7_supported, 1)); + HANDLE_CODE(bref.pack(eia0_supported, 1)); + HANDLE_CODE(bref.pack(eia1_128_supported, 1)); + HANDLE_CODE(bref.pack(eia2_128_supported, 1)); + HANDLE_CODE(bref.pack(eia3_128_supported, 1)); + HANDLE_CODE(bref.pack(eia4_supported, 1)); + HANDLE_CODE(bref.pack(eia5_supported, 1)); + HANDLE_CODE(bref.pack(eia6_supported, 1)); + HANDLE_CODE(bref.pack(eia7_supported, 1)); + } + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 8) { + asn1::log_error( + "Encoding Failed (UE security capability): Packed length (%d) is not in range of min: 2 and max 8 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: UE security capability +// Reference: 9.11.3.54 +SRSASN_CODE ue_security_capability_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 8) { + asn1::log_error("Decoding Failed (UE security capability): Length (%d) is not in range of min: 2 and max 8 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(ea0_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ea1_128_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ea2_128_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ea3_128_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ea4_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ea5_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ea6_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ea7_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ia0_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ia1_128_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ia2_128_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ia3_128_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ia4_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ia5_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ia6_5g_supported, 1)); + HANDLE_CODE(bref.unpack(ia7_5g_supported, 1)); + if (length > 2) { + eps_caps_present = true; + HANDLE_CODE(bref.unpack(eea0_supported, 1)); + HANDLE_CODE(bref.unpack(eea1_128_supported, 1)); + HANDLE_CODE(bref.unpack(eea2_128_supported, 1)); + HANDLE_CODE(bref.unpack(eea3_128_supported, 1)); + HANDLE_CODE(bref.unpack(eea4_supported, 1)); + HANDLE_CODE(bref.unpack(eea5_supported, 1)); + HANDLE_CODE(bref.unpack(eea6_supported, 1)); + HANDLE_CODE(bref.unpack(eea7_supported, 1)); + HANDLE_CODE(bref.unpack(eia0_supported, 1)); + HANDLE_CODE(bref.unpack(eia1_128_supported, 1)); + HANDLE_CODE(bref.unpack(eia2_128_supported, 1)); + HANDLE_CODE(bref.unpack(eia3_128_supported, 1)); + HANDLE_CODE(bref.unpack(eia4_supported, 1)); + HANDLE_CODE(bref.unpack(eia5_supported, 1)); + HANDLE_CODE(bref.unpack(eia6_supported, 1)); + HANDLE_CODE(bref.unpack(eia7_supported, 1)); + } + if (length > 4) { + HANDLE_CODE(bref.advance_bits((length - 4) * 8)); + } + return SRSASN_SUCCESS; +} + +// IE: NSSAI +// Reference: 9.11.3.37 +SRSASN_CODE nssai_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + for (auto& s_nssai : s_nssai_list) { + HANDLE_CODE(s_nssai.pack(bref)); + } + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 144) { + asn1::log_error("Encoding Failed (NSSAI): Packed length (%d) is not in range of min: 2 and max 144 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: NSSAI +// Reference: 9.11.3.37 +SRSASN_CODE nssai_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 144) { + asn1::log_error("Decoding Failed (NSSAI): Length (%d) is not in range of min: 2 and max 144 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + asn1::cbit_ref bref_start = bref; + while (floorl(bref.distance(bref_start) / 8) < length) { + s_nssai_t s_nssai; + HANDLE_CODE(s_nssai.unpack(bref)); + s_nssai_list.push_back(s_nssai); + } + return SRSASN_SUCCESS; +} + +// IE: 5GS tracking area identity +// Reference: 9.11.3.8 +SRSASN_CODE tracking_area_identity_5gs_t::pack(asn1::bit_ref& bref) +{ + pack_mcc_mnc(mcc.data(), mnc.data(), bref); + HANDLE_CODE(bref.pack(tac, 3 * 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GS tracking area identity +// Reference: 9.11.3.8 +SRSASN_CODE tracking_area_identity_5gs_t::unpack(asn1::cbit_ref& bref) +{ + unpack_mcc_mnc(mcc.data(), mnc.data(), bref); + HANDLE_CODE(bref.unpack(tac, 3 * 8)); + return SRSASN_SUCCESS; +} + +// IE: S1 UE network capability +// Reference: 9.11.3.48 +SRSASN_CODE s1_ue_network_capability_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(eea0_supported, 1)); + HANDLE_CODE(bref.pack(eea1_128_supported, 1)); + HANDLE_CODE(bref.pack(eea2_128_supported, 1)); + HANDLE_CODE(bref.pack(eea3_128_supported, 1)); + HANDLE_CODE(bref.pack(eea4_supported, 1)); + HANDLE_CODE(bref.pack(eea5_supported, 1)); + HANDLE_CODE(bref.pack(eea6_supported, 1)); + HANDLE_CODE(bref.pack(eea7_supported, 1)); + HANDLE_CODE(bref.pack(eia0_supported, 1)); + + HANDLE_CODE(bref.pack(eia1_128_supported, 1)); + HANDLE_CODE(bref.pack(eia2_128_supported, 1)); + HANDLE_CODE(bref.pack(eia3_128_supported, 1)); + HANDLE_CODE(bref.pack(eia4_supported, 1)); + HANDLE_CODE(bref.pack(eia5_supported, 1)); + HANDLE_CODE(bref.pack(eia6_supported, 1)); + HANDLE_CODE(bref.pack(eia7_supported, 1)); + + HANDLE_CODE(bref.pack(uea0_supported, 1)); + HANDLE_CODE(bref.pack(uea1_128_supported, 1)); + HANDLE_CODE(bref.pack(uea2_128_supported, 1)); + HANDLE_CODE(bref.pack(uea3_128_supported, 1)); + HANDLE_CODE(bref.pack(uea4_supported, 1)); + HANDLE_CODE(bref.pack(uea5_supported, 1)); + HANDLE_CODE(bref.pack(uea6_supported, 1)); + HANDLE_CODE(bref.pack(uea7_supported, 1)); + + HANDLE_CODE(bref.pack(ucs2_support, 1)); + HANDLE_CODE(bref.pack(uia1_128_supported, 1)); + HANDLE_CODE(bref.pack(uia2_128_supported, 1)); + HANDLE_CODE(bref.pack(uia3_128_supported, 1)); + HANDLE_CODE(bref.pack(uia4_supported, 1)); + HANDLE_CODE(bref.pack(uia5_supported, 1)); + HANDLE_CODE(bref.pack(uia6_supported, 1)); + HANDLE_CODE(bref.pack(uia7_supported, 1)); + + HANDLE_CODE(bref.pack(pro_se_dd_supported, 1)); + HANDLE_CODE(bref.pack(pro_se_supported, 1)); + HANDLE_CODE(bref.pack(h245_ash_supported, 1)); + HANDLE_CODE(bref.pack(acc_csfb_supported, 1)); + HANDLE_CODE(bref.pack(llp_supported, 1)); + HANDLE_CODE(bref.pack(lcs_supported, 1)); + HANDLE_CODE(bref.pack(srvcc_capability_supported, 1)); + HANDLE_CODE(bref.pack(nf_capability_supported, 1)); + + HANDLE_CODE(bref.pack(e_pco_supported, 1)); + HANDLE_CODE(bref.pack(hc_cp_c_io_t_supported, 1)); + HANDLE_CODE(bref.pack(e_rw_o_pdn_supported, 1)); + HANDLE_CODE(bref.pack(s1_u_data_supported, 1)); + HANDLE_CODE(bref.pack(up_c_io_t_supported, 1)); + HANDLE_CODE(bref.pack(cp_c_io_t_supported, 1)); + HANDLE_CODE(bref.pack(pro_se_relay_supported, 1)); + + HANDLE_CODE(bref.pack(pro_se_dc_supported, 1)); + HANDLE_CODE(bref.pack(max_15_eps_bearer_supported, 1)); + HANDLE_CODE(bref.pack(sgc_supported, 1)); + HANDLE_CODE(bref.pack(n1mode_supported, 1)); + HANDLE_CODE(bref.pack(dcnr_supported, 1)); + HANDLE_CODE(bref.pack(cp_backoff_supported, 1)); + HANDLE_CODE(bref.pack(restrict_ec_supported, 1)); + HANDLE_CODE(bref.pack(v2_x_pc5_supported, 1)); + HANDLE_CODE(bref.pack(multiple_drb_supported, 1)); + + HANDLE_CODE(bref.advance_bits(3)); + HANDLE_CODE(bref.pack(nr_pc5_supported, 1)); + HANDLE_CODE(bref.pack(up_mt_edt_supported, 1)); + HANDLE_CODE(bref.pack(cp_mt_edt_supported, 1)); + HANDLE_CODE(bref.pack(wus_supported, 1)); + HANDLE_CODE(bref.pack(racs_supported, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 13) { + asn1::log_error( + "Encoding Failed (S1 UE network capability): Packed length (%d) is not in range of min: 2 and max 13 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: S1 UE network capability +// Reference: 9.11.3.48 +SRSASN_CODE s1_ue_network_capability_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 13) { + asn1::log_error( + "Decoding Failed (S1 UE network capability): Length (%d) is not in range of min: 2 and max 13 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(eea0_supported, 1)); + HANDLE_CODE(bref.unpack(eea1_128_supported, 1)); + HANDLE_CODE(bref.unpack(eea2_128_supported, 1)); + HANDLE_CODE(bref.unpack(eea3_128_supported, 1)); + HANDLE_CODE(bref.unpack(eea4_supported, 1)); + HANDLE_CODE(bref.unpack(eea5_supported, 1)); + HANDLE_CODE(bref.unpack(eea6_supported, 1)); + HANDLE_CODE(bref.unpack(eea7_supported, 1)); + + HANDLE_CODE(bref.unpack(eia0_supported, 1)); + HANDLE_CODE(bref.unpack(eia1_128_supported, 1)); + HANDLE_CODE(bref.unpack(eia2_128_supported, 1)); + HANDLE_CODE(bref.unpack(eia3_128_supported, 1)); + HANDLE_CODE(bref.unpack(eia4_supported, 1)); + HANDLE_CODE(bref.unpack(eia5_supported, 1)); + HANDLE_CODE(bref.unpack(eia6_supported, 1)); + HANDLE_CODE(bref.unpack(eia7_supported, 1)); + + if (length < 3) { + return SRSASN_SUCCESS; + } + + HANDLE_CODE(bref.unpack(uea0_supported, 1)); + HANDLE_CODE(bref.unpack(uea1_128_supported, 1)); + HANDLE_CODE(bref.unpack(uea2_128_supported, 1)); + HANDLE_CODE(bref.unpack(uea3_128_supported, 1)); + HANDLE_CODE(bref.unpack(uea4_supported, 1)); + HANDLE_CODE(bref.unpack(uea5_supported, 1)); + HANDLE_CODE(bref.unpack(uea6_supported, 1)); + HANDLE_CODE(bref.unpack(uea7_supported, 1)); + + if (length < 4) { + return SRSASN_SUCCESS; + } + + HANDLE_CODE(bref.unpack(ucs2_support, 1)); + HANDLE_CODE(bref.unpack(uia1_128_supported, 1)); + HANDLE_CODE(bref.unpack(uia2_128_supported, 1)); + HANDLE_CODE(bref.unpack(uia3_128_supported, 1)); + HANDLE_CODE(bref.unpack(uia4_supported, 1)); + HANDLE_CODE(bref.unpack(uia5_supported, 1)); + HANDLE_CODE(bref.unpack(uia6_supported, 1)); + HANDLE_CODE(bref.unpack(uia7_supported, 1)); + + if (length < 5) { + return SRSASN_SUCCESS; + } + + HANDLE_CODE(bref.unpack(pro_se_dd_supported, 1)); + HANDLE_CODE(bref.unpack(pro_se_supported, 1)); + HANDLE_CODE(bref.unpack(h245_ash_supported, 1)); + HANDLE_CODE(bref.unpack(acc_csfb_supported, 1)); + HANDLE_CODE(bref.unpack(llp_supported, 1)); + HANDLE_CODE(bref.unpack(lcs_supported, 1)); + HANDLE_CODE(bref.unpack(srvcc_capability_supported, 1)); + HANDLE_CODE(bref.unpack(nf_capability_supported, 1)); + + if (length < 6) { + return SRSASN_SUCCESS; + } + + HANDLE_CODE(bref.unpack(e_pco_supported, 1)); + HANDLE_CODE(bref.unpack(hc_cp_c_io_t_supported, 1)); + HANDLE_CODE(bref.unpack(e_rw_o_pdn_supported, 1)); + HANDLE_CODE(bref.unpack(s1_u_data_supported, 1)); + HANDLE_CODE(bref.unpack(up_c_io_t_supported, 1)); + HANDLE_CODE(bref.unpack(cp_c_io_t_supported, 1)); + HANDLE_CODE(bref.unpack(pro_se_relay_supported, 1)); + HANDLE_CODE(bref.unpack(pro_se_dc_supported, 1)); + + if (length < 7) { + return SRSASN_SUCCESS; + } + + HANDLE_CODE(bref.unpack(max_15_eps_bearer_supported, 1)); + HANDLE_CODE(bref.unpack(sgc_supported, 1)); + HANDLE_CODE(bref.unpack(n1mode_supported, 1)); + HANDLE_CODE(bref.unpack(dcnr_supported, 1)); + HANDLE_CODE(bref.unpack(cp_backoff_supported, 1)); + HANDLE_CODE(bref.unpack(restrict_ec_supported, 1)); + HANDLE_CODE(bref.unpack(v2_x_pc5_supported, 1)); + HANDLE_CODE(bref.unpack(multiple_drb_supported, 1)); + + if (length < 8) { + return SRSASN_SUCCESS; + } + // 3 spare bits + bref.advance_bits(3); + + HANDLE_CODE(bref.unpack(nr_pc5_supported, 1)); + HANDLE_CODE(bref.unpack(up_mt_edt_supported, 1)); + HANDLE_CODE(bref.unpack(cp_mt_edt_supported, 1)); + HANDLE_CODE(bref.unpack(wus_supported, 1)); + HANDLE_CODE(bref.unpack(racs_supported, 1)); + return SRSASN_SUCCESS; +} + +// IE: Uplink data status +// Reference: 9.11.3.57 +SRSASN_CODE uplink_data_status_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(psi_7, 1)); + HANDLE_CODE(bref.pack(psi_6, 1)); + HANDLE_CODE(bref.pack(psi_5, 1)); + HANDLE_CODE(bref.pack(psi_4, 1)); + HANDLE_CODE(bref.pack(psi_3, 1)); + HANDLE_CODE(bref.pack(psi_2, 1)); + HANDLE_CODE(bref.pack(psi_1, 1)); + HANDLE_CODE(bref.pack(psi_0, 1)); + HANDLE_CODE(bref.pack(psi_15, 1)); + HANDLE_CODE(bref.pack(psi_14, 1)); + HANDLE_CODE(bref.pack(psi_13, 1)); + HANDLE_CODE(bref.pack(psi_12, 1)); + HANDLE_CODE(bref.pack(psi_11, 1)); + HANDLE_CODE(bref.pack(psi_10, 1)); + HANDLE_CODE(bref.pack(psi_9, 1)); + HANDLE_CODE(bref.pack(psi_8, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 32) { + asn1::log_error( + "Encoding Failed (Uplink data status): Packed length (%d) is not in range of min: 2 and max 32 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Uplink data status +// Reference: 9.11.3.57 +SRSASN_CODE uplink_data_status_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 32) { + asn1::log_error("Decoding Failed (Uplink data status): Length (%d) is not in range of min: 2 and max 32 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(psi_7, 1)); + HANDLE_CODE(bref.unpack(psi_6, 1)); + HANDLE_CODE(bref.unpack(psi_5, 1)); + HANDLE_CODE(bref.unpack(psi_4, 1)); + HANDLE_CODE(bref.unpack(psi_3, 1)); + HANDLE_CODE(bref.unpack(psi_2, 1)); + HANDLE_CODE(bref.unpack(psi_1, 1)); + HANDLE_CODE(bref.unpack(psi_0, 1)); + HANDLE_CODE(bref.unpack(psi_15, 1)); + HANDLE_CODE(bref.unpack(psi_14, 1)); + HANDLE_CODE(bref.unpack(psi_13, 1)); + HANDLE_CODE(bref.unpack(psi_12, 1)); + HANDLE_CODE(bref.unpack(psi_11, 1)); + HANDLE_CODE(bref.unpack(psi_10, 1)); + HANDLE_CODE(bref.unpack(psi_9, 1)); + HANDLE_CODE(bref.unpack(psi_8, 1)); + + if (length > 2) { + bref.advance_bits((length - 2) * 8); + } + return SRSASN_SUCCESS; +} + +// IE: PDU session status +// Reference: 9.11.3.44 +SRSASN_CODE pdu_session_status_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(psi_7, 1)); + HANDLE_CODE(bref.pack(psi_6, 1)); + HANDLE_CODE(bref.pack(psi_5, 1)); + HANDLE_CODE(bref.pack(psi_4, 1)); + HANDLE_CODE(bref.pack(psi_3, 1)); + HANDLE_CODE(bref.pack(psi_2, 1)); + HANDLE_CODE(bref.pack(psi_1, 1)); + HANDLE_CODE(bref.pack(psi_0, 1)); + HANDLE_CODE(bref.pack(psi_15, 1)); + HANDLE_CODE(bref.pack(psi_14, 1)); + HANDLE_CODE(bref.pack(psi_13, 1)); + HANDLE_CODE(bref.pack(psi_12, 1)); + HANDLE_CODE(bref.pack(psi_11, 1)); + HANDLE_CODE(bref.pack(psi_10, 1)); + HANDLE_CODE(bref.pack(psi_9, 1)); + HANDLE_CODE(bref.pack(psi_8, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 32) { + asn1::log_error( + "Encoding Failed (PDU session status): Packed length (%d) is not in range of min: 2 and max 32 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: PDU session status +// Reference: 9.11.3.44 +SRSASN_CODE pdu_session_status_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 32) { + asn1::log_error("Decoding Failed (PDU session status): Length (%d) is not in range of min: 2 and max 32 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(psi_7, 1)); + HANDLE_CODE(bref.unpack(psi_6, 1)); + HANDLE_CODE(bref.unpack(psi_5, 1)); + HANDLE_CODE(bref.unpack(psi_4, 1)); + HANDLE_CODE(bref.unpack(psi_3, 1)); + HANDLE_CODE(bref.unpack(psi_2, 1)); + HANDLE_CODE(bref.unpack(psi_1, 1)); + HANDLE_CODE(bref.unpack(psi_0, 1)); + HANDLE_CODE(bref.unpack(psi_15, 1)); + HANDLE_CODE(bref.unpack(psi_14, 1)); + HANDLE_CODE(bref.unpack(psi_13, 1)); + HANDLE_CODE(bref.unpack(psi_12, 1)); + HANDLE_CODE(bref.unpack(psi_11, 1)); + HANDLE_CODE(bref.unpack(psi_10, 1)); + HANDLE_CODE(bref.unpack(psi_9, 1)); + HANDLE_CODE(bref.unpack(psi_8, 1)); + + if (length > 2) { + bref.advance_bits((length - 2) * 8); + } + return SRSASN_SUCCESS; +} + +// IE: MICO indication +// Reference: 9.11.3.31 +SRSASN_CODE mico_indication_t::pack(asn1::bit_ref& bref) +{ + // 2 Spare bits + HANDLE_CODE(bref.pack(0x0, 2)); + HANDLE_CODE(bref.pack(sprti, 1)); + HANDLE_CODE(bref.pack(aai, 1)); + return SRSASN_SUCCESS; +} + +// IE: MICO indication +// Reference: 9.11.3.31 +SRSASN_CODE mico_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 2 Spare bits + bref.advance_bits(2); + HANDLE_CODE(bref.unpack(sprti, 1)); + HANDLE_CODE(bref.unpack(aai, 1)); + return SRSASN_SUCCESS; +} + +// IE: UE status +// Reference: 9.11.3.56 +SRSASN_CODE ue_status_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 6 Spare bits + HANDLE_CODE(bref.pack(0x0, 6)); + HANDLE_CODE(bref.pack(n1_mode_reg, 1)); + HANDLE_CODE(bref.pack(s1_mode_reg, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (UE status): Packed length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: UE status +// Reference: 9.11.3.56 +SRSASN_CODE ue_status_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (UE status): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 6 Spare bits + bref.advance_bits(6); + HANDLE_CODE(bref.unpack(n1_mode_reg, 1)); + HANDLE_CODE(bref.unpack(s1_mode_reg, 1)); + return SRSASN_SUCCESS; +} + +// IE: Allowed PDU session status +// Reference: 9.11.3.13 +SRSASN_CODE allowed_pdu_session_status_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(psi_7, 1)); + HANDLE_CODE(bref.pack(psi_6, 1)); + HANDLE_CODE(bref.pack(psi_5, 1)); + HANDLE_CODE(bref.pack(psi_4, 1)); + HANDLE_CODE(bref.pack(psi_3, 1)); + HANDLE_CODE(bref.pack(psi_2, 1)); + HANDLE_CODE(bref.pack(psi_1, 1)); + HANDLE_CODE(bref.pack(psi_0, 1)); + HANDLE_CODE(bref.pack(psi_15, 1)); + HANDLE_CODE(bref.pack(psi_14, 1)); + HANDLE_CODE(bref.pack(psi_13, 1)); + HANDLE_CODE(bref.pack(psi_12, 1)); + HANDLE_CODE(bref.pack(psi_11, 1)); + HANDLE_CODE(bref.pack(psi_10, 1)); + HANDLE_CODE(bref.pack(psi_9, 1)); + HANDLE_CODE(bref.pack(psi_8, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 32) { + asn1::log_error( + "Encoding Failed (Allowed PDU session status): Packed length (%d) is not in range of min: 2 and max 32 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Allowed PDU session status +// Reference: 9.11.3.13 +SRSASN_CODE allowed_pdu_session_status_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 32) { + asn1::log_error( + "Decoding Failed (Allowed PDU session status): Length (%d) is not in range of min: 2 and max 32 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(psi_7, 1)); + HANDLE_CODE(bref.unpack(psi_6, 1)); + HANDLE_CODE(bref.unpack(psi_5, 1)); + HANDLE_CODE(bref.unpack(psi_4, 1)); + HANDLE_CODE(bref.unpack(psi_3, 1)); + HANDLE_CODE(bref.unpack(psi_2, 1)); + HANDLE_CODE(bref.unpack(psi_1, 1)); + HANDLE_CODE(bref.unpack(psi_0, 1)); + HANDLE_CODE(bref.unpack(psi_15, 1)); + HANDLE_CODE(bref.unpack(psi_14, 1)); + HANDLE_CODE(bref.unpack(psi_13, 1)); + HANDLE_CODE(bref.unpack(psi_12, 1)); + HANDLE_CODE(bref.unpack(psi_11, 1)); + HANDLE_CODE(bref.unpack(psi_10, 1)); + HANDLE_CODE(bref.unpack(psi_9, 1)); + HANDLE_CODE(bref.unpack(psi_8, 1)); + + if (length > 2) { + bref.advance_bits((length - 2) * 8); + } + return SRSASN_SUCCESS; +} + +// IE: UE usage setting +// Reference: 9.11.3.55 +SRSASN_CODE ue_usage_setting_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 7 Spare bits + HANDLE_CODE(bref.pack(0x0, 7)); + HANDLE_CODE(ue_usage_setting.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (UE usage setting): Packed length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: UE usage setting +// Reference: 9.11.3.55 +SRSASN_CODE ue_usage_setting_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (UE usage setting): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 7 Spare bits + bref.advance_bits(7); + HANDLE_CODE(ue_usage_setting.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* ue_usage_setting_t::UE_usage_setting_type_::to_string() const +{ + switch (value) { + case UE_usage_setting_type_::voice_centric: + return "voice centric"; + case UE_usage_setting_type_::data_centric: + return "data centric"; + default: + return "Invalid Choice"; + } +} +// IE: 5GS DRX parameters +// Reference: 9.11.3.2A +SRSASN_CODE drx_parameters_5gs_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 4 Spare bits + HANDLE_CODE(bref.pack(0x0, 4)); + HANDLE_CODE(drx_value.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (5GS DRX parameters): Packed length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GS DRX parameters +// Reference: 9.11.3.2A +SRSASN_CODE drx_parameters_5gs_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (5GS DRX parameters): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 4 Spare bits + bref.advance_bits(4); + HANDLE_CODE(drx_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* drx_parameters_5gs_t::drx_value_type_::to_string() const +{ + switch (value) { + case drx_value_type_::drx_value_not_specified: + return "DRX value not specified"; + case drx_value_type_::drx_cycle_parameter_t_32: + return "DRX cycle parameter T 32"; + case drx_value_type_::drx_cycle_parameter_t_64: + return "DRX cycle parameter T 64"; + case drx_value_type_::drx_cycle_parameter_t_128: + return "DRX cycle parameter T 128"; + case drx_value_type_::drx_cycle_parameter_t_256: + return "DRX cycle parameter T 256"; + default: + return "Invalid Choice"; + } +} +// IE: EPS NAS message container +// Reference: 9.11.3.24 +SRSASN_CODE eps_nas_message_container_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + HANDLE_CODE(bref.pack_bytes(eps_nas_message_container.data(), eps_nas_message_container.size())); + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: EPS NAS message container +// Reference: 9.11.3.24 +SRSASN_CODE eps_nas_message_container_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + eps_nas_message_container.resize(length); + HANDLE_CODE(bref.unpack_bytes(eps_nas_message_container.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: LADN indication +// Reference: 9.11.3.29 +SRSASN_CODE ladn_indication_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + for (auto& dnn : ladn_dnn_values) { + HANDLE_CODE(dnn.pack(bref)); + } + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // min. length of 0 not checked: uint underflow + if (length > 808) { + asn1::log_error("Encoding Failed (LADN indication): Packed length (%d) is not in range of max 808 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: LADN indication +// Reference: 9.11.3.29 +SRSASN_CODE ladn_indication_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // min. length of 0 not checked: uint underflow + if (length > 808) { + asn1::log_error("Decoding Failed (LADN indication): Length (%d) is not in range of max 808 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + asn1::cbit_ref bref_start = bref; + while (floorl(bref.distance(bref_start) / 8) < length) { + dnn_t dnn; + dnn.unpack(bref); + ladn_dnn_values.push_back(dnn); + } + return SRSASN_SUCCESS; +} + +// IE: Payload container type +// Reference: 9.11.3.40 +SRSASN_CODE payload_container_type_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(payload_container_type.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: Payload container type +// Reference: 9.11.3.40 +SRSASN_CODE payload_container_type_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(payload_container_type.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* payload_container_type_t::Payload_container_type_type_::to_string() const +{ + switch (value) { + case Payload_container_type_type_::n1_sm_information: + return "N1 SM information"; + case Payload_container_type_type_::sms: + return "SMS"; + case Payload_container_type_type_::lte_positioning_protocol_lpp_message_container: + return "LTE Positioning Protocol LPP message container"; + case Payload_container_type_type_::sor_transparent_container: + return "SOR transparent container"; + case Payload_container_type_type_::ue_policy_container: + return "UE policy container"; + case Payload_container_type_type_::ue_parameters_update_transparent_container: + return "UE parameters update transparent container"; + case Payload_container_type_type_::location_services_message_container: + return "Location services message container"; + case Payload_container_type_type_::c_io_t_user_data_container: + return "CIoT user data container"; + case Payload_container_type_type_::multiple_payloads: + return "Multiple payloads"; + default: + return "Invalid Choice"; + } +} +// IE: Payload container +// Reference: 9.11.3.39 +SRSASN_CODE payload_container_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + HANDLE_CODE(bref.pack_bytes(payload_container_contents.data(), payload_container_contents.size())); + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // max. length of 65535 not checked: uint overflow + if (length < 1) { + asn1::log_error("Encoding Failed (Payload container): Packed length (%d) is not in range of min: 1 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: Payload container +// Reference: 9.11.3.39 +SRSASN_CODE payload_container_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // max. length of 65535 not checked: uint overflow + if (length < 1) { + asn1::log_error("Decoding Failed (Payload container): Length (%d) is not in range of min: 1 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + payload_container_contents.resize(length); + HANDLE_CODE(bref.unpack_bytes(payload_container_contents.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Network slicing indication +// Reference: 9.11.3.36 +SRSASN_CODE network_slicing_indication_t::pack(asn1::bit_ref& bref) +{ + // 2 Spare bits + HANDLE_CODE(bref.pack(0x0, 2)); + HANDLE_CODE(bref.pack(nssci, 1)); + HANDLE_CODE(bref.pack(dcni, 1)); + return SRSASN_SUCCESS; +} + +// IE: Network slicing indication +// Reference: 9.11.3.36 +SRSASN_CODE network_slicing_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 2 Spare bits + bref.advance_bits(2); + HANDLE_CODE(bref.unpack(nssci, 1)); + HANDLE_CODE(bref.unpack(dcni, 1)); + return SRSASN_SUCCESS; +} + +// IE: 5GS update type +// Reference: 9.11.3.9A +SRSASN_CODE update_type_5gs_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 2 Spare bits + HANDLE_CODE(bref.pack(0x0, 2)); + HANDLE_CODE(pnb_eps_c_io_t.pack(bref)); + HANDLE_CODE(pnb_5gs_c_io_t.pack(bref)); + HANDLE_CODE(ng_ran_rcu.pack(bref)); + HANDLE_CODE(sms_requested.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GS update type +// Reference: 9.11.3.9A +SRSASN_CODE update_type_5gs_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); // 2 Spare bits + bref.advance_bits(2); + HANDLE_CODE(pnb_eps_c_io_t.unpack(bref)); + HANDLE_CODE(pnb_5gs_c_io_t.unpack(bref)); + HANDLE_CODE(ng_ran_rcu.unpack(bref)); + HANDLE_CODE(sms_requested.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* update_type_5gs_t::SMS_requested_type_::to_string() const +{ + switch (value) { + case SMS_requested_type_::sms_over_nas_not_supported: + return "SMS over NAS not supported"; + case SMS_requested_type_::sms_over_nas_supported: + return "SMS over NAS supported"; + default: + return "Invalid Choice"; + } +} +const char* update_type_5gs_t::NG_RAN_RCU_type_::to_string() const +{ + switch (value) { + case NG_RAN_RCU_type_::ue_radio_capability_update_not_needed: + return "UE radio capability update not needed"; + case NG_RAN_RCU_type_::ue_radio_capability_update_needed: + return "UE radio capability update needed"; + default: + return "Invalid Choice"; + } +} +const char* update_type_5gs_t::PNB_5GS_CIoT_type_::to_string() const +{ + switch (value) { + case PNB_5GS_CIoT_type_::no_additional_information: + return "no additional information"; + case PNB_5GS_CIoT_type_::control_plane_c_io_t_5gs_optimization: + return "control plane CIoT 5GS optimization"; + case PNB_5GS_CIoT_type_::user_plane_c_io_t_5gs_optimization: + return "user plane CIoT 5GS optimization"; + case PNB_5GS_CIoT_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} +const char* update_type_5gs_t::PNB_EPS_CIoT_type_::to_string() const +{ + switch (value) { + case PNB_EPS_CIoT_type_::no_additional_information: + return "no additional information"; + case PNB_EPS_CIoT_type_::control_plane_c_io_t_eps_optimization: + return "control plane CIoT EPS optimization"; + case PNB_EPS_CIoT_type_::user_plane_c_io_t_eps_optimization: + return "user plane CIoT EPS optimization"; + case PNB_EPS_CIoT_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} +// IE: Mobile station classmark 2 +// Reference: 9.11.3.31C +SRSASN_CODE mobile_station_classmark_2_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 3) { + asn1::log_error("Encoding Failed (Mobile station classmark 2): Packed length (%d) does not equal expected length 3", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Mobile station classmark 2 +// Reference: 9.11.3.31C +SRSASN_CODE mobile_station_classmark_2_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 3) { + asn1::log_error("Decoding Failed (Mobile station classmark 2): Length (%d) does not equal expected length 3", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: Supported codec list +// Reference: 9.11.3.51A +SRSASN_CODE supported_codec_list_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 3) { + asn1::log_error("Encoding Failed (Supported codec list): Packed length (%d) is not in range of min: 3 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Supported codec list +// Reference: 9.11.3.51A +SRSASN_CODE supported_codec_list_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 3) { + asn1::log_error("Decoding Failed (Supported codec list): Length (%d) is not in range of min: 3 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: message container +// Reference: 9.11.3.33 +SRSASN_CODE message_container_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + HANDLE_CODE(bref.pack_bytes(nas_message_container.data(), nas_message_container.size())); + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + if (length < 1 || length > 65532) { + asn1::log_error( + "Encoding Failed (message container): Packed length (%d) is not in range of min: 1 and max 65532 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: message container +// Reference: 9.11.3.33 +SRSASN_CODE message_container_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + if (length < 1 || length > 65532) { + asn1::log_error("Decoding Failed (message container): Length (%d) is not in range of min: 1 and max 65532 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + nas_message_container.resize(length); + HANDLE_CODE(bref.unpack_bytes(nas_message_container.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: EPS bearer context status +// Reference: 9.11.3.23A +SRSASN_CODE eps_bearer_context_status_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(ebi_7, 1)); + HANDLE_CODE(bref.pack(ebi_6, 1)); + HANDLE_CODE(bref.pack(ebi_5, 1)); + HANDLE_CODE(bref.pack(ebi_4, 1)); + HANDLE_CODE(bref.pack(ebi_3, 1)); + HANDLE_CODE(bref.pack(ebi_2, 1)); + HANDLE_CODE(bref.pack(ebi_1, 1)); + HANDLE_CODE(bref.pack(ebi_0, 1)); + HANDLE_CODE(bref.pack(ebi_15, 1)); + HANDLE_CODE(bref.pack(ebi_14, 1)); + HANDLE_CODE(bref.pack(ebi_13, 1)); + HANDLE_CODE(bref.pack(ebi_12, 1)); + HANDLE_CODE(bref.pack(ebi_11, 1)); + HANDLE_CODE(bref.pack(ebi_10, 1)); + HANDLE_CODE(bref.pack(ebi_9, 1)); + HANDLE_CODE(bref.pack(ebi_8, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 2) { + asn1::log_error("Encoding Failed (EPS bearer context status): Packed length (%d) does not equal expected length 2", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: EPS bearer context status +// Reference: 9.11.3.23A +SRSASN_CODE eps_bearer_context_status_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 2) { + asn1::log_error("Decoding Failed (EPS bearer context status): Length (%d) does not equal expected length 2", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(ebi_7, 1)); + HANDLE_CODE(bref.unpack(ebi_6, 1)); + HANDLE_CODE(bref.unpack(ebi_5, 1)); + HANDLE_CODE(bref.unpack(ebi_4, 1)); + HANDLE_CODE(bref.unpack(ebi_3, 1)); + HANDLE_CODE(bref.unpack(ebi_2, 1)); + HANDLE_CODE(bref.unpack(ebi_1, 1)); + HANDLE_CODE(bref.unpack(ebi_0, 1)); + HANDLE_CODE(bref.unpack(ebi_15, 1)); + HANDLE_CODE(bref.unpack(ebi_14, 1)); + HANDLE_CODE(bref.unpack(ebi_13, 1)); + HANDLE_CODE(bref.unpack(ebi_12, 1)); + HANDLE_CODE(bref.unpack(ebi_11, 1)); + HANDLE_CODE(bref.unpack(ebi_10, 1)); + HANDLE_CODE(bref.unpack(ebi_9, 1)); + HANDLE_CODE(bref.unpack(ebi_8, 1)); + return SRSASN_SUCCESS; +} + +// IE: Extended DRX parameters +// Reference: 9.11.3.26A +SRSASN_CODE extended_drx_parameters_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(paging__time__window.pack(bref)); + HANDLE_CODE(e_drx_value.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (Extended DRX parameters): Packed length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Extended DRX parameters +// Reference: 9.11.3.26A +SRSASN_CODE extended_drx_parameters_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (Extended DRX parameters): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(paging__time__window.unpack(bref)); + HANDLE_CODE(e_drx_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* extended_drx_parameters_t::Paging_Time_Window_type_::to_string() const +{ + switch (value) { + case Paging_Time_Window_type_::seconds_0: + return "seconds_0"; + case Paging_Time_Window_type_::second_1: + return "second_1"; + case Paging_Time_Window_type_::seconds_2: + return "seconds_2"; + case Paging_Time_Window_type_::seconds_3: + return "seconds_3"; + case Paging_Time_Window_type_::seconds_4: + return "seconds_4"; + case Paging_Time_Window_type_::seconds_5: + return "seconds_5"; + case Paging_Time_Window_type_::seconds_6: + return "seconds_6"; + case Paging_Time_Window_type_::seconds_7: + return "seconds_7"; + case Paging_Time_Window_type_::seconds_8: + return "seconds_8"; + case Paging_Time_Window_type_::seconds_9: + return "seconds_9"; + case Paging_Time_Window_type_::seconds_10: + return "seconds_10"; + case Paging_Time_Window_type_::seconds_12: + return "seconds_12"; + case Paging_Time_Window_type_::seconds_14: + return "seconds_14"; + case Paging_Time_Window_type_::seconds_16: + return "seconds_16"; + case Paging_Time_Window_type_::seconds_18: + return "seconds_18"; + case Paging_Time_Window_type_::seconds_20: + return "seconds_20"; + default: + return "Invalid Choice"; + } +} +const char* extended_drx_parameters_t::eDRX_value_type_::to_string() const +{ + switch (value) { + case eDRX_value_type_::second_1_28: + return "second_1_28"; + case eDRX_value_type_::second_2_56: + return "second_2_56"; + case eDRX_value_type_::second_3_84: + return "second_3_84"; + case eDRX_value_type_::second_5_12: + return "second_5_12"; + case eDRX_value_type_::second_6_4: + return "second_6_4"; + case eDRX_value_type_::second_7_68: + return "second_7_68"; + case eDRX_value_type_::second_8_96: + return "second_8_96"; + case eDRX_value_type_::second_10_24: + return "second_10_24"; + case eDRX_value_type_::second_11_52: + return "second_11_52"; + case eDRX_value_type_::second_12_8: + return "second_12_8"; + case eDRX_value_type_::second_14_08: + return "second_14_08"; + case eDRX_value_type_::second_15_36: + return "second_15_36"; + case eDRX_value_type_::second_16_64: + return "second_16_64"; + case eDRX_value_type_::second_17_92: + return "second_17_92"; + case eDRX_value_type_::second_19_20: + return "second_19_20"; + case eDRX_value_type_::second_20_48: + return "second_20_48"; + default: + return "Invalid Choice"; + } +} +// IE: GPRS timer 3 +// Reference: 9.11.2.5 +SRSASN_CODE gprs_timer_3_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(unit.pack(bref)); + HANDLE_CODE(bref.pack(timer_value, 5)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (GPRS timer 3): Packed length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: GPRS timer 3 +// Reference: 9.11.2.5 +SRSASN_CODE gprs_timer_3_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (GPRS timer 3): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(unit.unpack(bref)); + HANDLE_CODE(bref.unpack(timer_value, 5)); + return SRSASN_SUCCESS; +} + +const char* gprs_timer_3_t::Unit_type_::to_string() const +{ + switch (value) { + case Unit_type_::value_is_incremented_in_multiples_of_10_minutes: + return "value is incremented in multiples of 10 minutes"; + case Unit_type_::value_is_incremented_in_multiples_of_1_hour: + return "value is incremented in multiples of 1 hour"; + case Unit_type_::value_is_incremented_in_multiples_of_10_hours: + return "value is incremented in multiples of 10 hours"; + case Unit_type_::value_is_incremented_in_multiples_of_2_seconds: + return "value is incremented in multiples of 2 seconds"; + case Unit_type_::value_is_incremented_in_multiples_of_30_seconds: + return "value is incremented in multiples of 30 seconds"; + case Unit_type_::value_is_incremented_in_multiples_of_1_minute: + return "value is incremented in multiples of 1 minute"; + case Unit_type_::value_is_incremented_in_multiples_of_320_hours: + return "value is incremented in multiples of 320 hours"; + case Unit_type_::value_indicates_that_the_timer_is_deactivated: + return "value indicates that the timer is deactivated"; + default: + return "Invalid Choice"; + } +} +// IE: UE radio capability ID +// Reference: 9.11.3.68 +SRSASN_CODE ue_radio_capability_id_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(ue_radio_capability_id.data(), ue_radio_capability_id.size())); + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: UE radio capability ID +// Reference: 9.11.3.68 +SRSASN_CODE ue_radio_capability_id_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + ue_radio_capability_id.resize(length); + HANDLE_CODE(bref.unpack_bytes(ue_radio_capability_id.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Mapped NSSAI +// Reference: 9.11.3.31B +SRSASN_CODE mapped_nssai_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 40) { + asn1::log_error("Encoding Failed (Mapped NSSAI): Packed length (%d) is not in range of min: 2 and max 40 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Mapped NSSAI +// Reference: 9.11.3.31B +SRSASN_CODE mapped_nssai_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 40) { + asn1::log_error("Decoding Failed (Mapped NSSAI): Length (%d) is not in range of min: 2 and max 40 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: Additional information requested +// Reference: 9.11.3.12A +SRSASN_CODE additional_information_requested_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 7 Spare bits + HANDLE_CODE(bref.pack(0x0, 7)); + HANDLE_CODE(bref.pack(cipher_key, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Additional information requested +// Reference: 9.11.3.12A +SRSASN_CODE additional_information_requested_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); // 7 Spare bits + bref.advance_bits(7); + HANDLE_CODE(bref.unpack(cipher_key, 1)); + return SRSASN_SUCCESS; +} + +// IE: WUS assistance information +// Reference: 9.11.3.71 +SRSASN_CODE wus_assistance_information_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1) { + asn1::log_error("Encoding Failed (WUS assistance information): Packed length (%d) is not in range of min: 1 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: WUS assistance information +// Reference: 9.11.3.71 +SRSASN_CODE wus_assistance_information_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1) { + asn1::log_error("Decoding Failed (WUS assistance information): Length (%d) is not in range of min: 1 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: N5GC indication +// Reference: 9.11.3.72 +SRSASN_CODE n5gc_indication_t::pack(asn1::bit_ref& bref) +{ + // 3 Spare bits + HANDLE_CODE(bref.pack(0x0, 3)); + HANDLE_CODE(bref.pack(n5gcreg, 1)); + return SRSASN_SUCCESS; +} + +// IE: N5GC indication +// Reference: 9.11.3.72 +SRSASN_CODE n5gc_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 3 Spare bits + bref.advance_bits(3); + HANDLE_CODE(bref.unpack(n5gcreg, 1)); + return SRSASN_SUCCESS; +} + +// IE: NB-N1 mode DRX parameters +// Reference: 9.11.3.73 +SRSASN_CODE nb_n1_mode_drx_parameters_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 4 Spare bits + HANDLE_CODE(bref.pack(0x0, 4)); + HANDLE_CODE(nb_n1_mode_drx_value.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (NB-N1 mode DRX parameters): Packed length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: NB-N1 mode DRX parameters +// Reference: 9.11.3.73 +SRSASN_CODE nb_n1_mode_drx_parameters_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (NB-N1 mode DRX parameters): Length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 4 Spare bits + bref.advance_bits(4); + HANDLE_CODE(nb_n1_mode_drx_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* nb_n1_mode_drx_parameters_t::nb_n1_mode_drx_value_type_::to_string() const +{ + switch (value) { + case nb_n1_mode_drx_value_type_::drx_value_not_specified: + return "DRX value not specified"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_32: + return "DRX cycle parameter T 32"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_64: + return "DRX cycle parameter T 64"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_128: + return "DRX cycle parameter T 128"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_256: + return "DRX cycle parameter T 256"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_512: + return "DRX cycle parameter T 512"; + case nb_n1_mode_drx_value_type_::drx_cycle_parameter_t_1024: + return "DRX cycle parameter T 1024"; + default: + return "Invalid Choice"; + } +} +// IE: 5GS registration result +// Reference: 9.11.3.6 +SRSASN_CODE registration_result_5gs_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 2 Spare bits + HANDLE_CODE(bref.pack(0x0, 2)); + HANDLE_CODE(emergency_registered.pack(bref)); + HANDLE_CODE(nssaa_to_be_performed.pack(bref)); + HANDLE_CODE(sms_allowed.pack(bref)); + HANDLE_CODE(registration_result.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (5GS registration result): Packed length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GS registration result +// Reference: 9.11.3.6 +SRSASN_CODE registration_result_5gs_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (5GS registration result): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 2 Spare bits + bref.advance_bits(2); + HANDLE_CODE(emergency_registered.unpack(bref)); + HANDLE_CODE(nssaa_to_be_performed.unpack(bref)); + HANDLE_CODE(sms_allowed.unpack(bref)); + HANDLE_CODE(registration_result.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* registration_result_5gs_t::Emergency_registered_type_::to_string() const +{ + switch (value) { + case Emergency_registered_type_::not_registered_for_emergency_services: + return "Not registered for emergency services"; + case Emergency_registered_type_::registered_for_emergency_services: + return "Registered for emergency services"; + default: + return "Invalid Choice"; + } +} +const char* registration_result_5gs_t::NSSAA_to_be_performed_type_::to_string() const +{ + switch (value) { + case NSSAA_to_be_performed_type_::nssaa_is_not_to_be_performed: + return "NSSAA is not to be performed"; + case NSSAA_to_be_performed_type_::nssaa_is_to_be_performed: + return "NSSAA is to be performed"; + default: + return "Invalid Choice"; + } +} +const char* registration_result_5gs_t::SMS_allowed_type_::to_string() const +{ + switch (value) { + case SMS_allowed_type_::sms_over_nas_not_allowed: + return "SMS over NAS not allowed"; + case SMS_allowed_type_::sms_over_nas_allowed: + return "SMS over NAS allowed"; + default: + return "Invalid Choice"; + } +} +const char* registration_result_5gs_t::registration_result_type_::to_string() const +{ + switch (value) { + case registration_result_type_::access_3_gpp: + return "access 3GPP"; + case registration_result_type_::non_3_gpp_access: + return "Non-3GPP access"; + case registration_result_type_::access_3_gpp_and_non_3_gpp_access: + return "access 3GPP and non-3GPP access"; + case registration_result_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} +// IE: PLMN list +// Reference: 9.11.3.45 +SRSASN_CODE plmn_list_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: PLMN list +// Reference: 9.11.3.45 +SRSASN_CODE plmn_list_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: 5GS tracking area identity list +// Reference: 9.11.3.9 +SRSASN_CODE tracking_area_identity_list_5gs_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 7) { + asn1::log_error( + "Encoding Failed (5GS tracking area identity list): Packed length (%d) does not equal expected length 7", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GS tracking area identity list +// Reference: 9.11.3.9 +SRSASN_CODE tracking_area_identity_list_5gs_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 7) { + asn1::log_error("Decoding Failed (5GS tracking area identity list): Length (%d) does not equal expected length 7", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +const char* tracking_area_identity_list_5gs_t::type_of_list_type_::to_string() const +{ + switch (value) { + case type_of_list_type_::list_of_ta_cs_belonging_to_one_plmn_or_snpn_with_non_consecutive_tac_values: + return "list of TACs belonging to one PLMN or SNPN, with non-consecutive TAC values"; + case type_of_list_type_::list_of_ta_cs_belonging_to_one_plmn_or_snpn_with_consecutive_tac_values: + return "list of TACs belonging to one PLMN or SNPN, with consecutive TAC values"; + case type_of_list_type_::list_of_ta_is_belonging_to_different_plm_ns: + return "list of TAIs belonging to different PLMNs"; + case type_of_list_type_::reserved: + return "Reserved"; + default: + return "Invalid Choice"; + } +} +// IE: Rejected NSSAI +// Reference: 9.11.3.46 +SRSASN_CODE rejected_nssai_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Rejected NSSAI +// Reference: 9.11.3.46 +SRSASN_CODE rejected_nssai_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: 5GS network feature support +// Reference: 9.11.3.5 +SRSASN_CODE network_feature_support_5gs_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1 || length > 3) { + asn1::log_error( + "Encoding Failed (5GS network feature support): Packed length (%d) is not in range of min: 1 and max 3 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GS network feature support +// Reference: 9.11.3.5 +SRSASN_CODE network_feature_support_5gs_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1 || length > 3) { + asn1::log_error( + "Decoding Failed (5GS network feature support): Length (%d) is not in range of min: 1 and max 3 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: PDU session reactivation result +// Reference: 9.11.3.42 +SRSASN_CODE pdu_session_reactivation_result_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(psi_7, 1)); + HANDLE_CODE(bref.pack(psi_6, 1)); + HANDLE_CODE(bref.pack(psi_5, 1)); + HANDLE_CODE(bref.pack(psi_4, 1)); + HANDLE_CODE(bref.pack(psi_3, 1)); + HANDLE_CODE(bref.pack(psi_2, 1)); + HANDLE_CODE(bref.pack(psi_1, 1)); + HANDLE_CODE(bref.pack(psi_0, 1)); + HANDLE_CODE(bref.pack(psi_15, 1)); + HANDLE_CODE(bref.pack(psi_14, 1)); + HANDLE_CODE(bref.pack(psi_13, 1)); + HANDLE_CODE(bref.pack(psi_12, 1)); + HANDLE_CODE(bref.pack(psi_11, 1)); + HANDLE_CODE(bref.pack(psi_10, 1)); + HANDLE_CODE(bref.pack(psi_9, 1)); + HANDLE_CODE(bref.pack(psi_8, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 32) { + asn1::log_error("Encoding Failed (PDU session reactivation result): Packed length (%d) is not in range of min: 2 " + "and max 32 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: PDU session reactivation result +// Reference: 9.11.3.42 +SRSASN_CODE pdu_session_reactivation_result_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 32) { + asn1::log_error( + "Decoding Failed (PDU session reactivation result): Length (%d) is not in range of min: 2 and max 32 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + HANDLE_CODE(bref.unpack(psi_7, 1)); + HANDLE_CODE(bref.unpack(psi_6, 1)); + HANDLE_CODE(bref.unpack(psi_5, 1)); + HANDLE_CODE(bref.unpack(psi_4, 1)); + HANDLE_CODE(bref.unpack(psi_3, 1)); + HANDLE_CODE(bref.unpack(psi_2, 1)); + HANDLE_CODE(bref.unpack(psi_1, 1)); + HANDLE_CODE(bref.unpack(psi_0, 1)); + + HANDLE_CODE(bref.unpack(psi_15, 1)); + HANDLE_CODE(bref.unpack(psi_14, 1)); + HANDLE_CODE(bref.unpack(psi_13, 1)); + HANDLE_CODE(bref.unpack(psi_12, 1)); + HANDLE_CODE(bref.unpack(psi_11, 1)); + HANDLE_CODE(bref.unpack(psi_10, 1)); + HANDLE_CODE(bref.unpack(psi_9, 1)); + HANDLE_CODE(bref.unpack(psi_8, 1)); + + if (length > 2) { + HANDLE_CODE(bref.advance_bits((length - 2) * 8)); + } + return SRSASN_SUCCESS; +} + +// IE: PDU session reactivation result error cause +// Reference: 9.11.3.43 +SRSASN_CODE pdu_session_reactivation_result_error_cause_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + if (length < 2 || length > 512) { + asn1::log_error("Encoding Failed (PDU session reactivation result error cause): Packed length (%d) is not in range " + "of min: 2 and max 512 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: PDU session reactivation result error cause +// Reference: 9.11.3.43 +SRSASN_CODE pdu_session_reactivation_result_error_cause_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + if (length < 2 || length > 512) { + asn1::log_error("Decoding Failed (PDU session reactivation result error cause): Length (%d) is not in range of " + "min: 2 and max 512 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: LADN information +// Reference: 9.11.3.30 +SRSASN_CODE ladn_information_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // min. length of 0 not checked: uint underflow + if (length > 1712) { + asn1::log_error("Encoding Failed (LADN information): Packed length (%d) is not in range of max 1712 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: LADN information +// Reference: 9.11.3.30 +SRSASN_CODE ladn_information_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // min. length of 0 not checked: uint underflow + if (length > 1712) { + asn1::log_error("Decoding Failed (LADN information): Length (%d) is not in range of max 1712 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: Service area list +// Reference: 9.11.3.49 +SRSASN_CODE service_area_list_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Service area list +// Reference: 9.11.3.49 +SRSASN_CODE service_area_list_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: GPRS timer 2 +// Reference: 9.11.2.4 +SRSASN_CODE gprs_timer_2_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(timer_value, 8)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (GPRS timer 2): Packed length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: GPRS timer 2 +// Reference: 9.11.2.4 +SRSASN_CODE gprs_timer_2_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (GPRS timer 2): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(timer_value, 8)); + return SRSASN_SUCCESS; +} + +// IE: Emergency number list +// Reference: 9.11.3.23 +SRSASN_CODE emergency_number_list_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 3 || length > 48) { + asn1::log_error( + "Encoding Failed (Emergency number list): Packed length (%d) is not in range of min: 3 and max 48 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Emergency number list +// Reference: 9.11.3.23 +SRSASN_CODE emergency_number_list_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 3 || length > 48) { + asn1::log_error("Decoding Failed (Emergency number list): Length (%d) is not in range of min: 3 and max 48 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: Extended emergency number list +// Reference: 9.11.3.26 +SRSASN_CODE extended_emergency_number_list_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // max. length of 65535 not checked: uint overflow + if (length < 4) { + asn1::log_error( + "Encoding Failed (Extended emergency number list): Packed length (%d) is not in range of min: 4 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: Extended emergency number list +// Reference: 9.11.3.26 +SRSASN_CODE extended_emergency_number_list_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // max. length of 65535 not checked: uint overflow + if (length < 4) { + asn1::log_error("Decoding Failed (Extended emergency number list): Length (%d) is not in range of min: 4 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: SOR transparent container +// Reference: 9.11.3.51 +SRSASN_CODE sor_transparent_container_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + if (length < 17) { + asn1::log_error("Encoding Failed (SOR transparent container): Packed length (%d) is not in range of min: 17 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: SOR transparent container +// Reference: 9.11.3.51 +SRSASN_CODE sor_transparent_container_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + if (length < 17) { + asn1::log_error("Decoding Failed (SOR transparent container): Length (%d) is not in range of min: 17 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: EAP message +// Reference: 9.11.2.2 +SRSASN_CODE eap_message_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + HANDLE_CODE(bref.pack_bytes(eap_message.data(), eap_message.size())); + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + if (length < 4 || length > 1500) { + asn1::log_error("Encoding Failed (EAP message): Packed length (%d) is not in range of min: 4 and max 1500 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: EAP message +// Reference: 9.11.2.2 +SRSASN_CODE eap_message_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + if (length < 4 || length > 1500) { + asn1::log_error("Decoding Failed (EAP message): Length (%d) is not in range of min: 4 and max 1500 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + eap_message.resize(length); + HANDLE_CODE(bref.unpack_bytes(eap_message.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: NSSAI inclusion mode +// Reference: 9.11.3.37A +SRSASN_CODE nssai_inclusion_mode_t::pack(asn1::bit_ref& bref) +{ + // 2 Spare bits + HANDLE_CODE(bref.pack(0x0, 2)); + HANDLE_CODE(nssai_inclusion_mode.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: NSSAI inclusion mode +// Reference: 9.11.3.37A +SRSASN_CODE nssai_inclusion_mode_t::unpack(asn1::cbit_ref& bref) +{ + // 2 Spare bits + bref.advance_bits(2); + HANDLE_CODE(nssai_inclusion_mode.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* nssai_inclusion_mode_t::NSSAI_inclusion_mode_type_::to_string() const +{ + switch (value) { + case NSSAI_inclusion_mode_type_::nssai_inclusion_mode_a: + return "NSSAI inclusion mode A"; + case NSSAI_inclusion_mode_type_::nssai_inclusion_mode_b: + return "NSSAI inclusion mode B"; + case NSSAI_inclusion_mode_type_::nssai_inclusion_mode_c: + return "NSSAI inclusion mode C"; + case NSSAI_inclusion_mode_type_::nssai_inclusion_mode_d: + return "NSSAI inclusion mode D"; + default: + return "Invalid Choice"; + } +} +// IE: Operator-defined access category definitions +// Reference: 9.11.3.38 +SRSASN_CODE operator_defined_access_category_definitions_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: Operator-defined access category definitions +// Reference: 9.11.3.38 +SRSASN_CODE operator_defined_access_category_definitions_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: Non-3GPP NW provided policies +// Reference: 9.11.3.36A +SRSASN_CODE non_3_gpp_nw_provided_policies_t::pack(asn1::bit_ref& bref) +{ + // 3 Spare bits + HANDLE_CODE(bref.pack(0x0, 3)); + HANDLE_CODE(bref.pack(n3_en, 1)); + return SRSASN_SUCCESS; +} + +// IE: Non-3GPP NW provided policies +// Reference: 9.11.3.36A +SRSASN_CODE non_3_gpp_nw_provided_policies_t::unpack(asn1::cbit_ref& bref) +{ + // 3 Spare bits + bref.advance_bits(3); + HANDLE_CODE(bref.unpack(n3_en, 1)); + return SRSASN_SUCCESS; +} + +// IE: UE radio capability ID deletion indication +// Reference: 9.11.3.69 +SRSASN_CODE ue_radio_capability_id_deletion_indication_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(deletion_request.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: UE radio capability ID deletion indication +// Reference: 9.11.3.69 +SRSASN_CODE ue_radio_capability_id_deletion_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(deletion_request.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* ue_radio_capability_id_deletion_indication_t::Deletion_request_type_::to_string() const +{ + switch (value) { + case Deletion_request_type_::ue_radio_capability_id_deletion_not_requested: + return "UE radio capability ID deletion not requested"; + case Deletion_request_type_::network_assigned_ue_radio_capability_i_ds_deletion_requested: + return "Network-assigned UE radio capability IDs deletion requested"; + default: + return "Invalid Choice"; + } +} +// IE: Ciphering key data +// Reference: 9.11.3.18C +SRSASN_CODE ciphering_key_data_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + if (length < 31 || length > 2672) { + asn1::log_error( + "Encoding Failed (Ciphering key data): Packed length (%d) is not in range of min: 31 and max 2672 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: Ciphering key data +// Reference: 9.11.3.18C +SRSASN_CODE ciphering_key_data_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + if (length < 31 || length > 2672) { + asn1::log_error("Decoding Failed (Ciphering key data): Length (%d) is not in range of min: 31 and max 2672 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: CAG information list +// Reference: 9.11.3.18A +SRSASN_CODE cag_information_list_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: CAG information list +// Reference: 9.11.3.18A +SRSASN_CODE cag_information_list_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: Truncated 5G-S-TMSI configuration +// Reference: 9.11.3.70 +SRSASN_CODE truncated_5g_s_tmsi_configuration_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(truncated_amf__set_id_value, 4)); + HANDLE_CODE(bref.pack(truncated_amf__pointer_value, 4)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error( + "Encoding Failed (Truncated 5G-S-TMSI configuration): Packed length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Truncated 5G-S-TMSI configuration +// Reference: 9.11.3.70 +SRSASN_CODE truncated_5g_s_tmsi_configuration_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (Truncated 5G-S-TMSI configuration): Length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(truncated_amf__set_id_value, 4)); + HANDLE_CODE(bref.unpack(truncated_amf__pointer_value, 4)); + return SRSASN_SUCCESS; +} + +// IE: 5GMM cause +// Reference: 9.11.3.2 +SRSASN_CODE cause_5gmm_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(cause_5gmm.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: 5GMM cause +// Reference: 9.11.3.2 +SRSASN_CODE cause_5gmm_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(cause_5gmm.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* cause_5gmm_t::cause_5gmm_type_::to_string() const +{ + switch (value) { + case cause_5gmm_type_::illegal_ue: + return "Illegal UE"; + case cause_5gmm_type_::pei_not_accepted: + return "PEI not accepted"; + case cause_5gmm_type_::illegal_me: + return "Illegal ME"; + case cause_5gmm_type_::services_not_allowed_5gs: + return "Services not allowed 5GS"; + case cause_5gmm_type_::ue_identity_cannot_be_derived_by_the_network: + return "UE identity cannot be derived by the network"; + case cause_5gmm_type_::implicitly_de_registered: + return "Implicitly de-registered"; + case cause_5gmm_type_::plmn_not_allowed: + return "PLMN not allowed"; + case cause_5gmm_type_::tracking_area_not_allowed: + return "Tracking area not allowed"; + case cause_5gmm_type_::roaming_not_allowed_in_this_tracking_area: + return "Roaming not allowed in this tracking area"; + case cause_5gmm_type_::no_suitable_cells_in_tracking_area: + return "No suitable cells in tracking area"; + case cause_5gmm_type_::mac_failure: + return "MAC failure"; + case cause_5gmm_type_::synch_failure: + return "Synch failure"; + case cause_5gmm_type_::congestion: + return "Congestion"; + case cause_5gmm_type_::ue_security_capabilities_mismatch: + return "UE security capabilities mismatch"; + case cause_5gmm_type_::security_mode_rejected_unspecified: + return "Security mode rejected, unspecified"; + case cause_5gmm_type_::non_5g_authentication_unacceptable: + return "Non-5G authentication unacceptable"; + case cause_5gmm_type_::n1_mode_not_allowed: + return "N1 mode not allowed"; + case cause_5gmm_type_::restricted_service_area: + return "Restricted service area"; + case cause_5gmm_type_::redirection_to_epc_required: + return "Redirection to EPC required"; + case cause_5gmm_type_::ladn_not_available: + return "LADN not available"; + case cause_5gmm_type_::no_network_slices_available: + return "No network slices available"; + case cause_5gmm_type_::maximum_number_of_pdu_sessions_reached_: + return "Maximum number of PDU sessions reached"; + case cause_5gmm_type_::insufficient_resources_for_specific_slice_and_dnn: + return "Insufficient resources for specific slice and DNN"; + case cause_5gmm_type_::insufficient_resources_for_specific_slice: + return "Insufficient resources for specific slice"; + case cause_5gmm_type_::ng_ksi_already_in_use: + return "ngKSI already in use"; + case cause_5gmm_type_::non_3_gpp_access_to_5gcn_not_allowed: + return "Non-3GPP access to 5GCN not allowed"; + case cause_5gmm_type_::serving_network_not_authorized: + return "Serving network not authorized"; + case cause_5gmm_type_::temporarily_not_authorized_for_this_snpn: + return "Temporarily not authorized for this SNPN"; + case cause_5gmm_type_::permanently_not_authorized_for_this_snpn: + return "Permanently not authorized for this SNPN"; + case cause_5gmm_type_::not_authorized_for_this_cag_or_authorized_for_cag_cells_only: + return "Not authorized for this CAG or authorized for CAG cells only"; + case cause_5gmm_type_::wireline_access_area_not_allowed: + return "Wireline access area not allowed"; + case cause_5gmm_type_::payload_was_not_forwarded: + return "Payload was not forwarded"; + case cause_5gmm_type_::dnn_not_supported_or_not_subscribed_in_the_slice: + return "DNN not supported or not subscribed in the slice"; + case cause_5gmm_type_::insufficient_user_plane_resources_for_the_pdu_session: + return "Insufficient user-plane resources for the PDU session"; + case cause_5gmm_type_::semantically_incorrect_message: + return "Semantically incorrect message"; + case cause_5gmm_type_::invalid_mandatory_information: + return "Invalid mandatory information"; + case cause_5gmm_type_::message_type_non_existent_or_not_implemented: + return "Message type non-existent or not implemented"; + case cause_5gmm_type_::message_type_not_compatible_with_the_protocol_state: + return "Message type not compatible with the protocol state"; + case cause_5gmm_type_::information_element_non_existent_or_not_implemented: + return "Information element non-existent or not implemented"; + case cause_5gmm_type_::conditional_ie_error: + return "Conditional IE error"; + case cause_5gmm_type_::message_not_compatible_with_the_protocol_state: + return "Message not compatible with the protocol state"; + case cause_5gmm_type_::protocol_error_unspecified: + return "Protocol error, unspecified"; + default: + return "Invalid Choice"; + } +} +// IE: De-registration type +// Reference: 9.11.3.20 +SRSASN_CODE de_registration_type_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(switch_off.pack(bref)); + HANDLE_CODE(re_registration_required.pack(bref)); + HANDLE_CODE(access_type.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: De-registration type +// Reference: 9.11.3.20 +SRSASN_CODE de_registration_type_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(switch_off.unpack(bref)); + HANDLE_CODE(re_registration_required.unpack(bref)); + HANDLE_CODE(access_type.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* de_registration_type_t::switch_off_type_::to_string() const +{ + switch (value) { + case switch_off_type_::normal_de_registration: + return "Normal de-registration"; + case switch_off_type_::switch_off: + return "Switch Off"; + default: + return "Invalid Choice"; + } +} +const char* de_registration_type_t::re_registration_required_type_::to_string() const +{ + switch (value) { + case re_registration_required_type_::re_registration_not_required: + return "re-registration not required"; + case re_registration_required_type_::re_registration_required: + return "re-registration required"; + default: + return "Invalid Choice"; + } +} +const char* de_registration_type_t::access_type_type_::to_string() const +{ + switch (value) { + case access_type_type_::access_3_gpp: + return "access 3GPP"; + case access_type_type_::non_3_gpp_access: + return "Non-3GPP access"; + case access_type_type_::access_3_gpp_and_non_3_gpp_access: + return "access 3GPP and non-3GPP access"; + default: + return "Invalid Choice"; + } +} +// IE: Spare half octet +// Reference: 9.5 +SRSASN_CODE spare_half_octet_t::pack(asn1::bit_ref& bref) +{ + // 4 Spare bits + HANDLE_CODE(bref.pack(0x0, 4)); + return SRSASN_SUCCESS; +} + +// IE: Spare half octet +// Reference: 9.5 +SRSASN_CODE spare_half_octet_t::unpack(asn1::cbit_ref& bref) +{ + // 4 Spare bits + bref.advance_bits(4); + return SRSASN_SUCCESS; +} + +// IE: Service type +// Reference: 9.11.3.50 +SRSASN_CODE service_type_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(service_type_value.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: Service type +// Reference: 9.11.3.50 +SRSASN_CODE service_type_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(service_type_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* service_type_t::Service_type_value_type_::to_string() const +{ + switch (value) { + case Service_type_value_type_::signalling: + return "signalling"; + case Service_type_value_type_::data: + return "data"; + case Service_type_value_type_::mobile_terminated_services: + return "mobile terminated services"; + case Service_type_value_type_::emergency_services: + return "emergency services"; + case Service_type_value_type_::emergency_services_fallback: + return "emergency services fallback"; + case Service_type_value_type_::high_priority_access: + return "high priority access"; + case Service_type_value_type_::elevated_signalling: + return "elevated signalling"; + case Service_type_value_type_::unused_shall_be_interpreted_as_signalling: + return "unused shall be interpreted as signalling"; + case Service_type_value_type_::unused_shall_be_interpreted_as_signalling_1: + return "unused shall be interpreted as signalling_1"; + case Service_type_value_type_::unused_shall_be_interpreted_as_data: + return "unused shall be interpreted as data"; + case Service_type_value_type_::unused_shall_be_interpreted_as_data_1: + return "unused shall be interpreted as data_1"; + case Service_type_value_type_::unused_shall_be_interpreted_as_data_2: + return "unused shall be interpreted as data_2"; + default: + return "Invalid Choice"; + } +} +// IE: Configuration update indication +// Reference: 9.11.3.18 +SRSASN_CODE configuration_update_indication_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(control_plane_service_type_value.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: Configuration update indication +// Reference: 9.11.3.18 +SRSASN_CODE configuration_update_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(control_plane_service_type_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* configuration_update_indication_t::control_plane_service_type_value_type_::to_string() const +{ + switch (value) { + case control_plane_service_type_value_type_::mobile_originating_request: + return "mobile originating request"; + case control_plane_service_type_value_type_::mobile_terminating_request: + return "mobile terminating request"; + case control_plane_service_type_value_type_::emergency_services: + return "emergency services"; + case control_plane_service_type_value_type_::emergency_services_fallback: + return "emergency services fallback"; + default: + return "Invalid Choice"; + } +} +// IE: Network name +// Reference: 9.11.3.35 +SRSASN_CODE network_name_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Network name +// Reference: 9.11.3.35 +SRSASN_CODE network_name_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: Time zone +// Reference: 9.11.3.52 +SRSASN_CODE time_zone_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(bref.pack(time_zone, 8)); + return SRSASN_SUCCESS; +} + +// IE: Time zone +// Reference: 9.11.3.52 +SRSASN_CODE time_zone_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(bref.unpack(time_zone, 8)); + return SRSASN_SUCCESS; +} + +// IE: Time zone and time +// Reference: 9.11.3.53 +SRSASN_CODE time_zone_and_time_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(bref.pack(year, 8)); + HANDLE_CODE(bref.pack(month, 8)); + HANDLE_CODE(bref.pack(day, 8)); + HANDLE_CODE(bref.pack(hour, 8)); + HANDLE_CODE(bref.pack(minute, 8)); + HANDLE_CODE(bref.pack(second, 8)); + HANDLE_CODE(bref.pack(time_zone, 8)); + return SRSASN_SUCCESS; +} + +// IE: Time zone and time +// Reference: 9.11.3.53 +SRSASN_CODE time_zone_and_time_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(bref.unpack(year, 8)); + HANDLE_CODE(bref.unpack(month, 8)); + HANDLE_CODE(bref.unpack(day, 8)); + HANDLE_CODE(bref.unpack(hour, 8)); + HANDLE_CODE(bref.unpack(minute, 8)); + HANDLE_CODE(bref.unpack(second, 8)); + HANDLE_CODE(bref.unpack(time_zone, 8)); + return SRSASN_SUCCESS; +} + +// IE: Daylight saving time +// Reference: 9.11.3.19 +SRSASN_CODE daylight_saving_time_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(value.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (Daylight saving time): Packed length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Daylight saving time +// Reference: 9.11.3.19 +SRSASN_CODE daylight_saving_time_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (Daylight saving time): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* daylight_saving_time_t::value_type_::to_string() const +{ + switch (value) { + case value_type_::no_adjustment_for_daylight_saving_time: + return "No adjustment for Daylight Saving Time"; + case value_type_::hour_1_adjustment_for_daylight_saving_time: + return "hour 1 adjustment for Daylight Saving Time"; + case value_type_::hours_2_adjustment_for_daylight_saving_time: + return "hours 2 adjustment for Daylight Saving Time"; + case value_type_::reserved: + return "Reserved"; + default: + return "Invalid Choice"; + } +} +// IE: SMS indication +// Reference: 9.11.3.50A +SRSASN_CODE sms_indication_t::pack(asn1::bit_ref& bref) +{ + // 3 Spare bits + HANDLE_CODE(bref.pack(0x0, 3)); + HANDLE_CODE(bref.pack(sms_availability_indication, 1)); + return SRSASN_SUCCESS; +} + +// IE: SMS indication +// Reference: 9.11.3.50A +SRSASN_CODE sms_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 3 Spare bits + bref.advance_bits(3); + HANDLE_CODE(bref.unpack(sms_availability_indication, 1)); + return SRSASN_SUCCESS; +} + +// IE: Additional configuration indication +// Reference: 9.11.3.74 +SRSASN_CODE additional_configuration_indication_t::pack(asn1::bit_ref& bref) +{ + // 3 Spare bits + HANDLE_CODE(bref.pack(0x0, 3)); + HANDLE_CODE(scmr.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: Additional configuration indication +// Reference: 9.11.3.74 +SRSASN_CODE additional_configuration_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 3 Spare bits + bref.advance_bits(3); + HANDLE_CODE(scmr.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* additional_configuration_indication_t::SCMR_type_::to_string() const +{ + switch (value) { + case SCMR_type_::no_additional_information: + return "no additional information"; + case SCMR_type_::release_of_n1_nas_signalling_connection_not_required: + return "release of N1 NAS signalling connection not required"; + default: + return "Invalid Choice"; + } +} +// IE: ABBA +// Reference: 9.11.3.10 +SRSASN_CODE abba_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(abba_contents.data(), abba_contents.size())); + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2) { + asn1::log_error("Encoding Failed (ABBA): Packed length (%d) is not in range of min: 2 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: ABBA +// Reference: 9.11.3.10 +SRSASN_CODE abba_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2) { + asn1::log_error("Decoding Failed (ABBA): Length (%d) is not in range of min: 2 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + abba_contents.resize(length); + HANDLE_CODE(bref.unpack_bytes(abba_contents.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Authentication parameter RAND +// Reference: 9.11.3.16 +SRSASN_CODE authentication_parameter_rand_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(bref.pack_bytes(rand.data(), 16)); + return SRSASN_SUCCESS; +} + +// IE: Authentication parameter RAND +// Reference: 9.11.3.16 +SRSASN_CODE authentication_parameter_rand_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(bref.unpack_bytes(rand.data(), 16)); + return SRSASN_SUCCESS; +} + +// IE: Authentication parameter AUTN +// Reference: 9.11.3.15 +SRSASN_CODE authentication_parameter_autn_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(autn.data(), autn.size())); + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 16) { + asn1::log_error( + "Encoding Failed (Authentication parameter AUTN): Packed length (%d) does not equal expected length 16", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Authentication parameter AUTN +// Reference: 9.11.3.15 +SRSASN_CODE authentication_parameter_autn_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 16) { + asn1::log_error("Decoding Failed (Authentication parameter AUTN): Length (%d) does not equal expected length 16", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + autn.resize(length); + HANDLE_CODE(bref.unpack_bytes(autn.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Authentication response parameter +// Reference: 9.11.3.17 +SRSASN_CODE authentication_response_parameter_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(res.data(), res.size())); + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 16) { + asn1::log_error( + "Encoding Failed (Authentication response parameter): Packed length (%d) does not equal expected length 16", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Authentication response parameter +// Reference: 9.11.3.17 +SRSASN_CODE authentication_response_parameter_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 16) { + asn1::log_error( + "Decoding Failed (Authentication response parameter): Length (%d) does not equal expected length 16", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + res.resize(length); + HANDLE_CODE(bref.unpack_bytes(res.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Authentication failure parameter +// Reference: 9.11.3.14 +SRSASN_CODE authentication_failure_parameter_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(auth_failure.data(), auth_failure.size())); + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 14) { + asn1::log_error( + "Encoding Failed (Authentication failure parameter): Packed length (%d) does not equal expected length 14", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Authentication failure parameter +// Reference: 9.11.3.14 +SRSASN_CODE authentication_failure_parameter_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 14) { + asn1::log_error("Decoding Failed (Authentication failure parameter): Length (%d) does not equal expected length 14", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + auth_failure.resize(length); + HANDLE_CODE(bref.unpack_bytes(auth_failure.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: 5GS identity type +// Reference: 9.11.3.3 +SRSASN_CODE identity_type_5gs_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(type_of_identity.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: 5GS identity type +// Reference: 9.11.3.3 +SRSASN_CODE identity_type_5gs_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(type_of_identity.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* identity_type_5gs_t::identity_types_::to_string() const +{ + switch (value) { + case identity_types_::suci: + return "SUCI"; + case identity_types_::guti_5g: + return "5G-GUTI"; + case identity_types_::imei: + return "IMEI"; + case identity_types_::s_tmsi_5g: + return "5G-S-TMSI"; + case identity_types_::imeisv: + return "IMEISV"; + case identity_types_::mac_address: + return "MAC address"; + case identity_types_::eui_64: + return "EUI-64"; + default: + return "Invalid Choice"; + } +} +// IE: security algorithms +// Reference: 9.11.3.34 +SRSASN_CODE security_algorithms_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(ciphering_algorithm.pack(bref)); + HANDLE_CODE(integrity_protection_algorithm.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: security algorithms +// Reference: 9.11.3.34 +SRSASN_CODE security_algorithms_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(ciphering_algorithm.unpack(bref)); + HANDLE_CODE(integrity_protection_algorithm.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* security_algorithms_t::integrity_protection_algorithm_type_::to_string() const +{ + switch (value) { + case integrity_protection_algorithm_type_::ia0_5g: + return "IA0-5G"; + case integrity_protection_algorithm_type_::ia1_128_5g: + return "IA1-128-5G"; + case integrity_protection_algorithm_type_::ia2_128_5g: + return "IA2-128-5G"; + case integrity_protection_algorithm_type_::ia3_128_5g: + return "IA3-128-5G"; + case integrity_protection_algorithm_type_::ia4_5g: + return "IA4-5G"; + case integrity_protection_algorithm_type_::ia5_5g: + return "IA5-5G"; + case integrity_protection_algorithm_type_::ia6_5g: + return "IA6-5G"; + case integrity_protection_algorithm_type_::ia7_5g: + return "IA7-5G"; + default: + return "Invalid Choice"; + } +} +const char* security_algorithms_t::ciphering_algorithm_type_::to_string() const +{ + switch (value) { + case ciphering_algorithm_type_::ea0_5g: + return "EA0-5G"; + case ciphering_algorithm_type_::ea1_128_5g: + return "EA1-128-5G"; + case ciphering_algorithm_type_::ea2_128_5g: + return "EA2-128-5G"; + case ciphering_algorithm_type_::ea3_128_5g: + return "EA3-128-5G"; + case ciphering_algorithm_type_::ea4_5g: + return "EA4-5G"; + case ciphering_algorithm_type_::ea5_5g: + return "EA5-5G"; + case ciphering_algorithm_type_::ea6_5g: + return "EA6-5G"; + case ciphering_algorithm_type_::ea7_5g: + return "EA7-5G"; + default: + return "Invalid Choice"; + } +} +// IE: IMEISV request +// Reference: 9.11.3.28 +SRSASN_CODE imeisv_request_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(imeisv_request.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: IMEISV request +// Reference: 9.11.3.28 +SRSASN_CODE imeisv_request_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(imeisv_request.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* imeisv_request_t::imeisv_request_type_::to_string() const +{ + switch (value) { + case imeisv_request_type_::imeisv_not_requested: + return "IMEISV not requested"; + case imeisv_request_type_::imeisv_requested: + return "IMEISV requested"; + default: + return "Invalid Choice"; + } +} +// IE: EPS NAS security algorithms +// Reference: 9.11.3.25 +SRSASN_CODE eps_nas_security_algorithms_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(ciphering_algorithm.pack(bref)); + + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(integrity_protection_algorithm.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: EPS NAS security algorithms +// Reference: 9.11.3.25 +SRSASN_CODE eps_nas_security_algorithms_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(ciphering_algorithm.unpack(bref)); + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(integrity_protection_algorithm.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* eps_nas_security_algorithms_t::integrity_protection_algorithm_type_::to_string() const +{ + switch (value) { + case integrity_protection_algorithm_type_::eia0: + return "EIA0"; + case integrity_protection_algorithm_type_::eia1_128: + return "EIA1-128"; + case integrity_protection_algorithm_type_::eia2_128: + return "EIA2-128"; + case integrity_protection_algorithm_type_::eia3_128: + return "EIA3-128"; + case integrity_protection_algorithm_type_::eia4: + return "EIA4"; + case integrity_protection_algorithm_type_::eia5: + return "EIA5"; + case integrity_protection_algorithm_type_::eia6: + return "EIA6"; + case integrity_protection_algorithm_type_::eia7: + return "EIA7"; + default: + return "Invalid Choice"; + } +} +const char* eps_nas_security_algorithms_t::ciphering_algorithm_type_::to_string() const +{ + switch (value) { + case ciphering_algorithm_type_::eea0: + return "EEA0"; + case ciphering_algorithm_type_::eea1_128: + return "EEA1-128"; + case ciphering_algorithm_type_::eea2_128: + return "EEA2-128"; + case ciphering_algorithm_type_::eea3_128: + return "EEA3-128"; + case ciphering_algorithm_type_::eea4: + return "EEA4"; + case ciphering_algorithm_type_::eea5: + return "EEA5"; + case ciphering_algorithm_type_::eea6: + return "EEA6"; + case ciphering_algorithm_type_::eea7: + return "EEA7"; + default: + return "Invalid Choice"; + } +} +// IE: Additional 5G security information +// Reference: 9.11.3.12 +SRSASN_CODE additional_5g_security_information_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 6 Spare bits + HANDLE_CODE(bref.pack(0x0, 6)); + HANDLE_CODE(bref.pack(rinmr, 1)); + HANDLE_CODE(bref.pack(hdp, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error( + "Encoding Failed (Additional 5G security information): Packed length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Additional 5G security information +// Reference: 9.11.3.12 +SRSASN_CODE additional_5g_security_information_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error( + "Decoding Failed (Additional 5G security information): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 6 Spare bits + bref.advance_bits(6); + HANDLE_CODE(bref.unpack(rinmr, 1)); + HANDLE_CODE(bref.unpack(hdp, 1)); + return SRSASN_SUCCESS; +} + +// IE: S1 UE security capability +// Reference: 9.11.3.48A +SRSASN_CODE s1_ue_security_capability_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(eea0, 1)); + HANDLE_CODE(bref.pack(eea1_128, 1)); + HANDLE_CODE(bref.pack(eea2_128, 1)); + HANDLE_CODE(bref.pack(eea3_128, 1)); + HANDLE_CODE(bref.pack(eea4, 1)); + HANDLE_CODE(bref.pack(eea5, 1)); + HANDLE_CODE(bref.pack(eea6, 1)); + HANDLE_CODE(bref.pack(eea7, 1)); + HANDLE_CODE(bref.pack(eia0, 1)); + HANDLE_CODE(bref.pack(eia1_128, 1)); + HANDLE_CODE(bref.pack(eia2_128, 1)); + HANDLE_CODE(bref.pack(eia3_128, 1)); + HANDLE_CODE(bref.pack(eia4, 1)); + HANDLE_CODE(bref.pack(eia5, 1)); + HANDLE_CODE(bref.pack(eia6, 1)); + HANDLE_CODE(bref.pack(eia7, 1)); + HANDLE_CODE(bref.pack(uea0, 1)); + HANDLE_CODE(bref.pack(uea1, 1)); + HANDLE_CODE(bref.pack(uea2, 1)); + HANDLE_CODE(bref.pack(uea3, 1)); + HANDLE_CODE(bref.pack(uea4, 1)); + HANDLE_CODE(bref.pack(uea5, 1)); + HANDLE_CODE(bref.pack(uea6, 1)); + HANDLE_CODE(bref.pack(uea7, 1)); + + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(bref.pack(uia1, 1)); + HANDLE_CODE(bref.pack(uia2, 1)); + HANDLE_CODE(bref.pack(uia3, 1)); + HANDLE_CODE(bref.pack(uia4, 1)); + HANDLE_CODE(bref.pack(uia5, 1)); + HANDLE_CODE(bref.pack(uia6, 1)); + HANDLE_CODE(bref.pack(uia7, 1)); + + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(bref.pack(gea1, 1)); + HANDLE_CODE(bref.pack(gea2, 1)); + HANDLE_CODE(bref.pack(gea3, 1)); + HANDLE_CODE(bref.pack(gea4, 1)); + HANDLE_CODE(bref.pack(gea5, 1)); + HANDLE_CODE(bref.pack(gea6, 1)); + HANDLE_CODE(bref.pack(gea7, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 2 || length > 5) { + asn1::log_error( + "Encoding Failed (S1 UE security capability): Packed length (%d) is not in range of min: 2 and max 5 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: S1 UE security capability +// Reference: 9.11.3.48A +SRSASN_CODE s1_ue_security_capability_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 2 || length > 5) { + asn1::log_error( + "Decoding Failed (S1 UE security capability): Length (%d) is not in range of min: 2 and max 5 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(eea0, 1)); + HANDLE_CODE(bref.unpack(eea1_128, 1)); + HANDLE_CODE(bref.unpack(eea2_128, 1)); + HANDLE_CODE(bref.unpack(eea3_128, 1)); + HANDLE_CODE(bref.unpack(eea4, 1)); + HANDLE_CODE(bref.unpack(eea5, 1)); + HANDLE_CODE(bref.unpack(eea6, 1)); + HANDLE_CODE(bref.unpack(eea7, 1)); + + HANDLE_CODE(bref.unpack(eia0, 1)); + HANDLE_CODE(bref.unpack(eia1_128, 1)); + HANDLE_CODE(bref.unpack(eia2_128, 1)); + HANDLE_CODE(bref.unpack(eia3_128, 1)); + HANDLE_CODE(bref.unpack(eia4, 1)); + HANDLE_CODE(bref.unpack(eia5, 1)); + HANDLE_CODE(bref.unpack(eia6, 1)); + HANDLE_CODE(bref.unpack(eia7, 1)); + + if (length < 3) { + return SRSASN_SUCCESS; + } + + HANDLE_CODE(bref.unpack(uea0, 1)); + HANDLE_CODE(bref.unpack(uea1, 1)); + HANDLE_CODE(bref.unpack(uea2, 1)); + HANDLE_CODE(bref.unpack(uea3, 1)); + HANDLE_CODE(bref.unpack(uea4, 1)); + HANDLE_CODE(bref.unpack(uea5, 1)); + HANDLE_CODE(bref.unpack(uea6, 1)); + HANDLE_CODE(bref.unpack(uea7, 1)); + + if (length < 4) { + return SRSASN_SUCCESS; + } + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(bref.unpack(uia1, 1)); + HANDLE_CODE(bref.unpack(uia2, 1)); + HANDLE_CODE(bref.unpack(uia3, 1)); + HANDLE_CODE(bref.unpack(uia4, 1)); + HANDLE_CODE(bref.unpack(uia5, 1)); + HANDLE_CODE(bref.unpack(uia6, 1)); + HANDLE_CODE(bref.unpack(uia7, 1)); + + if (length < 5) { + return SRSASN_SUCCESS; + } + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(bref.unpack(gea1, 1)); + HANDLE_CODE(bref.unpack(gea2, 1)); + HANDLE_CODE(bref.unpack(gea3, 1)); + HANDLE_CODE(bref.unpack(gea4, 1)); + HANDLE_CODE(bref.unpack(gea5, 1)); + HANDLE_CODE(bref.unpack(gea6, 1)); + HANDLE_CODE(bref.unpack(gea7, 1)); + return SRSASN_SUCCESS; +} + +// IE: Access type +// Reference: 9.11.2.1A +SRSASN_CODE access_type_t::pack(asn1::bit_ref& bref) +{ + // 2 Spare bits + HANDLE_CODE(bref.pack(0x0, 2)); + HANDLE_CODE(access_type_value.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: Access type +// Reference: 9.11.2.1A +SRSASN_CODE access_type_t::unpack(asn1::cbit_ref& bref) +{ + // 2 Spare bits + bref.advance_bits(2); + HANDLE_CODE(access_type_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* access_type_t::Access_type_value_type_::to_string() const +{ + switch (value) { + case Access_type_value_type_::access_3_gpp: + return "access_3GPP"; + case Access_type_value_type_::non_3_gpp_access: + return "Non_3GPP_access"; + default: + return "Invalid Choice"; + } +} +// IE: PDU session identity 2 +// Reference: 9.11.3.41 +SRSASN_CODE pdu_session_identity_2_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(bref.pack(pdu_session_identity_2_value, 8)); + return SRSASN_SUCCESS; +} + +// IE: PDU session identity 2 +// Reference: 9.11.3.41 +SRSASN_CODE pdu_session_identity_2_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(bref.unpack(pdu_session_identity_2_value, 8)); + return SRSASN_SUCCESS; +} + +// IE: Request type +// Reference: 9.11.3.47 +SRSASN_CODE request_type_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(request_type_value.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: Request type +// Reference: 9.11.3.47 +SRSASN_CODE request_type_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(request_type_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* request_type_t::Request_type_value_type_::to_string() const +{ + switch (value) { + case Request_type_value_type_::initial_request: + return "initial request"; + case Request_type_value_type_::existing_pdu_session: + return "existing PDU session"; + case Request_type_value_type_::initial_emergency_request: + return "initial emergency request"; + case Request_type_value_type_::existing_emergency_pdu_session: + return "existing emergency PDU session"; + case Request_type_value_type_::modification_request: + return "modification request"; + case Request_type_value_type_::ma_pdu_request: + return "MA PDU request"; + case Request_type_value_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} +// IE: S-NSSAI +// Reference: 9.11.2.8 +SRSASN_CODE s_nssai_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + if (type == s_nssai_t::SST_type_::options::sst) { + HANDLE_CODE(bref.pack(sst, 8)); + } else if (type == s_nssai_t::SST_type_::options::sst_and_mapped_hplmn_sst) { + HANDLE_CODE(bref.pack(sst, 8)); + HANDLE_CODE(bref.pack(mapped_hplmn_sst, 8)); + } else if (type == s_nssai_t::SST_type_::options::sst_and_sd) { + HANDLE_CODE(bref.pack(sst, 8)); + HANDLE_CODE(bref.pack(sd, 24)); + } else if (type == s_nssai_t::SST_type_::options::sst_sd_mapped_hplmn_sst_and_mapped_hplmn_sd) { + HANDLE_CODE(bref.pack(sst, 8)); + HANDLE_CODE(bref.pack(sd, 24)); + HANDLE_CODE(bref.pack(mapped_hplmn_sst, 8)); + HANDLE_CODE(bref.pack(mapped_hplmn_sd, 24)); + } else { + asn1::log_error("Not such a length type for s_nssai"); + return SRSASN_ERROR_ENCODE_FAIL; + } + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1 || length > 8) { + asn1::log_error("Encoding Failed (S-NSSAI): Packed length (%d) is not in range of min: 1 and max 8 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: S-NSSAI +// Reference: 9.11.2.8 +SRSASN_CODE s_nssai_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1 || length > 8) { + asn1::log_error("Decoding Failed (S-NSSAI): Length (%d) is not in range of min: 1 and max 8 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + if (length == s_nssai_t::SST_type_::options::sst) { + type = s_nssai_t::SST_type_::options::sst; + HANDLE_CODE(bref.unpack(sst, 8)); + } else if (length == s_nssai_t::SST_type_::options::sst_and_mapped_hplmn_sst) { + type = s_nssai_t::SST_type_::options::sst_and_mapped_hplmn_sst; + HANDLE_CODE(bref.unpack(sst, 8)); + HANDLE_CODE(bref.unpack(mapped_hplmn_sst, 8)); + } else if (length == s_nssai_t::SST_type_::options::sst_and_sd) { + type = s_nssai_t::SST_type_::options::sst_and_sd; + HANDLE_CODE(bref.unpack(sst, 8)); + HANDLE_CODE(bref.unpack(sd, 24)); + } else if (length == s_nssai_t::SST_type_::options::sst_sd_mapped_hplmn_sst_and_mapped_hplmn_sd) { + type = s_nssai_t::SST_type_::options::sst_sd_mapped_hplmn_sst_and_mapped_hplmn_sd; + HANDLE_CODE(bref.unpack(sst, 8)); + HANDLE_CODE(bref.unpack(sd, 24)); + HANDLE_CODE(bref.unpack(mapped_hplmn_sst, 8)); + HANDLE_CODE(bref.unpack(mapped_hplmn_sd, 24)); + } else { + asn1::log_error("Not such a type for s_nssai"); + return SRSASN_ERROR_DECODE_FAIL; + } + return SRSASN_SUCCESS; +} + +const char* s_nssai_t::SST_type_::to_string() const +{ + switch (value) { + case SST_type_::sst: + return "SST"; + case SST_type_::sst_and_mapped_hplmn_sst: + return "SST and mapped HPLMN SST"; + case SST_type_::sst_and_sd: + return "SST and SD"; + case SST_type_::sst_sd_and_mapped_hplmn_sst: + return "SST, SD and mapped HPLMN SST"; + case SST_type_::sst_sd_mapped_hplmn_sst_and_mapped_hplmn_sd: + return "SST, SD, mapped HPLMN SST and mapped HPLMN SD"; + default: + return "Invalid Choice"; + } +} +// IE: DNN +// Reference: 9.11.2.1B +SRSASN_CODE dnn_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(dnn_value.data(), dnn_value.size())); + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1 || length > 100) { + asn1::log_error("Encoding Failed (DNN): Packed length (%d) is not in range of min: 1 and max 100 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: DNN +// Reference: 9.11.2.1B +SRSASN_CODE dnn_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1 || length > 100) { + asn1::log_error("Decoding Failed (DNN): Length (%d) is not in range of min: 1 and max 100 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + dnn_value.resize(length); + HANDLE_CODE(bref.unpack_bytes(dnn_value.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Additional information +// Reference: 9.11.2.1 +SRSASN_CODE additional_information_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(additional_information_value.data(), additional_information_value.size())); + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1) { + asn1::log_error("Encoding Failed (Additional information): Packed length (%d) is not in range of min: 1 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Additional information +// Reference: 9.11.2.1 +SRSASN_CODE additional_information_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1) { + asn1::log_error("Decoding Failed (Additional information): Length (%d) is not in range of min: 1 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + additional_information_value.resize(length); + HANDLE_CODE(bref.unpack_bytes(additional_information_value.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: MA PDU session information +// Reference: 9.11.3.31A +SRSASN_CODE ma_pdu_session_information_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(ma_pdu_session_information_value.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: MA PDU session information +// Reference: 9.11.3.31A +SRSASN_CODE ma_pdu_session_information_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(ma_pdu_session_information_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* ma_pdu_session_information_t::MA_PDU_session_information_value_type_::to_string() const +{ + switch (value) { + case MA_PDU_session_information_value_type_::ma_pdu_session_network_upgrade_is_allowed: + return "MA PDU session network upgrade is allowed"; + default: + return "Invalid Choice"; + } +} +// IE: Release assistance indication +// Reference: 9.11.3.46A +SRSASN_CODE release_assistance_indication_t::pack(asn1::bit_ref& bref) +{ + // 2 Spare bits + HANDLE_CODE(bref.pack(0x0, 2)); + HANDLE_CODE(downlink_data_expected.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: Release assistance indication +// Reference: 9.11.3.46A +SRSASN_CODE release_assistance_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 2 Spare bits + bref.advance_bits(2); + HANDLE_CODE(downlink_data_expected.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* release_assistance_indication_t::Downlink_data_expected_type_::to_string() const +{ + switch (value) { + case Downlink_data_expected_type_::no_information_regarding_ddx_is_conveyed: + return "No information regarding DDX is conveyed"; + case Downlink_data_expected_type_::no_further_uplink_and_no_further_downlink_data: + return "No further uplink and no further downlink data"; + case Downlink_data_expected_type_::only_a_single_downlink_data_transmission: + return "Only a single downlink data transmission"; + case Downlink_data_expected_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} +// IE: Integrity protection maximum data rate +// Reference: 9.11.4.7 +SRSASN_CODE integrity_protection_maximum_data_rate_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(max_data_rate_upip_uplink.pack(bref)); + HANDLE_CODE(max_data_rate_upip_downlink.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: Integrity protection maximum data rate +// Reference: 9.11.4.7 +SRSASN_CODE integrity_protection_maximum_data_rate_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(max_data_rate_upip_uplink.unpack(bref)); + HANDLE_CODE(max_data_rate_upip_downlink.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_uplink_type_::to_string() const +{ + switch (value) { + case max_data_rate_UPIP_uplink_type_::kbps_64: + return "kbps 64"; + case max_data_rate_UPIP_uplink_type_::null: + return "NULL"; + case max_data_rate_UPIP_uplink_type_::full_data_rate: + return "Full data rate"; + default: + return "Invalid Choice"; + } +} +const char* integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_downlink_type_::to_string() const +{ + switch (value) { + case max_data_rate_UPIP_downlink_type_::kbps_64: + return "kbps 64"; + case max_data_rate_UPIP_downlink_type_::null: + return "NULL"; + case max_data_rate_UPIP_downlink_type_::full_data_rate: + return "Full data rate"; + default: + return "Invalid Choice"; + } +} +// IE: PDU session type +// Reference: 9.11.4.11 +SRSASN_CODE pdu_session_type_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(pdu_session_type_value.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: PDU session type +// Reference: 9.11.4.11 +SRSASN_CODE pdu_session_type_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(pdu_session_type_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* pdu_session_type_t::PDU_session_type_value_type_::to_string() const +{ + switch (value) { + case PDU_session_type_value_type_::ipv4: + return "ipv4"; + case PDU_session_type_value_type_::ipv6: + return "ipv6"; + case PDU_session_type_value_type_::ipv4v6: + return "ipv4v6"; + case PDU_session_type_value_type_::unstructured: + return "Unstructured"; + case PDU_session_type_value_type_::ethernet: + return "Ethernet"; + case PDU_session_type_value_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} +// IE: SSC mode +// Reference: 9.11.4.16 +SRSASN_CODE ssc_mode_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(ssc_mode_value.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: SSC mode +// Reference: 9.11.4.16 +SRSASN_CODE ssc_mode_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(ssc_mode_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* ssc_mode_t::SSC_mode_value_type_::to_string() const +{ + switch (value) { + case SSC_mode_value_type_::ssc_mode_1: + return "SSC mode 1"; + case SSC_mode_value_type_::ssc_mode_2: + return "SSC mode 2"; + case SSC_mode_value_type_::ssc_mode_3: + return "SSC mode 3"; + case SSC_mode_value_type_::unused_or_ssc_mode_1: + return "unused or SSC mode 1"; + case SSC_mode_value_type_::unused_or_ssc_mode_2: + return "unused or SSC mode 2"; + case SSC_mode_value_type_::unused_or_ssc_mode_3: + return "unused or SSC mode 3"; + case SSC_mode_value_type_::reserved: + return "reserved"; + default: + return "Invalid Choice"; + } +} +// IE: 5GSM capability +// Reference: 9.11.4.1 +SRSASN_CODE capability_5gsm_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1 || length > 13) { + asn1::log_error("Encoding Failed (5GSM capability): Packed length (%d) is not in range of min: 1 and max 13 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GSM capability +// Reference: 9.11.4.1 +SRSASN_CODE capability_5gsm_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1 || length > 13) { + asn1::log_error("Decoding Failed (5GSM capability): Length (%d) is not in range of min: 1 and max 13 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: Maximum number of supported packet filters +// Reference: 9.11.4.9 +SRSASN_CODE maximum_number_of_supported_packet_filters_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(bref.pack(maximum_number_of_supported_packet_filters, 11)); + + // 5 Spare bits + HANDLE_CODE(bref.pack(0x0, 5)); + return SRSASN_SUCCESS; +} + +// IE: Maximum number of supported packet filters +// Reference: 9.11.4.9 +SRSASN_CODE maximum_number_of_supported_packet_filters_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(bref.unpack(maximum_number_of_supported_packet_filters, 11)); + // 5 Spare bits + bref.advance_bits(5); + return SRSASN_SUCCESS; +} + +// IE: Always-on PDU session requested +// Reference: 9.11.4.4 +SRSASN_CODE always_on_pdu_session_requested_t::pack(asn1::bit_ref& bref) +{ + // 3 Spare bits + HANDLE_CODE(bref.pack(0x0, 3)); + HANDLE_CODE(bref.pack(apsi, 1)); + return SRSASN_SUCCESS; +} + +// IE: Always-on PDU session requested +// Reference: 9.11.4.4 +SRSASN_CODE always_on_pdu_session_requested_t::unpack(asn1::cbit_ref& bref) +{ + // 3 Spare bits + bref.advance_bits(3); + HANDLE_CODE(bref.unpack(apsi, 1)); + return SRSASN_SUCCESS; +} + +// IE: SM PDU DN request container +// Reference: 9.11.4.15 +SRSASN_CODE sm_pdu_dn_request_container_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(dn_specific_identity.data(), dn_specific_identity.size())); + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1 || length > 253) { + asn1::log_error( + "Encoding Failed (SM PDU DN request container): Packed length (%d) is not in range of min: 1 and max 253 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: SM PDU DN request container +// Reference: 9.11.4.15 +SRSASN_CODE sm_pdu_dn_request_container_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1 || length > 253) { + asn1::log_error( + "Decoding Failed (SM PDU DN request container): Length (%d) is not in range of min: 1 and max 253 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + dn_specific_identity.resize(length); + HANDLE_CODE(bref.unpack_bytes(dn_specific_identity.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Extended protocol configuration options +// Reference: 9.11.4.6 +SRSASN_CODE extended_protocol_configuration_options_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // max. length of 65535 not checked: uint overflow + if (length < 1) { + asn1::log_error( + "Encoding Failed (Extended protocol configuration options): Packed length (%d) is not in range of min: 1 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: Extended protocol configuration options +// Reference: 9.11.4.6 +SRSASN_CODE extended_protocol_configuration_options_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // max. length of 65535 not checked: uint overflow + if (length < 1) { + asn1::log_error( + "Decoding Failed (Extended protocol configuration options): Length (%d) is not in range of min: 1 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: IP header compression configuration +// Reference: 9.11.4.24 +SRSASN_CODE ip_header_compression_configuration_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + // max. length of 255 not checked: uint overflow + if (length < 3) { + asn1::log_error( + "Encoding Failed (IP header compression configuration): Packed length (%d) is not in range of min: 3 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: IP header compression configuration +// Reference: 9.11.4.24 +SRSASN_CODE ip_header_compression_configuration_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + // max. length of 255 not checked: uint overflow + if (length < 3) { + asn1::log_error( + "Decoding Failed (IP header compression configuration): Length (%d) is not in range of min: 3 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: DS-TT Ethernet port MAC address +// Reference: 9.11.4.25 +SRSASN_CODE ds_tt__ethernet_port_mac_address_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(ds_tt__ethernet_port_mac_address_contents.data(), 6)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 6) { + asn1::log_error( + "Encoding Failed (DS-TT Ethernet port MAC address): Packed length (%d) does not equal expected length 6", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: DS-TT Ethernet port MAC address +// Reference: 9.11.4.25 +SRSASN_CODE ds_tt__ethernet_port_mac_address_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 6) { + asn1::log_error("Decoding Failed (DS-TT Ethernet port MAC address): Length (%d) does not equal expected length 6", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack_bytes(ds_tt__ethernet_port_mac_address_contents.data(), 6)); + return SRSASN_SUCCESS; +} + +// IE: UE-DS-TT residence time +// Reference: 9.11.4.26 +SRSASN_CODE ue_ds_tt_residence_time_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack_bytes(ue_ds_tt_residence_time_contents.data(), 8)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 8) { + asn1::log_error("Encoding Failed (UE-DS-TT residence time): Packed length (%d) does not equal expected length 8", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: UE-DS-TT residence time +// Reference: 9.11.4.26 +SRSASN_CODE ue_ds_tt_residence_time_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 8) { + asn1::log_error("Decoding Failed (UE-DS-TT residence time): Length (%d) does not equal expected length 8", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack_bytes(ue_ds_tt_residence_time_contents.data(), 8)); + return SRSASN_SUCCESS; +} + +// IE: Port management information container +// Reference: 9.11.4.27 +SRSASN_CODE port_management_information_container_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + HANDLE_CODE( + bref.pack_bytes(port_management_information_container.data(), port_management_information_container.size())); + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // max. length of 65535 not checked: uint overflow + if (length < 1) { + asn1::log_error( + "Encoding Failed (Port management information container): Packed length (%d) is not in range of min: 1 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: Port management information container +// Reference: 9.11.4.27 +SRSASN_CODE port_management_information_container_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // max. length of 65535 not checked: uint overflow + if (length < 1) { + asn1::log_error( + "Decoding Failed (Port management information container): Length (%d) is not in range of min: 1 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + port_management_information_container.resize(length); + HANDLE_CODE(bref.unpack_bytes(port_management_information_container.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Ethernet header compression configuration +// Reference: 9.11.4.28 +SRSASN_CODE ethernet_header_compression_configuration_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 6 Spare bits + HANDLE_CODE(bref.pack(0x0, 6)); + HANDLE_CODE(cid__length.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (Ethernet header compression configuration): Packed length (%d) does not equal " + "expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Ethernet header compression configuration +// Reference: 9.11.4.28 +SRSASN_CODE ethernet_header_compression_configuration_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error( + "Decoding Failed (Ethernet header compression configuration): Length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 6 Spare bits + bref.advance_bits(6); + HANDLE_CODE(cid__length.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* ethernet_header_compression_configuration_t::CID_Length_type_::to_string() const +{ + switch (value) { + case CID_Length_type_::ethernet_header_compression_not_used: + return "Ethernet header compression not used"; + case CID_Length_type_::bits_7: + return "bits_7"; + case CID_Length_type_::bits_15: + return "bits_15"; + default: + return "Invalid Choice"; + } +} +// IE: PDU address +// Reference: 9.11.4.10 +SRSASN_CODE pdu_address_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 4 Spare bits + HANDLE_CODE(bref.pack(0x0, 4)); + HANDLE_CODE(bref.pack(si6_lla, 1)); + HANDLE_CODE(pdu_session_type_value.pack(bref)); + + if (pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4) { + HANDLE_CODE(bref.pack_bytes(ipv4.data(), 4)); + } else if (pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv6) { + HANDLE_CODE(bref.pack_bytes(ipv6.data(), 8)); + } else if (pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4v6) { + HANDLE_CODE(bref.pack_bytes(ipv6.data(), 8)); + HANDLE_CODE(bref.pack_bytes(ipv4.data(), 4)); + } + + if (si6_lla == true) { + HANDLE_CODE(bref.pack_bytes(smf_i_pv6_link_local_address.data(), 16)); + } + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 5 || length > 29) { + asn1::log_error("Encoding Failed (PDU address): Packed length (%d) is not in range of min: 5 and max 29 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: PDU address +// Reference: 9.11.4.10 +SRSASN_CODE pdu_address_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 5 || length > 29) { + asn1::log_error("Decoding Failed (PDU address): Length (%d) is not in range of min: 5 and max 29 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 4 Spare bits + bref.advance_bits(4); + HANDLE_CODE(bref.unpack(si6_lla, 1)); + HANDLE_CODE(pdu_session_type_value.unpack(bref)); + if (length == 5 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4 && + si6_lla == false) { + HANDLE_CODE(bref.unpack_bytes(ipv4.data(), 4)); + } else if (length == 9 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv6 && + si6_lla == false) { + HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 8)); + } else if (length == 13 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4v6 && + si6_lla == false) { + HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 8)); + HANDLE_CODE(bref.unpack_bytes(ipv4.data(), 4)); + } else if (length == 25 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv6 && + si6_lla == true) { + HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 8)); + HANDLE_CODE(bref.unpack_bytes(smf_i_pv6_link_local_address.data(), 8)); + } else if (length == 29 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4v6 && + si6_lla == true) { + HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 8)); + HANDLE_CODE(bref.unpack_bytes(ipv4.data(), 4)); + HANDLE_CODE(bref.unpack_bytes(smf_i_pv6_link_local_address.data(), 8)); + } else { + asn1::log_error("Not expected combination of length and type field"); + return SRSASN_ERROR_DECODE_FAIL; + } + return SRSASN_SUCCESS; +} + +const char* pdu_address_t::PDU_session_type_value_type_::to_string() const +{ + switch (value) { + case PDU_session_type_value_type_::ipv4: + return "ipv4"; + case PDU_session_type_value_type_::ipv6: + return "ipv6"; + case PDU_session_type_value_type_::ipv4v6: + return "ipv4v6"; + default: + return "Invalid Choice"; + } +} +// IE: QoS rules +// Reference: 9.11.4.13 +SRSASN_CODE qo_s_rules_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // max. length of 65535 not checked: uint overflow + if (length < 4) { + asn1::log_error("Encoding Failed (QoS rules): Packed length (%d) is not in range of min: 4 bytes", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: QoS rules +// Reference: 9.11.4.13 +SRSASN_CODE qo_s_rules_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // max. length of 65535 not checked: uint overflow + if (length < 4) { + asn1::log_error("Decoding Failed (QoS rules): Length (%d) is not in range of min: 4 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.advance_bits(length * 8)); + return SRSASN_SUCCESS; +} + +// IE: Session-AMBR +// Reference: 9.11.4.14 +SRSASN_CODE session_ambr_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(unit_session_ambr_for_downlink.pack(bref)); + HANDLE_CODE(bref.pack(session_ambr_for_downlink, 16)); + HANDLE_CODE(unit_session_ambr_for_uplink.pack(bref)); + HANDLE_CODE(bref.pack(session_ambr_for_uplink, 16)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 6) { + asn1::log_error("Encoding Failed (Session-AMBR): Packed length (%d) does not equal expected length 6", length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Session-AMBR +// Reference: 9.11.4.14 +SRSASN_CODE session_ambr_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 6) { + asn1::log_error("Decoding Failed (Session-AMBR): Length (%d) does not equal expected length 6", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(unit_session_ambr_for_downlink.unpack(bref)); + HANDLE_CODE(bref.unpack(session_ambr_for_downlink, 16)); + HANDLE_CODE(unit_session_ambr_for_uplink.unpack(bref)); + HANDLE_CODE(bref.unpack(session_ambr_for_uplink, 16)); + return SRSASN_SUCCESS; +} + +const char* session_ambr_t::unit_session_AMBR_type_::to_string() const +{ + switch (value) { + case unit_session_AMBR_type_::not_used: + return "not used"; + case unit_session_AMBR_type_::inc_by_1_kbps: + return "inc by 1 Kbps"; + case unit_session_AMBR_type_::inc_by_4_kbps: + return "inc by 4 Kbps"; + case unit_session_AMBR_type_::inc_by_16_kbps: + return "inc by 16 Kbps"; + case unit_session_AMBR_type_::inc_by_64_kbps: + return "inc by 64 Kbps"; + case unit_session_AMBR_type_::inc_by_256_kbps: + return "inc by 256 kbps"; + case unit_session_AMBR_type_::inc_by_1_mbps: + return "inc by 1 Mbps"; + case unit_session_AMBR_type_::inc_by_4_mbps: + return "inc by 4 Mbps"; + case unit_session_AMBR_type_::inc_by_16_mbps: + return "inc by 16 Mbps"; + case unit_session_AMBR_type_::inc_by_64_mbps: + return "inc by 64 Mbps"; + case unit_session_AMBR_type_::inc_by_256_mbps: + return "inc by 256 Mbps"; + case unit_session_AMBR_type_::inc_by_1_gbps: + return "inc by 1 Gbps"; + case unit_session_AMBR_type_::inc_by_4_gbps: + return "inc by 4 Gbps"; + case unit_session_AMBR_type_::inc_by_16_gbps: + return "inc by 16 Gbps"; + case unit_session_AMBR_type_::inc_by_64_gbps: + return "inc by 64 Gbps"; + case unit_session_AMBR_type_::inc_by_256_gbps: + return "inc by 256 Gbps"; + case unit_session_AMBR_type_::inc_by_1_tbps: + return "inc by 1 Tbps"; + case unit_session_AMBR_type_::inc_by_4_tbps: + return "inc by 4 Tbps"; + case unit_session_AMBR_type_::inc_by_16_tbps: + return "inc by 16 Tbps"; + case unit_session_AMBR_type_::inc_by_64_tbps: + return "inc by 64 Tbps"; + case unit_session_AMBR_type_::inc_by_256_tbps: + return "inc by 256 Tbps"; + case unit_session_AMBR_type_::inc_by_1_pbps: + return "inc by 1 Pbps"; + case unit_session_AMBR_type_::inc_by_4_pbps: + return "inc by 4 Pbps"; + case unit_session_AMBR_type_::inc_by_16_pbps: + return "inc by 16 Pbps"; + case unit_session_AMBR_type_::inc_by_64_pbps: + return "inc by 64 Pbps"; + case unit_session_AMBR_type_::inc_by_256_pbps: + return "inc by 256 Pbps"; + default: + return "Invalid Choice"; + } +} +// IE: 5GSM cause +// Reference: 9.11.4.2 +SRSASN_CODE cause_5gsm_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(cause_value.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: 5GSM cause +// Reference: 9.11.4.2 +SRSASN_CODE cause_5gsm_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(cause_value.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* cause_5gsm_t::cause_value_type_::to_string() const +{ + switch (value) { + case cause_value_type_::operator_determined_barring: + return "Operator determined barring"; + case cause_value_type_::insufficient_resources: + return "Insufficient resources"; + case cause_value_type_::missing_or_unknown_dnn: + return "Missing or unknown DNN"; + case cause_value_type_::unknown_pdu_session_type: + return "Unknown PDU session type"; + case cause_value_type_::user_authentication_or_authorization_failed: + return "User authentication or authorization failed"; + case cause_value_type_::request_rejected_unspecified: + return "Request rejected, unspecified"; + case cause_value_type_::service_option_not_supported: + return "Service option not supported"; + case cause_value_type_::requested_service_option_not_subscribed: + return "Requested service option not subscribed"; + case cause_value_type_::pti_already_in_use: + return "PTI already in use"; + case cause_value_type_::regular_deactivation: + return "Regular deactivation"; + case cause_value_type_::network_failure: + return "Network failure"; + case cause_value_type_::reactivation_requested: + return "Reactivation requested"; + case cause_value_type_::semantic_error_in_the_tft_operation: + return "Semantic error in the TFT operation"; + case cause_value_type_::syntactical_error_in_the_tft_operation: + return "Syntactical error in the TFT operation"; + case cause_value_type_::invalid_pdu_session_identity: + return "Invalid PDU session identity"; + case cause_value_type_::semantic_errors_in_packet_filter: + return "Semantic errors in packet filter"; + case cause_value_type_::syntactical_error_in_packet_filter: + return "Syntactical error in packet filter"; + case cause_value_type_::out_of_ladn_service_area: + return "Out of LADN service area"; + case cause_value_type_::pti_mismatch: + return "PTI mismatch"; + case cause_value_type_::pdu_session_type_i_pv4_only_allowed: + return "PDU session type IPv4 only allowed"; + case cause_value_type_::pdu_session_type_i_pv6_only_allowed: + return "PDU session type IPv6 only allowed"; + case cause_value_type_::pdu_session_does_not_exist: + return "PDU session does not exist"; + case cause_value_type_::pdu_session_type_i_pv4v6_only_allowed: + return "PDU session type IPv4v6 only allowed"; + case cause_value_type_::pdu_session_type_unstructured_only_allowed: + return "PDU session type Unstructured only allowed"; + case cause_value_type_::unsupported_5_qi_value: + return "Unsupported 5QI value"; + case cause_value_type_::pdu_session_type_ethernet_only_allowed: + return "PDU session type Ethernet only allowed"; + case cause_value_type_::insufficient_resources_for_specific_slice_and_dnn: + return "Insufficient resources for specific slice and DNN"; + case cause_value_type_::not_supported_ssc_mode: + return "Not supported SSC mode"; + case cause_value_type_::insufficient_resources_for_specific_slice: + return "Insufficient resources for specific slice"; + case cause_value_type_::missing_or_unknown_dnn_in_a_slice: + return "Missing or unknown DNN in a slice"; + case cause_value_type_::invalid_pti_value: + return "Invalid PTI value"; + case cause_value_type_::maximum_data_rate_per_ue_for_user_plane_integrity_protection_is_too_low: + return "Maximum data rate per UE for user-plane integrity protection is too low"; + case cause_value_type_::semantic_error_in_the_qo_s_operation: + return "Semantic error in the QoS operation"; + case cause_value_type_::syntactical_error_in_the_qo_s_operation: + return "Syntactical error in the QoS operation"; + case cause_value_type_::invalid_mapped_eps_bearer_identity: + return "Invalid mapped EPS bearer identity"; + case cause_value_type_::semantically_incorrect_message: + return "Semantically incorrect message"; + case cause_value_type_::invalid_mandatory_information: + return "Invalid mandatory information"; + case cause_value_type_::message_type_non_existent_or_not_implemented: + return "Message type non-existent or not implemented"; + case cause_value_type_::message_type_not_compatible_with_the_protocol_state: + return "Message type not compatible with the protocol state"; + case cause_value_type_::information_element_non_existent_or_not_implemented: + return "Information element non-existent or not implemented"; + case cause_value_type_::conditional_ie_error: + return "Conditional IE error"; + case cause_value_type_::message_not_compatible_with_the_protocol_state: + return "Message not compatible with the protocol state"; + case cause_value_type_::protocol_error_unspecified: + return "Protocol error, unspecified"; + default: + return "Invalid Choice"; + } +} +// IE: GPRS timer +// Reference: 9.11.2.3 +SRSASN_CODE gprs_timer_t::pack(asn1::bit_ref& bref) +{ + HANDLE_CODE(unit.pack(bref)); + HANDLE_CODE(bref.pack(timer_value, 5)); + return SRSASN_SUCCESS; +} + +// IE: GPRS timer +// Reference: 9.11.2.3 +SRSASN_CODE gprs_timer_t::unpack(asn1::cbit_ref& bref) +{ + HANDLE_CODE(unit.unpack(bref)); + HANDLE_CODE(bref.unpack(timer_value, 5)); + return SRSASN_SUCCESS; +} + +const char* gprs_timer_t::Unit_type_::to_string() const +{ + switch (value) { + case Unit_type_::value_is_incremented_in_multiples_of_2_seconds: + return "value is incremented in multiples of 2 seconds"; + case Unit_type_::value_is_incremented_in_multiples_of_1_minute: + return "value is incremented in multiples of 1 minute"; + case Unit_type_::value_is_incremented_in_multiples_of_decihours: + return "value is incremented in multiples of decihours"; + case Unit_type_::value_indicates_that_the_timer_is_deactivated: + return "value indicates that the timer is deactivated"; + default: + return "Invalid Choice"; + } +} +// IE: Always-on PDU session indication +// Reference: 9.11.4.3 +SRSASN_CODE always_on_pdu_session_indication_t::pack(asn1::bit_ref& bref) +{ + // 3 Spare bits + HANDLE_CODE(bref.pack(0x0, 3)); + HANDLE_CODE(bref.pack(apsr, 1)); + return SRSASN_SUCCESS; +} + +// IE: Always-on PDU session indication +// Reference: 9.11.4.3 +SRSASN_CODE always_on_pdu_session_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 3 Spare bits + bref.advance_bits(3); + HANDLE_CODE(bref.unpack(apsr, 1)); + return SRSASN_SUCCESS; +} + +// IE: Mapped EPS bearer contexts +// Reference: 9.11.4.8 +SRSASN_CODE mapped_eps_bearer_contexts_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // max. length of 65535 not checked: uint overflow + if (length < 4) { + asn1::log_error("Encoding Failed (Mapped EPS bearer contexts): Packed length (%d) is not in range of min: 4 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: Mapped EPS bearer contexts +// Reference: 9.11.4.8 +SRSASN_CODE mapped_eps_bearer_contexts_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // max. length of 65535 not checked: uint overflow + if (length < 4) { + asn1::log_error("Decoding Failed (Mapped EPS bearer contexts): Length (%d) is not in range of min: 4 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: QoS flow descriptions +// Reference: 9.11.4.12 +SRSASN_CODE qo_s_flow_descriptions_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + // TODO proper packing + + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // max. length of 65535 not checked: uint overflow + if (length < 3) { + asn1::log_error("Encoding Failed (QoS flow descriptions): Packed length (%d) is not in range of min: 3 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: QoS flow descriptions +// Reference: 9.11.4.12 +SRSASN_CODE qo_s_flow_descriptions_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // max. length of 65535 not checked: uint overflow + if (length < 3) { + asn1::log_error("Decoding Failed (QoS flow descriptions): Length (%d) is not in range of min: 3 bytes", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + + // TODO proper unpacking + bref.advance_bits(length * 8); + return SRSASN_SUCCESS; +} + +// IE: 5GSM network feature support +// Reference: 9.11.4.18 +SRSASN_CODE network_feature_support_5gsm_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 7 Spare bits + HANDLE_CODE(bref.pack(0x0, 7)); + HANDLE_CODE(ept_s1.pack(bref)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length < 1 || length > 13) { + asn1::log_error( + "Encoding Failed (5GSM network feature support): Packed length (%d) is not in range of min: 1 and max 13 bytes", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: 5GSM network feature support +// Reference: 9.11.4.18 +SRSASN_CODE network_feature_support_5gsm_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length < 1 || length > 13) { + asn1::log_error( + "Decoding Failed (5GSM network feature support): Length (%d) is not in range of min: 1 and max 13 bytes", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 7 Spare bits + bref.advance_bits(7); + HANDLE_CODE(ept_s1.unpack(bref)); + if (length > 1) { + HANDLE_CODE(bref.advance_bits((length - 1) * 8)); + } + return SRSASN_SUCCESS; +} + +const char* network_feature_support_5gsm_t::EPT_S1_type_::to_string() const +{ + switch (value) { + case EPT_S1_type_::ethernet_pdn_type_in_s1_mode_not_supported: + return "Ethernet PDN type in S1 mode not supported"; + case EPT_S1_type_::ethernet_pdn_type_in_s1_mode_supported: + return "Ethernet PDN type in S1 mode supported"; + default: + return "Invalid Choice"; + } +} +// IE: Serving PLMN rate control +// Reference: 9.11.4.20 +SRSASN_CODE serving_plmn_rate_control_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + HANDLE_CODE(bref.pack(serving_plmn_rate_control_value, 16)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 2) { + asn1::log_error("Encoding Failed (Serving PLMN rate control): Packed length (%d) does not equal expected length 2", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Serving PLMN rate control +// Reference: 9.11.4.20 +SRSASN_CODE serving_plmn_rate_control_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 2) { + asn1::log_error("Decoding Failed (Serving PLMN rate control): Length (%d) does not equal expected length 2", + length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + HANDLE_CODE(bref.unpack(serving_plmn_rate_control_value, 16)); + return SRSASN_SUCCESS; +} + +// IE: ATSSS container +// Reference: 9.11.4.22 +SRSASN_CODE atsss_container_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(16)); + + HANDLE_CODE(bref.pack_bytes(nas_message_container.data(), nas_message_container.size())); + bref.align_bytes_zero(); + uint16_t length = (uint16_t)(ceilf((float)bref.distance(bref_length) / 8) - 2); + + // min. length of 0 not checked: uint underflow + // max. length of 65535 not checked: uint overflow + HANDLE_CODE(bref_length.pack(length, 16)); + return SRSASN_SUCCESS; +} + +// IE: ATSSS container +// Reference: 9.11.4.22 +SRSASN_CODE atsss_container_t::unpack(asn1::cbit_ref& bref) +{ + uint16_t length = 0; + HANDLE_CODE(bref.unpack(length, 16)); + // min. length of 0 not checked: uint underflow + nas_message_container.resize(length); + HANDLE_CODE(bref.unpack_bytes(nas_message_container.data(), length)); + return SRSASN_SUCCESS; +} + +// IE: Control plane only indication +// Reference: 9.11.4.23 +SRSASN_CODE control_plane_only_indication_t::pack(asn1::bit_ref& bref) +{ + // 3 Spare bits + HANDLE_CODE(bref.pack(0x0, 3)); + HANDLE_CODE(bref.pack(cpoi, 1)); + return SRSASN_SUCCESS; +} + +// IE: Control plane only indication +// Reference: 9.11.4.23 +SRSASN_CODE control_plane_only_indication_t::unpack(asn1::cbit_ref& bref) +{ + // 3 Spare bits + bref.advance_bits(3); + HANDLE_CODE(bref.unpack(cpoi, 1)); + return SRSASN_SUCCESS; +} + +// IE: Allowed SSC mode +// Reference: 9.11.4.5 +SRSASN_CODE allowed_ssc_mode_t::pack(asn1::bit_ref& bref) +{ + // 1 Spare bits + HANDLE_CODE(bref.pack(0x0, 1)); + HANDLE_CODE(bref.pack(ssc3, 1)); + HANDLE_CODE(bref.pack(ssc2, 1)); + HANDLE_CODE(bref.pack(ssc1, 1)); + return SRSASN_SUCCESS; +} + +// IE: Allowed SSC mode +// Reference: 9.11.4.5 +SRSASN_CODE allowed_ssc_mode_t::unpack(asn1::cbit_ref& bref) +{ + // 1 Spare bits + bref.advance_bits(1); + HANDLE_CODE(bref.unpack(ssc3, 1)); + HANDLE_CODE(bref.unpack(ssc2, 1)); + HANDLE_CODE(bref.unpack(ssc1, 1)); + return SRSASN_SUCCESS; +} + +// IE: 5GSM congestion re-attempt indicator +// Reference: 9.11.4.21 +SRSASN_CODE congestion_re_attempt_indicator_5gsm_t::pack(asn1::bit_ref& bref) +{ + // 7 Spare bits + HANDLE_CODE(bref.pack(0x0, 7)); + HANDLE_CODE(abo.pack(bref)); + return SRSASN_SUCCESS; +} + +// IE: 5GSM congestion re-attempt indicator +// Reference: 9.11.4.21 +SRSASN_CODE congestion_re_attempt_indicator_5gsm_t::unpack(asn1::cbit_ref& bref) +{ + // 7 Spare bits + bref.advance_bits(7); + HANDLE_CODE(abo.unpack(bref)); + return SRSASN_SUCCESS; +} + +const char* congestion_re_attempt_indicator_5gsm_t::abo_type_::to_string() const +{ + switch (value) { + case abo_type_::the_back_off_timer_is_applied_in_the_registered_plmn: + return "The back-off timer is applied in the registered PLMN"; + case abo_type_::the_back_off_timer_is_applied_in_all_plm_ns: + return "The back-off timer is applied in all PLMNs"; + default: + return "Invalid Choice"; + } +} +// IE: Re-attempt indicator +// Reference: 9.11.4.17 +SRSASN_CODE re_attempt_indicator_t::pack(asn1::bit_ref& bref) +{ + // Save length bref pointer + asn1::bit_ref bref_length = bref; + HANDLE_CODE(bref.advance_bits(8)); + + // 6 Spare bits + HANDLE_CODE(bref.pack(0x0, 6)); + HANDLE_CODE(bref.pack(eplmnc, 1)); + HANDLE_CODE(bref.pack(ratc, 1)); + + bref.align_bytes_zero(); + uint8_t length = (uint8_t)(ceilf((float)bref.distance(bref_length) / 8) - 1); + + if (length != 1) { + asn1::log_error("Encoding Failed (Re-attempt indicator): Packed length (%d) does not equal expected length 1", + length); + return asn1::SRSASN_ERROR_ENCODE_FAIL; + } + HANDLE_CODE(bref_length.pack(length, 8)); + return SRSASN_SUCCESS; +} + +// IE: Re-attempt indicator +// Reference: 9.11.4.17 +SRSASN_CODE re_attempt_indicator_t::unpack(asn1::cbit_ref& bref) +{ + uint8_t length = 0; + HANDLE_CODE(bref.unpack(length, 8)); + if (length != 1) { + asn1::log_error("Decoding Failed (Re-attempt indicator): Length (%d) does not equal expected length 1", length); + return asn1::SRSASN_ERROR_DECODE_FAIL; + } + // 6 Spare bits + bref.advance_bits(6); + HANDLE_CODE(bref.unpack(eplmnc, 1)); + HANDLE_CODE(bref.unpack(ratc, 1)); + return SRSASN_SUCCESS; +} + +} // namespace nas_5g +} // namespace srsran diff --git a/lib/src/asn1/nas_5g_msg.cc b/lib/src/asn1/nas_5g_msg.cc new file mode 100644 index 000000000..6f7247a8e --- /dev/null +++ b/lib/src/asn1/nas_5g_msg.cc @@ -0,0 +1,4207 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsran/asn1/nas_5g_msg.h" +#include "srsran/asn1/nas_5g_ies.h" +#include "srsran/asn1/nas_5g_utils.h" + +#include "srsran/asn1/asn1_utils.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/config.h" + +#include +#include +#include + +namespace srsran { +namespace nas_5g { + +SRSASN_CODE registration_request_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(ng_ksi.pack(bref)); + HANDLE_CODE(registration_type_5gs.pack(bref)); + HANDLE_CODE(mobile_identity_5gs.pack(bref)); + + // Optional fields + if (non_current_native_nas_key_set_identifier_present == true) { + HANDLE_CODE(bref.pack(ie_iei_non_current_native_nas_key_set_identifier, 4)); + HANDLE_CODE(non_current_native_nas_key_set_identifier.pack(bref)); + } + if (capability_5gmm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_capability_5gmm, 8)); + HANDLE_CODE(capability_5gmm.pack(bref)); + } + if (ue_security_capability_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_security_capability, 8)); + HANDLE_CODE(ue_security_capability.pack(bref)); + } + if (requested_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_requested_nssai, 8)); + HANDLE_CODE(requested_nssai.pack(bref)); + } + if (last_visited_registered_tai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_last_visited_registered_tai, 8)); + HANDLE_CODE(last_visited_registered_tai.pack(bref)); + } + if (s1_ue_network_capability_present == true) { + HANDLE_CODE(bref.pack(ie_iei_s1_ue_network_capability, 8)); + HANDLE_CODE(s1_ue_network_capability.pack(bref)); + } + if (uplink_data_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_uplink_data_status, 8)); + HANDLE_CODE(uplink_data_status.pack(bref)); + } + if (pdu_session_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_status, 8)); + HANDLE_CODE(pdu_session_status.pack(bref)); + } + if (mico_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_mico_indication, 4)); + HANDLE_CODE(mico_indication.pack(bref)); + } + if (ue_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_status, 8)); + HANDLE_CODE(ue_status.pack(bref)); + } + if (additional_guti_present == true) { + HANDLE_CODE(bref.pack(ie_iei_additional_guti, 8)); + HANDLE_CODE(additional_guti.pack(bref)); + } + if (allowed_pdu_session_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_allowed_pdu_session_status, 8)); + HANDLE_CODE(allowed_pdu_session_status.pack(bref)); + } + if (ue_usage_setting_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_usage_setting, 8)); + HANDLE_CODE(ue_usage_setting.pack(bref)); + } + if (requested_drx_parameters_present == true) { + HANDLE_CODE(bref.pack(ie_iei_requested_drx_parameters, 8)); + HANDLE_CODE(requested_drx_parameters.pack(bref)); + } + if (eps_nas_message_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eps_nas_message_container, 8)); + HANDLE_CODE(eps_nas_message_container.pack(bref)); + } + if (ladn_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ladn_indication, 8)); + HANDLE_CODE(ladn_indication.pack(bref)); + } + if (payload_container_type_present == true) { + HANDLE_CODE(bref.pack(ie_iei_payload_container_type, 4)); + HANDLE_CODE(payload_container_type.pack(bref)); + } + if (payload_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_payload_container, 8)); + HANDLE_CODE(payload_container.pack(bref)); + } + if (network_slicing_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_network_slicing_indication, 4)); + HANDLE_CODE(network_slicing_indication.pack(bref)); + } + if (update_type_5gs_present == true) { + HANDLE_CODE(bref.pack(ie_iei_update_type_5gs, 8)); + HANDLE_CODE(update_type_5gs.pack(bref)); + } + if (mobile_station_classmark_2_present == true) { + HANDLE_CODE(bref.pack(ie_iei_mobile_station_classmark_2, 8)); + HANDLE_CODE(mobile_station_classmark_2.pack(bref)); + } + if (supported_codecs_present == true) { + HANDLE_CODE(bref.pack(ie_iei_supported_codecs, 8)); + HANDLE_CODE(supported_codecs.pack(bref)); + } + if (nas_message_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_nas_message_container, 8)); + HANDLE_CODE(nas_message_container.pack(bref)); + } + if (eps_bearer_context_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eps_bearer_context_status, 8)); + HANDLE_CODE(eps_bearer_context_status.pack(bref)); + } + if (requested_extended_drx_parameters_present == true) { + HANDLE_CODE(bref.pack(ie_iei_requested_extended_drx_parameters, 8)); + HANDLE_CODE(requested_extended_drx_parameters.pack(bref)); + } + if (t3324_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3324_value, 8)); + HANDLE_CODE(t3324_value.pack(bref)); + } + if (ue_radio_capability_id_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_radio_capability_id, 8)); + HANDLE_CODE(ue_radio_capability_id.pack(bref)); + } + if (requested_mapped_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_requested_mapped_nssai, 8)); + HANDLE_CODE(requested_mapped_nssai.pack(bref)); + } + if (additional_information_requested_present == true) { + HANDLE_CODE(bref.pack(ie_iei_additional_information_requested, 8)); + HANDLE_CODE(additional_information_requested.pack(bref)); + } + if (requested_wus_assistance_information_present == true) { + HANDLE_CODE(bref.pack(ie_iei_requested_wus_assistance_information, 8)); + HANDLE_CODE(requested_wus_assistance_information.pack(bref)); + } + if (n5gc_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_n5gc_indication, 4)); + HANDLE_CODE(n5gc_indication.pack(bref)); + } + if (requested_nb_n1_mode_drx_parameters_present == true) { + HANDLE_CODE(bref.pack(ie_iei_requested_nb_n1_mode_drx_parameters, 8)); + HANDLE_CODE(requested_nb_n1_mode_drx_parameters.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE registration_request_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(ng_ksi.unpack(bref)); + HANDLE_CODE(registration_type_5gs.unpack(bref)); + HANDLE_CODE(mobile_identity_5gs.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_non_current_native_nas_key_set_identifier: + non_current_native_nas_key_set_identifier_present = true; + HANDLE_CODE(non_current_native_nas_key_set_identifier.unpack(bref)); + break; + case ie_iei_capability_5gmm: + capability_5gmm_present = true; + HANDLE_CODE(capability_5gmm.unpack(bref)); + break; + case ie_iei_ue_security_capability: + ue_security_capability_present = true; + HANDLE_CODE(ue_security_capability.unpack(bref)); + break; + case ie_iei_requested_nssai: + requested_nssai_present = true; + HANDLE_CODE(requested_nssai.unpack(bref)); + break; + case ie_iei_last_visited_registered_tai: + last_visited_registered_tai_present = true; + HANDLE_CODE(last_visited_registered_tai.unpack(bref)); + break; + case ie_iei_s1_ue_network_capability: + s1_ue_network_capability_present = true; + HANDLE_CODE(s1_ue_network_capability.unpack(bref)); + break; + case ie_iei_uplink_data_status: + uplink_data_status_present = true; + HANDLE_CODE(uplink_data_status.unpack(bref)); + break; + case ie_iei_pdu_session_status: + pdu_session_status_present = true; + HANDLE_CODE(pdu_session_status.unpack(bref)); + break; + case ie_iei_mico_indication: + mico_indication_present = true; + HANDLE_CODE(mico_indication.unpack(bref)); + break; + case ie_iei_ue_status: + ue_status_present = true; + HANDLE_CODE(ue_status.unpack(bref)); + break; + case ie_iei_additional_guti: + additional_guti_present = true; + HANDLE_CODE(additional_guti.unpack(bref)); + break; + case ie_iei_allowed_pdu_session_status: + allowed_pdu_session_status_present = true; + HANDLE_CODE(allowed_pdu_session_status.unpack(bref)); + break; + case ie_iei_ue_usage_setting: + ue_usage_setting_present = true; + HANDLE_CODE(ue_usage_setting.unpack(bref)); + break; + case ie_iei_requested_drx_parameters: + requested_drx_parameters_present = true; + HANDLE_CODE(requested_drx_parameters.unpack(bref)); + break; + case ie_iei_eps_nas_message_container: + eps_nas_message_container_present = true; + HANDLE_CODE(eps_nas_message_container.unpack(bref)); + break; + case ie_iei_ladn_indication: + ladn_indication_present = true; + HANDLE_CODE(ladn_indication.unpack(bref)); + break; + case ie_iei_payload_container_type: + payload_container_type_present = true; + HANDLE_CODE(payload_container_type.unpack(bref)); + break; + case ie_iei_payload_container: + payload_container_present = true; + HANDLE_CODE(payload_container.unpack(bref)); + break; + case ie_iei_network_slicing_indication: + network_slicing_indication_present = true; + HANDLE_CODE(network_slicing_indication.unpack(bref)); + break; + case ie_iei_update_type_5gs: + update_type_5gs_present = true; + HANDLE_CODE(update_type_5gs.unpack(bref)); + break; + case ie_iei_mobile_station_classmark_2: + mobile_station_classmark_2_present = true; + HANDLE_CODE(mobile_station_classmark_2.unpack(bref)); + break; + case ie_iei_supported_codecs: + supported_codecs_present = true; + HANDLE_CODE(supported_codecs.unpack(bref)); + break; + case ie_iei_nas_message_container: + nas_message_container_present = true; + HANDLE_CODE(nas_message_container.unpack(bref)); + break; + case ie_iei_eps_bearer_context_status: + eps_bearer_context_status_present = true; + HANDLE_CODE(eps_bearer_context_status.unpack(bref)); + break; + case ie_iei_requested_extended_drx_parameters: + requested_extended_drx_parameters_present = true; + HANDLE_CODE(requested_extended_drx_parameters.unpack(bref)); + break; + case ie_iei_t3324_value: + t3324_value_present = true; + HANDLE_CODE(t3324_value.unpack(bref)); + break; + case ie_iei_ue_radio_capability_id: + ue_radio_capability_id_present = true; + HANDLE_CODE(ue_radio_capability_id.unpack(bref)); + break; + case ie_iei_requested_mapped_nssai: + requested_mapped_nssai_present = true; + HANDLE_CODE(requested_mapped_nssai.unpack(bref)); + break; + case ie_iei_additional_information_requested: + additional_information_requested_present = true; + HANDLE_CODE(additional_information_requested.unpack(bref)); + break; + case ie_iei_requested_wus_assistance_information: + requested_wus_assistance_information_present = true; + HANDLE_CODE(requested_wus_assistance_information.unpack(bref)); + break; + case ie_iei_n5gc_indication: + n5gc_indication_present = true; + HANDLE_CODE(n5gc_indication.unpack(bref)); + break; + case ie_iei_requested_nb_n1_mode_drx_parameters: + requested_nb_n1_mode_drx_parameters_present = true; + HANDLE_CODE(requested_nb_n1_mode_drx_parameters.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE registration_accept_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(registration_result_5gs.pack(bref)); + + // Optional fields + if (guti_5g_present == true) { + HANDLE_CODE(bref.pack(ie_iei_guti_5g, 8)); + HANDLE_CODE(guti_5g.pack(bref)); + } + if (equivalent_plm_ns_present == true) { + HANDLE_CODE(bref.pack(ie_iei_equivalent_plm_ns, 8)); + HANDLE_CODE(equivalent_plm_ns.pack(bref)); + } + if (tai_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_tai_list, 8)); + HANDLE_CODE(tai_list.pack(bref)); + } + if (allowed_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_allowed_nssai, 8)); + HANDLE_CODE(allowed_nssai.pack(bref)); + } + if (rejected_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_rejected_nssai, 8)); + HANDLE_CODE(rejected_nssai.pack(bref)); + } + if (configured_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_configured_nssai, 8)); + HANDLE_CODE(configured_nssai.pack(bref)); + } + if (network_feature_support_5gs_present == true) { + HANDLE_CODE(bref.pack(ie_iei_network_feature_support_5gs, 8)); + HANDLE_CODE(network_feature_support_5gs.pack(bref)); + } + if (pdu_session_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_status, 8)); + HANDLE_CODE(pdu_session_status.pack(bref)); + } + if (pdu_session_reactivation_result_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_reactivation_result, 8)); + HANDLE_CODE(pdu_session_reactivation_result.pack(bref)); + } + if (pdu_session_reactivation_result_error_cause_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_reactivation_result_error_cause, 8)); + HANDLE_CODE(pdu_session_reactivation_result_error_cause.pack(bref)); + } + if (ladn_information_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ladn_information, 8)); + HANDLE_CODE(ladn_information.pack(bref)); + } + if (mico_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_mico_indication, 4)); + HANDLE_CODE(mico_indication.pack(bref)); + } + if (network_slicing_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_network_slicing_indication, 4)); + HANDLE_CODE(network_slicing_indication.pack(bref)); + } + if (service_area_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_service_area_list, 8)); + HANDLE_CODE(service_area_list.pack(bref)); + } + if (t3512_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3512_value, 8)); + HANDLE_CODE(t3512_value.pack(bref)); + } + if (non_3_gpp_de_registration_timer_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_non_3_gpp_de_registration_timer_value, 8)); + HANDLE_CODE(non_3_gpp_de_registration_timer_value.pack(bref)); + } + if (t3502_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3502_value, 8)); + HANDLE_CODE(t3502_value.pack(bref)); + } + if (emergency_number_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_emergency_number_list, 8)); + HANDLE_CODE(emergency_number_list.pack(bref)); + } + if (extended_emergency_number_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_emergency_number_list, 8)); + HANDLE_CODE(extended_emergency_number_list.pack(bref)); + } + if (sor_transparent_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_sor_transparent_container, 8)); + HANDLE_CODE(sor_transparent_container.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (nssai_inclusion_mode_present == true) { + HANDLE_CODE(bref.pack(ie_iei_nssai_inclusion_mode, 4)); + HANDLE_CODE(nssai_inclusion_mode.pack(bref)); + } + if (operator_defined_access_category_definitions_present == true) { + HANDLE_CODE(bref.pack(ie_iei_operator_defined_access_category_definitions, 8)); + HANDLE_CODE(operator_defined_access_category_definitions.pack(bref)); + } + if (negotiated_drx_parameters_present == true) { + HANDLE_CODE(bref.pack(ie_iei_negotiated_drx_parameters, 8)); + HANDLE_CODE(negotiated_drx_parameters.pack(bref)); + } + if (non_3_gpp_nw_policies_present == true) { + HANDLE_CODE(bref.pack(ie_iei_non_3_gpp_nw_policies, 4)); + HANDLE_CODE(non_3_gpp_nw_policies.pack(bref)); + } + if (eps_bearer_context_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eps_bearer_context_status, 8)); + HANDLE_CODE(eps_bearer_context_status.pack(bref)); + } + if (negotiated_extended_drx_parameters_present == true) { + HANDLE_CODE(bref.pack(ie_iei_negotiated_extended_drx_parameters, 8)); + HANDLE_CODE(negotiated_extended_drx_parameters.pack(bref)); + } + if (t3447_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3447_value, 8)); + HANDLE_CODE(t3447_value.pack(bref)); + } + if (t3448_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3448_value, 8)); + HANDLE_CODE(t3448_value.pack(bref)); + } + if (t3324_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3324_value, 8)); + HANDLE_CODE(t3324_value.pack(bref)); + } + if (ue_radio_capability_id_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_radio_capability_id, 8)); + HANDLE_CODE(ue_radio_capability_id.pack(bref)); + } + if (ue_radio_capability_id_deletion_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_radio_capability_id_deletion_indication, 4)); + HANDLE_CODE(ue_radio_capability_id_deletion_indication.pack(bref)); + } + if (pending_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pending_nssai, 8)); + HANDLE_CODE(pending_nssai.pack(bref)); + } + if (ciphering_key_data_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ciphering_key_data, 8)); + HANDLE_CODE(ciphering_key_data.pack(bref)); + } + if (cag_information_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cag_information_list, 8)); + HANDLE_CODE(cag_information_list.pack(bref)); + } + if (truncated_5g_s_tmsi_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_truncated_5g_s_tmsi_configuration, 8)); + HANDLE_CODE(truncated_5g_s_tmsi_configuration.pack(bref)); + } + if (negotiated_wus_assistance_information_present == true) { + HANDLE_CODE(bref.pack(ie_iei_negotiated_wus_assistance_information, 8)); + HANDLE_CODE(negotiated_wus_assistance_information.pack(bref)); + } + if (negotiated_nb_n1_mode_drx_parameters_present == true) { + HANDLE_CODE(bref.pack(ie_iei_negotiated_nb_n1_mode_drx_parameters, 8)); + HANDLE_CODE(negotiated_nb_n1_mode_drx_parameters.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE registration_accept_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(registration_result_5gs.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_guti_5g: + guti_5g_present = true; + HANDLE_CODE(guti_5g.unpack(bref)); + break; + case ie_iei_equivalent_plm_ns: + equivalent_plm_ns_present = true; + HANDLE_CODE(equivalent_plm_ns.unpack(bref)); + break; + case ie_iei_tai_list: + tai_list_present = true; + HANDLE_CODE(tai_list.unpack(bref)); + break; + case ie_iei_allowed_nssai: + allowed_nssai_present = true; + HANDLE_CODE(allowed_nssai.unpack(bref)); + break; + case ie_iei_rejected_nssai: + rejected_nssai_present = true; + HANDLE_CODE(rejected_nssai.unpack(bref)); + break; + case ie_iei_configured_nssai: + configured_nssai_present = true; + HANDLE_CODE(configured_nssai.unpack(bref)); + break; + case ie_iei_network_feature_support_5gs: + network_feature_support_5gs_present = true; + HANDLE_CODE(network_feature_support_5gs.unpack(bref)); + break; + case ie_iei_pdu_session_status: + pdu_session_status_present = true; + HANDLE_CODE(pdu_session_status.unpack(bref)); + break; + case ie_iei_pdu_session_reactivation_result: + pdu_session_reactivation_result_present = true; + HANDLE_CODE(pdu_session_reactivation_result.unpack(bref)); + break; + case ie_iei_pdu_session_reactivation_result_error_cause: + pdu_session_reactivation_result_error_cause_present = true; + HANDLE_CODE(pdu_session_reactivation_result_error_cause.unpack(bref)); + break; + case ie_iei_ladn_information: + ladn_information_present = true; + HANDLE_CODE(ladn_information.unpack(bref)); + break; + case ie_iei_mico_indication: + mico_indication_present = true; + HANDLE_CODE(mico_indication.unpack(bref)); + break; + case ie_iei_network_slicing_indication: + network_slicing_indication_present = true; + HANDLE_CODE(network_slicing_indication.unpack(bref)); + break; + case ie_iei_service_area_list: + service_area_list_present = true; + HANDLE_CODE(service_area_list.unpack(bref)); + break; + case ie_iei_t3512_value: + t3512_value_present = true; + HANDLE_CODE(t3512_value.unpack(bref)); + break; + case ie_iei_non_3_gpp_de_registration_timer_value: + non_3_gpp_de_registration_timer_value_present = true; + HANDLE_CODE(non_3_gpp_de_registration_timer_value.unpack(bref)); + break; + case ie_iei_t3502_value: + t3502_value_present = true; + HANDLE_CODE(t3502_value.unpack(bref)); + break; + case ie_iei_emergency_number_list: + emergency_number_list_present = true; + HANDLE_CODE(emergency_number_list.unpack(bref)); + break; + case ie_iei_extended_emergency_number_list: + extended_emergency_number_list_present = true; + HANDLE_CODE(extended_emergency_number_list.unpack(bref)); + break; + case ie_iei_sor_transparent_container: + sor_transparent_container_present = true; + HANDLE_CODE(sor_transparent_container.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_nssai_inclusion_mode: + nssai_inclusion_mode_present = true; + HANDLE_CODE(nssai_inclusion_mode.unpack(bref)); + break; + case ie_iei_operator_defined_access_category_definitions: + operator_defined_access_category_definitions_present = true; + HANDLE_CODE(operator_defined_access_category_definitions.unpack(bref)); + break; + case ie_iei_negotiated_drx_parameters: + negotiated_drx_parameters_present = true; + HANDLE_CODE(negotiated_drx_parameters.unpack(bref)); + break; + case ie_iei_non_3_gpp_nw_policies: + non_3_gpp_nw_policies_present = true; + HANDLE_CODE(non_3_gpp_nw_policies.unpack(bref)); + break; + case ie_iei_eps_bearer_context_status: + eps_bearer_context_status_present = true; + HANDLE_CODE(eps_bearer_context_status.unpack(bref)); + break; + case ie_iei_negotiated_extended_drx_parameters: + negotiated_extended_drx_parameters_present = true; + HANDLE_CODE(negotiated_extended_drx_parameters.unpack(bref)); + break; + case ie_iei_t3447_value: + t3447_value_present = true; + HANDLE_CODE(t3447_value.unpack(bref)); + break; + case ie_iei_t3448_value: + t3448_value_present = true; + HANDLE_CODE(t3448_value.unpack(bref)); + break; + case ie_iei_t3324_value: + t3324_value_present = true; + HANDLE_CODE(t3324_value.unpack(bref)); + break; + case ie_iei_ue_radio_capability_id: + ue_radio_capability_id_present = true; + HANDLE_CODE(ue_radio_capability_id.unpack(bref)); + break; + case ie_iei_ue_radio_capability_id_deletion_indication: + ue_radio_capability_id_deletion_indication_present = true; + HANDLE_CODE(ue_radio_capability_id_deletion_indication.unpack(bref)); + break; + case ie_iei_pending_nssai: + pending_nssai_present = true; + HANDLE_CODE(pending_nssai.unpack(bref)); + break; + case ie_iei_ciphering_key_data: + ciphering_key_data_present = true; + HANDLE_CODE(ciphering_key_data.unpack(bref)); + break; + case ie_iei_cag_information_list: + cag_information_list_present = true; + HANDLE_CODE(cag_information_list.unpack(bref)); + break; + case ie_iei_truncated_5g_s_tmsi_configuration: + truncated_5g_s_tmsi_configuration_present = true; + HANDLE_CODE(truncated_5g_s_tmsi_configuration.unpack(bref)); + break; + case ie_iei_negotiated_wus_assistance_information: + negotiated_wus_assistance_information_present = true; + HANDLE_CODE(negotiated_wus_assistance_information.unpack(bref)); + break; + case ie_iei_negotiated_nb_n1_mode_drx_parameters: + negotiated_nb_n1_mode_drx_parameters_present = true; + HANDLE_CODE(negotiated_nb_n1_mode_drx_parameters.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE registration_complete_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (sor_transparent_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_sor_transparent_container, 8)); + HANDLE_CODE(sor_transparent_container.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE registration_complete_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_sor_transparent_container: + sor_transparent_container_present = true; + HANDLE_CODE(sor_transparent_container.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE registration_reject_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.pack(bref)); + + // Optional fields + if (t3346_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3346_value, 8)); + HANDLE_CODE(t3346_value.pack(bref)); + } + if (t3502_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3502_value, 8)); + HANDLE_CODE(t3502_value.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (rejected_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_rejected_nssai, 8)); + HANDLE_CODE(rejected_nssai.pack(bref)); + } + if (cag_information_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cag_information_list, 8)); + HANDLE_CODE(cag_information_list.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE registration_reject_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_t3346_value: + t3346_value_present = true; + HANDLE_CODE(t3346_value.unpack(bref)); + break; + case ie_iei_t3502_value: + t3502_value_present = true; + HANDLE_CODE(t3502_value.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_rejected_nssai: + rejected_nssai_present = true; + HANDLE_CODE(rejected_nssai.unpack(bref)); + break; + case ie_iei_cag_information_list: + cag_information_list_present = true; + HANDLE_CODE(cag_information_list.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE deregistration_request_ue_originating_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(ng_ksi.pack(bref)); + HANDLE_CODE(de_registration_type.pack(bref)); + HANDLE_CODE(mobile_identity_5gs.pack(bref)); + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE deregistration_request_ue_originating_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(ng_ksi.unpack(bref)); + HANDLE_CODE(de_registration_type.unpack(bref)); + HANDLE_CODE(mobile_identity_5gs.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE deregistration_accept_ue_originating_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE deregistration_accept_ue_originating_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE deregistration_request_ue_terminated_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.pack(bref)); + HANDLE_CODE(de_registration_type.pack(bref)); + + // Optional fields + if (cause_5gmm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cause_5gmm, 8)); + HANDLE_CODE(cause_5gmm.pack(bref)); + } + if (t3346_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3346_value, 8)); + HANDLE_CODE(t3346_value.pack(bref)); + } + if (rejected_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_rejected_nssai, 8)); + HANDLE_CODE(rejected_nssai.pack(bref)); + } + if (cag_information_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cag_information_list, 8)); + HANDLE_CODE(cag_information_list.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE deregistration_request_ue_terminated_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.unpack(bref)); + HANDLE_CODE(de_registration_type.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_cause_5gmm: + cause_5gmm_present = true; + HANDLE_CODE(cause_5gmm.unpack(bref)); + break; + case ie_iei_t3346_value: + t3346_value_present = true; + HANDLE_CODE(t3346_value.unpack(bref)); + break; + case ie_iei_rejected_nssai: + rejected_nssai_present = true; + HANDLE_CODE(rejected_nssai.unpack(bref)); + break; + case ie_iei_cag_information_list: + cag_information_list_present = true; + HANDLE_CODE(cag_information_list.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE deregistration_accept_ue_terminated_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE deregistration_accept_ue_terminated_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE service_request_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(service_type.pack(bref)); + HANDLE_CODE(ng_ksi.pack(bref)); + HANDLE_CODE(s_tmsi_5g.pack(bref)); + + // Optional fields + if (uplink_data_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_uplink_data_status, 8)); + HANDLE_CODE(uplink_data_status.pack(bref)); + } + if (pdu_session_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_status, 8)); + HANDLE_CODE(pdu_session_status.pack(bref)); + } + if (allowed_pdu_session_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_allowed_pdu_session_status, 8)); + HANDLE_CODE(allowed_pdu_session_status.pack(bref)); + } + if (nas_message_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_nas_message_container, 8)); + HANDLE_CODE(nas_message_container.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE service_request_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(service_type.unpack(bref)); + HANDLE_CODE(ng_ksi.unpack(bref)); + HANDLE_CODE(s_tmsi_5g.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_uplink_data_status: + uplink_data_status_present = true; + HANDLE_CODE(uplink_data_status.unpack(bref)); + break; + case ie_iei_pdu_session_status: + pdu_session_status_present = true; + HANDLE_CODE(pdu_session_status.unpack(bref)); + break; + case ie_iei_allowed_pdu_session_status: + allowed_pdu_session_status_present = true; + HANDLE_CODE(allowed_pdu_session_status.unpack(bref)); + break; + case ie_iei_nas_message_container: + nas_message_container_present = true; + HANDLE_CODE(nas_message_container.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE service_reject_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (pdu_session_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_status, 8)); + HANDLE_CODE(pdu_session_status.pack(bref)); + } + if (pdu_session_reactivation_result_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_reactivation_result, 8)); + HANDLE_CODE(pdu_session_reactivation_result.pack(bref)); + } + if (pdu_session_reactivation_result_error_cause_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_reactivation_result_error_cause, 8)); + HANDLE_CODE(pdu_session_reactivation_result_error_cause.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (t3448_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3448_value, 8)); + HANDLE_CODE(t3448_value.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE service_reject_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_pdu_session_status: + pdu_session_status_present = true; + HANDLE_CODE(pdu_session_status.unpack(bref)); + break; + case ie_iei_pdu_session_reactivation_result: + pdu_session_reactivation_result_present = true; + HANDLE_CODE(pdu_session_reactivation_result.unpack(bref)); + break; + case ie_iei_pdu_session_reactivation_result_error_cause: + pdu_session_reactivation_result_error_cause_present = true; + HANDLE_CODE(pdu_session_reactivation_result_error_cause.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_t3448_value: + t3448_value_present = true; + HANDLE_CODE(t3448_value.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE service_accept_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.pack(bref)); + + // Optional fields + if (pdu_session_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_status, 8)); + HANDLE_CODE(pdu_session_status.pack(bref)); + } + if (t3346_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3346_value, 8)); + HANDLE_CODE(t3346_value.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (t3448_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3448_value, 8)); + HANDLE_CODE(t3448_value.pack(bref)); + } + if (cag_information_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cag_information_list, 8)); + HANDLE_CODE(cag_information_list.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE service_accept_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_pdu_session_status: + pdu_session_status_present = true; + HANDLE_CODE(pdu_session_status.unpack(bref)); + break; + case ie_iei_t3346_value: + t3346_value_present = true; + HANDLE_CODE(t3346_value.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_t3448_value: + t3448_value_present = true; + HANDLE_CODE(t3448_value.unpack(bref)); + break; + case ie_iei_cag_information_list: + cag_information_list_present = true; + HANDLE_CODE(cag_information_list.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE configuration_update_command_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (configuration_update_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_configuration_update_indication, 4)); + HANDLE_CODE(configuration_update_indication.pack(bref)); + } + if (guti_5g_present == true) { + HANDLE_CODE(bref.pack(ie_iei_guti_5g, 8)); + HANDLE_CODE(guti_5g.pack(bref)); + } + if (tai_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_tai_list, 8)); + HANDLE_CODE(tai_list.pack(bref)); + } + if (allowed_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_allowed_nssai, 8)); + HANDLE_CODE(allowed_nssai.pack(bref)); + } + if (service_area_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_service_area_list, 8)); + HANDLE_CODE(service_area_list.pack(bref)); + } + if (full_name_for_network_present == true) { + HANDLE_CODE(bref.pack(ie_iei_full_name_for_network, 8)); + HANDLE_CODE(full_name_for_network.pack(bref)); + } + if (short_name_for_network_present == true) { + HANDLE_CODE(bref.pack(ie_iei_short_name_for_network, 8)); + HANDLE_CODE(short_name_for_network.pack(bref)); + } + if (local_time_zone_present == true) { + HANDLE_CODE(bref.pack(ie_iei_local_time_zone, 8)); + HANDLE_CODE(local_time_zone.pack(bref)); + } + if (universal_time_and_local_time_zone_present == true) { + HANDLE_CODE(bref.pack(ie_iei_universal_time_and_local_time_zone, 8)); + HANDLE_CODE(universal_time_and_local_time_zone.pack(bref)); + } + if (network_daylight_saving_time_present == true) { + HANDLE_CODE(bref.pack(ie_iei_network_daylight_saving_time, 8)); + HANDLE_CODE(network_daylight_saving_time.pack(bref)); + } + if (ladn_information_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ladn_information, 8)); + HANDLE_CODE(ladn_information.pack(bref)); + } + if (mico_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_mico_indication, 4)); + HANDLE_CODE(mico_indication.pack(bref)); + } + if (network_slicing_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_network_slicing_indication, 4)); + HANDLE_CODE(network_slicing_indication.pack(bref)); + } + if (configured_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_configured_nssai, 8)); + HANDLE_CODE(configured_nssai.pack(bref)); + } + if (rejected_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_rejected_nssai, 8)); + HANDLE_CODE(rejected_nssai.pack(bref)); + } + if (operator_defined_access_category_definitions_present == true) { + HANDLE_CODE(bref.pack(ie_iei_operator_defined_access_category_definitions, 8)); + HANDLE_CODE(operator_defined_access_category_definitions.pack(bref)); + } + if (sms_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_sms_indication, 4)); + HANDLE_CODE(sms_indication.pack(bref)); + } + if (t3447_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_t3447_value, 8)); + HANDLE_CODE(t3447_value.pack(bref)); + } + if (cag_information_list_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cag_information_list, 8)); + HANDLE_CODE(cag_information_list.pack(bref)); + } + if (ue_radio_capability_id_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_radio_capability_id, 8)); + HANDLE_CODE(ue_radio_capability_id.pack(bref)); + } + if (ue_radio_capability_id_deletion_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_radio_capability_id_deletion_indication, 4)); + HANDLE_CODE(ue_radio_capability_id_deletion_indication.pack(bref)); + } + if (registration_result_5gs_present == true) { + HANDLE_CODE(bref.pack(ie_iei_registration_result_5gs, 8)); + HANDLE_CODE(registration_result_5gs.pack(bref)); + } + if (truncated_5g_s_tmsi_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_truncated_5g_s_tmsi_configuration, 8)); + HANDLE_CODE(truncated_5g_s_tmsi_configuration.pack(bref)); + } + if (additional_configuration_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_additional_configuration_indication, 4)); + HANDLE_CODE(additional_configuration_indication.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE configuration_update_command_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_configuration_update_indication: + configuration_update_indication_present = true; + HANDLE_CODE(configuration_update_indication.unpack(bref)); + break; + case ie_iei_guti_5g: + guti_5g_present = true; + HANDLE_CODE(guti_5g.unpack(bref)); + break; + case ie_iei_tai_list: + tai_list_present = true; + HANDLE_CODE(tai_list.unpack(bref)); + break; + case ie_iei_allowed_nssai: + allowed_nssai_present = true; + HANDLE_CODE(allowed_nssai.unpack(bref)); + break; + case ie_iei_service_area_list: + service_area_list_present = true; + HANDLE_CODE(service_area_list.unpack(bref)); + break; + case ie_iei_full_name_for_network: + full_name_for_network_present = true; + HANDLE_CODE(full_name_for_network.unpack(bref)); + break; + case ie_iei_short_name_for_network: + short_name_for_network_present = true; + HANDLE_CODE(short_name_for_network.unpack(bref)); + break; + case ie_iei_local_time_zone: + local_time_zone_present = true; + HANDLE_CODE(local_time_zone.unpack(bref)); + break; + case ie_iei_universal_time_and_local_time_zone: + universal_time_and_local_time_zone_present = true; + HANDLE_CODE(universal_time_and_local_time_zone.unpack(bref)); + break; + case ie_iei_network_daylight_saving_time: + network_daylight_saving_time_present = true; + HANDLE_CODE(network_daylight_saving_time.unpack(bref)); + break; + case ie_iei_ladn_information: + ladn_information_present = true; + HANDLE_CODE(ladn_information.unpack(bref)); + break; + case ie_iei_mico_indication: + mico_indication_present = true; + HANDLE_CODE(mico_indication.unpack(bref)); + break; + case ie_iei_network_slicing_indication: + network_slicing_indication_present = true; + HANDLE_CODE(network_slicing_indication.unpack(bref)); + break; + case ie_iei_configured_nssai: + configured_nssai_present = true; + HANDLE_CODE(configured_nssai.unpack(bref)); + break; + case ie_iei_rejected_nssai: + rejected_nssai_present = true; + HANDLE_CODE(rejected_nssai.unpack(bref)); + break; + case ie_iei_operator_defined_access_category_definitions: + operator_defined_access_category_definitions_present = true; + HANDLE_CODE(operator_defined_access_category_definitions.unpack(bref)); + break; + case ie_iei_sms_indication: + sms_indication_present = true; + HANDLE_CODE(sms_indication.unpack(bref)); + break; + case ie_iei_t3447_value: + t3447_value_present = true; + HANDLE_CODE(t3447_value.unpack(bref)); + break; + case ie_iei_cag_information_list: + cag_information_list_present = true; + HANDLE_CODE(cag_information_list.unpack(bref)); + break; + case ie_iei_ue_radio_capability_id: + ue_radio_capability_id_present = true; + HANDLE_CODE(ue_radio_capability_id.unpack(bref)); + break; + case ie_iei_ue_radio_capability_id_deletion_indication: + ue_radio_capability_id_deletion_indication_present = true; + HANDLE_CODE(ue_radio_capability_id_deletion_indication.unpack(bref)); + break; + case ie_iei_registration_result_5gs: + registration_result_5gs_present = true; + HANDLE_CODE(registration_result_5gs.unpack(bref)); + break; + case ie_iei_truncated_5g_s_tmsi_configuration: + truncated_5g_s_tmsi_configuration_present = true; + HANDLE_CODE(truncated_5g_s_tmsi_configuration.unpack(bref)); + break; + case ie_iei_additional_configuration_indication: + additional_configuration_indication_present = true; + HANDLE_CODE(additional_configuration_indication.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE configuration_update_complete_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE configuration_update_complete_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE authentication_request_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.pack(bref)); + HANDLE_CODE(ng_ksi.pack(bref)); + HANDLE_CODE(abba.pack(bref)); + + // Optional fields + if (authentication_parameter_rand_present == true) { + HANDLE_CODE(bref.pack(ie_iei_authentication_parameter_rand, 8)); + HANDLE_CODE(authentication_parameter_rand.pack(bref)); + } + if (authentication_parameter_autn_present == true) { + HANDLE_CODE(bref.pack(ie_iei_authentication_parameter_autn, 8)); + HANDLE_CODE(authentication_parameter_autn.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE authentication_request_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.unpack(bref)); + HANDLE_CODE(ng_ksi.unpack(bref)); + HANDLE_CODE(abba.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_authentication_parameter_rand: + authentication_parameter_rand_present = true; + HANDLE_CODE(authentication_parameter_rand.unpack(bref)); + break; + case ie_iei_authentication_parameter_autn: + authentication_parameter_autn_present = true; + HANDLE_CODE(authentication_parameter_autn.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE authentication_response_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (authentication_response_parameter_present == true) { + HANDLE_CODE(bref.pack(ie_iei_authentication_response_parameter, 8)); + HANDLE_CODE(authentication_response_parameter.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE authentication_response_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_authentication_response_parameter: + authentication_response_parameter_present = true; + HANDLE_CODE(authentication_response_parameter.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE authentication_reject_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE authentication_reject_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE authentication_failure_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.pack(bref)); + + // Optional fields + if (authentication_failure_parameter_present == true) { + HANDLE_CODE(bref.pack(ie_iei_authentication_failure_parameter, 8)); + HANDLE_CODE(authentication_failure_parameter.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE authentication_failure_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_authentication_failure_parameter: + authentication_failure_parameter_present = true; + HANDLE_CODE(authentication_failure_parameter.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE authentication_result_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.pack(bref)); + HANDLE_CODE(ng_ksi.pack(bref)); + HANDLE_CODE(eap_message.pack(bref)); + + // Optional fields + if (abba_present == true) { + HANDLE_CODE(bref.pack(ie_iei_abba, 8)); + HANDLE_CODE(abba.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE authentication_result_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.unpack(bref)); + HANDLE_CODE(ng_ksi.unpack(bref)); + HANDLE_CODE(eap_message.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_abba: + abba_present = true; + HANDLE_CODE(abba.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE identity_request_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.pack(bref)); + HANDLE_CODE(identity_type.pack(bref)); + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE identity_request_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.unpack(bref)); + HANDLE_CODE(identity_type.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE identity_response_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(mobile_identity.pack(bref)); + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE identity_response_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(mobile_identity.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE security_mode_command_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(selected_nas_security_algorithms.pack(bref)); + HANDLE_CODE(spare_half_octet.pack(bref)); + HANDLE_CODE(ng_ksi.pack(bref)); + HANDLE_CODE(replayed_ue_security_capabilities.pack(bref)); + + // Optional fields + if (imeisv_request_present == true) { + HANDLE_CODE(bref.pack(ie_iei_imeisv_request, 4)); + HANDLE_CODE(imeisv_request.pack(bref)); + } + if (selected_eps_nas_security_algorithms_present == true) { + HANDLE_CODE(bref.pack(ie_iei_selected_eps_nas_security_algorithms, 8)); + HANDLE_CODE(selected_eps_nas_security_algorithms.pack(bref)); + } + if (additional_5g_security_information_present == true) { + HANDLE_CODE(bref.pack(ie_iei_additional_5g_security_information, 8)); + HANDLE_CODE(additional_5g_security_information.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (abba_present == true) { + HANDLE_CODE(bref.pack(ie_iei_abba, 8)); + HANDLE_CODE(abba.pack(bref)); + } + if (replayed_s1_ue_security_capabilities_present == true) { + HANDLE_CODE(bref.pack(ie_iei_replayed_s1_ue_security_capabilities, 8)); + HANDLE_CODE(replayed_s1_ue_security_capabilities.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE security_mode_command_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(selected_nas_security_algorithms.unpack(bref)); + HANDLE_CODE(spare_half_octet.unpack(bref)); + HANDLE_CODE(ng_ksi.unpack(bref)); + HANDLE_CODE(replayed_ue_security_capabilities.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_imeisv_request: + imeisv_request_present = true; + HANDLE_CODE(imeisv_request.unpack(bref)); + break; + case ie_iei_selected_eps_nas_security_algorithms: + selected_eps_nas_security_algorithms_present = true; + HANDLE_CODE(selected_eps_nas_security_algorithms.unpack(bref)); + break; + case ie_iei_additional_5g_security_information: + additional_5g_security_information_present = true; + HANDLE_CODE(additional_5g_security_information.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_abba: + abba_present = true; + HANDLE_CODE(abba.unpack(bref)); + break; + case ie_iei_replayed_s1_ue_security_capabilities: + replayed_s1_ue_security_capabilities_present = true; + HANDLE_CODE(replayed_s1_ue_security_capabilities.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE security_mode_complete_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (imeisv_present == true) { + HANDLE_CODE(bref.pack(ie_iei_imeisv, 8)); + HANDLE_CODE(imeisv.pack(bref)); + } + if (nas_message_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_nas_message_container, 8)); + HANDLE_CODE(nas_message_container.pack(bref)); + } + if (non_imeisv_pei_present == true) { + HANDLE_CODE(bref.pack(ie_iei_non_imeisv_pei, 8)); + HANDLE_CODE(non_imeisv_pei.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE security_mode_complete_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_imeisv: + imeisv_present = true; + HANDLE_CODE(imeisv.unpack(bref)); + break; + case ie_iei_nas_message_container: + nas_message_container_present = true; + HANDLE_CODE(nas_message_container.unpack(bref)); + break; + case ie_iei_non_imeisv_pei: + non_imeisv_pei_present = true; + HANDLE_CODE(non_imeisv_pei.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE security_mode_reject_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.pack(bref)); + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE security_mode_reject_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE status_5gmm_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.pack(bref)); + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE status_5gmm_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gmm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE notification_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.pack(bref)); + HANDLE_CODE(access_type.pack(bref)); + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE notification_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.unpack(bref)); + HANDLE_CODE(access_type.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE notification_response_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (pdu_session_status_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_status, 8)); + HANDLE_CODE(pdu_session_status.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE notification_response_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_pdu_session_status: + pdu_session_status_present = true; + HANDLE_CODE(pdu_session_status.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE ul_nas_transport_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.pack(bref)); + HANDLE_CODE(payload_container_type.pack(bref)); + HANDLE_CODE(payload_container.pack(bref)); + + // Optional fields + if (pdu_session_id_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_id, 8)); + HANDLE_CODE(pdu_session_id.pack(bref)); + } + if (old_pdu_session_id_present == true) { + HANDLE_CODE(bref.pack(ie_iei_old_pdu_session_id, 8)); + HANDLE_CODE(old_pdu_session_id.pack(bref)); + } + if (request_type_present == true) { + HANDLE_CODE(bref.pack(ie_iei_request_type, 4)); + HANDLE_CODE(request_type.pack(bref)); + } + if (s_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_s_nssai, 8)); + HANDLE_CODE(s_nssai.pack(bref)); + } + if (dnn_present == true) { + HANDLE_CODE(bref.pack(ie_iei_dnn, 8)); + HANDLE_CODE(dnn.pack(bref)); + } + if (additional_information_present == true) { + HANDLE_CODE(bref.pack(ie_iei_additional_information, 8)); + HANDLE_CODE(additional_information.pack(bref)); + } + if (ma_pdu_session_information_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ma_pdu_session_information, 4)); + HANDLE_CODE(ma_pdu_session_information.pack(bref)); + } + if (release_assistance_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_release_assistance_indication, 4)); + HANDLE_CODE(release_assistance_indication.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE ul_nas_transport_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.unpack(bref)); + HANDLE_CODE(payload_container_type.unpack(bref)); + HANDLE_CODE(payload_container.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_pdu_session_id: + pdu_session_id_present = true; + HANDLE_CODE(pdu_session_id.unpack(bref)); + break; + case ie_iei_old_pdu_session_id: + old_pdu_session_id_present = true; + HANDLE_CODE(old_pdu_session_id.unpack(bref)); + break; + case ie_iei_request_type: + request_type_present = true; + HANDLE_CODE(request_type.unpack(bref)); + break; + case ie_iei_s_nssai: + s_nssai_present = true; + HANDLE_CODE(s_nssai.unpack(bref)); + break; + case ie_iei_dnn: + dnn_present = true; + HANDLE_CODE(dnn.unpack(bref)); + break; + case ie_iei_additional_information: + additional_information_present = true; + HANDLE_CODE(additional_information.unpack(bref)); + break; + case ie_iei_ma_pdu_session_information: + ma_pdu_session_information_present = true; + HANDLE_CODE(ma_pdu_session_information.unpack(bref)); + break; + case ie_iei_release_assistance_indication: + release_assistance_indication_present = true; + HANDLE_CODE(release_assistance_indication.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE dl_nas_transport_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.pack(bref)); + HANDLE_CODE(payload_container_type.pack(bref)); + HANDLE_CODE(payload_container.pack(bref)); + + // Optional fields + if (pdu_session_id_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_id, 8)); + HANDLE_CODE(pdu_session_id.pack(bref)); + } + if (additional_information_present == true) { + HANDLE_CODE(bref.pack(ie_iei_additional_information, 8)); + HANDLE_CODE(additional_information.pack(bref)); + } + if (cause_5gmm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cause_5gmm, 8)); + HANDLE_CODE(cause_5gmm.pack(bref)); + } + if (back_off_timer_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_back_off_timer_value, 8)); + HANDLE_CODE(back_off_timer_value.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE dl_nas_transport_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(spare_half_octet.unpack(bref)); + HANDLE_CODE(payload_container_type.unpack(bref)); + HANDLE_CODE(payload_container.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_pdu_session_id: + pdu_session_id_present = true; + HANDLE_CODE(pdu_session_id.unpack(bref)); + break; + case ie_iei_additional_information: + additional_information_present = true; + HANDLE_CODE(additional_information.unpack(bref)); + break; + case ie_iei_cause_5gmm: + cause_5gmm_present = true; + HANDLE_CODE(cause_5gmm.unpack(bref)); + break; + case ie_iei_back_off_timer_value: + back_off_timer_value_present = true; + HANDLE_CODE(back_off_timer_value.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_establishment_request_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(integrity_protection_maximum_data_rate.pack(bref)); + + // Optional fields + if (pdu_session_type_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_session_type, 4)); + HANDLE_CODE(pdu_session_type.pack(bref)); + } + if (ssc_mode_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ssc_mode, 4)); + HANDLE_CODE(ssc_mode.pack(bref)); + } + if (capability_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_capability_5gsm, 8)); + HANDLE_CODE(capability_5gsm.pack(bref)); + } + if (maximum_number_of_supported_packet_filters_present == true) { + HANDLE_CODE(bref.pack(ie_iei_maximum_number_of_supported_packet_filters, 8)); + HANDLE_CODE(maximum_number_of_supported_packet_filters.pack(bref)); + } + if (always_on_pdu_session_requested_present == true) { + HANDLE_CODE(bref.pack(ie_iei_always_on_pdu_session_requested, 4)); + HANDLE_CODE(always_on_pdu_session_requested.pack(bref)); + } + if (sm_pdu_dn_request_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_sm_pdu_dn_request_container, 8)); + HANDLE_CODE(sm_pdu_dn_request_container.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + if (ip_header_compression_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ip_header_compression_configuration, 8)); + HANDLE_CODE(ip_header_compression_configuration.pack(bref)); + } + if (ds_tt__ethernet_port_mac_address_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ds_tt__ethernet_port_mac_address, 8)); + HANDLE_CODE(ds_tt__ethernet_port_mac_address.pack(bref)); + } + if (ue_ds_tt_residence_time_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ue_ds_tt_residence_time, 8)); + HANDLE_CODE(ue_ds_tt_residence_time.pack(bref)); + } + if (port_management_information_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_port_management_information_container, 8)); + HANDLE_CODE(port_management_information_container.pack(bref)); + } + if (ethernet_header_compression_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ethernet_header_compression_configuration, 8)); + HANDLE_CODE(ethernet_header_compression_configuration.pack(bref)); + } + if (suggested_interface_identifier_present == true) { + HANDLE_CODE(bref.pack(ie_iei_suggested_interface_identifier, 8)); + HANDLE_CODE(suggested_interface_identifier.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_establishment_request_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(integrity_protection_maximum_data_rate.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_pdu_session_type: + pdu_session_type_present = true; + HANDLE_CODE(pdu_session_type.unpack(bref)); + break; + case ie_iei_ssc_mode: + ssc_mode_present = true; + HANDLE_CODE(ssc_mode.unpack(bref)); + break; + case ie_iei_capability_5gsm: + capability_5gsm_present = true; + HANDLE_CODE(capability_5gsm.unpack(bref)); + break; + case ie_iei_maximum_number_of_supported_packet_filters: + maximum_number_of_supported_packet_filters_present = true; + HANDLE_CODE(maximum_number_of_supported_packet_filters.unpack(bref)); + break; + case ie_iei_always_on_pdu_session_requested: + always_on_pdu_session_requested_present = true; + HANDLE_CODE(always_on_pdu_session_requested.unpack(bref)); + break; + case ie_iei_sm_pdu_dn_request_container: + sm_pdu_dn_request_container_present = true; + HANDLE_CODE(sm_pdu_dn_request_container.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + case ie_iei_ip_header_compression_configuration: + ip_header_compression_configuration_present = true; + HANDLE_CODE(ip_header_compression_configuration.unpack(bref)); + break; + case ie_iei_ds_tt__ethernet_port_mac_address: + ds_tt__ethernet_port_mac_address_present = true; + HANDLE_CODE(ds_tt__ethernet_port_mac_address.unpack(bref)); + break; + case ie_iei_ue_ds_tt_residence_time: + ue_ds_tt_residence_time_present = true; + HANDLE_CODE(ue_ds_tt_residence_time.unpack(bref)); + break; + case ie_iei_port_management_information_container: + port_management_information_container_present = true; + HANDLE_CODE(port_management_information_container.unpack(bref)); + break; + case ie_iei_ethernet_header_compression_configuration: + ethernet_header_compression_configuration_present = true; + HANDLE_CODE(ethernet_header_compression_configuration.unpack(bref)); + break; + case ie_iei_suggested_interface_identifier: + suggested_interface_identifier_present = true; + HANDLE_CODE(suggested_interface_identifier.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_establishment_accept_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(selected_ssc_mode.pack(bref)); + HANDLE_CODE(selected_pdu_session_type.pack(bref)); + HANDLE_CODE(authorized__qo_s_rules.pack(bref)); + HANDLE_CODE(session_ambr.pack(bref)); + + // Optional fields + if (cause_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cause_5gsm, 8)); + HANDLE_CODE(cause_5gsm.pack(bref)); + } + if (pdu_address_present == true) { + HANDLE_CODE(bref.pack(ie_iei_pdu_address, 8)); + HANDLE_CODE(pdu_address.pack(bref)); + } + if (rq_timer_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_rq_timer_value, 8)); + HANDLE_CODE(rq_timer_value.pack(bref)); + } + if (s_nssai_present == true) { + HANDLE_CODE(bref.pack(ie_iei_s_nssai, 8)); + HANDLE_CODE(s_nssai.pack(bref)); + } + if (always_on_pdu_session_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_always_on_pdu_session_indication, 4)); + HANDLE_CODE(always_on_pdu_session_indication.pack(bref)); + } + if (mapped_eps_bearer_contexts_present == true) { + HANDLE_CODE(bref.pack(ie_iei_mapped_eps_bearer_contexts, 8)); + HANDLE_CODE(mapped_eps_bearer_contexts.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (authorized__qo_s_flow_descriptions_present == true) { + HANDLE_CODE(bref.pack(ie_iei_authorized__qo_s_flow_descriptions, 8)); + HANDLE_CODE(authorized__qo_s_flow_descriptions.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + if (dnn_present == true) { + HANDLE_CODE(bref.pack(ie_iei_dnn, 8)); + HANDLE_CODE(dnn.pack(bref)); + } + if (network_feature_support_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_network_feature_support_5gsm, 8)); + HANDLE_CODE(network_feature_support_5gsm.pack(bref)); + } + if (serving_plmn_rate_control_present == true) { + HANDLE_CODE(bref.pack(ie_iei_serving_plmn_rate_control, 8)); + HANDLE_CODE(serving_plmn_rate_control.pack(bref)); + } + if (atsss_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_atsss_container, 8)); + HANDLE_CODE(atsss_container.pack(bref)); + } + if (control_plane_only_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_control_plane_only_indication, 4)); + HANDLE_CODE(control_plane_only_indication.pack(bref)); + } + if (ip_header_compression_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ip_header_compression_configuration, 8)); + HANDLE_CODE(ip_header_compression_configuration.pack(bref)); + } + if (ethernet_header_compression_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ethernet_header_compression_configuration, 8)); + HANDLE_CODE(ethernet_header_compression_configuration.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_establishment_accept_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(selected_ssc_mode.unpack(bref)); + HANDLE_CODE(selected_pdu_session_type.unpack(bref)); + HANDLE_CODE(authorized__qo_s_rules.unpack(bref)); + HANDLE_CODE(session_ambr.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_cause_5gsm: + cause_5gsm_present = true; + HANDLE_CODE(cause_5gsm.unpack(bref)); + break; + case ie_iei_pdu_address: + pdu_address_present = true; + HANDLE_CODE(pdu_address.unpack(bref)); + break; + case ie_iei_rq_timer_value: + rq_timer_value_present = true; + HANDLE_CODE(rq_timer_value.unpack(bref)); + break; + case ie_iei_s_nssai: + s_nssai_present = true; + HANDLE_CODE(s_nssai.unpack(bref)); + break; + case ie_iei_always_on_pdu_session_indication: + always_on_pdu_session_indication_present = true; + HANDLE_CODE(always_on_pdu_session_indication.unpack(bref)); + break; + case ie_iei_mapped_eps_bearer_contexts: + mapped_eps_bearer_contexts_present = true; + HANDLE_CODE(mapped_eps_bearer_contexts.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_authorized__qo_s_flow_descriptions: + authorized__qo_s_flow_descriptions_present = true; + HANDLE_CODE(authorized__qo_s_flow_descriptions.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + case ie_iei_dnn: + dnn_present = true; + HANDLE_CODE(dnn.unpack(bref)); + break; + case ie_iei_network_feature_support_5gsm: + network_feature_support_5gsm_present = true; + HANDLE_CODE(network_feature_support_5gsm.unpack(bref)); + break; + case ie_iei_serving_plmn_rate_control: + serving_plmn_rate_control_present = true; + HANDLE_CODE(serving_plmn_rate_control.unpack(bref)); + break; + case ie_iei_atsss_container: + atsss_container_present = true; + HANDLE_CODE(atsss_container.unpack(bref)); + break; + case ie_iei_control_plane_only_indication: + control_plane_only_indication_present = true; + HANDLE_CODE(control_plane_only_indication.unpack(bref)); + break; + case ie_iei_ip_header_compression_configuration: + ip_header_compression_configuration_present = true; + HANDLE_CODE(ip_header_compression_configuration.unpack(bref)); + break; + case ie_iei_ethernet_header_compression_configuration: + ethernet_header_compression_configuration_present = true; + HANDLE_CODE(ethernet_header_compression_configuration.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_establishment_reject_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.pack(bref)); + + // Optional fields + if (back_off_timer_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_back_off_timer_value, 8)); + HANDLE_CODE(back_off_timer_value.pack(bref)); + } + if (allowed_ssc_mode_present == true) { + HANDLE_CODE(bref.pack(ie_iei_allowed_ssc_mode, 4)); + HANDLE_CODE(allowed_ssc_mode.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (congestion_re_attempt_indicator_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_congestion_re_attempt_indicator_5gsm, 8)); + HANDLE_CODE(congestion_re_attempt_indicator_5gsm.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + if (re_attempt_indicator_present == true) { + HANDLE_CODE(bref.pack(ie_iei_re_attempt_indicator, 8)); + HANDLE_CODE(re_attempt_indicator.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_establishment_reject_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_back_off_timer_value: + back_off_timer_value_present = true; + HANDLE_CODE(back_off_timer_value.unpack(bref)); + break; + case ie_iei_allowed_ssc_mode: + allowed_ssc_mode_present = true; + HANDLE_CODE(allowed_ssc_mode.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_congestion_re_attempt_indicator_5gsm: + congestion_re_attempt_indicator_5gsm_present = true; + HANDLE_CODE(congestion_re_attempt_indicator_5gsm.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + case ie_iei_re_attempt_indicator: + re_attempt_indicator_present = true; + HANDLE_CODE(re_attempt_indicator.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_authentication_command_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(eap_message.pack(bref)); + + // Optional fields + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_authentication_command_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(eap_message.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_authentication_complete_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(eap_message.pack(bref)); + + // Optional fields + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_authentication_complete_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(eap_message.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_authentication_result_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_authentication_result_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_modification_request_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (capability_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_capability_5gsm, 8)); + HANDLE_CODE(capability_5gsm.pack(bref)); + } + if (cause_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cause_5gsm, 8)); + HANDLE_CODE(cause_5gsm.pack(bref)); + } + if (maximum_number_of_supported_packet_filters_present == true) { + HANDLE_CODE(bref.pack(ie_iei_maximum_number_of_supported_packet_filters, 8)); + HANDLE_CODE(maximum_number_of_supported_packet_filters.pack(bref)); + } + if (always_on_pdu_session_requested_present == true) { + HANDLE_CODE(bref.pack(ie_iei_always_on_pdu_session_requested, 4)); + HANDLE_CODE(always_on_pdu_session_requested.pack(bref)); + } + if (integrity_protection_maximum_data_rate_present == true) { + HANDLE_CODE(bref.pack(ie_iei_integrity_protection_maximum_data_rate, 8)); + HANDLE_CODE(integrity_protection_maximum_data_rate.pack(bref)); + } + if (requested__qo_s_rules_present == true) { + HANDLE_CODE(bref.pack(ie_iei_requested__qo_s_rules, 8)); + HANDLE_CODE(requested__qo_s_rules.pack(bref)); + } + if (requested__qo_s_flow_descriptions_present == true) { + HANDLE_CODE(bref.pack(ie_iei_requested__qo_s_flow_descriptions, 8)); + HANDLE_CODE(requested__qo_s_flow_descriptions.pack(bref)); + } + if (mapped_eps_bearer_contexts_present == true) { + HANDLE_CODE(bref.pack(ie_iei_mapped_eps_bearer_contexts, 8)); + HANDLE_CODE(mapped_eps_bearer_contexts.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + if (port_management_information_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_port_management_information_container, 8)); + HANDLE_CODE(port_management_information_container.pack(bref)); + } + if (ip_header_compression_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ip_header_compression_configuration, 8)); + HANDLE_CODE(ip_header_compression_configuration.pack(bref)); + } + if (ethernet_header_compression_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ethernet_header_compression_configuration, 8)); + HANDLE_CODE(ethernet_header_compression_configuration.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_modification_request_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_capability_5gsm: + capability_5gsm_present = true; + HANDLE_CODE(capability_5gsm.unpack(bref)); + break; + case ie_iei_cause_5gsm: + cause_5gsm_present = true; + HANDLE_CODE(cause_5gsm.unpack(bref)); + break; + case ie_iei_maximum_number_of_supported_packet_filters: + maximum_number_of_supported_packet_filters_present = true; + HANDLE_CODE(maximum_number_of_supported_packet_filters.unpack(bref)); + break; + case ie_iei_always_on_pdu_session_requested: + always_on_pdu_session_requested_present = true; + HANDLE_CODE(always_on_pdu_session_requested.unpack(bref)); + break; + case ie_iei_integrity_protection_maximum_data_rate: + integrity_protection_maximum_data_rate_present = true; + HANDLE_CODE(integrity_protection_maximum_data_rate.unpack(bref)); + break; + case ie_iei_requested__qo_s_rules: + requested__qo_s_rules_present = true; + HANDLE_CODE(requested__qo_s_rules.unpack(bref)); + break; + case ie_iei_requested__qo_s_flow_descriptions: + requested__qo_s_flow_descriptions_present = true; + HANDLE_CODE(requested__qo_s_flow_descriptions.unpack(bref)); + break; + case ie_iei_mapped_eps_bearer_contexts: + mapped_eps_bearer_contexts_present = true; + HANDLE_CODE(mapped_eps_bearer_contexts.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + case ie_iei_port_management_information_container: + port_management_information_container_present = true; + HANDLE_CODE(port_management_information_container.unpack(bref)); + break; + case ie_iei_ip_header_compression_configuration: + ip_header_compression_configuration_present = true; + HANDLE_CODE(ip_header_compression_configuration.unpack(bref)); + break; + case ie_iei_ethernet_header_compression_configuration: + ethernet_header_compression_configuration_present = true; + HANDLE_CODE(ethernet_header_compression_configuration.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_modification_reject_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.pack(bref)); + + // Optional fields + if (back_off_timer_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_back_off_timer_value, 8)); + HANDLE_CODE(back_off_timer_value.pack(bref)); + } + if (congestion_re_attempt_indicator_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_congestion_re_attempt_indicator_5gsm, 8)); + HANDLE_CODE(congestion_re_attempt_indicator_5gsm.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + if (re_attempt_indicator_present == true) { + HANDLE_CODE(bref.pack(ie_iei_re_attempt_indicator, 8)); + HANDLE_CODE(re_attempt_indicator.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_modification_reject_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_back_off_timer_value: + back_off_timer_value_present = true; + HANDLE_CODE(back_off_timer_value.unpack(bref)); + break; + case ie_iei_congestion_re_attempt_indicator_5gsm: + congestion_re_attempt_indicator_5gsm_present = true; + HANDLE_CODE(congestion_re_attempt_indicator_5gsm.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + case ie_iei_re_attempt_indicator: + re_attempt_indicator_present = true; + HANDLE_CODE(re_attempt_indicator.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_modification_command_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (cause_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cause_5gsm, 8)); + HANDLE_CODE(cause_5gsm.pack(bref)); + } + if (session_ambr_present == true) { + HANDLE_CODE(bref.pack(ie_iei_session_ambr, 8)); + HANDLE_CODE(session_ambr.pack(bref)); + } + if (rq_timer_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_rq_timer_value, 8)); + HANDLE_CODE(rq_timer_value.pack(bref)); + } + if (always_on_pdu_session_indication_present == true) { + HANDLE_CODE(bref.pack(ie_iei_always_on_pdu_session_indication, 4)); + HANDLE_CODE(always_on_pdu_session_indication.pack(bref)); + } + if (authorized__qo_s_rules_present == true) { + HANDLE_CODE(bref.pack(ie_iei_authorized__qo_s_rules, 8)); + HANDLE_CODE(authorized__qo_s_rules.pack(bref)); + } + if (mapped_eps_bearer_contexts_present == true) { + HANDLE_CODE(bref.pack(ie_iei_mapped_eps_bearer_contexts, 8)); + HANDLE_CODE(mapped_eps_bearer_contexts.pack(bref)); + } + if (authorized__qo_s_flow_descriptions_present == true) { + HANDLE_CODE(bref.pack(ie_iei_authorized__qo_s_flow_descriptions, 8)); + HANDLE_CODE(authorized__qo_s_flow_descriptions.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + if (atsss_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_atsss_container, 8)); + HANDLE_CODE(atsss_container.pack(bref)); + } + if (ip_header_compression_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ip_header_compression_configuration, 8)); + HANDLE_CODE(ip_header_compression_configuration.pack(bref)); + } + if (port_management_information_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_port_management_information_container, 8)); + HANDLE_CODE(port_management_information_container.pack(bref)); + } + if (serving_plmn_rate_control_present == true) { + HANDLE_CODE(bref.pack(ie_iei_serving_plmn_rate_control, 8)); + HANDLE_CODE(serving_plmn_rate_control.pack(bref)); + } + if (ethernet_header_compression_configuration_present == true) { + HANDLE_CODE(bref.pack(ie_iei_ethernet_header_compression_configuration, 8)); + HANDLE_CODE(ethernet_header_compression_configuration.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_modification_command_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_cause_5gsm: + cause_5gsm_present = true; + HANDLE_CODE(cause_5gsm.unpack(bref)); + break; + case ie_iei_session_ambr: + session_ambr_present = true; + HANDLE_CODE(session_ambr.unpack(bref)); + break; + case ie_iei_rq_timer_value: + rq_timer_value_present = true; + HANDLE_CODE(rq_timer_value.unpack(bref)); + break; + case ie_iei_always_on_pdu_session_indication: + always_on_pdu_session_indication_present = true; + HANDLE_CODE(always_on_pdu_session_indication.unpack(bref)); + break; + case ie_iei_authorized__qo_s_rules: + authorized__qo_s_rules_present = true; + HANDLE_CODE(authorized__qo_s_rules.unpack(bref)); + break; + case ie_iei_mapped_eps_bearer_contexts: + mapped_eps_bearer_contexts_present = true; + HANDLE_CODE(mapped_eps_bearer_contexts.unpack(bref)); + break; + case ie_iei_authorized__qo_s_flow_descriptions: + authorized__qo_s_flow_descriptions_present = true; + HANDLE_CODE(authorized__qo_s_flow_descriptions.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + case ie_iei_atsss_container: + atsss_container_present = true; + HANDLE_CODE(atsss_container.unpack(bref)); + break; + case ie_iei_ip_header_compression_configuration: + ip_header_compression_configuration_present = true; + HANDLE_CODE(ip_header_compression_configuration.unpack(bref)); + break; + case ie_iei_port_management_information_container: + port_management_information_container_present = true; + HANDLE_CODE(port_management_information_container.unpack(bref)); + break; + case ie_iei_serving_plmn_rate_control: + serving_plmn_rate_control_present = true; + HANDLE_CODE(serving_plmn_rate_control.unpack(bref)); + break; + case ie_iei_ethernet_header_compression_configuration: + ethernet_header_compression_configuration_present = true; + HANDLE_CODE(ethernet_header_compression_configuration.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_modification_complete_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + if (port_management_information_container_present == true) { + HANDLE_CODE(bref.pack(ie_iei_port_management_information_container, 8)); + HANDLE_CODE(port_management_information_container.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_modification_complete_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + case ie_iei_port_management_information_container: + port_management_information_container_present = true; + HANDLE_CODE(port_management_information_container.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_modification_command_reject_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.pack(bref)); + + // Optional fields + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_modification_command_reject_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_release_request_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (cause_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cause_5gsm, 8)); + HANDLE_CODE(cause_5gsm.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_release_request_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_cause_5gsm: + cause_5gsm_present = true; + HANDLE_CODE(cause_5gsm.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_release_reject_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.pack(bref)); + + // Optional fields + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_release_reject_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_release_command_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.pack(bref)); + + // Optional fields + if (back_off_timer_value_present == true) { + HANDLE_CODE(bref.pack(ie_iei_back_off_timer_value, 8)); + HANDLE_CODE(back_off_timer_value.pack(bref)); + } + if (eap_message_present == true) { + HANDLE_CODE(bref.pack(ie_iei_eap_message, 8)); + HANDLE_CODE(eap_message.pack(bref)); + } + if (congestion_re_attempt_indicator_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_congestion_re_attempt_indicator_5gsm, 8)); + HANDLE_CODE(congestion_re_attempt_indicator_5gsm.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + if (access_type_present == true) { + HANDLE_CODE(bref.pack(ie_iei_access_type, 4)); + HANDLE_CODE(access_type.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_release_command_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_back_off_timer_value: + back_off_timer_value_present = true; + HANDLE_CODE(back_off_timer_value.unpack(bref)); + break; + case ie_iei_eap_message: + eap_message_present = true; + HANDLE_CODE(eap_message.unpack(bref)); + break; + case ie_iei_congestion_re_attempt_indicator_5gsm: + congestion_re_attempt_indicator_5gsm_present = true; + HANDLE_CODE(congestion_re_attempt_indicator_5gsm.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + case ie_iei_access_type: + access_type_present = true; + HANDLE_CODE(access_type.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE pdu_session_release_complete_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + + // Optional fields + if (cause_5gsm_present == true) { + HANDLE_CODE(bref.pack(ie_iei_cause_5gsm, 8)); + HANDLE_CODE(cause_5gsm.pack(bref)); + } + if (extended_protocol_configuration_options_present == true) { + HANDLE_CODE(bref.pack(ie_iei_extended_protocol_configuration_options, 8)); + HANDLE_CODE(extended_protocol_configuration_options.pack(bref)); + } + + return SRSASN_SUCCESS; +} +SRSASN_CODE pdu_session_release_complete_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + case ie_iei_cause_5gsm: + cause_5gsm_present = true; + HANDLE_CODE(cause_5gsm.unpack(bref)); + break; + case ie_iei_extended_protocol_configuration_options: + extended_protocol_configuration_options_present = true; + HANDLE_CODE(extended_protocol_configuration_options.unpack(bref)); + break; + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +SRSASN_CODE status_5gsm_t::pack(asn1::bit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.pack(bref)); + + // Optional fields + + return SRSASN_SUCCESS; +} +SRSASN_CODE status_5gsm_t::unpack(asn1::cbit_ref& bref) +{ + // Mandatory fields + HANDLE_CODE(cause_5gsm.unpack(bref)); + + // Optional fields + + while (bref.distance_bytes_end() > 0) { + // some iei are only 1/2 byte long which are > 8 + // otherwise a complete byte + uint8_t iei; + HANDLE_CODE(bref.unpack(iei, 4)); + if (iei < 8) { + uint8_t iei_tmp; + HANDLE_CODE(bref.unpack(iei_tmp, 4)); + iei = iei << 4 | iei_tmp; + } + + switch (iei) { + default: + asn1::log_error("Invalid IE %x", iei); + break; + } + } + + return SRSASN_SUCCESS; +} + +// Include from nas5g/infiles/nas_5g_msg.cc.in + +SRSASN_CODE nas_5gs_hdr::unpack_outer(asn1::cbit_ref& bref) +{ + unpack_enum(bref, &extended_protocol_discriminator); + // Security header type associated with a spare half octet; or PDU session identity + switch (extended_protocol_discriminator) { + case extended_protocol_discriminator_5gmm: + HANDLE_CODE(bref.advance_bits(4)); // spare + unpack_enum(bref, &security_header_type); + if (security_header_type == plain_5gs_nas_message) { + HANDLE_CODE(message_type.unpack(bref)); + } else { + HANDLE_CODE(bref.unpack(message_authentication_code, 32)); + HANDLE_CODE(bref.unpack(sequence_number, 8)); + } + break; + case extended_protocol_discriminator_5gsm: + // The PDU session identity and the procedure transaction identity are only used in messages with extended + // protocol discriminator 5GS session management. Octet 2a with the procedure transaction identity shall only be + // included in these messages. + HANDLE_CODE(bref.unpack(pdu_session_identity, 8)); + HANDLE_CODE(bref.unpack(procedure_transaction_identity, 8)); + HANDLE_CODE(message_type.unpack(bref)); + break; + default: + asn1::log_error("Unsupported extended protocol discriminator %x\n", extended_protocol_discriminator); + return SRSASN_ERROR_DECODE_FAIL; + } + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_hdr::unpack(asn1::cbit_ref& bref) +{ + unpack_outer(bref); + if (security_header_type != plain_5gs_nas_message) { + unpack_enum(bref, &inner_extended_protocol_discriminator); + // Security header type associated with a spare half octet; or PDU session identity + switch (inner_extended_protocol_discriminator) { + case extended_protocol_discriminator_5gmm: + HANDLE_CODE(bref.advance_bits(4)); // spare + unpack_enum(bref, &inner_security_header_type); + if (inner_security_header_type == plain_5gs_nas_message) { + HANDLE_CODE(message_type.unpack(bref)); + } else { + asn1::log_error("Expected inner security type to be plain\n"); + return SRSASN_ERROR_DECODE_FAIL; + } + break; + case extended_protocol_discriminator_5gsm: + // The PDU session identity and the procedure transaction identity are only used in messages with extended + // protocol discriminator 5GS session management. Octet 2a with the procedure transaction identity shall only be + // included in these messages. + HANDLE_CODE(bref.unpack(pdu_session_identity, 8)); + HANDLE_CODE(bref.unpack(procedure_transaction_identity, 8)); + HANDLE_CODE(message_type.unpack(bref)); + break; + default: + asn1::log_error("Unsupported extended protocol discriminator %x\n", inner_extended_protocol_discriminator); + return SRSASN_ERROR_DECODE_FAIL; + } + } + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_hdr::pack_outer(asn1::bit_ref& bref) +{ + pack_enum(bref, extended_protocol_discriminator); + // Security header type associated with a spare half octet; or PDU session identity + switch (extended_protocol_discriminator) { + case extended_protocol_discriminator_5gmm: + HANDLE_CODE(bref.pack(0x0, 4)); // spare + pack_enum(bref, security_header_type); + if (security_header_type == plain_5gs_nas_message) { + HANDLE_CODE(message_type.pack(bref)); + } else { + HANDLE_CODE(bref.pack(message_authentication_code, 32)); + HANDLE_CODE(bref.pack(sequence_number, 8)); + } + break; + case extended_protocol_discriminator_5gsm: + // The PDU session identity and the procedure transaction identity are only used in messages with extended + // protocol discriminator 5GS session management. Octet 2a with the procedure transaction identity shall only be + // included in these messages. + HANDLE_CODE(bref.pack(pdu_session_identity, 8)); + HANDLE_CODE(bref.pack(procedure_transaction_identity, 8)); + HANDLE_CODE(message_type.pack(bref)); + break; + default: + asn1::log_error("Unsupported extended protocol discriminator %x\n", extended_protocol_discriminator); + return SRSASN_ERROR_DECODE_FAIL; + } + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_hdr::pack(asn1::bit_ref& bref) +{ + pack_outer(bref); + if (security_header_type != plain_5gs_nas_message) { + pack_enum(bref, inner_extended_protocol_discriminator); + if (inner_extended_protocol_discriminator == extended_protocol_discriminator_5gsm) { + HANDLE_CODE(bref.pack(pdu_session_identity, 4)); + } else { + HANDLE_CODE(bref.pack(0x0, 4)); + } + pack_enum(bref, inner_security_header_type); + if (inner_extended_protocol_discriminator == extended_protocol_discriminator_5gsm) { + HANDLE_CODE(bref.pack(procedure_transaction_identity, 8)); + } + HANDLE_CODE(message_type.pack(bref)); + } + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_msg::pack(unique_byte_buffer_t& buf) +{ + asn1::bit_ref msg_bref(buf->msg, buf->get_tailroom()); + HANDLE_CODE(pack(msg_bref)); + buf->N_bytes = msg_bref.distance_bytes(); + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_msg::pack(std::vector& buf) +{ + buf.resize(SRSRAN_MAX_BUFFER_SIZE_BYTES); + asn1::bit_ref msg_bref(buf.data(), buf.size()); + HANDLE_CODE(pack(msg_bref)); + buf.resize(msg_bref.distance_bytes()); + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_msg::pack(asn1::bit_ref& msg_bref) +{ + HANDLE_CODE(hdr.pack(msg_bref)); + switch (hdr.message_type) { + case msg_types::options::registration_request: { + registration_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::registration_accept: { + registration_accept_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::registration_complete: { + registration_complete_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::registration_reject: { + registration_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::deregistration_request_ue_originating: { + deregistration_request_ue_originating_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::deregistration_accept_ue_originating: { + deregistration_accept_ue_originating_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::deregistration_request_ue_terminated: { + deregistration_request_ue_terminated_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::deregistration_accept_ue_terminated: { + deregistration_accept_ue_terminated_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::service_request: { + service_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::service_reject: { + service_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::service_accept: { + service_accept_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::configuration_update_command: { + configuration_update_command_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::configuration_update_complete: { + configuration_update_complete_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::authentication_request: { + authentication_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::authentication_response: { + authentication_response_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::authentication_reject: { + authentication_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::authentication_failure: { + authentication_failure_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::authentication_result: { + authentication_result_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::identity_request: { + identity_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::identity_response: { + identity_response_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::security_mode_command: { + security_mode_command_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::security_mode_complete: { + security_mode_complete_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::security_mode_reject: { + security_mode_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::status_5gmm: { + status_5gmm_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::notification: { + notification_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::notification_response: { + notification_response_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::ul_nas_transport: { + ul_nas_transport_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::dl_nas_transport: { + dl_nas_transport_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_establishment_request: { + pdu_session_establishment_request_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_establishment_accept: { + pdu_session_establishment_accept_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_establishment_reject: { + pdu_session_establishment_reject_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_authentication_command: { + pdu_session_authentication_command_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_authentication_complete: { + pdu_session_authentication_complete_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_authentication_result: { + pdu_session_authentication_result_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_request: { + pdu_session_modification_request_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_reject: { + pdu_session_modification_reject_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_command: { + pdu_session_modification_command_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_complete: { + pdu_session_modification_complete_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_command_reject: { + pdu_session_modification_command_reject_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_release_request: { + pdu_session_release_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_release_reject: { + pdu_session_release_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_release_command: { + pdu_session_release_command_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::pdu_session_release_complete: { + pdu_session_release_complete_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + case msg_types::options::status_5gsm: { + status_5gsm_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->pack(msg_bref)); + break; + } + + default: + break; + } + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_msg::unpack_outer_hdr(const unique_byte_buffer_t& buf) +{ + asn1::cbit_ref msg_bref(buf->msg, buf->N_bytes); + HANDLE_CODE(hdr.unpack_outer(msg_bref)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_msg::unpack_outer_hdr(const std::vector& buf) +{ + asn1::cbit_ref msg_bref(buf.data(), buf.size()); + HANDLE_CODE(hdr.unpack_outer(msg_bref)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_msg::unpack(const unique_byte_buffer_t& buf) +{ + asn1::cbit_ref msg_bref(buf->msg, buf->N_bytes); + HANDLE_CODE(unpack(msg_bref)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_msg::unpack(const std::vector& buf) +{ + asn1::cbit_ref msg_bref(buf.data(), buf.size()); + HANDLE_CODE(unpack(msg_bref)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE nas_5gs_msg::unpack(asn1::cbit_ref& msg_bref) +{ + HANDLE_CODE(hdr.unpack(msg_bref)); + switch (hdr.message_type) { + case msg_types::options::registration_request: { + msg_container = srslog::detail::any{registration_request_t()}; + registration_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::registration_accept: { + msg_container = srslog::detail::any{registration_accept_t()}; + registration_accept_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::registration_complete: { + msg_container = srslog::detail::any{registration_complete_t()}; + registration_complete_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::registration_reject: { + msg_container = srslog::detail::any{registration_reject_t()}; + registration_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::deregistration_request_ue_originating: { + msg_container = srslog::detail::any{deregistration_request_ue_originating_t()}; + deregistration_request_ue_originating_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::deregistration_accept_ue_originating: { + msg_container = srslog::detail::any{deregistration_accept_ue_originating_t()}; + deregistration_accept_ue_originating_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::deregistration_request_ue_terminated: { + msg_container = srslog::detail::any{deregistration_request_ue_terminated_t()}; + deregistration_request_ue_terminated_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::deregistration_accept_ue_terminated: { + msg_container = srslog::detail::any{deregistration_accept_ue_terminated_t()}; + deregistration_accept_ue_terminated_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::service_request: { + msg_container = srslog::detail::any{service_request_t()}; + service_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::service_reject: { + msg_container = srslog::detail::any{service_reject_t()}; + service_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::service_accept: { + msg_container = srslog::detail::any{service_accept_t()}; + service_accept_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::configuration_update_command: { + msg_container = srslog::detail::any{configuration_update_command_t()}; + configuration_update_command_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::configuration_update_complete: { + msg_container = srslog::detail::any{configuration_update_complete_t()}; + configuration_update_complete_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::authentication_request: { + msg_container = srslog::detail::any{authentication_request_t()}; + authentication_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::authentication_response: { + msg_container = srslog::detail::any{authentication_response_t()}; + authentication_response_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::authentication_reject: { + msg_container = srslog::detail::any{authentication_reject_t()}; + authentication_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::authentication_failure: { + msg_container = srslog::detail::any{authentication_failure_t()}; + authentication_failure_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::authentication_result: { + msg_container = srslog::detail::any{authentication_result_t()}; + authentication_result_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::identity_request: { + msg_container = srslog::detail::any{identity_request_t()}; + identity_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::identity_response: { + msg_container = srslog::detail::any{identity_response_t()}; + identity_response_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::security_mode_command: { + msg_container = srslog::detail::any{security_mode_command_t()}; + security_mode_command_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::security_mode_complete: { + msg_container = srslog::detail::any{security_mode_complete_t()}; + security_mode_complete_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::security_mode_reject: { + msg_container = srslog::detail::any{security_mode_reject_t()}; + security_mode_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::status_5gmm: { + msg_container = srslog::detail::any{status_5gmm_t()}; + status_5gmm_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::notification: { + msg_container = srslog::detail::any{notification_t()}; + notification_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::notification_response: { + msg_container = srslog::detail::any{notification_response_t()}; + notification_response_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::ul_nas_transport: { + msg_container = srslog::detail::any{ul_nas_transport_t()}; + ul_nas_transport_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::dl_nas_transport: { + msg_container = srslog::detail::any{dl_nas_transport_t()}; + dl_nas_transport_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_establishment_request: { + msg_container = srslog::detail::any{pdu_session_establishment_request_t()}; + pdu_session_establishment_request_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_establishment_accept: { + msg_container = srslog::detail::any{pdu_session_establishment_accept_t()}; + pdu_session_establishment_accept_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_establishment_reject: { + msg_container = srslog::detail::any{pdu_session_establishment_reject_t()}; + pdu_session_establishment_reject_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_authentication_command: { + msg_container = srslog::detail::any{pdu_session_authentication_command_t()}; + pdu_session_authentication_command_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_authentication_complete: { + msg_container = srslog::detail::any{pdu_session_authentication_complete_t()}; + pdu_session_authentication_complete_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_authentication_result: { + msg_container = srslog::detail::any{pdu_session_authentication_result_t()}; + pdu_session_authentication_result_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_request: { + msg_container = srslog::detail::any{pdu_session_modification_request_t()}; + pdu_session_modification_request_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_reject: { + msg_container = srslog::detail::any{pdu_session_modification_reject_t()}; + pdu_session_modification_reject_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_command: { + msg_container = srslog::detail::any{pdu_session_modification_command_t()}; + pdu_session_modification_command_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_complete: { + msg_container = srslog::detail::any{pdu_session_modification_complete_t()}; + pdu_session_modification_complete_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_modification_command_reject: { + msg_container = srslog::detail::any{pdu_session_modification_command_reject_t()}; + pdu_session_modification_command_reject_t* msg = + srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_release_request: { + msg_container = srslog::detail::any{pdu_session_release_request_t()}; + pdu_session_release_request_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_release_reject: { + msg_container = srslog::detail::any{pdu_session_release_reject_t()}; + pdu_session_release_reject_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_release_command: { + msg_container = srslog::detail::any{pdu_session_release_command_t()}; + pdu_session_release_command_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::pdu_session_release_complete: { + msg_container = srslog::detail::any{pdu_session_release_complete_t()}; + pdu_session_release_complete_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + case msg_types::options::status_5gsm: { + msg_container = srslog::detail::any{status_5gsm_t()}; + status_5gsm_t* msg = srslog::detail::any_cast(&msg_container); + HANDLE_CODE(msg->unpack(msg_bref)); + break; + } + + default: + break; + } + return SRSASN_SUCCESS; +} + +} // namespace nas_5g +} // namespace srsran diff --git a/lib/src/asn1/nas_5g_utils.cc b/lib/src/asn1/nas_5g_utils.cc new file mode 100644 index 000000000..28f615eb0 --- /dev/null +++ b/lib/src/asn1/nas_5g_utils.cc @@ -0,0 +1,65 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/asn1/nas_5g_utils.h" + +#include "srsran/asn1/asn1_utils.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/config.h" + +#include +#include +#include + +namespace srsran { +namespace nas_5g { + +SRSASN_CODE unpack_mcc_mnc(uint8_t* mcc_bytes, uint8_t* mnc_bytes, asn1::cbit_ref& bref) +{ + // MCC digit 2 | MCC digit 1 | octet 5 + // MNC digit 3 | MCC digit 3 | octet 6 + // MNC digit 2 | MNC digit 1 | octet 7 + HANDLE_CODE(bref.unpack(mcc_bytes[1], 4)); + HANDLE_CODE(bref.unpack(mcc_bytes[0], 4)); + HANDLE_CODE(bref.unpack(mnc_bytes[2], 4)); + HANDLE_CODE(bref.unpack(mcc_bytes[2], 4)); + HANDLE_CODE(bref.unpack(mnc_bytes[1], 4)); + HANDLE_CODE(bref.unpack(mnc_bytes[0], 4)); + return SRSASN_SUCCESS; +} + +SRSASN_CODE pack_mcc_mnc(uint8_t* mcc_bytes, uint8_t* mnc_bytes, asn1::bit_ref& bref) +{ + // MCC digit 2 | MCC digit 1 | octet 5 + // MNC digit 3 | MCC digit 3 | octet 6 + // MNC digit 2 | MNC digit 1 | octet 7 + HANDLE_CODE(bref.pack(mcc_bytes[1], 4)); + HANDLE_CODE(bref.pack(mcc_bytes[0], 4)); + HANDLE_CODE(bref.pack(mnc_bytes[2], 4)); + HANDLE_CODE(bref.pack(mcc_bytes[2], 4)); + HANDLE_CODE(bref.pack(mnc_bytes[1], 4)); + HANDLE_CODE(bref.pack(mnc_bytes[0], 4)); + return SRSASN_SUCCESS; +} + +} // namespace nas_5g +} // namespace srsran \ No newline at end of file diff --git a/lib/src/asn1/ngap.cc b/lib/src/asn1/ngap.cc index fe55a2580..7e6973dda 100644 --- a/lib/src/asn1/ngap.cc +++ b/lib/src/asn1/ngap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,241 +23,12 @@ #include using namespace asn1; -using namespace asn1::ngap_nr; +using namespace asn1::ngap; /******************************************************************************* * Struct Methods ******************************************************************************/ -// Criticality ::= ENUMERATED -const char* crit_opts::to_string() const -{ - static const char* options[] = {"reject", "ignore", "notify"}; - return convert_enum_idx(options, 3, value, "crit_e"); -} - -// Presence ::= ENUMERATED -const char* presence_opts::to_string() const -{ - static const char* options[] = {"optional", "conditional", "mandatory"}; - return convert_enum_idx(options, 3, value, "presence_e"); -} - -// ProtocolIE-Field{NGAP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{NGAP-PROTOCOL-IES}} -template -SRSASN_CODE protocol_ie_field_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - warn_assert(crit != ies_set_paramT_::get_crit(id), __func__, __LINE__); - HANDLE_CODE(crit.pack(bref)); - HANDLE_CODE(value.pack(bref)); - - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ie_field_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - value = ies_set_paramT_::get_value(id); - HANDLE_CODE(value.unpack(bref)); - - return SRSASN_SUCCESS; -} -template -void protocol_ie_field_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} -template -bool protocol_ie_field_s::load_info_obj(const uint32_t& id_) -{ - if (not ies_set_paramT_::is_id_valid(id_)) { - return false; - } - id = id_; - crit = ies_set_paramT_::get_crit(id); - value = ies_set_paramT_::get_value(id); - return value.type().value != ies_set_paramT_::value_c::types_opts::nulltype; -} - -uint32_t ngap_protocol_ies_empty_o::idx_to_id(uint32_t idx) -{ - asn1::log_error("object set is empty\n"); - return 0; -} -bool ngap_protocol_ies_empty_o::is_id_valid(const uint32_t& id) -{ - asn1::log_error("object set is empty\n"); - return false; -} -crit_e ngap_protocol_ies_empty_o::get_crit(const uint32_t& id) -{ - return {}; -} -ngap_protocol_ies_empty_o::value_c ngap_protocol_ies_empty_o::get_value(const uint32_t& id) -{ - return {}; -} -presence_e ngap_protocol_ies_empty_o::get_presence(const uint32_t& id) -{ - return {}; -} - -// Value ::= OPEN TYPE -void ngap_protocol_ies_empty_o::value_c::to_json(json_writer& j) const -{ - j.start_obj(); - j.end_obj(); -} -SRSASN_CODE ngap_protocol_ies_empty_o::value_c::pack(bit_ref& bref) const -{ - varlength_field_pack_guard varlen_scope(bref, true); - return SRSASN_SUCCESS; -} -SRSASN_CODE ngap_protocol_ies_empty_o::value_c::unpack(cbit_ref& bref) -{ - varlength_field_unpack_guard varlen_scope(bref, true); - return SRSASN_SUCCESS; -} - -const char* ngap_protocol_ies_empty_o::value_c::types_opts::to_string() const -{ - static const char* options[] = {}; - return convert_enum_idx(options, 0, value, "ngap_protocol_ies_empty_o::value_c::types"); -} - -// ProtocolExtensionField{NGAP-PROTOCOL-EXTENSION : ExtensionSetParam} ::= SEQUENCE{{NGAP-PROTOCOL-EXTENSION}} -template -SRSASN_CODE protocol_ext_field_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - warn_assert(crit != ext_set_paramT_::get_crit(id), __func__, __LINE__); - HANDLE_CODE(crit.pack(bref)); - HANDLE_CODE(ext_value.pack(bref)); - - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ext_field_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - ext_value = ext_set_paramT_::get_ext(id); - HANDLE_CODE(ext_value.unpack(bref)); - - return SRSASN_SUCCESS; -} -template -void protocol_ext_field_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} -template -bool protocol_ext_field_s::load_info_obj(const uint32_t& id_) -{ - if (not ext_set_paramT_::is_id_valid(id_)) { - return false; - } - id = id_; - crit = ext_set_paramT_::get_crit(id); - ext_value = ext_set_paramT_::get_ext(id); - return ext_value.type().value != ext_set_paramT_::ext_c::types_opts::nulltype; -} - -// ProtocolIE-SingleContainer{NGAP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{NGAP-PROTOCOL-IES}} -template -SRSASN_CODE protocol_ie_single_container_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - warn_assert(crit != ies_set_paramT_::get_crit(id), __func__, __LINE__); - HANDLE_CODE(crit.pack(bref)); - HANDLE_CODE(value.pack(bref)); - - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ie_single_container_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - value = ies_set_paramT_::get_value(id); - HANDLE_CODE(value.unpack(bref)); - - return SRSASN_SUCCESS; -} -template -void protocol_ie_single_container_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} -template -bool protocol_ie_single_container_s::load_info_obj(const uint32_t& id_) -{ - if (not ies_set_paramT_::is_id_valid(id_)) { - return false; - } - id = id_; - crit = ies_set_paramT_::get_crit(id); - value = ies_set_paramT_::get_value(id); - return value.type().value != ies_set_paramT_::value_c::types_opts::nulltype; -} - -uint32_t ngap_protocol_ext_empty_o::idx_to_id(uint32_t idx) -{ - asn1::log_error("object set is empty\n"); - return 0; -} -bool ngap_protocol_ext_empty_o::is_id_valid(const uint32_t& id) -{ - asn1::log_error("object set is empty\n"); - return false; -} -crit_e ngap_protocol_ext_empty_o::get_crit(const uint32_t& id) -{ - return {}; -} -ngap_protocol_ext_empty_o::ext_c ngap_protocol_ext_empty_o::get_ext(const uint32_t& id) -{ - return {}; -} -presence_e ngap_protocol_ext_empty_o::get_presence(const uint32_t& id) -{ - return {}; -} - -// Extension ::= OPEN TYPE -void ngap_protocol_ext_empty_o::ext_c::to_json(json_writer& j) const -{ - j.start_obj(); - j.end_obj(); -} -SRSASN_CODE ngap_protocol_ext_empty_o::ext_c::pack(bit_ref& bref) const -{ - varlength_field_pack_guard varlen_scope(bref, true); - return SRSASN_SUCCESS; -} -SRSASN_CODE ngap_protocol_ext_empty_o::ext_c::unpack(cbit_ref& bref) -{ - varlength_field_unpack_guard varlen_scope(bref, true); - return SRSASN_SUCCESS; -} - -const char* ngap_protocol_ext_empty_o::ext_c::types_opts::to_string() const -{ - static const char* options[] = {}; - return convert_enum_idx(options, 0, value, "ngap_protocol_ext_empty_o::ext_c::types"); -} - // CPTransportLayerInformation ::= CHOICE void cp_transport_layer_info_c::destroy_() { @@ -393,63 +164,6 @@ const char* cp_transport_layer_info_c::types_opts::to_string() const return convert_enum_idx(options, 2, value, "cp_transport_layer_info_c::types"); } -template -protocol_ext_container_item_s::protocol_ext_container_item_s(uint32_t id_, crit_e crit_) : id(id_), crit(crit_) - -{} -template -SRSASN_CODE protocol_ext_container_item_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.pack(bref)); - { - varlength_field_pack_guard varlen_scope(bref, true); - HANDLE_CODE(ext.pack(bref)); - } - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ext_container_item_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - { - varlength_field_unpack_guard varlen_scope(bref, true); - HANDLE_CODE(ext.unpack(bref)); - } - return SRSASN_SUCCESS; -} -template -void protocol_ext_container_item_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} - -SRSASN_CODE protocol_ext_container_empty_l::pack(bit_ref& bref) const -{ - uint32_t nof_ies = 0; - pack_length(bref, nof_ies, 1u, 65535u, true); - - return SRSASN_SUCCESS; -} -SRSASN_CODE protocol_ext_container_empty_l::unpack(cbit_ref& bref) -{ - uint32_t nof_ies = 0; - unpack_length(nof_ies, bref, 1u, 65535u, true); - if (nof_ies > 0) { - return SRSASN_ERROR_DECODE_FAIL; - } - return SRSASN_SUCCESS; -} -void protocol_ext_container_empty_l::to_json(json_writer& j) const -{ - j.start_obj(); - j.end_obj(); -} - // AMF-TNLAssociationSetupItem ::= SEQUENCE SRSASN_CODE amf_tnlassoc_setup_item_s::pack(bit_ref& bref) const { @@ -1279,42 +993,7 @@ uint8_t amf_cfg_upd_ies_o::value_c::types_opts::to_number() const return 0; } -template -protocol_ie_container_item_s::protocol_ie_container_item_s(uint32_t id_, crit_e crit_) : id(id_), crit(crit_) - -{} -template -SRSASN_CODE protocol_ie_container_item_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.pack(bref)); - { - varlength_field_pack_guard varlen_scope(bref, true); - HANDLE_CODE(value.pack(bref)); - } - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ie_container_item_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - { - varlength_field_unpack_guard varlen_scope(bref, true); - HANDLE_CODE(value.unpack(bref)); - } - return SRSASN_SUCCESS; -} -template -void protocol_ie_container_item_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; amf_cfg_upd_ies_container::amf_cfg_upd_ies_container() : amf_name(1, crit_e::reject), @@ -1367,53 +1046,67 @@ SRSASN_CODE amf_cfg_upd_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 1: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 1: { amf_name_present = true; - amf_name.id = c.id; - amf_name.crit = c.crit; - amf_name.value = c.value.amf_name(); + amf_name.id = id; + HANDLE_CODE(amf_name.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_name.value.unpack(bref)); break; - case 96: + } + case 96: { served_guami_list_present = true; - served_guami_list.id = c.id; - served_guami_list.crit = c.crit; - served_guami_list.value = c.value.served_guami_list(); + served_guami_list.id = id; + HANDLE_CODE(served_guami_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(served_guami_list.value.unpack(bref)); break; - case 86: + } + case 86: { relative_amf_capacity_present = true; - relative_amf_capacity.id = c.id; - relative_amf_capacity.crit = c.crit; - relative_amf_capacity.value = c.value.relative_amf_capacity(); + relative_amf_capacity.id = id; + HANDLE_CODE(relative_amf_capacity.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(relative_amf_capacity.value.unpack(bref)); break; - case 80: + } + case 80: { plmn_support_list_present = true; - plmn_support_list.id = c.id; - plmn_support_list.crit = c.crit; - plmn_support_list.value = c.value.plmn_support_list(); + plmn_support_list.id = id; + HANDLE_CODE(plmn_support_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(plmn_support_list.value.unpack(bref)); break; - case 6: + } + case 6: { amf_tnlassoc_to_add_list_present = true; - amf_tnlassoc_to_add_list.id = c.id; - amf_tnlassoc_to_add_list.crit = c.crit; - amf_tnlassoc_to_add_list.value = c.value.amf_tnlassoc_to_add_list(); + amf_tnlassoc_to_add_list.id = id; + HANDLE_CODE(amf_tnlassoc_to_add_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_tnlassoc_to_add_list.value.unpack(bref)); break; - case 7: + } + case 7: { amf_tnlassoc_to_rem_list_present = true; - amf_tnlassoc_to_rem_list.id = c.id; - amf_tnlassoc_to_rem_list.crit = c.crit; - amf_tnlassoc_to_rem_list.value = c.value.amf_tnlassoc_to_rem_list(); + amf_tnlassoc_to_rem_list.id = id; + HANDLE_CODE(amf_tnlassoc_to_rem_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_tnlassoc_to_rem_list.value.unpack(bref)); break; - case 8: + } + case 8: { amf_tnlassoc_to_upd_list_present = true; - amf_tnlassoc_to_upd_list.id = c.id; - amf_tnlassoc_to_upd_list.crit = c.crit; - amf_tnlassoc_to_upd_list.value = c.value.amf_tnlassoc_to_upd_list(); + amf_tnlassoc_to_upd_list.id = id; + HANDLE_CODE(amf_tnlassoc_to_upd_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_tnlassoc_to_upd_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -1454,29 +1147,6 @@ void amf_cfg_upd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// AMFConfigurationUpdate ::= SEQUENCE -SRSASN_CODE amf_cfg_upd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE amf_cfg_upd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void amf_cfg_upd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // CauseMisc ::= ENUMERATED const char* cause_misc_opts::to_string() const { @@ -1897,7 +1567,7 @@ SRSASN_CODE crit_diagnostics_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(proc_code_present, 1)); HANDLE_CODE(bref.pack(trigger_msg_present, 1)); HANDLE_CODE(bref.pack(proc_crit_present, 1)); - HANDLE_CODE(bref.pack(ies_crit_diagnostics_present, 1)); + HANDLE_CODE(bref.pack(ies_crit_diagnostics.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); if (proc_code_present) { @@ -1909,7 +1579,7 @@ SRSASN_CODE crit_diagnostics_s::pack(bit_ref& bref) const if (proc_crit_present) { HANDLE_CODE(proc_crit.pack(bref)); } - if (ies_crit_diagnostics_present) { + if (ies_crit_diagnostics.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ies_crit_diagnostics, 1, 256, true)); } if (ie_exts_present) { @@ -1924,6 +1594,7 @@ SRSASN_CODE crit_diagnostics_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(proc_code_present, 1)); HANDLE_CODE(bref.unpack(trigger_msg_present, 1)); HANDLE_CODE(bref.unpack(proc_crit_present, 1)); + bool ies_crit_diagnostics_present; HANDLE_CODE(bref.unpack(ies_crit_diagnostics_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -1957,7 +1628,7 @@ void crit_diagnostics_s::to_json(json_writer& j) const if (proc_crit_present) { j.write_str("procedureCriticality", proc_crit.to_string()); } - if (ies_crit_diagnostics_present) { + if (ies_crit_diagnostics.size() > 0) { j.start_array("iEsCriticalityDiagnostics"); for (const auto& e1 : ies_crit_diagnostics) { e1.to_json(j); @@ -2216,7 +1887,7 @@ const char* amf_cfg_upd_ack_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "amf_cfg_upd_ack_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; amf_cfg_upd_ack_ies_container::amf_cfg_upd_ack_ies_container() : amf_tnlassoc_setup_list(5, crit_e::ignore), @@ -2249,29 +1920,35 @@ SRSASN_CODE amf_cfg_upd_ack_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 5: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 5: { amf_tnlassoc_setup_list_present = true; - amf_tnlassoc_setup_list.id = c.id; - amf_tnlassoc_setup_list.crit = c.crit; - amf_tnlassoc_setup_list.value = c.value.amf_tnlassoc_setup_list(); + amf_tnlassoc_setup_list.id = id; + HANDLE_CODE(amf_tnlassoc_setup_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_tnlassoc_setup_list.value.unpack(bref)); break; - case 4: + } + case 4: { amf_tnlassoc_failed_to_setup_list_present = true; - amf_tnlassoc_failed_to_setup_list.id = c.id; - amf_tnlassoc_failed_to_setup_list.crit = c.crit; - amf_tnlassoc_failed_to_setup_list.value = c.value.amf_tnlassoc_failed_to_setup_list(); + amf_tnlassoc_failed_to_setup_list.id = id; + HANDLE_CODE(amf_tnlassoc_failed_to_setup_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_tnlassoc_failed_to_setup_list.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -2296,29 +1973,6 @@ void amf_cfg_upd_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// AMFConfigurationUpdateAcknowledge ::= SEQUENCE -SRSASN_CODE amf_cfg_upd_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE amf_cfg_upd_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void amf_cfg_upd_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // TimeToWait ::= ENUMERATED const char* time_to_wait_opts::to_string() const { @@ -2566,7 +2220,7 @@ const char* amf_cfg_upd_fail_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "amf_cfg_upd_fail_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; amf_cfg_upd_fail_ies_container::amf_cfg_upd_fail_ies_container() : cause(15, crit_e::ignore), time_to_wait(107, crit_e::ignore), crit_diagnostics(19, crit_e::ignore) @@ -2596,29 +2250,35 @@ SRSASN_CODE amf_cfg_upd_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 1; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 15: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 107: + } + case 107: { time_to_wait_present = true; - time_to_wait.id = c.id; - time_to_wait.crit = c.crit; - time_to_wait.value = c.value.time_to_wait(); + time_to_wait.id = id; + HANDLE_CODE(time_to_wait.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_to_wait.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -2645,29 +2305,6 @@ void amf_cfg_upd_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// AMFConfigurationUpdateFailure ::= SEQUENCE -SRSASN_CODE amf_cfg_upd_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE amf_cfg_upd_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void amf_cfg_upd_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // GNB-ID ::= CHOICE void gnb_id_c::destroy_() { @@ -3781,28 +3418,6 @@ const char* amf_status_ind_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "amf_status_ind_ies_o::value_c::types"); } -// AMFStatusIndication ::= SEQUENCE -SRSASN_CODE amf_status_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(pack_dyn_seq_of(bref, protocol_ies, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE amf_status_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(unpack_dyn_seq_of(protocol_ies, bref, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -void amf_status_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - j.end_obj(); -} - // DataForwardingAccepted ::= ENUMERATED const char* data_forwarding_accepted_opts::to_string() const { @@ -4537,18 +4152,18 @@ void area_of_interest_tai_item_s::to_json(json_writer& j) const SRSASN_CODE area_of_interest_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(area_of_interest_tai_list_present, 1)); - HANDLE_CODE(bref.pack(area_of_interest_cell_list_present, 1)); - HANDLE_CODE(bref.pack(area_of_interest_ran_node_list_present, 1)); + HANDLE_CODE(bref.pack(area_of_interest_tai_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(area_of_interest_cell_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(area_of_interest_ran_node_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); - if (area_of_interest_tai_list_present) { + if (area_of_interest_tai_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, area_of_interest_tai_list, 1, 16, true)); } - if (area_of_interest_cell_list_present) { + if (area_of_interest_cell_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, area_of_interest_cell_list, 1, 256, true)); } - if (area_of_interest_ran_node_list_present) { + if (area_of_interest_ran_node_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, area_of_interest_ran_node_list, 1, 64, true)); } if (ie_exts_present) { @@ -4560,8 +4175,11 @@ SRSASN_CODE area_of_interest_s::pack(bit_ref& bref) const SRSASN_CODE area_of_interest_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool area_of_interest_tai_list_present; HANDLE_CODE(bref.unpack(area_of_interest_tai_list_present, 1)); + bool area_of_interest_cell_list_present; HANDLE_CODE(bref.unpack(area_of_interest_cell_list_present, 1)); + bool area_of_interest_ran_node_list_present; HANDLE_CODE(bref.unpack(area_of_interest_ran_node_list_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -4583,21 +4201,21 @@ SRSASN_CODE area_of_interest_s::unpack(cbit_ref& bref) void area_of_interest_s::to_json(json_writer& j) const { j.start_obj(); - if (area_of_interest_tai_list_present) { + if (area_of_interest_tai_list.size() > 0) { j.start_array("areaOfInterestTAIList"); for (const auto& e1 : area_of_interest_tai_list) { e1.to_json(j); } j.end_array(); } - if (area_of_interest_cell_list_present) { + if (area_of_interest_cell_list.size() > 0) { j.start_array("areaOfInterestCellList"); for (const auto& e1 : area_of_interest_cell_list) { e1.to_json(j); } j.end_array(); } - if (area_of_interest_ran_node_list_present) { + if (area_of_interest_ran_node_list.size() > 0) { j.start_array("areaOfInterestRANNodeList"); for (const auto& e1 : area_of_interest_ran_node_list) { e1.to_json(j); @@ -6945,7 +6563,7 @@ const char* cell_traffic_trace_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 5, value, "cell_traffic_trace_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; cell_traffic_trace_ies_container::cell_traffic_trace_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -6975,41 +6593,51 @@ SRSASN_CODE cell_traffic_trace_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 44: + } + case 44: { nof_mandatory_ies--; - ngran_trace_id.id = c.id; - ngran_trace_id.crit = c.crit; - ngran_trace_id.value = c.value.ngran_trace_id(); + ngran_trace_id.id = id; + HANDLE_CODE(ngran_trace_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ngran_trace_id.value.unpack(bref)); break; - case 43: + } + case 43: { nof_mandatory_ies--; - ngran_cgi.id = c.id; - ngran_cgi.crit = c.crit; - ngran_cgi.value = c.value.ngran_cgi(); + ngran_cgi.id = id; + HANDLE_CODE(ngran_cgi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ngran_cgi.value.unpack(bref)); break; - case 109: + } + case 109: { nof_mandatory_ies--; - trace_collection_entity_ip_address.id = c.id; - trace_collection_entity_ip_address.crit = c.crit; - trace_collection_entity_ip_address.value = c.value.trace_collection_entity_ip_address(); + trace_collection_entity_ip_address.id = id; + HANDLE_CODE(trace_collection_entity_ip_address.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(trace_collection_entity_ip_address.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -7036,29 +6664,6 @@ void cell_traffic_trace_ies_container::to_json(json_writer& j) const j.end_obj(); } -// CellTrafficTrace ::= SEQUENCE -SRSASN_CODE cell_traffic_trace_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE cell_traffic_trace_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void cell_traffic_trace_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // CellSize ::= ENUMERATED const char* cell_size_opts::to_string() const { @@ -7286,7 +6891,7 @@ SRSASN_CODE expected_ue_behaviour_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(expected_ue_activity_behaviour_present, 1)); HANDLE_CODE(bref.pack(expected_ho_interv_present, 1)); HANDLE_CODE(bref.pack(expected_ue_mob_present, 1)); - HANDLE_CODE(bref.pack(expected_ue_moving_trajectory_present, 1)); + HANDLE_CODE(bref.pack(expected_ue_moving_trajectory.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); if (expected_ue_activity_behaviour_present) { @@ -7298,7 +6903,7 @@ SRSASN_CODE expected_ue_behaviour_s::pack(bit_ref& bref) const if (expected_ue_mob_present) { HANDLE_CODE(expected_ue_mob.pack(bref)); } - if (expected_ue_moving_trajectory_present) { + if (expected_ue_moving_trajectory.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, expected_ue_moving_trajectory, 1, 16, true)); } if (ie_exts_present) { @@ -7313,6 +6918,7 @@ SRSASN_CODE expected_ue_behaviour_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(expected_ue_activity_behaviour_present, 1)); HANDLE_CODE(bref.unpack(expected_ho_interv_present, 1)); HANDLE_CODE(bref.unpack(expected_ue_mob_present, 1)); + bool expected_ue_moving_trajectory_present; HANDLE_CODE(bref.unpack(expected_ue_moving_trajectory_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -7347,7 +6953,7 @@ void expected_ue_behaviour_s::to_json(json_writer& j) const if (expected_ue_mob_present) { j.write_str("expectedUEMobility", expected_ue_mob.to_string()); } - if (expected_ue_moving_trajectory_present) { + if (expected_ue_moving_trajectory.size() > 0) { j.start_array("expectedUEMovingTrajectory"); for (const auto& e1 : expected_ue_moving_trajectory) { e1.to_json(j); @@ -8482,7 +8088,7 @@ const char* deactiv_trace_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "deactiv_trace_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; deactiv_trace_ies_container::deactiv_trace_ies_container() : amf_ue_ngap_id(10, crit_e::reject), ran_ue_ngap_id(85, crit_e::reject), ngran_trace_id(44, crit_e::ignore) @@ -8506,29 +8112,35 @@ SRSASN_CODE deactiv_trace_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 44: + } + case 44: { nof_mandatory_ies--; - ngran_trace_id.id = c.id; - ngran_trace_id.crit = c.crit; - ngran_trace_id.value = c.value.ngran_trace_id(); + ngran_trace_id.id = id; + HANDLE_CODE(ngran_trace_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ngran_trace_id.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -8551,29 +8163,6 @@ void deactiv_trace_ies_container::to_json(json_writer& j) const j.end_obj(); } -// DeactivateTrace ::= SEQUENCE -SRSASN_CODE deactiv_trace_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE deactiv_trace_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void deactiv_trace_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // ForbiddenAreaInformation-Item ::= SEQUENCE SRSASN_CODE forbidden_area_info_item_s::pack(bit_ref& bref) const { @@ -8660,15 +8249,15 @@ void rat_restricts_item_s::to_json(json_writer& j) const SRSASN_CODE service_area_info_item_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(allowed_tacs_present, 1)); - HANDLE_CODE(bref.pack(not_allowed_tacs_present, 1)); + HANDLE_CODE(bref.pack(allowed_tacs.size() > 0, 1)); + HANDLE_CODE(bref.pack(not_allowed_tacs.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(plmn_id.pack(bref)); - if (allowed_tacs_present) { + if (allowed_tacs.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, allowed_tacs, 1, 16, true)); } - if (not_allowed_tacs_present) { + if (not_allowed_tacs.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, not_allowed_tacs, 1, 16, true)); } if (ie_exts_present) { @@ -8680,7 +8269,9 @@ SRSASN_CODE service_area_info_item_s::pack(bit_ref& bref) const SRSASN_CODE service_area_info_item_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool allowed_tacs_present; HANDLE_CODE(bref.unpack(allowed_tacs_present, 1)); + bool not_allowed_tacs_present; HANDLE_CODE(bref.unpack(not_allowed_tacs_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -8701,14 +8292,14 @@ void service_area_info_item_s::to_json(json_writer& j) const { j.start_obj(); j.write_str("pLMNIdentity", plmn_id.to_string()); - if (allowed_tacs_present) { + if (allowed_tacs.size() > 0) { j.start_array("allowedTACs"); for (const auto& e1 : allowed_tacs) { j.write_str(e1.to_string()); } j.end_array(); } - if (not_allowed_tacs_present) { + if (not_allowed_tacs.size() > 0) { j.start_array("notAllowedTACs"); for (const auto& e1 : not_allowed_tacs) { j.write_str(e1.to_string()); @@ -8787,26 +8378,26 @@ const char* mob_restrict_list_ext_ies_o::ext_c::types_opts::to_string() const SRSASN_CODE mob_restrict_list_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(equivalent_plmns_present, 1)); - HANDLE_CODE(bref.pack(rat_restricts_present, 1)); - HANDLE_CODE(bref.pack(forbidden_area_info_present, 1)); - HANDLE_CODE(bref.pack(service_area_info_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(equivalent_plmns.size() > 0, 1)); + HANDLE_CODE(bref.pack(rat_restricts.size() > 0, 1)); + HANDLE_CODE(bref.pack(forbidden_area_info.size() > 0, 1)); + HANDLE_CODE(bref.pack(service_area_info.size() > 0, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); HANDLE_CODE(serving_plmn.pack(bref)); - if (equivalent_plmns_present) { + if (equivalent_plmns.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, equivalent_plmns, 1, 15, true)); } - if (rat_restricts_present) { + if (rat_restricts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rat_restricts, 1, 16, true)); } - if (forbidden_area_info_present) { + if (forbidden_area_info.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, forbidden_area_info, 1, 16, true)); } - if (service_area_info_present) { + if (service_area_info.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, service_area_info, 1, 16, true)); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -8815,10 +8406,15 @@ SRSASN_CODE mob_restrict_list_s::pack(bit_ref& bref) const SRSASN_CODE mob_restrict_list_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool equivalent_plmns_present; HANDLE_CODE(bref.unpack(equivalent_plmns_present, 1)); + bool rat_restricts_present; HANDLE_CODE(bref.unpack(rat_restricts_present, 1)); + bool forbidden_area_info_present; HANDLE_CODE(bref.unpack(forbidden_area_info_present, 1)); + bool service_area_info_present; HANDLE_CODE(bref.unpack(service_area_info_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); HANDLE_CODE(serving_plmn.unpack(bref)); @@ -8844,35 +8440,35 @@ void mob_restrict_list_s::to_json(json_writer& j) const { j.start_obj(); j.write_str("servingPLMN", serving_plmn.to_string()); - if (equivalent_plmns_present) { + if (equivalent_plmns.size() > 0) { j.start_array("equivalentPLMNs"); for (const auto& e1 : equivalent_plmns) { j.write_str(e1.to_string()); } j.end_array(); } - if (rat_restricts_present) { + if (rat_restricts.size() > 0) { j.start_array("rATRestrictions"); for (const auto& e1 : rat_restricts) { e1.to_json(j); } j.end_array(); } - if (forbidden_area_info_present) { + if (forbidden_area_info.size() > 0) { j.start_array("forbiddenAreaInformation"); for (const auto& e1 : forbidden_area_info) { e1.to_json(j); } j.end_array(); } - if (service_area_info_present) { + if (service_area_info.size() > 0) { j.start_array("serviceAreaInformation"); for (const auto& e1 : service_area_info) { e1.to_json(j); } j.end_array(); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -9382,7 +8978,7 @@ const char* dl_nas_transport_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 9, value, "dl_nas_transport_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_nas_transport_ies_container::dl_nas_transport_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -9438,65 +9034,83 @@ SRSASN_CODE dl_nas_transport_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 48: + } + case 48: { old_amf_present = true; - old_amf.id = c.id; - old_amf.crit = c.crit; - old_amf.value = c.value.old_amf(); + old_amf.id = id; + HANDLE_CODE(old_amf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(old_amf.value.unpack(bref)); break; - case 83: + } + case 83: { ran_paging_prio_present = true; - ran_paging_prio.id = c.id; - ran_paging_prio.crit = c.crit; - ran_paging_prio.value = c.value.ran_paging_prio(); + ran_paging_prio.id = id; + HANDLE_CODE(ran_paging_prio.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_paging_prio.value.unpack(bref)); break; - case 38: + } + case 38: { nof_mandatory_ies--; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 36: + } + case 36: { mob_restrict_list_present = true; - mob_restrict_list.id = c.id; - mob_restrict_list.crit = c.crit; - mob_restrict_list.value = c.value.mob_restrict_list(); + mob_restrict_list.id = id; + HANDLE_CODE(mob_restrict_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mob_restrict_list.value.unpack(bref)); break; - case 31: + } + case 31: { idx_to_rfsp_present = true; - idx_to_rfsp.id = c.id; - idx_to_rfsp.crit = c.crit; - idx_to_rfsp.value = c.value.idx_to_rfsp(); + idx_to_rfsp.id = id; + HANDLE_CODE(idx_to_rfsp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(idx_to_rfsp.value.unpack(bref)); break; - case 110: + } + case 110: { ue_aggregate_maximum_bit_rate_present = true; - ue_aggregate_maximum_bit_rate.id = c.id; - ue_aggregate_maximum_bit_rate.crit = c.crit; - ue_aggregate_maximum_bit_rate.value = c.value.ue_aggregate_maximum_bit_rate(); + ue_aggregate_maximum_bit_rate.id = id; + HANDLE_CODE(ue_aggregate_maximum_bit_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_aggregate_maximum_bit_rate.value.unpack(bref)); break; - case 0: + } + case 0: { allowed_nssai_present = true; - allowed_nssai.id = c.id; - allowed_nssai.crit = c.crit; - allowed_nssai.value = c.value.allowed_nssai(); + allowed_nssai.id = id; + HANDLE_CODE(allowed_nssai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(allowed_nssai.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -9543,29 +9157,6 @@ void dl_nas_transport_ies_container::to_json(json_writer& j) const j.end_obj(); } -// DownlinkNASTransport ::= SEQUENCE -SRSASN_CODE dl_nas_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_nas_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_nas_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // DownlinkNonUEAssociatedNRPPaTransportIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES uint32_t dl_non_ueassociated_nrp_pa_transport_ies_o::idx_to_id(uint32_t idx) { @@ -9767,7 +9358,7 @@ const char* dl_non_ueassociated_nrp_pa_transport_ies_o::value_c::types_opts::to_ return convert_enum_idx(options, 2, value, "dl_non_ueassociated_nrp_pa_transport_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_non_ueassociated_nrp_pa_transport_ies_container::dl_non_ueassociated_nrp_pa_transport_ies_container() : routing_id(89, crit_e::reject), nrp_pa_pdu(46, crit_e::reject) @@ -9790,23 +9381,27 @@ SRSASN_CODE dl_non_ueassociated_nrp_pa_transport_ies_container::unpack(cbit_ref& uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 89: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 89: { nof_mandatory_ies--; - routing_id.id = c.id; - routing_id.crit = c.crit; - routing_id.value = c.value.routing_id(); + routing_id.id = id; + HANDLE_CODE(routing_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(routing_id.value.unpack(bref)); break; - case 46: + } + case 46: { nof_mandatory_ies--; - nrp_pa_pdu.id = c.id; - nrp_pa_pdu.crit = c.crit; - nrp_pa_pdu.value = c.value.nrp_pa_pdu(); + nrp_pa_pdu.id = id; + HANDLE_CODE(nrp_pa_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrp_pa_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -9827,41 +9422,18 @@ void dl_non_ueassociated_nrp_pa_transport_ies_container::to_json(json_writer& j) j.end_obj(); } -// DownlinkNonUEAssociatedNRPPaTransport ::= SEQUENCE -SRSASN_CODE dl_non_ueassociated_nrp_pa_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_non_ueassociated_nrp_pa_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_non_ueassociated_nrp_pa_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // XnExtTLA-Item ::= SEQUENCE SRSASN_CODE xn_ext_tla_item_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(ipsec_tla_present, 1)); - HANDLE_CODE(bref.pack(gtp_tlas_present, 1)); + HANDLE_CODE(bref.pack(gtp_tlas.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); if (ipsec_tla_present) { HANDLE_CODE(ipsec_tla.pack(bref)); } - if (gtp_tlas_present) { + if (gtp_tlas.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, gtp_tlas, 1, 16, true)); } if (ie_exts_present) { @@ -9874,6 +9446,7 @@ SRSASN_CODE xn_ext_tla_item_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(ipsec_tla_present, 1)); + bool gtp_tlas_present; HANDLE_CODE(bref.unpack(gtp_tlas_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -9895,7 +9468,7 @@ void xn_ext_tla_item_s::to_json(json_writer& j) const if (ipsec_tla_present) { j.write_str("iPsecTLA", ipsec_tla.to_string()); } - if (gtp_tlas_present) { + if (gtp_tlas.size() > 0) { j.start_array("gTP-TLAs"); for (const auto& e1 : gtp_tlas) { j.write_str(e1.to_string()); @@ -9913,11 +9486,11 @@ void xn_ext_tla_item_s::to_json(json_writer& j) const SRSASN_CODE xn_tnl_cfg_info_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(xn_extended_transport_layer_addresses_present, 1)); + HANDLE_CODE(bref.pack(xn_extended_transport_layer_addresses.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, xn_transport_layer_addresses, 1, 16, true)); - if (xn_extended_transport_layer_addresses_present) { + if (xn_extended_transport_layer_addresses.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, xn_extended_transport_layer_addresses, 1, 2, true)); } if (ie_exts_present) { @@ -9929,6 +9502,7 @@ SRSASN_CODE xn_tnl_cfg_info_s::pack(bit_ref& bref) const SRSASN_CODE xn_tnl_cfg_info_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool xn_extended_transport_layer_addresses_present; HANDLE_CODE(bref.unpack(xn_extended_transport_layer_addresses_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -9950,7 +9524,7 @@ void xn_tnl_cfg_info_s::to_json(json_writer& j) const j.write_str(e1.to_string()); } j.end_array(); - if (xn_extended_transport_layer_addresses_present) { + if (xn_extended_transport_layer_addresses.size() > 0) { j.start_array("xnExtendedTransportLayerAddresses"); for (const auto& e1 : xn_extended_transport_layer_addresses) { e1.to_json(j); @@ -10513,7 +10087,7 @@ const char* dl_ran_cfg_transfer_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 2, value, "dl_ran_cfg_transfer_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_ran_cfg_transfer_ies_container::dl_ran_cfg_transfer_ies_container() : son_cfg_transfer_dl(98, crit_e::ignore), endc_son_cfg_transfer_dl(157, crit_e::ignore) @@ -10540,23 +10114,27 @@ SRSASN_CODE dl_ran_cfg_transfer_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 98: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 98: { son_cfg_transfer_dl_present = true; - son_cfg_transfer_dl.id = c.id; - son_cfg_transfer_dl.crit = c.crit; - son_cfg_transfer_dl.value = c.value.son_cfg_transfer_dl(); + son_cfg_transfer_dl.id = id; + HANDLE_CODE(son_cfg_transfer_dl.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(son_cfg_transfer_dl.value.unpack(bref)); break; - case 157: + } + case 157: { endc_son_cfg_transfer_dl_present = true; - endc_son_cfg_transfer_dl.id = c.id; - endc_son_cfg_transfer_dl.crit = c.crit; - endc_son_cfg_transfer_dl.value = c.value.endc_son_cfg_transfer_dl(); + endc_son_cfg_transfer_dl.id = id; + HANDLE_CODE(endc_son_cfg_transfer_dl.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(endc_son_cfg_transfer_dl.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -10577,29 +10155,6 @@ void dl_ran_cfg_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// DownlinkRANConfigurationTransfer ::= SEQUENCE -SRSASN_CODE dl_ran_cfg_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_ran_cfg_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_ran_cfg_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // RANStatusTransfer-TransparentContainer ::= SEQUENCE SRSASN_CODE ran_status_transfer_transparent_container_s::pack(bit_ref& bref) const { @@ -10873,7 +10428,7 @@ const char* dl_ran_status_transfer_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "dl_ran_status_transfer_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_ran_status_transfer_ies_container::dl_ran_status_transfer_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -10899,29 +10454,35 @@ SRSASN_CODE dl_ran_status_transfer_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 84: + } + case 84: { nof_mandatory_ies--; - ran_status_transfer_transparent_container.id = c.id; - ran_status_transfer_transparent_container.crit = c.crit; - ran_status_transfer_transparent_container.value = c.value.ran_status_transfer_transparent_container(); + ran_status_transfer_transparent_container.id = id; + HANDLE_CODE(ran_status_transfer_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_status_transfer_transparent_container.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -10944,29 +10505,6 @@ void dl_ran_status_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// DownlinkRANStatusTransfer ::= SEQUENCE -SRSASN_CODE dl_ran_status_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_ran_status_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_ran_status_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // DownlinkUEAssociatedNRPPaTransportIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES uint32_t dl_ueassociated_nrp_pa_transport_ies_o::idx_to_id(uint32_t idx) { @@ -11235,7 +10773,7 @@ const char* dl_ueassociated_nrp_pa_transport_ies_o::value_c::types_opts::to_stri return convert_enum_idx(options, 4, value, "dl_ueassociated_nrp_pa_transport_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_ueassociated_nrp_pa_transport_ies_container::dl_ueassociated_nrp_pa_transport_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -11263,35 +10801,43 @@ SRSASN_CODE dl_ueassociated_nrp_pa_transport_ies_container::unpack(cbit_ref& bre uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 89: + } + case 89: { nof_mandatory_ies--; - routing_id.id = c.id; - routing_id.crit = c.crit; - routing_id.value = c.value.routing_id(); + routing_id.id = id; + HANDLE_CODE(routing_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(routing_id.value.unpack(bref)); break; - case 46: + } + case 46: { nof_mandatory_ies--; - nrp_pa_pdu.id = c.id; - nrp_pa_pdu.crit = c.crit; - nrp_pa_pdu.value = c.value.nrp_pa_pdu(); + nrp_pa_pdu.id = id; + HANDLE_CODE(nrp_pa_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrp_pa_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -11316,29 +10862,6 @@ void dl_ueassociated_nrp_pa_transport_ies_container::to_json(json_writer& j) con j.end_obj(); } -// DownlinkUEAssociatedNRPPaTransport ::= SEQUENCE -SRSASN_CODE dl_ueassociated_nrp_pa_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_ueassociated_nrp_pa_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_ueassociated_nrp_pa_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // DelayCritical ::= ENUMERATED const char* delay_crit_opts::to_string() const { @@ -11900,7 +11423,7 @@ const char* error_ind_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 4, value, "error_ind_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; error_ind_ies_container::error_ind_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -11938,35 +11461,43 @@ SRSASN_CODE error_ind_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { amf_ue_ngap_id_present = true; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { ran_ue_ngap_id_present = true; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 15: + } + case 15: { cause_present = true; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -11995,29 +11526,6 @@ void error_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ErrorIndication ::= SEQUENCE -SRSASN_CODE error_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE error_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void error_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // FiveG-S-TMSI ::= SEQUENCE SRSASN_CODE five_g_s_tmsi_s::pack(bit_ref& bref) const { @@ -12374,7 +11882,7 @@ const char* ho_cancel_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "ho_cancel_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_cancel_ies_container::ho_cancel_ies_container() : amf_ue_ngap_id(10, crit_e::reject), ran_ue_ngap_id(85, crit_e::reject), cause(15, crit_e::ignore) @@ -12398,29 +11906,35 @@ SRSASN_CODE ho_cancel_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -12443,29 +11957,6 @@ void ho_cancel_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverCancel ::= SEQUENCE -SRSASN_CODE ho_cancel_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_cancel_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_cancel_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverCancelAcknowledgeIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES uint32_t ho_cancel_ack_ies_o::idx_to_id(uint32_t idx) { @@ -12695,7 +12186,7 @@ const char* ho_cancel_ack_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "ho_cancel_ack_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_cancel_ack_ies_container::ho_cancel_ack_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), ran_ue_ngap_id(85, crit_e::ignore), crit_diagnostics(19, crit_e::ignore) @@ -12722,29 +12213,35 @@ SRSASN_CODE ho_cancel_ack_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -12769,29 +12266,6 @@ void ho_cancel_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverCancelAcknowledge ::= SEQUENCE -SRSASN_CODE ho_cancel_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_cancel_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_cancel_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // PDUSessionResourceHandoverItem ::= SEQUENCE SRSASN_CODE pdu_session_res_ho_item_s::pack(bit_ref& bref) const { @@ -13307,7 +12781,7 @@ const char* ho_cmd_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 8, value, "ho_cmd_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_cmd_ies_container::ho_cmd_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -13352,59 +12826,75 @@ SRSASN_CODE ho_cmd_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 29: + } + case 29: { nof_mandatory_ies--; - handov_type.id = c.id; - handov_type.crit = c.crit; - handov_type.value = c.value.handov_type(); + handov_type.id = id; + HANDLE_CODE(handov_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(handov_type.value.unpack(bref)); break; - case 39: + } + case 39: { nas_security_params_from_ngran_present = true; - nas_security_params_from_ngran.id = c.id; - nas_security_params_from_ngran.crit = c.crit; - nas_security_params_from_ngran.value = c.value.nas_security_params_from_ngran(); + nas_security_params_from_ngran.id = id; + HANDLE_CODE(nas_security_params_from_ngran.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_security_params_from_ngran.value.unpack(bref)); break; - case 59: + } + case 59: { nof_mandatory_ies--; - pdu_session_res_ho_list.id = c.id; - pdu_session_res_ho_list.crit = c.crit; - pdu_session_res_ho_list.value = c.value.pdu_session_res_ho_list(); + pdu_session_res_ho_list.id = id; + HANDLE_CODE(pdu_session_res_ho_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_ho_list.value.unpack(bref)); break; - case 78: + } + case 78: { pdu_session_res_to_release_list_ho_cmd_present = true; - pdu_session_res_to_release_list_ho_cmd.id = c.id; - pdu_session_res_to_release_list_ho_cmd.crit = c.crit; - pdu_session_res_to_release_list_ho_cmd.value = c.value.pdu_session_res_to_release_list_ho_cmd(); + pdu_session_res_to_release_list_ho_cmd.id = id; + HANDLE_CODE(pdu_session_res_to_release_list_ho_cmd.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_to_release_list_ho_cmd.value.unpack(bref)); break; - case 106: + } + case 106: { nof_mandatory_ies--; - target_to_source_transparent_container.id = c.id; - target_to_source_transparent_container.crit = c.crit; - target_to_source_transparent_container.value = c.value.target_to_source_transparent_container(); + target_to_source_transparent_container.id = id; + HANDLE_CODE(target_to_source_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(target_to_source_transparent_container.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -13443,29 +12933,6 @@ void ho_cmd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverCommand ::= SEQUENCE -SRSASN_CODE ho_cmd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_cmd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_cmd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // QosFlowPerTNLInformation ::= SEQUENCE SRSASN_CODE qos_flow_per_tnl_info_s::pack(bit_ref& bref) const { @@ -13653,20 +13120,20 @@ SRSASN_CODE ho_cmd_transfer_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(dlforwarding_up_tnl_info_present, 1)); - HANDLE_CODE(bref.pack(qos_flow_to_be_forwarded_list_present, 1)); - HANDLE_CODE(bref.pack(data_forwarding_resp_drb_list_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(qos_flow_to_be_forwarded_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(data_forwarding_resp_drb_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); if (dlforwarding_up_tnl_info_present) { HANDLE_CODE(dlforwarding_up_tnl_info.pack(bref)); } - if (qos_flow_to_be_forwarded_list_present) { + if (qos_flow_to_be_forwarded_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_to_be_forwarded_list, 1, 64, true)); } - if (data_forwarding_resp_drb_list_present) { + if (data_forwarding_resp_drb_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, data_forwarding_resp_drb_list, 1, 32, true)); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -13676,8 +13143,11 @@ SRSASN_CODE ho_cmd_transfer_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(dlforwarding_up_tnl_info_present, 1)); + bool qos_flow_to_be_forwarded_list_present; HANDLE_CODE(bref.unpack(qos_flow_to_be_forwarded_list_present, 1)); + bool data_forwarding_resp_drb_list_present; HANDLE_CODE(bref.unpack(data_forwarding_resp_drb_list_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); if (dlforwarding_up_tnl_info_present) { @@ -13702,21 +13172,21 @@ void ho_cmd_transfer_s::to_json(json_writer& j) const j.write_fieldname("dLForwardingUP-TNLInformation"); dlforwarding_up_tnl_info.to_json(j); } - if (qos_flow_to_be_forwarded_list_present) { + if (qos_flow_to_be_forwarded_list.size() > 0) { j.start_array("qosFlowToBeForwardedList"); for (const auto& e1 : qos_flow_to_be_forwarded_list) { e1.to_json(j); } j.end_array(); } - if (data_forwarding_resp_drb_list_present) { + if (data_forwarding_resp_drb_list.size() > 0) { j.start_array("dataForwardingResponseDRBList"); for (const auto& e1 : data_forwarding_resp_drb_list) { e1.to_json(j); } j.end_array(); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -13961,7 +13431,7 @@ uint8_t ho_fail_ies_o::value_c::types_opts::to_number() const return map_enum_number(options, 1, value, "ho_fail_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_fail_ies_container::ho_fail_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), cause(15, crit_e::ignore), crit_diagnostics(19, crit_e::ignore) @@ -13988,29 +13458,35 @@ SRSASN_CODE ho_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -14035,29 +13511,6 @@ void ho_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverFailure ::= SEQUENCE -SRSASN_CODE ho_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // UserLocationInformationEUTRA ::= SEQUENCE SRSASN_CODE user_location_info_eutra_s::pack(bit_ref& bref) const { @@ -14629,7 +14082,7 @@ const char* ho_notify_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "ho_notify_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_notify_ies_container::ho_notify_ies_container() : amf_ue_ngap_id(10, crit_e::reject), ran_ue_ngap_id(85, crit_e::reject), user_location_info(121, crit_e::ignore) @@ -14653,29 +14106,35 @@ SRSASN_CODE ho_notify_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 121: + } + case 121: { nof_mandatory_ies--; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -14698,29 +14157,6 @@ void ho_notify_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverNotify ::= SEQUENCE -SRSASN_CODE ho_notify_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_notify_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_notify_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverPreparationFailureIEs ::= OBJECT SET OF NGAP-PROTOCOL-IES uint32_t ho_prep_fail_ies_o::idx_to_id(uint32_t idx) { @@ -14990,7 +14426,7 @@ const char* ho_prep_fail_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 4, value, "ho_prep_fail_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_prep_fail_ies_container::ho_prep_fail_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -15021,35 +14457,43 @@ SRSASN_CODE ho_prep_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -15076,29 +14520,6 @@ void ho_prep_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverPreparationFailure ::= SEQUENCE -SRSASN_CODE ho_prep_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_prep_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_prep_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverPreparationUnsuccessfulTransfer ::= SEQUENCE SRSASN_CODE ho_prep_unsuccessful_transfer_s::pack(bit_ref& bref) const { @@ -15214,13 +14635,13 @@ const char* trace_depth_opts::to_string() const SRSASN_CODE location_report_request_type_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(area_of_interest_list_present, 1)); + HANDLE_CODE(bref.pack(area_of_interest_list.size() > 0, 1)); HANDLE_CODE(bref.pack(location_report_ref_id_to_be_cancelled_present, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(event_type.pack(bref)); HANDLE_CODE(report_area.pack(bref)); - if (area_of_interest_list_present) { + if (area_of_interest_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, area_of_interest_list, 1, 64, true)); } if (location_report_ref_id_to_be_cancelled_present) { @@ -15235,6 +14656,7 @@ SRSASN_CODE location_report_request_type_s::pack(bit_ref& bref) const SRSASN_CODE location_report_request_type_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool area_of_interest_list_present; HANDLE_CODE(bref.unpack(area_of_interest_list_present, 1)); HANDLE_CODE(bref.unpack(location_report_ref_id_to_be_cancelled_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -15258,7 +14680,7 @@ void location_report_request_type_s::to_json(json_writer& j) const j.start_obj(); j.write_str("eventType", event_type.to_string()); j.write_str("reportArea", "cell"); - if (area_of_interest_list_present) { + if (area_of_interest_list.size() > 0) { j.start_array("areaOfInterestList"); for (const auto& e1 : area_of_interest_list) { e1.to_json(j); @@ -16298,7 +15720,7 @@ uint8_t ho_request_ies_o::value_c::types_opts::to_number() const return 0; } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_request_ies_container::ho_request_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -16383,125 +15805,163 @@ SRSASN_CODE ho_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 10; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 29: + } + case 29: { nof_mandatory_ies--; - handov_type.id = c.id; - handov_type.crit = c.crit; - handov_type.value = c.value.handov_type(); + handov_type.id = id; + HANDLE_CODE(handov_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(handov_type.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 110: + } + case 110: { nof_mandatory_ies--; - ue_aggregate_maximum_bit_rate.id = c.id; - ue_aggregate_maximum_bit_rate.crit = c.crit; - ue_aggregate_maximum_bit_rate.value = c.value.ue_aggregate_maximum_bit_rate(); + ue_aggregate_maximum_bit_rate.id = id; + HANDLE_CODE(ue_aggregate_maximum_bit_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_aggregate_maximum_bit_rate.value.unpack(bref)); break; - case 18: + } + case 18: { core_network_assist_info_present = true; - core_network_assist_info.id = c.id; - core_network_assist_info.crit = c.crit; - core_network_assist_info.value = c.value.core_network_assist_info(); + core_network_assist_info.id = id; + HANDLE_CODE(core_network_assist_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(core_network_assist_info.value.unpack(bref)); break; - case 119: + } + case 119: { nof_mandatory_ies--; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 93: + } + case 93: { nof_mandatory_ies--; - security_context.id = c.id; - security_context.crit = c.crit; - security_context.value = c.value.security_context(); + security_context.id = id; + HANDLE_CODE(security_context.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_context.value.unpack(bref)); break; - case 41: + } + case 41: { new_security_context_ind_present = true; - new_security_context_ind.id = c.id; - new_security_context_ind.crit = c.crit; - new_security_context_ind.value = c.value.new_security_context_ind(); + new_security_context_ind.id = id; + HANDLE_CODE(new_security_context_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(new_security_context_ind.value.unpack(bref)); break; - case 37: + } + case 37: { nasc_present = true; - nasc.id = c.id; - nasc.crit = c.crit; - nasc.value = c.value.nasc(); + nasc.id = id; + HANDLE_CODE(nasc.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nasc.value.unpack(bref)); break; - case 73: + } + case 73: { nof_mandatory_ies--; - pdu_session_res_setup_list_ho_req.id = c.id; - pdu_session_res_setup_list_ho_req.crit = c.crit; - pdu_session_res_setup_list_ho_req.value = c.value.pdu_session_res_setup_list_ho_req(); + pdu_session_res_setup_list_ho_req.id = id; + HANDLE_CODE(pdu_session_res_setup_list_ho_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_setup_list_ho_req.value.unpack(bref)); break; - case 0: + } + case 0: { nof_mandatory_ies--; - allowed_nssai.id = c.id; - allowed_nssai.crit = c.crit; - allowed_nssai.value = c.value.allowed_nssai(); + allowed_nssai.id = id; + HANDLE_CODE(allowed_nssai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(allowed_nssai.value.unpack(bref)); break; - case 108: + } + case 108: { trace_activation_present = true; - trace_activation.id = c.id; - trace_activation.crit = c.crit; - trace_activation.value = c.value.trace_activation(); + trace_activation.id = id; + HANDLE_CODE(trace_activation.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(trace_activation.value.unpack(bref)); break; - case 34: + } + case 34: { masked_imeisv_present = true; - masked_imeisv.id = c.id; - masked_imeisv.crit = c.crit; - masked_imeisv.value = c.value.masked_imeisv(); + masked_imeisv.id = id; + HANDLE_CODE(masked_imeisv.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(masked_imeisv.value.unpack(bref)); break; - case 101: + } + case 101: { nof_mandatory_ies--; - source_to_target_transparent_container.id = c.id; - source_to_target_transparent_container.crit = c.crit; - source_to_target_transparent_container.value = c.value.source_to_target_transparent_container(); + source_to_target_transparent_container.id = id; + HANDLE_CODE(source_to_target_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(source_to_target_transparent_container.value.unpack(bref)); break; - case 36: + } + case 36: { mob_restrict_list_present = true; - mob_restrict_list.id = c.id; - mob_restrict_list.crit = c.crit; - mob_restrict_list.value = c.value.mob_restrict_list(); + mob_restrict_list.id = id; + HANDLE_CODE(mob_restrict_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mob_restrict_list.value.unpack(bref)); break; - case 33: + } + case 33: { location_report_request_type_present = true; - location_report_request_type.id = c.id; - location_report_request_type.crit = c.crit; - location_report_request_type.value = c.value.location_report_request_type(); + location_report_request_type.id = id; + HANDLE_CODE(location_report_request_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(location_report_request_type.value.unpack(bref)); break; - case 91: + } + case 91: { rrc_inactive_transition_report_request_present = true; - rrc_inactive_transition_report_request.id = c.id; - rrc_inactive_transition_report_request.crit = c.crit; - rrc_inactive_transition_report_request.value = c.value.rrc_inactive_transition_report_request(); + rrc_inactive_transition_report_request.id = id; + HANDLE_CODE(rrc_inactive_transition_report_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_inactive_transition_report_request.value.unpack(bref)); break; - case 28: + } + case 28: { nof_mandatory_ies--; - guami.id = c.id; - guami.crit = c.crit; - guami.value = c.value.guami(); + guami.id = id; + HANDLE_CODE(guami.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(guami.value.unpack(bref)); break; - case 146: + } + case 146: { redirection_voice_fallback_present = true; - redirection_voice_fallback.id = c.id; - redirection_voice_fallback.crit = c.crit; - redirection_voice_fallback.value = c.value.redirection_voice_fallback(); + redirection_voice_fallback.id = id; + HANDLE_CODE(redirection_voice_fallback.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(redirection_voice_fallback.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -16574,29 +16034,6 @@ void ho_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverRequest ::= SEQUENCE -SRSASN_CODE ho_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // PDUSessionResourceAdmittedItem ::= SEQUENCE SRSASN_CODE pdu_session_res_admitted_item_s::pack(bit_ref& bref) const { @@ -17033,7 +16470,7 @@ const char* ho_request_ack_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 6, value, "ho_request_ack_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_request_ack_ies_container::ho_request_ack_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -17071,47 +16508,59 @@ SRSASN_CODE ho_request_ack_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 53: + } + case 53: { nof_mandatory_ies--; - pdu_session_res_admitted_list.id = c.id; - pdu_session_res_admitted_list.crit = c.crit; - pdu_session_res_admitted_list.value = c.value.pdu_session_res_admitted_list(); + pdu_session_res_admitted_list.id = id; + HANDLE_CODE(pdu_session_res_admitted_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_admitted_list.value.unpack(bref)); break; - case 56: + } + case 56: { pdu_session_res_failed_to_setup_list_ho_ack_present = true; - pdu_session_res_failed_to_setup_list_ho_ack.id = c.id; - pdu_session_res_failed_to_setup_list_ho_ack.crit = c.crit; - pdu_session_res_failed_to_setup_list_ho_ack.value = c.value.pdu_session_res_failed_to_setup_list_ho_ack(); + pdu_session_res_failed_to_setup_list_ho_ack.id = id; + HANDLE_CODE(pdu_session_res_failed_to_setup_list_ho_ack.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_failed_to_setup_list_ho_ack.value.unpack(bref)); break; - case 106: + } + case 106: { nof_mandatory_ies--; - target_to_source_transparent_container.id = c.id; - target_to_source_transparent_container.crit = c.crit; - target_to_source_transparent_container.value = c.value.target_to_source_transparent_container(); + target_to_source_transparent_container.id = id; + HANDLE_CODE(target_to_source_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(target_to_source_transparent_container.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -17144,29 +16593,6 @@ void ho_request_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverRequestAcknowledge ::= SEQUENCE -SRSASN_CODE ho_request_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_request_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_request_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // ConfidentialityProtectionResult ::= ENUMERATED const char* confidentiality_protection_result_opts::to_string() const { @@ -17331,9 +16757,9 @@ SRSASN_CODE ho_request_ack_transfer_s::pack(bit_ref& bref) const bref.pack(ext, 1); HANDLE_CODE(bref.pack(dlforwarding_up_tnl_info_present, 1)); HANDLE_CODE(bref.pack(security_result_present, 1)); - HANDLE_CODE(bref.pack(qos_flow_failed_to_setup_list_present, 1)); - HANDLE_CODE(bref.pack(data_forwarding_resp_drb_list_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(qos_flow_failed_to_setup_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(data_forwarding_resp_drb_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); HANDLE_CODE(dl_ngu_up_tnl_info.pack(bref)); if (dlforwarding_up_tnl_info_present) { @@ -17343,13 +16769,13 @@ SRSASN_CODE ho_request_ack_transfer_s::pack(bit_ref& bref) const HANDLE_CODE(security_result.pack(bref)); } HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_setup_resp_list, 1, 64, true)); - if (qos_flow_failed_to_setup_list_present) { + if (qos_flow_failed_to_setup_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_failed_to_setup_list, 1, 64, true)); } - if (data_forwarding_resp_drb_list_present) { + if (data_forwarding_resp_drb_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, data_forwarding_resp_drb_list, 1, 32, true)); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -17360,8 +16786,11 @@ SRSASN_CODE ho_request_ack_transfer_s::unpack(cbit_ref& bref) bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(dlforwarding_up_tnl_info_present, 1)); HANDLE_CODE(bref.unpack(security_result_present, 1)); + bool qos_flow_failed_to_setup_list_present; HANDLE_CODE(bref.unpack(qos_flow_failed_to_setup_list_present, 1)); + bool data_forwarding_resp_drb_list_present; HANDLE_CODE(bref.unpack(data_forwarding_resp_drb_list_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); HANDLE_CODE(dl_ngu_up_tnl_info.unpack(bref)); @@ -17402,21 +16831,21 @@ void ho_request_ack_transfer_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (qos_flow_failed_to_setup_list_present) { + if (qos_flow_failed_to_setup_list.size() > 0) { j.start_array("qosFlowFailedToSetupList"); for (const auto& e1 : qos_flow_failed_to_setup_list) { e1.to_json(j); } j.end_array(); } - if (data_forwarding_resp_drb_list_present) { + if (data_forwarding_resp_drb_list.size() > 0) { j.start_array("dataForwardingResponseDRBList"); for (const auto& e1 : data_forwarding_resp_drb_list) { e1.to_json(j); } j.end_array(); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -18095,7 +17524,7 @@ const char* ho_required_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 8, value, "ho_required_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_required_ies_container::ho_required_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -18134,59 +17563,75 @@ SRSASN_CODE ho_required_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 7; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 29: + } + case 29: { nof_mandatory_ies--; - handov_type.id = c.id; - handov_type.crit = c.crit; - handov_type.value = c.value.handov_type(); + handov_type.id = id; + HANDLE_CODE(handov_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(handov_type.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 105: + } + case 105: { nof_mandatory_ies--; - target_id.id = c.id; - target_id.crit = c.crit; - target_id.value = c.value.target_id(); + target_id.id = id; + HANDLE_CODE(target_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(target_id.value.unpack(bref)); break; - case 22: + } + case 22: { direct_forwarding_path_availability_present = true; - direct_forwarding_path_availability.id = c.id; - direct_forwarding_path_availability.crit = c.crit; - direct_forwarding_path_availability.value = c.value.direct_forwarding_path_availability(); + direct_forwarding_path_availability.id = id; + HANDLE_CODE(direct_forwarding_path_availability.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(direct_forwarding_path_availability.value.unpack(bref)); break; - case 61: + } + case 61: { nof_mandatory_ies--; - pdu_session_res_list_ho_rqd.id = c.id; - pdu_session_res_list_ho_rqd.crit = c.crit; - pdu_session_res_list_ho_rqd.value = c.value.pdu_session_res_list_ho_rqd(); + pdu_session_res_list_ho_rqd.id = id; + HANDLE_CODE(pdu_session_res_list_ho_rqd.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_list_ho_rqd.value.unpack(bref)); break; - case 101: + } + case 101: { nof_mandatory_ies--; - source_to_target_transparent_container.id = c.id; - source_to_target_transparent_container.crit = c.crit; - source_to_target_transparent_container.value = c.value.source_to_target_transparent_container(); + source_to_target_transparent_container.id = id; + HANDLE_CODE(source_to_target_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(source_to_target_transparent_container.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -18221,29 +17666,6 @@ void ho_required_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverRequired ::= SEQUENCE -SRSASN_CODE ho_required_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_required_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_required_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverRequiredTransfer ::= SEQUENCE SRSASN_CODE ho_required_transfer_s::pack(bit_ref& bref) const { @@ -18811,7 +18233,7 @@ const char* init_context_setup_fail_ies_o::value_c::types_opts::to_string() cons return convert_enum_idx(options, 5, value, "init_context_setup_fail_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; init_context_setup_fail_ies_container::init_context_setup_fail_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -18847,41 +18269,51 @@ SRSASN_CODE init_context_setup_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 132: + } + case 132: { pdu_session_res_failed_to_setup_list_cxt_fail_present = true; - pdu_session_res_failed_to_setup_list_cxt_fail.id = c.id; - pdu_session_res_failed_to_setup_list_cxt_fail.crit = c.crit; - pdu_session_res_failed_to_setup_list_cxt_fail.value = c.value.pdu_session_res_failed_to_setup_list_cxt_fail(); + pdu_session_res_failed_to_setup_list_cxt_fail.id = id; + HANDLE_CODE(pdu_session_res_failed_to_setup_list_cxt_fail.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_failed_to_setup_list_cxt_fail.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -18912,38 +18344,15 @@ void init_context_setup_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// InitialContextSetupFailure ::= SEQUENCE -SRSASN_CODE init_context_setup_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE init_context_setup_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void init_context_setup_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // PDUSessionResourceSetupItemCxtReq ::= SEQUENCE SRSASN_CODE pdu_session_res_setup_item_cxt_req_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(nas_pdu_present, 1)); + HANDLE_CODE(bref.pack(nas_pdu.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(pack_integer(bref, pdu_session_id, (uint16_t)0u, (uint16_t)255u, false, true)); - if (nas_pdu_present) { + if (nas_pdu.size() > 0) { HANDLE_CODE(nas_pdu.pack(bref)); } HANDLE_CODE(s_nssai.pack(bref)); @@ -18957,6 +18366,7 @@ SRSASN_CODE pdu_session_res_setup_item_cxt_req_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_setup_item_cxt_req_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool nas_pdu_present; HANDLE_CODE(bref.unpack(nas_pdu_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -18976,7 +18386,7 @@ void pdu_session_res_setup_item_cxt_req_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("pDUSessionID", pdu_session_id); - if (nas_pdu_present) { + if (nas_pdu.size() > 0) { j.write_str("nAS-PDU", nas_pdu.to_string()); } j.write_fieldname("s-NSSAI"); @@ -18993,14 +18403,14 @@ void pdu_session_res_setup_item_cxt_req_s::to_json(json_writer& j) const SRSASN_CODE ue_radio_cap_for_paging_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(ueradio_cap_for_paging_of_nr_present, 1)); - HANDLE_CODE(bref.pack(ueradio_cap_for_paging_of_eutra_present, 1)); + HANDLE_CODE(bref.pack(ueradio_cap_for_paging_of_nr.size() > 0, 1)); + HANDLE_CODE(bref.pack(ueradio_cap_for_paging_of_eutra.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); - if (ueradio_cap_for_paging_of_nr_present) { + if (ueradio_cap_for_paging_of_nr.size() > 0) { HANDLE_CODE(ueradio_cap_for_paging_of_nr.pack(bref)); } - if (ueradio_cap_for_paging_of_eutra_present) { + if (ueradio_cap_for_paging_of_eutra.size() > 0) { HANDLE_CODE(ueradio_cap_for_paging_of_eutra.pack(bref)); } if (ie_exts_present) { @@ -19012,7 +18422,9 @@ SRSASN_CODE ue_radio_cap_for_paging_s::pack(bit_ref& bref) const SRSASN_CODE ue_radio_cap_for_paging_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool ueradio_cap_for_paging_of_nr_present; HANDLE_CODE(bref.unpack(ueradio_cap_for_paging_of_nr_present, 1)); + bool ueradio_cap_for_paging_of_eutra_present; HANDLE_CODE(bref.unpack(ueradio_cap_for_paging_of_eutra_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -19031,10 +18443,10 @@ SRSASN_CODE ue_radio_cap_for_paging_s::unpack(cbit_ref& bref) void ue_radio_cap_for_paging_s::to_json(json_writer& j) const { j.start_obj(); - if (ueradio_cap_for_paging_of_nr_present) { + if (ueradio_cap_for_paging_of_nr.size() > 0) { j.write_str("uERadioCapabilityForPagingOfNR", ueradio_cap_for_paging_of_nr.to_string()); } - if (ueradio_cap_for_paging_of_eutra_present) { + if (ueradio_cap_for_paging_of_eutra.size() > 0) { j.write_str("uERadioCapabilityForPagingOfEUTRA", ueradio_cap_for_paging_of_eutra.to_string()); } if (ie_exts_present) { @@ -19947,7 +19359,7 @@ const char* init_context_setup_request_ies_o::value_c::types_opts::to_string() c return convert_enum_idx(options, 20, value, "init_context_setup_request_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; init_context_setup_request_ies_container::init_context_setup_request_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -20049,131 +19461,171 @@ SRSASN_CODE init_context_setup_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 6; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 48: + } + case 48: { old_amf_present = true; - old_amf.id = c.id; - old_amf.crit = c.crit; - old_amf.value = c.value.old_amf(); + old_amf.id = id; + HANDLE_CODE(old_amf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(old_amf.value.unpack(bref)); break; - case 110: + } + case 110: { ue_aggregate_maximum_bit_rate_present = true; - ue_aggregate_maximum_bit_rate.id = c.id; - ue_aggregate_maximum_bit_rate.crit = c.crit; - ue_aggregate_maximum_bit_rate.value = c.value.ue_aggregate_maximum_bit_rate(); + ue_aggregate_maximum_bit_rate.id = id; + HANDLE_CODE(ue_aggregate_maximum_bit_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_aggregate_maximum_bit_rate.value.unpack(bref)); break; - case 18: + } + case 18: { core_network_assist_info_present = true; - core_network_assist_info.id = c.id; - core_network_assist_info.crit = c.crit; - core_network_assist_info.value = c.value.core_network_assist_info(); + core_network_assist_info.id = id; + HANDLE_CODE(core_network_assist_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(core_network_assist_info.value.unpack(bref)); break; - case 28: + } + case 28: { nof_mandatory_ies--; - guami.id = c.id; - guami.crit = c.crit; - guami.value = c.value.guami(); + guami.id = id; + HANDLE_CODE(guami.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(guami.value.unpack(bref)); break; - case 71: + } + case 71: { pdu_session_res_setup_list_cxt_req_present = true; - pdu_session_res_setup_list_cxt_req.id = c.id; - pdu_session_res_setup_list_cxt_req.crit = c.crit; - pdu_session_res_setup_list_cxt_req.value = c.value.pdu_session_res_setup_list_cxt_req(); + pdu_session_res_setup_list_cxt_req.id = id; + HANDLE_CODE(pdu_session_res_setup_list_cxt_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_setup_list_cxt_req.value.unpack(bref)); break; - case 0: + } + case 0: { nof_mandatory_ies--; - allowed_nssai.id = c.id; - allowed_nssai.crit = c.crit; - allowed_nssai.value = c.value.allowed_nssai(); + allowed_nssai.id = id; + HANDLE_CODE(allowed_nssai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(allowed_nssai.value.unpack(bref)); break; - case 119: + } + case 119: { nof_mandatory_ies--; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 94: + } + case 94: { nof_mandatory_ies--; - security_key.id = c.id; - security_key.crit = c.crit; - security_key.value = c.value.security_key(); + security_key.id = id; + HANDLE_CODE(security_key.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_key.value.unpack(bref)); break; - case 108: + } + case 108: { trace_activation_present = true; - trace_activation.id = c.id; - trace_activation.crit = c.crit; - trace_activation.value = c.value.trace_activation(); + trace_activation.id = id; + HANDLE_CODE(trace_activation.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(trace_activation.value.unpack(bref)); break; - case 36: + } + case 36: { mob_restrict_list_present = true; - mob_restrict_list.id = c.id; - mob_restrict_list.crit = c.crit; - mob_restrict_list.value = c.value.mob_restrict_list(); + mob_restrict_list.id = id; + HANDLE_CODE(mob_restrict_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mob_restrict_list.value.unpack(bref)); break; - case 117: + } + case 117: { ue_radio_cap_present = true; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; - case 31: + } + case 31: { idx_to_rfsp_present = true; - idx_to_rfsp.id = c.id; - idx_to_rfsp.crit = c.crit; - idx_to_rfsp.value = c.value.idx_to_rfsp(); + idx_to_rfsp.id = id; + HANDLE_CODE(idx_to_rfsp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(idx_to_rfsp.value.unpack(bref)); break; - case 34: + } + case 34: { masked_imeisv_present = true; - masked_imeisv.id = c.id; - masked_imeisv.crit = c.crit; - masked_imeisv.value = c.value.masked_imeisv(); + masked_imeisv.id = id; + HANDLE_CODE(masked_imeisv.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(masked_imeisv.value.unpack(bref)); break; - case 38: + } + case 38: { nas_pdu_present = true; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 24: + } + case 24: { emergency_fallback_ind_present = true; - emergency_fallback_ind.id = c.id; - emergency_fallback_ind.crit = c.crit; - emergency_fallback_ind.value = c.value.emergency_fallback_ind(); + emergency_fallback_ind.id = id; + HANDLE_CODE(emergency_fallback_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(emergency_fallback_ind.value.unpack(bref)); break; - case 91: + } + case 91: { rrc_inactive_transition_report_request_present = true; - rrc_inactive_transition_report_request.id = c.id; - rrc_inactive_transition_report_request.crit = c.crit; - rrc_inactive_transition_report_request.value = c.value.rrc_inactive_transition_report_request(); + rrc_inactive_transition_report_request.id = id; + HANDLE_CODE(rrc_inactive_transition_report_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_inactive_transition_report_request.value.unpack(bref)); break; - case 118: + } + case 118: { ue_radio_cap_for_paging_present = true; - ue_radio_cap_for_paging.id = c.id; - ue_radio_cap_for_paging.crit = c.crit; - ue_radio_cap_for_paging.value = c.value.ue_radio_cap_for_paging(); + ue_radio_cap_for_paging.id = id; + HANDLE_CODE(ue_radio_cap_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap_for_paging.value.unpack(bref)); break; - case 146: + } + case 146: { redirection_voice_fallback_present = true; - redirection_voice_fallback.id = c.id; - redirection_voice_fallback.crit = c.crit; - redirection_voice_fallback.value = c.value.redirection_voice_fallback(); + redirection_voice_fallback.id = id; + HANDLE_CODE(redirection_voice_fallback.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(redirection_voice_fallback.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -20258,29 +19710,6 @@ void init_context_setup_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// InitialContextSetupRequest ::= SEQUENCE -SRSASN_CODE init_context_setup_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE init_context_setup_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void init_context_setup_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // PDUSessionResourceFailedToSetupItemCxtRes ::= SEQUENCE SRSASN_CODE pdu_session_res_failed_to_setup_item_cxt_res_s::pack(bit_ref& bref) const { @@ -20680,7 +20109,7 @@ const char* init_context_setup_resp_ies_o::value_c::types_opts::to_string() cons return convert_enum_idx(options, 5, value, "init_context_setup_resp_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; init_context_setup_resp_ies_container::init_context_setup_resp_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -20719,41 +20148,51 @@ SRSASN_CODE init_context_setup_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 72: + } + case 72: { pdu_session_res_setup_list_cxt_res_present = true; - pdu_session_res_setup_list_cxt_res.id = c.id; - pdu_session_res_setup_list_cxt_res.crit = c.crit; - pdu_session_res_setup_list_cxt_res.value = c.value.pdu_session_res_setup_list_cxt_res(); + pdu_session_res_setup_list_cxt_res.id = id; + HANDLE_CODE(pdu_session_res_setup_list_cxt_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_setup_list_cxt_res.value.unpack(bref)); break; - case 55: + } + case 55: { pdu_session_res_failed_to_setup_list_cxt_res_present = true; - pdu_session_res_failed_to_setup_list_cxt_res.id = c.id; - pdu_session_res_failed_to_setup_list_cxt_res.crit = c.crit; - pdu_session_res_failed_to_setup_list_cxt_res.value = c.value.pdu_session_res_failed_to_setup_list_cxt_res(); + pdu_session_res_failed_to_setup_list_cxt_res.id = id; + HANDLE_CODE(pdu_session_res_failed_to_setup_list_cxt_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_failed_to_setup_list_cxt_res.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -20786,29 +20225,6 @@ void init_context_setup_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// InitialContextSetupResponse ::= SEQUENCE -SRSASN_CODE init_context_setup_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE init_context_setup_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void init_context_setup_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // RRCEstablishmentCause ::= ENUMERATED const char* rrcestablishment_cause_opts::to_string() const { @@ -21265,7 +20681,7 @@ uint8_t init_ue_msg_ies_o::value_c::types_opts::to_number() const return map_enum_number(options, 1, value, "init_ue_msg_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; init_ue_msg_ies_container::init_ue_msg_ies_container() : ran_ue_ngap_id(85, crit_e::reject), @@ -21313,59 +20729,75 @@ SRSASN_CODE init_ue_msg_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 85: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 38: + } + case 38: { nof_mandatory_ies--; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 121: + } + case 121: { nof_mandatory_ies--; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 90: + } + case 90: { nof_mandatory_ies--; - rrcestablishment_cause.id = c.id; - rrcestablishment_cause.crit = c.crit; - rrcestablishment_cause.value = c.value.rrcestablishment_cause(); + rrcestablishment_cause.id = id; + HANDLE_CODE(rrcestablishment_cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrcestablishment_cause.value.unpack(bref)); break; - case 26: + } + case 26: { five_g_s_tmsi_present = true; - five_g_s_tmsi.id = c.id; - five_g_s_tmsi.crit = c.crit; - five_g_s_tmsi.value = c.value.five_g_s_tmsi(); + five_g_s_tmsi.id = id; + HANDLE_CODE(five_g_s_tmsi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(five_g_s_tmsi.value.unpack(bref)); break; - case 3: + } + case 3: { amf_set_id_present = true; - amf_set_id.id = c.id; - amf_set_id.crit = c.crit; - amf_set_id.value = c.value.amf_set_id(); + amf_set_id.id = id; + HANDLE_CODE(amf_set_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_set_id.value.unpack(bref)); break; - case 112: + } + case 112: { ue_context_request_present = true; - ue_context_request.id = c.id; - ue_context_request.crit = c.crit; - ue_context_request.value = c.value.ue_context_request(); + ue_context_request.id = id; + HANDLE_CODE(ue_context_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_context_request.value.unpack(bref)); break; - case 0: + } + case 0: { allowed_nssai_present = true; - allowed_nssai.id = c.id; - allowed_nssai.crit = c.crit; - allowed_nssai.value = c.value.allowed_nssai(); + allowed_nssai.id = id; + HANDLE_CODE(allowed_nssai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(allowed_nssai.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -21406,37 +20838,6 @@ void init_ue_msg_ies_container::to_json(json_writer& j) const j.end_obj(); } -// InitialUEMessage ::= SEQUENCE -SRSASN_CODE init_ue_msg_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - bref.align_bytes_zero(); - - return SRSASN_SUCCESS; -} -SRSASN_CODE init_ue_msg_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - bref.align_bytes(); - - return SRSASN_SUCCESS; -} -void init_ue_msg_s::to_json(json_writer& j) const -{ - j.start_array(); - j.start_obj(); - j.start_obj("InitialUEMessage"); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); - j.end_obj(); - j.end_array(); -} - // OverloadAction ::= ENUMERATED const char* overload_action_opts::to_string() const { @@ -22093,10 +21494,10 @@ void pdu_session_res_failed_to_setup_item_su_res_s::to_json(json_writer& j) cons SRSASN_CODE pdu_session_res_item_cxt_rel_cpl_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, pdu_session_id, (uint16_t)0u, (uint16_t)255u, false, true)); - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -22105,6 +21506,7 @@ SRSASN_CODE pdu_session_res_item_cxt_rel_cpl_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_item_cxt_rel_cpl_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); HANDLE_CODE(unpack_integer(pdu_session_id, bref, (uint16_t)0u, (uint16_t)255u, false, true)); @@ -22118,7 +21520,7 @@ void pdu_session_res_item_cxt_rel_cpl_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("pDUSessionID", pdu_session_id); - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -22242,15 +21644,15 @@ void pdu_session_res_modify_item_mod_ind_s::to_json(json_writer& j) const SRSASN_CODE pdu_session_res_modify_item_mod_req_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(nas_pdu_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(nas_pdu.size() > 0, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, pdu_session_id, (uint16_t)0u, (uint16_t)255u, false, true)); - if (nas_pdu_present) { + if (nas_pdu.size() > 0) { HANDLE_CODE(nas_pdu.pack(bref)); } HANDLE_CODE(pdu_session_res_modify_request_transfer.pack(bref)); - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -22259,7 +21661,9 @@ SRSASN_CODE pdu_session_res_modify_item_mod_req_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_modify_item_mod_req_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool nas_pdu_present; HANDLE_CODE(bref.unpack(nas_pdu_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); HANDLE_CODE(unpack_integer(pdu_session_id, bref, (uint16_t)0u, (uint16_t)255u, false, true)); @@ -22277,11 +21681,11 @@ void pdu_session_res_modify_item_mod_req_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("pDUSessionID", pdu_session_id); - if (nas_pdu_present) { + if (nas_pdu.size() > 0) { j.write_str("nAS-PDU", nas_pdu.to_string()); } j.write_str("pDUSessionResourceModifyRequestTransfer", pdu_session_res_modify_request_transfer.to_string()); - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -22564,11 +21968,11 @@ void pdu_session_res_secondary_ratusage_item_s::to_json(json_writer& j) const SRSASN_CODE pdu_session_res_setup_item_su_req_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(pdu_session_nas_pdu_present, 1)); + HANDLE_CODE(bref.pack(pdu_session_nas_pdu.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(pack_integer(bref, pdu_session_id, (uint16_t)0u, (uint16_t)255u, false, true)); - if (pdu_session_nas_pdu_present) { + if (pdu_session_nas_pdu.size() > 0) { HANDLE_CODE(pdu_session_nas_pdu.pack(bref)); } HANDLE_CODE(s_nssai.pack(bref)); @@ -22582,6 +21986,7 @@ SRSASN_CODE pdu_session_res_setup_item_su_req_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_setup_item_su_req_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool pdu_session_nas_pdu_present; HANDLE_CODE(bref.unpack(pdu_session_nas_pdu_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -22601,7 +22006,7 @@ void pdu_session_res_setup_item_su_req_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("pDUSessionID", pdu_session_id); - if (pdu_session_nas_pdu_present) { + if (pdu_session_nas_pdu.size() > 0) { j.write_str("pDUSessionNAS-PDU", pdu_session_nas_pdu.to_string()); } j.write_fieldname("s-NSSAI"); @@ -38799,7 +38204,7 @@ const char* write_replace_warning_resp_ies_o::value_c::types_opts::to_string() c return convert_enum_idx(options, 4, value, "write_replace_warning_resp_ies_o::value_c::types"); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; location_report_ies_container::location_report_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -38837,47 +38242,59 @@ SRSASN_CODE location_report_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 121: + } + case 121: { nof_mandatory_ies--; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 116: + } + case 116: { ue_presence_in_area_of_interest_list_present = true; - ue_presence_in_area_of_interest_list.id = c.id; - ue_presence_in_area_of_interest_list.crit = c.crit; - ue_presence_in_area_of_interest_list.value = c.value.ue_presence_in_area_of_interest_list(); + ue_presence_in_area_of_interest_list.id = id; + HANDLE_CODE(ue_presence_in_area_of_interest_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_presence_in_area_of_interest_list.value.unpack(bref)); break; - case 33: + } + case 33: { nof_mandatory_ies--; - location_report_request_type.id = c.id; - location_report_request_type.crit = c.crit; - location_report_request_type.value = c.value.location_report_request_type(); + location_report_request_type.id = id; + HANDLE_CODE(location_report_request_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(location_report_request_type.value.unpack(bref)); break; - case 149: + } + case 149: { ps_cell_info_present = true; - ps_cell_info.id = c.id; - ps_cell_info.crit = c.crit; - ps_cell_info.value = c.value.ps_cell_info(); + ps_cell_info.id = id; + HANDLE_CODE(ps_cell_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ps_cell_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -38910,30 +38327,7 @@ void location_report_ies_container::to_json(json_writer& j) const j.end_obj(); } -// LocationReport ::= SEQUENCE -SRSASN_CODE location_report_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE location_report_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void location_report_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; location_report_ctrl_ies_container::location_report_ctrl_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -38959,29 +38353,35 @@ SRSASN_CODE location_report_ctrl_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 33: + } + case 33: { nof_mandatory_ies--; - location_report_request_type.id = c.id; - location_report_request_type.crit = c.crit; - location_report_request_type.value = c.value.location_report_request_type(); + location_report_request_type.id = id; + HANDLE_CODE(location_report_request_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(location_report_request_type.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39004,30 +38404,7 @@ void location_report_ctrl_ies_container::to_json(json_writer& j) const j.end_obj(); } -// LocationReportingControl ::= SEQUENCE -SRSASN_CODE location_report_ctrl_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE location_report_ctrl_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void location_report_ctrl_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; location_report_fail_ind_ies_container::location_report_fail_ind_ies_container() : amf_ue_ngap_id(10, crit_e::reject), ran_ue_ngap_id(85, crit_e::reject), cause(15, crit_e::ignore) @@ -39051,29 +38428,35 @@ SRSASN_CODE location_report_fail_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39096,30 +38479,7 @@ void location_report_fail_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// LocationReportingFailureIndication ::= SEQUENCE -SRSASN_CODE location_report_fail_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE location_report_fail_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void location_report_fail_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; nas_non_delivery_ind_ies_container::nas_non_delivery_ind_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -39147,35 +38507,43 @@ SRSASN_CODE nas_non_delivery_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 38: + } + case 38: { nof_mandatory_ies--; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39200,30 +38568,7 @@ void nas_non_delivery_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// NASNonDeliveryIndication ::= SEQUENCE -SRSASN_CODE nas_non_delivery_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE nas_non_delivery_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void nas_non_delivery_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ng_reset_ies_container::ng_reset_ies_container() : cause(15, crit_e::ignore), reset_type(88, crit_e::reject) {} SRSASN_CODE ng_reset_ies_container::pack(bit_ref& bref) const @@ -39244,23 +38589,27 @@ SRSASN_CODE ng_reset_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 15: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 88: + } + case 88: { nof_mandatory_ies--; - reset_type.id = c.id; - reset_type.crit = c.crit; - reset_type.value = c.value.reset_type(); + reset_type.id = id; + HANDLE_CODE(reset_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(reset_type.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39281,30 +38630,7 @@ void ng_reset_ies_container::to_json(json_writer& j) const j.end_obj(); } -// NGReset ::= SEQUENCE -SRSASN_CODE ng_reset_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ng_reset_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ng_reset_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ng_reset_ack_ies_container::ng_reset_ack_ies_container() : ue_associated_lc_ng_conn_list(111, crit_e::ignore), crit_diagnostics(19, crit_e::ignore) @@ -39331,23 +38657,27 @@ SRSASN_CODE ng_reset_ack_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 111: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 111: { ue_associated_lc_ng_conn_list_present = true; - ue_associated_lc_ng_conn_list.id = c.id; - ue_associated_lc_ng_conn_list.crit = c.crit; - ue_associated_lc_ng_conn_list.value = c.value.ue_associated_lc_ng_conn_list(); + ue_associated_lc_ng_conn_list.id = id; + HANDLE_CODE(ue_associated_lc_ng_conn_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_associated_lc_ng_conn_list.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39368,30 +38698,7 @@ void ng_reset_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// NGResetAcknowledge ::= SEQUENCE -SRSASN_CODE ng_reset_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ng_reset_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ng_reset_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ng_setup_fail_ies_container::ng_setup_fail_ies_container() : cause(15, crit_e::ignore), time_to_wait(107, crit_e::ignore), crit_diagnostics(19, crit_e::ignore) @@ -39421,29 +38728,35 @@ SRSASN_CODE ng_setup_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 1; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 15: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 107: + } + case 107: { time_to_wait_present = true; - time_to_wait.id = c.id; - time_to_wait.crit = c.crit; - time_to_wait.value = c.value.time_to_wait(); + time_to_wait.id = id; + HANDLE_CODE(time_to_wait.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_to_wait.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39470,30 +38783,7 @@ void ng_setup_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// NGSetupFailure ::= SEQUENCE -SRSASN_CODE ng_setup_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ng_setup_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ng_setup_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ng_setup_request_ies_container::ng_setup_request_ies_container() : global_ran_node_id(27, crit_e::reject), @@ -39529,41 +38819,51 @@ SRSASN_CODE ng_setup_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 27: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 27: { nof_mandatory_ies--; - global_ran_node_id.id = c.id; - global_ran_node_id.crit = c.crit; - global_ran_node_id.value = c.value.global_ran_node_id(); + global_ran_node_id.id = id; + HANDLE_CODE(global_ran_node_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(global_ran_node_id.value.unpack(bref)); break; - case 82: + } + case 82: { ran_node_name_present = true; - ran_node_name.id = c.id; - ran_node_name.crit = c.crit; - ran_node_name.value = c.value.ran_node_name(); + ran_node_name.id = id; + HANDLE_CODE(ran_node_name.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_node_name.value.unpack(bref)); break; - case 102: + } + case 102: { nof_mandatory_ies--; - supported_ta_list.id = c.id; - supported_ta_list.crit = c.crit; - supported_ta_list.value = c.value.supported_ta_list(); + supported_ta_list.id = id; + HANDLE_CODE(supported_ta_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(supported_ta_list.value.unpack(bref)); break; - case 21: + } + case 21: { nof_mandatory_ies--; - default_paging_drx.id = c.id; - default_paging_drx.crit = c.crit; - default_paging_drx.value = c.value.default_paging_drx(); + default_paging_drx.id = id; + HANDLE_CODE(default_paging_drx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(default_paging_drx.value.unpack(bref)); break; - case 147: + } + case 147: { ue_retention_info_present = true; - ue_retention_info.id = c.id; - ue_retention_info.crit = c.crit; - ue_retention_info.value = c.value.ue_retention_info(); + ue_retention_info.id = id; + HANDLE_CODE(ue_retention_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_retention_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39594,30 +38894,7 @@ void ng_setup_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// NGSetupRequest ::= SEQUENCE -SRSASN_CODE ng_setup_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ng_setup_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ng_setup_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ng_setup_resp_ies_container::ng_setup_resp_ies_container() : amf_name(1, crit_e::reject), @@ -39655,47 +38932,59 @@ SRSASN_CODE ng_setup_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 1: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 1: { nof_mandatory_ies--; - amf_name.id = c.id; - amf_name.crit = c.crit; - amf_name.value = c.value.amf_name(); + amf_name.id = id; + HANDLE_CODE(amf_name.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_name.value.unpack(bref)); break; - case 96: + } + case 96: { nof_mandatory_ies--; - served_guami_list.id = c.id; - served_guami_list.crit = c.crit; - served_guami_list.value = c.value.served_guami_list(); + served_guami_list.id = id; + HANDLE_CODE(served_guami_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(served_guami_list.value.unpack(bref)); break; - case 86: + } + case 86: { nof_mandatory_ies--; - relative_amf_capacity.id = c.id; - relative_amf_capacity.crit = c.crit; - relative_amf_capacity.value = c.value.relative_amf_capacity(); + relative_amf_capacity.id = id; + HANDLE_CODE(relative_amf_capacity.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(relative_amf_capacity.value.unpack(bref)); break; - case 80: + } + case 80: { nof_mandatory_ies--; - plmn_support_list.id = c.id; - plmn_support_list.crit = c.crit; - plmn_support_list.value = c.value.plmn_support_list(); + plmn_support_list.id = id; + HANDLE_CODE(plmn_support_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(plmn_support_list.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 147: + } + case 147: { ue_retention_info_present = true; - ue_retention_info.id = c.id; - ue_retention_info.crit = c.crit; - ue_retention_info.value = c.value.ue_retention_info(); + ue_retention_info.id = id; + HANDLE_CODE(ue_retention_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_retention_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39728,30 +39017,7 @@ void ng_setup_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// NGSetupResponse ::= SEQUENCE -SRSASN_CODE ng_setup_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ng_setup_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ng_setup_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; overload_start_ies_container::overload_start_ies_container() : amf_overload_resp(2, crit_e::reject), @@ -39784,29 +39050,35 @@ SRSASN_CODE overload_start_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 2: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 2: { amf_overload_resp_present = true; - amf_overload_resp.id = c.id; - amf_overload_resp.crit = c.crit; - amf_overload_resp.value = c.value.amf_overload_resp(); + amf_overload_resp.id = id; + HANDLE_CODE(amf_overload_resp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_overload_resp.value.unpack(bref)); break; - case 9: + } + case 9: { amf_traffic_load_reduction_ind_present = true; - amf_traffic_load_reduction_ind.id = c.id; - amf_traffic_load_reduction_ind.crit = c.crit; - amf_traffic_load_reduction_ind.value = c.value.amf_traffic_load_reduction_ind(); + amf_traffic_load_reduction_ind.id = id; + HANDLE_CODE(amf_traffic_load_reduction_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_traffic_load_reduction_ind.value.unpack(bref)); break; - case 49: + } + case 49: { overload_start_nssai_list_present = true; - overload_start_nssai_list.id = c.id; - overload_start_nssai_list.crit = c.crit; - overload_start_nssai_list.value = c.value.overload_start_nssai_list(); + overload_start_nssai_list.id = id; + HANDLE_CODE(overload_start_nssai_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(overload_start_nssai_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -39831,75 +39103,7 @@ void overload_start_ies_container::to_json(json_writer& j) const j.end_obj(); } -// OverloadStart ::= SEQUENCE -SRSASN_CODE overload_start_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE overload_start_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void overload_start_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -SRSASN_CODE protocol_ie_container_empty_l::pack(bit_ref& bref) const -{ - uint32_t nof_ies = 0; - pack_length(bref, nof_ies, 0u, 65535u, true); - - return SRSASN_SUCCESS; -} -SRSASN_CODE protocol_ie_container_empty_l::unpack(cbit_ref& bref) -{ - uint32_t nof_ies = 0; - unpack_length(nof_ies, bref, 0u, 65535u, true); - if (nof_ies > 0) { - return SRSASN_ERROR_DECODE_FAIL; - } - return SRSASN_SUCCESS; -} -void protocol_ie_container_empty_l::to_json(json_writer& j) const -{ - j.start_obj(); - j.end_obj(); -} - -// OverloadStop ::= SEQUENCE -SRSASN_CODE overload_stop_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE overload_stop_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void overload_stop_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_modify_confirm_ies_container::pdu_session_res_modify_confirm_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -39935,41 +39139,51 @@ SRSASN_CODE pdu_session_res_modify_confirm_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 62: + } + case 62: { nof_mandatory_ies--; - pdu_session_res_modify_list_mod_cfm.id = c.id; - pdu_session_res_modify_list_mod_cfm.crit = c.crit; - pdu_session_res_modify_list_mod_cfm.value = c.value.pdu_session_res_modify_list_mod_cfm(); + pdu_session_res_modify_list_mod_cfm.id = id; + HANDLE_CODE(pdu_session_res_modify_list_mod_cfm.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_modify_list_mod_cfm.value.unpack(bref)); break; - case 131: + } + case 131: { pdu_session_res_failed_to_modify_list_mod_cfm_present = true; - pdu_session_res_failed_to_modify_list_mod_cfm.id = c.id; - pdu_session_res_failed_to_modify_list_mod_cfm.crit = c.crit; - pdu_session_res_failed_to_modify_list_mod_cfm.value = c.value.pdu_session_res_failed_to_modify_list_mod_cfm(); + pdu_session_res_failed_to_modify_list_mod_cfm.id = id; + HANDLE_CODE(pdu_session_res_failed_to_modify_list_mod_cfm.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_failed_to_modify_list_mod_cfm.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40000,30 +39214,7 @@ void pdu_session_res_modify_confirm_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceModifyConfirm ::= SEQUENCE -SRSASN_CODE pdu_session_res_modify_confirm_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_modify_confirm_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_modify_confirm_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_modify_ind_ies_container::pdu_session_res_modify_ind_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -40049,29 +39240,35 @@ SRSASN_CODE pdu_session_res_modify_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 63: + } + case 63: { nof_mandatory_ies--; - pdu_session_res_modify_list_mod_ind.id = c.id; - pdu_session_res_modify_list_mod_ind.crit = c.crit; - pdu_session_res_modify_list_mod_ind.value = c.value.pdu_session_res_modify_list_mod_ind(); + pdu_session_res_modify_list_mod_ind.id = id; + HANDLE_CODE(pdu_session_res_modify_list_mod_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_modify_list_mod_ind.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40094,30 +39291,7 @@ void pdu_session_res_modify_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceModifyIndication ::= SEQUENCE -SRSASN_CODE pdu_session_res_modify_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_modify_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_modify_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_modify_request_ies_container::pdu_session_res_modify_request_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -40148,35 +39322,43 @@ SRSASN_CODE pdu_session_res_modify_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 83: + } + case 83: { ran_paging_prio_present = true; - ran_paging_prio.id = c.id; - ran_paging_prio.crit = c.crit; - ran_paging_prio.value = c.value.ran_paging_prio(); + ran_paging_prio.id = id; + HANDLE_CODE(ran_paging_prio.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_paging_prio.value.unpack(bref)); break; - case 64: + } + case 64: { nof_mandatory_ies--; - pdu_session_res_modify_list_mod_req.id = c.id; - pdu_session_res_modify_list_mod_req.crit = c.crit; - pdu_session_res_modify_list_mod_req.value = c.value.pdu_session_res_modify_list_mod_req(); + pdu_session_res_modify_list_mod_req.id = id; + HANDLE_CODE(pdu_session_res_modify_list_mod_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_modify_list_mod_req.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40203,30 +39385,7 @@ void pdu_session_res_modify_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceModifyRequest ::= SEQUENCE -SRSASN_CODE pdu_session_res_modify_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_modify_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_modify_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_modify_resp_ies_container::pdu_session_res_modify_resp_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -40270,47 +39429,59 @@ SRSASN_CODE pdu_session_res_modify_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 65: + } + case 65: { pdu_session_res_modify_list_mod_res_present = true; - pdu_session_res_modify_list_mod_res.id = c.id; - pdu_session_res_modify_list_mod_res.crit = c.crit; - pdu_session_res_modify_list_mod_res.value = c.value.pdu_session_res_modify_list_mod_res(); + pdu_session_res_modify_list_mod_res.id = id; + HANDLE_CODE(pdu_session_res_modify_list_mod_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_modify_list_mod_res.value.unpack(bref)); break; - case 54: + } + case 54: { pdu_session_res_failed_to_modify_list_mod_res_present = true; - pdu_session_res_failed_to_modify_list_mod_res.id = c.id; - pdu_session_res_failed_to_modify_list_mod_res.crit = c.crit; - pdu_session_res_failed_to_modify_list_mod_res.value = c.value.pdu_session_res_failed_to_modify_list_mod_res(); + pdu_session_res_failed_to_modify_list_mod_res.id = id; + HANDLE_CODE(pdu_session_res_failed_to_modify_list_mod_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_failed_to_modify_list_mod_res.value.unpack(bref)); break; - case 121: + } + case 121: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40347,30 +39518,7 @@ void pdu_session_res_modify_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceModifyResponse ::= SEQUENCE -SRSASN_CODE pdu_session_res_modify_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_modify_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_modify_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_notify_ies_container::pdu_session_res_notify_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -40409,41 +39557,51 @@ SRSASN_CODE pdu_session_res_notify_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 66: + } + case 66: { pdu_session_res_notify_list_present = true; - pdu_session_res_notify_list.id = c.id; - pdu_session_res_notify_list.crit = c.crit; - pdu_session_res_notify_list.value = c.value.pdu_session_res_notify_list(); + pdu_session_res_notify_list.id = id; + HANDLE_CODE(pdu_session_res_notify_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_notify_list.value.unpack(bref)); break; - case 67: + } + case 67: { pdu_session_res_released_list_not_present = true; - pdu_session_res_released_list_not.id = c.id; - pdu_session_res_released_list_not.crit = c.crit; - pdu_session_res_released_list_not.value = c.value.pdu_session_res_released_list_not(); + pdu_session_res_released_list_not.id = id; + HANDLE_CODE(pdu_session_res_released_list_not.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_released_list_not.value.unpack(bref)); break; - case 121: + } + case 121: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40476,30 +39634,7 @@ void pdu_session_res_notify_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceNotify ::= SEQUENCE -SRSASN_CODE pdu_session_res_notify_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_notify_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_notify_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_release_cmd_ies_container::pdu_session_res_release_cmd_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -40535,41 +39670,51 @@ SRSASN_CODE pdu_session_res_release_cmd_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 83: + } + case 83: { ran_paging_prio_present = true; - ran_paging_prio.id = c.id; - ran_paging_prio.crit = c.crit; - ran_paging_prio.value = c.value.ran_paging_prio(); + ran_paging_prio.id = id; + HANDLE_CODE(ran_paging_prio.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_paging_prio.value.unpack(bref)); break; - case 38: + } + case 38: { nas_pdu_present = true; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 79: + } + case 79: { nof_mandatory_ies--; - pdu_session_res_to_release_list_rel_cmd.id = c.id; - pdu_session_res_to_release_list_rel_cmd.crit = c.crit; - pdu_session_res_to_release_list_rel_cmd.value = c.value.pdu_session_res_to_release_list_rel_cmd(); + pdu_session_res_to_release_list_rel_cmd.id = id; + HANDLE_CODE(pdu_session_res_to_release_list_rel_cmd.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_to_release_list_rel_cmd.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40600,30 +39745,7 @@ void pdu_session_res_release_cmd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceReleaseCommand ::= SEQUENCE -SRSASN_CODE pdu_session_res_release_cmd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_release_cmd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_release_cmd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_release_resp_ies_container::pdu_session_res_release_resp_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -40659,41 +39781,51 @@ SRSASN_CODE pdu_session_res_release_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 70: + } + case 70: { nof_mandatory_ies--; - pdu_session_res_released_list_rel_res.id = c.id; - pdu_session_res_released_list_rel_res.crit = c.crit; - pdu_session_res_released_list_rel_res.value = c.value.pdu_session_res_released_list_rel_res(); + pdu_session_res_released_list_rel_res.id = id; + HANDLE_CODE(pdu_session_res_released_list_rel_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_released_list_rel_res.value.unpack(bref)); break; - case 121: + } + case 121: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40724,30 +39856,7 @@ void pdu_session_res_release_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceReleaseResponse ::= SEQUENCE -SRSASN_CODE pdu_session_res_release_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_release_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_release_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_setup_request_ies_container::pdu_session_res_setup_request_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -40788,47 +39897,59 @@ SRSASN_CODE pdu_session_res_setup_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 83: + } + case 83: { ran_paging_prio_present = true; - ran_paging_prio.id = c.id; - ran_paging_prio.crit = c.crit; - ran_paging_prio.value = c.value.ran_paging_prio(); + ran_paging_prio.id = id; + HANDLE_CODE(ran_paging_prio.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_paging_prio.value.unpack(bref)); break; - case 38: + } + case 38: { nas_pdu_present = true; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 74: + } + case 74: { nof_mandatory_ies--; - pdu_session_res_setup_list_su_req.id = c.id; - pdu_session_res_setup_list_su_req.crit = c.crit; - pdu_session_res_setup_list_su_req.value = c.value.pdu_session_res_setup_list_su_req(); + pdu_session_res_setup_list_su_req.id = id; + HANDLE_CODE(pdu_session_res_setup_list_su_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_setup_list_su_req.value.unpack(bref)); break; - case 110: + } + case 110: { ue_aggregate_maximum_bit_rate_present = true; - ue_aggregate_maximum_bit_rate.id = c.id; - ue_aggregate_maximum_bit_rate.crit = c.crit; - ue_aggregate_maximum_bit_rate.value = c.value.ue_aggregate_maximum_bit_rate(); + ue_aggregate_maximum_bit_rate.id = id; + HANDLE_CODE(ue_aggregate_maximum_bit_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_aggregate_maximum_bit_rate.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40863,30 +39984,7 @@ void pdu_session_res_setup_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceSetupRequest ::= SEQUENCE -SRSASN_CODE pdu_session_res_setup_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_setup_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_setup_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_setup_resp_ies_container::pdu_session_res_setup_resp_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -40925,41 +40023,51 @@ SRSASN_CODE pdu_session_res_setup_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 75: + } + case 75: { pdu_session_res_setup_list_su_res_present = true; - pdu_session_res_setup_list_su_res.id = c.id; - pdu_session_res_setup_list_su_res.crit = c.crit; - pdu_session_res_setup_list_su_res.value = c.value.pdu_session_res_setup_list_su_res(); + pdu_session_res_setup_list_su_res.id = id; + HANDLE_CODE(pdu_session_res_setup_list_su_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_setup_list_su_res.value.unpack(bref)); break; - case 58: + } + case 58: { pdu_session_res_failed_to_setup_list_su_res_present = true; - pdu_session_res_failed_to_setup_list_su_res.id = c.id; - pdu_session_res_failed_to_setup_list_su_res.crit = c.crit; - pdu_session_res_failed_to_setup_list_su_res.value = c.value.pdu_session_res_failed_to_setup_list_su_res(); + pdu_session_res_failed_to_setup_list_su_res.id = id; + HANDLE_CODE(pdu_session_res_failed_to_setup_list_su_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_failed_to_setup_list_su_res.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -40992,30 +40100,7 @@ void pdu_session_res_setup_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PDUSessionResourceSetupResponse ::= SEQUENCE -SRSASN_CODE pdu_session_res_setup_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_setup_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_setup_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pws_cancel_request_ies_container::pws_cancel_request_ies_container() : msg_id(35, crit_e::reject), @@ -41049,35 +40134,43 @@ SRSASN_CODE pws_cancel_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 35: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 35: { nof_mandatory_ies--; - msg_id.id = c.id; - msg_id.crit = c.crit; - msg_id.value = c.value.msg_id(); + msg_id.id = id; + HANDLE_CODE(msg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(msg_id.value.unpack(bref)); break; - case 95: + } + case 95: { nof_mandatory_ies--; - serial_num.id = c.id; - serial_num.crit = c.crit; - serial_num.value = c.value.serial_num(); + serial_num.id = id; + HANDLE_CODE(serial_num.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(serial_num.value.unpack(bref)); break; - case 122: + } + case 122: { warning_area_list_present = true; - warning_area_list.id = c.id; - warning_area_list.crit = c.crit; - warning_area_list.value = c.value.warning_area_list(); + warning_area_list.id = id; + HANDLE_CODE(warning_area_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_area_list.value.unpack(bref)); break; - case 14: + } + case 14: { cancel_all_warning_msgs_present = true; - cancel_all_warning_msgs.id = c.id; - cancel_all_warning_msgs.crit = c.crit; - cancel_all_warning_msgs.value = c.value.cancel_all_warning_msgs(); + cancel_all_warning_msgs.id = id; + HANDLE_CODE(cancel_all_warning_msgs.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cancel_all_warning_msgs.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -41106,30 +40199,7 @@ void pws_cancel_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PWSCancelRequest ::= SEQUENCE -SRSASN_CODE pws_cancel_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pws_cancel_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pws_cancel_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pws_cancel_resp_ies_container::pws_cancel_resp_ies_container() : msg_id(35, crit_e::reject), @@ -41163,35 +40233,43 @@ SRSASN_CODE pws_cancel_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 35: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 35: { nof_mandatory_ies--; - msg_id.id = c.id; - msg_id.crit = c.crit; - msg_id.value = c.value.msg_id(); + msg_id.id = id; + HANDLE_CODE(msg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(msg_id.value.unpack(bref)); break; - case 95: + } + case 95: { nof_mandatory_ies--; - serial_num.id = c.id; - serial_num.crit = c.crit; - serial_num.value = c.value.serial_num(); + serial_num.id = id; + HANDLE_CODE(serial_num.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(serial_num.value.unpack(bref)); break; - case 12: + } + case 12: { broadcast_cancelled_area_list_present = true; - broadcast_cancelled_area_list.id = c.id; - broadcast_cancelled_area_list.crit = c.crit; - broadcast_cancelled_area_list.value = c.value.broadcast_cancelled_area_list(); + broadcast_cancelled_area_list.id = id; + HANDLE_CODE(broadcast_cancelled_area_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(broadcast_cancelled_area_list.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -41220,30 +40298,7 @@ void pws_cancel_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PWSCancelResponse ::= SEQUENCE -SRSASN_CODE pws_cancel_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pws_cancel_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pws_cancel_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pws_fail_ind_ies_container::pws_fail_ind_ies_container() : pws_failed_cell_id_list(81, crit_e::reject), global_ran_node_id(27, crit_e::reject) @@ -41266,23 +40321,27 @@ SRSASN_CODE pws_fail_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 81: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 81: { nof_mandatory_ies--; - pws_failed_cell_id_list.id = c.id; - pws_failed_cell_id_list.crit = c.crit; - pws_failed_cell_id_list.value = c.value.pws_failed_cell_id_list(); + pws_failed_cell_id_list.id = id; + HANDLE_CODE(pws_failed_cell_id_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pws_failed_cell_id_list.value.unpack(bref)); break; - case 27: + } + case 27: { nof_mandatory_ies--; - global_ran_node_id.id = c.id; - global_ran_node_id.crit = c.crit; - global_ran_node_id.value = c.value.global_ran_node_id(); + global_ran_node_id.id = id; + HANDLE_CODE(global_ran_node_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(global_ran_node_id.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -41303,30 +40362,7 @@ void pws_fail_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PWSFailureIndication ::= SEQUENCE -SRSASN_CODE pws_fail_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pws_fail_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pws_fail_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pws_restart_ind_ies_container::pws_restart_ind_ies_container() : cell_id_list_for_restart(16, crit_e::reject), @@ -41357,35 +40393,43 @@ SRSASN_CODE pws_restart_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 16: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 16: { nof_mandatory_ies--; - cell_id_list_for_restart.id = c.id; - cell_id_list_for_restart.crit = c.crit; - cell_id_list_for_restart.value = c.value.cell_id_list_for_restart(); + cell_id_list_for_restart.id = id; + HANDLE_CODE(cell_id_list_for_restart.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cell_id_list_for_restart.value.unpack(bref)); break; - case 27: + } + case 27: { nof_mandatory_ies--; - global_ran_node_id.id = c.id; - global_ran_node_id.crit = c.crit; - global_ran_node_id.value = c.value.global_ran_node_id(); + global_ran_node_id.id = id; + HANDLE_CODE(global_ran_node_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(global_ran_node_id.value.unpack(bref)); break; - case 104: + } + case 104: { nof_mandatory_ies--; - tai_list_for_restart.id = c.id; - tai_list_for_restart.crit = c.crit; - tai_list_for_restart.value = c.value.tai_list_for_restart(); + tai_list_for_restart.id = id; + HANDLE_CODE(tai_list_for_restart.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai_list_for_restart.value.unpack(bref)); break; - case 23: + } + case 23: { emergency_area_id_list_for_restart_present = true; - emergency_area_id_list_for_restart.id = c.id; - emergency_area_id_list_for_restart.crit = c.crit; - emergency_area_id_list_for_restart.value = c.value.emergency_area_id_list_for_restart(); + emergency_area_id_list_for_restart.id = id; + HANDLE_CODE(emergency_area_id_list_for_restart.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(emergency_area_id_list_for_restart.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -41412,30 +40456,7 @@ void pws_restart_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PWSRestartIndication ::= SEQUENCE -SRSASN_CODE pws_restart_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pws_restart_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pws_restart_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; paging_ies_container::paging_ies_container() : ue_paging_id(115, crit_e::ignore), @@ -41484,53 +40505,67 @@ SRSASN_CODE paging_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 115: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 115: { nof_mandatory_ies--; - ue_paging_id.id = c.id; - ue_paging_id.crit = c.crit; - ue_paging_id.value = c.value.ue_paging_id(); + ue_paging_id.id = id; + HANDLE_CODE(ue_paging_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_paging_id.value.unpack(bref)); break; - case 50: + } + case 50: { paging_drx_present = true; - paging_drx.id = c.id; - paging_drx.crit = c.crit; - paging_drx.value = c.value.paging_drx(); + paging_drx.id = id; + HANDLE_CODE(paging_drx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(paging_drx.value.unpack(bref)); break; - case 103: + } + case 103: { nof_mandatory_ies--; - tai_list_for_paging.id = c.id; - tai_list_for_paging.crit = c.crit; - tai_list_for_paging.value = c.value.tai_list_for_paging(); + tai_list_for_paging.id = id; + HANDLE_CODE(tai_list_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai_list_for_paging.value.unpack(bref)); break; - case 52: + } + case 52: { paging_prio_present = true; - paging_prio.id = c.id; - paging_prio.crit = c.crit; - paging_prio.value = c.value.paging_prio(); + paging_prio.id = id; + HANDLE_CODE(paging_prio.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(paging_prio.value.unpack(bref)); break; - case 118: + } + case 118: { ue_radio_cap_for_paging_present = true; - ue_radio_cap_for_paging.id = c.id; - ue_radio_cap_for_paging.crit = c.crit; - ue_radio_cap_for_paging.value = c.value.ue_radio_cap_for_paging(); + ue_radio_cap_for_paging.id = id; + HANDLE_CODE(ue_radio_cap_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap_for_paging.value.unpack(bref)); break; - case 51: + } + case 51: { paging_origin_present = true; - paging_origin.id = c.id; - paging_origin.crit = c.crit; - paging_origin.value = c.value.paging_origin(); + paging_origin.id = id; + HANDLE_CODE(paging_origin.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(paging_origin.value.unpack(bref)); break; - case 11: + } + case 11: { assist_data_for_paging_present = true; - assist_data_for_paging.id = c.id; - assist_data_for_paging.crit = c.crit; - assist_data_for_paging.value = c.value.assist_data_for_paging(); + assist_data_for_paging.id = id; + HANDLE_CODE(assist_data_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(assist_data_for_paging.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -41571,30 +40606,7 @@ void paging_ies_container::to_json(json_writer& j) const j.end_obj(); } -// Paging ::= SEQUENCE -SRSASN_CODE paging_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE paging_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void paging_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; path_switch_request_ies_container::path_switch_request_ies_container() : ran_ue_ngap_id(85, crit_e::reject), @@ -41629,47 +40641,59 @@ SRSASN_CODE path_switch_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 85: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 100: + } + case 100: { nof_mandatory_ies--; - source_amf_ue_ngap_id.id = c.id; - source_amf_ue_ngap_id.crit = c.crit; - source_amf_ue_ngap_id.value = c.value.source_amf_ue_ngap_id(); + source_amf_ue_ngap_id.id = id; + HANDLE_CODE(source_amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(source_amf_ue_ngap_id.value.unpack(bref)); break; - case 121: + } + case 121: { nof_mandatory_ies--; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 119: + } + case 119: { nof_mandatory_ies--; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 76: + } + case 76: { nof_mandatory_ies--; - pdu_session_res_to_be_switched_dl_list.id = c.id; - pdu_session_res_to_be_switched_dl_list.crit = c.crit; - pdu_session_res_to_be_switched_dl_list.value = c.value.pdu_session_res_to_be_switched_dl_list(); + pdu_session_res_to_be_switched_dl_list.id = id; + HANDLE_CODE(pdu_session_res_to_be_switched_dl_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_to_be_switched_dl_list.value.unpack(bref)); break; - case 57: + } + case 57: { pdu_session_res_failed_to_setup_list_ps_req_present = true; - pdu_session_res_failed_to_setup_list_ps_req.id = c.id; - pdu_session_res_failed_to_setup_list_ps_req.crit = c.crit; - pdu_session_res_failed_to_setup_list_ps_req.value = c.value.pdu_session_res_failed_to_setup_list_ps_req(); + pdu_session_res_failed_to_setup_list_ps_req.id = id; + HANDLE_CODE(pdu_session_res_failed_to_setup_list_ps_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_failed_to_setup_list_ps_req.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -41700,30 +40724,7 @@ void path_switch_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PathSwitchRequest ::= SEQUENCE -SRSASN_CODE path_switch_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE path_switch_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void path_switch_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; path_switch_request_ack_ies_container::path_switch_request_ack_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -41788,83 +40789,107 @@ SRSASN_CODE path_switch_request_ack_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 119: + } + case 119: { ue_security_cap_present = true; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 93: + } + case 93: { nof_mandatory_ies--; - security_context.id = c.id; - security_context.crit = c.crit; - security_context.value = c.value.security_context(); + security_context.id = id; + HANDLE_CODE(security_context.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_context.value.unpack(bref)); break; - case 41: + } + case 41: { new_security_context_ind_present = true; - new_security_context_ind.id = c.id; - new_security_context_ind.crit = c.crit; - new_security_context_ind.value = c.value.new_security_context_ind(); + new_security_context_ind.id = id; + HANDLE_CODE(new_security_context_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(new_security_context_ind.value.unpack(bref)); break; - case 77: + } + case 77: { nof_mandatory_ies--; - pdu_session_res_switched_list.id = c.id; - pdu_session_res_switched_list.crit = c.crit; - pdu_session_res_switched_list.value = c.value.pdu_session_res_switched_list(); + pdu_session_res_switched_list.id = id; + HANDLE_CODE(pdu_session_res_switched_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_switched_list.value.unpack(bref)); break; - case 68: + } + case 68: { pdu_session_res_released_list_ps_ack_present = true; - pdu_session_res_released_list_ps_ack.id = c.id; - pdu_session_res_released_list_ps_ack.crit = c.crit; - pdu_session_res_released_list_ps_ack.value = c.value.pdu_session_res_released_list_ps_ack(); + pdu_session_res_released_list_ps_ack.id = id; + HANDLE_CODE(pdu_session_res_released_list_ps_ack.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_released_list_ps_ack.value.unpack(bref)); break; - case 0: + } + case 0: { nof_mandatory_ies--; - allowed_nssai.id = c.id; - allowed_nssai.crit = c.crit; - allowed_nssai.value = c.value.allowed_nssai(); + allowed_nssai.id = id; + HANDLE_CODE(allowed_nssai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(allowed_nssai.value.unpack(bref)); break; - case 18: + } + case 18: { core_network_assist_info_present = true; - core_network_assist_info.id = c.id; - core_network_assist_info.crit = c.crit; - core_network_assist_info.value = c.value.core_network_assist_info(); + core_network_assist_info.id = id; + HANDLE_CODE(core_network_assist_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(core_network_assist_info.value.unpack(bref)); break; - case 91: + } + case 91: { rrc_inactive_transition_report_request_present = true; - rrc_inactive_transition_report_request.id = c.id; - rrc_inactive_transition_report_request.crit = c.crit; - rrc_inactive_transition_report_request.value = c.value.rrc_inactive_transition_report_request(); + rrc_inactive_transition_report_request.id = id; + HANDLE_CODE(rrc_inactive_transition_report_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_inactive_transition_report_request.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 146: + } + case 146: { redirection_voice_fallback_present = true; - redirection_voice_fallback.id = c.id; - redirection_voice_fallback.crit = c.crit; - redirection_voice_fallback.value = c.value.redirection_voice_fallback(); + redirection_voice_fallback.id = id; + HANDLE_CODE(redirection_voice_fallback.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(redirection_voice_fallback.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -41919,30 +40944,7 @@ void path_switch_request_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PathSwitchRequestAcknowledge ::= SEQUENCE -SRSASN_CODE path_switch_request_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE path_switch_request_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void path_switch_request_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; path_switch_request_fail_ies_container::path_switch_request_fail_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -41973,35 +40975,43 @@ SRSASN_CODE path_switch_request_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 69: + } + case 69: { nof_mandatory_ies--; - pdu_session_res_released_list_ps_fail.id = c.id; - pdu_session_res_released_list_ps_fail.crit = c.crit; - pdu_session_res_released_list_ps_fail.value = c.value.pdu_session_res_released_list_ps_fail(); + pdu_session_res_released_list_ps_fail.id = id; + HANDLE_CODE(pdu_session_res_released_list_ps_fail.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_released_list_ps_fail.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -42028,29 +41038,6 @@ void path_switch_request_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PathSwitchRequestFailure ::= SEQUENCE -SRSASN_CODE path_switch_request_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE path_switch_request_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void path_switch_request_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - template private_ie_container_item_s::private_ie_container_item_s(private_ie_id_c id_, crit_e crit_) : id(id_), crit(crit_) @@ -42141,7 +41128,7 @@ void private_msg_s::to_json(json_writer& j) const j.end_array(); } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ran_cfg_upd_ies_container::ran_cfg_upd_ies_container() : ran_node_name(82, crit_e::ignore), @@ -42179,35 +41166,43 @@ SRSASN_CODE ran_cfg_upd_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 82: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 82: { ran_node_name_present = true; - ran_node_name.id = c.id; - ran_node_name.crit = c.crit; - ran_node_name.value = c.value.ran_node_name(); + ran_node_name.id = id; + HANDLE_CODE(ran_node_name.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_node_name.value.unpack(bref)); break; - case 102: + } + case 102: { supported_ta_list_present = true; - supported_ta_list.id = c.id; - supported_ta_list.crit = c.crit; - supported_ta_list.value = c.value.supported_ta_list(); + supported_ta_list.id = id; + HANDLE_CODE(supported_ta_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(supported_ta_list.value.unpack(bref)); break; - case 21: + } + case 21: { default_paging_drx_present = true; - default_paging_drx.id = c.id; - default_paging_drx.crit = c.crit; - default_paging_drx.value = c.value.default_paging_drx(); + default_paging_drx.id = id; + HANDLE_CODE(default_paging_drx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(default_paging_drx.value.unpack(bref)); break; - case 27: + } + case 27: { global_ran_node_id_present = true; - global_ran_node_id.id = c.id; - global_ran_node_id.crit = c.crit; - global_ran_node_id.value = c.value.global_ran_node_id(); + global_ran_node_id.id = id; + HANDLE_CODE(global_ran_node_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(global_ran_node_id.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -42236,52 +41231,7 @@ void ran_cfg_upd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// RANConfigurationUpdate ::= SEQUENCE -SRSASN_CODE ran_cfg_upd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ran_cfg_upd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ran_cfg_upd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -// RANConfigurationUpdateAcknowledge ::= SEQUENCE -SRSASN_CODE ran_cfg_upd_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(pack_dyn_seq_of(bref, protocol_ies, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ran_cfg_upd_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(unpack_dyn_seq_of(protocol_ies, bref, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -void ran_cfg_upd_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ran_cfg_upd_fail_ies_container::ran_cfg_upd_fail_ies_container() : cause(15, crit_e::ignore), time_to_wait(107, crit_e::ignore), crit_diagnostics(19, crit_e::ignore) @@ -42311,29 +41261,35 @@ SRSASN_CODE ran_cfg_upd_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 1; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 15: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 107: + } + case 107: { time_to_wait_present = true; - time_to_wait.id = c.id; - time_to_wait.crit = c.crit; - time_to_wait.value = c.value.time_to_wait(); + time_to_wait.id = id; + HANDLE_CODE(time_to_wait.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_to_wait.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -42360,30 +41316,7 @@ void ran_cfg_upd_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// RANConfigurationUpdateFailure ::= SEQUENCE -SRSASN_CODE ran_cfg_upd_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ran_cfg_upd_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ran_cfg_upd_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; rrc_inactive_transition_report_ies_container::rrc_inactive_transition_report_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -42411,35 +41344,43 @@ SRSASN_CODE rrc_inactive_transition_report_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 92: + } + case 92: { nof_mandatory_ies--; - rrc_state.id = c.id; - rrc_state.crit = c.crit; - rrc_state.value = c.value.rrc_state(); + rrc_state.id = id; + HANDLE_CODE(rrc_state.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_state.value.unpack(bref)); break; - case 121: + } + case 121: { nof_mandatory_ies--; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -42464,30 +41405,7 @@ void rrc_inactive_transition_report_ies_container::to_json(json_writer& j) const j.end_obj(); } -// RRCInactiveTransitionReport ::= SEQUENCE -SRSASN_CODE rrc_inactive_transition_report_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE rrc_inactive_transition_report_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void rrc_inactive_transition_report_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; reroute_nas_request_ies_container::reroute_nas_request_ies_container() : ran_ue_ngap_id(85, crit_e::reject), @@ -42523,41 +41441,51 @@ SRSASN_CODE reroute_nas_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 85: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 10: + } + case 10: { amf_ue_ngap_id_present = true; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 42: + } + case 42: { nof_mandatory_ies--; - ngap_msg.id = c.id; - ngap_msg.crit = c.crit; - ngap_msg.value = c.value.ngap_msg(); + ngap_msg.id = id; + HANDLE_CODE(ngap_msg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ngap_msg.value.unpack(bref)); break; - case 3: + } + case 3: { nof_mandatory_ies--; - amf_set_id.id = c.id; - amf_set_id.crit = c.crit; - amf_set_id.value = c.value.amf_set_id(); + amf_set_id.id = id; + HANDLE_CODE(amf_set_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_set_id.value.unpack(bref)); break; - case 0: + } + case 0: { allowed_nssai_present = true; - allowed_nssai.id = c.id; - allowed_nssai.crit = c.crit; - allowed_nssai.value = c.value.allowed_nssai(); + allowed_nssai.id = id; + HANDLE_CODE(allowed_nssai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(allowed_nssai.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -42588,30 +41516,7 @@ void reroute_nas_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// RerouteNASRequest ::= SEQUENCE -SRSASN_CODE reroute_nas_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE reroute_nas_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void reroute_nas_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; secondary_rat_data_usage_report_ies_container::secondary_rat_data_usage_report_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -42642,35 +41547,43 @@ SRSASN_CODE secondary_rat_data_usage_report_ies_container::unpack(cbit_ref& bref uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 142: + } + case 142: { nof_mandatory_ies--; - pdu_session_res_secondary_ratusage_list.id = c.id; - pdu_session_res_secondary_ratusage_list.crit = c.crit; - pdu_session_res_secondary_ratusage_list.value = c.value.pdu_session_res_secondary_ratusage_list(); + pdu_session_res_secondary_ratusage_list.id = id; + HANDLE_CODE(pdu_session_res_secondary_ratusage_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_secondary_ratusage_list.value.unpack(bref)); break; - case 143: + } + case 143: { ho_flag_present = true; - ho_flag.id = c.id; - ho_flag.crit = c.crit; - ho_flag.value = c.value.ho_flag(); + ho_flag.id = id; + HANDLE_CODE(ho_flag.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ho_flag.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -42697,30 +41610,7 @@ void secondary_rat_data_usage_report_ies_container::to_json(json_writer& j) cons j.end_obj(); } -// SecondaryRATDataUsageReport ::= SEQUENCE -SRSASN_CODE secondary_rat_data_usage_report_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE secondary_rat_data_usage_report_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void secondary_rat_data_usage_report_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; trace_fail_ind_ies_container::trace_fail_ind_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -42748,35 +41638,43 @@ SRSASN_CODE trace_fail_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 44: + } + case 44: { nof_mandatory_ies--; - ngran_trace_id.id = c.id; - ngran_trace_id.crit = c.crit; - ngran_trace_id.value = c.value.ngran_trace_id(); + ngran_trace_id.id = id; + HANDLE_CODE(ngran_trace_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ngran_trace_id.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -42801,30 +41699,7 @@ void trace_fail_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// TraceFailureIndication ::= SEQUENCE -SRSASN_CODE trace_fail_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE trace_fail_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void trace_fail_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; trace_start_ies_container::trace_start_ies_container() : amf_ue_ngap_id(10, crit_e::reject), ran_ue_ngap_id(85, crit_e::reject), trace_activation(108, crit_e::ignore) @@ -42848,29 +41723,35 @@ SRSASN_CODE trace_start_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 108: + } + case 108: { nof_mandatory_ies--; - trace_activation.id = c.id; - trace_activation.crit = c.crit; - trace_activation.value = c.value.trace_activation(); + trace_activation.id = id; + HANDLE_CODE(trace_activation.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(trace_activation.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -42893,30 +41774,7 @@ void trace_start_ies_container::to_json(json_writer& j) const j.end_obj(); } -// TraceStart ::= SEQUENCE -SRSASN_CODE trace_start_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE trace_start_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void trace_start_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_mod_fail_ies_container::ue_context_mod_fail_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -42947,35 +41805,43 @@ SRSASN_CODE ue_context_mod_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -43002,30 +41868,7 @@ void ue_context_mod_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextModificationFailure ::= SEQUENCE -SRSASN_CODE ue_context_mod_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_mod_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_mod_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_mod_request_ies_container::ue_context_mod_request_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -43094,77 +41937,99 @@ SRSASN_CODE ue_context_mod_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 83: + } + case 83: { ran_paging_prio_present = true; - ran_paging_prio.id = c.id; - ran_paging_prio.crit = c.crit; - ran_paging_prio.value = c.value.ran_paging_prio(); + ran_paging_prio.id = id; + HANDLE_CODE(ran_paging_prio.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_paging_prio.value.unpack(bref)); break; - case 94: + } + case 94: { security_key_present = true; - security_key.id = c.id; - security_key.crit = c.crit; - security_key.value = c.value.security_key(); + security_key.id = id; + HANDLE_CODE(security_key.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_key.value.unpack(bref)); break; - case 31: + } + case 31: { idx_to_rfsp_present = true; - idx_to_rfsp.id = c.id; - idx_to_rfsp.crit = c.crit; - idx_to_rfsp.value = c.value.idx_to_rfsp(); + idx_to_rfsp.id = id; + HANDLE_CODE(idx_to_rfsp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(idx_to_rfsp.value.unpack(bref)); break; - case 110: + } + case 110: { ue_aggregate_maximum_bit_rate_present = true; - ue_aggregate_maximum_bit_rate.id = c.id; - ue_aggregate_maximum_bit_rate.crit = c.crit; - ue_aggregate_maximum_bit_rate.value = c.value.ue_aggregate_maximum_bit_rate(); + ue_aggregate_maximum_bit_rate.id = id; + HANDLE_CODE(ue_aggregate_maximum_bit_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_aggregate_maximum_bit_rate.value.unpack(bref)); break; - case 119: + } + case 119: { ue_security_cap_present = true; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 18: + } + case 18: { core_network_assist_info_present = true; - core_network_assist_info.id = c.id; - core_network_assist_info.crit = c.crit; - core_network_assist_info.value = c.value.core_network_assist_info(); + core_network_assist_info.id = id; + HANDLE_CODE(core_network_assist_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(core_network_assist_info.value.unpack(bref)); break; - case 24: + } + case 24: { emergency_fallback_ind_present = true; - emergency_fallback_ind.id = c.id; - emergency_fallback_ind.crit = c.crit; - emergency_fallback_ind.value = c.value.emergency_fallback_ind(); + emergency_fallback_ind.id = id; + HANDLE_CODE(emergency_fallback_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(emergency_fallback_ind.value.unpack(bref)); break; - case 40: + } + case 40: { new_amf_ue_ngap_id_present = true; - new_amf_ue_ngap_id.id = c.id; - new_amf_ue_ngap_id.crit = c.crit; - new_amf_ue_ngap_id.value = c.value.new_amf_ue_ngap_id(); + new_amf_ue_ngap_id.id = id; + HANDLE_CODE(new_amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(new_amf_ue_ngap_id.value.unpack(bref)); break; - case 91: + } + case 91: { rrc_inactive_transition_report_request_present = true; - rrc_inactive_transition_report_request.id = c.id; - rrc_inactive_transition_report_request.crit = c.crit; - rrc_inactive_transition_report_request.value = c.value.rrc_inactive_transition_report_request(); + rrc_inactive_transition_report_request.id = id; + HANDLE_CODE(rrc_inactive_transition_report_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_inactive_transition_report_request.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -43221,30 +42086,7 @@ void ue_context_mod_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextModificationRequest ::= SEQUENCE -SRSASN_CODE ue_context_mod_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_mod_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_mod_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_mod_resp_ies_container::ue_context_mod_resp_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -43283,41 +42125,51 @@ SRSASN_CODE ue_context_mod_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 92: + } + case 92: { rrc_state_present = true; - rrc_state.id = c.id; - rrc_state.crit = c.crit; - rrc_state.value = c.value.rrc_state(); + rrc_state.id = id; + HANDLE_CODE(rrc_state.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_state.value.unpack(bref)); break; - case 121: + } + case 121: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -43350,30 +42202,7 @@ void ue_context_mod_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextModificationResponse ::= SEQUENCE -SRSASN_CODE ue_context_mod_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_mod_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_mod_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_release_cmd_ies_container::ue_context_release_cmd_ies_container() : ue_ngap_ids(114, crit_e::reject), cause(15, crit_e::ignore) @@ -43396,23 +42225,27 @@ SRSASN_CODE ue_context_release_cmd_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 114: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 114: { nof_mandatory_ies--; - ue_ngap_ids.id = c.id; - ue_ngap_ids.crit = c.crit; - ue_ngap_ids.value = c.value.ue_ngap_ids(); + ue_ngap_ids.id = id; + HANDLE_CODE(ue_ngap_ids.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_ngap_ids.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -43433,30 +42266,7 @@ void ue_context_release_cmd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextReleaseCommand ::= SEQUENCE -SRSASN_CODE ue_context_release_cmd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_release_cmd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_release_cmd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_release_complete_ies_container::ue_context_release_complete_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -43500,48 +42310,59 @@ SRSASN_CODE ue_context_release_complete_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 121: + } + case 121: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 32: + } + case 32: { info_on_recommended_cells_and_ran_nodes_for_paging_present = true; - info_on_recommended_cells_and_ran_nodes_for_paging.id = c.id; - info_on_recommended_cells_and_ran_nodes_for_paging.crit = c.crit; - info_on_recommended_cells_and_ran_nodes_for_paging.value = - c.value.info_on_recommended_cells_and_ran_nodes_for_paging(); + info_on_recommended_cells_and_ran_nodes_for_paging.id = id; + HANDLE_CODE(info_on_recommended_cells_and_ran_nodes_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(info_on_recommended_cells_and_ran_nodes_for_paging.value.unpack(bref)); break; - case 60: + } + case 60: { pdu_session_res_list_cxt_rel_cpl_present = true; - pdu_session_res_list_cxt_rel_cpl.id = c.id; - pdu_session_res_list_cxt_rel_cpl.crit = c.crit; - pdu_session_res_list_cxt_rel_cpl.value = c.value.pdu_session_res_list_cxt_rel_cpl(); + pdu_session_res_list_cxt_rel_cpl.id = id; + HANDLE_CODE(pdu_session_res_list_cxt_rel_cpl.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_list_cxt_rel_cpl.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -43578,30 +42399,7 @@ void ue_context_release_complete_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextReleaseComplete ::= SEQUENCE -SRSASN_CODE ue_context_release_complete_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_release_complete_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_release_complete_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_release_request_ies_container::ue_context_release_request_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -43632,35 +42430,43 @@ SRSASN_CODE ue_context_release_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 133: + } + case 133: { pdu_session_res_list_cxt_rel_req_present = true; - pdu_session_res_list_cxt_rel_req.id = c.id; - pdu_session_res_list_cxt_rel_req.crit = c.crit; - pdu_session_res_list_cxt_rel_req.value = c.value.pdu_session_res_list_cxt_rel_req(); + pdu_session_res_list_cxt_rel_req.id = id; + HANDLE_CODE(pdu_session_res_list_cxt_rel_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_res_list_cxt_rel_req.value.unpack(bref)); break; - case 15: + } + case 15: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -43687,30 +42493,7 @@ void ue_context_release_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextReleaseRequest ::= SEQUENCE -SRSASN_CODE ue_context_release_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_release_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_release_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_radio_cap_check_request_ies_container::ue_radio_cap_check_request_ies_container() : amf_ue_ngap_id(10, crit_e::reject), ran_ue_ngap_id(85, crit_e::reject), ue_radio_cap(117, crit_e::ignore) @@ -43737,29 +42520,35 @@ SRSASN_CODE ue_radio_cap_check_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 117: + } + case 117: { ue_radio_cap_present = true; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -43784,30 +42573,7 @@ void ue_radio_cap_check_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UERadioCapabilityCheckRequest ::= SEQUENCE -SRSASN_CODE ue_radio_cap_check_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_radio_cap_check_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_radio_cap_check_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_radio_cap_check_resp_ies_container::ue_radio_cap_check_resp_ies_container() : amf_ue_ngap_id(10, crit_e::ignore), @@ -43838,35 +42604,43 @@ SRSASN_CODE ue_radio_cap_check_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 30: + } + case 30: { nof_mandatory_ies--; - ims_voice_support_ind.id = c.id; - ims_voice_support_ind.crit = c.crit; - ims_voice_support_ind.value = c.value.ims_voice_support_ind(); + ims_voice_support_ind.id = id; + HANDLE_CODE(ims_voice_support_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ims_voice_support_ind.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -43893,30 +42667,7 @@ void ue_radio_cap_check_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UERadioCapabilityCheckResponse ::= SEQUENCE -SRSASN_CODE ue_radio_cap_check_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_radio_cap_check_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_radio_cap_check_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_radio_cap_info_ind_ies_container::ue_radio_cap_info_ind_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -43947,35 +42698,43 @@ SRSASN_CODE ue_radio_cap_info_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 117: + } + case 117: { nof_mandatory_ies--; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; - case 118: + } + case 118: { ue_radio_cap_for_paging_present = true; - ue_radio_cap_for_paging.id = c.id; - ue_radio_cap_for_paging.crit = c.crit; - ue_radio_cap_for_paging.value = c.value.ue_radio_cap_for_paging(); + ue_radio_cap_for_paging.id = id; + HANDLE_CODE(ue_radio_cap_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap_for_paging.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44002,30 +42761,7 @@ void ue_radio_cap_info_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UERadioCapabilityInfoIndication ::= SEQUENCE -SRSASN_CODE ue_radio_cap_info_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_radio_cap_info_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_radio_cap_info_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; uetnla_binding_release_request_ies_container::uetnla_binding_release_request_ies_container() : amf_ue_ngap_id(10, crit_e::reject), ran_ue_ngap_id(85, crit_e::reject) @@ -44048,23 +42784,27 @@ SRSASN_CODE uetnla_binding_release_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44085,30 +42825,7 @@ void uetnla_binding_release_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UETNLABindingReleaseRequest ::= SEQUENCE -SRSASN_CODE uetnla_binding_release_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE uetnla_binding_release_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void uetnla_binding_release_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_nas_transport_ies_container::ul_nas_transport_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -44136,35 +42853,43 @@ SRSASN_CODE ul_nas_transport_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 38: + } + case 38: { nof_mandatory_ies--; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 121: + } + case 121: { nof_mandatory_ies--; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44189,30 +42914,7 @@ void ul_nas_transport_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UplinkNASTransport ::= SEQUENCE -SRSASN_CODE ul_nas_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_nas_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_nas_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_non_ueassociated_nrp_pa_transport_ies_container::ul_non_ueassociated_nrp_pa_transport_ies_container() : routing_id(89, crit_e::reject), nrp_pa_pdu(46, crit_e::reject) @@ -44235,23 +42937,27 @@ SRSASN_CODE ul_non_ueassociated_nrp_pa_transport_ies_container::unpack(cbit_ref& uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 89: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 89: { nof_mandatory_ies--; - routing_id.id = c.id; - routing_id.crit = c.crit; - routing_id.value = c.value.routing_id(); + routing_id.id = id; + HANDLE_CODE(routing_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(routing_id.value.unpack(bref)); break; - case 46: + } + case 46: { nof_mandatory_ies--; - nrp_pa_pdu.id = c.id; - nrp_pa_pdu.crit = c.crit; - nrp_pa_pdu.value = c.value.nrp_pa_pdu(); + nrp_pa_pdu.id = id; + HANDLE_CODE(nrp_pa_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrp_pa_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44272,30 +42978,7 @@ void ul_non_ueassociated_nrp_pa_transport_ies_container::to_json(json_writer& j) j.end_obj(); } -// UplinkNonUEAssociatedNRPPaTransport ::= SEQUENCE -SRSASN_CODE ul_non_ueassociated_nrp_pa_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_non_ueassociated_nrp_pa_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_non_ueassociated_nrp_pa_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_ran_cfg_transfer_ies_container::ul_ran_cfg_transfer_ies_container() : son_cfg_transfer_ul(99, crit_e::ignore), endc_son_cfg_transfer_ul(158, crit_e::ignore) @@ -44322,23 +43005,27 @@ SRSASN_CODE ul_ran_cfg_transfer_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 99: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 99: { son_cfg_transfer_ul_present = true; - son_cfg_transfer_ul.id = c.id; - son_cfg_transfer_ul.crit = c.crit; - son_cfg_transfer_ul.value = c.value.son_cfg_transfer_ul(); + son_cfg_transfer_ul.id = id; + HANDLE_CODE(son_cfg_transfer_ul.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(son_cfg_transfer_ul.value.unpack(bref)); break; - case 158: + } + case 158: { endc_son_cfg_transfer_ul_present = true; - endc_son_cfg_transfer_ul.id = c.id; - endc_son_cfg_transfer_ul.crit = c.crit; - endc_son_cfg_transfer_ul.value = c.value.endc_son_cfg_transfer_ul(); + endc_son_cfg_transfer_ul.id = id; + HANDLE_CODE(endc_son_cfg_transfer_ul.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(endc_son_cfg_transfer_ul.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44359,30 +43046,7 @@ void ul_ran_cfg_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UplinkRANConfigurationTransfer ::= SEQUENCE -SRSASN_CODE ul_ran_cfg_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_ran_cfg_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_ran_cfg_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_ran_status_transfer_ies_container::ul_ran_status_transfer_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -44408,29 +43072,35 @@ SRSASN_CODE ul_ran_status_transfer_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 84: + } + case 84: { nof_mandatory_ies--; - ran_status_transfer_transparent_container.id = c.id; - ran_status_transfer_transparent_container.crit = c.crit; - ran_status_transfer_transparent_container.value = c.value.ran_status_transfer_transparent_container(); + ran_status_transfer_transparent_container.id = id; + HANDLE_CODE(ran_status_transfer_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_status_transfer_transparent_container.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44453,30 +43123,7 @@ void ul_ran_status_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UplinkRANStatusTransfer ::= SEQUENCE -SRSASN_CODE ul_ran_status_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_ran_status_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_ran_status_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_ueassociated_nrp_pa_transport_ies_container::ul_ueassociated_nrp_pa_transport_ies_container() : amf_ue_ngap_id(10, crit_e::reject), @@ -44504,35 +43151,43 @@ SRSASN_CODE ul_ueassociated_nrp_pa_transport_ies_container::unpack(cbit_ref& bre uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 10: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 10: { nof_mandatory_ies--; - amf_ue_ngap_id.id = c.id; - amf_ue_ngap_id.crit = c.crit; - amf_ue_ngap_id.value = c.value.amf_ue_ngap_id(); + amf_ue_ngap_id.id = id; + HANDLE_CODE(amf_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(amf_ue_ngap_id.value.unpack(bref)); break; - case 85: + } + case 85: { nof_mandatory_ies--; - ran_ue_ngap_id.id = c.id; - ran_ue_ngap_id.crit = c.crit; - ran_ue_ngap_id.value = c.value.ran_ue_ngap_id(); + ran_ue_ngap_id.id = id; + HANDLE_CODE(ran_ue_ngap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ran_ue_ngap_id.value.unpack(bref)); break; - case 89: + } + case 89: { nof_mandatory_ies--; - routing_id.id = c.id; - routing_id.crit = c.crit; - routing_id.value = c.value.routing_id(); + routing_id.id = id; + HANDLE_CODE(routing_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(routing_id.value.unpack(bref)); break; - case 46: + } + case 46: { nof_mandatory_ies--; - nrp_pa_pdu.id = c.id; - nrp_pa_pdu.crit = c.crit; - nrp_pa_pdu.value = c.value.nrp_pa_pdu(); + nrp_pa_pdu.id = id; + HANDLE_CODE(nrp_pa_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrp_pa_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44557,30 +43212,7 @@ void ul_ueassociated_nrp_pa_transport_ies_container::to_json(json_writer& j) con j.end_obj(); } -// UplinkUEAssociatedNRPPaTransport ::= SEQUENCE -SRSASN_CODE ul_ueassociated_nrp_pa_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_ueassociated_nrp_pa_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_ueassociated_nrp_pa_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; write_replace_warning_request_ies_container::write_replace_warning_request_ies_container() : msg_id(35, crit_e::reject), @@ -44643,77 +43275,99 @@ SRSASN_CODE write_replace_warning_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 35: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 35: { nof_mandatory_ies--; - msg_id.id = c.id; - msg_id.crit = c.crit; - msg_id.value = c.value.msg_id(); + msg_id.id = id; + HANDLE_CODE(msg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(msg_id.value.unpack(bref)); break; - case 95: + } + case 95: { nof_mandatory_ies--; - serial_num.id = c.id; - serial_num.crit = c.crit; - serial_num.value = c.value.serial_num(); + serial_num.id = id; + HANDLE_CODE(serial_num.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(serial_num.value.unpack(bref)); break; - case 122: + } + case 122: { warning_area_list_present = true; - warning_area_list.id = c.id; - warning_area_list.crit = c.crit; - warning_area_list.value = c.value.warning_area_list(); + warning_area_list.id = id; + HANDLE_CODE(warning_area_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_area_list.value.unpack(bref)); break; - case 87: + } + case 87: { nof_mandatory_ies--; - repeat_period.id = c.id; - repeat_period.crit = c.crit; - repeat_period.value = c.value.repeat_period(); + repeat_period.id = id; + HANDLE_CODE(repeat_period.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(repeat_period.value.unpack(bref)); break; - case 47: + } + case 47: { nof_mandatory_ies--; - nof_broadcasts_requested.id = c.id; - nof_broadcasts_requested.crit = c.crit; - nof_broadcasts_requested.value = c.value.nof_broadcasts_requested(); + nof_broadcasts_requested.id = id; + HANDLE_CODE(nof_broadcasts_requested.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nof_broadcasts_requested.value.unpack(bref)); break; - case 125: + } + case 125: { warning_type_present = true; - warning_type.id = c.id; - warning_type.crit = c.crit; - warning_type.value = c.value.warning_type(); + warning_type.id = id; + HANDLE_CODE(warning_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_type.value.unpack(bref)); break; - case 124: + } + case 124: { warning_security_info_present = true; - warning_security_info.id = c.id; - warning_security_info.crit = c.crit; - warning_security_info.value = c.value.warning_security_info(); + warning_security_info.id = id; + HANDLE_CODE(warning_security_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_security_info.value.unpack(bref)); break; - case 20: + } + case 20: { data_coding_scheme_present = true; - data_coding_scheme.id = c.id; - data_coding_scheme.crit = c.crit; - data_coding_scheme.value = c.value.data_coding_scheme(); + data_coding_scheme.id = id; + HANDLE_CODE(data_coding_scheme.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(data_coding_scheme.value.unpack(bref)); break; - case 123: + } + case 123: { warning_msg_contents_present = true; - warning_msg_contents.id = c.id; - warning_msg_contents.crit = c.crit; - warning_msg_contents.value = c.value.warning_msg_contents(); + warning_msg_contents.id = id; + HANDLE_CODE(warning_msg_contents.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_msg_contents.value.unpack(bref)); break; - case 17: + } + case 17: { concurrent_warning_msg_ind_present = true; - concurrent_warning_msg_ind.id = c.id; - concurrent_warning_msg_ind.crit = c.crit; - concurrent_warning_msg_ind.value = c.value.concurrent_warning_msg_ind(); + concurrent_warning_msg_ind.id = id; + HANDLE_CODE(concurrent_warning_msg_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(concurrent_warning_msg_ind.value.unpack(bref)); break; - case 141: + } + case 141: { warning_area_coordinates_present = true; - warning_area_coordinates.id = c.id; - warning_area_coordinates.crit = c.crit; - warning_area_coordinates.value = c.value.warning_area_coordinates(); + warning_area_coordinates.id = id; + HANDLE_CODE(warning_area_coordinates.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_area_coordinates.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44766,30 +43420,7 @@ void write_replace_warning_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// WriteReplaceWarningRequest ::= SEQUENCE -SRSASN_CODE write_replace_warning_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE write_replace_warning_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void write_replace_warning_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; write_replace_warning_resp_ies_container::write_replace_warning_resp_ies_container() : msg_id(35, crit_e::reject), @@ -44823,35 +43454,43 @@ SRSASN_CODE write_replace_warning_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 35: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 35: { nof_mandatory_ies--; - msg_id.id = c.id; - msg_id.crit = c.crit; - msg_id.value = c.value.msg_id(); + msg_id.id = id; + HANDLE_CODE(msg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(msg_id.value.unpack(bref)); break; - case 95: + } + case 95: { nof_mandatory_ies--; - serial_num.id = c.id; - serial_num.crit = c.crit; - serial_num.value = c.value.serial_num(); + serial_num.id = id; + HANDLE_CODE(serial_num.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(serial_num.value.unpack(bref)); break; - case 13: + } + case 13: { broadcast_completed_area_list_present = true; - broadcast_completed_area_list.id = c.id; - broadcast_completed_area_list.crit = c.crit; - broadcast_completed_area_list.value = c.value.broadcast_completed_area_list(); + broadcast_completed_area_list.id = id; + HANDLE_CODE(broadcast_completed_area_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(broadcast_completed_area_list.value.unpack(bref)); break; - case 19: + } + case 19: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -44880,29 +43519,6 @@ void write_replace_warning_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// WriteReplaceWarningResponse ::= SEQUENCE -SRSASN_CODE write_replace_warning_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE write_replace_warning_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void write_replace_warning_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // NGAP-ELEMENTARY-PROCEDURES ::= OBJECT SET OF NGAP-ELEMENTARY-PROCEDURE uint16_t ngap_elem_procs_o::idx_to_proc_code(uint32_t idx) { @@ -48914,12 +47530,12 @@ void qos_flow_info_item_s::to_json(json_writer& j) const SRSASN_CODE pdu_session_res_info_item_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(drbs_to_qos_flows_map_list_present, 1)); + HANDLE_CODE(bref.pack(drbs_to_qos_flows_map_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(pack_integer(bref, pdu_session_id, (uint16_t)0u, (uint16_t)255u, false, true)); HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_info_list, 1, 64, true)); - if (drbs_to_qos_flows_map_list_present) { + if (drbs_to_qos_flows_map_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, drbs_to_qos_flows_map_list, 1, 32, true)); } if (ie_exts_present) { @@ -48931,6 +47547,7 @@ SRSASN_CODE pdu_session_res_info_item_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_info_item_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool drbs_to_qos_flows_map_list_present; HANDLE_CODE(bref.unpack(drbs_to_qos_flows_map_list_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -48954,7 +47571,7 @@ void pdu_session_res_info_item_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (drbs_to_qos_flows_map_list_present) { + if (drbs_to_qos_flows_map_list.size() > 0) { j.start_array("dRBsToQosFlowsMappingList"); for (const auto& e1 : drbs_to_qos_flows_map_list) { e1.to_json(j); @@ -49049,16 +47666,16 @@ void up_transport_layer_info_pair_item_s::to_json(json_writer& j) const SRSASN_CODE pdu_session_res_modify_confirm_transfer_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(add_ng_uuptnl_info_present, 1)); - HANDLE_CODE(bref.pack(qos_flow_failed_to_modify_list_present, 1)); + HANDLE_CODE(bref.pack(add_ng_uuptnl_info.size() > 0, 1)); + HANDLE_CODE(bref.pack(qos_flow_failed_to_modify_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_modify_confirm_list, 1, 64, true)); HANDLE_CODE(ulngu_up_tnl_info.pack(bref)); - if (add_ng_uuptnl_info_present) { + if (add_ng_uuptnl_info.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, add_ng_uuptnl_info, 1, 3, true)); } - if (qos_flow_failed_to_modify_list_present) { + if (qos_flow_failed_to_modify_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_failed_to_modify_list, 1, 64, true)); } if (ie_exts_present) { @@ -49070,7 +47687,9 @@ SRSASN_CODE pdu_session_res_modify_confirm_transfer_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_modify_confirm_transfer_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool add_ng_uuptnl_info_present; HANDLE_CODE(bref.unpack(add_ng_uuptnl_info_present, 1)); + bool qos_flow_failed_to_modify_list_present; HANDLE_CODE(bref.unpack(qos_flow_failed_to_modify_list_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -49098,14 +47717,14 @@ void pdu_session_res_modify_confirm_transfer_s::to_json(json_writer& j) const j.end_array(); j.write_fieldname("uLNGU-UP-TNLInformation"); ulngu_up_tnl_info.to_json(j); - if (add_ng_uuptnl_info_present) { + if (add_ng_uuptnl_info.size() > 0) { j.start_array("additionalNG-UUPTNLInformation"); for (const auto& e1 : add_ng_uuptnl_info) { e1.to_json(j); } j.end_array(); } - if (qos_flow_failed_to_modify_list_present) { + if (qos_flow_failed_to_modify_list.size() > 0) { j.start_array("qosFlowFailedToModifyList"); for (const auto& e1 : qos_flow_failed_to_modify_list) { e1.to_json(j); @@ -49270,13 +47889,13 @@ SRSASN_CODE secondary_ratusage_info_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(pdu_session_usage_report_present, 1)); - HANDLE_CODE(bref.pack(qos_flows_usage_report_list_present, 1)); + HANDLE_CODE(bref.pack(qos_flows_usage_report_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_ext_present, 1)); if (pdu_session_usage_report_present) { HANDLE_CODE(pdu_session_usage_report.pack(bref)); } - if (qos_flows_usage_report_list_present) { + if (qos_flows_usage_report_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flows_usage_report_list, 1, 64, true)); } if (ie_ext_present) { @@ -49289,6 +47908,7 @@ SRSASN_CODE secondary_ratusage_info_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(pdu_session_usage_report_present, 1)); + bool qos_flows_usage_report_list_present; HANDLE_CODE(bref.unpack(qos_flows_usage_report_list_present, 1)); HANDLE_CODE(bref.unpack(ie_ext_present, 1)); @@ -49311,7 +47931,7 @@ void secondary_ratusage_info_s::to_json(json_writer& j) const j.write_fieldname("pDUSessionUsageReport"); pdu_session_usage_report.to_json(j); } - if (qos_flows_usage_report_list_present) { + if (qos_flows_usage_report_list.size() > 0) { j.start_array("qosFlowsUsageReportList"); for (const auto& e1 : qos_flows_usage_report_list) { e1.to_json(j); @@ -49528,7 +48148,7 @@ const char* pdu_session_res_modify_ind_transfer_ext_ies_o::ext_c::types_opts::to return convert_enum_idx(options, 2, value, "pdu_session_res_modify_ind_transfer_ext_ies_o::ext_c::types"); } -template struct asn1::ngap_nr::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; pdu_session_res_modify_ind_transfer_ext_ies_container::pdu_session_res_modify_ind_transfer_ext_ies_container() : secondary_ratusage_info(144, crit_e::ignore), security_result(156, crit_e::ignore) @@ -49555,23 +48175,27 @@ SRSASN_CODE pdu_session_res_modify_ind_transfer_ext_ies_container::unpack(cbit_r unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 144: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 144: { secondary_ratusage_info_present = true; - secondary_ratusage_info.id = c.id; - secondary_ratusage_info.crit = c.crit; - secondary_ratusage_info.ext = c.ext_value.secondary_ratusage_info(); + secondary_ratusage_info.id = id; + HANDLE_CODE(secondary_ratusage_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_ratusage_info.ext.unpack(bref)); break; - case 156: + } + case 156: { security_result_present = true; - security_result.id = c.id; - security_result.crit = c.crit; - security_result.ext = c.ext_value.security_result(); + security_result.id = id; + HANDLE_CODE(security_result.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_result.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -49596,11 +48220,11 @@ void pdu_session_res_modify_ind_transfer_ext_ies_container::to_json(json_writer& SRSASN_CODE pdu_session_res_modify_ind_transfer_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(add_dl_qos_flow_per_tnl_info_present, 1)); + HANDLE_CODE(bref.pack(add_dl_qos_flow_per_tnl_info.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(dlqos_flow_per_tnl_info.pack(bref)); - if (add_dl_qos_flow_per_tnl_info_present) { + if (add_dl_qos_flow_per_tnl_info.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, add_dl_qos_flow_per_tnl_info, 1, 3, true)); } if (ie_exts_present) { @@ -49612,6 +48236,7 @@ SRSASN_CODE pdu_session_res_modify_ind_transfer_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_modify_ind_transfer_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool add_dl_qos_flow_per_tnl_info_present; HANDLE_CODE(bref.unpack(add_dl_qos_flow_per_tnl_info_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -49630,7 +48255,7 @@ void pdu_session_res_modify_ind_transfer_s::to_json(json_writer& j) const j.start_obj(); j.write_fieldname("dLQosFlowPerTNLInformation"); dlqos_flow_per_tnl_info.to_json(j); - if (add_dl_qos_flow_per_tnl_info_present) { + if (add_dl_qos_flow_per_tnl_info.size() > 0) { j.start_array("additionalDLQosFlowPerTNLInformation"); for (const auto& e1 : add_dl_qos_flow_per_tnl_info) { e1.to_json(j); @@ -50457,7 +49082,7 @@ uint8_t pdu_session_res_modify_request_transfer_ies_o::value_c::types_opts::to_n return 0; } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_modify_request_transfer_ies_container::pdu_session_res_modify_request_transfer_ies_container() : pdu_session_aggregate_maximum_bit_rate(130, crit_e::reject), @@ -50505,47 +49130,59 @@ SRSASN_CODE pdu_session_res_modify_request_transfer_ies_container::unpack(cbit_r unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 130: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 130: { pdu_session_aggregate_maximum_bit_rate_present = true; - pdu_session_aggregate_maximum_bit_rate.id = c.id; - pdu_session_aggregate_maximum_bit_rate.crit = c.crit; - pdu_session_aggregate_maximum_bit_rate.value = c.value.pdu_session_aggregate_maximum_bit_rate(); + pdu_session_aggregate_maximum_bit_rate.id = id; + HANDLE_CODE(pdu_session_aggregate_maximum_bit_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_aggregate_maximum_bit_rate.value.unpack(bref)); break; - case 140: + } + case 140: { ul_ngu_up_tnl_modify_list_present = true; - ul_ngu_up_tnl_modify_list.id = c.id; - ul_ngu_up_tnl_modify_list.crit = c.crit; - ul_ngu_up_tnl_modify_list.value = c.value.ul_ngu_up_tnl_modify_list(); + ul_ngu_up_tnl_modify_list.id = id; + HANDLE_CODE(ul_ngu_up_tnl_modify_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ul_ngu_up_tnl_modify_list.value.unpack(bref)); break; - case 129: + } + case 129: { network_instance_present = true; - network_instance.id = c.id; - network_instance.crit = c.crit; - network_instance.value = c.value.network_instance(); + network_instance.id = id; + HANDLE_CODE(network_instance.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(network_instance.value.unpack(bref)); break; - case 135: + } + case 135: { qos_flow_add_or_modify_request_list_present = true; - qos_flow_add_or_modify_request_list.id = c.id; - qos_flow_add_or_modify_request_list.crit = c.crit; - qos_flow_add_or_modify_request_list.value = c.value.qos_flow_add_or_modify_request_list(); + qos_flow_add_or_modify_request_list.id = id; + HANDLE_CODE(qos_flow_add_or_modify_request_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(qos_flow_add_or_modify_request_list.value.unpack(bref)); break; - case 137: + } + case 137: { qos_flow_to_release_list_present = true; - qos_flow_to_release_list.id = c.id; - qos_flow_to_release_list.crit = c.crit; - qos_flow_to_release_list.value = c.value.qos_flow_to_release_list(); + qos_flow_to_release_list.id = id; + HANDLE_CODE(qos_flow_to_release_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(qos_flow_to_release_list.value.unpack(bref)); break; - case 126: + } + case 126: { add_ul_ngu_up_tnl_info_present = true; - add_ul_ngu_up_tnl_info.id = c.id; - add_ul_ngu_up_tnl_info.crit = c.crit; - add_ul_ngu_up_tnl_info.value = c.value.add_ul_ngu_up_tnl_info(); + add_ul_ngu_up_tnl_info.id = id; + HANDLE_CODE(add_ul_ngu_up_tnl_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_ul_ngu_up_tnl_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -50582,29 +49219,6 @@ void pdu_session_res_modify_request_transfer_ies_container::to_json(json_writer& j.end_obj(); } -// PDUSessionResourceModifyRequestTransfer ::= SEQUENCE -SRSASN_CODE pdu_session_res_modify_request_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_modify_request_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_modify_request_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // QosFlowAddOrModifyResponseItem ::= SEQUENCE SRSASN_CODE qos_flow_add_or_modify_resp_item_s::pack(bit_ref& bref) const { @@ -50713,10 +49327,10 @@ SRSASN_CODE pdu_session_res_modify_resp_transfer_s::pack(bit_ref& bref) const bref.pack(ext, 1); HANDLE_CODE(bref.pack(dl_ngu_up_tnl_info_present, 1)); HANDLE_CODE(bref.pack(ul_ngu_up_tnl_info_present, 1)); - HANDLE_CODE(bref.pack(qos_flow_add_or_modify_resp_list_present, 1)); - HANDLE_CODE(bref.pack(add_dl_qos_flow_per_tnl_info_present, 1)); - HANDLE_CODE(bref.pack(qos_flow_failed_to_add_or_modify_list_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(qos_flow_add_or_modify_resp_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(add_dl_qos_flow_per_tnl_info.size() > 0, 1)); + HANDLE_CODE(bref.pack(qos_flow_failed_to_add_or_modify_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); if (dl_ngu_up_tnl_info_present) { HANDLE_CODE(dl_ngu_up_tnl_info.pack(bref)); @@ -50724,16 +49338,16 @@ SRSASN_CODE pdu_session_res_modify_resp_transfer_s::pack(bit_ref& bref) const if (ul_ngu_up_tnl_info_present) { HANDLE_CODE(ul_ngu_up_tnl_info.pack(bref)); } - if (qos_flow_add_or_modify_resp_list_present) { + if (qos_flow_add_or_modify_resp_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_add_or_modify_resp_list, 1, 64, true)); } - if (add_dl_qos_flow_per_tnl_info_present) { + if (add_dl_qos_flow_per_tnl_info.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, add_dl_qos_flow_per_tnl_info, 1, 3, true)); } - if (qos_flow_failed_to_add_or_modify_list_present) { + if (qos_flow_failed_to_add_or_modify_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_failed_to_add_or_modify_list, 1, 64, true)); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -50744,9 +49358,13 @@ SRSASN_CODE pdu_session_res_modify_resp_transfer_s::unpack(cbit_ref& bref) bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(dl_ngu_up_tnl_info_present, 1)); HANDLE_CODE(bref.unpack(ul_ngu_up_tnl_info_present, 1)); + bool qos_flow_add_or_modify_resp_list_present; HANDLE_CODE(bref.unpack(qos_flow_add_or_modify_resp_list_present, 1)); + bool add_dl_qos_flow_per_tnl_info_present; HANDLE_CODE(bref.unpack(add_dl_qos_flow_per_tnl_info_present, 1)); + bool qos_flow_failed_to_add_or_modify_list_present; HANDLE_CODE(bref.unpack(qos_flow_failed_to_add_or_modify_list_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); if (dl_ngu_up_tnl_info_present) { @@ -50781,28 +49399,28 @@ void pdu_session_res_modify_resp_transfer_s::to_json(json_writer& j) const j.write_fieldname("uL-NGU-UP-TNLInformation"); ul_ngu_up_tnl_info.to_json(j); } - if (qos_flow_add_or_modify_resp_list_present) { + if (qos_flow_add_or_modify_resp_list.size() > 0) { j.start_array("qosFlowAddOrModifyResponseList"); for (const auto& e1 : qos_flow_add_or_modify_resp_list) { e1.to_json(j); } j.end_array(); } - if (add_dl_qos_flow_per_tnl_info_present) { + if (add_dl_qos_flow_per_tnl_info.size() > 0) { j.start_array("additionalDLQosFlowPerTNLInformation"); for (const auto& e1 : add_dl_qos_flow_per_tnl_info) { e1.to_json(j); } j.end_array(); } - if (qos_flow_failed_to_add_or_modify_list_present) { + if (qos_flow_failed_to_add_or_modify_list.size() > 0) { j.start_array("qosFlowFailedToAddOrModifyList"); for (const auto& e1 : qos_flow_failed_to_add_or_modify_list) { e1.to_json(j); } j.end_array(); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -50924,10 +49542,10 @@ const char* pdu_session_res_notify_released_transfer_ext_ies_o::ext_c::types_opt SRSASN_CODE pdu_session_res_notify_released_transfer_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); HANDLE_CODE(cause.pack(bref)); - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -50936,6 +49554,7 @@ SRSASN_CODE pdu_session_res_notify_released_transfer_s::pack(bit_ref& bref) cons SRSASN_CODE pdu_session_res_notify_released_transfer_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); HANDLE_CODE(cause.unpack(bref)); @@ -50950,7 +49569,7 @@ void pdu_session_res_notify_released_transfer_s::to_json(json_writer& j) const j.start_obj(); j.write_fieldname("cause"); cause.to_json(j); - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -51068,17 +49687,17 @@ const char* pdu_session_res_notify_transfer_ext_ies_o::ext_c::types_opts::to_str SRSASN_CODE pdu_session_res_notify_transfer_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(qos_flow_notify_list_present, 1)); - HANDLE_CODE(bref.pack(qos_flow_released_list_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(qos_flow_notify_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(qos_flow_released_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); - if (qos_flow_notify_list_present) { + if (qos_flow_notify_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_notify_list, 1, 64, true)); } - if (qos_flow_released_list_present) { + if (qos_flow_released_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_released_list, 1, 64, true)); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -51087,8 +49706,11 @@ SRSASN_CODE pdu_session_res_notify_transfer_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_notify_transfer_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool qos_flow_notify_list_present; HANDLE_CODE(bref.unpack(qos_flow_notify_list_present, 1)); + bool qos_flow_released_list_present; HANDLE_CODE(bref.unpack(qos_flow_released_list_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); if (qos_flow_notify_list_present) { @@ -51106,21 +49728,21 @@ SRSASN_CODE pdu_session_res_notify_transfer_s::unpack(cbit_ref& bref) void pdu_session_res_notify_transfer_s::to_json(json_writer& j) const { j.start_obj(); - if (qos_flow_notify_list_present) { + if (qos_flow_notify_list.size() > 0) { j.start_array("qosFlowNotifyList"); for (const auto& e1 : qos_flow_notify_list) { e1.to_json(j); } j.end_array(); } - if (qos_flow_released_list_present) { + if (qos_flow_released_list.size() > 0) { j.start_array("qosFlowReleasedList"); for (const auto& e1 : qos_flow_released_list) { e1.to_json(j); } j.end_array(); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -51230,9 +49852,9 @@ const char* pdu_session_res_release_resp_transfer_ext_ies_o::ext_c::types_opts:: SRSASN_CODE pdu_session_res_release_resp_transfer_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -51241,6 +49863,7 @@ SRSASN_CODE pdu_session_res_release_resp_transfer_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_release_resp_transfer_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); if (ie_exts_present) { @@ -51252,7 +49875,7 @@ SRSASN_CODE pdu_session_res_release_resp_transfer_s::unpack(cbit_ref& bref) void pdu_session_res_release_resp_transfer_s::to_json(json_writer& j) const { j.start_obj(); - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -51415,14 +50038,14 @@ SRSASN_CODE security_ind_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(maximum_integrity_protected_data_rate_ul_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); HANDLE_CODE(integrity_protection_ind.pack(bref)); HANDLE_CODE(confidentiality_protection_ind.pack(bref)); if (maximum_integrity_protected_data_rate_ul_present) { HANDLE_CODE(maximum_integrity_protected_data_rate_ul.pack(bref)); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -51432,6 +50055,7 @@ SRSASN_CODE security_ind_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(maximum_integrity_protected_data_rate_ul_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); HANDLE_CODE(integrity_protection_ind.unpack(bref)); @@ -51453,7 +50077,7 @@ void security_ind_s::to_json(json_writer& j) const if (maximum_integrity_protected_data_rate_ul_present) { j.write_str("maximumIntegrityProtectedDataRate-UL", maximum_integrity_protected_data_rate_ul.to_string()); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -51907,7 +50531,7 @@ uint8_t pdu_session_res_setup_request_transfer_ies_o::value_c::types_opts::to_nu return 0; } -template struct asn1::ngap_nr::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pdu_session_res_setup_request_transfer_ies_container::pdu_session_res_setup_request_transfer_ies_container() : pdu_session_aggregate_maximum_bit_rate(130, crit_e::reject), @@ -51958,59 +50582,75 @@ SRSASN_CODE pdu_session_res_setup_request_transfer_ies_container::unpack(cbit_re uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 130: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 130: { pdu_session_aggregate_maximum_bit_rate_present = true; - pdu_session_aggregate_maximum_bit_rate.id = c.id; - pdu_session_aggregate_maximum_bit_rate.crit = c.crit; - pdu_session_aggregate_maximum_bit_rate.value = c.value.pdu_session_aggregate_maximum_bit_rate(); + pdu_session_aggregate_maximum_bit_rate.id = id; + HANDLE_CODE(pdu_session_aggregate_maximum_bit_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_aggregate_maximum_bit_rate.value.unpack(bref)); break; - case 139: + } + case 139: { nof_mandatory_ies--; - ul_ngu_up_tnl_info.id = c.id; - ul_ngu_up_tnl_info.crit = c.crit; - ul_ngu_up_tnl_info.value = c.value.ul_ngu_up_tnl_info(); + ul_ngu_up_tnl_info.id = id; + HANDLE_CODE(ul_ngu_up_tnl_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ul_ngu_up_tnl_info.value.unpack(bref)); break; - case 126: + } + case 126: { add_ul_ngu_up_tnl_info_present = true; - add_ul_ngu_up_tnl_info.id = c.id; - add_ul_ngu_up_tnl_info.crit = c.crit; - add_ul_ngu_up_tnl_info.value = c.value.add_ul_ngu_up_tnl_info(); + add_ul_ngu_up_tnl_info.id = id; + HANDLE_CODE(add_ul_ngu_up_tnl_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_ul_ngu_up_tnl_info.value.unpack(bref)); break; - case 127: + } + case 127: { data_forwarding_not_possible_present = true; - data_forwarding_not_possible.id = c.id; - data_forwarding_not_possible.crit = c.crit; - data_forwarding_not_possible.value = c.value.data_forwarding_not_possible(); + data_forwarding_not_possible.id = id; + HANDLE_CODE(data_forwarding_not_possible.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(data_forwarding_not_possible.value.unpack(bref)); break; - case 134: + } + case 134: { nof_mandatory_ies--; - pdu_session_type.id = c.id; - pdu_session_type.crit = c.crit; - pdu_session_type.value = c.value.pdu_session_type(); + pdu_session_type.id = id; + HANDLE_CODE(pdu_session_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pdu_session_type.value.unpack(bref)); break; - case 138: + } + case 138: { security_ind_present = true; - security_ind.id = c.id; - security_ind.crit = c.crit; - security_ind.value = c.value.security_ind(); + security_ind.id = id; + HANDLE_CODE(security_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_ind.value.unpack(bref)); break; - case 129: + } + case 129: { network_instance_present = true; - network_instance.id = c.id; - network_instance.crit = c.crit; - network_instance.value = c.value.network_instance(); + network_instance.id = id; + HANDLE_CODE(network_instance.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(network_instance.value.unpack(bref)); break; - case 136: + } + case 136: { nof_mandatory_ies--; - qos_flow_setup_request_list.id = c.id; - qos_flow_setup_request_list.crit = c.crit; - qos_flow_setup_request_list.value = c.value.qos_flow_setup_request_list(); + qos_flow_setup_request_list.id = id; + HANDLE_CODE(qos_flow_setup_request_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(qos_flow_setup_request_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -52053,46 +50693,23 @@ void pdu_session_res_setup_request_transfer_ies_container::to_json(json_writer& j.end_obj(); } -// PDUSessionResourceSetupRequestTransfer ::= SEQUENCE -SRSASN_CODE pdu_session_res_setup_request_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pdu_session_res_setup_request_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pdu_session_res_setup_request_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // PDUSessionResourceSetupResponseTransfer ::= SEQUENCE SRSASN_CODE pdu_session_res_setup_resp_transfer_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(add_dl_qos_flow_per_tnl_info_present, 1)); + HANDLE_CODE(bref.pack(add_dl_qos_flow_per_tnl_info.size() > 0, 1)); HANDLE_CODE(bref.pack(security_result_present, 1)); - HANDLE_CODE(bref.pack(qos_flow_failed_to_setup_list_present, 1)); + HANDLE_CODE(bref.pack(qos_flow_failed_to_setup_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(dlqos_flow_per_tnl_info.pack(bref)); - if (add_dl_qos_flow_per_tnl_info_present) { + if (add_dl_qos_flow_per_tnl_info.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, add_dl_qos_flow_per_tnl_info, 1, 3, true)); } if (security_result_present) { HANDLE_CODE(security_result.pack(bref)); } - if (qos_flow_failed_to_setup_list_present) { + if (qos_flow_failed_to_setup_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_failed_to_setup_list, 1, 64, true)); } if (ie_exts_present) { @@ -52104,8 +50721,10 @@ SRSASN_CODE pdu_session_res_setup_resp_transfer_s::pack(bit_ref& bref) const SRSASN_CODE pdu_session_res_setup_resp_transfer_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool add_dl_qos_flow_per_tnl_info_present; HANDLE_CODE(bref.unpack(add_dl_qos_flow_per_tnl_info_present, 1)); HANDLE_CODE(bref.unpack(security_result_present, 1)); + bool qos_flow_failed_to_setup_list_present; HANDLE_CODE(bref.unpack(qos_flow_failed_to_setup_list_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -52130,7 +50749,7 @@ void pdu_session_res_setup_resp_transfer_s::to_json(json_writer& j) const j.start_obj(); j.write_fieldname("dLQosFlowPerTNLInformation"); dlqos_flow_per_tnl_info.to_json(j); - if (add_dl_qos_flow_per_tnl_info_present) { + if (add_dl_qos_flow_per_tnl_info.size() > 0) { j.start_array("additionalDLQosFlowPerTNLInformation"); for (const auto& e1 : add_dl_qos_flow_per_tnl_info) { e1.to_json(j); @@ -52141,7 +50760,7 @@ void pdu_session_res_setup_resp_transfer_s::to_json(json_writer& j) const j.write_fieldname("securityResult"); security_result.to_json(j); } - if (qos_flow_failed_to_setup_list_present) { + if (qos_flow_failed_to_setup_list.size() > 0) { j.start_array("qosFlowFailedToSetupList"); for (const auto& e1 : qos_flow_failed_to_setup_list) { e1.to_json(j); @@ -52276,7 +50895,7 @@ SRSASN_CODE path_switch_request_ack_transfer_s::pack(bit_ref& bref) const bref.pack(ext, 1); HANDLE_CODE(bref.pack(ul_ngu_up_tnl_info_present, 1)); HANDLE_CODE(bref.pack(security_ind_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); if (ul_ngu_up_tnl_info_present) { HANDLE_CODE(ul_ngu_up_tnl_info.pack(bref)); @@ -52284,7 +50903,7 @@ SRSASN_CODE path_switch_request_ack_transfer_s::pack(bit_ref& bref) const if (security_ind_present) { HANDLE_CODE(security_ind.pack(bref)); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -52295,6 +50914,7 @@ SRSASN_CODE path_switch_request_ack_transfer_s::unpack(cbit_ref& bref) bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(ul_ngu_up_tnl_info_present, 1)); HANDLE_CODE(bref.unpack(security_ind_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); if (ul_ngu_up_tnl_info_present) { @@ -52320,7 +50940,7 @@ void path_switch_request_ack_transfer_s::to_json(json_writer& j) const j.write_fieldname("securityIndication"); security_ind.to_json(j); } - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -52518,7 +51138,7 @@ SRSASN_CODE path_switch_request_transfer_s::pack(bit_ref& bref) const bref.pack(ext, 1); HANDLE_CODE(bref.pack(dl_ngu_tnl_info_reused_present, 1)); HANDLE_CODE(bref.pack(user_plane_security_info_present, 1)); - HANDLE_CODE(bref.pack(ie_exts_present, 1)); + HANDLE_CODE(bref.pack(ie_exts.size() > 0, 1)); HANDLE_CODE(dl_ngu_up_tnl_info.pack(bref)); if (dl_ngu_tnl_info_reused_present) { @@ -52528,7 +51148,7 @@ SRSASN_CODE path_switch_request_transfer_s::pack(bit_ref& bref) const HANDLE_CODE(user_plane_security_info.pack(bref)); } HANDLE_CODE(pack_dyn_seq_of(bref, qos_flow_accepted_list, 1, 64, true)); - if (ie_exts_present) { + if (ie_exts.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ie_exts, 1, 65535, true)); } @@ -52539,6 +51159,7 @@ SRSASN_CODE path_switch_request_transfer_s::unpack(cbit_ref& bref) bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(dl_ngu_tnl_info_reused_present, 1)); HANDLE_CODE(bref.unpack(user_plane_security_info_present, 1)); + bool ie_exts_present; HANDLE_CODE(bref.unpack(ie_exts_present, 1)); HANDLE_CODE(dl_ngu_up_tnl_info.unpack(bref)); @@ -52572,7 +51193,7 @@ void path_switch_request_transfer_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (ie_exts_present) { + if (ie_exts.size() > 0) { j.write_fieldname("iE-Extensions"); } j.end_obj(); @@ -52751,16 +51372,16 @@ void secondary_rat_data_usage_report_transfer_s::to_json(json_writer& j) const SRSASN_CODE source_ngran_node_to_target_ngran_node_transparent_container_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(pdu_session_res_info_list_present, 1)); - HANDLE_CODE(bref.pack(erab_info_list_present, 1)); + HANDLE_CODE(bref.pack(pdu_session_res_info_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(erab_info_list.size() > 0, 1)); HANDLE_CODE(bref.pack(idx_to_rfsp_present, 1)); HANDLE_CODE(bref.pack(ie_exts_present, 1)); HANDLE_CODE(rrc_container.pack(bref)); - if (pdu_session_res_info_list_present) { + if (pdu_session_res_info_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, pdu_session_res_info_list, 1, 256, true)); } - if (erab_info_list_present) { + if (erab_info_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, erab_info_list, 1, 256, true)); } HANDLE_CODE(target_cell_id.pack(bref)); @@ -52777,7 +51398,9 @@ SRSASN_CODE source_ngran_node_to_target_ngran_node_transparent_container_s::pack SRSASN_CODE source_ngran_node_to_target_ngran_node_transparent_container_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool pdu_session_res_info_list_present; HANDLE_CODE(bref.unpack(pdu_session_res_info_list_present, 1)); + bool erab_info_list_present; HANDLE_CODE(bref.unpack(erab_info_list_present, 1)); HANDLE_CODE(bref.unpack(idx_to_rfsp_present, 1)); HANDLE_CODE(bref.unpack(ie_exts_present, 1)); @@ -52804,14 +51427,14 @@ void source_ngran_node_to_target_ngran_node_transparent_container_s::to_json(jso { j.start_obj(); j.write_str("rRCContainer", rrc_container.to_string()); - if (pdu_session_res_info_list_present) { + if (pdu_session_res_info_list.size() > 0) { j.start_array("pDUSessionResourceInformationList"); for (const auto& e1 : pdu_session_res_info_list) { e1.to_json(j); } j.end_array(); } - if (erab_info_list_present) { + if (erab_info_list.size() > 0) { j.start_array("e-RABInformationList"); for (const auto& e1 : erab_info_list) { e1.to_json(j); diff --git a/lib/src/asn1/rrc.cc b/lib/src/asn1/rrc.cc index 150f5971f..22860179a 100644 --- a/lib/src/asn1/rrc.cc +++ b/lib/src/asn1/rrc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/bcch_msg.cc b/lib/src/asn1/rrc/bcch_msg.cc index 4aa83cf8c..020dc25d0 100644 --- a/lib/src/asn1/rrc/bcch_msg.cc +++ b/lib/src/asn1/rrc/bcch_msg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/common.cc b/lib/src/asn1/rrc/common.cc index 41621adf2..652d10c2e 100644 --- a/lib/src/asn1/rrc/common.cc +++ b/lib/src/asn1/rrc/common.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/common_ext.cc b/lib/src/asn1/rrc/common_ext.cc index 836f9ac7f..ebb024dad 100644 --- a/lib/src/asn1/rrc/common_ext.cc +++ b/lib/src/asn1/rrc/common_ext.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/dl_ccch_msg.cc b/lib/src/asn1/rrc/dl_ccch_msg.cc index ff0c6c2f8..281349388 100644 --- a/lib/src/asn1/rrc/dl_ccch_msg.cc +++ b/lib/src/asn1/rrc/dl_ccch_msg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/dl_dcch_msg.cc b/lib/src/asn1/rrc/dl_dcch_msg.cc index 136e88df1..be2ec0c67 100644 --- a/lib/src/asn1/rrc/dl_dcch_msg.cc +++ b/lib/src/asn1/rrc/dl_dcch_msg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/ho_cmd.cc b/lib/src/asn1/rrc/ho_cmd.cc index 7dd1a05cf..6e0f57c12 100644 --- a/lib/src/asn1/rrc/ho_cmd.cc +++ b/lib/src/asn1/rrc/ho_cmd.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/meascfg.cc b/lib/src/asn1/rrc/meascfg.cc index 3a888c12b..6f05115f0 100644 --- a/lib/src/asn1/rrc/meascfg.cc +++ b/lib/src/asn1/rrc/meascfg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/paging.cc b/lib/src/asn1/rrc/paging.cc index 4c63956de..2b999e6f3 100644 --- a/lib/src/asn1/rrc/paging.cc +++ b/lib/src/asn1/rrc/paging.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/phy_ded.cc b/lib/src/asn1/rrc/phy_ded.cc index 156c05f0d..54245b26f 100644 --- a/lib/src/asn1/rrc/phy_ded.cc +++ b/lib/src/asn1/rrc/phy_ded.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/rr_common.cc b/lib/src/asn1/rrc/rr_common.cc index f6a43cf55..97ae4b124 100644 --- a/lib/src/asn1/rrc/rr_common.cc +++ b/lib/src/asn1/rrc/rr_common.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/rr_ded.cc b/lib/src/asn1/rrc/rr_ded.cc index 47dfb61dd..f6bd93404 100644 --- a/lib/src/asn1/rrc/rr_ded.cc +++ b/lib/src/asn1/rrc/rr_ded.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/security.cc b/lib/src/asn1/rrc/security.cc index ce556ccd7..ffba00f59 100644 --- a/lib/src/asn1/rrc/security.cc +++ b/lib/src/asn1/rrc/security.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/si.cc b/lib/src/asn1/rrc/si.cc index 0ccdce4dc..80b23294e 100644 --- a/lib/src/asn1/rrc/si.cc +++ b/lib/src/asn1/rrc/si.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/uecap.cc b/lib/src/asn1/rrc/uecap.cc index 4718c554c..8c80ab5f4 100644 --- a/lib/src/asn1/rrc/uecap.cc +++ b/lib/src/asn1/rrc/uecap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/ul_ccch_msg.cc b/lib/src/asn1/rrc/ul_ccch_msg.cc index 9adbf9f5e..be24c8a49 100644 --- a/lib/src/asn1/rrc/ul_ccch_msg.cc +++ b/lib/src/asn1/rrc/ul_ccch_msg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc/ul_dcch_msg.cc b/lib/src/asn1/rrc/ul_dcch_msg.cc index e11254a37..b6e618d5e 100644 --- a/lib/src/asn1/rrc/ul_dcch_msg.cc +++ b/lib/src/asn1/rrc/ul_dcch_msg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc_nbiot.cc b/lib/src/asn1/rrc_nbiot.cc index cf36bbfe0..bb75f8a54 100644 --- a/lib/src/asn1/rrc_nbiot.cc +++ b/lib/src/asn1/rrc_nbiot.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/asn1/rrc_nr.cc b/lib/src/asn1/rrc_nr.cc index 6780b7567..1f9026813 100644 --- a/lib/src/asn1/rrc_nr.cc +++ b/lib/src/asn1/rrc_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -381,10 +381,10 @@ void eutra_freq_neigh_cell_info_s::to_json(json_writer& j) const // EUTRA-MultiBandInfo ::= SEQUENCE SRSASN_CODE eutra_multi_band_info_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(eutra_ns_pmax_list_present, 1)); + HANDLE_CODE(bref.pack(eutra_ns_pmax_list.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, eutra_freq_band_ind, (uint16_t)1u, (uint16_t)256u)); - if (eutra_ns_pmax_list_present) { + if (eutra_ns_pmax_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, eutra_ns_pmax_list, 1, 8)); } @@ -392,6 +392,7 @@ SRSASN_CODE eutra_multi_band_info_s::pack(bit_ref& bref) const } SRSASN_CODE eutra_multi_band_info_s::unpack(cbit_ref& bref) { + bool eutra_ns_pmax_list_present; HANDLE_CODE(bref.unpack(eutra_ns_pmax_list_present, 1)); HANDLE_CODE(unpack_integer(eutra_freq_band_ind, bref, (uint16_t)1u, (uint16_t)256u)); @@ -405,7 +406,7 @@ void eutra_multi_band_info_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("eutra-FreqBandIndicator", eutra_freq_band_ind); - if (eutra_ns_pmax_list_present) { + if (eutra_ns_pmax_list.size() > 0) { j.start_array("eutra-NS-PmaxList"); for (const auto& e1 : eutra_ns_pmax_list) { e1.to_json(j); @@ -538,12 +539,12 @@ void inter_freq_neigh_cell_info_s::to_json(json_writer& j) const SRSASN_CODE nr_multi_band_info_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(freq_band_ind_nr_present, 1)); - HANDLE_CODE(bref.pack(nr_ns_pmax_list_present, 1)); + HANDLE_CODE(bref.pack(nr_ns_pmax_list.size() > 0, 1)); if (freq_band_ind_nr_present) { HANDLE_CODE(pack_integer(bref, freq_band_ind_nr, (uint16_t)1u, (uint16_t)1024u)); } - if (nr_ns_pmax_list_present) { + if (nr_ns_pmax_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, nr_ns_pmax_list, 1, 8)); } @@ -552,6 +553,7 @@ SRSASN_CODE nr_multi_band_info_s::pack(bit_ref& bref) const SRSASN_CODE nr_multi_band_info_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(freq_band_ind_nr_present, 1)); + bool nr_ns_pmax_list_present; HANDLE_CODE(bref.unpack(nr_ns_pmax_list_present, 1)); if (freq_band_ind_nr_present) { @@ -569,7 +571,7 @@ void nr_multi_band_info_s::to_json(json_writer& j) const if (freq_band_ind_nr_present) { j.write_int("freqBandIndicatorNR", freq_band_ind_nr); } - if (nr_ns_pmax_list_present) { + if (nr_ns_pmax_list.size() > 0) { j.start_array("nr-NS-PmaxList"); for (const auto& e1 : nr_ns_pmax_list) { e1.to_json(j); @@ -743,8 +745,8 @@ const char* cell_resel_sub_prio_opts::to_number_string() const SRSASN_CODE ctrl_res_set_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(tci_states_pdcch_to_add_list_present, 1)); - HANDLE_CODE(bref.pack(tci_states_pdcch_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(tci_states_pdcch_to_add_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(tci_states_pdcch_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(tci_present_in_dci_present, 1)); HANDLE_CODE(bref.pack(pdcch_dmrs_scrambling_id_present, 1)); @@ -753,10 +755,10 @@ SRSASN_CODE ctrl_res_set_s::pack(bit_ref& bref) const HANDLE_CODE(pack_integer(bref, dur, (uint8_t)1u, (uint8_t)3u)); HANDLE_CODE(cce_reg_map_type.pack(bref)); HANDLE_CODE(precoder_granularity.pack(bref)); - if (tci_states_pdcch_to_add_list_present) { + if (tci_states_pdcch_to_add_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, tci_states_pdcch_to_add_list, 1, 64, integer_packer(0, 127))); } - if (tci_states_pdcch_to_release_list_present) { + if (tci_states_pdcch_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, tci_states_pdcch_to_release_list, 1, 64, integer_packer(0, 127))); } if (pdcch_dmrs_scrambling_id_present) { @@ -768,7 +770,9 @@ SRSASN_CODE ctrl_res_set_s::pack(bit_ref& bref) const SRSASN_CODE ctrl_res_set_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool tci_states_pdcch_to_add_list_present; HANDLE_CODE(bref.unpack(tci_states_pdcch_to_add_list_present, 1)); + bool tci_states_pdcch_to_release_list_present; HANDLE_CODE(bref.unpack(tci_states_pdcch_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(tci_present_in_dci_present, 1)); HANDLE_CODE(bref.unpack(pdcch_dmrs_scrambling_id_present, 1)); @@ -799,14 +803,14 @@ void ctrl_res_set_s::to_json(json_writer& j) const j.write_fieldname("cce-REG-MappingType"); cce_reg_map_type.to_json(j); j.write_str("precoderGranularity", precoder_granularity.to_string()); - if (tci_states_pdcch_to_add_list_present) { + if (tci_states_pdcch_to_add_list.size() > 0) { j.start_array("tci-StatesPDCCH-ToAddList"); for (const auto& e1 : tci_states_pdcch_to_add_list) { j.write_int(e1); } j.end_array(); } - if (tci_states_pdcch_to_release_list_present) { + if (tci_states_pdcch_to_release_list.size() > 0) { j.start_array("tci-StatesPDCCH-ToReleaseList"); for (const auto& e1 : tci_states_pdcch_to_release_list) { j.write_int(e1); @@ -2567,21 +2571,21 @@ void bwp_s::to_json(json_writer& j) const // CarrierFreqEUTRA ::= SEQUENCE SRSASN_CODE carrier_freq_eutra_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(eutra_multi_band_info_list_present, 1)); - HANDLE_CODE(bref.pack(eutra_freq_neigh_cell_list_present, 1)); - HANDLE_CODE(bref.pack(eutra_black_cell_list_present, 1)); + HANDLE_CODE(bref.pack(eutra_multi_band_info_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(eutra_freq_neigh_cell_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(eutra_black_cell_list.size() > 0, 1)); HANDLE_CODE(bref.pack(cell_resel_prio_present, 1)); HANDLE_CODE(bref.pack(cell_resel_sub_prio_present, 1)); HANDLE_CODE(bref.pack(thresh_x_q_present, 1)); HANDLE_CODE(pack_integer(bref, carrier_freq, (uint32_t)0u, (uint32_t)262143u)); - if (eutra_multi_band_info_list_present) { + if (eutra_multi_band_info_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, eutra_multi_band_info_list, 1, 8)); } - if (eutra_freq_neigh_cell_list_present) { + if (eutra_freq_neigh_cell_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, eutra_freq_neigh_cell_list, 1, 8)); } - if (eutra_black_cell_list_present) { + if (eutra_black_cell_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, eutra_black_cell_list, 1, 16)); } HANDLE_CODE(allowed_meas_bw.pack(bref)); @@ -2606,8 +2610,11 @@ SRSASN_CODE carrier_freq_eutra_s::pack(bit_ref& bref) const } SRSASN_CODE carrier_freq_eutra_s::unpack(cbit_ref& bref) { + bool eutra_multi_band_info_list_present; HANDLE_CODE(bref.unpack(eutra_multi_band_info_list_present, 1)); + bool eutra_freq_neigh_cell_list_present; HANDLE_CODE(bref.unpack(eutra_freq_neigh_cell_list_present, 1)); + bool eutra_black_cell_list_present; HANDLE_CODE(bref.unpack(eutra_black_cell_list_present, 1)); HANDLE_CODE(bref.unpack(cell_resel_prio_present, 1)); HANDLE_CODE(bref.unpack(cell_resel_sub_prio_present, 1)); @@ -2647,21 +2654,21 @@ void carrier_freq_eutra_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("carrierFreq", carrier_freq); - if (eutra_multi_band_info_list_present) { + if (eutra_multi_band_info_list.size() > 0) { j.start_array("eutra-multiBandInfoList"); for (const auto& e1 : eutra_multi_band_info_list) { e1.to_json(j); } j.end_array(); } - if (eutra_freq_neigh_cell_list_present) { + if (eutra_freq_neigh_cell_list.size() > 0) { j.start_array("eutra-FreqNeighCellList"); for (const auto& e1 : eutra_freq_neigh_cell_list) { e1.to_json(j); } j.end_array(); } - if (eutra_black_cell_list_present) { + if (eutra_black_cell_list.size() > 0) { j.start_array("eutra-BlackCellList"); for (const auto& e1 : eutra_black_cell_list) { e1.to_json(j); @@ -2695,8 +2702,8 @@ void carrier_freq_eutra_s::to_json(json_writer& j) const SRSASN_CODE inter_freq_carrier_freq_info_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(freq_band_list_present, 1)); - HANDLE_CODE(bref.pack(freq_band_list_sul_present, 1)); + HANDLE_CODE(bref.pack(freq_band_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(freq_band_list_sul.size() > 0, 1)); HANDLE_CODE(bref.pack(nrof_ss_blocks_to_average_present, 1)); HANDLE_CODE(bref.pack(abs_thresh_ss_blocks_consolidation_present, 1)); HANDLE_CODE(bref.pack(smtc_present, 1)); @@ -2710,14 +2717,14 @@ SRSASN_CODE inter_freq_carrier_freq_info_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(cell_resel_prio_present, 1)); HANDLE_CODE(bref.pack(cell_resel_sub_prio_present, 1)); HANDLE_CODE(bref.pack(q_offset_freq_present, 1)); - HANDLE_CODE(bref.pack(inter_freq_neigh_cell_list_present, 1)); - HANDLE_CODE(bref.pack(inter_freq_black_cell_list_present, 1)); + HANDLE_CODE(bref.pack(inter_freq_neigh_cell_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(inter_freq_black_cell_list.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, dl_carrier_freq, (uint32_t)0u, (uint32_t)3279165u)); - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_band_list, 1, 8)); } - if (freq_band_list_sul_present) { + if (freq_band_list_sul.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_band_list_sul, 1, 8)); } if (nrof_ss_blocks_to_average_present) { @@ -2766,10 +2773,10 @@ SRSASN_CODE inter_freq_carrier_freq_info_s::pack(bit_ref& bref) const if (q_offset_freq_present) { HANDLE_CODE(q_offset_freq.pack(bref)); } - if (inter_freq_neigh_cell_list_present) { + if (inter_freq_neigh_cell_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, inter_freq_neigh_cell_list, 1, 16)); } - if (inter_freq_black_cell_list_present) { + if (inter_freq_black_cell_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, inter_freq_black_cell_list, 1, 16)); } @@ -2778,7 +2785,9 @@ SRSASN_CODE inter_freq_carrier_freq_info_s::pack(bit_ref& bref) const SRSASN_CODE inter_freq_carrier_freq_info_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool freq_band_list_present; HANDLE_CODE(bref.unpack(freq_band_list_present, 1)); + bool freq_band_list_sul_present; HANDLE_CODE(bref.unpack(freq_band_list_sul_present, 1)); HANDLE_CODE(bref.unpack(nrof_ss_blocks_to_average_present, 1)); HANDLE_CODE(bref.unpack(abs_thresh_ss_blocks_consolidation_present, 1)); @@ -2793,7 +2802,9 @@ SRSASN_CODE inter_freq_carrier_freq_info_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(cell_resel_prio_present, 1)); HANDLE_CODE(bref.unpack(cell_resel_sub_prio_present, 1)); HANDLE_CODE(bref.unpack(q_offset_freq_present, 1)); + bool inter_freq_neigh_cell_list_present; HANDLE_CODE(bref.unpack(inter_freq_neigh_cell_list_present, 1)); + bool inter_freq_black_cell_list_present; HANDLE_CODE(bref.unpack(inter_freq_black_cell_list_present, 1)); HANDLE_CODE(unpack_integer(dl_carrier_freq, bref, (uint32_t)0u, (uint32_t)3279165u)); @@ -2862,14 +2873,14 @@ void inter_freq_carrier_freq_info_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("dl-CarrierFreq", dl_carrier_freq); - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { j.start_array("frequencyBandList"); for (const auto& e1 : freq_band_list) { e1.to_json(j); } j.end_array(); } - if (freq_band_list_sul_present) { + if (freq_band_list_sul.size() > 0) { j.start_array("frequencyBandListSUL"); for (const auto& e1 : freq_band_list_sul) { e1.to_json(j); @@ -2930,14 +2941,14 @@ void inter_freq_carrier_freq_info_s::to_json(json_writer& j) const if (q_offset_freq_present) { j.write_str("q-OffsetFreq", q_offset_freq.to_string()); } - if (inter_freq_neigh_cell_list_present) { + if (inter_freq_neigh_cell_list.size() > 0) { j.start_array("interFreqNeighCellList"); for (const auto& e1 : inter_freq_neigh_cell_list) { e1.to_json(j); } j.end_array(); } - if (inter_freq_black_cell_list_present) { + if (inter_freq_black_cell_list.size() > 0) { j.start_array("interFreqBlackCellList"); for (const auto& e1 : inter_freq_black_cell_list) { e1.to_json(j); @@ -3014,7 +3025,7 @@ SRSASN_CODE pdcch_cfg_common_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(ctrl_res_set_zero_present, 1)); HANDLE_CODE(bref.pack(common_ctrl_res_set_present, 1)); HANDLE_CODE(bref.pack(search_space_zero_present, 1)); - HANDLE_CODE(bref.pack(common_search_space_list_present, 1)); + HANDLE_CODE(bref.pack(common_search_space_list.size() > 0, 1)); HANDLE_CODE(bref.pack(search_space_sib1_present, 1)); HANDLE_CODE(bref.pack(search_space_other_sys_info_present, 1)); HANDLE_CODE(bref.pack(paging_search_space_present, 1)); @@ -3029,7 +3040,7 @@ SRSASN_CODE pdcch_cfg_common_s::pack(bit_ref& bref) const if (search_space_zero_present) { HANDLE_CODE(pack_integer(bref, search_space_zero, (uint8_t)0u, (uint8_t)15u)); } - if (common_search_space_list_present) { + if (common_search_space_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, common_search_space_list, 1, 4)); } if (search_space_sib1_present) { @@ -3067,6 +3078,7 @@ SRSASN_CODE pdcch_cfg_common_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(ctrl_res_set_zero_present, 1)); HANDLE_CODE(bref.unpack(common_ctrl_res_set_present, 1)); HANDLE_CODE(bref.unpack(search_space_zero_present, 1)); + bool common_search_space_list_present; HANDLE_CODE(bref.unpack(common_search_space_list_present, 1)); HANDLE_CODE(bref.unpack(search_space_sib1_present, 1)); HANDLE_CODE(bref.unpack(search_space_other_sys_info_present, 1)); @@ -3128,7 +3140,7 @@ void pdcch_cfg_common_s::to_json(json_writer& j) const if (search_space_zero_present) { j.write_int("searchSpaceZero", search_space_zero); } - if (common_search_space_list_present) { + if (common_search_space_list.size() > 0) { j.start_array("commonSearchSpaceList"); for (const auto& e1 : common_search_space_list) { e1.to_json(j); @@ -3554,9 +3566,9 @@ const char* pdcch_cfg_common_s::first_pdcch_monitoring_occasion_of_po_c_::types_ SRSASN_CODE pdsch_cfg_common_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(pdsch_time_domain_alloc_list_present, 1)); + HANDLE_CODE(bref.pack(pdsch_time_domain_alloc_list.size() > 0, 1)); - if (pdsch_time_domain_alloc_list_present) { + if (pdsch_time_domain_alloc_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, pdsch_time_domain_alloc_list, 1, 16)); } @@ -3565,6 +3577,7 @@ SRSASN_CODE pdsch_cfg_common_s::pack(bit_ref& bref) const SRSASN_CODE pdsch_cfg_common_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool pdsch_time_domain_alloc_list_present; HANDLE_CODE(bref.unpack(pdsch_time_domain_alloc_list_present, 1)); if (pdsch_time_domain_alloc_list_present) { @@ -3576,7 +3589,7 @@ SRSASN_CODE pdsch_cfg_common_s::unpack(cbit_ref& bref) void pdsch_cfg_common_s::to_json(json_writer& j) const { j.start_obj(); - if (pdsch_time_domain_alloc_list_present) { + if (pdsch_time_domain_alloc_list.size() > 0) { j.start_array("pdsch-TimeDomainAllocationList"); for (const auto& e1 : pdsch_time_domain_alloc_list) { e1.to_json(j); @@ -3695,11 +3708,11 @@ SRSASN_CODE pusch_cfg_common_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(group_hop_enabled_transform_precoding_present, 1)); - HANDLE_CODE(bref.pack(pusch_time_domain_alloc_list_present, 1)); + HANDLE_CODE(bref.pack(pusch_time_domain_alloc_list.size() > 0, 1)); HANDLE_CODE(bref.pack(msg3_delta_preamb_present, 1)); HANDLE_CODE(bref.pack(p0_nominal_with_grant_present, 1)); - if (pusch_time_domain_alloc_list_present) { + if (pusch_time_domain_alloc_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, pusch_time_domain_alloc_list, 1, 16)); } if (msg3_delta_preamb_present) { @@ -3715,6 +3728,7 @@ SRSASN_CODE pusch_cfg_common_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(group_hop_enabled_transform_precoding_present, 1)); + bool pusch_time_domain_alloc_list_present; HANDLE_CODE(bref.unpack(pusch_time_domain_alloc_list_present, 1)); HANDLE_CODE(bref.unpack(msg3_delta_preamb_present, 1)); HANDLE_CODE(bref.unpack(p0_nominal_with_grant_present, 1)); @@ -3737,7 +3751,7 @@ void pusch_cfg_common_s::to_json(json_writer& j) const if (group_hop_enabled_transform_precoding_present) { j.write_str("groupHoppingEnabledTransformPrecoding", "enabled"); } - if (pusch_time_domain_alloc_list_present) { + if (pusch_time_domain_alloc_list.size() > 0) { j.start_array("pusch-TimeDomainAllocationList"); for (const auto& e1 : pusch_time_domain_alloc_list) { e1.to_json(j); @@ -4493,61 +4507,6 @@ uint8_t sib_type_info_s::type_opts::to_number() const return map_enum_number(options, 8, value, "sib_type_info_s::type_e_"); } -// SetupRelease{ElementTypeParam} ::= CHOICE -template -void setup_release_c::set(typename types::options e) -{ - type_ = e; -} -template -void setup_release_c::to_json(json_writer& j) const -{ - j.start_obj(); - switch (type_) { - case types::release: - break; - case types::setup: - break; - default: - log_invalid_choice_id(type_, "setup_release_c"); - } - j.end_obj(); -} -template -SRSASN_CODE setup_release_c::pack(bit_ref& bref) const -{ - type_.pack(bref); - switch (type_) { - case types::release: - break; - case types::setup: - HANDLE_CODE(c.pack(bref)); - break; - default: - log_invalid_choice_id(type_, "setup_release_c"); - return SRSASN_ERROR_ENCODE_FAIL; - } - return SRSASN_SUCCESS; -} -template -SRSASN_CODE setup_release_c::unpack(cbit_ref& bref) -{ - types e; - e.unpack(bref); - set(e); - switch (type_) { - case types::release: - break; - case types::setup: - HANDLE_CODE(c.unpack(bref)); - break; - default: - log_invalid_choice_id(type_, "setup_release_c"); - return SRSASN_ERROR_DECODE_FAIL; - } - return SRSASN_SUCCESS; -} - // UAC-BarringPerCat ::= SEQUENCE SRSASN_CODE uac_barr_per_cat_s::pack(bit_ref& bref) const { @@ -4752,12 +4711,12 @@ void freq_info_dl_sib_s::to_json(json_writer& j) const SRSASN_CODE freq_info_ul_sib_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(freq_band_list_present, 1)); + HANDLE_CODE(bref.pack(freq_band_list.size() > 0, 1)); HANDLE_CODE(bref.pack(absolute_freq_point_a_present, 1)); HANDLE_CODE(bref.pack(p_max_present, 1)); HANDLE_CODE(bref.pack(freq_shift7p5khz_present, 1)); - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_band_list, 1, 8)); } if (absolute_freq_point_a_present) { @@ -4773,6 +4732,7 @@ SRSASN_CODE freq_info_ul_sib_s::pack(bit_ref& bref) const SRSASN_CODE freq_info_ul_sib_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool freq_band_list_present; HANDLE_CODE(bref.unpack(freq_band_list_present, 1)); HANDLE_CODE(bref.unpack(absolute_freq_point_a_present, 1)); HANDLE_CODE(bref.unpack(p_max_present, 1)); @@ -4794,7 +4754,7 @@ SRSASN_CODE freq_info_ul_sib_s::unpack(cbit_ref& bref) void freq_info_ul_sib_s::to_json(json_writer& j) const { j.start_obj(); - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { j.start_array("frequencyBandList"); for (const auto& e1 : freq_band_list) { e1.to_json(j); @@ -6007,8 +5967,8 @@ SRSASN_CODE sib2_s::intra_freq_cell_resel_info_s_::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(q_rx_lev_min_sul_present, 1)); HANDLE_CODE(bref.pack(q_qual_min_present, 1)); HANDLE_CODE(bref.pack(s_intra_search_q_present, 1)); - HANDLE_CODE(bref.pack(freq_band_list_present, 1)); - HANDLE_CODE(bref.pack(freq_band_list_sul_present, 1)); + HANDLE_CODE(bref.pack(freq_band_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(freq_band_list_sul.size() > 0, 1)); HANDLE_CODE(bref.pack(p_max_present, 1)); HANDLE_CODE(bref.pack(smtc_present, 1)); HANDLE_CODE(bref.pack(ss_rssi_meas_present, 1)); @@ -6026,10 +5986,10 @@ SRSASN_CODE sib2_s::intra_freq_cell_resel_info_s_::pack(bit_ref& bref) const HANDLE_CODE(pack_integer(bref, s_intra_search_q, (uint8_t)0u, (uint8_t)31u)); } HANDLE_CODE(pack_integer(bref, t_resel_nr, (uint8_t)0u, (uint8_t)7u)); - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_band_list, 1, 8)); } - if (freq_band_list_sul_present) { + if (freq_band_list_sul.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_band_list_sul, 1, 8)); } if (p_max_present) { @@ -6068,7 +6028,9 @@ SRSASN_CODE sib2_s::intra_freq_cell_resel_info_s_::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(q_rx_lev_min_sul_present, 1)); HANDLE_CODE(bref.unpack(q_qual_min_present, 1)); HANDLE_CODE(bref.unpack(s_intra_search_q_present, 1)); + bool freq_band_list_present; HANDLE_CODE(bref.unpack(freq_band_list_present, 1)); + bool freq_band_list_sul_present; HANDLE_CODE(bref.unpack(freq_band_list_sul_present, 1)); HANDLE_CODE(bref.unpack(p_max_present, 1)); HANDLE_CODE(bref.unpack(smtc_present, 1)); @@ -6139,14 +6101,14 @@ void sib2_s::intra_freq_cell_resel_info_s_::to_json(json_writer& j) const j.write_int("s-IntraSearchQ", s_intra_search_q); } j.write_int("t-ReselectionNR", t_resel_nr); - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { j.start_array("frequencyBandList"); for (const auto& e1 : freq_band_list) { e1.to_json(j); } j.end_array(); } - if (freq_band_list_sul_present) { + if (freq_band_list_sul.size() > 0) { j.start_array("frequencyBandListSUL"); for (const auto& e1 : freq_band_list_sul) { e1.to_json(j); @@ -6182,17 +6144,17 @@ void sib2_s::intra_freq_cell_resel_info_s_::to_json(json_writer& j) const SRSASN_CODE sib3_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(intra_freq_neigh_cell_list_present, 1)); - HANDLE_CODE(bref.pack(intra_freq_black_cell_list_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(intra_freq_neigh_cell_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(intra_freq_black_cell_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); - if (intra_freq_neigh_cell_list_present) { + if (intra_freq_neigh_cell_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, intra_freq_neigh_cell_list, 1, 16)); } - if (intra_freq_black_cell_list_present) { + if (intra_freq_black_cell_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, intra_freq_black_cell_list, 1, 16)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -6201,8 +6163,11 @@ SRSASN_CODE sib3_s::pack(bit_ref& bref) const SRSASN_CODE sib3_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool intra_freq_neigh_cell_list_present; HANDLE_CODE(bref.unpack(intra_freq_neigh_cell_list_present, 1)); + bool intra_freq_black_cell_list_present; HANDLE_CODE(bref.unpack(intra_freq_black_cell_list_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); if (intra_freq_neigh_cell_list_present) { @@ -6220,21 +6185,21 @@ SRSASN_CODE sib3_s::unpack(cbit_ref& bref) void sib3_s::to_json(json_writer& j) const { j.start_obj(); - if (intra_freq_neigh_cell_list_present) { + if (intra_freq_neigh_cell_list.size() > 0) { j.start_array("intraFreqNeighCellList"); for (const auto& e1 : intra_freq_neigh_cell_list) { e1.to_json(j); } j.end_array(); } - if (intra_freq_black_cell_list_present) { + if (intra_freq_black_cell_list.size() > 0) { j.start_array("intraFreqBlackCellList"); for (const auto& e1 : intra_freq_black_cell_list) { e1.to_json(j); } j.end_array(); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } j.end_obj(); @@ -6244,10 +6209,10 @@ void sib3_s::to_json(json_writer& j) const SRSASN_CODE sib4_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, inter_freq_carrier_freq_list, 1, 8)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -6256,6 +6221,7 @@ SRSASN_CODE sib4_s::pack(bit_ref& bref) const SRSASN_CODE sib4_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(unpack_dyn_seq_of(inter_freq_carrier_freq_list, bref, 1, 8)); @@ -6273,7 +6239,7 @@ void sib4_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } j.end_obj(); @@ -6283,18 +6249,18 @@ void sib4_s::to_json(json_writer& j) const SRSASN_CODE sib5_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(carrier_freq_list_eutra_present, 1)); + HANDLE_CODE(bref.pack(carrier_freq_list_eutra.size() > 0, 1)); HANDLE_CODE(bref.pack(t_resel_eutra_sf_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); - if (carrier_freq_list_eutra_present) { + if (carrier_freq_list_eutra.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, carrier_freq_list_eutra, 1, 8)); } HANDLE_CODE(pack_integer(bref, t_resel_eutra, (uint8_t)0u, (uint8_t)7u)); if (t_resel_eutra_sf_present) { HANDLE_CODE(t_resel_eutra_sf.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -6303,8 +6269,10 @@ SRSASN_CODE sib5_s::pack(bit_ref& bref) const SRSASN_CODE sib5_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool carrier_freq_list_eutra_present; HANDLE_CODE(bref.unpack(carrier_freq_list_eutra_present, 1)); HANDLE_CODE(bref.unpack(t_resel_eutra_sf_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); if (carrier_freq_list_eutra_present) { @@ -6323,7 +6291,7 @@ SRSASN_CODE sib5_s::unpack(cbit_ref& bref) void sib5_s::to_json(json_writer& j) const { j.start_obj(); - if (carrier_freq_list_eutra_present) { + if (carrier_freq_list_eutra.size() > 0) { j.start_array("carrierFreqListEUTRA"); for (const auto& e1 : carrier_freq_list_eutra) { e1.to_json(j); @@ -6335,7 +6303,7 @@ void sib5_s::to_json(json_writer& j) const j.write_fieldname("t-ReselectionEUTRA-SF"); t_resel_eutra_sf.to_json(j); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } j.end_obj(); @@ -6345,12 +6313,12 @@ void sib5_s::to_json(json_writer& j) const SRSASN_CODE sib6_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(msg_id.pack(bref)); HANDLE_CODE(serial_num.pack(bref)); HANDLE_CODE(warning_type.pack(bref)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -6359,6 +6327,7 @@ SRSASN_CODE sib6_s::pack(bit_ref& bref) const SRSASN_CODE sib6_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(msg_id.unpack(bref)); @@ -6376,7 +6345,7 @@ void sib6_s::to_json(json_writer& j) const j.write_str("messageIdentifier", msg_id.to_string()); j.write_str("serialNumber", serial_num.to_string()); j.write_str("warningType", warning_type.to_string()); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } j.end_obj(); @@ -6387,7 +6356,7 @@ SRSASN_CODE sib7_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(data_coding_scheme_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(msg_id.pack(bref)); HANDLE_CODE(serial_num.pack(bref)); @@ -6397,7 +6366,7 @@ SRSASN_CODE sib7_s::pack(bit_ref& bref) const if (data_coding_scheme_present) { HANDLE_CODE(data_coding_scheme.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -6407,6 +6376,7 @@ SRSASN_CODE sib7_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(data_coding_scheme_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(msg_id.unpack(bref)); @@ -6434,7 +6404,7 @@ void sib7_s::to_json(json_writer& j) const if (data_coding_scheme_present) { j.write_str("dataCodingScheme", data_coding_scheme.to_string()); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } j.end_obj(); @@ -6451,8 +6421,8 @@ SRSASN_CODE sib8_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(data_coding_scheme_present, 1)); - HANDLE_CODE(bref.pack(warning_area_coordinates_segment_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(warning_area_coordinates_segment.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(msg_id.pack(bref)); HANDLE_CODE(serial_num.pack(bref)); @@ -6462,10 +6432,10 @@ SRSASN_CODE sib8_s::pack(bit_ref& bref) const if (data_coding_scheme_present) { HANDLE_CODE(data_coding_scheme.pack(bref)); } - if (warning_area_coordinates_segment_present) { + if (warning_area_coordinates_segment.size() > 0) { HANDLE_CODE(warning_area_coordinates_segment.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -6475,7 +6445,9 @@ SRSASN_CODE sib8_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(data_coding_scheme_present, 1)); + bool warning_area_coordinates_segment_present; HANDLE_CODE(bref.unpack(warning_area_coordinates_segment_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(msg_id.unpack(bref)); @@ -6506,10 +6478,10 @@ void sib8_s::to_json(json_writer& j) const if (data_coding_scheme_present) { j.write_str("dataCodingScheme", data_coding_scheme.to_string()); } - if (warning_area_coordinates_segment_present) { + if (warning_area_coordinates_segment.size() > 0) { j.write_str("warningAreaCoordinatesSegment", warning_area_coordinates_segment.to_string()); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } j.end_obj(); @@ -6526,7 +6498,7 @@ SRSASN_CODE sib9_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(time_info_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); if (time_info_present) { HANDLE_CODE(bref.pack(time_info.day_light_saving_time_present, 1)); @@ -6543,7 +6515,7 @@ SRSASN_CODE sib9_s::pack(bit_ref& bref) const HANDLE_CODE(pack_integer(bref, time_info.local_time_offset, (int8_t)-63, (int8_t)64)); } } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -6553,6 +6525,7 @@ SRSASN_CODE sib9_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(time_info_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); if (time_info_present) { @@ -6594,7 +6567,7 @@ void sib9_s::to_json(json_writer& j) const } j.end_obj(); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } j.end_obj(); @@ -7242,11 +7215,11 @@ uint8_t serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::to_num // SystemInformation-IEs ::= SEQUENCE SRSASN_CODE sys_info_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, sib_type_and_info, 1, 32)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -7254,6 +7227,7 @@ SRSASN_CODE sys_info_ies_s::pack(bit_ref& bref) const } SRSASN_CODE sys_info_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -7272,7 +7246,7 @@ void sys_info_ies_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -7723,7 +7697,7 @@ SRSASN_CODE sib1_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(ue_timers_and_consts_present, 1)); HANDLE_CODE(bref.pack(uac_barr_info_present, 1)); HANDLE_CODE(bref.pack(use_full_resume_id_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (cell_sel_info_present) { @@ -7759,13 +7733,13 @@ SRSASN_CODE sib1_s::pack(bit_ref& bref) const HANDLE_CODE(ue_timers_and_consts.pack(bref)); } if (uac_barr_info_present) { - HANDLE_CODE(bref.pack(uac_barr_info.uac_barr_for_common_present, 1)); - HANDLE_CODE(bref.pack(uac_barr_info.uac_barr_per_plmn_list_present, 1)); + HANDLE_CODE(bref.pack(uac_barr_info.uac_barr_for_common.size() > 0, 1)); + HANDLE_CODE(bref.pack(uac_barr_info.uac_barr_per_plmn_list.size() > 0, 1)); HANDLE_CODE(bref.pack(uac_barr_info.uac_access_category1_sel_assist_info_present, 1)); - if (uac_barr_info.uac_barr_for_common_present) { + if (uac_barr_info.uac_barr_for_common.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, uac_barr_info.uac_barr_for_common, 1, 63)); } - if (uac_barr_info.uac_barr_per_plmn_list_present) { + if (uac_barr_info.uac_barr_per_plmn_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, uac_barr_info.uac_barr_per_plmn_list, 1, 12)); } HANDLE_CODE(pack_dyn_seq_of(bref, uac_barr_info.uac_barr_info_set_list, 1, 8)); @@ -7773,7 +7747,7 @@ SRSASN_CODE sib1_s::pack(bit_ref& bref) const HANDLE_CODE(uac_barr_info.uac_access_category1_sel_assist_info.pack(bref)); } } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -7790,6 +7764,7 @@ SRSASN_CODE sib1_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(ue_timers_and_consts_present, 1)); HANDLE_CODE(bref.unpack(uac_barr_info_present, 1)); HANDLE_CODE(bref.unpack(use_full_resume_id_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -7826,13 +7801,15 @@ SRSASN_CODE sib1_s::unpack(cbit_ref& bref) HANDLE_CODE(ue_timers_and_consts.unpack(bref)); } if (uac_barr_info_present) { - HANDLE_CODE(bref.unpack(uac_barr_info.uac_barr_for_common_present, 1)); - HANDLE_CODE(bref.unpack(uac_barr_info.uac_barr_per_plmn_list_present, 1)); + bool uac_barr_for_common_present; + HANDLE_CODE(bref.unpack(uac_barr_for_common_present, 1)); + bool uac_barr_per_plmn_list_present; + HANDLE_CODE(bref.unpack(uac_barr_per_plmn_list_present, 1)); HANDLE_CODE(bref.unpack(uac_barr_info.uac_access_category1_sel_assist_info_present, 1)); - if (uac_barr_info.uac_barr_for_common_present) { + if (uac_barr_for_common_present) { HANDLE_CODE(unpack_dyn_seq_of(uac_barr_info.uac_barr_for_common, bref, 1, 63)); } - if (uac_barr_info.uac_barr_per_plmn_list_present) { + if (uac_barr_per_plmn_list_present) { HANDLE_CODE(unpack_dyn_seq_of(uac_barr_info.uac_barr_per_plmn_list, bref, 1, 12)); } HANDLE_CODE(unpack_dyn_seq_of(uac_barr_info.uac_barr_info_set_list, bref, 1, 8)); @@ -7894,14 +7871,14 @@ void sib1_s::to_json(json_writer& j) const if (uac_barr_info_present) { j.write_fieldname("uac-BarringInfo"); j.start_obj(); - if (uac_barr_info.uac_barr_for_common_present) { + if (uac_barr_info.uac_barr_for_common.size() > 0) { j.start_array("uac-BarringForCommon"); for (const auto& e1 : uac_barr_info.uac_barr_for_common) { e1.to_json(j); } j.end_array(); } - if (uac_barr_info.uac_barr_per_plmn_list_present) { + if (uac_barr_info.uac_barr_per_plmn_list.size() > 0) { j.start_array("uac-BarringPerPLMN-List"); for (const auto& e1 : uac_barr_info.uac_barr_per_plmn_list) { e1.to_json(j); @@ -7922,7 +7899,7 @@ void sib1_s::to_json(json_writer& j) const if (use_full_resume_id_present) { j.write_str("useFullResumeID", "true"); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -8916,17 +8893,17 @@ uint16_t pdcp_cfg_s::t_reordering_opts::to_number() const SRSASN_CODE sdap_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(mapped_qos_flows_to_add_present, 1)); - HANDLE_CODE(bref.pack(mapped_qos_flows_to_release_present, 1)); + HANDLE_CODE(bref.pack(mapped_qos_flows_to_add.size() > 0, 1)); + HANDLE_CODE(bref.pack(mapped_qos_flows_to_release.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, pdu_session, (uint16_t)0u, (uint16_t)255u)); HANDLE_CODE(sdap_hdr_dl.pack(bref)); HANDLE_CODE(sdap_hdr_ul.pack(bref)); HANDLE_CODE(bref.pack(default_drb, 1)); - if (mapped_qos_flows_to_add_present) { + if (mapped_qos_flows_to_add.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, mapped_qos_flows_to_add, 1, 64, integer_packer(0, 63))); } - if (mapped_qos_flows_to_release_present) { + if (mapped_qos_flows_to_release.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, mapped_qos_flows_to_release, 1, 64, integer_packer(0, 63))); } @@ -8935,7 +8912,9 @@ SRSASN_CODE sdap_cfg_s::pack(bit_ref& bref) const SRSASN_CODE sdap_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool mapped_qos_flows_to_add_present; HANDLE_CODE(bref.unpack(mapped_qos_flows_to_add_present, 1)); + bool mapped_qos_flows_to_release_present; HANDLE_CODE(bref.unpack(mapped_qos_flows_to_release_present, 1)); HANDLE_CODE(unpack_integer(pdu_session, bref, (uint16_t)0u, (uint16_t)255u)); @@ -8958,14 +8937,14 @@ void sdap_cfg_s::to_json(json_writer& j) const j.write_str("sdap-HeaderDL", sdap_hdr_dl.to_string()); j.write_str("sdap-HeaderUL", sdap_hdr_ul.to_string()); j.write_bool("defaultDRB", default_drb); - if (mapped_qos_flows_to_add_present) { + if (mapped_qos_flows_to_add.size() > 0) { j.start_array("mappedQoS-FlowsToAdd"); for (const auto& e1 : mapped_qos_flows_to_add) { j.write_int(e1); } j.end_array(); } - if (mapped_qos_flows_to_release_present) { + if (mapped_qos_flows_to_release.size() > 0) { j.start_array("mappedQoS-FlowsToRelease"); for (const auto& e1 : mapped_qos_flows_to_release) { j.write_int(e1); @@ -9310,19 +9289,19 @@ const char* security_cfg_s::key_to_use_opts::to_string() const SRSASN_CODE radio_bearer_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(srb_to_add_mod_list_present, 1)); + HANDLE_CODE(bref.pack(srb_to_add_mod_list.size() > 0, 1)); HANDLE_CODE(bref.pack(srb3_to_release_present, 1)); - HANDLE_CODE(bref.pack(drb_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(drb_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(drb_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(drb_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(security_cfg_present, 1)); - if (srb_to_add_mod_list_present) { + if (srb_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, srb_to_add_mod_list, 1, 2)); } - if (drb_to_add_mod_list_present) { + if (drb_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, drb_to_add_mod_list, 1, 29)); } - if (drb_to_release_list_present) { + if (drb_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, drb_to_release_list, 1, 29, integer_packer(1, 32))); } if (security_cfg_present) { @@ -9334,9 +9313,12 @@ SRSASN_CODE radio_bearer_cfg_s::pack(bit_ref& bref) const SRSASN_CODE radio_bearer_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool srb_to_add_mod_list_present; HANDLE_CODE(bref.unpack(srb_to_add_mod_list_present, 1)); HANDLE_CODE(bref.unpack(srb3_to_release_present, 1)); + bool drb_to_add_mod_list_present; HANDLE_CODE(bref.unpack(drb_to_add_mod_list_present, 1)); + bool drb_to_release_list_present; HANDLE_CODE(bref.unpack(drb_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(security_cfg_present, 1)); @@ -9358,7 +9340,7 @@ SRSASN_CODE radio_bearer_cfg_s::unpack(cbit_ref& bref) void radio_bearer_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (srb_to_add_mod_list_present) { + if (srb_to_add_mod_list.size() > 0) { j.start_array("srb-ToAddModList"); for (const auto& e1 : srb_to_add_mod_list) { e1.to_json(j); @@ -9368,14 +9350,14 @@ void radio_bearer_cfg_s::to_json(json_writer& j) const if (srb3_to_release_present) { j.write_str("srb3-ToRelease", "true"); } - if (drb_to_add_mod_list_present) { + if (drb_to_add_mod_list.size() > 0) { j.start_array("drb-ToAddModList"); for (const auto& e1 : drb_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (drb_to_release_list_present) { + if (drb_to_release_list.size() > 0) { j.start_array("drb-ToReleaseList"); for (const auto& e1 : drb_to_release_list) { j.write_int(e1); @@ -9393,13 +9375,13 @@ void radio_bearer_cfg_s::to_json(json_writer& j) const SRSASN_CODE rrc_reject_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(wait_time_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (wait_time_present) { HANDLE_CODE(pack_integer(bref, wait_time, (uint8_t)1u, (uint8_t)16u)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -9408,6 +9390,7 @@ SRSASN_CODE rrc_reject_ies_s::pack(bit_ref& bref) const SRSASN_CODE rrc_reject_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(wait_time_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -9426,7 +9409,7 @@ void rrc_reject_ies_s::to_json(json_writer& j) const if (wait_time_present) { j.write_int("waitTime", wait_time); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -9440,12 +9423,12 @@ void rrc_reject_ies_s::to_json(json_writer& j) const // RRCSetup-IEs ::= SEQUENCE SRSASN_CODE rrc_setup_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(radio_bearer_cfg.pack(bref)); HANDLE_CODE(master_cell_group.pack(bref)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -9453,6 +9436,7 @@ SRSASN_CODE rrc_setup_ies_s::pack(bit_ref& bref) const } SRSASN_CODE rrc_setup_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -9470,7 +9454,7 @@ void rrc_setup_ies_s::to_json(json_writer& j) const j.write_fieldname("radioBearerConfig"); radio_bearer_cfg.to_json(j); j.write_str("masterCellGroup", master_cell_group.to_string()); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -11980,10 +11964,10 @@ int8_t periodical_report_cfg_inter_rat_s::report_amount_opts::to_number() const // RAN-AreaConfig ::= SEQUENCE SRSASN_CODE ran_area_cfg_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(ran_area_code_list_present, 1)); + HANDLE_CODE(bref.pack(ran_area_code_list.size() > 0, 1)); HANDLE_CODE(tac.pack(bref)); - if (ran_area_code_list_present) { + if (ran_area_code_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ran_area_code_list, 1, 32, integer_packer(0, 255))); } @@ -11991,6 +11975,7 @@ SRSASN_CODE ran_area_cfg_s::pack(bit_ref& bref) const } SRSASN_CODE ran_area_cfg_s::unpack(cbit_ref& bref) { + bool ran_area_code_list_present; HANDLE_CODE(bref.unpack(ran_area_code_list_present, 1)); HANDLE_CODE(tac.unpack(bref)); @@ -12004,7 +11989,7 @@ void ran_area_cfg_s::to_json(json_writer& j) const { j.start_obj(); j.write_str("trackingAreaCode", tac.to_string()); - if (ran_area_code_list_present) { + if (ran_area_code_list.size() > 0) { j.start_array("ran-AreaCodeList"); for (const auto& e1 : ran_area_code_list) { j.write_int(e1); @@ -12204,9 +12189,9 @@ void report_sftd_nr_s::to_json(json_writer& j) const // SSB-MTC2 ::= SEQUENCE SRSASN_CODE ssb_mtc2_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(pci_list_present, 1)); + HANDLE_CODE(bref.pack(pci_list.size() > 0, 1)); - if (pci_list_present) { + if (pci_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, pci_list, 1, 64, integer_packer(0, 1007))); } HANDLE_CODE(periodicity.pack(bref)); @@ -12215,6 +12200,7 @@ SRSASN_CODE ssb_mtc2_s::pack(bit_ref& bref) const } SRSASN_CODE ssb_mtc2_s::unpack(cbit_ref& bref) { + bool pci_list_present; HANDLE_CODE(bref.unpack(pci_list_present, 1)); if (pci_list_present) { @@ -12227,7 +12213,7 @@ SRSASN_CODE ssb_mtc2_s::unpack(cbit_ref& bref) void ssb_mtc2_s::to_json(json_writer& j) const { j.start_obj(); - if (pci_list_present) { + if (pci_list.size() > 0) { j.start_array("pci-List"); for (const auto& e1 : pci_list) { j.write_int(e1); @@ -12417,24 +12403,24 @@ const char* mrdc_secondary_cell_group_cfg_s::mrdc_secondary_cell_group_c_::types SRSASN_CODE meas_obj_eutra_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(cells_to_rem_list_eutran_present, 1)); - HANDLE_CODE(bref.pack(cells_to_add_mod_list_eutran_present, 1)); - HANDLE_CODE(bref.pack(black_cells_to_rem_list_eutran_present, 1)); - HANDLE_CODE(bref.pack(black_cells_to_add_mod_list_eutran_present, 1)); + HANDLE_CODE(bref.pack(cells_to_rem_list_eutran.size() > 0, 1)); + HANDLE_CODE(bref.pack(cells_to_add_mod_list_eutran.size() > 0, 1)); + HANDLE_CODE(bref.pack(black_cells_to_rem_list_eutran.size() > 0, 1)); + HANDLE_CODE(bref.pack(black_cells_to_add_mod_list_eutran.size() > 0, 1)); HANDLE_CODE(bref.pack(eutra_q_offset_range_present, 1)); HANDLE_CODE(pack_integer(bref, carrier_freq, (uint32_t)0u, (uint32_t)262143u)); HANDLE_CODE(allowed_meas_bw.pack(bref)); - if (cells_to_rem_list_eutran_present) { + if (cells_to_rem_list_eutran.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, cells_to_rem_list_eutran, 1, 32, integer_packer(1, 32))); } - if (cells_to_add_mod_list_eutran_present) { + if (cells_to_add_mod_list_eutran.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, cells_to_add_mod_list_eutran, 1, 32)); } - if (black_cells_to_rem_list_eutran_present) { + if (black_cells_to_rem_list_eutran.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, black_cells_to_rem_list_eutran, 1, 32, integer_packer(1, 32))); } - if (black_cells_to_add_mod_list_eutran_present) { + if (black_cells_to_add_mod_list_eutran.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, black_cells_to_add_mod_list_eutran, 1, 32)); } HANDLE_CODE(bref.pack(eutra_presence_ant_port1, 1)); @@ -12448,9 +12434,13 @@ SRSASN_CODE meas_obj_eutra_s::pack(bit_ref& bref) const SRSASN_CODE meas_obj_eutra_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool cells_to_rem_list_eutran_present; HANDLE_CODE(bref.unpack(cells_to_rem_list_eutran_present, 1)); + bool cells_to_add_mod_list_eutran_present; HANDLE_CODE(bref.unpack(cells_to_add_mod_list_eutran_present, 1)); + bool black_cells_to_rem_list_eutran_present; HANDLE_CODE(bref.unpack(black_cells_to_rem_list_eutran_present, 1)); + bool black_cells_to_add_mod_list_eutran_present; HANDLE_CODE(bref.unpack(black_cells_to_add_mod_list_eutran_present, 1)); HANDLE_CODE(bref.unpack(eutra_q_offset_range_present, 1)); @@ -12481,28 +12471,28 @@ void meas_obj_eutra_s::to_json(json_writer& j) const j.start_obj(); j.write_int("carrierFreq", carrier_freq); j.write_str("allowedMeasBandwidth", allowed_meas_bw.to_string()); - if (cells_to_rem_list_eutran_present) { + if (cells_to_rem_list_eutran.size() > 0) { j.start_array("cellsToRemoveListEUTRAN"); for (const auto& e1 : cells_to_rem_list_eutran) { j.write_int(e1); } j.end_array(); } - if (cells_to_add_mod_list_eutran_present) { + if (cells_to_add_mod_list_eutran.size() > 0) { j.start_array("cellsToAddModListEUTRAN"); for (const auto& e1 : cells_to_add_mod_list_eutran) { e1.to_json(j); } j.end_array(); } - if (black_cells_to_rem_list_eutran_present) { + if (black_cells_to_rem_list_eutran.size() > 0) { j.start_array("blackCellsToRemoveListEUTRAN"); for (const auto& e1 : black_cells_to_rem_list_eutran) { j.write_int(e1); } j.end_array(); } - if (black_cells_to_add_mod_list_eutran_present) { + if (black_cells_to_add_mod_list_eutran.size() > 0) { j.start_array("blackCellsToAddModListEUTRAN"); for (const auto& e1 : black_cells_to_add_mod_list_eutran) { e1.to_json(j); @@ -12530,12 +12520,12 @@ SRSASN_CODE meas_obj_nr_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(abs_thresh_csi_rs_consolidation_present, 1)); HANDLE_CODE(bref.pack(nrof_ss_blocks_to_average_present, 1)); HANDLE_CODE(bref.pack(nrof_csi_rs_res_to_average_present, 1)); - HANDLE_CODE(bref.pack(cells_to_rem_list_present, 1)); - HANDLE_CODE(bref.pack(cells_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(black_cells_to_rem_list_present, 1)); - HANDLE_CODE(bref.pack(black_cells_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(white_cells_to_rem_list_present, 1)); - HANDLE_CODE(bref.pack(white_cells_to_add_mod_list_present, 1)); + HANDLE_CODE(bref.pack(cells_to_rem_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(cells_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(black_cells_to_rem_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(black_cells_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(white_cells_to_rem_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(white_cells_to_add_mod_list.size() > 0, 1)); if (ssb_freq_present) { HANDLE_CODE(pack_integer(bref, ssb_freq, (uint32_t)0u, (uint32_t)3279165u)); @@ -12567,22 +12557,22 @@ SRSASN_CODE meas_obj_nr_s::pack(bit_ref& bref) const } HANDLE_CODE(pack_integer(bref, quant_cfg_idx, (uint8_t)1u, (uint8_t)2u)); HANDLE_CODE(offset_mo.pack(bref)); - if (cells_to_rem_list_present) { + if (cells_to_rem_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, cells_to_rem_list, 1, 32, integer_packer(0, 1007))); } - if (cells_to_add_mod_list_present) { + if (cells_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, cells_to_add_mod_list, 1, 32)); } - if (black_cells_to_rem_list_present) { + if (black_cells_to_rem_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, black_cells_to_rem_list, 1, 8, integer_packer(1, 8))); } - if (black_cells_to_add_mod_list_present) { + if (black_cells_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, black_cells_to_add_mod_list, 1, 8)); } - if (white_cells_to_rem_list_present) { + if (white_cells_to_rem_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, white_cells_to_rem_list, 1, 8, integer_packer(1, 8))); } - if (white_cells_to_add_mod_list_present) { + if (white_cells_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, white_cells_to_add_mod_list, 1, 8)); } @@ -12619,11 +12609,17 @@ SRSASN_CODE meas_obj_nr_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(abs_thresh_csi_rs_consolidation_present, 1)); HANDLE_CODE(bref.unpack(nrof_ss_blocks_to_average_present, 1)); HANDLE_CODE(bref.unpack(nrof_csi_rs_res_to_average_present, 1)); + bool cells_to_rem_list_present; HANDLE_CODE(bref.unpack(cells_to_rem_list_present, 1)); + bool cells_to_add_mod_list_present; HANDLE_CODE(bref.unpack(cells_to_add_mod_list_present, 1)); + bool black_cells_to_rem_list_present; HANDLE_CODE(bref.unpack(black_cells_to_rem_list_present, 1)); + bool black_cells_to_add_mod_list_present; HANDLE_CODE(bref.unpack(black_cells_to_add_mod_list_present, 1)); + bool white_cells_to_rem_list_present; HANDLE_CODE(bref.unpack(white_cells_to_rem_list_present, 1)); + bool white_cells_to_add_mod_list_present; HANDLE_CODE(bref.unpack(white_cells_to_add_mod_list_present, 1)); if (ssb_freq_present) { @@ -12733,42 +12729,42 @@ void meas_obj_nr_s::to_json(json_writer& j) const j.write_int("quantityConfigIndex", quant_cfg_idx); j.write_fieldname("offsetMO"); offset_mo.to_json(j); - if (cells_to_rem_list_present) { + if (cells_to_rem_list.size() > 0) { j.start_array("cellsToRemoveList"); for (const auto& e1 : cells_to_rem_list) { j.write_int(e1); } j.end_array(); } - if (cells_to_add_mod_list_present) { + if (cells_to_add_mod_list.size() > 0) { j.start_array("cellsToAddModList"); for (const auto& e1 : cells_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (black_cells_to_rem_list_present) { + if (black_cells_to_rem_list.size() > 0) { j.start_array("blackCellsToRemoveList"); for (const auto& e1 : black_cells_to_rem_list) { j.write_int(e1); } j.end_array(); } - if (black_cells_to_add_mod_list_present) { + if (black_cells_to_add_mod_list.size() > 0) { j.start_array("blackCellsToAddModList"); for (const auto& e1 : black_cells_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (white_cells_to_rem_list_present) { + if (white_cells_to_rem_list.size() > 0) { j.start_array("whiteCellsToRemoveList"); for (const auto& e1 : white_cells_to_rem_list) { j.write_int(e1); } j.end_array(); } - if (white_cells_to_add_mod_list_present) { + if (white_cells_to_add_mod_list.size() > 0) { j.start_array("whiteCellsToAddModList"); for (const auto& e1 : white_cells_to_add_mod_list) { e1.to_json(j); @@ -13853,14 +13849,14 @@ const char* rat_type_opts::to_string() const SRSASN_CODE rrc_recfg_v1560_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(mrdc_secondary_cell_group_cfg_present, 1)); - HANDLE_CODE(bref.pack(radio_bearer_cfg2_present, 1)); + HANDLE_CODE(bref.pack(radio_bearer_cfg2.size() > 0, 1)); HANDLE_CODE(bref.pack(sk_counter_present, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (mrdc_secondary_cell_group_cfg_present) { HANDLE_CODE(mrdc_secondary_cell_group_cfg.pack(bref)); } - if (radio_bearer_cfg2_present) { + if (radio_bearer_cfg2.size() > 0) { HANDLE_CODE(radio_bearer_cfg2.pack(bref)); } if (sk_counter_present) { @@ -13872,6 +13868,7 @@ SRSASN_CODE rrc_recfg_v1560_ies_s::pack(bit_ref& bref) const SRSASN_CODE rrc_recfg_v1560_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(mrdc_secondary_cell_group_cfg_present, 1)); + bool radio_bearer_cfg2_present; HANDLE_CODE(bref.unpack(radio_bearer_cfg2_present, 1)); HANDLE_CODE(bref.unpack(sk_counter_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -13895,7 +13892,7 @@ void rrc_recfg_v1560_ies_s::to_json(json_writer& j) const j.write_fieldname("mrdc-SecondaryCellGroupConfig"); mrdc_secondary_cell_group_cfg.to_json(j); } - if (radio_bearer_cfg2_present) { + if (radio_bearer_cfg2.size() > 0) { j.write_str("radioBearerConfig2", radio_bearer_cfg2.to_string()); } if (sk_counter_present) { @@ -14140,11 +14137,11 @@ void drb_count_msb_info_s::to_json(json_writer& j) const SRSASN_CODE master_key_upd_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(nas_container_present, 1)); + HANDLE_CODE(bref.pack(nas_container.size() > 0, 1)); HANDLE_CODE(bref.pack(key_set_change_ind, 1)); HANDLE_CODE(pack_integer(bref, next_hop_chaining_count, (uint8_t)0u, (uint8_t)7u)); - if (nas_container_present) { + if (nas_container.size() > 0) { HANDLE_CODE(nas_container.pack(bref)); } @@ -14153,6 +14150,7 @@ SRSASN_CODE master_key_upd_s::pack(bit_ref& bref) const SRSASN_CODE master_key_upd_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool nas_container_present; HANDLE_CODE(bref.unpack(nas_container_present, 1)); HANDLE_CODE(bref.unpack(key_set_change_ind, 1)); @@ -14168,7 +14166,7 @@ void master_key_upd_s::to_json(json_writer& j) const j.start_obj(); j.write_bool("keySetChangeIndicator", key_set_change_ind); j.write_int("nextHopChainingCount", next_hop_chaining_count); - if (nas_container_present) { + if (nas_container.size() > 0) { j.write_str("nas-Container", nas_container.to_string()); } j.end_obj(); @@ -14477,9 +14475,9 @@ uint16_t periodic_rnau_timer_value_opts::to_number() const SRSASN_CODE quant_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(quant_cfg_nr_list_present, 1)); + HANDLE_CODE(bref.pack(quant_cfg_nr_list.size() > 0, 1)); - if (quant_cfg_nr_list_present) { + if (quant_cfg_nr_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, quant_cfg_nr_list, 1, 2)); } @@ -14502,6 +14500,7 @@ SRSASN_CODE quant_cfg_s::pack(bit_ref& bref) const SRSASN_CODE quant_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool quant_cfg_nr_list_present; HANDLE_CODE(bref.unpack(quant_cfg_nr_list_present, 1)); if (quant_cfg_nr_list_present) { @@ -14528,7 +14527,7 @@ SRSASN_CODE quant_cfg_s::unpack(cbit_ref& bref) void quant_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (quant_cfg_nr_list_present) { + if (quant_cfg_nr_list.size() > 0) { j.start_array("quantityConfigNR-List"); for (const auto& e1 : quant_cfg_nr_list) { e1.to_json(j); @@ -14780,10 +14779,10 @@ uint8_t redirected_carrier_info_eutra_s::cn_type_opts::to_number() const SRSASN_CODE ue_cap_rat_request_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(cap_request_filt_present, 1)); + HANDLE_CODE(bref.pack(cap_request_filt.size() > 0, 1)); HANDLE_CODE(rat_type.pack(bref)); - if (cap_request_filt_present) { + if (cap_request_filt.size() > 0) { HANDLE_CODE(cap_request_filt.pack(bref)); } @@ -14792,6 +14791,7 @@ SRSASN_CODE ue_cap_rat_request_s::pack(bit_ref& bref) const SRSASN_CODE ue_cap_rat_request_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool cap_request_filt_present; HANDLE_CODE(bref.unpack(cap_request_filt_present, 1)); HANDLE_CODE(rat_type.unpack(bref)); @@ -14805,7 +14805,7 @@ void ue_cap_rat_request_s::to_json(json_writer& j) const { j.start_obj(); j.write_str("rat-Type", rat_type.to_string()); - if (cap_request_filt_present) { + if (cap_request_filt.size() > 0) { j.write_str("capabilityRequestFilter", cap_request_filt.to_string()); } j.end_obj(); @@ -14815,14 +14815,14 @@ void ue_cap_rat_request_s::to_json(json_writer& j) const SRSASN_CODE cell_resel_priorities_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(freq_prio_list_eutra_present, 1)); - HANDLE_CODE(bref.pack(freq_prio_list_nr_present, 1)); + HANDLE_CODE(bref.pack(freq_prio_list_eutra.size() > 0, 1)); + HANDLE_CODE(bref.pack(freq_prio_list_nr.size() > 0, 1)); HANDLE_CODE(bref.pack(t320_present, 1)); - if (freq_prio_list_eutra_present) { + if (freq_prio_list_eutra.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_prio_list_eutra, 1, 8)); } - if (freq_prio_list_nr_present) { + if (freq_prio_list_nr.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_prio_list_nr, 1, 8)); } if (t320_present) { @@ -14834,7 +14834,9 @@ SRSASN_CODE cell_resel_priorities_s::pack(bit_ref& bref) const SRSASN_CODE cell_resel_priorities_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool freq_prio_list_eutra_present; HANDLE_CODE(bref.unpack(freq_prio_list_eutra_present, 1)); + bool freq_prio_list_nr_present; HANDLE_CODE(bref.unpack(freq_prio_list_nr_present, 1)); HANDLE_CODE(bref.unpack(t320_present, 1)); @@ -14853,14 +14855,14 @@ SRSASN_CODE cell_resel_priorities_s::unpack(cbit_ref& bref) void cell_resel_priorities_s::to_json(json_writer& j) const { j.start_obj(); - if (freq_prio_list_eutra_present) { + if (freq_prio_list_eutra.size() > 0) { j.start_array("freqPriorityListEUTRA"); for (const auto& e1 : freq_prio_list_eutra) { e1.to_json(j); } j.end_array(); } - if (freq_prio_list_nr_present) { + if (freq_prio_list_nr.size() > 0) { j.start_array("freqPriorityListNR"); for (const auto& e1 : freq_prio_list_nr) { e1.to_json(j); @@ -14888,33 +14890,33 @@ uint8_t cell_resel_priorities_s::t320_opts::to_number() const SRSASN_CODE meas_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(meas_obj_to_rem_list_present, 1)); - HANDLE_CODE(bref.pack(meas_obj_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(report_cfg_to_rem_list_present, 1)); - HANDLE_CODE(bref.pack(report_cfg_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(meas_id_to_rem_list_present, 1)); - HANDLE_CODE(bref.pack(meas_id_to_add_mod_list_present, 1)); + HANDLE_CODE(bref.pack(meas_obj_to_rem_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(meas_obj_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(report_cfg_to_rem_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(report_cfg_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(meas_id_to_rem_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(meas_id_to_add_mod_list.size() > 0, 1)); HANDLE_CODE(bref.pack(s_measure_cfg_present, 1)); HANDLE_CODE(bref.pack(quant_cfg_present, 1)); HANDLE_CODE(bref.pack(meas_gap_cfg_present, 1)); HANDLE_CODE(bref.pack(meas_gap_sharing_cfg_present, 1)); - if (meas_obj_to_rem_list_present) { + if (meas_obj_to_rem_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_obj_to_rem_list, 1, 64, integer_packer(1, 64))); } - if (meas_obj_to_add_mod_list_present) { + if (meas_obj_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_obj_to_add_mod_list, 1, 64)); } - if (report_cfg_to_rem_list_present) { + if (report_cfg_to_rem_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, report_cfg_to_rem_list, 1, 64, integer_packer(1, 64))); } - if (report_cfg_to_add_mod_list_present) { + if (report_cfg_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, report_cfg_to_add_mod_list, 1, 64)); } - if (meas_id_to_rem_list_present) { + if (meas_id_to_rem_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_id_to_rem_list, 1, 64, integer_packer(1, 64))); } - if (meas_id_to_add_mod_list_present) { + if (meas_id_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_id_to_add_mod_list, 1, 64)); } if (s_measure_cfg_present) { @@ -14935,11 +14937,17 @@ SRSASN_CODE meas_cfg_s::pack(bit_ref& bref) const SRSASN_CODE meas_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool meas_obj_to_rem_list_present; HANDLE_CODE(bref.unpack(meas_obj_to_rem_list_present, 1)); + bool meas_obj_to_add_mod_list_present; HANDLE_CODE(bref.unpack(meas_obj_to_add_mod_list_present, 1)); + bool report_cfg_to_rem_list_present; HANDLE_CODE(bref.unpack(report_cfg_to_rem_list_present, 1)); + bool report_cfg_to_add_mod_list_present; HANDLE_CODE(bref.unpack(report_cfg_to_add_mod_list_present, 1)); + bool meas_id_to_rem_list_present; HANDLE_CODE(bref.unpack(meas_id_to_rem_list_present, 1)); + bool meas_id_to_add_mod_list_present; HANDLE_CODE(bref.unpack(meas_id_to_add_mod_list_present, 1)); HANDLE_CODE(bref.unpack(s_measure_cfg_present, 1)); HANDLE_CODE(bref.unpack(quant_cfg_present, 1)); @@ -14982,42 +14990,42 @@ SRSASN_CODE meas_cfg_s::unpack(cbit_ref& bref) void meas_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (meas_obj_to_rem_list_present) { + if (meas_obj_to_rem_list.size() > 0) { j.start_array("measObjectToRemoveList"); for (const auto& e1 : meas_obj_to_rem_list) { j.write_int(e1); } j.end_array(); } - if (meas_obj_to_add_mod_list_present) { + if (meas_obj_to_add_mod_list.size() > 0) { j.start_array("measObjectToAddModList"); for (const auto& e1 : meas_obj_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (report_cfg_to_rem_list_present) { + if (report_cfg_to_rem_list.size() > 0) { j.start_array("reportConfigToRemoveList"); for (const auto& e1 : report_cfg_to_rem_list) { j.write_int(e1); } j.end_array(); } - if (report_cfg_to_add_mod_list_present) { + if (report_cfg_to_add_mod_list.size() > 0) { j.start_array("reportConfigToAddModList"); for (const auto& e1 : report_cfg_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (meas_id_to_rem_list_present) { + if (meas_id_to_rem_list.size() > 0) { j.start_array("measIdToRemoveList"); for (const auto& e1 : meas_id_to_rem_list) { j.write_int(e1); } j.end_array(); } - if (meas_id_to_add_mod_list_present) { + if (meas_id_to_add_mod_list.size() > 0) { j.start_array("measIdToAddModList"); for (const auto& e1 : meas_id_to_add_mod_list) { e1.to_json(j); @@ -15155,28 +15163,28 @@ const char* meas_cfg_s::s_measure_cfg_c_::types_opts::to_string() const // RRCReconfiguration-v1530-IEs ::= SEQUENCE SRSASN_CODE rrc_recfg_v1530_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(master_cell_group_present, 1)); + HANDLE_CODE(bref.pack(master_cell_group.size() > 0, 1)); HANDLE_CODE(bref.pack(full_cfg_present, 1)); - HANDLE_CODE(bref.pack(ded_nas_msg_list_present, 1)); + HANDLE_CODE(bref.pack(ded_nas_msg_list.size() > 0, 1)); HANDLE_CODE(bref.pack(master_key_upd_present, 1)); - HANDLE_CODE(bref.pack(ded_sib1_delivery_present, 1)); - HANDLE_CODE(bref.pack(ded_sys_info_delivery_present, 1)); + HANDLE_CODE(bref.pack(ded_sib1_delivery.size() > 0, 1)); + HANDLE_CODE(bref.pack(ded_sys_info_delivery.size() > 0, 1)); HANDLE_CODE(bref.pack(other_cfg_present, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (master_cell_group_present) { + if (master_cell_group.size() > 0) { HANDLE_CODE(master_cell_group.pack(bref)); } - if (ded_nas_msg_list_present) { + if (ded_nas_msg_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ded_nas_msg_list, 1, 29)); } if (master_key_upd_present) { HANDLE_CODE(master_key_upd.pack(bref)); } - if (ded_sib1_delivery_present) { + if (ded_sib1_delivery.size() > 0) { HANDLE_CODE(ded_sib1_delivery.pack(bref)); } - if (ded_sys_info_delivery_present) { + if (ded_sys_info_delivery.size() > 0) { HANDLE_CODE(ded_sys_info_delivery.pack(bref)); } if (other_cfg_present) { @@ -15190,11 +15198,15 @@ SRSASN_CODE rrc_recfg_v1530_ies_s::pack(bit_ref& bref) const } SRSASN_CODE rrc_recfg_v1530_ies_s::unpack(cbit_ref& bref) { + bool master_cell_group_present; HANDLE_CODE(bref.unpack(master_cell_group_present, 1)); HANDLE_CODE(bref.unpack(full_cfg_present, 1)); + bool ded_nas_msg_list_present; HANDLE_CODE(bref.unpack(ded_nas_msg_list_present, 1)); HANDLE_CODE(bref.unpack(master_key_upd_present, 1)); + bool ded_sib1_delivery_present; HANDLE_CODE(bref.unpack(ded_sib1_delivery_present, 1)); + bool ded_sys_info_delivery_present; HANDLE_CODE(bref.unpack(ded_sys_info_delivery_present, 1)); HANDLE_CODE(bref.unpack(other_cfg_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -15226,13 +15238,13 @@ SRSASN_CODE rrc_recfg_v1530_ies_s::unpack(cbit_ref& bref) void rrc_recfg_v1530_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (master_cell_group_present) { + if (master_cell_group.size() > 0) { j.write_str("masterCellGroup", master_cell_group.to_string()); } if (full_cfg_present) { j.write_str("fullConfig", "true"); } - if (ded_nas_msg_list_present) { + if (ded_nas_msg_list.size() > 0) { j.start_array("dedicatedNAS-MessageList"); for (const auto& e1 : ded_nas_msg_list) { j.write_str(e1.to_string()); @@ -15243,10 +15255,10 @@ void rrc_recfg_v1530_ies_s::to_json(json_writer& j) const j.write_fieldname("masterKeyUpdate"); master_key_upd.to_json(j); } - if (ded_sib1_delivery_present) { + if (ded_sib1_delivery.size() > 0) { j.write_str("dedicatedSIB1-Delivery", ded_sib1_delivery.to_string()); } - if (ded_sys_info_delivery_present) { + if (ded_sys_info_delivery.size() > 0) { j.write_str("dedicatedSystemInformationDelivery", ded_sys_info_delivery.to_string()); } if (other_cfg_present) { @@ -15300,11 +15312,11 @@ void rrc_release_v1540_ies_s::to_json(json_writer& j) const // RRCResume-v1560-IEs ::= SEQUENCE SRSASN_CODE rrc_resume_v1560_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(radio_bearer_cfg2_present, 1)); + HANDLE_CODE(bref.pack(radio_bearer_cfg2.size() > 0, 1)); HANDLE_CODE(bref.pack(sk_counter_present, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (radio_bearer_cfg2_present) { + if (radio_bearer_cfg2.size() > 0) { HANDLE_CODE(radio_bearer_cfg2.pack(bref)); } if (sk_counter_present) { @@ -15315,6 +15327,7 @@ SRSASN_CODE rrc_resume_v1560_ies_s::pack(bit_ref& bref) const } SRSASN_CODE rrc_resume_v1560_ies_s::unpack(cbit_ref& bref) { + bool radio_bearer_cfg2_present; HANDLE_CODE(bref.unpack(radio_bearer_cfg2_present, 1)); HANDLE_CODE(bref.unpack(sk_counter_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -15331,7 +15344,7 @@ SRSASN_CODE rrc_resume_v1560_ies_s::unpack(cbit_ref& bref) void rrc_resume_v1560_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (radio_bearer_cfg2_present) { + if (radio_bearer_cfg2.size() > 0) { j.write_str("radioBearerConfig2", radio_bearer_cfg2.to_string()); } if (sk_counter_present) { @@ -15563,11 +15576,11 @@ void suspend_cfg_s::to_json(json_writer& j) const // CounterCheck-IEs ::= SEQUENCE SRSASN_CODE counter_check_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, drb_count_msb_info_list, 1, 29)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -15575,6 +15588,7 @@ SRSASN_CODE counter_check_ies_s::pack(bit_ref& bref) const } SRSASN_CODE counter_check_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -15593,7 +15607,7 @@ void counter_check_ies_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -15607,14 +15621,14 @@ void counter_check_ies_s::to_json(json_writer& j) const // DLInformationTransfer-IEs ::= SEQUENCE SRSASN_CODE dl_info_transfer_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(ded_nas_msg_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(ded_nas_msg.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (ded_nas_msg_present) { + if (ded_nas_msg.size() > 0) { HANDLE_CODE(ded_nas_msg.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -15622,7 +15636,9 @@ SRSASN_CODE dl_info_transfer_ies_s::pack(bit_ref& bref) const } SRSASN_CODE dl_info_transfer_ies_s::unpack(cbit_ref& bref) { + bool ded_nas_msg_present; HANDLE_CODE(bref.unpack(ded_nas_msg_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -15638,10 +15654,10 @@ SRSASN_CODE dl_info_transfer_ies_s::unpack(cbit_ref& bref) void dl_info_transfer_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (ded_nas_msg_present) { + if (ded_nas_msg.size() > 0) { j.write_str("dedicatedNAS-Message", ded_nas_msg.to_string()); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -15655,16 +15671,16 @@ void dl_info_transfer_ies_s::to_json(json_writer& j) const // MobilityFromNRCommand-IEs ::= SEQUENCE SRSASN_CODE mob_from_nr_cmd_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(nas_security_param_from_nr_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(nas_security_param_from_nr.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(target_rat_type.pack(bref)); HANDLE_CODE(target_rat_msg_container.pack(bref)); - if (nas_security_param_from_nr_present) { + if (nas_security_param_from_nr.size() > 0) { HANDLE_CODE(nas_security_param_from_nr.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -15672,7 +15688,9 @@ SRSASN_CODE mob_from_nr_cmd_ies_s::pack(bit_ref& bref) const } SRSASN_CODE mob_from_nr_cmd_ies_s::unpack(cbit_ref& bref) { + bool nas_security_param_from_nr_present; HANDLE_CODE(bref.unpack(nas_security_param_from_nr_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -15692,10 +15710,10 @@ void mob_from_nr_cmd_ies_s::to_json(json_writer& j) const j.start_obj(); j.write_str("targetRAT-Type", target_rat_type.to_string()); j.write_str("targetRAT-MessageContainer", target_rat_msg_container.to_string()); - if (nas_security_param_from_nr_present) { + if (nas_security_param_from_nr.size() > 0) { j.write_str("nas-SecurityParamFromNR", nas_security_param_from_nr.to_string()); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -15716,21 +15734,21 @@ const char* mob_from_nr_cmd_ies_s::target_rat_type_opts::to_string() const SRSASN_CODE rrc_recfg_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(radio_bearer_cfg_present, 1)); - HANDLE_CODE(bref.pack(secondary_cell_group_present, 1)); + HANDLE_CODE(bref.pack(secondary_cell_group.size() > 0, 1)); HANDLE_CODE(bref.pack(meas_cfg_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (radio_bearer_cfg_present) { HANDLE_CODE(radio_bearer_cfg.pack(bref)); } - if (secondary_cell_group_present) { + if (secondary_cell_group.size() > 0) { HANDLE_CODE(secondary_cell_group.pack(bref)); } if (meas_cfg_present) { HANDLE_CODE(meas_cfg.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } if (non_crit_ext_present) { @@ -15742,8 +15760,10 @@ SRSASN_CODE rrc_recfg_ies_s::pack(bit_ref& bref) const SRSASN_CODE rrc_recfg_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(radio_bearer_cfg_present, 1)); + bool secondary_cell_group_present; HANDLE_CODE(bref.unpack(secondary_cell_group_present, 1)); HANDLE_CODE(bref.unpack(meas_cfg_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -15772,14 +15792,14 @@ void rrc_recfg_ies_s::to_json(json_writer& j) const j.write_fieldname("radioBearerConfig"); radio_bearer_cfg.to_json(j); } - if (secondary_cell_group_present) { + if (secondary_cell_group.size() > 0) { j.write_str("secondaryCellGroup", secondary_cell_group.to_string()); } if (meas_cfg_present) { j.write_fieldname("measConfig"); meas_cfg.to_json(j); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -15792,11 +15812,11 @@ void rrc_recfg_ies_s::to_json(json_writer& j) const // RRCReestablishment-IEs ::= SEQUENCE SRSASN_CODE rrc_reest_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(pack_integer(bref, next_hop_chaining_count, (uint8_t)0u, (uint8_t)7u)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -15804,6 +15824,7 @@ SRSASN_CODE rrc_reest_ies_s::pack(bit_ref& bref) const } SRSASN_CODE rrc_reest_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -15818,7 +15839,7 @@ void rrc_reest_ies_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("nextHopChainingCount", next_hop_chaining_count); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -15836,7 +15857,7 @@ SRSASN_CODE rrc_release_ies_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(cell_resel_priorities_present, 1)); HANDLE_CODE(bref.pack(suspend_cfg_present, 1)); HANDLE_CODE(bref.pack(depriorit_req_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (redirected_carrier_info_present) { @@ -15852,7 +15873,7 @@ SRSASN_CODE rrc_release_ies_s::pack(bit_ref& bref) const HANDLE_CODE(depriorit_req.depriorit_type.pack(bref)); HANDLE_CODE(depriorit_req.depriorit_timer.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } if (non_crit_ext_present) { @@ -15867,6 +15888,7 @@ SRSASN_CODE rrc_release_ies_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(cell_resel_priorities_present, 1)); HANDLE_CODE(bref.unpack(suspend_cfg_present, 1)); HANDLE_CODE(bref.unpack(depriorit_req_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -15914,7 +15936,7 @@ void rrc_release_ies_s::to_json(json_writer& j) const j.write_str("deprioritisationTimer", depriorit_req.depriorit_timer.to_string()); j.end_obj(); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -15945,22 +15967,22 @@ uint8_t rrc_release_ies_s::depriorit_req_s_::depriorit_timer_opts::to_number() c SRSASN_CODE rrc_resume_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(radio_bearer_cfg_present, 1)); - HANDLE_CODE(bref.pack(master_cell_group_present, 1)); + HANDLE_CODE(bref.pack(master_cell_group.size() > 0, 1)); HANDLE_CODE(bref.pack(meas_cfg_present, 1)); HANDLE_CODE(bref.pack(full_cfg_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (radio_bearer_cfg_present) { HANDLE_CODE(radio_bearer_cfg.pack(bref)); } - if (master_cell_group_present) { + if (master_cell_group.size() > 0) { HANDLE_CODE(master_cell_group.pack(bref)); } if (meas_cfg_present) { HANDLE_CODE(meas_cfg.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } if (non_crit_ext_present) { @@ -15972,9 +15994,11 @@ SRSASN_CODE rrc_resume_ies_s::pack(bit_ref& bref) const SRSASN_CODE rrc_resume_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(radio_bearer_cfg_present, 1)); + bool master_cell_group_present; HANDLE_CODE(bref.unpack(master_cell_group_present, 1)); HANDLE_CODE(bref.unpack(meas_cfg_present, 1)); HANDLE_CODE(bref.unpack(full_cfg_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -16003,7 +16027,7 @@ void rrc_resume_ies_s::to_json(json_writer& j) const j.write_fieldname("radioBearerConfig"); radio_bearer_cfg.to_json(j); } - if (master_cell_group_present) { + if (master_cell_group.size() > 0) { j.write_str("masterCellGroup", master_cell_group.to_string()); } if (meas_cfg_present) { @@ -16013,7 +16037,7 @@ void rrc_resume_ies_s::to_json(json_writer& j) const if (full_cfg_present) { j.write_str("fullConfig", "true"); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -16026,11 +16050,11 @@ void rrc_resume_ies_s::to_json(json_writer& j) const // SecurityModeCommand-IEs ::= SEQUENCE SRSASN_CODE security_mode_cmd_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(security_cfg_smc.pack(bref)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -16038,6 +16062,7 @@ SRSASN_CODE security_mode_cmd_ies_s::pack(bit_ref& bref) const } SRSASN_CODE security_mode_cmd_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -16053,7 +16078,7 @@ void security_mode_cmd_ies_s::to_json(json_writer& j) const j.start_obj(); j.write_fieldname("securityConfigSMC"); security_cfg_smc.to_json(j); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -16067,14 +16092,14 @@ void security_mode_cmd_ies_s::to_json(json_writer& j) const // UECapabilityEnquiry-IEs ::= SEQUENCE SRSASN_CODE ue_cap_enquiry_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); - HANDLE_CODE(bref.pack(ue_cap_enquiry_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); + HANDLE_CODE(bref.pack(ue_cap_enquiry_ext.size() > 0, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, ue_cap_rat_request_list, 1, 8)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } - if (ue_cap_enquiry_ext_present) { + if (ue_cap_enquiry_ext.size() > 0) { HANDLE_CODE(ue_cap_enquiry_ext.pack(bref)); } @@ -16082,7 +16107,9 @@ SRSASN_CODE ue_cap_enquiry_ies_s::pack(bit_ref& bref) const } SRSASN_CODE ue_cap_enquiry_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); + bool ue_cap_enquiry_ext_present; HANDLE_CODE(bref.unpack(ue_cap_enquiry_ext_present, 1)); HANDLE_CODE(unpack_dyn_seq_of(ue_cap_rat_request_list, bref, 1, 8)); @@ -16103,10 +16130,10 @@ void ue_cap_enquiry_ies_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } - if (ue_cap_enquiry_ext_present) { + if (ue_cap_enquiry_ext.size() > 0) { j.write_str("ue-CapabilityEnquiryExt", ue_cap_enquiry_ext.to_string()); } j.end_obj(); @@ -17656,14 +17683,14 @@ void paging_record_s::to_json(json_writer& j) const // Paging ::= SEQUENCE SRSASN_CODE paging_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(paging_record_list_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(paging_record_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (paging_record_list_present) { + if (paging_record_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, paging_record_list, 1, 32)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -17671,7 +17698,9 @@ SRSASN_CODE paging_s::pack(bit_ref& bref) const } SRSASN_CODE paging_s::unpack(cbit_ref& bref) { + bool paging_record_list_present; HANDLE_CODE(bref.unpack(paging_record_list_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -17687,14 +17716,14 @@ SRSASN_CODE paging_s::unpack(cbit_ref& bref) void paging_s::to_json(json_writer& j) const { j.start_obj(); - if (paging_record_list_present) { + if (paging_record_list.size() > 0) { j.start_array("pagingRecordList"); for (const auto& e1 : paging_record_list) { e1.to_json(j); } j.end_array(); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -19273,14 +19302,14 @@ void results_per_ssb_idx_s::to_json(json_writer& j) const SRSASN_CODE cgi_info_nr_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(plmn_id_info_list_present, 1)); - HANDLE_CODE(bref.pack(freq_band_list_present, 1)); + HANDLE_CODE(bref.pack(plmn_id_info_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(freq_band_list.size() > 0, 1)); HANDLE_CODE(bref.pack(no_sib1_present, 1)); - if (plmn_id_info_list_present) { + if (plmn_id_info_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, plmn_id_info_list, 1, 12)); } - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_band_list, 1, 8, integer_packer(1, 1024))); } if (no_sib1_present) { @@ -19293,7 +19322,9 @@ SRSASN_CODE cgi_info_nr_s::pack(bit_ref& bref) const SRSASN_CODE cgi_info_nr_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool plmn_id_info_list_present; HANDLE_CODE(bref.unpack(plmn_id_info_list_present, 1)); + bool freq_band_list_present; HANDLE_CODE(bref.unpack(freq_band_list_present, 1)); HANDLE_CODE(bref.unpack(no_sib1_present, 1)); @@ -19313,14 +19344,14 @@ SRSASN_CODE cgi_info_nr_s::unpack(cbit_ref& bref) void cgi_info_nr_s::to_json(json_writer& j) const { j.start_obj(); - if (plmn_id_info_list_present) { + if (plmn_id_info_list.size() > 0) { j.start_array("plmn-IdentityInfoList"); for (const auto& e1 : plmn_id_info_list) { e1.to_json(j); } j.end_array(); } - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { j.start_array("frequencyBandList"); for (const auto& e1 : freq_band_list) { j.write_int(e1); @@ -19416,22 +19447,22 @@ void cell_access_related_info_eutra_epc_s::to_json(json_writer& j) const SRSASN_CODE cgi_info_eutra_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(cgi_info_epc_present, 1)); - HANDLE_CODE(bref.pack(cgi_info_minus5_gc_present, 1)); - HANDLE_CODE(bref.pack(multi_band_info_list_present, 1)); + HANDLE_CODE(bref.pack(cgi_info_minus5_gc.size() > 0, 1)); + HANDLE_CODE(bref.pack(multi_band_info_list.size() > 0, 1)); HANDLE_CODE(bref.pack(freq_band_ind_prio_present, 1)); if (cgi_info_epc_present) { - HANDLE_CODE(bref.pack(cgi_info_epc.cgi_info_epc_list_present, 1)); + HANDLE_CODE(bref.pack(cgi_info_epc.cgi_info_epc_list.size() > 0, 1)); HANDLE_CODE(cgi_info_epc.cgi_info_epc_legacy.pack(bref)); - if (cgi_info_epc.cgi_info_epc_list_present) { + if (cgi_info_epc.cgi_info_epc_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, cgi_info_epc.cgi_info_epc_list, 1, 12)); } } - if (cgi_info_minus5_gc_present) { + if (cgi_info_minus5_gc.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, cgi_info_minus5_gc, 1, 12)); } HANDLE_CODE(pack_integer(bref, freq_band_ind, (uint16_t)1u, (uint16_t)256u)); - if (multi_band_info_list_present) { + if (multi_band_info_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, multi_band_info_list, 1, 8, integer_packer(1, 256))); } @@ -19440,14 +19471,17 @@ SRSASN_CODE cgi_info_eutra_s::pack(bit_ref& bref) const SRSASN_CODE cgi_info_eutra_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(cgi_info_epc_present, 1)); + bool cgi_info_minus5_gc_present; HANDLE_CODE(bref.unpack(cgi_info_minus5_gc_present, 1)); + bool multi_band_info_list_present; HANDLE_CODE(bref.unpack(multi_band_info_list_present, 1)); HANDLE_CODE(bref.unpack(freq_band_ind_prio_present, 1)); if (cgi_info_epc_present) { - HANDLE_CODE(bref.unpack(cgi_info_epc.cgi_info_epc_list_present, 1)); + bool cgi_info_epc_list_present; + HANDLE_CODE(bref.unpack(cgi_info_epc_list_present, 1)); HANDLE_CODE(cgi_info_epc.cgi_info_epc_legacy.unpack(bref)); - if (cgi_info_epc.cgi_info_epc_list_present) { + if (cgi_info_epc_list_present) { HANDLE_CODE(unpack_dyn_seq_of(cgi_info_epc.cgi_info_epc_list, bref, 1, 12)); } } @@ -19469,7 +19503,7 @@ void cgi_info_eutra_s::to_json(json_writer& j) const j.start_obj(); j.write_fieldname("cgi-info-EPC-legacy"); cgi_info_epc.cgi_info_epc_legacy.to_json(j); - if (cgi_info_epc.cgi_info_epc_list_present) { + if (cgi_info_epc.cgi_info_epc_list.size() > 0) { j.start_array("cgi-info-EPC-list"); for (const auto& e1 : cgi_info_epc.cgi_info_epc_list) { e1.to_json(j); @@ -19478,7 +19512,7 @@ void cgi_info_eutra_s::to_json(json_writer& j) const } j.end_obj(); } - if (cgi_info_minus5_gc_present) { + if (cgi_info_minus5_gc.size() > 0) { j.start_array("cgi-info-5GC"); for (const auto& e1 : cgi_info_minus5_gc) { e1.to_json(j); @@ -19486,7 +19520,7 @@ void cgi_info_eutra_s::to_json(json_writer& j) const j.end_array(); } j.write_int("freqBandIndicator", freq_band_ind); - if (multi_band_info_list_present) { + if (multi_band_info_list.size() > 0) { j.start_array("multiBandInfoList"); for (const auto& e1 : multi_band_info_list) { j.write_int(e1); @@ -19570,12 +19604,12 @@ SRSASN_CODE meas_result_nr_s::pack(bit_ref& bref) const HANDLE_CODE(meas_result.cell_results.results_csi_rs_cell.pack(bref)); } if (meas_result.rs_idx_results_present) { - HANDLE_CODE(bref.pack(meas_result.rs_idx_results.results_ssb_idxes_present, 1)); - HANDLE_CODE(bref.pack(meas_result.rs_idx_results.results_csi_rs_idxes_present, 1)); - if (meas_result.rs_idx_results.results_ssb_idxes_present) { + HANDLE_CODE(bref.pack(meas_result.rs_idx_results.results_ssb_idxes.size() > 0, 1)); + HANDLE_CODE(bref.pack(meas_result.rs_idx_results.results_csi_rs_idxes.size() > 0, 1)); + if (meas_result.rs_idx_results.results_ssb_idxes.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_result.rs_idx_results.results_ssb_idxes, 1, 64)); } - if (meas_result.rs_idx_results.results_csi_rs_idxes_present) { + if (meas_result.rs_idx_results.results_csi_rs_idxes.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_result.rs_idx_results.results_csi_rs_idxes, 1, 64)); } } @@ -19614,12 +19648,14 @@ SRSASN_CODE meas_result_nr_s::unpack(cbit_ref& bref) HANDLE_CODE(meas_result.cell_results.results_csi_rs_cell.unpack(bref)); } if (meas_result.rs_idx_results_present) { - HANDLE_CODE(bref.unpack(meas_result.rs_idx_results.results_ssb_idxes_present, 1)); - HANDLE_CODE(bref.unpack(meas_result.rs_idx_results.results_csi_rs_idxes_present, 1)); - if (meas_result.rs_idx_results.results_ssb_idxes_present) { + bool results_ssb_idxes_present; + HANDLE_CODE(bref.unpack(results_ssb_idxes_present, 1)); + bool results_csi_rs_idxes_present; + HANDLE_CODE(bref.unpack(results_csi_rs_idxes_present, 1)); + if (results_ssb_idxes_present) { HANDLE_CODE(unpack_dyn_seq_of(meas_result.rs_idx_results.results_ssb_idxes, bref, 1, 64)); } - if (meas_result.rs_idx_results.results_csi_rs_idxes_present) { + if (results_csi_rs_idxes_present) { HANDLE_CODE(unpack_dyn_seq_of(meas_result.rs_idx_results.results_csi_rs_idxes, bref, 1, 64)); } } @@ -19663,14 +19699,14 @@ void meas_result_nr_s::to_json(json_writer& j) const if (meas_result.rs_idx_results_present) { j.write_fieldname("rsIndexResults"); j.start_obj(); - if (meas_result.rs_idx_results.results_ssb_idxes_present) { + if (meas_result.rs_idx_results.results_ssb_idxes.size() > 0) { j.start_array("resultsSSB-Indexes"); for (const auto& e1 : meas_result.rs_idx_results.results_ssb_idxes) { e1.to_json(j); } j.end_array(); } - if (meas_result.rs_idx_results.results_csi_rs_idxes_present) { + if (meas_result.rs_idx_results.results_csi_rs_idxes.size() > 0) { j.start_array("resultsCSI-RS-Indexes"); for (const auto& e1 : meas_result.rs_idx_results.results_csi_rs_idxes) { e1.to_json(j); @@ -19859,7 +19895,7 @@ SRSASN_CODE meas_result2_nr_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(ssb_freq_present, 1)); HANDLE_CODE(bref.pack(ref_freq_csi_rs_present, 1)); HANDLE_CODE(bref.pack(meas_result_serving_cell_present, 1)); - HANDLE_CODE(bref.pack(meas_result_neigh_cell_list_nr_present, 1)); + HANDLE_CODE(bref.pack(meas_result_neigh_cell_list_nr.size() > 0, 1)); if (ssb_freq_present) { HANDLE_CODE(pack_integer(bref, ssb_freq, (uint32_t)0u, (uint32_t)3279165u)); @@ -19870,7 +19906,7 @@ SRSASN_CODE meas_result2_nr_s::pack(bit_ref& bref) const if (meas_result_serving_cell_present) { HANDLE_CODE(meas_result_serving_cell.pack(bref)); } - if (meas_result_neigh_cell_list_nr_present) { + if (meas_result_neigh_cell_list_nr.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_result_neigh_cell_list_nr, 1, 8)); } @@ -19882,6 +19918,7 @@ SRSASN_CODE meas_result2_nr_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(ssb_freq_present, 1)); HANDLE_CODE(bref.unpack(ref_freq_csi_rs_present, 1)); HANDLE_CODE(bref.unpack(meas_result_serving_cell_present, 1)); + bool meas_result_neigh_cell_list_nr_present; HANDLE_CODE(bref.unpack(meas_result_neigh_cell_list_nr_present, 1)); if (ssb_freq_present) { @@ -19912,7 +19949,7 @@ void meas_result2_nr_s::to_json(json_writer& j) const j.write_fieldname("measResultServingCell"); meas_result_serving_cell.to_json(j); } - if (meas_result_neigh_cell_list_nr_present) { + if (meas_result_neigh_cell_list_nr.size() > 0) { j.start_array("measResultNeighCellListNR"); for (const auto& e1 : meas_result_neigh_cell_list_nr) { e1.to_json(j); @@ -20545,14 +20582,14 @@ const char* fail_info_rlc_bearer_s::fail_type_opts::to_string() const SRSASN_CODE fail_report_scg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(meas_result_freq_list_present, 1)); - HANDLE_CODE(bref.pack(meas_result_scg_fail_present, 1)); + HANDLE_CODE(bref.pack(meas_result_freq_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(meas_result_scg_fail.size() > 0, 1)); HANDLE_CODE(fail_type.pack(bref)); - if (meas_result_freq_list_present) { + if (meas_result_freq_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_result_freq_list, 1, 8)); } - if (meas_result_scg_fail_present) { + if (meas_result_scg_fail.size() > 0) { HANDLE_CODE(meas_result_scg_fail.pack(bref)); } @@ -20561,7 +20598,9 @@ SRSASN_CODE fail_report_scg_s::pack(bit_ref& bref) const SRSASN_CODE fail_report_scg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool meas_result_freq_list_present; HANDLE_CODE(bref.unpack(meas_result_freq_list_present, 1)); + bool meas_result_scg_fail_present; HANDLE_CODE(bref.unpack(meas_result_scg_fail_present, 1)); HANDLE_CODE(fail_type.unpack(bref)); @@ -20578,14 +20617,14 @@ void fail_report_scg_s::to_json(json_writer& j) const { j.start_obj(); j.write_str("failureType", fail_type.to_string()); - if (meas_result_freq_list_present) { + if (meas_result_freq_list.size() > 0) { j.start_array("measResultFreqList"); for (const auto& e1 : meas_result_freq_list) { e1.to_json(j); } j.end_array(); } - if (meas_result_scg_fail_present) { + if (meas_result_scg_fail.size() > 0) { j.write_str("measResultSCG-Failure", meas_result_scg_fail.to_string()); } j.end_obj(); @@ -20620,14 +20659,14 @@ uint16_t fail_report_scg_s::fail_type_opts::to_number() const SRSASN_CODE fail_report_scg_eutra_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(meas_result_freq_list_mrdc_present, 1)); - HANDLE_CODE(bref.pack(meas_result_scg_fail_mrdc_present, 1)); + HANDLE_CODE(bref.pack(meas_result_freq_list_mrdc.size() > 0, 1)); + HANDLE_CODE(bref.pack(meas_result_scg_fail_mrdc.size() > 0, 1)); HANDLE_CODE(fail_type.pack(bref)); - if (meas_result_freq_list_mrdc_present) { + if (meas_result_freq_list_mrdc.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_result_freq_list_mrdc, 1, 8)); } - if (meas_result_scg_fail_mrdc_present) { + if (meas_result_scg_fail_mrdc.size() > 0) { HANDLE_CODE(meas_result_scg_fail_mrdc.pack(bref)); } @@ -20636,7 +20675,9 @@ SRSASN_CODE fail_report_scg_eutra_s::pack(bit_ref& bref) const SRSASN_CODE fail_report_scg_eutra_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool meas_result_freq_list_mrdc_present; HANDLE_CODE(bref.unpack(meas_result_freq_list_mrdc_present, 1)); + bool meas_result_scg_fail_mrdc_present; HANDLE_CODE(bref.unpack(meas_result_scg_fail_mrdc_present, 1)); HANDLE_CODE(fail_type.unpack(bref)); @@ -20653,14 +20694,14 @@ void fail_report_scg_eutra_s::to_json(json_writer& j) const { j.start_obj(); j.write_str("failureType", fail_type.to_string()); - if (meas_result_freq_list_mrdc_present) { + if (meas_result_freq_list_mrdc.size() > 0) { j.start_array("measResultFreqListMRDC"); for (const auto& e1 : meas_result_freq_list_mrdc) { e1.to_json(j); } j.end_array(); } - if (meas_result_scg_fail_mrdc_present) { + if (meas_result_scg_fail_mrdc.size() > 0) { j.write_str("measResultSCG-FailureMRDC", meas_result_scg_fail_mrdc.to_string()); } j.end_obj(); @@ -21058,10 +21099,10 @@ const char* meas_results_s::meas_result_neigh_cells_c_::types_opts::to_string() // RRCReconfigurationComplete-v1530-IEs ::= SEQUENCE SRSASN_CODE rrc_recfg_complete_v1530_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(ul_tx_direct_current_list_present, 1)); + HANDLE_CODE(bref.pack(ul_tx_direct_current_list.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (ul_tx_direct_current_list_present) { + if (ul_tx_direct_current_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ul_tx_direct_current_list, 1, 32)); } if (non_crit_ext_present) { @@ -21072,6 +21113,7 @@ SRSASN_CODE rrc_recfg_complete_v1530_ies_s::pack(bit_ref& bref) const } SRSASN_CODE rrc_recfg_complete_v1530_ies_s::unpack(cbit_ref& bref) { + bool ul_tx_direct_current_list_present; HANDLE_CODE(bref.unpack(ul_tx_direct_current_list_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21087,7 +21129,7 @@ SRSASN_CODE rrc_recfg_complete_v1530_ies_s::unpack(cbit_ref& bref) void rrc_recfg_complete_v1530_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (ul_tx_direct_current_list_present) { + if (ul_tx_direct_current_list.size() > 0) { j.start_array("uplinkTxDirectCurrentList"); for (const auto& e1 : ul_tx_direct_current_list) { e1.to_json(j); @@ -21272,10 +21314,10 @@ const char* s_nssai_c::types_opts::to_string() const // SCGFailureInformation-v1590-IEs ::= SEQUENCE SRSASN_CODE scg_fail_info_v1590_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21283,6 +21325,7 @@ SRSASN_CODE scg_fail_info_v1590_ies_s::pack(bit_ref& bref) const } SRSASN_CODE scg_fail_info_v1590_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21295,7 +21338,7 @@ SRSASN_CODE scg_fail_info_v1590_ies_s::unpack(cbit_ref& bref) void scg_fail_info_v1590_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21309,10 +21352,10 @@ void scg_fail_info_v1590_ies_s::to_json(json_writer& j) const // SCGFailureInformationEUTRA-v1590-IEs ::= SEQUENCE SRSASN_CODE scg_fail_info_eutra_v1590_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21320,6 +21363,7 @@ SRSASN_CODE scg_fail_info_eutra_v1590_ies_s::pack(bit_ref& bref) const } SRSASN_CODE scg_fail_info_eutra_v1590_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21332,7 +21376,7 @@ SRSASN_CODE scg_fail_info_eutra_v1590_ies_s::unpack(cbit_ref& bref) void scg_fail_info_eutra_v1590_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21384,11 +21428,11 @@ void ueassist_info_v1540_ies_s::to_json(json_writer& j) const // CounterCheckResponse-IEs ::= SEQUENCE SRSASN_CODE counter_check_resp_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, drb_count_info_list, 0, 29)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21396,6 +21440,7 @@ SRSASN_CODE counter_check_resp_ies_s::pack(bit_ref& bref) const } SRSASN_CODE counter_check_resp_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21414,7 +21459,7 @@ void counter_check_resp_ies_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21429,13 +21474,13 @@ void counter_check_resp_ies_s::to_json(json_writer& j) const SRSASN_CODE fail_info_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(fail_info_rlc_bearer_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (fail_info_rlc_bearer_present) { HANDLE_CODE(fail_info_rlc_bearer.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21444,6 +21489,7 @@ SRSASN_CODE fail_info_ies_s::pack(bit_ref& bref) const SRSASN_CODE fail_info_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(fail_info_rlc_bearer_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21463,7 +21509,7 @@ void fail_info_ies_s::to_json(json_writer& j) const j.write_fieldname("failureInfoRLC-Bearer"); fail_info_rlc_bearer.to_json(j); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21477,11 +21523,11 @@ void fail_info_ies_s::to_json(json_writer& j) const // LocationMeasurementIndication-IEs ::= SEQUENCE SRSASN_CODE location_meas_ind_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(meas_ind.pack(bref)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21489,6 +21535,7 @@ SRSASN_CODE location_meas_ind_ies_s::pack(bit_ref& bref) const } SRSASN_CODE location_meas_ind_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21504,7 +21551,7 @@ void location_meas_ind_ies_s::to_json(json_writer& j) const j.start_obj(); j.write_fieldname("measurementIndication"); meas_ind.to_json(j); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21518,11 +21565,11 @@ void location_meas_ind_ies_s::to_json(json_writer& j) const // MeasurementReport-IEs ::= SEQUENCE SRSASN_CODE meas_report_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(meas_results.pack(bref)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21530,6 +21577,7 @@ SRSASN_CODE meas_report_ies_s::pack(bit_ref& bref) const } SRSASN_CODE meas_report_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21545,7 +21593,7 @@ void meas_report_ies_s::to_json(json_writer& j) const j.start_obj(); j.write_fieldname("measResults"); meas_results.to_json(j); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21559,10 +21607,10 @@ void meas_report_ies_s::to_json(json_writer& j) const // RRCReconfigurationComplete-IEs ::= SEQUENCE SRSASN_CODE rrc_recfg_complete_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } if (non_crit_ext_present) { @@ -21573,6 +21621,7 @@ SRSASN_CODE rrc_recfg_complete_ies_s::pack(bit_ref& bref) const } SRSASN_CODE rrc_recfg_complete_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21588,7 +21637,7 @@ SRSASN_CODE rrc_recfg_complete_ies_s::unpack(cbit_ref& bref) void rrc_recfg_complete_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21601,10 +21650,10 @@ void rrc_recfg_complete_ies_s::to_json(json_writer& j) const // RRCReestablishmentComplete-IEs ::= SEQUENCE SRSASN_CODE rrc_reest_complete_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21612,6 +21661,7 @@ SRSASN_CODE rrc_reest_complete_ies_s::pack(bit_ref& bref) const } SRSASN_CODE rrc_reest_complete_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21624,7 +21674,7 @@ SRSASN_CODE rrc_reest_complete_ies_s::unpack(cbit_ref& bref) void rrc_reest_complete_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21638,22 +21688,22 @@ void rrc_reest_complete_ies_s::to_json(json_writer& j) const // RRCResumeComplete-IEs ::= SEQUENCE SRSASN_CODE rrc_resume_complete_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(ded_nas_msg_present, 1)); + HANDLE_CODE(bref.pack(ded_nas_msg.size() > 0, 1)); HANDLE_CODE(bref.pack(sel_plmn_id_present, 1)); - HANDLE_CODE(bref.pack(ul_tx_direct_current_list_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(ul_tx_direct_current_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (ded_nas_msg_present) { + if (ded_nas_msg.size() > 0) { HANDLE_CODE(ded_nas_msg.pack(bref)); } if (sel_plmn_id_present) { HANDLE_CODE(pack_integer(bref, sel_plmn_id, (uint8_t)1u, (uint8_t)12u)); } - if (ul_tx_direct_current_list_present) { + if (ul_tx_direct_current_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ul_tx_direct_current_list, 1, 32)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21661,9 +21711,12 @@ SRSASN_CODE rrc_resume_complete_ies_s::pack(bit_ref& bref) const } SRSASN_CODE rrc_resume_complete_ies_s::unpack(cbit_ref& bref) { + bool ded_nas_msg_present; HANDLE_CODE(bref.unpack(ded_nas_msg_present, 1)); HANDLE_CODE(bref.unpack(sel_plmn_id_present, 1)); + bool ul_tx_direct_current_list_present; HANDLE_CODE(bref.unpack(ul_tx_direct_current_list_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21685,20 +21738,20 @@ SRSASN_CODE rrc_resume_complete_ies_s::unpack(cbit_ref& bref) void rrc_resume_complete_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (ded_nas_msg_present) { + if (ded_nas_msg.size() > 0) { j.write_str("dedicatedNAS-Message", ded_nas_msg.to_string()); } if (sel_plmn_id_present) { j.write_int("selectedPLMN-Identity", sel_plmn_id); } - if (ul_tx_direct_current_list_present) { + if (ul_tx_direct_current_list.size() > 0) { j.start_array("uplinkTxDirectCurrentList"); for (const auto& e1 : ul_tx_direct_current_list) { e1.to_json(j); } j.end_array(); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -21714,9 +21767,9 @@ SRSASN_CODE rrc_setup_complete_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(registered_amf_present, 1)); HANDLE_CODE(bref.pack(guami_type_present, 1)); - HANDLE_CODE(bref.pack(s_nssai_list_present, 1)); + HANDLE_CODE(bref.pack(s_nssai_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ng_minus5_g_s_tmsi_value_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(pack_integer(bref, sel_plmn_id, (uint8_t)1u, (uint8_t)12u)); @@ -21726,14 +21779,14 @@ SRSASN_CODE rrc_setup_complete_ies_s::pack(bit_ref& bref) const if (guami_type_present) { HANDLE_CODE(guami_type.pack(bref)); } - if (s_nssai_list_present) { + if (s_nssai_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, s_nssai_list, 1, 8)); } HANDLE_CODE(ded_nas_msg.pack(bref)); if (ng_minus5_g_s_tmsi_value_present) { HANDLE_CODE(ng_minus5_g_s_tmsi_value.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -21743,8 +21796,10 @@ SRSASN_CODE rrc_setup_complete_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(registered_amf_present, 1)); HANDLE_CODE(bref.unpack(guami_type_present, 1)); + bool s_nssai_list_present; HANDLE_CODE(bref.unpack(s_nssai_list_present, 1)); HANDLE_CODE(bref.unpack(ng_minus5_g_s_tmsi_value_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -21779,7 +21834,7 @@ void rrc_setup_complete_ies_s::to_json(json_writer& j) const if (guami_type_present) { j.write_str("guami-Type", guami_type.to_string()); } - if (s_nssai_list_present) { + if (s_nssai_list.size() > 0) { j.start_array("s-NSSAI-List"); for (const auto& e1 : s_nssai_list) { e1.to_json(j); @@ -21791,7 +21846,7 @@ void rrc_setup_complete_ies_s::to_json(json_writer& j) const j.write_fieldname("ng-5G-S-TMSI-Value"); ng_minus5_g_s_tmsi_value.to_json(j); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -22032,10 +22087,10 @@ void scg_fail_info_eutra_ies_s::to_json(json_writer& j) const // SecurityModeComplete-IEs ::= SEQUENCE SRSASN_CODE security_mode_complete_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -22043,6 +22098,7 @@ SRSASN_CODE security_mode_complete_ies_s::pack(bit_ref& bref) const } SRSASN_CODE security_mode_complete_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -22055,7 +22111,7 @@ SRSASN_CODE security_mode_complete_ies_s::unpack(cbit_ref& bref) void security_mode_complete_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -22069,10 +22125,10 @@ void security_mode_complete_ies_s::to_json(json_writer& j) const // SecurityModeFailure-IEs ::= SEQUENCE SRSASN_CODE security_mode_fail_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -22080,6 +22136,7 @@ SRSASN_CODE security_mode_fail_ies_s::pack(bit_ref& bref) const } SRSASN_CODE security_mode_fail_ies_s::unpack(cbit_ref& bref) { + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -22092,7 +22149,7 @@ SRSASN_CODE security_mode_fail_ies_s::unpack(cbit_ref& bref) void security_mode_fail_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -22107,13 +22164,13 @@ void security_mode_fail_ies_s::to_json(json_writer& j) const SRSASN_CODE ueassist_info_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(delay_budget_report_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (delay_budget_report_present) { HANDLE_CODE(delay_budget_report.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } if (non_crit_ext_present) { @@ -22125,6 +22182,7 @@ SRSASN_CODE ueassist_info_ies_s::pack(bit_ref& bref) const SRSASN_CODE ueassist_info_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(delay_budget_report_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -22147,7 +22205,7 @@ void ueassist_info_ies_s::to_json(json_writer& j) const j.write_fieldname("delayBudgetReport"); delay_budget_report.to_json(j); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -22161,13 +22219,13 @@ void ueassist_info_ies_s::to_json(json_writer& j) const SRSASN_CODE ue_cap_info_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(ue_cap_rat_container_list_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (ue_cap_rat_container_list_present) { HANDLE_CODE(pack_dyn_seq_of(bref, ue_cap_rat_container_list, 0, 8)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -22176,6 +22234,7 @@ SRSASN_CODE ue_cap_info_ies_s::pack(bit_ref& bref) const SRSASN_CODE ue_cap_info_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(ue_cap_rat_container_list_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -22198,7 +22257,7 @@ void ue_cap_info_ies_s::to_json(json_writer& j) const } j.end_array(); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -22212,14 +22271,14 @@ void ue_cap_info_ies_s::to_json(json_writer& j) const // ULInformationTransfer-IEs ::= SEQUENCE SRSASN_CODE ul_info_transfer_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(ded_nas_msg_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(ded_nas_msg.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (ded_nas_msg_present) { + if (ded_nas_msg.size() > 0) { HANDLE_CODE(ded_nas_msg.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -22227,7 +22286,9 @@ SRSASN_CODE ul_info_transfer_ies_s::pack(bit_ref& bref) const } SRSASN_CODE ul_info_transfer_ies_s::unpack(cbit_ref& bref) { + bool ded_nas_msg_present; HANDLE_CODE(bref.unpack(ded_nas_msg_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -22243,10 +22304,10 @@ SRSASN_CODE ul_info_transfer_ies_s::unpack(cbit_ref& bref) void ul_info_transfer_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (ded_nas_msg_present) { + if (ded_nas_msg.size() > 0) { j.write_str("dedicatedNAS-Message", ded_nas_msg.to_string()); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -22260,18 +22321,18 @@ void ul_info_transfer_ies_s::to_json(json_writer& j) const // ULInformationTransferMRDC-IEs ::= SEQUENCE SRSASN_CODE ul_info_transfer_mrdc_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(ul_dcch_msg_nr_present, 1)); - HANDLE_CODE(bref.pack(ul_dcch_msg_eutra_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(ul_dcch_msg_nr.size() > 0, 1)); + HANDLE_CODE(bref.pack(ul_dcch_msg_eutra.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (ul_dcch_msg_nr_present) { + if (ul_dcch_msg_nr.size() > 0) { HANDLE_CODE(ul_dcch_msg_nr.pack(bref)); } - if (ul_dcch_msg_eutra_present) { + if (ul_dcch_msg_eutra.size() > 0) { HANDLE_CODE(ul_dcch_msg_eutra.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -22279,8 +22340,11 @@ SRSASN_CODE ul_info_transfer_mrdc_ies_s::pack(bit_ref& bref) const } SRSASN_CODE ul_info_transfer_mrdc_ies_s::unpack(cbit_ref& bref) { + bool ul_dcch_msg_nr_present; HANDLE_CODE(bref.unpack(ul_dcch_msg_nr_present, 1)); + bool ul_dcch_msg_eutra_present; HANDLE_CODE(bref.unpack(ul_dcch_msg_eutra_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -22299,13 +22363,13 @@ SRSASN_CODE ul_info_transfer_mrdc_ies_s::unpack(cbit_ref& bref) void ul_info_transfer_mrdc_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (ul_dcch_msg_nr_present) { + if (ul_dcch_msg_nr.size() > 0) { j.write_str("ul-DCCH-MessageNR", ul_dcch_msg_nr.to_string()); } - if (ul_dcch_msg_eutra_present) { + if (ul_dcch_msg_eutra.size() > 0) { j.write_str("ul-DCCH-MessageEUTRA", ul_dcch_msg_eutra.to_string()); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -24474,11 +24538,11 @@ void ul_dcch_msg_s::to_json(json_writer& j) const SRSASN_CODE bfr_csirs_res_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(ra_occasion_list_present, 1)); + HANDLE_CODE(bref.pack(ra_occasion_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ra_preamb_idx_present, 1)); HANDLE_CODE(pack_integer(bref, csi_rs, (uint8_t)0u, (uint8_t)191u)); - if (ra_occasion_list_present) { + if (ra_occasion_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ra_occasion_list, 1, 64, integer_packer(0, 511))); } if (ra_preamb_idx_present) { @@ -24490,6 +24554,7 @@ SRSASN_CODE bfr_csirs_res_s::pack(bit_ref& bref) const SRSASN_CODE bfr_csirs_res_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool ra_occasion_list_present; HANDLE_CODE(bref.unpack(ra_occasion_list_present, 1)); HANDLE_CODE(bref.unpack(ra_preamb_idx_present, 1)); @@ -24507,7 +24572,7 @@ void bfr_csirs_res_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("csi-RS", csi_rs); - if (ra_occasion_list_present) { + if (ra_occasion_list.size() > 0) { j.start_array("ra-OccasionList"); for (const auto& e1 : ra_occasion_list) { j.write_int(e1); @@ -26856,25 +26921,25 @@ void zp_csi_rs_res_set_s::to_json(json_writer& j) const SRSASN_CODE pdcch_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(ctrl_res_set_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(ctrl_res_set_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(search_spaces_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(search_spaces_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(ctrl_res_set_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(ctrl_res_set_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(search_spaces_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(search_spaces_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(dl_preemption_present, 1)); HANDLE_CODE(bref.pack(tpc_pusch_present, 1)); HANDLE_CODE(bref.pack(tpc_pucch_present, 1)); HANDLE_CODE(bref.pack(tpc_srs_present, 1)); - if (ctrl_res_set_to_add_mod_list_present) { + if (ctrl_res_set_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ctrl_res_set_to_add_mod_list, 1, 3)); } - if (ctrl_res_set_to_release_list_present) { + if (ctrl_res_set_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ctrl_res_set_to_release_list, 1, 3, integer_packer(0, 11))); } - if (search_spaces_to_add_mod_list_present) { + if (search_spaces_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, search_spaces_to_add_mod_list, 1, 10)); } - if (search_spaces_to_release_list_present) { + if (search_spaces_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, search_spaces_to_release_list, 1, 10, integer_packer(0, 39))); } if (dl_preemption_present) { @@ -26895,9 +26960,13 @@ SRSASN_CODE pdcch_cfg_s::pack(bit_ref& bref) const SRSASN_CODE pdcch_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool ctrl_res_set_to_add_mod_list_present; HANDLE_CODE(bref.unpack(ctrl_res_set_to_add_mod_list_present, 1)); + bool ctrl_res_set_to_release_list_present; HANDLE_CODE(bref.unpack(ctrl_res_set_to_release_list_present, 1)); + bool search_spaces_to_add_mod_list_present; HANDLE_CODE(bref.unpack(search_spaces_to_add_mod_list_present, 1)); + bool search_spaces_to_release_list_present; HANDLE_CODE(bref.unpack(search_spaces_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(dl_preemption_present, 1)); HANDLE_CODE(bref.unpack(tpc_pusch_present, 1)); @@ -26934,28 +27003,28 @@ SRSASN_CODE pdcch_cfg_s::unpack(cbit_ref& bref) void pdcch_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (ctrl_res_set_to_add_mod_list_present) { + if (ctrl_res_set_to_add_mod_list.size() > 0) { j.start_array("controlResourceSetToAddModList"); for (const auto& e1 : ctrl_res_set_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (ctrl_res_set_to_release_list_present) { + if (ctrl_res_set_to_release_list.size() > 0) { j.start_array("controlResourceSetToReleaseList"); for (const auto& e1 : ctrl_res_set_to_release_list) { j.write_int(e1); } j.end_array(); } - if (search_spaces_to_add_mod_list_present) { + if (search_spaces_to_add_mod_list.size() > 0) { j.start_array("searchSpacesToAddModList"); for (const auto& e1 : search_spaces_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (search_spaces_to_release_list_present) { + if (search_spaces_to_release_list.size() > 0) { j.start_array("searchSpacesToReleaseList"); for (const auto& e1 : search_spaces_to_release_list) { j.write_int(e1); @@ -26988,23 +27057,23 @@ SRSASN_CODE pdsch_cfg_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(data_scrambling_id_pdsch_present, 1)); HANDLE_CODE(bref.pack(dmrs_dl_for_pdsch_map_type_a_present, 1)); HANDLE_CODE(bref.pack(dmrs_dl_for_pdsch_map_type_b_present, 1)); - HANDLE_CODE(bref.pack(tci_states_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(tci_states_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(tci_states_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(tci_states_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(vrb_to_prb_interleaver_present, 1)); HANDLE_CODE(bref.pack(pdsch_time_domain_alloc_list_present, 1)); HANDLE_CODE(bref.pack(pdsch_aggregation_factor_present, 1)); - HANDLE_CODE(bref.pack(rate_match_pattern_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(rate_match_pattern_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(rate_match_pattern_group1_present, 1)); - HANDLE_CODE(bref.pack(rate_match_pattern_group2_present, 1)); + HANDLE_CODE(bref.pack(rate_match_pattern_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(rate_match_pattern_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(rate_match_pattern_group1.size() > 0, 1)); + HANDLE_CODE(bref.pack(rate_match_pattern_group2.size() > 0, 1)); HANDLE_CODE(bref.pack(mcs_table_present, 1)); HANDLE_CODE(bref.pack(max_nrof_code_words_sched_by_dci_present, 1)); - HANDLE_CODE(bref.pack(zp_csi_rs_res_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(zp_csi_rs_res_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(aperiodic_zp_csi_rs_res_sets_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(aperiodic_zp_csi_rs_res_sets_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(sp_zp_csi_rs_res_sets_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(sp_zp_csi_rs_res_sets_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(zp_csi_rs_res_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(zp_csi_rs_res_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(aperiodic_zp_csi_rs_res_sets_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(aperiodic_zp_csi_rs_res_sets_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(sp_zp_csi_rs_res_sets_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(sp_zp_csi_rs_res_sets_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(p_zp_csi_rs_res_set_present, 1)); if (data_scrambling_id_pdsch_present) { @@ -27016,10 +27085,10 @@ SRSASN_CODE pdsch_cfg_s::pack(bit_ref& bref) const if (dmrs_dl_for_pdsch_map_type_b_present) { HANDLE_CODE(dmrs_dl_for_pdsch_map_type_b.pack(bref)); } - if (tci_states_to_add_mod_list_present) { + if (tci_states_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, tci_states_to_add_mod_list, 1, 128)); } - if (tci_states_to_release_list_present) { + if (tci_states_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, tci_states_to_release_list, 1, 128, integer_packer(0, 127))); } if (vrb_to_prb_interleaver_present) { @@ -27032,16 +27101,16 @@ SRSASN_CODE pdsch_cfg_s::pack(bit_ref& bref) const if (pdsch_aggregation_factor_present) { HANDLE_CODE(pdsch_aggregation_factor.pack(bref)); } - if (rate_match_pattern_to_add_mod_list_present) { + if (rate_match_pattern_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rate_match_pattern_to_add_mod_list, 1, 4)); } - if (rate_match_pattern_to_release_list_present) { + if (rate_match_pattern_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rate_match_pattern_to_release_list, 1, 4, integer_packer(0, 3))); } - if (rate_match_pattern_group1_present) { + if (rate_match_pattern_group1.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rate_match_pattern_group1, 1, 8)); } - if (rate_match_pattern_group2_present) { + if (rate_match_pattern_group2.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rate_match_pattern_group2, 1, 8)); } HANDLE_CODE(rbg_size.pack(bref)); @@ -27052,23 +27121,23 @@ SRSASN_CODE pdsch_cfg_s::pack(bit_ref& bref) const HANDLE_CODE(max_nrof_code_words_sched_by_dci.pack(bref)); } HANDLE_CODE(prb_bundling_type.pack(bref)); - if (zp_csi_rs_res_to_add_mod_list_present) { + if (zp_csi_rs_res_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, zp_csi_rs_res_to_add_mod_list, 1, 32)); } - if (zp_csi_rs_res_to_release_list_present) { + if (zp_csi_rs_res_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, zp_csi_rs_res_to_release_list, 1, 32, integer_packer(0, 31))); } - if (aperiodic_zp_csi_rs_res_sets_to_add_mod_list_present) { + if (aperiodic_zp_csi_rs_res_sets_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, aperiodic_zp_csi_rs_res_sets_to_add_mod_list, 1, 16)); } - if (aperiodic_zp_csi_rs_res_sets_to_release_list_present) { + if (aperiodic_zp_csi_rs_res_sets_to_release_list.size() > 0) { HANDLE_CODE( pack_dyn_seq_of(bref, aperiodic_zp_csi_rs_res_sets_to_release_list, 1, 16, integer_packer(0, 15))); } - if (sp_zp_csi_rs_res_sets_to_add_mod_list_present) { + if (sp_zp_csi_rs_res_sets_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sp_zp_csi_rs_res_sets_to_add_mod_list, 1, 16)); } - if (sp_zp_csi_rs_res_sets_to_release_list_present) { + if (sp_zp_csi_rs_res_sets_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sp_zp_csi_rs_res_sets_to_release_list, 1, 16, integer_packer(0, 15))); } if (p_zp_csi_rs_res_set_present) { @@ -27083,22 +27152,34 @@ SRSASN_CODE pdsch_cfg_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(data_scrambling_id_pdsch_present, 1)); HANDLE_CODE(bref.unpack(dmrs_dl_for_pdsch_map_type_a_present, 1)); HANDLE_CODE(bref.unpack(dmrs_dl_for_pdsch_map_type_b_present, 1)); + bool tci_states_to_add_mod_list_present; HANDLE_CODE(bref.unpack(tci_states_to_add_mod_list_present, 1)); + bool tci_states_to_release_list_present; HANDLE_CODE(bref.unpack(tci_states_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(vrb_to_prb_interleaver_present, 1)); HANDLE_CODE(bref.unpack(pdsch_time_domain_alloc_list_present, 1)); HANDLE_CODE(bref.unpack(pdsch_aggregation_factor_present, 1)); + bool rate_match_pattern_to_add_mod_list_present; HANDLE_CODE(bref.unpack(rate_match_pattern_to_add_mod_list_present, 1)); + bool rate_match_pattern_to_release_list_present; HANDLE_CODE(bref.unpack(rate_match_pattern_to_release_list_present, 1)); + bool rate_match_pattern_group1_present; HANDLE_CODE(bref.unpack(rate_match_pattern_group1_present, 1)); + bool rate_match_pattern_group2_present; HANDLE_CODE(bref.unpack(rate_match_pattern_group2_present, 1)); HANDLE_CODE(bref.unpack(mcs_table_present, 1)); HANDLE_CODE(bref.unpack(max_nrof_code_words_sched_by_dci_present, 1)); + bool zp_csi_rs_res_to_add_mod_list_present; HANDLE_CODE(bref.unpack(zp_csi_rs_res_to_add_mod_list_present, 1)); + bool zp_csi_rs_res_to_release_list_present; HANDLE_CODE(bref.unpack(zp_csi_rs_res_to_release_list_present, 1)); + bool aperiodic_zp_csi_rs_res_sets_to_add_mod_list_present; HANDLE_CODE(bref.unpack(aperiodic_zp_csi_rs_res_sets_to_add_mod_list_present, 1)); + bool aperiodic_zp_csi_rs_res_sets_to_release_list_present; HANDLE_CODE(bref.unpack(aperiodic_zp_csi_rs_res_sets_to_release_list_present, 1)); + bool sp_zp_csi_rs_res_sets_to_add_mod_list_present; HANDLE_CODE(bref.unpack(sp_zp_csi_rs_res_sets_to_add_mod_list_present, 1)); + bool sp_zp_csi_rs_res_sets_to_release_list_present; HANDLE_CODE(bref.unpack(sp_zp_csi_rs_res_sets_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(p_zp_csi_rs_res_set_present, 1)); @@ -27186,14 +27267,14 @@ void pdsch_cfg_s::to_json(json_writer& j) const j.write_fieldname("dmrs-DownlinkForPDSCH-MappingTypeB"); dmrs_dl_for_pdsch_map_type_b.to_json(j); } - if (tci_states_to_add_mod_list_present) { + if (tci_states_to_add_mod_list.size() > 0) { j.start_array("tci-StatesToAddModList"); for (const auto& e1 : tci_states_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (tci_states_to_release_list_present) { + if (tci_states_to_release_list.size() > 0) { j.start_array("tci-StatesToReleaseList"); for (const auto& e1 : tci_states_to_release_list) { j.write_int(e1); @@ -27211,28 +27292,28 @@ void pdsch_cfg_s::to_json(json_writer& j) const if (pdsch_aggregation_factor_present) { j.write_str("pdsch-AggregationFactor", pdsch_aggregation_factor.to_string()); } - if (rate_match_pattern_to_add_mod_list_present) { + if (rate_match_pattern_to_add_mod_list.size() > 0) { j.start_array("rateMatchPatternToAddModList"); for (const auto& e1 : rate_match_pattern_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (rate_match_pattern_to_release_list_present) { + if (rate_match_pattern_to_release_list.size() > 0) { j.start_array("rateMatchPatternToReleaseList"); for (const auto& e1 : rate_match_pattern_to_release_list) { j.write_int(e1); } j.end_array(); } - if (rate_match_pattern_group1_present) { + if (rate_match_pattern_group1.size() > 0) { j.start_array("rateMatchPatternGroup1"); for (const auto& e1 : rate_match_pattern_group1) { e1.to_json(j); } j.end_array(); } - if (rate_match_pattern_group2_present) { + if (rate_match_pattern_group2.size() > 0) { j.start_array("rateMatchPatternGroup2"); for (const auto& e1 : rate_match_pattern_group2) { e1.to_json(j); @@ -27248,42 +27329,42 @@ void pdsch_cfg_s::to_json(json_writer& j) const } j.write_fieldname("prb-BundlingType"); prb_bundling_type.to_json(j); - if (zp_csi_rs_res_to_add_mod_list_present) { + if (zp_csi_rs_res_to_add_mod_list.size() > 0) { j.start_array("zp-CSI-RS-ResourceToAddModList"); for (const auto& e1 : zp_csi_rs_res_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (zp_csi_rs_res_to_release_list_present) { + if (zp_csi_rs_res_to_release_list.size() > 0) { j.start_array("zp-CSI-RS-ResourceToReleaseList"); for (const auto& e1 : zp_csi_rs_res_to_release_list) { j.write_int(e1); } j.end_array(); } - if (aperiodic_zp_csi_rs_res_sets_to_add_mod_list_present) { + if (aperiodic_zp_csi_rs_res_sets_to_add_mod_list.size() > 0) { j.start_array("aperiodic-ZP-CSI-RS-ResourceSetsToAddModList"); for (const auto& e1 : aperiodic_zp_csi_rs_res_sets_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (aperiodic_zp_csi_rs_res_sets_to_release_list_present) { + if (aperiodic_zp_csi_rs_res_sets_to_release_list.size() > 0) { j.start_array("aperiodic-ZP-CSI-RS-ResourceSetsToReleaseList"); for (const auto& e1 : aperiodic_zp_csi_rs_res_sets_to_release_list) { j.write_int(e1); } j.end_array(); } - if (sp_zp_csi_rs_res_sets_to_add_mod_list_present) { + if (sp_zp_csi_rs_res_sets_to_add_mod_list.size() > 0) { j.start_array("sp-ZP-CSI-RS-ResourceSetsToAddModList"); for (const auto& e1 : sp_zp_csi_rs_res_sets_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (sp_zp_csi_rs_res_sets_to_release_list_present) { + if (sp_zp_csi_rs_res_sets_to_release_list.size() > 0) { j.start_array("sp-ZP-CSI-RS-ResourceSetsToReleaseList"); for (const auto& e1 : sp_zp_csi_rs_res_sets_to_release_list) { j.write_int(e1); @@ -27565,15 +27646,15 @@ const char* pdsch_cfg_s::prb_bundling_type_c_::types_opts::to_string() const SRSASN_CODE radio_link_monitoring_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(fail_detection_res_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(fail_detection_res_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(fail_detection_res_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(fail_detection_res_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(beam_fail_instance_max_count_present, 1)); HANDLE_CODE(bref.pack(beam_fail_detection_timer_present, 1)); - if (fail_detection_res_to_add_mod_list_present) { + if (fail_detection_res_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, fail_detection_res_to_add_mod_list, 1, 10)); } - if (fail_detection_res_to_release_list_present) { + if (fail_detection_res_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, fail_detection_res_to_release_list, 1, 10, integer_packer(0, 9))); } if (beam_fail_instance_max_count_present) { @@ -27588,7 +27669,9 @@ SRSASN_CODE radio_link_monitoring_cfg_s::pack(bit_ref& bref) const SRSASN_CODE radio_link_monitoring_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool fail_detection_res_to_add_mod_list_present; HANDLE_CODE(bref.unpack(fail_detection_res_to_add_mod_list_present, 1)); + bool fail_detection_res_to_release_list_present; HANDLE_CODE(bref.unpack(fail_detection_res_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(beam_fail_instance_max_count_present, 1)); HANDLE_CODE(bref.unpack(beam_fail_detection_timer_present, 1)); @@ -27611,14 +27694,14 @@ SRSASN_CODE radio_link_monitoring_cfg_s::unpack(cbit_ref& bref) void radio_link_monitoring_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (fail_detection_res_to_add_mod_list_present) { + if (fail_detection_res_to_add_mod_list.size() > 0) { j.start_array("failureDetectionResourcesToAddModList"); for (const auto& e1 : fail_detection_res_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (fail_detection_res_to_release_list_present) { + if (fail_detection_res_to_release_list.size() > 0) { j.start_array("failureDetectionResourcesToReleaseList"); for (const auto& e1 : fail_detection_res_to_release_list) { j.write_int(e1); @@ -29776,8 +29859,8 @@ SRSASN_CODE pucch_pwr_ctrl_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(delta_f_pucch_f2_present, 1)); HANDLE_CODE(bref.pack(delta_f_pucch_f3_present, 1)); HANDLE_CODE(bref.pack(delta_f_pucch_f4_present, 1)); - HANDLE_CODE(bref.pack(p0_set_present, 1)); - HANDLE_CODE(bref.pack(pathloss_ref_rss_present, 1)); + HANDLE_CODE(bref.pack(p0_set.size() > 0, 1)); + HANDLE_CODE(bref.pack(pathloss_ref_rss.size() > 0, 1)); HANDLE_CODE(bref.pack(two_pucch_pc_adjustment_states_present, 1)); if (delta_f_pucch_f0_present) { @@ -29795,10 +29878,10 @@ SRSASN_CODE pucch_pwr_ctrl_s::pack(bit_ref& bref) const if (delta_f_pucch_f4_present) { HANDLE_CODE(pack_integer(bref, delta_f_pucch_f4, (int8_t)-16, (int8_t)15)); } - if (p0_set_present) { + if (p0_set.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, p0_set, 1, 8)); } - if (pathloss_ref_rss_present) { + if (pathloss_ref_rss.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, pathloss_ref_rss, 1, 4)); } @@ -29812,7 +29895,9 @@ SRSASN_CODE pucch_pwr_ctrl_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(delta_f_pucch_f2_present, 1)); HANDLE_CODE(bref.unpack(delta_f_pucch_f3_present, 1)); HANDLE_CODE(bref.unpack(delta_f_pucch_f4_present, 1)); + bool p0_set_present; HANDLE_CODE(bref.unpack(p0_set_present, 1)); + bool pathloss_ref_rss_present; HANDLE_CODE(bref.unpack(pathloss_ref_rss_present, 1)); HANDLE_CODE(bref.unpack(two_pucch_pc_adjustment_states_present, 1)); @@ -29858,14 +29943,14 @@ void pucch_pwr_ctrl_s::to_json(json_writer& j) const if (delta_f_pucch_f4_present) { j.write_int("deltaF-PUCCH-f4", delta_f_pucch_f4); } - if (p0_set_present) { + if (p0_set.size() > 0) { j.start_array("p0-Set"); for (const auto& e1 : p0_set) { e1.to_json(j); } j.end_array(); } - if (pathloss_ref_rss_present) { + if (pathloss_ref_rss.size() > 0) { j.start_array("pathlossReferenceRSs"); for (const auto& e1 : pathloss_ref_rss) { e1.to_json(j); @@ -30405,13 +30490,13 @@ SRSASN_CODE pusch_pwr_ctrl_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(tpc_accumulation_present, 1)); HANDLE_CODE(bref.pack(msg3_alpha_present, 1)); HANDLE_CODE(bref.pack(p0_nominal_without_grant_present, 1)); - HANDLE_CODE(bref.pack(p0_alpha_sets_present, 1)); - HANDLE_CODE(bref.pack(pathloss_ref_rs_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(pathloss_ref_rs_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(p0_alpha_sets.size() > 0, 1)); + HANDLE_CODE(bref.pack(pathloss_ref_rs_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(pathloss_ref_rs_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(two_pusch_pc_adjustment_states_present, 1)); HANDLE_CODE(bref.pack(delta_mcs_present, 1)); - HANDLE_CODE(bref.pack(sri_pusch_map_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(sri_pusch_map_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(sri_pusch_map_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(sri_pusch_map_to_release_list.size() > 0, 1)); if (msg3_alpha_present) { HANDLE_CODE(msg3_alpha.pack(bref)); @@ -30419,19 +30504,19 @@ SRSASN_CODE pusch_pwr_ctrl_s::pack(bit_ref& bref) const if (p0_nominal_without_grant_present) { HANDLE_CODE(pack_integer(bref, p0_nominal_without_grant, (int16_t)-202, (int16_t)24)); } - if (p0_alpha_sets_present) { + if (p0_alpha_sets.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, p0_alpha_sets, 1, 30)); } - if (pathloss_ref_rs_to_add_mod_list_present) { + if (pathloss_ref_rs_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, pathloss_ref_rs_to_add_mod_list, 1, 4)); } - if (pathloss_ref_rs_to_release_list_present) { + if (pathloss_ref_rs_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, pathloss_ref_rs_to_release_list, 1, 4, integer_packer(0, 3))); } - if (sri_pusch_map_to_add_mod_list_present) { + if (sri_pusch_map_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sri_pusch_map_to_add_mod_list, 1, 16)); } - if (sri_pusch_map_to_release_list_present) { + if (sri_pusch_map_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sri_pusch_map_to_release_list, 1, 16, integer_packer(0, 15))); } @@ -30442,12 +30527,17 @@ SRSASN_CODE pusch_pwr_ctrl_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(tpc_accumulation_present, 1)); HANDLE_CODE(bref.unpack(msg3_alpha_present, 1)); HANDLE_CODE(bref.unpack(p0_nominal_without_grant_present, 1)); + bool p0_alpha_sets_present; HANDLE_CODE(bref.unpack(p0_alpha_sets_present, 1)); + bool pathloss_ref_rs_to_add_mod_list_present; HANDLE_CODE(bref.unpack(pathloss_ref_rs_to_add_mod_list_present, 1)); + bool pathloss_ref_rs_to_release_list_present; HANDLE_CODE(bref.unpack(pathloss_ref_rs_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(two_pusch_pc_adjustment_states_present, 1)); HANDLE_CODE(bref.unpack(delta_mcs_present, 1)); + bool sri_pusch_map_to_add_mod_list_present; HANDLE_CODE(bref.unpack(sri_pusch_map_to_add_mod_list_present, 1)); + bool sri_pusch_map_to_release_list_present; HANDLE_CODE(bref.unpack(sri_pusch_map_to_release_list_present, 1)); if (msg3_alpha_present) { @@ -30486,21 +30576,21 @@ void pusch_pwr_ctrl_s::to_json(json_writer& j) const if (p0_nominal_without_grant_present) { j.write_int("p0-NominalWithoutGrant", p0_nominal_without_grant); } - if (p0_alpha_sets_present) { + if (p0_alpha_sets.size() > 0) { j.start_array("p0-AlphaSets"); for (const auto& e1 : p0_alpha_sets) { e1.to_json(j); } j.end_array(); } - if (pathloss_ref_rs_to_add_mod_list_present) { + if (pathloss_ref_rs_to_add_mod_list.size() > 0) { j.start_array("pathlossReferenceRSToAddModList"); for (const auto& e1 : pathloss_ref_rs_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (pathloss_ref_rs_to_release_list_present) { + if (pathloss_ref_rs_to_release_list.size() > 0) { j.start_array("pathlossReferenceRSToReleaseList"); for (const auto& e1 : pathloss_ref_rs_to_release_list) { j.write_int(e1); @@ -30513,14 +30603,14 @@ void pusch_pwr_ctrl_s::to_json(json_writer& j) const if (delta_mcs_present) { j.write_str("deltaMCS", "enabled"); } - if (sri_pusch_map_to_add_mod_list_present) { + if (sri_pusch_map_to_add_mod_list.size() > 0) { j.start_array("sri-PUSCH-MappingToAddModList"); for (const auto& e1 : sri_pusch_map_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (sri_pusch_map_to_release_list_present) { + if (sri_pusch_map_to_release_list.size() > 0) { j.start_array("sri-PUSCH-MappingToReleaseList"); for (const auto& e1 : sri_pusch_map_to_release_list) { j.write_int(e1); @@ -31058,14 +31148,14 @@ const char* srs_res_s::res_type_c_::types_opts::to_string() const SRSASN_CODE srs_res_set_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(srs_res_id_list_present, 1)); + HANDLE_CODE(bref.pack(srs_res_id_list.size() > 0, 1)); HANDLE_CODE(bref.pack(alpha_present, 1)); HANDLE_CODE(bref.pack(p0_present, 1)); HANDLE_CODE(bref.pack(pathloss_ref_rs_present, 1)); HANDLE_CODE(bref.pack(srs_pwr_ctrl_adjustment_states_present, 1)); HANDLE_CODE(pack_integer(bref, srs_res_set_id, (uint8_t)0u, (uint8_t)15u)); - if (srs_res_id_list_present) { + if (srs_res_id_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, srs_res_id_list, 1, 16, integer_packer(0, 63))); } HANDLE_CODE(res_type.pack(bref)); @@ -31088,6 +31178,7 @@ SRSASN_CODE srs_res_set_s::pack(bit_ref& bref) const SRSASN_CODE srs_res_set_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool srs_res_id_list_present; HANDLE_CODE(bref.unpack(srs_res_id_list_present, 1)); HANDLE_CODE(bref.unpack(alpha_present, 1)); HANDLE_CODE(bref.unpack(p0_present, 1)); @@ -31119,7 +31210,7 @@ void srs_res_set_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("srs-ResourceSetId", srs_res_set_id); - if (srs_res_id_list_present) { + if (srs_res_id_list.size() > 0) { j.start_array("srs-ResourceIdList"); for (const auto& e1 : srs_res_id_list) { j.write_int(e1); @@ -32142,7 +32233,7 @@ SRSASN_CODE beam_fail_recovery_cfg_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(root_seq_idx_bfr_present, 1)); HANDLE_CODE(bref.pack(rach_cfg_bfr_present, 1)); HANDLE_CODE(bref.pack(rsrp_thres_ssb_present, 1)); - HANDLE_CODE(bref.pack(candidate_beam_rs_list_present, 1)); + HANDLE_CODE(bref.pack(candidate_beam_rs_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ssb_per_rach_occasion_present, 1)); HANDLE_CODE(bref.pack(ra_ssb_occasion_mask_idx_present, 1)); HANDLE_CODE(bref.pack(recovery_search_space_id_present, 1)); @@ -32158,7 +32249,7 @@ SRSASN_CODE beam_fail_recovery_cfg_s::pack(bit_ref& bref) const if (rsrp_thres_ssb_present) { HANDLE_CODE(pack_integer(bref, rsrp_thres_ssb, (uint8_t)0u, (uint8_t)127u)); } - if (candidate_beam_rs_list_present) { + if (candidate_beam_rs_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, candidate_beam_rs_list, 1, 16)); } if (ssb_per_rach_occasion_present) { @@ -32199,6 +32290,7 @@ SRSASN_CODE beam_fail_recovery_cfg_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(root_seq_idx_bfr_present, 1)); HANDLE_CODE(bref.unpack(rach_cfg_bfr_present, 1)); HANDLE_CODE(bref.unpack(rsrp_thres_ssb_present, 1)); + bool candidate_beam_rs_list_present; HANDLE_CODE(bref.unpack(candidate_beam_rs_list_present, 1)); HANDLE_CODE(bref.unpack(ssb_per_rach_occasion_present, 1)); HANDLE_CODE(bref.unpack(ra_ssb_occasion_mask_idx_present, 1)); @@ -32262,7 +32354,7 @@ void beam_fail_recovery_cfg_s::to_json(json_writer& j) const if (rsrp_thres_ssb_present) { j.write_int("rsrp-ThresholdSSB", rsrp_thres_ssb); } - if (candidate_beam_rs_list_present) { + if (candidate_beam_rs_list.size() > 0) { j.start_array("candidateBeamRSList"); for (const auto& e1 : candidate_beam_rs_list) { e1.to_json(j); @@ -32607,32 +32699,32 @@ const char* cfgured_grant_cfg_s::periodicity_opts::to_string() const SRSASN_CODE pucch_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(res_set_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(res_set_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(res_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(res_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(res_set_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(res_set_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(res_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(res_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(format1_present, 1)); HANDLE_CODE(bref.pack(format2_present, 1)); HANDLE_CODE(bref.pack(format3_present, 1)); HANDLE_CODE(bref.pack(format4_present, 1)); - HANDLE_CODE(bref.pack(sched_request_res_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(sched_request_res_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(multi_csi_pucch_res_list_present, 1)); - HANDLE_CODE(bref.pack(dl_data_to_ul_ack_present, 1)); - HANDLE_CODE(bref.pack(spatial_relation_info_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(spatial_relation_info_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(sched_request_res_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(sched_request_res_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(multi_csi_pucch_res_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(dl_data_to_ul_ack.size() > 0, 1)); + HANDLE_CODE(bref.pack(spatial_relation_info_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(spatial_relation_info_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(pucch_pwr_ctrl_present, 1)); - if (res_set_to_add_mod_list_present) { + if (res_set_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, res_set_to_add_mod_list, 1, 4)); } - if (res_set_to_release_list_present) { + if (res_set_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, res_set_to_release_list, 1, 4, integer_packer(0, 3))); } - if (res_to_add_mod_list_present) { + if (res_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, res_to_add_mod_list, 1, 128)); } - if (res_to_release_list_present) { + if (res_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, res_to_release_list, 1, 128, integer_packer(0, 127))); } if (format1_present) { @@ -32647,22 +32739,22 @@ SRSASN_CODE pucch_cfg_s::pack(bit_ref& bref) const if (format4_present) { HANDLE_CODE(format4.pack(bref)); } - if (sched_request_res_to_add_mod_list_present) { + if (sched_request_res_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sched_request_res_to_add_mod_list, 1, 8)); } - if (sched_request_res_to_release_list_present) { + if (sched_request_res_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sched_request_res_to_release_list, 1, 8, integer_packer(1, 8))); } - if (multi_csi_pucch_res_list_present) { + if (multi_csi_pucch_res_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, multi_csi_pucch_res_list, 1, 2, integer_packer(0, 127))); } - if (dl_data_to_ul_ack_present) { + if (dl_data_to_ul_ack.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, dl_data_to_ul_ack, 1, 8, integer_packer(0, 15))); } - if (spatial_relation_info_to_add_mod_list_present) { + if (spatial_relation_info_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, spatial_relation_info_to_add_mod_list, 1, 8)); } - if (spatial_relation_info_to_release_list_present) { + if (spatial_relation_info_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, spatial_relation_info_to_release_list, 1, 8, integer_packer(1, 8))); } if (pucch_pwr_ctrl_present) { @@ -32674,19 +32766,29 @@ SRSASN_CODE pucch_cfg_s::pack(bit_ref& bref) const SRSASN_CODE pucch_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool res_set_to_add_mod_list_present; HANDLE_CODE(bref.unpack(res_set_to_add_mod_list_present, 1)); + bool res_set_to_release_list_present; HANDLE_CODE(bref.unpack(res_set_to_release_list_present, 1)); + bool res_to_add_mod_list_present; HANDLE_CODE(bref.unpack(res_to_add_mod_list_present, 1)); + bool res_to_release_list_present; HANDLE_CODE(bref.unpack(res_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(format1_present, 1)); HANDLE_CODE(bref.unpack(format2_present, 1)); HANDLE_CODE(bref.unpack(format3_present, 1)); HANDLE_CODE(bref.unpack(format4_present, 1)); + bool sched_request_res_to_add_mod_list_present; HANDLE_CODE(bref.unpack(sched_request_res_to_add_mod_list_present, 1)); + bool sched_request_res_to_release_list_present; HANDLE_CODE(bref.unpack(sched_request_res_to_release_list_present, 1)); + bool multi_csi_pucch_res_list_present; HANDLE_CODE(bref.unpack(multi_csi_pucch_res_list_present, 1)); + bool dl_data_to_ul_ack_present; HANDLE_CODE(bref.unpack(dl_data_to_ul_ack_present, 1)); + bool spatial_relation_info_to_add_mod_list_present; HANDLE_CODE(bref.unpack(spatial_relation_info_to_add_mod_list_present, 1)); + bool spatial_relation_info_to_release_list_present; HANDLE_CODE(bref.unpack(spatial_relation_info_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(pucch_pwr_ctrl_present, 1)); @@ -32741,28 +32843,28 @@ SRSASN_CODE pucch_cfg_s::unpack(cbit_ref& bref) void pucch_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (res_set_to_add_mod_list_present) { + if (res_set_to_add_mod_list.size() > 0) { j.start_array("resourceSetToAddModList"); for (const auto& e1 : res_set_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (res_set_to_release_list_present) { + if (res_set_to_release_list.size() > 0) { j.start_array("resourceSetToReleaseList"); for (const auto& e1 : res_set_to_release_list) { j.write_int(e1); } j.end_array(); } - if (res_to_add_mod_list_present) { + if (res_to_add_mod_list.size() > 0) { j.start_array("resourceToAddModList"); for (const auto& e1 : res_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (res_to_release_list_present) { + if (res_to_release_list.size() > 0) { j.start_array("resourceToReleaseList"); for (const auto& e1 : res_to_release_list) { j.write_int(e1); @@ -32785,42 +32887,42 @@ void pucch_cfg_s::to_json(json_writer& j) const j.write_fieldname("format4"); format4.to_json(j); } - if (sched_request_res_to_add_mod_list_present) { + if (sched_request_res_to_add_mod_list.size() > 0) { j.start_array("schedulingRequestResourceToAddModList"); for (const auto& e1 : sched_request_res_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (sched_request_res_to_release_list_present) { + if (sched_request_res_to_release_list.size() > 0) { j.start_array("schedulingRequestResourceToReleaseList"); for (const auto& e1 : sched_request_res_to_release_list) { j.write_int(e1); } j.end_array(); } - if (multi_csi_pucch_res_list_present) { + if (multi_csi_pucch_res_list.size() > 0) { j.start_array("multi-CSI-PUCCH-ResourceList"); for (const auto& e1 : multi_csi_pucch_res_list) { j.write_int(e1); } j.end_array(); } - if (dl_data_to_ul_ack_present) { + if (dl_data_to_ul_ack.size() > 0) { j.start_array("dl-DataToUL-ACK"); for (const auto& e1 : dl_data_to_ul_ack) { j.write_int(e1); } j.end_array(); } - if (spatial_relation_info_to_add_mod_list_present) { + if (spatial_relation_info_to_add_mod_list.size() > 0) { j.start_array("spatialRelationInfoToAddModList"); for (const auto& e1 : spatial_relation_info_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (spatial_relation_info_to_release_list_present) { + if (spatial_relation_info_to_release_list.size() > 0) { j.start_array("spatialRelationInfoToReleaseList"); for (const auto& e1 : spatial_relation_info_to_release_list) { j.write_int(e1); @@ -32844,7 +32946,7 @@ SRSASN_CODE pusch_cfg_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(dmrs_ul_for_pusch_map_type_b_present, 1)); HANDLE_CODE(bref.pack(pusch_pwr_ctrl_present, 1)); HANDLE_CODE(bref.pack(freq_hop_present, 1)); - HANDLE_CODE(bref.pack(freq_hop_offset_lists_present, 1)); + HANDLE_CODE(bref.pack(freq_hop_offset_lists.size() > 0, 1)); HANDLE_CODE(bref.pack(pusch_time_domain_alloc_list_present, 1)); HANDLE_CODE(bref.pack(pusch_aggregation_factor_present, 1)); HANDLE_CODE(bref.pack(mcs_table_present, 1)); @@ -32874,7 +32976,7 @@ SRSASN_CODE pusch_cfg_s::pack(bit_ref& bref) const if (freq_hop_present) { HANDLE_CODE(freq_hop.pack(bref)); } - if (freq_hop_offset_lists_present) { + if (freq_hop_offset_lists.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_hop_offset_lists, 1, 4, integer_packer(1, 274))); } HANDLE_CODE(res_alloc.pack(bref)); @@ -32914,6 +33016,7 @@ SRSASN_CODE pusch_cfg_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(dmrs_ul_for_pusch_map_type_b_present, 1)); HANDLE_CODE(bref.unpack(pusch_pwr_ctrl_present, 1)); HANDLE_CODE(bref.unpack(freq_hop_present, 1)); + bool freq_hop_offset_lists_present; HANDLE_CODE(bref.unpack(freq_hop_offset_lists_present, 1)); HANDLE_CODE(bref.unpack(pusch_time_domain_alloc_list_present, 1)); HANDLE_CODE(bref.unpack(pusch_aggregation_factor_present, 1)); @@ -32999,7 +33102,7 @@ void pusch_cfg_s::to_json(json_writer& j) const if (freq_hop_present) { j.write_str("frequencyHopping", freq_hop.to_string()); } - if (freq_hop_offset_lists_present) { + if (freq_hop_offset_lists.size() > 0) { j.start_array("frequencyHoppingOffsetLists"); for (const auto& e1 : freq_hop_offset_lists) { j.write_int(e1); @@ -33114,22 +33217,22 @@ const char* pusch_cfg_s::codebook_subset_opts::to_string() const SRSASN_CODE srs_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(srs_res_set_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(srs_res_set_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(srs_res_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(srs_res_to_add_mod_list_present, 1)); + HANDLE_CODE(bref.pack(srs_res_set_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(srs_res_set_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(srs_res_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(srs_res_to_add_mod_list.size() > 0, 1)); HANDLE_CODE(bref.pack(tpc_accumulation_present, 1)); - if (srs_res_set_to_release_list_present) { + if (srs_res_set_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, srs_res_set_to_release_list, 1, 16, integer_packer(0, 15))); } - if (srs_res_set_to_add_mod_list_present) { + if (srs_res_set_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, srs_res_set_to_add_mod_list, 1, 16)); } - if (srs_res_to_release_list_present) { + if (srs_res_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, srs_res_to_release_list, 1, 64, integer_packer(0, 63))); } - if (srs_res_to_add_mod_list_present) { + if (srs_res_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, srs_res_to_add_mod_list, 1, 64)); } @@ -33138,9 +33241,13 @@ SRSASN_CODE srs_cfg_s::pack(bit_ref& bref) const SRSASN_CODE srs_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool srs_res_set_to_release_list_present; HANDLE_CODE(bref.unpack(srs_res_set_to_release_list_present, 1)); + bool srs_res_set_to_add_mod_list_present; HANDLE_CODE(bref.unpack(srs_res_set_to_add_mod_list_present, 1)); + bool srs_res_to_release_list_present; HANDLE_CODE(bref.unpack(srs_res_to_release_list_present, 1)); + bool srs_res_to_add_mod_list_present; HANDLE_CODE(bref.unpack(srs_res_to_add_mod_list_present, 1)); HANDLE_CODE(bref.unpack(tpc_accumulation_present, 1)); @@ -33162,28 +33269,28 @@ SRSASN_CODE srs_cfg_s::unpack(cbit_ref& bref) void srs_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (srs_res_set_to_release_list_present) { + if (srs_res_set_to_release_list.size() > 0) { j.start_array("srs-ResourceSetToReleaseList"); for (const auto& e1 : srs_res_set_to_release_list) { j.write_int(e1); } j.end_array(); } - if (srs_res_set_to_add_mod_list_present) { + if (srs_res_set_to_add_mod_list.size() > 0) { j.start_array("srs-ResourceSetToAddModList"); for (const auto& e1 : srs_res_set_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (srs_res_to_release_list_present) { + if (srs_res_to_release_list.size() > 0) { j.start_array("srs-ResourceToReleaseList"); for (const auto& e1 : srs_res_to_release_list) { j.write_int(e1); } j.end_array(); } - if (srs_res_to_add_mod_list_present) { + if (srs_res_to_add_mod_list.size() > 0) { j.start_array("srs-ResourceToAddModList"); for (const auto& e1 : srs_res_to_add_mod_list) { e1.to_json(j); @@ -37823,7 +37930,7 @@ void csi_associated_report_cfg_info_s::res_for_ch_c_::to_json(json_writer& j) co j.write_fieldname("nzp-CSI-RS"); j.start_obj(); j.write_int("resourceSet", c.get().res_set); - if (c.get().qcl_info_present) { + if (c.get().qcl_info.size() > 0) { j.start_array("qcl-info"); for (const auto& e1 : c.get().qcl_info) { j.write_int(e1); @@ -37845,9 +37952,9 @@ SRSASN_CODE csi_associated_report_cfg_info_s::res_for_ch_c_::pack(bit_ref& bref) type_.pack(bref); switch (type_) { case types::nzp_csi_rs: - HANDLE_CODE(bref.pack(c.get().qcl_info_present, 1)); + HANDLE_CODE(bref.pack(c.get().qcl_info.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, c.get().res_set, (uint8_t)1u, (uint8_t)16u)); - if (c.get().qcl_info_present) { + if (c.get().qcl_info.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, c.get().qcl_info, 1, 16, integer_packer(0, 127))); } break; @@ -37867,9 +37974,10 @@ SRSASN_CODE csi_associated_report_cfg_info_s::res_for_ch_c_::unpack(cbit_ref& br set(e); switch (type_) { case types::nzp_csi_rs: - HANDLE_CODE(bref.unpack(c.get().qcl_info_present, 1)); + bool qcl_info_present; + HANDLE_CODE(bref.unpack(qcl_info_present, 1)); HANDLE_CODE(unpack_integer(c.get().res_set, bref, (uint8_t)1u, (uint8_t)16u)); - if (c.get().qcl_info_present) { + if (qcl_info_present) { HANDLE_CODE(unpack_dyn_seq_of(c.get().qcl_info, bref, 1, 16, integer_packer(0, 127))); } break; @@ -40948,7 +41056,7 @@ SRSASN_CODE csi_report_cfg_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(codebook_cfg_present, 1)); HANDLE_CODE(bref.pack(dummy_present, 1)); HANDLE_CODE(bref.pack(cqi_table_present, 1)); - HANDLE_CODE(bref.pack(non_pmi_port_ind_present, 1)); + HANDLE_CODE(bref.pack(non_pmi_port_ind.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, report_cfg_id, (uint8_t)0u, (uint8_t)47u)); if (carrier_present) { @@ -40990,7 +41098,7 @@ SRSASN_CODE csi_report_cfg_s::pack(bit_ref& bref) const HANDLE_CODE(cqi_table.pack(bref)); } HANDLE_CODE(subband_size.pack(bref)); - if (non_pmi_port_ind_present) { + if (non_pmi_port_ind.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, non_pmi_port_ind, 1, 128)); } @@ -41020,6 +41128,7 @@ SRSASN_CODE csi_report_cfg_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(codebook_cfg_present, 1)); HANDLE_CODE(bref.unpack(dummy_present, 1)); HANDLE_CODE(bref.unpack(cqi_table_present, 1)); + bool non_pmi_port_ind_present; HANDLE_CODE(bref.unpack(non_pmi_port_ind_present, 1)); HANDLE_CODE(unpack_integer(report_cfg_id, bref, (uint8_t)0u, (uint8_t)47u)); @@ -41131,7 +41240,7 @@ void csi_report_cfg_s::to_json(json_writer& j) const j.write_str("cqi-Table", cqi_table.to_string()); } j.write_str("subbandSize", subband_size.to_string()); - if (non_pmi_port_ind_present) { + if (non_pmi_port_ind.size() > 0) { j.start_array("non-PMI-PortIndication"); for (const auto& e1 : non_pmi_port_ind) { e1.to_json(j); @@ -42380,7 +42489,7 @@ void csi_res_cfg_s::csi_rs_res_set_list_c_::to_json(json_writer& j) const case types::nzp_csi_rs_ssb: j.write_fieldname("nzp-CSI-RS-SSB"); j.start_obj(); - if (c.get().nzp_csi_rs_res_set_list_present) { + if (c.get().nzp_csi_rs_res_set_list.size() > 0) { j.start_array("nzp-CSI-RS-ResourceSetList"); for (const auto& e1 : c.get().nzp_csi_rs_res_set_list) { j.write_int(e1); @@ -42413,9 +42522,9 @@ SRSASN_CODE csi_res_cfg_s::csi_rs_res_set_list_c_::pack(bit_ref& bref) const type_.pack(bref); switch (type_) { case types::nzp_csi_rs_ssb: - HANDLE_CODE(bref.pack(c.get().nzp_csi_rs_res_set_list_present, 1)); + HANDLE_CODE(bref.pack(c.get().nzp_csi_rs_res_set_list.size() > 0, 1)); HANDLE_CODE(bref.pack(c.get().csi_ssb_res_set_list_present, 1)); - if (c.get().nzp_csi_rs_res_set_list_present) { + if (c.get().nzp_csi_rs_res_set_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of( bref, c.get().nzp_csi_rs_res_set_list, 1, 16, integer_packer(0, 63))); } @@ -42442,9 +42551,10 @@ SRSASN_CODE csi_res_cfg_s::csi_rs_res_set_list_c_::unpack(cbit_ref& bref) set(e); switch (type_) { case types::nzp_csi_rs_ssb: - HANDLE_CODE(bref.unpack(c.get().nzp_csi_rs_res_set_list_present, 1)); + bool nzp_csi_rs_res_set_list_present; + HANDLE_CODE(bref.unpack(nzp_csi_rs_res_set_list_present, 1)); HANDLE_CODE(bref.unpack(c.get().csi_ssb_res_set_list_present, 1)); - if (c.get().nzp_csi_rs_res_set_list_present) { + if (nzp_csi_rs_res_set_list_present) { HANDLE_CODE(unpack_dyn_seq_of( c.get().nzp_csi_rs_res_set_list, bref, 1, 16, integer_packer(0, 63))); } @@ -42653,64 +42763,64 @@ const char* nzp_csi_rs_res_set_s::repeat_opts::to_string() const SRSASN_CODE csi_meas_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(nzp_csi_rs_res_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(nzp_csi_rs_res_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(nzp_csi_rs_res_set_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(nzp_csi_rs_res_set_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(csi_im_res_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(csi_im_res_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(csi_im_res_set_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(csi_im_res_set_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(csi_ssb_res_set_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(csi_ssb_res_set_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(csi_res_cfg_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(csi_res_cfg_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(csi_report_cfg_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(csi_report_cfg_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(nzp_csi_rs_res_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(nzp_csi_rs_res_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(nzp_csi_rs_res_set_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(nzp_csi_rs_res_set_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_im_res_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_im_res_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_im_res_set_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_im_res_set_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_ssb_res_set_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_ssb_res_set_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_res_cfg_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_res_cfg_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_report_cfg_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(csi_report_cfg_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(report_trigger_size_present, 1)); HANDLE_CODE(bref.pack(aperiodic_trigger_state_list_present, 1)); HANDLE_CODE(bref.pack(semi_persistent_on_pusch_trigger_state_list_present, 1)); - if (nzp_csi_rs_res_to_add_mod_list_present) { + if (nzp_csi_rs_res_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, nzp_csi_rs_res_to_add_mod_list, 1, 192)); } - if (nzp_csi_rs_res_to_release_list_present) { + if (nzp_csi_rs_res_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, nzp_csi_rs_res_to_release_list, 1, 192, integer_packer(0, 191))); } - if (nzp_csi_rs_res_set_to_add_mod_list_present) { + if (nzp_csi_rs_res_set_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, nzp_csi_rs_res_set_to_add_mod_list, 1, 64)); } - if (nzp_csi_rs_res_set_to_release_list_present) { + if (nzp_csi_rs_res_set_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, nzp_csi_rs_res_set_to_release_list, 1, 64, integer_packer(0, 63))); } - if (csi_im_res_to_add_mod_list_present) { + if (csi_im_res_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_im_res_to_add_mod_list, 1, 32)); } - if (csi_im_res_to_release_list_present) { + if (csi_im_res_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_im_res_to_release_list, 1, 32, integer_packer(0, 31))); } - if (csi_im_res_set_to_add_mod_list_present) { + if (csi_im_res_set_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_im_res_set_to_add_mod_list, 1, 64)); } - if (csi_im_res_set_to_release_list_present) { + if (csi_im_res_set_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_im_res_set_to_release_list, 1, 64, integer_packer(0, 63))); } - if (csi_ssb_res_set_to_add_mod_list_present) { + if (csi_ssb_res_set_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_ssb_res_set_to_add_mod_list, 1, 64)); } - if (csi_ssb_res_set_to_release_list_present) { + if (csi_ssb_res_set_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_ssb_res_set_to_release_list, 1, 64, integer_packer(0, 63))); } - if (csi_res_cfg_to_add_mod_list_present) { + if (csi_res_cfg_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_res_cfg_to_add_mod_list, 1, 112)); } - if (csi_res_cfg_to_release_list_present) { + if (csi_res_cfg_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_res_cfg_to_release_list, 1, 112, integer_packer(0, 111))); } - if (csi_report_cfg_to_add_mod_list_present) { + if (csi_report_cfg_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_report_cfg_to_add_mod_list, 1, 48)); } - if (csi_report_cfg_to_release_list_present) { + if (csi_report_cfg_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, csi_report_cfg_to_release_list, 1, 48, integer_packer(0, 47))); } if (report_trigger_size_present) { @@ -42728,19 +42838,33 @@ SRSASN_CODE csi_meas_cfg_s::pack(bit_ref& bref) const SRSASN_CODE csi_meas_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool nzp_csi_rs_res_to_add_mod_list_present; HANDLE_CODE(bref.unpack(nzp_csi_rs_res_to_add_mod_list_present, 1)); + bool nzp_csi_rs_res_to_release_list_present; HANDLE_CODE(bref.unpack(nzp_csi_rs_res_to_release_list_present, 1)); + bool nzp_csi_rs_res_set_to_add_mod_list_present; HANDLE_CODE(bref.unpack(nzp_csi_rs_res_set_to_add_mod_list_present, 1)); + bool nzp_csi_rs_res_set_to_release_list_present; HANDLE_CODE(bref.unpack(nzp_csi_rs_res_set_to_release_list_present, 1)); + bool csi_im_res_to_add_mod_list_present; HANDLE_CODE(bref.unpack(csi_im_res_to_add_mod_list_present, 1)); + bool csi_im_res_to_release_list_present; HANDLE_CODE(bref.unpack(csi_im_res_to_release_list_present, 1)); + bool csi_im_res_set_to_add_mod_list_present; HANDLE_CODE(bref.unpack(csi_im_res_set_to_add_mod_list_present, 1)); + bool csi_im_res_set_to_release_list_present; HANDLE_CODE(bref.unpack(csi_im_res_set_to_release_list_present, 1)); + bool csi_ssb_res_set_to_add_mod_list_present; HANDLE_CODE(bref.unpack(csi_ssb_res_set_to_add_mod_list_present, 1)); + bool csi_ssb_res_set_to_release_list_present; HANDLE_CODE(bref.unpack(csi_ssb_res_set_to_release_list_present, 1)); + bool csi_res_cfg_to_add_mod_list_present; HANDLE_CODE(bref.unpack(csi_res_cfg_to_add_mod_list_present, 1)); + bool csi_res_cfg_to_release_list_present; HANDLE_CODE(bref.unpack(csi_res_cfg_to_release_list_present, 1)); + bool csi_report_cfg_to_add_mod_list_present; HANDLE_CODE(bref.unpack(csi_report_cfg_to_add_mod_list_present, 1)); + bool csi_report_cfg_to_release_list_present; HANDLE_CODE(bref.unpack(csi_report_cfg_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(report_trigger_size_present, 1)); HANDLE_CODE(bref.unpack(aperiodic_trigger_state_list_present, 1)); @@ -42803,98 +42927,98 @@ SRSASN_CODE csi_meas_cfg_s::unpack(cbit_ref& bref) void csi_meas_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (nzp_csi_rs_res_to_add_mod_list_present) { + if (nzp_csi_rs_res_to_add_mod_list.size() > 0) { j.start_array("nzp-CSI-RS-ResourceToAddModList"); for (const auto& e1 : nzp_csi_rs_res_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (nzp_csi_rs_res_to_release_list_present) { + if (nzp_csi_rs_res_to_release_list.size() > 0) { j.start_array("nzp-CSI-RS-ResourceToReleaseList"); for (const auto& e1 : nzp_csi_rs_res_to_release_list) { j.write_int(e1); } j.end_array(); } - if (nzp_csi_rs_res_set_to_add_mod_list_present) { + if (nzp_csi_rs_res_set_to_add_mod_list.size() > 0) { j.start_array("nzp-CSI-RS-ResourceSetToAddModList"); for (const auto& e1 : nzp_csi_rs_res_set_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (nzp_csi_rs_res_set_to_release_list_present) { + if (nzp_csi_rs_res_set_to_release_list.size() > 0) { j.start_array("nzp-CSI-RS-ResourceSetToReleaseList"); for (const auto& e1 : nzp_csi_rs_res_set_to_release_list) { j.write_int(e1); } j.end_array(); } - if (csi_im_res_to_add_mod_list_present) { + if (csi_im_res_to_add_mod_list.size() > 0) { j.start_array("csi-IM-ResourceToAddModList"); for (const auto& e1 : csi_im_res_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (csi_im_res_to_release_list_present) { + if (csi_im_res_to_release_list.size() > 0) { j.start_array("csi-IM-ResourceToReleaseList"); for (const auto& e1 : csi_im_res_to_release_list) { j.write_int(e1); } j.end_array(); } - if (csi_im_res_set_to_add_mod_list_present) { + if (csi_im_res_set_to_add_mod_list.size() > 0) { j.start_array("csi-IM-ResourceSetToAddModList"); for (const auto& e1 : csi_im_res_set_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (csi_im_res_set_to_release_list_present) { + if (csi_im_res_set_to_release_list.size() > 0) { j.start_array("csi-IM-ResourceSetToReleaseList"); for (const auto& e1 : csi_im_res_set_to_release_list) { j.write_int(e1); } j.end_array(); } - if (csi_ssb_res_set_to_add_mod_list_present) { + if (csi_ssb_res_set_to_add_mod_list.size() > 0) { j.start_array("csi-SSB-ResourceSetToAddModList"); for (const auto& e1 : csi_ssb_res_set_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (csi_ssb_res_set_to_release_list_present) { + if (csi_ssb_res_set_to_release_list.size() > 0) { j.start_array("csi-SSB-ResourceSetToReleaseList"); for (const auto& e1 : csi_ssb_res_set_to_release_list) { j.write_int(e1); } j.end_array(); } - if (csi_res_cfg_to_add_mod_list_present) { + if (csi_res_cfg_to_add_mod_list.size() > 0) { j.start_array("csi-ResourceConfigToAddModList"); for (const auto& e1 : csi_res_cfg_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (csi_res_cfg_to_release_list_present) { + if (csi_res_cfg_to_release_list.size() > 0) { j.start_array("csi-ResourceConfigToReleaseList"); for (const auto& e1 : csi_res_cfg_to_release_list) { j.write_int(e1); } j.end_array(); } - if (csi_report_cfg_to_add_mod_list_present) { + if (csi_report_cfg_to_add_mod_list.size() > 0) { j.start_array("csi-ReportConfigToAddModList"); for (const auto& e1 : csi_report_cfg_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (csi_report_cfg_to_release_list_present) { + if (csi_report_cfg_to_release_list.size() > 0) { j.start_array("csi-ReportConfigToReleaseList"); for (const auto& e1 : csi_report_cfg_to_release_list) { j.write_int(e1); @@ -43368,13 +43492,13 @@ void freq_info_dl_s::to_json(json_writer& j) const SRSASN_CODE freq_info_ul_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(freq_band_list_present, 1)); + HANDLE_CODE(bref.pack(freq_band_list.size() > 0, 1)); HANDLE_CODE(bref.pack(absolute_freq_point_a_present, 1)); HANDLE_CODE(bref.pack(add_spec_emission_present, 1)); HANDLE_CODE(bref.pack(p_max_present, 1)); HANDLE_CODE(bref.pack(freq_shift7p5khz_present, 1)); - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_band_list, 1, 8, integer_packer(1, 1024))); } if (absolute_freq_point_a_present) { @@ -43393,6 +43517,7 @@ SRSASN_CODE freq_info_ul_s::pack(bit_ref& bref) const SRSASN_CODE freq_info_ul_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool freq_band_list_present; HANDLE_CODE(bref.unpack(freq_band_list_present, 1)); HANDLE_CODE(bref.unpack(absolute_freq_point_a_present, 1)); HANDLE_CODE(bref.unpack(add_spec_emission_present, 1)); @@ -43418,7 +43543,7 @@ SRSASN_CODE freq_info_ul_s::unpack(cbit_ref& bref) void freq_info_ul_s::to_json(json_writer& j) const { j.start_obj(); - if (freq_band_list_present) { + if (freq_band_list.size() > 0) { j.start_array("frequencyBandList"); for (const auto& e1 : freq_band_list) { j.write_int(e1); @@ -43483,9 +43608,9 @@ uint8_t pusch_code_block_group_tx_s::max_code_block_groups_per_transport_block_o // SRS-TPC-PDCCH-Config ::= SEQUENCE SRSASN_CODE srs_tpc_pdcch_cfg_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(srs_cc_set_idxlist_present, 1)); + HANDLE_CODE(bref.pack(srs_cc_set_idxlist.size() > 0, 1)); - if (srs_cc_set_idxlist_present) { + if (srs_cc_set_idxlist.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, srs_cc_set_idxlist, 1, 4)); } @@ -43493,6 +43618,7 @@ SRSASN_CODE srs_tpc_pdcch_cfg_s::pack(bit_ref& bref) const } SRSASN_CODE srs_tpc_pdcch_cfg_s::unpack(cbit_ref& bref) { + bool srs_cc_set_idxlist_present; HANDLE_CODE(bref.unpack(srs_cc_set_idxlist_present, 1)); if (srs_cc_set_idxlist_present) { @@ -43504,7 +43630,7 @@ SRSASN_CODE srs_tpc_pdcch_cfg_s::unpack(cbit_ref& bref) void srs_tpc_pdcch_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (srs_cc_set_idxlist_present) { + if (srs_cc_set_idxlist.size() > 0) { j.start_array("srs-CC-SetIndexlist"); for (const auto& e1 : srs_cc_set_idxlist) { e1.to_json(j); @@ -43519,7 +43645,7 @@ SRSASN_CODE slot_format_combinations_per_cell_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(subcarrier_spacing2_present, 1)); - HANDLE_CODE(bref.pack(slot_format_combinations_present, 1)); + HANDLE_CODE(bref.pack(slot_format_combinations.size() > 0, 1)); HANDLE_CODE(bref.pack(position_in_dci_present, 1)); HANDLE_CODE(pack_integer(bref, serving_cell_id, (uint8_t)0u, (uint8_t)31u)); @@ -43527,7 +43653,7 @@ SRSASN_CODE slot_format_combinations_per_cell_s::pack(bit_ref& bref) const if (subcarrier_spacing2_present) { HANDLE_CODE(subcarrier_spacing2.pack(bref)); } - if (slot_format_combinations_present) { + if (slot_format_combinations.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, slot_format_combinations, 1, 512)); } if (position_in_dci_present) { @@ -43540,6 +43666,7 @@ SRSASN_CODE slot_format_combinations_per_cell_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(subcarrier_spacing2_present, 1)); + bool slot_format_combinations_present; HANDLE_CODE(bref.unpack(slot_format_combinations_present, 1)); HANDLE_CODE(bref.unpack(position_in_dci_present, 1)); @@ -43565,7 +43692,7 @@ void slot_format_combinations_per_cell_s::to_json(json_writer& j) const if (subcarrier_spacing2_present) { j.write_str("subcarrierSpacing2", subcarrier_spacing2.to_string()); } - if (slot_format_combinations_present) { + if (slot_format_combinations.size() > 0) { j.start_array("slotFormatCombinations"); for (const auto& e1 : slot_format_combinations) { e1.to_json(j); @@ -43806,11 +43933,11 @@ int32_t poll_pdu_opts::to_number() const // RateMatchPatternLTE-CRS ::= SEQUENCE SRSASN_CODE rate_match_pattern_lte_crs_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(mbsfn_sf_cfg_list_present, 1)); + HANDLE_CODE(bref.pack(mbsfn_sf_cfg_list.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, carrier_freq_dl, (uint16_t)0u, (uint16_t)16383u)); HANDLE_CODE(carrier_bw_dl.pack(bref)); - if (mbsfn_sf_cfg_list_present) { + if (mbsfn_sf_cfg_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, mbsfn_sf_cfg_list, 1, 8)); } HANDLE_CODE(nrof_crs_ports.pack(bref)); @@ -43820,6 +43947,7 @@ SRSASN_CODE rate_match_pattern_lte_crs_s::pack(bit_ref& bref) const } SRSASN_CODE rate_match_pattern_lte_crs_s::unpack(cbit_ref& bref) { + bool mbsfn_sf_cfg_list_present; HANDLE_CODE(bref.unpack(mbsfn_sf_cfg_list_present, 1)); HANDLE_CODE(unpack_integer(carrier_freq_dl, bref, (uint16_t)0u, (uint16_t)16383u)); @@ -43837,7 +43965,7 @@ void rate_match_pattern_lte_crs_s::to_json(json_writer& j) const j.start_obj(); j.write_int("carrierFreqDL", carrier_freq_dl); j.write_str("carrierBandwidthDL", carrier_bw_dl.to_string()); - if (mbsfn_sf_cfg_list_present) { + if (mbsfn_sf_cfg_list.size() > 0) { j.start_array("mbsfn-SubframeConfigList"); for (const auto& e1 : mbsfn_sf_cfg_list) { e1.to_json(j); @@ -43912,7 +44040,7 @@ SRSASN_CODE srs_carrier_switching_s::pack(bit_ref& bref) const bref.pack(ext, 1); HANDLE_CODE(bref.pack(srs_switch_from_serv_cell_idx_present, 1)); HANDLE_CODE(bref.pack(srs_tpc_pdcch_group_present, 1)); - HANDLE_CODE(bref.pack(monitoring_cells_present, 1)); + HANDLE_CODE(bref.pack(monitoring_cells.size() > 0, 1)); if (srs_switch_from_serv_cell_idx_present) { HANDLE_CODE(pack_integer(bref, srs_switch_from_serv_cell_idx, (uint8_t)0u, (uint8_t)31u)); @@ -43921,7 +44049,7 @@ SRSASN_CODE srs_carrier_switching_s::pack(bit_ref& bref) const if (srs_tpc_pdcch_group_present) { HANDLE_CODE(srs_tpc_pdcch_group.pack(bref)); } - if (monitoring_cells_present) { + if (monitoring_cells.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, monitoring_cells, 1, 32, integer_packer(0, 31))); } @@ -43932,6 +44060,7 @@ SRSASN_CODE srs_carrier_switching_s::unpack(cbit_ref& bref) bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(srs_switch_from_serv_cell_idx_present, 1)); HANDLE_CODE(bref.unpack(srs_tpc_pdcch_group_present, 1)); + bool monitoring_cells_present; HANDLE_CODE(bref.unpack(monitoring_cells_present, 1)); if (srs_switch_from_serv_cell_idx_present) { @@ -43958,7 +44087,7 @@ void srs_carrier_switching_s::to_json(json_writer& j) const j.write_fieldname("srs-TPC-PDCCH-Group"); srs_tpc_pdcch_group.to_json(j); } - if (monitoring_cells_present) { + if (monitoring_cells.size() > 0) { j.start_array("monitoringCells"); for (const auto& e1 : monitoring_cells) { j.write_int(e1); @@ -44119,15 +44248,15 @@ const char* srs_carrier_switching_s::srs_tpc_pdcch_group_c_::types_opts::to_stri SRSASN_CODE slot_format_ind_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(slot_format_comb_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(slot_format_comb_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(slot_format_comb_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(slot_format_comb_to_release_list.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, sfi_rnti, (uint32_t)0u, (uint32_t)65535u)); HANDLE_CODE(pack_integer(bref, dci_payload_size, (uint8_t)1u, (uint8_t)128u)); - if (slot_format_comb_to_add_mod_list_present) { + if (slot_format_comb_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, slot_format_comb_to_add_mod_list, 1, 16)); } - if (slot_format_comb_to_release_list_present) { + if (slot_format_comb_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, slot_format_comb_to_release_list, 1, 16, integer_packer(0, 31))); } @@ -44136,7 +44265,9 @@ SRSASN_CODE slot_format_ind_s::pack(bit_ref& bref) const SRSASN_CODE slot_format_ind_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool slot_format_comb_to_add_mod_list_present; HANDLE_CODE(bref.unpack(slot_format_comb_to_add_mod_list_present, 1)); + bool slot_format_comb_to_release_list_present; HANDLE_CODE(bref.unpack(slot_format_comb_to_release_list_present, 1)); HANDLE_CODE(unpack_integer(sfi_rnti, bref, (uint32_t)0u, (uint32_t)65535u)); @@ -44155,14 +44286,14 @@ void slot_format_ind_s::to_json(json_writer& j) const j.start_obj(); j.write_int("sfi-RNTI", sfi_rnti); j.write_int("dci-PayloadSize", dci_payload_size); - if (slot_format_comb_to_add_mod_list_present) { + if (slot_format_comb_to_add_mod_list.size() > 0) { j.start_array("slotFormatCombToAddModList"); for (const auto& e1 : slot_format_comb_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (slot_format_comb_to_release_list_present) { + if (slot_format_comb_to_release_list.size() > 0) { j.start_array("slotFormatCombToReleaseList"); for (const auto& e1 : slot_format_comb_to_release_list) { j.write_int(e1); @@ -44914,8 +45045,8 @@ SRSASN_CODE serving_cell_cfg_common_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(ssb_positions_in_burst_present, 1)); HANDLE_CODE(bref.pack(ssb_periodicity_serving_cell_present, 1)); HANDLE_CODE(bref.pack(lte_crs_to_match_around_present, 1)); - HANDLE_CODE(bref.pack(rate_match_pattern_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(rate_match_pattern_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(rate_match_pattern_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(rate_match_pattern_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(ssb_subcarrier_spacing_present, 1)); HANDLE_CODE(bref.pack(tdd_ul_dl_cfg_common_present, 1)); @@ -44944,10 +45075,10 @@ SRSASN_CODE serving_cell_cfg_common_s::pack(bit_ref& bref) const if (lte_crs_to_match_around_present) { HANDLE_CODE(lte_crs_to_match_around.pack(bref)); } - if (rate_match_pattern_to_add_mod_list_present) { + if (rate_match_pattern_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rate_match_pattern_to_add_mod_list, 1, 4)); } - if (rate_match_pattern_to_release_list_present) { + if (rate_match_pattern_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rate_match_pattern_to_release_list, 1, 4, integer_packer(0, 3))); } if (ssb_subcarrier_spacing_present) { @@ -44971,7 +45102,9 @@ SRSASN_CODE serving_cell_cfg_common_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(ssb_positions_in_burst_present, 1)); HANDLE_CODE(bref.unpack(ssb_periodicity_serving_cell_present, 1)); HANDLE_CODE(bref.unpack(lte_crs_to_match_around_present, 1)); + bool rate_match_pattern_to_add_mod_list_present; HANDLE_CODE(bref.unpack(rate_match_pattern_to_add_mod_list_present, 1)); + bool rate_match_pattern_to_release_list_present; HANDLE_CODE(bref.unpack(rate_match_pattern_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(ssb_subcarrier_spacing_present, 1)); HANDLE_CODE(bref.unpack(tdd_ul_dl_cfg_common_present, 1)); @@ -45050,14 +45183,14 @@ void serving_cell_cfg_common_s::to_json(json_writer& j) const j.write_fieldname("lte-CRS-ToMatchAround"); lte_crs_to_match_around.to_json(j); } - if (rate_match_pattern_to_add_mod_list_present) { + if (rate_match_pattern_to_add_mod_list.size() > 0) { j.start_array("rateMatchPatternToAddModList"); for (const auto& e1 : rate_match_pattern_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (rate_match_pattern_to_release_list_present) { + if (rate_match_pattern_to_release_list.size() > 0) { j.start_array("rateMatchPatternToReleaseList"); for (const auto& e1 : rate_match_pattern_to_release_list) { j.write_int(e1); @@ -45298,13 +45431,13 @@ void tag_s::to_json(json_writer& j) const SRSASN_CODE tdd_ul_dl_cfg_ded_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(slot_specific_cfgs_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(slot_specific_cfgs_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(slot_specific_cfgs_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(slot_specific_cfgs_to_release_list.size() > 0, 1)); - if (slot_specific_cfgs_to_add_mod_list_present) { + if (slot_specific_cfgs_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, slot_specific_cfgs_to_add_mod_list, 1, 320)); } - if (slot_specific_cfgs_to_release_list_present) { + if (slot_specific_cfgs_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, slot_specific_cfgs_to_release_list, 1, 320, integer_packer(0, 319))); } @@ -45313,7 +45446,9 @@ SRSASN_CODE tdd_ul_dl_cfg_ded_s::pack(bit_ref& bref) const SRSASN_CODE tdd_ul_dl_cfg_ded_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool slot_specific_cfgs_to_add_mod_list_present; HANDLE_CODE(bref.unpack(slot_specific_cfgs_to_add_mod_list_present, 1)); + bool slot_specific_cfgs_to_release_list_present; HANDLE_CODE(bref.unpack(slot_specific_cfgs_to_release_list_present, 1)); if (slot_specific_cfgs_to_add_mod_list_present) { @@ -45328,14 +45463,14 @@ SRSASN_CODE tdd_ul_dl_cfg_ded_s::unpack(cbit_ref& bref) void tdd_ul_dl_cfg_ded_s::to_json(json_writer& j) const { j.start_obj(); - if (slot_specific_cfgs_to_add_mod_list_present) { + if (slot_specific_cfgs_to_add_mod_list.size() > 0) { j.start_array("slotSpecificConfigurationsToAddModList"); for (const auto& e1 : slot_specific_cfgs_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (slot_specific_cfgs_to_release_list_present) { + if (slot_specific_cfgs_to_release_list.size() > 0) { j.start_array("slotSpecificConfigurationsToReleaseList"); for (const auto& e1 : slot_specific_cfgs_to_release_list) { j.write_int(e1); @@ -45433,8 +45568,8 @@ SRSASN_CODE ul_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(init_ul_bwp_present, 1)); - HANDLE_CODE(bref.pack(ul_bwp_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(ul_bwp_to_add_mod_list_present, 1)); + HANDLE_CODE(bref.pack(ul_bwp_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(ul_bwp_to_add_mod_list.size() > 0, 1)); HANDLE_CODE(bref.pack(first_active_ul_bwp_id_present, 1)); HANDLE_CODE(bref.pack(pusch_serving_cell_cfg_present, 1)); HANDLE_CODE(bref.pack(carrier_switching_present, 1)); @@ -45442,10 +45577,10 @@ SRSASN_CODE ul_cfg_s::pack(bit_ref& bref) const if (init_ul_bwp_present) { HANDLE_CODE(init_ul_bwp.pack(bref)); } - if (ul_bwp_to_release_list_present) { + if (ul_bwp_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ul_bwp_to_release_list, 1, 4, integer_packer(0, 4))); } - if (ul_bwp_to_add_mod_list_present) { + if (ul_bwp_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ul_bwp_to_add_mod_list, 1, 4)); } if (first_active_ul_bwp_id_present) { @@ -45483,7 +45618,9 @@ SRSASN_CODE ul_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(init_ul_bwp_present, 1)); + bool ul_bwp_to_release_list_present; HANDLE_CODE(bref.unpack(ul_bwp_to_release_list_present, 1)); + bool ul_bwp_to_add_mod_list_present; HANDLE_CODE(bref.unpack(ul_bwp_to_add_mod_list_present, 1)); HANDLE_CODE(bref.unpack(first_active_ul_bwp_id_present, 1)); HANDLE_CODE(bref.unpack(pusch_serving_cell_cfg_present, 1)); @@ -45536,14 +45673,14 @@ void ul_cfg_s::to_json(json_writer& j) const j.write_fieldname("initialUplinkBWP"); init_ul_bwp.to_json(j); } - if (ul_bwp_to_release_list_present) { + if (ul_bwp_to_release_list.size() > 0) { j.start_array("uplinkBWP-ToReleaseList"); for (const auto& e1 : ul_bwp_to_release_list) { j.write_int(e1); } j.end_array(); } - if (ul_bwp_to_add_mod_list_present) { + if (ul_bwp_to_add_mod_list.size() > 0) { j.start_array("uplinkBWP-ToAddModList"); for (const auto& e1 : ul_bwp_to_add_mod_list) { e1.to_json(j); @@ -46449,8 +46586,8 @@ void lc_ch_cfg_s::to_json(json_writer& j) const SRSASN_CODE lc_ch_cfg_s::ul_specific_params_s_::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(allowed_serving_cells_present, 1)); - HANDLE_CODE(bref.pack(allowed_scs_list_present, 1)); + HANDLE_CODE(bref.pack(allowed_serving_cells.size() > 0, 1)); + HANDLE_CODE(bref.pack(allowed_scs_list.size() > 0, 1)); HANDLE_CODE(bref.pack(max_pusch_dur_present, 1)); HANDLE_CODE(bref.pack(cfgured_grant_type1_allowed_present, 1)); HANDLE_CODE(bref.pack(lc_ch_group_present, 1)); @@ -46459,10 +46596,10 @@ SRSASN_CODE lc_ch_cfg_s::ul_specific_params_s_::pack(bit_ref& bref) const HANDLE_CODE(pack_integer(bref, prio, (uint8_t)1u, (uint8_t)16u)); HANDLE_CODE(prioritised_bit_rate.pack(bref)); HANDLE_CODE(bucket_size_dur.pack(bref)); - if (allowed_serving_cells_present) { + if (allowed_serving_cells.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, allowed_serving_cells, 1, 31, integer_packer(0, 31))); } - if (allowed_scs_list_present) { + if (allowed_scs_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, allowed_scs_list, 1, 5)); } if (max_pusch_dur_present) { @@ -46489,7 +46626,9 @@ SRSASN_CODE lc_ch_cfg_s::ul_specific_params_s_::pack(bit_ref& bref) const SRSASN_CODE lc_ch_cfg_s::ul_specific_params_s_::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool allowed_serving_cells_present; HANDLE_CODE(bref.unpack(allowed_serving_cells_present, 1)); + bool allowed_scs_list_present; HANDLE_CODE(bref.unpack(allowed_scs_list_present, 1)); HANDLE_CODE(bref.unpack(max_pusch_dur_present, 1)); HANDLE_CODE(bref.unpack(cfgured_grant_type1_allowed_present, 1)); @@ -46532,14 +46671,14 @@ void lc_ch_cfg_s::ul_specific_params_s_::to_json(json_writer& j) const j.write_int("priority", prio); j.write_str("prioritisedBitRate", prioritised_bit_rate.to_string()); j.write_str("bucketSizeDuration", bucket_size_dur.to_string()); - if (allowed_serving_cells_present) { + if (allowed_serving_cells.size() > 0) { j.start_array("allowedServingCells"); for (const auto& e1 : allowed_serving_cells) { j.write_int(e1); } j.end_array(); } - if (allowed_scs_list_present) { + if (allowed_scs_list.size() > 0) { j.start_array("allowedSCS-List"); for (const auto& e1 : allowed_scs_list) { j.write_str(e1.to_string()); @@ -47263,13 +47402,13 @@ const char* recfg_with_sync_s::rach_cfg_ded_c_::types_opts::to_string() const // SchedulingRequestConfig ::= SEQUENCE SRSASN_CODE sched_request_cfg_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(sched_request_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(sched_request_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(sched_request_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(sched_request_to_release_list.size() > 0, 1)); - if (sched_request_to_add_mod_list_present) { + if (sched_request_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sched_request_to_add_mod_list, 1, 8)); } - if (sched_request_to_release_list_present) { + if (sched_request_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sched_request_to_release_list, 1, 8, integer_packer(0, 7))); } @@ -47277,7 +47416,9 @@ SRSASN_CODE sched_request_cfg_s::pack(bit_ref& bref) const } SRSASN_CODE sched_request_cfg_s::unpack(cbit_ref& bref) { + bool sched_request_to_add_mod_list_present; HANDLE_CODE(bref.unpack(sched_request_to_add_mod_list_present, 1)); + bool sched_request_to_release_list_present; HANDLE_CODE(bref.unpack(sched_request_to_release_list_present, 1)); if (sched_request_to_add_mod_list_present) { @@ -47292,14 +47433,14 @@ SRSASN_CODE sched_request_cfg_s::unpack(cbit_ref& bref) void sched_request_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (sched_request_to_add_mod_list_present) { + if (sched_request_to_add_mod_list.size() > 0) { j.start_array("schedulingRequestToAddModList"); for (const auto& e1 : sched_request_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (sched_request_to_release_list_present) { + if (sched_request_to_release_list.size() > 0) { j.start_array("schedulingRequestToReleaseList"); for (const auto& e1 : sched_request_to_release_list) { j.write_int(e1); @@ -47315,8 +47456,8 @@ SRSASN_CODE serving_cell_cfg_s::pack(bit_ref& bref) const bref.pack(ext, 1); HANDLE_CODE(bref.pack(tdd_ul_dl_cfg_ded_present, 1)); HANDLE_CODE(bref.pack(init_dl_bwp_present, 1)); - HANDLE_CODE(bref.pack(dl_bwp_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(dl_bwp_to_add_mod_list_present, 1)); + HANDLE_CODE(bref.pack(dl_bwp_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(dl_bwp_to_add_mod_list.size() > 0, 1)); HANDLE_CODE(bref.pack(first_active_dl_bwp_id_present, 1)); HANDLE_CODE(bref.pack(bwp_inactivity_timer_present, 1)); HANDLE_CODE(bref.pack(default_dl_bwp_id_present, 1)); @@ -47337,10 +47478,10 @@ SRSASN_CODE serving_cell_cfg_s::pack(bit_ref& bref) const if (init_dl_bwp_present) { HANDLE_CODE(init_dl_bwp.pack(bref)); } - if (dl_bwp_to_release_list_present) { + if (dl_bwp_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, dl_bwp_to_release_list, 1, 4, integer_packer(0, 4))); } - if (dl_bwp_to_add_mod_list_present) { + if (dl_bwp_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, dl_bwp_to_add_mod_list, 1, 4)); } if (first_active_dl_bwp_id_present) { @@ -47417,7 +47558,9 @@ SRSASN_CODE serving_cell_cfg_s::unpack(cbit_ref& bref) bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(tdd_ul_dl_cfg_ded_present, 1)); HANDLE_CODE(bref.unpack(init_dl_bwp_present, 1)); + bool dl_bwp_to_release_list_present; HANDLE_CODE(bref.unpack(dl_bwp_to_release_list_present, 1)); + bool dl_bwp_to_add_mod_list_present; HANDLE_CODE(bref.unpack(dl_bwp_to_add_mod_list_present, 1)); HANDLE_CODE(bref.unpack(first_active_dl_bwp_id_present, 1)); HANDLE_CODE(bref.unpack(bwp_inactivity_timer_present, 1)); @@ -47529,14 +47672,14 @@ void serving_cell_cfg_s::to_json(json_writer& j) const j.write_fieldname("initialDownlinkBWP"); init_dl_bwp.to_json(j); } - if (dl_bwp_to_release_list_present) { + if (dl_bwp_to_release_list.size() > 0) { j.start_array("downlinkBWP-ToReleaseList"); for (const auto& e1 : dl_bwp_to_release_list) { j.write_int(e1); } j.end_array(); } - if (dl_bwp_to_add_mod_list_present) { + if (dl_bwp_to_add_mod_list.size() > 0) { j.start_array("downlinkBWP-ToAddModList"); for (const auto& e1 : dl_bwp_to_add_mod_list) { e1.to_json(j); @@ -47669,13 +47812,13 @@ const char* serving_cell_cfg_s::pathloss_ref_linking_opts::to_string() const // TAG-Config ::= SEQUENCE SRSASN_CODE tag_cfg_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(tag_to_release_list_present, 1)); - HANDLE_CODE(bref.pack(tag_to_add_mod_list_present, 1)); + HANDLE_CODE(bref.pack(tag_to_release_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(tag_to_add_mod_list.size() > 0, 1)); - if (tag_to_release_list_present) { + if (tag_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, tag_to_release_list, 1, 4, integer_packer(0, 3))); } - if (tag_to_add_mod_list_present) { + if (tag_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, tag_to_add_mod_list, 1, 4)); } @@ -47683,7 +47826,9 @@ SRSASN_CODE tag_cfg_s::pack(bit_ref& bref) const } SRSASN_CODE tag_cfg_s::unpack(cbit_ref& bref) { + bool tag_to_release_list_present; HANDLE_CODE(bref.unpack(tag_to_release_list_present, 1)); + bool tag_to_add_mod_list_present; HANDLE_CODE(bref.unpack(tag_to_add_mod_list_present, 1)); if (tag_to_release_list_present) { @@ -47698,14 +47843,14 @@ SRSASN_CODE tag_cfg_s::unpack(cbit_ref& bref) void tag_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (tag_to_release_list_present) { + if (tag_to_release_list.size() > 0) { j.start_array("tag-ToReleaseList"); for (const auto& e1 : tag_to_release_list) { j.write_int(e1); } j.end_array(); } - if (tag_to_add_mod_list_present) { + if (tag_to_add_mod_list.size() > 0) { j.start_array("tag-ToAddModList"); for (const auto& e1 : tag_to_add_mod_list) { e1.to_json(j); @@ -48386,19 +48531,19 @@ void sp_cell_cfg_s::to_json(json_writer& j) const SRSASN_CODE cell_group_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(rlc_bearer_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(rlc_bearer_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(rlc_bearer_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(rlc_bearer_to_release_list.size() > 0, 1)); HANDLE_CODE(bref.pack(mac_cell_group_cfg_present, 1)); HANDLE_CODE(bref.pack(phys_cell_group_cfg_present, 1)); HANDLE_CODE(bref.pack(sp_cell_cfg_present, 1)); - HANDLE_CODE(bref.pack(scell_to_add_mod_list_present, 1)); - HANDLE_CODE(bref.pack(scell_to_release_list_present, 1)); + HANDLE_CODE(bref.pack(scell_to_add_mod_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(scell_to_release_list.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, cell_group_id, (uint8_t)0u, (uint8_t)3u)); - if (rlc_bearer_to_add_mod_list_present) { + if (rlc_bearer_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rlc_bearer_to_add_mod_list, 1, 32)); } - if (rlc_bearer_to_release_list_present) { + if (rlc_bearer_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, rlc_bearer_to_release_list, 1, 32, integer_packer(1, 32))); } if (mac_cell_group_cfg_present) { @@ -48410,10 +48555,10 @@ SRSASN_CODE cell_group_cfg_s::pack(bit_ref& bref) const if (sp_cell_cfg_present) { HANDLE_CODE(sp_cell_cfg.pack(bref)); } - if (scell_to_add_mod_list_present) { + if (scell_to_add_mod_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, scell_to_add_mod_list, 1, 31)); } - if (scell_to_release_list_present) { + if (scell_to_release_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, scell_to_release_list, 1, 31, integer_packer(1, 31))); } @@ -48433,12 +48578,16 @@ SRSASN_CODE cell_group_cfg_s::pack(bit_ref& bref) const SRSASN_CODE cell_group_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool rlc_bearer_to_add_mod_list_present; HANDLE_CODE(bref.unpack(rlc_bearer_to_add_mod_list_present, 1)); + bool rlc_bearer_to_release_list_present; HANDLE_CODE(bref.unpack(rlc_bearer_to_release_list_present, 1)); HANDLE_CODE(bref.unpack(mac_cell_group_cfg_present, 1)); HANDLE_CODE(bref.unpack(phys_cell_group_cfg_present, 1)); HANDLE_CODE(bref.unpack(sp_cell_cfg_present, 1)); + bool scell_to_add_mod_list_present; HANDLE_CODE(bref.unpack(scell_to_add_mod_list_present, 1)); + bool scell_to_release_list_present; HANDLE_CODE(bref.unpack(scell_to_release_list_present, 1)); HANDLE_CODE(unpack_integer(cell_group_id, bref, (uint8_t)0u, (uint8_t)3u)); @@ -48480,14 +48629,14 @@ void cell_group_cfg_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("cellGroupId", cell_group_id); - if (rlc_bearer_to_add_mod_list_present) { + if (rlc_bearer_to_add_mod_list.size() > 0) { j.start_array("rlc-BearerToAddModList"); for (const auto& e1 : rlc_bearer_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (rlc_bearer_to_release_list_present) { + if (rlc_bearer_to_release_list.size() > 0) { j.start_array("rlc-BearerToReleaseList"); for (const auto& e1 : rlc_bearer_to_release_list) { j.write_int(e1); @@ -48506,14 +48655,14 @@ void cell_group_cfg_s::to_json(json_writer& j) const j.write_fieldname("spCellConfig"); sp_cell_cfg.to_json(j); } - if (scell_to_add_mod_list_present) { + if (scell_to_add_mod_list.size() > 0) { j.start_array("sCellToAddModList"); for (const auto& e1 : scell_to_add_mod_list) { e1.to_json(j); } j.end_array(); } - if (scell_to_release_list_present) { + if (scell_to_release_list.size() > 0) { j.start_array("sCellToReleaseList"); for (const auto& e1 : scell_to_release_list) { j.write_int(e1); @@ -49170,10 +49319,10 @@ SRSASN_CODE feature_set_dl_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(time_dur_for_qcl_present, 1)); HANDLE_CODE(bref.pack(pdsch_processing_type1_different_tb_per_slot_present, 1)); HANDLE_CODE(bref.pack(dummy3_present, 1)); - HANDLE_CODE(bref.pack(dummy4_present, 1)); - HANDLE_CODE(bref.pack(dummy5_present, 1)); - HANDLE_CODE(bref.pack(dummy6_present, 1)); - HANDLE_CODE(bref.pack(dummy7_present, 1)); + HANDLE_CODE(bref.pack(dummy4.size() > 0, 1)); + HANDLE_CODE(bref.pack(dummy5.size() > 0, 1)); + HANDLE_CODE(bref.pack(dummy6.size() > 0, 1)); + HANDLE_CODE(bref.pack(dummy7.size() > 0, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, feature_set_list_per_dl_cc, 1, 32, integer_packer(1, 1024))); if (intra_band_freq_separation_dl_present) { @@ -49216,16 +49365,16 @@ SRSASN_CODE feature_set_dl_s::pack(bit_ref& bref) const if (dummy3_present) { HANDLE_CODE(dummy3.pack(bref)); } - if (dummy4_present) { + if (dummy4.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, dummy4, 1, 16)); } - if (dummy5_present) { + if (dummy5.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, dummy5, 1, 16)); } - if (dummy6_present) { + if (dummy6.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, dummy6, 1, 16)); } - if (dummy7_present) { + if (dummy7.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, dummy7, 1, 16)); } @@ -49247,9 +49396,13 @@ SRSASN_CODE feature_set_dl_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(time_dur_for_qcl_present, 1)); HANDLE_CODE(bref.unpack(pdsch_processing_type1_different_tb_per_slot_present, 1)); HANDLE_CODE(bref.unpack(dummy3_present, 1)); + bool dummy4_present; HANDLE_CODE(bref.unpack(dummy4_present, 1)); + bool dummy5_present; HANDLE_CODE(bref.unpack(dummy5_present, 1)); + bool dummy6_present; HANDLE_CODE(bref.unpack(dummy6_present, 1)); + bool dummy7_present; HANDLE_CODE(bref.unpack(dummy7_present, 1)); HANDLE_CODE(unpack_dyn_seq_of(feature_set_list_per_dl_cc, bref, 1, 32, integer_packer(1, 1024))); @@ -49381,28 +49534,28 @@ void feature_set_dl_s::to_json(json_writer& j) const j.write_fieldname("dummy3"); dummy3.to_json(j); } - if (dummy4_present) { + if (dummy4.size() > 0) { j.start_array("dummy4"); for (const auto& e1 : dummy4) { e1.to_json(j); } j.end_array(); } - if (dummy5_present) { + if (dummy5.size() > 0) { j.start_array("dummy5"); for (const auto& e1 : dummy5) { e1.to_json(j); } j.end_array(); } - if (dummy6_present) { + if (dummy6.size() > 0) { j.start_array("dummy6"); for (const auto& e1 : dummy6) { e1.to_json(j); } j.end_array(); } - if (dummy7_present) { + if (dummy7.size() > 0) { j.start_array("dummy7"); for (const auto& e1 : dummy7) { e1.to_json(j); @@ -50577,21 +50730,21 @@ void feature_set_ul_per_cc_v1540_s::to_json(json_writer& j) const SRSASN_CODE feature_sets_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(feature_sets_dl_present, 1)); - HANDLE_CODE(bref.pack(feature_sets_dl_per_cc_present, 1)); - HANDLE_CODE(bref.pack(feature_sets_ul_present, 1)); - HANDLE_CODE(bref.pack(feature_sets_ul_per_cc_present, 1)); + HANDLE_CODE(bref.pack(feature_sets_dl.size() > 0, 1)); + HANDLE_CODE(bref.pack(feature_sets_dl_per_cc.size() > 0, 1)); + HANDLE_CODE(bref.pack(feature_sets_ul.size() > 0, 1)); + HANDLE_CODE(bref.pack(feature_sets_ul_per_cc.size() > 0, 1)); - if (feature_sets_dl_present) { + if (feature_sets_dl.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, feature_sets_dl, 1, 1024)); } - if (feature_sets_dl_per_cc_present) { + if (feature_sets_dl_per_cc.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, feature_sets_dl_per_cc, 1, 1024)); } - if (feature_sets_ul_present) { + if (feature_sets_ul.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, feature_sets_ul, 1, 1024)); } - if (feature_sets_ul_per_cc_present) { + if (feature_sets_ul_per_cc.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, feature_sets_ul_per_cc, 1, 1024)); } @@ -50633,9 +50786,13 @@ SRSASN_CODE feature_sets_s::pack(bit_ref& bref) const SRSASN_CODE feature_sets_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool feature_sets_dl_present; HANDLE_CODE(bref.unpack(feature_sets_dl_present, 1)); + bool feature_sets_dl_per_cc_present; HANDLE_CODE(bref.unpack(feature_sets_dl_per_cc_present, 1)); + bool feature_sets_ul_present; HANDLE_CODE(bref.unpack(feature_sets_ul_present, 1)); + bool feature_sets_ul_per_cc_present; HANDLE_CODE(bref.unpack(feature_sets_ul_per_cc_present, 1)); if (feature_sets_dl_present) { @@ -50693,28 +50850,28 @@ SRSASN_CODE feature_sets_s::unpack(cbit_ref& bref) void feature_sets_s::to_json(json_writer& j) const { j.start_obj(); - if (feature_sets_dl_present) { + if (feature_sets_dl.size() > 0) { j.start_array("featureSetsDownlink"); for (const auto& e1 : feature_sets_dl) { e1.to_json(j); } j.end_array(); } - if (feature_sets_dl_per_cc_present) { + if (feature_sets_dl_per_cc.size() > 0) { j.start_array("featureSetsDownlinkPerCC"); for (const auto& e1 : feature_sets_dl_per_cc) { e1.to_json(j); } j.end_array(); } - if (feature_sets_ul_present) { + if (feature_sets_ul.size() > 0) { j.start_array("featureSetsUplink"); for (const auto& e1 : feature_sets_ul) { e1.to_json(j); } j.end_array(); } - if (feature_sets_ul_per_cc_present) { + if (feature_sets_ul_per_cc.size() > 0) { j.start_array("featureSetsUplinkPerCC"); for (const auto& e1 : feature_sets_ul_per_cc) { e1.to_json(j); @@ -52104,7 +52261,7 @@ SRSASN_CODE nrdc_params_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(tdd_add_ue_nrdc_cap_present, 1)); HANDLE_CODE(bref.pack(fr1_add_ue_nrdc_cap_present, 1)); HANDLE_CODE(bref.pack(fr2_add_ue_nrdc_cap_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(dummy_present, 1)); if (meas_and_mob_params_nrdc_present) { @@ -52125,7 +52282,7 @@ SRSASN_CODE nrdc_params_s::pack(bit_ref& bref) const if (fr2_add_ue_nrdc_cap_present) { HANDLE_CODE(fr2_add_ue_nrdc_cap.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } @@ -52139,6 +52296,7 @@ SRSASN_CODE nrdc_params_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(tdd_add_ue_nrdc_cap_present, 1)); HANDLE_CODE(bref.unpack(fr1_add_ue_nrdc_cap_present, 1)); HANDLE_CODE(bref.unpack(fr2_add_ue_nrdc_cap_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(dummy_present, 1)); @@ -52193,7 +52351,7 @@ void nrdc_params_s::to_json(json_writer& j) const j.write_fieldname("fr2-Add-UE-NRDC-Capabilities"); fr2_add_ue_nrdc_cap.to_json(j); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (dummy_present) { @@ -53447,9 +53605,9 @@ uint16_t naics_cap_entry_s::nof_aggregated_prb_opts::to_number() const SRSASN_CODE phy_params_mrdc_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(naics_cap_list_present, 1)); + HANDLE_CODE(bref.pack(naics_cap_list.size() > 0, 1)); - if (naics_cap_list_present) { + if (naics_cap_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, naics_cap_list, 1, 8)); } @@ -53472,6 +53630,7 @@ SRSASN_CODE phy_params_mrdc_s::pack(bit_ref& bref) const SRSASN_CODE phy_params_mrdc_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool naics_cap_list_present; HANDLE_CODE(bref.unpack(naics_cap_list_present, 1)); if (naics_cap_list_present) { @@ -53498,7 +53657,7 @@ SRSASN_CODE phy_params_mrdc_s::unpack(cbit_ref& bref) void phy_params_mrdc_s::to_json(json_writer& j) const { j.start_obj(); - if (naics_cap_list_present) { + if (naics_cap_list.size() > 0) { j.start_array("naics-Capability-List"); for (const auto& e1 : naics_cap_list) { e1.to_json(j); @@ -53518,14 +53677,14 @@ void phy_params_mrdc_s::to_json(json_writer& j) const SRSASN_CODE rf_params_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(supported_band_combination_list_present, 1)); - HANDLE_CODE(bref.pack(applied_freq_band_list_filt_present, 1)); + HANDLE_CODE(bref.pack(supported_band_combination_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(applied_freq_band_list_filt.size() > 0, 1)); HANDLE_CODE(pack_dyn_seq_of(bref, supported_band_list_nr, 1, 1024)); - if (supported_band_combination_list_present) { + if (supported_band_combination_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, supported_band_combination_list, 1, 65536)); } - if (applied_freq_band_list_filt_present) { + if (applied_freq_band_list_filt.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, applied_freq_band_list_filt, 1, 1280)); } @@ -53568,7 +53727,9 @@ SRSASN_CODE rf_params_s::pack(bit_ref& bref) const SRSASN_CODE rf_params_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool supported_band_combination_list_present; HANDLE_CODE(bref.unpack(supported_band_combination_list_present, 1)); + bool applied_freq_band_list_filt_present; HANDLE_CODE(bref.unpack(applied_freq_band_list_filt_present, 1)); HANDLE_CODE(unpack_dyn_seq_of(supported_band_list_nr, bref, 1, 1024)); @@ -53625,14 +53786,14 @@ void rf_params_s::to_json(json_writer& j) const e1.to_json(j); } j.end_array(); - if (supported_band_combination_list_present) { + if (supported_band_combination_list.size() > 0) { j.start_array("supportedBandCombinationList"); for (const auto& e1 : supported_band_combination_list) { e1.to_json(j); } j.end_array(); } - if (applied_freq_band_list_filt_present) { + if (applied_freq_band_list_filt.size() > 0) { j.start_array("appliedFreqBandListFilter"); for (const auto& e1 : applied_freq_band_list_filt) { e1.to_json(j); @@ -53672,13 +53833,13 @@ void rf_params_s::to_json(json_writer& j) const SRSASN_CODE rf_params_mrdc_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(supported_band_combination_list_present, 1)); - HANDLE_CODE(bref.pack(applied_freq_band_list_filt_present, 1)); + HANDLE_CODE(bref.pack(supported_band_combination_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(applied_freq_band_list_filt.size() > 0, 1)); - if (supported_band_combination_list_present) { + if (supported_band_combination_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, supported_band_combination_list, 1, 65536)); } - if (applied_freq_band_list_filt_present) { + if (applied_freq_band_list_filt.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, applied_freq_band_list_filt, 1, 1280)); } @@ -53754,32 +53915,32 @@ SRSASN_CODE rf_params_mrdc_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(supported_band_combination_list_nedc_only_v15a0.is_present(), 1)); if (supported_band_combination_list_nedc_only_v15a0.is_present()) { HANDLE_CODE(bref.pack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540_present, 1)); + supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540.size() > 0, 1)); HANDLE_CODE(bref.pack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560_present, 1)); + supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560.size() > 0, 1)); HANDLE_CODE(bref.pack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570_present, 1)); + supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570.size() > 0, 1)); HANDLE_CODE(bref.pack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580_present, 1)); + supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580.size() > 0, 1)); HANDLE_CODE(bref.pack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590_present, 1)); - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540_present) { + supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590.size() > 0, 1)); + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540.size() > 0) { HANDLE_CODE(pack_dyn_seq_of( bref, supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540, 1, 65536)); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560.size() > 0) { HANDLE_CODE(pack_dyn_seq_of( bref, supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560, 1, 65536)); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570.size() > 0) { HANDLE_CODE(pack_dyn_seq_of( bref, supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570, 1, 65536)); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580.size() > 0) { HANDLE_CODE(pack_dyn_seq_of( bref, supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580, 1, 65536)); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590.size() > 0) { HANDLE_CODE(pack_dyn_seq_of( bref, supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590, 1, 65536)); } @@ -53791,7 +53952,9 @@ SRSASN_CODE rf_params_mrdc_s::pack(bit_ref& bref) const SRSASN_CODE rf_params_mrdc_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool supported_band_combination_list_present; HANDLE_CODE(bref.unpack(supported_band_combination_list_present, 1)); + bool applied_freq_band_list_filt_present; HANDLE_CODE(bref.unpack(applied_freq_band_list_filt_present, 1)); if (supported_band_combination_list_present) { @@ -53880,33 +54043,33 @@ SRSASN_CODE rf_params_mrdc_s::unpack(cbit_ref& bref) supported_band_combination_list_nedc_only_v15a0.set_present( supported_band_combination_list_nedc_only_v15a0_present); if (supported_band_combination_list_nedc_only_v15a0.is_present()) { - HANDLE_CODE(bref.unpack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540_present, 1)); - HANDLE_CODE(bref.unpack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560_present, 1)); - HANDLE_CODE(bref.unpack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570_present, 1)); - HANDLE_CODE(bref.unpack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580_present, 1)); - HANDLE_CODE(bref.unpack( - supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590_present, 1)); - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540_present) { + bool supported_band_combination_list_v1540_present; + HANDLE_CODE(bref.unpack(supported_band_combination_list_v1540_present, 1)); + bool supported_band_combination_list_v1560_present; + HANDLE_CODE(bref.unpack(supported_band_combination_list_v1560_present, 1)); + bool supported_band_combination_list_v1570_present; + HANDLE_CODE(bref.unpack(supported_band_combination_list_v1570_present, 1)); + bool supported_band_combination_list_v1580_present; + HANDLE_CODE(bref.unpack(supported_band_combination_list_v1580_present, 1)); + bool supported_band_combination_list_v1590_present; + HANDLE_CODE(bref.unpack(supported_band_combination_list_v1590_present, 1)); + if (supported_band_combination_list_v1540_present) { HANDLE_CODE(unpack_dyn_seq_of( supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540, bref, 1, 65536)); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560_present) { + if (supported_band_combination_list_v1560_present) { HANDLE_CODE(unpack_dyn_seq_of( supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560, bref, 1, 65536)); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570_present) { + if (supported_band_combination_list_v1570_present) { HANDLE_CODE(unpack_dyn_seq_of( supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570, bref, 1, 65536)); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580_present) { + if (supported_band_combination_list_v1580_present) { HANDLE_CODE(unpack_dyn_seq_of( supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580, bref, 1, 65536)); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590_present) { + if (supported_band_combination_list_v1590_present) { HANDLE_CODE(unpack_dyn_seq_of( supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590, bref, 1, 65536)); } @@ -53918,14 +54081,14 @@ SRSASN_CODE rf_params_mrdc_s::unpack(cbit_ref& bref) void rf_params_mrdc_s::to_json(json_writer& j) const { j.start_obj(); - if (supported_band_combination_list_present) { + if (supported_band_combination_list.size() > 0) { j.start_array("supportedBandCombinationList"); for (const auto& e1 : supported_band_combination_list) { e1.to_json(j); } j.end_array(); } - if (applied_freq_band_list_filt_present) { + if (applied_freq_band_list_filt.size() > 0) { j.start_array("appliedFreqBandListFilter"); for (const auto& e1 : applied_freq_band_list_filt) { e1.to_json(j); @@ -53988,35 +54151,35 @@ void rf_params_mrdc_s::to_json(json_writer& j) const if (supported_band_combination_list_nedc_only_v15a0.is_present()) { j.write_fieldname("supportedBandCombinationListNEDC-Only-v15a0"); j.start_obj(); - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540.size() > 0) { j.start_array("supportedBandCombinationList-v1540"); for (const auto& e1 : supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1540) { e1.to_json(j); } j.end_array(); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560.size() > 0) { j.start_array("supportedBandCombinationList-v1560"); for (const auto& e1 : supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1560) { e1.to_json(j); } j.end_array(); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570.size() > 0) { j.start_array("supportedBandCombinationList-v1570"); for (const auto& e1 : supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1570) { e1.to_json(j); } j.end_array(); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580.size() > 0) { j.start_array("supportedBandCombinationList-v1580"); for (const auto& e1 : supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1580) { e1.to_json(j); } j.end_array(); } - if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590_present) { + if (supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590.size() > 0) { j.start_array("supportedBandCombinationList-v1590"); for (const auto& e1 : supported_band_combination_list_nedc_only_v15a0->supported_band_combination_list_v1590) { e1.to_json(j); @@ -54061,10 +54224,10 @@ void ue_cap_request_filt_nr_v1540_s::to_json(json_writer& j) const // UE-CapabilityRequestFilterNR ::= SEQUENCE SRSASN_CODE ue_cap_request_filt_nr_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(freq_band_list_filt_present, 1)); + HANDLE_CODE(bref.pack(freq_band_list_filt.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (freq_band_list_filt_present) { + if (freq_band_list_filt.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, freq_band_list_filt, 1, 1280)); } if (non_crit_ext_present) { @@ -54075,6 +54238,7 @@ SRSASN_CODE ue_cap_request_filt_nr_s::pack(bit_ref& bref) const } SRSASN_CODE ue_cap_request_filt_nr_s::unpack(cbit_ref& bref) { + bool freq_band_list_filt_present; HANDLE_CODE(bref.unpack(freq_band_list_filt_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -54090,7 +54254,7 @@ SRSASN_CODE ue_cap_request_filt_nr_s::unpack(cbit_ref& bref) void ue_cap_request_filt_nr_s::to_json(json_writer& j) const { j.start_obj(); - if (freq_band_list_filt_present) { + if (freq_band_list_filt.size() > 0) { j.start_array("frequencyBandListFilter"); for (const auto& e1 : freq_band_list_filt) { e1.to_json(j); @@ -54165,13 +54329,13 @@ void pdcp_params_mrdc_s::to_json(json_writer& j) const // UE-MRDC-Capability-v1560 ::= SEQUENCE SRSASN_CODE ue_mrdc_cap_v1560_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(rx_filts_present, 1)); + HANDLE_CODE(bref.pack(rx_filts.size() > 0, 1)); HANDLE_CODE(bref.pack(meas_and_mob_params_mrdc_v1560_present, 1)); HANDLE_CODE(bref.pack(fdd_add_ue_mrdc_cap_v1560_present, 1)); HANDLE_CODE(bref.pack(tdd_add_ue_mrdc_cap_v1560_present, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (rx_filts_present) { + if (rx_filts.size() > 0) { HANDLE_CODE(rx_filts.pack(bref)); } if (meas_and_mob_params_mrdc_v1560_present) { @@ -54188,6 +54352,7 @@ SRSASN_CODE ue_mrdc_cap_v1560_s::pack(bit_ref& bref) const } SRSASN_CODE ue_mrdc_cap_v1560_s::unpack(cbit_ref& bref) { + bool rx_filts_present; HANDLE_CODE(bref.unpack(rx_filts_present, 1)); HANDLE_CODE(bref.unpack(meas_and_mob_params_mrdc_v1560_present, 1)); HANDLE_CODE(bref.unpack(fdd_add_ue_mrdc_cap_v1560_present, 1)); @@ -54212,7 +54377,7 @@ SRSASN_CODE ue_mrdc_cap_v1560_s::unpack(cbit_ref& bref) void ue_mrdc_cap_v1560_s::to_json(json_writer& j) const { j.start_obj(); - if (rx_filts_present) { + if (rx_filts.size() > 0) { j.write_str("receivedFilters", rx_filts.to_string()); } if (meas_and_mob_params_mrdc_v1560_present) { @@ -54245,9 +54410,9 @@ SRSASN_CODE ue_mrdc_cap_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(tdd_add_ue_mrdc_cap_present, 1)); HANDLE_CODE(bref.pack(fr1_add_ue_mrdc_cap_present, 1)); HANDLE_CODE(bref.pack(fr2_add_ue_mrdc_cap_present, 1)); - HANDLE_CODE(bref.pack(feature_set_combinations_present, 1)); + HANDLE_CODE(bref.pack(feature_set_combinations.size() > 0, 1)); HANDLE_CODE(bref.pack(pdcp_params_mrdc_v1530_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (meas_and_mob_params_mrdc_present) { @@ -54272,7 +54437,7 @@ SRSASN_CODE ue_mrdc_cap_s::pack(bit_ref& bref) const if (fr2_add_ue_mrdc_cap_present) { HANDLE_CODE(fr2_add_ue_mrdc_cap.pack(bref)); } - if (feature_set_combinations_present) { + if (feature_set_combinations.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, feature_set_combinations, 1, @@ -54282,7 +54447,7 @@ SRSASN_CODE ue_mrdc_cap_s::pack(bit_ref& bref) const if (pdcp_params_mrdc_v1530_present) { HANDLE_CODE(pdcp_params_mrdc_v1530.pack(bref)); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } if (non_crit_ext_present) { @@ -54300,8 +54465,10 @@ SRSASN_CODE ue_mrdc_cap_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(tdd_add_ue_mrdc_cap_present, 1)); HANDLE_CODE(bref.unpack(fr1_add_ue_mrdc_cap_present, 1)); HANDLE_CODE(bref.unpack(fr2_add_ue_mrdc_cap_present, 1)); + bool feature_set_combinations_present; HANDLE_CODE(bref.unpack(feature_set_combinations_present, 1)); HANDLE_CODE(bref.unpack(pdcp_params_mrdc_v1530_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -54379,7 +54546,7 @@ void ue_mrdc_cap_s::to_json(json_writer& j) const j.write_fieldname("fr2-Add-UE-MRDC-Capabilities"); fr2_add_ue_mrdc_cap.to_json(j); } - if (feature_set_combinations_present) { + if (feature_set_combinations.size() > 0) { j.start_array("featureSetCombinations"); for (const auto& e1 : feature_set_combinations) { j.start_array(); @@ -54398,7 +54565,7 @@ void ue_mrdc_cap_s::to_json(json_writer& j) const j.write_fieldname("pdcp-ParametersMRDC-v1530"); pdcp_params_mrdc_v1530.to_json(j); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -54472,13 +54639,13 @@ void ue_nr_cap_v1570_s::to_json(json_writer& j) const SRSASN_CODE ue_nr_cap_v1560_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(nrdc_params_present, 1)); - HANDLE_CODE(bref.pack(rx_filts_present, 1)); + HANDLE_CODE(bref.pack(rx_filts.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (nrdc_params_present) { HANDLE_CODE(nrdc_params.pack(bref)); } - if (rx_filts_present) { + if (rx_filts.size() > 0) { HANDLE_CODE(rx_filts.pack(bref)); } if (non_crit_ext_present) { @@ -54490,6 +54657,7 @@ SRSASN_CODE ue_nr_cap_v1560_s::pack(bit_ref& bref) const SRSASN_CODE ue_nr_cap_v1560_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(nrdc_params_present, 1)); + bool rx_filts_present; HANDLE_CODE(bref.unpack(rx_filts_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -54512,7 +54680,7 @@ void ue_nr_cap_v1560_s::to_json(json_writer& j) const j.write_fieldname("nrdc-Parameters"); nrdc_params.to_json(j); } - if (rx_filts_present) { + if (rx_filts.size() > 0) { j.write_str("receivedFilters", rx_filts.to_string()); } if (non_crit_ext_present) { @@ -55080,8 +55248,8 @@ SRSASN_CODE ue_nr_cap_s::pack(bit_ref& bref) const HANDLE_CODE(bref.pack(fr1_add_ue_nr_cap_present, 1)); HANDLE_CODE(bref.pack(fr2_add_ue_nr_cap_present, 1)); HANDLE_CODE(bref.pack(feature_sets_present, 1)); - HANDLE_CODE(bref.pack(feature_set_combinations_present, 1)); - HANDLE_CODE(bref.pack(late_non_crit_ext_present, 1)); + HANDLE_CODE(bref.pack(feature_set_combinations.size() > 0, 1)); + HANDLE_CODE(bref.pack(late_non_crit_ext.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); HANDLE_CODE(access_stratum_release.pack(bref)); @@ -55112,14 +55280,14 @@ SRSASN_CODE ue_nr_cap_s::pack(bit_ref& bref) const if (feature_sets_present) { HANDLE_CODE(feature_sets.pack(bref)); } - if (feature_set_combinations_present) { + if (feature_set_combinations.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, feature_set_combinations, 1, 1024, SeqOfPacker >(1, 32, SeqOfPacker(1, 128, Packer())))); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { HANDLE_CODE(late_non_crit_ext.pack(bref)); } if (non_crit_ext_present) { @@ -55138,7 +55306,9 @@ SRSASN_CODE ue_nr_cap_s::unpack(cbit_ref& bref) HANDLE_CODE(bref.unpack(fr1_add_ue_nr_cap_present, 1)); HANDLE_CODE(bref.unpack(fr2_add_ue_nr_cap_present, 1)); HANDLE_CODE(bref.unpack(feature_sets_present, 1)); + bool feature_set_combinations_present; HANDLE_CODE(bref.unpack(feature_set_combinations_present, 1)); + bool late_non_crit_ext_present; HANDLE_CODE(bref.unpack(late_non_crit_ext_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -55228,7 +55398,7 @@ void ue_nr_cap_s::to_json(json_writer& j) const j.write_fieldname("featureSets"); feature_sets.to_json(j); } - if (feature_set_combinations_present) { + if (feature_set_combinations.size() > 0) { j.start_array("featureSetCombinations"); for (const auto& e1 : feature_set_combinations) { j.start_array(); @@ -55243,7 +55413,7 @@ void ue_nr_cap_s::to_json(json_writer& j) const } j.end_array(); } - if (late_non_crit_ext_present) { + if (late_non_crit_ext.size() > 0) { j.write_str("lateNonCriticalExtension", late_non_crit_ext.to_string()); } if (non_crit_ext_present) { @@ -55346,25 +55516,25 @@ SRSASN_CODE as_cfg_s::pack(bit_ref& bref) const if (ext) { ext_groups_packer_guard group_flags; - group_flags[0] |= source_rb_sn_cfg_present; - group_flags[0] |= source_scg_nr_cfg_present; - group_flags[0] |= source_scg_eutra_cfg_present; + group_flags[0] |= source_rb_sn_cfg.size() > 0; + group_flags[0] |= source_scg_nr_cfg.size() > 0; + group_flags[0] |= source_scg_eutra_cfg.size() > 0; group_flags[1] |= source_scg_cfgured_present; group_flags.pack(bref); if (group_flags[0]) { varlength_field_pack_guard varlen_scope(bref, false); - HANDLE_CODE(bref.pack(source_rb_sn_cfg_present, 1)); - HANDLE_CODE(bref.pack(source_scg_nr_cfg_present, 1)); - HANDLE_CODE(bref.pack(source_scg_eutra_cfg_present, 1)); - if (source_rb_sn_cfg_present) { + HANDLE_CODE(bref.pack(source_rb_sn_cfg.size() > 0, 1)); + HANDLE_CODE(bref.pack(source_scg_nr_cfg.size() > 0, 1)); + HANDLE_CODE(bref.pack(source_scg_eutra_cfg.size() > 0, 1)); + if (source_rb_sn_cfg.size() > 0) { HANDLE_CODE(source_rb_sn_cfg.pack(bref)); } - if (source_scg_nr_cfg_present) { + if (source_scg_nr_cfg.size() > 0) { HANDLE_CODE(source_scg_nr_cfg.pack(bref)); } - if (source_scg_eutra_cfg_present) { + if (source_scg_eutra_cfg.size() > 0) { HANDLE_CODE(source_scg_eutra_cfg.pack(bref)); } } @@ -55388,8 +55558,11 @@ SRSASN_CODE as_cfg_s::unpack(cbit_ref& bref) if (group_flags[0]) { varlength_field_unpack_guard varlen_scope(bref, false); + bool source_rb_sn_cfg_present; HANDLE_CODE(bref.unpack(source_rb_sn_cfg_present, 1)); + bool source_scg_nr_cfg_present; HANDLE_CODE(bref.unpack(source_scg_nr_cfg_present, 1)); + bool source_scg_eutra_cfg_present; HANDLE_CODE(bref.unpack(source_scg_eutra_cfg_present, 1)); if (source_rb_sn_cfg_present) { HANDLE_CODE(source_rb_sn_cfg.unpack(bref)); @@ -55414,13 +55587,13 @@ void as_cfg_s::to_json(json_writer& j) const j.start_obj(); j.write_str("rrcReconfiguration", rrc_recfg.to_string()); if (ext) { - if (source_rb_sn_cfg_present) { + if (source_rb_sn_cfg.size() > 0) { j.write_str("sourceRB-SN-Config", source_rb_sn_cfg.to_string()); } - if (source_scg_nr_cfg_present) { + if (source_scg_nr_cfg.size() > 0) { j.write_str("sourceSCG-NR-Config", source_scg_nr_cfg.to_string()); } - if (source_scg_eutra_cfg_present) { + if (source_scg_eutra_cfg.size() > 0) { j.write_str("sourceSCG-EUTRA-Config", source_scg_eutra_cfg.to_string()); } if (source_scg_cfgured_present) { @@ -55510,13 +55683,13 @@ void band_combination_info_sn_s::to_json(json_writer& j) const SRSASN_CODE cfg_restrict_info_scg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(allowed_bc_list_mrdc_present, 1)); + HANDLE_CODE(bref.pack(allowed_bc_list_mrdc.size() > 0, 1)); HANDLE_CODE(bref.pack(pwr_coordination_fr1_present, 1)); HANDLE_CODE(bref.pack(serv_cell_idx_range_scg_present, 1)); HANDLE_CODE(bref.pack(max_meas_freqs_scg_present, 1)); HANDLE_CODE(bref.pack(dummy_present, 1)); - if (allowed_bc_list_mrdc_present) { + if (allowed_bc_list_mrdc.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, allowed_bc_list_mrdc, 1, 65536)); } if (pwr_coordination_fr1_present) { @@ -55591,6 +55764,7 @@ SRSASN_CODE cfg_restrict_info_scg_s::pack(bit_ref& bref) const SRSASN_CODE cfg_restrict_info_scg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool allowed_bc_list_mrdc_present; HANDLE_CODE(bref.unpack(allowed_bc_list_mrdc_present, 1)); HANDLE_CODE(bref.unpack(pwr_coordination_fr1_present, 1)); HANDLE_CODE(bref.unpack(serv_cell_idx_range_scg_present, 1)); @@ -55669,7 +55843,7 @@ SRSASN_CODE cfg_restrict_info_scg_s::unpack(cbit_ref& bref) void cfg_restrict_info_scg_s::to_json(json_writer& j) const { j.start_obj(); - if (allowed_bc_list_mrdc_present) { + if (allowed_bc_list_mrdc.size() > 0) { j.start_array("allowedBC-ListMRDC"); for (const auto& e1 : allowed_bc_list_mrdc) { e1.to_json(j); @@ -55734,11 +55908,11 @@ void cfg_restrict_info_scg_s::to_json(json_writer& j) const // ReestablishmentInfo ::= SEQUENCE SRSASN_CODE reest_info_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(add_reestab_info_list_present, 1)); + HANDLE_CODE(bref.pack(add_reestab_info_list.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, source_pci, (uint16_t)0u, (uint16_t)1007u)); HANDLE_CODE(target_cell_short_mac_i.pack(bref)); - if (add_reestab_info_list_present) { + if (add_reestab_info_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, add_reestab_info_list, 1, 32)); } @@ -55746,6 +55920,7 @@ SRSASN_CODE reest_info_s::pack(bit_ref& bref) const } SRSASN_CODE reest_info_s::unpack(cbit_ref& bref) { + bool add_reestab_info_list_present; HANDLE_CODE(bref.unpack(add_reestab_info_list_present, 1)); HANDLE_CODE(unpack_integer(source_pci, bref, (uint16_t)0u, (uint16_t)1007u)); @@ -55761,7 +55936,7 @@ void reest_info_s::to_json(json_writer& j) const j.start_obj(); j.write_int("sourcePhysCellId", source_pci); j.write_str("targetCellShortMAC-I", target_cell_short_mac_i.to_string()); - if (add_reestab_info_list_present) { + if (add_reestab_info_list.size() > 0) { j.start_array("additionalReestabInfoList"); for (const auto& e1 : add_reestab_info_list) { e1.to_json(j); @@ -55788,7 +55963,7 @@ SRSASN_CODE as_context_s::pack(bit_ref& bref) const if (ext) { ext_groups_packer_guard group_flags; group_flags[0] |= ran_notif_area_info.is_present(); - group_flags[1] |= ue_assist_info_present; + group_flags[1] |= ue_assist_info.size() > 0; group_flags[2] |= sel_band_combination_sn.is_present(); group_flags.pack(bref); @@ -55803,8 +55978,8 @@ SRSASN_CODE as_context_s::pack(bit_ref& bref) const if (group_flags[1]) { varlength_field_pack_guard varlen_scope(bref, false); - HANDLE_CODE(bref.pack(ue_assist_info_present, 1)); - if (ue_assist_info_present) { + HANDLE_CODE(bref.pack(ue_assist_info.size() > 0, 1)); + if (ue_assist_info.size() > 0) { HANDLE_CODE(ue_assist_info.pack(bref)); } } @@ -55849,6 +56024,7 @@ SRSASN_CODE as_context_s::unpack(cbit_ref& bref) if (group_flags[1]) { varlength_field_unpack_guard varlen_scope(bref, false); + bool ue_assist_info_present; HANDLE_CODE(bref.unpack(ue_assist_info_present, 1)); if (ue_assist_info_present) { HANDLE_CODE(ue_assist_info.unpack(bref)); @@ -55883,7 +56059,7 @@ void as_context_s::to_json(json_writer& j) const j.write_fieldname("ran-NotificationAreaInfo"); ran_notif_area_info->to_json(j); } - if (ue_assist_info_present) { + if (ue_assist_info.size() > 0) { j.write_str("ueAssistanceInformation", ue_assist_info.to_string()); } if (sel_band_combination_sn.is_present()) { @@ -55949,8 +56125,8 @@ SRSASN_CODE affected_carrier_freq_comb_info_mrdc_s::pack(bit_ref& bref) const HANDLE_CODE(victim_sys_type.pack(bref)); HANDLE_CODE(interference_direction_mrdc.pack(bref)); if (affected_carrier_freq_comb_mrdc_present) { - HANDLE_CODE(bref.pack(affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra_present, 1)); - if (affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra_present) { + HANDLE_CODE(bref.pack(affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra.size() > 0, 1)); + if (affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra, 1, @@ -55973,8 +56149,9 @@ SRSASN_CODE affected_carrier_freq_comb_info_mrdc_s::unpack(cbit_ref& bref) HANDLE_CODE(victim_sys_type.unpack(bref)); HANDLE_CODE(interference_direction_mrdc.unpack(bref)); if (affected_carrier_freq_comb_mrdc_present) { - HANDLE_CODE(bref.unpack(affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra_present, 1)); - if (affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra_present) { + bool affected_carrier_freq_comb_eutra_present; + HANDLE_CODE(bref.unpack(affected_carrier_freq_comb_eutra_present, 1)); + if (affected_carrier_freq_comb_eutra_present) { HANDLE_CODE(unpack_dyn_seq_of(affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra, bref, 1, @@ -55999,7 +56176,7 @@ void affected_carrier_freq_comb_info_mrdc_s::to_json(json_writer& j) const if (affected_carrier_freq_comb_mrdc_present) { j.write_fieldname("affectedCarrierFreqCombMRDC"); j.start_obj(); - if (affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra_present) { + if (affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra.size() > 0) { j.start_array("affectedCarrierFreqCombEUTRA"); for (const auto& e1 : affected_carrier_freq_comb_mrdc.affected_carrier_freq_comb_eutra) { j.write_int(e1); @@ -56058,14 +56235,14 @@ uint8_t ph_ul_carrier_scg_s::ph_type1or3_opts::to_number() const // CG-Config-v1590-IEs ::= SEQUENCE SRSASN_CODE cg_cfg_v1590_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(scell_frequencies_sn_nr_present, 1)); - HANDLE_CODE(bref.pack(scell_frequencies_sn_eutra_present, 1)); + HANDLE_CODE(bref.pack(scell_frequencies_sn_nr.size() > 0, 1)); + HANDLE_CODE(bref.pack(scell_frequencies_sn_eutra.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (scell_frequencies_sn_nr_present) { + if (scell_frequencies_sn_nr.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, scell_frequencies_sn_nr, 1, 31, integer_packer(0, 3279165))); } - if (scell_frequencies_sn_eutra_present) { + if (scell_frequencies_sn_eutra.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, scell_frequencies_sn_eutra, 1, 31, integer_packer(0, 262143))); } @@ -56073,7 +56250,9 @@ SRSASN_CODE cg_cfg_v1590_ies_s::pack(bit_ref& bref) const } SRSASN_CODE cg_cfg_v1590_ies_s::unpack(cbit_ref& bref) { + bool scell_frequencies_sn_nr_present; HANDLE_CODE(bref.unpack(scell_frequencies_sn_nr_present, 1)); + bool scell_frequencies_sn_eutra_present; HANDLE_CODE(bref.unpack(scell_frequencies_sn_eutra_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -56089,14 +56268,14 @@ SRSASN_CODE cg_cfg_v1590_ies_s::unpack(cbit_ref& bref) void cg_cfg_v1590_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (scell_frequencies_sn_nr_present) { + if (scell_frequencies_sn_nr.size() > 0) { j.start_array("scellFrequenciesSN-NR"); for (const auto& e1 : scell_frequencies_sn_nr) { j.write_int(e1); } j.end_array(); } - if (scell_frequencies_sn_eutra_present) { + if (scell_frequencies_sn_eutra.size() > 0) { j.start_array("scellFrequenciesSN-EUTRA"); for (const auto& e1 : scell_frequencies_sn_eutra) { j.write_int(e1); @@ -56155,9 +56334,9 @@ void ph_info_scg_s::to_json(json_writer& j) const SRSASN_CODE cg_cfg_v1560_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(pscell_freq_eutra_present, 1)); - HANDLE_CODE(bref.pack(scg_cell_group_cfg_eutra_present, 1)); - HANDLE_CODE(bref.pack(candidate_cell_info_list_sn_eutra_present, 1)); - HANDLE_CODE(bref.pack(candidate_serving_freq_list_eutra_present, 1)); + HANDLE_CODE(bref.pack(scg_cell_group_cfg_eutra.size() > 0, 1)); + HANDLE_CODE(bref.pack(candidate_cell_info_list_sn_eutra.size() > 0, 1)); + HANDLE_CODE(bref.pack(candidate_serving_freq_list_eutra.size() > 0, 1)); HANDLE_CODE(bref.pack(need_for_gaps_present, 1)); HANDLE_CODE(bref.pack(drx_cfg_scg_present, 1)); HANDLE_CODE(bref.pack(report_cgi_request_eutra_present, 1)); @@ -56166,13 +56345,13 @@ SRSASN_CODE cg_cfg_v1560_ies_s::pack(bit_ref& bref) const if (pscell_freq_eutra_present) { HANDLE_CODE(pack_integer(bref, pscell_freq_eutra, (uint32_t)0u, (uint32_t)262143u)); } - if (scg_cell_group_cfg_eutra_present) { + if (scg_cell_group_cfg_eutra.size() > 0) { HANDLE_CODE(scg_cell_group_cfg_eutra.pack(bref)); } - if (candidate_cell_info_list_sn_eutra_present) { + if (candidate_cell_info_list_sn_eutra.size() > 0) { HANDLE_CODE(candidate_cell_info_list_sn_eutra.pack(bref)); } - if (candidate_serving_freq_list_eutra_present) { + if (candidate_serving_freq_list_eutra.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, candidate_serving_freq_list_eutra, 1, 32, integer_packer(0, 262143))); } if (drx_cfg_scg_present) { @@ -56198,8 +56377,11 @@ SRSASN_CODE cg_cfg_v1560_ies_s::pack(bit_ref& bref) const SRSASN_CODE cg_cfg_v1560_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(pscell_freq_eutra_present, 1)); + bool scg_cell_group_cfg_eutra_present; HANDLE_CODE(bref.unpack(scg_cell_group_cfg_eutra_present, 1)); + bool candidate_cell_info_list_sn_eutra_present; HANDLE_CODE(bref.unpack(candidate_cell_info_list_sn_eutra_present, 1)); + bool candidate_serving_freq_list_eutra_present; HANDLE_CODE(bref.unpack(candidate_serving_freq_list_eutra_present, 1)); HANDLE_CODE(bref.unpack(need_for_gaps_present, 1)); HANDLE_CODE(bref.unpack(drx_cfg_scg_present, 1)); @@ -56244,13 +56426,13 @@ void cg_cfg_v1560_ies_s::to_json(json_writer& j) const if (pscell_freq_eutra_present) { j.write_int("pSCellFrequencyEUTRA", pscell_freq_eutra); } - if (scg_cell_group_cfg_eutra_present) { + if (scg_cell_group_cfg_eutra.size() > 0) { j.write_str("scg-CellGroupConfigEUTRA", scg_cell_group_cfg_eutra.to_string()); } - if (candidate_cell_info_list_sn_eutra_present) { + if (candidate_cell_info_list_sn_eutra.size() > 0) { j.write_str("candidateCellInfoListSN-EUTRA", candidate_cell_info_list_sn_eutra.to_string()); } - if (candidate_serving_freq_list_eutra_present) { + if (candidate_serving_freq_list_eutra.size() > 0) { j.start_array("candidateServingFreqListEUTRA"); for (const auto& e1 : candidate_serving_freq_list_eutra) { j.write_int(e1); @@ -56355,7 +56537,7 @@ SRSASN_CODE cg_cfg_v1540_ies_s::pack(bit_ref& bref) const { HANDLE_CODE(bref.pack(pscell_freq_present, 1)); HANDLE_CODE(bref.pack(report_cgi_request_nr_present, 1)); - HANDLE_CODE(bref.pack(ph_info_scg_present, 1)); + HANDLE_CODE(bref.pack(ph_info_scg.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); if (pscell_freq_present) { @@ -56370,7 +56552,7 @@ SRSASN_CODE cg_cfg_v1540_ies_s::pack(bit_ref& bref) const bref, report_cgi_request_nr.requested_cell_info.cell_for_which_to_report_cgi, (uint16_t)0u, (uint16_t)1007u)); } } - if (ph_info_scg_present) { + if (ph_info_scg.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ph_info_scg, 1, 32)); } if (non_crit_ext_present) { @@ -56383,6 +56565,7 @@ SRSASN_CODE cg_cfg_v1540_ies_s::unpack(cbit_ref& bref) { HANDLE_CODE(bref.unpack(pscell_freq_present, 1)); HANDLE_CODE(bref.unpack(report_cgi_request_nr_present, 1)); + bool ph_info_scg_present; HANDLE_CODE(bref.unpack(ph_info_scg_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -56425,7 +56608,7 @@ void cg_cfg_v1540_ies_s::to_json(json_writer& j) const } j.end_obj(); } - if (ph_info_scg_present) { + if (ph_info_scg.size() > 0) { j.start_array("ph-InfoSCG"); for (const auto& e1 : ph_info_scg) { e1.to_json(j); @@ -57065,9 +57248,9 @@ uint16_t drx_info_s::short_drx_s_::drx_short_cycle_opts::to_number() const SRSASN_CODE meas_cfg_sn_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(measured_frequencies_sn_present, 1)); + HANDLE_CODE(bref.pack(measured_frequencies_sn.size() > 0, 1)); - if (measured_frequencies_sn_present) { + if (measured_frequencies_sn.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, measured_frequencies_sn, 1, 32)); } @@ -57076,6 +57259,7 @@ SRSASN_CODE meas_cfg_sn_s::pack(bit_ref& bref) const SRSASN_CODE meas_cfg_sn_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool measured_frequencies_sn_present; HANDLE_CODE(bref.unpack(measured_frequencies_sn_present, 1)); if (measured_frequencies_sn_present) { @@ -57087,7 +57271,7 @@ SRSASN_CODE meas_cfg_sn_s::unpack(cbit_ref& bref) void meas_cfg_sn_s::to_json(json_writer& j) const { j.start_obj(); - if (measured_frequencies_sn_present) { + if (measured_frequencies_sn.size() > 0) { j.start_array("measuredFrequenciesSN"); for (const auto& e1 : measured_frequencies_sn) { e1.to_json(j); @@ -57100,21 +57284,21 @@ void meas_cfg_sn_s::to_json(json_writer& j) const // CG-Config-IEs ::= SEQUENCE SRSASN_CODE cg_cfg_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(scg_cell_group_cfg_present, 1)); - HANDLE_CODE(bref.pack(scg_rb_cfg_present, 1)); + HANDLE_CODE(bref.pack(scg_cell_group_cfg.size() > 0, 1)); + HANDLE_CODE(bref.pack(scg_rb_cfg.size() > 0, 1)); HANDLE_CODE(bref.pack(cfg_restrict_mod_req_present, 1)); HANDLE_CODE(bref.pack(drx_info_scg_present, 1)); - HANDLE_CODE(bref.pack(candidate_cell_info_list_sn_present, 1)); + HANDLE_CODE(bref.pack(candidate_cell_info_list_sn.size() > 0, 1)); HANDLE_CODE(bref.pack(meas_cfg_sn_present, 1)); HANDLE_CODE(bref.pack(sel_band_combination_present, 1)); - HANDLE_CODE(bref.pack(fr_info_list_scg_present, 1)); - HANDLE_CODE(bref.pack(candidate_serving_freq_list_nr_present, 1)); + HANDLE_CODE(bref.pack(fr_info_list_scg.size() > 0, 1)); + HANDLE_CODE(bref.pack(candidate_serving_freq_list_nr.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (scg_cell_group_cfg_present) { + if (scg_cell_group_cfg.size() > 0) { HANDLE_CODE(scg_cell_group_cfg.pack(bref)); } - if (scg_rb_cfg_present) { + if (scg_rb_cfg.size() > 0) { HANDLE_CODE(scg_rb_cfg.pack(bref)); } if (cfg_restrict_mod_req_present) { @@ -57123,7 +57307,7 @@ SRSASN_CODE cg_cfg_ies_s::pack(bit_ref& bref) const if (drx_info_scg_present) { HANDLE_CODE(drx_info_scg.pack(bref)); } - if (candidate_cell_info_list_sn_present) { + if (candidate_cell_info_list_sn.size() > 0) { HANDLE_CODE(candidate_cell_info_list_sn.pack(bref)); } if (meas_cfg_sn_present) { @@ -57132,10 +57316,10 @@ SRSASN_CODE cg_cfg_ies_s::pack(bit_ref& bref) const if (sel_band_combination_present) { HANDLE_CODE(sel_band_combination.pack(bref)); } - if (fr_info_list_scg_present) { + if (fr_info_list_scg.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, fr_info_list_scg, 1, 31)); } - if (candidate_serving_freq_list_nr_present) { + if (candidate_serving_freq_list_nr.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, candidate_serving_freq_list_nr, 1, 32, integer_packer(0, 3279165))); } if (non_crit_ext_present) { @@ -57146,14 +57330,19 @@ SRSASN_CODE cg_cfg_ies_s::pack(bit_ref& bref) const } SRSASN_CODE cg_cfg_ies_s::unpack(cbit_ref& bref) { + bool scg_cell_group_cfg_present; HANDLE_CODE(bref.unpack(scg_cell_group_cfg_present, 1)); + bool scg_rb_cfg_present; HANDLE_CODE(bref.unpack(scg_rb_cfg_present, 1)); HANDLE_CODE(bref.unpack(cfg_restrict_mod_req_present, 1)); HANDLE_CODE(bref.unpack(drx_info_scg_present, 1)); + bool candidate_cell_info_list_sn_present; HANDLE_CODE(bref.unpack(candidate_cell_info_list_sn_present, 1)); HANDLE_CODE(bref.unpack(meas_cfg_sn_present, 1)); HANDLE_CODE(bref.unpack(sel_band_combination_present, 1)); + bool fr_info_list_scg_present; HANDLE_CODE(bref.unpack(fr_info_list_scg_present, 1)); + bool candidate_serving_freq_list_nr_present; HANDLE_CODE(bref.unpack(candidate_serving_freq_list_nr_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -57193,10 +57382,10 @@ SRSASN_CODE cg_cfg_ies_s::unpack(cbit_ref& bref) void cg_cfg_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (scg_cell_group_cfg_present) { + if (scg_cell_group_cfg.size() > 0) { j.write_str("scg-CellGroupConfig", scg_cell_group_cfg.to_string()); } - if (scg_rb_cfg_present) { + if (scg_rb_cfg.size() > 0) { j.write_str("scg-RB-Config", scg_rb_cfg.to_string()); } if (cfg_restrict_mod_req_present) { @@ -57207,7 +57396,7 @@ void cg_cfg_ies_s::to_json(json_writer& j) const j.write_fieldname("drx-InfoSCG"); drx_info_scg.to_json(j); } - if (candidate_cell_info_list_sn_present) { + if (candidate_cell_info_list_sn.size() > 0) { j.write_str("candidateCellInfoListSN", candidate_cell_info_list_sn.to_string()); } if (meas_cfg_sn_present) { @@ -57218,14 +57407,14 @@ void cg_cfg_ies_s::to_json(json_writer& j) const j.write_fieldname("selectedBandCombination"); sel_band_combination.to_json(j); } - if (fr_info_list_scg_present) { + if (fr_info_list_scg.size() > 0) { j.start_array("fr-InfoListSCG"); for (const auto& e1 : fr_info_list_scg) { e1.to_json(j); } j.end_array(); } - if (candidate_serving_freq_list_nr_present) { + if (candidate_serving_freq_list_nr.size() > 0) { j.start_array("candidateServingFreqListNR"); for (const auto& e1 : candidate_serving_freq_list_nr) { j.write_int(e1); @@ -57422,10 +57611,10 @@ uint8_t cg_cfg_s::crit_exts_c_::types_opts::to_number() const // CG-ConfigInfo-v1590-IEs ::= SEQUENCE SRSASN_CODE cg_cfg_info_v1590_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(serv_frequencies_mn_nr_present, 1)); + HANDLE_CODE(bref.pack(serv_frequencies_mn_nr.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (serv_frequencies_mn_nr_present) { + if (serv_frequencies_mn_nr.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, serv_frequencies_mn_nr, 1, 31, integer_packer(0, 3279165))); } @@ -57433,6 +57622,7 @@ SRSASN_CODE cg_cfg_info_v1590_ies_s::pack(bit_ref& bref) const } SRSASN_CODE cg_cfg_info_v1590_ies_s::unpack(cbit_ref& bref) { + bool serv_frequencies_mn_nr_present; HANDLE_CODE(bref.unpack(serv_frequencies_mn_nr_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -57445,7 +57635,7 @@ SRSASN_CODE cg_cfg_info_v1590_ies_s::unpack(cbit_ref& bref) void cg_cfg_info_v1590_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (serv_frequencies_mn_nr_present) { + if (serv_frequencies_mn_nr.size() > 0) { j.start_array("servFrequenciesMN-NR"); for (const auto& e1 : serv_frequencies_mn_nr) { j.write_int(e1); @@ -57496,14 +57686,14 @@ uint8_t ph_ul_carrier_mcg_s::ph_type1or3_opts::to_number() const // CG-ConfigInfo-v1570-IEs ::= SEQUENCE SRSASN_CODE cg_cfg_info_v1570_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(sftd_freq_list_nr_present, 1)); - HANDLE_CODE(bref.pack(sftd_freq_list_eutra_present, 1)); + HANDLE_CODE(bref.pack(sftd_freq_list_nr.size() > 0, 1)); + HANDLE_CODE(bref.pack(sftd_freq_list_eutra.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (sftd_freq_list_nr_present) { + if (sftd_freq_list_nr.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sftd_freq_list_nr, 1, 3, integer_packer(0, 3279165))); } - if (sftd_freq_list_eutra_present) { + if (sftd_freq_list_eutra.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, sftd_freq_list_eutra, 1, 3, integer_packer(0, 262143))); } if (non_crit_ext_present) { @@ -57514,7 +57704,9 @@ SRSASN_CODE cg_cfg_info_v1570_ies_s::pack(bit_ref& bref) const } SRSASN_CODE cg_cfg_info_v1570_ies_s::unpack(cbit_ref& bref) { + bool sftd_freq_list_nr_present; HANDLE_CODE(bref.unpack(sftd_freq_list_nr_present, 1)); + bool sftd_freq_list_eutra_present; HANDLE_CODE(bref.unpack(sftd_freq_list_eutra_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -57533,14 +57725,14 @@ SRSASN_CODE cg_cfg_info_v1570_ies_s::unpack(cbit_ref& bref) void cg_cfg_info_v1570_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (sftd_freq_list_nr_present) { + if (sftd_freq_list_nr.size() > 0) { j.start_array("sftdFrequencyList-NR"); for (const auto& e1 : sftd_freq_list_nr) { j.write_int(e1); } j.end_array(); } - if (sftd_freq_list_eutra_present) { + if (sftd_freq_list_eutra.size() > 0) { j.start_array("sftdFrequencyList-EUTRA"); for (const auto& e1 : sftd_freq_list_eutra) { j.write_int(e1); @@ -57597,23 +57789,23 @@ void ph_info_mcg_s::to_json(json_writer& j) const // CG-ConfigInfo-v1560-IEs ::= SEQUENCE SRSASN_CODE cg_cfg_info_v1560_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(candidate_cell_info_list_mn_eutra_present, 1)); - HANDLE_CODE(bref.pack(candidate_cell_info_list_sn_eutra_present, 1)); - HANDLE_CODE(bref.pack(source_cfg_scg_eutra_present, 1)); + HANDLE_CODE(bref.pack(candidate_cell_info_list_mn_eutra.size() > 0, 1)); + HANDLE_CODE(bref.pack(candidate_cell_info_list_sn_eutra.size() > 0, 1)); + HANDLE_CODE(bref.pack(source_cfg_scg_eutra.size() > 0, 1)); HANDLE_CODE(bref.pack(scg_fail_info_eutra_present, 1)); HANDLE_CODE(bref.pack(drx_cfg_mcg_present, 1)); HANDLE_CODE(bref.pack(meas_result_report_cgi_eutra_present, 1)); - HANDLE_CODE(bref.pack(meas_result_cell_list_sftd_eutra_present, 1)); - HANDLE_CODE(bref.pack(fr_info_list_mcg_present, 1)); + HANDLE_CODE(bref.pack(meas_result_cell_list_sftd_eutra.size() > 0, 1)); + HANDLE_CODE(bref.pack(fr_info_list_mcg.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (candidate_cell_info_list_mn_eutra_present) { + if (candidate_cell_info_list_mn_eutra.size() > 0) { HANDLE_CODE(candidate_cell_info_list_mn_eutra.pack(bref)); } - if (candidate_cell_info_list_sn_eutra_present) { + if (candidate_cell_info_list_sn_eutra.size() > 0) { HANDLE_CODE(candidate_cell_info_list_sn_eutra.pack(bref)); } - if (source_cfg_scg_eutra_present) { + if (source_cfg_scg_eutra.size() > 0) { HANDLE_CODE(source_cfg_scg_eutra.pack(bref)); } if (scg_fail_info_eutra_present) { @@ -57629,10 +57821,10 @@ SRSASN_CODE cg_cfg_info_v1560_ies_s::pack(bit_ref& bref) const bref, meas_result_report_cgi_eutra.cell_for_which_to_report_cgi_eutra, (uint16_t)0u, (uint16_t)503u)); HANDLE_CODE(meas_result_report_cgi_eutra.cgi_info_eutra.pack(bref)); } - if (meas_result_cell_list_sftd_eutra_present) { + if (meas_result_cell_list_sftd_eutra.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_result_cell_list_sftd_eutra, 1, 3)); } - if (fr_info_list_mcg_present) { + if (fr_info_list_mcg.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, fr_info_list_mcg, 1, 31)); } if (non_crit_ext_present) { @@ -57643,13 +57835,18 @@ SRSASN_CODE cg_cfg_info_v1560_ies_s::pack(bit_ref& bref) const } SRSASN_CODE cg_cfg_info_v1560_ies_s::unpack(cbit_ref& bref) { + bool candidate_cell_info_list_mn_eutra_present; HANDLE_CODE(bref.unpack(candidate_cell_info_list_mn_eutra_present, 1)); + bool candidate_cell_info_list_sn_eutra_present; HANDLE_CODE(bref.unpack(candidate_cell_info_list_sn_eutra_present, 1)); + bool source_cfg_scg_eutra_present; HANDLE_CODE(bref.unpack(source_cfg_scg_eutra_present, 1)); HANDLE_CODE(bref.unpack(scg_fail_info_eutra_present, 1)); HANDLE_CODE(bref.unpack(drx_cfg_mcg_present, 1)); HANDLE_CODE(bref.unpack(meas_result_report_cgi_eutra_present, 1)); + bool meas_result_cell_list_sftd_eutra_present; HANDLE_CODE(bref.unpack(meas_result_cell_list_sftd_eutra_present, 1)); + bool fr_info_list_mcg_present; HANDLE_CODE(bref.unpack(fr_info_list_mcg_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -57690,13 +57887,13 @@ SRSASN_CODE cg_cfg_info_v1560_ies_s::unpack(cbit_ref& bref) void cg_cfg_info_v1560_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (candidate_cell_info_list_mn_eutra_present) { + if (candidate_cell_info_list_mn_eutra.size() > 0) { j.write_str("candidateCellInfoListMN-EUTRA", candidate_cell_info_list_mn_eutra.to_string()); } - if (candidate_cell_info_list_sn_eutra_present) { + if (candidate_cell_info_list_sn_eutra.size() > 0) { j.write_str("candidateCellInfoListSN-EUTRA", candidate_cell_info_list_sn_eutra.to_string()); } - if (source_cfg_scg_eutra_present) { + if (source_cfg_scg_eutra.size() > 0) { j.write_str("sourceConfigSCG-EUTRA", source_cfg_scg_eutra.to_string()); } if (scg_fail_info_eutra_present) { @@ -57719,14 +57916,14 @@ void cg_cfg_info_v1560_ies_s::to_json(json_writer& j) const meas_result_report_cgi_eutra.cgi_info_eutra.to_json(j); j.end_obj(); } - if (meas_result_cell_list_sftd_eutra_present) { + if (meas_result_cell_list_sftd_eutra.size() > 0) { j.start_array("measResultCellListSFTD-EUTRA"); for (const auto& e1 : meas_result_cell_list_sftd_eutra) { e1.to_json(j); } j.end_array(); } - if (fr_info_list_mcg_present) { + if (fr_info_list_mcg.size() > 0) { j.start_array("fr-InfoListMCG"); for (const auto& e1 : fr_info_list_mcg) { e1.to_json(j); @@ -57754,11 +57951,11 @@ uint16_t cg_cfg_info_v1560_ies_s::scg_fail_info_eutra_s_::fail_type_eutra_opts:: // CG-ConfigInfo-v1540-IEs ::= SEQUENCE SRSASN_CODE cg_cfg_info_v1540_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(ph_info_mcg_present, 1)); + HANDLE_CODE(bref.pack(ph_info_mcg.size() > 0, 1)); HANDLE_CODE(bref.pack(meas_result_report_cgi_present, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (ph_info_mcg_present) { + if (ph_info_mcg.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, ph_info_mcg, 1, 32)); } if (meas_result_report_cgi_present) { @@ -57774,6 +57971,7 @@ SRSASN_CODE cg_cfg_info_v1540_ies_s::pack(bit_ref& bref) const } SRSASN_CODE cg_cfg_info_v1540_ies_s::unpack(cbit_ref& bref) { + bool ph_info_mcg_present; HANDLE_CODE(bref.unpack(ph_info_mcg_present, 1)); HANDLE_CODE(bref.unpack(meas_result_report_cgi_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -57796,7 +57994,7 @@ SRSASN_CODE cg_cfg_info_v1540_ies_s::unpack(cbit_ref& bref) void cg_cfg_info_v1540_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (ph_info_mcg_present) { + if (ph_info_mcg.size() > 0) { j.start_array("ph-InfoMCG"); for (const auto& e1 : ph_info_mcg) { e1.to_json(j); @@ -57849,11 +58047,11 @@ void mrdc_assist_info_s::to_json(json_writer& j) const SRSASN_CODE meas_cfg_mn_s::pack(bit_ref& bref) const { bref.pack(ext, 1); - HANDLE_CODE(bref.pack(measured_frequencies_mn_present, 1)); + HANDLE_CODE(bref.pack(measured_frequencies_mn.size() > 0, 1)); HANDLE_CODE(bref.pack(meas_gap_cfg_present, 1)); HANDLE_CODE(bref.pack(gap_purpose_present, 1)); - if (measured_frequencies_mn_present) { + if (measured_frequencies_mn.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, measured_frequencies_mn, 1, 32)); } if (meas_gap_cfg_present) { @@ -57882,6 +58080,7 @@ SRSASN_CODE meas_cfg_mn_s::pack(bit_ref& bref) const SRSASN_CODE meas_cfg_mn_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); + bool measured_frequencies_mn_present; HANDLE_CODE(bref.unpack(measured_frequencies_mn_present, 1)); HANDLE_CODE(bref.unpack(meas_gap_cfg_present, 1)); HANDLE_CODE(bref.unpack(gap_purpose_present, 1)); @@ -57916,7 +58115,7 @@ SRSASN_CODE meas_cfg_mn_s::unpack(cbit_ref& bref) void meas_cfg_mn_s::to_json(json_writer& j) const { j.start_obj(); - if (measured_frequencies_mn_present) { + if (measured_frequencies_mn.size() > 0) { j.start_array("measuredFrequenciesMN"); for (const auto& e1 : measured_frequencies_mn) { e1.to_json(j); @@ -57956,30 +58155,30 @@ uint8_t meas_cfg_mn_s::gap_purpose_opts::to_number() const // CG-ConfigInfo-IEs ::= SEQUENCE SRSASN_CODE cg_cfg_info_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(ue_cap_info_present, 1)); - HANDLE_CODE(bref.pack(candidate_cell_info_list_mn_present, 1)); - HANDLE_CODE(bref.pack(candidate_cell_info_list_sn_present, 1)); - HANDLE_CODE(bref.pack(meas_result_cell_list_sftd_nr_present, 1)); + HANDLE_CODE(bref.pack(ue_cap_info.size() > 0, 1)); + HANDLE_CODE(bref.pack(candidate_cell_info_list_mn.size() > 0, 1)); + HANDLE_CODE(bref.pack(candidate_cell_info_list_sn.size() > 0, 1)); + HANDLE_CODE(bref.pack(meas_result_cell_list_sftd_nr.size() > 0, 1)); HANDLE_CODE(bref.pack(scg_fail_info_present, 1)); HANDLE_CODE(bref.pack(cfg_restrict_info_present, 1)); HANDLE_CODE(bref.pack(drx_info_mcg_present, 1)); HANDLE_CODE(bref.pack(meas_cfg_mn_present, 1)); - HANDLE_CODE(bref.pack(source_cfg_scg_present, 1)); - HANDLE_CODE(bref.pack(scg_rb_cfg_present, 1)); - HANDLE_CODE(bref.pack(mcg_rb_cfg_present, 1)); + HANDLE_CODE(bref.pack(source_cfg_scg.size() > 0, 1)); + HANDLE_CODE(bref.pack(scg_rb_cfg.size() > 0, 1)); + HANDLE_CODE(bref.pack(mcg_rb_cfg.size() > 0, 1)); HANDLE_CODE(bref.pack(mrdc_assist_info_present, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (ue_cap_info_present) { + if (ue_cap_info.size() > 0) { HANDLE_CODE(ue_cap_info.pack(bref)); } - if (candidate_cell_info_list_mn_present) { + if (candidate_cell_info_list_mn.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, candidate_cell_info_list_mn, 1, 8)); } - if (candidate_cell_info_list_sn_present) { + if (candidate_cell_info_list_sn.size() > 0) { HANDLE_CODE(candidate_cell_info_list_sn.pack(bref)); } - if (meas_result_cell_list_sftd_nr_present) { + if (meas_result_cell_list_sftd_nr.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_result_cell_list_sftd_nr, 1, 3)); } if (scg_fail_info_present) { @@ -57995,13 +58194,13 @@ SRSASN_CODE cg_cfg_info_ies_s::pack(bit_ref& bref) const if (meas_cfg_mn_present) { HANDLE_CODE(meas_cfg_mn.pack(bref)); } - if (source_cfg_scg_present) { + if (source_cfg_scg.size() > 0) { HANDLE_CODE(source_cfg_scg.pack(bref)); } - if (scg_rb_cfg_present) { + if (scg_rb_cfg.size() > 0) { HANDLE_CODE(scg_rb_cfg.pack(bref)); } - if (mcg_rb_cfg_present) { + if (mcg_rb_cfg.size() > 0) { HANDLE_CODE(mcg_rb_cfg.pack(bref)); } if (mrdc_assist_info_present) { @@ -58015,16 +58214,23 @@ SRSASN_CODE cg_cfg_info_ies_s::pack(bit_ref& bref) const } SRSASN_CODE cg_cfg_info_ies_s::unpack(cbit_ref& bref) { + bool ue_cap_info_present; HANDLE_CODE(bref.unpack(ue_cap_info_present, 1)); + bool candidate_cell_info_list_mn_present; HANDLE_CODE(bref.unpack(candidate_cell_info_list_mn_present, 1)); + bool candidate_cell_info_list_sn_present; HANDLE_CODE(bref.unpack(candidate_cell_info_list_sn_present, 1)); + bool meas_result_cell_list_sftd_nr_present; HANDLE_CODE(bref.unpack(meas_result_cell_list_sftd_nr_present, 1)); HANDLE_CODE(bref.unpack(scg_fail_info_present, 1)); HANDLE_CODE(bref.unpack(cfg_restrict_info_present, 1)); HANDLE_CODE(bref.unpack(drx_info_mcg_present, 1)); HANDLE_CODE(bref.unpack(meas_cfg_mn_present, 1)); + bool source_cfg_scg_present; HANDLE_CODE(bref.unpack(source_cfg_scg_present, 1)); + bool scg_rb_cfg_present; HANDLE_CODE(bref.unpack(scg_rb_cfg_present, 1)); + bool mcg_rb_cfg_present; HANDLE_CODE(bref.unpack(mcg_rb_cfg_present, 1)); HANDLE_CODE(bref.unpack(mrdc_assist_info_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -58075,20 +58281,20 @@ SRSASN_CODE cg_cfg_info_ies_s::unpack(cbit_ref& bref) void cg_cfg_info_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (ue_cap_info_present) { + if (ue_cap_info.size() > 0) { j.write_str("ue-CapabilityInfo", ue_cap_info.to_string()); } - if (candidate_cell_info_list_mn_present) { + if (candidate_cell_info_list_mn.size() > 0) { j.start_array("candidateCellInfoListMN"); for (const auto& e1 : candidate_cell_info_list_mn) { e1.to_json(j); } j.end_array(); } - if (candidate_cell_info_list_sn_present) { + if (candidate_cell_info_list_sn.size() > 0) { j.write_str("candidateCellInfoListSN", candidate_cell_info_list_sn.to_string()); } - if (meas_result_cell_list_sftd_nr_present) { + if (meas_result_cell_list_sftd_nr.size() > 0) { j.start_array("measResultCellListSFTD-NR"); for (const auto& e1 : meas_result_cell_list_sftd_nr) { e1.to_json(j); @@ -58114,13 +58320,13 @@ void cg_cfg_info_ies_s::to_json(json_writer& j) const j.write_fieldname("measConfigMN"); meas_cfg_mn.to_json(j); } - if (source_cfg_scg_present) { + if (source_cfg_scg.size() > 0) { j.write_str("sourceConfigSCG", source_cfg_scg.to_string()); } - if (scg_rb_cfg_present) { + if (scg_rb_cfg.size() > 0) { j.write_str("scg-RB-Config", scg_rb_cfg.to_string()); } - if (mcg_rb_cfg_present) { + if (mcg_rb_cfg.size() > 0) { j.write_str("mcg-RB-Config", mcg_rb_cfg.to_string()); } if (mrdc_assist_info_present) { @@ -58660,12 +58866,12 @@ SRSASN_CODE rrm_cfg_s::pack(bit_ref& bref) const { bref.pack(ext, 1); HANDLE_CODE(bref.pack(ue_inactive_time_present, 1)); - HANDLE_CODE(bref.pack(candidate_cell_info_list_present, 1)); + HANDLE_CODE(bref.pack(candidate_cell_info_list.size() > 0, 1)); if (ue_inactive_time_present) { HANDLE_CODE(ue_inactive_time.pack(bref)); } - if (candidate_cell_info_list_present) { + if (candidate_cell_info_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, candidate_cell_info_list, 1, 8)); } @@ -58689,6 +58895,7 @@ SRSASN_CODE rrm_cfg_s::unpack(cbit_ref& bref) { bref.unpack(ext, 1); HANDLE_CODE(bref.unpack(ue_inactive_time_present, 1)); + bool candidate_cell_info_list_present; HANDLE_CODE(bref.unpack(candidate_cell_info_list_present, 1)); if (ue_inactive_time_present) { @@ -58721,7 +58928,7 @@ void rrm_cfg_s::to_json(json_writer& j) const if (ue_inactive_time_present) { j.write_str("ue-InactiveTime", ue_inactive_time.to_string()); } - if (candidate_cell_info_list_present) { + if (candidate_cell_info_list.size() > 0) { j.start_array("candidateCellInfoList"); for (const auto& e1 : candidate_cell_info_list) { e1.to_json(j); @@ -59138,10 +59345,10 @@ void meas_timing_cfg_v1550_ies_s::to_json(json_writer& j) const // MeasurementTimingConfiguration-IEs ::= SEQUENCE SRSASN_CODE meas_timing_cfg_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(meas_timing_present, 1)); + HANDLE_CODE(bref.pack(meas_timing.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (meas_timing_present) { + if (meas_timing.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_timing, 1, 32)); } if (non_crit_ext_present) { @@ -59152,6 +59359,7 @@ SRSASN_CODE meas_timing_cfg_ies_s::pack(bit_ref& bref) const } SRSASN_CODE meas_timing_cfg_ies_s::unpack(cbit_ref& bref) { + bool meas_timing_present; HANDLE_CODE(bref.unpack(meas_timing_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -59167,7 +59375,7 @@ SRSASN_CODE meas_timing_cfg_ies_s::unpack(cbit_ref& bref) void meas_timing_cfg_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (meas_timing_present) { + if (meas_timing.size() > 0) { j.start_array("measTiming"); for (const auto& e1 : meas_timing) { e1.to_json(j); @@ -59614,10 +59822,10 @@ uint8_t ue_radio_access_cap_info_s::crit_exts_c_::types_opts::to_number() const // UERadioPagingInformation-IEs ::= SEQUENCE SRSASN_CODE ue_radio_paging_info_ies_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(supported_band_list_nr_for_paging_present, 1)); + HANDLE_CODE(bref.pack(supported_band_list_nr_for_paging.size() > 0, 1)); HANDLE_CODE(bref.pack(non_crit_ext_present, 1)); - if (supported_band_list_nr_for_paging_present) { + if (supported_band_list_nr_for_paging.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, supported_band_list_nr_for_paging, 1, 1024, integer_packer(1, 1024))); } @@ -59625,6 +59833,7 @@ SRSASN_CODE ue_radio_paging_info_ies_s::pack(bit_ref& bref) const } SRSASN_CODE ue_radio_paging_info_ies_s::unpack(cbit_ref& bref) { + bool supported_band_list_nr_for_paging_present; HANDLE_CODE(bref.unpack(supported_band_list_nr_for_paging_present, 1)); HANDLE_CODE(bref.unpack(non_crit_ext_present, 1)); @@ -59637,7 +59846,7 @@ SRSASN_CODE ue_radio_paging_info_ies_s::unpack(cbit_ref& bref) void ue_radio_paging_info_ies_s::to_json(json_writer& j) const { j.start_obj(); - if (supported_band_list_nr_for_paging_present) { + if (supported_band_list_nr_for_paging.size() > 0) { j.start_array("supportedBandListNRForPaging"); for (const auto& e1 : supported_band_list_nr_for_paging) { j.write_int(e1); @@ -59876,19 +60085,19 @@ uint8_t ue_radio_paging_info_s::crit_exts_c_::types_opts::to_number() const // VarMeasConfig ::= SEQUENCE SRSASN_CODE var_meas_cfg_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(meas_id_list_present, 1)); - HANDLE_CODE(bref.pack(meas_obj_list_present, 1)); - HANDLE_CODE(bref.pack(report_cfg_list_present, 1)); + HANDLE_CODE(bref.pack(meas_id_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(meas_obj_list.size() > 0, 1)); + HANDLE_CODE(bref.pack(report_cfg_list.size() > 0, 1)); HANDLE_CODE(bref.pack(quant_cfg_present, 1)); HANDLE_CODE(bref.pack(s_measure_cfg_present, 1)); - if (meas_id_list_present) { + if (meas_id_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_id_list, 1, 64)); } - if (meas_obj_list_present) { + if (meas_obj_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, meas_obj_list, 1, 64)); } - if (report_cfg_list_present) { + if (report_cfg_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, report_cfg_list, 1, 64)); } if (quant_cfg_present) { @@ -59902,8 +60111,11 @@ SRSASN_CODE var_meas_cfg_s::pack(bit_ref& bref) const } SRSASN_CODE var_meas_cfg_s::unpack(cbit_ref& bref) { + bool meas_id_list_present; HANDLE_CODE(bref.unpack(meas_id_list_present, 1)); + bool meas_obj_list_present; HANDLE_CODE(bref.unpack(meas_obj_list_present, 1)); + bool report_cfg_list_present; HANDLE_CODE(bref.unpack(report_cfg_list_present, 1)); HANDLE_CODE(bref.unpack(quant_cfg_present, 1)); HANDLE_CODE(bref.unpack(s_measure_cfg_present, 1)); @@ -59929,21 +60141,21 @@ SRSASN_CODE var_meas_cfg_s::unpack(cbit_ref& bref) void var_meas_cfg_s::to_json(json_writer& j) const { j.start_obj(); - if (meas_id_list_present) { + if (meas_id_list.size() > 0) { j.start_array("measIdList"); for (const auto& e1 : meas_id_list) { e1.to_json(j); } j.end_array(); } - if (meas_obj_list_present) { + if (meas_obj_list.size() > 0) { j.start_array("measObjectList"); for (const auto& e1 : meas_obj_list) { e1.to_json(j); } j.end_array(); } - if (report_cfg_list_present) { + if (report_cfg_list.size() > 0) { j.start_array("reportConfigList"); for (const auto& e1 : report_cfg_list) { e1.to_json(j); @@ -60074,10 +60286,10 @@ const char* var_meas_cfg_s::s_measure_cfg_c_::types_opts::to_string() const // VarMeasReport ::= SEQUENCE SRSASN_CODE var_meas_report_s::pack(bit_ref& bref) const { - HANDLE_CODE(bref.pack(cells_triggered_list_present, 1)); + HANDLE_CODE(bref.pack(cells_triggered_list.size() > 0, 1)); HANDLE_CODE(pack_integer(bref, meas_id, (uint8_t)1u, (uint8_t)64u)); - if (cells_triggered_list_present) { + if (cells_triggered_list.size() > 0) { HANDLE_CODE(pack_dyn_seq_of(bref, cells_triggered_list, 1, 32)); } HANDLE_CODE(pack_unconstrained_integer(bref, nof_reports_sent)); @@ -60086,6 +60298,7 @@ SRSASN_CODE var_meas_report_s::pack(bit_ref& bref) const } SRSASN_CODE var_meas_report_s::unpack(cbit_ref& bref) { + bool cells_triggered_list_present; HANDLE_CODE(bref.unpack(cells_triggered_list_present, 1)); HANDLE_CODE(unpack_integer(meas_id, bref, (uint8_t)1u, (uint8_t)64u)); @@ -60100,7 +60313,7 @@ void var_meas_report_s::to_json(json_writer& j) const { j.start_obj(); j.write_int("measId", meas_id); - if (cells_triggered_list_present) { + if (cells_triggered_list.size() > 0) { j.start_array("cellsTriggeredList"); for (const auto& e1 : cells_triggered_list) { e1.to_json(j); diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index fb302fd63..cbbd66360 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,7 +20,9 @@ */ #include "srsran/asn1/rrc_nr_utils.h" +#include "srsran/asn1/obj_id_cmp_utils.h" #include "srsran/asn1/rrc_nr.h" +#include "srsran/common/band_helper.h" #include "srsran/config.h" #include "srsran/interfaces/pdcp_interface_types.h" #include "srsran/interfaces/rlc_interface_types.h" @@ -97,38 +99,98 @@ bool make_mac_phr_cfg_t(const phr_cfg_s& asn1_type, phr_cfg_nr_t* phr_cfg_nr) return true; } -rach_nr_cfg_t make_mac_rach_cfg(const rach_cfg_common_s& asn1_type) +void make_mac_rach_cfg(const rach_cfg_common_s& asn1_type, rach_cfg_nr_t* rach_cfg_nr) { - rach_nr_cfg_t rach_nr_cfg = {}; - rach_nr_cfg.powerRampingStep = asn1_type.rach_cfg_generic.pwr_ramp_step.to_number(); - rach_nr_cfg.ra_responseWindow = asn1_type.rach_cfg_generic.ra_resp_win.to_number(); - rach_nr_cfg.prach_ConfigurationIndex = asn1_type.rach_cfg_generic.prach_cfg_idx; - rach_nr_cfg.PreambleReceivedTargetPower = asn1_type.rach_cfg_generic.preamb_rx_target_pwr; - rach_nr_cfg.preambleTransMax = asn1_type.rach_cfg_generic.preamb_trans_max.to_number(); - rach_nr_cfg.ra_ContentionResolutionTimer = asn1_type.ra_contention_resolution_timer.to_number(); - return rach_nr_cfg; + rach_cfg_nr->powerRampingStep = asn1_type.rach_cfg_generic.pwr_ramp_step.to_number(); + rach_cfg_nr->ra_responseWindow = asn1_type.rach_cfg_generic.ra_resp_win.to_number(); + rach_cfg_nr->prach_ConfigurationIndex = asn1_type.rach_cfg_generic.prach_cfg_idx; + rach_cfg_nr->PreambleReceivedTargetPower = asn1_type.rach_cfg_generic.preamb_rx_target_pwr; + rach_cfg_nr->preambleTransMax = asn1_type.rach_cfg_generic.preamb_trans_max.to_number(); + rach_cfg_nr->ra_ContentionResolutionTimer = asn1_type.ra_contention_resolution_timer.to_number(); }; -rlc_config_t make_rlc_config_t(const rlc_cfg_c& asn1_type) +int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_t* cfg_out) { - rlc_config_t rlc_cfg = rlc_config_t::default_rlc_um_nr_config(); + rlc_config_t rlc_cfg = {}; rlc_cfg.rat = srsran_rat_t::nr; switch (asn1_type.type().value) { case rlc_cfg_c::types_opts::am: + rlc_cfg = rlc_config_t::default_rlc_am_nr_config(); + if (asn1_type.am().dl_am_rlc.sn_field_len_present && asn1_type.am().ul_am_rlc.sn_field_len_present && + asn1_type.am().dl_am_rlc.sn_field_len != asn1_type.am().ul_am_rlc.sn_field_len) { + asn1::log_warning("NR RLC sequence number length is not the same in uplink and downlink"); + return SRSRAN_ERROR; + } + rlc_cfg.rlc_mode = rlc_mode_t::am; + switch (asn1_type.am().dl_am_rlc.sn_field_len.value) { + case asn1::rrc_nr::sn_field_len_am_opts::options::size12: + rlc_cfg.am_nr.tx_sn_field_length = rlc_am_nr_sn_size_t::size12bits; + rlc_cfg.am_nr.rx_sn_field_length = rlc_am_nr_sn_size_t::size12bits; + break; + case asn1::rrc_nr::sn_field_len_am_opts::options::size18: + rlc_cfg.am_nr.tx_sn_field_length = rlc_am_nr_sn_size_t::size18bits; + rlc_cfg.am_nr.rx_sn_field_length = rlc_am_nr_sn_size_t::size18bits; + break; + default: + break; + } + rlc_cfg.am_nr.t_poll_retx = asn1_type.am().ul_am_rlc.t_poll_retx.to_number(); + rlc_cfg.am_nr.poll_pdu = asn1_type.am().ul_am_rlc.poll_pdu.to_number(); + rlc_cfg.am_nr.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number(); + rlc_cfg.am_nr.max_retx_thresh = asn1_type.am().ul_am_rlc.max_retx_thres.to_number(); + rlc_cfg.am_nr.t_reassembly = asn1_type.am().dl_am_rlc.t_reassembly.to_number(); + rlc_cfg.am_nr.t_status_prohibit = asn1_type.am().dl_am_rlc.t_status_prohibit.to_number(); break; case rlc_cfg_c::types_opts::um_bi_dir: - case rlc_cfg_c::types_opts::um_uni_dir_dl: - case rlc_cfg_c::types_opts::um_uni_dir_ul: + rlc_cfg = rlc_config_t::default_rlc_um_nr_config(); rlc_cfg.rlc_mode = rlc_mode_t::um; - rlc_cfg.um_nr.t_reassembly_ms = asn1_type.um_bi_dir().dl_um_rlc.t_reassembly.value; - rlc_cfg.um_nr.sn_field_length = (rlc_um_nr_sn_size_t)asn1_type.um_bi_dir().dl_um_rlc.sn_field_len.value; - rlc_cfg.um_nr.mod = (rlc_cfg.um_nr.sn_field_length == rlc_um_nr_sn_size_t::size6bits) ? 64 : 4096; - rlc_cfg.um_nr.UM_Window_Size = (rlc_cfg.um_nr.sn_field_length == rlc_um_nr_sn_size_t::size6bits) ? 32 : 2048; + rlc_cfg.um_nr.t_reassembly_ms = asn1_type.um_bi_dir().dl_um_rlc.t_reassembly.to_number(); + rlc_cfg.um_nr.bearer_id = bearer_id; + if (asn1_type.um_bi_dir().dl_um_rlc.sn_field_len_present && + asn1_type.um_bi_dir().ul_um_rlc.sn_field_len_present && + asn1_type.um_bi_dir().dl_um_rlc.sn_field_len != asn1_type.um_bi_dir().ul_um_rlc.sn_field_len) { + asn1::log_warning("NR RLC sequence number length is not the same in uplink and downlink"); + return SRSRAN_ERROR; + } + + switch (asn1_type.um_bi_dir().dl_um_rlc.sn_field_len.value) { + case asn1::rrc_nr::sn_field_len_um_opts::options::size6: + rlc_cfg.um_nr.sn_field_length = rlc_um_nr_sn_size_t::size6bits; + break; + case asn1::rrc_nr::sn_field_len_um_opts::options::size12: + rlc_cfg.um_nr.sn_field_length = rlc_um_nr_sn_size_t::size12bits; + break; + default: + break; + } + rlc_cfg.um_nr.t_reassembly_ms = asn1_type.um_bi_dir().dl_um_rlc.t_reassembly.to_number(); break; + case rlc_cfg_c::types_opts::um_uni_dir_dl: + asn1::log_warning("NR RLC type %s is not supported", asn1_type.type().to_string()); + return SRSRAN_ERROR; + case rlc_cfg_c::types_opts::um_uni_dir_ul: + asn1::log_warning("NR RLC type %s is not supported", asn1_type.type().to_string()); + return SRSRAN_ERROR; default: break; } - return rlc_cfg; + + *cfg_out = rlc_cfg; + return SRSRAN_SUCCESS; +} + +srsran::pdcp_config_t make_nr_srb_pdcp_config_t(const uint8_t bearer_id, bool is_ue) +{ + pdcp_config_t cfg(bearer_id, + PDCP_RB_IS_SRB, + is_ue ? SECURITY_DIRECTION_UPLINK : SECURITY_DIRECTION_DOWNLINK, + is_ue ? SECURITY_DIRECTION_DOWNLINK : SECURITY_DIRECTION_UPLINK, + PDCP_SN_LEN_12, + pdcp_t_reordering_t::ms500, + pdcp_discard_timer_t::infinity, + false, + srsran_rat_t::nr); + return cfg; } srsran::pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue, const pdcp_cfg_s& pdcp_cfg) @@ -190,14 +252,119 @@ srsran::pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue } } - pdcp_t_reordering_t t_reordering = pdcp_t_reordering_t::ms500; + pdcp_t_reordering_t t_reordering = pdcp_t_reordering_t::infinity; if (pdcp_cfg.t_reordering_present) { switch (pdcp_cfg.t_reordering.to_number()) { case 0: t_reordering = pdcp_t_reordering_t::ms0; break; - default: + case 1: + t_reordering = pdcp_t_reordering_t::ms1; + break; + case 2: + t_reordering = pdcp_t_reordering_t::ms2; + break; + case 4: + t_reordering = pdcp_t_reordering_t::ms4; + break; + case 5: + t_reordering = pdcp_t_reordering_t::ms5; + break; + case 8: + t_reordering = pdcp_t_reordering_t::ms8; + break; + case 10: + t_reordering = pdcp_t_reordering_t::ms10; + break; + case 15: + t_reordering = pdcp_t_reordering_t::ms15; + break; + case 20: + t_reordering = pdcp_t_reordering_t::ms20; + break; + case 30: + t_reordering = pdcp_t_reordering_t::ms30; + break; + case 40: + t_reordering = pdcp_t_reordering_t::ms40; + break; + case 50: + t_reordering = pdcp_t_reordering_t::ms50; + break; + case 60: + t_reordering = pdcp_t_reordering_t::ms60; + break; + case 80: + t_reordering = pdcp_t_reordering_t::ms80; + break; + case 100: + t_reordering = pdcp_t_reordering_t::ms100; + break; + case 120: + t_reordering = pdcp_t_reordering_t::ms120; + break; + case 140: + t_reordering = pdcp_t_reordering_t::ms140; + break; + case 160: + t_reordering = pdcp_t_reordering_t::ms160; + break; + case 180: + t_reordering = pdcp_t_reordering_t::ms180; + break; + case 200: + t_reordering = pdcp_t_reordering_t::ms200; + break; + case 220: + t_reordering = pdcp_t_reordering_t::ms220; + break; + case 240: + t_reordering = pdcp_t_reordering_t::ms240; + break; + case 260: + t_reordering = pdcp_t_reordering_t::ms260; + break; + case 280: + t_reordering = pdcp_t_reordering_t::ms280; + break; + case 300: + t_reordering = pdcp_t_reordering_t::ms300; + break; + case 500: t_reordering = pdcp_t_reordering_t::ms500; + break; + case 750: + t_reordering = pdcp_t_reordering_t::ms750; + break; + case 1000: + t_reordering = pdcp_t_reordering_t::ms1000; + break; + case 1250: + t_reordering = pdcp_t_reordering_t::ms1250; + break; + case 1500: + t_reordering = pdcp_t_reordering_t::ms1500; + break; + case 1750: + t_reordering = pdcp_t_reordering_t::ms1750; + break; + case 2000: + t_reordering = pdcp_t_reordering_t::ms2000; + break; + case 2250: + t_reordering = pdcp_t_reordering_t::ms2250; + break; + case 2500: + t_reordering = pdcp_t_reordering_t::ms2500; + break; + case 2750: + t_reordering = pdcp_t_reordering_t::ms2750; + break; + case 3000: + t_reordering = pdcp_t_reordering_t::ms3000; + break; + default: + t_reordering = pdcp_t_reordering_t::ms50; } } @@ -226,14 +393,22 @@ srsran::pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue return cfg; } -bool make_phy_rach_cfg(const rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* prach_cfg) +bool make_phy_rach_cfg(const rach_cfg_common_s& asn1_type, + srsran_duplex_mode_t duplex_mode, + srsran_prach_cfg_t* prach_cfg) { prach_cfg->is_nr = true; prach_cfg->config_idx = asn1_type.rach_cfg_generic.prach_cfg_idx; prach_cfg->zero_corr_zone = (uint32_t)asn1_type.rach_cfg_generic.zero_correlation_zone_cfg; - prach_cfg->num_ra_preambles = 64; // Hard-coded - prach_cfg->hs_flag = false; // Hard-coded - prach_cfg->tdd_config = {}; // Hard-coded + prach_cfg->num_ra_preambles = 64; + if (asn1_type.total_nof_ra_preambs_present) { + prach_cfg->num_ra_preambles = asn1_type.total_nof_ra_preambs; + } + prach_cfg->hs_flag = false; // Hard-coded + prach_cfg->tdd_config = {}; + if (duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + prach_cfg->tdd_config.configured = true; + } // As the current PRACH is based on LTE, the freq-offset shall be subtracted 1 for aligning with NR bandwidth // For example. A 52 PRB cell with an freq_offset of 1 will match a LTE 50 PRB cell with freq_offset of 0 @@ -243,11 +418,12 @@ bool make_phy_rach_cfg(const rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* p return false; } - switch (prach_cfg->root_seq_idx = asn1_type.prach_root_seq_idx.type()) { + switch (asn1_type.prach_root_seq_idx.type().value) { case rach_cfg_common_s::prach_root_seq_idx_c_::types_opts::l839: prach_cfg->root_seq_idx = (uint32_t)asn1_type.prach_root_seq_idx.l839(); break; case rach_cfg_common_s::prach_root_seq_idx_c_::types_opts::l139: + prach_cfg->root_seq_idx = (uint32_t)asn1_type.prach_root_seq_idx.l139(); default: asn1::log_error("Not-implemented option for prach_root_seq_idx type %s", asn1_type.prach_root_seq_idx.type().to_string()); @@ -257,22 +433,58 @@ bool make_phy_rach_cfg(const rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* p return true; }; -bool make_phy_tdd_cfg(const tdd_ul_dl_cfg_common_s& tdd_ul_dl_cfg_common, - srsran_tdd_config_nr_t* in_srsran_tdd_config_nr) +bool fill_rach_cfg_common(const srsran_prach_cfg_t& prach_cfg, asn1::rrc_nr::rach_cfg_common_s& asn1_type) { - srsran_tdd_config_nr_t srsran_tdd_config_nr = {}; + asn1_type = {}; + // rach-ConfigGeneric + asn1_type.rach_cfg_generic.prach_cfg_idx = prach_cfg.config_idx; + asn1_type.rach_cfg_generic.msg1_fdm.value = rach_cfg_generic_s::msg1_fdm_opts::one; + asn1_type.rach_cfg_generic.msg1_freq_start = prach_cfg.freq_offset; + asn1_type.rach_cfg_generic.zero_correlation_zone_cfg = prach_cfg.zero_corr_zone; + asn1_type.rach_cfg_generic.preamb_rx_target_pwr = -110; + asn1_type.rach_cfg_generic.preamb_trans_max.value = rach_cfg_generic_s::preamb_trans_max_opts::n7; + asn1_type.rach_cfg_generic.pwr_ramp_step.value = rach_cfg_generic_s::pwr_ramp_step_opts::db4; + asn1_type.rach_cfg_generic.ra_resp_win.value = rach_cfg_generic_s::ra_resp_win_opts::sl10; + + // totalNumberOfRA-Preambles + if (prach_cfg.num_ra_preambles != 64) { + asn1_type.total_nof_ra_preambs_present = true; + asn1_type.total_nof_ra_preambs = prach_cfg.num_ra_preambles; + } + + // ssb-perRACH-OccasionAndCB-PreamblesPerSSB + asn1_type.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present = true; + if (not asn1::number_to_enum(asn1_type.ssb_per_rach_occasion_and_cb_preambs_per_ssb.set_one(), + prach_cfg.num_ra_preambles)) { + asn1::log_error("Invalid number of RA preambles=%d", prach_cfg.num_ra_preambles); + return false; + } + + asn1_type.ra_contention_resolution_timer.value = rach_cfg_common_s::ra_contention_resolution_timer_opts::sf64; + asn1_type.prach_root_seq_idx.set_l839() = prach_cfg.root_seq_idx; + asn1_type.restricted_set_cfg.value = rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set; + + return true; +} + +bool make_phy_tdd_cfg(const tdd_ul_dl_cfg_common_s& tdd_ul_dl_cfg_common, + srsran_duplex_config_nr_t* in_srsran_duplex_config_nr) +{ + srsran_duplex_config_nr_t srsran_duplex_config_nr = {}; + srsran_duplex_config_nr.mode = SRSRAN_DUPLEX_MODE_TDD; + switch (tdd_ul_dl_cfg_common.pattern1.dl_ul_tx_periodicity) { case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1: - srsran_tdd_config_nr.pattern1.period_ms = 1; + srsran_duplex_config_nr.tdd.pattern1.period_ms = 1; break; case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2: - srsran_tdd_config_nr.pattern1.period_ms = 2; + srsran_duplex_config_nr.tdd.pattern1.period_ms = 2; break; case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms5: - srsran_tdd_config_nr.pattern1.period_ms = 5; + srsran_duplex_config_nr.tdd.pattern1.period_ms = 5; break; case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10: - srsran_tdd_config_nr.pattern1.period_ms = 10; + srsran_duplex_config_nr.tdd.pattern1.period_ms = 10; break; case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1p25: @@ -284,55 +496,115 @@ bool make_phy_tdd_cfg(const tdd_ul_dl_cfg_common_s& tdd_ul_dl_cfg_common, tdd_ul_dl_cfg_common.pattern1.dl_ul_tx_periodicity.to_string()); return false; } - srsran_tdd_config_nr.pattern1.nof_dl_slots = tdd_ul_dl_cfg_common.pattern1.nrof_dl_slots; - srsran_tdd_config_nr.pattern1.nof_dl_symbols = tdd_ul_dl_cfg_common.pattern1.nrof_dl_symbols; - srsran_tdd_config_nr.pattern1.nof_ul_slots = tdd_ul_dl_cfg_common.pattern1.nrof_ul_slots; - srsran_tdd_config_nr.pattern1.nof_ul_symbols = tdd_ul_dl_cfg_common.pattern1.nrof_ul_symbols; - // Copy and return struct - *in_srsran_tdd_config_nr = srsran_tdd_config_nr; + srsran_duplex_config_nr.tdd.pattern1.nof_dl_slots = tdd_ul_dl_cfg_common.pattern1.nrof_dl_slots; + srsran_duplex_config_nr.tdd.pattern1.nof_dl_symbols = tdd_ul_dl_cfg_common.pattern1.nrof_dl_symbols; + srsran_duplex_config_nr.tdd.pattern1.nof_ul_slots = tdd_ul_dl_cfg_common.pattern1.nrof_ul_slots; + srsran_duplex_config_nr.tdd.pattern1.nof_ul_symbols = tdd_ul_dl_cfg_common.pattern1.nrof_ul_symbols; - if (not tdd_ul_dl_cfg_common.pattern2_present) { - return true; + if (tdd_ul_dl_cfg_common.pattern2_present) { + switch (tdd_ul_dl_cfg_common.pattern2.dl_ul_tx_periodicity) { + case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1: + srsran_duplex_config_nr.tdd.pattern2.period_ms = 1; + break; + case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2: + srsran_duplex_config_nr.tdd.pattern2.period_ms = 2; + break; + case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms5: + srsran_duplex_config_nr.tdd.pattern2.period_ms = 5; + break; + case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10: + srsran_duplex_config_nr.tdd.pattern2.period_ms = 10; + break; + + case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1p25: + case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms0p5: + case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms0p625: + case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2p5: + default: + asn1::log_warning("Invalid option for pattern2 dl_ul_tx_periodicity_opts %s", + tdd_ul_dl_cfg_common.pattern2.dl_ul_tx_periodicity.to_string()); + return false; + } + + srsran_duplex_config_nr.tdd.pattern2.nof_dl_slots = tdd_ul_dl_cfg_common.pattern2.nrof_dl_slots; + srsran_duplex_config_nr.tdd.pattern2.nof_dl_symbols = tdd_ul_dl_cfg_common.pattern2.nrof_dl_symbols; + srsran_duplex_config_nr.tdd.pattern2.nof_ul_slots = tdd_ul_dl_cfg_common.pattern2.nrof_ul_slots; + srsran_duplex_config_nr.tdd.pattern2.nof_ul_symbols = tdd_ul_dl_cfg_common.pattern2.nrof_ul_symbols; } - switch (tdd_ul_dl_cfg_common.pattern2.dl_ul_tx_periodicity) { - case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1: - srsran_tdd_config_nr.pattern2.period_ms = 1; - break; - case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2: - srsran_tdd_config_nr.pattern2.period_ms = 2; - break; - case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms5: - srsran_tdd_config_nr.pattern2.period_ms = 5; - break; - case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10: - srsran_tdd_config_nr.pattern2.period_ms = 10; - break; - - case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1p25: - case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms0p5: - case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms0p625: - case tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2p5: - default: - asn1::log_warning("Invalid option for pattern2 dl_ul_tx_periodicity_opts %s", - tdd_ul_dl_cfg_common.pattern2.dl_ul_tx_periodicity.to_string()); - return false; - } - - srsran_tdd_config_nr.pattern2.nof_dl_slots = tdd_ul_dl_cfg_common.pattern2.nrof_dl_slots; - srsran_tdd_config_nr.pattern2.nof_dl_symbols = tdd_ul_dl_cfg_common.pattern2.nrof_dl_symbols; - srsran_tdd_config_nr.pattern2.nof_ul_slots = tdd_ul_dl_cfg_common.pattern2.nrof_ul_slots; - srsran_tdd_config_nr.pattern2.nof_ul_symbols = tdd_ul_dl_cfg_common.pattern2.nrof_ul_symbols; // Copy and return struct - *in_srsran_tdd_config_nr = srsran_tdd_config_nr; + *in_srsran_duplex_config_nr = srsran_duplex_config_nr; return true; } -bool make_phy_harq_ack_cfg(const phys_cell_group_cfg_s& phys_cell_group_cfg, - srsran_ue_dl_nr_harq_ack_cfg_t* in_srsran_ue_dl_nr_harq_ack_cfg) +bool make_phy_tdd_cfg(const srsran_duplex_config_nr_t& srsran_duplex_config_nr, + srsran_subcarrier_spacing_t scs, + asn1::rrc_nr::tdd_ul_dl_cfg_common_s* tdd_ul_dl_cfg_common) { - srsran_ue_dl_nr_harq_ack_cfg_t srsran_ue_dl_nr_harq_ack_cfg = {}; + if (srsran_duplex_config_nr.mode == SRSRAN_DUPLEX_MODE_FDD) { + return true; + } + tdd_ul_dl_cfg_common->ref_subcarrier_spacing.value = (asn1::rrc_nr::subcarrier_spacing_e::options)scs; + + switch (srsran_duplex_config_nr.tdd.pattern1.period_ms) { + case 1: + tdd_ul_dl_cfg_common->pattern1.dl_ul_tx_periodicity = tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1; + break; + case 2: + tdd_ul_dl_cfg_common->pattern1.dl_ul_tx_periodicity = tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2; + break; + case 5: + tdd_ul_dl_cfg_common->pattern1.dl_ul_tx_periodicity = tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms5; + break; + case 10: + tdd_ul_dl_cfg_common->pattern1.dl_ul_tx_periodicity = tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10; + break; + default: + asn1::log_warning("Invalid option for dl_ul_tx_periodicity_opts %d", + srsran_duplex_config_nr.tdd.pattern1.period_ms); + return false; + } + tdd_ul_dl_cfg_common->pattern1.nrof_dl_slots = srsran_duplex_config_nr.tdd.pattern1.nof_dl_slots; + tdd_ul_dl_cfg_common->pattern1.nrof_dl_symbols = srsran_duplex_config_nr.tdd.pattern1.nof_dl_symbols; + tdd_ul_dl_cfg_common->pattern1.nrof_ul_slots = srsran_duplex_config_nr.tdd.pattern1.nof_ul_slots; + tdd_ul_dl_cfg_common->pattern1.nrof_ul_symbols = srsran_duplex_config_nr.tdd.pattern1.nof_ul_symbols; + + if (srsran_duplex_config_nr.tdd.pattern2.period_ms == 0) { + return true; + } + + tdd_ul_dl_cfg_common->pattern2_present = true; + switch (srsran_duplex_config_nr.tdd.pattern2.period_ms) { + case 1: + tdd_ul_dl_cfg_common->pattern2.dl_ul_tx_periodicity.value = tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms1; + break; + case 2: + tdd_ul_dl_cfg_common->pattern2.dl_ul_tx_periodicity.value = tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms2; + break; + case 5: + tdd_ul_dl_cfg_common->pattern2.dl_ul_tx_periodicity.value = tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms5; + break; + case 10: + tdd_ul_dl_cfg_common->pattern2.dl_ul_tx_periodicity.value = tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10; + break; + default: + asn1::log_warning("Invalid option for pattern2 dl_ul_tx_periodicity_opts %d", + srsran_duplex_config_nr.tdd.pattern2.period_ms); + return false; + } + tdd_ul_dl_cfg_common->pattern2.nrof_dl_slots = srsran_duplex_config_nr.tdd.pattern2.nof_dl_slots; + tdd_ul_dl_cfg_common->pattern2.nrof_dl_symbols = srsran_duplex_config_nr.tdd.pattern2.nof_dl_symbols; + tdd_ul_dl_cfg_common->pattern2.nrof_ul_slots = srsran_duplex_config_nr.tdd.pattern2.nof_ul_slots; + tdd_ul_dl_cfg_common->pattern2.nrof_ul_symbols = srsran_duplex_config_nr.tdd.pattern2.nof_ul_symbols; + + return true; +} + +bool make_phy_harq_ack_cfg(const phys_cell_group_cfg_s& phys_cell_group_cfg, + srsran_harq_ack_cfg_hl_t* in_srsran_ue_dl_nr_harq_ack_cfg) +{ + srsran_harq_ack_cfg_hl_t srsran_ue_dl_nr_harq_ack_cfg = {}; switch (phys_cell_group_cfg.pdsch_harq_ack_codebook) { case phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::dynamic_value: srsran_ue_dl_nr_harq_ack_cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; @@ -352,6 +624,21 @@ bool make_phy_harq_ack_cfg(const phys_cell_group_cfg_s& phys_cell_group_cfg, return true; } +void make_phy_search_space0_cfg(srsran_search_space_t* in_srsran_search_space) +{ + in_srsran_search_space->id = 0; + in_srsran_search_space->coreset_id = 0; + in_srsran_search_space->type = srsran_search_space_type_common_0; + in_srsran_search_space->nof_candidates[0] = 0; + in_srsran_search_space->nof_candidates[1] = 0; + in_srsran_search_space->nof_candidates[2] = 4; + in_srsran_search_space->nof_candidates[3] = 2; + in_srsran_search_space->nof_candidates[4] = 0; + in_srsran_search_space->nof_formats = 1; + in_srsran_search_space->formats[0] = srsran_dci_format_nr_1_0; + in_srsran_search_space->duration = 1; +} + bool make_phy_search_space_cfg(const search_space_s& search_space, srsran_search_space_t* in_srsran_search_space) { srsran_search_space_t srsran_search_space = {}; @@ -362,15 +649,20 @@ bool make_phy_search_space_cfg(const search_space_s& search_space, srsran_search } srsran_search_space.coreset_id = search_space.ctrl_res_set_id; + srsran_search_space.duration = 1; + if (search_space.dur_present) { + srsran_search_space.duration = search_space.dur; + } + if (not search_space.nrof_candidates_present) { asn1::log_warning("nrof_candidates_present option not present"); return false; } - srsran_search_space.nof_candidates[0] = search_space.nrof_candidates.aggregation_level1.value; - srsran_search_space.nof_candidates[1] = search_space.nrof_candidates.aggregation_level2.value; - srsran_search_space.nof_candidates[2] = search_space.nrof_candidates.aggregation_level4.value; - srsran_search_space.nof_candidates[3] = search_space.nrof_candidates.aggregation_level8.value; - srsran_search_space.nof_candidates[4] = search_space.nrof_candidates.aggregation_level16.value; + srsran_search_space.nof_candidates[0] = search_space.nrof_candidates.aggregation_level1.to_number(); + srsran_search_space.nof_candidates[1] = search_space.nrof_candidates.aggregation_level2.to_number(); + srsran_search_space.nof_candidates[2] = search_space.nrof_candidates.aggregation_level4.to_number(); + srsran_search_space.nof_candidates[3] = search_space.nrof_candidates.aggregation_level8.to_number(); + srsran_search_space.nof_candidates[4] = search_space.nrof_candidates.aggregation_level16.to_number(); if (not search_space.search_space_type_present) { asn1::log_warning("nrof_candidates option not present"); @@ -460,42 +752,41 @@ bool make_phy_csi_report(const csi_report_cfg_s& csi_report_cfg, } if (srsran_csi_hl_report_cfg.type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { - srsran_csi_hl_report_cfg.periodic.period = - csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type().to_number(); - switch (csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type()) { + const auto& csi_periodic = csi_report_cfg.report_cfg_type.periodic(); + srsran_csi_hl_report_cfg.periodic.period = csi_periodic.report_slot_cfg.type().to_number(); + switch (csi_periodic.report_slot_cfg.type()) { case csi_report_periodicity_and_offset_c::types_opts::slots4: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots4(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots4(); break; case csi_report_periodicity_and_offset_c::types_opts::slots5: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots5(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots5(); break; case csi_report_periodicity_and_offset_c::types_opts::slots8: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots8(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots8(); break; case csi_report_periodicity_and_offset_c::types_opts::slots10: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots10(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots10(); break; case csi_report_periodicity_and_offset_c::types_opts::slots16: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots16(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots16(); break; case csi_report_periodicity_and_offset_c::types_opts::slots20: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots20(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots20(); break; case csi_report_periodicity_and_offset_c::types_opts::slots40: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots40(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots40(); break; case csi_report_periodicity_and_offset_c::types_opts::slots80: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots80(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots80(); break; case csi_report_periodicity_and_offset_c::types_opts::slots160: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots160(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots160(); break; case csi_report_periodicity_and_offset_c::types_opts::slots320: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots320(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots320(); break; default: - asn1::log_warning("Invalid option for report_slot_cfg %s", - csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type().to_string()); + asn1::log_warning("Invalid option for report_slot_cfg %s", csi_periodic.report_slot_cfg.type().to_string()); return false; } } @@ -579,6 +870,7 @@ bool make_phy_csi_report(const csi_report_cfg_s& csi_report_cfg, asn1::log_warning("Invalid option for cqi_table %s", csi_report_cfg.cqi_table.to_string()); return false; } + *in_srsran_csi_hl_report_cfg = srsran_csi_hl_report_cfg; return true; } @@ -685,6 +977,10 @@ bool make_phy_res_config(const pucch_res_s& pucch_res, { srsran_pucch_nr_resource_t srsran_pucch_nr_resource = {}; srsran_pucch_nr_resource.starting_prb = pucch_res.start_prb; + srsran_pucch_nr_resource.intra_slot_hopping = pucch_res.intra_slot_freq_hop_present; + if (pucch_res.second_hop_prb_present) { + srsran_pucch_nr_resource.second_hop_prb = pucch_res.second_hop_prb; + } switch (pucch_res.format.type()) { case pucch_res_s::format_c_::types_opts::format0: srsran_pucch_nr_resource.format = SRSRAN_PUCCH_NR_FORMAT_0; @@ -719,6 +1015,42 @@ bool make_phy_res_config(const pucch_res_s& pucch_res, return true; } +bool make_phy_res_config(const srsran_pucch_nr_resource_t& in_pucch_res, + asn1::rrc_nr::pucch_res_s& out_pucch_res, + uint32_t pucch_res_id) +{ + out_pucch_res.pucch_res_id = pucch_res_id; + out_pucch_res.start_prb = in_pucch_res.starting_prb; + + switch (in_pucch_res.format) { + case SRSRAN_PUCCH_NR_FORMAT_0: + asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_0 conversion not supported"); + return false; + case SRSRAN_PUCCH_NR_FORMAT_1: + out_pucch_res.format.set_format1(); + out_pucch_res.format.format1().init_cyclic_shift = in_pucch_res.initial_cyclic_shift; + out_pucch_res.format.format1().nrof_symbols = in_pucch_res.nof_symbols; + out_pucch_res.format.format1().start_symbol_idx = in_pucch_res.start_symbol_idx; + out_pucch_res.format.format1().time_domain_occ = in_pucch_res.time_domain_occ; + return true; + case SRSRAN_PUCCH_NR_FORMAT_2: + out_pucch_res.format.set_format2(); + out_pucch_res.format.format2().nrof_symbols = in_pucch_res.nof_symbols; + out_pucch_res.format.format2().start_symbol_idx = in_pucch_res.start_symbol_idx; + out_pucch_res.format.format2().nrof_prbs = in_pucch_res.nof_prb; + return true; + case SRSRAN_PUCCH_NR_FORMAT_3: + asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_3 conversion not supported"); + return true; + case SRSRAN_PUCCH_NR_FORMAT_4: + asn1::log_warning("SRSRAN_PUCCH_NR_FORMAT_4 conversion not supported"); + return false; + default: + asn1::log_warning("Invalid NR PUCCH format"); + } + return false; +} + bool make_phy_sr_resource(const sched_request_res_cfg_s& sched_request_res_cfg, srsran_pucch_nr_sr_resource_t* in_srsran_pucch_nr_sr_resource) { @@ -936,8 +1268,8 @@ bool make_phy_pusch_scaling(const uci_on_pusch_s& uci_on_pusch, float* in_scalin bool make_phy_zp_csi_rs_resource(const asn1::rrc_nr::zp_csi_rs_res_s& zp_csi_rs_res, srsran_csi_rs_zp_resource_t* out_zp_csi_rs_resource) { - srsran_csi_rs_zp_resource_t zp_csi_rs_resource; - zp_csi_rs_resource.id = zp_csi_rs_res.zp_csi_rs_res_id; + srsran_csi_rs_zp_resource_t zp_csi_rs_resource = {}; + zp_csi_rs_resource.id = zp_csi_rs_res.zp_csi_rs_res_id; switch (zp_csi_rs_res.res_map.freq_domain_alloc.type()) { case csi_rs_res_map_s::freq_domain_alloc_c_::types_opts::options::row1: zp_csi_rs_resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; @@ -1021,6 +1353,15 @@ bool make_phy_zp_csi_rs_resource(const asn1::rrc_nr::zp_csi_rs_res_s& zp_csi_rs_ } zp_csi_rs_resource.resource_mapping.freq_band.nof_rb = zp_csi_rs_res.res_map.freq_band.nrof_rbs; zp_csi_rs_resource.resource_mapping.freq_band.start_rb = zp_csi_rs_res.res_map.freq_band.start_rb; + + // Validate CSI-RS resource mapping + if (not srsran_csi_rs_resource_mapping_is_valid(&zp_csi_rs_resource.resource_mapping)) { + asn1::json_writer json_writer; + zp_csi_rs_res.res_map.to_json(json_writer); + asn1::log_error("Resource mapping is invalid or not implemented: %s", json_writer.to_string()); + return false; + } + if (zp_csi_rs_res.periodicity_and_offset_present) { switch (zp_csi_rs_res.periodicity_and_offset.type()) { case csi_res_periodicity_and_offset_c::types_opts::options::slots4: @@ -1092,8 +1433,8 @@ bool make_phy_zp_csi_rs_resource(const asn1::rrc_nr::zp_csi_rs_res_s& zp_csi_rs_ bool make_phy_nzp_csi_rs_resource(const asn1::rrc_nr::nzp_csi_rs_res_s& asn1_nzp_csi_rs_res, srsran_csi_rs_nzp_resource_t* out_csi_rs_nzp_resource) { - srsran_csi_rs_nzp_resource_t csi_rs_nzp_resource; - csi_rs_nzp_resource.id = asn1_nzp_csi_rs_res.nzp_csi_rs_res_id; + srsran_csi_rs_nzp_resource_t csi_rs_nzp_resource = {}; + csi_rs_nzp_resource.id = asn1_nzp_csi_rs_res.nzp_csi_rs_res_id; switch (asn1_nzp_csi_rs_res.res_map.freq_domain_alloc.type()) { case csi_rs_res_map_s::freq_domain_alloc_c_::types_opts::options::row1: csi_rs_nzp_resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; @@ -1179,6 +1520,14 @@ bool make_phy_nzp_csi_rs_resource(const asn1::rrc_nr::nzp_csi_rs_res_s& asn1_nzp csi_rs_nzp_resource.resource_mapping.freq_band.nof_rb = asn1_nzp_csi_rs_res.res_map.freq_band.nrof_rbs; csi_rs_nzp_resource.resource_mapping.freq_band.start_rb = asn1_nzp_csi_rs_res.res_map.freq_band.start_rb; + // Validate CSI-RS resource mapping + if (not srsran_csi_rs_resource_mapping_is_valid(&csi_rs_nzp_resource.resource_mapping)) { + asn1::json_writer json_writer; + asn1_nzp_csi_rs_res.res_map.to_json(json_writer); + asn1::log_error("Resource mapping is invalid or not implemented: %s", json_writer.to_string()); + return false; + } + csi_rs_nzp_resource.power_control_offset = asn1_nzp_csi_rs_res.pwr_ctrl_offset; if (asn1_nzp_csi_rs_res.pwr_ctrl_offset_ss_present) { csi_rs_nzp_resource.power_control_offset_ss = asn1_nzp_csi_rs_res.pwr_ctrl_offset_ss.to_number(); @@ -1254,11 +1603,36 @@ bool make_phy_nzp_csi_rs_resource(const asn1::rrc_nr::nzp_csi_rs_res_s& asn1_nzp return true; } +static inline srsran_subcarrier_spacing_t make_subcarrier_spacing(const subcarrier_spacing_e& asn1_scs) +{ + switch (asn1_scs) { + case subcarrier_spacing_opts::options::khz15: + return srsran_subcarrier_spacing_15kHz; + case subcarrier_spacing_opts::options::khz30: + return srsran_subcarrier_spacing_30kHz; + case subcarrier_spacing_opts::options::khz60: + return srsran_subcarrier_spacing_60kHz; + case subcarrier_spacing_opts::options::khz120: + return srsran_subcarrier_spacing_120kHz; + case subcarrier_spacing_opts::options::khz240: + return srsran_subcarrier_spacing_240kHz; + case subcarrier_spacing_opts::spare3: + case subcarrier_spacing_opts::spare2: + case subcarrier_spacing_opts::spare1: + case subcarrier_spacing_opts::nulltype: + default: + asn1::log_warning("Not supported subcarrier spacing "); + break; + } + + return srsran_subcarrier_spacing_invalid; +} + bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrier_nr_t* out_carrier_nr) { uint32_t absolute_frequency_ssb = 0; if (asn1_freq_info_dl.absolute_freq_ssb_present) { - absolute_frequency_ssb = asn1_freq_info_dl.absolute_freq_ssb_present; + absolute_frequency_ssb = asn1_freq_info_dl.absolute_freq_ssb; } else { asn1::log_warning("Option absolute_freq_ssb not present"); return false; @@ -1268,61 +1642,543 @@ bool make_phy_carrier_cfg(const freq_info_dl_s& asn1_freq_info_dl, srsran_carrie return false; } - srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz; - switch (asn1_freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing) { - case subcarrier_spacing_opts::options::khz15: - scs = srsran_subcarrier_spacing_15kHz; - break; - case subcarrier_spacing_opts::options::khz30: - scs = srsran_subcarrier_spacing_30kHz; - break; - case subcarrier_spacing_opts::options::khz60: - scs = srsran_subcarrier_spacing_60kHz; - break; - case subcarrier_spacing_opts::options::khz120: - scs = srsran_subcarrier_spacing_120kHz; - break; - case subcarrier_spacing_opts::options::khz240: - scs = srsran_subcarrier_spacing_240kHz; - break; - default: - asn1::log_warning("Not supported subcarrier spacing "); + srsran_subcarrier_spacing_t scs = + make_subcarrier_spacing(asn1_freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing); + if (scs == srsran_subcarrier_spacing_invalid) { + return false; } + // As the carrier structure requires parameters from different objects, set fields separately - out_carrier_nr->absolute_frequency_ssb = absolute_frequency_ssb; - out_carrier_nr->absolute_frequency_point_a = asn1_freq_info_dl.absolute_freq_point_a; - out_carrier_nr->offset_to_carrier = asn1_freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; - out_carrier_nr->nof_prb = asn1_freq_info_dl.scs_specific_carrier_list[0].carrier_bw; - out_carrier_nr->scs = scs; + srsran::srsran_band_helper bands; + out_carrier_nr->ssb_center_freq_hz = bands.nr_arfcn_to_freq(absolute_frequency_ssb); + out_carrier_nr->dl_center_frequency_hz = bands.get_center_freq_from_abs_freq_point_a( + asn1_freq_info_dl.scs_specific_carrier_list[0].carrier_bw, asn1_freq_info_dl.absolute_freq_point_a); + out_carrier_nr->ul_center_frequency_hz = out_carrier_nr->dl_center_frequency_hz; // needs to be updated for FDD + out_carrier_nr->offset_to_carrier = asn1_freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; + out_carrier_nr->nof_prb = asn1_freq_info_dl.scs_specific_carrier_list[0].carrier_bw; + out_carrier_nr->scs = scs; return true; } -} // namespace srsran -namespace srsenb { - -int set_sched_cell_cfg_sib1(srsenb::sched_interface::cell_cfg_t* sched_cfg, const asn1::rrc_nr::sib1_s& sib1) +template +static inline void make_ssb_positions_in_burst(const bitstring_t& ans1_position_in_burst, + std::array& position_in_burst) { - bzero(sched_cfg, sizeof(srsenb::sched_interface::cell_cfg_t)); - - // set SIB1 and SIB2+ period - sched_cfg->sibs[0].period_rf = 16; // SIB1 is always 16 rf - for (uint32_t i = 0; i < sib1.si_sched_info.sched_info_list.size(); i++) { - sched_cfg->sibs[i + 1].period_rf = sib1.si_sched_info.sched_info_list[i].si_periodicity.to_number(); + for (uint32_t i = 0; i < SRSRAN_SSB_NOF_CANDIDATES; i++) { + if (i < ans1_position_in_burst.length()) { + position_in_burst[i] = ans1_position_in_burst.get(ans1_position_in_burst.length() - 1 - i); + } else { + position_in_burst[i] = false; + } } - - // si-WindowLength - sched_cfg->si_window_ms = sib1.si_sched_info.si_win_len.to_number(); - - // setup PRACH - if (not sib1.si_sched_info.si_request_cfg.rach_occasions_si_present) { - asn1::log_warning("rach_occasions_si option not present"); - return SRSRAN_ERROR; - } - sched_cfg->prach_rar_window = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.ra_resp_win.to_number(); - sched_cfg->prach_freq_offset = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.msg1_freq_start; - sched_cfg->maxharq_msg3tx = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.preamb_trans_max; - - return SRSRAN_SUCCESS; } -} // namespace srsenb +void fill_ssb_pos_in_burst(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& cfg, phy_cfg_nr_t::ssb_cfg_t* out_ssb) +{ + auto& ssb_pos = cfg.ssb_positions_in_burst; + + out_ssb->position_in_burst = {}; + uint32_t N = ssb_pos.in_one_group.length(); + for (uint32_t i = 0; i < N; ++i) { + out_ssb->position_in_burst[i] = ssb_pos.in_one_group.get(i); + } + if (ssb_pos.group_presence_present) { + for (uint32_t i = 1; i < ssb_pos.group_presence.length(); ++i) { + if (ssb_pos.group_presence.get(i)) { + std::copy(out_ssb->position_in_burst.begin(), + out_ssb->position_in_burst.begin() + N, + out_ssb->position_in_burst.begin() + i * N); + } + } + } +} + +bool fill_ssb_pattern_scs(const srsran_carrier_nr_t& carrier, + srsran_ssb_pattern_t* pattern, + srsran_subcarrier_spacing_t* ssb_scs) +{ + srsran::srsran_band_helper bands; + uint16_t band = bands.get_band_from_dl_freq_Hz(carrier.ssb_center_freq_hz); + if (band == UINT16_MAX) { + asn1::log_error("Invalid band for SSB frequency %.3f MHz", carrier.ssb_center_freq_hz); + return false; + } + + // TODO: Generalize conversion for other SCS + *pattern = bands.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz); + if (*pattern == SRSRAN_SSB_PATTERN_A) { + *ssb_scs = carrier.scs; + } else { + // try to optain SSB pattern for same band with 30kHz SCS + *pattern = bands.get_ssb_pattern(band, srsran_subcarrier_spacing_30kHz); + if (*pattern == SRSRAN_SSB_PATTERN_B || *pattern == SRSRAN_SSB_PATTERN_C) { + // SSB SCS is 30 kHz + *ssb_scs = srsran_subcarrier_spacing_30kHz; + } else { + asn1::log_error("Can't derive SSB pattern from band %d", band); + return false; + } + } + return true; +} + +bool fill_phy_ssb_cfg(const srsran_carrier_nr_t& carrier, + const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + srsran_ssb_cfg_t* out_ssb) +{ + *out_ssb = {}; + + out_ssb->center_freq_hz = carrier.dl_center_frequency_hz; + out_ssb->ssb_freq_hz = carrier.ssb_center_freq_hz; + if (not fill_ssb_pattern_scs(carrier, &out_ssb->pattern, &out_ssb->scs)) { + return false; + } + + out_ssb->duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + if (serv_cell_cfg.tdd_ul_dl_cfg_common_present) { + out_ssb->duplex_mode = SRSRAN_DUPLEX_MODE_TDD; + } + + out_ssb->periodicity_ms = serv_cell_cfg.ssb_periodicity_serving_cell.to_number(); + return true; +} + +// SA case +bool fill_phy_ssb_cfg(const srsran_carrier_nr_t& carrier, + const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + phy_cfg_nr_t::ssb_cfg_t* out_ssb) +{ + // Derive SSB pattern and SCS + if (not fill_ssb_pattern_scs(carrier, &out_ssb->pattern, &out_ssb->scs)) { + return false; + } + fill_ssb_pos_in_burst(serv_cell_cfg, out_ssb); + out_ssb->periodicity_ms = (uint32_t)serv_cell_cfg.ssb_periodicity_serving_cell.to_number(); + + return true; +} + +bool make_phy_ssb_cfg(const srsran_carrier_nr_t& carrier, + const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg, + phy_cfg_nr_t::ssb_cfg_t* out_ssb) +{ + srsran::srsran_band_helper bands; + uint16_t band = bands.get_band_from_dl_freq_Hz(carrier.ssb_center_freq_hz); + if (band == UINT16_MAX) { + asn1::log_error("Invalid band for SSB frequency %.3f MHz", carrier.ssb_center_freq_hz); + return false; + } + + phy_cfg_nr_t::ssb_cfg_t ssb = {}; + + // Parse subcarrier spacing + if (serv_cell_cfg.ssb_subcarrier_spacing_present) { + switch (serv_cell_cfg.ssb_subcarrier_spacing) { + case subcarrier_spacing_e::khz15: + ssb.scs = srsran_subcarrier_spacing_15kHz; + break; + case subcarrier_spacing_e::khz30: + ssb.scs = srsran_subcarrier_spacing_30kHz; + break; + default: + asn1::log_error("SSB SCS %s not supported", serv_cell_cfg.ssb_subcarrier_spacing.to_string()); + return false; + } + } else { + ssb.scs = bands.get_ssb_scs(band); + if (ssb.scs == srsran_subcarrier_spacing_invalid) { + asn1::log_error("SSB SCS not available for band %d", band); + return false; + } + } + + // Get the SSB pattern + ssb.pattern = bands.get_ssb_pattern(band, ssb.scs); + if (ssb.pattern == SRSRAN_SSB_PATTERN_INVALID) { + asn1::log_error( + "Band %d and SS/PBCH block SCS %s results on invalid pattern", band, srsran_subcarrier_spacing_to_str(ssb.scs)); + return false; + } + + if (serv_cell_cfg.ssb_positions_in_burst_present) { + switch (serv_cell_cfg.ssb_positions_in_burst.type()) { + case serving_cell_cfg_common_s::ssb_positions_in_burst_c_::types_opts::short_bitmap: + make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.short_bitmap(), ssb.position_in_burst); + break; + case serving_cell_cfg_common_s::ssb_positions_in_burst_c_::types_opts::medium_bitmap: + make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.medium_bitmap(), ssb.position_in_burst); + break; + case serving_cell_cfg_common_s::ssb_positions_in_burst_c_::types_opts::long_bitmap: + make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.long_bitmap(), ssb.position_in_burst); + break; + case serving_cell_cfg_common_s::ssb_positions_in_burst_c_::types_opts::nulltype: + asn1::log_warning("SSB position in burst nulltype"); + return false; + } + } else { + asn1::log_warning("SSB position in burst not present"); + return false; + } + if (serv_cell_cfg.ssb_periodicity_serving_cell_present) { + ssb.periodicity_ms = (uint32_t)serv_cell_cfg.ssb_periodicity_serving_cell.to_number(); + } else { + asn1::log_warning("SSB periodicity not present"); + return false; + } + if (serv_cell_cfg.ssb_subcarrier_spacing_present) { + ssb.scs = make_subcarrier_spacing(serv_cell_cfg.ssb_subcarrier_spacing); + if (ssb.scs == srsran_subcarrier_spacing_invalid) { + return false; + } + } else { + asn1::log_warning("SSB subcarrier spacing not present"); + return false; + } + if (out_ssb != nullptr) { + *out_ssb = ssb; + } + return true; +} + +bool make_phy_mib(const asn1::rrc_nr::mib_s& mib_cfg, srsran_mib_nr_t* mib) +{ + mib->sfn = 0; + mib->ssb_idx = 0; + mib->hrf = false; + mib->scs_common = + mib_cfg.sub_carrier_spacing_common.value == asn1::rrc_nr::mib_s::sub_carrier_spacing_common_opts::scs15or60 + ? srsran_subcarrier_spacing_15kHz + : srsran_subcarrier_spacing_30kHz; + mib->ssb_offset = mib_cfg.ssb_subcarrier_offset; + mib->dmrs_typeA_pos = (srsran_dmrs_sch_typeA_pos_t)mib_cfg.dmrs_type_a_position.value; + mib->coreset0_idx = mib_cfg.pdcch_cfg_sib1.ctrl_res_set_zero; + mib->ss0_idx = mib_cfg.pdcch_cfg_sib1.search_space_zero; + mib->cell_barred = mib_cfg.cell_barred.value == asn1::rrc_nr::mib_s::cell_barred_opts::barred; + mib->intra_freq_reselection = mib_cfg.intra_freq_resel.value == asn1::rrc_nr::mib_s::intra_freq_resel_opts::allowed; + return true; +} + +bool make_pdsch_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_sch_hl_cfg_nr_t* sch_hl) +{ + if (serv_cell.csi_meas_cfg_present and serv_cell.csi_meas_cfg.is_setup()) { + auto& setup = serv_cell.csi_meas_cfg.setup(); + + // Configure NZP-CSI + for (auto& nzp_set : setup.nzp_csi_rs_res_set_to_add_mod_list) { + auto& uecfg_set = sch_hl->nzp_csi_rs_sets[nzp_set.nzp_csi_res_set_id]; + uecfg_set.trs_info = nzp_set.trs_info_present; + uecfg_set.count = nzp_set.nzp_csi_rs_res.size(); + uint32_t count = 0; + for (uint8_t nzp_rs_idx : nzp_set.nzp_csi_rs_res) { + auto& res = uecfg_set.data[count++]; + if (not srsran::make_phy_nzp_csi_rs_resource(setup.nzp_csi_rs_res_to_add_mod_list[nzp_rs_idx], &res)) { + return false; + } + } + } + } + + if (serv_cell.init_dl_bwp.pdsch_cfg_present and serv_cell.init_dl_bwp.pdsch_cfg.is_setup()) { + const auto& setup = serv_cell.init_dl_bwp.pdsch_cfg.setup(); + if (setup.p_zp_csi_rs_res_set_present) { + auto& setup_set = setup.p_zp_csi_rs_res_set.setup(); + sch_hl->p_zp_csi_rs_set.count = setup_set.zp_csi_rs_res_id_list.size(); + for (uint8_t zp_res_id : setup_set.zp_csi_rs_res_id_list) { + const asn1::rrc_nr::zp_csi_rs_res_s& setup_res = setup.zp_csi_rs_res_to_add_mod_list[zp_res_id]; + auto& res = sch_hl->p_zp_csi_rs_set.data[zp_res_id]; + if (not srsran::make_phy_zp_csi_rs_resource(setup_res, &res)) { + return false; + } + } + } + } + + return true; +} + +bool make_csi_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_csi_hl_cfg_t* csi_hl) +{ + if (serv_cell.csi_meas_cfg_present and serv_cell.csi_meas_cfg.is_setup()) { + auto& setup = serv_cell.csi_meas_cfg.setup(); + + // Configure CSI-Report + for (uint32_t i = 0; i < setup.csi_report_cfg_to_add_mod_list.size(); ++i) { + const auto& csi_rep = setup.csi_report_cfg_to_add_mod_list[i]; + if (not make_phy_csi_report(csi_rep, &csi_hl->reports[i])) { + return false; + } + if (csi_rep.report_cfg_type.type().value == csi_report_cfg_s::report_cfg_type_c_::types_opts::periodic) { + const auto& pucch_setup = serv_cell.ul_cfg.init_ul_bwp.pucch_cfg.setup(); + srsran_pucch_nr_resource_t& resource = csi_hl->reports[i].periodic.resource; + uint32_t pucch_resource_id = csi_rep.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res; + const auto& asn1_resource = pucch_setup.res_to_add_mod_list[pucch_resource_id]; + uint32_t format2_rate = 0; + if (pucch_setup.format2_present and + pucch_setup.format2.type().value == asn1::setup_release_c::types_opts::setup and + pucch_setup.format2.setup().max_code_rate_present) { + format2_rate = pucch_setup.format2.setup().max_code_rate.to_number(); + } + if (not make_phy_res_config(asn1_resource, format2_rate, &resource)) { + return false; + } + } + } + } + + return true; +} + +bool make_duplex_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell, + srsran_duplex_config_nr_t* duplex_cfg) +{ + duplex_cfg->mode = serv_cell.tdd_ul_dl_cfg_common_present ? SRSRAN_DUPLEX_MODE_TDD : SRSRAN_DUPLEX_MODE_FDD; + if (serv_cell.tdd_ul_dl_cfg_common_present) { + if (not make_phy_tdd_cfg(serv_cell.tdd_ul_dl_cfg_common, duplex_cfg)) { + return false; + } + } + return true; +} + +bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch) +{ + for (const ctrl_res_set_s& coreset : pdcch_cfg.ctrl_res_set_to_add_mod_list) { + pdcch->coreset_present[coreset.ctrl_res_set_id] = true; + make_phy_coreset_cfg(coreset, &pdcch->coreset[coreset.ctrl_res_set_id]); + } + + for (const search_space_s& ss : pdcch_cfg.search_spaces_to_add_mod_list) { + pdcch->search_space_present[ss.search_space_id] = true; + make_phy_search_space_cfg(ss, &pdcch->search_space[ss.search_space_id]); + } + return true; +} + +bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch) +{ + if (pdcch_cfg.common_ctrl_res_set_present) { + pdcch->coreset_present[pdcch_cfg.common_ctrl_res_set.ctrl_res_set_id] = true; + make_phy_coreset_cfg(pdcch_cfg.common_ctrl_res_set, &pdcch->coreset[pdcch_cfg.common_ctrl_res_set.ctrl_res_set_id]); + } + for (const search_space_s& ss : pdcch_cfg.common_search_space_list) { + pdcch->search_space_present[ss.search_space_id] = true; + if (not make_phy_search_space_cfg(ss, &pdcch->search_space[ss.search_space_id])) { + asn1::log_error("Failed to convert SearchSpace Configuration"); + return false; + } + if (pdcch_cfg.ra_search_space_present and pdcch_cfg.ra_search_space == ss.search_space_id) { + pdcch->ra_search_space_present = true; + pdcch->ra_search_space = pdcch->search_space[ss.search_space_id]; + pdcch->ra_search_space.type = srsran_search_space_type_common_1; + pdcch->ra_search_space.nof_formats = 1; + pdcch->ra_search_space.formats[0] = srsran_dci_format_nr_1_0; + } + } + return true; +} + +void fill_phy_pucch_cfg_common(const asn1::rrc_nr::pucch_cfg_common_s& pucch_cfg, srsran_pucch_nr_common_cfg_t* pucch) +{ + if (pucch_cfg.pucch_res_common_present) { + pucch->resource_common = pucch_cfg.pucch_res_common; + } + if (pucch_cfg.hop_id_present) { + pucch->hopping_id_present = true; + pucch->hopping_id = pucch_cfg.hop_id; + } + if (pucch_cfg.p0_nominal_present) { + pucch->p0_nominal = pucch_cfg.p0_nominal; + } + + switch (pucch_cfg.pucch_group_hop) { + case pucch_cfg_common_s::pucch_group_hop_opts::enable: + pucch->group_hopping = SRSRAN_PUCCH_NR_GROUP_HOPPING_ENABLE; + break; + case pucch_cfg_common_s::pucch_group_hop_opts::disable: + pucch->group_hopping = SRSRAN_PUCCH_NR_GROUP_HOPPING_DISABLE; + break; + default: + pucch->group_hopping = SRSRAN_PUCCH_NR_GROUP_HOPPING_NEITHER; + break; + } +} + +bool fill_phy_pucch_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch_nr_hl_cfg_t* pucch) +{ + // sanity check to avoid pucch->sets[n] goes out of bound + if (pucch_cfg.res_set_to_add_mod_list.size() > SRSRAN_PUCCH_NR_MAX_NOF_SETS) { + return false; + } + + // iterate over the sets of resourceSetToAddModList + for (size_t n = 0; n < pucch_cfg.res_set_to_add_mod_list.size(); n++) { + auto& res_set = pucch_cfg.res_set_to_add_mod_list[n]; + pucch->sets[n].nof_resources = res_set.res_list.size(); + if (res_set.max_payload_size_present) { + pucch->sets[n].max_payload_size = res_set.max_payload_size; + } + // NOTE: res_set.pucch_res_set_id does not have a corresponding field in the PHY struct + + // for each set, iterate over the elements (an element is an index). For each of the element or index, find the + // corresponding pucch_res_s object in the pucch_cfg.res_to_add_mod_list + for (size_t res_idx = 0; res_idx < res_set.res_list.size(); res_idx++) { + size_t pucch_resource_id = res_set.res_list[res_idx]; + + // Find the pucch_res_s object corresponding to pucch_resource_id in the pucch_cfg.res_to_add_mod_list + size_t m = 0; + while (m <= pucch_cfg.res_to_add_mod_list.size()) { + if (m == pucch_cfg.res_to_add_mod_list.size()) { + // if we get here, the list pucch_cfg.res_to_add_mod_list does not contain any object corresponding to + // pucch_resource_id + return false; + } + if (pucch_cfg.res_to_add_mod_list[m].pucch_res_id == pucch_resource_id) { + break; // item found, exit the loop + } + m++; + } + + // Below is the object corresponding to pucch_resource_id in the pucch_cfg.res_to_add_mod_list + const auto& asn1_resource = pucch_cfg.res_to_add_mod_list[m]; + + // sanity check to avoid pucch->sets[n].resources[res_idx] goes out of bound; + if (res_idx >= SRSRAN_PUCCH_NR_MAX_NOF_RESOURCES_PER_SET) { + return false; + } + + auto& resource = pucch->sets[n].resources[res_idx]; + uint32_t format2_rate = 0; + if (pucch_cfg.format2_present and + pucch_cfg.format2.type().value == asn1::setup_release_c::types_opts::setup and + pucch_cfg.format2.setup().max_code_rate_present) { + format2_rate = pucch_cfg.format2.setup().max_code_rate.to_number(); + } + if (not make_phy_res_config(asn1_resource, format2_rate, &resource)) { + return false; + } + } + } + + // configure scheduling request resources + return fill_phy_pucch_hl_cfg(pucch_cfg, pucch); +} + +bool fill_phy_pucch_hl_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch_nr_hl_cfg_t* pucch) +{ + for (size_t n = 0; n < pucch_cfg.sched_request_res_to_add_mod_list.size(); n++) { + // fill each sr_resource's cnf + auto& asn_sr_res = pucch_cfg.sched_request_res_to_add_mod_list[n]; + srsran_pucch_nr_sr_resource_t* sr_res = &pucch->sr_resources[asn_sr_res.sched_request_res_id]; + make_phy_sr_resource(asn_sr_res, sr_res); + + // get the pucch_resource from pucch_cfg.res_to_add_mod_list and copy it into the sr_resouce.resource + const auto& asn1_pucch_resource = pucch_cfg.res_to_add_mod_list[asn_sr_res.res]; + auto& pucch_resource = sr_res->resource; + uint32_t format2_rate = 0; + if (pucch_cfg.format2_present and + pucch_cfg.format2.type().value == asn1::setup_release_c::types_opts::setup and + pucch_cfg.format2.setup().max_code_rate_present) { + format2_rate = pucch_cfg.format2.setup().max_code_rate.to_number(); + } + if (not make_phy_res_config(asn1_pucch_resource, format2_rate, &pucch_resource)) { + return false; + } + } + + return true; +} + +bool fill_phy_pdsch_cfg_common(const asn1::rrc_nr::pdsch_cfg_common_s& pdsch_cfg, srsran_sch_hl_cfg_nr_t* pdsch) +{ + for (uint32_t i = 0; i < pdsch_cfg.pdsch_time_domain_alloc_list.size(); i++) { + srsran_sch_time_ra_t common_time_ra; + if (make_phy_common_time_ra(pdsch_cfg.pdsch_time_domain_alloc_list[i], &common_time_ra) == true) { + pdsch->common_time_ra[i] = common_time_ra; + pdsch->nof_common_time_ra = i + 1; + } else { + asn1::log_warning("Warning while building common_time_ra structure"); + return false; + } + } + return true; +} + +bool fill_phy_pusch_cfg_common(const asn1::rrc_nr::pusch_cfg_common_s& pusch_cfg, srsran_sch_hl_cfg_nr_t* pusch) +{ + for (uint32_t i = 0; i < pusch_cfg.pusch_time_domain_alloc_list.size(); i++) { + srsran_sch_time_ra_t common_time_ra; + if (make_phy_common_time_ra(pusch_cfg.pusch_time_domain_alloc_list[i], &common_time_ra) == true) { + pusch->common_time_ra[i] = common_time_ra; + pusch->nof_common_time_ra = i + 1; + } else { + asn1::log_warning("Warning while building common_time_ra structure"); + return false; + } + } + return true; +} + +void fill_phy_carrier_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + srsran_carrier_nr_t* out_carrier_nr) +{ + // TODO: Currently ony one carrier is supported + auto& freq_info_dl = serv_cell_cfg.dl_cfg_common.freq_info_dl; + out_carrier_nr->offset_to_carrier = freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; + out_carrier_nr->scs = make_subcarrier_spacing(freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing); + out_carrier_nr->nof_prb = freq_info_dl.scs_specific_carrier_list[0].carrier_bw; + + auto& freq_info_ul = serv_cell_cfg.ul_cfg_common.freq_info_ul; + srsran::srsran_band_helper bands; + out_carrier_nr->ul_center_frequency_hz = bands.get_center_freq_from_abs_freq_point_a( + freq_info_ul.scs_specific_carrier_list[0].carrier_bw, freq_info_ul.absolute_freq_point_a); +} + +void fill_phy_ssb_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, + phy_cfg_nr_t::ssb_cfg_t* out_ssb) +{ + out_ssb->periodicity_ms = serv_cell_cfg.ssb_periodicity_serving_cell.to_number(); + + if (serv_cell_cfg.ssb_positions_in_burst.group_presence_present) { + make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.group_presence, out_ssb->position_in_burst); + } else { + make_ssb_positions_in_burst(serv_cell_cfg.ssb_positions_in_burst.in_one_group, out_ssb->position_in_burst); + } +} + +/************************** + * Asn1 Obj Id + *************************/ + +ASN1_OBJ_ID_DEFINE(asn1::rrc_nr::srb_to_add_mod_s, srb_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc_nr::drb_to_add_mod_s, drb_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc_nr::meas_obj_to_add_mod_s, meas_obj_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc_nr::report_cfg_to_add_mod_s, report_cfg_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc_nr::meas_id_to_add_mod_s, meas_id); + +} // namespace srsran + +namespace asn1 { + +namespace rrc_nr { + +bool operator==(const srb_to_add_mod_s& lhs, const srb_to_add_mod_s& rhs) +{ + if (lhs.srb_id != rhs.srb_id or lhs.pdcp_cfg_present != rhs.pdcp_cfg_present) { + return false; + } + // TODO: check remaining fields + return true; +} +bool operator==(const drb_to_add_mod_s& lhs, const drb_to_add_mod_s& rhs) +{ + if (lhs.drb_id != rhs.drb_id or lhs.pdcp_cfg_present != rhs.pdcp_cfg_present or + lhs.cn_assoc_present != rhs.cn_assoc_present) { + return false; + } + // TODO: check remaining fields + return true; +} + +} // namespace rrc_nr + +} // namespace asn1 diff --git a/lib/src/asn1/rrc_utils.cc b/lib/src/asn1/rrc_utils.cc index 53df1f35b..d63d7ac74 100644 --- a/lib/src/asn1/rrc_utils.cc +++ b/lib/src/asn1/rrc_utils.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,6 +20,7 @@ */ #include "srsran/asn1/rrc_utils.h" +#include "srsran/asn1/obj_id_cmp_utils.h" #include "srsran/asn1/rrc.h" #include "srsran/config.h" #include @@ -141,7 +142,9 @@ srsran::rlc_config_t make_rlc_config_t(const asn1::rrc::rlc_cfg_c& asn1_type) rlc_cfg.rlc_mode = rlc_mode_t::am; rlc_cfg.am.t_poll_retx = asn1_type.am().ul_am_rlc.t_poll_retx.to_number(); rlc_cfg.am.poll_pdu = asn1_type.am().ul_am_rlc.poll_pdu.to_number(); - rlc_cfg.am.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number() * 1000; // KB + rlc_cfg.am.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number() < 0 + ? -1 + : asn1_type.am().ul_am_rlc.poll_byte.to_number() * 1000; // KB rlc_cfg.am.max_retx_thresh = asn1_type.am().ul_am_rlc.max_retx_thres.to_number(); rlc_cfg.am.t_reordering = asn1_type.am().dl_am_rlc.t_reordering.to_number(); rlc_cfg.am.t_status_prohibit = asn1_type.am().dl_am_rlc.t_status_prohibit.to_number(); @@ -566,7 +569,8 @@ void set_phy_cfg_t_dedicated_cfg(phy_cfg_t* cfg, const asn1::rrc::phys_cfg_ded_s cqi_report_periodic.cqi_format_ind_periodic_r10.type().value == asn1::rrc::cqi_report_periodic_r10_c::setup_s_::cqi_format_ind_periodic_r10_c_::types::subband_cqi_r10; if (cfg->dl_cfg.cqi_report.format_is_subband) { - cfg->dl_cfg.cqi_report.subband_size = cqi_report_periodic.cqi_format_ind_periodic_r10.subband_cqi_r10().k; + cfg->dl_cfg.cqi_report.subband_wideband_ratio = + cqi_report_periodic.cqi_format_ind_periodic_r10.subband_cqi_r10().k; } if (cqi_report_periodic.ri_cfg_idx_present) { cfg->dl_cfg.cqi_report.ri_idx = cqi_report_periodic.ri_cfg_idx; @@ -593,7 +597,7 @@ void set_phy_cfg_t_dedicated_cfg(phy_cfg_t* cfg, const asn1::rrc::phys_cfg_ded_s asn1_type.cqi_report_cfg.cqi_report_periodic.setup().cqi_format_ind_periodic.type().value == asn1::rrc::cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::subband_cqi; if (cfg->dl_cfg.cqi_report.format_is_subband) { - cfg->dl_cfg.cqi_report.subband_size = + cfg->dl_cfg.cqi_report.subband_wideband_ratio = asn1_type.cqi_report_cfg.cqi_report_periodic.setup().cqi_format_ind_periodic.subband_cqi().k; } if (asn1_type.cqi_report_cfg.cqi_report_periodic.setup().ri_cfg_idx_present) { @@ -869,7 +873,7 @@ void set_phy_cfg_t_scell_config(phy_cfg_t* cfg, const asn1::rrc::scell_to_add_mo cqi_cfg.cqi_format_ind_periodic_r10.type().value == cqi_cfg_t::cqi_format_ind_periodic_r10_c_::types::subband_cqi_r10; if (cfg->dl_cfg.cqi_report.format_is_subband) { - cfg->dl_cfg.cqi_report.subband_size = cqi_cfg.cqi_format_ind_periodic_r10.subband_cqi_r10().k; + cfg->dl_cfg.cqi_report.subband_wideband_ratio = cqi_cfg.cqi_format_ind_periodic_r10.subband_cqi_r10().k; } if (cqi_cfg.ri_cfg_idx_present) { cfg->dl_cfg.cqi_report.ri_idx = cqi_cfg.ri_cfg_idx; @@ -1093,88 +1097,18 @@ sib13_t make_sib13(const asn1::rrc::sib_type13_r9_s& asn1_type) return sib13; } -} // namespace srsran - -namespace asn1 { -namespace rrc { - /************************** - * RRC Obj Id + * Asn1 Obj Id *************************/ -uint8_t get_rrc_obj_id(const srb_to_add_mod_s& srb) -{ - return srb.srb_id; -} -uint8_t get_rrc_obj_id(const drb_to_add_mod_s& drb) -{ - return drb.drb_id; -} -uint8_t get_rrc_obj_id(const black_cells_to_add_mod_s& obj) -{ - return obj.cell_idx; -} -uint8_t get_rrc_obj_id(const cells_to_add_mod_s& obj) -{ - return obj.cell_idx; -} -uint8_t get_rrc_obj_id(const cells_to_add_mod_nr_r15_s& obj) -{ - return obj.cell_idx_r15; -} -uint8_t get_rrc_obj_id(const meas_obj_to_add_mod_s& obj) -{ - return obj.meas_obj_id; -} -uint8_t get_rrc_obj_id(const report_cfg_to_add_mod_s& obj) -{ - return obj.report_cfg_id; -} -uint8_t get_rrc_obj_id(const meas_id_to_add_mod_s& obj) -{ - return obj.meas_id; -} -uint8_t get_rrc_obj_id(const scell_to_add_mod_r10_s& obj) -{ - return obj.scell_idx_r10; -} +ASN1_OBJ_ID_DEFINE(asn1::rrc::srb_to_add_mod_s, srb_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc::drb_to_add_mod_s, drb_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc::black_cells_to_add_mod_s, cell_idx); +ASN1_OBJ_ID_DEFINE(asn1::rrc::cells_to_add_mod_s, cell_idx); +ASN1_OBJ_ID_DEFINE(asn1::rrc::cells_to_add_mod_nr_r15_s, cell_idx_r15); +ASN1_OBJ_ID_DEFINE(asn1::rrc::meas_obj_to_add_mod_s, meas_obj_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc::report_cfg_to_add_mod_s, report_cfg_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc::meas_id_to_add_mod_s, meas_id); +ASN1_OBJ_ID_DEFINE(asn1::rrc::scell_to_add_mod_r10_s, scell_idx_r10); -void set_rrc_obj_id(srb_to_add_mod_s& srb, uint8_t id) -{ - srb.srb_id = id; -} -void set_rrc_obj_id(drb_to_add_mod_s& drb, uint8_t id) -{ - drb.drb_id = id; -} -void set_rrc_obj_id(black_cells_to_add_mod_s& obj, uint8_t id) -{ - obj.cell_idx = id; -} -void set_rrc_obj_id(cells_to_add_mod_s& obj, uint8_t id) -{ - obj.cell_idx = id; -} -void set_rrc_obj_id(cells_to_add_mod_nr_r15_s& obj, uint8_t id) -{ - obj.cell_idx_r15 = id; -} -void set_rrc_obj_id(meas_obj_to_add_mod_s& obj, uint8_t id) -{ - obj.meas_obj_id = id; -} -void set_rrc_obj_id(report_cfg_to_add_mod_s& obj, uint8_t id) -{ - obj.report_cfg_id = id; -} -void set_rrc_obj_id(meas_id_to_add_mod_s& obj, uint8_t id) -{ - obj.meas_id = id; -} -void set_rrc_obj_id(scell_to_add_mod_r10_s& obj, uint8_t id) -{ - obj.scell_idx_r10 = id; -} - -} // namespace rrc -} // namespace asn1 +} // namespace srsran diff --git a/lib/src/asn1/s1ap.cc b/lib/src/asn1/s1ap.cc index cdcd63622..19038f660 100644 --- a/lib/src/asn1/s1ap.cc +++ b/lib/src/asn1/s1ap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,20 +29,6 @@ using namespace asn1::s1ap; * Struct Methods ******************************************************************************/ -// Criticality ::= ENUMERATED -const char* crit_opts::to_string() const -{ - static const char* options[] = {"reject", "ignore", "notify"}; - return convert_enum_idx(options, 3, value, "crit_e"); -} - -// Presence ::= ENUMERATED -const char* presence_opts::to_string() const -{ - static const char* options[] = {"optional", "conditional", "mandatory"}; - return convert_enum_idx(options, 3, value, "presence_e"); -} - // PrivateIE-ID ::= CHOICE void private_ie_id_c::set(types::options e) { @@ -139,130 +125,6 @@ void private_ie_field_s::to_json(json_writer& j) const j.end_obj(); } -// ProtocolExtensionField{S1AP-PROTOCOL-EXTENSION : ExtensionSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-EXTENSION}} -template -SRSASN_CODE protocol_ext_field_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - warn_assert(crit != ext_set_paramT_::get_crit(id), __func__, __LINE__); - HANDLE_CODE(crit.pack(bref)); - HANDLE_CODE(ext_value.pack(bref)); - - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ext_field_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - ext_value = ext_set_paramT_::get_ext(id); - HANDLE_CODE(ext_value.unpack(bref)); - - return SRSASN_SUCCESS; -} -template -void protocol_ext_field_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} -template -bool protocol_ext_field_s::load_info_obj(const uint32_t& id_) -{ - if (not ext_set_paramT_::is_id_valid(id_)) { - return false; - } - id = id_; - crit = ext_set_paramT_::get_crit(id); - ext_value = ext_set_paramT_::get_ext(id); - return ext_value.type().value != ext_set_paramT_::ext_c::types_opts::nulltype; -} - -// ProtocolIE-Field{S1AP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-IES}} -template -SRSASN_CODE protocol_ie_field_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - warn_assert(crit != ies_set_paramT_::get_crit(id), __func__, __LINE__); - HANDLE_CODE(crit.pack(bref)); - HANDLE_CODE(value.pack(bref)); - - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ie_field_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - value = ies_set_paramT_::get_value(id); - HANDLE_CODE(value.unpack(bref)); - - return SRSASN_SUCCESS; -} -template -void protocol_ie_field_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} -template -bool protocol_ie_field_s::load_info_obj(const uint32_t& id_) -{ - if (not ies_set_paramT_::is_id_valid(id_)) { - return false; - } - id = id_; - crit = ies_set_paramT_::get_crit(id); - value = ies_set_paramT_::get_value(id); - return value.type().value != ies_set_paramT_::value_c::types_opts::nulltype; -} - -// ProtocolIE-SingleContainer{S1AP-PROTOCOL-IES : IEsSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-IES}} -template -SRSASN_CODE protocol_ie_single_container_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - warn_assert(crit != ies_set_paramT_::get_crit(id), __func__, __LINE__); - HANDLE_CODE(crit.pack(bref)); - HANDLE_CODE(value.pack(bref)); - - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ie_single_container_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - value = ies_set_paramT_::get_value(id); - HANDLE_CODE(value.unpack(bref)); - - return SRSASN_SUCCESS; -} -template -void protocol_ie_single_container_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} -template -bool protocol_ie_single_container_s::load_info_obj(const uint32_t& id_) -{ - if (not ies_set_paramT_::is_id_valid(id_)) { - return false; - } - id = id_; - crit = ies_set_paramT_::get_crit(id); - value = ies_set_paramT_::get_value(id); - return value.type().value != ies_set_paramT_::value_c::types_opts::nulltype; -} -template bool protocol_ie_single_container_s::load_info_obj(const uint32_t& id_); - // ProtocolIE-FieldPair{S1AP-PROTOCOL-IES-PAIR : IEsSetParam} ::= SEQUENCE{{S1AP-PROTOCOL-IES-PAIR}} template SRSASN_CODE protocol_ie_field_pair_s::pack(bit_ref& bref) const @@ -336,109 +198,6 @@ void activ_cells_list_item_s::to_json(json_writer& j) const j.end_obj(); } -uint32_t s1ap_protocol_ext_empty_o::idx_to_id(uint32_t idx) -{ - asn1::log_error("object set is empty\n"); - return 0; -} -bool s1ap_protocol_ext_empty_o::is_id_valid(const uint32_t& id) -{ - asn1::log_error("object set is empty\n"); - return false; -} -crit_e s1ap_protocol_ext_empty_o::get_crit(const uint32_t& id) -{ - return {}; -} -s1ap_protocol_ext_empty_o::ext_c s1ap_protocol_ext_empty_o::get_ext(const uint32_t& id) -{ - return {}; -} -presence_e s1ap_protocol_ext_empty_o::get_presence(const uint32_t& id) -{ - return {}; -} - -// Extension ::= OPEN TYPE -void s1ap_protocol_ext_empty_o::ext_c::to_json(json_writer& j) const -{ - j.start_obj(); - j.end_obj(); -} -SRSASN_CODE s1ap_protocol_ext_empty_o::ext_c::pack(bit_ref& bref) const -{ - varlength_field_pack_guard varlen_scope(bref, true); - return SRSASN_SUCCESS; -} -SRSASN_CODE s1ap_protocol_ext_empty_o::ext_c::unpack(cbit_ref& bref) -{ - varlength_field_unpack_guard varlen_scope(bref, true); - return SRSASN_SUCCESS; -} - -const char* s1ap_protocol_ext_empty_o::ext_c::types_opts::to_string() const -{ - static const char* options[] = {}; - return convert_enum_idx(options, 0, value, "s1ap_protocol_ext_empty_o::ext_c::types"); -} - -template -protocol_ext_container_item_s::protocol_ext_container_item_s(uint32_t id_, crit_e crit_) : id(id_), crit(crit_) - -{} -template -SRSASN_CODE protocol_ext_container_item_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.pack(bref)); - { - varlength_field_pack_guard varlen_scope(bref, true); - HANDLE_CODE(ext.pack(bref)); - } - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ext_container_item_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - { - varlength_field_unpack_guard varlen_scope(bref, true); - HANDLE_CODE(ext.unpack(bref)); - } - return SRSASN_SUCCESS; -} -template -void protocol_ext_container_item_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} - -SRSASN_CODE protocol_ext_container_empty_l::pack(bit_ref& bref) const -{ - uint32_t nof_ies = 0; - pack_length(bref, nof_ies, 1u, 65535u, true); - - return SRSASN_SUCCESS; -} -SRSASN_CODE protocol_ext_container_empty_l::unpack(cbit_ref& bref) -{ - uint32_t nof_ies = 0; - unpack_length(nof_ies, bref, 1u, 65535u, true); - if (nof_ies > 0) { - return SRSASN_ERROR_DECODE_FAIL; - } - return SRSASN_SUCCESS; -} -void protocol_ext_container_empty_l::to_json(json_writer& j) const -{ - j.start_obj(); - j.end_obj(); -} - // GUMMEI ::= SEQUENCE SRSASN_CODE gummei_s::pack(bit_ref& bref) const { @@ -1493,7 +1252,7 @@ const char* recommended_cell_item_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "recommended_cell_item_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // NextPagingAreaScope ::= ENUMERATED const char* next_paging_area_scope_opts::to_string() const @@ -2184,7 +1943,7 @@ void coun_tvalue_s::to_json(json_writer& j) const j.end_obj(); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; bearers_subject_to_status_transfer_item_ext_ies_container::bearers_subject_to_status_transfer_item_ext_ies_container() : ulcount_value_extended(179, crit_e::ignore), @@ -2232,47 +1991,59 @@ SRSASN_CODE bearers_subject_to_status_transfer_item_ext_ies_container::unpack(cb unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 179: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 179: { ulcount_value_extended_present = true; - ulcount_value_extended.id = c.id; - ulcount_value_extended.crit = c.crit; - ulcount_value_extended.ext = c.ext_value.ulcount_value_extended(); + ulcount_value_extended.id = id; + HANDLE_CODE(ulcount_value_extended.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ulcount_value_extended.ext.unpack(bref)); break; - case 180: + } + case 180: { dlcount_value_extended_present = true; - dlcount_value_extended.id = c.id; - dlcount_value_extended.crit = c.crit; - dlcount_value_extended.ext = c.ext_value.dlcount_value_extended(); + dlcount_value_extended.id = id; + HANDLE_CODE(dlcount_value_extended.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(dlcount_value_extended.ext.unpack(bref)); break; - case 181: + } + case 181: { receive_status_of_ulpdcpsdus_extended_present = true; - receive_status_of_ulpdcpsdus_extended.id = c.id; - receive_status_of_ulpdcpsdus_extended.crit = c.crit; - receive_status_of_ulpdcpsdus_extended.ext = c.ext_value.receive_status_of_ulpdcpsdus_extended(); + receive_status_of_ulpdcpsdus_extended.id = id; + HANDLE_CODE(receive_status_of_ulpdcpsdus_extended.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(receive_status_of_ulpdcpsdus_extended.ext.unpack(bref)); break; - case 217: + } + case 217: { ulcount_value_pdcp_snlen18_present = true; - ulcount_value_pdcp_snlen18.id = c.id; - ulcount_value_pdcp_snlen18.crit = c.crit; - ulcount_value_pdcp_snlen18.ext = c.ext_value.ulcount_value_pdcp_snlen18(); + ulcount_value_pdcp_snlen18.id = id; + HANDLE_CODE(ulcount_value_pdcp_snlen18.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ulcount_value_pdcp_snlen18.ext.unpack(bref)); break; - case 218: + } + case 218: { dlcount_value_pdcp_snlen18_present = true; - dlcount_value_pdcp_snlen18.id = c.id; - dlcount_value_pdcp_snlen18.crit = c.crit; - dlcount_value_pdcp_snlen18.ext = c.ext_value.dlcount_value_pdcp_snlen18(); + dlcount_value_pdcp_snlen18.id = id; + HANDLE_CODE(dlcount_value_pdcp_snlen18.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(dlcount_value_pdcp_snlen18.ext.unpack(bref)); break; - case 219: + } + case 219: { receive_status_of_ulpdcpsdus_pdcp_snlen18_present = true; - receive_status_of_ulpdcpsdus_pdcp_snlen18.id = c.id; - receive_status_of_ulpdcpsdus_pdcp_snlen18.crit = c.crit; - receive_status_of_ulpdcpsdus_pdcp_snlen18.ext = c.ext_value.receive_status_of_ulpdcpsdus_pdcp_snlen18(); + receive_status_of_ulpdcpsdus_pdcp_snlen18.id = id; + HANDLE_CODE(receive_status_of_ulpdcpsdus_pdcp_snlen18.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(receive_status_of_ulpdcpsdus_pdcp_snlen18.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -2427,7 +2198,7 @@ const char* bearers_subject_to_status_transfer_item_ies_o::value_c::types_opts:: return convert_enum_idx(options, 1, value, "bearers_subject_to_status_transfer_item_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // BluetoothMeasConfig ::= ENUMERATED const char* bluetooth_meas_cfg_opts::to_string() const @@ -4748,42 +4519,7 @@ const char* cell_traffic_trace_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 6, value, "cell_traffic_trace_ies_o::value_c::types"); } -template -protocol_ie_container_item_s::protocol_ie_container_item_s(uint32_t id_, crit_e crit_) : id(id_), crit(crit_) - -{} -template -SRSASN_CODE protocol_ie_container_item_s::pack(bit_ref& bref) const -{ - HANDLE_CODE(pack_integer(bref, id, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.pack(bref)); - { - varlength_field_pack_guard varlen_scope(bref, true); - HANDLE_CODE(value.pack(bref)); - } - return SRSASN_SUCCESS; -} -template -SRSASN_CODE protocol_ie_container_item_s::unpack(cbit_ref& bref) -{ - HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); - HANDLE_CODE(crit.unpack(bref)); - { - varlength_field_unpack_guard varlen_scope(bref, true); - HANDLE_CODE(value.unpack(bref)); - } - return SRSASN_SUCCESS; -} -template -void protocol_ie_container_item_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_int("id", id); - j.write_str("criticality", crit.to_string()); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; cell_traffic_trace_ies_container::cell_traffic_trace_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -4818,47 +4554,59 @@ SRSASN_CODE cell_traffic_trace_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 86: + } + case 86: { nof_mandatory_ies--; - e_utran_trace_id.id = c.id; - e_utran_trace_id.crit = c.crit; - e_utran_trace_id.value = c.value.e_utran_trace_id(); + e_utran_trace_id.id = id; + HANDLE_CODE(e_utran_trace_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(e_utran_trace_id.value.unpack(bref)); break; - case 100: + } + case 100: { nof_mandatory_ies--; - eutran_cgi.id = c.id; - eutran_cgi.crit = c.crit; - eutran_cgi.value = c.value.eutran_cgi(); + eutran_cgi.id = id; + HANDLE_CODE(eutran_cgi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(eutran_cgi.value.unpack(bref)); break; - case 131: + } + case 131: { nof_mandatory_ies--; - trace_collection_entity_ip_address.id = c.id; - trace_collection_entity_ip_address.crit = c.crit; - trace_collection_entity_ip_address.value = c.value.trace_collection_entity_ip_address(); + trace_collection_entity_ip_address.id = id; + HANDLE_CODE(trace_collection_entity_ip_address.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(trace_collection_entity_ip_address.value.unpack(bref)); break; - case 166: + } + case 166: { privacy_ind_present = true; - privacy_ind.id = c.id; - privacy_ind.crit = c.crit; - privacy_ind.value = c.value.privacy_ind(); + privacy_ind.id = id; + HANDLE_CODE(privacy_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(privacy_ind.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -4889,29 +4637,6 @@ void cell_traffic_trace_ies_container::to_json(json_writer& j) const j.end_obj(); } -// CellTrafficTrace ::= SEQUENCE -SRSASN_CODE cell_traffic_trace_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE cell_traffic_trace_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void cell_traffic_trace_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // Cell-Size ::= ENUMERATED const char* cell_size_opts::to_string() const { @@ -5528,7 +5253,7 @@ const char* erab_qos_params_ext_ies_o::ext_c::types_opts::to_string() const return convert_enum_idx(options, 2, value, "erab_qos_params_ext_ies_o::ext_c::types"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; gbr_qos_info_ext_ies_container::gbr_qos_info_ext_ies_container() : extended_erab_maximum_bitrate_dl(255, crit_e::ignore), @@ -5566,35 +5291,43 @@ SRSASN_CODE gbr_qos_info_ext_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 255: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 255: { extended_erab_maximum_bitrate_dl_present = true; - extended_erab_maximum_bitrate_dl.id = c.id; - extended_erab_maximum_bitrate_dl.crit = c.crit; - extended_erab_maximum_bitrate_dl.ext = c.ext_value.extended_erab_maximum_bitrate_dl(); + extended_erab_maximum_bitrate_dl.id = id; + HANDLE_CODE(extended_erab_maximum_bitrate_dl.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(extended_erab_maximum_bitrate_dl.ext.unpack(bref)); break; - case 256: + } + case 256: { extended_erab_maximum_bitrate_ul_present = true; - extended_erab_maximum_bitrate_ul.id = c.id; - extended_erab_maximum_bitrate_ul.crit = c.crit; - extended_erab_maximum_bitrate_ul.ext = c.ext_value.extended_erab_maximum_bitrate_ul(); + extended_erab_maximum_bitrate_ul.id = id; + HANDLE_CODE(extended_erab_maximum_bitrate_ul.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(extended_erab_maximum_bitrate_ul.ext.unpack(bref)); break; - case 257: + } + case 257: { extended_erab_guaranteed_bitrate_dl_present = true; - extended_erab_guaranteed_bitrate_dl.id = c.id; - extended_erab_guaranteed_bitrate_dl.crit = c.crit; - extended_erab_guaranteed_bitrate_dl.ext = c.ext_value.extended_erab_guaranteed_bitrate_dl(); + extended_erab_guaranteed_bitrate_dl.id = id; + HANDLE_CODE(extended_erab_guaranteed_bitrate_dl.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(extended_erab_guaranteed_bitrate_dl.ext.unpack(bref)); break; - case 258: + } + case 258: { extended_erab_guaranteed_bitrate_ul_present = true; - extended_erab_guaranteed_bitrate_ul.id = c.id; - extended_erab_guaranteed_bitrate_ul.crit = c.crit; - extended_erab_guaranteed_bitrate_ul.ext = c.ext_value.extended_erab_guaranteed_bitrate_ul(); + extended_erab_guaranteed_bitrate_ul.id = id; + HANDLE_CODE(extended_erab_guaranteed_bitrate_ul.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(extended_erab_guaranteed_bitrate_ul.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -5777,7 +5510,7 @@ void dl_cp_security_info_s::to_json(json_writer& j) const j.end_obj(); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; erab_qos_params_ext_ies_container::erab_qos_params_ext_ies_container() : dl_packet_loss_rate(273, crit_e::ignore), ul_packet_loss_rate(274, crit_e::ignore) @@ -5804,23 +5537,27 @@ SRSASN_CODE erab_qos_params_ext_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 273: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 273: { dl_packet_loss_rate_present = true; - dl_packet_loss_rate.id = c.id; - dl_packet_loss_rate.crit = c.crit; - dl_packet_loss_rate.ext = c.ext_value.dl_packet_loss_rate(); + dl_packet_loss_rate.id = id; + HANDLE_CODE(dl_packet_loss_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(dl_packet_loss_rate.ext.unpack(bref)); break; - case 274: + } + case 274: { ul_packet_loss_rate_present = true; - ul_packet_loss_rate.id = c.id; - ul_packet_loss_rate.crit = c.crit; - ul_packet_loss_rate.ext = c.ext_value.ul_packet_loss_rate(); + ul_packet_loss_rate.id = id; + HANDLE_CODE(ul_packet_loss_rate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ul_packet_loss_rate.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -6491,7 +6228,7 @@ const char* conn_establishment_ind_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 9, value, "conn_establishment_ind_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; conn_establishment_ind_ies_container::conn_establishment_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -6550,65 +6287,83 @@ SRSASN_CODE conn_establishment_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 74: + } + case 74: { ue_radio_cap_present = true; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; - case 251: + } + case 251: { enhanced_coverage_restricted_present = true; - enhanced_coverage_restricted.id = c.id; - enhanced_coverage_restricted.crit = c.crit; - enhanced_coverage_restricted.value = c.value.enhanced_coverage_restricted(); + enhanced_coverage_restricted.id = id; + HANDLE_CODE(enhanced_coverage_restricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enhanced_coverage_restricted.value.unpack(bref)); break; - case 253: + } + case 253: { dl_cp_security_info_present = true; - dl_cp_security_info.id = c.id; - dl_cp_security_info.crit = c.crit; - dl_cp_security_info.value = c.value.dl_cp_security_info(); + dl_cp_security_info.id = id; + HANDLE_CODE(dl_cp_security_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(dl_cp_security_info.value.unpack(bref)); break; - case 271: + } + case 271: { ce_mode_brestricted_present = true; - ce_mode_brestricted.id = c.id; - ce_mode_brestricted.crit = c.crit; - ce_mode_brestricted.value = c.value.ce_mode_brestricted(); + ce_mode_brestricted.id = id; + HANDLE_CODE(ce_mode_brestricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ce_mode_brestricted.value.unpack(bref)); break; - case 280: + } + case 280: { end_ind_present = true; - end_ind.id = c.id; - end_ind.crit = c.crit; - end_ind.value = c.value.end_ind(); + end_ind.id = id; + HANDLE_CODE(end_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(end_ind.value.unpack(bref)); break; - case 278: + } + case 278: { subscription_based_ue_differentiation_info_present = true; - subscription_based_ue_differentiation_info.id = c.id; - subscription_based_ue_differentiation_info.crit = c.crit; - subscription_based_ue_differentiation_info.value = c.value.subscription_based_ue_differentiation_info(); + subscription_based_ue_differentiation_info.id = id; + HANDLE_CODE(subscription_based_ue_differentiation_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscription_based_ue_differentiation_info.value.unpack(bref)); break; - case 252: + } + case 252: { ue_level_qos_params_present = true; - ue_level_qos_params.id = c.id; - ue_level_qos_params.crit = c.crit; - ue_level_qos_params.value = c.value.ue_level_qos_params(); + ue_level_qos_params.id = id; + HANDLE_CODE(ue_level_qos_params.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_level_qos_params.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -6657,29 +6412,6 @@ void conn_establishment_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ConnectionEstablishmentIndication ::= SEQUENCE -SRSASN_CODE conn_establishment_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE conn_establishment_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void conn_establishment_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // ENB-ID ::= CHOICE void enb_id_c::destroy_() { @@ -7596,7 +7328,7 @@ const char* deactiv_trace_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "deactiv_trace_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; deactiv_trace_ies_container::deactiv_trace_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject), e_utran_trace_id(86, crit_e::ignore) @@ -7620,29 +7352,35 @@ SRSASN_CODE deactiv_trace_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 86: + } + case 86: { nof_mandatory_ies--; - e_utran_trace_id.id = c.id; - e_utran_trace_id.crit = c.crit; - e_utran_trace_id.value = c.value.e_utran_trace_id(); + e_utran_trace_id.id = id; + HANDLE_CODE(e_utran_trace_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(e_utran_trace_id.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -7665,29 +7403,6 @@ void deactiv_trace_ies_container::to_json(json_writer& j) const j.end_obj(); } -// DeactivateTrace ::= SEQUENCE -SRSASN_CODE deactiv_trace_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE deactiv_trace_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void deactiv_trace_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // ForbiddenLAs-Item ::= SEQUENCE SRSASN_CODE forbidden_las_item_s::pack(bit_ref& bref) const { @@ -8131,7 +7846,7 @@ const char* dlnaspdu_delivery_ack_request_opts::to_string() const return convert_enum_idx(options, 1, value, "dlnaspdu_delivery_ack_request_e"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; ho_restrict_list_ext_ies_container::ho_restrict_list_ext_ies_container() : nrrestrictin_ep_sas_secondary_rat(261, crit_e::ignore), @@ -8174,41 +7889,51 @@ SRSASN_CODE ho_restrict_list_ext_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 261: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 261: { nrrestrictin_ep_sas_secondary_rat_present = true; - nrrestrictin_ep_sas_secondary_rat.id = c.id; - nrrestrictin_ep_sas_secondary_rat.crit = c.crit; - nrrestrictin_ep_sas_secondary_rat.ext = c.ext_value.nrrestrictin_ep_sas_secondary_rat(); + nrrestrictin_ep_sas_secondary_rat.id = id; + HANDLE_CODE(nrrestrictin_ep_sas_secondary_rat.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrrestrictin_ep_sas_secondary_rat.ext.unpack(bref)); break; - case 270: + } + case 270: { unlicensed_spec_restrict_present = true; - unlicensed_spec_restrict.id = c.id; - unlicensed_spec_restrict.crit = c.crit; - unlicensed_spec_restrict.ext = c.ext_value.unlicensed_spec_restrict(); + unlicensed_spec_restrict.id = id; + HANDLE_CODE(unlicensed_spec_restrict.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(unlicensed_spec_restrict.ext.unpack(bref)); break; - case 282: + } + case 282: { cn_type_restricts_present = true; - cn_type_restricts.id = c.id; - cn_type_restricts.crit = c.crit; - cn_type_restricts.ext = c.ext_value.cn_type_restricts(); + cn_type_restricts.id = id; + HANDLE_CODE(cn_type_restricts.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cn_type_restricts.ext.unpack(bref)); break; - case 287: + } + case 287: { nrrestrictin5_gs_present = true; - nrrestrictin5_gs.id = c.id; - nrrestrictin5_gs.crit = c.crit; - nrrestrictin5_gs.ext = c.ext_value.nrrestrictin5_gs(); + nrrestrictin5_gs.id = id; + HANDLE_CODE(nrrestrictin5_gs.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrrestrictin5_gs.ext.unpack(bref)); break; - case 290: + } + case 290: { last_ng_ranplmn_id_present = true; - last_ng_ranplmn_id.id = c.id; - last_ng_ranplmn_id.crit = c.crit; - last_ng_ranplmn_id.ext = c.ext_value.last_ng_ranplmn_id(); + last_ng_ranplmn_id.id = id; + HANDLE_CODE(last_ng_ranplmn_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(last_ng_ranplmn_id.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -9104,7 +8829,7 @@ const char* dl_nas_transport_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 16, value, "dl_nas_transport_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_nas_transport_ies_container::dl_nas_transport_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -9195,107 +8920,139 @@ SRSASN_CODE dl_nas_transport_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 26: + } + case 26: { nof_mandatory_ies--; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 41: + } + case 41: { ho_restrict_list_present = true; - ho_restrict_list.id = c.id; - ho_restrict_list.crit = c.crit; - ho_restrict_list.value = c.value.ho_restrict_list(); + ho_restrict_list.id = id; + HANDLE_CODE(ho_restrict_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ho_restrict_list.value.unpack(bref)); break; - case 106: + } + case 106: { subscriber_profile_idfor_rfp_present = true; - subscriber_profile_idfor_rfp.id = c.id; - subscriber_profile_idfor_rfp.crit = c.crit; - subscriber_profile_idfor_rfp.value = c.value.subscriber_profile_idfor_rfp(); + subscriber_profile_idfor_rfp.id = id; + HANDLE_CODE(subscriber_profile_idfor_rfp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscriber_profile_idfor_rfp.value.unpack(bref)); break; - case 124: + } + case 124: { srvcc_operation_possible_present = true; - srvcc_operation_possible.id = c.id; - srvcc_operation_possible.crit = c.crit; - srvcc_operation_possible.value = c.value.srvcc_operation_possible(); + srvcc_operation_possible.id = id; + HANDLE_CODE(srvcc_operation_possible.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(srvcc_operation_possible.value.unpack(bref)); break; - case 74: + } + case 74: { ue_radio_cap_present = true; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; - case 249: + } + case 249: { dlnaspdu_delivery_ack_request_present = true; - dlnaspdu_delivery_ack_request.id = c.id; - dlnaspdu_delivery_ack_request.crit = c.crit; - dlnaspdu_delivery_ack_request.value = c.value.dlnaspdu_delivery_ack_request(); + dlnaspdu_delivery_ack_request.id = id; + HANDLE_CODE(dlnaspdu_delivery_ack_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(dlnaspdu_delivery_ack_request.value.unpack(bref)); break; - case 251: + } + case 251: { enhanced_coverage_restricted_present = true; - enhanced_coverage_restricted.id = c.id; - enhanced_coverage_restricted.crit = c.crit; - enhanced_coverage_restricted.value = c.value.enhanced_coverage_restricted(); + enhanced_coverage_restricted.id = id; + HANDLE_CODE(enhanced_coverage_restricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enhanced_coverage_restricted.value.unpack(bref)); break; - case 269: + } + case 269: { nrue_security_cap_present = true; - nrue_security_cap.id = c.id; - nrue_security_cap.crit = c.crit; - nrue_security_cap.value = c.value.nrue_security_cap(); + nrue_security_cap.id = id; + HANDLE_CODE(nrue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrue_security_cap.value.unpack(bref)); break; - case 271: + } + case 271: { ce_mode_brestricted_present = true; - ce_mode_brestricted.id = c.id; - ce_mode_brestricted.crit = c.crit; - ce_mode_brestricted.value = c.value.ce_mode_brestricted(); + ce_mode_brestricted.id = id; + HANDLE_CODE(ce_mode_brestricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ce_mode_brestricted.value.unpack(bref)); break; - case 275: + } + case 275: { ue_cap_info_request_present = true; - ue_cap_info_request.id = c.id; - ue_cap_info_request.crit = c.crit; - ue_cap_info_request.value = c.value.ue_cap_info_request(); + ue_cap_info_request.id = id; + HANDLE_CODE(ue_cap_info_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_cap_info_request.value.unpack(bref)); break; - case 280: + } + case 280: { end_ind_present = true; - end_ind.id = c.id; - end_ind.crit = c.crit; - end_ind.value = c.value.end_ind(); + end_ind.id = id; + HANDLE_CODE(end_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(end_ind.value.unpack(bref)); break; - case 283: + } + case 283: { pending_data_ind_present = true; - pending_data_ind.id = c.id; - pending_data_ind.crit = c.crit; - pending_data_ind.value = c.value.pending_data_ind(); + pending_data_ind.id = id; + HANDLE_CODE(pending_data_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pending_data_ind.value.unpack(bref)); break; - case 278: + } + case 278: { subscription_based_ue_differentiation_info_present = true; - subscription_based_ue_differentiation_info.id = c.id; - subscription_based_ue_differentiation_info.crit = c.crit; - subscription_based_ue_differentiation_info.value = c.value.subscription_based_ue_differentiation_info(); + subscription_based_ue_differentiation_info.id = id; + HANDLE_CODE(subscription_based_ue_differentiation_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscription_based_ue_differentiation_info.value.unpack(bref)); break; - case 299: + } + case 299: { add_rrm_prio_idx_present = true; - add_rrm_prio_idx.id = c.id; - add_rrm_prio_idx.crit = c.crit; - add_rrm_prio_idx.value = c.value.add_rrm_prio_idx(); + add_rrm_prio_idx.id = id; + HANDLE_CODE(add_rrm_prio_idx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_rrm_prio_idx.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -9370,29 +9127,6 @@ void dl_nas_transport_ies_container::to_json(json_writer& j) const j.end_obj(); } -// DownlinkNASTransport ::= SEQUENCE -SRSASN_CODE dl_nas_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_nas_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_nas_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // DownlinkNonUEAssociatedLPPaTransport-IEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t dl_non_ueassociated_lp_pa_transport_ies_o::idx_to_id(uint32_t idx) { @@ -9595,7 +9329,7 @@ uint8_t dl_non_ueassociated_lp_pa_transport_ies_o::value_c::types_opts::to_numbe return map_enum_number(options, 1, value, "dl_non_ueassociated_lp_pa_transport_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_non_ueassociated_lp_pa_transport_ies_container::dl_non_ueassociated_lp_pa_transport_ies_container() : routing_id(148, crit_e::reject), lp_pa_pdu(147, crit_e::reject) @@ -9618,23 +9352,27 @@ SRSASN_CODE dl_non_ueassociated_lp_pa_transport_ies_container::unpack(cbit_ref& uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 148: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 148: { nof_mandatory_ies--; - routing_id.id = c.id; - routing_id.crit = c.crit; - routing_id.value = c.value.routing_id(); + routing_id.id = id; + HANDLE_CODE(routing_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(routing_id.value.unpack(bref)); break; - case 147: + } + case 147: { nof_mandatory_ies--; - lp_pa_pdu.id = c.id; - lp_pa_pdu.crit = c.crit; - lp_pa_pdu.value = c.value.lp_pa_pdu(); + lp_pa_pdu.id = id; + HANDLE_CODE(lp_pa_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lp_pa_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -9655,29 +9393,6 @@ void dl_non_ueassociated_lp_pa_transport_ies_container::to_json(json_writer& j) j.end_obj(); } -// DownlinkNonUEAssociatedLPPaTransport ::= SEQUENCE -SRSASN_CODE dl_non_ueassociated_lp_pa_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_non_ueassociated_lp_pa_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_non_ueassociated_lp_pa_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABDataForwardingItem ::= SEQUENCE SRSASN_CODE erab_data_forwarding_item_s::pack(bit_ref& bref) const { @@ -10186,7 +9901,7 @@ const char* dl_s1cdma2000tunnelling_ies_o::value_c::types_opts::to_string() cons return convert_enum_idx(options, 6, value, "dl_s1cdma2000tunnelling_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_s1cdma2000tunnelling_ies_container::dl_s1cdma2000tunnelling_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -10224,47 +9939,59 @@ SRSASN_CODE dl_s1cdma2000tunnelling_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 12: + } + case 12: { erab_subjectto_data_forwarding_list_present = true; - erab_subjectto_data_forwarding_list.id = c.id; - erab_subjectto_data_forwarding_list.crit = c.crit; - erab_subjectto_data_forwarding_list.value = c.value.erab_subjectto_data_forwarding_list(); + erab_subjectto_data_forwarding_list.id = id; + HANDLE_CODE(erab_subjectto_data_forwarding_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_subjectto_data_forwarding_list.value.unpack(bref)); break; - case 83: + } + case 83: { cdma2000_ho_status_present = true; - cdma2000_ho_status.id = c.id; - cdma2000_ho_status.crit = c.crit; - cdma2000_ho_status.value = c.value.cdma2000_ho_status(); + cdma2000_ho_status.id = id; + HANDLE_CODE(cdma2000_ho_status.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_ho_status.value.unpack(bref)); break; - case 71: + } + case 71: { nof_mandatory_ies--; - cdma2000_rat_type.id = c.id; - cdma2000_rat_type.crit = c.crit; - cdma2000_rat_type.value = c.value.cdma2000_rat_type(); + cdma2000_rat_type.id = id; + HANDLE_CODE(cdma2000_rat_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_rat_type.value.unpack(bref)); break; - case 70: + } + case 70: { nof_mandatory_ies--; - cdma2000_pdu.id = c.id; - cdma2000_pdu.crit = c.crit; - cdma2000_pdu.value = c.value.cdma2000_pdu(); + cdma2000_pdu.id = id; + HANDLE_CODE(cdma2000_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -10297,29 +10024,6 @@ void dl_s1cdma2000tunnelling_ies_container::to_json(json_writer& j) const j.end_obj(); } -// DownlinkS1cdma2000tunnelling ::= SEQUENCE -SRSASN_CODE dl_s1cdma2000tunnelling_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_s1cdma2000tunnelling_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_s1cdma2000tunnelling_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // DownlinkUEAssociatedLPPaTransport-IEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t dl_ueassociated_lp_pa_transport_ies_o::idx_to_id(uint32_t idx) { @@ -10584,7 +10288,7 @@ const char* dl_ueassociated_lp_pa_transport_ies_o::value_c::types_opts::to_strin return convert_enum_idx(options, 4, value, "dl_ueassociated_lp_pa_transport_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; dl_ueassociated_lp_pa_transport_ies_container::dl_ueassociated_lp_pa_transport_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -10612,35 +10316,43 @@ SRSASN_CODE dl_ueassociated_lp_pa_transport_ies_container::unpack(cbit_ref& bref uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 148: + } + case 148: { nof_mandatory_ies--; - routing_id.id = c.id; - routing_id.crit = c.crit; - routing_id.value = c.value.routing_id(); + routing_id.id = id; + HANDLE_CODE(routing_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(routing_id.value.unpack(bref)); break; - case 147: + } + case 147: { nof_mandatory_ies--; - lp_pa_pdu.id = c.id; - lp_pa_pdu.crit = c.crit; - lp_pa_pdu.value = c.value.lp_pa_pdu(); + lp_pa_pdu.id = id; + HANDLE_CODE(lp_pa_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lp_pa_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -10665,29 +10377,6 @@ void dl_ueassociated_lp_pa_transport_ies_container::to_json(json_writer& j) cons j.end_obj(); } -// DownlinkUEAssociatedLPPaTransport ::= SEQUENCE -SRSASN_CODE dl_ueassociated_lp_pa_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE dl_ueassociated_lp_pa_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void dl_ueassociated_lp_pa_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABAdmittedItem ::= SEQUENCE SRSASN_CODE erab_admitted_item_s::pack(bit_ref& bref) const { @@ -11261,7 +10950,7 @@ const char* erab_info_list_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "erab_info_list_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // E-RABItem ::= SEQUENCE SRSASN_CODE erab_item_s::pack(bit_ref& bref) const @@ -11365,7 +11054,7 @@ const char* erab_item_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "erab_item_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // E-RABModifyItemBearerModConf ::= SEQUENCE SRSASN_CODE erab_modify_item_bearer_mod_conf_s::pack(bit_ref& bref) const @@ -11465,7 +11154,7 @@ const char* erab_modify_item_bearer_mod_conf_ies_o::value_c::types_opts::to_stri return convert_enum_idx(options, 1, value, "erab_modify_item_bearer_mod_conf_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // E-RABModificationConfirmIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t erab_mod_confirm_ies_o::idx_to_id(uint32_t idx) @@ -11863,7 +11552,7 @@ const char* erab_mod_confirm_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 7, value, "erab_mod_confirm_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; erab_mod_confirm_ies_container::erab_mod_confirm_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -11912,53 +11601,67 @@ SRSASN_CODE erab_mod_confirm_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 203: + } + case 203: { erab_modify_list_bearer_mod_conf_present = true; - erab_modify_list_bearer_mod_conf.id = c.id; - erab_modify_list_bearer_mod_conf.crit = c.crit; - erab_modify_list_bearer_mod_conf.value = c.value.erab_modify_list_bearer_mod_conf(); + erab_modify_list_bearer_mod_conf.id = id; + HANDLE_CODE(erab_modify_list_bearer_mod_conf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_modify_list_bearer_mod_conf.value.unpack(bref)); break; - case 205: + } + case 205: { erab_failed_to_modify_list_bearer_mod_conf_present = true; - erab_failed_to_modify_list_bearer_mod_conf.id = c.id; - erab_failed_to_modify_list_bearer_mod_conf.crit = c.crit; - erab_failed_to_modify_list_bearer_mod_conf.value = c.value.erab_failed_to_modify_list_bearer_mod_conf(); + erab_failed_to_modify_list_bearer_mod_conf.id = id; + HANDLE_CODE(erab_failed_to_modify_list_bearer_mod_conf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_failed_to_modify_list_bearer_mod_conf.value.unpack(bref)); break; - case 210: + } + case 210: { erab_to_be_released_list_bearer_mod_conf_present = true; - erab_to_be_released_list_bearer_mod_conf.id = c.id; - erab_to_be_released_list_bearer_mod_conf.crit = c.crit; - erab_to_be_released_list_bearer_mod_conf.value = c.value.erab_to_be_released_list_bearer_mod_conf(); + erab_to_be_released_list_bearer_mod_conf.id = id; + HANDLE_CODE(erab_to_be_released_list_bearer_mod_conf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_released_list_bearer_mod_conf.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 146: + } + case 146: { csg_membership_status_present = true; - csg_membership_status.id = c.id; - csg_membership_status.crit = c.crit; - csg_membership_status.value = c.value.csg_membership_status(); + csg_membership_status.id = id; + HANDLE_CODE(csg_membership_status.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_status.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -11999,29 +11702,6 @@ void erab_mod_confirm_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABModificationConfirm ::= SEQUENCE -SRSASN_CODE erab_mod_confirm_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_mod_confirm_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_mod_confirm_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABUsageReportItem ::= SEQUENCE SRSASN_CODE erabusage_report_item_s::pack(bit_ref& bref) const { @@ -12129,7 +11809,7 @@ const char* erabusage_report_item_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "erabusage_report_item_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // NR-CGI ::= SEQUENCE SRSASN_CODE nr_cgi_s::pack(bit_ref& bref) const @@ -12595,7 +12275,7 @@ const char* user_location_info_ext_ies_o::ext_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "user_location_info_ext_ies_o::ext_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // TunnelInformation ::= SEQUENCE SRSASN_CODE tunnel_info_s::pack(bit_ref& bref) const @@ -13124,7 +12804,7 @@ const char* erab_mod_ind_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 8, value, "erab_mod_ind_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; erab_mod_ind_ies_container::erab_mod_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -13175,59 +12855,75 @@ SRSASN_CODE erab_mod_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 199: + } + case 199: { nof_mandatory_ies--; - erab_to_be_modified_list_bearer_mod_ind.id = c.id; - erab_to_be_modified_list_bearer_mod_ind.crit = c.crit; - erab_to_be_modified_list_bearer_mod_ind.value = c.value.erab_to_be_modified_list_bearer_mod_ind(); + erab_to_be_modified_list_bearer_mod_ind.id = id; + HANDLE_CODE(erab_to_be_modified_list_bearer_mod_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_modified_list_bearer_mod_ind.value.unpack(bref)); break; - case 201: + } + case 201: { erab_not_to_be_modified_list_bearer_mod_ind_present = true; - erab_not_to_be_modified_list_bearer_mod_ind.id = c.id; - erab_not_to_be_modified_list_bearer_mod_ind.crit = c.crit; - erab_not_to_be_modified_list_bearer_mod_ind.value = c.value.erab_not_to_be_modified_list_bearer_mod_ind(); + erab_not_to_be_modified_list_bearer_mod_ind.id = id; + HANDLE_CODE(erab_not_to_be_modified_list_bearer_mod_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_not_to_be_modified_list_bearer_mod_ind.value.unpack(bref)); break; - case 226: + } + case 226: { csg_membership_info_present = true; - csg_membership_info.id = c.id; - csg_membership_info.crit = c.crit; - csg_membership_info.value = c.value.csg_membership_info(); + csg_membership_info.id = id; + HANDLE_CODE(csg_membership_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_info.value.unpack(bref)); break; - case 176: + } + case 176: { tunnel_info_for_bbf_present = true; - tunnel_info_for_bbf.id = c.id; - tunnel_info_for_bbf.crit = c.crit; - tunnel_info_for_bbf.value = c.value.tunnel_info_for_bbf(); + tunnel_info_for_bbf.id = id; + HANDLE_CODE(tunnel_info_for_bbf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tunnel_info_for_bbf.value.unpack(bref)); break; - case 264: + } + case 264: { secondary_rat_data_usage_report_list_present = true; - secondary_rat_data_usage_report_list.id = c.id; - secondary_rat_data_usage_report_list.crit = c.crit; - secondary_rat_data_usage_report_list.value = c.value.secondary_rat_data_usage_report_list(); + secondary_rat_data_usage_report_list.id = id; + HANDLE_CODE(secondary_rat_data_usage_report_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_report_list.value.unpack(bref)); break; - case 189: + } + case 189: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -13270,29 +12966,6 @@ void erab_mod_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABModificationIndication ::= SEQUENCE -SRSASN_CODE erab_mod_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_mod_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_mod_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABModifyItemBearerModRes ::= SEQUENCE SRSASN_CODE erab_modify_item_bearer_mod_res_s::pack(bit_ref& bref) const { @@ -13391,7 +13064,7 @@ const char* erab_modify_item_bearer_mod_res_ies_o::value_c::types_opts::to_strin return convert_enum_idx(options, 1, value, "erab_modify_item_bearer_mod_res_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // TransportInformation ::= SEQUENCE SRSASN_CODE transport_info_s::pack(bit_ref& bref) const @@ -13764,7 +13437,7 @@ const char* ue_aggregate_maximum_bitrates_ext_ies_o::ext_c::types_opts::to_strin return convert_enum_idx(options, 2, value, "ue_aggregate_maximum_bitrates_ext_ies_o::ext_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // SecondaryRATDataUsageRequest ::= ENUMERATED const char* secondary_rat_data_usage_request_opts::to_string() const @@ -13773,7 +13446,7 @@ const char* secondary_rat_data_usage_request_opts::to_string() const return convert_enum_idx(options, 1, value, "secondary_rat_data_usage_request_e"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; ue_aggregate_maximum_bitrates_ext_ies_container::ue_aggregate_maximum_bitrates_ext_ies_container() : extended_u_eaggregate_maximum_bit_rate_dl(259, crit_e::ignore), @@ -13801,23 +13474,27 @@ SRSASN_CODE ue_aggregate_maximum_bitrates_ext_ies_container::unpack(cbit_ref& br unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 259: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 259: { extended_u_eaggregate_maximum_bit_rate_dl_present = true; - extended_u_eaggregate_maximum_bit_rate_dl.id = c.id; - extended_u_eaggregate_maximum_bit_rate_dl.crit = c.crit; - extended_u_eaggregate_maximum_bit_rate_dl.ext = c.ext_value.extended_u_eaggregate_maximum_bit_rate_dl(); + extended_u_eaggregate_maximum_bit_rate_dl.id = id; + HANDLE_CODE(extended_u_eaggregate_maximum_bit_rate_dl.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(extended_u_eaggregate_maximum_bit_rate_dl.ext.unpack(bref)); break; - case 260: + } + case 260: { extended_u_eaggregate_maximum_bit_rate_ul_present = true; - extended_u_eaggregate_maximum_bit_rate_ul.id = c.id; - extended_u_eaggregate_maximum_bit_rate_ul.crit = c.crit; - extended_u_eaggregate_maximum_bit_rate_ul.ext = c.ext_value.extended_u_eaggregate_maximum_bit_rate_ul(); + extended_u_eaggregate_maximum_bit_rate_ul.id = id; + HANDLE_CODE(extended_u_eaggregate_maximum_bit_rate_ul.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(extended_u_eaggregate_maximum_bit_rate_ul.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -14188,7 +13865,7 @@ const char* erab_modify_request_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 5, value, "erab_modify_request_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; erab_modify_request_ies_container::erab_modify_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -14224,41 +13901,51 @@ SRSASN_CODE erab_modify_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 66: + } + case 66: { ueaggregate_maximum_bitrate_present = true; - ueaggregate_maximum_bitrate.id = c.id; - ueaggregate_maximum_bitrate.crit = c.crit; - ueaggregate_maximum_bitrate.value = c.value.ueaggregate_maximum_bitrate(); + ueaggregate_maximum_bitrate.id = id; + HANDLE_CODE(ueaggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueaggregate_maximum_bitrate.value.unpack(bref)); break; - case 30: + } + case 30: { nof_mandatory_ies--; - erab_to_be_modified_list_bearer_mod_req.id = c.id; - erab_to_be_modified_list_bearer_mod_req.crit = c.crit; - erab_to_be_modified_list_bearer_mod_req.value = c.value.erab_to_be_modified_list_bearer_mod_req(); + erab_to_be_modified_list_bearer_mod_req.id = id; + HANDLE_CODE(erab_to_be_modified_list_bearer_mod_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_modified_list_bearer_mod_req.value.unpack(bref)); break; - case 268: + } + case 268: { secondary_rat_data_usage_request_present = true; - secondary_rat_data_usage_request.id = c.id; - secondary_rat_data_usage_request.crit = c.crit; - secondary_rat_data_usage_request.value = c.value.secondary_rat_data_usage_request(); + secondary_rat_data_usage_request.id = id; + HANDLE_CODE(secondary_rat_data_usage_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_request.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -14289,29 +13976,6 @@ void erab_modify_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABModifyRequest ::= SEQUENCE -SRSASN_CODE erab_modify_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_modify_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_modify_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABModifyResponseIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t erab_modify_resp_ies_o::idx_to_id(uint32_t idx) { @@ -14674,8 +14338,7 @@ const char* erab_modify_resp_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 6, value, "erab_modify_resp_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_field_s; erab_modify_resp_ies_container::erab_modify_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -14719,47 +14382,59 @@ SRSASN_CODE erab_modify_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 31: + } + case 31: { erab_modify_list_bearer_mod_res_present = true; - erab_modify_list_bearer_mod_res.id = c.id; - erab_modify_list_bearer_mod_res.crit = c.crit; - erab_modify_list_bearer_mod_res.value = c.value.erab_modify_list_bearer_mod_res(); + erab_modify_list_bearer_mod_res.id = id; + HANDLE_CODE(erab_modify_list_bearer_mod_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_modify_list_bearer_mod_res.value.unpack(bref)); break; - case 32: + } + case 32: { erab_failed_to_modify_list_present = true; - erab_failed_to_modify_list.id = c.id; - erab_failed_to_modify_list.crit = c.crit; - erab_failed_to_modify_list.value = c.value.erab_failed_to_modify_list(); + erab_failed_to_modify_list.id = id; + HANDLE_CODE(erab_failed_to_modify_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_failed_to_modify_list.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 264: + } + case 264: { secondary_rat_data_usage_report_list_present = true; - secondary_rat_data_usage_report_list.id = c.id; - secondary_rat_data_usage_report_list.crit = c.crit; - secondary_rat_data_usage_report_list.value = c.value.secondary_rat_data_usage_report_list(); + secondary_rat_data_usage_report_list.id = id; + HANDLE_CODE(secondary_rat_data_usage_report_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_report_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -14796,29 +14471,6 @@ void erab_modify_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABModifyResponse ::= SEQUENCE -SRSASN_CODE erab_modify_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_modify_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_modify_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABReleaseCommandIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t erab_release_cmd_ies_o::idx_to_id(uint32_t idx) { @@ -15130,7 +14782,7 @@ const char* erab_release_cmd_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 5, value, "erab_release_cmd_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; erab_release_cmd_ies_container::erab_release_cmd_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -15166,41 +14818,51 @@ SRSASN_CODE erab_release_cmd_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 66: + } + case 66: { ueaggregate_maximum_bitrate_present = true; - ueaggregate_maximum_bitrate.id = c.id; - ueaggregate_maximum_bitrate.crit = c.crit; - ueaggregate_maximum_bitrate.value = c.value.ueaggregate_maximum_bitrate(); + ueaggregate_maximum_bitrate.id = id; + HANDLE_CODE(ueaggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueaggregate_maximum_bitrate.value.unpack(bref)); break; - case 33: + } + case 33: { nof_mandatory_ies--; - erab_to_be_released_list.id = c.id; - erab_to_be_released_list.crit = c.crit; - erab_to_be_released_list.value = c.value.erab_to_be_released_list(); + erab_to_be_released_list.id = id; + HANDLE_CODE(erab_to_be_released_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_released_list.value.unpack(bref)); break; - case 26: + } + case 26: { nas_pdu_present = true; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -15231,29 +14893,6 @@ void erab_release_cmd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABReleaseCommand ::= SEQUENCE -SRSASN_CODE erab_release_cmd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_release_cmd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_release_cmd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABReleaseIndicationIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t erab_release_ind_ies_o::idx_to_id(uint32_t idx) { @@ -15573,7 +15212,7 @@ const char* erab_release_ind_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 5, value, "erab_release_ind_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; erab_release_ind_ies_container::erab_release_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -15609,41 +15248,51 @@ SRSASN_CODE erab_release_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 110: + } + case 110: { nof_mandatory_ies--; - erab_released_list.id = c.id; - erab_released_list.crit = c.crit; - erab_released_list.value = c.value.erab_released_list(); + erab_released_list.id = id; + HANDLE_CODE(erab_released_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_released_list.value.unpack(bref)); break; - case 189: + } + case 189: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 264: + } + case 264: { secondary_rat_data_usage_report_list_present = true; - secondary_rat_data_usage_report_list.id = c.id; - secondary_rat_data_usage_report_list.crit = c.crit; - secondary_rat_data_usage_report_list.value = c.value.secondary_rat_data_usage_report_list(); + secondary_rat_data_usage_report_list.id = id; + HANDLE_CODE(secondary_rat_data_usage_report_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_report_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -15674,29 +15323,6 @@ void erab_release_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABReleaseIndication ::= SEQUENCE -SRSASN_CODE erab_release_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_release_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_release_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABReleaseItemBearerRelComp ::= SEQUENCE SRSASN_CODE erab_release_item_bearer_rel_comp_s::pack(bit_ref& bref) const { @@ -15795,7 +15421,7 @@ const char* erab_release_item_bearer_rel_comp_ies_o::value_c::types_opts::to_str return convert_enum_idx(options, 1, value, "erab_release_item_bearer_rel_comp_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // E-RABReleaseResponseIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t erab_release_resp_ies_o::idx_to_id(uint32_t idx) @@ -16199,7 +15825,7 @@ const char* erab_release_resp_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 7, value, "erab_release_resp_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; erab_release_resp_ies_container::erab_release_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -16248,53 +15874,67 @@ SRSASN_CODE erab_release_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 69: + } + case 69: { erab_release_list_bearer_rel_comp_present = true; - erab_release_list_bearer_rel_comp.id = c.id; - erab_release_list_bearer_rel_comp.crit = c.crit; - erab_release_list_bearer_rel_comp.value = c.value.erab_release_list_bearer_rel_comp(); + erab_release_list_bearer_rel_comp.id = id; + HANDLE_CODE(erab_release_list_bearer_rel_comp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_release_list_bearer_rel_comp.value.unpack(bref)); break; - case 34: + } + case 34: { erab_failed_to_release_list_present = true; - erab_failed_to_release_list.id = c.id; - erab_failed_to_release_list.crit = c.crit; - erab_failed_to_release_list.value = c.value.erab_failed_to_release_list(); + erab_failed_to_release_list.id = id; + HANDLE_CODE(erab_failed_to_release_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_failed_to_release_list.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 189: + } + case 189: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 264: + } + case 264: { secondary_rat_data_usage_report_list_present = true; - secondary_rat_data_usage_report_list.id = c.id; - secondary_rat_data_usage_report_list.crit = c.crit; - secondary_rat_data_usage_report_list.value = c.value.secondary_rat_data_usage_report_list(); + secondary_rat_data_usage_report_list.id = id; + HANDLE_CODE(secondary_rat_data_usage_report_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_report_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -16335,29 +15975,6 @@ void erab_release_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABReleaseResponse ::= SEQUENCE -SRSASN_CODE erab_release_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_release_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_release_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABSetupItemBearerSURes ::= SEQUENCE SRSASN_CODE erab_setup_item_bearer_su_res_s::pack(bit_ref& bref) const { @@ -16566,9 +16183,9 @@ const char* erab_setup_item_ctxt_su_res_ies_o::value_c::types_opts::to_string() return convert_enum_idx(options, 1, value, "erab_setup_item_ctxt_su_res_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // BearerType ::= ENUMERATED const char* bearer_type_opts::to_string() const @@ -16812,7 +16429,7 @@ const char* erab_to_be_setup_item_bearer_su_req_ext_ies_o::ext_c::types_opts::to return convert_enum_idx(options, 3, value, "erab_to_be_setup_item_bearer_su_req_ext_ies_o::ext_c::types"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; erab_to_be_setup_item_bearer_su_req_ext_ies_container::erab_to_be_setup_item_bearer_su_req_ext_ies_container() : correlation_id(156, crit_e::ignore), sipto_correlation_id(183, crit_e::ignore), bearer_type(233, crit_e::reject) @@ -16843,29 +16460,35 @@ SRSASN_CODE erab_to_be_setup_item_bearer_su_req_ext_ies_container::unpack(cbit_r unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 156: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 156: { correlation_id_present = true; - correlation_id.id = c.id; - correlation_id.crit = c.crit; - correlation_id.ext = c.ext_value.correlation_id(); + correlation_id.id = id; + HANDLE_CODE(correlation_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(correlation_id.ext.unpack(bref)); break; - case 183: + } + case 183: { sipto_correlation_id_present = true; - sipto_correlation_id.id = c.id; - sipto_correlation_id.crit = c.crit; - sipto_correlation_id.ext = c.ext_value.sipto_correlation_id(); + sipto_correlation_id.id = id; + HANDLE_CODE(sipto_correlation_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(sipto_correlation_id.ext.unpack(bref)); break; - case 233: + } + case 233: { bearer_type_present = true; - bearer_type.id = c.id; - bearer_type.crit = c.crit; - bearer_type.ext = c.ext_value.bearer_type(); + bearer_type.id = id; + HANDLE_CODE(bearer_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(bearer_type.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -17002,7 +16625,7 @@ const char* erab_to_be_setup_item_bearer_su_req_ies_o::value_c::types_opts::to_s return convert_enum_idx(options, 1, value, "erab_to_be_setup_item_bearer_su_req_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // E-RABSetupRequestIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t erab_setup_request_ies_o::idx_to_id(uint32_t idx) @@ -17278,7 +16901,7 @@ const char* erab_setup_request_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 4, value, "erab_setup_request_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; erab_setup_request_ies_container::erab_setup_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -17309,35 +16932,43 @@ SRSASN_CODE erab_setup_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 66: + } + case 66: { ueaggregate_maximum_bitrate_present = true; - ueaggregate_maximum_bitrate.id = c.id; - ueaggregate_maximum_bitrate.crit = c.crit; - ueaggregate_maximum_bitrate.value = c.value.ueaggregate_maximum_bitrate(); + ueaggregate_maximum_bitrate.id = id; + HANDLE_CODE(ueaggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueaggregate_maximum_bitrate.value.unpack(bref)); break; - case 16: + } + case 16: { nof_mandatory_ies--; - erab_to_be_setup_list_bearer_su_req.id = c.id; - erab_to_be_setup_list_bearer_su_req.crit = c.crit; - erab_to_be_setup_list_bearer_su_req.value = c.value.erab_to_be_setup_list_bearer_su_req(); + erab_to_be_setup_list_bearer_su_req.id = id; + HANDLE_CODE(erab_to_be_setup_list_bearer_su_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_setup_list_bearer_su_req.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -17364,29 +16995,6 @@ void erab_setup_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABSetupRequest ::= SEQUENCE -SRSASN_CODE erab_setup_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_setup_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_setup_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABSetupResponseIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t erab_setup_resp_ies_o::idx_to_id(uint32_t idx) { @@ -17704,7 +17312,7 @@ const char* erab_setup_resp_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 5, value, "erab_setup_resp_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; erab_setup_resp_ies_container::erab_setup_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -17743,41 +17351,51 @@ SRSASN_CODE erab_setup_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 28: + } + case 28: { erab_setup_list_bearer_su_res_present = true; - erab_setup_list_bearer_su_res.id = c.id; - erab_setup_list_bearer_su_res.crit = c.crit; - erab_setup_list_bearer_su_res.value = c.value.erab_setup_list_bearer_su_res(); + erab_setup_list_bearer_su_res.id = id; + HANDLE_CODE(erab_setup_list_bearer_su_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_setup_list_bearer_su_res.value.unpack(bref)); break; - case 29: + } + case 29: { erab_failed_to_setup_list_bearer_su_res_present = true; - erab_failed_to_setup_list_bearer_su_res.id = c.id; - erab_failed_to_setup_list_bearer_su_res.crit = c.crit; - erab_failed_to_setup_list_bearer_su_res.value = c.value.erab_failed_to_setup_list_bearer_su_res(); + erab_failed_to_setup_list_bearer_su_res.id = id; + HANDLE_CODE(erab_failed_to_setup_list_bearer_su_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_failed_to_setup_list_bearer_su_res.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -17810,29 +17428,6 @@ void erab_setup_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// E-RABSetupResponse ::= SEQUENCE -SRSASN_CODE erab_setup_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE erab_setup_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void erab_setup_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // E-RABToBeSetupItemCtxtSUReqExtIEs ::= OBJECT SET OF S1AP-PROTOCOL-EXTENSION uint32_t erab_to_be_setup_item_ctxt_su_req_ext_ies_o::idx_to_id(uint32_t idx) { @@ -18068,7 +17663,7 @@ const char* erab_to_be_setup_item_ctxt_su_req_ext_ies_o::ext_c::types_opts::to_s return convert_enum_idx(options, 3, value, "erab_to_be_setup_item_ctxt_su_req_ext_ies_o::ext_c::types"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; erab_to_be_setup_item_ctxt_su_req_ext_ies_container::erab_to_be_setup_item_ctxt_su_req_ext_ies_container() : correlation_id(156, crit_e::ignore), sipto_correlation_id(183, crit_e::ignore), bearer_type(233, crit_e::reject) @@ -18099,29 +17694,35 @@ SRSASN_CODE erab_to_be_setup_item_ctxt_su_req_ext_ies_container::unpack(cbit_ref unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 156: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 156: { correlation_id_present = true; - correlation_id.id = c.id; - correlation_id.crit = c.crit; - correlation_id.ext = c.ext_value.correlation_id(); + correlation_id.id = id; + HANDLE_CODE(correlation_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(correlation_id.ext.unpack(bref)); break; - case 183: + } + case 183: { sipto_correlation_id_present = true; - sipto_correlation_id.id = c.id; - sipto_correlation_id.crit = c.crit; - sipto_correlation_id.ext = c.ext_value.sipto_correlation_id(); + sipto_correlation_id.id = id; + HANDLE_CODE(sipto_correlation_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(sipto_correlation_id.ext.unpack(bref)); break; - case 233: + } + case 233: { bearer_type_present = true; - bearer_type.id = c.id; - bearer_type.crit = c.crit; - bearer_type.ext = c.ext_value.bearer_type(); + bearer_type.id = id; + HANDLE_CODE(bearer_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(bearer_type.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -18448,7 +18049,7 @@ const char* erab_to_be_setup_item_ho_req_ext_ies_o::ext_c::types_opts::to_string return convert_enum_idx(options, 2, value, "erab_to_be_setup_item_ho_req_ext_ies_o::ext_c::types"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; erab_to_be_setup_item_ho_req_ext_ies_container::erab_to_be_setup_item_ho_req_ext_ies_container() : data_forwarding_not_possible(143, crit_e::ignore), bearer_type(233, crit_e::reject) @@ -18475,23 +18076,27 @@ SRSASN_CODE erab_to_be_setup_item_ho_req_ext_ies_container::unpack(cbit_ref& bre unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 143: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 143: { data_forwarding_not_possible_present = true; - data_forwarding_not_possible.id = c.id; - data_forwarding_not_possible.crit = c.crit; - data_forwarding_not_possible.ext = c.ext_value.data_forwarding_not_possible(); + data_forwarding_not_possible.id = id; + HANDLE_CODE(data_forwarding_not_possible.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(data_forwarding_not_possible.ext.unpack(bref)); break; - case 233: + } + case 233: { bearer_type_present = true; - bearer_type.id = c.id; - bearer_type.crit = c.crit; - bearer_type.ext = c.ext_value.bearer_type(); + bearer_type.id = id; + HANDLE_CODE(bearer_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(bearer_type.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -18620,7 +18225,7 @@ const char* erab_to_be_setup_item_ho_req_ies_o::value_c::types_opts::to_string() return convert_enum_idx(options, 1, value, "erab_to_be_setup_item_ho_req_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // E-RABToBeSwitchedDLItem ::= SEQUENCE SRSASN_CODE erab_to_be_switched_dl_item_s::pack(bit_ref& bref) const @@ -19658,7 +19263,7 @@ const char* son_info_reply_ext_ies_o::ext_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "son_info_reply_ext_ies_o::ext_c::types"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; x2_tnl_cfg_info_ext_ies_container::x2_tnl_cfg_info_ext_ies_container() : enbx2_extended_transport_layer_addresses(153, crit_e::ignore), @@ -19686,23 +19291,27 @@ SRSASN_CODE x2_tnl_cfg_info_ext_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 153: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 153: { enbx2_extended_transport_layer_addresses_present = true; - enbx2_extended_transport_layer_addresses.id = c.id; - enbx2_extended_transport_layer_addresses.crit = c.crit; - enbx2_extended_transport_layer_addresses.ext = c.ext_value.enbx2_extended_transport_layer_addresses(); + enbx2_extended_transport_layer_addresses.id = id; + HANDLE_CODE(enbx2_extended_transport_layer_addresses.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enbx2_extended_transport_layer_addresses.ext.unpack(bref)); break; - case 193: + } + case 193: { enb_indirect_x2_transport_layer_addresses_present = true; - enb_indirect_x2_transport_layer_addresses.id = c.id; - enb_indirect_x2_transport_layer_addresses.crit = c.crit; - enb_indirect_x2_transport_layer_addresses.ext = c.ext_value.enb_indirect_x2_transport_layer_addresses(); + enb_indirect_x2_transport_layer_addresses.id = id; + HANDLE_CODE(enb_indirect_x2_transport_layer_addresses.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_indirect_x2_transport_layer_addresses.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -19881,7 +19490,7 @@ void en_dc_transfer_type_request_s::to_json(json_writer& j) const j.end_obj(); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // SONInformationReply ::= SEQUENCE SRSASN_CODE son_info_reply_s::pack(bit_ref& bref) const @@ -20725,7 +20334,7 @@ uint8_t enbcp_relocation_ind_ies_o::value_c::types_opts::to_number() const return map_enum_number(options, 1, value, "enbcp_relocation_ind_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; enbcp_relocation_ind_ies_container::enbcp_relocation_ind_ies_container() : enb_ue_s1ap_id(8, crit_e::reject), @@ -20755,41 +20364,51 @@ SRSASN_CODE enbcp_relocation_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 8: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 96: + } + case 96: { nof_mandatory_ies--; - s_tmsi.id = c.id; - s_tmsi.crit = c.crit; - s_tmsi.value = c.value.s_tmsi(); + s_tmsi.id = id; + HANDLE_CODE(s_tmsi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(s_tmsi.value.unpack(bref)); break; - case 100: + } + case 100: { nof_mandatory_ies--; - eutran_cgi.id = c.id; - eutran_cgi.crit = c.crit; - eutran_cgi.value = c.value.eutran_cgi(); + eutran_cgi.id = id; + HANDLE_CODE(eutran_cgi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(eutran_cgi.value.unpack(bref)); break; - case 67: + } + case 67: { nof_mandatory_ies--; - tai.id = c.id; - tai.crit = c.crit; - tai.value = c.value.tai(); + tai.id = id; + HANDLE_CODE(tai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai.value.unpack(bref)); break; - case 254: + } + case 254: { nof_mandatory_ies--; - ul_cp_security_info.id = c.id; - ul_cp_security_info.crit = c.crit; - ul_cp_security_info.value = c.value.ul_cp_security_info(); + ul_cp_security_info.id = id; + HANDLE_CODE(ul_cp_security_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ul_cp_security_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -20816,29 +20435,6 @@ void enbcp_relocation_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ENBCPRelocationIndication ::= SEQUENCE -SRSASN_CODE enbcp_relocation_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE enbcp_relocation_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void enbcp_relocation_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // ListeningSubframePattern ::= SEQUENCE SRSASN_CODE listening_sf_pattern_s::pack(bit_ref& bref) const { @@ -21246,7 +20842,7 @@ void targetenb_id_s::to_json(json_writer& j) const j.end_obj(); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; son_cfg_transfer_ext_ies_container::son_cfg_transfer_ext_ies_container() : x2_tnl_cfg_info(152, crit_e::ignore), synchronisation_info(209, crit_e::ignore) @@ -21273,23 +20869,27 @@ SRSASN_CODE son_cfg_transfer_ext_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 152: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 152: { x2_tnl_cfg_info_present = true; - x2_tnl_cfg_info.id = c.id; - x2_tnl_cfg_info.crit = c.crit; - x2_tnl_cfg_info.ext = c.ext_value.x2_tnl_cfg_info(); + x2_tnl_cfg_info.id = id; + HANDLE_CODE(x2_tnl_cfg_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(x2_tnl_cfg_info.ext.unpack(bref)); break; - case 209: + } + case 209: { synchronisation_info_present = true; - synchronisation_info.id = c.id; - synchronisation_info.crit = c.crit; - synchronisation_info.ext = c.ext_value.synchronisation_info(); + synchronisation_info.id = id; + HANDLE_CODE(synchronisation_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(synchronisation_info.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -21556,7 +21156,7 @@ const char* enb_cfg_transfer_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 2, value, "enb_cfg_transfer_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; enb_cfg_transfer_ies_container::enb_cfg_transfer_ies_container() : son_cfg_transfer_ect(129, crit_e::ignore), en_dcson_cfg_transfer_ect(294, crit_e::ignore) @@ -21583,23 +21183,27 @@ SRSASN_CODE enb_cfg_transfer_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 129: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 129: { son_cfg_transfer_ect_present = true; - son_cfg_transfer_ect.id = c.id; - son_cfg_transfer_ect.crit = c.crit; - son_cfg_transfer_ect.value = c.value.son_cfg_transfer_ect(); + son_cfg_transfer_ect.id = id; + HANDLE_CODE(son_cfg_transfer_ect.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(son_cfg_transfer_ect.value.unpack(bref)); break; - case 294: + } + case 294: { en_dcson_cfg_transfer_ect_present = true; - en_dcson_cfg_transfer_ect.id = c.id; - en_dcson_cfg_transfer_ect.crit = c.crit; - en_dcson_cfg_transfer_ect.value = c.value.en_dcson_cfg_transfer_ect(); + en_dcson_cfg_transfer_ect.id = id; + HANDLE_CODE(en_dcson_cfg_transfer_ect.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(en_dcson_cfg_transfer_ect.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -21620,29 +21224,6 @@ void enb_cfg_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ENBConfigurationTransfer ::= SEQUENCE -SRSASN_CODE enb_cfg_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE enb_cfg_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void enb_cfg_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // NB-IoT-DefaultPagingDRX ::= ENUMERATED const char* nb_io_t_default_paging_drx_opts::to_string() const { @@ -22069,7 +21650,7 @@ const char* enb_cfg_upd_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 7, value, "enb_cfg_upd_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; enb_cfg_upd_ies_container::enb_cfg_upd_ies_container() : enbname(60, crit_e::ignore), @@ -22122,53 +21703,67 @@ SRSASN_CODE enb_cfg_upd_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 60: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 60: { enbname_present = true; - enbname.id = c.id; - enbname.crit = c.crit; - enbname.value = c.value.enbname(); + enbname.id = id; + HANDLE_CODE(enbname.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enbname.value.unpack(bref)); break; - case 64: + } + case 64: { supported_tas_present = true; - supported_tas.id = c.id; - supported_tas.crit = c.crit; - supported_tas.value = c.value.supported_tas(); + supported_tas.id = id; + HANDLE_CODE(supported_tas.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(supported_tas.value.unpack(bref)); break; - case 128: + } + case 128: { csg_id_list_present = true; - csg_id_list.id = c.id; - csg_id_list.crit = c.crit; - csg_id_list.value = c.value.csg_id_list(); + csg_id_list.id = id; + HANDLE_CODE(csg_id_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_id_list.value.unpack(bref)); break; - case 137: + } + case 137: { default_paging_drx_present = true; - default_paging_drx.id = c.id; - default_paging_drx.crit = c.crit; - default_paging_drx.value = c.value.default_paging_drx(); + default_paging_drx.id = id; + HANDLE_CODE(default_paging_drx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(default_paging_drx.value.unpack(bref)); break; - case 234: + } + case 234: { nb_io_t_default_paging_drx_present = true; - nb_io_t_default_paging_drx.id = c.id; - nb_io_t_default_paging_drx.crit = c.crit; - nb_io_t_default_paging_drx.value = c.value.nb_io_t_default_paging_drx(); + nb_io_t_default_paging_drx.id = id; + HANDLE_CODE(nb_io_t_default_paging_drx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nb_io_t_default_paging_drx.value.unpack(bref)); break; - case 292: + } + case 292: { connectedeng_nb_to_add_list_present = true; - connectedeng_nb_to_add_list.id = c.id; - connectedeng_nb_to_add_list.crit = c.crit; - connectedeng_nb_to_add_list.value = c.value.connectedeng_nb_to_add_list(); + connectedeng_nb_to_add_list.id = id; + HANDLE_CODE(connectedeng_nb_to_add_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(connectedeng_nb_to_add_list.value.unpack(bref)); break; - case 293: + } + case 293: { connectedeng_nb_to_rem_list_present = true; - connectedeng_nb_to_rem_list.id = c.id; - connectedeng_nb_to_rem_list.crit = c.crit; - connectedeng_nb_to_rem_list.value = c.value.connectedeng_nb_to_rem_list(); + connectedeng_nb_to_rem_list.id = id; + HANDLE_CODE(connectedeng_nb_to_rem_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(connectedeng_nb_to_rem_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -22209,29 +21804,6 @@ void enb_cfg_upd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ENBConfigurationUpdate ::= SEQUENCE -SRSASN_CODE enb_cfg_upd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE enb_cfg_upd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void enb_cfg_upd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // ENBConfigurationUpdateAcknowledgeIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t enb_cfg_upd_ack_ies_o::idx_to_id(uint32_t idx) { @@ -22294,28 +21866,6 @@ const char* enb_cfg_upd_ack_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "enb_cfg_upd_ack_ies_o::value_c::types"); } -// ENBConfigurationUpdateAcknowledge ::= SEQUENCE -SRSASN_CODE enb_cfg_upd_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(pack_dyn_seq_of(bref, protocol_ies, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE enb_cfg_upd_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(unpack_dyn_seq_of(protocol_ies, bref, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -void enb_cfg_upd_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - j.end_obj(); -} - // TimeToWait ::= ENUMERATED const char* time_to_wait_opts::to_string() const { @@ -22563,7 +22113,7 @@ const char* enb_cfg_upd_fail_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "enb_cfg_upd_fail_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; enb_cfg_upd_fail_ies_container::enb_cfg_upd_fail_ies_container() : cause(2, crit_e::ignore), time_to_wait(65, crit_e::ignore), crit_diagnostics(58, crit_e::ignore) @@ -22593,29 +22143,35 @@ SRSASN_CODE enb_cfg_upd_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 1; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 2: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 65: + } + case 65: { time_to_wait_present = true; - time_to_wait.id = c.id; - time_to_wait.crit = c.crit; - time_to_wait.value = c.value.time_to_wait(); + time_to_wait.id = id; + HANDLE_CODE(time_to_wait.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_to_wait.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -22642,29 +22198,6 @@ void enb_cfg_upd_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ENBConfigurationUpdateFailure ::= SEQUENCE -SRSASN_CODE enb_cfg_upd_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE enb_cfg_upd_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void enb_cfg_upd_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // LAI ::= SEQUENCE SRSASN_CODE lai_s::pack(bit_ref& bref) const { @@ -23117,28 +22650,6 @@ const char* enb_direct_info_transfer_ies_o::value_c::types_opts::to_string() con return convert_enum_idx(options, 1, value, "enb_direct_info_transfer_ies_o::value_c::types"); } -// ENBDirectInformationTransfer ::= SEQUENCE -SRSASN_CODE enb_direct_info_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(pack_dyn_seq_of(bref, protocol_ies, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE enb_direct_info_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(unpack_dyn_seq_of(protocol_ies, bref, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -void enb_direct_info_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - j.end_obj(); -} - // ENBStatusTransferIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t enb_status_transfer_ies_o::idx_to_id(uint32_t idx) { @@ -23372,7 +22883,7 @@ const char* enb_status_transfer_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "enb_status_transfer_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; enb_status_transfer_ies_container::enb_status_transfer_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -23398,29 +22909,35 @@ SRSASN_CODE enb_status_transfer_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 90: + } + case 90: { nof_mandatory_ies--; - enb_status_transfer_transparent_container.id = c.id; - enb_status_transfer_transparent_container.crit = c.crit; - enb_status_transfer_transparent_container.value = c.value.enb_status_transfer_transparent_container(); + enb_status_transfer_transparent_container.id = id; + HANDLE_CODE(enb_status_transfer_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_status_transfer_transparent_container.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -23443,29 +22960,6 @@ void enb_status_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ENBStatusTransfer ::= SEQUENCE -SRSASN_CODE enb_status_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE enb_status_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void enb_status_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // EUTRANResponse ::= SEQUENCE SRSASN_CODE eutran_resp_s::pack(bit_ref& bref) const { @@ -23800,7 +23294,7 @@ const char* error_ind_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 5, value, "error_ind_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; error_ind_ies_container::error_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -23843,41 +23337,51 @@ SRSASN_CODE error_ind_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { mme_ue_s1ap_id_present = true; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { enb_ue_s1ap_id_present = true; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { cause_present = true; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 96: + } + case 96: { s_tmsi_present = true; - s_tmsi.id = c.id; - s_tmsi.crit = c.crit; - s_tmsi.value = c.value.s_tmsi(); + s_tmsi.id = id; + HANDLE_CODE(s_tmsi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(s_tmsi.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -23910,29 +23414,6 @@ void error_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ErrorIndication ::= SEQUENCE -SRSASN_CODE error_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE error_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void error_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // NumberOfMeasurementReportingLevels ::= ENUMERATED const char* nof_meas_report_levels_opts::to_string() const { @@ -24526,7 +24007,7 @@ const char* ho_cancel_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "ho_cancel_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_cancel_ies_container::ho_cancel_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject), cause(2, crit_e::ignore) @@ -24550,29 +24031,35 @@ SRSASN_CODE ho_cancel_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -24595,29 +24082,6 @@ void ho_cancel_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverCancel ::= SEQUENCE -SRSASN_CODE ho_cancel_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_cancel_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_cancel_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverCancelAcknowledgeIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t ho_cancel_ack_ies_o::idx_to_id(uint32_t idx) { @@ -24847,7 +24311,7 @@ const char* ho_cancel_ack_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 3, value, "ho_cancel_ack_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_cancel_ack_ies_container::ho_cancel_ack_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), enb_ue_s1ap_id(8, crit_e::ignore), crit_diagnostics(58, crit_e::ignore) @@ -24874,29 +24338,35 @@ SRSASN_CODE ho_cancel_ack_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -24921,29 +24391,6 @@ void ho_cancel_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverCancelAcknowledge ::= SEQUENCE -SRSASN_CODE ho_cancel_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_cancel_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_cancel_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverType ::= ENUMERATED const char* handov_type_opts::to_string() const { @@ -25432,7 +24879,7 @@ const char* ho_cmd_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 9, value, "ho_cmd_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_cmd_ies_container::ho_cmd_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -25485,66 +24932,83 @@ SRSASN_CODE ho_cmd_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 1: + } + case 1: { nof_mandatory_ies--; - handov_type.id = c.id; - handov_type.crit = c.crit; - handov_type.value = c.value.handov_type(); + handov_type.id = id; + HANDLE_CODE(handov_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(handov_type.value.unpack(bref)); break; - case 135: + } + case 135: { nas_security_paramsfrom_e_utran_present = true; - nas_security_paramsfrom_e_utran.id = c.id; - nas_security_paramsfrom_e_utran.crit = c.crit; - nas_security_paramsfrom_e_utran.value = c.value.nas_security_paramsfrom_e_utran(); + nas_security_paramsfrom_e_utran.id = id; + HANDLE_CODE(nas_security_paramsfrom_e_utran.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_security_paramsfrom_e_utran.value.unpack(bref)); break; - case 12: + } + case 12: { erab_subjectto_data_forwarding_list_present = true; - erab_subjectto_data_forwarding_list.id = c.id; - erab_subjectto_data_forwarding_list.crit = c.crit; - erab_subjectto_data_forwarding_list.value = c.value.erab_subjectto_data_forwarding_list(); + erab_subjectto_data_forwarding_list.id = id; + HANDLE_CODE(erab_subjectto_data_forwarding_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_subjectto_data_forwarding_list.value.unpack(bref)); break; - case 13: + } + case 13: { erab_to_release_list_ho_cmd_present = true; - erab_to_release_list_ho_cmd.id = c.id; - erab_to_release_list_ho_cmd.crit = c.crit; - erab_to_release_list_ho_cmd.value = c.value.erab_to_release_list_ho_cmd(); + erab_to_release_list_ho_cmd.id = id; + HANDLE_CODE(erab_to_release_list_ho_cmd.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_release_list_ho_cmd.value.unpack(bref)); break; - case 123: + } + case 123: { nof_mandatory_ies--; - target_to_source_transparent_container.id = c.id; - target_to_source_transparent_container.crit = c.crit; - target_to_source_transparent_container.value = c.value.target_to_source_transparent_container(); + target_to_source_transparent_container.id = id; + HANDLE_CODE(target_to_source_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(target_to_source_transparent_container.value.unpack(bref)); break; - case 139: + } + case 139: { target_to_source_transparent_container_secondary_present = true; - target_to_source_transparent_container_secondary.id = c.id; - target_to_source_transparent_container_secondary.crit = c.crit; - target_to_source_transparent_container_secondary.value = - c.value.target_to_source_transparent_container_secondary(); + target_to_source_transparent_container_secondary.id = id; + HANDLE_CODE(target_to_source_transparent_container_secondary.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(target_to_source_transparent_container_secondary.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -25589,29 +25053,6 @@ void ho_cmd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverCommand ::= SEQUENCE -SRSASN_CODE ho_cmd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_cmd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_cmd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverFailureIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t ho_fail_ies_o::idx_to_id(uint32_t idx) { @@ -25851,7 +25292,7 @@ uint8_t ho_fail_ies_o::value_c::types_opts::to_number() const return map_enum_number(options, 1, value, "ho_fail_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_fail_ies_container::ho_fail_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), cause(2, crit_e::ignore), crit_diagnostics(58, crit_e::ignore) @@ -25878,29 +25319,35 @@ SRSASN_CODE ho_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -25925,29 +25372,6 @@ void ho_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverFailure ::= SEQUENCE -SRSASN_CODE ho_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverNotifyIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t ho_notify_ies_o::idx_to_id(uint32_t idx) { @@ -26338,7 +25762,7 @@ const char* ho_notify_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 7, value, "ho_notify_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_notify_ies_container::ho_notify_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -26381,53 +25805,67 @@ SRSASN_CODE ho_notify_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 100: + } + case 100: { nof_mandatory_ies--; - eutran_cgi.id = c.id; - eutran_cgi.crit = c.crit; - eutran_cgi.value = c.value.eutran_cgi(); + eutran_cgi.id = id; + HANDLE_CODE(eutran_cgi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(eutran_cgi.value.unpack(bref)); break; - case 67: + } + case 67: { nof_mandatory_ies--; - tai.id = c.id; - tai.crit = c.crit; - tai.value = c.value.tai(); + tai.id = id; + HANDLE_CODE(tai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai.value.unpack(bref)); break; - case 176: + } + case 176: { tunnel_info_for_bbf_present = true; - tunnel_info_for_bbf.id = c.id; - tunnel_info_for_bbf.crit = c.crit; - tunnel_info_for_bbf.value = c.value.tunnel_info_for_bbf(); + tunnel_info_for_bbf.id = id; + HANDLE_CODE(tunnel_info_for_bbf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tunnel_info_for_bbf.value.unpack(bref)); break; - case 186: + } + case 186: { lhn_id_present = true; - lhn_id.id = c.id; - lhn_id.crit = c.crit; - lhn_id.value = c.value.lhn_id(); + lhn_id.id = id; + HANDLE_CODE(lhn_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lhn_id.value.unpack(bref)); break; - case 288: + } + case 288: { ps_cell_info_present = true; - ps_cell_info.id = c.id; - ps_cell_info.crit = c.crit; - ps_cell_info.value = c.value.ps_cell_info(); + ps_cell_info.id = id; + HANDLE_CODE(ps_cell_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ps_cell_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -26464,29 +25902,6 @@ void ho_notify_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverNotify ::= SEQUENCE -SRSASN_CODE ho_notify_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_notify_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_notify_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // HandoverPreparationFailureIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t ho_prep_fail_ies_o::idx_to_id(uint32_t idx) { @@ -26756,7 +26171,7 @@ const char* ho_prep_fail_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 4, value, "ho_prep_fail_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_prep_fail_ies_container::ho_prep_fail_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -26787,35 +26202,43 @@ SRSASN_CODE ho_prep_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -26842,29 +26265,6 @@ void ho_prep_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverPreparationFailure ::= SEQUENCE -SRSASN_CODE ho_prep_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_prep_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_prep_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // MBSFN-ResultToLogInfo ::= SEQUENCE SRSASN_CODE mbsfn_result_to_log_info_s::pack(bit_ref& bref) const { @@ -28303,7 +27703,7 @@ const char* mdt_mode_ext_ie_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "mdt_mode_ext_ie_o::value_c::types"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; immediate_mdt_ext_ies_container::immediate_mdt_ext_ies_container() : m3_cfg(171, crit_e::ignore), @@ -28361,59 +27761,75 @@ SRSASN_CODE immediate_mdt_ext_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 171: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 171: { m3_cfg_present = true; - m3_cfg.id = c.id; - m3_cfg.crit = c.crit; - m3_cfg.ext = c.ext_value.m3_cfg(); + m3_cfg.id = id; + HANDLE_CODE(m3_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(m3_cfg.ext.unpack(bref)); break; - case 172: + } + case 172: { m4_cfg_present = true; - m4_cfg.id = c.id; - m4_cfg.crit = c.crit; - m4_cfg.ext = c.ext_value.m4_cfg(); + m4_cfg.id = id; + HANDLE_CODE(m4_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(m4_cfg.ext.unpack(bref)); break; - case 173: + } + case 173: { m5_cfg_present = true; - m5_cfg.id = c.id; - m5_cfg.crit = c.crit; - m5_cfg.ext = c.ext_value.m5_cfg(); + m5_cfg.id = id; + HANDLE_CODE(m5_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(m5_cfg.ext.unpack(bref)); break; - case 174: + } + case 174: { mdt_location_info_present = true; - mdt_location_info.id = c.id; - mdt_location_info.crit = c.crit; - mdt_location_info.ext = c.ext_value.mdt_location_info(); + mdt_location_info.id = id; + HANDLE_CODE(mdt_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mdt_location_info.ext.unpack(bref)); break; - case 220: + } + case 220: { m6_cfg_present = true; - m6_cfg.id = c.id; - m6_cfg.crit = c.crit; - m6_cfg.ext = c.ext_value.m6_cfg(); + m6_cfg.id = id; + HANDLE_CODE(m6_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(m6_cfg.ext.unpack(bref)); break; - case 221: + } + case 221: { m7_cfg_present = true; - m7_cfg.id = c.id; - m7_cfg.crit = c.crit; - m7_cfg.ext = c.ext_value.m7_cfg(); + m7_cfg.id = id; + HANDLE_CODE(m7_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(m7_cfg.ext.unpack(bref)); break; - case 284: + } + case 284: { bluetooth_meas_cfg_present = true; - bluetooth_meas_cfg.id = c.id; - bluetooth_meas_cfg.crit = c.crit; - bluetooth_meas_cfg.ext = c.ext_value.bluetooth_meas_cfg(); + bluetooth_meas_cfg.id = id; + HANDLE_CODE(bluetooth_meas_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(bluetooth_meas_cfg.ext.unpack(bref)); break; - case 285: + } + case 285: { wlan_meas_cfg_present = true; - wlan_meas_cfg.id = c.id; - wlan_meas_cfg.crit = c.crit; - wlan_meas_cfg.ext = c.ext_value.wlan_meas_cfg(); + wlan_meas_cfg.id = id; + HANDLE_CODE(wlan_meas_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(wlan_meas_cfg.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -28521,7 +27937,7 @@ void immediate_mdt_s::to_json(json_writer& j) const j.end_obj(); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; logged_mdt_ext_ies_container::logged_mdt_ext_ies_container() : bluetooth_meas_cfg(284, crit_e::ignore), wlan_meas_cfg(285, crit_e::ignore) @@ -28548,23 +27964,27 @@ SRSASN_CODE logged_mdt_ext_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 284: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 284: { bluetooth_meas_cfg_present = true; - bluetooth_meas_cfg.id = c.id; - bluetooth_meas_cfg.crit = c.crit; - bluetooth_meas_cfg.ext = c.ext_value.bluetooth_meas_cfg(); + bluetooth_meas_cfg.id = id; + HANDLE_CODE(bluetooth_meas_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(bluetooth_meas_cfg.ext.unpack(bref)); break; - case 285: + } + case 285: { wlan_meas_cfg_present = true; - wlan_meas_cfg.id = c.id; - wlan_meas_cfg.crit = c.crit; - wlan_meas_cfg.ext = c.ext_value.wlan_meas_cfg(); + wlan_meas_cfg.id = id; + HANDLE_CODE(wlan_meas_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(wlan_meas_cfg.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -28624,7 +28044,7 @@ void logged_mdt_s::to_json(json_writer& j) const j.end_obj(); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // ServiceType ::= ENUMERATED const char* service_type_opts::to_string() const @@ -29555,7 +28975,7 @@ void security_context_s::to_json(json_writer& j) const j.end_obj(); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; trace_activation_ext_ies_container::trace_activation_ext_ies_container() : mdt_cfg(162, crit_e::ignore), ue_app_layer_meas_cfg(262, crit_e::ignore) @@ -29582,23 +29002,27 @@ SRSASN_CODE trace_activation_ext_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 162: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 162: { mdt_cfg_present = true; - mdt_cfg.id = c.id; - mdt_cfg.crit = c.crit; - mdt_cfg.ext = c.ext_value.mdt_cfg(); + mdt_cfg.id = id; + HANDLE_CODE(mdt_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mdt_cfg.ext.unpack(bref)); break; - case 262: + } + case 262: { ue_app_layer_meas_cfg_present = true; - ue_app_layer_meas_cfg.id = c.id; - ue_app_layer_meas_cfg.crit = c.crit; - ue_app_layer_meas_cfg.ext = c.ext_value.ue_app_layer_meas_cfg(); + ue_app_layer_meas_cfg.id = id; + HANDLE_CODE(ue_app_layer_meas_cfg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_app_layer_meas_cfg.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -31152,7 +30576,7 @@ const char* ho_request_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 32, value, "ho_request_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_request_ies_container::ho_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -31308,203 +30732,267 @@ SRSASN_CODE ho_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 8; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 1: + } + case 1: { nof_mandatory_ies--; - handov_type.id = c.id; - handov_type.crit = c.crit; - handov_type.value = c.value.handov_type(); + handov_type.id = id; + HANDLE_CODE(handov_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(handov_type.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 66: + } + case 66: { nof_mandatory_ies--; - ueaggregate_maximum_bitrate.id = c.id; - ueaggregate_maximum_bitrate.crit = c.crit; - ueaggregate_maximum_bitrate.value = c.value.ueaggregate_maximum_bitrate(); + ueaggregate_maximum_bitrate.id = id; + HANDLE_CODE(ueaggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueaggregate_maximum_bitrate.value.unpack(bref)); break; - case 53: + } + case 53: { nof_mandatory_ies--; - erab_to_be_setup_list_ho_req.id = c.id; - erab_to_be_setup_list_ho_req.crit = c.crit; - erab_to_be_setup_list_ho_req.value = c.value.erab_to_be_setup_list_ho_req(); + erab_to_be_setup_list_ho_req.id = id; + HANDLE_CODE(erab_to_be_setup_list_ho_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_setup_list_ho_req.value.unpack(bref)); break; - case 104: + } + case 104: { nof_mandatory_ies--; - source_to_target_transparent_container.id = c.id; - source_to_target_transparent_container.crit = c.crit; - source_to_target_transparent_container.value = c.value.source_to_target_transparent_container(); + source_to_target_transparent_container.id = id; + HANDLE_CODE(source_to_target_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(source_to_target_transparent_container.value.unpack(bref)); break; - case 107: + } + case 107: { nof_mandatory_ies--; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 41: + } + case 41: { ho_restrict_list_present = true; - ho_restrict_list.id = c.id; - ho_restrict_list.crit = c.crit; - ho_restrict_list.value = c.value.ho_restrict_list(); + ho_restrict_list.id = id; + HANDLE_CODE(ho_restrict_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ho_restrict_list.value.unpack(bref)); break; - case 25: + } + case 25: { trace_activation_present = true; - trace_activation.id = c.id; - trace_activation.crit = c.crit; - trace_activation.value = c.value.trace_activation(); + trace_activation.id = id; + HANDLE_CODE(trace_activation.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(trace_activation.value.unpack(bref)); break; - case 98: + } + case 98: { request_type_present = true; - request_type.id = c.id; - request_type.crit = c.crit; - request_type.value = c.value.request_type(); + request_type.id = id; + HANDLE_CODE(request_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(request_type.value.unpack(bref)); break; - case 124: + } + case 124: { srvcc_operation_possible_present = true; - srvcc_operation_possible.id = c.id; - srvcc_operation_possible.crit = c.crit; - srvcc_operation_possible.value = c.value.srvcc_operation_possible(); + srvcc_operation_possible.id = id; + HANDLE_CODE(srvcc_operation_possible.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(srvcc_operation_possible.value.unpack(bref)); break; - case 40: + } + case 40: { nof_mandatory_ies--; - security_context.id = c.id; - security_context.crit = c.crit; - security_context.value = c.value.security_context(); + security_context.id = id; + HANDLE_CODE(security_context.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_context.value.unpack(bref)); break; - case 136: + } + case 136: { nas_security_paramsto_e_utran_present = true; - nas_security_paramsto_e_utran.id = c.id; - nas_security_paramsto_e_utran.crit = c.crit; - nas_security_paramsto_e_utran.value = c.value.nas_security_paramsto_e_utran(); + nas_security_paramsto_e_utran.id = id; + HANDLE_CODE(nas_security_paramsto_e_utran.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_security_paramsto_e_utran.value.unpack(bref)); break; - case 127: + } + case 127: { csg_id_present = true; - csg_id.id = c.id; - csg_id.crit = c.crit; - csg_id.value = c.value.csg_id(); + csg_id.id = id; + HANDLE_CODE(csg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_id.value.unpack(bref)); break; - case 146: + } + case 146: { csg_membership_status_present = true; - csg_membership_status.id = c.id; - csg_membership_status.crit = c.crit; - csg_membership_status.value = c.value.csg_membership_status(); + csg_membership_status.id = id; + HANDLE_CODE(csg_membership_status.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_status.value.unpack(bref)); break; - case 75: + } + case 75: { gummei_id_present = true; - gummei_id.id = c.id; - gummei_id.crit = c.crit; - gummei_id.value = c.value.gummei_id(); + gummei_id.id = id; + HANDLE_CODE(gummei_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(gummei_id.value.unpack(bref)); break; - case 158: + } + case 158: { mme_ue_s1ap_id_minus2_present = true; - mme_ue_s1ap_id_minus2.id = c.id; - mme_ue_s1ap_id_minus2.crit = c.crit; - mme_ue_s1ap_id_minus2.value = c.value.mme_ue_s1ap_id_minus2(); + mme_ue_s1ap_id_minus2.id = id; + HANDLE_CODE(mme_ue_s1ap_id_minus2.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id_minus2.value.unpack(bref)); break; - case 165: + } + case 165: { management_based_mdt_allowed_present = true; - management_based_mdt_allowed.id = c.id; - management_based_mdt_allowed.crit = c.crit; - management_based_mdt_allowed.value = c.value.management_based_mdt_allowed(); + management_based_mdt_allowed.id = id; + HANDLE_CODE(management_based_mdt_allowed.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(management_based_mdt_allowed.value.unpack(bref)); break; - case 177: + } + case 177: { management_based_mdtplmn_list_present = true; - management_based_mdtplmn_list.id = c.id; - management_based_mdtplmn_list.crit = c.crit; - management_based_mdtplmn_list.value = c.value.management_based_mdtplmn_list(); + management_based_mdtplmn_list.id = id; + HANDLE_CODE(management_based_mdtplmn_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(management_based_mdtplmn_list.value.unpack(bref)); break; - case 192: + } + case 192: { masked_imeisv_present = true; - masked_imeisv.id = c.id; - masked_imeisv.crit = c.crit; - masked_imeisv.value = c.value.masked_imeisv(); + masked_imeisv.id = id; + HANDLE_CODE(masked_imeisv.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(masked_imeisv.value.unpack(bref)); break; - case 196: + } + case 196: { expected_ue_behaviour_present = true; - expected_ue_behaviour.id = c.id; - expected_ue_behaviour.crit = c.crit; - expected_ue_behaviour.value = c.value.expected_ue_behaviour(); + expected_ue_behaviour.id = id; + HANDLE_CODE(expected_ue_behaviour.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(expected_ue_behaviour.value.unpack(bref)); break; - case 195: + } + case 195: { pro_se_authorized_present = true; - pro_se_authorized.id = c.id; - pro_se_authorized.crit = c.crit; - pro_se_authorized.value = c.value.pro_se_authorized(); + pro_se_authorized.id = id; + HANDLE_CODE(pro_se_authorized.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pro_se_authorized.value.unpack(bref)); break; - case 241: + } + case 241: { ueuser_plane_cio_tsupport_ind_present = true; - ueuser_plane_cio_tsupport_ind.id = c.id; - ueuser_plane_cio_tsupport_ind.crit = c.crit; - ueuser_plane_cio_tsupport_ind.value = c.value.ueuser_plane_cio_tsupport_ind(); + ueuser_plane_cio_tsupport_ind.id = id; + HANDLE_CODE(ueuser_plane_cio_tsupport_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueuser_plane_cio_tsupport_ind.value.unpack(bref)); break; - case 240: + } + case 240: { v2xservices_authorized_present = true; - v2xservices_authorized.id = c.id; - v2xservices_authorized.crit = c.crit; - v2xservices_authorized.value = c.value.v2xservices_authorized(); + v2xservices_authorized.id = id; + HANDLE_CODE(v2xservices_authorized.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(v2xservices_authorized.value.unpack(bref)); break; - case 248: + } + case 248: { ue_sidelink_aggregate_maximum_bitrate_present = true; - ue_sidelink_aggregate_maximum_bitrate.id = c.id; - ue_sidelink_aggregate_maximum_bitrate.crit = c.crit; - ue_sidelink_aggregate_maximum_bitrate.value = c.value.ue_sidelink_aggregate_maximum_bitrate(); + ue_sidelink_aggregate_maximum_bitrate.id = id; + HANDLE_CODE(ue_sidelink_aggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_sidelink_aggregate_maximum_bitrate.value.unpack(bref)); break; - case 251: + } + case 251: { enhanced_coverage_restricted_present = true; - enhanced_coverage_restricted.id = c.id; - enhanced_coverage_restricted.crit = c.crit; - enhanced_coverage_restricted.value = c.value.enhanced_coverage_restricted(); + enhanced_coverage_restricted.id = id; + HANDLE_CODE(enhanced_coverage_restricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enhanced_coverage_restricted.value.unpack(bref)); break; - case 269: + } + case 269: { nrue_security_cap_present = true; - nrue_security_cap.id = c.id; - nrue_security_cap.crit = c.crit; - nrue_security_cap.value = c.value.nrue_security_cap(); + nrue_security_cap.id = id; + HANDLE_CODE(nrue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrue_security_cap.value.unpack(bref)); break; - case 271: + } + case 271: { ce_mode_brestricted_present = true; - ce_mode_brestricted.id = c.id; - ce_mode_brestricted.crit = c.crit; - ce_mode_brestricted.value = c.value.ce_mode_brestricted(); + ce_mode_brestricted.id = id; + HANDLE_CODE(ce_mode_brestricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ce_mode_brestricted.value.unpack(bref)); break; - case 277: + } + case 277: { aerial_uesubscription_info_present = true; - aerial_uesubscription_info.id = c.id; - aerial_uesubscription_info.crit = c.crit; - aerial_uesubscription_info.value = c.value.aerial_uesubscription_info(); + aerial_uesubscription_info.id = id; + HANDLE_CODE(aerial_uesubscription_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(aerial_uesubscription_info.value.unpack(bref)); break; - case 283: + } + case 283: { pending_data_ind_present = true; - pending_data_ind.id = c.id; - pending_data_ind.crit = c.crit; - pending_data_ind.value = c.value.pending_data_ind(); + pending_data_ind.id = id; + HANDLE_CODE(pending_data_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pending_data_ind.value.unpack(bref)); break; - case 278: + } + case 278: { subscription_based_ue_differentiation_info_present = true; - subscription_based_ue_differentiation_info.id = c.id; - subscription_based_ue_differentiation_info.crit = c.crit; - subscription_based_ue_differentiation_info.value = c.value.subscription_based_ue_differentiation_info(); + subscription_based_ue_differentiation_info.id = id; + HANDLE_CODE(subscription_based_ue_differentiation_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscription_based_ue_differentiation_info.value.unpack(bref)); break; - case 299: + } + case 299: { add_rrm_prio_idx_present = true; - add_rrm_prio_idx.id = c.id; - add_rrm_prio_idx.crit = c.crit; - add_rrm_prio_idx.value = c.value.add_rrm_prio_idx(); + add_rrm_prio_idx.id = id; + HANDLE_CODE(add_rrm_prio_idx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_rrm_prio_idx.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -31633,29 +31121,6 @@ void ho_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverRequest ::= SEQUENCE -SRSASN_CODE ho_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // CE-mode-B-SupportIndicator ::= ENUMERATED const char* ce_mode_b_support_ind_opts::to_string() const { @@ -32124,7 +31589,7 @@ const char* ho_request_ack_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 9, value, "ho_request_ack_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_request_ack_ies_container::ho_request_ack_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -32177,65 +31642,83 @@ SRSASN_CODE ho_request_ack_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 18: + } + case 18: { nof_mandatory_ies--; - erab_admitted_list.id = c.id; - erab_admitted_list.crit = c.crit; - erab_admitted_list.value = c.value.erab_admitted_list(); + erab_admitted_list.id = id; + HANDLE_CODE(erab_admitted_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_admitted_list.value.unpack(bref)); break; - case 19: + } + case 19: { erab_failed_to_setup_list_ho_req_ack_present = true; - erab_failed_to_setup_list_ho_req_ack.id = c.id; - erab_failed_to_setup_list_ho_req_ack.crit = c.crit; - erab_failed_to_setup_list_ho_req_ack.value = c.value.erab_failed_to_setup_list_ho_req_ack(); + erab_failed_to_setup_list_ho_req_ack.id = id; + HANDLE_CODE(erab_failed_to_setup_list_ho_req_ack.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_failed_to_setup_list_ho_req_ack.value.unpack(bref)); break; - case 123: + } + case 123: { nof_mandatory_ies--; - target_to_source_transparent_container.id = c.id; - target_to_source_transparent_container.crit = c.crit; - target_to_source_transparent_container.value = c.value.target_to_source_transparent_container(); + target_to_source_transparent_container.id = id; + HANDLE_CODE(target_to_source_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(target_to_source_transparent_container.value.unpack(bref)); break; - case 127: + } + case 127: { csg_id_present = true; - csg_id.id = c.id; - csg_id.crit = c.crit; - csg_id.value = c.value.csg_id(); + csg_id.id = id; + HANDLE_CODE(csg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_id.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 145: + } + case 145: { cell_access_mode_present = true; - cell_access_mode.id = c.id; - cell_access_mode.crit = c.crit; - cell_access_mode.value = c.value.cell_access_mode(); + cell_access_mode.id = id; + HANDLE_CODE(cell_access_mode.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cell_access_mode.value.unpack(bref)); break; - case 242: + } + case 242: { ce_mode_b_support_ind_present = true; - ce_mode_b_support_ind.id = c.id; - ce_mode_b_support_ind.crit = c.crit; - ce_mode_b_support_ind.value = c.value.ce_mode_b_support_ind(); + ce_mode_b_support_ind.id = id; + HANDLE_CODE(ce_mode_b_support_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ce_mode_b_support_ind.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -32280,29 +31763,6 @@ void ho_request_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverRequestAcknowledge ::= SEQUENCE -SRSASN_CODE ho_request_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_request_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_request_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // TargetNgRanNode-ID ::= SEQUENCE SRSASN_CODE target_ng_ran_node_id_s::pack(bit_ref& bref) const { @@ -33198,7 +32658,7 @@ const char* ho_required_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 14, value, "ho_required_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ho_required_ies_container::ho_required_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -33270,96 +32730,123 @@ SRSASN_CODE ho_required_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 6; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 1: + } + case 1: { nof_mandatory_ies--; - handov_type.id = c.id; - handov_type.crit = c.crit; - handov_type.value = c.value.handov_type(); + handov_type.id = id; + HANDLE_CODE(handov_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(handov_type.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 4: + } + case 4: { nof_mandatory_ies--; - target_id.id = c.id; - target_id.crit = c.crit; - target_id.value = c.value.target_id(); + target_id.id = id; + HANDLE_CODE(target_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(target_id.value.unpack(bref)); break; - case 79: + } + case 79: { direct_forwarding_path_availability_present = true; - direct_forwarding_path_availability.id = c.id; - direct_forwarding_path_availability.crit = c.crit; - direct_forwarding_path_availability.value = c.value.direct_forwarding_path_availability(); + direct_forwarding_path_availability.id = id; + HANDLE_CODE(direct_forwarding_path_availability.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(direct_forwarding_path_availability.value.unpack(bref)); break; - case 125: + } + case 125: { srvccho_ind_present = true; - srvccho_ind.id = c.id; - srvccho_ind.crit = c.crit; - srvccho_ind.value = c.value.srvccho_ind(); + srvccho_ind.id = id; + HANDLE_CODE(srvccho_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(srvccho_ind.value.unpack(bref)); break; - case 104: + } + case 104: { nof_mandatory_ies--; - source_to_target_transparent_container.id = c.id; - source_to_target_transparent_container.crit = c.crit; - source_to_target_transparent_container.value = c.value.source_to_target_transparent_container(); + source_to_target_transparent_container.id = id; + HANDLE_CODE(source_to_target_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(source_to_target_transparent_container.value.unpack(bref)); break; - case 138: + } + case 138: { source_to_target_transparent_container_secondary_present = true; - source_to_target_transparent_container_secondary.id = c.id; - source_to_target_transparent_container_secondary.crit = c.crit; - source_to_target_transparent_container_secondary.value = - c.value.source_to_target_transparent_container_secondary(); + source_to_target_transparent_container_secondary.id = id; + HANDLE_CODE(source_to_target_transparent_container_secondary.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(source_to_target_transparent_container_secondary.value.unpack(bref)); break; - case 132: + } + case 132: { ms_classmark2_present = true; - ms_classmark2.id = c.id; - ms_classmark2.crit = c.crit; - ms_classmark2.value = c.value.ms_classmark2(); + ms_classmark2.id = id; + HANDLE_CODE(ms_classmark2.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ms_classmark2.value.unpack(bref)); break; - case 133: + } + case 133: { ms_classmark3_present = true; - ms_classmark3.id = c.id; - ms_classmark3.crit = c.crit; - ms_classmark3.value = c.value.ms_classmark3(); + ms_classmark3.id = id; + HANDLE_CODE(ms_classmark3.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ms_classmark3.value.unpack(bref)); break; - case 127: + } + case 127: { csg_id_present = true; - csg_id.id = c.id; - csg_id.crit = c.crit; - csg_id.value = c.value.csg_id(); + csg_id.id = id; + HANDLE_CODE(csg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_id.value.unpack(bref)); break; - case 145: + } + case 145: { cell_access_mode_present = true; - cell_access_mode.id = c.id; - cell_access_mode.crit = c.crit; - cell_access_mode.value = c.value.cell_access_mode(); + cell_access_mode.id = id; + HANDLE_CODE(cell_access_mode.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cell_access_mode.value.unpack(bref)); break; - case 150: + } + case 150: { ps_service_not_available_present = true; - ps_service_not_available.id = c.id; - ps_service_not_available.crit = c.crit; - ps_service_not_available.value = c.value.ps_service_not_available(); + ps_service_not_available.id = id; + HANDLE_CODE(ps_service_not_available.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ps_service_not_available.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -33420,29 +32907,6 @@ void ho_required_ies_container::to_json(json_writer& j) const j.end_obj(); } -// HandoverRequired ::= SEQUENCE -SRSASN_CODE ho_required_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ho_required_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ho_required_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // MMEPagingTarget ::= CHOICE void mme_paging_target_c::destroy_() { @@ -33678,7 +33142,7 @@ const char* recommended_enb_item_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 1, value, "recommended_enb_item_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // RecommendedENBsForPaging ::= SEQUENCE SRSASN_CODE recommended_enbs_for_paging_s::pack(bit_ref& bref) const @@ -34031,7 +33495,7 @@ const char* init_context_setup_fail_ies_o::value_c::types_opts::to_string() cons return convert_enum_idx(options, 4, value, "init_context_setup_fail_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; init_context_setup_fail_ies_container::init_context_setup_fail_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -34062,35 +33526,43 @@ SRSASN_CODE init_context_setup_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -34117,29 +33589,6 @@ void init_context_setup_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// InitialContextSetupFailure ::= SEQUENCE -SRSASN_CODE init_context_setup_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE init_context_setup_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void init_context_setup_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // AdditionalCSFallbackIndicator ::= ENUMERATED const char* add_cs_fallback_ind_opts::to_string() const { @@ -35495,7 +34944,7 @@ const char* init_context_setup_request_ies_o::value_c::types_opts::to_string() c return convert_enum_idx(options, 32, value, "init_context_setup_request_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; init_context_setup_request_ies_container::init_context_setup_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -35657,203 +35106,267 @@ SRSASN_CODE init_context_setup_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 6; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 66: + } + case 66: { nof_mandatory_ies--; - ueaggregate_maximum_bitrate.id = c.id; - ueaggregate_maximum_bitrate.crit = c.crit; - ueaggregate_maximum_bitrate.value = c.value.ueaggregate_maximum_bitrate(); + ueaggregate_maximum_bitrate.id = id; + HANDLE_CODE(ueaggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueaggregate_maximum_bitrate.value.unpack(bref)); break; - case 24: + } + case 24: { nof_mandatory_ies--; - erab_to_be_setup_list_ctxt_su_req.id = c.id; - erab_to_be_setup_list_ctxt_su_req.crit = c.crit; - erab_to_be_setup_list_ctxt_su_req.value = c.value.erab_to_be_setup_list_ctxt_su_req(); + erab_to_be_setup_list_ctxt_su_req.id = id; + HANDLE_CODE(erab_to_be_setup_list_ctxt_su_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_setup_list_ctxt_su_req.value.unpack(bref)); break; - case 107: + } + case 107: { nof_mandatory_ies--; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 73: + } + case 73: { nof_mandatory_ies--; - security_key.id = c.id; - security_key.crit = c.crit; - security_key.value = c.value.security_key(); + security_key.id = id; + HANDLE_CODE(security_key.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_key.value.unpack(bref)); break; - case 25: + } + case 25: { trace_activation_present = true; - trace_activation.id = c.id; - trace_activation.crit = c.crit; - trace_activation.value = c.value.trace_activation(); + trace_activation.id = id; + HANDLE_CODE(trace_activation.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(trace_activation.value.unpack(bref)); break; - case 41: + } + case 41: { ho_restrict_list_present = true; - ho_restrict_list.id = c.id; - ho_restrict_list.crit = c.crit; - ho_restrict_list.value = c.value.ho_restrict_list(); + ho_restrict_list.id = id; + HANDLE_CODE(ho_restrict_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ho_restrict_list.value.unpack(bref)); break; - case 74: + } + case 74: { ue_radio_cap_present = true; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; - case 106: + } + case 106: { subscriber_profile_idfor_rfp_present = true; - subscriber_profile_idfor_rfp.id = c.id; - subscriber_profile_idfor_rfp.crit = c.crit; - subscriber_profile_idfor_rfp.value = c.value.subscriber_profile_idfor_rfp(); + subscriber_profile_idfor_rfp.id = id; + HANDLE_CODE(subscriber_profile_idfor_rfp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscriber_profile_idfor_rfp.value.unpack(bref)); break; - case 108: + } + case 108: { cs_fallback_ind_present = true; - cs_fallback_ind.id = c.id; - cs_fallback_ind.crit = c.crit; - cs_fallback_ind.value = c.value.cs_fallback_ind(); + cs_fallback_ind.id = id; + HANDLE_CODE(cs_fallback_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cs_fallback_ind.value.unpack(bref)); break; - case 124: + } + case 124: { srvcc_operation_possible_present = true; - srvcc_operation_possible.id = c.id; - srvcc_operation_possible.crit = c.crit; - srvcc_operation_possible.value = c.value.srvcc_operation_possible(); + srvcc_operation_possible.id = id; + HANDLE_CODE(srvcc_operation_possible.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(srvcc_operation_possible.value.unpack(bref)); break; - case 146: + } + case 146: { csg_membership_status_present = true; - csg_membership_status.id = c.id; - csg_membership_status.crit = c.crit; - csg_membership_status.value = c.value.csg_membership_status(); + csg_membership_status.id = id; + HANDLE_CODE(csg_membership_status.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_status.value.unpack(bref)); break; - case 159: + } + case 159: { registered_lai_present = true; - registered_lai.id = c.id; - registered_lai.crit = c.crit; - registered_lai.value = c.value.registered_lai(); + registered_lai.id = id; + HANDLE_CODE(registered_lai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(registered_lai.value.unpack(bref)); break; - case 75: + } + case 75: { gummei_id_present = true; - gummei_id.id = c.id; - gummei_id.crit = c.crit; - gummei_id.value = c.value.gummei_id(); + gummei_id.id = id; + HANDLE_CODE(gummei_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(gummei_id.value.unpack(bref)); break; - case 158: + } + case 158: { mme_ue_s1ap_id_minus2_present = true; - mme_ue_s1ap_id_minus2.id = c.id; - mme_ue_s1ap_id_minus2.crit = c.crit; - mme_ue_s1ap_id_minus2.value = c.value.mme_ue_s1ap_id_minus2(); + mme_ue_s1ap_id_minus2.id = id; + HANDLE_CODE(mme_ue_s1ap_id_minus2.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id_minus2.value.unpack(bref)); break; - case 165: + } + case 165: { management_based_mdt_allowed_present = true; - management_based_mdt_allowed.id = c.id; - management_based_mdt_allowed.crit = c.crit; - management_based_mdt_allowed.value = c.value.management_based_mdt_allowed(); + management_based_mdt_allowed.id = id; + HANDLE_CODE(management_based_mdt_allowed.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(management_based_mdt_allowed.value.unpack(bref)); break; - case 177: + } + case 177: { management_based_mdtplmn_list_present = true; - management_based_mdtplmn_list.id = c.id; - management_based_mdtplmn_list.crit = c.crit; - management_based_mdtplmn_list.value = c.value.management_based_mdtplmn_list(); + management_based_mdtplmn_list.id = id; + HANDLE_CODE(management_based_mdtplmn_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(management_based_mdtplmn_list.value.unpack(bref)); break; - case 187: + } + case 187: { add_cs_fallback_ind_present = true; - add_cs_fallback_ind.id = c.id; - add_cs_fallback_ind.crit = c.crit; - add_cs_fallback_ind.value = c.value.add_cs_fallback_ind(); + add_cs_fallback_ind.id = id; + HANDLE_CODE(add_cs_fallback_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_cs_fallback_ind.value.unpack(bref)); break; - case 192: + } + case 192: { masked_imeisv_present = true; - masked_imeisv.id = c.id; - masked_imeisv.crit = c.crit; - masked_imeisv.value = c.value.masked_imeisv(); + masked_imeisv.id = id; + HANDLE_CODE(masked_imeisv.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(masked_imeisv.value.unpack(bref)); break; - case 196: + } + case 196: { expected_ue_behaviour_present = true; - expected_ue_behaviour.id = c.id; - expected_ue_behaviour.crit = c.crit; - expected_ue_behaviour.value = c.value.expected_ue_behaviour(); + expected_ue_behaviour.id = id; + HANDLE_CODE(expected_ue_behaviour.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(expected_ue_behaviour.value.unpack(bref)); break; - case 195: + } + case 195: { pro_se_authorized_present = true; - pro_se_authorized.id = c.id; - pro_se_authorized.crit = c.crit; - pro_se_authorized.value = c.value.pro_se_authorized(); + pro_se_authorized.id = id; + HANDLE_CODE(pro_se_authorized.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pro_se_authorized.value.unpack(bref)); break; - case 241: + } + case 241: { ueuser_plane_cio_tsupport_ind_present = true; - ueuser_plane_cio_tsupport_ind.id = c.id; - ueuser_plane_cio_tsupport_ind.crit = c.crit; - ueuser_plane_cio_tsupport_ind.value = c.value.ueuser_plane_cio_tsupport_ind(); + ueuser_plane_cio_tsupport_ind.id = id; + HANDLE_CODE(ueuser_plane_cio_tsupport_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueuser_plane_cio_tsupport_ind.value.unpack(bref)); break; - case 240: + } + case 240: { v2xservices_authorized_present = true; - v2xservices_authorized.id = c.id; - v2xservices_authorized.crit = c.crit; - v2xservices_authorized.value = c.value.v2xservices_authorized(); + v2xservices_authorized.id = id; + HANDLE_CODE(v2xservices_authorized.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(v2xservices_authorized.value.unpack(bref)); break; - case 248: + } + case 248: { ue_sidelink_aggregate_maximum_bitrate_present = true; - ue_sidelink_aggregate_maximum_bitrate.id = c.id; - ue_sidelink_aggregate_maximum_bitrate.crit = c.crit; - ue_sidelink_aggregate_maximum_bitrate.value = c.value.ue_sidelink_aggregate_maximum_bitrate(); + ue_sidelink_aggregate_maximum_bitrate.id = id; + HANDLE_CODE(ue_sidelink_aggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_sidelink_aggregate_maximum_bitrate.value.unpack(bref)); break; - case 251: + } + case 251: { enhanced_coverage_restricted_present = true; - enhanced_coverage_restricted.id = c.id; - enhanced_coverage_restricted.crit = c.crit; - enhanced_coverage_restricted.value = c.value.enhanced_coverage_restricted(); + enhanced_coverage_restricted.id = id; + HANDLE_CODE(enhanced_coverage_restricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enhanced_coverage_restricted.value.unpack(bref)); break; - case 269: + } + case 269: { nrue_security_cap_present = true; - nrue_security_cap.id = c.id; - nrue_security_cap.crit = c.crit; - nrue_security_cap.value = c.value.nrue_security_cap(); + nrue_security_cap.id = id; + HANDLE_CODE(nrue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrue_security_cap.value.unpack(bref)); break; - case 271: + } + case 271: { ce_mode_brestricted_present = true; - ce_mode_brestricted.id = c.id; - ce_mode_brestricted.crit = c.crit; - ce_mode_brestricted.value = c.value.ce_mode_brestricted(); + ce_mode_brestricted.id = id; + HANDLE_CODE(ce_mode_brestricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ce_mode_brestricted.value.unpack(bref)); break; - case 277: + } + case 277: { aerial_uesubscription_info_present = true; - aerial_uesubscription_info.id = c.id; - aerial_uesubscription_info.crit = c.crit; - aerial_uesubscription_info.value = c.value.aerial_uesubscription_info(); + aerial_uesubscription_info.id = id; + HANDLE_CODE(aerial_uesubscription_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(aerial_uesubscription_info.value.unpack(bref)); break; - case 283: + } + case 283: { pending_data_ind_present = true; - pending_data_ind.id = c.id; - pending_data_ind.crit = c.crit; - pending_data_ind.value = c.value.pending_data_ind(); + pending_data_ind.id = id; + HANDLE_CODE(pending_data_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pending_data_ind.value.unpack(bref)); break; - case 278: + } + case 278: { subscription_based_ue_differentiation_info_present = true; - subscription_based_ue_differentiation_info.id = c.id; - subscription_based_ue_differentiation_info.crit = c.crit; - subscription_based_ue_differentiation_info.value = c.value.subscription_based_ue_differentiation_info(); + subscription_based_ue_differentiation_info.id = id; + HANDLE_CODE(subscription_based_ue_differentiation_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscription_based_ue_differentiation_info.value.unpack(bref)); break; - case 299: + } + case 299: { add_rrm_prio_idx_present = true; - add_rrm_prio_idx.id = c.id; - add_rrm_prio_idx.crit = c.crit; - add_rrm_prio_idx.value = c.value.add_rrm_prio_idx(); + add_rrm_prio_idx.id = id; + HANDLE_CODE(add_rrm_prio_idx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_rrm_prio_idx.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -35986,29 +35499,6 @@ void init_context_setup_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// InitialContextSetupRequest ::= SEQUENCE -SRSASN_CODE init_context_setup_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE init_context_setup_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void init_context_setup_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // InitialContextSetupResponseIEs ::= OBJECT SET OF S1AP-PROTOCOL-IES uint32_t init_context_setup_resp_ies_o::idx_to_id(uint32_t idx) { @@ -36327,7 +35817,7 @@ const char* init_context_setup_resp_ies_o::value_c::types_opts::to_string() cons return convert_enum_idx(options, 5, value, "init_context_setup_resp_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; init_context_setup_resp_ies_container::init_context_setup_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -36363,41 +35853,51 @@ SRSASN_CODE init_context_setup_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 51: + } + case 51: { nof_mandatory_ies--; - erab_setup_list_ctxt_su_res.id = c.id; - erab_setup_list_ctxt_su_res.crit = c.crit; - erab_setup_list_ctxt_su_res.value = c.value.erab_setup_list_ctxt_su_res(); + erab_setup_list_ctxt_su_res.id = id; + HANDLE_CODE(erab_setup_list_ctxt_su_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_setup_list_ctxt_su_res.value.unpack(bref)); break; - case 48: + } + case 48: { erab_failed_to_setup_list_ctxt_su_res_present = true; - erab_failed_to_setup_list_ctxt_su_res.id = c.id; - erab_failed_to_setup_list_ctxt_su_res.crit = c.crit; - erab_failed_to_setup_list_ctxt_su_res.value = c.value.erab_failed_to_setup_list_ctxt_su_res(); + erab_failed_to_setup_list_ctxt_su_res.id = id; + HANDLE_CODE(erab_failed_to_setup_list_ctxt_su_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_failed_to_setup_list_ctxt_su_res.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -36428,29 +35928,6 @@ void init_context_setup_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// InitialContextSetupResponse ::= SEQUENCE -SRSASN_CODE init_context_setup_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE init_context_setup_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void init_context_setup_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // Coverage-Level ::= ENUMERATED const char* coverage_level_opts::to_string() const { @@ -37447,7 +36924,7 @@ const char* init_ue_msg_ies_o::value_c::types_opts::to_string() const return convert_enum_idx(options, 22, value, "init_ue_msg_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; init_ue_msg_ies_container::init_ue_msg_ies_container() : enb_ue_s1ap_id(8, crit_e::reject), @@ -37562,143 +37039,187 @@ SRSASN_CODE init_ue_msg_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 8: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 26: + } + case 26: { nof_mandatory_ies--; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 67: + } + case 67: { nof_mandatory_ies--; - tai.id = c.id; - tai.crit = c.crit; - tai.value = c.value.tai(); + tai.id = id; + HANDLE_CODE(tai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai.value.unpack(bref)); break; - case 100: + } + case 100: { nof_mandatory_ies--; - eutran_cgi.id = c.id; - eutran_cgi.crit = c.crit; - eutran_cgi.value = c.value.eutran_cgi(); + eutran_cgi.id = id; + HANDLE_CODE(eutran_cgi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(eutran_cgi.value.unpack(bref)); break; - case 134: + } + case 134: { nof_mandatory_ies--; - rrc_establishment_cause.id = c.id; - rrc_establishment_cause.crit = c.crit; - rrc_establishment_cause.value = c.value.rrc_establishment_cause(); + rrc_establishment_cause.id = id; + HANDLE_CODE(rrc_establishment_cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_establishment_cause.value.unpack(bref)); break; - case 96: + } + case 96: { s_tmsi_present = true; - s_tmsi.id = c.id; - s_tmsi.crit = c.crit; - s_tmsi.value = c.value.s_tmsi(); + s_tmsi.id = id; + HANDLE_CODE(s_tmsi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(s_tmsi.value.unpack(bref)); break; - case 127: + } + case 127: { csg_id_present = true; - csg_id.id = c.id; - csg_id.crit = c.crit; - csg_id.value = c.value.csg_id(); + csg_id.id = id; + HANDLE_CODE(csg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_id.value.unpack(bref)); break; - case 75: + } + case 75: { gummei_id_present = true; - gummei_id.id = c.id; - gummei_id.crit = c.crit; - gummei_id.value = c.value.gummei_id(); + gummei_id.id = id; + HANDLE_CODE(gummei_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(gummei_id.value.unpack(bref)); break; - case 145: + } + case 145: { cell_access_mode_present = true; - cell_access_mode.id = c.id; - cell_access_mode.crit = c.crit; - cell_access_mode.value = c.value.cell_access_mode(); + cell_access_mode.id = id; + HANDLE_CODE(cell_access_mode.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cell_access_mode.value.unpack(bref)); break; - case 155: + } + case 155: { gw_transport_layer_address_present = true; - gw_transport_layer_address.id = c.id; - gw_transport_layer_address.crit = c.crit; - gw_transport_layer_address.value = c.value.gw_transport_layer_address(); + gw_transport_layer_address.id = id; + HANDLE_CODE(gw_transport_layer_address.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(gw_transport_layer_address.value.unpack(bref)); break; - case 160: + } + case 160: { relay_node_ind_present = true; - relay_node_ind.id = c.id; - relay_node_ind.crit = c.crit; - relay_node_ind.value = c.value.relay_node_ind(); + relay_node_ind.id = id; + HANDLE_CODE(relay_node_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(relay_node_ind.value.unpack(bref)); break; - case 170: + } + case 170: { gummei_type_present = true; - gummei_type.id = c.id; - gummei_type.crit = c.crit; - gummei_type.value = c.value.gummei_type(); + gummei_type.id = id; + HANDLE_CODE(gummei_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(gummei_type.value.unpack(bref)); break; - case 176: + } + case 176: { tunnel_info_for_bbf_present = true; - tunnel_info_for_bbf.id = c.id; - tunnel_info_for_bbf.crit = c.crit; - tunnel_info_for_bbf.value = c.value.tunnel_info_for_bbf(); + tunnel_info_for_bbf.id = id; + HANDLE_CODE(tunnel_info_for_bbf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tunnel_info_for_bbf.value.unpack(bref)); break; - case 184: + } + case 184: { sipto_l_gw_transport_layer_address_present = true; - sipto_l_gw_transport_layer_address.id = c.id; - sipto_l_gw_transport_layer_address.crit = c.crit; - sipto_l_gw_transport_layer_address.value = c.value.sipto_l_gw_transport_layer_address(); + sipto_l_gw_transport_layer_address.id = id; + HANDLE_CODE(sipto_l_gw_transport_layer_address.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(sipto_l_gw_transport_layer_address.value.unpack(bref)); break; - case 186: + } + case 186: { lhn_id_present = true; - lhn_id.id = c.id; - lhn_id.crit = c.crit; - lhn_id.value = c.value.lhn_id(); + lhn_id.id = id; + HANDLE_CODE(lhn_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lhn_id.value.unpack(bref)); break; - case 223: + } + case 223: { mme_group_id_present = true; - mme_group_id.id = c.id; - mme_group_id.crit = c.crit; - mme_group_id.value = c.value.mme_group_id(); + mme_group_id.id = id; + HANDLE_CODE(mme_group_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_group_id.value.unpack(bref)); break; - case 230: + } + case 230: { ue_usage_type_present = true; - ue_usage_type.id = c.id; - ue_usage_type.crit = c.crit; - ue_usage_type.value = c.value.ue_usage_type(); + ue_usage_type.id = id; + HANDLE_CODE(ue_usage_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_usage_type.value.unpack(bref)); break; - case 242: + } + case 242: { ce_mode_b_support_ind_present = true; - ce_mode_b_support_ind.id = c.id; - ce_mode_b_support_ind.crit = c.crit; - ce_mode_b_support_ind.value = c.value.ce_mode_b_support_ind(); + ce_mode_b_support_ind.id = id; + HANDLE_CODE(ce_mode_b_support_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ce_mode_b_support_ind.value.unpack(bref)); break; - case 246: + } + case 246: { dcn_id_present = true; - dcn_id.id = c.id; - dcn_id.crit = c.crit; - dcn_id.value = c.value.dcn_id(); + dcn_id.id = id; + HANDLE_CODE(dcn_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(dcn_id.value.unpack(bref)); break; - case 250: + } + case 250: { coverage_level_present = true; - coverage_level.id = c.id; - coverage_level.crit = c.crit; - coverage_level.value = c.value.coverage_level(); + coverage_level.id = id; + HANDLE_CODE(coverage_level.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(coverage_level.value.unpack(bref)); break; - case 263: + } + case 263: { ue_application_layer_meas_cap_present = true; - ue_application_layer_meas_cap.id = c.id; - ue_application_layer_meas_cap.crit = c.crit; - ue_application_layer_meas_cap.value = c.value.ue_application_layer_meas_cap(); + ue_application_layer_meas_cap.id = id; + HANDLE_CODE(ue_application_layer_meas_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_application_layer_meas_cap.value.unpack(bref)); break; - case 281: + } + case 281: { edt_session_present = true; - edt_session.id = c.id; - edt_session.crit = c.crit; - edt_session.value = c.value.edt_session(); + edt_session.id = id; + HANDLE_CODE(edt_session.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(edt_session.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -37793,37 +37314,6 @@ void init_ue_msg_ies_container::to_json(json_writer& j) const j.end_obj(); } -// InitialUEMessage ::= SEQUENCE -SRSASN_CODE init_ue_msg_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - bref.align_bytes_zero(); - - return SRSASN_SUCCESS; -} -SRSASN_CODE init_ue_msg_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - bref.align_bytes(); - - return SRSASN_SUCCESS; -} -void init_ue_msg_s::to_json(json_writer& j) const -{ - j.start_array(); - j.start_obj(); - j.start_obj("InitialUEMessage"); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); - j.end_obj(); - j.end_array(); -} - // UE-associatedLogicalS1-ConnectionItem ::= SEQUENCE SRSASN_CODE ue_associated_lc_s1_conn_item_s::pack(bit_ref& bref) const { @@ -38382,7 +37872,7 @@ uint8_t ue_associated_lc_s1_conn_item_res_ack_o::value_c::types_opts::to_number( return map_enum_number(options, 1, value, "ue_associated_lc_s1_conn_item_res_ack_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // CNDomain ::= ENUMERATED const char* cn_domain_opts::to_string() const @@ -38719,7 +38209,7 @@ const char* srvcc_operation_not_possible_opts::to_string() const return convert_enum_idx(options, 1, value, "srvcc_operation_not_possible_e"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // UE-RetentionInformation ::= ENUMERATED const char* ue_retention_info_opts::to_string() const @@ -38859,7 +38349,7 @@ const char* ue_s1ap_ids_c::types_opts::to_string() const return convert_enum_idx(options, 2, value, "ue_s1ap_ids_c::types"); } -template struct asn1::s1ap::protocol_ie_single_container_s; +template struct asn1::protocol_ie_single_container_s; // UEPagingID ::= CHOICE void ue_paging_id_c::destroy_() @@ -56080,7 +55570,7 @@ const char* write_replace_warning_resp_ies_o::value_c::types_opts::to_string() c return convert_enum_idx(options, 4, value, "write_replace_warning_resp_ies_o::value_c::types"); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; kill_request_ies_container::kill_request_ies_container() : msg_id(111, crit_e::reject), @@ -56114,35 +55604,43 @@ SRSASN_CODE kill_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 111: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 111: { nof_mandatory_ies--; - msg_id.id = c.id; - msg_id.crit = c.crit; - msg_id.value = c.value.msg_id(); + msg_id.id = id; + HANDLE_CODE(msg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(msg_id.value.unpack(bref)); break; - case 112: + } + case 112: { nof_mandatory_ies--; - serial_num.id = c.id; - serial_num.crit = c.crit; - serial_num.value = c.value.serial_num(); + serial_num.id = id; + HANDLE_CODE(serial_num.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(serial_num.value.unpack(bref)); break; - case 113: + } + case 113: { warning_area_list_present = true; - warning_area_list.id = c.id; - warning_area_list.crit = c.crit; - warning_area_list.value = c.value.warning_area_list(); + warning_area_list.id = id; + HANDLE_CODE(warning_area_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_area_list.value.unpack(bref)); break; - case 191: + } + case 191: { kill_all_warning_msgs_present = true; - kill_all_warning_msgs.id = c.id; - kill_all_warning_msgs.crit = c.crit; - kill_all_warning_msgs.value = c.value.kill_all_warning_msgs(); + kill_all_warning_msgs.id = id; + HANDLE_CODE(kill_all_warning_msgs.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(kill_all_warning_msgs.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -56171,30 +55669,7 @@ void kill_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// KillRequest ::= SEQUENCE -SRSASN_CODE kill_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE kill_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void kill_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; kill_resp_ies_container::kill_resp_ies_container() : msg_id(111, crit_e::reject), @@ -56228,35 +55703,43 @@ SRSASN_CODE kill_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 111: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 111: { nof_mandatory_ies--; - msg_id.id = c.id; - msg_id.crit = c.crit; - msg_id.value = c.value.msg_id(); + msg_id.id = id; + HANDLE_CODE(msg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(msg_id.value.unpack(bref)); break; - case 112: + } + case 112: { nof_mandatory_ies--; - serial_num.id = c.id; - serial_num.crit = c.crit; - serial_num.value = c.value.serial_num(); + serial_num.id = id; + HANDLE_CODE(serial_num.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(serial_num.value.unpack(bref)); break; - case 141: + } + case 141: { broadcast_cancelled_area_list_present = true; - broadcast_cancelled_area_list.id = c.id; - broadcast_cancelled_area_list.crit = c.crit; - broadcast_cancelled_area_list.value = c.value.broadcast_cancelled_area_list(); + broadcast_cancelled_area_list.id = id; + HANDLE_CODE(broadcast_cancelled_area_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(broadcast_cancelled_area_list.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -56285,30 +55768,7 @@ void kill_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// KillResponse ::= SEQUENCE -SRSASN_CODE kill_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE kill_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void kill_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; location_report_ies_container::location_report_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -56343,47 +55803,59 @@ SRSASN_CODE location_report_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 100: + } + case 100: { nof_mandatory_ies--; - eutran_cgi.id = c.id; - eutran_cgi.crit = c.crit; - eutran_cgi.value = c.value.eutran_cgi(); + eutran_cgi.id = id; + HANDLE_CODE(eutran_cgi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(eutran_cgi.value.unpack(bref)); break; - case 67: + } + case 67: { nof_mandatory_ies--; - tai.id = c.id; - tai.crit = c.crit; - tai.value = c.value.tai(); + tai.id = id; + HANDLE_CODE(tai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai.value.unpack(bref)); break; - case 98: + } + case 98: { nof_mandatory_ies--; - request_type.id = c.id; - request_type.crit = c.crit; - request_type.value = c.value.request_type(); + request_type.id = id; + HANDLE_CODE(request_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(request_type.value.unpack(bref)); break; - case 288: + } + case 288: { ps_cell_info_present = true; - ps_cell_info.id = c.id; - ps_cell_info.crit = c.crit; - ps_cell_info.value = c.value.ps_cell_info(); + ps_cell_info.id = id; + HANDLE_CODE(ps_cell_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ps_cell_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -56414,30 +55886,7 @@ void location_report_ies_container::to_json(json_writer& j) const j.end_obj(); } -// LocationReport ::= SEQUENCE -SRSASN_CODE location_report_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE location_report_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void location_report_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; location_report_ctrl_ies_container::location_report_ctrl_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject), request_type(98, crit_e::ignore) @@ -56461,29 +55910,35 @@ SRSASN_CODE location_report_ctrl_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 98: + } + case 98: { nof_mandatory_ies--; - request_type.id = c.id; - request_type.crit = c.crit; - request_type.value = c.value.request_type(); + request_type.id = id; + HANDLE_CODE(request_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(request_type.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -56506,30 +55961,7 @@ void location_report_ctrl_ies_container::to_json(json_writer& j) const j.end_obj(); } -// LocationReportingControl ::= SEQUENCE -SRSASN_CODE location_report_ctrl_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE location_report_ctrl_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void location_report_ctrl_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; location_report_fail_ind_ies_container::location_report_fail_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject), cause(2, crit_e::ignore) @@ -56553,29 +55985,35 @@ SRSASN_CODE location_report_fail_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -56598,30 +56036,7 @@ void location_report_fail_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// LocationReportingFailureIndication ::= SEQUENCE -SRSASN_CODE location_report_fail_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE location_report_fail_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void location_report_fail_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; mmecp_relocation_ind_ies_container::mmecp_relocation_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject) @@ -56644,23 +56059,27 @@ SRSASN_CODE mmecp_relocation_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -56681,30 +56100,7 @@ void mmecp_relocation_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// MMECPRelocationIndication ::= SEQUENCE -SRSASN_CODE mmecp_relocation_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE mmecp_relocation_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void mmecp_relocation_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; mme_cfg_transfer_ies_container::mme_cfg_transfer_ies_container() : son_cfg_transfer_mct(130, crit_e::ignore), en_dcson_cfg_transfer_mct(295, crit_e::ignore) @@ -56731,23 +56127,27 @@ SRSASN_CODE mme_cfg_transfer_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 130: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 130: { son_cfg_transfer_mct_present = true; - son_cfg_transfer_mct.id = c.id; - son_cfg_transfer_mct.crit = c.crit; - son_cfg_transfer_mct.value = c.value.son_cfg_transfer_mct(); + son_cfg_transfer_mct.id = id; + HANDLE_CODE(son_cfg_transfer_mct.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(son_cfg_transfer_mct.value.unpack(bref)); break; - case 295: + } + case 295: { en_dcson_cfg_transfer_mct_present = true; - en_dcson_cfg_transfer_mct.id = c.id; - en_dcson_cfg_transfer_mct.crit = c.crit; - en_dcson_cfg_transfer_mct.value = c.value.en_dcson_cfg_transfer_mct(); + en_dcson_cfg_transfer_mct.id = id; + HANDLE_CODE(en_dcson_cfg_transfer_mct.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(en_dcson_cfg_transfer_mct.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -56768,30 +56168,7 @@ void mme_cfg_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// MMEConfigurationTransfer ::= SEQUENCE -SRSASN_CODE mme_cfg_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE mme_cfg_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void mme_cfg_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; mme_cfg_upd_ies_container::mme_cfg_upd_ies_container() : mm_ename(61, crit_e::ignore), @@ -56829,35 +56206,43 @@ SRSASN_CODE mme_cfg_upd_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 61: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 61: { mm_ename_present = true; - mm_ename.id = c.id; - mm_ename.crit = c.crit; - mm_ename.value = c.value.mm_ename(); + mm_ename.id = id; + HANDLE_CODE(mm_ename.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mm_ename.value.unpack(bref)); break; - case 105: + } + case 105: { served_gummeis_present = true; - served_gummeis.id = c.id; - served_gummeis.crit = c.crit; - served_gummeis.value = c.value.served_gummeis(); + served_gummeis.id = id; + HANDLE_CODE(served_gummeis.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(served_gummeis.value.unpack(bref)); break; - case 87: + } + case 87: { relative_mme_capacity_present = true; - relative_mme_capacity.id = c.id; - relative_mme_capacity.crit = c.crit; - relative_mme_capacity.value = c.value.relative_mme_capacity(); + relative_mme_capacity.id = id; + HANDLE_CODE(relative_mme_capacity.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(relative_mme_capacity.value.unpack(bref)); break; - case 247: + } + case 247: { served_dcns_present = true; - served_dcns.id = c.id; - served_dcns.crit = c.crit; - served_dcns.value = c.value.served_dcns(); + served_dcns.id = id; + HANDLE_CODE(served_dcns.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(served_dcns.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -56886,52 +56271,7 @@ void mme_cfg_upd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// MMEConfigurationUpdate ::= SEQUENCE -SRSASN_CODE mme_cfg_upd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE mme_cfg_upd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void mme_cfg_upd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -// MMEConfigurationUpdateAcknowledge ::= SEQUENCE -SRSASN_CODE mme_cfg_upd_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(pack_dyn_seq_of(bref, protocol_ies, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE mme_cfg_upd_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(unpack_dyn_seq_of(protocol_ies, bref, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -void mme_cfg_upd_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; mme_cfg_upd_fail_ies_container::mme_cfg_upd_fail_ies_container() : cause(2, crit_e::ignore), time_to_wait(65, crit_e::ignore), crit_diagnostics(58, crit_e::ignore) @@ -56961,29 +56301,35 @@ SRSASN_CODE mme_cfg_upd_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 1; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 2: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 65: + } + case 65: { time_to_wait_present = true; - time_to_wait.id = c.id; - time_to_wait.crit = c.crit; - time_to_wait.value = c.value.time_to_wait(); + time_to_wait.id = id; + HANDLE_CODE(time_to_wait.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_to_wait.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -57010,52 +56356,7 @@ void mme_cfg_upd_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// MMEConfigurationUpdateFailure ::= SEQUENCE -SRSASN_CODE mme_cfg_upd_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE mme_cfg_upd_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void mme_cfg_upd_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -// MMEDirectInformationTransfer ::= SEQUENCE -SRSASN_CODE mme_direct_info_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(pack_dyn_seq_of(bref, protocol_ies, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE mme_direct_info_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(unpack_dyn_seq_of(protocol_ies, bref, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -void mme_direct_info_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; mme_status_transfer_ies_container::mme_status_transfer_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -57081,29 +56382,35 @@ SRSASN_CODE mme_status_transfer_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 90: + } + case 90: { nof_mandatory_ies--; - enb_status_transfer_transparent_container.id = c.id; - enb_status_transfer_transparent_container.crit = c.crit; - enb_status_transfer_transparent_container.value = c.value.enb_status_transfer_transparent_container(); + enb_status_transfer_transparent_container.id = id; + HANDLE_CODE(enb_status_transfer_transparent_container.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_status_transfer_transparent_container.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -57126,30 +56433,7 @@ void mme_status_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// MMEStatusTransfer ::= SEQUENCE -SRSASN_CODE mme_status_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE mme_status_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void mme_status_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; nas_delivery_ind_ies_container::nas_delivery_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject) @@ -57172,23 +56456,27 @@ SRSASN_CODE nas_delivery_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -57209,30 +56497,7 @@ void nas_delivery_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// NASDeliveryIndication ::= SEQUENCE -SRSASN_CODE nas_delivery_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE nas_delivery_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void nas_delivery_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; nas_non_delivery_ind_ies_container::nas_non_delivery_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -57260,35 +56525,43 @@ SRSASN_CODE nas_non_delivery_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 26: + } + case 26: { nof_mandatory_ies--; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -57313,30 +56586,7 @@ void nas_non_delivery_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// NASNonDeliveryIndication ::= SEQUENCE -SRSASN_CODE nas_non_delivery_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE nas_non_delivery_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void nas_non_delivery_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; overload_start_ies_container::overload_start_ies_container() : overload_resp(101, crit_e::reject), gummei_list(154, crit_e::ignore), traffic_load_reduction_ind(161, crit_e::ignore) @@ -57366,29 +56616,35 @@ SRSASN_CODE overload_start_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 1; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 101: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 101: { nof_mandatory_ies--; - overload_resp.id = c.id; - overload_resp.crit = c.crit; - overload_resp.value = c.value.overload_resp(); + overload_resp.id = id; + HANDLE_CODE(overload_resp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(overload_resp.value.unpack(bref)); break; - case 154: + } + case 154: { gummei_list_present = true; - gummei_list.id = c.id; - gummei_list.crit = c.crit; - gummei_list.value = c.value.gummei_list(); + gummei_list.id = id; + HANDLE_CODE(gummei_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(gummei_list.value.unpack(bref)); break; - case 161: + } + case 161: { traffic_load_reduction_ind_present = true; - traffic_load_reduction_ind.id = c.id; - traffic_load_reduction_ind.crit = c.crit; - traffic_load_reduction_ind.value = c.value.traffic_load_reduction_ind(); + traffic_load_reduction_ind.id = id; + HANDLE_CODE(traffic_load_reduction_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(traffic_load_reduction_ind.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -57415,52 +56671,7 @@ void overload_start_ies_container::to_json(json_writer& j) const j.end_obj(); } -// OverloadStart ::= SEQUENCE -SRSASN_CODE overload_start_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE overload_start_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void overload_start_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -// OverloadStop ::= SEQUENCE -SRSASN_CODE overload_stop_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(pack_dyn_seq_of(bref, protocol_ies, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE overload_stop_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(unpack_dyn_seq_of(protocol_ies, bref, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -void overload_stop_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pws_fail_ind_ies_container::pws_fail_ind_ies_container() : pw_sfailed_ecgi_list(222, crit_e::reject), global_enb_id(59, crit_e::reject) @@ -57483,23 +56694,27 @@ SRSASN_CODE pws_fail_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 222: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 222: { nof_mandatory_ies--; - pw_sfailed_ecgi_list.id = c.id; - pw_sfailed_ecgi_list.crit = c.crit; - pw_sfailed_ecgi_list.value = c.value.pw_sfailed_ecgi_list(); + pw_sfailed_ecgi_list.id = id; + HANDLE_CODE(pw_sfailed_ecgi_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pw_sfailed_ecgi_list.value.unpack(bref)); break; - case 59: + } + case 59: { nof_mandatory_ies--; - global_enb_id.id = c.id; - global_enb_id.crit = c.crit; - global_enb_id.value = c.value.global_enb_id(); + global_enb_id.id = id; + HANDLE_CODE(global_enb_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(global_enb_id.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -57520,30 +56735,7 @@ void pws_fail_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PWSFailureIndication ::= SEQUENCE -SRSASN_CODE pws_fail_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pws_fail_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pws_fail_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; pws_restart_ind_ies_container::pws_restart_ind_ies_container() : ecgi_list_for_restart(182, crit_e::reject), @@ -57574,35 +56766,43 @@ SRSASN_CODE pws_restart_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 182: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 182: { nof_mandatory_ies--; - ecgi_list_for_restart.id = c.id; - ecgi_list_for_restart.crit = c.crit; - ecgi_list_for_restart.value = c.value.ecgi_list_for_restart(); + ecgi_list_for_restart.id = id; + HANDLE_CODE(ecgi_list_for_restart.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ecgi_list_for_restart.value.unpack(bref)); break; - case 59: + } + case 59: { nof_mandatory_ies--; - global_enb_id.id = c.id; - global_enb_id.crit = c.crit; - global_enb_id.value = c.value.global_enb_id(); + global_enb_id.id = id; + HANDLE_CODE(global_enb_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(global_enb_id.value.unpack(bref)); break; - case 188: + } + case 188: { nof_mandatory_ies--; - tai_list_for_restart.id = c.id; - tai_list_for_restart.crit = c.crit; - tai_list_for_restart.value = c.value.tai_list_for_restart(); + tai_list_for_restart.id = id; + HANDLE_CODE(tai_list_for_restart.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai_list_for_restart.value.unpack(bref)); break; - case 190: + } + case 190: { emergency_area_id_list_for_restart_present = true; - emergency_area_id_list_for_restart.id = c.id; - emergency_area_id_list_for_restart.crit = c.crit; - emergency_area_id_list_for_restart.value = c.value.emergency_area_id_list_for_restart(); + emergency_area_id_list_for_restart.id = id; + HANDLE_CODE(emergency_area_id_list_for_restart.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(emergency_area_id_list_for_restart.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -57629,30 +56829,7 @@ void pws_restart_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PWSRestartIndication ::= SEQUENCE -SRSASN_CODE pws_restart_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE pws_restart_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void pws_restart_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; paging_ies_container::paging_ies_container() : ue_id_idx_value(80, crit_e::ignore), @@ -57735,101 +56912,131 @@ SRSASN_CODE paging_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 80: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 80: { nof_mandatory_ies--; - ue_id_idx_value.id = c.id; - ue_id_idx_value.crit = c.crit; - ue_id_idx_value.value = c.value.ue_id_idx_value(); + ue_id_idx_value.id = id; + HANDLE_CODE(ue_id_idx_value.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_id_idx_value.value.unpack(bref)); break; - case 43: + } + case 43: { nof_mandatory_ies--; - ue_paging_id.id = c.id; - ue_paging_id.crit = c.crit; - ue_paging_id.value = c.value.ue_paging_id(); + ue_paging_id.id = id; + HANDLE_CODE(ue_paging_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_paging_id.value.unpack(bref)); break; - case 44: + } + case 44: { paging_drx_present = true; - paging_drx.id = c.id; - paging_drx.crit = c.crit; - paging_drx.value = c.value.paging_drx(); + paging_drx.id = id; + HANDLE_CODE(paging_drx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(paging_drx.value.unpack(bref)); break; - case 109: + } + case 109: { nof_mandatory_ies--; - cn_domain.id = c.id; - cn_domain.crit = c.crit; - cn_domain.value = c.value.cn_domain(); + cn_domain.id = id; + HANDLE_CODE(cn_domain.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cn_domain.value.unpack(bref)); break; - case 46: + } + case 46: { nof_mandatory_ies--; - tai_list.id = c.id; - tai_list.crit = c.crit; - tai_list.value = c.value.tai_list(); + tai_list.id = id; + HANDLE_CODE(tai_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai_list.value.unpack(bref)); break; - case 128: + } + case 128: { csg_id_list_present = true; - csg_id_list.id = c.id; - csg_id_list.crit = c.crit; - csg_id_list.value = c.value.csg_id_list(); + csg_id_list.id = id; + HANDLE_CODE(csg_id_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_id_list.value.unpack(bref)); break; - case 151: + } + case 151: { paging_prio_present = true; - paging_prio.id = c.id; - paging_prio.crit = c.crit; - paging_prio.value = c.value.paging_prio(); + paging_prio.id = id; + HANDLE_CODE(paging_prio.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(paging_prio.value.unpack(bref)); break; - case 198: + } + case 198: { ue_radio_cap_for_paging_present = true; - ue_radio_cap_for_paging.id = c.id; - ue_radio_cap_for_paging.crit = c.crit; - ue_radio_cap_for_paging.value = c.value.ue_radio_cap_for_paging(); + ue_radio_cap_for_paging.id = id; + HANDLE_CODE(ue_radio_cap_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap_for_paging.value.unpack(bref)); break; - case 211: + } + case 211: { assist_data_for_paging_present = true; - assist_data_for_paging.id = c.id; - assist_data_for_paging.crit = c.crit; - assist_data_for_paging.value = c.value.assist_data_for_paging(); + assist_data_for_paging.id = id; + HANDLE_CODE(assist_data_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(assist_data_for_paging.value.unpack(bref)); break; - case 227: + } + case 227: { paging_e_drx_info_present = true; - paging_e_drx_info.id = c.id; - paging_e_drx_info.crit = c.crit; - paging_e_drx_info.value = c.value.paging_e_drx_info(); + paging_e_drx_info.id = id; + HANDLE_CODE(paging_e_drx_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(paging_e_drx_info.value.unpack(bref)); break; - case 231: + } + case 231: { extended_ue_id_idx_value_present = true; - extended_ue_id_idx_value.id = c.id; - extended_ue_id_idx_value.crit = c.crit; - extended_ue_id_idx_value.value = c.value.extended_ue_id_idx_value(); + extended_ue_id_idx_value.id = id; + HANDLE_CODE(extended_ue_id_idx_value.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(extended_ue_id_idx_value.value.unpack(bref)); break; - case 239: + } + case 239: { nb_io_t_paging_e_drx_info_present = true; - nb_io_t_paging_e_drx_info.id = c.id; - nb_io_t_paging_e_drx_info.crit = c.crit; - nb_io_t_paging_e_drx_info.value = c.value.nb_io_t_paging_e_drx_info(); + nb_io_t_paging_e_drx_info.id = id; + HANDLE_CODE(nb_io_t_paging_e_drx_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nb_io_t_paging_e_drx_info.value.unpack(bref)); break; - case 244: + } + case 244: { nb_io_t_ue_id_idx_value_present = true; - nb_io_t_ue_id_idx_value.id = c.id; - nb_io_t_ue_id_idx_value.crit = c.crit; - nb_io_t_ue_id_idx_value.value = c.value.nb_io_t_ue_id_idx_value(); + nb_io_t_ue_id_idx_value.id = id; + HANDLE_CODE(nb_io_t_ue_id_idx_value.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nb_io_t_ue_id_idx_value.value.unpack(bref)); break; - case 251: + } + case 251: { enhanced_coverage_restricted_present = true; - enhanced_coverage_restricted.id = c.id; - enhanced_coverage_restricted.crit = c.crit; - enhanced_coverage_restricted.value = c.value.enhanced_coverage_restricted(); + enhanced_coverage_restricted.id = id; + HANDLE_CODE(enhanced_coverage_restricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enhanced_coverage_restricted.value.unpack(bref)); break; - case 271: + } + case 271: { ce_mode_brestricted_present = true; - ce_mode_brestricted.id = c.id; - ce_mode_brestricted.crit = c.crit; - ce_mode_brestricted.value = c.value.ce_mode_brestricted(); + ce_mode_brestricted.id = id; + HANDLE_CODE(ce_mode_brestricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ce_mode_brestricted.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -57898,30 +57105,7 @@ void paging_ies_container::to_json(json_writer& j) const j.end_obj(); } -// Paging ::= SEQUENCE -SRSASN_CODE paging_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE paging_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void paging_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; path_switch_request_ies_container::path_switch_request_ies_container() : enb_ue_s1ap_id(8, crit_e::reject), @@ -57998,101 +57182,131 @@ SRSASN_CODE path_switch_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 6; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 8: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 22: + } + case 22: { nof_mandatory_ies--; - erab_to_be_switched_dl_list.id = c.id; - erab_to_be_switched_dl_list.crit = c.crit; - erab_to_be_switched_dl_list.value = c.value.erab_to_be_switched_dl_list(); + erab_to_be_switched_dl_list.id = id; + HANDLE_CODE(erab_to_be_switched_dl_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_switched_dl_list.value.unpack(bref)); break; - case 88: + } + case 88: { nof_mandatory_ies--; - source_mme_ue_s1ap_id.id = c.id; - source_mme_ue_s1ap_id.crit = c.crit; - source_mme_ue_s1ap_id.value = c.value.source_mme_ue_s1ap_id(); + source_mme_ue_s1ap_id.id = id; + HANDLE_CODE(source_mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(source_mme_ue_s1ap_id.value.unpack(bref)); break; - case 100: + } + case 100: { nof_mandatory_ies--; - eutran_cgi.id = c.id; - eutran_cgi.crit = c.crit; - eutran_cgi.value = c.value.eutran_cgi(); + eutran_cgi.id = id; + HANDLE_CODE(eutran_cgi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(eutran_cgi.value.unpack(bref)); break; - case 67: + } + case 67: { nof_mandatory_ies--; - tai.id = c.id; - tai.crit = c.crit; - tai.value = c.value.tai(); + tai.id = id; + HANDLE_CODE(tai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai.value.unpack(bref)); break; - case 107: + } + case 107: { nof_mandatory_ies--; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 127: + } + case 127: { csg_id_present = true; - csg_id.id = c.id; - csg_id.crit = c.crit; - csg_id.value = c.value.csg_id(); + csg_id.id = id; + HANDLE_CODE(csg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_id.value.unpack(bref)); break; - case 145: + } + case 145: { cell_access_mode_present = true; - cell_access_mode.id = c.id; - cell_access_mode.crit = c.crit; - cell_access_mode.value = c.value.cell_access_mode(); + cell_access_mode.id = id; + HANDLE_CODE(cell_access_mode.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cell_access_mode.value.unpack(bref)); break; - case 157: + } + case 157: { source_mme_gummei_present = true; - source_mme_gummei.id = c.id; - source_mme_gummei.crit = c.crit; - source_mme_gummei.value = c.value.source_mme_gummei(); + source_mme_gummei.id = id; + HANDLE_CODE(source_mme_gummei.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(source_mme_gummei.value.unpack(bref)); break; - case 146: + } + case 146: { csg_membership_status_present = true; - csg_membership_status.id = c.id; - csg_membership_status.crit = c.crit; - csg_membership_status.value = c.value.csg_membership_status(); + csg_membership_status.id = id; + HANDLE_CODE(csg_membership_status.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_status.value.unpack(bref)); break; - case 176: + } + case 176: { tunnel_info_for_bbf_present = true; - tunnel_info_for_bbf.id = c.id; - tunnel_info_for_bbf.crit = c.crit; - tunnel_info_for_bbf.value = c.value.tunnel_info_for_bbf(); + tunnel_info_for_bbf.id = id; + HANDLE_CODE(tunnel_info_for_bbf.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tunnel_info_for_bbf.value.unpack(bref)); break; - case 186: + } + case 186: { lhn_id_present = true; - lhn_id.id = c.id; - lhn_id.crit = c.crit; - lhn_id.value = c.value.lhn_id(); + lhn_id.id = id; + HANDLE_CODE(lhn_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lhn_id.value.unpack(bref)); break; - case 245: + } + case 245: { rrc_resume_cause_present = true; - rrc_resume_cause.id = c.id; - rrc_resume_cause.crit = c.crit; - rrc_resume_cause.value = c.value.rrc_resume_cause(); + rrc_resume_cause.id = id; + HANDLE_CODE(rrc_resume_cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_resume_cause.value.unpack(bref)); break; - case 269: + } + case 269: { nrue_security_cap_present = true; - nrue_security_cap.id = c.id; - nrue_security_cap.crit = c.crit; - nrue_security_cap.value = c.value.nrue_security_cap(); + nrue_security_cap.id = id; + HANDLE_CODE(nrue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrue_security_cap.value.unpack(bref)); break; - case 288: + } + case 288: { ps_cell_info_present = true; - ps_cell_info.id = c.id; - ps_cell_info.crit = c.crit; - ps_cell_info.value = c.value.ps_cell_info(); + ps_cell_info.id = id; + HANDLE_CODE(ps_cell_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ps_cell_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -58157,30 +57371,7 @@ void path_switch_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PathSwitchRequest ::= SEQUENCE -SRSASN_CODE path_switch_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE path_switch_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void path_switch_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; path_switch_request_ack_ies_container::path_switch_request_ack_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -58296,137 +57487,179 @@ SRSASN_CODE path_switch_request_ack_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 66: + } + case 66: { ueaggregate_maximum_bitrate_present = true; - ueaggregate_maximum_bitrate.id = c.id; - ueaggregate_maximum_bitrate.crit = c.crit; - ueaggregate_maximum_bitrate.value = c.value.ueaggregate_maximum_bitrate(); + ueaggregate_maximum_bitrate.id = id; + HANDLE_CODE(ueaggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueaggregate_maximum_bitrate.value.unpack(bref)); break; - case 95: + } + case 95: { erab_to_be_switched_ul_list_present = true; - erab_to_be_switched_ul_list.id = c.id; - erab_to_be_switched_ul_list.crit = c.crit; - erab_to_be_switched_ul_list.value = c.value.erab_to_be_switched_ul_list(); + erab_to_be_switched_ul_list.id = id; + HANDLE_CODE(erab_to_be_switched_ul_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_switched_ul_list.value.unpack(bref)); break; - case 33: + } + case 33: { erab_to_be_released_list_present = true; - erab_to_be_released_list.id = c.id; - erab_to_be_released_list.crit = c.crit; - erab_to_be_released_list.value = c.value.erab_to_be_released_list(); + erab_to_be_released_list.id = id; + HANDLE_CODE(erab_to_be_released_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_to_be_released_list.value.unpack(bref)); break; - case 40: + } + case 40: { nof_mandatory_ies--; - security_context.id = c.id; - security_context.crit = c.crit; - security_context.value = c.value.security_context(); + security_context.id = id; + HANDLE_CODE(security_context.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_context.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 158: + } + case 158: { mme_ue_s1ap_id_minus2_present = true; - mme_ue_s1ap_id_minus2.id = c.id; - mme_ue_s1ap_id_minus2.crit = c.crit; - mme_ue_s1ap_id_minus2.value = c.value.mme_ue_s1ap_id_minus2(); + mme_ue_s1ap_id_minus2.id = id; + HANDLE_CODE(mme_ue_s1ap_id_minus2.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id_minus2.value.unpack(bref)); break; - case 146: + } + case 146: { csg_membership_status_present = true; - csg_membership_status.id = c.id; - csg_membership_status.crit = c.crit; - csg_membership_status.value = c.value.csg_membership_status(); + csg_membership_status.id = id; + HANDLE_CODE(csg_membership_status.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_status.value.unpack(bref)); break; - case 195: + } + case 195: { pro_se_authorized_present = true; - pro_se_authorized.id = c.id; - pro_se_authorized.crit = c.crit; - pro_se_authorized.value = c.value.pro_se_authorized(); + pro_se_authorized.id = id; + HANDLE_CODE(pro_se_authorized.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pro_se_authorized.value.unpack(bref)); break; - case 241: + } + case 241: { ueuser_plane_cio_tsupport_ind_present = true; - ueuser_plane_cio_tsupport_ind.id = c.id; - ueuser_plane_cio_tsupport_ind.crit = c.crit; - ueuser_plane_cio_tsupport_ind.value = c.value.ueuser_plane_cio_tsupport_ind(); + ueuser_plane_cio_tsupport_ind.id = id; + HANDLE_CODE(ueuser_plane_cio_tsupport_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueuser_plane_cio_tsupport_ind.value.unpack(bref)); break; - case 240: + } + case 240: { v2xservices_authorized_present = true; - v2xservices_authorized.id = c.id; - v2xservices_authorized.crit = c.crit; - v2xservices_authorized.value = c.value.v2xservices_authorized(); + v2xservices_authorized.id = id; + HANDLE_CODE(v2xservices_authorized.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(v2xservices_authorized.value.unpack(bref)); break; - case 248: + } + case 248: { ue_sidelink_aggregate_maximum_bitrate_present = true; - ue_sidelink_aggregate_maximum_bitrate.id = c.id; - ue_sidelink_aggregate_maximum_bitrate.crit = c.crit; - ue_sidelink_aggregate_maximum_bitrate.value = c.value.ue_sidelink_aggregate_maximum_bitrate(); + ue_sidelink_aggregate_maximum_bitrate.id = id; + HANDLE_CODE(ue_sidelink_aggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_sidelink_aggregate_maximum_bitrate.value.unpack(bref)); break; - case 251: + } + case 251: { enhanced_coverage_restricted_present = true; - enhanced_coverage_restricted.id = c.id; - enhanced_coverage_restricted.crit = c.crit; - enhanced_coverage_restricted.value = c.value.enhanced_coverage_restricted(); + enhanced_coverage_restricted.id = id; + HANDLE_CODE(enhanced_coverage_restricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enhanced_coverage_restricted.value.unpack(bref)); break; - case 269: + } + case 269: { nrue_security_cap_present = true; - nrue_security_cap.id = c.id; - nrue_security_cap.crit = c.crit; - nrue_security_cap.value = c.value.nrue_security_cap(); + nrue_security_cap.id = id; + HANDLE_CODE(nrue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrue_security_cap.value.unpack(bref)); break; - case 271: + } + case 271: { ce_mode_brestricted_present = true; - ce_mode_brestricted.id = c.id; - ce_mode_brestricted.crit = c.crit; - ce_mode_brestricted.value = c.value.ce_mode_brestricted(); + ce_mode_brestricted.id = id; + HANDLE_CODE(ce_mode_brestricted.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ce_mode_brestricted.value.unpack(bref)); break; - case 277: + } + case 277: { aerial_uesubscription_info_present = true; - aerial_uesubscription_info.id = c.id; - aerial_uesubscription_info.crit = c.crit; - aerial_uesubscription_info.value = c.value.aerial_uesubscription_info(); + aerial_uesubscription_info.id = id; + HANDLE_CODE(aerial_uesubscription_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(aerial_uesubscription_info.value.unpack(bref)); break; - case 283: + } + case 283: { pending_data_ind_present = true; - pending_data_ind.id = c.id; - pending_data_ind.crit = c.crit; - pending_data_ind.value = c.value.pending_data_ind(); + pending_data_ind.id = id; + HANDLE_CODE(pending_data_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pending_data_ind.value.unpack(bref)); break; - case 278: + } + case 278: { subscription_based_ue_differentiation_info_present = true; - subscription_based_ue_differentiation_info.id = c.id; - subscription_based_ue_differentiation_info.crit = c.crit; - subscription_based_ue_differentiation_info.value = c.value.subscription_based_ue_differentiation_info(); + subscription_based_ue_differentiation_info.id = id; + HANDLE_CODE(subscription_based_ue_differentiation_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscription_based_ue_differentiation_info.value.unpack(bref)); break; - case 41: + } + case 41: { ho_restrict_list_present = true; - ho_restrict_list.id = c.id; - ho_restrict_list.crit = c.crit; - ho_restrict_list.value = c.value.ho_restrict_list(); + ho_restrict_list.id = id; + HANDLE_CODE(ho_restrict_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ho_restrict_list.value.unpack(bref)); break; - case 299: + } + case 299: { add_rrm_prio_idx_present = true; - add_rrm_prio_idx.id = c.id; - add_rrm_prio_idx.crit = c.crit; - add_rrm_prio_idx.value = c.value.add_rrm_prio_idx(); + add_rrm_prio_idx.id = id; + HANDLE_CODE(add_rrm_prio_idx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_rrm_prio_idx.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -58521,30 +57754,7 @@ void path_switch_request_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PathSwitchRequestAcknowledge ::= SEQUENCE -SRSASN_CODE path_switch_request_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE path_switch_request_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void path_switch_request_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; path_switch_request_fail_ies_container::path_switch_request_fail_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -58575,35 +57785,43 @@ SRSASN_CODE path_switch_request_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -58630,29 +57848,6 @@ void path_switch_request_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// PathSwitchRequestFailure ::= SEQUENCE -SRSASN_CODE path_switch_request_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE path_switch_request_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void path_switch_request_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - template private_ie_container_item_s::private_ie_container_item_s(private_ie_id_c id_, crit_e crit_) : id(id_), crit(crit_) @@ -58743,7 +57938,7 @@ void private_msg_s::to_json(json_writer& j) const j.end_array(); } -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; reroute_nas_request_ies_container::reroute_nas_request_ies_container() : enb_ue_s1ap_id(8, crit_e::reject), @@ -58784,47 +57979,59 @@ SRSASN_CODE reroute_nas_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 8: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 0: + } + case 0: { mme_ue_s1ap_id_present = true; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 225: + } + case 225: { nof_mandatory_ies--; - s1_msg.id = c.id; - s1_msg.crit = c.crit; - s1_msg.value = c.value.s1_msg(); + s1_msg.id = id; + HANDLE_CODE(s1_msg.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(s1_msg.value.unpack(bref)); break; - case 223: + } + case 223: { nof_mandatory_ies--; - mme_group_id.id = c.id; - mme_group_id.crit = c.crit; - mme_group_id.value = c.value.mme_group_id(); + mme_group_id.id = id; + HANDLE_CODE(mme_group_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_group_id.value.unpack(bref)); break; - case 224: + } + case 224: { add_guti_present = true; - add_guti.id = c.id; - add_guti.crit = c.crit; - add_guti.value = c.value.add_guti(); + add_guti.id = id; + HANDLE_CODE(add_guti.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_guti.value.unpack(bref)); break; - case 230: + } + case 230: { ue_usage_type_present = true; - ue_usage_type.id = c.id; - ue_usage_type.crit = c.crit; - ue_usage_type.value = c.value.ue_usage_type(); + ue_usage_type.id = id; + HANDLE_CODE(ue_usage_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_usage_type.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -58859,30 +58066,7 @@ void reroute_nas_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// RerouteNASRequest ::= SEQUENCE -SRSASN_CODE reroute_nas_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE reroute_nas_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void reroute_nas_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; reset_ies_container::reset_ies_container() : cause(2, crit_e::ignore), reset_type(92, crit_e::reject) {} SRSASN_CODE reset_ies_container::pack(bit_ref& bref) const @@ -58903,23 +58087,27 @@ SRSASN_CODE reset_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 2: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 92: + } + case 92: { nof_mandatory_ies--; - reset_type.id = c.id; - reset_type.crit = c.crit; - reset_type.value = c.value.reset_type(); + reset_type.id = id; + HANDLE_CODE(reset_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(reset_type.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -58940,30 +58128,7 @@ void reset_ies_container::to_json(json_writer& j) const j.end_obj(); } -// Reset ::= SEQUENCE -SRSASN_CODE reset_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE reset_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void reset_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; reset_ack_ies_container::reset_ack_ies_container() : ue_associated_lc_s1_conn_list_res_ack(93, crit_e::ignore), crit_diagnostics(58, crit_e::ignore) @@ -58990,23 +58155,27 @@ SRSASN_CODE reset_ack_ies_container::unpack(cbit_ref& bref) unpack_length(nof_ies, bref, 0u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 93: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 93: { ue_associated_lc_s1_conn_list_res_ack_present = true; - ue_associated_lc_s1_conn_list_res_ack.id = c.id; - ue_associated_lc_s1_conn_list_res_ack.crit = c.crit; - ue_associated_lc_s1_conn_list_res_ack.value = c.value.ue_associated_lc_s1_conn_list_res_ack(); + ue_associated_lc_s1_conn_list_res_ack.id = id; + HANDLE_CODE(ue_associated_lc_s1_conn_list_res_ack.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_associated_lc_s1_conn_list_res_ack.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -59027,52 +58196,7 @@ void reset_ack_ies_container::to_json(json_writer& j) const j.end_obj(); } -// ResetAcknowledge ::= SEQUENCE -SRSASN_CODE reset_ack_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE reset_ack_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void reset_ack_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -// RetrieveUEInformation ::= SEQUENCE -SRSASN_CODE retrieve_ue_info_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(pack_dyn_seq_of(bref, protocol_ies, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE retrieve_ue_info_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(unpack_dyn_seq_of(protocol_ies, bref, 0, 65535, true)); - - return SRSASN_SUCCESS; -} -void retrieve_ue_info_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; s1_setup_fail_ies_container::s1_setup_fail_ies_container() : cause(2, crit_e::ignore), time_to_wait(65, crit_e::ignore), crit_diagnostics(58, crit_e::ignore) @@ -59102,29 +58226,35 @@ SRSASN_CODE s1_setup_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 1; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 2: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 65: + } + case 65: { time_to_wait_present = true; - time_to_wait.id = c.id; - time_to_wait.crit = c.crit; - time_to_wait.value = c.value.time_to_wait(); + time_to_wait.id = id; + HANDLE_CODE(time_to_wait.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_to_wait.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -59151,30 +58281,7 @@ void s1_setup_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// S1SetupFailure ::= SEQUENCE -SRSASN_CODE s1_setup_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE s1_setup_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void s1_setup_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; s1_setup_request_ies_container::s1_setup_request_ies_container() : global_enb_id(59, crit_e::reject), @@ -59225,59 +58332,75 @@ SRSASN_CODE s1_setup_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 59: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 59: { nof_mandatory_ies--; - global_enb_id.id = c.id; - global_enb_id.crit = c.crit; - global_enb_id.value = c.value.global_enb_id(); + global_enb_id.id = id; + HANDLE_CODE(global_enb_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(global_enb_id.value.unpack(bref)); break; - case 60: + } + case 60: { enbname_present = true; - enbname.id = c.id; - enbname.crit = c.crit; - enbname.value = c.value.enbname(); + enbname.id = id; + HANDLE_CODE(enbname.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enbname.value.unpack(bref)); break; - case 64: + } + case 64: { nof_mandatory_ies--; - supported_tas.id = c.id; - supported_tas.crit = c.crit; - supported_tas.value = c.value.supported_tas(); + supported_tas.id = id; + HANDLE_CODE(supported_tas.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(supported_tas.value.unpack(bref)); break; - case 137: + } + case 137: { nof_mandatory_ies--; - default_paging_drx.id = c.id; - default_paging_drx.crit = c.crit; - default_paging_drx.value = c.value.default_paging_drx(); + default_paging_drx.id = id; + HANDLE_CODE(default_paging_drx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(default_paging_drx.value.unpack(bref)); break; - case 128: + } + case 128: { csg_id_list_present = true; - csg_id_list.id = c.id; - csg_id_list.crit = c.crit; - csg_id_list.value = c.value.csg_id_list(); + csg_id_list.id = id; + HANDLE_CODE(csg_id_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_id_list.value.unpack(bref)); break; - case 228: + } + case 228: { ue_retention_info_present = true; - ue_retention_info.id = c.id; - ue_retention_info.crit = c.crit; - ue_retention_info.value = c.value.ue_retention_info(); + ue_retention_info.id = id; + HANDLE_CODE(ue_retention_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_retention_info.value.unpack(bref)); break; - case 234: + } + case 234: { nb_io_t_default_paging_drx_present = true; - nb_io_t_default_paging_drx.id = c.id; - nb_io_t_default_paging_drx.crit = c.crit; - nb_io_t_default_paging_drx.value = c.value.nb_io_t_default_paging_drx(); + nb_io_t_default_paging_drx.id = id; + HANDLE_CODE(nb_io_t_default_paging_drx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nb_io_t_default_paging_drx.value.unpack(bref)); break; - case 291: + } + case 291: { connectedeng_nb_list_present = true; - connectedeng_nb_list.id = c.id; - connectedeng_nb_list.crit = c.crit; - connectedeng_nb_list.value = c.value.connectedeng_nb_list(); + connectedeng_nb_list.id = id; + HANDLE_CODE(connectedeng_nb_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(connectedeng_nb_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -59320,30 +58443,7 @@ void s1_setup_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// S1SetupRequest ::= SEQUENCE -SRSASN_CODE s1_setup_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE s1_setup_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void s1_setup_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; s1_setup_resp_ies_container::s1_setup_resp_ies_container() : mm_ename(61, crit_e::ignore), @@ -59392,53 +58492,67 @@ SRSASN_CODE s1_setup_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 61: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 61: { mm_ename_present = true; - mm_ename.id = c.id; - mm_ename.crit = c.crit; - mm_ename.value = c.value.mm_ename(); + mm_ename.id = id; + HANDLE_CODE(mm_ename.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mm_ename.value.unpack(bref)); break; - case 105: + } + case 105: { nof_mandatory_ies--; - served_gummeis.id = c.id; - served_gummeis.crit = c.crit; - served_gummeis.value = c.value.served_gummeis(); + served_gummeis.id = id; + HANDLE_CODE(served_gummeis.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(served_gummeis.value.unpack(bref)); break; - case 87: + } + case 87: { nof_mandatory_ies--; - relative_mme_capacity.id = c.id; - relative_mme_capacity.crit = c.crit; - relative_mme_capacity.value = c.value.relative_mme_capacity(); + relative_mme_capacity.id = id; + HANDLE_CODE(relative_mme_capacity.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(relative_mme_capacity.value.unpack(bref)); break; - case 163: + } + case 163: { mme_relay_support_ind_present = true; - mme_relay_support_ind.id = c.id; - mme_relay_support_ind.crit = c.crit; - mme_relay_support_ind.value = c.value.mme_relay_support_ind(); + mme_relay_support_ind.id = id; + HANDLE_CODE(mme_relay_support_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_relay_support_ind.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 228: + } + case 228: { ue_retention_info_present = true; - ue_retention_info.id = c.id; - ue_retention_info.crit = c.crit; - ue_retention_info.value = c.value.ue_retention_info(); + ue_retention_info.id = id; + HANDLE_CODE(ue_retention_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_retention_info.value.unpack(bref)); break; - case 247: + } + case 247: { served_dcns_present = true; - served_dcns.id = c.id; - served_dcns.crit = c.crit; - served_dcns.value = c.value.served_dcns(); + served_dcns.id = id; + HANDLE_CODE(served_dcns.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(served_dcns.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -59479,30 +58593,7 @@ void s1_setup_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// S1SetupResponse ::= SEQUENCE -SRSASN_CODE s1_setup_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE s1_setup_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void s1_setup_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; secondary_rat_data_usage_report_ies_container::secondary_rat_data_usage_report_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -59543,47 +58634,59 @@ SRSASN_CODE secondary_rat_data_usage_report_ies_container::unpack(cbit_ref& bref uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 264: + } + case 264: { nof_mandatory_ies--; - secondary_rat_data_usage_report_list.id = c.id; - secondary_rat_data_usage_report_list.crit = c.crit; - secondary_rat_data_usage_report_list.value = c.value.secondary_rat_data_usage_report_list(); + secondary_rat_data_usage_report_list.id = id; + HANDLE_CODE(secondary_rat_data_usage_report_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_report_list.value.unpack(bref)); break; - case 266: + } + case 266: { ho_flag_present = true; - ho_flag.id = c.id; - ho_flag.crit = c.crit; - ho_flag.value = c.value.ho_flag(); + ho_flag.id = id; + HANDLE_CODE(ho_flag.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ho_flag.value.unpack(bref)); break; - case 189: + } + case 189: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 297: + } + case 297: { time_since_secondary_node_release_present = true; - time_since_secondary_node_release.id = c.id; - time_since_secondary_node_release.crit = c.crit; - time_since_secondary_node_release.value = c.value.time_since_secondary_node_release(); + time_since_secondary_node_release.id = id; + HANDLE_CODE(time_since_secondary_node_release.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_since_secondary_node_release.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -59618,30 +58721,7 @@ void secondary_rat_data_usage_report_ies_container::to_json(json_writer& j) cons j.end_obj(); } -// SecondaryRATDataUsageReport ::= SEQUENCE -SRSASN_CODE secondary_rat_data_usage_report_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE secondary_rat_data_usage_report_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void secondary_rat_data_usage_report_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; trace_fail_ind_ies_container::trace_fail_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -59669,35 +58749,43 @@ SRSASN_CODE trace_fail_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 86: + } + case 86: { nof_mandatory_ies--; - e_utran_trace_id.id = c.id; - e_utran_trace_id.crit = c.crit; - e_utran_trace_id.value = c.value.e_utran_trace_id(); + e_utran_trace_id.id = id; + HANDLE_CODE(e_utran_trace_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(e_utran_trace_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -59722,30 +58810,7 @@ void trace_fail_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// TraceFailureIndication ::= SEQUENCE -SRSASN_CODE trace_fail_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE trace_fail_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void trace_fail_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; trace_start_ies_container::trace_start_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject), trace_activation(25, crit_e::ignore) @@ -59769,29 +58834,35 @@ SRSASN_CODE trace_start_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 25: + } + case 25: { nof_mandatory_ies--; - trace_activation.id = c.id; - trace_activation.crit = c.crit; - trace_activation.value = c.value.trace_activation(); + trace_activation.id = id; + HANDLE_CODE(trace_activation.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(trace_activation.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -59814,30 +58885,7 @@ void trace_start_ies_container::to_json(json_writer& j) const j.end_obj(); } -// TraceStart ::= SEQUENCE -SRSASN_CODE trace_start_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE trace_start_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void trace_start_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_cap_info_ind_ies_container::ue_cap_info_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -59878,47 +58926,59 @@ SRSASN_CODE ue_cap_info_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 74: + } + case 74: { nof_mandatory_ies--; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; - case 198: + } + case 198: { ue_radio_cap_for_paging_present = true; - ue_radio_cap_for_paging.id = c.id; - ue_radio_cap_for_paging.crit = c.crit; - ue_radio_cap_for_paging.value = c.value.ue_radio_cap_for_paging(); + ue_radio_cap_for_paging.id = id; + HANDLE_CODE(ue_radio_cap_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap_for_paging.value.unpack(bref)); break; - case 263: + } + case 263: { ue_application_layer_meas_cap_present = true; - ue_application_layer_meas_cap.id = c.id; - ue_application_layer_meas_cap.crit = c.crit; - ue_application_layer_meas_cap.value = c.value.ue_application_layer_meas_cap(); + ue_application_layer_meas_cap.id = id; + HANDLE_CODE(ue_application_layer_meas_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_application_layer_meas_cap.value.unpack(bref)); break; - case 272: + } + case 272: { lte_m_ind_present = true; - lte_m_ind.id = c.id; - lte_m_ind.crit = c.crit; - lte_m_ind.value = c.value.lte_m_ind(); + lte_m_ind.id = id; + HANDLE_CODE(lte_m_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lte_m_ind.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -59953,30 +59013,7 @@ void ue_cap_info_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UECapabilityInfoIndication ::= SEQUENCE -SRSASN_CODE ue_cap_info_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_cap_info_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_cap_info_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_mod_confirm_ies_container::ue_context_mod_confirm_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -60010,35 +59047,43 @@ SRSASN_CODE ue_context_mod_confirm_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 146: + } + case 146: { csg_membership_status_present = true; - csg_membership_status.id = c.id; - csg_membership_status.crit = c.crit; - csg_membership_status.value = c.value.csg_membership_status(); + csg_membership_status.id = id; + HANDLE_CODE(csg_membership_status.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_status.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -60067,30 +59112,7 @@ void ue_context_mod_confirm_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextModificationConfirm ::= SEQUENCE -SRSASN_CODE ue_context_mod_confirm_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_mod_confirm_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_mod_confirm_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_mod_fail_ies_container::ue_context_mod_fail_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -60121,35 +59143,43 @@ SRSASN_CODE ue_context_mod_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -60176,30 +59206,7 @@ void ue_context_mod_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextModificationFailure ::= SEQUENCE -SRSASN_CODE ue_context_mod_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_mod_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_mod_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_mod_ind_ies_container::ue_context_mod_ind_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject), csg_membership_info(226, crit_e::reject) @@ -60226,29 +59233,35 @@ SRSASN_CODE ue_context_mod_ind_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 226: + } + case 226: { csg_membership_info_present = true; - csg_membership_info.id = c.id; - csg_membership_info.crit = c.crit; - csg_membership_info.value = c.value.csg_membership_info(); + csg_membership_info.id = id; + HANDLE_CODE(csg_membership_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -60273,30 +59286,7 @@ void ue_context_mod_ind_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextModificationIndication ::= SEQUENCE -SRSASN_CODE ue_context_mod_ind_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_mod_ind_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_mod_ind_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_mod_request_ies_container::ue_context_mod_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -60400,119 +59390,155 @@ SRSASN_CODE ue_context_mod_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 73: + } + case 73: { security_key_present = true; - security_key.id = c.id; - security_key.crit = c.crit; - security_key.value = c.value.security_key(); + security_key.id = id; + HANDLE_CODE(security_key.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_key.value.unpack(bref)); break; - case 106: + } + case 106: { subscriber_profile_idfor_rfp_present = true; - subscriber_profile_idfor_rfp.id = c.id; - subscriber_profile_idfor_rfp.crit = c.crit; - subscriber_profile_idfor_rfp.value = c.value.subscriber_profile_idfor_rfp(); + subscriber_profile_idfor_rfp.id = id; + HANDLE_CODE(subscriber_profile_idfor_rfp.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscriber_profile_idfor_rfp.value.unpack(bref)); break; - case 66: + } + case 66: { ueaggregate_maximum_bitrate_present = true; - ueaggregate_maximum_bitrate.id = c.id; - ueaggregate_maximum_bitrate.crit = c.crit; - ueaggregate_maximum_bitrate.value = c.value.ueaggregate_maximum_bitrate(); + ueaggregate_maximum_bitrate.id = id; + HANDLE_CODE(ueaggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ueaggregate_maximum_bitrate.value.unpack(bref)); break; - case 108: + } + case 108: { cs_fallback_ind_present = true; - cs_fallback_ind.id = c.id; - cs_fallback_ind.crit = c.crit; - cs_fallback_ind.value = c.value.cs_fallback_ind(); + cs_fallback_ind.id = id; + HANDLE_CODE(cs_fallback_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cs_fallback_ind.value.unpack(bref)); break; - case 107: + } + case 107: { ue_security_cap_present = true; - ue_security_cap.id = c.id; - ue_security_cap.crit = c.crit; - ue_security_cap.value = c.value.ue_security_cap(); + ue_security_cap.id = id; + HANDLE_CODE(ue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_security_cap.value.unpack(bref)); break; - case 146: + } + case 146: { csg_membership_status_present = true; - csg_membership_status.id = c.id; - csg_membership_status.crit = c.crit; - csg_membership_status.value = c.value.csg_membership_status(); + csg_membership_status.id = id; + HANDLE_CODE(csg_membership_status.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(csg_membership_status.value.unpack(bref)); break; - case 159: + } + case 159: { registered_lai_present = true; - registered_lai.id = c.id; - registered_lai.crit = c.crit; - registered_lai.value = c.value.registered_lai(); + registered_lai.id = id; + HANDLE_CODE(registered_lai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(registered_lai.value.unpack(bref)); break; - case 187: + } + case 187: { add_cs_fallback_ind_present = true; - add_cs_fallback_ind.id = c.id; - add_cs_fallback_ind.crit = c.crit; - add_cs_fallback_ind.value = c.value.add_cs_fallback_ind(); + add_cs_fallback_ind.id = id; + HANDLE_CODE(add_cs_fallback_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_cs_fallback_ind.value.unpack(bref)); break; - case 195: + } + case 195: { pro_se_authorized_present = true; - pro_se_authorized.id = c.id; - pro_se_authorized.crit = c.crit; - pro_se_authorized.value = c.value.pro_se_authorized(); + pro_se_authorized.id = id; + HANDLE_CODE(pro_se_authorized.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pro_se_authorized.value.unpack(bref)); break; - case 124: + } + case 124: { srvcc_operation_possible_present = true; - srvcc_operation_possible.id = c.id; - srvcc_operation_possible.crit = c.crit; - srvcc_operation_possible.value = c.value.srvcc_operation_possible(); + srvcc_operation_possible.id = id; + HANDLE_CODE(srvcc_operation_possible.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(srvcc_operation_possible.value.unpack(bref)); break; - case 243: + } + case 243: { srvcc_operation_not_possible_present = true; - srvcc_operation_not_possible.id = c.id; - srvcc_operation_not_possible.crit = c.crit; - srvcc_operation_not_possible.value = c.value.srvcc_operation_not_possible(); + srvcc_operation_not_possible.id = id; + HANDLE_CODE(srvcc_operation_not_possible.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(srvcc_operation_not_possible.value.unpack(bref)); break; - case 240: + } + case 240: { v2xservices_authorized_present = true; - v2xservices_authorized.id = c.id; - v2xservices_authorized.crit = c.crit; - v2xservices_authorized.value = c.value.v2xservices_authorized(); + v2xservices_authorized.id = id; + HANDLE_CODE(v2xservices_authorized.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(v2xservices_authorized.value.unpack(bref)); break; - case 248: + } + case 248: { ue_sidelink_aggregate_maximum_bitrate_present = true; - ue_sidelink_aggregate_maximum_bitrate.id = c.id; - ue_sidelink_aggregate_maximum_bitrate.crit = c.crit; - ue_sidelink_aggregate_maximum_bitrate.value = c.value.ue_sidelink_aggregate_maximum_bitrate(); + ue_sidelink_aggregate_maximum_bitrate.id = id; + HANDLE_CODE(ue_sidelink_aggregate_maximum_bitrate.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_sidelink_aggregate_maximum_bitrate.value.unpack(bref)); break; - case 269: + } + case 269: { nrue_security_cap_present = true; - nrue_security_cap.id = c.id; - nrue_security_cap.crit = c.crit; - nrue_security_cap.value = c.value.nrue_security_cap(); + nrue_security_cap.id = id; + HANDLE_CODE(nrue_security_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nrue_security_cap.value.unpack(bref)); break; - case 277: + } + case 277: { aerial_uesubscription_info_present = true; - aerial_uesubscription_info.id = c.id; - aerial_uesubscription_info.crit = c.crit; - aerial_uesubscription_info.value = c.value.aerial_uesubscription_info(); + aerial_uesubscription_info.id = id; + HANDLE_CODE(aerial_uesubscription_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(aerial_uesubscription_info.value.unpack(bref)); break; - case 299: + } + case 299: { add_rrm_prio_idx_present = true; - add_rrm_prio_idx.id = c.id; - add_rrm_prio_idx.crit = c.crit; - add_rrm_prio_idx.value = c.value.add_rrm_prio_idx(); + add_rrm_prio_idx.id = id; + HANDLE_CODE(add_rrm_prio_idx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_rrm_prio_idx.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -60597,30 +59623,7 @@ void ue_context_mod_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextModificationRequest ::= SEQUENCE -SRSASN_CODE ue_context_mod_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_mod_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_mod_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_mod_resp_ies_container::ue_context_mod_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), enb_ue_s1ap_id(8, crit_e::ignore), crit_diagnostics(58, crit_e::ignore) @@ -60647,29 +59650,35 @@ SRSASN_CODE ue_context_mod_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -60694,30 +59703,7 @@ void ue_context_mod_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextModificationResponse ::= SEQUENCE -SRSASN_CODE ue_context_mod_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_mod_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_mod_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_release_cmd_ies_container::ue_context_release_cmd_ies_container() : ue_s1ap_ids(99, crit_e::reject), cause(2, crit_e::ignore) @@ -60740,23 +59726,27 @@ SRSASN_CODE ue_context_release_cmd_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 99: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 99: { nof_mandatory_ies--; - ue_s1ap_ids.id = c.id; - ue_s1ap_ids.crit = c.crit; - ue_s1ap_ids.value = c.value.ue_s1ap_ids(); + ue_s1ap_ids.id = id; + HANDLE_CODE(ue_s1ap_ids.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_s1ap_ids.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -60777,30 +59767,7 @@ void ue_context_release_cmd_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextReleaseCommand ::= SEQUENCE -SRSASN_CODE ue_context_release_cmd_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_release_cmd_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_release_cmd_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_release_complete_ies_container::ue_context_release_complete_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -60854,59 +59821,75 @@ SRSASN_CODE ue_context_release_complete_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 189: + } + case 189: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 213: + } + case 213: { info_on_recommended_cells_and_enbs_for_paging_present = true; - info_on_recommended_cells_and_enbs_for_paging.id = c.id; - info_on_recommended_cells_and_enbs_for_paging.crit = c.crit; - info_on_recommended_cells_and_enbs_for_paging.value = c.value.info_on_recommended_cells_and_enbs_for_paging(); + info_on_recommended_cells_and_enbs_for_paging.id = id; + HANDLE_CODE(info_on_recommended_cells_and_enbs_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(info_on_recommended_cells_and_enbs_for_paging.value.unpack(bref)); break; - case 212: + } + case 212: { cell_id_and_ce_level_for_ce_capable_ues_present = true; - cell_id_and_ce_level_for_ce_capable_ues.id = c.id; - cell_id_and_ce_level_for_ce_capable_ues.crit = c.crit; - cell_id_and_ce_level_for_ce_capable_ues.value = c.value.cell_id_and_ce_level_for_ce_capable_ues(); + cell_id_and_ce_level_for_ce_capable_ues.id = id; + HANDLE_CODE(cell_id_and_ce_level_for_ce_capable_ues.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cell_id_and_ce_level_for_ce_capable_ues.value.unpack(bref)); break; - case 264: + } + case 264: { secondary_rat_data_usage_report_list_present = true; - secondary_rat_data_usage_report_list.id = c.id; - secondary_rat_data_usage_report_list.crit = c.crit; - secondary_rat_data_usage_report_list.value = c.value.secondary_rat_data_usage_report_list(); + secondary_rat_data_usage_report_list.id = id; + HANDLE_CODE(secondary_rat_data_usage_report_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_report_list.value.unpack(bref)); break; - case 297: + } + case 297: { time_since_secondary_node_release_present = true; - time_since_secondary_node_release.id = c.id; - time_since_secondary_node_release.crit = c.crit; - time_since_secondary_node_release.value = c.value.time_since_secondary_node_release(); + time_since_secondary_node_release.id = id; + HANDLE_CODE(time_since_secondary_node_release.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_since_secondary_node_release.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -60951,30 +59934,7 @@ void ue_context_release_complete_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextReleaseComplete ::= SEQUENCE -SRSASN_CODE ue_context_release_complete_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_release_complete_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_release_complete_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_release_request_ies_container::ue_context_release_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -61010,41 +59970,51 @@ SRSASN_CODE ue_context_release_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 164: + } + case 164: { gw_context_release_ind_present = true; - gw_context_release_ind.id = c.id; - gw_context_release_ind.crit = c.crit; - gw_context_release_ind.value = c.value.gw_context_release_ind(); + gw_context_release_ind.id = id; + HANDLE_CODE(gw_context_release_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(gw_context_release_ind.value.unpack(bref)); break; - case 264: + } + case 264: { secondary_rat_data_usage_report_list_present = true; - secondary_rat_data_usage_report_list.id = c.id; - secondary_rat_data_usage_report_list.crit = c.crit; - secondary_rat_data_usage_report_list.value = c.value.secondary_rat_data_usage_report_list(); + secondary_rat_data_usage_report_list.id = id; + HANDLE_CODE(secondary_rat_data_usage_report_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_report_list.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -61075,30 +60045,7 @@ void ue_context_release_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextReleaseRequest ::= SEQUENCE -SRSASN_CODE ue_context_release_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_release_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_release_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_resume_fail_ies_container::ue_context_resume_fail_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -61129,35 +60076,43 @@ SRSASN_CODE ue_context_resume_fail_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 2: + } + case 2: { nof_mandatory_ies--; - cause.id = c.id; - cause.crit = c.crit; - cause.value = c.value.cause(); + cause.id = id; + HANDLE_CODE(cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cause.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -61184,30 +60139,7 @@ void ue_context_resume_fail_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextResumeFailure ::= SEQUENCE -SRSASN_CODE ue_context_resume_fail_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_resume_fail_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_resume_fail_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_resume_request_ies_container::ue_context_resume_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -61241,35 +60173,43 @@ SRSASN_CODE ue_context_resume_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 235: + } + case 235: { erab_failed_to_resume_list_resume_req_present = true; - erab_failed_to_resume_list_resume_req.id = c.id; - erab_failed_to_resume_list_resume_req.crit = c.crit; - erab_failed_to_resume_list_resume_req.value = c.value.erab_failed_to_resume_list_resume_req(); + erab_failed_to_resume_list_resume_req.id = id; + HANDLE_CODE(erab_failed_to_resume_list_resume_req.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_failed_to_resume_list_resume_req.value.unpack(bref)); break; - case 245: + } + case 245: { rrc_resume_cause_present = true; - rrc_resume_cause.id = c.id; - rrc_resume_cause.crit = c.crit; - rrc_resume_cause.value = c.value.rrc_resume_cause(); + rrc_resume_cause.id = id; + HANDLE_CODE(rrc_resume_cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(rrc_resume_cause.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -61298,30 +60238,7 @@ void ue_context_resume_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextResumeRequest ::= SEQUENCE -SRSASN_CODE ue_context_resume_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_resume_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_resume_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_resume_resp_ies_container::ue_context_resume_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -61365,47 +60282,59 @@ SRSASN_CODE ue_context_resume_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 237: + } + case 237: { erab_failed_to_resume_list_resume_res_present = true; - erab_failed_to_resume_list_resume_res.id = c.id; - erab_failed_to_resume_list_resume_res.crit = c.crit; - erab_failed_to_resume_list_resume_res.value = c.value.erab_failed_to_resume_list_resume_res(); + erab_failed_to_resume_list_resume_res.id = id; + HANDLE_CODE(erab_failed_to_resume_list_resume_res.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(erab_failed_to_resume_list_resume_res.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 40: + } + case 40: { security_context_present = true; - security_context.id = c.id; - security_context.crit = c.crit; - security_context.value = c.value.security_context(); + security_context.id = id; + HANDLE_CODE(security_context.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_context.value.unpack(bref)); break; - case 283: + } + case 283: { pending_data_ind_present = true; - pending_data_ind.id = c.id; - pending_data_ind.crit = c.crit; - pending_data_ind.value = c.value.pending_data_ind(); + pending_data_ind.id = id; + HANDLE_CODE(pending_data_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pending_data_ind.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -61442,30 +60371,7 @@ void ue_context_resume_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextResumeResponse ::= SEQUENCE -SRSASN_CODE ue_context_resume_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_resume_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_resume_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_suspend_request_ies_container::ue_context_suspend_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -61514,53 +60420,67 @@ SRSASN_CODE ue_context_suspend_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 213: + } + case 213: { info_on_recommended_cells_and_enbs_for_paging_present = true; - info_on_recommended_cells_and_enbs_for_paging.id = c.id; - info_on_recommended_cells_and_enbs_for_paging.crit = c.crit; - info_on_recommended_cells_and_enbs_for_paging.value = c.value.info_on_recommended_cells_and_enbs_for_paging(); + info_on_recommended_cells_and_enbs_for_paging.id = id; + HANDLE_CODE(info_on_recommended_cells_and_enbs_for_paging.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(info_on_recommended_cells_and_enbs_for_paging.value.unpack(bref)); break; - case 212: + } + case 212: { cell_id_and_ce_level_for_ce_capable_ues_present = true; - cell_id_and_ce_level_for_ce_capable_ues.id = c.id; - cell_id_and_ce_level_for_ce_capable_ues.crit = c.crit; - cell_id_and_ce_level_for_ce_capable_ues.value = c.value.cell_id_and_ce_level_for_ce_capable_ues(); + cell_id_and_ce_level_for_ce_capable_ues.id = id; + HANDLE_CODE(cell_id_and_ce_level_for_ce_capable_ues.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cell_id_and_ce_level_for_ce_capable_ues.value.unpack(bref)); break; - case 264: + } + case 264: { secondary_rat_data_usage_report_list_present = true; - secondary_rat_data_usage_report_list.id = c.id; - secondary_rat_data_usage_report_list.crit = c.crit; - secondary_rat_data_usage_report_list.value = c.value.secondary_rat_data_usage_report_list(); + secondary_rat_data_usage_report_list.id = id; + HANDLE_CODE(secondary_rat_data_usage_report_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(secondary_rat_data_usage_report_list.value.unpack(bref)); break; - case 189: + } + case 189: { user_location_info_present = true; - user_location_info.id = c.id; - user_location_info.crit = c.crit; - user_location_info.value = c.value.user_location_info(); + user_location_info.id = id; + HANDLE_CODE(user_location_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(user_location_info.value.unpack(bref)); break; - case 297: + } + case 297: { time_since_secondary_node_release_present = true; - time_since_secondary_node_release.id = c.id; - time_since_secondary_node_release.crit = c.crit; - time_since_secondary_node_release.value = c.value.time_since_secondary_node_release(); + time_since_secondary_node_release.id = id; + HANDLE_CODE(time_since_secondary_node_release.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_since_secondary_node_release.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -61601,30 +60521,7 @@ void ue_context_suspend_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextSuspendRequest ::= SEQUENCE -SRSASN_CODE ue_context_suspend_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_suspend_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_suspend_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_context_suspend_resp_ies_container::ue_context_suspend_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -61658,35 +60555,43 @@ SRSASN_CODE ue_context_suspend_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; - case 40: + } + case 40: { security_context_present = true; - security_context.id = c.id; - security_context.crit = c.crit; - security_context.value = c.value.security_context(); + security_context.id = id; + HANDLE_CODE(security_context.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(security_context.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -61715,30 +60620,7 @@ void ue_context_suspend_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEContextSuspendResponse ::= SEQUENCE -SRSASN_CODE ue_context_suspend_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_context_suspend_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_context_suspend_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_info_transfer_ies_container::ue_info_transfer_ies_container() : s_tmsi(96, crit_e::reject), @@ -61780,41 +60662,51 @@ SRSASN_CODE ue_info_transfer_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 1; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 96: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 96: { nof_mandatory_ies--; - s_tmsi.id = c.id; - s_tmsi.crit = c.crit; - s_tmsi.value = c.value.s_tmsi(); + s_tmsi.id = id; + HANDLE_CODE(s_tmsi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(s_tmsi.value.unpack(bref)); break; - case 252: + } + case 252: { ue_level_qos_params_present = true; - ue_level_qos_params.id = c.id; - ue_level_qos_params.crit = c.crit; - ue_level_qos_params.value = c.value.ue_level_qos_params(); + ue_level_qos_params.id = id; + HANDLE_CODE(ue_level_qos_params.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_level_qos_params.value.unpack(bref)); break; - case 74: + } + case 74: { ue_radio_cap_present = true; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; - case 278: + } + case 278: { subscription_based_ue_differentiation_info_present = true; - subscription_based_ue_differentiation_info.id = c.id; - subscription_based_ue_differentiation_info.crit = c.crit; - subscription_based_ue_differentiation_info.value = c.value.subscription_based_ue_differentiation_info(); + subscription_based_ue_differentiation_info.id = id; + HANDLE_CODE(subscription_based_ue_differentiation_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(subscription_based_ue_differentiation_info.value.unpack(bref)); break; - case 283: + } + case 283: { pending_data_ind_present = true; - pending_data_ind.id = c.id; - pending_data_ind.crit = c.crit; - pending_data_ind.value = c.value.pending_data_ind(); + pending_data_ind.id = id; + HANDLE_CODE(pending_data_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(pending_data_ind.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -61849,30 +60741,7 @@ void ue_info_transfer_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UEInformationTransfer ::= SEQUENCE -SRSASN_CODE ue_info_transfer_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_info_transfer_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_info_transfer_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_radio_cap_match_request_ies_container::ue_radio_cap_match_request_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), enb_ue_s1ap_id(8, crit_e::reject), ue_radio_cap(74, crit_e::ignore) @@ -61899,29 +60768,35 @@ SRSASN_CODE ue_radio_cap_match_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 74: + } + case 74: { ue_radio_cap_present = true; - ue_radio_cap.id = c.id; - ue_radio_cap.crit = c.crit; - ue_radio_cap.value = c.value.ue_radio_cap(); + ue_radio_cap.id = id; + HANDLE_CODE(ue_radio_cap.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_radio_cap.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -61946,30 +60821,7 @@ void ue_radio_cap_match_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UERadioCapabilityMatchRequest ::= SEQUENCE -SRSASN_CODE ue_radio_cap_match_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_radio_cap_match_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_radio_cap_match_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ue_radio_cap_match_resp_ies_container::ue_radio_cap_match_resp_ies_container() : mme_ue_s1ap_id(0, crit_e::ignore), @@ -62000,35 +60852,43 @@ SRSASN_CODE ue_radio_cap_match_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 3; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 169: + } + case 169: { nof_mandatory_ies--; - voice_support_match_ind.id = c.id; - voice_support_match_ind.crit = c.crit; - voice_support_match_ind.value = c.value.voice_support_match_ind(); + voice_support_match_ind.id = id; + HANDLE_CODE(voice_support_match_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(voice_support_match_ind.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -62055,30 +60915,7 @@ void ue_radio_cap_match_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UERadioCapabilityMatchResponse ::= SEQUENCE -SRSASN_CODE ue_radio_cap_match_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ue_radio_cap_match_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ue_radio_cap_match_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_nas_transport_ies_container::ul_nas_transport_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -62128,65 +60965,83 @@ SRSASN_CODE ul_nas_transport_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 26: + } + case 26: { nof_mandatory_ies--; - nas_pdu.id = c.id; - nas_pdu.crit = c.crit; - nas_pdu.value = c.value.nas_pdu(); + nas_pdu.id = id; + HANDLE_CODE(nas_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(nas_pdu.value.unpack(bref)); break; - case 100: + } + case 100: { nof_mandatory_ies--; - eutran_cgi.id = c.id; - eutran_cgi.crit = c.crit; - eutran_cgi.value = c.value.eutran_cgi(); + eutran_cgi.id = id; + HANDLE_CODE(eutran_cgi.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(eutran_cgi.value.unpack(bref)); break; - case 67: + } + case 67: { nof_mandatory_ies--; - tai.id = c.id; - tai.crit = c.crit; - tai.value = c.value.tai(); + tai.id = id; + HANDLE_CODE(tai.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(tai.value.unpack(bref)); break; - case 155: + } + case 155: { gw_transport_layer_address_present = true; - gw_transport_layer_address.id = c.id; - gw_transport_layer_address.crit = c.crit; - gw_transport_layer_address.value = c.value.gw_transport_layer_address(); + gw_transport_layer_address.id = id; + HANDLE_CODE(gw_transport_layer_address.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(gw_transport_layer_address.value.unpack(bref)); break; - case 184: + } + case 184: { sipto_l_gw_transport_layer_address_present = true; - sipto_l_gw_transport_layer_address.id = c.id; - sipto_l_gw_transport_layer_address.crit = c.crit; - sipto_l_gw_transport_layer_address.value = c.value.sipto_l_gw_transport_layer_address(); + sipto_l_gw_transport_layer_address.id = id; + HANDLE_CODE(sipto_l_gw_transport_layer_address.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(sipto_l_gw_transport_layer_address.value.unpack(bref)); break; - case 186: + } + case 186: { lhn_id_present = true; - lhn_id.id = c.id; - lhn_id.crit = c.crit; - lhn_id.value = c.value.lhn_id(); + lhn_id.id = id; + HANDLE_CODE(lhn_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lhn_id.value.unpack(bref)); break; - case 288: + } + case 288: { ps_cell_info_present = true; - ps_cell_info.id = c.id; - ps_cell_info.crit = c.crit; - ps_cell_info.value = c.value.ps_cell_info(); + ps_cell_info.id = id; + HANDLE_CODE(ps_cell_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ps_cell_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -62229,30 +61084,7 @@ void ul_nas_transport_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UplinkNASTransport ::= SEQUENCE -SRSASN_CODE ul_nas_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_nas_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_nas_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_non_ueassociated_lp_pa_transport_ies_container::ul_non_ueassociated_lp_pa_transport_ies_container() : routing_id(148, crit_e::reject), lp_pa_pdu(147, crit_e::reject) @@ -62275,23 +61107,27 @@ SRSASN_CODE ul_non_ueassociated_lp_pa_transport_ies_container::unpack(cbit_ref& uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 148: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 148: { nof_mandatory_ies--; - routing_id.id = c.id; - routing_id.crit = c.crit; - routing_id.value = c.value.routing_id(); + routing_id.id = id; + HANDLE_CODE(routing_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(routing_id.value.unpack(bref)); break; - case 147: + } + case 147: { nof_mandatory_ies--; - lp_pa_pdu.id = c.id; - lp_pa_pdu.crit = c.crit; - lp_pa_pdu.value = c.value.lp_pa_pdu(); + lp_pa_pdu.id = id; + HANDLE_CODE(lp_pa_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lp_pa_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -62312,30 +61148,7 @@ void ul_non_ueassociated_lp_pa_transport_ies_container::to_json(json_writer& j) j.end_obj(); } -// UplinkNonUEAssociatedLPPaTransport ::= SEQUENCE -SRSASN_CODE ul_non_ueassociated_lp_pa_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_non_ueassociated_lp_pa_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_non_ueassociated_lp_pa_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_s1cdma2000tunnelling_ies_container::ul_s1cdma2000tunnelling_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -62385,65 +61198,83 @@ SRSASN_CODE ul_s1cdma2000tunnelling_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 5; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 71: + } + case 71: { nof_mandatory_ies--; - cdma2000_rat_type.id = c.id; - cdma2000_rat_type.crit = c.crit; - cdma2000_rat_type.value = c.value.cdma2000_rat_type(); + cdma2000_rat_type.id = id; + HANDLE_CODE(cdma2000_rat_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_rat_type.value.unpack(bref)); break; - case 72: + } + case 72: { nof_mandatory_ies--; - cdma2000_sector_id.id = c.id; - cdma2000_sector_id.crit = c.crit; - cdma2000_sector_id.value = c.value.cdma2000_sector_id(); + cdma2000_sector_id.id = id; + HANDLE_CODE(cdma2000_sector_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_sector_id.value.unpack(bref)); break; - case 84: + } + case 84: { cdma2000_ho_required_ind_present = true; - cdma2000_ho_required_ind.id = c.id; - cdma2000_ho_required_ind.crit = c.crit; - cdma2000_ho_required_ind.value = c.value.cdma2000_ho_required_ind(); + cdma2000_ho_required_ind.id = id; + HANDLE_CODE(cdma2000_ho_required_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_ho_required_ind.value.unpack(bref)); break; - case 102: + } + case 102: { cdma2000_one_xsrvcc_info_present = true; - cdma2000_one_xsrvcc_info.id = c.id; - cdma2000_one_xsrvcc_info.crit = c.crit; - cdma2000_one_xsrvcc_info.value = c.value.cdma2000_one_xsrvcc_info(); + cdma2000_one_xsrvcc_info.id = id; + HANDLE_CODE(cdma2000_one_xsrvcc_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_one_xsrvcc_info.value.unpack(bref)); break; - case 97: + } + case 97: { cdma2000_one_xrand_present = true; - cdma2000_one_xrand.id = c.id; - cdma2000_one_xrand.crit = c.crit; - cdma2000_one_xrand.value = c.value.cdma2000_one_xrand(); + cdma2000_one_xrand.id = id; + HANDLE_CODE(cdma2000_one_xrand.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_one_xrand.value.unpack(bref)); break; - case 70: + } + case 70: { nof_mandatory_ies--; - cdma2000_pdu.id = c.id; - cdma2000_pdu.crit = c.crit; - cdma2000_pdu.value = c.value.cdma2000_pdu(); + cdma2000_pdu.id = id; + HANDLE_CODE(cdma2000_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(cdma2000_pdu.value.unpack(bref)); break; - case 140: + } + case 140: { eutran_round_trip_delay_estimation_info_present = true; - eutran_round_trip_delay_estimation_info.id = c.id; - eutran_round_trip_delay_estimation_info.crit = c.crit; - eutran_round_trip_delay_estimation_info.value = c.value.eutran_round_trip_delay_estimation_info(); + eutran_round_trip_delay_estimation_info.id = id; + HANDLE_CODE(eutran_round_trip_delay_estimation_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(eutran_round_trip_delay_estimation_info.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -62486,30 +61317,7 @@ void ul_s1cdma2000tunnelling_ies_container::to_json(json_writer& j) const j.end_obj(); } -// UplinkS1cdma2000tunnelling ::= SEQUENCE -SRSASN_CODE ul_s1cdma2000tunnelling_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_s1cdma2000tunnelling_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_s1cdma2000tunnelling_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; ul_ueassociated_lp_pa_transport_ies_container::ul_ueassociated_lp_pa_transport_ies_container() : mme_ue_s1ap_id(0, crit_e::reject), @@ -62537,35 +61345,43 @@ SRSASN_CODE ul_ueassociated_lp_pa_transport_ies_container::unpack(cbit_ref& bref uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 0: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 0: { nof_mandatory_ies--; - mme_ue_s1ap_id.id = c.id; - mme_ue_s1ap_id.crit = c.crit; - mme_ue_s1ap_id.value = c.value.mme_ue_s1ap_id(); + mme_ue_s1ap_id.id = id; + HANDLE_CODE(mme_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mme_ue_s1ap_id.value.unpack(bref)); break; - case 8: + } + case 8: { nof_mandatory_ies--; - enb_ue_s1ap_id.id = c.id; - enb_ue_s1ap_id.crit = c.crit; - enb_ue_s1ap_id.value = c.value.enb_ue_s1ap_id(); + enb_ue_s1ap_id.id = id; + HANDLE_CODE(enb_ue_s1ap_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(enb_ue_s1ap_id.value.unpack(bref)); break; - case 148: + } + case 148: { nof_mandatory_ies--; - routing_id.id = c.id; - routing_id.crit = c.crit; - routing_id.value = c.value.routing_id(); + routing_id.id = id; + HANDLE_CODE(routing_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(routing_id.value.unpack(bref)); break; - case 147: + } + case 147: { nof_mandatory_ies--; - lp_pa_pdu.id = c.id; - lp_pa_pdu.crit = c.crit; - lp_pa_pdu.value = c.value.lp_pa_pdu(); + lp_pa_pdu.id = id; + HANDLE_CODE(lp_pa_pdu.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(lp_pa_pdu.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -62590,30 +61406,7 @@ void ul_ueassociated_lp_pa_transport_ies_container::to_json(json_writer& j) cons j.end_obj(); } -// UplinkUEAssociatedLPPaTransport ::= SEQUENCE -SRSASN_CODE ul_ueassociated_lp_pa_transport_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE ul_ueassociated_lp_pa_transport_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void ul_ueassociated_lp_pa_transport_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; write_replace_warning_request_ies_container::write_replace_warning_request_ies_container() : msg_id(111, crit_e::reject), @@ -62681,83 +61474,107 @@ SRSASN_CODE write_replace_warning_request_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 4; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 111: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 111: { nof_mandatory_ies--; - msg_id.id = c.id; - msg_id.crit = c.crit; - msg_id.value = c.value.msg_id(); + msg_id.id = id; + HANDLE_CODE(msg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(msg_id.value.unpack(bref)); break; - case 112: + } + case 112: { nof_mandatory_ies--; - serial_num.id = c.id; - serial_num.crit = c.crit; - serial_num.value = c.value.serial_num(); + serial_num.id = id; + HANDLE_CODE(serial_num.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(serial_num.value.unpack(bref)); break; - case 113: + } + case 113: { warning_area_list_present = true; - warning_area_list.id = c.id; - warning_area_list.crit = c.crit; - warning_area_list.value = c.value.warning_area_list(); + warning_area_list.id = id; + HANDLE_CODE(warning_area_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_area_list.value.unpack(bref)); break; - case 114: + } + case 114: { nof_mandatory_ies--; - repeat_period.id = c.id; - repeat_period.crit = c.crit; - repeat_period.value = c.value.repeat_period(); + repeat_period.id = id; + HANDLE_CODE(repeat_period.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(repeat_period.value.unpack(bref)); break; - case 144: + } + case 144: { extended_repeat_period_present = true; - extended_repeat_period.id = c.id; - extended_repeat_period.crit = c.crit; - extended_repeat_period.value = c.value.extended_repeat_period(); + extended_repeat_period.id = id; + HANDLE_CODE(extended_repeat_period.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(extended_repeat_period.value.unpack(bref)); break; - case 115: + } + case 115: { nof_mandatory_ies--; - numof_broadcast_request.id = c.id; - numof_broadcast_request.crit = c.crit; - numof_broadcast_request.value = c.value.numof_broadcast_request(); + numof_broadcast_request.id = id; + HANDLE_CODE(numof_broadcast_request.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(numof_broadcast_request.value.unpack(bref)); break; - case 116: + } + case 116: { warning_type_present = true; - warning_type.id = c.id; - warning_type.crit = c.crit; - warning_type.value = c.value.warning_type(); + warning_type.id = id; + HANDLE_CODE(warning_type.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_type.value.unpack(bref)); break; - case 117: + } + case 117: { warning_security_info_present = true; - warning_security_info.id = c.id; - warning_security_info.crit = c.crit; - warning_security_info.value = c.value.warning_security_info(); + warning_security_info.id = id; + HANDLE_CODE(warning_security_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_security_info.value.unpack(bref)); break; - case 118: + } + case 118: { data_coding_scheme_present = true; - data_coding_scheme.id = c.id; - data_coding_scheme.crit = c.crit; - data_coding_scheme.value = c.value.data_coding_scheme(); + data_coding_scheme.id = id; + HANDLE_CODE(data_coding_scheme.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(data_coding_scheme.value.unpack(bref)); break; - case 119: + } + case 119: { warning_msg_contents_present = true; - warning_msg_contents.id = c.id; - warning_msg_contents.crit = c.crit; - warning_msg_contents.value = c.value.warning_msg_contents(); + warning_msg_contents.id = id; + HANDLE_CODE(warning_msg_contents.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_msg_contents.value.unpack(bref)); break; - case 142: + } + case 142: { concurrent_warning_msg_ind_present = true; - concurrent_warning_msg_ind.id = c.id; - concurrent_warning_msg_ind.crit = c.crit; - concurrent_warning_msg_ind.value = c.value.concurrent_warning_msg_ind(); + concurrent_warning_msg_ind.id = id; + HANDLE_CODE(concurrent_warning_msg_ind.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(concurrent_warning_msg_ind.value.unpack(bref)); break; - case 286: + } + case 286: { warning_area_coordinates_present = true; - warning_area_coordinates.id = c.id; - warning_area_coordinates.crit = c.crit; - warning_area_coordinates.value = c.value.warning_area_coordinates(); + warning_area_coordinates.id = id; + HANDLE_CODE(warning_area_coordinates.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(warning_area_coordinates.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -62814,30 +61631,7 @@ void write_replace_warning_request_ies_container::to_json(json_writer& j) const j.end_obj(); } -// WriteReplaceWarningRequest ::= SEQUENCE -SRSASN_CODE write_replace_warning_request_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE write_replace_warning_request_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void write_replace_warning_request_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - -template struct asn1::s1ap::protocol_ie_field_s; +template struct asn1::protocol_ie_field_s; write_replace_warning_resp_ies_container::write_replace_warning_resp_ies_container() : msg_id(111, crit_e::reject), @@ -62871,35 +61665,43 @@ SRSASN_CODE write_replace_warning_resp_ies_container::unpack(cbit_ref& bref) uint32_t nof_mandatory_ies = 2; for (; nof_ies > 0; --nof_ies) { - protocol_ie_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 111: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 111: { nof_mandatory_ies--; - msg_id.id = c.id; - msg_id.crit = c.crit; - msg_id.value = c.value.msg_id(); + msg_id.id = id; + HANDLE_CODE(msg_id.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(msg_id.value.unpack(bref)); break; - case 112: + } + case 112: { nof_mandatory_ies--; - serial_num.id = c.id; - serial_num.crit = c.crit; - serial_num.value = c.value.serial_num(); + serial_num.id = id; + HANDLE_CODE(serial_num.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(serial_num.value.unpack(bref)); break; - case 120: + } + case 120: { broadcast_completed_area_list_present = true; - broadcast_completed_area_list.id = c.id; - broadcast_completed_area_list.crit = c.crit; - broadcast_completed_area_list.value = c.value.broadcast_completed_area_list(); + broadcast_completed_area_list.id = id; + HANDLE_CODE(broadcast_completed_area_list.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(broadcast_completed_area_list.value.unpack(bref)); break; - case 58: + } + case 58: { crit_diagnostics_present = true; - crit_diagnostics.id = c.id; - crit_diagnostics.crit = c.crit; - crit_diagnostics.value = c.value.crit_diagnostics(); + crit_diagnostics.id = id; + HANDLE_CODE(crit_diagnostics.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(crit_diagnostics.value.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -62928,29 +61730,6 @@ void write_replace_warning_resp_ies_container::to_json(json_writer& j) const j.end_obj(); } -// WriteReplaceWarningResponse ::= SEQUENCE -SRSASN_CODE write_replace_warning_resp_s::pack(bit_ref& bref) const -{ - bref.pack(ext, 1); - HANDLE_CODE(protocol_ies.pack(bref)); - - return SRSASN_SUCCESS; -} -SRSASN_CODE write_replace_warning_resp_s::unpack(cbit_ref& bref) -{ - bref.unpack(ext, 1); - HANDLE_CODE(protocol_ies.unpack(bref)); - - return SRSASN_SUCCESS; -} -void write_replace_warning_resp_s::to_json(json_writer& j) const -{ - j.start_obj(); - j.write_fieldname("protocolIEs"); - protocol_ies.to_json(j); - j.end_obj(); -} - // S1AP-ELEMENTARY-PROCEDURES ::= OBJECT SET OF S1AP-ELEMENTARY-PROCEDURE uint16_t s1ap_elem_procs_o::idx_to_proc_code(uint32_t idx) { @@ -66039,7 +64818,7 @@ uint8_t last_visited_eutran_cell_info_ext_ies_o::ext_c::types_opts::to_number() return map_enum_number(options, 1, value, "last_visited_eutran_cell_info_ext_ies_o::ext_c::types"); } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; last_visited_eutran_cell_info_ext_ies_container::last_visited_eutran_cell_info_ext_ies_container() : time_ue_stayed_in_cell_enhanced_granularity(167, crit_e::ignore), ho_cause(168, crit_e::ignore) @@ -66066,23 +64845,27 @@ SRSASN_CODE last_visited_eutran_cell_info_ext_ies_container::unpack(cbit_ref& br unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 167: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 167: { time_ue_stayed_in_cell_enhanced_granularity_present = true; - time_ue_stayed_in_cell_enhanced_granularity.id = c.id; - time_ue_stayed_in_cell_enhanced_granularity.crit = c.crit; - time_ue_stayed_in_cell_enhanced_granularity.ext = c.ext_value.time_ue_stayed_in_cell_enhanced_granularity(); + time_ue_stayed_in_cell_enhanced_granularity.id = id; + HANDLE_CODE(time_ue_stayed_in_cell_enhanced_granularity.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(time_ue_stayed_in_cell_enhanced_granularity.ext.unpack(bref)); break; - case 168: + } + case 168: { ho_cause_present = true; - ho_cause.id = c.id; - ho_cause.crit = c.crit; - ho_cause.ext = c.ext_value.ho_cause(); + ho_cause.id = id; + HANDLE_CODE(ho_cause.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ho_cause.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } @@ -67979,7 +66762,7 @@ uint8_t sourceenb_to_targetenb_transparent_container_ext_ies_o::ext_c::types_opt return 0; } -template struct asn1::s1ap::protocol_ext_field_s; +template struct asn1::protocol_ext_field_s; sourceenb_to_targetenb_transparent_container_ext_ies_container:: sourceenb_to_targetenb_transparent_container_ext_ies_container() : @@ -68023,41 +66806,51 @@ SRSASN_CODE sourceenb_to_targetenb_transparent_container_ext_ies_container::unpa unpack_length(nof_ies, bref, 1u, 65535u, true); for (; nof_ies > 0; --nof_ies) { - protocol_ext_field_s c; - HANDLE_CODE(c.unpack(bref)); - switch (c.id) { - case 175: + uint32_t id; + HANDLE_CODE(unpack_integer(id, bref, (uint32_t)0u, (uint32_t)65535u, false, true)); + switch (id) { + case 175: { mob_info_present = true; - mob_info.id = c.id; - mob_info.crit = c.crit; - mob_info.ext = c.ext_value.mob_info(); + mob_info.id = id; + HANDLE_CODE(mob_info.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(mob_info.ext.unpack(bref)); break; - case 194: + } + case 194: { ue_history_info_from_the_ue_present = true; - ue_history_info_from_the_ue.id = c.id; - ue_history_info_from_the_ue.crit = c.crit; - ue_history_info_from_the_ue.ext = c.ext_value.ue_history_info_from_the_ue(); + ue_history_info_from_the_ue.id = id; + HANDLE_CODE(ue_history_info_from_the_ue.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(ue_history_info_from_the_ue.ext.unpack(bref)); break; - case 296: + } + case 296: { im_svoice_ep_sfallbackfrom5_g_present = true; - im_svoice_ep_sfallbackfrom5_g.id = c.id; - im_svoice_ep_sfallbackfrom5_g.crit = c.crit; - im_svoice_ep_sfallbackfrom5_g.ext = c.ext_value.im_svoice_ep_sfallbackfrom5_g(); + im_svoice_ep_sfallbackfrom5_g.id = id; + HANDLE_CODE(im_svoice_ep_sfallbackfrom5_g.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(im_svoice_ep_sfallbackfrom5_g.ext.unpack(bref)); break; - case 299: + } + case 299: { add_rrm_prio_idx_present = true; - add_rrm_prio_idx.id = c.id; - add_rrm_prio_idx.crit = c.crit; - add_rrm_prio_idx.ext = c.ext_value.add_rrm_prio_idx(); + add_rrm_prio_idx.id = id; + HANDLE_CODE(add_rrm_prio_idx.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(add_rrm_prio_idx.ext.unpack(bref)); break; - case 300: + } + case 300: { contextat_source_present = true; - contextat_source.id = c.id; - contextat_source.crit = c.crit; - contextat_source.ext = c.ext_value.contextat_source(); + contextat_source.id = id; + HANDLE_CODE(contextat_source.crit.unpack(bref)); + varlength_field_unpack_guard varlen_scope(bref, true); + HANDLE_CODE(contextat_source.ext.unpack(bref)); break; + } default: - asn1::log_error("Unpacked object ID=%d is not recognized\n", c.id); + asn1::log_error("Unpacked object ID=%d is not recognized\n", id); return SRSASN_ERROR_DECODE_FAIL; } } diff --git a/lib/src/asn1/s1ap_utils.cc b/lib/src/asn1/s1ap_utils.cc index 9a121c6d6..3dc5f1623 100644 --- a/lib/src/asn1/s1ap_utils.cc +++ b/lib/src/asn1/s1ap_utils.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,21 +35,21 @@ template <> uint32_t get_obj_id >( const protocol_ie_single_container_s& obj) { - return obj.value.erab_to_be_setup_item_ctxt_su_req().erab_id; + return obj->erab_to_be_setup_item_ctxt_su_req().erab_id; } template <> uint32_t get_obj_id >( const protocol_ie_single_container_s& obj) { - return obj.value.erab_to_be_setup_item_bearer_su_req().erab_id; + return obj->erab_to_be_setup_item_bearer_su_req().erab_id; } template <> uint32_t get_obj_id >( const protocol_ie_single_container_s& obj) { - return obj.value.erab_to_be_modified_item_bearer_mod_req().erab_id; + return obj->erab_to_be_modified_item_bearer_mod_req().erab_id; } } // namespace s1ap diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt index 0ce3d805b..51181f508 100644 --- a/lib/src/common/CMakeLists.txt +++ b/lib/src/common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,12 +18,12 @@ # and at http://www.gnu.org/licenses/. # - set(SOURCES arch_select.cc enb_events.cc backtrace.c byte_buffer.cc band_helper.cc + bearer_manager.cc buffer_pool.cc crash_handler.cc gen_mch_tables.c @@ -34,8 +34,12 @@ set(SOURCES arch_select.cc network_utils.cc mac_pcap_net.cc pcap.c + phy_cfg_nr.cc + phy_cfg_nr_default.cc + rrc_common.cc rlc_pcap.cc s1ap_pcap.cc + ngap_pcap.cc security.cc standard_streams.cc thread_pool.cc @@ -44,8 +48,7 @@ set(SOURCES arch_select.cc time_prof.cc version.c zuc.cc - s3g.cc - basic_vnf.cc) + s3g.cc) # Avoid warnings caused by libmbedtls about deprecated functions set_source_files_properties(security.cc PROPERTIES COMPILE_FLAGS -Wno-deprecated-declarations) @@ -57,9 +60,9 @@ add_dependencies(srsran_common gen_build_info) add_executable(arch_select arch_select.cc) target_include_directories(srsran_common PUBLIC ${SEC_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${BACKWARD_INCLUDE_DIRS}) -target_link_libraries(srsran_common srsran_phy srslog ${SEC_LIBRARIES} ${BACKWARD_LIBRARIES} ${SCTP_LIBRARIES}) +target_link_libraries(srsran_common srsran_phy support srslog ${SEC_LIBRARIES} ${BACKWARD_LIBRARIES} ${SCTP_LIBRARIES}) target_compile_definitions(srsran_common PRIVATE ${BACKWARD_DEFINITIONS}) -INSTALL(TARGETS srsran_common DESTINATION ${LIBRARY_DIR}) +install(TARGETS srsran_common DESTINATION ${LIBRARY_DIR} OPTIONAL) add_subdirectory(test) diff --git a/lib/src/common/arch_select.cc b/lib/src/common/arch_select.cc index aa320f830..c73bb22fe 100644 --- a/lib/src/common/arch_select.cc +++ b/lib/src/common/arch_select.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/backtrace.c b/lib/src/common/backtrace.c index 8452ad704..03e3d728d 100644 --- a/lib/src/common/backtrace.c +++ b/lib/src/common/backtrace.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/band_helper.cc b/lib/src/common/band_helper.cc index f4a7d8791..1c45c707a 100644 --- a/lib/src/common/band_helper.cc +++ b/lib/src/common/band_helper.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,6 +21,7 @@ #include "srsran/common/band_helper.h" #include +#include namespace srsran { @@ -28,15 +29,31 @@ namespace srsran { constexpr std::array srsran_band_helper::nr_band_table_fr1; constexpr std::array srsran_band_helper::nr_fr_params; +constexpr std::array + srsran_band_helper::nr_operating_bands_fr1; +constexpr std::array + srsran_band_helper::nr_band_ss_raster_table; // Formula in 5.4.2.1 double srsran_band_helper::nr_arfcn_to_freq(uint32_t nr_arfcn) { nr_raster_params params = get_raster_params(nr_arfcn); + if (not is_valid_raster_param(params)) { + return 0.0; + } return (params.F_REF_Offs_MHz * 1e6 + params.delta_F_global_kHz * (nr_arfcn - params.N_REF_Offs) * 1e3); } -// Implements 5.4.2.1 in TS 38.401 +uint32_t srsran_band_helper::freq_to_nr_arfcn(double freq) +{ + nr_raster_params params = get_raster_params(freq); + if (not is_valid_raster_param(params)) { + return 0; + } + return (((freq - params.F_REF_Offs_MHz * 1e6) / 1e3 / params.delta_F_global_kHz) + params.N_REF_Offs); +} + +// Implements 5.4.2.1 in TS 38.104 std::vector srsran_band_helper::get_bands_nr(uint32_t nr_arfcn, srsran_band_helper::delta_f_raster_t delta_f_raster) { @@ -60,6 +77,223 @@ std::vector srsran_band_helper::get_bands_nr(uint32_t return bands; } +uint16_t srsran_band_helper::get_band_from_dl_freq_Hz(double freq) const +{ + uint32_t freq_MHz = (uint32_t)round(freq / 1e6); + for (const nr_operating_band& band : nr_operating_bands_fr1) { + if (freq_MHz >= band.F_DL_low and freq_MHz <= band.F_DL_high) { + return band.band; + } + } + return UINT16_MAX; +} + +uint16_t srsran_band_helper::get_band_from_dl_arfcn(uint32_t arfcn) const +{ + for (const nr_band& band : nr_band_table_fr1) { + // Check given ARFCN is between the first and last possible ARFCN and matches step + if (arfcn >= band.dl_nref_first and arfcn <= band.dl_nref_last and + (arfcn - band.dl_nref_first) % band.dl_nref_step == 0) { + return band.band; + } + } + return UINT16_MAX; +} + +uint32_t srsran_band_helper::get_ul_arfcn_from_dl_arfcn(uint32_t dl_arfcn) const +{ + // return same ARFCN for TDD bands + if (get_duplex_mode(get_band_from_dl_arfcn(dl_arfcn)) == SRSRAN_DUPLEX_MODE_TDD) { + return dl_arfcn; + } + + // derive UL ARFCN for FDD bands + for (const auto& band : nr_band_table_fr1) { + if (band.band == get_band_from_dl_arfcn(dl_arfcn)) { + uint32_t offset = (dl_arfcn - band.dl_nref_first) / band.dl_nref_step; + return (band.ul_nref_first + offset * band.ul_nref_step); + } + } + + return 0; +} + +double srsran_band_helper::get_center_freq_from_abs_freq_point_a(uint32_t nof_prb, uint32_t freq_point_a_arfcn) +{ + // for FR1 unit of resources blocks for freq calc is always 180kHz regardless for actual SCS of carrier + // TODO: add offset_to_carrier + double abs_freq_point_a_freq = nr_arfcn_to_freq(freq_point_a_arfcn); + return abs_freq_point_a_freq + + (nof_prb / 2 * SRSRAN_SUBC_SPACING_NR(srsran_subcarrier_spacing_t::srsran_subcarrier_spacing_15kHz) * + SRSRAN_NRE); +} + +uint32_t srsran_band_helper::get_abs_freq_point_a_arfcn(uint32_t nof_prb, uint32_t arfcn) +{ + return freq_to_nr_arfcn(get_abs_freq_point_a_from_center_freq(nof_prb, nr_arfcn_to_freq(arfcn))); +} + +double srsran_band_helper::get_abs_freq_point_a_from_center_freq(uint32_t nof_prb, double center_freq) +{ + // for FR1 unit of resources blocks for freq calc is always 180kHz regardless for actual SCS of carrier + // TODO: add offset_to_carrier + return center_freq - + (nof_prb / 2 * SRSRAN_SUBC_SPACING_NR(srsran_subcarrier_spacing_t::srsran_subcarrier_spacing_15kHz) * + SRSRAN_NRE); +} + +uint32_t srsran_band_helper::find_lower_bound_abs_freq_ssb(uint16_t band, + srsran_subcarrier_spacing_t scs, + uint32_t min_center_freq_hz) +{ + sync_raster_t sync_raster = get_sync_raster(band, scs); + if (!sync_raster.valid()) { + return 0; + } + + double ssb_bw_hz = SRSRAN_SSB_BW_SUBC * SRSRAN_SUBC_SPACING_NR(scs); + while (!sync_raster.end()) { + double abs_freq_ssb_hz = sync_raster.get_frequency(); + + if ((abs_freq_ssb_hz > min_center_freq_hz + ssb_bw_hz / 2) and + ((uint32_t)std::round(abs_freq_ssb_hz - min_center_freq_hz) % SRSRAN_SUBC_SPACING_NR(scs) == 0)) { + return freq_to_nr_arfcn(abs_freq_ssb_hz); + } + + sync_raster.next(); + } + return 0; +} + +uint32_t srsran_band_helper::get_abs_freq_ssb_arfcn(uint16_t band, + srsran_subcarrier_spacing_t scs, + uint32_t freq_point_a_arfcn, + uint32_t coreset0_offset_rb) +{ + double freq_point_a_hz = nr_arfcn_to_freq(freq_point_a_arfcn); + double coreset0_offset_hz = coreset0_offset_rb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(scs); + return find_lower_bound_abs_freq_ssb(band, scs, freq_point_a_hz + coreset0_offset_hz); +} + +srsran_ssb_pattern_t srsran_band_helper::get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs) +{ + // Look for the given band and SCS + for (const nr_band_ss_raster& ss_raster : nr_band_ss_raster_table) { + // Check if band and SCS match! + if (ss_raster.band == band && ss_raster.scs == scs) { + return ss_raster.pattern; + } + + // As bands are in ascending order, do not waste more time if the current band is bigger + if (ss_raster.band > band) { + return SRSRAN_SSB_PATTERN_INVALID; + } + } + + // Band is out of range, so consider invalid + return SRSRAN_SSB_PATTERN_INVALID; +} + +srsran_subcarrier_spacing_t srsran_band_helper::get_ssb_scs(uint16_t band) const +{ + // Look for the given band and SCS + for (const nr_band_ss_raster& ss_raster : nr_band_ss_raster_table) { + // Check if band and SCS match! + if (ss_raster.band == band) { + return ss_raster.scs; + } + + // As bands are in ascending order, do not waste more time if the current band is bigger + if (ss_raster.band > band) { + return srsran_subcarrier_spacing_invalid; + } + } + return srsran_subcarrier_spacing_invalid; +} + +srsran_duplex_mode_t srsran_band_helper::get_duplex_mode(uint16_t band) const +{ + // Look for the given band + for (const nr_operating_band& b : nr_operating_bands_fr1) { + // Check if band and SCS match! + if (b.band == band) { + return b.duplex_mode; + } + + // As bands are in ascending order, do not waste more time if the current band is bigger + if (b.band > band) { + return SRSRAN_DUPLEX_MODE_INVALID; + } + } + + // Band is out of range, so consider invalid + return SRSRAN_DUPLEX_MODE_INVALID; +} + +struct sync_raster_impl : public srsran_band_helper::sync_raster_t { +public: + sync_raster_impl(uint32_t gscn_f, uint32_t gscn_s, uint32_t gscn_l) : sync_raster_t(gscn_f, gscn_s, gscn_l) + { + // Do nothing + } +}; + +double srsran_band_helper::sync_raster_t::get_frequency() const +{ + // see TS38.104 table 5.4.3.1-1 + + // Row 1 + if (gscn_last <= 7498) { + return N * 1200e3 + M[M_idx] * 50e3; + } + + // Row 2 + if (7499 <= gscn_last and gscn_last <= 22255) { + return 3000e6 + N * 1.44e6; + } + + // Row 3 + if (22256 <= gscn_last and gscn_last <= 26639) { + return 2425.08e6 + N * 17.28e6; + } + + // Unhandled case + return NAN; +} + +uint32_t srsran_band_helper::sync_raster_t::get_gscn() const +{ + if (gscn_last <= 7498) { + return 3 * N + (M[M_idx] - 3) / 2; + } else if (7499 <= gscn_last and gscn_last <= 22255) { + return 7499 + N; + } else if (22256 <= gscn_last and gscn_last <= 26639) { + return 22256 + N; + } + + return 0; +} + +srsran_band_helper::sync_raster_t srsran_band_helper::get_sync_raster(uint16_t band, + srsran_subcarrier_spacing_t scs) const +{ + // Look for the given band and SCS + for (const nr_band_ss_raster& ss_raster : nr_band_ss_raster_table) { + // Check if band and SCS match! + if (ss_raster.band == band && ss_raster.scs == scs) { + return sync_raster_impl(ss_raster.gscn_first, ss_raster.gscn_step, ss_raster.gscn_last); + } + + // As bands are in ascending order, do not waste more time if the current band is bigger + if (ss_raster.band > band) { + return sync_raster_impl(0, 0, 0); + } + } + + // Band is out of range, so consider invalid + return sync_raster_impl(0, 0, 0); +} + srsran_band_helper::nr_raster_params srsran_band_helper::get_raster_params(uint32_t nr_arfcn) { for (auto& fr : nr_fr_params) { @@ -70,4 +304,24 @@ srsran_band_helper::nr_raster_params srsran_band_helper::get_raster_params(uint3 return {}; // return empty params } -} // namespace srsran \ No newline at end of file +srsran_band_helper::nr_raster_params srsran_band_helper::get_raster_params(double freq) +{ + for (auto& fr : nr_fr_params) { + if (freq >= fr.freq_range_start * 1e6 && freq <= fr.freq_range_end * 1e6) { + return fr; + } + } + return {}; // return empty params +} + +bool srsran_band_helper::is_valid_raster_param(const srsran_band_helper::nr_raster_params& raster) +{ + for (auto& fr : nr_fr_params) { + if (fr == raster) { + return true; + } + } + return false; +} + +} // namespace srsran diff --git a/lib/src/common/basic_vnf.cc b/lib/src/common/basic_vnf.cc deleted file mode 100644 index a6d2d3fb4..000000000 --- a/lib/src/common/basic_vnf.cc +++ /dev/null @@ -1,406 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/common/basic_vnf.h" -#include "srsran/common/buffer_pool.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include -#include -#include - -#define RAND_SEED (12314) -#define RX_TIMEOUT_MS (1000) - -namespace srsran { - -struct srsran_pnf_info_t { - // TODO: fill when needed -}; - -struct srsran_vnf_info_t {}; - -srsran_basic_vnf::srsran_basic_vnf(const vnf_args_t& args_, stack_interface_phy_nr* stack_) : - m_args(args_), thread("BASIC_VNF_P7"), m_tx_req_msg(new basic_vnf_api::tx_request_msg_t) -{ - logger.set_level(srslog::str_to_basic_level(m_args.log_level)); - logger.set_hex_dump_max_size(m_args.log_hex_limit); - - if (m_args.type == "gnb" || m_args.type == "ue") { - if (m_args.type == "gnb") { - m_gnb_stack = (srsenb::stack_interface_phy_nr*)stack_; - } else { - m_ue_stack = (srsue::stack_interface_phy_nr*)stack_; - } - - logger.info("Initializing VNF for gNB"); - start(); - } else { - logger.error("Unknown VNF type. Exiting."); - } -} - -srsran_basic_vnf::~srsran_basic_vnf() -{ - stop(); -} - -void srsran_basic_vnf::run_thread() -{ - // Bind to UDP socket - sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (sockfd < 0) { - perror("socket"); - return; - } - - // Make sockets reusable - int enable = 1; -#if defined(SO_REUSEADDR) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { - perror("setsockopt(SO_REUSEADDR) failed"); - } -#endif -#if defined(SO_REUSEPORT) - if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &enable, sizeof(int)) < 0) { - perror("setsockopt(SO_REUSEPORT) failed"); - } -#endif - - servaddr.sin_family = AF_INET; - servaddr.sin_addr.s_addr = htonl(INADDR_ANY); - servaddr.sin_port = htons(m_args.bind_port); - - if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in))) { - perror("bind"); - return; - } - - struct pollfd fd; - fd.fd = sockfd; - fd.events = POLLIN; - - const uint32_t max_basic_api_pdu = sizeof(basic_vnf_api::dl_ind_msg_t) + 32; // larger than biggest message - std::unique_ptr > rx_buffer = - std::unique_ptr >(new std::array); - - running = true; - - logger.info("Started VNF handler listening on %s:%d", m_args.bind_addr.c_str(), m_args.bind_port); - - while (running) { - int ret = poll(&fd, 1, RX_TIMEOUT_MS); - switch (ret) { - case -1: - printf("Error occured.\n"); - break; - case 0: - // Timeout - break; - default: - - socklen_t len = sizeof(client_addr); - ret = recvfrom(sockfd, rx_buffer->data(), rx_buffer->size(), MSG_WAITALL, (struct sockaddr*)&client_addr, &len); - - handle_msg(rx_buffer->data(), ret); - break; - } - } - logger.info("VNF thread stopped"); -} - -int srsran_basic_vnf::handle_msg(const uint8_t* buffer, const uint32_t len) -{ - basic_vnf_api::msg_header_t* header = (basic_vnf_api::msg_header_t*)buffer; - - logger.info("Received %s (%d B)", basic_vnf_api::msg_type_text[header->type], len); - - switch (header->type) { - case basic_vnf_api::SF_IND: - handle_sf_ind((basic_vnf_api::sf_ind_msg_t*)header); - break; - case basic_vnf_api::DL_CONFIG: - printf("Error: %s not handled by VNF\n", basic_vnf_api::msg_type_text[header->type]); - break; - case basic_vnf_api::DL_IND: - handle_dl_ind((basic_vnf_api::dl_ind_msg_t*)header); - break; - case basic_vnf_api::UL_IND: - handle_ul_ind((basic_vnf_api::ul_ind_msg_t*)header); - break; - case basic_vnf_api::RX_DATA_IND: - handle_rx_data_ind((basic_vnf_api::rx_data_ind_msg_t*)header); - break; - default: - printf("Unknown msg type.\n"); - break; - } - return 0; -} - -int srsran_basic_vnf::handle_sf_ind(basic_vnf_api::sf_ind_msg_t* msg) -{ - int ret = SRSRAN_SUCCESS; - logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti); - - // store Rx timestamp - last_sf_indication_time = msg->t1; - - if (m_gnb_stack != nullptr) { - m_gnb_stack->sf_indication(msg->tti); - } else if (m_ue_stack != nullptr) { - m_ue_stack->sf_indication(msg->tti); - } else { - ret = SRSRAN_ERROR; - } - - return ret; -} - -int srsran_basic_vnf::handle_dl_ind(basic_vnf_api::dl_ind_msg_t* msg) -{ - int ret = SRSRAN_ERROR; - logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti); - - uint32_t cc_idx = 0; - - // fill DL struct - srsue::stack_interface_phy_nr::mac_nr_grant_dl_t dl_grant = {}; - dl_grant.tti = msg->tti; - - if (msg->nof_pdus > SRSRAN_MAX_TB) { - logger.error("Too many TBs (%d > %d)", msg->nof_pdus, SRSRAN_MAX_TB); - goto exit; - } - - for (uint32_t i = 0; i < msg->nof_pdus; ++i) { - srsue::stack_interface_phy_nr::tb_action_dl_result_t result = {}; - result.payload = srsran::make_byte_buffer(); - if (result.payload != nullptr && result.payload->get_tailroom() >= msg->pdus[i].length) { - result.ack = true; - memcpy(result.payload->msg, msg->pdus[i].data, msg->pdus[i].length); - result.payload->N_bytes = msg->pdus[i].length; - if (msg->pdus[i].type == basic_vnf_api::PDSCH) { - m_ue_stack->tb_decoded(cc_idx, dl_grant, std::move(result)); - } - } else { - logger.error("TB too big to fit into buffer (%d)", msg->pdus[i].length); - return SRSRAN_ERROR; - } - } - - ret = SRSRAN_SUCCESS; - -exit: - - return ret; -} - -int srsran_basic_vnf::handle_ul_ind(basic_vnf_api::ul_ind_msg_t* msg) -{ - logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->tti); - - if (msg->pdus.type != basic_vnf_api::PUSCH) { - logger.error("Received UL indication for wrong PDU type"); - return SRSRAN_ERROR; - } - - uint32_t cc_idx = 0; - - // fill DL struct - srsue::stack_interface_phy_nr::mac_nr_grant_ul_t ul_grant = {}; - ul_grant.tti = msg->tti; - ul_grant.tbs = msg->pdus.length; - ul_grant.rnti = msg->rnti; - - srsue::stack_interface_phy_nr::tb_action_ul_t ul_action = {}; - m_ue_stack->new_grant_ul(cc_idx, ul_grant, &ul_action); - - return SRSRAN_SUCCESS; -} - -int srsran_basic_vnf::handle_rx_data_ind(basic_vnf_api::rx_data_ind_msg_t* msg) -{ - logger.info("Received %s for TTI=%d", basic_vnf_api::msg_type_text[msg->header.type], msg->sfn); - - if (msg->nof_pdus != 1 || msg->pdus[0].type != basic_vnf_api::PUSCH) { - logger.error("Received UL indication for wrong PDU type"); - return SRSRAN_ERROR; - } - - // fill struct - srsenb::stack_interface_phy_nr::rx_data_ind_t rx_data = {}; - rx_data.tti = msg->sfn; - rx_data.tb = srsran::make_byte_buffer(); - if (rx_data.tb->get_tailroom() >= msg->pdus[0].length) { - // copy actual data - memcpy(rx_data.tb->msg, msg->pdus[0].data, msg->pdus[0].length); - rx_data.tb->N_bytes = msg->pdus[0].length; - if (msg->pdus[0].type == basic_vnf_api::PUSCH) { - m_gnb_stack->rx_data_indication(rx_data); - } - } - - return SRSRAN_SUCCESS; -} - -int srsran_basic_vnf::dl_config_request(const srsenb::phy_interface_stack_nr::dl_config_request_t& request) -{ - // Generate DL Config - basic_vnf_api::dl_conf_msg_t dl_conf = {}; - dl_conf.header.type = basic_vnf_api::DL_CONFIG; - dl_conf.header.msg_len = sizeof(dl_conf) - sizeof(basic_vnf_api::msg_header_t); - - dl_conf.t1 = last_sf_indication_time; // play back the time - dl_conf.t2 = 0xaa; // TODO: add timestamp - dl_conf.tti = request.tti; - dl_conf.beam_id = request.beam_id; - - // Send entire struct - uint32_t len = sizeof(dl_conf); - - // Send it to PNF - logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[dl_conf.header.type], len); - int n = 0; - if ((n = sendto(sockfd, &dl_conf, len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) < 0) { - logger.error("sendto failed, ret=%d", n); - } - - return 0; -} - -/// Tx request from UE, i.e. UL transmission -int srsran_basic_vnf::tx_request(const srsue::phy_interface_stack_nr::tx_request_t& request) -{ - // Generate Tx request - m_tx_req_msg->header.type = basic_vnf_api::TX_REQUEST; - m_tx_req_msg->header.msg_len = 0; // set further down - - m_tx_req_msg->tti = request.tti; - - m_tx_req_msg->nof_pdus = 1; - m_tx_req_msg->pdus[0].index = 0; - m_tx_req_msg->pdus[0].type = basic_vnf_api::PUSCH; - m_tx_req_msg->pdus[0].length = request.tb_len; - - if (request.tb_len <= MAX_PDU_SIZE) { - // copy data from TB0 - memcpy(m_tx_req_msg->pdus[0].data, request.data, request.tb_len); - } else { - logger.error("Trying to send %d B PDU. Maximum size is %d B", request.tb_len, MAX_PDU_SIZE); - } - - // calculate actual length of - uint32_t len = calc_full_msg_len(*m_tx_req_msg.get()); - - // update msg header length field - m_tx_req_msg->header.msg_len = len - sizeof(basic_vnf_api::msg_header_t); - - // Send it to PNF - logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], len); - int n = 0; - if ((n = sendto(sockfd, m_tx_req_msg.get(), len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) < - 0) { - logger.error("sendto failed, ret=%d", n); - } - - return 0; -} - -int srsran_basic_vnf::tx_request(const srsenb::phy_interface_stack_nr::tx_request_t& request) -{ - if (request.nof_pdus > MAX_NUM_PDUS) { - logger.error("Trying to send %d PDUs but only %d supported", request.nof_pdus, MAX_NUM_PDUS); - return SRSRAN_ERROR; - } - if (request.nof_pdus == 0) { - return SRSRAN_SUCCESS; - } - - // Generate Tx request - m_tx_req_msg->header.type = basic_vnf_api::TX_REQUEST; - m_tx_req_msg->header.msg_len = 0; // set further down - - m_tx_req_msg->nof_pdus = request.nof_pdus; - m_tx_req_msg->tti = request.tti; - - for (uint32_t i = 0; i < m_tx_req_msg->nof_pdus; ++i) { - if (request.pdus[i].length <= MAX_PDU_SIZE) { - m_tx_req_msg->pdus[i].index = i; - m_tx_req_msg->pdus[i].type = request.pdus[i].pbch.mib_present ? basic_vnf_api::MAC_PBCH : basic_vnf_api::PDSCH; - m_tx_req_msg->pdus[i].length = request.pdus[i].length; - // copy data from TB0 - memcpy(m_tx_req_msg->pdus[i].data, request.pdus[i].data[0], m_tx_req_msg->pdus[i].length); - } else { - logger.error("Trying to send %d B PDU. Maximum size is %d B", request.pdus[i].length, MAX_PDU_SIZE); - } - } - - // calculate actual length of message - uint32_t len = calc_full_msg_len(*m_tx_req_msg.get()); - - // update msg header length field - m_tx_req_msg->header.msg_len = len - sizeof(basic_vnf_api::msg_header_t); - - // Send it to PNF - logger.info("Sending %s (%d B)", basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], len); - if (logger.debug.enabled()) { - for (uint32_t i = 0; i < m_tx_req_msg->nof_pdus; ++i) { - logger.debug(m_tx_req_msg->pdus[i].data, - m_tx_req_msg->pdus[i].length, - "Sending PDU %s:%d (%d bytes)", - basic_vnf_api::msg_type_text[m_tx_req_msg->header.type], - m_tx_req_msg->pdus[i].index, - m_tx_req_msg->pdus[i].length); - } - } - int n = 0; - if ((n = sendto(sockfd, m_tx_req_msg.get(), len, MSG_CONFIRM, (struct sockaddr*)&client_addr, sizeof(client_addr))) < - 0) { - logger.error("sendto failed, ret=%d", n); - } - - return 0; -} - -uint32_t srsran_basic_vnf::calc_full_msg_len(const basic_vnf_api::tx_request_msg_t& msg) -{ - // start with mandatory part - uint32_t len = sizeof(basic_vnf_api::msg_header_t) + 3 * sizeof(uint32_t); - - // add all PDUs - for (uint32_t i = 0; i < msg.nof_pdus; ++i) { - len += 2 * sizeof(uint16_t) + sizeof(basic_vnf_api::pdu_type_t) + msg.pdus[i].length; - } - - return len; -} - -bool srsran_basic_vnf::stop() -{ - if (running) { - running = false; - wait_thread_finish(); - } - - return true; -} - -} // namespace srsran diff --git a/lib/src/common/bearer_manager.cc b/lib/src/common/bearer_manager.cc new file mode 100644 index 000000000..747250f36 --- /dev/null +++ b/lib/src/common/bearer_manager.cc @@ -0,0 +1,225 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/bearer_manager.h" + +namespace srsran { + +namespace detail { + +const ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::invalid_rb{srsran::srsran_rat_t::nulltype, 0, 0}; + +bool ue_bearer_manager_impl::add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) +{ + auto bearer_it = bearers.find(eps_bearer_id); + if (bearer_it != bearers.end()) { + return false; + } + bearers.emplace(eps_bearer_id, radio_bearer_t{rat, lcid, eps_bearer_id}); + lcid_to_eps_bearer_id.emplace(lcid, eps_bearer_id); + return true; +} + +bool ue_bearer_manager_impl::remove_eps_bearer(uint8_t eps_bearer_id) +{ + auto bearer_it = bearers.find(eps_bearer_id); + if (bearer_it == bearers.end()) { + return false; + } + uint32_t lcid = bearer_it->second.lcid; + bearers.erase(bearer_it); + lcid_to_eps_bearer_id.erase(lcid); + return true; +} + +void ue_bearer_manager_impl::reset() +{ + lcid_to_eps_bearer_id.clear(); + bearers.clear(); +} + +bool ue_bearer_manager_impl::has_active_radio_bearer(uint32_t eps_bearer_id) +{ + return bearers.count(eps_bearer_id) > 0; +} + +ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_radio_bearer(uint32_t eps_bearer_id) const +{ + auto it = bearers.find(eps_bearer_id); + return it != bearers.end() ? it->second : invalid_rb; +} + +ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_eps_bearer_id_for_lcid(uint32_t lcid) const +{ + auto lcid_it = lcid_to_eps_bearer_id.find(lcid); + return lcid_it != lcid_to_eps_bearer_id.end() ? bearers.at(lcid_it->second) : invalid_rb; +} + +bool ue_bearer_manager_impl::set_five_qi(uint32_t eps_bearer_id, uint16_t five_qi) +{ + auto it = bearers.find(eps_bearer_id); + if (it == bearers.end()) { + return false; + } + it->second.five_qi = five_qi; + return true; +} + +} // namespace detail +} // namespace srsran + +namespace srsue { + +ue_bearer_manager::ue_bearer_manager() : logger(srslog::fetch_basic_logger("STCK", false)) +{ + pthread_rwlock_init(&rwlock, nullptr); +} + +ue_bearer_manager::~ue_bearer_manager() +{ + pthread_rwlock_destroy(&rwlock); +} + +void ue_bearer_manager::add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) +{ + srsran::rwlock_write_guard rw_lock(rwlock); + if (impl.add_eps_bearer(eps_bearer_id, rat, lcid)) { + logger.info( + "Bearers: Registered EPS bearer ID %d for lcid=%d over %s-PDCP", eps_bearer_id, lcid, to_string(rat).c_str()); + } else { + logger.warning("Bearers: EPS bearer ID %d already registered", eps_bearer_id); + } +} + +void ue_bearer_manager::remove_eps_bearer(uint8_t eps_bearer_id) +{ + srsran::rwlock_write_guard rw_lock(rwlock); + if (impl.remove_eps_bearer(eps_bearer_id)) { + logger.info("Bearers: Removed mapping for EPS bearer ID %d", eps_bearer_id); + } else { + logger.error("Bearers: Can't remove EPS bearer ID %d", eps_bearer_id); + } +} + +void ue_bearer_manager::reset() +{ + srsran::rwlock_write_guard rw_lock(rwlock); + impl.reset(); + logger.info("Bearers: Reset EPS bearer manager"); +} + +} // namespace srsue + +namespace srsenb { + +enb_bearer_manager::enb_bearer_manager() : logger(srslog::fetch_basic_logger("STCK", false)) {} + +enb_bearer_manager::~enb_bearer_manager() {} + +void enb_bearer_manager::add_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) +{ + auto user_it = users_map.find(rnti); + if (user_it == users_map.end()) { + // add empty bearer map + // users_map.emplace( ) returns pair + auto p = users_map.emplace(rnti, srsran::detail::ue_bearer_manager_impl{}); + if (!p.second) { + logger.error("Bearers: Unable to add a new bearer map for rnti=0x%x", rnti); + return; + } + user_it = p.first; + } + + if (user_it->second.add_eps_bearer(eps_bearer_id, rat, lcid)) { + logger.info("Bearers: Registered eps-BearerID=%d for rnti=0x%x, lcid=%d over %s-PDCP", + eps_bearer_id, + rnti, + lcid, + to_string(rat).c_str()); + } else { + logger.warning("Bearers: EPS bearer ID %d for rnti=0x%x already registered", eps_bearer_id, rnti); + } +} + +void enb_bearer_manager::remove_eps_bearer(uint16_t rnti, uint8_t eps_bearer_id) +{ + auto user_it = users_map.find(rnti); + if (user_it == users_map.end()) { + logger.info("Bearers: No EPS bearer registered for rnti=0x%x", rnti); + return; + } + + if (user_it->second.remove_eps_bearer(eps_bearer_id)) { + logger.info("Bearers: Removed mapping for EPS bearer ID %d for rnti=0x%x", eps_bearer_id, rnti); + } else { + logger.info("Bearers: Can't remove EPS bearer ID %d, rnti=0x%x", eps_bearer_id, rnti); + } +} + +void enb_bearer_manager::rem_user(uint16_t rnti) +{ + auto user_it = users_map.find(rnti); + if (user_it == users_map.end()) { + logger.info("Bearers: No EPS bearer registered for rnti=0x%x", rnti); + return; + } + + logger.info("Bearers: Removed rnti=0x%x from EPS bearer manager", rnti); + users_map.erase(user_it); +} + +bool enb_bearer_manager::has_active_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id) +{ + auto user_it = users_map.find(rnti); + if (user_it == users_map.end()) { + return false; + } + return user_it->second.has_active_radio_bearer(eps_bearer_id); +} + +enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_lcid_bearer(uint16_t rnti, uint32_t lcid) const +{ + auto user_it = users_map.find(rnti); + if (user_it == users_map.end()) { + return srsran::detail::ue_bearer_manager_impl::invalid_rb; + } + return user_it->second.get_eps_bearer_id_for_lcid(lcid); +} + +enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id) +{ + auto user_it = users_map.find(rnti); + if (user_it == users_map.end()) { + return srsran::detail::ue_bearer_manager_impl::invalid_rb; + } + return user_it->second.get_radio_bearer(eps_bearer_id); +} + +bool enb_bearer_manager::set_five_qi(uint16_t rnti, uint32_t eps_bearer_id, uint16_t five_qi) +{ + auto user_it = users_map.find(rnti); + if (user_it == users_map.end()) { + return false; + } + return user_it->second.set_five_qi(eps_bearer_id, five_qi); +} + +} // namespace srsenb diff --git a/lib/src/common/buffer_pool.cc b/lib/src/common/buffer_pool.cc index d4d6e12c0..f300211fa 100644 --- a/lib/src/common/buffer_pool.cc +++ b/lib/src/common/buffer_pool.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/byte_buffer.cc b/lib/src/common/byte_buffer.cc index d6870a99e..1778218e8 100644 --- a/lib/src/common/byte_buffer.cc +++ b/lib/src/common/byte_buffer.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/crash_handler.cc b/lib/src/common/crash_handler.cc index ec8ae8ad3..f15624b10 100644 --- a/lib/src/common/crash_handler.cc +++ b/lib/src/common/crash_handler.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,44 +31,38 @@ using namespace backward; void srsran_debug_handle_crash(int argc, char** argv) { - backward::SignalHandling sh; + static backward::SignalHandling sh; } #else // HAVE_BACKWARD #include "srsran/common/backtrace.h" #include "srsran/version.h" -const static char crash_file_name[] = "./srsRAN.backtrace.crash"; -static int bt_argc; -static char** bt_argv; +static int bt_argc; +static char** bt_argv; static void crash_handler(int sig) { - FILE* f = fopen(crash_file_name, "a"); - if (!f) { - printf("srsRAN crashed... we could not save backtrace in '%s'...\n", crash_file_name); - } else { - time_t lnTime; - struct tm stTime; - char strdate[32]; + FILE* f = stderr; + time_t lnTime; + struct tm stTime; + char strdate[32]; - time(&lnTime); - gmtime_r(&lnTime, &stTime); + time(&lnTime); + gmtime_r(&lnTime, &stTime); - strftime(strdate, sizeof(strdate), "%d/%m/%Y %H:%M:%S", &stTime); + strftime(strdate, sizeof(strdate), "%d/%m/%Y %H:%M:%S", &stTime); - fprintf(f, "--- command='"); - for (int i = 0; i < bt_argc; i++) { - fprintf(f, "%s%s", (i == 0) ? "" : " ", bt_argv[i]); - } - fprintf(f, "' version=%s signal=%d date='%s' ---\n", SRSRAN_VERSION_STRING, sig, strdate); - - srsran_backtrace_print(f); - fprintf(f, "\n"); - - printf("srsRAN crashed... backtrace saved in '%s'...\n", crash_file_name); - fclose(f); + fprintf(f, "--- command='"); + for (int i = 0; i < bt_argc; i++) { + fprintf(f, "%s%s", (i == 0) ? "" : " ", bt_argv[i]); } - printf("--- exiting ---\n"); + fprintf(f, "' version=%s signal=%d date='%s' ---\n", SRSRAN_VERSION_STRING, sig, strdate); + + srsran_backtrace_print(f); + + fprintf(f, "srsRAN crashed. Please send this backtrace to the developers ...\n"); + + fprintf(f, "--- exiting ---\n"); exit(1); } @@ -84,4 +78,4 @@ void srsran_debug_handle_crash(int argc, char** argv) signal(SIGPIPE, crash_handler); } -#endif // HAVE_BACKWARD \ No newline at end of file +#endif // HAVE_BACKWARD diff --git a/lib/src/common/enb_events.cc b/lib/src/common/enb_events.cc index cfe9cf18b..84dc53b5d 100644 --- a/lib/src/common/enb_events.cc +++ b/lib/src/common/enb_events.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,8 @@ #include "srsran/common/enb_events.h" #include "srsran/srslog/context.h" #include "srsran/srslog/log_channel.h" +#include +#include using namespace srsenb; @@ -32,17 +34,34 @@ class null_event_logger : public event_logger_interface { public: void log_rrc_event(uint32_t enb_cc_idx, - const std::string& asn1, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, unsigned type, unsigned additional_info, uint16_t rnti) override {} void log_s1_ctx_create(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {} void log_s1_ctx_delete(uint32_t enb_cc_idx, uint32_t mme_id, uint32_t enb_id, uint16_t rnti) override {} - void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override {} - void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override {} - void log_measurement_report(uint32_t enb_cc_idx, const std::string& asn1, uint16_t rnti) override {} - void log_rlf(uint32_t enb_cc_idx, const std::string& asn1, uint16_t rnti) override {} + void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id, const std::string& hnb_name) override {} + void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id, const std::string& hnb_name) override {} + void log_measurement_report(uint32_t enb_cc_idx, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, + uint16_t rnti) override + {} + void log_rlf_report(uint32_t enb_cc_idx, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, + uint16_t rnti) override + {} + void log_rlf_detected(uint32_t enb_cc_idx, const std::string& type, uint16_t rnti) override {} + void log_handover_command(uint32_t enb_cc_idx, + uint32_t target_pci, + uint32_t target_earfcn, + uint16_t new_ue_rnti, + uint16_t rnti) override + {} + void log_connection_resume(uint32_t enb_cc_idx, uint16_t resume_rnti, uint16_t rnti) override {} }; } // namespace @@ -54,12 +73,54 @@ static double get_time_stamp() return std::chrono::duration_cast(tp).count() * 1e-3; } +/// Escapes the input string. +static std::string escape_string(const std::string& s) +{ + fmt::memory_buffer buff; + for (auto c : s) { + switch (c) { + case ' ': + break; + case '"': + fmt::format_to(buff, "\\\""); + break; + case '\\': + fmt::format_to(buff, "\\\\"); + break; + case '\b': + fmt::format_to(buff, "\\b"); + break; + case '\f': + fmt::format_to(buff, "\\f"); + break; + case '\n': + fmt::format_to(buff, "\\n"); + break; + case '\r': + fmt::format_to(buff, "\\r"); + break; + case '\t': + fmt::format_to(buff, "\\t"); + break; + default: + // Cast to signed char for machines that treat chars as an unsigned type. + if ((signed char)c >= '\x00' && (signed char)c <= '\x1f') { + fmt::format_to(buff, "\\u{:04x}", c); + } else { + buff.push_back(c); + } + } + } + return fmt::to_string(buff); +} + namespace { /// Common metrics to all events. DECLARE_METRIC("type", metric_type_tag, std::string, ""); DECLARE_METRIC("timestamp", metric_timestamp_tag, double, ""); -DECLARE_METRIC("sector_id", metric_sector_id, uint32_t, ""); +DECLARE_METRIC("carrier_id", metric_carrier_id, uint32_t, ""); +DECLARE_METRIC("cell_id", metric_cell_id, uint32_t, ""); DECLARE_METRIC("event_name", metric_event_name, std::string, ""); DECLARE_METRIC("rnti", metric_rnti, uint16_t, ""); @@ -73,8 +134,12 @@ DECLARE_METRIC("pci", metric_pci, uint32_t, ""); DECLARE_METRIC("cell_identity", metric_cell_identity, std::string, ""); DECLARE_METRIC("sib9_home_enb_name", metric_sib9_home_enb_name, std::string, ""); DECLARE_METRIC_SET("event_data", mset_sector_event, metric_pci, metric_cell_identity, metric_sib9_home_enb_name); -using sector_event_t = srslog:: - build_context_type; +using sector_event_t = srslog::build_context_type; /// Context for a RRC event. DECLARE_METRIC("asn1_type", metric_asn1_type, uint32_t, ""); @@ -86,46 +151,103 @@ DECLARE_METRIC_SET("event_data", metric_asn1_message, metric_asn1_type, metric_additional); -using rrc_event_t = srslog:: - build_context_type; +using rrc_event_t = srslog::build_context_type; /// Context for S1 context create/delete. DECLARE_METRIC("mme_ue_s1ap_id", metric_ue_mme_id, uint32_t, ""); DECLARE_METRIC("enb_ue_s1ap_id", metric_ue_enb_id, uint32_t, ""); DECLARE_METRIC_SET("event_data", mset_s1apctx_event, metric_ue_mme_id, metric_ue_enb_id, metric_rnti); -using s1apctx_event_t = srslog:: - build_context_type; +using s1apctx_event_t = srslog::build_context_type; -/// Context for the RLF event. -DECLARE_METRIC_SET("event_data", mset_rlfctx_event, metric_asn1_length, metric_asn1_message, metric_rnti); -using rlfctx_event_t = srslog:: - build_context_type; +/// Context for the RLF report event. +DECLARE_METRIC_SET("event_data", mset_rlf_report_event, metric_asn1_length, metric_asn1_message, metric_rnti); +using rlf_report_event_t = srslog::build_context_type; /// Context for measurement report. DECLARE_METRIC_SET("event_data", mset_meas_report_event, metric_asn1_length, metric_asn1_message, metric_rnti); using meas_report_event_t = srslog::build_context_type; +/// Context for the handover command event. +DECLARE_METRIC("target_pci", metric_target_pci, uint32_t, ""); +DECLARE_METRIC("target_earfcn", metric_target_earfcn, uint32_t, ""); +DECLARE_METRIC("new_ue_rnti", metric_new_ue_rnti, uint32_t, ""); +DECLARE_METRIC_SET("event_data", + mset_ho_cmd_event, + metric_rnti, + metric_target_pci, + metric_target_earfcn, + metric_new_ue_rnti); +using ho_cmd_t = srslog::build_context_type; + +/// Context for the connection resume event. +DECLARE_METRIC("resume_rnti", metric_resume_rnti, uint32_t, ""); +DECLARE_METRIC_SET("event_data", mset_conn_resume_event, metric_rnti, metric_resume_rnti); +using conn_resume_t = srslog::build_context_type; + +/// Context for the RLF detected event. +DECLARE_METRIC("type", metric_rlf_type, std::string, ""); +DECLARE_METRIC_SET("event_data", mset_rlf_detected_event, metric_rnti, metric_rlf_type); +using rlf_detected_t = srslog::build_context_type; + /// Logs events into the configured log channel. class logging_event_logger : public event_logger_interface { public: - explicit logging_event_logger(srslog::log_channel& c) : event_channel(c) {} + logging_event_logger(srslog::log_channel& c, event_logger::asn1_output_format asn1_format) : + event_channel(c), asn1_format(asn1_format) + {} void log_rrc_event(uint32_t enb_cc_idx, - const std::string& asn1, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, unsigned type, unsigned additional_info, uint16_t rnti) override { rrc_event_t ctx(""); + const std::string& asn1 = + (asn1_format == event_logger::asn1_output_format::octets) ? asn1_oct_str : escape_string(asn1_txt_str); + ctx.write("event"); ctx.write(get_time_stamp()); - ctx.write(enb_cc_idx); + ctx.write(enb_cc_idx); + ctx.write(get_pci(enb_cc_idx)); ctx.write("rrc_log"); ctx.get().write(rnti); ctx.get().write(asn1.size()); @@ -141,7 +263,8 @@ public: ctx.write("event"); ctx.write(get_time_stamp()); - ctx.write(enb_cc_idx); + ctx.write(enb_cc_idx); + ctx.write(get_pci(enb_cc_idx)); ctx.write("s1_context_create"); ctx.get().write(mme_id); ctx.get().write(enb_id); @@ -155,7 +278,8 @@ public: ctx.write("event"); ctx.write(get_time_stamp()); - ctx.write(enb_cc_idx); + ctx.write(enb_cc_idx); + ctx.write(get_pci(enb_cc_idx)); ctx.write("s1_context_delete"); ctx.get().write(mme_id); ctx.get().write(enb_id); @@ -163,41 +287,52 @@ public: event_channel(ctx); } - void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override + void log_sector_start(uint32_t cc_idx, uint32_t pci, uint32_t cell_id, const std::string& hnb_name) override { + register_pci(cc_idx, pci); + sector_event_t ctx(""); ctx.write("event"); ctx.write(get_time_stamp()); - ctx.write(cc_idx); + ctx.write(cc_idx); + ctx.write(pci); ctx.write("sector_start"); ctx.get().write(pci); ctx.get().write(fmt::to_string(cell_id)); - ctx.get().write("TODO"); + ctx.get().write(hnb_name); event_channel(ctx); } - void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id) override + void log_sector_stop(uint32_t cc_idx, uint32_t pci, uint32_t cell_id, const std::string& hnb_name) override { sector_event_t ctx(""); ctx.write("event"); ctx.write(get_time_stamp()); - ctx.write(cc_idx); + ctx.write(cc_idx); + ctx.write(get_pci(cc_idx)); ctx.write("sector_stop"); ctx.get().write(pci); ctx.get().write(fmt::to_string(cell_id)); - ctx.get().write("TODO"); + ctx.get().write(hnb_name); event_channel(ctx); } - void log_measurement_report(uint32_t enb_cc_idx, const std::string& asn1, uint16_t rnti) override + void log_measurement_report(uint32_t enb_cc_idx, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, + uint16_t rnti) override { meas_report_event_t ctx(""); + const std::string& asn1 = + (asn1_format == event_logger::asn1_output_format::octets) ? asn1_oct_str : escape_string(asn1_txt_str); + ctx.write("event"); ctx.write(get_time_stamp()); - ctx.write(enb_cc_idx); + ctx.write(enb_cc_idx); + ctx.write(get_pci(enb_cc_idx)); ctx.write("measurement_report"); ctx.get().write(asn1.size()); ctx.get().write(asn1); @@ -205,22 +340,97 @@ public: event_channel(ctx); } - void log_rlf(uint32_t enb_cc_idx, const std::string& asn1, uint16_t rnti) override + void log_rlf_report(uint32_t enb_cc_idx, + const std::string& asn1_oct_str, + const std::string& asn1_txt_str, + uint16_t rnti) override { - rlfctx_event_t ctx(""); + rlf_report_event_t ctx(""); + + const std::string& asn1 = + (asn1_format == event_logger::asn1_output_format::octets) ? asn1_oct_str : escape_string(asn1_txt_str); ctx.write("event"); ctx.write(get_time_stamp()); - ctx.write(enb_cc_idx); - ctx.write("radio_link_failure"); - ctx.get().write(asn1.size()); - ctx.get().write(asn1); - ctx.get().write(rnti); + ctx.write(enb_cc_idx); + ctx.write(get_pci(enb_cc_idx)); + ctx.write("rlf_report"); + ctx.get().write(asn1.size()); + ctx.get().write(asn1); + ctx.get().write(rnti); + event_channel(ctx); + } + + void log_rlf_detected(uint32_t enb_cc_idx, const std::string& type, uint16_t rnti) override + { + rlf_detected_t ctx(""); + + ctx.write("event"); + ctx.write(get_time_stamp()); + ctx.write(enb_cc_idx); + ctx.write(get_pci(enb_cc_idx)); + ctx.write("rlf_detected"); + ctx.get().write(rnti); + ctx.get().write(type); + event_channel(ctx); + } + + void log_handover_command(uint32_t enb_cc_idx, + uint32_t target_pci, + uint32_t target_earfcn, + uint16_t new_ue_rnti, + uint16_t rnti) override + { + ho_cmd_t ctx(""); + + ctx.write("event"); + ctx.write(get_time_stamp()); + ctx.write(enb_cc_idx); + ctx.write(get_pci(enb_cc_idx)); + ctx.write("ho_command"); + ctx.get().write(rnti); + ctx.get().write(target_pci); + ctx.get().write(target_earfcn); + ctx.get().write(new_ue_rnti); + event_channel(ctx); + } + + void log_connection_resume(uint32_t enb_cc_idx, uint16_t resume_rnti, uint16_t rnti) override + { + conn_resume_t ctx(""); + + ctx.write("event"); + ctx.write(get_time_stamp()); + ctx.write(enb_cc_idx); + ctx.write(get_pci(enb_cc_idx)); + ctx.write("connection_resume"); + ctx.get().write(rnti); + ctx.get().write(resume_rnti); event_channel(ctx); } private: - srslog::log_channel& event_channel; + /// Associates the corresponding cc_idx with its PCI. + void register_pci(uint32_t cc_idx, uint32_t pci) + { + std::lock_guard lock(m); + cc_idx_to_pci_map[cc_idx] = pci; + } + + /// Returns the PCI value associated to specified cc_idx, otherwise returns an invalid PCI value if not previous + /// association has been set. + uint32_t get_pci(uint32_t cc_idx) const + { + std::lock_guard lock(m); + auto it = cc_idx_to_pci_map.find(cc_idx); + return (it != cc_idx_to_pci_map.cend()) ? it->second : 999; + } + +private: + srslog::log_channel& event_channel; + event_logger::asn1_output_format asn1_format; + std::unordered_map cc_idx_to_pci_map; + mutable std::mutex m; }; } // namespace @@ -232,7 +442,7 @@ event_logger_interface& event_logger::get() return *pimpl; } -void event_logger::configure(srslog::log_channel& c) +void event_logger::configure(srslog::log_channel& c, asn1_output_format asn1_format) { - pimpl = std::unique_ptr(new logging_event_logger(c)); + pimpl = std::unique_ptr(new logging_event_logger(c, asn1_format)); } diff --git a/lib/src/common/gen_mch_tables.c b/lib/src/common/gen_mch_tables.c index 27797cf21..c240f7a59 100644 --- a/lib/src/common/gen_mch_tables.c +++ b/lib/src/common/gen_mch_tables.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/liblte_security.cc b/lib/src/common/liblte_security.cc index 0687b0b3b..cd941afc2 100644 --- a/lib/src/common/liblte_security.cc +++ b/lib/src/common/liblte_security.cc @@ -21,6 +21,8 @@ #include "srsran/common/ssl.h" #include "srsran/common/zuc.h" +#include + /******************************************************************************* LOCAL FUNCTION PROTOTYPES *******************************************************************************/ @@ -120,6 +122,70 @@ LIBLTE_ERROR_ENUM liblte_security_generate_k_enb(uint8* k_asme, uint32 nas_count return (err); } +LIBLTE_ERROR_ENUM liblte_security_generate_res_star(uint8_t* ck, + uint8_t* ik, + const char* serving_network_name, + uint8_t* rand, + uint8_t* res, + size_t res_len, + uint8_t* res_star) +{ + LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; + uint8_t key[32]; + uint8_t* s; + + if (ck != NULL && ik != NULL && serving_network_name != NULL && rand != NULL && res != NULL && res_star != NULL) { + // Construct S + uint16_t ssn_length = strlen(serving_network_name); + uint16_t rand_length = 16; + uint32_t s_len = 1 + ssn_length + 2 + rand_length + 2 + res_len + 2; + + uint8_t output[32] = {}; + + s = (uint8_t*)calloc(s_len, sizeof(uint8_t)); + if (s == nullptr) { + return err; + } + + uint32_t i = 0; + s[i] = 0x6B; // FC + i++; + + // SSN + memcpy(&s[i], serving_network_name, strlen(serving_network_name)); + i += ssn_length; + uint16_t ssn_length_value = htons(ssn_length); + memcpy(&s[i], &ssn_length_value, sizeof(ssn_length)); + i += sizeof(ssn_length_value); + + // RAND + memcpy(&s[i], rand, rand_length); + i += rand_length; + uint16_t rand_length_value = htons(rand_length); + memcpy(&s[i], &rand_length_value, sizeof(rand_length)); + i += sizeof(rand_length_value); + + // RES + memcpy(&s[i], res, res_len); + i += res_len; + uint16_t res_length_value = htons(res_len); + memcpy(&s[i], &res_length_value, sizeof(res_length_value)); + i += sizeof(res_length_value); + + // The input key Key shall be equal to the concatenation CK || IK of CK and IK. + memcpy(key, ck, 16); + memcpy(key + 16, ik, 16); + + // Derive output + sha256(key, 32, s, s_len, output, 0); + memcpy(res_star, output + 16, 16); + + free(s); + err = LIBLTE_SUCCESS; + } + return (err); +} + /********************************************************************* Name: liblte_security_generate_k_enb_star @@ -192,22 +258,18 @@ LIBLTE_ERROR_ENUM liblte_security_generate_k_nas(uint8* LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; uint8 s[7]; - if (k_asme != NULL && k_nas_enc != NULL) { //{} - if (enc_alg_id != LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_EEA0) { - // Construct S for KNASenc - s[0] = 0x15; // FC - s[1] = 0x01; // P0 - s[2] = 0x00; // First byte of L0 - s[3] = 0x01; // Second byte of L0 - s[4] = enc_alg_id; // P1 - s[5] = 0x00; // First byte of L1 - s[6] = 0x01; // Second byte of L1 + if (k_asme != NULL && k_nas_enc != NULL) { + // Construct S for KNASenc + s[0] = 0x15; // FC + s[1] = 0x01; // P0 + s[2] = 0x00; // First byte of L0 + s[3] = 0x01; // Second byte of L0 + s[4] = enc_alg_id; // P1 + s[5] = 0x00; // First byte of L1 + s[6] = 0x01; // Second byte of L1 - // Derive KNASenc - sha256(k_asme, 32, s, 7, k_nas_enc, 0); - } else { - memset(k_nas_enc, 0, 32); - } + // Derive KNASenc + sha256(k_asme, 32, s, 7, k_nas_enc, 0); } if (k_asme != NULL && k_nas_int != NULL) { @@ -366,7 +428,6 @@ LIBLTE_ERROR_ENUM liblte_security_generate_k_nr_up(uint8* LIBLTE_ERROR_ENUM liblte_security_generate_sk_gnb(uint8_t* k_enb, uint8_t* sk_gnb, uint16_t scg_counter) { - LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; uint8 s[5]; @@ -565,97 +626,96 @@ LIBLTE_ERROR_ENUM liblte_security_128_eia2(const uint8* key, LIBLTE_BIT_MSG_STRUCT* msg, uint8* mac) { - LIBLTE_ERROR_ENUM err = LIBLTE_ERROR_INVALID_INPUTS; - uint8 M[msg->N_bits * 8 + 8 + 16]; - aes_context ctx; - uint32 i; - uint32 j; - uint32 n; - uint32 pad_bits; - uint8 const_zero[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8 L[16]; - uint8 K1[16]; - uint8 K2[16]; - uint8 T[16]; - uint8 tmp[16]; - - if (key != NULL && msg != NULL && mac != NULL) { - // Subkey L generation - aes_setkey_enc(&ctx, key, 128); - aes_crypt_ecb(&ctx, AES_ENCRYPT, const_zero, L); - - // Subkey K1 generation - for (i = 0; i < 15; i++) { - K1[i] = (L[i] << 1) | ((L[i + 1] >> 7) & 0x01); - } - K1[15] = L[15] << 1; - if (L[0] & 0x80) { - K1[15] ^= 0x87; - } - - // Subkey K2 generation - for (i = 0; i < 15; i++) { - K2[i] = (K1[i] << 1) | ((K1[i + 1] >> 7) & 0x01); - } - K2[15] = K1[15] << 1; - if (K1[0] & 0x80) { - K2[15] ^= 0x87; - } - - // Construct M - memset(M, 0, msg->N_bits * 8 + 8 + 16); - M[0] = (count >> 24) & 0xFF; - M[1] = (count >> 16) & 0xFF; - M[2] = (count >> 8) & 0xFF; - M[3] = count & 0xFF; - M[4] = (bearer << 3) | (direction << 2); - for (i = 0; i < msg->N_bits / 8; i++) { - M[8 + i] = 0; - for (j = 0; j < 8; j++) { - M[8 + i] |= msg->msg[i * 8 + j] << (7 - j); - } - } - if ((msg->N_bits % 8) != 0) { - M[8 + i] = 0; - for (j = 0; j < msg->N_bits % 8; j++) { - M[8 + i] |= msg->msg[i * 8 + j] << (7 - j); - } - } - - // MAC generation - n = (uint32)(ceilf((float)(msg->N_bits + 64) / (float)(128))); - for (i = 0; i < 16; i++) { - T[i] = 0; - } - for (i = 0; i < n - 1; i++) { - for (j = 0; j < 16; j++) { - tmp[j] = T[j] ^ M[i * 16 + j]; - } - aes_crypt_ecb(&ctx, AES_ENCRYPT, tmp, T); - } - pad_bits = (msg->N_bits + 64) % 128; - if (pad_bits == 0) { - for (j = 0; j < 16; j++) { - tmp[j] = T[j] ^ K1[j] ^ M[i * 16 + j]; - } - aes_crypt_ecb(&ctx, AES_ENCRYPT, tmp, T); - } else { - pad_bits = (128 - pad_bits) - 1; - M[i * 16 + (15 - (pad_bits / 8))] |= 0x1 << (pad_bits % 8); - for (j = 0; j < 16; j++) { - tmp[j] = T[j] ^ K2[j] ^ M[i * 16 + j]; - } - aes_crypt_ecb(&ctx, AES_ENCRYPT, tmp, T); - } - - for (i = 0; i < 4; i++) { - mac[i] = T[i]; - } - - err = LIBLTE_SUCCESS; + if (!key || !msg || !mac) { + return LIBLTE_ERROR_INVALID_INPUTS; } - return (err); + uint8 M[msg->N_bits * 8 + 8 + 16]; + aes_context ctx; + uint32 i; + uint32 j; + uint32 n; + uint32 pad_bits; + uint8 const_zero[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint8 L[16]; + uint8 K1[16]; + uint8 K2[16]; + uint8 T[16]; + uint8 tmp[16]; + + // Subkey L generation + aes_setkey_enc(&ctx, key, 128); + aes_crypt_ecb(&ctx, AES_ENCRYPT, const_zero, L); + + // Subkey K1 generation + for (i = 0; i < 15; i++) { + K1[i] = (L[i] << 1) | ((L[i + 1] >> 7) & 0x01); + } + K1[15] = L[15] << 1; + if (L[0] & 0x80) { + K1[15] ^= 0x87; + } + + // Subkey K2 generation + for (i = 0; i < 15; i++) { + K2[i] = (K1[i] << 1) | ((K1[i + 1] >> 7) & 0x01); + } + K2[15] = K1[15] << 1; + if (K1[0] & 0x80) { + K2[15] ^= 0x87; + } + + // Construct M + memset(M, 0, msg->N_bits * 8 + 8 + 16); + M[0] = (count >> 24) & 0xFF; + M[1] = (count >> 16) & 0xFF; + M[2] = (count >> 8) & 0xFF; + M[3] = count & 0xFF; + M[4] = (bearer << 3) | (direction << 2); + for (i = 0; i < msg->N_bits / 8; i++) { + M[8 + i] = 0; + for (j = 0; j < 8; j++) { + M[8 + i] |= msg->msg[i * 8 + j] << (7 - j); + } + } + if ((msg->N_bits % 8) != 0) { + M[8 + i] = 0; + for (j = 0; j < msg->N_bits % 8; j++) { + M[8 + i] |= msg->msg[i * 8 + j] << (7 - j); + } + } + + // MAC generation + n = (uint32)(ceilf((float)(msg->N_bits + 64) / (float)(128))); + for (i = 0; i < 16; i++) { + T[i] = 0; + } + for (i = 0; i < n - 1; i++) { + for (j = 0; j < 16; j++) { + tmp[j] = T[j] ^ M[i * 16 + j]; + } + aes_crypt_ecb(&ctx, AES_ENCRYPT, tmp, T); + } + pad_bits = (msg->N_bits + 64) % 128; + if (pad_bits == 0) { + for (j = 0; j < 16; j++) { + tmp[j] = T[j] ^ K1[j] ^ M[i * 16 + j]; + } + aes_crypt_ecb(&ctx, AES_ENCRYPT, tmp, T); + } else { + pad_bits = (128 - pad_bits) - 1; + M[i * 16 + (15 - (pad_bits / 8))] |= 0x1 << (pad_bits % 8); + for (j = 0; j < 16; j++) { + tmp[j] = T[j] ^ K2[j] ^ M[i * 16 + j]; + } + aes_crypt_ecb(&ctx, AES_ENCRYPT, tmp, T); + } + + for (i = 0; i < 4; i++) { + mac[i] = T[i]; + } + + return LIBLTE_SUCCESS; } uint32_t GET_WORD(uint32_t* DATA, uint32_t i) @@ -1155,13 +1215,13 @@ liblte_security_milenage_f2345(uint8* k, uint8* op_c, uint8* rand, uint8* res, u for (i = 0; i < 16; i++) { input[i] = rand[i] ^ op_c[i]; } - mbedtls_aes_crypt_ecb(&ctx, AES_ENCRYPT, input, temp); + aes_crypt_ecb(&ctx, AES_ENCRYPT, input, temp); // Compute out for RES and AK for (i = 0; i < 16; i++) { input[i] = temp[i] ^ op_c[i]; } input[15] ^= 1; - mbedtls_aes_crypt_ecb(&ctx, AES_ENCRYPT, input, out); + aes_crypt_ecb(&ctx, AES_ENCRYPT, input, out); for (i = 0; i < 16; i++) { out[i] ^= op_c[i]; } @@ -1181,7 +1241,7 @@ liblte_security_milenage_f2345(uint8* k, uint8* op_c, uint8* rand, uint8* res, u input[(i + 12) % 16] = temp[i] ^ op_c[i]; } input[15] ^= 2; - mbedtls_aes_crypt_ecb(&ctx, AES_ENCRYPT, input, out); + aes_crypt_ecb(&ctx, AES_ENCRYPT, input, out); for (i = 0; i < 16; i++) { out[i] ^= op_c[i]; } @@ -1196,7 +1256,7 @@ liblte_security_milenage_f2345(uint8* k, uint8* op_c, uint8* rand, uint8* res, u input[(i + 8) % 16] = temp[i] ^ op_c[i]; } input[15] ^= 4; - mbedtls_aes_crypt_ecb(&ctx, AES_ENCRYPT, input, out); + aes_crypt_ecb(&ctx, AES_ENCRYPT, input, out); for (i = 0; i < 16; i++) { out[i] ^= op_c[i]; } diff --git a/lib/src/common/mac_pcap.cc b/lib/src/common/mac_pcap.cc index 073b0d390..4da8c3076 100644 --- a/lib/src/common/mac_pcap.cc +++ b/lib/src/common/mac_pcap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,9 +39,9 @@ uint32_t mac_pcap::open(std::string filename_, uint32_t ue_id_) return SRSRAN_ERROR; } - // set DLT for selected RAT + // set UDP DLT dlt = UDP_DLT; - pcap_file = LTE_PCAP_Open(dlt, filename_.c_str()); + pcap_file = DLT_PCAP_Open(dlt, filename_.c_str()); if (pcap_file == nullptr) { logger.error("Couldn't open %s to write PCAP", filename_.c_str()); return SRSRAN_ERROR; @@ -77,7 +77,7 @@ uint32_t mac_pcap::close() { std::lock_guard lock(mutex); srsran::console("Saving MAC PCAP (DLT=%d) to %s\n", dlt, filename.c_str()); - LTE_PCAP_Close(pcap_file); + DLT_PCAP_Close(pcap_file); pcap_file = nullptr; } diff --git a/lib/src/common/mac_pcap_base.cc b/lib/src/common/mac_pcap_base.cc index 744716290..57b71dbe9 100644 --- a/lib/src/common/mac_pcap_base.cc +++ b/lib/src/common/mac_pcap_base.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,13 +22,28 @@ #include "srsran/common/mac_pcap_base.h" #include "srsran/config.h" #include "srsran/phy/common/phy_common.h" +#include "srsran/support/emergency_handlers.h" #include namespace srsran { -mac_pcap_base::mac_pcap_base() : logger(srslog::fetch_basic_logger("MAC")), thread("PCAP_WRITER_MAC") {} +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} -mac_pcap_base::~mac_pcap_base() {} +mac_pcap_base::mac_pcap_base() : logger(srslog::fetch_basic_logger("MAC")), thread("PCAP_WRITER_MAC") +{ + emergency_handler_id = add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} + +mac_pcap_base::~mac_pcap_base() +{ + if (emergency_handler_id > 0) { + remove_emergency_cleanup_handler(emergency_handler_id); + } +} void mac_pcap_base::enable(bool enable_) { @@ -54,7 +69,7 @@ void mac_pcap_base::run_thread() } // write remainder of queue - pcap_pdu_t pdu = {}; + pcap_pdu_t pdu = {}; while (queue.try_pop(pdu)) { std::lock_guard lock(mutex); write_pdu(pdu); diff --git a/lib/src/common/mac_pcap_net.cc b/lib/src/common/mac_pcap_net.cc index 2a3d085a2..a3869b939 100644 --- a/lib/src/common/mac_pcap_net.cc +++ b/lib/src/common/mac_pcap_net.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -58,9 +58,11 @@ uint32_t mac_pcap_net::open(std::string client_ip_addr_, client_udp_port_, bind_addr_str.c_str(), bind_udp_port_); - client_addr.sin_family = AF_INET; - client_addr.sin_addr.s_addr = inet_addr(client_ip_addr_.c_str()); - client_addr.sin_port = htons(client_udp_port_); + + if (not net_utils::set_sockaddr(&client_addr, client_ip_addr_.c_str(), client_udp_port_)) { + logger.error("Invalid client_ip_addr: %s", client_ip_addr_.c_str()); + return SRSRAN_ERROR; + } running = true; ue_id = ue_id_; // start writer thread diff --git a/lib/src/common/nas_pcap.cc b/lib/src/common/nas_pcap.cc index 71702458a..840998d4d 100644 --- a/lib/src/common/nas_pcap.cc +++ b/lib/src/common/nas_pcap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,19 +22,42 @@ #include "srsran/common/nas_pcap.h" #include "srsran/common/pcap.h" #include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" #include namespace srsran { +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} + +nas_pcap::nas_pcap() +{ + emergency_handler_id = add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} + +nas_pcap::~nas_pcap() +{ + if (emergency_handler_id > 0) { + remove_emergency_cleanup_handler(emergency_handler_id); + } +} + void nas_pcap::enable() { enable_write = true; } -uint32_t nas_pcap::open(std::string filename_, uint32_t ue_id_) +uint32_t nas_pcap::open(std::string filename_, uint32_t ue_id_, srsran_rat_t rat_type) { - filename = filename_; - pcap_file = LTE_PCAP_Open(NAS_LTE_DLT, filename.c_str()); + filename = filename_; + if (rat_type == srsran_rat_t::nr) { + pcap_file = DLT_PCAP_Open(NAS_5G_DLT, filename.c_str()); + } else { + pcap_file = DLT_PCAP_Open(NAS_LTE_DLT, filename.c_str()); + } if (pcap_file == nullptr) { return SRSRAN_ERROR; } @@ -46,7 +69,8 @@ uint32_t nas_pcap::open(std::string filename_, uint32_t ue_id_) void nas_pcap::close() { fprintf(stdout, "Saving NAS PCAP file (DLT=%d) to %s \n", NAS_LTE_DLT, filename.c_str()); - LTE_PCAP_Close(pcap_file); + DLT_PCAP_Close(pcap_file); + pcap_file = nullptr; } void nas_pcap::write_nas(uint8_t* pdu, uint32_t pdu_len_bytes) diff --git a/lib/src/common/network_utils.cc b/lib/src/common/network_utils.cc index b76beba02..a77b5061a 100644 --- a/lib/src/common/network_utils.cc +++ b/lib/src/common/network_utils.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -118,75 +118,7 @@ int open_socket(net_utils::addr_family ip_type, net_utils::socket_type socket_ty perror("Could not create socket\n"); return -1; } - - if (protocol == protocol_type::SCTP) { - // Sets the data_io_event to be able to use sendrecv_info - // Subscribes to the SCTP_SHUTDOWN event, to handle graceful shutdown - // Also subscribes to SCTP_PEER_ADDR_CHANGE, to handle ungraceful shutdown of the link. - struct sctp_event_subscribe evnts = {}; - evnts.sctp_data_io_event = 1; - evnts.sctp_shutdown_event = 1; - evnts.sctp_address_event = 1; - if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts)) != 0) { - srslog::fetch_basic_logger(LOGSERVICE).error("Failed to subscribe to SCTP_SHUTDOWN event: %s", strerror(errno)); - perror("Could not register socket to SCTP events\n"); - close(fd); - return -1; - } - - /* - * Modify SCTP default parameters for quicker detection of broken links. - * This includes changes to the SCTP_INITMSG parameters (to control the timeout of the connect() syscall) - * And changes to the maximum re-transmission timeout (rto_max), for quicker detection of broken links. - */ - // Set RTO_MAX to quickly detect broken links. - sctp_rtoinfo rto_opts; - socklen_t rto_sz = sizeof(sctp_rtoinfo); - rto_opts.srto_assoc_id = 0; - if (getsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &rto_opts, &rto_sz) < 0) { - printf("Error getting RTO_INFO sockopts\n"); - close(fd); - return -1; - } - - rto_opts.srto_max = 6000; // 6 seconds - - srslog::fetch_basic_logger(LOGSERVICE) - .debug( - "Setting RTO_INFO options on SCTP socket. Association %d, Initial RTO %d, Minimum RTO %d, Maximum RTO %d", - rto_opts.srto_assoc_id, - rto_opts.srto_initial, - rto_opts.srto_min, - rto_opts.srto_max); - - if (setsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &rto_opts, rto_sz) < 0) { - perror("Error setting RTO_INFO sockopts\n"); - close(fd); - return -1; - } - - // Set SCTP INITMSG options to reduce blocking timeout of connect() - sctp_initmsg init_opts; - socklen_t init_sz = sizeof(sctp_initmsg); - if (getsockopt(fd, SOL_SCTP, SCTP_INITMSG, &init_opts, &init_sz) < 0) { - printf("Error getting sockopts\n"); - close(fd); - return -1; - } - - init_opts.sinit_max_attempts = 3; - init_opts.sinit_max_init_timeo = 5000; // 5 seconds - - srslog::fetch_basic_logger(LOGSERVICE) - .debug("Setting SCTP_INITMSG options on SCTP socket. Max attempts %d, Max init attempts timeout %d", - init_opts.sinit_max_attempts, - init_opts.sinit_max_init_timeo); - if (setsockopt(fd, SOL_SCTP, SCTP_INITMSG, &init_opts, init_sz) < 0) { - perror("Error setting SCTP_INITMSG sockopts\n"); - close(fd); - return -1; - } - } + srslog::fetch_basic_logger(LOGSERVICE).debug("Opened %s socket=%d", net_utils::protocol_to_string(protocol), fd); return fd; } @@ -200,10 +132,17 @@ bool bind_addr(int fd, const sockaddr_in& addr_in) if (bind(fd, (struct sockaddr*)&addr_in, sizeof(addr_in)) != 0) { srslog::fetch_basic_logger(LOGSERVICE) - .error("Failed to bind on address %s: %s errno %d", get_ip(addr_in).c_str(), strerror(errno), errno); + .error("Failed to bind on address %s:%d. Socket=%d, strerror=%s, errno=%d", + get_ip(addr_in).c_str(), + get_port(addr_in), + fd, + strerror(errno), + errno); perror("bind()"); return false; } + srslog::fetch_basic_logger(LOGSERVICE) + .debug("Successfully bound to address %s:%d", get_ip(addr_in).c_str(), get_port(addr_in)); return true; } @@ -215,7 +154,11 @@ bool bind_addr(int fd, const char* bind_addr_str, int port, sockaddr_in* addr_re .error("Failed to convert IP address (%s) to sockaddr_in struct", bind_addr_str); return false; } - bind_addr(fd, addr_tmp); + + if (not bind_addr(fd, addr_tmp)) { + return false; + } + if (addr_result != nullptr) { *addr_result = addr_tmp; } @@ -245,6 +188,123 @@ bool connect_to(int fd, const char* dest_addr_str, int dest_port, sockaddr_in* d return true; } +bool start_listen(int fd) +{ + if (fd < 0) { + srslog::fetch_basic_logger(LOGSERVICE).error("Tried to listen for connections with an invalid socket."); + return false; + } + + // Listen for connections + if (listen(fd, SOMAXCONN) != 0) { + srslog::fetch_basic_logger(LOGSERVICE).error("Failed to listen to incoming SCTP connections"); + perror("listen()"); + return false; + } + return true; +} + +bool reuse_addr(int fd) +{ + if (fd < 0) { + srslog::fetch_basic_logger(LOGSERVICE).error("Trying reuse_addr a closed socket. Socket=%d", fd); + return false; + } + + int enable = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + srslog::fetch_basic_logger(LOGSERVICE).error("Failed to set SO_REUSEADDR. Socket=%d", fd); + return false; + } + srslog::fetch_basic_logger(LOGSERVICE).debug("Successfully set SO_REUSEADDR. Socket=%d", fd); + return true; +} + +bool sctp_subscribe_to_events(int fd) +{ + if (fd < 0) { + srslog::fetch_basic_logger(LOGSERVICE).error("Trying subscribe to SCTP events on a closed socket. Socket=%d", fd); + return false; + } + + // Sets the data_io_event to be able to use sendrecv_info + // Subscribes to the SCTP_SHUTDOWN event, to handle graceful shutdown + // Also subscribes to SCTP_PEER_ADDR_CHANGE, to handle ungraceful shutdown of the link. + struct sctp_event_subscribe evnts = {}; + evnts.sctp_data_io_event = 1; + evnts.sctp_shutdown_event = 1; + evnts.sctp_address_event = 1; + if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts)) != 0) { + srslog::fetch_basic_logger(LOGSERVICE).error("Failed to subscribe to SCTP_SHUTDOWN event: %s", strerror(errno)); + perror("Could not register socket to SCTP events\n"); + close(fd); + return false; + } + return true; +} + +/* + * Modify SCTP default parameters for quicker detection of broken links. + * Changes to the maximum re-transmission timeout (rto_max). + */ +bool sctp_set_rto_opts(int fd, int rto_max) +{ + // Set RTO_MAX to quickly detect broken links. + sctp_rtoinfo rto_opts; + socklen_t rto_sz = sizeof(sctp_rtoinfo); + rto_opts.srto_assoc_id = 0; + if (getsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &rto_opts, &rto_sz) < 0) { + printf("Error getting RTO_INFO sockopts\n"); + close(fd); + return false; + } + + rto_opts.srto_max = rto_max; + + srslog::fetch_basic_logger(LOGSERVICE) + .debug("Setting RTO_INFO options on SCTP socket. Association %d, Initial RTO %d, Minimum RTO %d, Maximum RTO %d", + rto_opts.srto_assoc_id, + rto_opts.srto_initial, + rto_opts.srto_min, + rto_opts.srto_max); + + if (setsockopt(fd, SOL_SCTP, SCTP_RTOINFO, &rto_opts, rto_sz) < 0) { + perror("Error setting RTO_INFO sockopts\n"); + close(fd); + return false; + } + return true; +} + +/* + * Modify SCTP default parameters for quicker detection of broken links. + * Changes to the SCTP_INITMSG parameters (to control the timeout of the connect() syscall) + */ +bool sctp_set_init_msg_opts(int fd, int init_max_attempts, int max_init_timeo) +{ + // Set SCTP INITMSG options to reduce blocking timeout of connect() + sctp_initmsg init_opts; + socklen_t init_sz = sizeof(sctp_initmsg); + if (getsockopt(fd, SOL_SCTP, SCTP_INITMSG, &init_opts, &init_sz) < 0) { + printf("Error getting sockopts\n"); + close(fd); + return false; + } + + init_opts.sinit_max_attempts = init_max_attempts; + init_opts.sinit_max_init_timeo = max_init_timeo; + + srslog::fetch_basic_logger(LOGSERVICE) + .debug("Setting SCTP_INITMSG options on SCTP socket. Max attempts %d, Max init attempts timeout %d", + init_opts.sinit_max_attempts, + init_opts.sinit_max_init_timeo); + if (setsockopt(fd, SOL_SCTP, SCTP_INITMSG, &init_opts, init_sz) < 0) { + perror("Error setting SCTP_INITMSG sockopts\n"); + close(fd); + return false; + } + return true; +} } // namespace net_utils /******************************************** @@ -269,12 +329,30 @@ unique_socket& unique_socket::operator=(unique_socket&& other) noexcept return *this; } +bool unique_socket::open_socket(net_utils::addr_family ip_type, + net_utils::socket_type socket_type, + net_utils::protocol_type protocol) +{ + if (is_open()) { + srslog::fetch_basic_logger(LOGSERVICE).error("Socket is already open."); + return false; + } + sockfd = net_utils::open_socket(ip_type, socket_type, protocol); + return is_open(); +} + void unique_socket::close() { if (sockfd >= 0) { - ::close(sockfd); + if (::close(sockfd) == -1) { + srslog::fetch_basic_logger(LOGSERVICE).error("Socket=%d could not be closed.", sockfd); + } else { + srslog::fetch_basic_logger(LOGSERVICE).debug("Socket=%d was closed.", sockfd); + } sockfd = -1; addr = {}; + } else { + srslog::fetch_basic_logger(LOGSERVICE).debug("Socket=%d could not be closed.", sockfd); } } @@ -288,55 +366,30 @@ bool unique_socket::connect_to(const char* dest_addr_str, int dest_port, sockadd return net_utils::connect_to(sockfd, dest_addr_str, dest_port, dest_sockaddr); } -bool unique_socket::open_socket(net_utils::addr_family ip_type, - net_utils::socket_type socket_type, - net_utils::protocol_type protocol) +bool unique_socket::start_listen() { - if (is_open()) { - srslog::fetch_basic_logger(LOGSERVICE).error("Socket is already open."); - return false; - } - sockfd = net_utils::open_socket(ip_type, socket_type, protocol); - return is_open(); + return net_utils::start_listen(sockfd); } -/*********************************************************************** - * SCTP socket - **********************************************************************/ - -namespace net_utils { - -bool sctp_init_socket(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port) +bool unique_socket::reuse_addr() { - if (not socket->open_socket(net_utils::addr_family::ipv4, socktype, net_utils::protocol_type::SCTP)) { - return false; - } - if (not socket->bind_addr(bind_addr_str, port)) { - socket->close(); - return false; - } - return true; + return net_utils::reuse_addr(sockfd); } -bool sctp_init_client(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str) +bool unique_socket::sctp_subscribe_to_events() { - return sctp_init_socket(socket, socktype, bind_addr_str, 0); + return net_utils::sctp_subscribe_to_events(sockfd); } -bool sctp_init_server(unique_socket* socket, net_utils::socket_type socktype, const char* bind_addr_str, int port) +bool unique_socket::sctp_set_rto_opts(int rto_max) { - if (not sctp_init_socket(socket, socktype, bind_addr_str, port)) { - return false; - } - // Listen for connections - if (listen(socket->fd(), SOMAXCONN) != 0) { - srslog::fetch_basic_logger(LOGSERVICE).error("Failed to listen to incoming SCTP connections"); - return false; - } - return true; + return net_utils::sctp_set_rto_opts(sockfd, rto_max); } -} // namespace net_utils +bool unique_socket::sctp_set_init_msg_opts(int max_init_attempts, int max_init_timeo) +{ + return net_utils::sctp_set_init_msg_opts(sockfd, max_init_attempts, max_init_timeo); +} /*************************************************************** * Rx Multisocket Handler @@ -468,7 +521,7 @@ void socket_manager::run_thread() FD_SET(pipefd[0], &total_fd_set); max_fd = std::max(pipefd[0], max_fd); - while (running) { + while (running.load(std::memory_order_relaxed)) { memcpy(&read_fd_set, &total_fd_set, sizeof(total_fd_set)); int n = select(max_fd + 1, &read_fd_set, nullptr, nullptr, nullptr); diff --git a/lib/src/common/ngap_pcap.cc b/lib/src/common/ngap_pcap.cc new file mode 100644 index 000000000..c7e197a1c --- /dev/null +++ b/lib/src/common/ngap_pcap.cc @@ -0,0 +1,77 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/ngap_pcap.h" +#include "srsran/common/pcap.h" +#include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" +#include + +namespace srsran { + +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} + +ngap_pcap::ngap_pcap() +{ + emergency_handler_id = add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} + +ngap_pcap::~ngap_pcap() +{ + if (emergency_handler_id > 0) { + remove_emergency_cleanup_handler(emergency_handler_id); + } +} + +void ngap_pcap::enable() +{ + enable_write = true; +} +void ngap_pcap::open(const char* filename_) +{ + filename = filename_; + pcap_file = DLT_PCAP_Open(NGAP_5G_DLT, filename.c_str()); + enable_write = true; +} +void ngap_pcap::close() +{ + if (!enable_write) { + return; + } + fprintf(stdout, "Saving NGAP PCAP file (DLT=%d) to %s\n", NGAP_5G_DLT, filename.c_str()); + DLT_PCAP_Close(pcap_file); +} + +void ngap_pcap::write_ngap(uint8_t* pdu, uint32_t pdu_len_bytes) +{ + if (enable_write) { + NGAP_Context_Info_t context; + if (pdu) { + LTE_PCAP_NGAP_WritePDU(pcap_file, &context, pdu, pdu_len_bytes); + } + } +} + +} // namespace srsran diff --git a/lib/src/common/pcap.c b/lib/src/common/pcap.c index 597acfcfa..4e3396847 100644 --- a/lib/src/common/pcap.c +++ b/lib/src/common/pcap.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,7 +27,7 @@ #include /* Open the file and write file header */ -FILE* LTE_PCAP_Open(uint32_t DLT, const char* fileName) +FILE* DLT_PCAP_Open(uint32_t DLT, const char* fileName) { pcap_hdr_t file_header = { 0xa1b2c3d4, /* magic number */ @@ -52,7 +52,7 @@ FILE* LTE_PCAP_Open(uint32_t DLT, const char* fileName) } /* Close the PCAP file */ -void LTE_PCAP_Close(FILE* fd) +void DLT_PCAP_Close(FILE* fd) { if (fd) { fclose(fd); @@ -345,6 +345,34 @@ int LTE_PCAP_S1AP_WritePDU(FILE* fd, S1AP_Context_Info_t* context, const unsigne return 1; } +/* Write an individual PDU (PCAP packet header + ngap-context + ngap-pdu) */ +int LTE_PCAP_NGAP_WritePDU(FILE* fd, NGAP_Context_Info_t* context, const unsigned char* PDU, unsigned int length) +{ + pcaprec_hdr_t packet_header; + + /* Can't write if file wasn't successfully opened */ + if (fd == NULL) { + printf("Error: Can't write to empty file handle\n"); + return 0; + } + + /****************************************************************/ + /* PCAP Header */ + struct timeval t; + gettimeofday(&t, NULL); + packet_header.ts_sec = t.tv_sec; + packet_header.ts_usec = t.tv_usec; + packet_header.incl_len = length; + packet_header.orig_len = length; + + /***************************************************************/ + /* Now write everything to the file */ + fwrite(&packet_header, sizeof(pcaprec_hdr_t), 1, fd); + fwrite(PDU, 1, length, fd); + + return 1; +} + /************************************************************************** * API functions for writing MAC-NR PCAP files * **************************************************************************/ @@ -452,4 +480,4 @@ int NR_PCAP_MAC_UDP_WritePDU(FILE* fd, mac_nr_context_info_t* context, const uns fwrite(PDU, 1, length, fd); return 1; -} \ No newline at end of file +} diff --git a/lib/src/common/phy_cfg_nr.cc b/lib/src/common/phy_cfg_nr.cc new file mode 100644 index 000000000..5fc37eff4 --- /dev/null +++ b/lib/src/common/phy_cfg_nr.cc @@ -0,0 +1,379 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/phy_cfg_nr.h" +#include "srsran/common/band_helper.h" +#include "srsran/srsran.h" + +namespace srsran { + +srsran_dci_cfg_nr_t phy_cfg_nr_t::get_dci_cfg() const +{ + srsran_dci_cfg_nr_t dci_cfg = {}; + + // Assume BWP bandwidth equals full channel bandwidth + dci_cfg.coreset0_bw = pdcch.coreset_present[0] ? srsran_coreset_get_bw(&pdcch.coreset[0]) : 0; + dci_cfg.bwp_dl_initial_bw = carrier.nof_prb; + dci_cfg.bwp_dl_active_bw = carrier.nof_prb; + dci_cfg.bwp_ul_initial_bw = carrier.nof_prb; + dci_cfg.bwp_ul_active_bw = carrier.nof_prb; + + // Iterate over all SS to select monitoring options + for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; i++) { + // Skip not configured SS + if (not pdcch.search_space_present[i]) { + continue; + } + + // Iterate all configured formats + for (uint32_t j = 0; j < pdcch.search_space[i].nof_formats; j++) { + if ((pdcch.search_space[i].type == srsran_search_space_type_common_3 or + pdcch.search_space[i].type == srsran_search_space_type_common_1) && + pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_0) { + dci_cfg.monitor_common_0_0 = true; + } else if (pdcch.search_space[i].type == srsran_search_space_type_ue && + pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_0) { + dci_cfg.monitor_0_0_and_1_0 = true; + } else if (pdcch.search_space[i].type == srsran_search_space_type_ue && + pdcch.search_space[i].formats[j] == srsran_dci_format_nr_0_1) { + dci_cfg.monitor_0_1_and_1_1 = true; + } + } + } + + // Set PUSCH parameters + dci_cfg.enable_sul = false; + dci_cfg.enable_hopping = false; + + // Set Format 0_1 and 1_1 parameters + dci_cfg.carrier_indicator_size = 0; + dci_cfg.harq_ack_codebok = harq_ack.harq_ack_codebook; + dci_cfg.nof_rb_groups = 0; + + // Format 0_1 specific configuration (for PUSCH only) + dci_cfg.nof_ul_bwp = 0; + dci_cfg.nof_ul_time_res = (pusch.nof_dedicated_time_ra > 0) + ? pusch.nof_dedicated_time_ra + : (pusch.nof_common_time_ra > 0) ? pusch.nof_common_time_ra : SRSRAN_MAX_NOF_TIME_RA; + dci_cfg.nof_srs = 1; + dci_cfg.nof_ul_layers = 1; + dci_cfg.pusch_nof_cbg = 0; + dci_cfg.report_trigger_size = 0; + dci_cfg.enable_transform_precoding = false; + dci_cfg.dynamic_dual_harq_ack_codebook = false; + dci_cfg.pusch_tx_config_non_codebook = false; + dci_cfg.pusch_ptrs = false; + dci_cfg.pusch_dynamic_betas = false; + dci_cfg.pusch_alloc_type = pusch.alloc; + dci_cfg.pusch_dmrs_type = pusch.dmrs_type; + dci_cfg.pusch_dmrs_max_len = pusch.dmrs_max_length; + + // Format 1_1 specific configuration (for PDSCH only) + dci_cfg.nof_dl_bwp = 0; + dci_cfg.nof_dl_time_res = (pdsch.nof_dedicated_time_ra > 0) + ? pdsch.nof_dedicated_time_ra + : (pdsch.nof_common_time_ra > 0) ? pdsch.nof_common_time_ra : SRSRAN_MAX_NOF_TIME_RA; + dci_cfg.nof_aperiodic_zp = 0; + dci_cfg.pdsch_nof_cbg = 0; + dci_cfg.nof_dl_to_ul_ack = harq_ack.nof_dl_data_to_ul_ack; + dci_cfg.pdsch_inter_prb_to_prb = false; + dci_cfg.pdsch_rm_pattern1 = false; + dci_cfg.pdsch_rm_pattern2 = false; + dci_cfg.pdsch_2cw = false; + dci_cfg.multiple_scell = false; + dci_cfg.pdsch_tci = false; + dci_cfg.pdsch_cbg_flush = false; + dci_cfg.pdsch_dynamic_bundling = false; + dci_cfg.pdsch_alloc_type = pdsch.alloc; + dci_cfg.pdsch_dmrs_type = pdsch.dmrs_type; + dci_cfg.pdsch_dmrs_max_len = pdsch.dmrs_max_length; + + return dci_cfg; +} + +bool phy_cfg_nr_t::assert_ss_id(uint32_t ss_id) const +{ + // Make sure SS access if bounded + if (ss_id >= SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE) { + return false; + } + + // Check SS is present + if (not pdcch.search_space_present[ss_id]) { + return false; + } + + // Extract CORESET id + uint32_t coreset_id = pdcch.search_space[ss_id].coreset_id; + + // Make sure CORESET id is bounded + if (coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET) { + return false; + } + + // Check CORESET is present + if (not pdcch.coreset_present[coreset_id]) { + return false; + } + + return true; +} + +bool phy_cfg_nr_t::get_dci_locations( + const uint32_t& slot_idx, + const uint16_t& rnti, + const uint32_t& ss_id, + const uint32_t& L, + srsran::bounded_vector& locations) const +{ + // Assert search space + if (not assert_ss_id(ss_id)) { + return SRSRAN_ERROR; + } + + // Select SS and CORESET + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + const srsran_coreset_t& coreset = pdcch.coreset[ss.coreset_id]; + + // Compute NCCE + std::array ncce = {}; + int n = srsran_pdcch_nr_locations_coreset(&coreset, &ss, rnti, L, slot_idx, ncce.data()); + if (n < SRSRAN_SUCCESS) { + return false; + } + + // Push locations + for (uint32_t i = 0; i < (uint32_t)n; i++) { + locations.push_back({L, ncce[i]}); + } + + return true; +} + +srsran_dci_format_nr_t phy_cfg_nr_t::get_dci_format_pdsch(uint32_t ss_id) const +{ + // Assert search space + if (not assert_ss_id(ss_id)) { + return SRSRAN_DCI_FORMAT_NR_COUNT; + } + + // Select SS + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + + // Extract number of formats + uint32_t nof_formats = SRSRAN_MIN(ss.nof_formats, SRSRAN_DCI_FORMAT_NR_COUNT); + + // Select DCI formats + for (uint32_t i = 0; i < nof_formats; i++) { + // Select DL format + if (ss.formats[i] == srsran_dci_format_nr_1_0 or ss.formats[i] == srsran_dci_format_nr_1_1) { + return ss.formats[i]; + } + } + + // If reached here, no valid DCI format is available + return SRSRAN_DCI_FORMAT_NR_COUNT; +} + +srsran_dci_format_nr_t phy_cfg_nr_t::get_dci_format_pusch(uint32_t ss_id) const +{ + // Assert search space + if (not assert_ss_id(ss_id)) { + return SRSRAN_DCI_FORMAT_NR_COUNT; + } + + // Select SS + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + + // Extract number of formats + uint32_t nof_formats = SRSRAN_MIN(ss.nof_formats, SRSRAN_DCI_FORMAT_NR_COUNT); + + // Select DCI formats + for (uint32_t i = 0; i < nof_formats; i++) { + // Select DL format + if (ss.formats[i] == srsran_dci_format_nr_0_0 or ss.formats[i] == srsran_dci_format_nr_0_1) { + return ss.formats[i]; + } + } + + // If reached here, no valid DCI format is available + return SRSRAN_DCI_FORMAT_NR_COUNT; +} + +bool phy_cfg_nr_t::get_dci_ctx_pdsch_rnti_c(uint32_t ss_id, + const srsran_dci_location_t& location, + const uint16_t& rnti, + srsran_dci_ctx_t& ctx) const +{ + // Get DCI format, includes SS Id assertion + srsran_dci_format_nr_t format = get_dci_format_pdsch(ss_id); + if (format == SRSRAN_DCI_FORMAT_NR_COUNT) { + return false; + } + + // Select search space + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + + // Fill context + ctx.location = location; + ctx.ss_type = ss.type; + ctx.coreset_id = ss.coreset_id; + ctx.coreset_start_rb = srsran_coreset_start_rb(&pdcch.coreset[ss.coreset_id]); + ctx.rnti_type = srsran_rnti_type_c; + ctx.format = format; + ctx.rnti = rnti; + + return true; +} + +bool phy_cfg_nr_t::get_dci_ctx_pusch_rnti_c(uint32_t ss_id, + const srsran_dci_location_t& location, + const uint16_t& rnti, + srsran_dci_ctx_t& ctx) const +{ + // Get DCI format, includes SS Id assertion + srsran_dci_format_nr_t format = get_dci_format_pusch(ss_id); + if (format == SRSRAN_DCI_FORMAT_NR_COUNT) { + return false; + } + + // Select search space + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + + // Fill context + ctx.location = location; + ctx.ss_type = ss.type; + ctx.coreset_id = ss.coreset_id; + ctx.coreset_start_rb = srsran_coreset_start_rb(&pdcch.coreset[ss.coreset_id]); + ctx.rnti_type = srsran_rnti_type_c; + ctx.format = format; + ctx.rnti = rnti; + + return true; +} + +bool phy_cfg_nr_t::get_pdsch_cfg(const srsran_slot_cfg_t& slot_cfg, + const srsran_dci_dl_nr_t& dci, + srsran_sch_cfg_nr_t& pdsch_cfg) const +{ + return srsran_ra_dl_dci_to_grant_nr(&carrier, &slot_cfg, &pdsch, &dci, &pdsch_cfg, &pdsch_cfg.grant) == + SRSRAN_SUCCESS; +} + +bool phy_cfg_nr_t::get_pusch_cfg(const srsran_slot_cfg_t& slot_cfg, + const srsran_dci_ul_nr_t& dci, + srsran_sch_cfg_nr_t& pusch_cfg) const +{ + return srsran_ra_ul_dci_to_grant_nr(&carrier, &slot_cfg, &pusch, &dci, &pusch_cfg, &pusch_cfg.grant) == + SRSRAN_SUCCESS; +} + +bool phy_cfg_nr_t::get_pdsch_ack_resource(const srsran_dci_dl_nr_t& dci_dl, + srsran_harq_ack_resource_t& ack_resource) const +{ + return srsran_harq_ack_resource(&harq_ack, &dci_dl, &ack_resource) == SRSRAN_SUCCESS; +} + +bool phy_cfg_nr_t::get_uci_cfg(const srsran_slot_cfg_t& slot_cfg, + const srsran_pdsch_ack_nr_t& pdsch_ack, + srsran_uci_cfg_nr_t& uci_cfg) const +{ + // Generate configuration for HARQ feedback + if (srsran_harq_ack_gen_uci_cfg(&harq_ack, &pdsch_ack, &uci_cfg) < SRSRAN_SUCCESS) { + return false; + } + + // Generate configuration for SR + uint32_t sr_resource_id[SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES] = {}; + int n = srsran_ue_ul_nr_sr_send_slot(pucch.sr_resources, slot_cfg.idx, sr_resource_id); + if (n < SRSRAN_SUCCESS) { + ERROR("Calculating SR opportunities"); + return false; + } + + if (n > 0) { + uci_cfg.pucch.sr_resource_id = sr_resource_id[0]; + uci_cfg.o_sr = srsran_ra_ul_nr_nof_sr_bits((uint32_t)n); + uci_cfg.sr_positive_present = true; + } + + // Generate configuration for CSI reports + n = srsran_csi_reports_generate(&csi, &slot_cfg, uci_cfg.csi); + if (n > SRSRAN_SUCCESS) { + uci_cfg.nof_csi = (uint32_t)n; + } + + return true; +} + +bool phy_cfg_nr_t::get_pucch_uci_cfg(const srsran_slot_cfg_t& slot_cfg, + const srsran_uci_cfg_nr_t& uci_cfg, + srsran_pucch_nr_common_cfg_t& cfg, + srsran_pucch_nr_resource_t& resource) const +{ + // Select PUCCH resource + if (srsran_ra_ul_nr_pucch_resource(&pucch, &uci_cfg, carrier.nof_prb, &resource) < SRSRAN_SUCCESS) { + ERROR("Selecting PUCCH resource"); + return false; + } + + return true; +} + +bool phy_cfg_nr_t::get_pusch_uci_cfg(const srsran_slot_cfg_t& slot_cfg, + const srsran_uci_cfg_nr_t& uci_cfg, + srsran_sch_cfg_nr_t& pusch_cfg) const +{ + // Generate configuration for PUSCH + if (srsran_ra_ul_set_grant_uci_nr(&carrier, &pusch, &uci_cfg, &pusch_cfg) < SRSRAN_SUCCESS) { + return false; + } + + return true; +} + +srsran_ssb_cfg_t phy_cfg_nr_t::get_ssb_cfg() const +{ + // Retrieve band + srsran::srsran_band_helper bh = srsran::srsran_band_helper(); + uint16_t band = bh.get_band_from_dl_freq_Hz(carrier.dl_center_frequency_hz); + srsran_assert(band != UINT16_MAX, + "DL frequency %f MHz does not belong to any valid band", + carrier.dl_center_frequency_hz / 1e6); + + // Make SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.center_freq_hz = carrier.dl_center_frequency_hz; + ssb_cfg.ssb_freq_hz = carrier.ssb_center_freq_hz; + ssb_cfg.scs = ssb.scs; + ssb_cfg.pattern = bh.get_ssb_pattern(band, ssb.scs); + ssb_cfg.duplex_mode = duplex.mode; + ssb_cfg.periodicity_ms = ssb.periodicity_ms; + + srsran_assert(ssb_cfg.pattern != SRSRAN_SSB_PATTERN_INVALID, + "Invalid SSB pattern for band %d and SSB subcarrier spacing %s", + band, + srsran_subcarrier_spacing_to_str(ssb.scs)); + + return ssb_cfg; +} + +} // namespace srsran diff --git a/lib/src/common/phy_cfg_nr_default.cc b/lib/src/common/phy_cfg_nr_default.cc new file mode 100644 index 000000000..7951e3c8b --- /dev/null +++ b/lib/src/common/phy_cfg_nr_default.cc @@ -0,0 +1,514 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/string_helpers.h" +#include "srsran/srsran.h" +#include +#include + +namespace srsran { + +template +T inc(T v) +{ + return static_cast(static_cast(v) + 1); +} + +phy_cfg_nr_default_t::reference_cfg_t::reference_cfg_t(const std::string& args) +{ + std::list param_list = {}; + string_parse_list(args, ',', param_list); + for (std::string& e : param_list) { + std::list param = {}; + string_parse_list(e, '=', param); + + // Skip if size is invalid + if (param.size() != 2) { + srsran_assertion_failure("Invalid reference argument '%s'", e.c_str()); + } + + if (param.front() == "carrier") { + for (carrier = R_CARRIER_CUSTOM_10MHZ; carrier < R_CARRIER_COUNT; carrier = inc(carrier)) { + if (R_CARRIER_STRING[carrier] == param.back()) { + break; + } + } + srsran_assert(carrier != R_CARRIER_COUNT, "Invalid carrier reference configuration '%s'", param.back().c_str()); + } else if (param.front() == "duplex") { + for (duplex = R_DUPLEX_FDD; duplex < R_DUPLEX_COUNT; duplex = inc(duplex)) { + if (R_DUPLEX_STRING[duplex] == param.back()) { + break; + } + } + srsran_assert(duplex != R_DUPLEX_COUNT, "Invalid duplex reference configuration '%s'", param.back().c_str()); + } else if (param.front() == "pdsch") { + for (pdsch = R_PDSCH_DEFAULT; pdsch < R_PDSCH_COUNT; pdsch = inc(pdsch)) { + if (R_PDSCH_STRING[pdsch] == param.back()) { + break; + } + } + srsran_assert(pdsch != R_PDSCH_COUNT, "Invalid PDSCH reference configuration '%s'", param.back().c_str()); + } else { + srsran_assertion_failure("Invalid %s reference component", param.front().c_str()); + } + } +} + +void phy_cfg_nr_default_t::make_carrier_custom_10MHz(srsran_carrier_nr_t& carrier) +{ + carrier.nof_prb = 52; + carrier.max_mimo_layers = 1; + carrier.pci = 500; + carrier.dl_center_frequency_hz = 2.6e9; + carrier.ssb_center_freq_hz = carrier.dl_center_frequency_hz; + carrier.offset_to_carrier = 0; + carrier.scs = srsran_subcarrier_spacing_15kHz; +} + +void phy_cfg_nr_default_t::make_carrier_custom_20MHz(srsran_carrier_nr_t& carrier) +{ + carrier.nof_prb = 106; + carrier.max_mimo_layers = 1; + carrier.pci = 500; + carrier.dl_center_frequency_hz = 2.6e9; + carrier.ssb_center_freq_hz = carrier.dl_center_frequency_hz; + carrier.offset_to_carrier = 0; + carrier.scs = srsran_subcarrier_spacing_15kHz; +} + +void phy_cfg_nr_default_t::make_tdd_custom_6_4(srsran_duplex_config_nr_t& conf) +{ + // Set the duplex mode to TDD + conf.mode = SRSRAN_DUPLEX_MODE_TDD; + + // Select TDD config + srsran_tdd_config_nr_t& tdd = conf.tdd; + + // Initialise pattern + tdd = {}; + + // Enable pattern 1 + tdd.pattern1.period_ms = 10; + tdd.pattern1.nof_dl_slots = 6; + tdd.pattern1.nof_dl_symbols = 0; + tdd.pattern1.nof_ul_slots = 4; + tdd.pattern1.nof_ul_symbols = 0; + + // Disable pattern 2 + tdd.pattern2.period_ms = 0; +} + +void phy_cfg_nr_default_t::make_tdd_fr1_15_1(srsran_duplex_config_nr_t& conf) +{ // Set the duplex mode to TDD + conf.mode = SRSRAN_DUPLEX_MODE_TDD; + + // Select TDD config + srsran_tdd_config_nr_t& tdd = conf.tdd; + + // Initialise pattern + tdd = {}; + + // Enable pattern 1 + tdd.pattern1.period_ms = 5; + tdd.pattern1.nof_dl_slots = 3; + tdd.pattern1.nof_dl_symbols = 10; + tdd.pattern1.nof_ul_slots = 1; + tdd.pattern1.nof_ul_symbols = 2; + + // Disable pattern 2 + tdd.pattern2.period_ms = 0; +} + +void phy_cfg_nr_default_t::make_pdcch_custom_common_ss(srsran_pdcch_cfg_nr_t& pdcch, const srsran_carrier_nr_t& carrier) +{ + // Configure CORESET ID 1 + pdcch.coreset_present[1] = true; + pdcch.coreset[1].id = 1; + pdcch.coreset[1].duration = 1; + pdcch.coreset[1].mapping_type = srsran_coreset_mapping_type_non_interleaved; + pdcch.coreset[1].precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + + // Generate frequency resources for the full BW + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { + pdcch.coreset[1].freq_resources[i] = i < SRSRAN_FLOOR(carrier.nof_prb, 6); + } + + // Configure Search Space 1 as common + pdcch.search_space_present[1] = true; + pdcch.search_space[1].id = 1; + pdcch.search_space[1].coreset_id = 1; + pdcch.search_space[1].duration = 1; + pdcch.search_space[1].formats[0] = srsran_dci_format_nr_0_0; // DCI format for PUSCH + pdcch.search_space[1].formats[1] = srsran_dci_format_nr_1_0; // DCI format for PDSCH + pdcch.search_space[1].nof_formats = 2; + pdcch.search_space[1].type = srsran_search_space_type_common_3; + + // Generate 1 candidate for each aggregation level if possible + for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) { + pdcch.search_space[1].nof_candidates[L] = + SRSRAN_MIN(2, srsran_pdcch_nr_max_candidates_coreset(&pdcch.coreset[1], L)); + } + + pdcch.ra_search_space_present = true; + pdcch.ra_search_space = pdcch.search_space[1]; + pdcch.ra_search_space.type = srsran_search_space_type_common_1; +} + +void phy_cfg_nr_default_t::make_pdsch_default(srsran_sch_hl_cfg_nr_t& pdsch) +{ + // Select PDSCH time resource allocation + pdsch.common_time_ra[0].k = 0; + pdsch.common_time_ra[0].mapping_type = srsran_sch_mapping_type_A; + pdsch.common_time_ra[0].sliv = srsran_ra_type2_to_riv(SRSRAN_NSYMB_PER_SLOT_NR - 1, 1, SRSRAN_NSYMB_PER_SLOT_NR); + pdsch.nof_common_time_ra = 1; + + // Set contiguous PRBs as default + pdsch.alloc = srsran_resource_alloc_type1; + + // Setup PDSCH DMRS type A position + pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2; +} + +void make_nzp_csi_rs_ts38101_table_5_2_1(const srsran_carrier_nr_t& carrier, srsran_csi_rs_nzp_set_t& trs) +{ + // Set defaults + trs = {}; + + trs.trs_info = true; + trs.count = 4; + + srsran_csi_rs_nzp_resource_t& res1 = trs.data[0]; + srsran_csi_rs_nzp_resource_t& res2 = trs.data[1]; + srsran_csi_rs_nzp_resource_t& res3 = trs.data[2]; + srsran_csi_rs_nzp_resource_t& res4 = trs.data[3]; + + res1.resource_mapping.frequency_domain_alloc[0] = true; + res2.resource_mapping.frequency_domain_alloc[0] = true; + res3.resource_mapping.frequency_domain_alloc[0] = true; + res4.resource_mapping.frequency_domain_alloc[0] = true; + + res1.resource_mapping.first_symbol_idx = 6; + res2.resource_mapping.first_symbol_idx = 10; + res3.resource_mapping.first_symbol_idx = 6; + res4.resource_mapping.first_symbol_idx = 10; + + res1.resource_mapping.nof_ports = 1; + res2.resource_mapping.nof_ports = 1; + res3.resource_mapping.nof_ports = 1; + res4.resource_mapping.nof_ports = 1; + + res1.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + res2.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + res3.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + res4.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + + res1.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + res2.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + res3.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + res4.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + + if (carrier.scs == srsran_subcarrier_spacing_15kHz) { + res1.periodicity.period = 20; + res2.periodicity.period = 20; + res3.periodicity.period = 20; + res4.periodicity.period = 20; + + res1.periodicity.offset = 10; + res2.periodicity.offset = 10; + res3.periodicity.offset = 11; + res4.periodicity.offset = 11; + } else if (carrier.scs == srsran_subcarrier_spacing_30kHz) { + res1.periodicity.period = 40; + res2.periodicity.period = 40; + res3.periodicity.period = 40; + res4.periodicity.period = 40; + + res1.periodicity.offset = 20; + res2.periodicity.offset = 20; + res3.periodicity.offset = 21; + res4.periodicity.offset = 21; + } else { + srsran_assertion_failure("Invalid subcarrier spacing %d kHz", 15U << (uint32_t)carrier.scs); + } + + res1.resource_mapping.freq_band = {0, carrier.nof_prb}; + res2.resource_mapping.freq_band = {0, carrier.nof_prb}; + res3.resource_mapping.freq_band = {0, carrier.nof_prb}; + res4.resource_mapping.freq_band = {0, carrier.nof_prb}; +} + +void phy_cfg_nr_default_t::make_pdsch_2_1_1_tdd(const srsran_carrier_nr_t& carrier, srsran_sch_hl_cfg_nr_t& pdsch) +{ + // Select PDSCH time resource allocation + pdsch.common_time_ra[0].mapping_type = srsran_sch_mapping_type_A; + pdsch.common_time_ra[0].k = 0; + pdsch.common_time_ra[0].sliv = srsran_ra_type2_to_riv(SRSRAN_NSYMB_PER_SLOT_NR - 2, 2, SRSRAN_NSYMB_PER_SLOT_NR); + pdsch.nof_common_time_ra = 1; + + // Setup PDSCH DMRS + pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2; + pdsch.dmrs_typeA.present = true; + pdsch.dmrs_typeA.additional_pos = srsran_dmrs_sch_add_pos_2; + + // Make default CSI-RS for tracking from TS38101 Table 5.2.1 + make_nzp_csi_rs_ts38101_table_5_2_1(carrier, pdsch.nzp_csi_rs_sets[0]); +} + +void phy_cfg_nr_default_t::make_pusch_default(srsran_sch_hl_cfg_nr_t& pusch) +{ + // Select PUSCH time resource allocation + pusch.common_time_ra[0].k = 4; + pusch.common_time_ra[0].mapping_type = srsran_sch_mapping_type_A; + pusch.common_time_ra[0].sliv = srsran_ra_type2_to_riv(SRSRAN_NSYMB_PER_SLOT_NR, 0, SRSRAN_NSYMB_PER_SLOT_NR); + pusch.nof_common_time_ra = 1; + + // Setup PUSCH DMRS type A position + pusch.typeA_pos = srsran_dmrs_sch_typeA_pos_2; + + pusch.scaling = 1.0f; + pusch.beta_offsets.ack_index1 = 9; + pusch.beta_offsets.ack_index2 = 9; + pusch.beta_offsets.ack_index3 = 9; + pusch.beta_offsets.csi1_index1 = 6; + pusch.beta_offsets.csi1_index2 = 6; + pusch.beta_offsets.csi2_index1 = 6; + pusch.beta_offsets.csi2_index2 = 6; +} + +void phy_cfg_nr_default_t::make_pucch_custom_one(srsran_pucch_nr_hl_cfg_t& pucch) +{ + // PUCCH Resource for format 1 + srsran_pucch_nr_resource_t resource_small = {}; + resource_small.starting_prb = 0; + resource_small.format = SRSRAN_PUCCH_NR_FORMAT_1; + resource_small.initial_cyclic_shift = 0; + resource_small.nof_symbols = 14; + resource_small.start_symbol_idx = 0; + resource_small.time_domain_occ = 0; + + // PUCCH Resource for format 2 + srsran_pucch_nr_resource_t resource_big = {}; + resource_big.starting_prb = 51; + resource_big.format = SRSRAN_PUCCH_NR_FORMAT_2; + resource_big.nof_prb = 1; + resource_big.nof_symbols = 2; + resource_big.start_symbol_idx = 12; + + // Resource for SR + srsran_pucch_nr_resource_t resource_sr = {}; + resource_sr.starting_prb = 51; + resource_sr.format = SRSRAN_PUCCH_NR_FORMAT_1; + resource_sr.initial_cyclic_shift = 0; + resource_sr.nof_symbols = 14; + resource_sr.start_symbol_idx = 0; + resource_sr.time_domain_occ = 0; + + pucch.enabled = true; + + // Set format 1 for 1-2 bits + pucch.sets[0].resources[0] = resource_small; + pucch.sets[0].resources[1] = resource_small; + pucch.sets[0].resources[2] = resource_small; + pucch.sets[0].resources[3] = resource_small; + pucch.sets[0].resources[4] = resource_small; + pucch.sets[0].resources[5] = resource_small; + pucch.sets[0].resources[6] = resource_small; + pucch.sets[0].resources[7] = resource_small; + pucch.sets[0].nof_resources = 8; + + // Set format 2 for more bits + pucch.sets[1].resources[0] = resource_big; + pucch.sets[1].resources[1] = resource_big; + pucch.sets[1].resources[2] = resource_big; + pucch.sets[1].resources[3] = resource_big; + pucch.sets[1].resources[4] = resource_big; + pucch.sets[1].resources[5] = resource_big; + pucch.sets[1].resources[6] = resource_big; + pucch.sets[1].resources[7] = resource_big; + pucch.sets[1].nof_resources = 8; + + // Configure scheduling request + pucch.sr_resources[1].configured = true; + pucch.sr_resources[1].sr_id = 0; + pucch.sr_resources[1].period = 40; + pucch.sr_resources[1].offset = 8; + pucch.sr_resources[1].resource = resource_sr; +} + +void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq, + const srsran_carrier_nr_t& carrier, + const srsran_duplex_config_nr_t& duplex_cfg) +{ + if (duplex_cfg.mode == SRSRAN_DUPLEX_MODE_TDD) { + const srsran_tdd_config_nr_t& tdd_cfg = duplex_cfg.tdd; + + // Generate as many entries as DL slots + harq.nof_dl_data_to_ul_ack = SRSRAN_MIN(tdd_cfg.pattern1.nof_dl_slots, SRSRAN_MAX_NOF_DL_DATA_TO_UL); + if (tdd_cfg.pattern1.nof_dl_symbols > 0) { + harq.nof_dl_data_to_ul_ack++; + } + + // Set PDSCH to ACK timing delay to 4 or more + for (uint32_t n = 0; n < harq.nof_dl_data_to_ul_ack; n++) { + // Set the first slots into the first UL slot + if (harq.nof_dl_data_to_ul_ack >= 4 and n < (harq.nof_dl_data_to_ul_ack - 4)) { + harq.dl_data_to_ul_ack[n] = harq.nof_dl_data_to_ul_ack - n; + continue; + } + + // After that try if n+4 is UL slot + if (srsran_duplex_nr_is_ul(&duplex_cfg, carrier.scs, n + 4)) { + harq.dl_data_to_ul_ack[n] = 4; + continue; + } + + // Otherwise set delay to the first UL slot of the next TDD period + harq.dl_data_to_ul_ack[n] = (tdd_cfg.pattern1.period_ms + tdd_cfg.pattern1.nof_dl_slots) - n; + } + } else { + harq.dl_data_to_ul_ack[0] = 4; + harq.nof_dl_data_to_ul_ack = 1; + } + + // Zero the rest + for (uint32_t i = harq.nof_dl_data_to_ul_ack; i < SRSRAN_MAX_NOF_DL_DATA_TO_UL; i++) { + harq.dl_data_to_ul_ack[i] = 0; + } + + // Select dynamic HARQ-ACK codebook + harq.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; +} + +void phy_cfg_nr_default_t::make_prach_default_lte(srsran_prach_cfg_t& prach) +{ + prach.is_nr = true; + prach.config_idx = 8; + prach.root_seq_idx = 0; + prach.zero_corr_zone = 0; + prach.freq_offset = 4; + prach.num_ra_preambles = 64; + prach.hs_flag = false; +} + +phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) +{ + switch (reference_cfg.carrier) { + case reference_cfg_t::R_CARRIER_CUSTOM_10MHZ: + make_carrier_custom_10MHz(carrier); + break; + case reference_cfg_t::R_CARRIER_CUSTOM_20MHZ: + make_carrier_custom_20MHz(carrier); + break; + case reference_cfg_t::R_CARRIER_COUNT: + srsran_assertion_failure("Invalid carrier reference"); + } + + switch (reference_cfg.duplex) { + case reference_cfg_t::R_DUPLEX_FDD: + duplex.mode = SRSRAN_DUPLEX_MODE_FDD; + break; + case reference_cfg_t::R_DUPLEX_TDD_CUSTOM_6_4: + make_tdd_custom_6_4(duplex); + break; + case reference_cfg_t::R_DUPLEX_TDD_FR1_15_1: + make_tdd_fr1_15_1(duplex); + break; + case reference_cfg_t::R_DUPLEX_COUNT: + srsran_assertion_failure("Invalid TDD reference"); + } + + if (duplex.mode == SRSRAN_DUPLEX_MODE_TDD) { + carrier.dl_center_frequency_hz = 3513.6e6; + carrier.ul_center_frequency_hz = 3513.6e6; + ssb.scs = srsran_subcarrier_spacing_30kHz; + } else { + carrier.dl_center_frequency_hz = 881.5e6; + carrier.ul_center_frequency_hz = 836.6e6; + ssb.scs = srsran_subcarrier_spacing_15kHz; + } + carrier.ssb_center_freq_hz = carrier.dl_center_frequency_hz; + ssb.position_in_burst[0] = true; + ssb.periodicity_ms = 10; + + switch (reference_cfg.pdcch) { + case reference_cfg_t::R_PDCCH_CUSTOM_COMMON_SS: + make_pdcch_custom_common_ss(pdcch, carrier); + break; + } + + switch (reference_cfg.pdsch) { + case reference_cfg_t::R_PDSCH_DEFAULT: + make_pdsch_default(pdsch); + break; + case reference_cfg_t::R_PDSCH_TS38101_5_2_1: + make_pdsch_2_1_1_tdd(carrier, pdsch); + break; + case reference_cfg_t::R_PDSCH_COUNT: + srsran_assertion_failure("Invalid PDSCH reference configuration"); + } + + switch (reference_cfg.pusch) { + case reference_cfg_t::R_PUSCH_DEFAULT: + make_pusch_default(pusch); + break; + } + + switch (reference_cfg.pucch) { + case reference_cfg_t::R_PUCCH_CUSTOM_ONE: + make_pucch_custom_one(pucch); + break; + } + + switch (reference_cfg.harq) { + case reference_cfg_t::R_HARQ_AUTO: + make_harq_auto(harq_ack, carrier, duplex); + break; + } + + switch (reference_cfg.prach) { + case reference_cfg_t::R_PRACH_DEFAULT_LTE: + make_prach_default_lte(prach); + break; + } + + prach.tdd_config.configured = (duplex.mode == SRSRAN_DUPLEX_MODE_TDD); + + // Make default CSI report configuration always + csi.reports[0].channel_meas_id = 0; + csi.reports[0].type = SRSRAN_CSI_REPORT_TYPE_PERIODIC; + csi.reports[0].periodic.period = 20; + csi.reports[0].periodic.offset = 9; + csi.reports[0].periodic.resource.format = SRSRAN_PUCCH_NR_FORMAT_2; + csi.reports[0].periodic.resource.starting_prb = 51; + csi.reports[0].periodic.resource.format = SRSRAN_PUCCH_NR_FORMAT_2; + csi.reports[0].periodic.resource.nof_prb = 1; + csi.reports[0].periodic.resource.nof_symbols = 2; + csi.reports[0].periodic.resource.start_symbol_idx = 10; + csi.reports[0].quantity = SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI; + csi.reports[0].cqi_table = SRSRAN_CSI_CQI_TABLE_1; + csi.reports[0].freq_cfg = SRSRAN_CSI_REPORT_FREQ_WIDEBAND; + csi.csi_resources[0].type = srsran_csi_hl_resource_cfg_t::SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NZP_CSI_RS_SSB; + csi.csi_resources[0].nzp_csi_rs_ssb.nzp_csi_rs_resource_set_id_list[0] = 0; + csi.csi_resources[0].nzp_csi_rs_ssb.nzp_csi_rs_resource_set_id_list_count = 1; +} + +} // namespace srsran diff --git a/lib/src/common/rlc_pcap.cc b/lib/src/common/rlc_pcap.cc index 15d94e16d..862744640 100644 --- a/lib/src/common/rlc_pcap.cc +++ b/lib/src/common/rlc_pcap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,10 +31,10 @@ void rlc_pcap::enable(bool en) enable_write = true; } -void rlc_pcap::open(const char* filename, rlc_config_t config) +void rlc_pcap::open(const char* filename, const rlc_config_t& config) { fprintf(stdout, "Opening RLC PCAP with DLT=%d\n", UDP_DLT); - pcap_file = LTE_PCAP_Open(UDP_DLT, filename); + pcap_file = DLT_PCAP_Open(UDP_DLT, filename); enable_write = true; if (config.rlc_mode == rlc_mode_t::am) { @@ -54,7 +54,7 @@ void rlc_pcap::open(const char* filename, rlc_config_t config) void rlc_pcap::close() { fprintf(stdout, "Saving RLC PCAP file\n"); - LTE_PCAP_Close(pcap_file); + DLT_PCAP_Close(pcap_file); } void rlc_pcap::set_ue_id(uint16_t ue_id_) diff --git a/lib/src/common/rrc_common.cc b/lib/src/common/rrc_common.cc new file mode 100644 index 000000000..59df62204 --- /dev/null +++ b/lib/src/common/rrc_common.cc @@ -0,0 +1,60 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/rrc/rrc_common.h" + +namespace srsran { + +uint8_t rrc_value_to_range(quant_s quant, const float value) +{ + uint8_t range = 0; + if (quant == quant_rsrp) { + if (value < -140) { + range = 0; + } else if (value < -44) { + range = 1u + (uint8_t)(value + 140); + } else { + range = 97; + } + } else { + if (value < -19.5) { + range = 0; + } else if (value < -3) { + range = 1u + (uint8_t)(2 * (value + 19.5)); + } else { + range = 34; + } + } + return range; +} + +float rrc_range_to_value(quant_s quant, const uint8_t range) +{ + float val = 0; + if (quant == quant_rsrp) { + val = -140 + (float)range; + } else { + val = -19.5f + (float)range / 2; + } + return val; +} + +} // namespace srsran \ No newline at end of file diff --git a/lib/src/common/s1ap_pcap.cc b/lib/src/common/s1ap_pcap.cc index 833a4e244..532c94553 100644 --- a/lib/src/common/s1ap_pcap.cc +++ b/lib/src/common/s1ap_pcap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,23 +22,46 @@ #include "srsran/common/s1ap_pcap.h" #include "srsran/common/pcap.h" #include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" #include namespace srsran { +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} + +s1ap_pcap::s1ap_pcap() +{ + emergency_handler_id = add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} + +s1ap_pcap::~s1ap_pcap() +{ + if (emergency_handler_id > 0) { + remove_emergency_cleanup_handler(emergency_handler_id); + } +} + void s1ap_pcap::enable() { enable_write = true; } -void s1ap_pcap::open(const char* filename) +void s1ap_pcap::open(const char* filename_) { - pcap_file = LTE_PCAP_Open(S1AP_LTE_DLT, filename); + filename = filename_; + pcap_file = DLT_PCAP_Open(S1AP_LTE_DLT, filename.c_str()); enable_write = true; } void s1ap_pcap::close() { - fprintf(stdout, "Saving S1AP PCAP file\n"); - LTE_PCAP_Close(pcap_file); + if (!enable_write) { + return; + } + fprintf(stdout, "Saving S1AP PCAP file (DLT=%d) to %s\n", S1AP_LTE_DLT, filename.c_str()); + DLT_PCAP_Close(pcap_file); } void s1ap_pcap::write_s1ap(uint8_t* pdu, uint32_t pdu_len_bytes) diff --git a/lib/src/common/s3g.cc b/lib/src/common/s3g.cc index 12561c04a..6d5b58c4a 100644 --- a/lib/src/common/s3g.cc +++ b/lib/src/common/s3g.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/security.cc b/lib/src/common/security.cc index 214e1a7dc..6449971ee 100644 --- a/lib/src/common/security.cc +++ b/lib/src/common/security.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,119 +20,790 @@ */ #include "srsran/common/security.h" +#include "mbedtls/md5.h" #include "srsran/common/liblte_security.h" #include "srsran/common/s3g.h" +#include "srsran/common/ssl.h" #include "srsran/config.h" +#include -#ifdef HAVE_MBEDTLS -#include "mbedtls/md5.h" -#endif -#ifdef HAVE_POLARSSL -#include "polarssl/md5.h" -#endif +#define FC_EPS_K_ASME_DERIVATION 0x10 +#define FC_EPS_K_ENB_DERIVATION 0x11 +#define FC_EPS_NH_DERIVATION 0x12 +#define FC_EPS_K_ENB_STAR_DERIVATION 0x13 +#define FC_EPS_ALGORITHM_KEY_DERIVATION 0x15 +#define ALGO_EPS_DISTINGUISHER_NAS_ENC_ALG 0x01 +#define ALGO_EPS_DISTINGUISHER_NAS_INT_ALG 0x02 +#define ALGO_EPS_DISTINGUISHER_RRC_ENC_ALG 0x03 +#define ALGO_EPS_DISTINGUISHER_RRC_INT_ALG 0x04 +#define ALGO_EPS_DISTINGUISHER_UP_ENC_ALG 0x05 +#define ALGO_EPS_DISTINGUISHER_UP_INT_ALG 0x06 + +#define FC_5G_K_GNB_STAR_DERIVATION 0x70 +#define FC_5G_ALGORITHM_KEY_DERIVATION 0x69 +#define FC_5G_KAUSF_DERIVATION 0x6A +#define FC_5G_RES_STAR_DERIVATION 0x6B +#define FC_5G_KSEAF_DERIVATION 0x6C +#define FC_5G_KAMF_DERIVATION 0x6D +#define FC_5G_KGNB_KN3IWF_DERIVATION 0x6E +#define FC_5G_NH_GNB_DERIVATION 0x6F + +#define ALGO_5G_DISTINGUISHER_NAS_ENC_ALG 0x01 +#define ALGO_5G_DISTINGUISHER_NAS_INT_ALG 0x02 +#define ALGO_5G_DISTINGUISHER_RRC_ENC_ALG 0x03 +#define ALGO_5G_DISTINGUISHER_RRC_INT_ALG 0x04 +#define ALGO_5G_DISTINGUISHER_UP_ENC_ALG 0x05 +#define ALGO_5G_DISTINGUISHER_UP_INT_ALG 0x06 namespace srsran { /****************************************************************************** * Key Generation *****************************************************************************/ -uint8_t security_generate_k_asme(uint8_t* ck, - uint8_t* ik, - uint8_t* ak, - uint8_t* sqn, - uint16_t mcc, - uint16_t mnc, - uint8_t* k_asme) +uint8_t security_generate_k_asme(const uint8_t* ck, + const uint8_t* ik, + const uint8_t* ak_xor_sqn_, + const uint16_t mcc, + const uint16_t mnc, + uint8_t* k_asme) { - return liblte_security_generate_k_asme(ck, ik, ak, sqn, mcc, mnc, k_asme); + if (ck == NULL || ik == NULL || ak_xor_sqn_ == NULL || k_asme == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + // The input key Key shall be equal to the concatenation CK || IK of CK and IK. + memcpy(key.data(), ck, 16); + memcpy(key.data() + 16, ik, 16); + + // Serving Network id + std::vector sn_id; + sn_id.resize(3); + sn_id[0] = (mcc & 0x00F0) | ((mcc & 0x0F00) >> 8); // First byte of P0 + if ((mnc & 0xFF00) == 0xFF00) { + // 2-digit MNC + sn_id[1] = 0xF0 | (mcc & 0x000F); // Second byte of P0 + sn_id[2] = ((mnc & 0x000F) << 4) | ((mnc & 0x00F0) >> 4); // Third byte of P0 + } else { + // 3-digit MNC + sn_id[1] = ((mnc & 0x000F) << 4) | (mcc & 0x000F); // Second byte of P0 + sn_id[2] = ((mnc & 0x00F0)) | ((mnc & 0x0F00) >> 8); // Third byte of P0 + } + + // AK XOR SQN + std::vector ak_xor_sqn; + ak_xor_sqn.resize(AK_LEN); + memcpy(ak_xor_sqn.data(), ak_xor_sqn_, ak_xor_sqn.size()); + + uint8_t output[32]; + if (kdf_common(FC_EPS_K_ASME_DERIVATION, key, sn_id, ak_xor_sqn, output) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + + memcpy(k_asme, output, 32); + return SRSRAN_SUCCESS; } -uint8_t security_generate_k_enb(uint8_t* k_asme, uint32_t nas_count, uint8_t* k_enb) +uint8_t security_generate_k_ausf(const uint8_t* ck, + const uint8_t* ik, + const uint8_t* ak_xor_sqn_, + const char* serving_network_name, + uint8_t* k_ausf) { - return liblte_security_generate_k_enb(k_asme, nas_count, k_enb); + if (ck == NULL || ik == NULL || ak_xor_sqn_ == NULL || serving_network_name == NULL || k_ausf == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + + std::array key; + + // The input key Key shall be equal to the concatenation CK || IK of CK and IK. + memcpy(key.data(), ck, 16); + memcpy(key.data() + 16, ik, 16); + + // Serving Network Name + std::vector ssn; + ssn.resize(strlen(serving_network_name)); + memcpy(ssn.data(), serving_network_name, ssn.size()); + + // AK XOR SQN + std::vector ak_xor_sqn; + ak_xor_sqn.resize(AK_LEN); + memcpy(ak_xor_sqn.data(), ak_xor_sqn_, ak_xor_sqn.size()); + + uint8_t output[32]; + if (kdf_common(FC_5G_KAUSF_DERIVATION, key, ssn, ak_xor_sqn, output) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + memcpy(k_ausf, output, 32); + + return SRSRAN_SUCCESS; } -uint8_t security_generate_k_enb_star(uint8_t* k_enb, uint32_t pci, uint32_t earfcn, uint8_t* k_enb_star) +uint8_t security_generate_k_seaf(const uint8_t* k_ausf, const char* serving_network_name, uint8_t* k_seaf) { - return liblte_security_generate_k_enb_star(k_enb, pci, earfcn, k_enb_star); + if (k_ausf == NULL || serving_network_name == NULL || k_seaf == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + + std::array key; + memcpy(key.data(), k_ausf, 32); + + // Serving Network Name + std::vector ssn; + ssn.resize(strlen(serving_network_name)); + memcpy(ssn.data(), serving_network_name, ssn.size()); + + if (kdf_common(FC_5G_KSEAF_DERIVATION, key, ssn, k_seaf) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } -uint8_t security_generate_nh(uint8_t* k_asme, uint8_t* sync, uint8_t* nh) +uint8_t security_generate_k_amf(const uint8_t* k_seaf, + const char* supi_, + const uint8_t* abba_, + const uint32_t abba_len, + uint8_t* k_amf) { - return liblte_security_generate_nh(k_asme, sync, nh); + if (k_seaf == NULL || supi_ == NULL || abba_ == NULL || k_amf == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + + std::array key; + memcpy(key.data(), k_seaf, 32); + + // SUPI + std::vector supi; + supi.resize(strlen(supi_)); + memcpy(supi.data(), supi_, supi.size()); + + // ABBA + std::vector abba; + abba.resize(abba_len); + memcpy(abba.data(), abba_, abba.size()); + + if (kdf_common(FC_5G_KAMF_DERIVATION, key, supi, abba, k_amf) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } -uint8_t security_generate_k_nas(uint8_t* k_asme, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_nas_enc, - uint8_t* k_nas_int) +uint8_t security_generate_k_gnb(const as_key_t& k_amf, const uint32_t nas_count_, as_key_t& k_gnb) { - return liblte_security_generate_k_nas(k_asme, - (LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM)enc_alg_id, - (LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM)int_alg_id, - k_nas_enc, - k_nas_int); + if (k_amf.empty()) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + + // NAS Count + std::vector nas_count; + nas_count.resize(4); + nas_count[0] = (nas_count_ >> 24) & 0xFF; + nas_count[1] = (nas_count_ >> 16) & 0xFF; + nas_count[2] = (nas_count_ >> 8) & 0xFF; + nas_count[3] = nas_count_ & 0xFF; + + // Access Type Distinguisher 3GPP access = 0x01 (TS 33501 Annex A.9) + std::vector access_type_distinguisher = {1}; + + if (kdf_common(FC_5G_KGNB_KN3IWF_DERIVATION, k_amf, nas_count, access_type_distinguisher, k_gnb.data()) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } -uint8_t security_generate_k_rrc(uint8_t* k_enb, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_rrc_enc, - uint8_t* k_rrc_int) +uint8_t security_generate_k_enb(const uint8_t* k_asme, const uint32_t nas_count_, uint8_t* k_enb) { - return liblte_security_generate_k_rrc(k_enb, - (LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM)enc_alg_id, - (LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM)int_alg_id, - k_rrc_enc, - k_rrc_int); + if (k_asme == NULL || k_enb == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + memcpy(key.data(), k_asme, 32); + + // NAS Count + std::vector nas_count; + nas_count.resize(4); + nas_count[0] = (nas_count_ >> 24) & 0xFF; + nas_count[1] = (nas_count_ >> 16) & 0xFF; + nas_count[2] = (nas_count_ >> 8) & 0xFF; + nas_count[3] = nas_count_ & 0xFF; + + if (kdf_common(FC_EPS_K_ENB_DERIVATION, key, nas_count, k_enb) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } -uint8_t security_generate_k_up(uint8_t* k_enb, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_up_enc, - uint8_t* k_up_int) +// Generate k_enb* according to TS 33.401 Appendix A.5 +uint8_t +security_generate_k_enb_star(const uint8_t* k_enb, const uint32_t pci_, const uint32_t earfcn_, uint8_t* k_enb_star) + { - return liblte_security_generate_k_up(k_enb, - (LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM)enc_alg_id, - (LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM)int_alg_id, - k_up_enc, - k_up_int); + return security_generate_k_nb_star_common(FC_EPS_K_ENB_STAR_DERIVATION, k_enb, pci_, earfcn_, k_enb_star); } -uint8_t security_generate_k_nr_rrc(uint8_t* k_gnb, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_rrc_enc, - uint8_t* k_rrc_int) +// Generate k_gnb* according to TS 33.501 Appendix A.11 +uint8_t +security_generate_k_gnb_star(const uint8_t* k_gnb, const uint32_t pci_, const uint32_t dl_arfcn_, uint8_t* k_gnb_star) + { - return liblte_security_generate_k_nr_rrc(k_gnb, - (LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM)enc_alg_id, - (LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM)int_alg_id, - k_rrc_enc, - k_rrc_int); + return security_generate_k_nb_star_common(FC_5G_K_GNB_STAR_DERIVATION, k_gnb, pci_, dl_arfcn_, k_gnb_star); } -uint8_t security_generate_k_nr_up(uint8_t* k_gnb, - CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, - INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, - uint8_t* k_up_enc, - uint8_t* k_up_int) +uint8_t security_generate_k_nb_star_common(uint8_t fc, + const uint8_t* k_enb, + const uint32_t pci_, + const uint32_t earfcn_, + uint8_t* k_enb_star) { - return liblte_security_generate_k_nr_up(k_gnb, - (LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_ENUM)enc_alg_id, - (LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_ENUM)int_alg_id, - k_up_enc, - k_up_int); + if (k_enb == NULL || k_enb_star == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + memcpy(key.data(), k_enb, 32); + + // PCI + std::vector pci; + pci.resize(2); + pci[0] = (pci_ >> 8) & 0xFF; + pci[1] = pci_ & 0xFF; + + // ARFCN, can be two or three bytes + std::vector earfcn; + if (earfcn_ < pow(2, 16)) { + earfcn.resize(2); + earfcn[0] = (earfcn_ >> 8) & 0xFF; + earfcn[1] = earfcn_ & 0xFF; + } else if (earfcn_ < pow(2, 24)) { + earfcn.resize(3); + earfcn[0] = (earfcn_ >> 16) & 0xFF; + earfcn[1] = (earfcn_ >> 8) & 0xFF; + earfcn[2] = earfcn_ & 0xFF; + } + + if (kdf_common(fc, key, pci, earfcn, k_enb_star) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } -uint8_t security_generate_sk_gnb(uint8_t* k_enb, uint8_t* sk_gnb, uint16_t scg_count) +uint8_t security_generate_nh(const uint8_t* k_asme, const uint8_t* sync_, uint8_t* nh) { - return liblte_security_generate_sk_gnb(k_enb, sk_gnb, scg_count); + if (k_asme == NULL || sync_ == NULL || nh == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + memcpy(key.data(), k_asme, 32); + + // PCI + std::vector sync; + sync.resize(32); + memcpy(sync.data(), sync_, 32); + + if (kdf_common(FC_EPS_NH_DERIVATION, key, sync, nh) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } +uint8_t security_generate_k_nas(const uint8_t* k_asme, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_nas_enc, + uint8_t* k_nas_int) +{ + if (k_asme == NULL || k_nas_enc == NULL || k_nas_int == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + memcpy(key.data(), k_asme, 32); + + // Derive NAS ENC + // algorithm type distinguisher + std::vector algo_distinguisher; + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_EPS_DISTINGUISHER_NAS_ENC_ALG; + + // algorithm type distinguisher + std::vector algorithm_identity; + algorithm_identity.resize(1); + algorithm_identity[0] = enc_alg_id; + + if (kdf_common(FC_EPS_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_nas_enc) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + + // Derive NAS INT + // algorithm type distinguisher + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_EPS_DISTINGUISHER_NAS_INT_ALG; + + // algorithm type distinguisher + algorithm_identity.resize(1); + algorithm_identity[0] = int_alg_id; + + // Derive NAS int + if (kdf_common(FC_EPS_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_nas_int) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + +uint8_t security_generate_k_nas_5g(const uint8_t* k_amf, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_nas_enc, + uint8_t* k_nas_int) +{ + if (k_amf == NULL || k_nas_enc == NULL || k_nas_int == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + memcpy(key.data(), k_amf, 32); + + // Derive NAS ENC + // algorithm type distinguisher + std::vector algo_distinguisher; + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_5G_DISTINGUISHER_NAS_ENC_ALG; + + // algorithm type distinguisher + std::vector algorithm_identity; + algorithm_identity.resize(1); + algorithm_identity[0] = enc_alg_id; + + if (kdf_common(FC_5G_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_nas_enc) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + + // Derive NAS INT + // algorithm type distinguisher + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_5G_DISTINGUISHER_NAS_INT_ALG; + + // algorithm type distinguisher + algorithm_identity.resize(1); + algorithm_identity[0] = int_alg_id; + + // Derive NAS int + if (kdf_common(FC_5G_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_nas_int) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + +uint8_t security_generate_k_rrc(const uint8_t* k_enb, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_rrc_enc, + uint8_t* k_rrc_int) +{ + if (k_enb == NULL || k_rrc_enc == NULL || k_rrc_int == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + memcpy(key.data(), k_enb, 32); + + // Derive RRC ENC + // algorithm type distinguisher + std::vector algo_distinguisher; + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_EPS_DISTINGUISHER_RRC_ENC_ALG; + + // algorithm type distinguisher + std::vector algorithm_identity; + algorithm_identity.resize(1); + algorithm_identity[0] = enc_alg_id; + + if (kdf_common(FC_EPS_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_rrc_enc) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + + // Derive RRC INT + // algorithm type distinguisher + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_EPS_DISTINGUISHER_RRC_INT_ALG; + + // algorithm type distinguisher + algorithm_identity.resize(1); + algorithm_identity[0] = int_alg_id; + + // Derive RRC int + if (kdf_common(FC_EPS_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_rrc_int) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + +uint8_t security_generate_k_up(const uint8_t* k_enb, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_up_enc, + uint8_t* k_up_int) +{ + if (k_enb == NULL || k_up_enc == NULL || k_up_int == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + memcpy(key.data(), k_enb, 32); + + // Derive UP ENC + // algorithm type distinguisher + std::vector algo_distinguisher; + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_EPS_DISTINGUISHER_UP_ENC_ALG; + + // algorithm type distinguisher + std::vector algorithm_identity; + algorithm_identity.resize(1); + algorithm_identity[0] = enc_alg_id; + + if (kdf_common(FC_EPS_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_up_enc) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + + // Derive UP INT + // algorithm type distinguisher + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_EPS_DISTINGUISHER_UP_INT_ALG; + + // algorithm type distinguisher + algorithm_identity.resize(1); + algorithm_identity[0] = int_alg_id; + + // Derive UP int + if (kdf_common(FC_EPS_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_up_int) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + +uint8_t security_generate_sk_gnb(const uint8_t* k_enb, const uint16_t scg_count_, uint8_t* sk_gnb) +{ + if (k_enb == NULL || sk_gnb == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + memcpy(key.data(), k_enb, 32); + + // SCG Count + std::vector scg_count; + scg_count.resize(2); + scg_count[0] = (scg_count_ >> 8) & 0xFF; // first byte of P0 + scg_count[1] = scg_count_ & 0xFF; // second byte of P0 + + // Derive sk_gnb + uint8_t output[32]; + if (kdf_common(0x1C, key, scg_count, output) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + memcpy(sk_gnb, output, 32); + + return SRSRAN_SUCCESS; +} + +uint8_t security_generate_k_nr_rrc(const uint8_t* k_gnb, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_rrc_enc, + uint8_t* k_rrc_int) +{ + if (k_gnb == NULL || k_rrc_enc == NULL || k_rrc_int == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + memcpy(key.data(), k_gnb, 32); + + // Derive RRC ENC + // algorithm type distinguisher + std::vector algo_distinguisher; + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_5G_DISTINGUISHER_RRC_ENC_ALG; + + // algorithm type distinguisher + std::vector algorithm_identity; + algorithm_identity.resize(1); + algorithm_identity[0] = enc_alg_id; + + if (kdf_common(FC_5G_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_rrc_enc) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + + // Derive RRC INT + // algorithm type distinguisher + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_5G_DISTINGUISHER_RRC_INT_ALG; + + // algorithm type distinguisher + algorithm_identity.resize(1); + algorithm_identity[0] = int_alg_id; + + // Derive RRC int + if (kdf_common(FC_5G_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_rrc_int) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + +uint8_t security_generate_k_nr_up(const uint8_t* k_gnb, + const CIPHERING_ALGORITHM_ID_ENUM enc_alg_id, + const INTEGRITY_ALGORITHM_ID_ENUM int_alg_id, + uint8_t* k_up_enc, + uint8_t* k_up_int) +{ + if (k_gnb == NULL || k_up_enc == NULL || k_up_int == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + memcpy(key.data(), k_gnb, 32); + + // Derive UP ENC + // algorithm type distinguisher + std::vector algo_distinguisher; + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_5G_DISTINGUISHER_UP_ENC_ALG; + + // algorithm type distinguisher + std::vector algorithm_identity; + algorithm_identity.resize(1); + algorithm_identity[0] = enc_alg_id; + + if (kdf_common(FC_5G_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_up_enc) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + + // Derive UP INT + // algorithm type distinguisher + algo_distinguisher.resize(1); + algo_distinguisher[0] = ALGO_5G_DISTINGUISHER_UP_INT_ALG; + + // algorithm type distinguisher + algorithm_identity.resize(1); + algorithm_identity[0] = int_alg_id; + + // Derive UP int + if (kdf_common(FC_5G_ALGORITHM_KEY_DERIVATION, key, algo_distinguisher, algorithm_identity, k_up_int) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + +uint8_t security_generate_res_star(const uint8_t* ck, + const uint8_t* ik, + const char* serving_network_name, + const uint8_t* rand_, + const uint8_t* res_, + const size_t res_len_, + uint8_t* res_star) +{ + if (ck == NULL || ik == NULL || serving_network_name == NULL || rand_ == NULL || res_ == NULL || res_star == NULL) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + std::array key; + + // The input key Key shall be equal to the concatenation CK || IK of CK and IK. + memcpy(key.data(), ck, 16); + memcpy(key.data() + 16, ik, 16); + + // Serving Network Name + std::vector ssn; + ssn.resize(strlen(serving_network_name)); + memcpy(ssn.data(), serving_network_name, strlen(serving_network_name)); + + // RAND + std::vector rand; + rand.resize(AKA_RAND_LEN); + memcpy(rand.data(), rand_, rand.size()); + + // RES + std::vector res; + res.resize(res_len_); + memcpy(res.data(), res_, res.size()); + + uint8_t output[32]; + if (kdf_common(FC_5G_RES_STAR_DERIVATION, key, ssn, rand, res, output) != SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + memcpy(res_star, output + 16, 16); + + return SRSRAN_SUCCESS; +} + +int kdf_common(const uint8_t fc, const std::array& key, const std::vector& P0, uint8_t* output) +{ + uint8_t* s; + uint32_t s_len = 1 + P0.size() + 2; + + s = (uint8_t*)calloc(s_len, sizeof(uint8_t)); + + if (s == nullptr) { + log_error("Unable to allocate memory in %s()", __FUNCTION__); + return SRSRAN_ERROR; + } + + uint32_t i = 0; + s[i] = fc; // FC + i++; + + // P0 + memcpy(&s[i], P0.data(), P0.size()); + i += P0.size(); + uint16_t p0_length_value = htons(P0.size()); + memcpy(&s[i], &p0_length_value, sizeof(p0_length_value)); + i += sizeof(p0_length_value); + + sha256(key.data(), key.size(), s, i, output, 0); + free(s); + + return SRSRAN_SUCCESS; +} + +int kdf_common(const uint8_t fc, + const std::array& key, + const std::vector& P0, + const std::vector& P1, + uint8_t* output) +{ + uint8_t* s; + uint32_t s_len = 1 + P0.size() + 2 + P1.size() + 2; + + s = (uint8_t*)calloc(s_len, sizeof(uint8_t)); + + if (s == nullptr) { + log_error("Unable to allocate memory in %s()", __FUNCTION__); + return SRSRAN_ERROR; + } + + uint32_t i = 0; + s[i] = fc; // FC + i++; + + // P0 + memcpy(&s[i], P0.data(), P0.size()); + i += P0.size(); + uint16_t p0_length_value = htons(P0.size()); + memcpy(&s[i], &p0_length_value, sizeof(p0_length_value)); + i += sizeof(p0_length_value); + + // P1 + memcpy(&s[i], P1.data(), P1.size()); + i += P1.size(); + uint16_t p1_length_value = htons(P1.size()); + memcpy(&s[i], &p1_length_value, sizeof(p1_length_value)); + i += sizeof(p1_length_value); + + sha256(key.data(), key.size(), s, i, output, 0); + free(s); + + return SRSRAN_SUCCESS; +} + +int kdf_common(const uint8_t fc, + const std::array& key, + const std::vector& P0, + const std::vector& P1, + const std::vector& P2, + uint8_t* output) +{ + uint8_t* s; + uint32_t s_len = 1 + P0.size() + 2 + P1.size() + 2 + P2.size() + 2; + + s = (uint8_t*)calloc(s_len, sizeof(uint8_t)); + + if (s == nullptr) { + log_error("Unable to allocate memory in %s()", __FUNCTION__); + return SRSRAN_ERROR; + } + + uint32_t i = 0; + s[i] = fc; // FC + i++; + + // P0 + memcpy(&s[i], P0.data(), P0.size()); + i += P0.size(); + uint16_t p0_length_value = htons(P0.size()); + memcpy(&s[i], &p0_length_value, sizeof(p0_length_value)); + i += sizeof(p0_length_value); + + // P1 + memcpy(&s[i], P1.data(), P1.size()); + i += P1.size(); + uint16_t p1_length_value = htons(P1.size()); + memcpy(&s[i], &p1_length_value, sizeof(p1_length_value)); + i += sizeof(p1_length_value); + + // P2 + memcpy(&s[i], P2.data(), P2.size()); + i += P2.size(); + uint16_t p2_length_value = htons(P2.size()); + memcpy(&s[i], &p2_length_value, sizeof(p2_length_value)); + i += sizeof(p2_length_value); + + sha256(key.data(), key.size(), s, i, output, 0); + free(s); + + return SRSRAN_SUCCESS; +} /****************************************************************************** * Integrity Protection *****************************************************************************/ @@ -173,12 +844,7 @@ uint8_t security_128_eia3(const uint8_t* key, uint8_t security_md5(const uint8_t* input, size_t len, uint8_t* output) { memset(output, 0x00, 16); -#ifdef HAVE_MBEDTLS mbedtls_md5(input, len, output); -#endif // HAVE_MBEDTLS -#ifdef HAVE_POLARSSL - md5(input, len, output); -#endif return SRSRAN_SUCCESS; } @@ -194,7 +860,6 @@ uint8_t security_128_eea1(uint8_t* key, uint32_t msg_len, uint8_t* msg_out) { - return liblte_security_encryption_eea1(key, count, bearer, direction, msg, msg_len * 8, msg_out); } @@ -206,7 +871,6 @@ uint8_t security_128_eea2(uint8_t* key, uint32_t msg_len, uint8_t* msg_out) { - return liblte_security_encryption_eea2(key, count, bearer, direction, msg, msg_len * 8, msg_out); } @@ -218,7 +882,6 @@ uint8_t security_128_eea3(uint8_t* key, uint32_t msg_len, uint8_t* msg_out) { - return liblte_security_encryption_eea3(key, count, bearer, direction, msg, msg_len * 8, msg_out); } @@ -251,4 +914,46 @@ uint8_t security_milenage_f5_star(uint8_t* k, uint8_t* op, uint8_t* rand, uint8_ return liblte_security_milenage_f5_star(k, op, rand, ak); } +int security_xor_f2345(uint8_t* k, uint8_t* rand, uint8_t* res, uint8_t* ck, uint8_t* ik, uint8_t* ak) +{ + uint8_t xdout[16]; + uint8_t cdout[8]; + // Use RAND and K to compute RES, CK, IK and AK + for (uint32_t i = 0; i < 16; i++) { + xdout[i] = k[i] ^ rand[i]; + } + for (uint32_t i = 0; i < 16; i++) { + res[i] = xdout[i]; + ck[i] = xdout[(i + 1) % 16]; + ik[i] = xdout[(i + 2) % 16]; + } + for (uint32_t i = 0; i < 6; i++) { + ak[i] = xdout[i + 3]; + } + return SRSRAN_SUCCESS; +} + +int security_xor_f1(uint8_t* k, uint8_t* rand, uint8_t* sqn, uint8_t* amf, uint8_t* mac_a) +{ + uint8_t xdout[16]; + uint8_t cdout[8]; + // Use RAND and K to compute RES, CK, IK and AK + for (uint32_t i = 0; i < 16; i++) { + xdout[i] = k[i] ^ rand[i]; + } + // Generate cdout + for (uint32_t i = 0; i < 6; i++) { + cdout[i] = sqn[i]; + } + for (uint32_t i = 0; i < 2; i++) { + cdout[6 + i] = amf[i]; + } + + // Generate MAC + for (uint32_t i = 0; i < 8; i++) { + mac_a[i] = xdout[i] ^ cdout[i]; + } + return SRSRAN_SUCCESS; +} + } // namespace srsran diff --git a/lib/src/common/standard_streams.cc b/lib/src/common/standard_streams.cc index c783e74e7..f965b3551 100644 --- a/lib/src/common/standard_streams.cc +++ b/lib/src/common/standard_streams.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/test/CMakeLists.txt b/lib/src/common/test/CMakeLists.txt index d15edb174..6a2a4e33b 100644 --- a/lib/src/common/test/CMakeLists.txt +++ b/lib/src/common/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/common/test/band_helper_test.cc b/lib/src/common/test/band_helper_test.cc index 03439b0bc..5f69ebc19 100644 --- a/lib/src/common/test/band_helper_test.cc +++ b/lib/src/common/test/band_helper_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,34 +29,60 @@ int bands_test_nr() TESTASSERT(bands.nr_arfcn_to_freq(632628) == 3489.42e6); TESTASSERT(bands.nr_arfcn_to_freq(633928) == 3508.92e6); // default refPointA TESTASSERT(bands.nr_arfcn_to_freq(634240) == 3513.6e6); // default ARFCN with freq divisible by 11.52 MHz - // b28 b67 + // n28 n67 TESTASSERT(bands.nr_arfcn_to_freq(140600) == 703.0e6); TESTASSERT(bands.nr_arfcn_to_freq(145800) == 729.0e6); TESTASSERT(bands.nr_arfcn_to_freq(153600) == 768.0e6); TESTASSERT(bands.nr_arfcn_to_freq(157600) == 788.0e6); - // b20 + // n20 TESTASSERT(bands.nr_arfcn_to_freq(158200) == 791.0e6); TESTASSERT(bands.nr_arfcn_to_freq(160200) == 801.0e6); TESTASSERT(bands.nr_arfcn_to_freq(162200) == 811.0e6); TESTASSERT(bands.nr_arfcn_to_freq(168400) == 842.0e6); - // b32 b75 + // n32 n75 TESTASSERT(bands.nr_arfcn_to_freq(290400) == 1452.0e6); TESTASSERT(bands.nr_arfcn_to_freq(294400) == 1472.0e6); - // b3 - TESTASSERT(bands.nr_arfcn_to_freq(342000) == 1710.0e6); - TESTASSERT(bands.nr_arfcn_to_freq(348000) == 1740.0e6); - TESTASSERT(bands.nr_arfcn_to_freq(361000) == 1805.0e6); - TESTASSERT(bands.nr_arfcn_to_freq(376000) == 1880.0e6); - // b1 + + // check actual freqs for FDD carrier (example values are for 52 PRB) + TESTASSERT(bands.get_center_freq_from_abs_freq_point_a(52, 175364) == 881.5e6); + TESTASSERT(bands.get_center_freq_from_abs_freq_point_a(52, 166364) == 836.5e6); + // n1 TESTASSERT(bands.nr_arfcn_to_freq(384000) == 1920.0e6); TESTASSERT(bands.nr_arfcn_to_freq(388030) == 1940.15e6); TESTASSERT(bands.nr_arfcn_to_freq(391830) == 1959.15e6); TESTASSERT(bands.nr_arfcn_to_freq(434000) == 2170.0e6); - // b7 b38 + // n3 + TESTASSERT(bands.get_duplex_mode(3) == SRSRAN_DUPLEX_MODE_FDD); + TESTASSERT(bands.nr_arfcn_to_freq(342000) == 1710.0e6); + TESTASSERT(bands.nr_arfcn_to_freq(348000) == 1740.0e6); + TESTASSERT(bands.nr_arfcn_to_freq(361000) == 1805.0e6); + TESTASSERT_EQ(1842.5e6, bands.nr_arfcn_to_freq(368500)); + TESTASSERT(bands.nr_arfcn_to_freq(376000) == 1880.0e6); + TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 368500) == 367564); + TESTASSERT(bands.get_abs_freq_ssb_arfcn(3, srsran_subcarrier_spacing_15kHz, 367564) > 367924); + TESTASSERT_EQ(368410, bands.get_abs_freq_ssb_arfcn(3, srsran_subcarrier_spacing_15kHz, 367564, 12)); + // n5 + TESTASSERT(bands.get_duplex_mode(5) == SRSRAN_DUPLEX_MODE_FDD); + TESTASSERT(bands.nr_arfcn_to_freq(176300) == 881.5e6); + TESTASSERT(bands.freq_to_nr_arfcn(881.5e6) == 176300); + TESTASSERT(bands.get_ul_arfcn_from_dl_arfcn(176300) == 167300); + TESTASSERT(bands.nr_arfcn_to_freq(167300) == 836.5e6); + TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 176300) == 175364); + TESTASSERT(bands.get_abs_freq_ssb_arfcn(5, srsran_subcarrier_spacing_15kHz, 175364) > 175724); + // n7 n38 + TESTASSERT(bands.get_duplex_mode(7) == SRSRAN_DUPLEX_MODE_FDD); TESTASSERT(bands.nr_arfcn_to_freq(500000) == 2500.0e6); TESTASSERT(bands.nr_arfcn_to_freq(508000) == 2540.0e6); TESTASSERT(bands.nr_arfcn_to_freq(522000) == 2610.0e6); TESTASSERT(bands.nr_arfcn_to_freq(538000) == 2690.0e6); + TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 529920) == 528984); + TESTASSERT(bands.get_abs_freq_ssb_arfcn(7, srsran_subcarrier_spacing_15kHz, 528984) > 529344); + + // n78 + TESTASSERT(bands.get_duplex_mode(78) == SRSRAN_DUPLEX_MODE_TDD); + TESTASSERT(bands.nr_arfcn_to_freq(634240) == 3513.6e6); + TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 634240) == 633928); + TESTASSERT(bands.get_abs_freq_ssb_arfcn(78, srsran_subcarrier_spacing_30kHz, 633928) > 634168); const uint32_t max_valid_nr_arfcn = 3279165; diff --git a/lib/src/common/test/thread_pool_test.cc b/lib/src/common/test/thread_pool_test.cc index 6579fbba0..f5a65899c 100644 --- a/lib/src/common/test/thread_pool_test.cc +++ b/lib/src/common/test/thread_pool_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/test/thread_test.cc b/lib/src/common/test/thread_test.cc index 7ae7e5aab..0ee5ede2d 100644 --- a/lib/src/common/test/thread_test.cc +++ b/lib/src/common/test/thread_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/thread_pool.cc b/lib/src/common/thread_pool.cc index 692770c50..f1cbe171f 100644 --- a/lib/src/common/thread_pool.cc +++ b/lib/src/common/thread_pool.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -50,24 +50,29 @@ void thread_pool::worker::setup(uint32_t id, thread_pool* parent, uint32_t prio, void thread_pool::worker::run_thread() { - set_name(std::string("WORKER") + std::to_string(my_id)); - while (my_parent->status[my_id] != STOP) { + set_name(my_parent->get_id() + std::string("WORKER") + std::to_string(my_id)); + while (running.load(std::memory_order_relaxed)) { wait_to_start(); - if (my_parent->status[my_id] != STOP) { + if (running.load(std::memory_order_relaxed)) { work_imp(); finished(); } } } +void thread_pool::worker::stop() +{ + running = false; +} + uint32_t thread_pool::worker::get_id() { return my_id; } -thread_pool::thread_pool(uint32_t max_workers_) : workers(max_workers_), status(max_workers_), cvar_worker(max_workers_) +thread_pool::thread_pool(uint32_t max_workers_, std::string id_) : + workers(max_workers_), max_workers(max_workers_), status(max_workers_), cvar_worker(max_workers_), id(id_) { - max_workers = max_workers_; for (uint32_t i = 0; i < max_workers; i++) { workers[i] = NULL; status[i] = IDLE; @@ -101,6 +106,7 @@ void thread_pool::stop() for (uint32_t i = 0; i < nof_workers; i++) { if (workers[i]) { debug_thread("stop(): stopping %d\n", i); + workers[i]->stop(); status[i] = STOP; cvar_worker[i].notify_all(); cvar_queue.notify_all(); @@ -256,6 +262,11 @@ uint32_t thread_pool::get_nof_workers() return nof_workers; } +std::string thread_pool::get_id() +{ + return id; +} + /************************************************************************** * task_thread_pool - uses a queue to enqueue callables, that start * once a worker is available @@ -390,6 +401,75 @@ void task_thread_pool::worker_t::run_thread() running = false; } +task_worker::task_worker(std::string thread_name_, + uint32_t queue_size, + bool start_deferred, + int32_t prio_, + uint32_t mask_) : + thread(std::move(thread_name_)), + prio(prio_), + mask(mask_), + pending_tasks(queue_size), + logger(srslog::fetch_basic_logger("POOL")) +{ + if (not start_deferred) { + start(prio_, mask_); + } +} + +task_worker::~task_worker() +{ + stop(); +} + +void task_worker::stop() +{ + if (not pending_tasks.is_stopped()) { + pending_tasks.stop(); + wait_thread_finish(); + } +} + +void task_worker::start(int32_t prio_, uint32_t mask_) +{ + prio = prio_; + mask = mask_; + + if (mask == 255) { + thread::start(prio); + } else { + thread::start_cpu_mask(prio, mask); + } +} + +void task_worker::push_task(task_t&& task) +{ + auto ret = pending_tasks.try_push(std::move(task)); + if (ret.is_error()) { + logger.error("Cannot push anymore tasks into the worker queue. maximum size is %u", + uint32_t(pending_tasks.max_size())); + return; + } +} + +uint32_t task_worker::nof_pending_tasks() const +{ + return pending_tasks.size(); +} + +void task_worker::run_thread() +{ + while (true) { + bool success; + task_t task = pending_tasks.pop_blocking(&success); + if (not success) { + break; + } + task(); + } + logger.info("Task worker %s finished.", thread::get_name().c_str()); +} + // Global thread pool for long, low-priority tasks task_thread_pool& get_background_workers() { diff --git a/lib/src/common/threads.c b/lib/src/common/threads.c index 018ea7d40..a590690c3 100644 --- a/lib/src/common/threads.c +++ b/lib/src/common/threads.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -89,7 +89,9 @@ bool threads_new_rt_cpu(pthread_t* thread, void* (*start_routine)(void*), void* #else // All threads have normal priority except prio_offset=0,1,2,3,4 if (prio_offset >= 0 && prio_offset < 5) { - param.sched_priority = sched_get_priority_max(SCHED_FIFO) - prio_offset; + // Subtract one to the priority offset to avoid scheduling threads with the highest priority that could contend with + // OS critical tasks. + param.sched_priority = sched_get_priority_max(SCHED_FIFO) - prio_offset - 1; if (pthread_attr_init(&attr)) { perror("pthread_attr_init"); } else { @@ -145,21 +147,25 @@ bool threads_new_rt_cpu(pthread_t* thread, void* (*start_routine)(void*), void* } } +// TSAN seems to have issues with thread attributes when running as normal user, disable them in that case +#if HAVE_TSAN + attr_enable = false; +#endif + int err = pthread_create(thread, attr_enable ? &attr : NULL, start_routine, arg); if (err) { if (EPERM == err) { - // Join failed thread for avoiding memory leak from previous trial - pthread_join(*thread, NULL); - - perror("Warning: Failed to create thread with real-time priority. Creating it with normal priority"); + fprintf(stderr, + "Warning: Failed to create thread with real-time priority. Creating it with normal priority: %s\n", + strerror(err)); err = pthread_create(thread, NULL, start_routine, arg); if (err) { - perror("pthread_create"); + fprintf(stderr, "Error: Failed to create thread with normal priority: %s\n", strerror(err)); } else { ret = true; } } else { - perror("pthread_create"); + fprintf(stderr, "Error: Failed to create thread with real-time priority: %s\n", strerror(err)); } } else { ret = true; diff --git a/lib/src/common/time_prof.cc b/lib/src/common/time_prof.cc index 86ee2dc66..97e7a162e 100644 --- a/lib/src/common/time_prof.cc +++ b/lib/src/common/time_prof.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/tti_sync_cv.cc b/lib/src/common/tti_sync_cv.cc index 4acfa01cc..04409029d 100644 --- a/lib/src/common/tti_sync_cv.cc +++ b/lib/src/common/tti_sync_cv.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/version.c b/lib/src/common/version.c index 89e0a7c97..1b9b45cce 100644 --- a/lib/src/common/version.c +++ b/lib/src/common/version.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/common/zuc.cc b/lib/src/common/zuc.cc index ae6f18ca4..1d3bcc91d 100644 --- a/lib/src/common/zuc.cc +++ b/lib/src/common/zuc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,7 @@ #define ROT(a, k) (((a) << k) | ((a) >> (32 - k))) /* the s-boxes */ -u8 S0[256] = { +static const u8 S0[256] = { 0x3e, 0x72, 0x5b, 0x47, 0xca, 0xe0, 0x00, 0x33, 0x04, 0xd1, 0x54, 0x98, 0x09, 0xb9, 0x6d, 0xcb, 0x7b, 0x1b, 0xf9, 0x32, 0xaf, 0x9d, 0x6a, 0xa5, 0xb8, 0x2d, 0xfc, 0x1d, 0x08, 0x53, 0x03, 0x90, 0x4d, 0x4e, 0x84, 0x99, 0xe4, 0xce, 0xd9, 0x91, 0xdd, 0xb6, 0x85, 0x48, 0x8b, 0x29, 0x6e, 0xac, 0xcd, 0xc1, 0xf8, 0x1e, 0x73, 0x43, 0x69, 0xc6, 0xb5, @@ -53,7 +53,7 @@ u8 S0[256] = { 0x25, 0x05, 0x3f, 0x0c, 0x30, 0xea, 0x70, 0xb7, 0xa1, 0xe8, 0xa9, 0x65, 0x8d, 0x27, 0x1a, 0xdb, 0x81, 0xb3, 0xa0, 0xf4, 0x45, 0x7a, 0x19, 0xdf, 0xee, 0x78, 0x34, 0x60}; -u8 S1[256] = { +static const u8 S1[256] = { 0x55, 0xc2, 0x63, 0x71, 0x3b, 0xc8, 0x47, 0x86, 0x9f, 0x3c, 0xda, 0x5b, 0x29, 0xaa, 0xfd, 0x77, 0x8c, 0xc5, 0x94, 0x0c, 0xa6, 0x1a, 0x13, 0x00, 0xe3, 0xa8, 0x16, 0x72, 0x40, 0xf9, 0xf8, 0x42, 0x44, 0x26, 0x68, 0x96, 0x81, 0xd9, 0x45, 0x3e, 0x10, 0x76, 0xc6, 0xa7, 0x8b, 0x39, 0x43, 0xe1, 0x3a, 0xb5, 0x56, 0x2a, 0xc0, 0x6d, 0xb3, 0x05, 0x22, @@ -70,22 +70,22 @@ u8 S1[256] = { 0xd7, 0xb0, 0x25, 0xac, 0xaf, 0x12, 0x03, 0xe2, 0xf2}; /* the constants D */ -u32 EK_d[16] = {0x44D7, - 0x26BC, - 0x626B, - 0x135E, - 0x5789, - 0x35E2, - 0x7135, - 0x09AF, - 0x4D78, - 0x2F13, - 0x6BC4, - 0x1AF1, - 0x5E26, - 0x3C4D, - 0x789A, - 0x47AC}; +static const u32 EK_d[16] = {0x44D7, + 0x26BC, + 0x626B, + 0x135E, + 0x5789, + 0x35E2, + 0x7135, + 0x09AF, + 0x4D78, + 0x2F13, + 0x6BC4, + 0x1AF1, + 0x5E26, + 0x3C4D, + 0x789A, + 0x47AC}; /* ——————————————————————- */ /* c = a + b mod (2^31 – 1) */ diff --git a/lib/src/gtpu/CMakeLists.txt b/lib/src/gtpu/CMakeLists.txt new file mode 100644 index 000000000..eacaa70b1 --- /dev/null +++ b/lib/src/gtpu/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES gtpu.cc) + +add_library(srsran_gtpu STATIC ${SOURCES}) +target_link_libraries(srsran_gtpu srsran_common srsran_asn1 ${ATOMIC_LIBS}) +install(TARGETS srsran_gtpu DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/upper/gtpu.cc b/lib/src/gtpu/gtpu.cc similarity index 86% rename from lib/src/upper/gtpu.cc rename to lib/src/gtpu/gtpu.cc index 91bd90ac1..3f88611fe 100644 --- a/lib/src/upper/gtpu.cc +++ b/lib/src/gtpu/gtpu.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -121,17 +121,25 @@ bool gtpu_read_ext_header(srsran::byte_buffer_t* pdu, return true; } - if (header->next_ext_hdr_type == GTPU_EXT_HEADER_PDCP_PDU_NUMBER) { - pdu->msg += HEADER_PDCP_PDU_NUMBER_SIZE; - pdu->N_bytes -= HEADER_PDCP_PDU_NUMBER_SIZE; - header->ext_buffer.resize(HEADER_PDCP_PDU_NUMBER_SIZE); - for (size_t i = 0; i < HEADER_PDCP_PDU_NUMBER_SIZE; ++i) { - header->ext_buffer[i] = **ptr; - (*ptr)++; - } - } else { - logger.error("gtpu_read_header - Unhandled GTP-U Extension Header Type: 0x%x", header->next_ext_hdr_type); - return false; + // TODO: Iterate over next headers until no more extension headers + switch (header->next_ext_hdr_type) { + case GTPU_EXT_HEADER_PDCP_PDU_NUMBER: + pdu->msg += HEADER_PDCP_PDU_NUMBER_SIZE; + pdu->N_bytes -= HEADER_PDCP_PDU_NUMBER_SIZE; + header->ext_buffer.resize(HEADER_PDCP_PDU_NUMBER_SIZE); + for (size_t i = 0; i < HEADER_PDCP_PDU_NUMBER_SIZE; ++i) { + header->ext_buffer[i] = **ptr; + (*ptr)++; + } + break; + case GTPU_EXT_HEADER_PDU_SESSION_CONTAINER: + pdu->msg += GTPU_EXT_HEADER_PDU_SESSION_CONTAINER_LEN; + pdu->N_bytes -= GTPU_EXT_HEADER_PDU_SESSION_CONTAINER_LEN; + // TODO: Save Header Extension + break; + default: + logger.error("gtpu_read_header - Unhandled GTP-U Extension Header Type: 0x%x", header->next_ext_hdr_type); + return false; } return true; } diff --git a/lib/src/mac/CMakeLists.txt b/lib/src/mac/CMakeLists.txt index c9f34d3c9..9fe5f92d8 100644 --- a/lib/src/mac/CMakeLists.txt +++ b/lib/src/mac/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -22,4 +22,6 @@ SET(SOURCES pdu.cc pdu_queue.cc mac_sch_pdu_nr.cc mac_rar_pdu_nr.cc) add_library(srsran_mac STATIC ${SOURCES}) target_link_libraries(srsran_mac srsran_common) -INSTALL(TARGETS srsran_mac DESTINATION ${LIBRARY_DIR}) \ No newline at end of file +install(TARGETS srsran_mac DESTINATION ${LIBRARY_DIR} OPTIONAL) + +add_subdirectory(test) \ No newline at end of file diff --git a/lib/src/mac/mac_rar_pdu_nr.cc b/lib/src/mac/mac_rar_pdu_nr.cc index 342d33499..47054e7af 100644 --- a/lib/src/mac/mac_rar_pdu_nr.cc +++ b/lib/src/mac/mac_rar_pdu_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -43,7 +43,6 @@ bool mac_rar_subpdu_nr::read_subpdu(const uint8_t* ptr) rapid = *ptr & 0x3f; // if PDU is not configured with SI request, extract MAC RAR if (parent->has_si_rapid() == false) { - const uint32_t MAC_RAR_NBYTES = 7; if (parent->get_remaining_len() >= MAC_RAR_NBYTES) { uint8_t* rar = const_cast(ptr + 1); // check reserved bits @@ -90,9 +89,42 @@ bool mac_rar_subpdu_nr::has_more_subpdus() } // Section 6.1.2 -uint32_t mac_rar_subpdu_nr::write_subpdu(const uint8_t* start_) +void mac_rar_subpdu_nr::write_subpdu(const uint8_t* start_) { - return 0; + uint8_t* ptr = const_cast(start_); + + if (type == RAPID) { + // write E/T/RAPID MAC subheader + *ptr = (uint8_t)((E_bit ? 1 : 0) << 7) | ((int)rar_subh_type_t::RAPID << 6) | ((uint8_t)rapid & 0x3f); + ptr += 1; + + // if PDU is not configured with SI request, insert MAC RAR + if (parent->has_si_rapid() == false) { + // high 7 bits of TA go into first octet + *ptr = (uint8_t)((ta >> 5) & 0x7f); + ptr += 1; + + // low 5 bit of TA and first 3 bit of UL grant + *ptr = (uint8_t)((ta & 0x1f) << 3) | (ul_grant.at(0) << 2) | (ul_grant.at(1) << 1) | (ul_grant.at(2)); + ptr += 1; + + // add remaining 3 full octets of UL grant + uint8_t* x = &ul_grant.at(3); + *(ptr + 0) = (uint8_t)srsran_bit_pack(&x, 8); + *(ptr + 1) = (uint8_t)srsran_bit_pack(&x, 8); + *(ptr + 2) = (uint8_t)srsran_bit_pack(&x, 8); + ptr += 3; + + // 2 byte C-RNTI + *(ptr + 0) = (uint8_t)((temp_crnti & 0xff00) >> 8); + *(ptr + 1) = (uint8_t)(temp_crnti & 0x00ff); + ptr += 2; + } + } else { + // write E/T/R/R/BI MAC subheader + *ptr = (uint8_t)((E_bit ? 1 : 0) << 7) | ((int)rar_subh_type_t::BACKOFF << 6) | ((uint8_t)backoff_indicator & 0xf); + ptr += 1; + } } uint32_t mac_rar_subpdu_nr::get_total_length() @@ -127,6 +159,8 @@ bool mac_rar_subpdu_nr::has_backoff() const void mac_rar_subpdu_nr::set_backoff(const uint8_t backoff_indicator_) { + type = rar_subh_type_t::BACKOFF; + payload_length = 0; backoff_indicator = backoff_indicator_; } @@ -140,28 +174,103 @@ std::array mac_rar_subpdu_nr::get_ul return ul_grant; } -std::string mac_rar_subpdu_nr::to_string() +void mac_rar_subpdu_nr::set_ta(const uint32_t ta_) { - std::stringstream ss; + ta = ta_; +} + +void mac_rar_subpdu_nr::set_temp_crnti(const uint16_t temp_crnti_) +{ + temp_crnti = temp_crnti_; +} + +void mac_rar_subpdu_nr::set_rapid(const uint8_t rapid_) +{ + type = rar_subh_type_t::RAPID; + payload_length = MAC_RAR_NBYTES; + rapid = rapid_; +} + +void mac_rar_subpdu_nr::set_ul_grant(std::array ul_grant_) +{ + ul_grant = ul_grant_; +} + +void mac_rar_subpdu_nr::set_is_last_subpdu() +{ + E_bit = false; +} + +void mac_rar_subpdu_nr::to_string(fmt::memory_buffer& buffer) +{ + // Add space for new subPDU + fmt::format_to(buffer, " "); + if (has_rapid()) { - ss << "RAPID: " << rapid << ", Temp C-RNTI: " << std::hex << temp_crnti << ", TA: " << ta << ", UL Grant: "; + char tmp[16] = {}; + srsran_vec_sprint_hex(tmp, sizeof(tmp), ul_grant.data(), UL_GRANT_NBITS); + fmt::format_to(buffer, "RAPID: {}, Temp C-RNTI: {:#04x}, TA: {}, UL Grant: {}", rapid, temp_crnti, ta, tmp); } else { - ss << "Backoff Indicator: " << backoff_indicator << " "; + fmt::format_to(buffer, "Backoff Indicator: {}", backoff_indicator); } - - char tmp[16] = {}; - srsran_vec_sprint_hex(tmp, sizeof(tmp), ul_grant.data(), UL_GRANT_NBITS); - ss << tmp; - - return ss.str(); } mac_rar_pdu_nr::mac_rar_pdu_nr() : logger(srslog::fetch_basic_logger("MAC-NR")) {} -bool mac_rar_pdu_nr::pack() +int mac_rar_pdu_nr::init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_) { - // not implemented yet - return false; + if (buffer_ == nullptr || buffer_->msg == nullptr) { + logger.error("Invalid buffer"); + return SRSRAN_ERROR; + } + buffer = buffer_; + buffer->N_bytes = 0; + subpdus.clear(); + pdu_len = pdu_len_; + remaining_len = pdu_len_; + return SRSRAN_SUCCESS; +} + +mac_rar_subpdu_nr& mac_rar_pdu_nr::add_subpdu() +{ + mac_rar_subpdu_nr rar_subpdu(this); + subpdus.push_back(rar_subpdu); + return subpdus.back(); +} + +int mac_rar_pdu_nr::pack() +{ + int ret = SRSRAN_ERROR; + if (buffer == nullptr) { + logger.error("Invalid buffer"); + return ret; + } + + // set E_bit for last subPDU + subpdus.back().set_is_last_subpdu(); + + // write subPDUs one by one + for (uint32_t i = 0; i < subpdus.size(); ++i) { + mac_rar_subpdu_nr& subpdu = subpdus.at(i); + if (remaining_len >= subpdu.get_total_length()) { + subpdu.write_subpdu(buffer->msg + buffer->N_bytes); + buffer->N_bytes += subpdu.get_total_length(); + remaining_len -= subpdu.get_total_length(); + } else { + logger.error("Not enough space in PDU to write subPDU"); + return ret; + } + } + + // fill up with padding, if any + if (remaining_len > 0) { + memset(buffer->msg + buffer->N_bytes, 0, remaining_len); + buffer->N_bytes += remaining_len; + } + + ret = SRSRAN_SUCCESS; + + return ret; } bool mac_rar_pdu_nr::has_si_rapid() @@ -221,13 +330,12 @@ uint32_t mac_rar_pdu_nr::get_remaining_len() return remaining_len; } -std::string mac_rar_pdu_nr::to_string() +void mac_rar_pdu_nr::to_string(fmt::memory_buffer& buffer) { - std::stringstream ss; + fmt::format_to(buffer, "DL"); for (auto& subpdu : subpdus) { - ss << subpdu.to_string() << " "; + subpdu.to_string(buffer); } - return ss.str(); } } // namespace srsran diff --git a/lib/src/mac/mac_sch_pdu_nr.cc b/lib/src/mac/mac_sch_pdu_nr.cc index c2da80b9c..ffb4340f2 100644 --- a/lib/src/mac/mac_sch_pdu_nr.cc +++ b/lib/src/mac/mac_sch_pdu_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,10 +32,21 @@ mac_sch_subpdu_nr::nr_lcid_sch_t mac_sch_subpdu_nr::get_type() return CCCH; } -bool mac_sch_subpdu_nr::is_sdu() +bool mac_sch_subpdu_nr::is_sdu() const { - // for UL-SCH LCID 52 is also valid for carrying SDUs - return (lcid <= 32 || (parent->is_ulsch() && lcid == 52)); + // UL-CCCH handling in done as CE + return (lcid <= 32 && !is_ul_ccch()); +} + +bool mac_sch_subpdu_nr::has_length_field() +{ + // CCCH (both versions) don't have a length field in the UL + if (parent->is_ulsch()) { + if (lcid == CCCH_SIZE_48 || lcid == CCCH_SIZE_64) { + return false; + } + } + return (is_sdu() || is_var_len_ce(lcid)); } // returns false for all reserved values in Table 6.2.1-1 and 6.2.1-2 @@ -44,9 +55,21 @@ bool mac_sch_subpdu_nr::is_valid_lcid() return (lcid <= 63 && ((parent->is_ulsch() && (lcid <= 32 || lcid >= 52)) || (lcid <= 32 || lcid >= 47))); } -bool mac_sch_subpdu_nr::is_var_len_ce() +bool mac_sch_subpdu_nr::is_var_len_ce(uint32_t lcid) { - return false; + if (parent->is_ulsch()) { + // UL fixed-size CE + switch (lcid) { + case LONG_TRUNC_BSR: + case LONG_BSR: + return true; + default: + return false; + } + } else { + // all currently supported CEs in the DL are fixed-size + return false; + } } // return length of PDU (or SRSRAN_ERROR otherwise) @@ -59,7 +82,7 @@ int32_t mac_sch_subpdu_nr::read_subheader(const uint8_t* ptr) header_length = 1; if (is_valid_lcid()) { - if ((is_sdu() || is_var_len_ce()) && not is_ul_ccch()) { + if (has_length_field()) { // Read first length byte sdu_length = (uint32_t)*ptr; ptr++; @@ -74,7 +97,7 @@ int32_t mac_sch_subpdu_nr::read_subheader(const uint8_t* ptr) } else { sdu_length = sizeof_ce(lcid, parent->is_ulsch()); } - sdu = (uint8_t*)ptr; + sdu.set_storage_to((uint8_t*)ptr); } else { srslog::fetch_basic_logger("MAC-NR").warning("Invalid LCID (%d) in MAC PDU", lcid); return SRSRAN_ERROR; @@ -84,8 +107,9 @@ int32_t mac_sch_subpdu_nr::read_subheader(const uint8_t* ptr) void mac_sch_subpdu_nr::set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_) { - lcid = lcid_; - sdu = const_cast(payload_); + // Use CCCH_SIZE_48 when SDU len fits + lcid = (lcid_ == CCCH_SIZE_64 && len_ == sizeof_ce(CCCH_SIZE_48, true)) ? CCCH_SIZE_48 : lcid_; + sdu.set_storage_to(const_cast(payload_)); header_length = is_ul_ccch() ? 1 : 2; sdu_length = len_; if (is_ul_ccch()) { @@ -113,39 +137,51 @@ void mac_sch_subpdu_nr::set_padding(const uint32_t len_) // Turn a subPDU into a C-RNTI CE, error checking takes place in the caller void mac_sch_subpdu_nr::set_c_rnti(const uint16_t crnti_) { - lcid = CRNTI; - header_length = 1; - sdu_length = sizeof_ce(lcid, parent->is_ulsch()); - sdu = ce_write_buffer.data(); - uint16_t crnti = htole32(crnti_); - ce_write_buffer.at(0) = (uint8_t)((crnti & 0xff00) >> 8); - ce_write_buffer.at(1) = (uint8_t)((crnti & 0x00ff)); + lcid = CRNTI; + header_length = 1; + sdu_length = sizeof_ce(lcid, parent->is_ulsch()); + uint16_t crnti = htole16(crnti_); + uint8_t* ptr = sdu.use_internal_storage(); + ptr[0] = (uint8_t)((crnti & 0xff00) >> 8); + ptr[1] = (uint8_t)((crnti & 0x00ff)); } // Turn a subPDU into a single entry PHR CE, error checking takes place in the caller void mac_sch_subpdu_nr::set_se_phr(const uint8_t phr_, const uint8_t pcmax_) { - lcid = SE_PHR; - header_length = 1; - sdu_length = sizeof_ce(lcid, parent->is_ulsch()); - sdu = ce_write_buffer.data(); - ce_write_buffer.at(0) = (uint8_t)(phr_ & 0x3f); - ce_write_buffer.at(1) = (uint8_t)(pcmax_ & 0x3f); + lcid = SE_PHR; + header_length = 1; + sdu_length = sizeof_ce(lcid, parent->is_ulsch()); + uint8_t* ptr = sdu.use_internal_storage(); + ptr[0] = (uint8_t)(phr_ & 0x3f); + ptr[1] = (uint8_t)(pcmax_ & 0x3f); } // Turn a subPDU into a single short BSR void mac_sch_subpdu_nr::set_sbsr(const lcg_bsr_t bsr_) { - lcid = SHORT_BSR; - header_length = 1; - sdu_length = sizeof_ce(lcid, parent->is_ulsch()); - sdu = ce_write_buffer.data(); - ce_write_buffer.at(0) = ((bsr_.lcg_id & 0x07) << 5) | (bsr_.buffer_size & 0x1f); + lcid = SHORT_BSR; + header_length = 1; + sdu_length = sizeof_ce(lcid, parent->is_ulsch()); + uint8_t* ptr = sdu.use_internal_storage(); + ptr[0] = ((bsr_.lcg_id & 0x07) << 5) | (bsr_.buffer_size & 0x1f); } // Turn a subPDU into a long BSR with variable size void mac_sch_subpdu_nr::set_lbsr(const std::array bsr_) {} +// Turn subPDU into a Con +void mac_sch_subpdu_nr::set_ue_con_res_id_ce(const mac_sch_subpdu_nr::ue_con_res_id_t id) +{ + lcid = CON_RES_ID; + header_length = 1; + sdu_length = sizeof_ce(lcid, parent->is_ulsch()); + uint8_t* ptr = sdu.use_internal_storage(); + for (int32_t i = 0; i < sdu_length; ++i) { + ptr[i] = id.at(i); + } +} + // Section 6.1.2 uint32_t mac_sch_subpdu_nr::write_subpdu(const uint8_t* start_) { @@ -172,7 +208,7 @@ uint32_t mac_sch_subpdu_nr::write_subpdu(const uint8_t* start_) // copy SDU payload if (sdu) { - memcpy(ptr, sdu, sdu_length); + memcpy(ptr, sdu.ptr(), sdu_length); } else { // clear memory memset(ptr, 0, sdu_length); @@ -184,30 +220,36 @@ uint32_t mac_sch_subpdu_nr::write_subpdu(const uint8_t* start_) return ptr - start_; } -uint32_t mac_sch_subpdu_nr::get_total_length() +uint32_t mac_sch_subpdu_nr::get_total_length() const { return (header_length + sdu_length); } -uint32_t mac_sch_subpdu_nr::get_sdu_length() +uint32_t mac_sch_subpdu_nr::get_sdu_length() const { return sdu_length; } -uint32_t mac_sch_subpdu_nr::get_lcid() +uint32_t mac_sch_subpdu_nr::get_lcid() const { return lcid; } uint8_t* mac_sch_subpdu_nr::get_sdu() { - return sdu; + return sdu.ptr(); } -uint16_t mac_sch_subpdu_nr::get_c_rnti() +const uint8_t* mac_sch_subpdu_nr::get_sdu() const +{ + return sdu.ptr(); +} + +uint16_t mac_sch_subpdu_nr::get_c_rnti() const { if (parent->is_ulsch() && lcid == CRNTI) { - return le16toh((uint16_t)sdu[0] << 8 | sdu[1]); + const uint8_t* ptr = sdu.ptr(); + return le16toh((uint16_t)ptr[0] << 8 | ptr[1]); } return 0; } @@ -215,7 +257,8 @@ uint16_t mac_sch_subpdu_nr::get_c_rnti() uint8_t mac_sch_subpdu_nr::get_phr() { if (parent->is_ulsch() && lcid == SE_PHR) { - return sdu[0] & 0x3f; + uint8_t* ptr = sdu.ptr(); + return ptr[0] & 0x3f; } return 0; } @@ -223,7 +266,8 @@ uint8_t mac_sch_subpdu_nr::get_phr() uint8_t mac_sch_subpdu_nr::get_pcmax() { if (parent->is_ulsch() && lcid == SE_PHR) { - return sdu[1] & 0x3f; + uint8_t* ptr = sdu.ptr(); + return ptr[1] & 0x3f; } return 0; } @@ -232,22 +276,83 @@ mac_sch_subpdu_nr::ta_t mac_sch_subpdu_nr::get_ta() { ta_t ta = {}; if (lcid == TA_CMD) { - ta.tag_id = (sdu[0] & 0xc0) >> 6; - ta.ta_command = sdu[0] & 0x3f; + uint8_t* ptr = sdu.ptr(); + ta.tag_id = (ptr[0] & 0xc0) >> 6; + ta.ta_command = ptr[0] & 0x3f; } return ta; } -mac_sch_subpdu_nr::lcg_bsr_t mac_sch_subpdu_nr::get_sbsr() +mac_sch_subpdu_nr::lcg_bsr_t mac_sch_subpdu_nr::get_sbsr() const { lcg_bsr_t sbsr = {}; - if (parent->is_ulsch() && lcid == SHORT_BSR) { - sbsr.lcg_id = (sdu[0] & 0xe0) >> 5; - sbsr.buffer_size = sdu[0] & 0x1f; + if (parent->is_ulsch() && (lcid == SHORT_BSR || lcid == SHORT_TRUNC_BSR)) { + const uint8_t* ptr = sdu.ptr(); + sbsr.lcg_id = (ptr[0] & 0xe0) >> 5; + sbsr.buffer_size = ptr[0] & 0x1f; } return sbsr; } +mac_sch_subpdu_nr::lbsr_t mac_sch_subpdu_nr::get_lbsr() const +{ + lbsr_t lbsr = {}; + lbsr.list.reserve(mac_sch_subpdu_nr::max_num_lcg_lbsr); + + if (parent->is_ulsch() && (lcid == LONG_BSR || lcid == LONG_TRUNC_BSR)) { + const uint8_t* ptr = sdu.ptr(); + lbsr.bitmap = *ptr; // read LCG bitmap + ptr++; // skip LCG bitmap + + // early stop if LBSR is empty + if (lbsr.bitmap == 0) { + return lbsr; + } + + int bsr_cnt = 0; + for (int i = 0; i < mac_sch_subpdu_nr::max_num_lcg_lbsr; i++) { + // If LCGi bit is enabled, it means the next 8-bit BSR value corresponds to it + if (lbsr.bitmap & (0x1 << i)) { + lcg_bsr_t bsr = {}; + bsr.lcg_id = i; + // For the Long truncated, some BSR words can be not present, assume BSR > 0 in that case + if (1 + bsr_cnt < sdu_length) { + bsr.buffer_size = ptr[bsr_cnt]; + bsr_cnt++; + } else if (lcid == LONG_TRUNC_BSR) { + bsr.buffer_size = 63; // just assume it has 526 bytes to transmit + } else { + fprintf(stderr, "Error parsing LongBSR CE: sdu_length=%d but there are %d active bsr\n", sdu_length, bsr_cnt); + } + lbsr.list.push_back(bsr); + } + } + } + + return lbsr; +} + +mac_sch_subpdu_nr::ue_con_res_id_t mac_sch_subpdu_nr::get_ue_con_res_id_ce() +{ + mac_sch_subpdu_nr::ue_con_res_id_t id = {}; + if (!parent->is_ulsch() && lcid == CON_RES_ID) { + const uint8_t* ptr = sdu.ptr(); + memcpy(id.data(), ptr, id.size()); + } + return id; +} + +uint64_t mac_sch_subpdu_nr::get_ue_con_res_id_ce_packed() +{ + if (!parent->is_ulsch() && lcid == CON_RES_ID) { + const uint8_t* payload = sdu.ptr(); + return le64toh(((uint64_t)payload[5]) | (((uint64_t)payload[4]) << 8) | (((uint64_t)payload[3]) << 16) | + (((uint64_t)payload[2]) << 24) | (((uint64_t)payload[1]) << 32) | (((uint64_t)payload[0]) << 40)); + } else { + return 0; + } +} + uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul) { if (is_ul) { @@ -258,12 +363,14 @@ uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul) return 8; case CRNTI: return 2; - case SHORT_TRUNC_BSR: - return 1; case SHORT_BSR: + case SHORT_TRUNC_BSR: return 1; case SE_PHR: return 2; + case LONG_BSR: + case LONG_TRUNC_BSR: + return 1; // minimum size, could be more than that case PADDING: return 0; } @@ -282,11 +389,84 @@ uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul) return 0; } -inline bool mac_sch_subpdu_nr::is_ul_ccch() +bool mac_sch_subpdu_nr::is_ul_ccch() const { return (parent->is_ulsch() && (lcid == CCCH_SIZE_48 || lcid == CCCH_SIZE_64)); } +void mac_sch_subpdu_nr::to_string(fmt::memory_buffer& buffer) +{ + // print subPDU + if (is_sdu()) { + fmt::format_to(buffer, " LCID={} len={}", get_lcid(), get_sdu_length()); + } else { + if (parent->is_ulsch()) { + // UL-SCH case + switch (get_lcid()) { + case mac_sch_subpdu_nr::CCCH_SIZE_48: + fmt::format_to(buffer, " CCCH48: len={}", get_sdu_length()); + break; + case mac_sch_subpdu_nr::CCCH_SIZE_64: + fmt::format_to(buffer, " CCCH64: len={}", get_sdu_length()); + break; + case mac_sch_subpdu_nr::CRNTI: + fmt::format_to(buffer, " C-RNTI: {:#04x}", get_c_rnti()); + break; + case mac_sch_subpdu_nr::SHORT_TRUNC_BSR: + fmt::format_to(buffer, " SHORT_TRUNC_BSR: len={}", get_total_length()); + break; + case mac_sch_subpdu_nr::LONG_TRUNC_BSR: + fmt::format_to(buffer, " LONG_TRUNC_BSR: len={}", get_total_length()); + break; + case mac_sch_subpdu_nr::SHORT_BSR: { + lcg_bsr_t sbsr = get_sbsr(); + fmt::format_to(buffer, " SBSR: lcg={} bs={}", sbsr.lcg_id, sbsr.buffer_size); + } break; + case mac_sch_subpdu_nr::LONG_BSR: { + mac_sch_subpdu_nr::lbsr_t lbsr = get_lbsr(); + fmt::format_to(buffer, " LBSR: bitmap={:#02x}", lbsr.bitmap); + for (const auto& lcg : lbsr.list) { + fmt::format_to(buffer, " lcg={} bs={}", lcg.lcg_id, lcg.buffer_size); + } + } break; + case mac_sch_subpdu_nr::SE_PHR: + fmt::format_to(buffer, " SE_PHR: ph={} pc={}", get_phr(), get_pcmax()); + break; + case mac_sch_subpdu_nr::PADDING: + fmt::format_to(buffer, " PAD: len={}", get_sdu_length()); + break; + default: + fmt::format_to(buffer, " CE={} total_len={}", get_lcid(), get_total_length()); + break; + } + } else { + // DL-SCH PDU + switch (get_lcid()) { + case mac_sch_subpdu_nr::TA_CMD: + fmt::format_to(buffer, " TA: id={} command={}", get_ta().tag_id, get_ta().ta_command); + break; + case mac_sch_subpdu_nr::CON_RES_ID: { + ue_con_res_id_t id = get_ue_con_res_id_ce(); + fmt::format_to(buffer, + " CON_RES: id={:x}{:x}{:x}{:x}{:x}{:x}", + id.at(0), + id.at(1), + id.at(2), + id.at(3), + id.at(4), + id.at(5)); + } break; + case mac_sch_subpdu_nr::PADDING: + fmt::format_to(buffer, " PAD: len={}", get_sdu_length()); + break; + default: + fmt::format_to(buffer, " CE={} total_len={}", get_lcid(), get_total_length()); + break; + } + } + } +} + void mac_sch_pdu_nr::pack() { // SDU and CEs are written in-place, only add padding if needed @@ -308,7 +488,7 @@ int mac_sch_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len) while (offset < len) { mac_sch_subpdu_nr sch_pdu(this); if (sch_pdu.read_subheader(payload + offset) == SRSRAN_ERROR) { - logger.error("Malformed MAC PDU (len=%d, offset=%d)\n", len, offset); + logger.error("Malformed MAC PDU (len=%d, offset=%d)", len, offset); return SRSRAN_ERROR; } offset += sch_pdu.get_total_length(); @@ -321,19 +501,19 @@ int mac_sch_pdu_nr::unpack(const uint8_t* payload, const uint32_t& len) subpdus.push_back(sch_pdu); } if (offset != len) { - logger.error("Malformed MAC PDU (len=%d, offset=%d)\n", len, offset); + logger.error("Malformed MAC PDU (len=%d, offset=%d)", len, offset); return SRSRAN_ERROR; } return SRSRAN_SUCCESS; } -uint32_t mac_sch_pdu_nr::get_num_subpdus() +const mac_sch_subpdu_nr& mac_sch_pdu_nr::get_subpdu(const uint32_t& index) const { - return subpdus.size(); + return subpdus.at(index); } -const mac_sch_subpdu_nr& mac_sch_pdu_nr::get_subpdu(const uint32_t& index) +mac_sch_subpdu_nr& mac_sch_pdu_nr::get_subpdu(uint32_t index) { return subpdus.at(index); } @@ -343,13 +523,18 @@ bool mac_sch_pdu_nr::is_ulsch() return ulsch; } -void mac_sch_pdu_nr::init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool ulsch_) +int mac_sch_pdu_nr::init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_, bool ulsch_) { + if (buffer_ == nullptr || buffer_->msg == nullptr) { + logger.error("Invalid buffer"); + return SRSRAN_ERROR; + } buffer = buffer_; subpdus.clear(); pdu_len = pdu_len_; remaining_len = pdu_len_; ulsch = ulsch_; + return SRSRAN_SUCCESS; } void mac_sch_pdu_nr::init_rx(bool ulsch_) @@ -365,13 +550,8 @@ uint32_t mac_sch_pdu_nr::size_header_sdu(const uint32_t lcid, const uint32_t nby { if (ulsch && (lcid == mac_sch_subpdu_nr::CCCH_SIZE_48 || lcid == mac_sch_subpdu_nr::CCCH_SIZE_64)) { return 1; - } else { - if (nbytes < 256) { - return 2; - } else { - return 3; - } } + return nbytes < 256 ? 2 : 3; } uint32_t mac_sch_pdu_nr::get_remaing_len() @@ -421,6 +601,13 @@ mac_sch_pdu_nr::add_lbsr_ce(const std::arraylen = len; - pdu->channel = channel; + pdu_t* pdu = (pdu_t*)ptr; + pdu->len = len; + pdu->channel = channel; + pdu->grant_nof_prbs = grant_nof_prbs; if (!pdu_q.try_push(pdu)) { logger.warning("Error pushing pdu: queue is full"); } @@ -83,7 +84,7 @@ bool pdu_queue::process_pdus() pdu_t* pdu; while (pdu_q.try_pop(pdu)) { if (callback) { - callback->process_pdu(pdu->ptr, pdu->len, pdu->channel); + callback->process_pdu(pdu->ptr, pdu->len, pdu->channel, pdu->grant_nof_prbs); } cnt++; have_data = true; diff --git a/lib/test/mac/CMakeLists.txt b/lib/src/mac/test/CMakeLists.txt similarity index 95% rename from lib/test/mac/CMakeLists.txt rename to lib/src/mac/test/CMakeLists.txt index 2f70ac86f..0e45f9df0 100644 --- a/lib/test/mac/CMakeLists.txt +++ b/lib/src/mac/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/test/mac/mac_pcap_test.cc b/lib/src/mac/test/mac_pcap_test.cc similarity index 99% rename from lib/test/mac/mac_pcap_test.cc rename to lib/src/mac/test/mac_pcap_test.cc index 67d60a989..c0bea4994 100644 --- a/lib/test/mac/mac_pcap_test.cc +++ b/lib/src/mac/test/mac_pcap_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/mac/mac_pdu_nr_test.cc b/lib/src/mac/test/mac_pdu_nr_test.cc similarity index 75% rename from lib/test/mac/mac_pdu_nr_test.cc rename to lib/src/mac/test/mac_pdu_nr_test.cc index eece35bd5..15a632f08 100644 --- a/lib/test/mac/mac_pdu_nr_test.cc +++ b/lib/src/mac/test/mac_pdu_nr_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,6 +20,7 @@ */ #include "srsran/common/mac_pcap.h" +#include "srsran/common/string_helpers.h" #include "srsran/common/test_common.h" #include "srsran/config.h" #include "srsran/mac/mac_rar_pdu_nr.h" @@ -32,6 +33,7 @@ #define PCAP 0 #define PCAP_CRNTI (0x1001) +#define PCAP_RAR_RNTI (0x0016) #define PCAP_TTI (666) using namespace srsran; @@ -226,8 +228,13 @@ int mac_dl_sch_pdu_pack_test5() pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); } + // pretty print PDU + fmt::memory_buffer buff; + tx_pdu.to_string(buff); + auto& mac_logger = srslog::fetch_basic_logger("MAC"); - mac_logger.info(tx_buffer.msg, tx_buffer.N_bytes, "Generated MAC PDU (%d B)", tx_buffer.N_bytes); + mac_logger.info( + tx_buffer.msg, tx_buffer.N_bytes, "Generated MAC PDU (%d B): %s", tx_buffer.N_bytes, srsran::to_c_str(buff)); return SRSRAN_SUCCESS; } @@ -254,7 +261,72 @@ int mac_dl_sch_pdu_unpack_test6() return SRSRAN_SUCCESS; } -int mac_rar_pdu_unpack_test7() +int mac_dl_sch_pdu_unpack_pack_test7() +{ + // MAC PDU with DL-SCH subheader with ConRes CE and dummy 8B SDU on SRB1 + uint8_t tv[] = { + 0x3e, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x04, 0x08, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x3f, 0x00}; + uint8_t con_res_id_tv[] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + + // unpack + srsran::mac_sch_pdu_nr rx_pdu; + rx_pdu.unpack(tv, sizeof(tv)); + + TESTASSERT(rx_pdu.get_num_subpdus() == 3); + + // Read ConRes CE + mac_sch_subpdu_nr subpdu = rx_pdu.get_subpdu(0); + TESTASSERT(subpdu.get_total_length() == 7); + TESTASSERT(subpdu.get_sdu_length() == 6); + TESTASSERT(subpdu.get_lcid() == 0x3e); + mac_sch_subpdu_nr::ue_con_res_id_t con_res = subpdu.get_ue_con_res_id_ce(); + TESTASSERT(memcmp(con_res.data(), con_res_id_tv, con_res.size()) == 0); + + // skip other subPDUs .. + + // pack again + const uint32_t sdu_len = 8; + uint8_t sdu[sdu_len] = {}; + + // populate SDU payload + for (uint32_t i = 0; i < sdu_len; i++) { + sdu[i] = i % 256; + } + + // pack buffer + byte_buffer_t tx_buffer; + + srsran::mac_sch_pdu_nr tx_pdu; + tx_pdu.init_tx(&tx_buffer, sizeof(tv)); + + // add ConRes CE + srsran::mac_sch_subpdu_nr::ue_con_res_id_t id = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66}; + TESTASSERT(tx_pdu.add_ue_con_res_id_ce(id) == SRSRAN_SUCCESS); + + // Add SDU + tx_pdu.add_sdu(4, sdu, sizeof(sdu)); + + tx_pdu.pack(); + + TESTASSERT(tx_buffer.N_bytes == sizeof(tv)); + TESTASSERT(memcmp(tx_buffer.msg, tv, tx_buffer.N_bytes) == 0); + + if (pcap_handle) { + pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } + + // pretty print PDU + fmt::memory_buffer buff; + tx_pdu.to_string(buff); + + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.info( + tx_buffer.msg, tx_buffer.N_bytes, "Generated MAC PDU (%d B): %s", tx_buffer.N_bytes, srsran::to_c_str(buff)); + + return SRSRAN_SUCCESS; +} + +int mac_rar_pdu_test7() { // MAC PDU with RAR PDU with single RAPID=0 // rapid=0 @@ -282,13 +354,17 @@ int mac_rar_pdu_unpack_test7() uint8_t mac_dl_rar_pdu[] = {0x40, 0x05, 0xa0, 0x00, 0x11, 0x46, 0x46, 0x16, 0x00, 0x00, 0x00}; if (pcap_handle) { - pcap_handle->write_dl_ra_rnti_nr(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu), 0x0016, true, PCAP_TTI); + pcap_handle->write_dl_ra_rnti_nr(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu), PCAP_RAR_RNTI, true, PCAP_TTI); } srsran::mac_rar_pdu_nr pdu; TESTASSERT(pdu.unpack(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu)) == true); - std::cout << pdu.to_string() << std::endl; + fmt::memory_buffer buff; + pdu.to_string(buff); + + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.info("Rx PDU: %s", srsran::to_c_str(buff)); TESTASSERT(pdu.get_num_subpdus() == 1); @@ -302,6 +378,30 @@ int mac_rar_pdu_unpack_test7() std::array msg3_grant = subpdu.get_ul_grant(); TESTASSERT(memcmp(msg3_grant.data(), tv_msg3_grant, msg3_grant.size()) == 0); + // pack again + byte_buffer_t tx_buffer; + tx_buffer.clear(); + + srsran::mac_rar_pdu_nr tx_pdu; + tx_pdu.init_tx(&tx_buffer, sizeof(mac_dl_rar_pdu)); + + mac_rar_subpdu_nr& rar_subpdu = tx_pdu.add_subpdu(); + rar_subpdu.set_ta(tv_ta); + rar_subpdu.set_rapid(tv_rapid); + rar_subpdu.set_temp_crnti(tv_tcrnti); + rar_subpdu.set_ul_grant(msg3_grant); + + TESTASSERT(tx_pdu.pack() == SRSRAN_SUCCESS); + TESTASSERT(tx_buffer.N_bytes == sizeof(mac_dl_rar_pdu)); + TESTASSERT(memcmp(tx_buffer.msg, mac_dl_rar_pdu, tx_buffer.N_bytes) == 0); + + tx_pdu.to_string(buff); + mac_logger.info("Tx PDU: %s", srsran::to_c_str(buff)); + + if (pcap_handle) { + pcap_handle->write_dl_ra_rnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_RAR_RNTI, true, PCAP_TTI); + } + return SRSRAN_SUCCESS; } @@ -490,6 +590,90 @@ int mac_ul_sch_pdu_unpack_and_pack_test3() return SRSRAN_SUCCESS; } +int mac_ul_sch_pdu_unpack_and_pack_test4() +{ + // MAC PDU with UL-SCH with C-RNTI, Long BSR (all zeros) and padding + uint8_t mac_ul_sch_pdu_1[] = {0x3a, 0x46, 0x01, 0x3e, 0x01, 0x00, 0x3f, 0x21, 0x21, 0x21, 0x21}; + const uint16_t TV_CRNTI = 0x4601; + + // only LBSR with only one LCG (LCG7 leftmost bit) reporting buffer state + uint8_t mac_ul_sch_pdu_2[] = {0x3e, 0x02, 0x80, 0xab}; + + // only LBSR with only two LCG (LCG7 leftmost bit. LCG0) reporting buffer state + uint8_t mac_ul_sch_pdu_3[] = {0x3e, 0x03, 0x81, 0xab, 0xcd}; + + if (pcap_handle) { + pcap_handle->write_ul_crnti_nr(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1), PCAP_CRNTI, true, PCAP_TTI); + pcap_handle->write_ul_crnti_nr(mac_ul_sch_pdu_2, sizeof(mac_ul_sch_pdu_2), PCAP_CRNTI, true, PCAP_TTI); + pcap_handle->write_ul_crnti_nr(mac_ul_sch_pdu_3, sizeof(mac_ul_sch_pdu_3), PCAP_CRNTI, true, PCAP_TTI); + } + + // pretty print PDU + fmt::memory_buffer buff; + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + + // first TV + srsran::mac_sch_pdu_nr pdu(true); + pdu.unpack(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1)); + pdu.to_string(buff); + mac_logger.info("Rx PDU: %s", srsran::to_c_str(buff)); + + TESTASSERT(pdu.get_num_subpdus() == 3); + + // 1st subPDU is C-RNTI CE + mac_sch_subpdu_nr subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_total_length() == 3); + TESTASSERT(subpdu.get_sdu_length() == 2); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::CRNTI); + TESTASSERT(subpdu.get_c_rnti() == TV_CRNTI); + + // 2nd subPDU is LBSR + subpdu = pdu.get_subpdu(1); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::LONG_BSR); + mac_sch_subpdu_nr::lbsr_t lbsr = subpdu.get_lbsr(); + TESTASSERT(lbsr.list.size() == 0); + + // 3rd is padding + subpdu = pdu.get_subpdu(2); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::PADDING); + + // TODO: pack again and test + + // 2nd TV + pdu.init_rx(true); + pdu.unpack(mac_ul_sch_pdu_2, sizeof(mac_ul_sch_pdu_2)); + buff.clear(); + pdu.to_string(buff); + mac_logger.info("Rx PDU: %s", srsran::to_c_str(buff)); + + TESTASSERT(pdu.get_num_subpdus() == 1); + subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::LONG_BSR); + lbsr = subpdu.get_lbsr(); + TESTASSERT(lbsr.list.size() == 1); + TESTASSERT(lbsr.list.at(0).lcg_id == 7); + TESTASSERT(lbsr.list.at(0).buffer_size == 0xab); + + // 3nd TV + pdu.init_rx(true); + pdu.unpack(mac_ul_sch_pdu_3, sizeof(mac_ul_sch_pdu_3)); + buff.clear(); + pdu.to_string(buff); + mac_logger.info("Rx PDU: %s", srsran::to_c_str(buff)); + + TESTASSERT(pdu.get_num_subpdus() == 1); + subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::LONG_BSR); + lbsr = subpdu.get_lbsr(); + TESTASSERT(lbsr.list.size() == 2); + TESTASSERT(lbsr.list.at(0).lcg_id == 0); + TESTASSERT(lbsr.list.at(0).buffer_size == 0xab); + TESTASSERT(lbsr.list.at(1).lcg_id == 7); + TESTASSERT(lbsr.list.at(1).buffer_size == 0xcd); + + return SRSRAN_SUCCESS; +} + int mac_ul_sch_pdu_pack_test4() { // MAC PDU with UL-SCH (with normal LCID) subheader for long SDU @@ -596,6 +780,13 @@ int mac_dl_sch_pdu_unpack_and_pack_test6() // 4th is padding subpdu = pdu_rx.get_subpdu(3); TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::PADDING); + + // pretty print PDU + fmt::memory_buffer buff; + pdu_rx.to_string(buff); + + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.info("Rx PDU: %s", srsran::to_c_str(buff)); } // Let's pack the entire PDU again @@ -621,6 +812,13 @@ int mac_dl_sch_pdu_unpack_and_pack_test6() // finish PDU packing pdu_tx.pack(); + // pretty print PDU + fmt::memory_buffer buff; + pdu_tx.to_string(buff); + + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.info("Tx PDU: %s", srsran::to_c_str(buff)); + // compare PDUs TESTASSERT(tx_buffer.N_bytes == sizeof(tv)); TESTASSERT(memcmp(tx_buffer.msg, tv, tx_buffer.N_bytes) == 0); @@ -658,6 +856,35 @@ int mac_ul_sch_pdu_unpack_test6() return SRSRAN_SUCCESS; } +int mac_ul_sch_pdu_unpack_test7() +{ + // TV1 - MAC PDU with UL-SCH with CCCH (48 bits) subPDU (LCID=0x34) and padding + uint8_t mac_ul_sch_pdu_1[] = {0x34, 0x10, 0xb7, 0xcd, 0x6e, 0x38, 0xa6, 0x3f, 0x21, 0x21, 0x21}; + const uint8_t* ccch_sdu = &mac_ul_sch_pdu_1[1]; + const uint32_t ccch_sdu_len = 6; + + if (pcap_handle) { + pcap_handle->write_ul_crnti_nr(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1), PCAP_CRNTI, true, PCAP_TTI); + } + + srsran::mac_sch_pdu_nr pdu(true); + pdu.unpack(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1)); + TESTASSERT(pdu.get_num_subpdus() == 2); + + // 1st is CCCH + mac_sch_subpdu_nr subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_total_length() == 7); + TESTASSERT(subpdu.get_sdu_length() == 6); + TESTASSERT(subpdu.get_lcid() == 0x34); + TESTASSERT(memcmp(subpdu.get_sdu(), ccch_sdu, ccch_sdu_len) == 0); + + // 2nd is padding + subpdu = pdu.get_subpdu(1); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::PADDING); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { #if PCAP @@ -701,7 +928,12 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } - if (mac_rar_pdu_unpack_test7()) { + if (mac_dl_sch_pdu_unpack_pack_test7()) { + fprintf(stderr, "mac_dl_sch_pdu_unpack_pack_test7() failed.\n"); + return SRSRAN_ERROR; + } + + if (mac_rar_pdu_test7()) { fprintf(stderr, "mac_rar_pdu_unpack_test7() failed.\n"); return SRSRAN_ERROR; } @@ -731,6 +963,11 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } + if (mac_ul_sch_pdu_unpack_and_pack_test4()) { + fprintf(stderr, "mac_ul_sch_pdu_unpack_and_pack_test4() failed.\n"); + return SRSRAN_ERROR; + } + if (mac_ul_sch_pdu_pack_test4()) { fprintf(stderr, "mac_ul_sch_pdu_pack_test4() failed.\n"); return SRSRAN_ERROR; @@ -751,9 +988,16 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } + if (mac_ul_sch_pdu_unpack_test7()) { + fprintf(stderr, "mac_ul_sch_pdu_unpack_test7() failed.\n"); + return SRSRAN_ERROR; + } + if (pcap_handle) { pcap_handle->close(); } + srslog::flush(); + return SRSRAN_SUCCESS; } diff --git a/lib/test/mac/pdu_test.cc b/lib/src/mac/test/pdu_test.cc similarity index 79% rename from lib/test/mac/pdu_test.cc rename to lib/src/mac/test/pdu_test.cc index dc6c8f73b..0c2cc808c 100644 --- a/lib/test/mac/pdu_test.cc +++ b/lib/src/mac/test/pdu_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -178,7 +178,7 @@ int mac_rar_pdu_pack_test2() class rlc_dummy : public srsran::read_pdu_interface { public: - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { uint32_t len = std::min(ul_queues[lcid], nof_bytes); @@ -1029,6 +1029,118 @@ int mac_sch_pdu_unpack_test3() return SRSRAN_SUCCESS; } +// Unpacking of UL-SCH PDU containing Padding, PHR and two SDUs for same LCID +int mac_sch_pdu_unpack_test4() +{ + static uint8_t tv[] = { + 0x3f, 0x3a, 0x23, 0x09, 0x03, 0x1e, 0xd9, 0xd0, 0x84, 0x1d, 0xc4, 0x77, 0xc7, 0x3b, 0xf0, 0x9a, 0x31, 0xc6, 0xb7, + 0x48, 0x61, 0x6e, 0xc3, 0x97, 0x48, 0xa6, 0xe3, 0xac, 0xc0, 0x75, 0x9f, 0xb2, 0xc8, 0xed, 0xb0, 0xad, 0xcc, 0xc2, + 0x0d, 0x28, 0xdd, 0xbb, 0x4b, 0x4c, 0xc5, 0xfc, 0x52, 0x40, 0xc2, 0x09, 0x89, 0x23, 0x77, 0x4c, 0xba, 0xbf, 0xf2, + 0x9d, 0x6b, 0xb2, 0x12, 0x0b, 0x64, 0xda, 0xf8, 0x14, 0x88, 0xc4, 0xd7, 0x95, 0xec, 0xb4, 0x50, 0x37, 0x00, 0x15, + 0x33, 0x52, 0x56, 0xf5, 0x5b, 0xdf, 0x18, 0xd2, 0x2b, 0xd2, 0x92, 0x1d, 0x6f, 0xfd, 0xcf, 0x82, 0x08, 0x33, 0x5c, + 0x00, 0x48, 0xe4, 0xc4, 0x1f, 0x79, 0xb0, 0xd3, 0xca, 0xe8, 0xd3, 0xdf, 0x1b, 0x25, 0x35, 0x11, 0x80, 0x14, 0x29, + 0x52, 0x3f, 0xfc, 0xe4, 0x5c, 0x6b, 0xe2, 0x2b, 0xed, 0xea, 0x5f, 0x4a, 0xeb, 0xa7, 0x2e, 0xaf, 0xc6, 0xa8, 0x60, + 0x99, 0x72, 0x48, 0x6c, 0x51, 0x63, 0x91, 0x87, 0x74, 0x11, 0x9b, 0x9e, 0x63, 0xdb, 0x9a, 0x48, 0x37, 0x05, 0x2a, + 0x63, 0xf3, 0x14, 0xc2, 0x3d, 0xff, 0x69, 0x6b, 0xaf, 0x2f, 0x13, 0x0f, 0xc8, 0x85, 0x57, 0x34, 0xd7, 0xba, 0xc5, + 0x5e, 0x2f, 0xd6, 0xf9, 0xcd, 0x39, 0xd4, 0x67, 0x81, 0x0c, 0x6c, 0xab, 0x6f, 0x5f, 0xc0, 0x31, 0x9c, 0xbf, 0x9a, + 0x08, 0x6e, 0xc9, 0x1b, 0x7d, 0x91, 0xa1, 0xd4, 0xc5, 0x78, 0x8f, 0x8a, 0xd6, 0xbe, 0x60, 0xcf, 0x8b, 0x99, 0xa4, + 0xf2, 0x1b, 0xb0, 0x5e, 0xc6, 0x1f, 0xbe, 0x86, 0x50, 0x5a, 0x46, 0xea, 0x62, 0xb4, 0xb3, 0x7e, 0x32, 0x44, 0x1f, + 0x06, 0x09, 0x97, 0x95, 0x93, 0x6d, 0x53, 0xf3, 0x3c, 0xde, 0x8c, 0xe0, 0xd0, 0xa7, 0x90, 0x2f, 0x6e, 0xaf, 0xed, + 0xf4, 0xff, 0x47, 0x3a, 0xe9, 0xaa, 0xef, 0x9c, 0x28, 0x21, 0xe0, 0x47, 0x27, 0xe9, 0xde, 0xbd, 0x7c, 0x4b, 0x10, + 0x6f, 0x87, 0xef, 0xfc, 0x68, 0xbf, 0xa3, 0xf8, 0xee, 0x11, 0xa8, 0xdb, 0x06, 0xa7, 0x23, 0x40, 0x91, 0xcd, 0x2f, + 0x2d, 0xf5, 0x50, 0x0e, 0x3c, 0x78, 0xf7, 0x1a, 0x35, 0x74, 0x65, 0x45, 0xe3, 0xec, 0x34, 0xdf, 0x54, 0xf4, 0x83, + 0x4d, 0xe2, 0x94, 0xf5, 0xbe, 0x9a, 0x9c, 0xe1, 0xdb, 0x2d, 0xae, 0x0a, 0x5b, 0xa3, 0x5b, 0x69, 0xdf, 0xd3, 0x60, + 0xf9, 0x08, 0xd4, 0x5e, 0x4d, 0xb8, 0x4a, 0x82, 0x97, 0x9f, 0x76, 0x1a, 0xec, 0x58, 0xaf, 0xe1, 0x16, 0x49, 0x7d, + 0xf7, 0x24, 0xab, 0xa5, 0x2f, 0x06, 0x48, 0x8a, 0x6f, 0x27, 0x5d, 0xcf, 0x20, 0x65, 0xa4, 0x7e, 0xb2, 0x5c, 0xc9, + 0x34, 0xf3, 0x68, 0xaa, 0x0e, 0x54, 0x03, 0xbd, 0x35, 0x19, 0x06, 0xb2, 0x11, 0x2b, 0x5d, 0xb6, 0x5a, 0x63, 0xff, + 0xe4, 0xd2, 0x26, 0x41, 0xa2, 0x47, 0xa6, 0x46, 0xc5, 0x58, 0xa2, 0x8e, 0x8d, 0x95, 0xf6, 0x37, 0xa3, 0x4a, 0x3a, + 0x60, 0x7f, 0x54, 0x67, 0x32, 0x65, 0x92, 0x8f, 0x1b, 0xec, 0xf3, 0x1a, 0xd0, 0xc5, 0x41, 0x11, 0x67, 0x88, 0xb7, + 0xad, 0x4d, 0x0f, 0x4f, 0xdc, 0x9c, 0xe5, 0xd2, 0xd4, 0x88, 0x1d, 0x0e, 0xe9, 0x9c, 0x62, 0x50, 0xce, 0xc7, 0xe2, + 0x5e, 0xe3, 0xce, 0x51, 0xfd, 0x9e, 0x16, 0x3e, 0xaf, 0x7e, 0xc6, 0x66, 0x2b, 0x14, 0x75, 0x7b, 0xf0, 0x12, 0x60, + 0xc2, 0xe6, 0xe8, 0xdf, 0xf4, 0xd1, 0x7c, 0x57, 0x21, 0x4a, 0x1e, 0x03, 0xa8, 0x01, 0xd1, 0xf9, 0xff, 0x6f, 0x10, + 0x3d, 0x1e, 0x8e, 0x04, 0x84, 0xb9, 0x18, 0xfa, 0x34, 0x08, 0x0c, 0x94, 0xca, 0xf2, 0x7d, 0xaa, 0xe6, 0x4e, 0x26, + 0x3d, 0x70, 0x70, 0x5c, 0x73, 0x19, 0x5d, 0x45, 0x12, 0x5c, 0xb4, 0x22, 0x9a, 0xd3, 0xb0, 0x9e, 0x57, 0x6a, 0xb6, + 0x51, 0x9e, 0xbe, 0x5d, 0x33, 0x88, 0x4f, 0xb0, 0x32, 0x36, 0xfe, 0x58, 0x73, 0x6e, 0xc9, 0xcf, 0xe2, 0xe2, 0x2d, + 0x27, 0xf4, 0x89, 0xdb, 0x17, 0x23, 0xae, 0xc7, 0xc1, 0x06, 0x31, 0x77, 0x57, 0xd0, 0x35, 0xb5, 0x03, 0xbe, 0x04, + 0xb3, 0xf0, 0x3a, 0xb1, 0x49, 0xae, 0x20, 0x12, 0x7d, 0x02, 0xf4, 0xaa, 0x29, 0xe8, 0x34, 0x04, 0xff, 0x57, 0xb3, + 0xc7, 0x19, 0xb9, 0xf8, 0x90, 0x10, 0xc8, 0xc6, 0xc5, 0xcb, 0x84, 0xca, 0x7e, 0x74, 0x04, 0x30, 0xbd, 0xb2, 0x50, + 0xcf, 0x30, 0x52, 0xc3, 0xda, 0x7b, 0xac, 0x0e, 0x7f, 0xab, 0x66, 0x32, 0x72, 0x7f, 0xeb, 0x6b, 0x0f, 0xfc, 0x33, + 0xd5, 0xc1, 0xff, 0x59, 0x8b, 0x7d, 0xce, 0x90, 0xad, 0x8b, 0x42, 0xfd, 0x5b, 0x72, 0x4f, 0x1e, 0x4d, 0xca, 0xca, + 0x5b, 0x4a, 0x76, 0xc1, 0x7c, 0xe8, 0x40, 0x68, 0x53, 0x50, 0x64, 0x87, 0x25, 0x25, 0x86, 0x7f, 0xb1, 0x03, 0x4d, + 0x41, 0xb1, 0xd8, 0x83, 0xae, 0x33, 0xf6, 0xfe, 0x52, 0x43, 0xc8, 0x1c, 0x9e, 0x12, 0x92, 0x60, 0x8f, 0x7b, 0xa0, + 0xf7, 0xce, 0xf0, 0x5b, 0x55, 0x16, 0x80, 0xdb, 0x95, 0x31, 0xdf, 0xe2, 0x72, 0x90, 0xba, 0xf6, 0x3e, 0xee, 0xec, + 0x3c, 0x40, 0x2f, 0x05, 0x5c, 0xcd, 0x17, 0xef, 0x2d, 0xa6, 0x6a, 0xce, 0x9d, 0x38, 0xbe, 0xf8, 0x8e, 0xd4, 0x79, + 0x69, 0x69, 0xaa, 0x48, 0x4b, 0x1d, 0xd8, 0x06, 0x13, 0x17, 0xf4, 0xff, 0x53, 0x34, 0x2e, 0x58, 0x90, 0xfb, 0x70, + 0x7f, 0x29, 0x16, 0xe9, 0xf7, 0xb4, 0x22, 0xb5, 0xac, 0xb0, 0x8a, 0x25, 0x19, 0xf3, 0xd0, 0x62, 0x3f, 0xed, 0x3a, + 0x45, 0x00, 0x51, 0x39, 0xff, 0xa5, 0x6d, 0x2d, 0xfd, 0xfd, 0x28, 0x6d, 0x7d, 0x51, 0x84, 0x66, 0x48, 0x38, 0x88, + 0xfe, 0xe4, 0x38, 0x88, 0x0a, 0x52, 0x7b, 0xda, 0xb4, 0xba, 0xc7, 0xee, 0xff, 0xc7, 0x40, 0x38, 0xc6, 0xe5, 0xa5, + 0xf3, 0xe2, 0xa3, 0x1b, 0x50, 0x20, 0x6d, 0xd4, 0x86, 0xb5, 0x0c, 0x0f, 0xb3, 0xf0, 0x47, 0x3b, 0xfa, 0x99, 0xb7, + 0xd4, 0x4d, 0x71, 0x9b, 0x3c, 0x71, 0x62, 0x7c, 0xa9, 0x28, 0x61, 0x4f, 0x1b, 0x43, 0xf2, 0x37, 0x93, 0x12, 0xa4, + 0x67, 0x98, 0x59, 0x73, 0xa7, 0x0d, 0x64, 0xef, 0x48, 0x5e, 0x88, 0xff, 0x33, 0xd6, 0x71, 0xce, 0x12, 0xe2, 0x31, + 0x8e, 0x8b, 0x59, 0xef, 0xda, 0x75, 0x32, 0xcc, 0xac, 0xc6, 0xde, 0x50, 0x2d, 0x77, 0xa9, 0xa1, 0x1e, 0xb6, 0x05, + 0x0d, 0xff, 0x63, 0x96, 0xfe, 0x96, 0x6c, 0x6f, 0x65, 0x7e, 0x51, 0x96, 0x0c, 0xdd, 0xef, 0xfb, 0xb7, 0x64, 0x2d, + 0x84, 0x10, 0xf3, 0x62, 0x60, 0x21, 0xd9, 0x0a, 0xc2, 0xf8, 0xc0, 0xc7, 0x05, 0xbf, 0x2a, 0x9b, 0xbe, 0xc1, 0x07, + 0x2d, 0x26, 0x85, 0x7f, 0xbc, 0x91, 0x5c, 0xab, 0x8c, 0x13, 0x10, 0xba, 0x97, 0x20, 0xad, 0xfa, 0x81, 0xce, 0xd3, + 0x8b, 0x90, 0xcb, 0x4b, 0x57, 0xd1, 0x0b, 0x82, 0x6c, 0xc9, 0x43, 0x74, 0xf6, 0x69, 0xf9, 0x75, 0x25, 0x8b, 0xd1, + 0xd0, 0x17, 0xe5, 0xe0, 0xd1, 0x7c, 0x01, 0x7f, 0x76, 0x82, 0x4d, 0x4a, 0x0d, 0xde, 0x15, 0x58, 0x35, 0xe6, 0x63, + 0xb7, 0x53, 0x2c, 0xfa, 0xc7, 0x23, 0x63, 0xc0, 0x98, 0x88, 0x4b, 0x6a, 0x59, 0x63, 0x4f, 0x39, 0x34, 0xcb, 0x3a, + 0xb3, 0x42, 0xbc, 0x01, 0x8c, 0xc9, 0xdf, 0xa1, 0x22, 0x14, 0x88, 0x85, 0xcc, 0xdb, 0xb2, 0xc6, 0xa2, 0xd5, 0x2a, + 0x62, 0x6d, 0xb2, 0xae, 0xd7, 0x0b, 0x11, 0x26, 0x45, 0x45, 0xf2, 0x7f, 0xf9, 0x34, 0x3c, 0xfa, 0xc0, 0x05, 0xd9, + 0x61, 0x27, 0xed, 0xe9, 0xad, 0xb9, 0xc4, 0x5f, 0x80, 0x66, 0x34, 0xaa, 0xc9, 0xa1, 0x5c, 0x77, 0x79, 0x68, 0x88, + 0x9f, 0xad, 0xcd, 0x91, 0x2c, 0xc6, 0xc5, 0x68, 0xc0, 0x85, 0x6e, 0x99, 0xe7, 0x95, 0x87, 0xd0, 0x42, 0x40, 0x95, + 0xa1, 0xc0, 0xfb, 0xd5, 0x6a, 0xc4, 0x77, 0xc7, 0x3b, 0xf0, 0x2f, 0xc3, 0x8f, 0xdc, 0x91, 0x21, 0x08, 0x57}; + + uint8_t* sdu1 = &tv[6]; + uint32_t sdu1_len = 9; + + uint8_t* sdu2 = &tv[15]; + uint32_t sdu2_len = 1048; + + srsran::sch_pdu pdu(5, srslog::fetch_basic_logger("MAC")); + pdu.init_rx(sizeof(tv), true); + pdu.parse_packet(tv); + + TESTASSERT(pdu.nof_subh() == 4); + + // Padding + TESTASSERT(pdu.next()); + TESTASSERT(pdu.get()->is_sdu() == false); + TESTASSERT(pdu.get()->ul_sch_ce_type() == srsran::ul_sch_lcid::PADDING); + + // PHR + TESTASSERT(pdu.next()); + TESTASSERT(pdu.get()->is_sdu() == false); + TESTASSERT(pdu.get()->ul_sch_ce_type() == srsran::ul_sch_lcid::PHR_REPORT); + TESTASSERT(pdu.get()->get_phr() == 7); + + // SDU1 + TESTASSERT(pdu.next()); + TESTASSERT(pdu.get()->is_sdu() == true); + TESTASSERT(pdu.get()->get_sdu_lcid() == 3); + TESTASSERT(pdu.get()->get_payload_size() == sdu1_len); + TESTASSERT(memcmp(sdu1, pdu.get()->get_sdu_ptr(), sdu1_len) == 0); + + // SDU2 + TESTASSERT(pdu.next()); + TESTASSERT(pdu.get()->is_sdu() == true); + TESTASSERT(pdu.get()->get_sdu_lcid() == 3); + TESTASSERT(pdu.get()->get_payload_size() == sdu2_len); + TESTASSERT(memcmp(sdu2, pdu.get()->get_sdu_ptr(), sdu2_len) == 0); + + // end of PDU + TESTASSERT(pdu.next() == false); + + fmt::memory_buffer buffer; + pdu.to_string(buffer); + std::cout << fmt::to_string(buffer) << std::endl; + +#if HAVE_PCAP + pcap_handle->write_ul_crnti(tv, sizeof(tv), 0x1001, true, 1, 0); +#endif + + return SRSRAN_SUCCESS; +} + int mac_slsch_pdu_unpack_test1() { // SL-SCH PDU captures from UXM 5G CV2X @@ -1085,6 +1197,7 @@ int main(int argc, char** argv) TESTASSERT(mac_sch_pdu_unpack_test1() == SRSRAN_SUCCESS); TESTASSERT(mac_sch_pdu_unpack_test2() == SRSRAN_SUCCESS); TESTASSERT(mac_sch_pdu_unpack_test3() == SRSRAN_SUCCESS); + TESTASSERT(mac_sch_pdu_unpack_test4() == SRSRAN_SUCCESS); TESTASSERT(mac_slsch_pdu_unpack_test1() == SRSRAN_SUCCESS); diff --git a/lib/src/pdcp/CMakeLists.txt b/lib/src/pdcp/CMakeLists.txt new file mode 100644 index 000000000..81a66819b --- /dev/null +++ b/lib/src/pdcp/CMakeLists.txt @@ -0,0 +1,28 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES pdcp.cc + pdcp_entity_base.cc + pdcp_entity_lte.cc + pdcp_entity_nr.cc) + +add_library(srsran_pdcp STATIC ${SOURCES}) +target_link_libraries(srsran_pdcp srsran_common srsran_asn1 ${ATOMIC_LIBS}) +install(TARGETS srsran_pdcp DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/upper/pdcp.cc b/lib/src/pdcp/pdcp.cc similarity index 81% rename from lib/src/upper/pdcp.cc rename to lib/src/pdcp/pdcp.cc index 18ce0f010..f824e8cf8 100644 --- a/lib/src/upper/pdcp.cc +++ b/lib/src/pdcp/pdcp.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,15 +39,6 @@ pdcp::~pdcp() pdcp_array_mrb.clear(); } -void pdcp::init(srsue::rlc_interface_pdcp* rlc_, - srsue::rrc_interface_pdcp* rrc_, - srsue::rrc_interface_pdcp* rrc_nr_, - srsue::gw_interface_pdcp* gw_) -{ - init(rlc_, rrc_, gw_); - rrc_nr = rrc_nr_; -} - void pdcp::init(srsue::rlc_interface_pdcp* rlc_, srsue::rrc_interface_pdcp* rrc_, srsue::gw_interface_pdcp* gw_) { rlc = rlc_; @@ -81,6 +72,15 @@ void pdcp::reset() pdcp_array.clear(); } +void pdcp::set_enabled(uint32_t lcid, bool enabled) +{ + if (valid_lcid(lcid)) { + pdcp_array.at(lcid)->set_enabled(enabled); + } else { + logger.warning("LCID %d doesn't exist while setting enabled", lcid); + } +} + /******************************************************************************* RRC/GW interface *******************************************************************************/ @@ -97,7 +97,7 @@ void pdcp::write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, int sn) if (valid_lcid(lcid)) { pdcp_array.at(lcid)->write_sdu(std::move(sdu), sn); } else { - logger.warning("Writing sdu: lcid=%d. Deallocating sdu", lcid); + logger.warning("LCID %d doesn't exist. Deallocating SDU", lcid); } } @@ -108,42 +108,46 @@ void pdcp::write_sdu_mch(uint32_t lcid, unique_byte_buffer_t sdu) } } -void pdcp::add_bearer(uint32_t lcid, pdcp_config_t cfg) +int pdcp::add_bearer(uint32_t lcid, const pdcp_config_t& cfg) { - if (not valid_lcid(lcid)) { - std::unique_ptr entity; - // For now we create an pdcp entity lte for nr due to it's maturity - if (cfg.rat == srsran::srsran_rat_t::lte) { - entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid}); - } else if (cfg.rat == srsran::srsran_rat_t::nr) { - if (rrc_nr == nullptr) { - logger.warning("Cannot add PDCP entity - missing rrc_nr parent pointer"); - return; - } - entity.reset(new pdcp_entity_lte{rlc, rrc_nr, gw, task_sched, logger, lcid}); - } - - if (not entity->configure(cfg)) { - logger.error("Can not configure PDCP entity"); - return; - } - - if (not pdcp_array.insert(std::make_pair(lcid, std::move(entity))).second) { - logger.error("Error inserting PDCP entity in to array."); - return; - } - logger.info( - "Add %s (lcid=%d, bearer_id=%d, sn_len=%dbits)", rrc->get_rb_name(lcid), lcid, cfg.bearer_id, cfg.sn_len); - { - std::lock_guard lock(cache_mutex); - valid_lcids_cached.insert(lcid); - } - } else { - logger.info("Bearer %s already configured.", rrc->get_rb_name(lcid)); + if (valid_lcid(lcid)) { + return pdcp_array[lcid]->configure(cfg) ? SRSRAN_SUCCESS : SRSRAN_ERROR; } + + std::unique_ptr entity; + + // For now we create an pdcp entity lte for nr due to it's maturity + if (cfg.rat == srsran::srsran_rat_t::lte) { + entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid}); + } else if (cfg.rat == srsran::srsran_rat_t::nr) { + entity.reset(new pdcp_entity_nr{rlc, rrc, gw, task_sched, logger, lcid}); + } + + if (not entity->configure(cfg)) { + logger.error("Can not configure PDCP entity"); + return SRSRAN_ERROR; + } + + if (not pdcp_array.insert(std::make_pair(lcid, std::move(entity))).second) { + logger.error("Error inserting PDCP entity in to array."); + return SRSRAN_ERROR; + } + + { + std::lock_guard lock(cache_mutex); + valid_lcids_cached.insert(lcid); + } + + logger.info("Add %s%d (lcid=%d, sn_len=%dbits)", + cfg.rb_type == PDCP_RB_IS_DRB ? "DRB" : "SRB", + cfg.bearer_id, + lcid, + cfg.sn_len); + + return SRSRAN_SUCCESS; } -void pdcp::add_bearer_mrb(uint32_t lcid, pdcp_config_t cfg) +void pdcp::add_bearer_mrb(uint32_t lcid, const pdcp_config_t& cfg) { if (not valid_mch_lcid(lcid)) { std::unique_ptr entity; @@ -171,10 +175,10 @@ void pdcp::del_bearer(uint32_t lcid) valid_lcids_cached.erase(lcid); } if (valid_lcid(lcid)) { + logger.info("Deleted PDCP bearer %s", pdcp_array[lcid]->get_rb_name()); pdcp_array.erase(lcid); - logger.info("Deleted PDCP bearer %s", rrc->get_rb_name(lcid)); } else { - logger.warning("Can't delete bearer %s. Bearer doesn't exist.", rrc->get_rb_name(lcid)); + logger.warning("Can't delete bearer with LCID=%s. Cause: bearer doesn't exist.", lcid); } } @@ -286,7 +290,7 @@ void pdcp::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) if (valid_lcid(lcid)) { pdcp_array.at(lcid)->write_pdu(std::move(pdu)); } else { - logger.warning("Writing pdu: lcid=%d. Deallocating pdu", lcid); + logger.warning("Dropping PDU, lcid=%d doesnt exists", lcid); } } @@ -367,12 +371,12 @@ void pdcp::get_metrics(pdcp_metrics_t& m, const uint32_t nof_tti) double rx_rate_mbps = (nof_tti > 0) ? ((metrics.num_rx_pdu_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0; double tx_rate_mbps = (nof_tti > 0) ? ((metrics.num_tx_pdu_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0; - logger.info("lcid=%d, rx_rate_mbps=%4.2f (real=%4.2f), tx_rate_mbps=%4.2f (real=%4.2f)", - it->first, - rx_rate_mbps, - rx_rate_mbps_real_time, - tx_rate_mbps, - tx_rate_mbps_real_time); + logger.debug("lcid=%d, rx_rate_mbps=%4.2f (real=%4.2f), tx_rate_mbps=%4.2f (real=%4.2f)", + it->first, + rx_rate_mbps, + rx_rate_mbps_real_time, + tx_rate_mbps, + tx_rate_mbps_real_time); m.bearer[it->first] = metrics; } diff --git a/lib/src/upper/pdcp_entity_base.cc b/lib/src/pdcp/pdcp_entity_base.cc similarity index 94% rename from lib/src/upper/pdcp_entity_base.cc rename to lib/src/pdcp/pdcp_entity_base.cc index 92f420324..a969338a2 100644 --- a/lib/src/upper/pdcp_entity_base.cc +++ b/lib/src/pdcp/pdcp_entity_base.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -114,25 +114,22 @@ bool pdcp_entity_base::integrity_verify(uint8_t* msg, uint32_t msg_len, uint32_t break; } - logger.debug("Integrity check input: COUNT %" PRIu32 ", Bearer ID %d, Direction %s", - count, - cfg.bearer_id, - cfg.rx_direction == SECURITY_DIRECTION_DOWNLINK ? "Downlink" : "Uplink"); - logger.debug(k_int, 32, "Integrity check key:"); - logger.debug(msg, msg_len, "Integrity check input msg:"); - if (sec_cfg.integ_algo != INTEGRITY_ALGORITHM_ID_EIA0) { for (uint8_t i = 0; i < 4; i++) { if (mac[i] != mac_exp[i]) { - logger.error(mac_exp, 4, "MAC mismatch (expected)"); - logger.error(mac, 4, "MAC mismatch (found)"); is_valid = false; break; } } - if (is_valid) { - logger.info(mac_exp, 4, "MAC match"); - } + srslog::log_channel& channel = is_valid ? logger.debug : logger.warning; + channel("Integrity check input - COUNT %" PRIu32 ", Bearer ID %d, Direction %s", + count, + cfg.bearer_id, + cfg.rx_direction == SECURITY_DIRECTION_DOWNLINK ? "Downlink" : "Uplink"); + channel(k_int, 32, "Integrity check key:"); + channel(mac_exp, 4, "MAC %s (expected):", is_valid ? "match" : "mismatch"); + channel(mac, 4, "MAC %s (found):", is_valid ? "match" : "mismatch"); + channel(msg, msg_len, "Integrity check input msg (Bytes=%" PRIu32 "):", msg_len); } return is_valid; diff --git a/lib/src/upper/pdcp_entity_lte.cc b/lib/src/pdcp/pdcp_entity_lte.cc similarity index 93% rename from lib/src/upper/pdcp_entity_lte.cc rename to lib/src/pdcp/pdcp_entity_lte.cc index 969fdae60..c38ecaecf 100644 --- a/lib/src/upper/pdcp_entity_lte.cc +++ b/lib/src/pdcp/pdcp_entity_lte.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -60,7 +60,18 @@ pdcp_entity_lte::~pdcp_entity_lte() bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_) { - cfg = cnfg_; + if (active) { + // Already configured + if (cnfg_ != cfg) { + logger.error("Bearer reconfiguration not supported. LCID=%s.", rb_name.c_str()); + return false; + } + return true; + } + + cfg = cnfg_; + rb_name = cfg.get_rb_name(); + maximum_pdcp_sn = (1u << cfg.sn_len) - 1u; st.last_submitted_pdcp_rx_sn = maximum_pdcp_sn; if (is_srb()) { @@ -79,7 +90,7 @@ bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_) // Queue Helpers maximum_allocated_sns_window = (1u << cfg.sn_len) / 2u; - logger.info("Init %s with bearer ID: %d", rrc->get_rb_name(lcid), cfg.bearer_id); + logger.info("Init %s with bearer ID: %d", rb_name.c_str(), cfg.bearer_id); logger.info("SN len bits: %d, SN len bytes: %d, reordering window: %d, Maximum SN: %d, discard timer: %d ms", cfg.sn_len, cfg.hdr_len_bytes, @@ -89,7 +100,7 @@ bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_) logger.info("Status Report Required: %s", cfg.status_report_required ? "True" : "False"); if (is_drb() and not rlc->rb_is_um(lcid)) { - undelivered_sdus = std::unique_ptr(new undelivered_sdus_queue(task_sched)); + undelivered_sdus = std::unique_ptr(new undelivered_sdus_queue(task_sched, maximum_pdcp_sn)); rx_counts_info.reserve(reordering_window); } @@ -104,7 +115,7 @@ bool pdcp_entity_lte::configure(const pdcp_config_t& cnfg_) // Reestablishment procedure: 36.323 5.2 void pdcp_entity_lte::reestablish() { - logger.info("Re-establish %s with bearer ID: %d", rrc->get_rb_name(lcid), cfg.bearer_id); + logger.info("Re-establish %s with bearer ID: %d", rb_name.c_str(), cfg.bearer_id); // For SRBs if (is_srb()) { st.next_pdcp_tx_sn = 0; @@ -126,7 +137,7 @@ void pdcp_entity_lte::reestablish() void pdcp_entity_lte::reset() { if (active) { - logger.debug("Reset %s", rrc->get_rb_name(lcid)); + logger.debug("Reset %s", rb_name.c_str()); } active = false; } @@ -134,8 +145,18 @@ void pdcp_entity_lte::reset() // GW/RRC interface void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn) { + if (!active) { + logger.warning("Dropping %s SDU due to inactive bearer", rb_name.c_str()); + return; + } + + if (rlc->is_suspended(lcid)) { + logger.warning("Trying to send SDU while re-establishment is in progress. Dropping SDU. LCID=%d", lcid); + return; + } + if (rlc->sdu_queue_is_full(lcid)) { - logger.info(sdu->msg, sdu->N_bytes, "Dropping %s SDU due to full queue", rrc->get_rb_name(lcid)); + logger.info(sdu->msg, sdu->N_bytes, "Dropping %s SDU due to full queue", rb_name.c_str()); return; } @@ -152,7 +173,7 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn) // If the bearer is mapped to RLC AM, save TX_COUNT and a copy of the PDU. // This will be used for reestablishment, where unack'ed PDUs will be re-transmitted. // PDUs will be removed from the queue, either when the lower layers will report - // a succesfull transmission or when the discard timer expires. + // a successfull transmission or when the discard timer expires. // Status report will also use this queue, to know the First Missing SDU (FMS). if (!rlc->rb_is_um(lcid) and is_drb()) { if (not store_sdu(used_sn, sdu)) { @@ -161,7 +182,6 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn) return; } } - // check for pending security config in transmit direction if (enable_security_tx_sn != -1 && enable_security_tx_sn == static_cast(tx_count)) { enable_integrity(DIRECTION_TX); @@ -190,7 +210,7 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn) logger.info(sdu->msg, sdu->N_bytes, "TX %s PDU, SN=%d, integrity=%s, encryption=%s", - rrc->get_rb_name(lcid), + rb_name.c_str(), used_sn, srsran_direction_text[integrity_direction], srsran_direction_text[encryption_direction]); @@ -210,12 +230,21 @@ void pdcp_entity_lte::write_sdu(unique_byte_buffer_t sdu, int upper_sn) // Pass PDU to lower layers metrics.num_tx_pdus++; metrics.num_tx_pdu_bytes += sdu->N_bytes; + // Count TX'd bytes as if they were ACK'd if RLC is UM + if (rlc->rb_is_um(lcid)) { + metrics.num_tx_acked_bytes = metrics.num_tx_pdu_bytes; + } rlc->write_sdu(lcid, std::move(sdu)); } // RLC interface void pdcp_entity_lte::write_pdu(unique_byte_buffer_t pdu) { + if (!active) { + logger.warning("Dropping %s PDU due to inactive bearer", rb_name.c_str()); + return; + } + // Handle control PDUs if (is_drb() && is_control_pdu(pdu)) { logger.info("Handling PDCP control PDU"); @@ -242,7 +271,7 @@ void pdcp_entity_lte::write_pdu(unique_byte_buffer_t pdu) logger.info(pdu->msg, pdu->N_bytes, "%s Rx PDU SN=%d (%d B, integrity=%s, encryption=%s)", - rrc->get_rb_name(lcid), + rb_name.c_str(), sn, pdu->N_bytes, srsran_direction_text[integrity_direction], @@ -302,7 +331,7 @@ void pdcp_entity_lte::handle_srb_pdu(srsran::unique_byte_buffer_t pdu) cipher_decrypt(&pdu->msg[cfg.hdr_len_bytes], pdu->N_bytes - cfg.hdr_len_bytes, count, &pdu->msg[cfg.hdr_len_bytes]); } - logger.debug(pdu->msg, pdu->N_bytes, "%s Rx SDU SN=%d", rrc->get_rb_name(lcid), sn); + logger.debug(pdu->msg, pdu->N_bytes, "%s Rx SDU SN=%d", rb_name.c_str(), sn); // Extract MAC uint8_t mac[4]; @@ -311,8 +340,11 @@ void pdcp_entity_lte::handle_srb_pdu(srsran::unique_byte_buffer_t pdu) // Perfrom integrity checks if (integrity_direction == DIRECTION_RX || integrity_direction == DIRECTION_TXRX) { if (not integrity_verify(pdu->msg, pdu->N_bytes, count, mac)) { - logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rrc->get_rb_name(lcid)); + logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rb_name.c_str()); + rrc->notify_pdcp_integrity_error(lcid); return; // Discard + } else { + logger.debug(pdu->msg, pdu->N_bytes, "%s: Integrity verification successful", rb_name.c_str()); } } @@ -349,7 +381,7 @@ void pdcp_entity_lte::handle_um_drb_pdu(srsran::unique_byte_buffer_t pdu) cipher_decrypt(pdu->msg, pdu->N_bytes, count, pdu->msg); } - logger.debug(pdu->msg, pdu->N_bytes, "%s Rx PDU SN=%d", rrc->get_rb_name(lcid), sn); + logger.debug(pdu->msg, pdu->N_bytes, "%s Rx PDU SN=%d", rb_name.c_str(), sn); st.next_pdcp_rx_sn = sn + 1; if (st.next_pdcp_rx_sn > maximum_pdcp_sn) { @@ -413,7 +445,7 @@ void pdcp_entity_lte::handle_am_drb_pdu(srsran::unique_byte_buffer_t pdu) // Decrypt cipher_decrypt(pdu->msg, pdu->N_bytes, count, pdu->msg); - logger.debug(pdu->msg, pdu->N_bytes, "%s Rx SDU SN=%d", rrc->get_rb_name(lcid), sn); + logger.debug(pdu->msg, pdu->N_bytes, "%s Rx SDU SN=%d", rb_name.c_str(), sn); // Update info on last PDU submitted to upper layers st.last_submitted_pdcp_rx_sn = sn; @@ -461,10 +493,14 @@ void pdcp_entity_lte::update_rx_counts_queue(uint32_t rx_count) rx_counts_info.pop_back(); fmc++; } - logger.debug("Queue too large. Updating. New FMC=%d, new back=%d, new queue_size=%zu", - fmc, - rx_counts_info.back(), - rx_counts_info.size()); + if (not rx_counts_info.empty()) { + logger.debug("Queue too large. Updating. New FMC=%d, new back=%d, new queue_size=%zu", + fmc, + rx_counts_info.back(), + rx_counts_info.size()); + } else { + logger.debug("Queue too large. Updating. New FMC=%d, new queue_size=%zu", fmc, rx_counts_info.size()); + } } if (rx_counts_info.empty()) { @@ -772,7 +808,8 @@ void pdcp_entity_lte::notify_failure(const pdcp_sn_vector_t& pdcp_sns) ***************************************************************************/ bool pdcp_entity_lte::check_valid_config() { - if (cfg.sn_len != PDCP_SN_LEN_5 && cfg.sn_len != PDCP_SN_LEN_7 && cfg.sn_len != PDCP_SN_LEN_12 && cfg.sn_len != PDCP_SN_LEN_18) { + if (cfg.sn_len != PDCP_SN_LEN_5 && cfg.sn_len != PDCP_SN_LEN_7 && cfg.sn_len != PDCP_SN_LEN_12 && + cfg.sn_len != PDCP_SN_LEN_18) { logger.error("Trying to configure bearer with invalid SN LEN=%d", cfg.sn_len); return false; } @@ -784,10 +821,6 @@ bool pdcp_entity_lte::check_valid_config() logger.error("Trying to configure SRB or RLC AM bearer with SN LEN of 7"); return false; } - if (cfg.sn_len == PDCP_SN_LEN_12 && is_srb()) { - logger.error("Trying to configure SRB with SN LEN of 12."); - return false; - } return true; } @@ -840,7 +873,7 @@ void pdcp_entity_lte::reset_metrics() /**************************************************************************** * Undelivered SDUs queue helpers ***************************************************************************/ -undelivered_sdus_queue::undelivered_sdus_queue(srsran::task_sched_handle task_sched) +undelivered_sdus_queue::undelivered_sdus_queue(srsran::task_sched_handle task_sched, uint32_t sn_mod) : sn_mod(sn_mod) { for (auto& e : sdus) { e.discard_timer = task_sched.get_unique_timer(); diff --git a/lib/src/upper/pdcp_entity_nr.cc b/lib/src/pdcp/pdcp_entity_nr.cc similarity index 51% rename from lib/src/upper/pdcp_entity_nr.cc rename to lib/src/pdcp/pdcp_entity_nr.cc index 8c9c12c76..aed6de94b 100644 --- a/lib/src/upper/pdcp_entity_nr.cc +++ b/lib/src/pdcp/pdcp_entity_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -41,36 +41,62 @@ pdcp_entity_nr::pdcp_entity_nr(srsue::rlc_interface_pdcp* rlc_, encryption_direction = DIRECTION_NONE; } -pdcp_entity_nr::~pdcp_entity_nr() {} +bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_) +{ + if (active) { + // Already configured + if (cnfg_ != cfg) { + logger.error("Bearer reconfiguration not supported. LCID=%s.", rb_name.c_str()); + return false; + } + return true; + } + + cfg = cnfg_; + rb_name = cfg.get_rb_name(); + window_size = 1 << (cfg.sn_len - 1); + + rlc_mode = rlc->rb_is_um(lcid) ? rlc_mode_t::UM : rlc_mode_t::AM; + + // t-Reordering timer + if (cfg.t_reordering != pdcp_t_reordering_t::infinity) { + reordering_timer = task_sched.get_unique_timer(); + if (static_cast(cfg.t_reordering) > 0) { + reordering_timer.set(static_cast(cfg.t_reordering), *reordering_fnc); + } + } else if (rlc_mode == rlc_mode_t::UM) { + logger.warning("%s possible PDCP-NR misconfiguration: using infinite re-ordering timer with RLC UM bearer.", + rb_name); + } + + active = true; + logger.info("%s PDCP-NR entity configured. SN_LEN=%d, Discard timer %d, Re-ordering timer %d, RLC=%s, RAT=%s", + rb_name, + cfg.sn_len, + cfg.discard_timer, + cfg.t_reordering, + rlc_mode == rlc_mode_t::UM ? "UM" : "AM", + to_string(cfg.rat)); + + // disable discard timer if using UM + if (rlc_mode == rlc_mode_t::UM) { + cfg.discard_timer = pdcp_discard_timer_t::infinity; + } + return true; +} // Reestablishment procedure: 38.323 5.2 void pdcp_entity_nr::reestablish() { - logger.info("Re-establish %s with bearer ID: %d", rrc->get_rb_name(lcid), cfg.bearer_id); + logger.info("Re-establish %s with bearer ID: %d", rb_name.c_str(), cfg.bearer_id); // TODO } -bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_) -{ - cfg = cnfg_; - window_size = 1 << (cfg.sn_len - 1); - - // Timers - reordering_timer = task_sched.get_unique_timer(); - - // configure timer - if (static_cast(cfg.t_reordering) > 0) { - reordering_timer.set(static_cast(cfg.t_reordering), *reordering_fnc); - } - active = true; - return true; -} - // Used to stop/pause the entity (called on RRC conn release) void pdcp_entity_nr::reset() { active = false; - logger.debug("Reset %s", rrc->get_rb_name(lcid)); + logger.debug("Reset %s", rb_name.c_str()); } // SDAP/RRC interface @@ -79,11 +105,17 @@ void pdcp_entity_nr::write_sdu(unique_byte_buffer_t sdu, int sn) // Log SDU logger.info(sdu->msg, sdu->N_bytes, - "TX %s SDU, integrity=%s, encryption=%s", - rrc->get_rb_name(lcid), + "TX %s SDU (%dB), integrity=%s, encryption=%s", + rb_name.c_str(), + sdu->N_bytes, srsran_direction_text[integrity_direction], srsran_direction_text[encryption_direction]); + if (rlc->sdu_queue_is_full(lcid)) { + logger.info(sdu->msg, sdu->N_bytes, "Dropping %s SDU due to full queue", rb_name.c_str()); + return; + } + // Check for COUNT overflow if (tx_overflow) { logger.warning("TX_NEXT has overflowed. Dropping packet"); @@ -105,22 +137,43 @@ void pdcp_entity_nr::write_sdu(unique_byte_buffer_t sdu, int sn) // Perform header compression TODO - // Integrity protection - uint8_t mac[4]; - integrity_generate(sdu->msg, sdu->N_bytes, tx_next, mac); - - // Ciphering - cipher_encrypt(sdu->msg, sdu->N_bytes, tx_next, sdu->msg); - // Write PDCP header info write_data_header(sdu, tx_next); + // TS 38.323, section 5.9: Integrity protection + // The data unit that is integrity protected is the PDU header + // and the data part of the PDU before ciphering. + uint8_t mac[4] = {}; + if (is_srb() || (is_drb() && (integrity_direction == DIRECTION_TX || integrity_direction == DIRECTION_TXRX))) { + integrity_generate(sdu->msg, sdu->N_bytes, tx_next, mac); + } // Append MAC-I - append_mac(sdu, mac); + if (is_srb() || (is_drb() && (integrity_direction == DIRECTION_TX || integrity_direction == DIRECTION_TXRX))) { + append_mac(sdu, mac); + } + + // TS 38.323, section 5.8: Ciphering + // The data unit that is ciphered is the MAC-I and the + // data part of the PDCP Data PDU except the + // SDAP header and the SDAP Control PDU if included in the PDCP SDU. + if (encryption_direction == DIRECTION_TX || encryption_direction == DIRECTION_TXRX) { + cipher_encrypt( + &sdu->msg[cfg.hdr_len_bytes], sdu->N_bytes - cfg.hdr_len_bytes, tx_next, &sdu->msg[cfg.hdr_len_bytes]); + } // Set meta-data for RLC AM sdu->md.pdcp_sn = tx_next; + logger.info(sdu->msg, + sdu->N_bytes, + "TX %s PDU (%dB), HFN=%d, SN=%d, integrity=%s, encryption=%s", + rb_name.c_str(), + sdu->N_bytes, + HFN(tx_next), + SN(tx_next), + srsran_direction_text[integrity_direction], + srsran_direction_text[encryption_direction]); + // Check if PDCP is associated with more than on RLC entity TODO // Write to lower layers rlc->write_sdu(lcid, std::move(sdu)); @@ -136,25 +189,36 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu) logger.info(pdu->msg, pdu->N_bytes, "RX %s PDU (%d B), integrity=%s, encryption=%s", - rrc->get_rb_name(lcid), + rb_name.c_str(), pdu->N_bytes, srsran_direction_text[integrity_direction], srsran_direction_text[encryption_direction]); + if (rx_overflow) { + logger.warning("Rx PDCP COUNTs have overflowed. Discarding SDU."); + return; + } + // Sanity check if (pdu->N_bytes <= cfg.hdr_len_bytes) { return; } + logger.debug("Rx PDCP state - RX_NEXT=%u, RX_DELIV=%u, RX_REORD=%u", rx_next, rx_deliv, rx_reord); // Extract RCVD_SN from header uint32_t rcvd_sn = read_data_header(pdu); - discard_data_header(pdu); // TODO: Check wheather the header is part of integrity check. - // Extract MAC - uint8_t mac[4]; - extract_mac(pdu, mac); - - // Calculate RCVD_COUNT + /* + * Calculate RCVD_COUNT: + * + * - if RCVD_SN < SN(RX_DELIV) – Window_Size: + * - RCVD_HFN = HFN(RX_DELIV) + 1. + * - else if RCVD_SN >= SN(RX_DELIV) + Window_Size: + * - RCVD_HFN = HFN(RX_DELIV) – 1. + * - else: + * - RCVD_HFN = HFN(RX_DELIV); + * - RCVD_COUNT = [RCVD_HFN, RCVD_SN]. + */ uint32_t rcvd_hfn, rcvd_count; if ((int64_t)rcvd_sn < (int64_t)SN(rx_deliv) - (int64_t)window_size) { rcvd_hfn = HFN(rx_deliv) + 1; @@ -165,18 +229,56 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu) } rcvd_count = COUNT(rcvd_hfn, rcvd_sn); - logger.debug("RCVD_HFN %u RCVD_SN %u, RCVD_COUNT %u", rcvd_hfn, rcvd_sn, rcvd_count); + logger.debug("Estimated RCVD_HFN=%u, RCVD_SN=%u, RCVD_COUNT=%u", rcvd_hfn, rcvd_sn, rcvd_count); - // Decripting - cipher_decrypt(pdu->msg, pdu->N_bytes, rcvd_count, pdu->msg); - - // Integrity check - bool is_valid = integrity_verify(pdu->msg, pdu->N_bytes, rcvd_count, mac); - if (!is_valid) { - return; // Invalid packet, drop. + /* + * TS 38.323, section 5.8: Deciphering + * + * The data unit that is ciphered is the MAC-I and the + * data part of the PDCP Data PDU except the + * SDAP header and the SDAP Control PDU if included in the PDCP SDU. + */ + if (encryption_direction == DIRECTION_RX || encryption_direction == DIRECTION_TXRX) { + cipher_decrypt( + &pdu->msg[cfg.hdr_len_bytes], pdu->N_bytes - cfg.hdr_len_bytes, rcvd_count, &pdu->msg[cfg.hdr_len_bytes]); } - // Check valid rcvd_count + /* + * Extract MAC-I: + * Always extract from SRBs, only extract from DRBs if integrity is enabled + */ + uint8_t mac[4] = {}; + if (is_srb() || (is_drb() && (integrity_direction == DIRECTION_TX || integrity_direction == DIRECTION_TXRX))) { + extract_mac(pdu, mac); + } + + /* + * TS 38.323, section 5.9: Integrity verification + * + * The data unit that is integrity protected is the PDU header + * and the data part of the PDU before ciphering. + */ + if (integrity_direction == DIRECTION_TX || integrity_direction == DIRECTION_TXRX) { + bool is_valid = integrity_verify(pdu->msg, pdu->N_bytes, rcvd_count, mac); + if (!is_valid) { + logger.error(pdu->msg, pdu->N_bytes, "%s Dropping PDU", rb_name.c_str()); + rrc->notify_pdcp_integrity_error(lcid); + return; // Invalid packet, drop. + } else { + logger.debug(pdu->msg, pdu->N_bytes, "%s: Integrity verification successful", rb_name.c_str()); + } + } + + // After checking the integrity, we can discard the header. + discard_data_header(pdu); + + /* + * Check valid rcvd_count: + * + * - if RCVD_COUNT < RX_DELIV; or + * - if the PDCP Data PDU with COUNT = RCVD_COUNT has been received before: + * - discard the PDCP Data PDU; + */ if (rcvd_count < rx_deliv) { logger.debug("Out-of-order after time-out, duplicate or COUNT wrap-around"); logger.debug("RCVD_COUNT %u, RCVD_COUNT %u", rcvd_count, rx_deliv); @@ -185,6 +287,7 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu) // Check if PDU has been received if (reorder_queue.find(rcvd_count) != reorder_queue.end()) { + logger.debug("Duplicate PDU, dropping"); return; // PDU already present, drop. } @@ -199,25 +302,36 @@ void pdcp_entity_nr::write_pdu(unique_byte_buffer_t pdu) // TODO if out-of-order configured, submit to upper layer if (rcvd_count == rx_deliv) { - // Deliver to upper layers in ascending order of associeted COUNT + // Deliver to upper layers in ascending order of associated COUNT deliver_all_consecutive_counts(); } // Handle reordering timers if (reordering_timer.is_running() and rx_deliv >= rx_reord) { reordering_timer.stop(); + logger.debug("Stopped t-Reordering - RX_DELIV=%d, RX_REORD=%ld", rx_deliv, rx_reord); } - if (not reordering_timer.is_running() and rx_deliv < rx_next) { - rx_reord = rx_next; - reordering_timer.run(); + if (cfg.t_reordering != pdcp_t_reordering_t::infinity) { + if (not reordering_timer.is_running() and rx_deliv < rx_next) { + rx_reord = rx_next; + reordering_timer.run(); + logger.debug("Started t-Reordering - RX_REORD=%ld, RX_DELIV=%ld, RX_NEXT=%ld", rx_reord, rx_deliv, rx_next); + } } + + logger.debug("Rx PDCP state - RX_NEXT=%u, RX_DELIV=%u, RX_REORD=%u", rx_next, rx_deliv, rx_reord); } // Notification of delivery/failure void pdcp_entity_nr::notify_delivery(const pdcp_sn_vector_t& pdcp_sns) { logger.debug("Received delivery notification from RLC. Nof SNs=%ld", pdcp_sns.size()); + for (uint32_t sn : pdcp_sns) { + // Remove timer from map + logger.debug("Stopping discard timer for SN=%ld", sn); + discard_timers_map.erase(sn); + } } void pdcp_entity_nr::notify_failure(const pdcp_sn_vector_t& pdcp_sns) @@ -229,7 +343,7 @@ void pdcp_entity_nr::notify_failure(const pdcp_sn_vector_t& pdcp_sns) * Packing / Unpacking Helpers */ -// Deliver all consecutivly associated COUNTs. +// Deliver all consecutively associated COUNTs. // Update RX_NEXT after submitting to higher layers void pdcp_entity_nr::deliver_all_consecutive_counts() { @@ -261,9 +375,10 @@ void pdcp_entity_nr::deliver_all_consecutive_counts() // Reordering Timer Callback (t-reordering) void pdcp_entity_nr::reordering_callback::operator()(uint32_t timer_id) { - parent->logger.debug("Reordering timer expired"); + parent->logger.info( + "Reordering timer expired. RX_REORD=%u, re-order queue size=%ld", parent->rx_reord, parent->reorder_queue.size()); - // Deliver all PDCP SDU(s) with associeted COUNT value(s) < RX_REORD + // Deliver all PDCP SDU(s) with associated COUNT value(s) < RX_REORD for (std::map::iterator it = parent->reorder_queue.begin(); it != parent->reorder_queue.end() && it->first < parent->rx_reord; parent->reorder_queue.erase(it++)) { @@ -271,10 +386,17 @@ void pdcp_entity_nr::reordering_callback::operator()(uint32_t timer_id) parent->pass_to_upper_layers(std::move(it->second)); } - // Deliver all PDCP SDU(s) consecutivly associeted COUNT value(s) starting from RX_REORD + // Update RX_DELIV to the first PDCP SDU not delivered to the upper layers + parent->rx_deliv = parent->rx_reord; + + // Deliver all PDCP SDU(s) consecutively associated COUNT value(s) starting from RX_REORD parent->deliver_all_consecutive_counts(); if (parent->rx_deliv < parent->rx_next) { + parent->logger.debug("Updating RX_REORD to %ld. Old RX_REORD=%ld, RX_DELIV=%ld", + parent->rx_next, + parent->rx_reord, + parent->rx_deliv); parent->rx_reord = parent->rx_next; parent->reordering_timer.run(); } @@ -283,7 +405,7 @@ void pdcp_entity_nr::reordering_callback::operator()(uint32_t timer_id) // Discard Timer Callback (discardTimer) void pdcp_entity_nr::discard_callback::operator()(uint32_t timer_id) { - parent->logger.debug("Discard timer expired for PDU with SN = %d", discard_sn); + parent->logger.debug("Discard timer expired for PDU with SN=%d", discard_sn); // Notify the RLC of the discard. It's the RLC to actually discard, if no segment was transmitted yet. parent->rlc->discard_sdu(parent->lcid, discard_sn); diff --git a/lib/src/phy/CMakeLists.txt b/lib/src/phy/CMakeLists.txt index e5a47a4b2..82be2bb9d 100644 --- a/lib/src/phy/CMakeLists.txt +++ b/lib/src/phy/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -35,6 +35,8 @@ add_subdirectory(resampling) add_subdirectory(scrambling) add_subdirectory(ue) add_subdirectory(enb) +add_subdirectory(gnb) +add_subdirectory(cfr) set(srsran_srcs $ $ $ @@ -51,8 +53,10 @@ set(srsran_srcs $ $ $ $ + $ + $ ) add_library(srsran_phy STATIC ${srsran_srcs} ) target_link_libraries(srsran_phy pthread m ${FFT_LIBRARIES}) -INSTALL(TARGETS srsran_phy DESTINATION ${LIBRARY_DIR}) +install(TARGETS srsran_phy DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/phy/agc/CMakeLists.txt b/lib/src/phy/agc/CMakeLists.txt index 3709e1bb2..9c9d9d235 100644 --- a/lib/src/phy/agc/CMakeLists.txt +++ b/lib/src/phy/agc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/agc/agc.c b/lib/src/phy/agc/agc.c index 350dd0f1c..afaaac1dd 100644 --- a/lib/src/phy/agc/agc.c +++ b/lib/src/phy/agc/agc.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/cfr/CMakeLists.txt b/lib/src/phy/cfr/CMakeLists.txt new file mode 100644 index 000000000..0c7346d4a --- /dev/null +++ b/lib/src/phy/cfr/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SRCS cfr.c) +add_library(srsran_cfr OBJECT ${SRCS}) + +add_subdirectory(test) + diff --git a/lib/src/phy/cfr/cfr.c b/lib/src/phy/cfr/cfr.c new file mode 100644 index 000000000..cc4b7045b --- /dev/null +++ b/lib/src/phy/cfr/cfr.c @@ -0,0 +1,402 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/cfr/cfr.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" + +// Uncomment this to use a literal implementation of the CFR algorithm +// #define CFR_PEAK_EXTRACTION + +// Uncomment this to filter by zeroing the FFT bins instead of applying a frequency window +#define CFR_LPF_WITH_ZEROS + +static inline float cfr_symb_peak(float* in_abs, int len); + +void srsran_cfr_process(srsran_cfr_t* q, cf_t* in, cf_t* out) +{ + if (q == NULL || in == NULL || out == NULL) { + return; + } + if (!q->cfg.cfr_enable) { + // If no processing, copy the input samples into the output buffer + if (in != out) { + srsran_vec_cf_copy(out, in, q->cfg.symbol_sz); + } + return; + } + + const float alpha = q->cfg.alpha; + const uint32_t symbol_sz = q->cfg.symbol_sz; + float beta = 0.0f; + + // Calculate absolute input values + srsran_vec_abs_cf(in, q->abs_buffer_in, symbol_sz); + + // In auto modes, the beta threshold is calculated based on the measured PAPR + if (q->cfg.cfr_mode == SRSRAN_CFR_THR_MANUAL) { + beta = q->cfg.manual_thr; + } else { + const float symb_peak = cfr_symb_peak(q->abs_buffer_in, q->cfg.symbol_sz); + const float pwr_symb_peak = symb_peak * symb_peak; + const float pwr_symb_avg = srsran_vec_avg_power_ff(q->abs_buffer_in, q->cfg.symbol_sz); + float symb_papr = 0.0f; + + if (isnormal(pwr_symb_avg) && isnormal(pwr_symb_peak)) { + if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_CMA) { + // Once cma_n reaches its max value, stop incrementing to prevent overflow. + // This turns the averaging into a de-facto EMA with an extremely slow time constant + q->pwr_avg_in = SRSRAN_VEC_CMA(pwr_symb_avg, q->pwr_avg_in, q->cma_n++); + q->cma_n = q->cma_n & UINT64_MAX ? q->cma_n : UINT64_MAX; + } else if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_EMA) { + q->pwr_avg_in = SRSRAN_VEC_EMA(pwr_symb_avg, q->pwr_avg_in, q->cfg.ema_alpha); + } + + symb_papr = pwr_symb_peak / q->pwr_avg_in; + } + float papr_reduction = symb_papr / q->max_papr_lin; + beta = (papr_reduction > 1) ? symb_peak / sqrtf(papr_reduction) : 0; + } + + // Clipping algorithm + if (isnormal(beta)) { +#ifdef CFR_PEAK_EXTRACTION + srsran_vec_cf_zero(q->peak_buffer, symbol_sz); + cf_t clip_thr = 0; + for (int i = 0; i < symbol_sz; i++) { + if (q->abs_buffer_in[i] > beta) { + clip_thr = beta * (in[i] / q->abs_buffer_in[i]); + q->peak_buffer[i] = in[i] - clip_thr; + } + } + + // Apply FFT filter to the peak signal + srsran_dft_run_c(&q->fft_plan, q->peak_buffer, q->peak_buffer); +#ifdef CFR_LPF_WITH_ZEROS + srsran_vec_cf_zero(q->peak_buffer + q->lpf_bw / 2 + q->cfg.dc_sc, symbol_sz - q->cfg.symbol_bw - q->cfg.dc_sc); +#else /* CFR_LPF_WITH_ZEROS */ + srsran_vec_prod_cfc(q->peak_buffer, q->lpf_spectrum, q->peak_buffer, symbol_sz); +#endif /* CFR_LPF_WITH_ZEROS */ + srsran_dft_run_c(&q->ifft_plan, q->peak_buffer, q->peak_buffer); + + // Scale the peak signal according to alpha + srsran_vec_sc_prod_cfc(q->peak_buffer, alpha, q->peak_buffer, symbol_sz); + + // Apply the filtered clipping + srsran_vec_sub_ccc(in, q->peak_buffer, out, symbol_sz); +#else /* CFR_PEAK_EXTRACTION */ + + // Generate a clipping envelope and clip the signal + srsran_vec_gen_clip_env(q->abs_buffer_in, beta, alpha, q->abs_buffer_in, symbol_sz); + srsran_vec_prod_cfc(in, q->abs_buffer_in, out, symbol_sz); + + // FFT filter + srsran_dft_run_c(&q->fft_plan, out, out); +#ifdef CFR_LPF_WITH_ZEROS + srsran_vec_cf_zero(out + q->lpf_bw / 2 + q->cfg.dc_sc, symbol_sz - q->cfg.symbol_bw - q->cfg.dc_sc); +#else /* CFR_LPF_WITH_ZEROS */ + srsran_vec_prod_cfc(out, q->lpf_spectrum, out, symbol_sz); +#endif /* CFR_LPF_WITH_ZEROS */ + srsran_dft_run_c(&q->ifft_plan, out, out); +#endif /* CFR_PEAK_EXTRACTION */ + + } else { + // If no processing, copy the input samples into the output buffer + if (in != out) { + srsran_vec_cf_copy(out, in, symbol_sz); + } + } + if (q->cfg.cfr_mode != SRSRAN_CFR_THR_MANUAL && q->cfg.measure_out_papr) { + srsran_vec_abs_cf(in, q->abs_buffer_out, symbol_sz); + + const float symb_peak = cfr_symb_peak(q->abs_buffer_out, q->cfg.symbol_sz); + const float pwr_symb_peak = symb_peak * symb_peak; + const float pwr_symb_avg = srsran_vec_avg_power_ff(q->abs_buffer_out, q->cfg.symbol_sz); + float symb_papr = 0.0f; + + if (isnormal(pwr_symb_avg) && isnormal(pwr_symb_peak)) { + if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_CMA) { + // Do not increment cma_n here, as it is being done when calculating input PAPR + q->pwr_avg_out = SRSRAN_VEC_CMA(pwr_symb_avg, q->pwr_avg_out, q->cma_n); + } + + else if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_EMA) { + q->pwr_avg_out = SRSRAN_VEC_EMA(pwr_symb_avg, q->pwr_avg_out, q->cfg.ema_alpha); + } + + symb_papr = pwr_symb_peak / q->pwr_avg_out; + } + + const float papr_out_db = srsran_convert_power_to_dB(symb_papr); + printf("Output PAPR: %f dB\n", papr_out_db); + } +} + +int srsran_cfr_init(srsran_cfr_t* q, srsran_cfr_cfg_t* cfg) +{ + int ret = SRSRAN_ERROR; + if (q == NULL || cfg == NULL) { + ERROR("Error, invalid inputs"); + ret = SRSRAN_ERROR_INVALID_INPUTS; + goto clean_exit; + } + if (!cfg->symbol_sz || !cfg->symbol_bw || cfg->alpha < 0 || cfg->alpha > 1) { + ERROR("Error, invalid configuration"); + goto clean_exit; + } + if (cfg->cfr_mode == SRSRAN_CFR_THR_INVALID) { + ERROR("Error, invalid CFR mode"); + goto clean_exit; + } + if (cfg->cfr_mode == SRSRAN_CFR_THR_MANUAL && cfg->manual_thr <= 0) { + ERROR("Error, invalid configuration for manual threshold"); + goto clean_exit; + } + if (cfg->cfr_mode == SRSRAN_CFR_THR_AUTO_CMA && (cfg->max_papr_db <= 0)) { + ERROR("Error, invalid configuration for CMA averaging"); + goto clean_exit; + } + if (cfg->cfr_mode == SRSRAN_CFR_THR_AUTO_EMA && + (cfg->max_papr_db <= 0 || (cfg->ema_alpha < 0 || cfg->ema_alpha > 1))) { + ERROR("Error, invalid configuration for EMA averaging"); + goto clean_exit; + } + + // Copy all the configuration parameters + q->cfg = *cfg; + q->max_papr_lin = srsran_convert_dB_to_power(q->cfg.max_papr_db); + q->pwr_avg_in = CFR_EMA_INIT_AVG_PWR; + q->cma_n = 0; + + if (q->cfg.measure_out_papr) { + q->pwr_avg_out = CFR_EMA_INIT_AVG_PWR; + } + + if (q->abs_buffer_in) { + free(q->abs_buffer_in); + } + q->abs_buffer_in = srsran_vec_f_malloc(q->cfg.symbol_sz); + if (!q->abs_buffer_in) { + ERROR("Error allocating abs_buffer_in"); + goto clean_exit; + } + + if (q->abs_buffer_out) { + free(q->abs_buffer_out); + } + q->abs_buffer_out = srsran_vec_f_malloc(q->cfg.symbol_sz); + if (!q->abs_buffer_out) { + ERROR("Error allocating abs_buffer_out"); + goto clean_exit; + } + + if (q->peak_buffer) { + free(q->peak_buffer); + } + q->peak_buffer = srsran_vec_cf_malloc(q->cfg.symbol_sz); + if (!q->peak_buffer) { + ERROR("Error allocating peak_buffer"); + goto clean_exit; + } + + // Allocate the filter + if (q->lpf_spectrum) { + free(q->lpf_spectrum); + } + q->lpf_spectrum = srsran_vec_f_malloc(q->cfg.symbol_sz); + if (!q->lpf_spectrum) { + ERROR("Error allocating lpf_spectrum"); + goto clean_exit; + } + + // The LPF bandwidth is exactly the OFDM symbol bandwidth, in number of FFT bins + q->lpf_bw = q->cfg.symbol_bw; + + // Initialise the filter + srsran_vec_f_zero(q->lpf_spectrum, q->cfg.symbol_sz); + + // DC subcarrier is in position 0, so the OFDM symbol can go from index 1 to q->lpf_bw / 2 + 1 + for (uint32_t i = 0; i < q->lpf_bw / 2 + q->cfg.dc_sc; i++) { + q->lpf_spectrum[i] = 1; + } + for (uint32_t i = q->cfg.symbol_sz - q->lpf_bw / 2; i < q->cfg.symbol_sz; i++) { + q->lpf_spectrum[i] = 1; + } + + // FFT plans, for 1 OFDM symbol + if (q->fft_plan.size) { + // Replan if it was initialised previously with bigger FFT size + if (q->fft_plan.size >= q->cfg.symbol_sz) { + if (srsran_dft_replan(&q->fft_plan, q->cfg.symbol_sz)) { + ERROR("Replaning DFT plan"); + goto clean_exit; + } + } else { + srsran_dft_plan_free(&q->fft_plan); + if (srsran_dft_plan_c(&q->fft_plan, q->cfg.symbol_sz, SRSRAN_DFT_FORWARD)) { + ERROR("Creating DFT plan"); + goto clean_exit; + } + } + } else { + // Create plan from zero otherwise + if (srsran_dft_plan_c(&q->fft_plan, q->cfg.symbol_sz, SRSRAN_DFT_FORWARD)) { + ERROR("Creating DFT plan"); + goto clean_exit; + } + } + + if (q->ifft_plan.size) { + if (q->ifft_plan.size >= q->cfg.symbol_sz) { + // Replan if it was initialised previously with bigger FFT size + if (srsran_dft_replan(&q->ifft_plan, q->cfg.symbol_sz)) { + ERROR("Replaning DFT plan"); + goto clean_exit; + } + } else { + srsran_dft_plan_free(&q->ifft_plan); + if (srsran_dft_plan_c(&q->ifft_plan, q->cfg.symbol_sz, SRSRAN_DFT_BACKWARD)) { + ERROR("Creating DFT plan"); + goto clean_exit; + } + } + } else { + // Create plan from zero otherwise + if (srsran_dft_plan_c(&q->ifft_plan, q->cfg.symbol_sz, SRSRAN_DFT_BACKWARD)) { + ERROR("Creating DFT plan"); + goto clean_exit; + } + } + + srsran_dft_plan_set_norm(&q->fft_plan, true); + srsran_dft_plan_set_norm(&q->ifft_plan, true); + + srsran_vec_cf_zero(q->peak_buffer, q->cfg.symbol_sz); + srsran_vec_f_zero(q->abs_buffer_in, q->cfg.symbol_sz); + srsran_vec_f_zero(q->abs_buffer_out, q->cfg.symbol_sz); + ret = SRSRAN_SUCCESS; + +clean_exit: + if (ret < SRSRAN_SUCCESS) { + srsran_cfr_free(q); + } + return ret; +} + +void srsran_cfr_free(srsran_cfr_t* q) +{ + if (q) { + srsran_dft_plan_free(&q->fft_plan); + srsran_dft_plan_free(&q->ifft_plan); + if (q->abs_buffer_in) { + free(q->abs_buffer_in); + } + if (q->abs_buffer_out) { + free(q->abs_buffer_out); + } + if (q->peak_buffer) { + free(q->peak_buffer); + } + if (q->lpf_spectrum) { + free(q->lpf_spectrum); + } + SRSRAN_MEM_ZERO(q, srsran_cfr_t, 1); + } +} + +// Find the peak absolute value of an OFDM symbol +static inline float cfr_symb_peak(float* in_abs, int len) +{ + const uint32_t max_index = srsran_vec_max_fi(in_abs, len); + return in_abs[max_index]; +} + +bool srsran_cfr_params_valid(srsran_cfr_cfg_t* cfr_conf) +{ + if (cfr_conf == NULL) { + return false; + } + if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_INVALID) { + return false; + } + if (cfr_conf->alpha < 0 || cfr_conf->alpha > 1) { + return false; + } + if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_MANUAL && cfr_conf->manual_thr <= 0) { + return false; + } + if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_AUTO_CMA && (cfr_conf->max_papr_db <= 0)) { + return false; + } + if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_AUTO_EMA && + (cfr_conf->max_papr_db <= 0 || (cfr_conf->ema_alpha < 0 || cfr_conf->ema_alpha > 1))) { + return false; + } + return true; +} + +int srsran_cfr_set_threshold(srsran_cfr_t* q, float thres) +{ + if (q == NULL) { + ERROR("Invalid CFR object"); + return SRSRAN_ERROR_INVALID_INPUTS; + } + if (thres <= 0.0f) { + ERROR("Invalid CFR threshold"); + return SRSRAN_ERROR; + } + q->cfg.manual_thr = thres; + return SRSRAN_SUCCESS; +} + +int srsran_cfr_set_papr(srsran_cfr_t* q, float papr) +{ + if (q == NULL) { + ERROR("Invalid CFR object"); + return SRSRAN_ERROR_INVALID_INPUTS; + } + if (papr <= 0.0f) { + ERROR("Invalid CFR configuration"); + return SRSRAN_ERROR; + } + q->cfg.max_papr_db = papr; + q->max_papr_lin = srsran_convert_dB_to_power(q->cfg.max_papr_db); + return SRSRAN_SUCCESS; +} + +srsran_cfr_mode_t srsran_cfr_str2mode(const char* mode_str) +{ + srsran_cfr_mode_t ret; + if (strcmp(mode_str, "")) { + if (!strcmp(mode_str, "manual")) { + ret = SRSRAN_CFR_THR_MANUAL; + } else if (!strcmp(mode_str, "auto_cma")) { + ret = SRSRAN_CFR_THR_AUTO_CMA; + } else if (!strcmp(mode_str, "auto_ema")) { + ret = SRSRAN_CFR_THR_AUTO_EMA; + } else { + ret = SRSRAN_CFR_THR_INVALID; // mode_str is not recognised + } + } else { + ret = SRSRAN_CFR_THR_INVALID; // mode_str is empty + } + return ret; +} diff --git a/lib/src/phy/cfr/test/CMakeLists.txt b/lib/src/phy/cfr/test/CMakeLists.txt new file mode 100644 index 000000000..fcf09a166 --- /dev/null +++ b/lib/src/phy/cfr/test/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +######################################################################## +# CFR Test +######################################################################## + +add_executable(cfr_test cfr_test.c) +target_link_libraries(cfr_test srsran_phy) + +add_test(cfr_test_default cfr_test) + diff --git a/lib/src/phy/cfr/test/cfr_test.c b/lib/src/phy/cfr/test/cfr_test.c new file mode 100644 index 000000000..3a2efbbe7 --- /dev/null +++ b/lib/src/phy/cfr/test/cfr_test.c @@ -0,0 +1,313 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include +#include + +#include "srsran/phy/utils/random.h" +#include "srsran/srsran.h" + +#define MAX_ACPR_DB -100 + + +// Default CFR type +static char* cfr_mode_str = "manual"; + +static int nof_prb = -1; +static srsran_cp_t cp = SRSRAN_CP_NORM; +static int nof_repetitions = 1; +static int nof_frames = 10; +static srsran_cfr_mode_t cfr_mode = SRSRAN_CFR_THR_MANUAL; +static float alpha = 1.0f; +static bool dc_empty = true; +static float thr_manual = 1.5f; +static float max_papr_db = 8.0f; +static float ema_alpha = (float)1 / (float)SRSRAN_CP_NORM_NSYMB; + +static uint32_t force_symbol_sz = 0; +static double elapsed_us(struct timeval* ts_start, struct timeval* ts_end) +{ + if (ts_end->tv_usec > ts_start->tv_usec) { + return ((double)ts_end->tv_sec - (double)ts_start->tv_sec) * 1000000 + (double)ts_end->tv_usec - + (double)ts_start->tv_usec; + } else { + return ((double)ts_end->tv_sec - (double)ts_start->tv_sec - 1) * 1000000 + ((double)ts_end->tv_usec + 1000000) - + (double)ts_start->tv_usec; + } +} + +static void usage(char* prog) +{ + printf("Usage: %s\n", prog); + printf("\t-N Force symbol size, 0 for auto [Default %d]\n", force_symbol_sz); + printf("\t-n Force number of Resource blocks [Default All]\n"); + printf("\t-e extended cyclic prefix [Default Normal]\n"); + printf("\t-f Number of frames [Default %d]\n", nof_frames); + printf("\t-r Number of repetitions [Default %d]\n", nof_repetitions); + printf("\t-m CFR mode: manual, auto_cma, auto_ema [Default %s]\n", cfr_mode_str); + printf("\t-d Use DC subcarrier: [Default DC empty]\n"); + printf("\t-a CFR alpha: [Default %.2f]\n", alpha); + printf("\t-t CFR manual threshold: [Default %.2f]\n", thr_manual); + printf("\t-p CFR Max PAPR in dB (auto modes): [Default %.2f]\n", max_papr_db); + printf("\t-E Power avg EMA alpha (EMA mode): [Default %.2f]\n", ema_alpha); +} + +static int parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "NnerfmatdpE")) != -1) { + switch (opt) { + case 'n': + nof_prb = (int)strtol(argv[optind], NULL, 10); + break; + case 'N': + force_symbol_sz = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'e': + cp = SRSRAN_CP_EXT; + break; + case 'r': + nof_repetitions = (int)strtol(argv[optind], NULL, 10); + break; + case 'f': + nof_frames = (int)strtol(argv[optind], NULL, 10); + break; + case 'm': + cfr_mode_str = argv[optind]; + break; + case 'a': + alpha = strtof(argv[optind], NULL); + break; + case 't': + thr_manual = strtof(argv[optind], NULL); + break; + case 'd': + dc_empty = false; + break; + case 'p': + max_papr_db = strtof(argv[optind], NULL); + break; + case 'E': + ema_alpha = strtof(argv[optind], NULL); + break; + default: + usage(argv[0]); + return SRSRAN_ERROR; + } + } + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + + srsran_random_t random_gen = srsran_random_init(0); + struct timeval start, end; + srsran_cfr_t cfr = {}; + cf_t* input = NULL; + cf_t* output = NULL; + cf_t* error = NULL; + float* acpr_buff = NULL; + float mse_dB = 0.0f; + float nmse_dB = 0.0f; + float evm = 0.0f; + int max_prb = 0.0f; + float acpr_in_dB = 0.0f; + float acpr_out_dB = 0.0f; + + srsran_dft_plan_t ofdm_ifft = {}; + srsran_dft_plan_t ofdm_fft = {}; + + if (parse_args(argc, argv) < SRSRAN_SUCCESS) { + ERROR("Error in parse_args"); + goto clean_exit; + } + + cfr_mode = srsran_cfr_str2mode(cfr_mode_str); + if (cfr_mode == SRSRAN_CFR_THR_INVALID) { + ERROR("CFR mode is not recognised"); + goto clean_exit; + } + + if (nof_prb == -1) { + nof_prb = 6; + max_prb = SRSRAN_MAX_PRB; + } else { + max_prb = nof_prb; + } + while (nof_prb <= max_prb) { + const uint32_t symbol_sz = (force_symbol_sz) ? force_symbol_sz : (uint32_t)srsran_symbol_sz(nof_prb); + const uint32_t symbol_bw = nof_prb * SRSRAN_NRE; + const uint32_t nof_symb_slot = SRSRAN_CP_NSYMB(cp); + const uint32_t nof_symb_frame = nof_symb_slot * SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NOF_SF_X_FRAME; + const uint32_t frame_sz = symbol_sz * nof_symb_frame; + const uint32_t total_nof_re = frame_sz * nof_frames; + const uint32_t total_nof_symb = nof_symb_frame * nof_frames; + printf("Running test for %d PRB, %d Frames: \t", nof_prb, nof_frames); + fflush(stdout); + + input = srsran_vec_cf_malloc(total_nof_re); + output = srsran_vec_cf_malloc(total_nof_re); + error = srsran_vec_cf_malloc(total_nof_re); + acpr_buff = srsran_vec_f_malloc(total_nof_symb); + if (!input || !output || !error || !acpr_buff) { + perror("malloc"); + goto clean_exit; + } + srsran_vec_cf_zero(input, total_nof_re); + srsran_vec_cf_zero(output, total_nof_re); + srsran_vec_cf_zero(error, total_nof_re); + srsran_vec_f_zero(acpr_buff, total_nof_symb); + + // Set the parameters for the CFR. + srsran_cfr_cfg_t cfr_tx_cfg = {}; + cfr_tx_cfg.cfr_enable = true; + cfr_tx_cfg.symbol_sz = symbol_sz; + cfr_tx_cfg.symbol_bw = nof_prb * SRSRAN_NRE; + cfr_tx_cfg.cfr_mode = cfr_mode; + cfr_tx_cfg.max_papr_db = max_papr_db; + cfr_tx_cfg.alpha = alpha; + cfr_tx_cfg.manual_thr = thr_manual; + cfr_tx_cfg.ema_alpha = ema_alpha; + cfr_tx_cfg.dc_sc = dc_empty; + + if (!srsran_cfr_params_valid(&cfr_tx_cfg)) { + ERROR("Invalid CFR configuration"); + goto clean_exit; + } + + if (srsran_cfr_init(&cfr, &cfr_tx_cfg)) { + ERROR("Error initializing CFR"); + goto clean_exit; + } + + if (srsran_dft_plan_c(&ofdm_ifft, (int)symbol_sz, SRSRAN_DFT_BACKWARD)) { + ERROR("Creating IFFT plan"); + goto clean_exit; + } + srsran_dft_plan_set_norm(&ofdm_ifft, true); + if (srsran_dft_plan_c(&ofdm_fft, (int)symbol_sz, SRSRAN_DFT_FORWARD)) { + ERROR("Creating FFT plan"); + goto clean_exit; + } + srsran_dft_plan_set_norm(&ofdm_fft, true); + + // Generate Random data + cf_t* ofdm_symb = NULL; + for (int i = 0; i < total_nof_symb; i++) { + ofdm_symb = input + i * symbol_sz; + srsran_random_uniform_complex_dist_vector(random_gen, ofdm_symb + dc_empty, symbol_bw / 2, -1.0f, +1.0f); + srsran_random_uniform_complex_dist_vector( + random_gen, ofdm_symb + symbol_sz - symbol_bw / 2, symbol_bw / 2, -1.0f, +1.0f); + acpr_buff[i] = srsran_vec_acpr_c(ofdm_symb, symbol_bw / 2 + dc_empty, symbol_bw / 2, symbol_sz); + srsran_dft_run_c(&ofdm_ifft, ofdm_symb, ofdm_symb); + } + // compute the average intput ACPR + acpr_in_dB = srsran_vec_acc_ff(acpr_buff, total_nof_symb) / (float)total_nof_symb; + acpr_in_dB = srsran_convert_power_to_dB(acpr_in_dB); + + // Execute CFR + gettimeofday(&start, NULL); + for (uint32_t i = 0; i < nof_repetitions; i++) { + for (uint32_t j = 0; j < nof_frames; j++) { + for (uint32_t k = 0; k < nof_symb_frame; k++) { + srsran_cfr_process(&cfr, + input + (size_t)((k * symbol_sz) + (j * frame_sz)), + output + (size_t)((k * symbol_sz) + (j * frame_sz))); + } + } + } + gettimeofday(&end, NULL); + printf("%.1fMsps \t", (float)(total_nof_re * nof_repetitions) / elapsed_us(&start, &end)); + + // Compute metrics + srsran_vec_sub_ccc(input, output, error, total_nof_re); + + float power_in = srsran_vec_avg_power_cf(input, total_nof_re); + float power_err = srsran_vec_avg_power_cf(error, total_nof_re); + + mse_dB = srsran_convert_power_to_dB(power_err); + nmse_dB = srsran_convert_power_to_dB(power_err / power_in); + evm = 100 * sqrtf(power_err / power_in); + + float snr_dB = srsran_convert_power_to_dB(power_in / power_err); + + float papr_in = srsran_convert_power_to_dB(srsran_vec_papr_c(input, total_nof_re)); + float papr_out = srsran_convert_power_to_dB(srsran_vec_papr_c(output, total_nof_re)); + + ofdm_symb = NULL; + for (int i = 0; i < total_nof_symb; i++) { + ofdm_symb = output + i * symbol_sz; + srsran_dft_run_c(&ofdm_fft, ofdm_symb, ofdm_symb); + acpr_buff[i] = srsran_vec_acpr_c(ofdm_symb, symbol_bw / 2 + dc_empty, symbol_bw / 2, symbol_sz); + } + + // Compute the output average ACPR + acpr_out_dB = srsran_vec_acc_ff(acpr_buff, total_nof_symb) / (float)total_nof_symb; + acpr_out_dB = srsran_convert_power_to_dB(acpr_out_dB); + + printf("MSE=%.3fdB NMSE=%.3fdB EVM=%.3f%% SNR=%.3fdB", mse_dB, nmse_dB, evm, snr_dB); + printf(" In-PAPR=%.3fdB Out-PAPR=%.3fdB", papr_in, papr_out); + printf(" In-ACPR=%.3fdB Out-ACPR=%.3fdB\n", acpr_in_dB, acpr_out_dB); + + srsran_dft_plan_free(&ofdm_ifft); + srsran_dft_plan_free(&ofdm_fft); + free(input); + free(output); + free(error); + free(acpr_buff); + input = NULL; + output = NULL; + error = NULL; + acpr_buff = NULL; + + ++nof_prb; + if (acpr_out_dB > MAX_ACPR_DB) { + printf("ACPR too large \n"); + goto clean_exit; + } + } + ret = SRSRAN_SUCCESS; + + // Free resources +clean_exit: + srsran_random_free(random_gen); + srsran_cfr_free(&cfr); + srsran_dft_plan_free(&ofdm_ifft); + srsran_dft_plan_free(&ofdm_fft); + if (input) { + free(input); + } + if (output) { + free(output); + } + if (error) { + free(error); + } + if (acpr_buff) { + free(acpr_buff); + } + return ret; +} diff --git a/lib/src/phy/ch_estimation/CMakeLists.txt b/lib/src/phy/ch_estimation/CMakeLists.txt index 2d523f3be..ce7e920cc 100644 --- a/lib/src/phy/ch_estimation/CMakeLists.txt +++ b/lib/src/phy/ch_estimation/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/ch_estimation/chest_common.c b/lib/src/phy/ch_estimation/chest_common.c index ad19756d8..73f18cb6a 100644 --- a/lib/src/phy/ch_estimation/chest_common.c +++ b/lib/src/phy/ch_estimation/chest_common.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/ch_estimation/chest_dl.c b/lib/src/phy/ch_estimation/chest_dl.c index b4e89f935..874387eba 100644 --- a/lib/src/phy/ch_estimation/chest_dl.c +++ b/lib/src/phy/ch_estimation/chest_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -329,18 +329,21 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf, float sum_power = 0.0f; uint32_t count = 0; uint32_t npilots = (ch_mode == SRSRAN_SF_MBSFN) ? SRSRAN_REFSIGNAL_NUM_SF_MBSFN(q->cell.nof_prb, port_id) - : srsran_refsignal_cs_nof_re(&q->csr_refs, sf, port_id); - uint32_t nsymbols = (ch_mode == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_nof_symbols() - : srsran_refsignal_cs_nof_symbols(&q->csr_refs, sf, port_id); + : srsran_refsignal_cs_nof_re(&q->csr_refs, sf, port_id); + uint32_t nsymbols = (ch_mode == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_nof_symbols() + : srsran_refsignal_cs_nof_symbols(&q->csr_refs, sf, port_id); + if (nsymbols == 0) { + ERROR("Invalid number of CRS symbols\n"); + return SRSRAN_ERROR; + } + uint32_t nref = npilots / nsymbols; uint32_t fidx = (ch_mode == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_fidx(1) : srsran_refsignal_cs_fidx(q->cell, 0, port_id, 0); - - cf_t* input2d[nsymbols + 2]; cf_t* tmp_noise = q->tmp_noise; - // Special case for 1 symbol - if (nsymbols == 1) { + // Special case for 1 or 2 symbol + if (nsymbols < 3) { srsran_vec_sc_prod_cfc(q->pilot_estimates + 1, weight, tmp_noise, nref - 2); srsran_vec_sum_ccc(q->pilot_estimates + 0, tmp_noise, tmp_noise, nref - 2); srsran_vec_sum_ccc(q->pilot_estimates + 2, tmp_noise, tmp_noise, nref - 2); @@ -350,30 +353,21 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf, return sum_power; } + // Convert pilots to 2D to ease access + cf_t* input2d[4]; // The maximum number of symbols is 4 for (int i = 0; i < nsymbols; i++) { - input2d[i + 1] = &q->pilot_estimates[i * nref]; + input2d[i] = &q->pilot_estimates[i * nref]; } - input2d[0] = &q->tmp_noise[nref]; - if (nsymbols > 3) { - srsran_vec_sc_prod_cfc(input2d[2], 2.0f, input2d[0], nref); - srsran_vec_sub_ccc(input2d[0], input2d[4], input2d[0], nref); - } else { - srsran_vec_sc_prod_cfc(input2d[2], 1.0f, input2d[0], nref); - } - - input2d[nsymbols + 1] = &q->tmp_noise[nref * 2]; - if (nsymbols > 3) { - srsran_vec_sc_prod_cfc(input2d[nsymbols - 1], 2.0f, input2d[nsymbols + 1], nref); - srsran_vec_sub_ccc(input2d[nsymbols + 1], input2d[nsymbols - 3], input2d[nsymbols + 1], nref); - } else { - srsran_vec_sc_prod_cfc(input2d[nsymbols - 1], 1.0f, input2d[nsymbols + 1], nref); - } - - for (int i = 1; i < nsymbols + 1; i++) { + // Compares surrounding pilots in time/frequency. It requires at least 3 symbols with pilots. + for (int i = 1; i < nsymbols - 1; i++) { + // Calculate previous and next symbol indexes offset uint32_t offset = ((fidx < 3) ^ (i & 1)) ? 0 : 1; + + // Write estimates from this symbols srsran_vec_sc_prod_cfc(input2d[i], weight, tmp_noise, nref); + // Add the previous symbol estimates and extrapolate first/last element srsran_vec_sum_ccc(&input2d[i - 1][0], &tmp_noise[offset], &tmp_noise[offset], nref - offset); srsran_vec_sum_ccc(&input2d[i - 1][1 - offset], &tmp_noise[0], &tmp_noise[0], nref + offset - 1); if (offset) { @@ -382,6 +376,7 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf, tmp_noise[nref - 1] += 2.0f * input2d[i - 1][nref - 2] - input2d[i - 1][nref - 1]; } + // Add the next symbol estimates and extrapolate first/last element srsran_vec_sum_ccc(&input2d[i + 1][0], &tmp_noise[offset], &tmp_noise[offset], nref - offset); srsran_vec_sum_ccc(&input2d[i + 1][1 - offset], &tmp_noise[0], &tmp_noise[0], nref + offset - 1); if (offset) { @@ -390,14 +385,18 @@ static float estimate_noise_pilots(srsran_chest_dl_t* q, srsran_dl_sf_cfg_t* sf, tmp_noise[nref - 1] += 2.0f * input2d[i + 1][nref - 2] - input2d[i + 1][nref - 1]; } + // Scale to normalise to this symbol srsran_vec_sc_prod_cfc(tmp_noise, 1.0f / (weight + 4.0f), tmp_noise, nref); + // Subtract this symbol srsran_vec_sub_ccc(input2d[i], tmp_noise, tmp_noise, nref); - sum_power = srsran_vec_avg_power_cf(tmp_noise, nref); + + // The left signal after the subtraction can be considered noise + sum_power += srsran_vec_avg_power_cf(tmp_noise, nref); count++; } - return sum_power / (float)count * sqrtf(weight + 4.0f); + return sum_power / (float)count; } static float estimate_noise_pss(srsran_chest_dl_t* q, cf_t* input, cf_t* ce) @@ -443,8 +442,8 @@ static void interpolate_pilots(srsran_chest_dl_t* q, uint32_t port_id) { /* interpolate the symbols with references in the freq domain */ - uint32_t nsymbols = (sf->sf_type == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_nof_symbols() + 1 - : srsran_refsignal_cs_nof_symbols(&q->csr_refs, sf, port_id); + uint32_t nsymbols = (sf->sf_type == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_nof_symbols() + 1 + : srsran_refsignal_cs_nof_symbols(&q->csr_refs, sf, port_id); uint32_t fidx_offset = 0; /* Interpolate in the frequency domain */ @@ -566,7 +565,7 @@ static void average_pilots(srsran_chest_dl_t* q, { uint32_t nsymbols = (sf->sf_type == SRSRAN_SF_MBSFN) ? srsran_refsignal_mbsfn_nof_symbols() : srsran_refsignal_cs_nof_symbols(&q->csr_refs, sf, port_id); - uint32_t nref = (sf->sf_type == SRSRAN_SF_MBSFN) ? 6 * q->cell.nof_prb : 2 * q->cell.nof_prb; + uint32_t nref = (sf->sf_type == SRSRAN_SF_MBSFN) ? 6 * q->cell.nof_prb : 2 * q->cell.nof_prb; // Average in the time domain if enabled if (cfg->estimator_alg == SRSRAN_ESTIMATOR_ALG_AVERAGE && nsymbols > 1) { @@ -977,12 +976,8 @@ static void fill_res(srsran_chest_dl_t* q, srsran_chest_dl_res_t* res) for (uint32_t port_id = 0; port_id < q->cell.nof_ports; port_id++) { res->rsrp_port_dbm[port_id] = srsran_convert_power_to_dBm(get_rsrp_port(q, port_id)); for (uint32_t a = 0; a < q->nof_rx_antennas; a++) { - if (q->noise_estimate[a]) { - res->snr_ant_port_db[a][port_id] = - srsran_convert_power_to_dB(q->rsrp[a][port_id] / q->noise_estimate[a][port_id]); - } else { - res->snr_ant_port_db[a][port_id] = 0.0f; - } + res->snr_ant_port_db[a][port_id] = + srsran_convert_power_to_dB(q->rsrp[a][port_id] / q->noise_estimate[a][port_id]); res->rsrp_ant_port_dbm[a][port_id] = srsran_convert_power_to_dBm(q->rsrp[a][port_id]); res->rsrq_ant_port_db[a][port_id] = srsran_convert_power_to_dB(q->cell.nof_prb * q->rsrp[a][port_id] / q->rssi[a][port_id]); diff --git a/lib/src/phy/ch_estimation/chest_dl_nbiot.c b/lib/src/phy/ch_estimation/chest_dl_nbiot.c index e13974691..a7d851e23 100644 --- a/lib/src/phy/ch_estimation/chest_dl_nbiot.c +++ b/lib/src/phy/ch_estimation/chest_dl_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -89,7 +89,7 @@ int srsran_chest_dl_nbiot_init(srsran_chest_dl_nbiot_t* q, uint32_t max_prb) } clean_exit: - if (ret != SRSRAN_SUCCESS) { + if (ret != SRSRAN_SUCCESS && q != NULL) { srsran_chest_dl_nbiot_free(q); } return ret; diff --git a/lib/src/phy/ch_estimation/chest_sl.c b/lib/src/phy/ch_estimation/chest_sl.c index e89dd894e..b25d34768 100644 --- a/lib/src/phy/ch_estimation/chest_sl.c +++ b/lib/src/phy/ch_estimation/chest_sl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -944,10 +944,10 @@ float srsran_chest_sl_estimate_noise(srsran_chest_sl_t* q) return q->noise_estimated; } -int srsran_chest_sl_init(srsran_chest_sl_t* q, - srsran_sl_channels_t channel, - srsran_cell_sl_t cell, - srsran_sl_comm_resource_pool_t sl_comm_resource_pool) +int srsran_chest_sl_init(srsran_chest_sl_t* q, + srsran_sl_channels_t channel, + srsran_cell_sl_t cell, + const srsran_sl_comm_resource_pool_t* sl_comm_resource_pool) { int ret = SRSRAN_ERROR_INVALID_INPUTS; if (q != NULL) { @@ -955,7 +955,7 @@ int srsran_chest_sl_init(srsran_chest_sl_t* q, q->channel = channel; q->cell = cell; - q->sl_comm_resource_pool = sl_comm_resource_pool; + q->sl_comm_resource_pool = *sl_comm_resource_pool; switch (channel) { case SRSRAN_SIDELINK_PSBCH: diff --git a/lib/src/phy/ch_estimation/chest_ul.c b/lib/src/phy/ch_estimation/chest_ul.c index 819aabe1c..531c2e222 100644 --- a/lib/src/phy/ch_estimation/chest_ul.c +++ b/lib/src/phy/ch_estimation/chest_ul.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -355,16 +355,30 @@ static void chest_ul_estimate(srsran_chest_ul_t* q, } } - // Estimate received pilot power + // Measure reference signal RE average power + cf_t corr = srsran_vec_acc_cc(q->pilot_recv_signal, nslots * nrefs_sym) / (nslots * nrefs_sym); + float rsrp_avg = __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + + // Measure EPRE + float epre = srsran_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym); + + // RSRP shall not be greater than EPRE + rsrp_avg = SRSRAN_MIN(rsrp_avg, epre); + + // Calculate SNR if (isnormal(res->noise_estimate)) { - res->snr = srsran_vec_avg_power_cf(q->pilot_recv_signal, nslots * nrefs_sym) / res->noise_estimate; + res->snr = epre / res->noise_estimate; } else { res->snr = NAN; } - // Convert measurements in logarithm scale - res->snr_db = srsran_convert_power_to_dB(res->snr); - res->noise_estimate_dbm = srsran_convert_power_to_dBm(res->noise_estimate); + // Set EPRE and RSRP + res->epre = epre; + res->epre_dBfs = srsran_convert_power_to_dB(res->epre); + res->rsrp = rsrp_avg; + res->rsrp_dBfs = srsran_convert_power_to_dB(res->rsrp); + res->snr_db = srsran_convert_power_to_dB(res->snr); + res->noise_estimate_dbFs = srsran_convert_power_to_dBm(res->noise_estimate); } int srsran_chest_ul_estimate_pusch(srsran_chest_ul_t* q, @@ -479,15 +493,12 @@ int srsran_chest_ul_estimate_pucch(srsran_chest_ul_t* q, srsran_vec_prod_conj_ccc(q->pilot_recv_signal, q->pilot_known_signal, q->pilot_estimates, nrefs_sf); } - // Measure power - float rsrp_avg = 0.0f; - for (int ns = 0; ns < SRSRAN_NOF_SLOTS_PER_SF; ns++) { - for (int i = 0; i < n_rs; i++) { - cf_t corr = srsran_vec_acc_cc(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs) / (SRSRAN_NRE); - rsrp_avg += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; - } - } - rsrp_avg /= SRSRAN_NOF_SLOTS_PER_SF * n_rs; + // Measure reference signal RE average power + cf_t corr = srsran_vec_acc_cc(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs) / + (SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs); + float rsrp_avg = __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + + // Measure EPRE float epre = srsran_vec_avg_power_cf(q->pilot_estimates, SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NRE * n_rs); // RSRP shall not be greater than EPRE @@ -562,11 +573,11 @@ int srsran_chest_ul_estimate_pucch(srsran_chest_ul_t* q, if (fpclassify(res->noise_estimate) == FP_ZERO) { res->noise_estimate = FLT_MIN; } - res->noise_estimate_dbm = srsran_convert_power_to_dBm(res->noise_estimate); + res->noise_estimate_dbFs = srsran_convert_power_to_dBm(res->noise_estimate); // Estimate SINR if (isnormal(res->noise_estimate)) { - res->snr = res->rsrp / res->noise_estimate; + res->snr = res->epre / res->noise_estimate; res->snr_db = srsran_convert_power_to_dB(res->snr); } else { res->snr = NAN; diff --git a/lib/src/phy/ch_estimation/csi_rs.c b/lib/src/phy/ch_estimation/csi_rs.c index c9514c79f..d0ba12278 100644 --- a/lib/src/phy/ch_estimation/csi_rs.c +++ b/lib/src/phy/ch_estimation/csi_rs.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,6 +37,13 @@ */ #define CSI_RS_MAX_SYMBOLS_SLOT 4 +#define RESOURCE_ERROR(R) \ + do { \ + char res_info_str[256]; \ + srsran_csi_rs_resource_mapping_info(R, res_info_str, (uint32_t)sizeof(res_info_str)); \ + ERROR("Unhandled configuration %s", res_info_str); \ + } while (false) + static int csi_rs_location_f(const srsran_csi_rs_resource_mapping_t* resource, uint32_t i) { uint32_t count = 0; @@ -65,11 +72,12 @@ static int csi_rs_location_f(const srsran_csi_rs_resource_mapping_t* resource, u } if (count == i) { - return j * mul; + return (int)(j * mul); } } - ERROR("Unhandled configuration"); + // Inform about an unhandled configuration + RESOURCE_ERROR(resource); return SRSRAN_ERROR; } @@ -98,8 +106,7 @@ static int csi_rs_location_get_k_list(const srsran_csi_rs_resource_mapping_t* re } // Row 2 - if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->nof_ports == 1 && - resource->cdm == srsran_csi_rs_cdm_nocdm) { + if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->cdm == srsran_csi_rs_cdm_nocdm) { if (resource->density == srsran_csi_rs_resource_mapping_density_one || resource->density == srsran_csi_rs_resource_mapping_density_dot5_even || resource->density == srsran_csi_rs_resource_mapping_density_dot5_odd) { @@ -123,7 +130,8 @@ static int csi_rs_location_get_k_list(const srsran_csi_rs_resource_mapping_t* re } } - ERROR("Unhandled configuration"); + // Inform about an unhandled configuration + RESOURCE_ERROR(resource); return SRSRAN_ERROR; } @@ -151,8 +159,7 @@ static int csi_rs_location_get_l_list(const srsran_csi_rs_resource_mapping_t* re } // Row 2 - if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->nof_ports == 1 && - resource->cdm == srsran_csi_rs_cdm_nocdm) { + if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->cdm == srsran_csi_rs_cdm_nocdm) { if (resource->density == srsran_csi_rs_resource_mapping_density_one || resource->density == srsran_csi_rs_resource_mapping_density_dot5_even || resource->density == srsran_csi_rs_resource_mapping_density_dot5_odd) { @@ -174,7 +181,8 @@ static int csi_rs_location_get_l_list(const srsran_csi_rs_resource_mapping_t* re } } - ERROR("Unhandled configuration"); + // Inform about an unhandled configuration + RESOURCE_ERROR(resource); return SRSRAN_ERROR; } @@ -186,7 +194,7 @@ static uint32_t csi_rs_cinit(const srsran_carrier_nr_t* carrier, uint32_t n = SRSRAN_SLOT_NR_MOD(carrier->scs, slot_cfg->idx); uint32_t n_id = resource->scrambling_id; - return ((SRSRAN_NSYMB_PER_SLOT_NR * n + l + 1UL) * (2UL * n_id) << 10UL) + n_id; + return SRSRAN_SEQUENCE_MOD((((SRSRAN_NSYMB_PER_SLOT_NR * n + l + 1UL) * (2UL * n_id + 1UL)) << 10UL) + n_id); } bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* periodicity, const srsran_slot_cfg_t* slot_cfg) @@ -206,24 +214,14 @@ bool srsran_csi_rs_send(const srsran_csi_rs_period_and_offset_t* periodicity, co static int csi_rs_nof_cdm_groups(const srsran_csi_rs_resource_mapping_t* resource) { + // Row 1 if (resource->row == srsran_csi_rs_resource_mapping_row_1 && resource->nof_ports == 1 && resource->density == srsran_csi_rs_resource_mapping_density_three && resource->cdm == srsran_csi_rs_cdm_nocdm) { return 1; } - // Row 1 - if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->nof_ports == 1 && - resource->cdm == srsran_csi_rs_cdm_nocdm) { - if (resource->density == srsran_csi_rs_resource_mapping_density_one || - resource->density == srsran_csi_rs_resource_mapping_density_dot5_even || - resource->density == srsran_csi_rs_resource_mapping_density_dot5_odd) { - return 1; - } - } - // Row 2 - if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->nof_ports == 1 && - resource->cdm == srsran_csi_rs_cdm_nocdm) { + if (resource->row == srsran_csi_rs_resource_mapping_row_2 && resource->cdm == srsran_csi_rs_cdm_nocdm) { if (resource->density == srsran_csi_rs_resource_mapping_density_one || resource->density == srsran_csi_rs_resource_mapping_density_dot5_even || resource->density == srsran_csi_rs_resource_mapping_density_dot5_odd) { @@ -237,17 +235,123 @@ static int csi_rs_nof_cdm_groups(const srsran_csi_rs_resource_mapping_t* resourc return 2; } - ERROR("Unhandled configuration"); + // Inform about an unhandled configuration + RESOURCE_ERROR(resource); return SRSRAN_ERROR; } +bool srsran_csi_rs_resource_mapping_is_valid(const srsran_csi_rs_resource_mapping_t* res) +{ + if (res == NULL) { + return false; + } + + if (csi_rs_nof_cdm_groups(res) < 1) { + return false; + } + + uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT] = {}; + if (csi_rs_location_get_l_list(res, 0, l_list) < SRSRAN_SUCCESS) { + return false; + } + + uint32_t k_list[CSI_RS_MAX_SUBC_PRB] = {}; + if (csi_rs_location_get_k_list(res, 0, k_list) < SRSRAN_SUCCESS) { + return false; + } + + return true; +} + +uint32_t srsran_csi_rs_resource_mapping_info(const srsran_csi_rs_resource_mapping_t* res, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + const char* row_str = "invalid"; + uint32_t nof_freq_domain = 0; + switch (res->row) { + case srsran_csi_rs_resource_mapping_row_1: + row_str = "1"; + nof_freq_domain = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; + break; + case srsran_csi_rs_resource_mapping_row_2: + row_str = "2"; + nof_freq_domain = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW2; + break; + case srsran_csi_rs_resource_mapping_row_4: + row_str = "4"; + nof_freq_domain = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW4; + break; + case srsran_csi_rs_resource_mapping_row_other: + row_str = "other"; + nof_freq_domain = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_OTHER; + break; + } + + const char* cdm_str = "invalid"; + switch (res->cdm) { + case srsran_csi_rs_cdm_nocdm: + cdm_str = "nocdm"; + break; + case srsran_csi_rs_cdm_fd_cdm2: + cdm_str = "FD-CDM2"; + break; + case srsran_csi_rs_cdm_cdm4_fd2_td2: + cdm_str = "CDM4-FD2-TD2"; + break; + case srsran_csi_rs_cdm_cdm8_fd2_td4: + cdm_str = "CDM8-FD2-TD4"; + break; + } + + const char* density_str = "invalid"; + switch (res->density) { + case srsran_csi_rs_resource_mapping_density_three: + density_str = "3"; + break; + case srsran_csi_rs_resource_mapping_density_dot5_even: + density_str = ".5 (even)"; + break; + case srsran_csi_rs_resource_mapping_density_dot5_odd: + density_str = ".5 (odd)"; + break; + case srsran_csi_rs_resource_mapping_density_one: + density_str = "1"; + break; + case srsran_csi_rs_resource_mapping_density_spare: + density_str = "spare"; + break; + } + + char frequency_domain_alloc[SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_MAX + 1]; + srsran_vec_sprint_bin(frequency_domain_alloc, + SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_MAX + 1, + (uint8_t*)res->frequency_domain_alloc, + nof_freq_domain); + + len = srsran_print_check(str, + str_len, + len, + "row=%s freq=%s nof_ports=%d fist_symb=%d fist_symb2=%d cdm=%s density=%s rb=(%d:%d)", + row_str, + frequency_domain_alloc, + res->nof_ports, + res->first_symbol_idx, + res->first_symbol_idx2, + cdm_str, + density_str, + res->freq_band.start_rb, + res->freq_band.start_rb + res->freq_band.nof_rb - 1); + + return len; +} + uint32_t csi_rs_count(srsran_csi_rs_density_t density, uint32_t nprb) { switch (density) { case srsran_csi_rs_resource_mapping_density_three: return nprb * 3; case srsran_csi_rs_resource_mapping_density_dot5_even: - return nprb / 2; case srsran_csi_rs_resource_mapping_density_dot5_odd: return nprb / 2; case srsran_csi_rs_resource_mapping_density_one: @@ -348,12 +452,13 @@ int srsran_csi_rs_append_resource_to_pattern(const srsran_carrier_nr_t* return SRSRAN_SUCCESS; } -int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - cf_t* grid) +int srsran_csi_rs_nzp_put_resource(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + cf_t* grid) { - if (carrier == NULL || resource == NULL || grid == NULL) { + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || resource == NULL || grid == NULL) { return SRSRAN_ERROR; } @@ -421,25 +526,76 @@ int srsran_csi_rs_nzp_put(const srsran_carrier_nr_t* carrier, return SRSRAN_SUCCESS; } -int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, - const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - const cf_t* grid, - srsran_csi_rs_measure_t* measure) +int srsran_csi_rs_nzp_put_set(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + cf_t* grid) { - if (carrier == NULL || resource == NULL || grid == NULL) { + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL) { return SRSRAN_ERROR; } + uint32_t count = 0; + + // Iterate all resources in set + for (uint32_t i = 0; i < set->count; i++) { + // Skip resource + if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) { + continue; + } + + // Put resource + if (srsran_csi_rs_nzp_put_resource(carrier, slot_cfg, &set->data[i], grid) < SRSRAN_SUCCESS) { + ERROR("Error putting NZP-CSI-RS resource"); + return SRSRAN_ERROR; + } + count++; + } + + return (int)count; +} + +/** + * @brief Internal NZP-CSI-RS measurement structure + */ +typedef struct { + uint32_t cri; ///< CSI-RS resource identifier + uint32_t l0; ///< First OFDM symbol carrying CSI-RS + float epre; ///< Linear EPRE + cf_t corr; ///< Correlation + float delay_us; ///< Estimated average delay + uint32_t nof_re; ///< Total number of resource elements +} csi_rs_nzp_resource_measure_t; + +static int csi_rs_nzp_measure_resource(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + const cf_t* grid, + csi_rs_nzp_resource_measure_t* measure) +{ // Force CDM group to 0 uint32_t j = 0; + // Get subcarrier indexes uint32_t k_list[CSI_RS_MAX_SUBC_PRB]; int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list); if (nof_k <= 0) { return SRSRAN_ERROR; } + // Calculate average CSI-RS RE stride + float avg_k_stride = (float)((k_list[0] + SRSRAN_NRE) - k_list[nof_k - 1]); + for (uint32_t i = 1; i < (uint32_t)nof_k; i++) { + avg_k_stride += (float)(k_list[i] - k_list[i - 1]); + } + avg_k_stride /= (float)nof_k; + if (!isnormal(avg_k_stride)) { + ERROR("Invalid avg_k_stride"); + return SRSRAN_ERROR; + } + + // Get symbol indexes uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT]; int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list); if (nof_l <= 0) { @@ -451,11 +607,18 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, uint32_t rb_end = csi_rs_rb_end(carrier, &resource->resource_mapping); uint32_t rb_stride = csi_rs_rb_stride(&resource->resource_mapping); - // Accumulators - float epre_acc = 0.0f; - cf_t rsrp_acc = 0.0f; - uint32_t count = 0; + // Calculate ideal number of RE per symbol + uint32_t nof_re = csi_rs_count(resource->resource_mapping.density, rb_end - rb_begin); + // Accumulators + float epre_acc = 0.0f; + cf_t corr_acc = 0.0f; + float delay_acc = 0.0f; + + // Initialise measurement + SRSRAN_MEM_ZERO(measure, csi_rs_nzp_resource_measure_t, 1); + + // Iterate time symbols for (int l_idx = 0; l_idx < nof_l; l_idx++) { // Get symbol index uint32_t l = l_list[l_idx]; @@ -468,61 +631,471 @@ int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, // Skip unallocated RB srsran_sequence_state_advance(&sequence_state, 2 * csi_rs_count(resource->resource_mapping.density, rb_begin)); - // Temporal R sequence - cf_t r[64]; - uint32_t r_idx = 64; + // Temporal Least Square Estimates + cf_t lse[CSI_RS_MAX_SUBC_PRB * SRSRAN_MAX_PRB_NR]; + uint32_t count_re = 0; - // Iterate over frequency domain + // Extract RE for (uint32_t n = rb_begin; n < rb_end; n += rb_stride) { for (uint32_t k_idx = 0; k_idx < nof_k; k_idx++) { // Calculate sub-carrier index k uint32_t k = SRSRAN_NRE * n + k_list[k_idx]; - // Do we need more r? - if (r_idx >= 64) { - // ... Generate a bunch of it! - srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)r, 64 * 2); - r_idx = 0; - } - - // Take CSI-RS from grid and measure - cf_t tmp = grid[l * SRSRAN_NRE * carrier->nof_prb + k] * conjf(r[r_idx++]); - rsrp_acc += tmp; - epre_acc += __real__ tmp * __real__ tmp + __imag__ tmp * __imag__ tmp; - count++; + lse[count_re++] = grid[l * SRSRAN_NRE * carrier->nof_prb + k]; } } - } - if (count) { - measure->epre = epre_acc / (float)count; - rsrp_acc /= (float)count; - measure->rsrp = (__real__ rsrp_acc * __real__ rsrp_acc + __imag__ rsrp_acc * __imag__ rsrp_acc); - if (measure->epre > measure->rsrp) { - measure->n0 = measure->epre - measure->rsrp; - } else { - measure->n0 = 0.0f; + // Verify RE count matches the expected number of RE + if (count_re == 0 || count_re != nof_re) { + ERROR("Unmatched number of RE (%d != %d)", count_re, nof_re); + return SRSRAN_ERROR; } + + // Compute LSE + cf_t r[CSI_RS_MAX_SUBC_PRB * SRSRAN_MAX_PRB_NR]; + srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)r, 2 * count_re); + srsran_vec_prod_conj_ccc(lse, r, lse, count_re); + + // Compute average delay + float delay = srsran_vec_estimate_frequency(lse, (int)count_re); + delay_acc += delay; + + // Pre-compensate delay to avoid RSRP measurements get affected by average delay + srsran_vec_apply_cfo(lse, delay, lse, (int)count_re); + + // Compute EPRE + epre_acc += srsran_vec_avg_power_cf(lse, count_re); + + // Compute correlation + corr_acc += srsran_vec_acc_cc(lse, count_re) / (float)count_re; } - measure->rsrp_dB = srsran_convert_power_to_dB(measure->rsrp); - measure->epre_dB = srsran_convert_power_to_dB(measure->epre); - measure->n0_dB = srsran_convert_power_to_dB(measure->n0); - measure->snr_dB = measure->rsrp_dB - measure->n0_dB; - measure->nof_re = count; + // Set measure fields + measure->cri = resource->id; + measure->l0 = l_list[0]; + measure->epre = epre_acc / (float)nof_l; + measure->corr = corr_acc / (float)nof_l; + measure->delay_us = 1e6f * delay_acc / ((float)nof_l * SRSRAN_SUBC_SPACING_NR(carrier->scs)); + measure->nof_re = nof_l * nof_re; return SRSRAN_SUCCESS; } -uint32_t srsran_csi_rs_measure_info(const srsran_csi_rs_measure_t* measure, char* str, uint32_t str_len) +static int csi_rs_nzp_measure_set(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + csi_rs_nzp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]) { - return srsran_print_check(str, - str_len, - 0, - "rsrp=%+.1f, epre=%+.1f, n0=%+.1f, snr=%+.1f, nof_re=%d", - measure->rsrp_dB, - measure->epre_dB, - measure->n0_dB, - measure->snr_dB, - measure->nof_re); -} \ No newline at end of file + uint32_t count = 0; + + // Iterate all resources in set + for (uint32_t i = 0; i < set->count; i++) { + // Skip resource + if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) { + continue; + } + + // Perform measurement + if (csi_rs_nzp_measure_resource(carrier, slot_cfg, &set->data[i], grid, &measurements[count]) < SRSRAN_SUCCESS) { + ERROR("Error measuring NZP-CSI-RS resource"); + return SRSRAN_ERROR; + } + count++; + } + + return count; +} + +int srsran_csi_rs_nzp_measure(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + const cf_t* grid, + srsran_csi_trs_measurements_t* measure) +{ + if (carrier == NULL || slot_cfg == NULL || resource == NULL || grid == NULL || measure == NULL) { + return SRSRAN_ERROR; + } + + csi_rs_nzp_resource_measure_t m = {}; + if (csi_rs_nzp_measure_resource(carrier, slot_cfg, resource, grid, &m) < SRSRAN_SUCCESS) { + ERROR("Error measuring NZP-CSI-RS resource"); + return SRSRAN_ERROR; + } + + // Copy measurements + measure->epre = m.epre; + measure->rsrp = SRSRAN_CSQABS(m.corr); + measure->delay_us = m.delay_us; + measure->nof_re = m.nof_re; + + // Estimate noise from EPRE and RSPR + if (measure->epre > measure->rsrp) { + measure->n0 = measure->epre - measure->rsrp; + } else { + measure->n0 = 0.0f; + } + + // CFO cannot be estimated with a single resource + measure->cfo_hz = 0.0f; + measure->cfo_hz_max = 0.0f; + + // Calculate logarithmic measurements + measure->rsrp_dB = srsran_convert_power_to_dB(measure->rsrp); + measure->epre_dB = srsran_convert_power_to_dB(measure->epre); + measure->n0_dB = srsran_convert_power_to_dB(measure->n0); + measure->snr_dB = measure->rsrp_dB - measure->n0_dB; + + return SRSRAN_SUCCESS; +} + +int srsran_csi_rs_nzp_measure_trs(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_trs_measurements_t* measure) +{ + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { + return SRSRAN_ERROR; + } + + // Verify it is a TRS set + if (!set->trs_info) { + ERROR("The set is not configured as TRS"); + return SRSRAN_ERROR; + } + + // Perform Measurements + csi_rs_nzp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]; + int ret = csi_rs_nzp_measure_set(carrier, slot_cfg, set, grid, measurements); + + // Return to prevent assigning negative values to count + if (ret < SRSRAN_SUCCESS) { + ERROR("Error performing measurements"); + return SRSRAN_ERROR; + } + uint32_t count = (uint32_t)ret; + + // No NZP-CSI-RS has been scheduled for this slot + if (count == 0) { + return 0; + } + + // Make sure at least 2 measurements are scheduled + if (count < 2) { + ERROR("Not enough NZP-CSI-RS (%d) have been scheduled for this slot", count); + return SRSRAN_ERROR; + } + + // Make sure initial simbols are in ascending order + for (uint32_t i = 1; i < count; i++) { + if (measurements[i].l0 <= measurements[i - 1].l0) { + ERROR("NZP-CSI-RS are not in ascending order (%d <= %d)", measurements[i].l0, measurements[i - 1].l0); + return SRSRAN_ERROR; + } + } + + // Average measurements + float epre_sum = 0.0f; + float rsrp_sum = 0.0f; + float delay_sum = 0.0f; + uint32_t nof_re = 0; + for (uint32_t i = 0; i < count; i++) { + epre_sum += measurements[i].epre / (float)count; + rsrp_sum += SRSRAN_CSQABS(measurements[i].corr) / (float)count; + delay_sum += measurements[i].delay_us / (float)count; + nof_re += measurements[i].nof_re; + } + + // Compute CFO + float cfo_sum = 0.0f; + float cfo_max = 0.0f; + for (uint32_t i = 1; i < count; i++) { + float time_diff = srsran_symbol_distance_s(measurements[i - 1].l0, measurements[i].l0, carrier->scs); + float phase_diff = cargf(measurements[i].corr * conjf(measurements[i - 1].corr)); + float cfo_max_temp = 0.0f; + + // Avoid zero division + if (isnormal(time_diff)) { + // Calculate maximum CFO from this pair of symbols + cfo_max_temp = 1.0f / time_diff; + + // Calculate the actual CFO of this pair of symbols + cfo_sum += phase_diff / (2.0f * M_PI * time_diff * (count - 1)); + } + + // Select the lowest CFO + cfo_max = SRSRAN_MIN(cfo_max_temp, cfo_max); + } + + // Copy measurements + measure->epre = epre_sum; + measure->rsrp = rsrp_sum; + measure->delay_us = delay_sum; + measure->cfo_hz = cfo_sum; + measure->cfo_hz_max = cfo_max; + measure->nof_re = nof_re; + + // Estimate noise from EPRE and RSPR + if (measure->epre > measure->rsrp) { + measure->n0 = measure->epre - measure->rsrp; + } else { + measure->n0 = 0.0f; + } + + // Calculate logarithmic measurements + measure->rsrp_dB = srsran_convert_power_to_dB(measure->rsrp); + measure->epre_dB = srsran_convert_power_to_dB(measure->epre); + measure->n0_dB = srsran_convert_power_to_dB(measure->n0); + measure->snr_dB = measure->rsrp_dB - measure->n0_dB; + + return count; +} + +int srsran_csi_rs_nzp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* set, + const cf_t* grid, + srsran_csi_channel_measurements_t* measure) +{ + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { + return SRSRAN_ERROR; + } + + // Perform Measurements + csi_rs_nzp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]; + int ret = csi_rs_nzp_measure_set(carrier, slot_cfg, set, grid, measurements); + + // Return to prevent assigning negative values to count + if (ret < SRSRAN_SUCCESS) { + ERROR("Error performing measurements"); + return SRSRAN_ERROR; + } + uint32_t count = (uint32_t)ret; + + // No NZP-CSI-RS has been scheduled for this slot + if (count == 0) { + return 0; + } + + // Average measurements + float epre_sum = 0.0f; + float rsrp_sum = 0.0f; + for (uint32_t i = 0; i < count; i++) { + epre_sum += measurements[i].epre / (float)count; + rsrp_sum += SRSRAN_CSQABS(measurements[i].corr) / (float)count; + } + + // Estimate noise from EPRE and RSPR + float n0 = 0.0f; + if (epre_sum > rsrp_sum) { + n0 = epre_sum - rsrp_sum; + } + float n0_db = srsran_convert_power_to_dB(n0); + + // Set measurements + measure->cri = measurements[0].cri; + measure->wideband_rsrp_dBm = srsran_convert_power_to_dB(rsrp_sum); + measure->wideband_epre_dBm = srsran_convert_power_to_dB(epre_sum); + measure->wideband_snr_db = measure->wideband_rsrp_dBm - n0_db; + + // Set other parameters + measure->K_csi_rs = count; + measure->nof_ports = 1; // No other value is currently supported + + // Return the number of active resources for this slot + return count; +} + +/** + * @brief Internal ZP-CSI-RS measurement structure + */ +typedef struct { + uint32_t cri; ///< CSI-RS resource identifier + uint32_t l0; ///< First OFDM symbol carrying CSI-RS + float epre; ///< Linear EPRE + uint32_t nof_re; ///< Total number of resource elements +} csi_rs_zp_resource_measure_t; + +static int csi_rs_zp_measure_resource(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_resource_t* resource, + const cf_t* grid, + csi_rs_zp_resource_measure_t* measure) +{ + // Force CDM group to 0 + uint32_t j = 0; + + // Get subcarrier indexes + uint32_t k_list[CSI_RS_MAX_SUBC_PRB]; + int nof_k = csi_rs_location_get_k_list(&resource->resource_mapping, j, k_list); + if (nof_k <= 0) { + return SRSRAN_ERROR; + } + + // Calculate average CSI-RS RE stride + float avg_k_stride = (float)((k_list[0] + SRSRAN_NRE) - k_list[nof_k - 1]); + for (uint32_t i = 1; i < (uint32_t)nof_k; i++) { + avg_k_stride += (float)(k_list[i] - k_list[i - 1]); + } + avg_k_stride /= (float)nof_k; + if (!isnormal(avg_k_stride)) { + ERROR("Invalid avg_k_stride"); + return SRSRAN_ERROR; + } + + // Get symbol indexes + uint32_t l_list[CSI_RS_MAX_SYMBOLS_SLOT]; + int nof_l = csi_rs_location_get_l_list(&resource->resource_mapping, j, l_list); + if (nof_l <= 0) { + return SRSRAN_ERROR; + } + + // Calculate Resource Block boundaries + uint32_t rb_begin = csi_rs_rb_begin(carrier, &resource->resource_mapping); + uint32_t rb_end = csi_rs_rb_end(carrier, &resource->resource_mapping); + uint32_t rb_stride = csi_rs_rb_stride(&resource->resource_mapping); + + // Calculate ideal number of RE per symbol + uint32_t nof_re = csi_rs_count(resource->resource_mapping.density, rb_end - rb_begin); + + // Accumulators + float epre_acc = 0.0f; + + // Initialise measurement + SRSRAN_MEM_ZERO(measure, csi_rs_zp_resource_measure_t, 1); + + // Iterate time symbols + for (int l_idx = 0; l_idx < nof_l; l_idx++) { + // Get symbol index + uint32_t l = l_list[l_idx]; + + // Temporal Least Square Estimates + cf_t temp[CSI_RS_MAX_SUBC_PRB * SRSRAN_MAX_PRB_NR]; + uint32_t count_re = 0; + + // Extract RE + for (uint32_t n = rb_begin; n < rb_end; n += rb_stride) { + for (uint32_t k_idx = 0; k_idx < nof_k; k_idx++) { + // Calculate sub-carrier index k + uint32_t k = SRSRAN_NRE * n + k_list[k_idx]; + + temp[count_re++] = grid[l * SRSRAN_NRE * carrier->nof_prb + k]; + } + } + + // Verify RE count matches the expected number of RE + if (count_re == 0 || count_re != nof_re) { + ERROR("Unmatched number of RE (%d != %d)", count_re, nof_re); + return SRSRAN_ERROR; + } + + // Compute EPRE + epre_acc += srsran_vec_avg_power_cf(temp, count_re); + } + + // Set measure fields + measure->cri = resource->id; + measure->l0 = l_list[0]; + measure->epre = epre_acc / (float)nof_l; + measure->nof_re = nof_l * nof_re; + + return SRSRAN_SUCCESS; +} + +static int csi_rs_zp_measure_set(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_set_t* set, + const cf_t* grid, + csi_rs_zp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]) +{ + uint32_t count = 0; + + // Iterate all resources in set + for (uint32_t i = 0; i < set->count; i++) { + // Skip resource + if (!srsran_csi_rs_send(&set->data[i].periodicity, slot_cfg)) { + continue; + } + + // Perform measurement + if (csi_rs_zp_measure_resource(carrier, slot_cfg, &set->data[i], grid, &measurements[count]) < SRSRAN_SUCCESS) { + ERROR("Error measuring NZP-CSI-RS resource"); + return SRSRAN_ERROR; + } + count++; + } + + return count; +} + +int srsran_csi_rs_zp_measure_channel(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_zp_set_t* set, + const cf_t* grid, + srsran_csi_channel_measurements_t* measure) +{ + // Verify inputs + if (carrier == NULL || slot_cfg == NULL || set == NULL || grid == NULL || measure == NULL) { + return SRSRAN_ERROR; + } + + // Perform Measurements + csi_rs_zp_resource_measure_t measurements[SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_PER_SET]; + int ret = csi_rs_zp_measure_set(carrier, slot_cfg, set, grid, measurements); + + // Return to prevent assigning negative values to count + if (ret < SRSRAN_SUCCESS) { + ERROR("Error performing measurements"); + return SRSRAN_ERROR; + } + uint32_t count = (uint32_t)ret; + + // No NZP-CSI-RS has been scheduled for this slot + if (count == 0) { + return 0; + } + + // Average measurements + float epre_sum = 0.0f; + for (uint32_t i = 0; i < count; i++) { + epre_sum += measurements[i].epre / (float)count; + } + + // Set measurements + measure->cri = measurements[0].cri; + measure->wideband_rsrp_dBm = NAN; + measure->wideband_epre_dBm = srsran_convert_power_to_dB(epre_sum); + measure->wideband_snr_db = NAN; + + // Set other parameters + measure->K_csi_rs = count; + measure->nof_ports = 1; // No other value is currently supported + + // Return the number of active resources for this slot + return count; +} + +uint32_t srsran_csi_rs_measure_info(const srsran_csi_trs_measurements_t* measure, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + len = srsran_print_check(str, + str_len, + len, + "rsrp=%+.1f epre=%+.1f n0=%+.1f snr=%+.1f delay_us=%+.1f ", + measure->rsrp_dB, + measure->epre_dB, + measure->n0_dB, + measure->snr_dB, + measure->delay_us); + + // Append measured CFO and the maximum CFO that can be measured + if (isnormal(measure->cfo_hz_max)) { + len = srsran_print_check(str, str_len, len, "cfo_hz=%+.1f cfo_hz_max=%+.1f", measure->cfo_hz, measure->cfo_hz_max); + } + + return len; +} diff --git a/lib/src/phy/ch_estimation/dmrs_pbch.c b/lib/src/phy/ch_estimation/dmrs_pbch.c new file mode 100644 index 000000000..273f93dec --- /dev/null +++ b/lib/src/phy/ch_estimation/dmrs_pbch.c @@ -0,0 +1,258 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/ch_estimation/dmrs_pbch.h" +#include "srsran/phy/common/sequence.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" +#include +#include + +/* + * Number of NR PBCH DMRS resource elements present in an SSB resource grid + */ +#define DMRS_PBCH_NOF_RE 144 + +static uint32_t dmrs_pbch_cinit(const srsran_dmrs_pbch_cfg_t* cfg) +{ + // Default values for L_max == 4 + uint64_t i_ssb = (cfg->ssb_idx & 0b11U) + 4UL * cfg->n_hf; // Least 2 significant bits + + if (cfg->L_max == 8 || cfg->L_max == 64) { + i_ssb = cfg->ssb_idx & 0b111U; // Least 3 significant bits + } + + return SRSRAN_SEQUENCE_MOD(((i_ssb + 1UL) * (SRSRAN_FLOOR(cfg->N_id, 4UL) + 1UL) << 11UL) + ((i_ssb + 1UL) << 6UL) + + (cfg->N_id % 4)); +} + +int srsran_dmrs_pbch_put(const srsran_dmrs_pbch_cfg_t* cfg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +{ + // Validate inputs + if (cfg == NULL || ssb_grid == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Calculate index shift + uint32_t v = cfg->N_id % 4; + + // Calculate power allocation + float beta = M_SQRT1_2; + if (isnormal(cfg->beta)) { + beta = cfg->beta; + } + + // Initialise sequence + uint32_t cinit = dmrs_pbch_cinit(cfg); + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, cinit); + + // Generate sequence + cf_t r[DMRS_PBCH_NOF_RE]; + srsran_sequence_state_gen_f(&sequence_state, beta, (float*)r, DMRS_PBCH_NOF_RE * 2); + + // r sequence read index + uint32_t r_idx = 0; + + // Put sequence in symbol 1 + for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + ssb_grid[SRSRAN_SSB_BW_SUBC * 1 + k] = r[r_idx++]; + } + + // Put sequence in symbol 2, lower section + for (uint32_t k = v; k < 48; k += 4) { + ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k] = r[r_idx++]; + } + + // Put sequence in symbol 2, upper section + for (uint32_t k = 192 + v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k] = r[r_idx++]; + } + + // Put sequence in symbol 3 + for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + ssb_grid[SRSRAN_SSB_BW_SUBC * 3 + k] = r[r_idx++]; + } + + return SRSRAN_SUCCESS; +} + +int dmrs_pbch_extract_lse(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + cf_t lse[DMRS_PBCH_NOF_RE]) +{ + // Calculate index shift + uint32_t v = cfg->N_id % 4; + + // Calculate power allocation + float beta = M_SQRT1_2; + if (isnormal(cfg->beta)) { + beta = cfg->beta; + } + + // Initialise sequence + uint32_t cinit = dmrs_pbch_cinit(cfg); + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, cinit); + + // Generate sequence + cf_t r[DMRS_PBCH_NOF_RE]; + srsran_sequence_state_gen_f(&sequence_state, beta, (float*)r, DMRS_PBCH_NOF_RE * 2); + + // r sequence read index + uint32_t r_idx = 0; + + // Put sequence in symbol 1 + for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 1 + k]; + } + + // Put sequence in symbol 2, lower section + for (uint32_t k = v; k < 48; k += 4) { + lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k]; + } + + // Put sequence in symbol 2, upper section + for (uint32_t k = 192 + v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 2 + k]; + } + + // Put sequence in symbol 3 + for (uint32_t k = v; k < SRSRAN_SSB_BW_SUBC; k += 4) { + lse[r_idx++] = ssb_grid[SRSRAN_SSB_BW_SUBC * 3 + k]; + } + + // Calculate actual least square estimates + srsran_vec_prod_conj_ccc(lse, r, lse, DMRS_PBCH_NOF_RE); + + return SRSRAN_SUCCESS; +} + +static int dmrs_pbch_meas_estimate(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + cf_t ce[SRSRAN_SSB_NOF_RE], + srsran_dmrs_pbch_meas_t* meas) +{ + // Extract least square estimates + cf_t lse[DMRS_PBCH_NOF_RE]; + if (dmrs_pbch_extract_lse(cfg, ssb_grid, lse) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + float scs_hz = SRSRAN_SUBC_SPACING_NR(cfg->scs); + if (!isnormal(scs_hz)) { + ERROR("Invalid SCS"); + return SRSRAN_ERROR; + } + + // Compute average delay in microseconds from the symbols 1 and 3 (symbol 2 does not carry PBCH in all the grid) + float avg_delay1_norm = srsran_vec_estimate_frequency(&lse[0], 60) / 4.0f; + float avg_delay3_norm = srsran_vec_estimate_frequency(&lse[84], 60) / 4.0f; + float avg_delay_norm = (avg_delay1_norm + avg_delay3_norm) / 2.0f; + float avg_delay_us = avg_delay_norm / scs_hz; + + // Generate a second SSB grid with the corrected average delay + cf_t ssb_grid_corrected[SRSRAN_SSB_NOF_RE]; + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + srsran_vec_apply_cfo(&ssb_grid[SRSRAN_SSB_BW_SUBC * l], + avg_delay_norm, + &ssb_grid_corrected[SRSRAN_SSB_BW_SUBC * l], + SRSRAN_SSB_BW_SUBC); + } + + // Extract LSE from corrected grid + if (dmrs_pbch_extract_lse(cfg, ssb_grid_corrected, lse) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Compute correlation of symbols 1 and 3 + cf_t corr1 = srsran_vec_acc_cc(&lse[0], 60) / 60.0f; + cf_t corr3 = srsran_vec_acc_cc(&lse[84], 60) / 60.0f; + + // Estimate CFO from correlation + float distance_s = srsran_symbol_distance_s(1, 3, cfg->scs); + float cfo_hz = 0.0f; + if (isnormal(distance_s)) { + cfo_hz = cargf(corr1 * conjf(corr3)) / (2.0f * (float)M_PI * distance_s); + } + + // Estimate wideband gain at each symbol carrying DMRS + cf_t wideband_gain_1 = + srsran_vec_acc_cc(&lse[0], 60) * cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(1, cfg->scs) * cfo_hz); + cf_t wideband_gain_2 = + srsran_vec_acc_cc(&lse[60], 24) * cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(2, cfg->scs) * cfo_hz); + cf_t wideband_gain_3 = + srsran_vec_acc_cc(&lse[84], 60) * cexpf(I * 2.0f * M_PI * srsran_symbol_offset_s(3, cfg->scs) * cfo_hz); + + // Estimate wideband gain equivalent at symbol 0 + cf_t wideband_gain = (wideband_gain_1 + wideband_gain_2 + wideband_gain_3) / DMRS_PBCH_NOF_RE; + + // Compute RSRP from correlation + float rsrp = (SRSRAN_CSQABS(corr1) + SRSRAN_CSQABS(corr3)) / 2.0f; + + // Compute EPRE + float epre = srsran_vec_avg_power_cf(lse, DMRS_PBCH_NOF_RE); + + // Write measurements + if (meas != NULL) { + meas->corr = rsrp / epre; + meas->epre = epre; + meas->rsrp = rsrp; + meas->cfo_hz = cfo_hz; + meas->avg_delay_us = avg_delay_us; + } + + // Generate estimated grid + if (ce != NULL) { + // Compute channel estimates + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + float t_s = srsran_symbol_offset_s(l, cfg->scs); + cf_t symbol_wideband_gain = cexpf(-I * 2.0f * M_PI * cfo_hz * t_s) * wideband_gain; + srsran_vec_gen_sine(symbol_wideband_gain, -avg_delay_norm, &ce[l * SRSRAN_SSB_BW_SUBC], SRSRAN_SSB_BW_SUBC); + } + } + + return SRSRAN_SUCCESS; +} + +int srsran_dmrs_pbch_measure(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + srsran_dmrs_pbch_meas_t* meas) +{ + // Validate inputs + if (cfg == NULL || ssb_grid == NULL || meas == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + return dmrs_pbch_meas_estimate(cfg, ssb_grid, NULL, meas); +} + +int srsran_dmrs_pbch_estimate(const srsran_dmrs_pbch_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + cf_t ce[SRSRAN_SSB_NOF_RE]) +{ + // Validate inputs + if (cfg == NULL || ssb_grid == NULL || ce == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + return dmrs_pbch_meas_estimate(cfg, ssb_grid, ce, NULL); +} \ No newline at end of file diff --git a/lib/src/phy/ch_estimation/dmrs_pdcch.c b/lib/src/phy/ch_estimation/dmrs_pdcch.c index d269aca84..f39725d83 100644 --- a/lib/src/phy/ch_estimation/dmrs_pdcch.c +++ b/lib/src/phy/ch_estimation/dmrs_pdcch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,15 +22,18 @@ #include "srsran/phy/ch_estimation/dmrs_pdcch.h" #include "srsran/phy/ch_estimation/chest_common.h" #include "srsran/phy/common/sequence.h" +#include "srsran/phy/phch/pdcch_nr.h" #include "srsran/phy/utils/convolution.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" #include #include +#define NOF_PILOTS_X_RB 3 + /// @brief Every frequency resource is 6 Resource blocks, every resource block carries 3 pilots. So 18 possible pilots /// per frequency resource. -#define NOF_PILOTS_X_FREQ_RES 18 +#define NOF_PILOTS_X_FREQ_RES (NOF_PILOTS_X_RB * 6) ///@brief Maximum number of pilots in a PDCCH candidate location #define DMRS_PDCCH_MAX_NOF_PILOTS_CANDIDATE \ @@ -60,13 +63,12 @@ static uint32_t dmrs_pdcch_get_cinit(uint32_t slot_idx, uint32_t symbol_idx, uin 2UL * n_id); } -static void dmrs_pdcch_put_symbol_noninterleaved(const srsran_carrier_nr_t* carrier, - const srsran_coreset_t* coreset, - const srsran_dci_location_t* dci_location, - uint32_t cinit, - cf_t* sf_symbol) +static void dmrs_pdcch_put_symbol(const srsran_carrier_nr_t* carrier, + const srsran_coreset_t* coreset, + const bool rb_mask[SRSRAN_MAX_PRB_NR], + uint32_t cinit, + cf_t* sf_symbol) { - uint32_t L = 1U << dci_location->L; uint32_t nof_freq_res = SRSRAN_MIN(carrier->nof_prb / 6, SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE); // Initialise sequence for this symbol @@ -74,67 +76,48 @@ static void dmrs_pdcch_put_symbol_noninterleaved(const srsran_carrier_nr_t* ca srsran_sequence_state_init(&sequence_state, cinit); uint32_t sequence_skip = 0; // Accumulates pilot locations to skip - // Calculate Resource block indexes range, every CCE is 6 REG, 1 REG is 6 RE in resource blocks - uint32_t rb_coreset_idx_begin = (dci_location->ncce * 6) / coreset->duration; - uint32_t rb_coreset_idx_end = ((dci_location->ncce + L) * 6) / coreset->duration; - // CORESET Resource Block counter uint32_t rb_coreset_idx = 0; - for (uint32_t i = 0; i < nof_freq_res; i++) { + + // Get CORESET offset + uint32_t offset_k = coreset->offset_rb * SRSRAN_NRE; + + // For each frequency resource (6 RB groups) + for (uint32_t res_idx = 0; res_idx < nof_freq_res; res_idx++) { // Skip frequency resource if outside of the CORESET - if (!coreset->freq_resources[i]) { + if (!coreset->freq_resources[res_idx]) { // Skip possible DMRS locations in this region sequence_skip += NOF_PILOTS_X_FREQ_RES; continue; } - // Skip if the frequency resource highest RB is lower than the first CCE resource block. - if ((rb_coreset_idx + 6) <= rb_coreset_idx_begin) { - // Skip possible DMRS locations in this region - sequence_skip += NOF_PILOTS_X_FREQ_RES; - - // Since this is part of the CORESET, count the RB as CORESET - rb_coreset_idx += 6; - continue; - } - - // Return if the first RB of the frequency resource is greater than the last CCE resource block - if (rb_coreset_idx > rb_coreset_idx_end) { - return; - } - - // Skip all discarded possible pilot locations - srsran_sequence_state_advance(&sequence_state, 2 * sequence_skip); - sequence_skip = 0; - - // Generate pilots - cf_t rl[NOF_PILOTS_X_FREQ_RES]; - srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, NOF_PILOTS_X_FREQ_RES * 2); - - // For each RB in the frequency resource - for (uint32_t j = 0; j < 6; j++) { - // Calculate absolute RB index - uint32_t n = i * 6 + j; - - // Skip if lower than begin - if (rb_coreset_idx < rb_coreset_idx_begin) { - rb_coreset_idx++; + // For each RB in the enabled frequency resource + for (uint32_t rb = 0; rb < 6; rb++, rb_coreset_idx++) { + // Skip if mask is disabled + if (!rb_mask[rb_coreset_idx]) { + sequence_skip += NOF_PILOTS_X_RB; continue; } - // Return if greater than end - if (rb_coreset_idx >= rb_coreset_idx_end) { - return; - } + // Skip all discarded possible pilot locations + srsran_sequence_state_advance(&sequence_state, 2 * sequence_skip); + sequence_skip = 0; + + // Generate pilots for the given RB + cf_t rl[NOF_PILOTS_X_RB]; + srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, NOF_PILOTS_X_RB * 2); + + // Absolute RB index in the resource grid + uint32_t n = res_idx * 6 + rb; // Write pilots in the symbol - for (uint32_t k_prime = 0; k_prime < 3; k_prime++) { - // Calculate sub-carrier index + for (uint32_t k_prime = 0; k_prime < NOF_PILOTS_X_RB; k_prime++) { + // Calculate absolute sub-carrier index in the resource grid uint32_t k = n * SRSRAN_NRE + 4 * k_prime + 1; - sf_symbol[k] = rl[3 * j + k_prime]; + // Write DMRS + sf_symbol[k + offset_k] = rl[k_prime]; } - rb_coreset_idx++; } } } @@ -149,11 +132,6 @@ int srsran_dmrs_pdcch_put(const srsran_carrier_nr_t* carrier, return SRSRAN_ERROR_INVALID_INPUTS; } - if (coreset->mapping_type == srsran_coreset_mapping_type_interleaved) { - ERROR("Error interleaved CORESET mapping is not currently implemented"); - return SRSRAN_ERROR; - } - if (coreset->duration < SRSRAN_CORESET_DURATION_MIN || coreset->duration > SRSRAN_CORESET_DURATION_MAX) { ERROR("Error CORESET duration %d is out-of-bounds (%d,%d)", coreset->duration, @@ -162,6 +140,13 @@ int srsran_dmrs_pdcch_put(const srsran_carrier_nr_t* carrier, return SRSRAN_ERROR; } + // Calculate CCE-to-REG mapping mask + bool rb_mask[SRSRAN_MAX_PRB_NR] = {}; + if (srsran_pdcch_nr_cce_to_reg_mapping(coreset, dci_location, rb_mask) < SRSRAN_SUCCESS) { + ERROR("Error in CCE-to-REG mapping"); + return SRSRAN_SUCCESS; + } + // Use cell id if the DMR scrambling id is not provided by higher layers uint32_t n_id = carrier->pci; if (coreset->dmrs_scrambling_id_present) { @@ -178,8 +163,7 @@ int srsran_dmrs_pdcch_put(const srsran_carrier_nr_t* carrier, DMRS_PDCCH_INFO_TX("n=%d; l=%d; cinit=%08x", slot_idx, l, cinit); // Put data - dmrs_pdcch_put_symbol_noninterleaved( - carrier, coreset, dci_location, cinit, &sf_symbols[carrier->nof_prb * SRSRAN_NRE * l]); + dmrs_pdcch_put_symbol(carrier, coreset, rb_mask, cinit, &sf_symbols[carrier->nof_prb * SRSRAN_NRE * l]); } return SRSRAN_SUCCESS; @@ -299,6 +283,9 @@ void srsran_dmrs_pdcch_estimator_free(srsran_dmrs_pdcch_estimator_t* q) static void srsran_dmrs_pdcch_extract(srsran_dmrs_pdcch_estimator_t* q, uint32_t cinit, const cf_t* sf_symbol, cf_t* lse) { + // Get CORESET offset + uint32_t offset_k = q->coreset.offset_rb * SRSRAN_NRE; + // Initialise pseudo-random sequence srsran_sequence_state_t sequence_state = {}; srsran_sequence_state_init(&sequence_state, cinit); @@ -307,7 +294,7 @@ srsran_dmrs_pdcch_extract(srsran_dmrs_pdcch_estimator_t* q, uint32_t cinit, cons uint32_t sequence_skip = 0; // Counts enabled frequency domain resources - uint32_t rb_coreset_idx = 0; + uint32_t lse_count = 0; // Iterate over all possible frequency resources uint32_t freq_domain_res_size = SRSRAN_MIN(q->carrier.nof_prb / 6, SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE); @@ -327,26 +314,23 @@ srsran_dmrs_pdcch_extract(srsran_dmrs_pdcch_estimator_t* q, uint32_t cinit, cons srsran_sequence_state_gen_f(&sequence_state, M_SQRT1_2, (float*)rl, NOF_PILOTS_X_FREQ_RES * 2); // Iterate all PRBs in the enabled frequency domain resource - for (uint32_t j = 0, idx = rb_coreset_idx * NOF_PILOTS_X_FREQ_RES; j < 6; j++) { + cf_t* lse_ptr = &lse[lse_count]; + for (uint32_t j = 0; j < 6; j++) { // Calculate Grid PRB index (n) uint32_t n = i * 6 + j; // For each pilot in the PRB - for (uint32_t k_prime = 0; k_prime < 3; k_prime++, idx++) { + for (uint32_t k_prime = 0; k_prime < 3; k_prime++) { // Calculate sub-carrier index uint32_t k = n * SRSRAN_NRE + 4 * k_prime + 1; // Extract symbol - lse[idx] = sf_symbol[k]; + lse[lse_count++] = sf_symbol[k + offset_k]; } } // Calculate least squared estimates - cf_t* lse_ptr = &lse[rb_coreset_idx * NOF_PILOTS_X_FREQ_RES]; srsran_vec_prod_conj_ccc(lse_ptr, rl, lse_ptr, NOF_PILOTS_X_FREQ_RES); - - // Increment frequency domain resource counter - rb_coreset_idx++; } } @@ -430,21 +414,18 @@ int srsran_dmrs_pdcch_get_measure(const srsran_dmrs_pdcch_estimator_t* q, return SRSRAN_ERROR_INVALID_INPUTS; } - uint32_t L = 1U << dci_location->L; - if (q->coreset.mapping_type == srsran_coreset_mapping_type_interleaved) { - ERROR("Error interleaved mapping not implemented"); - return SRSRAN_ERROR; - } - // Check that CORESET duration is not less than minimum if (q->coreset.duration < SRSRAN_CORESET_DURATION_MIN) { ERROR("Invalid CORESET duration"); return SRSRAN_ERROR; } - // Get base pilot; - uint32_t pilot_idx = (dci_location->ncce * 18) / q->coreset.duration; - uint32_t nof_pilots = (L * 18) / q->coreset.duration; + // Calculate CCE-to-REG mapping mask + bool rb_mask[SRSRAN_MAX_PRB_NR] = {}; + if (srsran_pdcch_nr_cce_to_reg_mapping(&q->coreset, dci_location, rb_mask) < SRSRAN_SUCCESS) { + ERROR("Error in CCE-to-REG mapping"); + return SRSRAN_SUCCESS; + } // Initialise measurements float rsrp = 0.0f; //< Averages linear RSRP @@ -453,26 +434,43 @@ int srsran_dmrs_pdcch_get_measure(const srsran_dmrs_pdcch_estimator_t* q, float sync_err_avg = 0.0f; //< Averages synchronization cf_t corr[SRSRAN_CORESET_DURATION_MAX] = {}; //< Saves correlation for the different symbols - // Iterate the CORESET duration + // For each CORESET symbol for (uint32_t l = 0; l < q->coreset.duration; l++) { - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + // Temporal least square estimates + cf_t tmp[DMRS_PDCCH_MAX_NOF_PILOTS_CANDIDATE] = {}; + uint32_t nof_pilots = 0; + + // For each RB in the CORESET + for (uint32_t rb = 0; rb < q->coreset_bw; rb++) { + // Skip RB if unused + if (!rb_mask[rb]) { + continue; + } + + // Copy LSE + srsran_vec_cf_copy(&tmp[nof_pilots], &q->lse[l][rb * NOF_PILOTS_X_RB], NOF_PILOTS_X_RB); + nof_pilots += NOF_PILOTS_X_RB; + } + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DMRS_PDCCH_DEBUG_RX("Measuring PDCCH l=%d; lse=", l); - srsran_vec_fprint_c(stdout, &q->lse[l][pilot_idx], nof_pilots); + srsran_vec_fprint_c(stdout, tmp, nof_pilots); } // Measure synchronization error and accumulate for average - float tmp_sync_err = srsran_vec_estimate_frequency(&q->lse[l][pilot_idx], nof_pilots); + float tmp_sync_err = srsran_vec_estimate_frequency(tmp, nof_pilots); sync_err_avg += tmp_sync_err; #if DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS - cf_t tmp[DMRS_PDCCH_MAX_NOF_PILOTS_CANDIDATE]; - // Pre-compensate synchronization error - srsran_vec_apply_cfo(&q->lse[l][pilot_idx], tmp_sync_err, tmp, nof_pilots); -#else // DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS - const cf_t* tmp = &q->lse[l][pilot_idx]; + srsran_vec_apply_cfo(tmp, tmp_sync_err, tmp, nof_pilots); #endif // DMRS_PDCCH_SYNC_PRECOMPENSATE_MEAS + // Prevent undefined division + if (!nof_pilots) { + ERROR("Error in DMRS correlation. nof_pilots cannot be zero"); + return SRSRAN_ERROR; + } // Correlate DMRS corr[l] = srsran_vec_acc_cc(tmp, nof_pilots) / (float)nof_pilots; @@ -536,10 +534,6 @@ int srsran_dmrs_pdcch_get_ce(const srsran_dmrs_pdcch_estimator_t* q, } uint32_t L = 1U << dci_location->L; - if (q->coreset.mapping_type == srsran_coreset_mapping_type_interleaved) { - ERROR("Error interleaved mapping not implemented"); - return SRSRAN_ERROR; - } // Check that CORESET duration is not less than minimum if (q->coreset.duration < SRSRAN_CORESET_DURATION_MIN) { @@ -547,16 +541,30 @@ int srsran_dmrs_pdcch_get_ce(const srsran_dmrs_pdcch_estimator_t* q, return SRSRAN_ERROR; } - // Calculate begin and end sub-carrier index for the selected candidate - uint32_t k_begin = (dci_location->ncce * SRSRAN_NRE * 6) / q->coreset.duration; - uint32_t k_end = k_begin + (L * 6 * SRSRAN_NRE) / q->coreset.duration; + // Calculate CCE-to-REG mapping mask + bool rb_mask[SRSRAN_MAX_PRB_NR] = {}; + if (srsran_pdcch_nr_cce_to_reg_mapping(&q->coreset, dci_location, rb_mask) < SRSRAN_SUCCESS) { + ERROR("Error in CCE-to-REG mapping"); + return SRSRAN_SUCCESS; + } // Extract CE for PDCCH uint32_t count = 0; + + // For each PDCCH symbol for (uint32_t l = 0; l < q->coreset.duration; l++) { - for (uint32_t k = k_begin; k < k_end; k++) { - if (k % 4 != 1) { - ce->ce[count++] = q->ce[q->coreset_bw * SRSRAN_NRE * l + k]; + // For each CORESET RB + for (uint32_t rb = 0; rb < q->coreset_bw; rb++) { + // Skip RB if unused + if (!rb_mask[rb]) { + continue; + } + + // Copy RB, skipping DMRS + for (uint32_t k = rb * SRSRAN_NRE; k < (rb + 1) * SRSRAN_NRE; k++) { + if (k % 4 != 1) { + ce->ce[count++] = q->ce[q->coreset_bw * SRSRAN_NRE * l + k]; + } } } } diff --git a/lib/src/phy/ch_estimation/dmrs_pucch.c b/lib/src/phy/ch_estimation/dmrs_pucch.c index 29303a608..1a98d5afe 100644 --- a/lib/src/phy/ch_estimation/dmrs_pucch.c +++ b/lib/src/phy/ch_estimation/dmrs_pucch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,6 +23,8 @@ #include "srsran/phy/common/sequence.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" +#include +#include // Implements TS 38.211 table 6.4.1.3.1.1-1: Number of DM-RS symbols and the corresponding N_PUCCH... static uint32_t dmrs_pucch_format1_n_pucch(const srsran_pucch_nr_resource_t* resource, uint32_t m_prime) @@ -118,58 +120,71 @@ int srsran_dmrs_pucch_format1_put(const srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0); - if (n_pucch == 0) { - ERROR("Error getting number of symbols"); - return SRSRAN_ERROR; - } - + // First symbol index uint32_t l_prime = resource->start_symbol_idx; - for (uint32_t m = 0; m < n_pucch; m++) { - // Clause 6.4.1.3.1.2 specifies l=0,2,4... - uint32_t l = m * 2; - // Get start of the sequence in resource grid - cf_t* slot_symbols_ptr = &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - - // Get Alpha index - uint32_t alpha_idx = 0; - if (srsran_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < - SRSRAN_SUCCESS) { - ERROR("Calculating alpha"); - } - - // get r_uv sequence from LUT object - const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); - if (r_uv == NULL) { - ERROR("Getting r_uv sequence"); + // Clause 6.4.1.3.1.2 specifies l=0,2,4... + for (uint32_t m_prime = 0, l = 0; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) { + // Get number of symbols carrying DMRS + uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, m_prime); + if (n_pucch == 0) { + ERROR("Error getting number of symbols"); return SRSRAN_ERROR; } - // Get w_i_m - cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + // Get the starting PRB + uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb; - // Compute z(n) = w(i) * r_uv(n) - cf_t z[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + for (uint32_t m = 0; m < n_pucch; m++, l += 2) { + // Get start of the sequence in resource grid + cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE]; - // Put z in the grid - srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE); + // Get Alpha index + uint32_t alpha_idx = 0; + if (srsran_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < + SRSRAN_SUCCESS) { + ERROR("Calculating alpha"); + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence"); + return SRSRAN_ERROR; + } + + // Get w_i_m + cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSRAN_NRE]; + srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 DMRS TX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f; z=", + m_prime, + m, + __real__ w_i_m, + __imag__ w_i_m); + srsran_vec_fprint_c(stdout, z, SRSRAN_NRE); + } + + // Put z in the grid + srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE); + } } return SRSRAN_SUCCESS; } int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, - const srsran_carrier_nr_t* carrier, const srsran_pucch_nr_common_cfg_t* cfg, const srsran_slot_cfg_t* slot, const srsran_pucch_nr_resource_t* resource, const cf_t* slot_symbols, srsran_chest_ul_res_t* res) { - if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL || - res == NULL) { + if (q == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL || res == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } @@ -181,67 +196,94 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, // Get group sequence uint32_t u = 0; uint32_t v = 0; - if (srsran_pucch_nr_group_sequence(carrier, cfg, &u, &v) < SRSRAN_SUCCESS) { + if (srsran_pucch_nr_group_sequence(&q->carrier, cfg, &u, &v) < SRSRAN_SUCCESS) { ERROR("Error getting group sequence"); return SRSRAN_ERROR; } - uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, 0); - if (n_pucch == 0) { - ERROR("Error getting number of symbols"); - return SRSRAN_ERROR; - } - + uint32_t start_rb_idx[SRSRAN_PUCCH_NR_FORMAT1_N_MAX]; + uint32_t symbol_idx[SRSRAN_PUCCH_NR_FORMAT1_N_MAX]; cf_t ce[SRSRAN_PUCCH_NR_FORMAT1_N_MAX][SRSRAN_NRE]; + + // First symbol index uint32_t l_prime = resource->start_symbol_idx; - for (uint32_t m = 0; m < n_pucch; m++) { - // Clause 6.4.1.3.1.2 specifies l=0,2,4... - uint32_t l = m * 2; - // Get start of the sequence in resource grid - const cf_t* slot_symbols_ptr = - &slot_symbols[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - - // Get Alpha index - uint32_t alpha_idx = 0; - if (srsran_pucch_nr_alpha_idx(carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < - SRSRAN_SUCCESS) { - ERROR("Calculating alpha"); - } - - // get r_uv sequence from LUT object - const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); - if (r_uv == NULL) { - ERROR("Getting r_uv sequence"); + uint32_t n_pucch_sum = 0; + for (uint32_t m_prime = 0, l = 0; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) { + // Get number of symbols carrying DMRS + uint32_t n_pucch = dmrs_pucch_format1_n_pucch(resource, m_prime); + if (n_pucch == 0) { + ERROR("Error getting number of symbols"); return SRSRAN_ERROR; } - // Get w_i_m - cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + // Prevent ce[m] overflow + assert(n_pucch <= SRSRAN_PUCCH_NR_FORMAT1_N_MAX); - // Compute z(n) = w(i) * r_uv(n) - cf_t z[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + // Get the starting PRB + uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb; + start_rb_idx[n_pucch_sum] = starting_prb; - // Calculate least square estimates for this symbol - srsran_vec_prod_conj_ccc(slot_symbols_ptr, z, ce[m], SRSRAN_NRE); + for (uint32_t m = 0; m < n_pucch; m++, l += 2) { // Clause 6.4.1.3.1.2 specifies l=0,2,4... + symbol_idx[n_pucch_sum] = l + l_prime; + + // Get start of the sequence in resource grid + const cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE]; + + // Get Alpha index + uint32_t alpha_idx = 0; + if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < + SRSRAN_SUCCESS) { + ERROR("Calculating alpha"); + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence"); + return SRSRAN_ERROR; + } + + // Get w_i_m + cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSRAN_NRE]; + srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + INFO("[PUCCH Format 1 DMRS RX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f", m_prime, m, __real__ w_i_m, __imag__ w_i_m); + srsran_vec_fprint_c(stdout, z, SRSRAN_NRE); + } + + // TODO: can ce[m] overflow? + // Calculate least square estimates for this symbol + srsran_vec_prod_conj_ccc(slot_symbols_ptr, z, ce[n_pucch_sum], SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 DMRS RX] ce[%d]=", n_pucch_sum); + srsran_vec_fprint_c(stdout, ce[n_pucch_sum], SRSRAN_NRE); + } + n_pucch_sum++; + } } // Perform measurements - float rsrp = 0.0f; - float epre = 0.0f; - float ta_err = 0.0f; - for (uint32_t m = 0; m < n_pucch; m++) { - cf_t corr = srsran_vec_acc_cc(ce[m], SRSRAN_NRE) / SRSRAN_NRE; - rsrp += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + float rsrp = 0.0f; + float epre = 0.0f; + float ta_err = 0.0f; + cf_t corr[SRSRAN_PUCCH_NR_FORMAT1_N_MAX] = {}; + for (uint32_t m = 0; m < n_pucch_sum; m++) { + corr[m] = srsran_vec_acc_cc(ce[m], SRSRAN_NRE) / SRSRAN_NRE; + rsrp += SRSRAN_CSQABS(corr[m]); epre += srsran_vec_avg_power_cf(ce[m], SRSRAN_NRE); ta_err += srsran_vec_estimate_frequency(ce[m], SRSRAN_NRE); } // Average measurements - rsrp /= n_pucch; - epre /= n_pucch; - ta_err /= n_pucch; + rsrp /= n_pucch_sum; + epre /= n_pucch_sum; + ta_err /= n_pucch_sum; // Set power measures rsrp = SRSRAN_MIN(rsrp, epre); @@ -249,33 +291,47 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, res->rsrp_dBfs = srsran_convert_power_to_dB(rsrp); res->epre = epre; res->epre_dBfs = srsran_convert_power_to_dB(epre); - res->noise_estimate = epre - rsrp; - res->noise_estimate_dbm = srsran_convert_power_to_dB(res->noise_estimate); + res->noise_estimate = SRSRAN_MAX(epre - rsrp, 1e-6f); + res->noise_estimate_dbFs = srsran_convert_power_to_dB(res->noise_estimate); res->snr = rsrp / res->noise_estimate; res->snr_db = srsran_convert_power_to_dB(res->snr); // Compute Time Aligment error in microseconds if (isnormal(ta_err)) { - ta_err /= 15e3f * (float)(1U << carrier->scs); // Convert from normalized frequency to seconds - ta_err *= 1e6f; // Convert to micro-seconds - ta_err = roundf(ta_err * 10.0f) / 10.0f; // Round to one tenth of micro-second + ta_err /= 15e3f * (float)(1U << q->carrier.scs); // Convert from normalized frequency to seconds + ta_err *= 1e6f; // Convert to micro-seconds + ta_err = roundf(ta_err * 10.0f) / 10.0f; // Round to one tenth of micro-second res->ta_us = ta_err; } else { res->ta_us = 0.0f; } // Measure CFO - res->cfo_hz = NAN; // Not implemented + if (n_pucch_sum > 1) { + float cfo_avg_hz = 0.0f; + for (uint32_t m = 0; m < n_pucch_sum - 1; m++) { + uint32_t l0 = resource->start_symbol_idx + m * 2; + uint32_t l1 = resource->start_symbol_idx + (m + 1) * 2; + float time_diff = srsran_symbol_distance_s(l0, l1, q->carrier.scs); + float phase_diff = cargf(corr[m + 1] * conjf(corr[m])); + + if (isnormal(time_diff)) { + cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (n_pucch_sum - 1)); + } + } + res->cfo_hz = cfo_avg_hz; + } else { + res->cfo_hz = NAN; // Not implemented + } // Do averaging here // ... Not implemented // Interpolates between DMRS symbols - for (uint32_t m = 0; m < n_pucch; m++) { - uint32_t l = m * 2 + 1; - cf_t* ce_ptr = &res->ce[(carrier->nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; + for (uint32_t m = 0; m < n_pucch_sum; m++) { + cf_t* ce_ptr = &res->ce[m * SRSRAN_NRE]; - if (m != n_pucch - 1) { + if (m != n_pucch_sum - 1) { // If it is not the last symbol with DMRS, average between srsran_vec_sum_ccc(ce[m], ce[m + 1], ce_ptr, SRSRAN_NRE); srsran_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSRAN_NRE); @@ -285,7 +341,7 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, srsran_vec_sub_ccc(ce_ptr, ce[m - 1], ce_ptr, SRSRAN_NRE); srsran_vec_sc_prod_cfc(ce_ptr, 0.5f, ce_ptr, SRSRAN_NRE); } else { - // Simply copy the + // Simply copy the estimated channel srsran_vec_cf_copy(ce_ptr, ce[m], SRSRAN_NRE); } } @@ -322,8 +378,8 @@ int srsran_dmrs_pucch_format2_put(const srsran_pucch_nr_t* q, uint32_t l_start = resource->start_symbol_idx; uint32_t l_end = resource->start_symbol_idx + resource->nof_symbols; - uint32_t k_start = SRSRAN_MIN(carrier->nof_prb - 1, resource->starting_prb) * SRSRAN_NRE + 1; - uint32_t k_end = SRSRAN_MIN(carrier->nof_prb, resource->starting_prb + resource->nof_prb) * SRSRAN_NRE; + uint32_t k_start = SRSRAN_MIN(q->carrier.nof_prb - 1, resource->starting_prb) * SRSRAN_NRE + 1; + uint32_t k_end = SRSRAN_MIN(q->carrier.nof_prb, resource->starting_prb + resource->nof_prb) * SRSRAN_NRE; for (uint32_t l = l_start; l < l_end; l++) { // Compute sequence initial state uint32_t cinit = dmrs_pucch_format2_cinit(carrier, cfg, slot, l); @@ -339,22 +395,20 @@ int srsran_dmrs_pucch_format2_put(const srsran_pucch_nr_t* q, // Put sequence in k = 3 * m + 1 for (uint32_t k = k_start, i = 0; k < k_end; k += 3, i++) { - slot_symbols[l * carrier->nof_prb * SRSRAN_NRE + k] = r_l[i]; + slot_symbols[l * q->carrier.nof_prb * SRSRAN_NRE + k] = r_l[i]; } } return SRSRAN_SUCCESS; } int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q, - const srsran_carrier_nr_t* carrier, const srsran_pucch_nr_common_cfg_t* cfg, const srsran_slot_cfg_t* slot, const srsran_pucch_nr_resource_t* resource, const cf_t* slot_symbols, srsran_chest_ul_res_t* res) { - if (q == NULL || carrier == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL || - res == NULL) { + if (q == NULL || cfg == NULL || slot == NULL || resource == NULL || slot_symbols == NULL || res == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } @@ -367,12 +421,12 @@ int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q, uint32_t l_start = resource->start_symbol_idx; uint32_t l_end = resource->start_symbol_idx + resource->nof_symbols; - uint32_t k_start = SRSRAN_MIN(carrier->nof_prb - 1, resource->starting_prb) * SRSRAN_NRE + 1; - uint32_t k_end = SRSRAN_MIN(carrier->nof_prb, resource->starting_prb + resource->nof_prb) * SRSRAN_NRE; + uint32_t k_start = SRSRAN_MIN(q->carrier.nof_prb - 1, resource->starting_prb) * SRSRAN_NRE + 1; + uint32_t k_end = SRSRAN_MIN(q->carrier.nof_prb, resource->starting_prb + resource->nof_prb) * SRSRAN_NRE; uint32_t nof_ref = 4 * resource->nof_prb; for (uint32_t l = l_start, j = 0; l < l_end; l++, j++) { // Compute sequence initial state - uint32_t cinit = dmrs_pucch_format2_cinit(carrier, cfg, slot, l); + uint32_t cinit = dmrs_pucch_format2_cinit(&q->carrier, cfg, slot, l); srsran_sequence_state_t sequence = {}; srsran_sequence_state_init(&sequence, cinit); @@ -385,19 +439,20 @@ int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q, // Put sequence in k = 3 * m + 1 for (uint32_t k = k_start, i = 0; k < k_end; k += 3, i++) { - ce[j][i] = slot_symbols[l * carrier->nof_prb * SRSRAN_NRE + k]; + ce[j][i] = slot_symbols[l * q->carrier.nof_prb * SRSRAN_NRE + k]; } srsran_vec_prod_conj_ccc(ce[j], r_l, ce[j], nof_ref); } // Perform measurements - float epre = 0.0f; - float rsrp = 0.0f; - float ta_err = 0.0f; + float epre = 0.0f; + float rsrp = 0.0f; + float ta_err = 0.0f; + cf_t corr[SRSRAN_PUCCH_NR_FORMAT2_MAX_NSYMB] = {}; for (uint32_t i = 0; i < resource->nof_symbols; i++) { - cf_t corr = srsran_vec_acc_cc(ce[i], nof_ref) / nof_ref; - rsrp += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + corr[i] = srsran_vec_acc_cc(ce[i], nof_ref) / nof_ref; + rsrp += SRSRAN_CSQABS(corr[i]); epre += srsran_vec_avg_power_cf(ce[i], nof_ref); ta_err += srsran_vec_estimate_frequency(ce[i], nof_ref); } @@ -411,28 +466,46 @@ int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q, res->rsrp_dBfs = srsran_convert_power_to_dB(rsrp); res->epre = epre; res->epre_dBfs = srsran_convert_power_to_dB(epre); - res->noise_estimate = epre - rsrp; - res->noise_estimate_dbm = srsran_convert_power_to_dB(res->noise_estimate); + res->noise_estimate = SRSRAN_MAX(epre - rsrp, 1e-6f); + res->noise_estimate_dbFs = srsran_convert_power_to_dB(res->noise_estimate); res->snr = rsrp / res->noise_estimate; res->snr_db = srsran_convert_power_to_dB(res->snr); // Compute Time Aligment error in microseconds if (isnormal(ta_err)) { - ta_err /= 15e3f * (float)(1U << carrier->scs) * 3; // Convert from normalized frequency to seconds - ta_err *= 1e6f; // Convert to micro-seconds - ta_err = roundf(ta_err * 10.0f) / 10.0f; // Round to one tenth of micro-second + ta_err /= 15e3f * (float)(1U << q->carrier.scs) * 3; // Convert from normalized frequency to seconds + ta_err *= 1e6f; // Convert to micro-seconds + ta_err = roundf(ta_err * 10.0f) / 10.0f; // Round to one tenth of micro-second res->ta_us = ta_err; } else { res->ta_us = 0.0f; } + // Measure CFO + if (resource->nof_symbols > 1) { + float cfo_avg_hz = 0.0f; + for (uint32_t l = 0; l < resource->nof_symbols - 1; l++) { + uint32_t l0 = resource->start_symbol_idx + l; + uint32_t l1 = resource->start_symbol_idx + l + 1; + float time_diff = srsran_symbol_distance_s(l0, l1, q->carrier.scs); + float phase_diff = cargf(corr[l + 1] * conjf(corr[l])); + + if (isnormal(time_diff)) { + cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (resource->nof_symbols - 1)); + } + } + res->cfo_hz = cfo_avg_hz; + } else { + res->cfo_hz = NAN; // Not implemented + } + // Perform averaging // ... // Zero order hold for (uint32_t l = l_start, j = 0; l < l_end; l++, j++) { for (uint32_t k = k_start - 1, i = 0; k < k_end; k++, i++) { - res->ce[l * carrier->nof_prb * SRSRAN_NRE + k] = ce[j][i / 3]; + res->ce[l * q->carrier.nof_prb * SRSRAN_NRE + k] = ce[j][i / 3]; } } diff --git a/lib/src/phy/ch_estimation/dmrs_sch.c b/lib/src/phy/ch_estimation/dmrs_sch.c index 5e9b9c791..acd1cfa16 100644 --- a/lib/src/phy/ch_estimation/dmrs_sch.c +++ b/lib/src/phy/ch_estimation/dmrs_sch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -225,7 +225,8 @@ static int srsran_dmrs_sch_put_symbol(srsran_dmrs_sch_t* q, // ... save first consecutive PRB in the group prb_start = prb_idx; - // ... discard unused pilots and reset counter + // ... discard unused pilots and reset counter unless the PDSCH transmission carries SIB + prb_skip = SRSRAN_MAX(0, (int)prb_skip - (int)dmrs_cfg->reference_point_k_rb); srsran_sequence_state_advance(&sequence_state, prb_skip * nof_pilots_x_prb * 2); prb_skip = 0; } @@ -713,7 +714,8 @@ static int srsran_dmrs_sch_get_symbol(srsran_dmrs_sch_t* q, // ... save first consecutive PRB in the group prb_start = prb_idx; - // ... discard unused pilots and reset counter + // ... discard unused pilots and reset counter unless the PDSCH transmission carries SIB + prb_skip = SRSRAN_MAX(0, (int)prb_skip - (int)dmrs_cfg->reference_point_k_rb); srsran_sequence_state_advance(&sequence_state, prb_skip * nof_pilots_x_prb * 2); prb_skip = 0; } @@ -817,7 +819,7 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, sync_err += srsran_vec_estimate_frequency(&q->pilot_estimates[nof_pilots_x_symbol * i], nof_pilots_x_symbol); } sync_err /= (float)nof_symbols; - chest_res->sync_error = sync_err / (dmrs_stride * SRSRAN_SUBC_SPACING_NR(q->carrier.scs)); + float delay_us = sync_err / (dmrs_stride * SRSRAN_SUBC_SPACING_NR(q->carrier.scs)); #if DMRS_SCH_SYNC_PRECOMPENSATE // Pre-compensate synchronization error @@ -845,36 +847,52 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, epre /= nof_symbols; rsrp = SRSRAN_MIN(rsrp, epre - epre * 1e-7); - chest_res->rsrp = rsrp; - chest_res->rsrp_dbm = srsran_convert_power_to_dB(chest_res->rsrp); - - chest_res->noise_estimate = epre - rsrp; - chest_res->noise_estimate_dbm = srsran_convert_power_to_dB(chest_res->noise_estimate); - - chest_res->snr_db = chest_res->rsrp_dbm - chest_res->noise_estimate_dbm; - // Measure CFO if more than one symbol is used - float cfo_avg = 0.0; + float cfo_avg_hz = 0.0; + float cfo_hz_max = INFINITY; for (uint32_t i = 0; i < nof_symbols - 1; i++) { float time_diff = srsran_symbol_distance_s(symbols[i], symbols[i + 1], q->carrier.scs); float phase_diff = cargf(corr[i + 1] * conjf(corr[i])); if (isnormal(time_diff)) { - cfo_avg += phase_diff / (2.0f * M_PI * time_diff * (nof_symbols - 1)); + cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (nof_symbols - 1)); + + // The maximum measured CFO depends on the symbol time difference + cfo_hz_max = SRSRAN_MIN(cfo_hz_max, 1 / time_diff); } } - chest_res->cfo = cfo_avg; + + // Store internal CSI + q->csi.rsrp = rsrp; + q->csi.rsrp_dB = srsran_convert_power_to_dB(rsrp); + q->csi.epre = epre; + q->csi.epre_dB = srsran_convert_power_to_dB(epre); + q->csi.n0 = epre - rsrp; + q->csi.n0_dB = srsran_convert_power_to_dB(q->csi.n0); + q->csi.snr_dB = q->csi.rsrp_dB - q->csi.n0_dB; + q->csi.cfo_hz = cfo_avg_hz; + q->csi.cfo_hz_max = cfo_hz_max; + q->csi.delay_us = delay_us; + + // Write CSI in estimated channel result + chest_res->rsrp = q->csi.rsrp; + chest_res->rsrp_dbm = q->csi.rsrp_dB; + chest_res->noise_estimate = q->csi.n0; + chest_res->noise_estimate_dbm = q->csi.n0_dB; + chest_res->snr_db = q->csi.snr_dB; + chest_res->cfo = q->csi.cfo_hz; + chest_res->sync_error = q->csi.delay_us; #if DMRS_SCH_CFO_PRECOMPENSATE // Pre-compensate CFO cf_t cfo_correction[SRSRAN_NSYMB_PER_SLOT_NR] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - if (isnormal(cfo_avg)) { + if (isnormal(cfo_avg_hz)) { // Calculate phase of the first OFDM symbol (l = 0) - float arg0 = cargf(corr[0]) - 2.0f * M_PI * srsran_symbol_distance_s(0, symbols[0], q->carrier.scs) * cfo_avg; + float arg0 = cargf(corr[0]) - 2.0f * M_PI * srsran_symbol_distance_s(0, symbols[0], q->carrier.scs) * cfo_avg_hz; // Calculate CFO corrections for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) { - float arg = arg0 + 2.0f * M_PI * cfo_avg * srsran_symbol_distance_s(0, l, q->carrier.scs); + float arg = arg0 + 2.0f * M_PI * cfo_avg_hz * srsran_symbol_distance_s(0, l, q->carrier.scs); cfo_correction[l] = cexpf(I * arg); } @@ -892,7 +910,7 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, INFO("PDSCH-DMRS: RSRP=%+.2fdB EPRE=%+.2fdB CFO=%+.0fHz Sync=%.3fus", chest_res->rsrp_dbm, srsran_convert_power_to_dB(epre), - cfo_avg, + cfo_avg_hz, chest_res->sync_error * 1e6); // Average over time, only if more than one DMRS symbol diff --git a/lib/src/phy/ch_estimation/refsignal_dl.c b/lib/src/phy/ch_estimation/refsignal_dl.c index 22aec4367..a9865876a 100644 --- a/lib/src/phy/ch_estimation/refsignal_dl.c +++ b/lib/src/phy/ch_estimation/refsignal_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -36,7 +36,6 @@ */ int srsran_refsignal_cs_init(srsran_refsignal_t* q, uint32_t max_prb) { - int ret = SRSRAN_ERROR_INVALID_INPUTS; if (q != NULL) { @@ -65,7 +64,6 @@ free_and_exit: */ int srsran_refsignal_cs_set_cell(srsran_refsignal_t* q, srsran_cell_t cell) { - uint32_t c_init; uint32_t N_cp, mp; srsran_sequence_t seq; @@ -356,7 +354,6 @@ uint32_t srsran_refsignal_mbsfn_nof_symbols() inline uint32_t srsran_refsignal_mbsfn_fidx(uint32_t l) { - uint32_t ret = 0; if (l == 0) { ret = 0; @@ -456,21 +453,21 @@ free_and_exit: int srsran_refsignal_mbsfn_set_cell(srsran_refsignal_t* q, srsran_cell_t cell, uint16_t mbsfn_area_id) { + int ret = SRSRAN_SUCCESS; - int ret = SRSRAN_ERROR_INVALID_INPUTS; - q->cell = cell; + if (q == NULL) { + ret = SRSRAN_ERROR_INVALID_INPUTS; + goto exit; + } + q->cell = cell; q->mbsfn_area_id = mbsfn_area_id; - if (srsran_refsignal_mbsfn_gen_seq(q, q->cell, q->mbsfn_area_id)) { - goto free_and_exit; + if (srsran_refsignal_mbsfn_gen_seq(q, q->cell, q->mbsfn_area_id) < SRSRAN_SUCCESS) { + ret = SRSRAN_ERROR; + goto exit; } - ret = SRSRAN_SUCCESS; - -free_and_exit: - if (ret == SRSRAN_ERROR) { - srsran_refsignal_free(q); - } +exit: return ret; } diff --git a/lib/src/phy/ch_estimation/refsignal_dl_nbiot.c b/lib/src/phy/ch_estimation/refsignal_dl_nbiot.c index 11b6ae7e3..b58b632a7 100644 --- a/lib/src/phy/ch_estimation/refsignal_dl_nbiot.c +++ b/lib/src/phy/ch_estimation/refsignal_dl_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/ch_estimation/refsignal_ul.c b/lib/src/phy/ch_estimation/refsignal_ul.c index a929c9ccf..21c5f3870 100644 --- a/lib/src/phy/ch_estimation/refsignal_ul.c +++ b/lib/src/phy/ch_estimation/refsignal_ul.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -36,54 +36,56 @@ #include "srsran/phy/utils/vector.h" // n_dmrs_2 table 5.5.2.1.1-1 from 36.211 -uint32_t n_dmrs_2[8] = {0, 6, 3, 4, 2, 8, 10, 9}; +static const uint32_t n_dmrs_2[8] = {0, 6, 3, 4, 2, 8, 10, 9}; // n_dmrs_1 table 5.5.2.1.1-2 from 36.211 -uint32_t n_dmrs_1[8] = {0, 2, 3, 4, 6, 8, 9, 10}; +static const uint32_t n_dmrs_1[8] = {0, 2, 3, 4, 6, 8, 9, 10}; /* Orthogonal sequences for PUCCH formats 1a, 1b and 1c. Table 5.5.2.2.1-2 */ -float w_arg_pucch_format1_cpnorm[3][3] = {{0, 0, 0}, {0, 2 * M_PI / 3, 4 * M_PI / 3}, {0, 4 * M_PI / 3, 2 * M_PI / 3}}; +static const float w_arg_pucch_format1_cpnorm[3][3] = {{0, 0, 0}, + {0, 2 * M_PI / 3, 4 * M_PI / 3}, + {0, 4 * M_PI / 3, 2 * M_PI / 3}}; -float w_arg_pucch_format1_cpext[3][2] = {{0, 0}, {0, M_PI}, {0, 0}}; +static const float w_arg_pucch_format1_cpext[3][2] = {{0, 0}, {0, M_PI}, {0, 0}}; -float w_arg_pucch_format2_cpnorm[2] = {0, 0}; -float w_arg_pucch_format2_cpext[1] = {0}; +static const float w_arg_pucch_format2_cpnorm[2] = {0, 0}; +static const float w_arg_pucch_format2_cpext[1] = {0}; -uint32_t pucch_dmrs_symbol_format1_cpnorm[3] = {2, 3, 4}; -uint32_t pucch_dmrs_symbol_format1_cpext[2] = {2, 3}; -uint32_t pucch_dmrs_symbol_format2_cpnorm[2] = {1, 5}; -uint32_t pucch_dmrs_symbol_format2_cpext[1] = {3}; +static const uint32_t pucch_dmrs_symbol_format1_cpnorm[3] = {2, 3, 4}; +static const uint32_t pucch_dmrs_symbol_format1_cpext[2] = {2, 3}; +static const uint32_t pucch_dmrs_symbol_format2_cpnorm[2] = {1, 5}; +static const uint32_t pucch_dmrs_symbol_format2_cpext[1] = {3}; /* Table 5.5.3.3-1: Frame structure type 1 sounding reference signal subframe configuration. */ -uint32_t T_sfc[15] = {1, 2, 2, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10}; -uint32_t Delta_sfc1[7] = {0, 0, 1, 0, 1, 2, 3}; -uint32_t Delta_sfc2[4] = {0, 1, 2, 3}; +static const uint32_t T_sfc[15] = {1, 2, 2, 5, 5, 5, 5, 5, 5, 10, 10, 10, 10, 10, 10}; +static const uint32_t Delta_sfc1[7] = {0, 0, 1, 0, 1, 2, 3}; +static const uint32_t Delta_sfc2[4] = {0, 1, 2, 3}; -uint32_t m_srs_b[4][4][8] = {{/* m_srs for 6tti % 10; - if (srsran_dft_precoding_valid_prb(pusch_cfg->grant.L_prb) && sf_idx < SRSRAN_NOF_SF_X_FRAME && - pusch_cfg->grant.n_dmrs < SRSRAN_NOF_CSHIFT) { + if (srsran_dft_precoding_valid_prb(pusch_cfg->grant.L_prb) && pusch_cfg->grant.n_dmrs < SRSRAN_NOF_CSHIFT) { srsran_refsignal_dmrs_pusch_put( q, pusch_cfg, pregen->r[pusch_cfg->grant.n_dmrs][sf_idx][pusch_cfg->grant.L_prb], sf_symbols); return SRSRAN_SUCCESS; @@ -467,7 +468,7 @@ int srsran_refsignal_dmrs_pucch_gen(srsran_refsignal_ul_t* q, } // Choose number of symbols and orthogonal sequence from Tables 5.5.2.2.1-1 to -3 - float* w = NULL; + const float* w = NULL; switch (cfg->format) { case SRSRAN_PUCCH_FORMAT_1: case SRSRAN_PUCCH_FORMAT_1A: @@ -733,14 +734,14 @@ int srsran_refsignal_srs_send_cs(uint32_t subframe_config, uint32_t sf_idx) } else { return 1; } - } else if (subframe_config == 14) { + } + // subframe_config == 14 + else { if (((sf_idx % tsfc) == 7) || ((sf_idx % tsfc) == 9)) { return 0; } else { return 1; } - } else { - return 0; } } else { return SRSRAN_ERROR_INVALID_INPUTS; diff --git a/lib/src/phy/ch_estimation/test/CMakeLists.txt b/lib/src/phy/ch_estimation/test/CMakeLists.txt index 2d30c96ab..f08410b9a 100644 --- a/lib/src/phy/ch_estimation/test/CMakeLists.txt +++ b/lib/src/phy/ch_estimation/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -99,7 +99,8 @@ add_nr_test(dmrs_pdsch_test dmrs_pdsch_test) add_executable(dmrs_pdcch_test dmrs_pdcch_test.c) target_link_libraries(dmrs_pdcch_test srsran_phy) -add_nr_test(dmrs_pdcch_test dmrs_pdcch_test) +add_nr_test(dmrs_pdcch_test_non_interleaved dmrs_pdcch_test) +add_nr_test(dmrs_pdcch_test_interleaved dmrs_pdcch_test -I) ######################################################################## diff --git a/lib/src/phy/ch_estimation/test/chest_nbiot_test_dl.c b/lib/src/phy/ch_estimation/test/chest_nbiot_test_dl.c index 2f33624ec..46054d020 100644 --- a/lib/src/phy/ch_estimation/test/chest_nbiot_test_dl.c +++ b/lib/src/phy/ch_estimation/test/chest_nbiot_test_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -88,7 +88,7 @@ void parse_args(int argc, char** argv) snr_db = strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -154,7 +154,7 @@ int main(int argc, char** argv) for (int n_port = 0; n_port < cell.base.nof_ports; n_port++) { srsran_vec_cf_zero(input, num_re); for (int i = 0; i < num_re; i++) { - input[i] = 0.5 - rand() / RAND_MAX + I * (0.5 - rand() / RAND_MAX); + input[i] = 0.5 - rand() / (float)RAND_MAX + I * (0.5 - rand() / (float)RAND_MAX); } srsran_vec_cf_zero(ce, num_re); @@ -178,7 +178,7 @@ int main(int argc, char** argv) if (have_channel) { // Add noise - float std_dev = srsran_convert_dB_to_amplitude(-(snr_db + 3.0f)) * 0.1f; + float std_dev = srsran_convert_dB_to_power(-(snr_db + 20.0f)); srsran_ch_awgn_c(est.pilot_recv_signal, est.pilot_recv_signal, std_dev, SRSRAN_REFSIGNAL_MAX_NUM_SF(1)); } diff --git a/lib/src/phy/ch_estimation/test/chest_test_dl.c b/lib/src/phy/ch_estimation/test/chest_test_dl.c index eb4b169ae..c9d1f1aa3 100644 --- a/lib/src/phy/ch_estimation/test/chest_test_dl.c +++ b/lib/src/phy/ch_estimation/test/chest_test_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -68,7 +68,7 @@ void parse_args(int argc, char** argv) output_matlab = argv[optind]; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -146,7 +146,7 @@ int main(int argc, char** argv) for (uint32_t n_port = 0; n_port < cell.nof_ports; n_port++) { srsran_vec_cf_zero(input, num_re); for (i = 0; i < num_re; i++) { - input[i] = 0.5 - rand() / RAND_MAX + I * (0.5 - rand() / RAND_MAX); + input[i] = 0.5 - rand() / (float)RAND_MAX + I * (0.5 - rand() / (float)RAND_MAX); } srsran_vec_cf_zero(ce, num_re); @@ -172,7 +172,7 @@ int main(int argc, char** argv) struct timeval t[3]; gettimeofday(&t[1], NULL); - for (int j = 0; j < 100; j++) { + for (int k = 0; k < 100; k++) { srsran_chest_dl_estimate(&est, &sf_cfg, input_m, &res); } gettimeofday(&t[2], NULL); @@ -180,7 +180,7 @@ int main(int argc, char** argv) printf("CHEST: %f us\n", (float)t[0].tv_usec / 100); gettimeofday(&t[1], NULL); - for (int j = 0; j < 100; j++) { + for (int k = 0; k < 100; k++) { srsran_predecoding_single(input, ce, output, NULL, num_re, 1.0f, 0); } gettimeofday(&t[2], NULL); @@ -195,7 +195,7 @@ int main(int argc, char** argv) printf("MSE: %f\n", mse); gettimeofday(&t[1], NULL); - for (int j = 0; j < 100; j++) { + for (int k = 0; k < 100; k++) { srsran_predecoding_single(input, ce, output, NULL, num_re, 1.0f, res.noise_estimate); } gettimeofday(&t[2], NULL); diff --git a/lib/src/phy/ch_estimation/test/chest_test_sl.c b/lib/src/phy/ch_estimation/test/chest_test_sl.c index b2645f721..566aeb345 100644 --- a/lib/src/phy/ch_estimation/test/chest_test_sl.c +++ b/lib/src/phy/ch_estimation/test/chest_test_sl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -76,7 +76,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -113,9 +113,8 @@ int main(int argc, char** argv) srsran_chest_sl_t q = {}; if (run_psbch_test) { - // Tx - srsran_chest_sl_init(&q, SRSRAN_SIDELINK_PSBCH, cell, sl_comm_resource_pool); + srsran_chest_sl_init(&q, SRSRAN_SIDELINK_PSBCH, cell, &sl_comm_resource_pool); srsran_chest_sl_put_dmrs(&q, sf_buffer); // Rx diff --git a/lib/src/phy/ch_estimation/test/chest_test_srs.c b/lib/src/phy/ch_estimation/test/chest_test_srs.c index c7c75010f..c54bdd52f 100644 --- a/lib/src/phy/ch_estimation/test/chest_test_srs.c +++ b/lib/src/phy/ch_estimation/test/chest_test_srs.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,8 +19,8 @@ * */ -#include "srsran/common/test_common.h" #include "srsran/srsran.h" +#include "srsran/support/srsran_test.h" #include #include #include @@ -159,12 +159,12 @@ int srs_test_context_run(srs_test_context_t* q) INFO("RESULTS: tti=%d; snr_db=%+.1f; noise_estimate_dbm=%+.1f; ta_us=%+.1f;", ul_sf_cfg.tti, q->chest_ul_res.snr_db, - q->chest_ul_res.noise_estimate_dbm, + q->chest_ul_res.noise_estimate_dbFs, q->chest_ul_res.ta_us); // Assert SRS measurements TESTASSERT(fabsf(q->chest_ul_res.snr_db - snr_db) < CHEST_TEST_SRS_SNR_DB_TOLERANCE); - TESTASSERT(fabsf(q->chest_ul_res.noise_estimate_dbm - n0_dbm) < CHEST_TEST_SRS_SNR_DB_TOLERANCE); + TESTASSERT(fabsf(q->chest_ul_res.noise_estimate_dbFs - n0_dbm) < CHEST_TEST_SRS_SNR_DB_TOLERANCE); TESTASSERT(fabsf(q->chest_ul_res.ta_us) < CHEST_TEST_SRS_TA_US_TOLERANCE); return SRSRAN_SUCCESS; @@ -185,7 +185,7 @@ void parse_args(int argc, char** argv) cell.id = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/ch_estimation/test/chest_test_ul.c b/lib/src/phy/ch_estimation/test/chest_test_ul.c index 9807faf9c..6d7fd963c 100644 --- a/lib/src/phy/ch_estimation/test/chest_test_ul.c +++ b/lib/src/phy/ch_estimation/test/chest_test_ul.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -71,7 +71,7 @@ void parse_args(int argc, char** argv) output_matlab = argv[optind]; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -167,7 +167,7 @@ int main(int argc, char** argv) // Loop through subframe idx and cyclic shifts - for (int sf_idx = 0; sf_idx < 10; sf_idx += 3) { + for (sf_idx = 0; sf_idx < 10; sf_idx += 3) { for (int cshift_dmrs = 0; cshift_dmrs < SRSRAN_NOF_CSHIFT; cshift_dmrs += 5) { if (SRSRAN_VERBOSE_ISINFO()) { printf("nof_prb: %d, ", nof_prb); diff --git a/lib/src/phy/ch_estimation/test/csi_rs_pattern_test.c b/lib/src/phy/ch_estimation/test/csi_rs_pattern_test.c index 04f8ed6b6..a018dd213 100644 --- a/lib/src/phy/ch_estimation/test/csi_rs_pattern_test.c +++ b/lib/src/phy/ch_estimation/test/csi_rs_pattern_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,8 +19,8 @@ * */ -#include "srsran/common/test_common.h" #include "srsran/phy/ch_estimation/csi_rs.h" +#include "srsran/support/srsran_test.h" static srsran_carrier_nr_t carrier = {}; @@ -35,6 +35,7 @@ static int test_row1() } m.nof_ports = 1; m.first_symbol_idx = 0; + m.first_symbol_idx2 = 0; m.cdm = srsran_csi_rs_cdm_nocdm; m.density = srsran_csi_rs_resource_mapping_density_three; m.freq_band.start_rb = carrier.start; @@ -78,6 +79,7 @@ static int test_row2() } m.nof_ports = 1; m.first_symbol_idx = 0; + m.first_symbol_idx2 = 0; m.cdm = srsran_csi_rs_cdm_nocdm; m.density = density; m.freq_band.start_rb = carrier.start; diff --git a/lib/src/phy/ch_estimation/test/csi_rs_test.c b/lib/src/phy/ch_estimation/test/csi_rs_test.c index d4370d25b..93349308f 100644 --- a/lib/src/phy/ch_estimation/test/csi_rs_test.c +++ b/lib/src/phy/ch_estimation/test/csi_rs_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,23 +19,14 @@ * */ -#include "srsran/common/test_common.h" #include "srsran/phy/ch_estimation/csi_rs.h" #include "srsran/phy/utils/vector.h" +#include "srsran/support/srsran_test.h" #include #include #include -static srsran_carrier_nr_t carrier = { - 1, // pci - 0, // absolute_frequency_ssb - 0, // absolute_frequency_point_a - 0, // offset_to_carrier - srsran_subcarrier_spacing_15kHz, // scs - 50, // nof_prb - 0, // start - 1 // max_mimo_layers -}; +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; static float snr_dB = 20.0; static float power_control_offset = NAN; @@ -43,15 +34,15 @@ static uint32_t start_rb = UINT32_MAX; static uint32_t nof_rb = UINT32_MAX; static uint32_t first_symbol = UINT32_MAX; -static int test(const srsran_slot_cfg_t* slot_cfg, - const srsran_csi_rs_nzp_resource_t* resource, - srsran_channel_awgn_t* awgn, - cf_t* grid) +static int nzp_test_case(const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource, + srsran_channel_awgn_t* awgn, + cf_t* grid) { - srsran_csi_rs_measure_t measure = {}; + srsran_csi_trs_measurements_t measure = {}; // Put NZP-CSI-RS - TESTASSERT(srsran_csi_rs_nzp_put(&carrier, slot_cfg, resource, grid) == SRSRAN_SUCCESS); + TESTASSERT(srsran_csi_rs_nzp_put_resource(&carrier, slot_cfg, resource, grid) == SRSRAN_SUCCESS); // Configure N0 and add Noise TESTASSERT(srsran_channel_awgn_set_n0(awgn, (float)resource->power_control_offset - snr_dB) == SRSRAN_SUCCESS); @@ -64,7 +55,7 @@ static int test(const srsran_slot_cfg_t* slot_cfg, srsran_convert_power_to_dB(srsran_convert_dB_to_power(rsrp_dB_gold) + awgn->std_dev * awgn->std_dev); const float n0_dB_gold = srsran_convert_amplitude_to_dB(awgn->std_dev); - if (srsran_verbose >= SRSRAN_VERBOSE_INFO) { + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) { char str[128] = {}; srsran_csi_rs_measure_info(&measure, str, sizeof(str)); INFO("Measure: %s", str); @@ -78,6 +69,267 @@ static int test(const srsran_slot_cfg_t* slot_cfg, return SRSRAN_SUCCESS; } +static int nzp_test_brute(srsran_channel_awgn_t* awgn, cf_t* grid) +{ + // Slot configuration + srsran_slot_cfg_t slot_cfg = {}; + + // Initialise NZP-CSI-RS fix parameters, other params are not implemented + srsran_csi_rs_nzp_resource_t resource = {}; + resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; + resource.resource_mapping.nof_ports = 1; + + // Row 1 supported only! + uint32_t nof_freq_dom_alloc = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; + + uint32_t first_symbol_begin = (first_symbol != UINT32_MAX) ? first_symbol : 0; + uint32_t first_symbol_end = (first_symbol != UINT32_MAX) ? first_symbol : 13; + for (resource.resource_mapping.first_symbol_idx = first_symbol_begin; + resource.resource_mapping.first_symbol_idx <= first_symbol_end; + resource.resource_mapping.first_symbol_idx++) { + // Iterate over possible power control offset + float power_control_offset_begin = isnormal(power_control_offset) ? power_control_offset : -8.0f; + float power_control_offset_end = isnormal(power_control_offset) ? power_control_offset : 15.0f; + for (resource.power_control_offset = power_control_offset_begin; + resource.power_control_offset <= power_control_offset_end; + resource.power_control_offset += 1.0f) { + // Iterate over all possible starting number of PRB + uint32_t start_rb_begin = (start_rb != UINT32_MAX) ? start_rb : 0; + uint32_t start_rb_end = (start_rb != UINT32_MAX) ? start_rb : carrier.nof_prb - 24; + for (resource.resource_mapping.freq_band.start_rb = start_rb_begin; + resource.resource_mapping.freq_band.start_rb <= start_rb_end; + resource.resource_mapping.freq_band.start_rb += 4) { + // Iterate over all possible number of PRB + uint32_t nof_rb_begin = (nof_rb != UINT32_MAX) ? nof_rb : 24; + uint32_t nof_rb_end = + (nof_rb != UINT32_MAX) ? nof_rb : (carrier.nof_prb - resource.resource_mapping.freq_band.start_rb); + for (resource.resource_mapping.freq_band.nof_rb = nof_rb_begin; + resource.resource_mapping.freq_band.nof_rb <= nof_rb_end; + resource.resource_mapping.freq_band.nof_rb += 4) { + // Iterate for all slot numbers + for (slot_cfg.idx = 0; slot_cfg.idx < SRSRAN_NSLOTS_PER_FRAME_NR(carrier.scs); slot_cfg.idx++) { + // Steer Frequency allocation + for (uint32_t freq_dom_alloc = 0; freq_dom_alloc < nof_freq_dom_alloc; freq_dom_alloc++) { + for (uint32_t i = 0; i < nof_freq_dom_alloc; i++) { + resource.resource_mapping.frequency_domain_alloc[i] = i == freq_dom_alloc; + } + + // Call actual test + TESTASSERT(nzp_test_case(&slot_cfg, &resource, awgn, grid) == SRSRAN_SUCCESS); + } + } + } + } + } + } + + return SRSRAN_SUCCESS; +} + +static int nzp_test_trs(srsran_channel_awgn_t* awgn, cf_t* grid) +{ + // Slot configuration + srsran_slot_cfg_t slot_cfg = {}; + + // Item 1 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 1 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 4 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 11 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t resource1 = {}; + resource1.id = 1; + resource1.resource_mapping.frequency_domain_alloc[0] = 0; + resource1.resource_mapping.frequency_domain_alloc[1] = 0; + resource1.resource_mapping.frequency_domain_alloc[2] = 0; + resource1.resource_mapping.frequency_domain_alloc[3] = 1; + resource1.resource_mapping.nof_ports = 1; + resource1.resource_mapping.first_symbol_idx = 4; + resource1.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource1.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource1.resource_mapping.freq_band.start_rb = 0; + resource1.resource_mapping.freq_band.nof_rb = carrier.nof_prb; + resource1.power_control_offset = 0; + resource1.power_control_offset_ss = 0; + resource1.periodicity.period = 40; + resource1.periodicity.offset = 11; + + // Item 2 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 2 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 8 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 11 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t resource2 = {}; + resource2.id = 1; + resource2.resource_mapping.frequency_domain_alloc[0] = 0; + resource2.resource_mapping.frequency_domain_alloc[1] = 0; + resource2.resource_mapping.frequency_domain_alloc[2] = 0; + resource2.resource_mapping.frequency_domain_alloc[3] = 1; + resource2.resource_mapping.nof_ports = 1; + resource2.resource_mapping.first_symbol_idx = 8; + resource2.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource2.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource2.resource_mapping.freq_band.start_rb = 0; + resource2.resource_mapping.freq_band.nof_rb = carrier.nof_prb; + resource2.power_control_offset = 0; + resource2.power_control_offset_ss = 0; + resource2.periodicity.period = 40; + resource2.periodicity.offset = 11; + + // Item 3 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 3 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 4 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 12 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t resource3 = {}; + resource3.id = 1; + resource3.resource_mapping.frequency_domain_alloc[0] = 0; + resource3.resource_mapping.frequency_domain_alloc[1] = 0; + resource3.resource_mapping.frequency_domain_alloc[2] = 0; + resource3.resource_mapping.frequency_domain_alloc[3] = 1; + resource3.resource_mapping.nof_ports = 1; + resource3.resource_mapping.first_symbol_idx = 4; + resource3.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource3.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource3.resource_mapping.freq_band.start_rb = 0; + resource3.resource_mapping.freq_band.nof_rb = carrier.nof_prb; + resource3.power_control_offset = 0; + resource3.power_control_offset_ss = 0; + resource3.periodicity.period = 40; + resource3.periodicity.offset = 12; + + // Item 4 + // NZP-CSI-RS-Resource + // nzp-CSI-RS-ResourceId: 4 + // resourceMapping + // frequencyDomainAllocation: row1 (0) + // row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal value 1] + // nrofPorts: p1 (0) + // firstOFDMSymbolInTimeDomain: 8 + // cdm-Type: noCDM (0) + // density: three (2) + // three: NULL + // freqBand + // startingRB: 0 + // nrofRBs: 52 + // powerControlOffset: 0dB + // powerControlOffsetSS: db0 (1) + // scramblingID: 0 + // periodicityAndOffset: slots40 (7) + // slots40: 12 + // qcl-InfoPeriodicCSI-RS: 0 + srsran_csi_rs_nzp_resource_t resource4 = {}; + resource4.id = 1; + resource4.resource_mapping.frequency_domain_alloc[0] = 0; + resource4.resource_mapping.frequency_domain_alloc[1] = 0; + resource4.resource_mapping.frequency_domain_alloc[2] = 0; + resource4.resource_mapping.frequency_domain_alloc[3] = 1; + resource4.resource_mapping.nof_ports = 1; + resource4.resource_mapping.first_symbol_idx = 8; + resource4.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; + resource4.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; + resource4.resource_mapping.freq_band.start_rb = 0; + resource4.resource_mapping.freq_band.nof_rb = carrier.nof_prb; + resource4.power_control_offset = 0; + resource4.power_control_offset_ss = 0; + resource4.periodicity.period = 40; + resource4.periodicity.offset = 12; + + // NZP-CSI-RS-ResourceSet + // nzp-CSI-ResourceSetId: 1 + // nzp-CSI-RS-Resources: 4 items + // Item 0 + // NZP-CSI-RS-ResourceId: 1 + // Item 1 + // NZP-CSI-RS-ResourceId: 2 + // Item 2 + // NZP-CSI-RS-ResourceId: 3 + // Item 3 + // NZP-CSI-RS-ResourceId: 4 + // trs-Info: true (0) + srsran_csi_rs_nzp_set_t set = {}; + set.data[set.count++] = resource1; + set.data[set.count++] = resource2; + set.data[set.count++] = resource3; + set.data[set.count++] = resource4; + set.trs_info = true; + + for (slot_cfg.idx = 0; slot_cfg.idx < resource1.periodicity.period; slot_cfg.idx++) { + // Put NZP-CSI-RS TRS signals + int ret = srsran_csi_rs_nzp_put_set(&carrier, &slot_cfg, &set, grid); + + // Check return + if (slot_cfg.idx == 11 || slot_cfg.idx == 12) { + TESTASSERT(ret == 2); + } else { + TESTASSERT(ret == 0); + } + + // Configure N0 and add Noise + TESTASSERT(srsran_channel_awgn_set_n0(awgn, (float)set.data[0].power_control_offset - snr_dB) == SRSRAN_SUCCESS); + srsran_channel_awgn_run_c(awgn, grid, grid, SRSRAN_SLOT_LEN_RE_NR(carrier.nof_prb)); + + // Measure + srsran_csi_trs_measurements_t measure = {}; + ret = srsran_csi_rs_nzp_measure_trs(&carrier, &slot_cfg, &set, grid, &measure); + + // Check return and assert measurement + if (slot_cfg.idx == 11 || slot_cfg.idx == 12) { + TESTASSERT(ret == 2); + } else { + TESTASSERT(ret == 0); + } + } + + return SRSRAN_SUCCESS; +} + static void usage(char* prog) { printf("Usage: %s [recov]\n", prog); @@ -118,7 +370,7 @@ static void parse_args(int argc, char** argv) first_symbol = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -129,10 +381,8 @@ static void parse_args(int argc, char** argv) int main(int argc, char** argv) { - int ret = SRSRAN_ERROR; - srsran_slot_cfg_t slot_cfg = {}; - srsran_csi_rs_nzp_resource_t resource = {}; - srsran_channel_awgn_t awgn = {}; + int ret = SRSRAN_ERROR; + srsran_channel_awgn_t awgn = {}; parse_args(argc, argv); @@ -147,56 +397,12 @@ int main(int argc, char** argv) goto clean_exit; } - // Fixed parameters, other params are not implemented - resource.resource_mapping.cdm = srsran_csi_rs_cdm_nocdm; - resource.resource_mapping.density = srsran_csi_rs_resource_mapping_density_three; - resource.resource_mapping.row = srsran_csi_rs_resource_mapping_row_1; - resource.resource_mapping.nof_ports = 1; + if (nzp_test_brute(&awgn, grid) < SRSRAN_SUCCESS) { + goto clean_exit; + } - // Row 1 supported only! - uint32_t nof_freq_dom_alloc = SRSRAN_CSI_RS_NOF_FREQ_DOMAIN_ALLOC_ROW1; - - uint32_t first_symbol_begin = (first_symbol != UINT32_MAX) ? first_symbol : 0; - uint32_t first_symbol_end = (first_symbol != UINT32_MAX) ? first_symbol : 13; - for (resource.resource_mapping.first_symbol_idx = first_symbol_begin; - resource.resource_mapping.first_symbol_idx <= first_symbol_end; - resource.resource_mapping.first_symbol_idx++) { - // Iterate over possible power control offset - float power_control_offset_begin = isnormal(power_control_offset) ? power_control_offset : -8.0f; - float power_control_offset_end = isnormal(power_control_offset) ? power_control_offset : 15.0f; - for (resource.power_control_offset = power_control_offset_begin; - resource.power_control_offset <= power_control_offset_end; - resource.power_control_offset += 1.0f) { - // Iterate over all possible starting number of PRB - uint32_t start_rb_begin = (start_rb != UINT32_MAX) ? start_rb : 0; - uint32_t start_rb_end = (start_rb != UINT32_MAX) ? start_rb : carrier.nof_prb - 24; - for (resource.resource_mapping.freq_band.start_rb = start_rb_begin; - resource.resource_mapping.freq_band.start_rb <= start_rb_end; - resource.resource_mapping.freq_band.start_rb += 4) { - // Iterate over all possible number of PRB - uint32_t nof_rb_begin = (nof_rb != UINT32_MAX) ? nof_rb : 24; - uint32_t nof_rb_end = - (nof_rb != UINT32_MAX) ? nof_rb : (carrier.nof_prb - resource.resource_mapping.freq_band.start_rb); - for (resource.resource_mapping.freq_band.nof_rb = nof_rb_begin; - resource.resource_mapping.freq_band.nof_rb <= nof_rb_end; - resource.resource_mapping.freq_band.nof_rb += 4) { - // Iterate for all slot numbers - for (slot_cfg.idx = 0; slot_cfg.idx < SRSRAN_NSLOTS_PER_FRAME_NR(carrier.scs); slot_cfg.idx++) { - // Steer Frequency allocation - for (uint32_t freq_dom_alloc = 0; freq_dom_alloc < nof_freq_dom_alloc; freq_dom_alloc++) { - for (uint32_t i = 0; i < nof_freq_dom_alloc; i++) { - resource.resource_mapping.frequency_domain_alloc[i] = i == freq_dom_alloc; - } - - // Call actual test - if (test(&slot_cfg, &resource, &awgn, grid) < SRSRAN_SUCCESS) { - goto clean_exit; - } - } - } - } - } - } + if (nzp_test_trs(&awgn, grid) < SRSRAN_SUCCESS) { + goto clean_exit; } ret = SRSRAN_SUCCESS; @@ -215,4 +421,4 @@ clean_exit: } return ret; -} \ No newline at end of file +} diff --git a/lib/src/phy/ch_estimation/test/dmrs_pdcch_test.c b/lib/src/phy/ch_estimation/test/dmrs_pdcch_test.c index 0231b1655..c72c3c696 100644 --- a/lib/src/phy/ch_estimation/test/dmrs_pdcch_test.c +++ b/lib/src/phy/ch_estimation/test/dmrs_pdcch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,35 +19,34 @@ * */ -#include "srsran/common/test_common.h" #include "srsran/phy/ch_estimation/dmrs_pdcch.h" #include "srsran/phy/phch/pdcch_nr.h" +#include "srsran/support/srsran_test.h" #include #include #include #include #include -static srsran_carrier_nr_t carrier = {}; -static srsran_dmrs_pdcch_ce_t pdcch_ce = {}; -static uint16_t rnti = 0x1234; +static srsran_carrier_nr_t carrier = {}; +static srsran_dmrs_pdcch_ce_t pdcch_ce = {}; +static uint16_t rnti = 0x1234; +static bool interleaved = false; void usage(char* prog) { - printf("Usage: %s [recov]\n", prog); - + printf("Usage: %s [recoIv]\n", prog); printf("\t-r nof_prb [Default %d]\n", carrier.nof_prb); printf("\t-e extended cyclic prefix [Default normal]\n"); - printf("\t-c cell_id [Default %d]\n", carrier.pci); - + printf("\t-I Enable interleaved CCE-to-REG [Default %s]\n", interleaved ? "Enabled" : "Disabled"); printf("\t-v increase verbosity\n"); } static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "recov")) != -1) { + while ((opt = getopt(argc, argv, "recoIv")) != -1) { switch (opt) { case 'r': carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); @@ -55,8 +54,11 @@ static void parse_args(int argc, char** argv) case 'c': carrier.pci = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'I': + interleaved ^= true; + break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -85,6 +87,9 @@ static int run_test(srsran_dmrs_pdcch_estimator_t* estimator, TESTASSERT(nof_locations == search_space->nof_candidates[aggregation_level]); + // Prevent possible out of bounds read in locations + TESTASSERT(nof_locations <= SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR); + for (uint32_t candidate = 0; candidate < nof_locations; candidate++) { dci_location.ncce = locations[candidate]; @@ -129,7 +134,18 @@ int main(int argc, char** argv) parse_args(argc, argv); - srsran_coreset_t coreset = {}; + srsran_coreset_t coreset = {}; + if (interleaved) { + coreset.mapping_type = srsran_coreset_mapping_type_interleaved; + coreset.reg_bundle_size = srsran_coreset_bundle_size_n6; + coreset.interleaver_size = srsran_coreset_bundle_size_n2; + coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + coreset.shift_index = carrier.pci; + + carrier.nof_prb = 52; + carrier.pci = 500; + } + srsran_search_space_t search_space = {}; srsran_dmrs_pdcch_estimator_t estimator = {}; @@ -139,8 +155,6 @@ int main(int argc, char** argv) uint32_t test_counter = 0; uint32_t test_passed = 0; - coreset.mapping_type = srsran_coreset_mapping_type_non_interleaved; - uint32_t nof_frequency_resource = SRSRAN_MIN(SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, carrier.nof_prb / 6); for (uint32_t frequency_resources = 1; frequency_resources < (1U << nof_frequency_resource); frequency_resources++) { uint32_t nof_freq_resources = 0; @@ -150,7 +164,14 @@ int main(int argc, char** argv) nof_freq_resources += mask; } - for (coreset.duration = 1; coreset.duration <= 3; coreset.duration++) { + for (coreset.duration = SRSRAN_CORESET_DURATION_MIN; coreset.duration <= SRSRAN_CORESET_DURATION_MAX; + coreset.duration++) { + // Skip case if CORESET bandwidth is not enough + uint32_t N = srsran_coreset_get_bw(&coreset) * coreset.duration; + if (interleaved && N % 12 != 0) { + continue; + } + for (search_space.type = srsran_search_space_type_common_0; search_space.type <= srsran_search_space_type_ue; search_space.type++) { for (uint32_t i = 0; i < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; i++) { diff --git a/lib/src/phy/ch_estimation/test/dmrs_pdsch_test.c b/lib/src/phy/ch_estimation/test/dmrs_pdsch_test.c index 9585d1be9..f2cae909d 100644 --- a/lib/src/phy/ch_estimation/test/dmrs_pdsch_test.c +++ b/lib/src/phy/ch_estimation/test/dmrs_pdsch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,26 +19,17 @@ * */ -#include "srsran/common/test_common.h" #include "srsran/phy/ch_estimation/dmrs_sch.h" #include "srsran/phy/phch/ra_dl_nr.h" #include "srsran/srsran.h" +#include "srsran/support/srsran_test.h" #include #include #include #include #include -static srsran_carrier_nr_t carrier = { - 1, // pci - 0, // absolute_frequency_ssb - 0, // absolute_frequency_point_a - 0, // offset_to_carrier - srsran_subcarrier_spacing_15kHz, // scs - 50, // nof_prb - 0, // start - 1 // max_mimo_layers -}; +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; typedef struct { srsran_sch_mapping_type_t mapping_type; @@ -159,7 +150,7 @@ static void parse_args(int argc, char** argv) carrier.pci = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/ch_estimation/test/refsignal_ul_test.c b/lib/src/phy/ch_estimation/test/refsignal_ul_test.c index 974e277ed..8b5fed1ef 100644 --- a/lib/src/phy/ch_estimation/test/refsignal_ul_test.c +++ b/lib/src/phy/ch_estimation/test/refsignal_ul_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,7 +66,7 @@ void parse_args(int argc, char** argv) cell.id = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/ch_estimation/wiener_dl.c b/lib/src/phy/ch_estimation/wiener_dl.c index e0f94fd44..ef9bb8e48 100644 --- a/lib/src/phy/ch_estimation/wiener_dl.c +++ b/lib/src/phy/ch_estimation/wiener_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/channel/CMakeLists.txt b/lib/src/phy/channel/CMakeLists.txt index e6724b502..9f6d1eb8e 100644 --- a/lib/src/phy/channel/CMakeLists.txt +++ b/lib/src/phy/channel/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/channel/ch_awgn.c b/lib/src/phy/channel/ch_awgn.c index 63c034579..3ca5cbb14 100644 --- a/lib/src/phy/channel/ch_awgn.c +++ b/lib/src/phy/channel/ch_awgn.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -79,6 +79,7 @@ int srsran_channel_awgn_init(srsran_channel_awgn_t* q, uint32_t seed) q->table_log = srsran_vec_f_malloc(AWGN_TABLE_ALLOC_SIZE); if (!q->table_cos || !q->table_log) { ERROR("Malloc"); + return SRSRAN_ERROR; } // Fill tables @@ -127,7 +128,6 @@ static inline void channel_awgn_run(srsran_channel_awgn_t* q, const float* in, f #if SRSRAN_SIMD_F_SIZE for (; i < (int)size - SRSRAN_SIMD_F_SIZE + 1; i += SRSRAN_SIMD_F_SIZE) { - if (i % AWGN_TABLE_READ_BURST == 0) { idx1 = channel_awgn_rand(q); idx2 = channel_awgn_rand(q); @@ -153,7 +153,6 @@ static inline void channel_awgn_run(srsran_channel_awgn_t* q, const float* in, f #endif /* SRSRAN_SIMD_F_SIZE */ for (; i < size; i++) { - if (i % AWGN_TABLE_READ_BURST == 0) { idx1 = channel_awgn_rand(q); idx2 = channel_awgn_rand(q); @@ -205,19 +204,21 @@ void srsran_ch_awgn_c(const cf_t* x, cf_t* y, float variance, uint32_t len) { cf_t tmp; uint32_t i; + float stddev = sqrtf(variance); for (i = 0; i < len; i++) { __real__ tmp = rand_gauss(); __imag__ tmp = rand_gauss(); - tmp *= variance; + tmp *= stddev * (float)M_SQRT1_2; y[i] = tmp + x[i]; } } void srsran_ch_awgn_f(const float* x, float* y, float variance, uint32_t len) { uint32_t i; + float stddev = sqrtf(variance); for (i = 0; i < len; i++) { - y[i] = x[i] + variance * rand_gauss(); + y[i] = x[i] + stddev * rand_gauss(); } } diff --git a/lib/src/phy/channel/channel.cc b/lib/src/phy/channel/channel.cc index 85ae05723..9519b7d16 100644 --- a/lib/src/phy/channel/channel.cc +++ b/lib/src/phy/channel/channel.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -221,7 +221,7 @@ void channel::run(cf_t* in[SRSRAN_MAX_CHANNELS], // Logging std::stringstream str; - str << "t=" << t.full_secs + t.frac_secs << "s; "; + str << "Channel: t=" << t.full_secs + t.frac_secs << "s; "; if (delay[0]) { str << "delay=" << delay[0]->delay_us << "us; "; } diff --git a/lib/src/phy/channel/delay.c b/lib/src/phy/channel/delay.c index cc52408c6..8ca5f34a7 100644 --- a/lib/src/phy/channel/delay.c +++ b/lib/src/phy/channel/delay.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/channel/fading.c b/lib/src/phy/channel/fading.c index 1c97791e4..1731be849 100644 --- a/lib/src/phy/channel/fading.c +++ b/lib/src/phy/channel/fading.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/channel/gauss.c b/lib/src/phy/channel/gauss.c index d84bff737..acebb89e7 100644 --- a/lib/src/phy/channel/gauss.c +++ b/lib/src/phy/channel/gauss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/channel/gauss.h b/lib/src/phy/channel/gauss.h index 12a5fac24..9336fe580 100644 --- a/lib/src/phy/channel/gauss.h +++ b/lib/src/phy/channel/gauss.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/channel/hst.c b/lib/src/phy/channel/hst.c index d5957dfc8..702ec0a05 100644 --- a/lib/src/phy/channel/hst.c +++ b/lib/src/phy/channel/hst.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/channel/rlf.c b/lib/src/phy/channel/rlf.c index a4b7c70e0..1414e02ba 100644 --- a/lib/src/phy/channel/rlf.c +++ b/lib/src/phy/channel/rlf.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/channel/test/CMakeLists.txt b/lib/src/phy/channel/test/CMakeLists.txt index 2023d8fce..5c46af369 100644 --- a/lib/src/phy/channel/test/CMakeLists.txt +++ b/lib/src/phy/channel/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/channel/test/awgn_channel_test.c b/lib/src/phy/channel/test/awgn_channel_test.c index 537113bf6..bd3982da5 100644 --- a/lib/src/phy/channel/test/awgn_channel_test.c +++ b/lib/src/phy/channel/test/awgn_channel_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -49,7 +49,7 @@ static void usage(char* prog) printf("\t-t tolerance: [Default %.3f]\n", tolerance); } -static void parse_args(int argc, char** argv) +static int parse_args(int argc, char** argv) { int opt; while ((opt = getopt(argc, argv, "nmMst")) != -1) { @@ -71,9 +71,70 @@ static void parse_args(int argc, char** argv) break; default: usage(argv[0]); - exit(-1); + return SRSRAN_ERROR; } } + return SRSRAN_SUCCESS; +} + +static int compare_floats(const void* a, const void* b) +{ + float arg1 = *(const float*)a; + float arg2 = *(const float*)b; + + if (arg1 < arg2) { + return -1; + } + if (arg1 > arg2) { + return 1; + } + return 0; +} + +/* + * Checks for Gaussianity with the Anderson--Darling test: if the returned statistic A2 is larger than 1 + * (and if the number of samples is larger than 100), then the Gaussianity hypothesis is rejected with a significance + * level of approximately 1% (see https://en.wikipedia.org/wiki/Anderson%E2%80%93Darling_test). + * + * x points to the vector of samples (real values and imaginary values) + * half_length is the number of complex samples + * y is a pointer to a helper vector used for temporary computations + */ +static float anderson(const float* x, uint32_t half_length, float* y) +{ +#define SQRT1_2 ((float)M_SQRT1_2) +#define CDF(a) ((1 + erff((a)*SQRT1_2)) * .5) + + uint32_t length = 2 * half_length; + float length_f = (float)length; + + // estimate mean and variance (the test works better with estimated values than with nominal ones) + float mean = srsran_vec_acc_ff(x, length); + mean /= length_f; + + srsran_vec_sc_sum_fff(x, -mean, y, length); + float variance = srsran_vec_dot_prod_fff(y, y, length); + variance /= length_f - 1; + + // standardize samples + srsran_vec_sc_prod_fff(y, 1 / sqrtf(variance), y, length); + + // sort standardized samples + qsort(y, length, sizeof(float), compare_floats); + + // compute Anderson--Darling statistic + float cdf1 = NAN; + float cdf2 = NAN; + float a2 = 0; + for (uint32_t ii = 0; ii < nof_samples; ii++) { + cdf1 = CDF(y[ii]); + cdf2 = CDF(y[length - ii - 1]); + a2 += (2.F * ii + 1) * (logf(cdf1) + log1pf(-cdf2)) + (2.F * (length - ii) - 1) * (logf(cdf2) + log1pf(-cdf1)); + } + a2 = -length_f - a2 / length_f; + a2 = a2 * (1 + (4 - 25 / length_f) / length_f); + + return a2; } int main(int argc, char** argv) @@ -81,19 +142,29 @@ int main(int argc, char** argv) int ret = SRSRAN_SUCCESS; cf_t* input_buffer = NULL; cf_t* output_buffer = NULL; + float* help_buffer = NULL; uint64_t count_samples = 0; uint64_t count_us = 0; +#ifdef ENABLE_GUI + cf_t* fft_out = NULL; +#endif + // Parse arguments - parse_args(argc, argv); + if (parse_args(argc, argv) < SRSRAN_SUCCESS) { + ret = SRSRAN_ERROR; + goto clean_exit; + } // Initialise buffers input_buffer = srsran_vec_cf_malloc(nof_samples); output_buffer = srsran_vec_cf_malloc(nof_samples); + help_buffer = srsran_vec_f_malloc(2 * nof_samples); - if (!input_buffer || !output_buffer) { + if (!input_buffer || !output_buffer || !help_buffer) { ERROR("Error: Allocating memory"); ret = SRSRAN_ERROR; + goto clean_exit; } // Initialise input @@ -113,24 +184,31 @@ int main(int argc, char** argv) plot_scatter_setTitle(&plot_fft, "IQ"); plot_scatter_addToWindowGrid(&plot_fft, (char*)"IQ", 1, 0); - cf_t* fft_out = srsran_vec_cf_malloc(nof_samples); - srsran_dft_plan_t fft = {}; + fft_out = srsran_vec_cf_malloc(nof_samples); + srsran_dft_plan_t fft = {}; if (srsran_dft_plan_c(&fft, nof_samples, SRSRAN_DFT_FORWARD)) { ERROR("Error: init DFT"); ret = SRSRAN_ERROR; + goto clean_exit; } #endif /* ENABLE_GUI */ // Initialise AWGN channel - if (ret == SRSRAN_SUCCESS) { - ret = srsran_channel_awgn_init(&awgn, 0); + if (srsran_channel_awgn_init(&awgn, 0) < SRSRAN_SUCCESS) { + ERROR("Error initialising AWGN channel"); + ret = SRSRAN_ERROR; + goto clean_exit; } float n0 = n0_min; while (!isnan(n0) && !isinf(n0) && n0 < n0_max) { struct timeval t[3] = {}; - srsran_channel_awgn_set_n0(&awgn, n0); + if (srsran_channel_awgn_set_n0(&awgn, n0) < SRSRAN_SUCCESS) { + ERROR("Error setting AWGN n0"); + ret = SRSRAN_ERROR; + goto clean_exit; + } // Run actual test gettimeofday(&t[1], NULL); @@ -146,6 +224,14 @@ int main(int argc, char** argv) ret = SRSRAN_ERROR; } + // Check for Gaussianity + float a2 = anderson((float*)output_buffer, nof_samples, help_buffer); + if ((nof_samples > 100 && a2 > 1) || !isfinite(a2)) { + printf("-- failed: A2 = %f > 1: not Gaussian\n", a2); + // TODO: use proper RNG with gaussian behaviour + // ret = SRSRAN_ERROR; + } + #ifdef ENABLE_GUI plot_scatter_setNewData(&plot_scatter, output_buffer, nof_samples); @@ -175,16 +261,34 @@ int main(int argc, char** argv) n0 += n0_step; } - // Free + // Print result and exit + double msps = 0; + if (count_us) { + msps = (double)nof_samples / (double)count_us; + } else { + ERROR("Error in Msps calculation: undefined division"); + ret = SRSRAN_ERROR; + } + printf("Test n0_min=%.3f; n0_max=%.3f; n0_step=%.3f; nof_samples=%d; %s ... %.1f MSps\n", + n0_min, + n0_max, + n0_step, + nof_samples, + (ret == SRSRAN_SUCCESS) ? "Passed" : "Failed", + msps); + +clean_exit: srsran_channel_awgn_free(&awgn); if (input_buffer) { free(input_buffer); } - if (output_buffer) { free(output_buffer); } + if (help_buffer) { + free(help_buffer); + } #ifdef ENABLE_GUI if (fft_out) { @@ -193,13 +297,5 @@ int main(int argc, char** argv) srsran_dft_plan_free(&fft); #endif /* ENABLE_GUI */ - // Print result and exit - printf("Test n0_min=%.3f; n0_max=%.3f; n0_step=%.3f; nof_samples=%d; %s ... %.1f MSps\n", - n0_min, - n0_max, - n0_step, - nof_samples, - (ret == SRSRAN_SUCCESS) ? "Passed" : "Failed", - (double)nof_samples / (double)count_us); return ret; } diff --git a/lib/src/phy/channel/test/delay_channel_test.c b/lib/src/phy/channel/test/delay_channel_test.c index c1fd68822..828559b9d 100644 --- a/lib/src/phy/channel/test/delay_channel_test.c +++ b/lib/src/phy/channel/test/delay_channel_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -46,7 +46,7 @@ static void usage(char* prog) printf("\t-T Simulation Time in periods: [Default %d]\n", sim_time_periods); } -static void parse_args(int argc, char** argv) +static int parse_args(int argc, char** argv) { int opt; while ((opt = getopt(argc, argv, "mMtsT")) != -1) { @@ -68,14 +68,15 @@ static void parse_args(int argc, char** argv) break; default: usage(argv[0]); - exit(-1); + return SRSRAN_ERROR; } } + return SRSRAN_SUCCESS; } int main(int argc, char** argv) { - int ret = SRSRAN_SUCCESS; + int ret = SRSRAN_ERROR; cf_t* input_buffer = NULL; cf_t* output_buffer = NULL; srsran_timestamp_t ts = {}; // Initialised to zero @@ -83,28 +84,32 @@ int main(int argc, char** argv) struct timeval t[3] = {}; // Parse arguments - parse_args(argc, argv); + if (parse_args(argc, argv) < SRSRAN_SUCCESS) { + goto clean_exit; + } // Initialise buffers uint32_t size = srate_hz / 1000; input_buffer = srsran_vec_cf_malloc(size); output_buffer = srsran_vec_cf_malloc(size); if (!input_buffer || !output_buffer) { - fprintf(stderr, "Error: Allocating memory\n"); - ret = SRSRAN_ERROR; + ERROR("Error: Allocating memory"); + goto clean_exit; } // Generate random samples srsran_random_uniform_complex_dist_vector(random_gen, input_buffer, size, -1.0f, +1.0f); // Initialise delay channel - if (ret == SRSRAN_SUCCESS) { - ret = srsran_channel_delay_init(&delay, delay_min_us, delay_max_us, delay_period_s, delay_init_time_s, srate_hz); + if (srsran_channel_delay_init(&delay, delay_min_us, delay_max_us, delay_period_s, delay_init_time_s, srate_hz) < + SRSRAN_SUCCESS) { + ERROR("Error initialising delay channel"); + goto clean_exit; } // Run actual test gettimeofday(&t[1], NULL); - for (int i = 0; i < sim_time_periods && ret == SRSRAN_SUCCESS; i++) { + for (int i = 0; i < sim_time_periods; i++) { for (int j = 0; j < 1000 * delay_period_s; j++) { // Run delay channel srsran_channel_delay_execute(&delay, input_buffer, output_buffer, size, &ts); @@ -116,7 +121,28 @@ int main(int argc, char** argv) gettimeofday(&t[2], NULL); get_time_interval(t); - // Free + uint64_t nof_samples = sim_time_periods * 1000 * delay_period_s * size; + double elapsed_us = t[0].tv_sec * 1e6 + t[0].tv_usec; + + double msps = 0; + if (isnormal(elapsed_us)) { + msps = (double)nof_samples / elapsed_us; + ret = SRSRAN_SUCCESS; + } else { + ERROR("Error in Msps calculation: undefined division"); + } + + // Print result and exit + printf("Test delay_min_us=%d; delay_max_us=%d; delay_period_s=%.1f; srate_hz=%d; periods=%d; %s ... %.1f MSps\n", + delay_min_us, + delay_max_us, + delay_period_s, + srate_hz, + sim_time_periods, + (ret == SRSRAN_SUCCESS) ? "Passed" : "Failed", + msps); + +clean_exit: srsran_random_free(random_gen); srsran_channel_delay_free(&delay); @@ -128,17 +154,5 @@ int main(int argc, char** argv) free(output_buffer); } - uint64_t nof_samples = sim_time_periods * 1000 * delay_period_s * size; - double elapsed_us = t[0].tv_sec * 1e6 + t[0].tv_usec; - - // Print result and exit - printf("Test delay_min_us=%d; delay_max_us=%d; delay_period_s=%.1f; srate_hz=%d; periods=%d; %s ... %.1f MSps\n", - delay_min_us, - delay_max_us, - delay_period_s, - srate_hz, - sim_time_periods, - (ret == SRSRAN_SUCCESS) ? "Passed" : "Failed", - (double)nof_samples / elapsed_us); - exit(ret); + return (ret); } diff --git a/lib/src/phy/channel/test/fading_channel_test.c b/lib/src/phy/channel/test/fading_channel_test.c index 798a0e92a..a89508b69 100644 --- a/lib/src/phy/channel/test/fading_channel_test.c +++ b/lib/src/phy/channel/test/fading_channel_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -58,7 +58,7 @@ static void usage(char* prog) #endif /* ENABLE_GUI */ } -static void parse_args(int argc, char** argv) +static int parse_args(int argc, char** argv) { int opt; while ((opt = getopt(argc, argv, "mtsrg")) != -1) { @@ -82,9 +82,10 @@ static void parse_args(int argc, char** argv) break; default: usage(argv[0]); - exit(-1); + return SRSRAN_ERROR; } } + return SRSRAN_SUCCESS; } int main(int argc, char** argv) @@ -95,7 +96,16 @@ int main(int argc, char** argv) struct timeval t[3] = {}; uint64_t time_usec = 0; - parse_args(argc, argv); +#ifdef ENABLE_GUI + cf_t* fft_buffer = NULL; + float* fft_mag = NULL; + float* imp = NULL; +#endif + + // Parse arguments + if (parse_args(argc, argv) < SRSRAN_SUCCESS) { + goto clean_exit; + } srsran_dft_plan_t ifft; srsran_dft_plan_c(&ifft, srate / 1000, SRSRAN_DFT_BACKWARD); @@ -105,10 +115,7 @@ int main(int argc, char** argv) plot_real_t plot_h = NULL; plot_real_t plot_imp = NULL; - srsran_dft_plan_t fft = {}; - cf_t* fft_buffer = NULL; - float* fft_mag = NULL; - float* imp = NULL; + srsran_dft_plan_t fft = {}; if (enable_gui) { sdrgui_init(); @@ -216,15 +223,17 @@ int main(int argc, char** argv) #endif /* ENABLE_GUI */ } - ret = SRSRAN_SUCCESS; - -clean_exit: - if (ret) { - printf("Error\n"); + // Print results and exit + double msps = 0; + if (time_usec) { + msps = duration_ms * (srate / 1000.0) / (double)time_usec; + printf("Ok ... %.1f MSps\n", msps); + ret = SRSRAN_SUCCESS; } else { - printf("Ok ... %.1f MSps\n", duration_ms * (srate / 1000.0) / (double)time_usec); + printf("Error in Msps calculation: undefined division\n"); } +clean_exit: srsran_dft_plan_free(&ifft); #ifdef ENABLE_GUI diff --git a/lib/src/phy/channel/test/hst_channel_test.c b/lib/src/phy/channel/test/hst_channel_test.c index 0ce471247..36cb63647 100644 --- a/lib/src/phy/channel/test/hst_channel_test.c +++ b/lib/src/phy/channel/test/hst_channel_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/common/CMakeLists.txt b/lib/src/phy/common/CMakeLists.txt index 0abed3744..6ddc7a066 100644 --- a/lib/src/phy/common/CMakeLists.txt +++ b/lib/src/phy/common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,7 +18,7 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES phy_common.c phy_common_sl.c phy_common_nr.c sequence.c timestamp.c zc_sequence.c) +set(SOURCES phy_common.c phy_common_sl.c phy_common_nr.c sequence.c timestamp.c zc_sequence.c sliv.c) add_library(srsran_phy_common OBJECT ${SOURCES}) add_subdirectory(test) \ No newline at end of file diff --git a/lib/src/phy/common/phy_common.c b/lib/src/phy/common/phy_common.c index c50f2d0d6..b4838ac53 100644 --- a/lib/src/phy/common/phy_common.c +++ b/lib/src/phy/common/phy_common.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -347,9 +347,9 @@ int srsran_symbol_sz_power2(uint32_t nof_prb) return 256; } else if (nof_prb <= 25) { return 512; - } else if (nof_prb <= 50) { + } else if (nof_prb <= 52) { return 1024; - } else if (nof_prb <= 75) { + } else if (nof_prb <= 79) { return 1536; } else if (nof_prb <= 110) { return 2048; @@ -370,9 +370,9 @@ int srsran_symbol_sz(uint32_t nof_prb) return 256; } else if (nof_prb <= 25) { return 384; - } else if (nof_prb <= 50) { + } else if (nof_prb <= 52) { return 768; - } else if (nof_prb <= 75) { + } else if (nof_prb <= 79) { return 1024; } else if (nof_prb <= 110) { return 1536; @@ -662,6 +662,7 @@ uint8_t srsran_band_get_band(uint32_t dl_earfcn) uint32_t i = SRSRAN_NOF_LTE_BANDS - 1; if (dl_earfcn > lte_bands[i].dl_earfcn_offset) { ERROR("Invalid DL_EARFCN=%d", dl_earfcn); + return 0; } i--; while (i > 0 && lte_bands[i].dl_earfcn_offset > dl_earfcn) { @@ -675,6 +676,7 @@ double srsran_band_fd(uint32_t dl_earfcn) uint32_t i = SRSRAN_NOF_LTE_BANDS - 1; if (dl_earfcn > lte_bands[i].dl_earfcn_offset) { ERROR("Invalid DL_EARFCN=%d", dl_earfcn); + return 0; } i--; while (i > 0 && lte_bands[i].dl_earfcn_offset > dl_earfcn) { @@ -688,6 +690,7 @@ double srsran_band_fu(uint32_t ul_earfcn) uint32_t i = SRSRAN_NOF_LTE_BANDS - 1; if (ul_earfcn > lte_bands[i].ul_earfcn_offset) { ERROR("Invalid UL_EARFCN=%d", ul_earfcn); + return 0; } i--; while (i > 0 && (lte_bands[i].ul_earfcn_offset > ul_earfcn || lte_bands[i].ul_earfcn_offset == 0)) { @@ -701,6 +704,7 @@ uint32_t srsran_band_ul_earfcn(uint32_t dl_earfcn) uint32_t i = SRSRAN_NOF_LTE_BANDS - 1; if (dl_earfcn > lte_bands[i].dl_earfcn_offset) { ERROR("Invalid DL_EARFCN=%d", dl_earfcn); + return 0; } i--; while (i > 0 && lte_bands[i].dl_earfcn_offset > dl_earfcn) { diff --git a/lib/src/phy/common/phy_common_nr.c b/lib/src/phy/common/phy_common_nr.c index 8e4828cc5..0ac76fe8a 100644 --- a/lib/src/phy/common/phy_common_nr.c +++ b/lib/src/phy/common/phy_common_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,7 +20,9 @@ */ #include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" +#include #include const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type) @@ -46,6 +48,54 @@ const char* srsran_rnti_type_str(srsran_rnti_type_t rnti_type) } return "unknown"; } +const char* srsran_rnti_type_str_short(srsran_rnti_type_t rnti_type) +{ + switch (rnti_type) { + case srsran_rnti_type_c: + return "c"; + case srsran_rnti_type_p: + return "p"; + case srsran_rnti_type_si: + return "si"; + case srsran_rnti_type_ra: + return "ra"; + case srsran_rnti_type_tc: + return "tc"; + case srsran_rnti_type_cs: + return "cs"; + case srsran_rnti_type_sp_csi: + return "sp-csi"; + case srsran_rnti_type_mcs_c: + return "mcs-c"; + default:; // Do nothing + } + return "unknown"; +} + +const char* srsran_ss_type_str(srsran_search_space_type_t ss_type) +{ + switch (ss_type) { + case srsran_search_space_type_common_0: + return "common0"; + case srsran_search_space_type_common_0A: + return "common0A"; + case srsran_search_space_type_common_1: + return "common1"; + case srsran_search_space_type_common_2: + return "common2"; + case srsran_search_space_type_common_3: + return "common3"; + case srsran_search_space_type_ue: + return "ue"; + case srsran_search_space_type_rar: + return "rar"; + case srsran_search_space_type_cg: + return "cg"; + default:; // Do nothing + break; + } + return "unknown"; +} const char* srsran_dci_format_nr_string(srsran_dci_format_nr_t format) { @@ -99,6 +149,26 @@ uint32_t srsran_coreset_get_sz(const srsran_coreset_t* coreset) return srsran_coreset_get_bw(coreset) * SRSRAN_NRE * coreset->duration; } +uint32_t srsran_coreset_start_rb(const srsran_coreset_t* coreset) +{ + // Protect CORESET access + if (coreset == NULL) { + return 0; + } + + // Iterates all the possible frequency resources trying to find the first enabled + for (uint32_t res = 0; res < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; res++) { + // If the frequency resource is enabled... + if (coreset->freq_resources[res]) { + // ... return the lowest resource block index + return 6 * res + coreset->offset_rb; + } + } + + // Returns the start resource index + return 0; +} + const char* srsran_sch_mapping_type_to_str(srsran_sch_mapping_type_t mapping_type) { switch (mapping_type) { @@ -144,6 +214,9 @@ srsran_mcs_table_t srsran_mcs_table_from_str(const char* str) static const uint32_t phy_common_nr_valid_symbol_sz[PHY_COMMON_NR_NOF_VALID_SYMB_SZ] = {128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096}; +static const uint32_t phy_common_nr_valid_std_symbol_sz[PHY_COMMON_NR_NOF_VALID_SYMB_SZ] = + {128, 256, 512, 1024, 1536, 2048, 4096}; + uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb) { uint32_t nof_re = nof_prb * SRSRAN_NRE; @@ -152,48 +225,96 @@ uint32_t srsran_min_symbol_sz_rb(uint32_t nof_prb) return 0; } - for (uint32_t i = 0; i < PHY_COMMON_NR_NOF_VALID_SYMB_SZ; i++) { - if (phy_common_nr_valid_symbol_sz[i] > nof_re) { - return phy_common_nr_valid_symbol_sz[i]; + // Select all valid symbol sizes by default + const uint32_t* symbol_table = phy_common_nr_valid_symbol_sz; + + // Select standard LTE symbol sizes table + if (srsran_symbol_size_is_standard()) { + symbol_table = phy_common_nr_valid_std_symbol_sz; + + // As the selected symbol size is the minimum that fits the entire number of RE, 106 RB would select into 1536 point + // symbol size. However, it shall use 2048 point symbol size to match LTE standard rate. Because of this, it forces + // bandwidths bigger than 79 RB that would use 1536 symbol size to select 2048. + if (nof_prb > 79 && nof_prb < 1536 / SRSRAN_NRE) { + return 2048; } } + // For each symbol size in the table... + for (uint32_t i = 0; i < PHY_COMMON_NR_NOF_VALID_SYMB_SZ; i++) { + // Check if the number of RE fit in the symbol + if (symbol_table[i] > nof_re) { + // Returns the smallest symbol size that fits the number of RE + return symbol_table[i]; + } + } + + // The number of RE exceeds the maximum symbol size return 0; } -float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, uint32_t numerology) +int srsran_symbol_sz_from_srate(double srate_hz, srsran_subcarrier_spacing_t scs) +{ + // Make sure srate is valid + if (!isnormal(srate_hz) || srate_hz < 0.0) { + return SRSRAN_ERROR; + } + + // Convert srate to integer and Hz + uint32_t srate_int_hz = (uint32_t)srate_hz; + + // Get subcarrier spacing in Hz + uint32_t scs_int_hz = SRSRAN_SUBC_SPACING_NR(scs); + + // Check the symbol size if a integer + if (srate_int_hz % scs_int_hz != 0) { + ERROR("Invalid sampling rate %.2f MHz with subcarrrier spacing %d kHz", srate_hz / 1e6, scs_int_hz / 1000); + return SRSRAN_ERROR; + } + + // Calculate symbol size in samples + uint32_t symbol_sz = srate_int_hz / scs_int_hz; + + // Verify the symbol size can have an integer cyclic prefix size + if ((symbol_sz * 144U) % 2048 != 0 && (symbol_sz * (16U << (uint32_t)scs)) % 2048 != 0) { + ERROR("The sampling rate %.2f MHz with subcarrrier spacing %d kHz", srate_hz / 1e6, scs_int_hz / 1000); + return SRSRAN_ERROR; + } + + return (int)symbol_sz; +} + +float srsran_symbol_offset_s(uint32_t l, srsran_subcarrier_spacing_t scs) +{ + // Compute at what symbol there is a longer CP + uint32_t cp_boundary = SRSRAN_EXT_CP_SYMBOL(scs); + + // Number of samples (DFT + short CP) in between the first and l symbols + uint32_t N = ((2048 + 144) * l) >> (uint32_t)scs; + + // Add extended CP samples from first OFDM symbol + N += 16 * SRSRAN_CEIL(l, cp_boundary); + + // Compute time using reference sampling rate + float TS = SRSRAN_LTE_TS; + + // Return symbol offset in seconds + return (float)N * TS; +} + +float srsran_symbol_distance_s(uint32_t l0, uint32_t l1, srsran_subcarrier_spacing_t scs) { // l0 must be smaller than l1 if (l0 >= l1) { return 0.0f; } - // Count number of symbols in between - uint32_t count = l1 - l0; - - // Compute at what symbol there is a longer CP - uint32_t cp_boundary = 7U << numerology; - - // Select whether extra CP shall be added - uint32_t extra_cp = 0; - if (l0 < cp_boundary && l1 >= cp_boundary) { - extra_cp = 16; - } - - // Compute reference FFT size - uint32_t N = (2048 + 144) * count + extra_cp; - - // Return symbol distance in microseconds - return (N << numerology) * SRSRAN_LTE_TS; + // Return symbol distance in seconds + return srsran_symbol_offset_s(l1, scs) - srsran_symbol_offset_s(l0, scs); } -bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) +static bool tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) { - // Protect NULL pointer access - if (cfg == NULL) { - return false; - } - // Prevent zero division if (cfg->pattern1.period_ms == 0 && cfg->pattern2.period_ms == 0) { return false; @@ -216,13 +337,24 @@ bool srsran_tdd_nr_is_dl(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, (slot_idx_period == pattern->nof_dl_slots && pattern->nof_dl_symbols != 0)); } -bool srsran_tdd_nr_is_ul(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) +bool srsran_duplex_nr_is_dl(const srsran_duplex_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) + { // Protect NULL pointer access if (cfg == NULL) { return false; } + // In case of TDD + if (cfg->mode == SRSRAN_DUPLEX_MODE_TDD) { + return tdd_nr_is_dl(&cfg->tdd, numerology, slot_idx); + } + + return true; +} + +static bool tdd_nr_is_ul(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) +{ // Prevent zero division if (cfg->pattern1.period_ms == 0 && cfg->pattern2.period_ms == 0) { return false; @@ -247,6 +379,21 @@ bool srsran_tdd_nr_is_ul(const srsran_tdd_config_nr_t* cfg, uint32_t numerology, return (slot_idx_period > start_ul || (slot_idx_period == start_ul && pattern->nof_ul_symbols != 0)); } +bool srsran_duplex_nr_is_ul(const srsran_duplex_config_nr_t* cfg, uint32_t numerology, uint32_t slot_idx) +{ + // Protect NULL pointer access + if (cfg == NULL) { + return false; + } + + // In case of TDD + if (cfg->mode == SRSRAN_DUPLEX_MODE_TDD) { + return tdd_nr_is_ul(&cfg->tdd, numerology, slot_idx); + } + + return true; +} + int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran_cell_t* cell) { // Protect memory access @@ -276,3 +423,431 @@ int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran_cell_t* ce return SRSRAN_SUCCESS; } + +uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len) +{ + if (meas == NULL || str == NULL || str_len == 0) { + return 0; + } + + return srsran_print_check(str, + str_len, + 0, + "rsrp=%+.1f epre=%+.1f n0=%+.1f snr=%+.1f cfo=%+.1f cfo_max=%.0f delay=%+.1f", + meas->rsrp_dB, + meas->epre_dB, + meas->n0_dB, + meas->snr_dB, + meas->cfo_hz, + meas->cfo_hz_max, + meas->delay_us); +} + +uint32_t srsran_csi_meas_info_short(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len) +{ + if (meas == NULL || str == NULL || str_len == 0) { + return 0; + } + + return srsran_print_check(str, + str_len, + 0, + "epre=%+.1f snr=%+.1f cfo=%+.1f delay=%+.1f ", + meas->epre_dB, + meas->snr_dB, + meas->cfo_hz, + meas->delay_us); +} + +srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str) +{ + if (str == NULL) { + return srsran_subcarrier_spacing_invalid; + } + + uint32_t scs = (uint32_t)roundf(strtof(str, NULL)); + switch (scs) { + case 15: + case 15000: + return srsran_subcarrier_spacing_15kHz; + case 30: + case 30000: + return srsran_subcarrier_spacing_30kHz; + case 60: + case 60000: + return srsran_subcarrier_spacing_60kHz; + case 120: + case 120000: + return srsran_subcarrier_spacing_120kHz; + case 240: + case 240000: + return srsran_subcarrier_spacing_240kHz; + default:; // Do nothing + } + + return srsran_subcarrier_spacing_invalid; +} + +const char* srsran_subcarrier_spacing_to_str(srsran_subcarrier_spacing_t scs) +{ + switch (scs) { + case srsran_subcarrier_spacing_15kHz: + return "15kHz"; + case srsran_subcarrier_spacing_30kHz: + return "30kHz"; + case srsran_subcarrier_spacing_60kHz: + return "60kHz"; + case srsran_subcarrier_spacing_120kHz: + return "120kHz"; + case srsran_subcarrier_spacing_240kHz: + return "240kHz"; + case srsran_subcarrier_spacing_invalid: + default: + return "invalid"; + } +} + +void srsran_combine_csi_trs_measurements(const srsran_csi_trs_measurements_t* a, + const srsran_csi_trs_measurements_t* b, + srsran_csi_trs_measurements_t* dst) +{ + // Verify inputs + if (a == NULL || b == NULL || dst == NULL) { + return; + } + + // Protect from zero division + uint32_t nof_re_sum = a->nof_re + b->nof_re; + if (nof_re_sum == 0) { + SRSRAN_MEM_ZERO(dst, srsran_csi_trs_measurements_t, 1); + return; + } + + // Perform proportional average + dst->rsrp = SRSRAN_VEC_PMA(a->rsrp, a->nof_re, b->rsrp, b->nof_re); + dst->rsrp_dB = SRSRAN_VEC_PMA(a->rsrp_dB, a->nof_re, b->rsrp_dB, b->nof_re); + dst->epre = SRSRAN_VEC_PMA(a->epre, a->nof_re, b->epre, b->nof_re); + dst->epre_dB = SRSRAN_VEC_PMA(a->epre_dB, a->nof_re, b->epre_dB, b->nof_re); + dst->n0 = SRSRAN_VEC_PMA(a->n0, a->nof_re, b->n0, b->nof_re); + dst->n0_dB = SRSRAN_VEC_PMA(a->n0_dB, a->nof_re, b->n0_dB, b->nof_re); + dst->snr_dB = SRSRAN_VEC_PMA(a->snr_dB, a->nof_re, b->snr_dB, b->nof_re); + dst->cfo_hz = SRSRAN_VEC_PMA(a->cfo_hz, a->nof_re, b->cfo_hz, b->nof_re); + dst->cfo_hz_max = SRSRAN_MAX(a->cfo_hz_max, b->cfo_hz_max); + dst->delay_us = SRSRAN_VEC_PMA(a->delay_us, a->nof_re, b->delay_us, b->nof_re); + dst->nof_re = nof_re_sum; +} + +uint32_t pdcch_nr_bundle_size(srsran_coreset_bundle_size_t x) +{ + switch (x) { + case srsran_coreset_bundle_size_n2: + return 2; + case srsran_coreset_bundle_size_n3: + return 3; + case srsran_coreset_bundle_size_n6: + return 6; + } + return 0; +} + +typedef struct { + uint32_t mux_pattern; + uint32_t nof_prb; + uint32_t nof_symb; + uint32_t offset_rb; ///< Defined by TS 36.213 section 13 UE procedure for monitoring Type0-PDCCH CSS sets: + ///< Offset respect to the SCS of the CORESET for Type0-PDCCH CSS set, provided by + ///< subCarrierSpacingCommon, from the smallest RB index of the CORESET for Type0-PDCCH CSS set + ///< to the smallest RB index of the common RB overlapping with the first RB of the + ///< corresponding SS/PBCH block. +} coreset_zero_entry_t; + +static const coreset_zero_entry_t coreset_zero_15_15[16] = { + {1, 24, 2, 0}, + {1, 24, 2, 2}, + {1, 24, 2, 4}, + {1, 24, 3, 0}, + {1, 24, 3, 2}, + {1, 24, 3, 4}, + {1, 48, 1, 12}, + {1, 48, 1, 16}, + {1, 48, 2, 12}, + {1, 48, 2, 16}, + {1, 48, 3, 12}, + {1, 48, 3, 16}, + {1, 96, 1, 38}, + {1, 96, 2, 38}, + {1, 96, 3, 38}, + {}, +}; + +static const coreset_zero_entry_t coreset_zero_15_30[16] = { + {1, 24, 2, 5}, + {1, 24, 2, 6}, + {1, 24, 2, 7}, + {1, 24, 2, 8}, + {1, 24, 3, 5}, + {1, 24, 3, 6}, + {1, 24, 3, 7}, + {1, 24, 3, 8}, + {1, 48, 1, 18}, + {1, 48, 1, 20}, + {1, 48, 2, 18}, + {1, 48, 2, 20}, + {1, 48, 3, 18}, + {1, 48, 3, 20}, + {}, + {}, +}; + +static const coreset_zero_entry_t coreset_zero_30_15[16] = { + {1, 48, 1, 2}, + {1, 48, 1, 6}, + {1, 48, 2, 2}, + {1, 48, 2, 6}, + {1, 48, 3, 2}, + {1, 48, 3, 6}, + {1, 96, 1, 28}, + {1, 96, 2, 28}, + {1, 96, 3, 28}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, +}; + +int srsran_coreset_to_str(srsran_coreset_t* coreset, char* str, uint32_t str_len) +{ + if (coreset == NULL || str == NULL || str_len == 0) { + return 0; + } + + char freq_res_str[SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE] = {}; + srsran_vec_sprint_bin( + freq_res_str, sizeof(freq_res_str), (uint8_t*)coreset->freq_resources, SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE); + + return srsran_print_check( + str, + str_len, + 0, + "\n" + " - coreset_id=%d\n" + " - mapping_type=%s\n" + " - duration=%d\n" + " - freq_res=%s\n" + " - dmrs_scrambling_present=%s (id=%d)\n" + " - precoder_granularity=%s\n" + " - interleaver_size=%d\n" + " - reg_bundle_size=%d\n" + " - shift_index=%d\n" + " - offset_rb=%d\n", + coreset->id, + coreset->mapping_type == srsran_coreset_mapping_type_non_interleaved ? "non-interleaved" : "interleaved", + coreset->duration, + freq_res_str, + coreset->dmrs_scrambling_id_present ? "true" : "false", + coreset->dmrs_scrambling_id, + coreset->precoder_granularity == srsran_coreset_precoder_granularity_contiguous ? "contiguous" : "reg_bundle", + pdcch_nr_bundle_size(coreset->interleaver_size), + pdcch_nr_bundle_size(coreset->reg_bundle_size), + coreset->shift_index, + coreset->offset_rb); +} + +int srsran_coreset_zero(uint32_t n_cell_id, + uint32_t ssb_pointA_freq_offset_Hz, + srsran_subcarrier_spacing_t ssb_scs, + srsran_subcarrier_spacing_t pdcch_scs, + uint32_t idx, + srsran_coreset_t* coreset) +{ + // Verify inputs + if (coreset == NULL || idx >= 16) { + ERROR("Invalid CORESET Zero inputs. coreset=%p, idx=%d", coreset, idx); + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Default entry to NULL + const coreset_zero_entry_t* entry = NULL; + + // Table 13-1: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set + // when {SS/PBCH block, PDCCH} SCS is {15, 15} kHz for frequency bands with minimum channel + // bandwidth 5 MHz or 10 MHz + if (ssb_scs == srsran_subcarrier_spacing_15kHz && pdcch_scs == srsran_subcarrier_spacing_15kHz) { + entry = &coreset_zero_15_15[idx]; + } + // Table 13-2: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set + // when {SS/PBCH block, PDCCH} SCS is {15, 30} kHz for frequency bands with minimum channel + // bandwidth 5 MHz or 10 MHz + if (ssb_scs == srsran_subcarrier_spacing_15kHz && pdcch_scs == srsran_subcarrier_spacing_30kHz) { + entry = &coreset_zero_15_30[idx]; + } + + // Table 13-3: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set + // when {SS/PBCH block, PDCCH} SCS is {30, 15} kHz for frequency bands with minimum channel + // bandwidth 5 MHz or 10 MHz + if (ssb_scs == srsran_subcarrier_spacing_30kHz && pdcch_scs == srsran_subcarrier_spacing_15kHz) { + entry = &coreset_zero_30_15[idx]; + } + + // Check a valid entry has been selected + if (entry == NULL) { + ERROR("Unhandled case ssb_scs=%s, pdcch_scs=%s", + srsran_subcarrier_spacing_to_str(ssb_scs), + srsran_subcarrier_spacing_to_str(pdcch_scs)); + return SRSRAN_ERROR; + } + + if (entry->nof_prb == 0) { + ERROR("Reserved case ssb_scs=%s, pdcch_scs=%s, idx=%d", + srsran_subcarrier_spacing_to_str(ssb_scs), + srsran_subcarrier_spacing_to_str(pdcch_scs), + idx); + return SRSRAN_ERROR; + } + + // Calculate CORESET offset in RB + uint32_t ssb_half_bw_Hz = SRSRAN_SUBC_SPACING_NR(ssb_scs) * (SRSRAN_SSB_BW_SUBC / 2U); + if (ssb_pointA_freq_offset_Hz > ssb_half_bw_Hz) { + // Move SSB center to lowest SSB subcarrier + ssb_pointA_freq_offset_Hz -= ssb_half_bw_Hz; + } else { + ssb_pointA_freq_offset_Hz = 0; + } + uint32_t ssb_pointA_freq_offset_rb = + SRSRAN_FLOOR(ssb_pointA_freq_offset_Hz, SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(pdcch_scs)); + uint32_t offset_rb = + (ssb_pointA_freq_offset_rb > entry->offset_rb) ? (ssb_pointA_freq_offset_rb - entry->offset_rb) : 0; + + // Set CORESET fields + coreset->id = 0; + coreset->dmrs_scrambling_id_present = false; + coreset->duration = entry->nof_symb; + coreset->offset_rb = offset_rb; + + // Set CCE-to-REG mapping according to TS 38.211 section 7.3.2.2 + coreset->mapping_type = srsran_coreset_mapping_type_interleaved; + coreset->reg_bundle_size = srsran_coreset_bundle_size_n6; + coreset->interleaver_size = srsran_coreset_bundle_size_n2; + coreset->precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + coreset->shift_index = n_cell_id; + + // Set CORESET frequency resource mask + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { + coreset->freq_resources[i] = (i < (entry->nof_prb / 6)); + } + + return SRSRAN_SUCCESS; +} + +int srsran_coreset0_ssb_offset(uint32_t idx, srsran_subcarrier_spacing_t ssb_scs, srsran_subcarrier_spacing_t pdcch_scs) +{ + // Verify inputs + if (idx >= 16) { + ERROR("Invalid CORESET Zero input. idx=%d", idx); + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Default entry to NULL + const coreset_zero_entry_t* entry = NULL; + + // Table 13-1: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set + // when {SS/PBCH block, PDCCH} SCS is {15, 15} kHz for frequency bands with minimum channel + // bandwidth 5 MHz or 10 MHz + if (ssb_scs == srsran_subcarrier_spacing_15kHz && pdcch_scs == srsran_subcarrier_spacing_15kHz) { + entry = &coreset_zero_15_15[idx]; + } + // Table 13-2: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set + // when {SS/PBCH block, PDCCH} SCS is {15, 30} kHz for frequency bands with minimum channel + // bandwidth 5 MHz or 10 MHz + if (ssb_scs == srsran_subcarrier_spacing_15kHz && pdcch_scs == srsran_subcarrier_spacing_30kHz) { + entry = &coreset_zero_15_30[idx]; + } + + // Table 13-3: Set of resource blocks and slot symbols of CORESET for Type0-PDCCH search space set + // when {SS/PBCH block, PDCCH} SCS is {30, 15} kHz for frequency bands with minimum channel + // bandwidth 5 MHz or 10 MHz + if (ssb_scs == srsran_subcarrier_spacing_30kHz && pdcch_scs == srsran_subcarrier_spacing_15kHz) { + entry = &coreset_zero_30_15[idx]; + } + + // Check a valid entry has been selected + if (entry == NULL) { + ERROR("Unhandled case ssb_scs=%s, pdcch_scs=%s", + srsran_subcarrier_spacing_to_str(ssb_scs), + srsran_subcarrier_spacing_to_str(pdcch_scs)); + return SRSRAN_ERROR; + } + + return entry->offset_rb; +} + +const char* srsran_ssb_pattern_to_str(srsran_ssb_pattern_t pattern) +{ + switch (pattern) { + case SRSRAN_SSB_PATTERN_A: + return "A"; + case SRSRAN_SSB_PATTERN_B: + return "B"; + case SRSRAN_SSB_PATTERN_C: + return "C"; + case SRSRAN_SSB_PATTERN_D: + return "D"; + case SRSRAN_SSB_PATTERN_E: + return "E"; + case SRSRAN_SSB_PATTERN_INVALID: + default: + break; + } + return "Invalid"; +} + +srsran_ssb_pattern_t srsran_ssb_pattern_fom_str(const char* str) +{ + if (str == NULL) { + return SRSRAN_SSB_PATTERN_INVALID; + } + + if (strcasecmp(str, "A") == 0) { + return SRSRAN_SSB_PATTERN_A; + } + + if (strcasecmp(str, "B") == 0) { + return SRSRAN_SSB_PATTERN_B; + } + + if (strcasecmp(str, "C") == 0) { + return SRSRAN_SSB_PATTERN_C; + } + + if (strcasecmp(str, "D") == 0) { + return SRSRAN_SSB_PATTERN_D; + } + + if (strcasecmp(str, "E") == 0) { + return SRSRAN_SSB_PATTERN_E; + } + + return SRSRAN_SSB_PATTERN_INVALID; +} + +bool srsran_carrier_nr_equal(const srsran_carrier_nr_t* a, const srsran_carrier_nr_t* b) +{ + if (a == NULL || b == NULL) { + return false; + } + + bool ret = (a->pci == b->pci); + ret = ret && (a->dl_center_frequency_hz == b->dl_center_frequency_hz); + ret = ret && (a->ul_center_frequency_hz == b->ul_center_frequency_hz); + ret = ret && (a->ssb_center_freq_hz == b->ssb_center_freq_hz); + ret = ret && (a->offset_to_carrier == b->offset_to_carrier); + ret = ret && (a->scs == b->scs); + ret = ret && (a->nof_prb == b->nof_prb); + ret = ret && (a->start == b->start); + ret = ret && (a->max_mimo_layers == b->max_mimo_layers); + + return ret; +} \ No newline at end of file diff --git a/lib/src/phy/common/phy_common_sl.c b/lib/src/phy/common/phy_common_sl.c index 1b2927520..17c10cdc6 100644 --- a/lib/src/phy/common/phy_common_sl.c +++ b/lib/src/phy/common/phy_common_sl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/common/sequence.c b/lib/src/phy/common/sequence.c index 38e2d37e5..eda2240c5 100644 --- a/lib/src/phy/common/sequence.c +++ b/lib/src/phy/common/sequence.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -220,10 +220,17 @@ void srsran_sequence_state_init(srsran_sequence_state_t* s, uint32_t seed) s->x2 = sequence_get_x2_init(seed); } +#define FLOAT_U32_XOR(DST, SRC, U32_MASK) \ + do { \ + uint32_t temp_u32; \ + memcpy(&temp_u32, &(SRC), 4); \ + temp_u32 ^= (U32_MASK); \ + memcpy(&(DST), &temp_u32, 4); \ + } while (false) + void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* out, uint32_t length) { - uint32_t i = 0; - const float xor [2] = {+0.0F, -0.0F}; + uint32_t i = 0; if (length >= SEQUENCE_PAR_BITS) { for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { @@ -255,7 +262,7 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* #endif // Finish the parallel bits with generic code for (; j < SEQUENCE_PAR_BITS; j++) { - *((uint32_t*)&out[i + j]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(c >> j) & 1U]); + FLOAT_U32_XOR(out[i + j], value, (c << (31U - j)) & 0x80000000); } // Step sequences @@ -265,7 +272,59 @@ void srsran_sequence_state_gen_f(srsran_sequence_state_t* s, float value, float* } for (; i < length; i++) { - *((uint32_t*)&out[i]) = *((uint32_t*)&value) ^ *((uint32_t*)&xor[(s->x1 ^ s->x2) & 1U]); + FLOAT_U32_XOR(out[i], value, (s->x1 ^ s->x2) << 31U); + + // Step sequences + s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2); + } +} + +void srsran_sequence_state_apply_f(srsran_sequence_state_t* s, const float* in, float* out, uint32_t length) +{ + uint32_t i = 0; + + if (length >= SEQUENCE_PAR_BITS) { + for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { + uint32_t c = (uint32_t)(s->x1 ^ s->x2); + + uint32_t j = 0; +#ifdef LV_HAVE_SSE + for (; j < SEQUENCE_PAR_BITS - 3; j += 4) { + // Preloads bits of interest in the 4 LSB + __m128i mask = _mm_set1_epi32(c >> j); + + // Masks each bit + mask = _mm_and_si128(mask, _mm_setr_epi32(1, 2, 4, 8)); + + // Get non zero mask + mask = _mm_cmpgt_epi32(mask, _mm_set1_epi32(0)); + + // And with MSB + mask = _mm_and_si128(mask, (__m128i)_mm_set1_ps(-0.0F)); + + // Load input + __m128 v = _mm_loadu_ps(in + i + j); + + // Loads input and perform sign XOR + v = _mm_xor_ps((__m128)mask, v); + + _mm_storeu_ps(out + i + j, v); + } +#endif // LV_HAVE_SSE + // Finish the parallel bits with generic code + for (; j < SEQUENCE_PAR_BITS; j++) { + FLOAT_U32_XOR(out[i + j], in[i + j], (c << (31U - j)) & 0x80000000); + } + + // Step sequences + s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2); + } + } + + for (; i < length; i++) { + FLOAT_U32_XOR(out[i], in[i], (s->x1 ^ s->x2) << 31U); // Step sequences s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); @@ -439,56 +498,10 @@ void srsran_sequence_free(srsran_sequence_t* q) void srsran_sequence_apply_f(const float* in, float* out, uint32_t length, uint32_t seed) { - uint32_t x1 = sequence_x1_init; // X1 initial state is fix - uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state + srsran_sequence_state_t seq = {}; + srsran_sequence_state_init(&seq, seed); - uint32_t i = 0; - - if (length >= SEQUENCE_PAR_BITS) { - for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { - uint32_t c = (uint32_t)(x1 ^ x2); - - uint32_t j = 0; -#ifdef LV_HAVE_SSE - for (; j < SEQUENCE_PAR_BITS - 3; j += 4) { - // Preloads bits of interest in the 4 LSB - __m128i mask = _mm_set1_epi32(c >> j); - - // Masks each bit - mask = _mm_and_si128(mask, _mm_setr_epi32(1, 2, 4, 8)); - - // Get non zero mask - mask = _mm_cmpgt_epi32(mask, _mm_set1_epi32(0)); - - // And with MSB - mask = _mm_and_si128(mask, (__m128i)_mm_set1_ps(-0.0F)); - - // Load input - __m128 v = _mm_loadu_ps(in + i + j); - - // Loads input and perform sign XOR - v = _mm_xor_ps((__m128)mask, v); - - _mm_storeu_ps(out + i + j, v); - } -#endif - for (; j < SEQUENCE_PAR_BITS; j++) { - ((uint32_t*)out)[i + j] = ((uint32_t*)in)[i] ^ (((c >> j) & 1U) << 31U); - } - - // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2); - } - } - - for (; i < length; i++) { - ((uint32_t*)out)[i] = ((uint32_t*)in)[i] ^ (((x1 ^ x2) & 1U) << 31U); - - // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_x2(x2); - } + srsran_sequence_state_apply_f(&seq, in, out, length); } void srsran_sequence_apply_s(const int16_t* in, int16_t* out, uint32_t length, uint32_t seed) @@ -547,16 +560,13 @@ void srsran_sequence_apply_s(const int16_t* in, int16_t* out, uint32_t length, u } } -void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uint32_t seed) +void srsran_sequence_state_apply_c(srsran_sequence_state_t* s, const int8_t* in, int8_t* out, uint32_t length) { - uint32_t x1 = sequence_x1_init; // X1 initial state is fix - uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state - uint32_t i = 0; if (length >= SEQUENCE_PAR_BITS) { for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { - uint32_t c = (uint32_t)(x1 ^ x2); + uint32_t c = (uint32_t)(s->x1 ^ s->x2); uint32_t j = 0; #ifdef LV_HAVE_SSE @@ -593,30 +603,34 @@ void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uin } // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2); + s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2); } } for (; i < length; i++) { - out[i] = in[i] * (((x1 ^ x2) & 1U) ? -1 : +1); + out[i] = in[i] * (((s->x1 ^ s->x2) & 1U) ? -1 : +1); // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_x2(x2); + s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2); } } -void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed) +void srsran_sequence_apply_c(const int8_t* in, int8_t* out, uint32_t length, uint32_t seed) { - uint32_t x1 = sequence_x1_init; // X1 initial state is fix - uint32_t x2 = sequence_get_x2_init(seed); // loads x2 initial state + srsran_sequence_state_t sequence_state; + srsran_sequence_state_init(&sequence_state, seed); + srsran_sequence_state_apply_c(&sequence_state, in, out, length); +} +void srsran_sequence_state_apply_bit(srsran_sequence_state_t* s, const uint8_t* in, uint8_t* out, uint32_t length) +{ uint32_t i = 0; if (length >= SEQUENCE_PAR_BITS) { for (; i < length - (SEQUENCE_PAR_BITS - 1); i += SEQUENCE_PAR_BITS) { - uint32_t c = (uint32_t)(x1 ^ x2); + uint32_t c = (uint32_t)(s->x1 ^ s->x2); uint32_t j = 0; #ifdef LV_HAVE_SSE @@ -652,20 +666,27 @@ void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length, } // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_par_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_par_x2(x2); + s->x1 = sequence_gen_LTE_pr_memless_step_par_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_par_x2(s->x2); } } for (; i < length; i++) { - out[i] = in[i] ^ ((x1 ^ x2) & 1U); + out[i] = in[i] ^ ((s->x1 ^ s->x2) & 1U); // Step sequences - x1 = sequence_gen_LTE_pr_memless_step_x1(x1); - x2 = sequence_gen_LTE_pr_memless_step_x2(x2); + s->x1 = sequence_gen_LTE_pr_memless_step_x1(s->x1); + s->x2 = sequence_gen_LTE_pr_memless_step_x2(s->x2); } } +void srsran_sequence_apply_bit(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed) +{ + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, seed); + srsran_sequence_state_apply_bit(&sequence_state, in, out, length); +} + void srsran_sequence_apply_packed(const uint8_t* in, uint8_t* out, uint32_t length, uint32_t seed) { uint32_t x1 = sequence_x1_init; // X1 initial state is fix diff --git a/lib/src/phy/common/sliv.c b/lib/src/phy/common/sliv.c new file mode 100644 index 000000000..872123226 --- /dev/null +++ b/lib/src/phy/common/sliv.c @@ -0,0 +1,43 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/common/sliv.h" + +void srsran_sliv_to_s_and_l(uint32_t N, uint32_t v, uint32_t* S, uint32_t* L) +{ + uint32_t low = v % N; + uint32_t high = v / N; + if (high + 1 + low <= N) { + *S = low; + *L = high + 1; + } else { + *S = N - 1 - low; + *L = N - high + 1; + } +} + +uint32_t srsran_sliv_from_s_and_l(uint32_t N, uint32_t S, uint32_t L) +{ + if ((L - 1) <= N / 2) { + return N * (L - 1) + S; + } + return N * (N - L + 1) + (N - 1 - S); +} \ No newline at end of file diff --git a/lib/src/phy/common/test/CMakeLists.txt b/lib/src/phy/common/test/CMakeLists.txt index 8a7ae9bb4..c6a7759c2 100644 --- a/lib/src/phy/common/test/CMakeLists.txt +++ b/lib/src/phy/common/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -26,3 +26,23 @@ add_executable(sequence_test sequence_test.c) target_link_libraries(sequence_test srsran_phy) add_test(sequence_test sequence_test) + +######################################################################## +# SLIV TEST +######################################################################## + +add_executable(sliv_test sliv_test.c) +target_link_libraries(sliv_test srsran_phy) + +add_test(sliv_test_14 sliv_test 14) +add_test(sliv_test_52 sliv_test 48) +add_test(sliv_test_52 sliv_test 52) + +######################################################################## +# PHY COMMON TEST +######################################################################## + +add_executable(phy_common_test phy_common_test.c) +target_link_libraries(phy_common_test srsran_phy) + +add_test(phy_common_test phy_common_test) \ No newline at end of file diff --git a/lib/src/phy/common/test/phy_common_test.c b/lib/src/phy/common/test/phy_common_test.c new file mode 100644 index 000000000..a9ffce247 --- /dev/null +++ b/lib/src/phy/common/test/phy_common_test.c @@ -0,0 +1,59 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsran/common/test_common.h" +#include "srsran/phy/common/phy_common.h" + +int srsran_default_rates_test() +{ + // Verify calculated sample rates for all valid PRB sizes. + // By default we use the reduced 3/4 sampling to save bandwidth on the fronthaul. +#ifdef FORCE_STANDARD_RATE + srsran_use_standard_symbol_size(false); +#endif + TESTASSERT(srsran_sampling_freq_hz(6) == 1920000); + TESTASSERT(srsran_sampling_freq_hz(15) == 3840000); + TESTASSERT(srsran_sampling_freq_hz(25) == 5760000); + TESTASSERT(srsran_sampling_freq_hz(50) == 11520000); + TESTASSERT(srsran_sampling_freq_hz(75) == 15360000); // need to use default rate for 15 MHz BW + TESTASSERT(srsran_sampling_freq_hz(100) == 23040000); + return SRSRAN_SUCCESS; +} + +int lte_standard_rates_test() +{ + // Verify calculated sample rates for all valid PRB sizes. + // Enable standard LTE rates (required by some RF HW). + srsran_use_standard_symbol_size(true); + TESTASSERT(srsran_sampling_freq_hz(6) == 1920000); + TESTASSERT(srsran_sampling_freq_hz(15) == 3840000); + TESTASSERT(srsran_sampling_freq_hz(25) == 7680000); + TESTASSERT(srsran_sampling_freq_hz(50) == 15360000); + TESTASSERT(srsran_sampling_freq_hz(75) == 23040000); + TESTASSERT(srsran_sampling_freq_hz(100) == 30720000); + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + TESTASSERT(srsran_default_rates_test() == SRSRAN_SUCCESS); + TESTASSERT(lte_standard_rates_test() == SRSRAN_SUCCESS); + return SRSRAN_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/phy/common/test/sequence_test.c b/lib/src/phy/common/test/sequence_test.c index d9f4918a3..94726d264 100644 --- a/lib/src/phy/common/test/sequence_test.c +++ b/lib/src/phy/common/test/sequence_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/common/test/sliv_test.c b/lib/src/phy/common/test/sliv_test.c new file mode 100644 index 000000000..db814e62b --- /dev/null +++ b/lib/src/phy/common/test/sliv_test.c @@ -0,0 +1,99 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsran/common/test_common.h" +#include "srsran/phy/common/sliv.h" +#include +#include +#include +#include + +static uint32_t N = 48; + +static int test() +{ + for (uint32_t s = 0; s < N; s++) { + for (uint32_t l = 1; l < N - s; l++) { + uint32_t sliv = srsran_sliv_from_s_and_l(N, s, l); + + uint32_t S = 0; + uint32_t L = 0; + srsran_sliv_to_s_and_l(N, sliv, &S, &L); + + if (s != S || l != L) { + printf("s=%d; l=%d; SLIV=%d; Start: %d; Length: %d;\n", s, l, sliv, S, L); + return SRSRAN_ERROR; + } + } + } + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + + // Parse N + if (argc >= 2) { + N = (uint32_t)strtol(argv[1], NULL, 10); + } + + // No input arguments are provided + if (argc <= 1) { + ERROR("Error: too few arguments"); + } + + // If two arguments, run brute force test + else if (argc == 2) { + ret = test(); + } + + // if three arguments, calculate start and length from sliv + else if (argc == 3) { + uint32_t sliv = (uint32_t)strtol(argv[2], NULL, 10); + uint32_t S = 0; + uint32_t L = 0; + + // check that N is not zero to prevent undefined division + if (N) { + srsran_sliv_to_s_and_l(N, sliv, &S, &L); + printf("SLIV=%d; Start: %d; Length: %d;\n", sliv, S, L); + ret = SRSRAN_SUCCESS; + } + else { + ERROR("Error: N cannot be 0 to prevent an undefined division"); + } + } + + // if four arguments, calculate sliv from start and length + else if (argc == 4) { + uint32_t s = (uint32_t)strtol(argv[2], NULL, 10); + uint32_t l = (uint32_t)strtol(argv[3], NULL, 10); + uint32_t sliv = srsran_sliv_from_s_and_l(N, s, l); + + printf("SLIV=%d; Start: %d; Length: %d;\n", sliv, s, l); + ret = SRSRAN_SUCCESS; + } + + else { + ERROR("Error: too many arguments"); + } + return ret; +} \ No newline at end of file diff --git a/lib/src/phy/common/timestamp.c b/lib/src/phy/common/timestamp.c index a5582f790..d3d27747c 100644 --- a/lib/src/phy/common/timestamp.c +++ b/lib/src/phy/common/timestamp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/common/zc_sequence.c b/lib/src/phy/common/zc_sequence.c index cea7b51b0..6a4ed24ca 100644 --- a/lib/src/phy/common/zc_sequence.c +++ b/lib/src/phy/common/zc_sequence.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,10 +24,13 @@ #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/primes.h" #include "srsran/phy/utils/vector.h" +#include #include +#define NOF_ZC_SEQ 30 + // Phi values for M_sc=12 Table 5.5.1.2-1 in TS 36.211 -static const float zc_sequence_lte_phi_M_sc_12[30][12] = { +static const float zc_sequence_lte_phi_M_sc_12[NOF_ZC_SEQ][12] = { {-1, 1, 3, -3, 3, 3, 1, 1, 3, 1, -3, 3}, {1, 1, 3, 3, 3, -1, 1, -3, -3, 1, -3, 3}, {1, 1, -3, -3, -3, -1, -3, -3, 1, -3, 1, -1}, {-1, 1, 1, 1, 1, -1, -3, -3, 1, -3, 3, -1}, {-1, 3, 1, -1, 1, -1, -3, -1, 1, -1, 1, 3}, {1, -3, 3, -1, -1, 1, 1, -1, -1, 3, -3, 1}, @@ -45,7 +48,7 @@ static const float zc_sequence_lte_phi_M_sc_12[30][12] = { {-1, 3, -3, 3, -1, 3, 3, -3, 3, 3, -1, -1}, {3, -3, -3, -1, -1, -3, -1, 3, -3, 3, 1, -1}}; // Phi values for M_sc=24 Table 5.5.1.2-2 in TS 36.211 -static const float zc_sequence_lte_phi_M_sc_24[30][24] = { +static const float zc_sequence_lte_phi_M_sc_24[NOF_ZC_SEQ][24] = { {-1, 3, 1, -3, 3, -1, 1, 3, -3, 3, 1, 3, -3, 3, 1, 1, -1, 1, 3, -3, 3, -3, -1, -3}, {-3, 3, -3, -3, -3, 1, -3, -3, 3, -1, 1, 1, 1, 3, 1, -1, 3, -3, -3, 1, 3, 1, 1, -3}, {3, -1, 3, 3, 1, 1, -3, 3, 3, 3, 3, 1, -1, 3, -1, 1, 1, -1, -3, -1, -1, 1, 3, 3}, @@ -78,7 +81,7 @@ static const float zc_sequence_lte_phi_M_sc_24[30][24] = { {1, 1, -1, -1, -3, -1, 3, -1, 3, -1, 1, 3, 1, -1, 3, 1, 3, -3, -3, 1, -1, -1, 1, 3}}; // Phi values for M_sc=12 Table 5.2.2.2-1 in TS 38.211 -static const float zc_sequence_nr_phi_M_sc_6[30][6] = { +static const float zc_sequence_nr_phi_M_sc_6[NOF_ZC_SEQ][6] = { {-3, -1, 3, 3, -1, -3}, {-3, 3, -1, -1, 3, -3}, {-3, -3, -3, 3, 1, -3}, {1, 1, 1, 3, -1, -3}, {1, 1, 1, -3, -1, 3}, {-3, 1, -1, -3, -3, -3}, {-3, 1, 3, -3, -3, -3}, {-3, -1, 1, -3, 1, -1}, {-3, -1, -3, 1, -3, -3}, {-3, -3, 1, -3, 3, -3}, {-3, 1, 3, 1, -3, -3}, {-3, -1, -3, 1, 1, -3}, @@ -89,7 +92,7 @@ static const float zc_sequence_nr_phi_M_sc_6[30][6] = { {1, 1, -1, 3, -3, -1}, {1, 1, -3, 1, -1, -1}}; // Phi values for M_sc=12 Table 5.2.2.2-2 in TS 38.211 -static const float zc_sequence_nr_phi_M_sc_12[30][12] = { +static const float zc_sequence_nr_phi_M_sc_12[NOF_ZC_SEQ][12] = { {-3, 1, -3, -3, -3, 3, -3, -1, 1, 1, 1, -3}, {-3, 3, 1, -3, 1, 3, -1, -1, 1, 3, 3, 3}, {-3, 3, 3, 1, -3, 3, -1, 1, 3, -3, 3, -3}, {-3, -3, -1, 3, 3, 3, -3, 3, -3, 1, -1, -3}, {-3, -1, -1, 1, 3, 1, 1, -1, 1, -1, -3, 1}, {-3, -3, 3, 1, -3, -3, -3, -1, 3, -1, 1, 3}, @@ -107,7 +110,7 @@ static const float zc_sequence_nr_phi_M_sc_12[30][12] = { {1, -1, 3, 1, 1, -1, -1, -1, 1, 3, -3, 1}, {-3, 3, -3, 3, -3, -3, 3, -1, -1, 1, 3, -3}}; // Phi values for M_sc=18 Table 5.2.2.2-3 in TS 38.211 -static const float zc_sequence_nr_phi_M_sc_18[30][18] = { +static const float zc_sequence_nr_phi_M_sc_18[NOF_ZC_SEQ][18] = { {-1, 3, -1, -3, 3, 1, -3, -1, 3, -3, -1, -1, 1, 1, 1, -1, -1, -1}, {3, -3, 3, -1, 1, 3, -3, -1, -3, -3, -1, -3, 3, 1, -1, 3, -3, 3}, {-3, 3, 1, -1, -1, 3, -3, -1, 1, 1, 1, 1, 1, -1, 3, -1, -3, -1}, @@ -140,7 +143,7 @@ static const float zc_sequence_nr_phi_M_sc_18[30][18] = { {-3, 3, 1, -1, -1, -1, -1, 1, -1, 3, 3, -3, -1, 1, 3, -1, 3, -1}}; // Phi values for M_sc=18 Table 5.2.2.2-3 in TS 38.211 -static const float zc_sequence_nr_phi_M_sc_24[30][24] = { +static const float zc_sequence_nr_phi_M_sc_24[NOF_ZC_SEQ][24] = { {-1, -3, 3, -1, 3, 1, 3, -1, 1, -3, -1, -3, -1, 1, 3, -3, -1, -3, 3, 3, 3, -3, -3, -3}, {-1, -3, 3, 1, 1, -3, 1, -3, -3, 1, -3, -1, -1, 3, -3, 3, 3, 3, -3, 1, 3, 3, -3, -3}, {-1, -3, -3, 1, -1, -1, -3, 1, 3, -1, -3, -1, -1, -3, 1, 1, 3, 1, -3, -1, -1, 3, -3, -3}, @@ -174,31 +177,37 @@ static const float zc_sequence_nr_phi_M_sc_24[30][24] = { static void zc_sequence_lte_r_uv_arg_1prb(uint32_t u, cf_t* tmp_arg) { + assert(u < NOF_ZC_SEQ); srsran_vec_sc_prod_fcc(zc_sequence_lte_phi_M_sc_12[u], M_PI_4, tmp_arg, SRSRAN_NRE); } static void zc_sequence_lte_r_uv_arg_2prb(uint32_t u, cf_t* tmp_arg) { + assert(u < NOF_ZC_SEQ); srsran_vec_sc_prod_fcc(zc_sequence_lte_phi_M_sc_24[u], M_PI_4, tmp_arg, 2 * SRSRAN_NRE); } static void zc_sequence_nr_r_uv_arg_0dot5prb(uint32_t u, cf_t* tmp_arg) { + assert(u < NOF_ZC_SEQ); srsran_vec_sc_prod_fcc(zc_sequence_nr_phi_M_sc_6[u], M_PI_4, tmp_arg, SRSRAN_NRE / 2); } static void zc_sequence_nr_r_uv_arg_1prb(uint32_t u, cf_t* tmp_arg) { + assert(u < NOF_ZC_SEQ); srsran_vec_sc_prod_fcc(zc_sequence_nr_phi_M_sc_12[u], M_PI_4, tmp_arg, SRSRAN_NRE); } static void zc_sequence_nr_r_uv_arg_1dot5prb(uint32_t u, cf_t* tmp_arg) { + assert(u < NOF_ZC_SEQ); srsran_vec_sc_prod_fcc(zc_sequence_nr_phi_M_sc_18[u], M_PI_4, tmp_arg, (3 * SRSRAN_NRE) / 2); } static void zc_sequence_nr_r_uv_arg_2prb(uint32_t u, cf_t* tmp_arg) { + assert(u < NOF_ZC_SEQ); srsran_vec_sc_prod_fcc(zc_sequence_nr_phi_M_sc_24[u], M_PI_4, tmp_arg, 2 * SRSRAN_NRE); } @@ -270,7 +279,7 @@ static int zc_sequence_nr_r_uv_arg(uint32_t M_zc, uint32_t u, uint32_t v, cf_t* static void zc_sequence_generate(uint32_t M_zc, float alpha, const cf_t* tmp_arg, cf_t* sequence) { for (uint32_t i = 0; i < M_zc; i++) { - sequence[i] = cexpf(I * (tmp_arg[i] + alpha * (float)i)); + sequence[i] = cexpf(I * (tmp_arg[i] + alpha * (cf_t)i)); } } diff --git a/lib/src/phy/dft/CMakeLists.txt b/lib/src/phy/dft/CMakeLists.txt index 168002aeb..1b3a4999a 100644 --- a/lib/src/phy/dft/CMakeLists.txt +++ b/lib/src/phy/dft/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/dft/dft_fftw.c b/lib/src/phy/dft/dft_fftw.c index 4f8b7d424..f0acfea3a 100644 --- a/lib/src/phy/dft/dft_fftw.c +++ b/lib/src/phy/dft/dft_fftw.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -59,19 +59,50 @@ __attribute__((constructor)) static void srsran_dft_load() #ifdef FFTW_WISDOM_FILE char full_path[256]; get_fftw_wisdom_file(full_path, sizeof(full_path)); - fftwf_import_wisdom_from_filename(full_path); + // lockf needs a file descriptor open for writing, so this must be r+ + FILE* fd = fopen(full_path, "r+"); + if (fd == NULL) { + return; + } + if (lockf(fileno(fd), F_LOCK, 0) == -1) { + perror("lockf()"); + fclose(fd); + return; + } + fftwf_import_wisdom_from_file(fd); + if (lockf(fileno(fd), F_ULOCK, 0) == -1) { + perror("u-lockf()"); + fclose(fd); + return; + } + fclose(fd); #else printf("Warning: FFTW Wisdom file not defined\n"); #endif } // This function is called in the ending of any executable where it is linked -__attribute__((destructor)) static void srsran_dft_exit() +__attribute__((destructor)) void srsran_dft_exit() { #ifdef FFTW_WISDOM_FILE char full_path[256]; get_fftw_wisdom_file(full_path, sizeof(full_path)); - fftwf_export_wisdom_to_filename(full_path); + FILE* fd = fopen(full_path, "w"); + if (fd == NULL) { + return; + } + if (lockf(fileno(fd), F_LOCK, 0) == -1) { + perror("lockf()"); + fclose(fd); + return; + } + fftwf_export_wisdom_to_file(fd); + if (lockf(fileno(fd), F_ULOCK, 0) == -1) { + perror("u-lockf()"); + fclose(fd); + return; + } + fclose(fd); #endif fftwf_cleanup(); } @@ -186,10 +217,11 @@ int srsran_dft_plan_guru_c(srsran_dft_plan_t* plan, pthread_mutex_lock(&fft_mutex); plan->p = fftwf_plan_guru_dft(1, &iodim, 1, &howmany_dims, in_buffer, out_buffer, sign, FFTW_TYPE); + pthread_mutex_unlock(&fft_mutex); + if (!plan->p) { return -1; } - pthread_mutex_unlock(&fft_mutex); plan->size = dft_points; plan->init_size = plan->size; diff --git a/lib/src/phy/dft/dft_precoding.c b/lib/src/phy/dft/dft_precoding.c index 65b4f559b..b79cfad4e 100644 --- a/lib/src/phy/dft/dft_precoding.c +++ b/lib/src/phy/dft/dft_precoding.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/dft/ofdm.c b/lib/src/phy/dft/ofdm.c index cc68f0758..b60d5fd54 100644 --- a/lib/src/phy/dft/ofdm.c +++ b/lib/src/phy/dft/ofdm.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -47,6 +47,11 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft cfg->symbol_sz = (uint32_t)symbol_sz_err; } + // Check if there is nothing to configure + if (memcmp(&q->cfg, cfg, sizeof(srsran_ofdm_cfg_t)) == 0) { + return SRSRAN_SUCCESS; + } + if (q->max_prb > 0) { // The object was already initialised, update only resizing params q->cfg.cp = cfg->cp; @@ -55,6 +60,9 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft } else { // Otherwise copy all parameters q->cfg = *cfg; + + // Phase compensation is set when it is calculated + q->cfg.phase_compensation_hz = 0.0; } uint32_t symbol_sz = q->cfg.symbol_sz; @@ -69,11 +77,24 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft q->slot_sz = (uint32_t)SRSRAN_SLOT_LEN(q->cfg.symbol_sz); q->sf_sz = (uint32_t)SRSRAN_SF_LEN(q->cfg.symbol_sz); + // Set the CFR parameters related to OFDM symbol and FFT size + q->cfg.cfr_tx_cfg.symbol_sz = symbol_sz; + q->cfg.cfr_tx_cfg.symbol_bw = q->nof_re; + + // in the DL, the DC carrier is empty but still counts when designing the filter BW + q->cfg.cfr_tx_cfg.dc_sc = (!q->cfg.keep_dc) && (!isnormal(q->cfg.freq_shift_f)); + if (q->cfg.cfr_tx_cfg.cfr_enable) { + if (srsran_cfr_init(&q->tx_cfr, &q->cfg.cfr_tx_cfg) < SRSRAN_SUCCESS) { + ERROR("Error while initialising CFR module"); + return SRSRAN_ERROR; + } + } + // Plan MBSFN if (q->fft_plan.size) { // Replan if it was initialised previously if (srsran_dft_replan(&q->fft_plan, q->cfg.symbol_sz)) { - ERROR("Reeplaning DFT plan"); + ERROR("Replanning DFT plan"); return SRSRAN_ERROR; } } else { @@ -86,7 +107,7 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft // Reallocate temporal buffer only if the new number of resource blocks is bigger than initial if (q->cfg.nof_prb > q->max_prb) { - // Free before reallocating if allocted + // Free before reallocating if allocated if (q->tmp) { free(q->tmp); free(q->shift_buffer); @@ -146,7 +167,7 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft srsran_vec_cf_zero(in_buffer, q->sf_sz); } - for (int slot = 0; slot < 2; slot++) { + for (int slot = 0; slot < SRSRAN_NOF_SLOTS_PER_SF; slot++) { // If Guru DFT was allocated, free if (q->fft_plan_sf[slot].size) { srsran_dft_plan_free(&q->fft_plan_sf[slot]); @@ -208,6 +229,12 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft srsran_dft_plan_set_norm(&q->fft_plan, q->cfg.normalize); srsran_dft_plan_set_dc(&q->fft_plan, (!cfg->keep_dc) && (!isnormal(q->cfg.freq_shift_f))); + // set phase compensation + if (srsran_ofdm_set_phase_compensation(q, cfg->phase_compensation_hz) < SRSRAN_SUCCESS) { + ERROR("Error setting phase compensation"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } @@ -237,6 +264,7 @@ void srsran_ofdm_free_(srsran_ofdm_t* q) if (q->window_offset_buffer) { free(q->window_offset_buffer); } + srsran_cfr_free(&q->tx_cfr); SRSRAN_MEM_ZERO(q, srsran_ofdm_t, 1); } @@ -284,6 +312,10 @@ int srsran_ofdm_tx_init(srsran_ofdm_t* q, srsran_cp_t cp, cf_t* in_buffer, cf_t* int srsran_ofdm_tx_init_cfg(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg) { + if (q == NULL || cfg == NULL) { + ERROR("Error, invalid inputs"); + return SRSRAN_ERROR_INVALID_INPUTS; + } return ofdm_init_mbsfn_(q, cfg, SRSRAN_DFT_BACKWARD); } @@ -322,6 +354,61 @@ int srsran_ofdm_tx_set_prb(srsran_ofdm_t* q, srsran_cp_t cp, uint32_t nof_prb) return ofdm_init_mbsfn_(q, &cfg, SRSRAN_DFT_BACKWARD); } +int srsran_ofdm_set_phase_compensation(srsran_ofdm_t* q, double center_freq_hz) +{ + // Validate pointer + if (q == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Check if the center frequency has changed + if (q->cfg.phase_compensation_hz == center_freq_hz) { + return SRSRAN_SUCCESS; + } + + // Save the current phase compensation + q->cfg.phase_compensation_hz = center_freq_hz; + + // If the center frequency is 0, NAN, INF, then skip + if (!isnormal(center_freq_hz)) { + return SRSRAN_SUCCESS; + } + + // Extract modulation required parameters + uint32_t symbol_sz = q->cfg.symbol_sz; + double scs = 15e3; //< Assume 15kHz subcarrier spacing + double srate_hz = symbol_sz * scs; + + // Assert parameters + if (!isnormal(srate_hz)) { + return SRSRAN_ERROR; + } + + // Otherwise calculate the phase + uint32_t count = 0; + for (uint32_t l = 0; l < q->nof_symbols * SRSRAN_NOF_SLOTS_PER_SF; l++) { + uint32_t cp_len = + SRSRAN_CP_ISNORM(q->cfg.cp) ? SRSRAN_CP_LEN_NORM(l % q->nof_symbols, symbol_sz) : SRSRAN_CP_LEN_EXT(symbol_sz); + + // Advance CP + count += cp_len; + + // Calculate symbol start time + double t_start = (double)count / srate_hz; + + // Calculate phase + double phase_rad = -2.0 * M_PI * center_freq_hz * t_start; + + // Calculate compensation phase in double precision and then convert to single + q->phase_compensation[l] = (cf_t)cexp(I * phase_rad); + + // Advance symbol + count += symbol_sz; + } + + return SRSRAN_SUCCESS; +} + void srsran_ofdm_rx_free(srsran_ofdm_t* q) { srsran_ofdm_free_(q); @@ -411,7 +498,18 @@ static void ofdm_rx_slot(srsran_ofdm_t* q, int slot_in_sf) memcpy(output + nof_re / 2, &tmp[dc], sizeof(cf_t) * nof_re / 2); // Normalize output - if (q->fft_plan.norm) { + if (isnormal(q->cfg.phase_compensation_hz)) { + // Get phase compensation + cf_t phase_compensation = conjf(q->phase_compensation[slot_in_sf * q->nof_symbols + i]); + + // Apply normalization + if (q->fft_plan.norm) { + phase_compensation *= norm; + } + + // Apply correction + srsran_vec_sc_prod_ccc(output, phase_compensation, output, nof_re); + } else if (q->fft_plan.norm) { srsran_vec_sc_prod_cfc(output, norm, output, nof_re); } @@ -482,7 +580,7 @@ void srsran_ofdm_rx_sf_ng(srsran_ofdm_t* q, cf_t* input, cf_t* output) } /* Transforms input OFDM symbols into output samples. - * Performs FFT on a each symbol and adds CP. + * Performs the FFT on each symbol and adds CP. */ static void ofdm_tx_slot(srsran_ofdm_t* q, int slot_in_sf) { @@ -512,8 +610,8 @@ static void ofdm_tx_slot(srsran_ofdm_t* q, int slot_in_sf) uint32_t dc = (q->fft_plan.dc) ? 1 : 0; for (int i = 0; i < nof_symbols; i++) { - memcpy(&tmp[dc], &input[nof_re / 2], nof_re / 2 * sizeof(cf_t)); - memcpy(&tmp[symbol_sz - nof_re / 2], &input[0], nof_re / 2 * sizeof(cf_t)); + srsran_vec_cf_copy(&tmp[dc], &input[nof_re / 2], nof_re / 2); + srsran_vec_cf_copy(&tmp[symbol_sz - nof_re / 2], &input[0], nof_re / 2); input += nof_re; tmp += symbol_sz; @@ -524,12 +622,28 @@ static void ofdm_tx_slot(srsran_ofdm_t* q, int slot_in_sf) for (int i = 0; i < nof_symbols; i++) { int cp_len = SRSRAN_CP_ISNORM(cp) ? SRSRAN_CP_LEN_NORM(i, symbol_sz) : SRSRAN_CP_LEN_EXT(symbol_sz); - if (q->fft_plan.norm) { + if (isnormal(q->cfg.phase_compensation_hz)) { + // Get phase compensation + cf_t phase_compensation = q->phase_compensation[slot_in_sf * q->nof_symbols + i]; + + // Apply normalization + if (q->fft_plan.norm) { + phase_compensation *= norm; + } + + // Apply correction + srsran_vec_sc_prod_ccc(&output[cp_len], phase_compensation, &output[cp_len], symbol_sz); + } else if (q->fft_plan.norm) { srsran_vec_sc_prod_cfc(&output[cp_len], norm, &output[cp_len], symbol_sz); } + // CFR: Process the time-domain signal without the CP + if (q->cfg.cfr_tx_cfg.cfr_enable) { + srsran_cfr_process(&q->tx_cfr, output + cp_len, output + cp_len); + } + /* add CP */ - memcpy(output, &output[symbol_sz], cp_len * sizeof(cf_t)); + srsran_vec_cf_copy(output, &output[symbol_sz], cp_len); output += symbol_sz + cp_len; } #endif @@ -574,3 +688,38 @@ void srsran_ofdm_tx_sf(srsran_ofdm_t* q) srsran_vec_prod_ccc(q->cfg.out_buffer, q->shift_buffer, q->cfg.out_buffer, q->sf_sz); } } + +int srsran_ofdm_set_cfr(srsran_ofdm_t* q, srsran_cfr_cfg_t* cfr) +{ + if (q == NULL || cfr == NULL) { + ERROR("Error, invalid inputs"); + return SRSRAN_ERROR_INVALID_INPUTS; + } + if (!q->max_prb) { + ERROR("Error, ofdm object not initialised"); + return SRSRAN_ERROR; + } + // Check if there is nothing to configure + if (memcmp(&q->cfg.cfr_tx_cfg, cfr, sizeof(srsran_cfr_cfg_t)) == 0) { + return SRSRAN_SUCCESS; + } + + // Copy the CFR config into the OFDM object + q->cfg.cfr_tx_cfg = *cfr; + + // Set the CFR parameters related to OFDM symbol and FFT size + q->cfg.cfr_tx_cfg.symbol_sz = q->cfg.symbol_sz; + q->cfg.cfr_tx_cfg.symbol_bw = q->nof_re; + + // in the LTE DL, the DC carrier is empty but still counts when designing the filter BW + // in the LTE UL, the DC carrier is used + q->cfg.cfr_tx_cfg.dc_sc = (!q->cfg.keep_dc) && (!isnormal(q->cfg.freq_shift_f)); + if (q->cfg.cfr_tx_cfg.cfr_enable) { + if (srsran_cfr_init(&q->tx_cfr, &q->cfg.cfr_tx_cfg) < SRSRAN_SUCCESS) { + ERROR("Error while initialising CFR module"); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/dft/test/CMakeLists.txt b/lib/src/phy/dft/test/CMakeLists.txt index 98ffdf173..ab588b771 100644 --- a/lib/src/phy/dft/test/CMakeLists.txt +++ b/lib/src/phy/dft/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -31,3 +31,5 @@ add_test(ofdm_shifted ofdm_test -s 0.5 -r 1) add_test(ofdm_offset ofdm_test -o 0.5 -r 1) add_test(ofdm_force ofdm_test -N 4096 -r 1) add_test(ofdm_extended_shifted_offset_force ofdm_test -e -o 0.5 -s 0.5 -N 4096 -r 1) +add_test(ofdm_normal_phase_compensation ofdm_test -r 1 -p 2.4e9) +add_test(ofdm_extended_phase_compensation ofdm_test -e -r 1 -p 2.4e9) diff --git a/lib/src/phy/dft/test/ofdm_test.c b/lib/src/phy/dft/test/ofdm_test.c index 0b526023e..a09b9bf07 100644 --- a/lib/src/phy/dft/test/ofdm_test.c +++ b/lib/src/phy/dft/test/ofdm_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,12 +29,13 @@ #include "srsran/phy/utils/random.h" #include "srsran/srsran.h" -static int nof_prb = -1; -static srsran_cp_t cp = SRSRAN_CP_NORM; -static int nof_repetitions = 1; -static float rx_window_offset = 0.5f; -static float freq_shift_f = 0.0f; -static uint32_t force_symbol_sz = 0; +static int nof_prb = -1; +static srsran_cp_t cp = SRSRAN_CP_NORM; +static int nof_repetitions = 1; +static float rx_window_offset = 0.5f; +static float freq_shift_f = 0.0f; +static double phase_compensation_hz = 0.0; +static uint32_t force_symbol_sz = 0; static double elapsed_us(struct timeval* ts_start, struct timeval* ts_end) { if (ts_end->tv_usec > ts_start->tv_usec) { @@ -55,12 +56,13 @@ static void usage(char* prog) printf("\t-r nof_repetitions [Default %d]\n", nof_repetitions); printf("\t-o rx window offset (portion of CP length) [Default %.1f]\n", rx_window_offset); printf("\t-s frequency shift (normalised with sampling rate) [Default %.1f]\n", freq_shift_f); + printf("\t-p Phase compensation carrier frequency in Hz [Default %.1f]\n", phase_compensation_hz); } static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "Nneros")) != -1) { + while ((opt = getopt(argc, argv, "Nnerosp")) != -1) { switch (opt) { case 'n': nof_prb = (int)strtol(argv[optind], NULL, 10); @@ -80,6 +82,9 @@ static void parse_args(int argc, char** argv) case 's': freq_shift_f = SRSRAN_MIN(1.0f, SRSRAN_MAX(0.0f, strtof(argv[optind], NULL))); break; + case 'p': + phase_compensation_hz = strtod(argv[optind], NULL); + break; default: usage(argv[0]); exit(-1); @@ -122,14 +127,15 @@ int main(int argc, char** argv) } srsran_vec_cf_zero(outifft, sf_len); - srsran_ofdm_cfg_t ofdm_cfg = {}; - ofdm_cfg.cp = cp; - ofdm_cfg.in_buffer = input; - ofdm_cfg.out_buffer = outifft; - ofdm_cfg.nof_prb = n_prb; - ofdm_cfg.symbol_sz = symbol_sz; - ofdm_cfg.freq_shift_f = freq_shift_f; - ofdm_cfg.normalize = true; + srsran_ofdm_cfg_t ofdm_cfg = {}; + ofdm_cfg.cp = cp; + ofdm_cfg.in_buffer = input; + ofdm_cfg.out_buffer = outifft; + ofdm_cfg.nof_prb = n_prb; + ofdm_cfg.symbol_sz = symbol_sz; + ofdm_cfg.freq_shift_f = freq_shift_f; + ofdm_cfg.normalize = true; + ofdm_cfg.phase_compensation_hz = phase_compensation_hz; if (srsran_ofdm_tx_init_cfg(&ifft, &ofdm_cfg)) { ERROR("Error initializing iFFT"); exit(-1); diff --git a/lib/src/phy/enb/CMakeLists.txt b/lib/src/phy/enb/CMakeLists.txt index a254c328c..472c0f244 100644 --- a/lib/src/phy/enb/CMakeLists.txt +++ b/lib/src/phy/enb/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/enb/enb_dl.c b/lib/src/phy/enb/enb_dl.c index b1f6f4ecb..3a25d9835 100644 --- a/lib/src/phy/enb/enb_dl.c +++ b/lib/src/phy/enb/enb_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -50,24 +50,17 @@ int srsran_enb_dl_init(srsran_enb_dl_t* q, cf_t* out_buffer[SRSRAN_MAX_PORTS], u goto clean_exit; } } + for (int i = 0; i < SRSRAN_MAX_PORTS; i++) { + q->out_buffer[i] = out_buffer[i]; + } srsran_ofdm_cfg_t ofdm_cfg = {}; ofdm_cfg.nof_prb = max_prb; - ofdm_cfg.cp = SRSRAN_CP_NORM; + ofdm_cfg.cp = SRSRAN_CP_EXT; ofdm_cfg.normalize = false; - for (int i = 0; i < SRSRAN_MAX_PORTS; i++) { - ofdm_cfg.in_buffer = q->sf_symbols[i]; - ofdm_cfg.out_buffer = out_buffer[i]; - ofdm_cfg.sf_type = SRSRAN_SF_NORM; - if (srsran_ofdm_tx_init_cfg(&q->ifft[i], &ofdm_cfg)) { - ERROR("Error initiating FFT (%d)", i); - goto clean_exit; - } - } - - ofdm_cfg.in_buffer = q->sf_symbols[0]; - ofdm_cfg.out_buffer = out_buffer[0]; - ofdm_cfg.sf_type = SRSRAN_SF_MBSFN; + ofdm_cfg.in_buffer = q->sf_symbols[0]; + ofdm_cfg.out_buffer = out_buffer[0]; + ofdm_cfg.sf_type = SRSRAN_SF_MBSFN; if (srsran_ofdm_tx_init_cfg(&q->ifft_mbsfn, &ofdm_cfg)) { ERROR("Error initiating FFT"); goto clean_exit; @@ -158,7 +151,21 @@ int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell) if (q->cell.nof_prb != 0) { srsran_regs_free(&q->regs); } - q->cell = cell; + q->cell = cell; + srsran_ofdm_cfg_t ofdm_cfg = {}; + ofdm_cfg.nof_prb = q->cell.nof_prb; + ofdm_cfg.cp = cell.cp; + ofdm_cfg.normalize = false; + for (int i = 0; i < SRSRAN_MAX_PORTS; i++) { + ofdm_cfg.in_buffer = q->sf_symbols[i]; + ofdm_cfg.out_buffer = q->out_buffer[i]; + ofdm_cfg.sf_type = SRSRAN_SF_NORM; + ofdm_cfg.cfr_tx_cfg = q->cfr_config; + if (srsran_ofdm_tx_init_cfg(&q->ifft[i], &ofdm_cfg)) { + ERROR("Error initiating FFT (%d)", i); + return SRSRAN_ERROR; + } + } if (srsran_regs_init(&q->regs, q->cell)) { ERROR("Error resizing REGs"); return SRSRAN_ERROR; @@ -232,6 +239,31 @@ int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell) return ret; } +int srsran_enb_dl_set_cfr(srsran_enb_dl_t* q, const srsran_cfr_cfg_t* cfr) +{ + if (q == NULL || cfr == NULL) { + ERROR("Error, invalid inputs"); + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Copy the cfr config into the eNB + q->cfr_config = *cfr; + + // Set the cfr for the ifft's + if (srsran_ofdm_set_cfr(&q->ifft_mbsfn, &q->cfr_config) < SRSRAN_SUCCESS) { + ERROR("Error setting the CFR for ifft_mbsfn"); + return SRSRAN_ERROR; + } + for (int i = 0; i < SRSRAN_MAX_PORTS; i++) { + if (srsran_ofdm_set_cfr(&q->ifft[i], &q->cfr_config) < SRSRAN_SUCCESS) { + ERROR("Error setting the CFR for the IFFT (%d)", i); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + #ifdef resolve void srsran_enb_dl_apply_power_allocation(srsran_enb_dl_t* q) { @@ -359,11 +391,11 @@ void srsran_enb_dl_put_phich(srsran_enb_dl_t* q, srsran_phich_grant_t* grant, bo srsran_phich_encode(&q->phich, &q->dl_sf, resource, ack, q->sf_symbols); } -bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, uint32_t ncce) +bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, const srsran_dci_location_t* loc) { if (SRSRAN_CFI_ISVALID(q->dl_sf.cfi)) { - return srsran_location_find_ncce( - q->common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], q->nof_common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], ncce); + return srsran_location_find_location( + q->common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], q->nof_common_locations[SRSRAN_CFI_IDX(q->dl_sf.cfi)], loc); } else { return false; } @@ -413,22 +445,22 @@ int srsran_enb_dl_put_pmch(srsran_enb_dl_t* q, srsran_pmch_cfg_t* pmch_cfg, uint void srsran_enb_dl_gen_signal(srsran_enb_dl_t* q) { - // TODO: PAPR control float norm_factor = enb_dl_get_norm_factor(q->cell.nof_prb); + // First apply the amplitude normalization, then perform the IFFT and optional CFR reduction if (q->dl_sf.sf_type == SRSRAN_SF_MBSFN) { - srsran_ofdm_tx_sf(&q->ifft_mbsfn); - srsran_vec_sc_prod_cfc(q->ifft_mbsfn.cfg.out_buffer, + srsran_vec_sc_prod_cfc(q->ifft_mbsfn.cfg.in_buffer, norm_factor, - q->ifft_mbsfn.cfg.out_buffer, - (uint32_t)SRSRAN_SF_LEN_PRB(q->cell.nof_prb)); + q->ifft_mbsfn.cfg.in_buffer, + SRSRAN_NOF_SLOTS_PER_SF * q->cell.nof_prb * SRSRAN_NRE * SRSRAN_CP_NSYMB(q->cell.cp)); + srsran_ofdm_tx_sf(&q->ifft_mbsfn); } else { for (int i = 0; i < q->cell.nof_ports; i++) { - srsran_ofdm_tx_sf(&q->ifft[i]); - srsran_vec_sc_prod_cfc(q->ifft[i].cfg.out_buffer, + srsran_vec_sc_prod_cfc(q->ifft[i].cfg.in_buffer, norm_factor, - q->ifft[i].cfg.out_buffer, - (uint32_t)SRSRAN_SF_LEN_PRB(q->cell.nof_prb)); + q->ifft[i].cfg.in_buffer, + SRSRAN_NOF_SLOTS_PER_SF * q->cell.nof_prb * SRSRAN_NRE * SRSRAN_CP_NSYMB(q->cell.cp)); + srsran_ofdm_tx_sf(&q->ifft[i]); } } } @@ -444,7 +476,15 @@ bool srsran_enb_dl_gen_cqi_periodic(const srsran_cell_t* cell, cqi_cfg->ri_len = srsran_ri_nof_bits(cell); cqi_enabled = true; } else if (srsran_cqi_periodic_send(&dl_cfg->cqi_report, tti, cell->frame_type)) { - cqi_cfg->type = SRSRAN_CQI_TYPE_WIDEBAND; + if (dl_cfg->cqi_report.format_is_subband && + srsran_cqi_periodic_is_subband(&dl_cfg->cqi_report, tti, cell->nof_prb, cell->frame_type)) { + // 36.213 table 7.2.2-1, periodic CQI supports UE-selected only + cqi_cfg->type = SRSRAN_CQI_TYPE_SUBBAND_UE; + cqi_cfg->L = srsran_cqi_hl_get_L(cell->nof_prb); + cqi_cfg->subband_label_2_bits = cqi_cfg->L > 1; + } else { + cqi_cfg->type = SRSRAN_CQI_TYPE_WIDEBAND; + } if (dl_cfg->tm == SRSRAN_TM4) { cqi_cfg->pmi_present = true; cqi_cfg->rank_is_not_one = last_ri > 0; diff --git a/lib/src/phy/enb/enb_dl_nr.c b/lib/src/phy/enb/enb_dl_nr.c deleted file mode 100644 index 0a89ed8df..000000000 --- a/lib/src/phy/enb/enb_dl_nr.c +++ /dev/null @@ -1,263 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/phy/enb/enb_dl_nr.h" -#include - -static int enb_dl_alloc_prb(srsran_enb_dl_nr_t* q, uint32_t new_nof_prb) -{ - if (q->max_prb < new_nof_prb) { - q->max_prb = new_nof_prb; - - for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { - if (q->sf_symbols[i] != NULL) { - free(q->sf_symbols[i]); - } - - q->sf_symbols[i] = srsran_vec_cf_malloc(SRSRAN_SLOT_LEN_RE_NR(q->max_prb)); - if (q->sf_symbols[i] == NULL) { - ERROR("Malloc"); - return SRSRAN_ERROR; - } - } - } - - return SRSRAN_SUCCESS; -} - -int srsran_enb_dl_nr_init(srsran_enb_dl_nr_t* q, cf_t* output[SRSRAN_MAX_PORTS], const srsran_enb_dl_nr_args_t* args) -{ - if (!q || !output || !args) { - return SRSRAN_ERROR_INVALID_INPUTS; - } - - if (args->nof_tx_antennas == 0) { - ERROR("Error invalid number of antennas (%d)", args->nof_tx_antennas); - return SRSRAN_ERROR; - } - - q->nof_tx_antennas = args->nof_tx_antennas; - - if (srsran_pdsch_nr_init_enb(&q->pdsch, &args->pdsch) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - - if (enb_dl_alloc_prb(q, args->nof_max_prb) < SRSRAN_SUCCESS) { - ERROR("Error allocating"); - return SRSRAN_ERROR; - } - - srsran_ofdm_cfg_t fft_cfg = {}; - fft_cfg.nof_prb = args->nof_max_prb; - fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(args->nof_max_prb); - fft_cfg.keep_dc = true; - - for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { - fft_cfg.in_buffer = q->sf_symbols[i]; - fft_cfg.out_buffer = output[i]; - srsran_ofdm_tx_init_cfg(&q->fft[i], &fft_cfg); - } - - if (srsran_dmrs_sch_init(&q->dmrs, false) < SRSRAN_SUCCESS) { - ERROR("Error DMRS"); - return SRSRAN_ERROR; - } - - if (srsran_pdcch_nr_init_tx(&q->pdcch, &args->pdcch) < SRSRAN_SUCCESS) { - ERROR("Error PDCCH"); - return SRSRAN_ERROR; - } - - return SRSRAN_SUCCESS; -} - -void srsran_enb_dl_nr_free(srsran_enb_dl_nr_t* q) -{ - if (q == NULL) { - return; - } - - for (uint32_t i = 0; i < SRSRAN_MAX_PORTS; i++) { - srsran_ofdm_rx_free(&q->fft[i]); - - if (q->sf_symbols[i] != NULL) { - free(q->sf_symbols[i]); - } - } - - srsran_pdsch_nr_free(&q->pdsch); - srsran_dmrs_sch_free(&q->dmrs); - - srsran_pdcch_nr_free(&q->pdcch); - - SRSRAN_MEM_ZERO(q, srsran_enb_dl_nr_t, 1); -} - -int srsran_enb_dl_nr_set_carrier(srsran_enb_dl_nr_t* q, const srsran_carrier_nr_t* carrier) -{ - if (srsran_pdsch_nr_set_carrier(&q->pdsch, carrier) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - - if (srsran_dmrs_sch_set_carrier(&q->dmrs, carrier) < SRSRAN_SUCCESS) { - ERROR("Error DMRS"); - return SRSRAN_ERROR; - } - - if (enb_dl_alloc_prb(q, carrier->nof_prb) < SRSRAN_SUCCESS) { - ERROR("Error allocating"); - return SRSRAN_ERROR; - } - - if (carrier->nof_prb != q->carrier.nof_prb) { - srsran_ofdm_cfg_t fft_cfg = {}; - fft_cfg.nof_prb = carrier->nof_prb; - fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); - fft_cfg.keep_dc = true; - - for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { - fft_cfg.in_buffer = q->sf_symbols[i]; - srsran_ofdm_tx_init_cfg(&q->fft[i], &fft_cfg); - } - } - - q->carrier = *carrier; - - return SRSRAN_SUCCESS; -} - -int srsran_enb_dl_nr_set_pdcch_config(srsran_enb_dl_nr_t* q, - const srsran_pdcch_cfg_nr_t* cfg, - const srsran_dci_cfg_nr_t* dci_cfg) -{ - if (q == NULL || cfg == NULL) { - return SRSRAN_ERROR_INVALID_INPUTS; - } - - q->pdcch_cfg = *cfg; - - if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, &q->pdcch_cfg.coreset[0]) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - - if (srsran_dci_nr_set_cfg(&q->dci, dci_cfg) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - - return SRSRAN_SUCCESS; -} - -void srsran_enb_dl_nr_gen_signal(srsran_enb_dl_nr_t* q) -{ - if (q == NULL) { - return; - } - - for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { - srsran_ofdm_tx_sf(&q->fft[i]); - } -} - -int srsran_enb_dl_nr_base_zero(srsran_enb_dl_nr_t* q) -{ - if (q == NULL) { - return SRSRAN_ERROR_INVALID_INPUTS; - } - - for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { - srsran_vec_cf_zero(q->sf_symbols[i], SRSRAN_SLOT_LEN_RE_NR(q->carrier.nof_prb)); - } - - return SRSRAN_SUCCESS; -} - -int srsran_enb_dl_nr_pdcch_put(srsran_enb_dl_nr_t* q, - const srsran_slot_cfg_t* slot_cfg, - const srsran_dci_dl_nr_t* dci_dl) -{ - if (q == NULL || slot_cfg == NULL || dci_dl == NULL) { - return SRSRAN_ERROR_INVALID_INPUTS; - } - - if (dci_dl->ctx.coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET || - !q->pdcch_cfg.coreset_present[dci_dl->ctx.coreset_id]) { - ERROR("Invalid CORESET ID %d", dci_dl->ctx.coreset_id); - return SRSRAN_ERROR; - } - srsran_coreset_t* coreset = &q->pdcch_cfg.coreset[dci_dl->ctx.coreset_id]; - - if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, coreset) < SRSRAN_SUCCESS) { - ERROR("Error setting PDCCH carrier/CORESET"); - return SRSRAN_ERROR; - } - - // Put DMRS - if (srsran_dmrs_pdcch_put(&q->carrier, coreset, slot_cfg, &dci_dl->ctx.location, q->sf_symbols[0]) < SRSRAN_SUCCESS) { - ERROR("Error putting PDCCH DMRS"); - return SRSRAN_ERROR; - } - - // Pack DCI - srsran_dci_msg_nr_t dci_msg = {}; - if (srsran_dci_nr_dl_pack(&q->dci, dci_dl, &dci_msg) < SRSRAN_SUCCESS) { - ERROR("Error packing DL DCI"); - return SRSRAN_ERROR; - } - - // PDCCH Encode - if (srsran_pdcch_nr_encode(&q->pdcch, &dci_msg, q->sf_symbols[0]) < SRSRAN_SUCCESS) { - ERROR("Error encoding PDCCH"); - return SRSRAN_ERROR; - } - - INFO("DCI DL NR: L=%d; ncce=%d;", dci_dl->ctx.location.L, dci_dl->ctx.location.ncce); - - return SRSRAN_SUCCESS; -} - -int srsran_enb_dl_nr_pdsch_put(srsran_enb_dl_nr_t* q, - const srsran_slot_cfg_t* slot, - const srsran_sch_cfg_nr_t* cfg, - uint8_t* data[SRSRAN_MAX_TB]) -{ - if (srsran_dmrs_sch_put_sf(&q->dmrs, slot, cfg, &cfg->grant, q->sf_symbols[0]) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - - if (srsran_pdsch_nr_encode(&q->pdsch, cfg, &cfg->grant, data, q->sf_symbols) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - - return SRSRAN_SUCCESS; -} - -int srsran_enb_dl_nr_pdsch_info(const srsran_enb_dl_nr_t* q, - const srsran_sch_cfg_nr_t* cfg, - char* str, - uint32_t str_len) -{ - int len = 0; - - // Append PDSCH info - len += srsran_pdsch_nr_tx_info(&q->pdsch, cfg, &cfg->grant, &str[len], str_len - len); - - return len; -} diff --git a/lib/src/phy/enb/enb_ul.c b/lib/src/phy/enb/enb_ul.c index fdb3c7e6b..20fc02366 100644 --- a/lib/src/phy/enb/enb_ul.c +++ b/lib/src/phy/enb/enb_ul.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -46,19 +46,7 @@ int srsran_enb_ul_init(srsran_enb_ul_t* q, cf_t* in_buffer, uint32_t max_prb) perror("malloc"); goto clean_exit; } - - srsran_ofdm_cfg_t ofdm_cfg = {}; - ofdm_cfg.nof_prb = max_prb; - ofdm_cfg.in_buffer = in_buffer; - ofdm_cfg.out_buffer = q->sf_symbols; - ofdm_cfg.cp = SRSRAN_CP_NORM; - ofdm_cfg.freq_shift_f = -0.5f; - ofdm_cfg.normalize = false; - ofdm_cfg.rx_window_offset = 0.5f; - if (srsran_ofdm_rx_init_cfg(&q->fft, &ofdm_cfg)) { - ERROR("Error initiating FFT"); - goto clean_exit; - } + q->in_buffer = in_buffer; if (srsran_pucch_init_enb(&q->pucch)) { ERROR("Error creating PUCCH object"); @@ -117,6 +105,18 @@ int srsran_enb_ul_set_cell(srsran_enb_ul_t* q, if (cell.id != q->cell.id || q->cell.nof_prb == 0) { q->cell = cell; + srsran_ofdm_cfg_t ofdm_cfg = {}; + ofdm_cfg.nof_prb = q->cell.nof_prb; + ofdm_cfg.in_buffer = q->in_buffer; + ofdm_cfg.out_buffer = q->sf_symbols; + ofdm_cfg.cp = q->cell.cp; + ofdm_cfg.freq_shift_f = -0.5f; + ofdm_cfg.normalize = false; + ofdm_cfg.rx_window_offset = 0.5f; + if (srsran_ofdm_rx_init_cfg(&q->fft, &ofdm_cfg)) { + ERROR("Error initiating FFT"); + return SRSRAN_ERROR; + } if (srsran_ofdm_rx_set_prb(&q->fft, q->cell.cp, q->cell.nof_prb)) { ERROR("Error initiating FFT"); return SRSRAN_ERROR; @@ -193,7 +193,9 @@ static int get_pucch(srsran_enb_ul_t* q, srsran_ul_sf_cfg_t* ul_sf, srsran_pucch ERROR("Error estimating PUCCH DMRS"); return SRSRAN_ERROR; } - pucch_res.snr_db = q->chest_res.snr_db; + pucch_res.snr_db = q->chest_res.snr_db; + pucch_res.rssi_dbFs = q->chest_res.epre_dBfs; + pucch_res.ni_dbFs = q->chest_res.noise_estimate_dbFs; ret = srsran_pucch_decode(&q->pucch, ul_sf, cfg, &q->chest_res, q->sf_symbols, &pucch_res); if (ret < SRSRAN_SUCCESS) { @@ -214,7 +216,7 @@ static int get_pucch(srsran_enb_ul_t* q, srsran_ul_sf_cfg_t* ul_sf, srsran_pucch // Compares correlation value, it stores the PUCCH result with the greatest correlation if (i == 0 || pucch_res.correlation > res->correlation) { - // Copy measurements only if PUCCH was decoded succesfully + // Copy measurements only if PUCCH was decoded successfully if (cfg->meas_ta_en) { pucch_res.ta_valid = !(isnan(q->chest_res.ta_us) || isinf(q->chest_res.ta_us)); pucch_res.ta_us = q->chest_res.ta_us; diff --git a/lib/src/phy/fec/CMakeLists.txt b/lib/src/phy/fec/CMakeLists.txt index 5e4e59612..f83ae57d8 100644 --- a/lib/src/phy/fec/CMakeLists.txt +++ b/lib/src/phy/fec/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/block/CMakeLists.txt b/lib/src/phy/fec/block/CMakeLists.txt index e6385c799..01f7a0749 100644 --- a/lib/src/phy/fec/block/CMakeLists.txt +++ b/lib/src/phy/fec/block/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/block/block.c b/lib/src/phy/fec/block/block.c index bde59aad2..140148f4d 100644 --- a/lib/src/phy/fec/block/block.c +++ b/lib/src/phy/fec/block/block.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/block/test/CMakeLists.txt b/lib/src/phy/fec/block/test/CMakeLists.txt index 074430b5d..fd8e8b80b 100644 --- a/lib/src/phy/fec/block/test/CMakeLists.txt +++ b/lib/src/phy/fec/block/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/block/test/block_test.c b/lib/src/phy/fec/block/test/block_test.c index e896fe31e..d3a993a79 100644 --- a/lib/src/phy/fec/block/test/block_test.c +++ b/lib/src/phy/fec/block/test/block_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -18,10 +18,10 @@ * and at http://www.gnu.org/licenses/. * */ -#include "srsran/common/test_common.h" #include "srsran/phy/fec/block/block.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/random.h" +#include "srsran/support/srsran_test.h" #include #include #include @@ -41,7 +41,7 @@ void usage(char* prog) printf("Usage: %s [Rv]\n", prog); printf("\t-R Number of repetitions [Default %d]\n", nof_repetitions); printf("\t-E Number of encoded bits [Default %d]\n", E); - printf("\t-v increase verbose [Default %d]\n", srsran_verbose); + printf("\t-v increase verbose [Default %d]\n", get_srsran_verbose_level()); } void parse_args(int argc, char** argv) @@ -56,7 +56,7 @@ void parse_args(int argc, char** argv) E = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/fec/cbsegm.c b/lib/src/phy/fec/cbsegm.c index bdf1f63f6..176e200e1 100644 --- a/lib/src/phy/fec/cbsegm.c +++ b/lib/src/phy/fec/cbsegm.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/convolutional/CMakeLists.txt b/lib/src/phy/fec/convolutional/CMakeLists.txt index a4d3dc7cd..441505b0f 100644 --- a/lib/src/phy/fec/convolutional/CMakeLists.txt +++ b/lib/src/phy/fec/convolutional/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/convolutional/convcoder.c b/lib/src/phy/fec/convolutional/convcoder.c index bbb6d10ba..3fc9874e2 100644 --- a/lib/src/phy/fec/convolutional/convcoder.c +++ b/lib/src/phy/fec/convolutional/convcoder.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/convolutional/parity.c b/lib/src/phy/fec/convolutional/parity.c index 14aa7dc97..6c3328eae 100644 --- a/lib/src/phy/fec/convolutional/parity.c +++ b/lib/src/phy/fec/convolutional/parity.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/convolutional/parity.h b/lib/src/phy/fec/convolutional/parity.h index 7e5399975..965f31c34 100644 --- a/lib/src/phy/fec/convolutional/parity.h +++ b/lib/src/phy/fec/convolutional/parity.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/convolutional/test/CMakeLists.txt b/lib/src/phy/fec/convolutional/test/CMakeLists.txt index 61bc8a5ab..3759bce24 100644 --- a/lib/src/phy/fec/convolutional/test/CMakeLists.txt +++ b/lib/src/phy/fec/convolutional/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -30,7 +30,11 @@ add_test(viterbi_40_2 viterbi_test -n 1000 -s 1 -l 40 -t -e 2.0) add_test(viterbi_40_3 viterbi_test -n 1000 -s 1 -l 40 -t -e 3.0) add_test(viterbi_40_4 viterbi_test -n 1000 -s 1 -l 40 -t -e 4.5) -add_test(viterbi_1000_0 viterbi_test -n 100 -s 1 -l 1000 -t -e 0.0) +if (HAVE_AVX2) + # The accuracy of the 8-bit implementation of the Viterbi decoder used on + # non-AVX2 machines falls below the theoretical accuracy at 0dB. + add_test(viterbi_1000_0 viterbi_test -n 100 -s 1 -l 1000 -t -e 0.0) +endif() add_test(viterbi_1000_2 viterbi_test -n 100 -s 1 -l 1000 -t -e 2.0) add_test(viterbi_1000_3 viterbi_test -n 100 -s 1 -l 1000 -t -e 3.0) add_test(viterbi_1000_4 viterbi_test -n 100 -s 1 -l 1000 -t -e 4.5) diff --git a/lib/src/phy/fec/convolutional/test/viterbi_test.c b/lib/src/phy/fec/convolutional/test/viterbi_test.c index bc28c64c4..2a0cf0059 100644 --- a/lib/src/phy/fec/convolutional/test/viterbi_test.c +++ b/lib/src/phy/fec/convolutional/test/viterbi_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -193,13 +193,13 @@ int main(int argc, char** argv) for (uint32_t i = 0; i < snr_points; i++) { ebno_db = SNR_MIN + i * ebno_inc; esno_db = ebno_db + srsran_convert_power_to_dB(1.0f / 3.0f); - var[i] = srsran_convert_dB_to_amplitude(esno_db); - varunc[i] = srsran_convert_dB_to_amplitude(ebno_db); + var[i] = srsran_convert_dB_to_power(-esno_db); + varunc[i] = srsran_convert_dB_to_power(-ebno_db); } } else { esno_db = ebno_db + srsran_convert_power_to_dB(1.0f / 3.0f); - var[0] = srsran_convert_dB_to_amplitude(esno_db); - varunc[0] = srsran_convert_dB_to_amplitude(ebno_db); + var[0] = srsran_convert_dB_to_power(-esno_db); + varunc[0] = srsran_convert_dB_to_power(-ebno_db); snr_points = 1; } diff --git a/lib/src/phy/fec/convolutional/test/viterbi_test.h b/lib/src/phy/fec/convolutional/test/viterbi_test.h index c14c76de8..3cf75cc66 100644 --- a/lib/src/phy/fec/convolutional/test/viterbi_test.h +++ b/lib/src/phy/fec/convolutional/test/viterbi_test.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -62,7 +62,7 @@ static expected_errors_t expected_errors[] = {{1000, 1, 40, true, 0.0, 7282}, {1000, 1, 56, true, 3.0, 176}, {1000, 1, 56, true, 4.5, 24}, - {100, 1, 1000, true, 0.0, 13208}, + {100, 1, 1000, true, 0.0, 16000}, {100, 1, 1000, true, 2.0, 939}, {100, 1, 1000, true, 3.0, 110}, {100, 1, 1000, true, 4.5, 5}, diff --git a/lib/src/phy/fec/convolutional/viterbi.c b/lib/src/phy/fec/convolutional/viterbi.c index ae35e6b17..36319d1b9 100644 --- a/lib/src/phy/fec/convolutional/viterbi.c +++ b/lib/src/phy/fec/convolutional/viterbi.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,11 +34,11 @@ #define DEB 0 -#define TB_ITER 3 +#define TB_ITER 5 #define DEFAULT_GAIN 100 -#define DEFAULT_GAIN_16 1000 +#define DEFAULT_GAIN_16 500 #define VITERBI_16 #ifndef LV_HAVE_AVX2 @@ -558,11 +558,10 @@ int srsran_viterbi_decode_f(srsran_viterbi_t* q, float* symbols, uint8_t* data, len = 3 * (frame_length + q->K - 1); } if (!q->decode_f) { - float max = 1e-9; - for (int i = 0; i < len; i++) { - if (fabs(symbols[i]) > max) { - max = fabs(symbols[i]); - } + float max = 1e-9; + uint32_t max_i = srsran_vec_max_abs_fi(symbols, len); + if (max_i < len && isnormal(symbols[max_i])) { + max = fabsf(symbols[max_i]); } #ifdef VITERBI_16 srsran_vec_quant_fus(symbols, q->symbols_us, q->gain_quant / max, 32767.5, 65535, len); diff --git a/lib/src/phy/fec/convolutional/viterbi37.h b/lib/src/phy/fec/convolutional/viterbi37.h index 57867679a..a4c4b3b88 100644 --- a/lib/src/phy/fec/convolutional/viterbi37.h +++ b/lib/src/phy/fec/convolutional/viterbi37.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/convolutional/viterbi37_avx2_16bit.c b/lib/src/phy/fec/convolutional/viterbi37_avx2_16bit.c index 9f0aaabaa..6d143dbb1 100644 --- a/lib/src/phy/fec/convolutional/viterbi37_avx2_16bit.c +++ b/lib/src/phy/fec/convolutional/viterbi37_avx2_16bit.c @@ -43,7 +43,6 @@ union branchtab27 { } Branchtab37_sse2[3]; -int firstGo_16bit; /* State info for instance of Viterbi decoder */ struct v37 { metric_t metrics1; /* path metric buffer 1 */ @@ -85,7 +84,6 @@ int init_viterbi37_avx2_16bit(void* p, int starting_state) vp->metrics1.c[i] = 63; clear_v37_avx2_16bit(vp); - firstGo_16bit = 1; vp->old_metrics = &vp->metrics1; vp->new_metrics = &vp->metrics2; vp->dp = vp->decisions; diff --git a/lib/src/phy/fec/crc.c b/lib/src/phy/fec/crc.c index 6b0d386b8..ec3253ce9 100644 --- a/lib/src/phy/fec/crc.c +++ b/lib/src/phy/fec/crc.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -186,8 +186,10 @@ uint32_t srsran_crc_attach(srsran_crc_t* h, uint8_t* data, int len) bool srsran_crc_match(srsran_crc_t* h, uint8_t* data, int len) { - uint8_t* ptr = &data[len]; - uint32_t checksum1 = srsran_crc_checksum(h, data, len); - uint32_t checksum2 = srsran_bit_pack(&ptr, h->order); - return (checksum1 == checksum2); + return srsran_crc_checksum(h, data, len + h->order) == 0; +} + +bool srsran_crc_match_byte(srsran_crc_t* h, uint8_t* data, int len) +{ + return srsran_crc_checksum_byte(h, data, len + h->order) == 0; } diff --git a/lib/src/phy/fec/ldpc/CMakeLists.txt b/lib/src/phy/fec/ldpc/CMakeLists.txt index ceba4e9d4..d2f076aa7 100644 --- a/lib/src/phy/fec/ldpc/CMakeLists.txt +++ b/lib/src/phy/fec/ldpc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/ldpc/base_graph.c b/lib/src/phy/fec/ldpc/base_graph.c index c6a047421..a0a3d14c9 100644 --- a/lib/src/phy/fec/ldpc/base_graph.c +++ b/lib/src/phy/fec/ldpc/base_graph.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_avx2_consts.h b/lib/src/phy/fec/ldpc/ldpc_avx2_consts.h index 1b33e1c28..7e4d776ad 100644 --- a/lib/src/phy/fec/ldpc/ldpc_avx2_consts.h +++ b/lib/src/phy/fec/ldpc/ldpc_avx2_consts.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_avx512_consts.h b/lib/src/phy/fec/ldpc/ldpc_avx512_consts.h index e8dc4da6d..809d5b599 100644 --- a/lib/src/phy/fec/ldpc/ldpc_avx512_consts.h +++ b/lib/src/phy/fec/ldpc/ldpc_avx512_consts.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_all.h b/lib/src/phy/fec/ldpc/ldpc_dec_all.h index 87c85e355..e72c699a0 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_all.h +++ b/lib/src/phy/fec/ldpc/ldpc_dec_all.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c.c b/lib/src/phy/fec/ldpc/ldpc_dec_c.c index 2127501a6..1b0cb2953 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2.c index 0b08ff2bf..62031f0dc 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2_flood.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2_flood.c index 5b353468c..5e43f2049 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2_flood.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2_flood.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -389,9 +389,8 @@ int update_ldpc_soft_bits_c_avx2_flood(void* p, const int8_t (*these_var_indices for (i_layer = 0; i_layer < vp->bgM; i_layer++) { current_var_index = these_var_indices[i_layer][0]; - this_check_to_var = vp->check_to_var + i_layer * (vp->hrr + 1); - for (i = 0; (current_var_index != -1) && (i < MAX_CNCT); i++) { + for (i = 1; (current_var_index != -1) && (i < MAX_CNCT); i++) { i_bit_tmp_base = (current_var_index <= vp->hrr) ? current_var_index : vp->hrr; tmp_epi8 = _mm256_adds_epi8(this_check_to_var[i_bit_tmp_base], vp->soft_bits.v[current_var_index]); @@ -404,7 +403,7 @@ int update_ldpc_soft_bits_c_avx2_flood(void* p, const int8_t (*these_var_indices mask_epi8 = _mm256_cmpgt_epi8(neg_infty7_epi8, tmp_epi8); vp->soft_bits.v[current_var_index] = _mm256_blendv_epi8(tmp_epi8, neg_infty8_epi8, mask_epi8); - current_var_index = these_var_indices[i_layer][i + 1]; + current_var_index = these_var_indices[i_layer][i]; } } diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long.c index d7c15db7c..178cb3ef1 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long_flood.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long_flood.c index e0f1631c2..426db7230 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long_flood.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx2long_flood.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -443,7 +443,7 @@ int update_ldpc_soft_bits_c_avx2long_flood(void* p, const int8_t (*these_var_ind current_var_index = these_var_indices[i_layer][0]; this_check_to_var = vp->check_to_var + i_layer * (vp->hrr + 1) * vp->n_subnodes; - for (i = 0; (current_var_index != -1) && (i < MAX_CNCT); i++) { + for (i = 1; (current_var_index != -1) && (i < MAX_CNCT); i++) { current_var_index_subnode = current_var_index * vp->n_subnodes; for (j = 0; j < vp->n_subnodes; j++) { i_bit_tmp_base = (current_var_index <= vp->hrr) ? current_var_index : vp->hrr; @@ -459,7 +459,7 @@ int update_ldpc_soft_bits_c_avx2long_flood(void* p, const int8_t (*these_var_ind vp->soft_bits[current_var_index_subnode + j].v = _mm256_blendv_epi8(tmp_epi8, neg_infty8_epi8, mask_epi8); } - current_var_index = these_var_indices[i_layer][i + 1]; + current_var_index = these_var_indices[i_layer][i]; } } diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512.c index 90c483347..e63ca891b 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512long.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512long.c index 3676b4502..f827c730d 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512long.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512long.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512long_flood.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512long_flood.c index 7c38d109b..f0927a639 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512long_flood.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_avx512long_flood.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_c_flood.c b/lib/src/phy/fec/ldpc/ldpc_dec_c_flood.c index a96e30a97..47baf38a2 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_c_flood.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_c_flood.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -326,7 +326,7 @@ int update_ldpc_soft_bits_c_flood(void* p, const int8_t (*these_var_indices)[MAX for (i_layer = 0; i_layer < vp->bgM; i_layer++) { current_var_index = these_var_indices[i_layer][0]; this_check_to_var = vp->check_to_var + i_layer * (vp->hrrN + vp->ls); - for (i = 0; (current_var_index != -1) && (i < MAX_CNCT); i++) { + for (i = 1; (current_var_index != -1) && (i < MAX_CNCT); i++) { // recall that current_var_index depends on i! current_var_index_ext = current_var_index * vp->ls; for (j = 0; j < vp->ls; j++) { @@ -341,7 +341,7 @@ int update_ldpc_soft_bits_c_flood(void* p, const int8_t (*these_var_indices)[MAX } vp->soft_bits[i_bit] = (int8_t)tmp; } - current_var_index = these_var_indices[i_layer][i + 1]; + current_var_index = these_var_indices[i_layer][i]; } } diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_f.c b/lib/src/phy/fec/ldpc/ldpc_dec_f.c index bb51ee346..6daed503d 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_f.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_f.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_dec_s.c b/lib/src/phy/fec/ldpc/ldpc_dec_s.c index 361530c67..0c34266fc 100644 --- a/lib/src/phy/fec/ldpc/ldpc_dec_s.c +++ b/lib/src/phy/fec/ldpc/ldpc_dec_s.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_decoder.c b/lib/src/phy/fec/ldpc/ldpc_decoder.c index a84c2e4b7..a5fa0a6f6 100644 --- a/lib/src/phy/fec/ldpc/ldpc_decoder.c +++ b/lib/src/phy/fec/ldpc/ldpc_decoder.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_enc_all.h b/lib/src/phy/fec/ldpc/ldpc_enc_all.h index 4bf323459..03d59411a 100644 --- a/lib/src/phy/fec/ldpc/ldpc_enc_all.h +++ b/lib/src/phy/fec/ldpc/ldpc_enc_all.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_enc_avx2.c b/lib/src/phy/fec/ldpc/ldpc_enc_avx2.c index 735e6fa41..47d2b5f2f 100644 --- a/lib/src/phy/fec/ldpc/ldpc_enc_avx2.c +++ b/lib/src/phy/fec/ldpc/ldpc_enc_avx2.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_enc_avx2long.c b/lib/src/phy/fec/ldpc/ldpc_enc_avx2long.c index 2b86a01c3..797620f13 100644 --- a/lib/src/phy/fec/ldpc/ldpc_enc_avx2long.c +++ b/lib/src/phy/fec/ldpc/ldpc_enc_avx2long.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_enc_avx512.c b/lib/src/phy/fec/ldpc/ldpc_enc_avx512.c index da1867cd7..0c6483779 100644 --- a/lib/src/phy/fec/ldpc/ldpc_enc_avx512.c +++ b/lib/src/phy/fec/ldpc/ldpc_enc_avx512.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_enc_avx512long.c b/lib/src/phy/fec/ldpc/ldpc_enc_avx512long.c index c5b096e2d..45dc78299 100644 --- a/lib/src/phy/fec/ldpc/ldpc_enc_avx512long.c +++ b/lib/src/phy/fec/ldpc/ldpc_enc_avx512long.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_enc_c.c b/lib/src/phy/fec/ldpc/ldpc_enc_c.c index 0fd0ade90..1205578bf 100644 --- a/lib/src/phy/fec/ldpc/ldpc_enc_c.c +++ b/lib/src/phy/fec/ldpc/ldpc_enc_c.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_encoder.c b/lib/src/phy/fec/ldpc/ldpc_encoder.c index 909f8f293..7a138836f 100644 --- a/lib/src/phy/fec/ldpc/ldpc_encoder.c +++ b/lib/src/phy/fec/ldpc/ldpc_encoder.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/ldpc_rm.c b/lib/src/phy/fec/ldpc/ldpc_rm.c index 40738099b..be515d3b9 100644 --- a/lib/src/phy/fec/ldpc/ldpc_rm.c +++ b/lib/src/phy/fec/ldpc/ldpc_rm.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/CMakeLists.txt b/lib/src/phy/fec/ldpc/test/CMakeLists.txt index ed5d0dcaa..38b89ed25 100644 --- a/lib/src/phy/fec/ldpc/test/CMakeLists.txt +++ b/lib/src/phy/fec/ldpc/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -60,7 +60,7 @@ endif(HAVE_AVX512) ### Test LDPC libs function(ldpc_unit_tests) foreach(i IN LISTS ARGN) - add_nr_test(NAME ${test_name}-LS${i} COMMAND ${test_command} -l${i} + add_nr_advanced_test(NAME ${test_name}-LS${i} COMMAND ${test_command} -l${i} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) endforeach() @@ -179,36 +179,36 @@ function(ldpc_rm_unit_tests) math(EXPR tmpN "${N} - (${N} % ${Div})") math(EXPR E "${Ordval}*(${tmpN})/${Div}") #twice the rate - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR M "${N} / 2") # Half size buffer - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR Div "2*${Ordval}") math(EXPR tmpN "${N} - (${N} % ${Div})") math(EXPR E "${Ordval}*(${tmpN})/${Div}") #twice the rate - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR M "${N}/ 2") # Half size buffer - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR Div "${Ordval}") math(EXPR tmpN "2*${N} - (2*${N} % ${Div})") #Half the rate math(EXPR E "${Ordval}*(${tmpN})/${Div}") - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${N} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${N} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) math(EXPR M "${N}/ 2") # Half size buffer - add_nr_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} + add_nr_advanced_test(NAME ${test_name}-b${bgval}-l${i}-e${E}-f10-m${Modval}-r${rvval}-M${M} COMMAND ${test_command} -b${bgval} -l${i} -e${E} -f10 -m${Modval} -r${rvval} -M${M} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ ) endforeach() diff --git a/lib/src/phy/fec/ldpc/test/ldpc_chain_test.c b/lib/src/phy/fec/ldpc/test/ldpc_chain_test.c index 3e0e33902..131056555 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_chain_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_chain_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -323,6 +323,7 @@ int main(int argc, char** argv) int n_error_words_avx512_flood = 0; #endif // lV_HAVE_AVX512 + float noise_var = srsran_convert_dB_to_power(-snr); float noise_std_dev = srsran_convert_dB_to_amplitude(-snr); int16_t inf15 = (1U << 14U) - 1; @@ -380,12 +381,12 @@ int main(int argc, char** argv) } // Apply AWGN - srsran_ch_awgn_f(symbols_rm, symbols_rm, noise_std_dev, batch_size * (rm_length + F)); + srsran_ch_awgn_f(symbols_rm, symbols_rm, noise_var, batch_size * (rm_length + F)); // Convert symbols into LLRs for (i = 0; i < batch_size; i++) { for (j = 0; j < rm_length + F; j++) { //+F because we have already considered fillerbits when modulating. - symbols[i * finalN + j] = symbols_rm[i * (rm_length + F) + j] * 2 / (noise_std_dev * noise_std_dev); + symbols[i * finalN + j] = symbols_rm[i * (rm_length + F) + j] * 2 / noise_var; } // the rest of symbols are undetermined, set LLR to 0 for (; j < finalN; j++) { diff --git a/lib/src/phy/fec/ldpc/test/ldpc_dec_avx2_test.c b/lib/src/phy/fec/ldpc/test/ldpc_dec_avx2_test.c index 9e6d15686..89532e31b 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_dec_avx2_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_dec_avx2_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/ldpc_dec_avx512_test.c b/lib/src/phy/fec/ldpc/test/ldpc_dec_avx512_test.c index a04b46d02..c5fe23ef2 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_dec_avx512_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_dec_avx512_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/ldpc_dec_c_test.c b/lib/src/phy/fec/ldpc/test/ldpc_dec_c_test.c index 9169ee222..cefe48132 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_dec_c_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_dec_c_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/ldpc_dec_s_test.c b/lib/src/phy/fec/ldpc/test/ldpc_dec_s_test.c index 0e5888229..2e3e957a3 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_dec_s_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_dec_s_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/ldpc_dec_test.c b/lib/src/phy/fec/ldpc/test/ldpc_dec_test.c index 588c24933..28eb14c00 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_dec_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_dec_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/ldpc_enc_avx2_test.c b/lib/src/phy/fec/ldpc/test/ldpc_enc_avx2_test.c index 32ab1b78b..e206bca05 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_enc_avx2_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_enc_avx2_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/ldpc_enc_avx512_test.c b/lib/src/phy/fec/ldpc/test/ldpc_enc_avx512_test.c index 386c7f2fe..2e44fa255 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_enc_avx512_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_enc_avx512_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/ldpc_enc_test.c b/lib/src/phy/fec/ldpc/test/ldpc_enc_test.c index 606dcb317..b4602a37f 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_enc_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_enc_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/ldpc/test/ldpc_rm_chain_test.c b/lib/src/phy/fec/ldpc/test/ldpc_rm_chain_test.c index e3e3b871e..b17874dbb 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_rm_chain_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_rm_chain_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -69,7 +69,7 @@ static uint8_t rv = 0; /*!< \brief Redundancy version { static srsran_mod_t mod_type = SRSRAN_MOD_BPSK; /*!< \brief Modulation type: BPSK, QPSK, QAM16, QAM64, QAM256 = 4 */ static uint32_t Nref = 0; /*!< \brief Limited buffer size. */ static float snr = 0; /*!< \brief Signal-to-Noise Ratio [dB]. */ -static uint8_t rm_aware = 1; /*!< \brief Flag rate matching aware encoding/decoding (1 to enable). */ +static uint8_t rm_aware = 1; /*!< \brief Flag rate matching aware encoding/decoding (1 to enable). */ static int finalK = 0; /*!< \brief Number of uncoded bits (message length, including punctured and filler bits). */ static int finalN = 0; /*!< \brief Number of coded bits (codeword length). */ @@ -102,7 +102,7 @@ void usage(char* prog) /*! * \brief Parses the input line. */ -void parse_args(int argc, char** argv) +int parse_args(int argc, char** argv) { int opt = 0; while ((opt = getopt(argc, argv, "b:l:e:f:r:m:w:M:s:B:N:E:")) != -1) { @@ -145,21 +145,24 @@ void parse_args(int argc, char** argv) break; default: usage(argv[0]); - exit(-1); + return SRSRAN_ERROR; } } + return SRSRAN_SUCCESS; } /*! * \brief Prints decoder statistics. */ -void print_decoder(char* title, int n_batches, int n_errors, double elapsed_time); +int print_decoder(char* title, int n_batches, int n_errors, double elapsed_time); /*! * \brief Main test function. */ int main(int argc, char** argv) { + int ret = SRSRAN_ERROR; + uint8_t* messages_true = NULL; uint8_t* messages_sim_f = NULL; uint8_t* messages_sim_s = NULL; @@ -178,29 +181,68 @@ int main(int argc, char** argv) int16_t* symbols_s = NULL; // unrm_symbols int8_t* symbols_c = NULL; // unrm_symbols + // LDPC encoder + srsran_ldpc_encoder_t encoder = {}; + + // LDPC decoder (8 bit) + srsran_ldpc_decoder_t decoder_c = {}; + // LDPC decoder (8 bit, flooded) + srsran_ldpc_decoder_t decoder_c_flood = {}; + // LDPC decoder (16 bit) + srsran_ldpc_decoder_t decoder_s = {}; + // LDPC decoder (float) + srsran_ldpc_decoder_t decoder_f = {}; + +#ifdef LV_HAVE_AVX2 + // LDPC decoder (8 bit, AVX2 version) + srsran_ldpc_decoder_t decoder_avx = {}; + // LDPC decoder (8 bit, flooded scheduling, AVX2 version) + srsran_ldpc_decoder_t decoder_avx_flood = {}; + +#endif + +#ifdef LV_HAVE_AVX512 + // LDPC decoder (8 bit, AVX512 version) + srsran_ldpc_decoder_t decoder_avx512 = {}; + // LDPC decoder (8 bit, flooded scheduling, AVX512 version) + srsran_ldpc_decoder_t decoder_avx512_flood = {}; +#endif + + // LDPC rate Matcher + srsran_ldpc_rm_t rm_tx = {}; + // LDPC rate DeMatcher + srsran_ldpc_rm_t rm_rx = {}; + // LDPC rate DeMatcher (int16_t) + srsran_ldpc_rm_t rm_rx_s = {}; + // LDPC rate DeMatcher (int8_t) + srsran_ldpc_rm_t rm_rx_c = {}; + + // Create a random generator + srsran_random_t random_gen = NULL; + random_gen = srsran_random_init(0); + int i = 0; int j = 0; - parse_args(argc, argv); - - // create an LDPC encoder - srsran_ldpc_encoder_t encoder; + if (parse_args(argc, argv) < SRSRAN_SUCCESS) { + goto clean_exit; + } #ifdef LV_HAVE_AVX512 if (srsran_ldpc_encoder_init(&encoder, SRSRAN_LDPC_ENCODER_AVX512, base_graph, lift_size) != 0) { perror("encoder init"); - exit(-1); + goto clean_exit; } #else // no AVX512 #ifdef LV_HAVE_AVX2 if (srsran_ldpc_encoder_init(&encoder, SRSRAN_LDPC_ENCODER_AVX2, base_graph, lift_size) != 0) { perror("encoder init"); - exit(-1); + goto clean_exit; } -#else // no AVX2 +#else // no AVX2 if (srsran_ldpc_encoder_init(&encoder, SRSRAN_LDPC_ENCODER_C, base_graph, lift_size) != 0) { perror("encoder init"); - exit(-1); + goto clean_exit; } #endif // LV_HAVE_AVX2 #endif // LV_HAVE_AVX512 @@ -215,32 +257,28 @@ int main(int argc, char** argv) Nref = finalN; } - // create a LDPC rate Matcher - srsran_ldpc_rm_t rm_tx; + // Init the LDPC rate Matcher if (srsran_ldpc_rm_tx_init(&rm_tx) != 0) { perror("rate matcher init"); - exit(-1); + goto clean_exit; } - // create a LDPC rate DeMatcher - srsran_ldpc_rm_t rm_rx; + // Init LDPC rate DeMatcher if (srsran_ldpc_rm_rx_init_f(&rm_rx) != 0) { perror("rate dematcher init"); - exit(-1); + goto clean_exit; } - // create a LDPC rate DeMatcher (int16_t) - srsran_ldpc_rm_t rm_rx_s; + // Init LDPC rate DeMatcher (int16_t) if (srsran_ldpc_rm_rx_init_s(&rm_rx_s) != 0) { perror("rate dematcher init (int16_t)"); - exit(-1); + goto clean_exit; } - // create a LDPC rate DeMatcher (int8_t) - srsran_ldpc_rm_t rm_rx_c; + // Init LDPC rate DeMatcher (int8_t) if (srsran_ldpc_rm_rx_init_c(&rm_rx_c) != 0) { perror("rate dematcher init (int8_t)"); - exit(-1); + goto clean_exit; } // Create LDPC configuration arguments @@ -249,73 +287,62 @@ int main(int argc, char** argv) decoder_args.ls = lift_size; decoder_args.scaling_fctr = MS_SF; - // create an LDPC decoder (float) - srsran_ldpc_decoder_t decoder_f; + // Init the LDPC decoder (float) decoder_args.type = SRSRAN_LDPC_DECODER_F; if (srsran_ldpc_decoder_init(&decoder_f, &decoder_args) != 0) { perror("decoder init"); - exit(-1); + goto clean_exit; } - // create an LDPC decoder (16 bit) - srsran_ldpc_decoder_t decoder_s; + // Init the LDPC decoder (16 bit) decoder_args.type = SRSRAN_LDPC_DECODER_S; if (srsran_ldpc_decoder_init(&decoder_s, &decoder_args) != 0) { perror("decoder init (int16_t)"); - exit(-1); + goto clean_exit; } - // create an LDPC decoder (8 bit) - srsran_ldpc_decoder_t decoder_c; + // Init the LDPC decoder (8 bit) decoder_args.type = SRSRAN_LDPC_DECODER_C; if (srsran_ldpc_decoder_init(&decoder_c, &decoder_args) != 0) { perror("decoder init (int8_t)"); - exit(-1); + goto clean_exit; } - // create an LDPC decoder (8 bit, flooded) - srsran_ldpc_decoder_t decoder_c_flood; + // Init the LDPC decoder (8 bit, flooded) decoder_args.type = SRSRAN_LDPC_DECODER_C_FLOOD; if (srsran_ldpc_decoder_init(&decoder_c_flood, &decoder_args) != 0) { perror("decoder init"); - exit(-1); + goto clean_exit; } #ifdef LV_HAVE_AVX2 - // create an LDPC decoder (8 bit, AVX2 version) - srsran_ldpc_decoder_t decoder_avx; + // Init the LDPC decoder (8 bit, AVX2 version) decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX2; if (srsran_ldpc_decoder_init(&decoder_avx, &decoder_args) != 0) { perror("decoder init"); - exit(-1); + goto clean_exit; } - // create an LDPC decoder (8 bit, flooded scheduling, AVX2 version) - srsran_ldpc_decoder_t decoder_avx_flood; + // Init the LDPC decoder (8 bit, flooded scheduling, AVX2 version) decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX2_FLOOD; if (srsran_ldpc_decoder_init(&decoder_avx_flood, &decoder_args) != 0) { perror("decoder init"); - exit(-1); + goto clean_exit; } #endif // LV_HAVE_AVX2 #ifdef LV_HAVE_AVX512 - // create an LDPC decoder (8 bit, AVX2 version) - srsran_ldpc_decoder_t decoder_avx512; + // Init the LDPC decoder (8 bit, AVX512 version) decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX512; if (srsran_ldpc_decoder_init(&decoder_avx512, &decoder_args) != 0) { perror("decoder init"); - exit(-1); + goto clean_exit; } - // create an LDPC decoder (8 bit, flooded scheduling, AVX512 version) - srsran_ldpc_decoder_t decoder_avx512_flood; + // Init LDPC decoder (8 bit, flooded scheduling, AVX512 version) decoder_args.type = SRSRAN_LDPC_DECODER_C_AVX512_FLOOD; if (srsran_ldpc_decoder_init(&decoder_avx512_flood, &decoder_args) != 0) { perror("decoder init"); - exit(-1); + goto clean_exit; } #endif // LV_HAVE_AVX512 - // create a random generator - srsran_random_t random_gen = srsran_random_init(0); - printf("Test LDPC chain:\n"); printf(" Base Graph -> BG%d\n", encoder.bg + 1); printf(" Lifting Size -> %d\n", encoder.ls); @@ -361,7 +388,7 @@ int main(int argc, char** argv) !messages_sim_avx512_flood || !codewords || !rm_codewords || !rm_symbols || !rm_symbols_s || !rm_symbols_c || !symbols || !symbols_s || !symbols_c) { perror("malloc"); - exit(-1); + goto clean_exit; } int i_bit = 0; @@ -391,6 +418,7 @@ int main(int argc, char** argv) int n_error_words_avx512_flood = 0; #endif // LV_HAVE_AVX512 + float noise_var = srsran_convert_dB_to_power(-snr); float noise_std_dev = srsran_convert_dB_to_amplitude(-snr); int16_t inf15 = (1U << 14U) - 1; @@ -460,12 +488,12 @@ int main(int argc, char** argv) } // Apply AWGN - srsran_ch_awgn_f(rm_symbols, rm_symbols, noise_std_dev, batch_size * rm_length); + srsran_ch_awgn_f(rm_symbols, rm_symbols, noise_var, batch_size * rm_length); // Convert symbols into LLRs for (i = 0; i < batch_size; i++) { for (j = 0; j < rm_length; j++) { - rm_symbols[i * rm_length + j] = rm_symbols[i * rm_length + j] * 2 / (noise_std_dev * noise_std_dev); + rm_symbols[i * rm_length + j] = rm_symbols[i * rm_length + j] * 2 / noise_var; } } @@ -488,7 +516,7 @@ int main(int argc, char** argv) rv, mod_type, Nref)) { - exit(-1); + goto clean_exit; } } @@ -528,7 +556,7 @@ int main(int argc, char** argv) rv, mod_type, Nref)) { - exit(-1); + goto clean_exit; } } @@ -568,7 +596,7 @@ int main(int argc, char** argv) rv, mod_type, Nref) < 0) { - exit(-1); + goto clean_exit; } } @@ -711,63 +739,120 @@ int main(int argc, char** argv) i_batch * batch_size * finalK / elapsed_time_enc, i_batch * batch_size * finalN / elapsed_time_enc); - print_decoder("FLOATING POINT", i_batch, n_error_words_f, elapsed_time_dec_f); - print_decoder("FIXED POINT (16 bits)", i_batch, n_error_words_s, elapsed_time_dec_s); - print_decoder("FIXED POINT (8 bits)", i_batch, n_error_words_c, elapsed_time_dec_c); - print_decoder("FIXED POINT (8 bits, flooded scheduling)", i_batch, n_error_words_c_flood, elapsed_time_dec_c_flood); + if (print_decoder("FLOATING POINT", i_batch, n_error_words_f, elapsed_time_dec_f) < SRSRAN_SUCCESS) { + goto clean_exit; + } + if (print_decoder("FIXED POINT (16 bits)", i_batch, n_error_words_s, elapsed_time_dec_s) < SRSRAN_SUCCESS) { + goto clean_exit; + } + if (print_decoder("FIXED POINT (8 bits)", i_batch, n_error_words_c, elapsed_time_dec_c) < SRSRAN_SUCCESS) { + goto clean_exit; + } + if (print_decoder( + "FIXED POINT (8 bits, flooded scheduling)", i_batch, n_error_words_c_flood, elapsed_time_dec_c_flood) < + SRSRAN_SUCCESS) { + goto clean_exit; + } #ifdef LV_HAVE_AVX2 - print_decoder("FIXED POINT (8 bits - AVX2)", i_batch, n_error_words_avx, elapsed_time_dec_avx); - print_decoder( - "FIXED POINT (8 bits, flooded scheduling - AVX2)", i_batch, n_error_words_avx_flood, elapsed_time_dec_avx_flood); + if (print_decoder("FIXED POINT (8 bits - AVX2)", i_batch, n_error_words_avx, elapsed_time_dec_avx) < SRSRAN_SUCCESS) { + goto clean_exit; + } + if (print_decoder("FIXED POINT (8 bits, flooded scheduling - AVX2)", + i_batch, + n_error_words_avx_flood, + elapsed_time_dec_avx_flood) < SRSRAN_SUCCESS) { + goto clean_exit; + } #endif // LV_HAVE_AVX2 #ifdef LV_HAVE_AVX512 - print_decoder("FIXED POINT (8 bits - AVX512)", i_batch, n_error_words_avx512, elapsed_time_dec_avx512); - print_decoder("FIXED POINT (8 bits, flooded scheduling - AVX512)", - i_batch, - n_error_words_avx512_flood, - elapsed_time_dec_avx512_flood); + if (print_decoder("FIXED POINT (8 bits - AVX512)", i_batch, n_error_words_avx512, elapsed_time_dec_avx512) < + SRSRAN_SUCCESS) { + goto clean_exit; + } + if (print_decoder("FIXED POINT (8 bits, flooded scheduling - AVX512)", + i_batch, + n_error_words_avx512_flood, + elapsed_time_dec_avx512_flood) < SRSRAN_SUCCESS) { + goto clean_exit; + } #endif // LV_HAVE_AVX512 if (n_error_words_s > 10 * n_error_words_f) { perror("16-bit performance too low!"); - exit(-1); + goto clean_exit; } if (n_error_words_c > 10 * n_error_words_f) { perror("8-bit performance too low!"); - exit(-1); + goto clean_exit; } #ifdef LV_HAVE_AVX512 if (n_error_words_avx512 != n_error_words_avx) { perror("The number of errors AVX512 and AVX2 differs !"); - exit(-1); + goto clean_exit; } if (n_error_words_avx512_flood != n_error_words_avx_flood) { perror("The number of errors of flooded AVX512 and AVX2 differs !"); - exit(-1); + goto clean_exit; } #endif // LV_HAVE_AVX512 printf("\nTest completed successfully!\n\n"); + ret = SRSRAN_SUCCESS; - free(symbols); - free(symbols_s); - free(symbols_c); - free(rm_symbols); - free(rm_symbols_s); - free(rm_symbols_c); - free(rm_codewords); - free(codewords); - free(messages_sim_avx); - free(messages_sim_avx_flood); - free(messages_sim_avx512); - free(messages_sim_avx512_flood); - free(messages_sim_c_flood); - free(messages_sim_c); - free(messages_sim_s); - free(messages_sim_f); - free(messages_true); +clean_exit: + if (symbols != NULL) { + free(symbols); + } + if (symbols_s != NULL) { + free(symbols_s); + } + if (symbols_c != NULL) { + free(symbols_c); + } + if (rm_symbols != NULL) { + free(rm_symbols); + } + if (rm_symbols_s != NULL) { + free(rm_symbols_s); + } + if (rm_symbols_c != NULL) { + free(rm_symbols_c); + } + if (rm_codewords != NULL) { + free(rm_codewords); + } + if (codewords != NULL) { + free(codewords); + } + if (messages_sim_avx != NULL) { + free(messages_sim_avx); + } + if (messages_sim_avx_flood != NULL) { + free(messages_sim_avx_flood); + } + if (messages_sim_avx512 != NULL) { + free(messages_sim_avx512); + } + if (messages_sim_avx512_flood != NULL) { + free(messages_sim_avx512_flood); + } + if (messages_sim_c_flood != NULL) { + free(messages_sim_c_flood); + } + if (messages_sim_c != NULL) { + free(messages_sim_c); + } + if (messages_sim_s != NULL) { + free(messages_sim_s); + } + if (messages_sim_f != NULL) { + free(messages_sim_f); + } + if (messages_true != NULL) { + free(messages_true); + } srsran_random_free(random_gen); #ifdef LV_HAVE_AVX2 srsran_ldpc_decoder_free(&decoder_avx); @@ -786,15 +871,23 @@ int main(int argc, char** argv) srsran_ldpc_rm_rx_free_f(&rm_rx); srsran_ldpc_rm_rx_free_s(&rm_rx_s); srsran_ldpc_rm_rx_free_c(&rm_rx_c); + + return ret; } -void print_decoder(char* title, int n_batches, int n_errors, double elapsed_time) +int print_decoder(char* title, int n_batches, int n_errors, double elapsed_time) { printf("\n**** %s ****", title); - printf("\nEstimated word error rate:\n %e (%d errors)\n", (double)n_errors / n_batches / batch_size, n_errors); + if (!isnormal(elapsed_time)) { + printf("\nError: elapsed time is not a valid number\n"); + return SRSRAN_ERROR; + } else { + printf("\nEstimated word error rate:\n %e (%d errors)\n", (double)n_errors / n_batches / batch_size, n_errors); - printf("Estimated throughput decoder:\n %e word/s\n %e bit/s (information)\n %e bit/s (encoded)\n", - n_batches * batch_size / elapsed_time, - n_batches * batch_size * finalK / elapsed_time, - n_batches * batch_size * finalN / elapsed_time); + printf("Estimated throughput decoder:\n %e word/s\n %e bit/s (information)\n %e bit/s (encoded)\n", + n_batches * batch_size / elapsed_time, + n_batches * batch_size * finalK / elapsed_time, + n_batches * batch_size * finalN / elapsed_time); + return SRSRAN_SUCCESS; + } } diff --git a/lib/src/phy/fec/ldpc/test/ldpc_rm_test.c b/lib/src/phy/fec/ldpc/test/ldpc_rm_test.c index f528d4da7..20a86df6b 100644 --- a/lib/src/phy/fec/ldpc/test/ldpc_rm_test.c +++ b/lib/src/phy/fec/ldpc/test/ldpc_rm_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/CMakeLists.txt b/lib/src/phy/fec/polar/CMakeLists.txt index 26842c863..e157f1f9d 100644 --- a/lib/src/phy/fec/polar/CMakeLists.txt +++ b/lib/src/phy/fec/polar/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/polar/polar_chanalloc.c b/lib/src/phy/fec/polar/polar_chanalloc.c index 31220d14b..f7282e9f3 100644 --- a/lib/src/phy/fec/polar/polar_chanalloc.c +++ b/lib/src/phy/fec/polar/polar_chanalloc.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -91,7 +91,7 @@ void srsran_polar_chanalloc_rx(const uint8_t* output_decoder, uint16_t i_o = 0; uint16_t iPC = 0; uint16_t iK = 0; - for (uint16_t iKPC = 0; iKPC < K + (uint16_t)nPC; iKPC++) { + for (uint16_t iKPC = 0; iKPC < (uint16_t)(K + nPC); iKPC++) { i_o = K_set[iKPC]; // includes parity bits if (i_o == PC_set[iPC]) { // skip iPC = iPC + 1; diff --git a/lib/src/phy/fec/polar/polar_code.c b/lib/src/phy/fec/polar/polar_code.c index b2e7c515e..545e9573d 100644 --- a/lib/src/phy/fec/polar/polar_code.c +++ b/lib/src/phy/fec/polar/polar_code.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -124,7 +124,7 @@ int get_code_params(srsran_polar_code_t* c, const uint16_t K, const uint16_t E, // number of parity check bits (nPC) and parity check bits of minimum bandwidth nWmPC uint8_t nPC = 0; uint8_t nWmPC = 0; - if (K >= 18 && K <= 25) { + if (K <= 25) { nPC = 3; if (E > K + 189) { nWmPC = 1; diff --git a/lib/src/phy/fec/polar/polar_decoder.c b/lib/src/phy/fec/polar/polar_decoder.c index 2e5f66d59..dccca73b0 100644 --- a/lib/src/phy/fec/polar/polar_decoder.c +++ b/lib/src/phy/fec/polar/polar_decoder.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_all.c b/lib/src/phy/fec/polar/polar_decoder_ssc_all.c index 5922028d8..85eaf5dd1 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_all.c +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_all.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -140,7 +140,7 @@ int compute_node_type(void* p, } uint16_t code_stage_size = 0; uint16_t code_size_log_s = 0; - for (s = 1; s < code_size_log + 1; s++) { + for (s = 1; s < (uint8_t)(code_size_log + 1); s++) { code_size_log_s = code_size_log - s; code_stage_size = (1U << code_size_log_s); for (uint16_t j = 0; j < code_stage_size; j++) { diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_all.h b/lib/src/phy/fec/polar/polar_decoder_ssc_all.h index df875b222..d4f5e1e07 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_all.h +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_all.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_c.c b/lib/src/phy/fec/polar/polar_decoder_ssc_c.c index 952309733..ad65b2153 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_c.c +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_c.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_c.h b/lib/src/phy/fec/polar/polar_decoder_ssc_c.h index 8b3d79b2b..ac95efcd6 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_c.h +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_c.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_c_avx2.c b/lib/src/phy/fec/polar/polar_decoder_ssc_c_avx2.c index 0a1baabca..feb48e72f 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_c_avx2.c +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_c_avx2.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_c_avx2.h b/lib/src/phy/fec/polar/polar_decoder_ssc_c_avx2.h index 509ebc85f..682b64a9a 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_c_avx2.h +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_c_avx2.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_f.c b/lib/src/phy/fec/polar/polar_decoder_ssc_f.c index 348e39aea..9975b6ee9 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_f.c +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_f.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_f.h b/lib/src/phy/fec/polar/polar_decoder_ssc_f.h index 5beba9cf7..0cf824234 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_f.h +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_f.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_s.c b/lib/src/phy/fec/polar/polar_decoder_ssc_s.c index 0706bb143..bf1b7eaca 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_s.c +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_s.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_ssc_s.h b/lib/src/phy/fec/polar/polar_decoder_ssc_s.h index cb87ef41b..65cc4ad73 100644 --- a/lib/src/phy/fec/polar/polar_decoder_ssc_s.h +++ b/lib/src/phy/fec/polar/polar_decoder_ssc_s.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_vector.c b/lib/src/phy/fec/polar/polar_decoder_vector.c index 99058b950..32067ca4b 100644 --- a/lib/src/phy/fec/polar/polar_decoder_vector.c +++ b/lib/src/phy/fec/polar/polar_decoder_vector.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_vector.h b/lib/src/phy/fec/polar/polar_decoder_vector.h index 88bd59a93..30e9b0548 100644 --- a/lib/src/phy/fec/polar/polar_decoder_vector.h +++ b/lib/src/phy/fec/polar/polar_decoder_vector.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_vector_avx2.c b/lib/src/phy/fec/polar/polar_decoder_vector_avx2.c index 48d6c9baf..58752d453 100644 --- a/lib/src/phy/fec/polar/polar_decoder_vector_avx2.c +++ b/lib/src/phy/fec/polar/polar_decoder_vector_avx2.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_decoder_vector_avx2.h b/lib/src/phy/fec/polar/polar_decoder_vector_avx2.h index 1e2bd55ff..d7a2a2fab 100644 --- a/lib/src/phy/fec/polar/polar_decoder_vector_avx2.h +++ b/lib/src/phy/fec/polar/polar_decoder_vector_avx2.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_encoder.c b/lib/src/phy/fec/polar/polar_encoder.c index f6b96197f..bf130c9af 100644 --- a/lib/src/phy/fec/polar/polar_encoder.c +++ b/lib/src/phy/fec/polar/polar_encoder.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_encoder_avx2.c b/lib/src/phy/fec/polar/polar_encoder_avx2.c index 11b7c8ed5..29b8e84e7 100644 --- a/lib/src/phy/fec/polar/polar_encoder_avx2.c +++ b/lib/src/phy/fec/polar/polar_encoder_avx2.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_encoder_avx2.h b/lib/src/phy/fec/polar/polar_encoder_avx2.h index 163153125..450b6dacb 100644 --- a/lib/src/phy/fec/polar/polar_encoder_avx2.h +++ b/lib/src/phy/fec/polar/polar_encoder_avx2.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_encoder_pipelined.c b/lib/src/phy/fec/polar/polar_encoder_pipelined.c index 5b3c9bca1..570e91398 100644 --- a/lib/src/phy/fec/polar/polar_encoder_pipelined.c +++ b/lib/src/phy/fec/polar/polar_encoder_pipelined.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_encoder_pipelined.h b/lib/src/phy/fec/polar/polar_encoder_pipelined.h index 2bef6d396..0f3a05739 100644 --- a/lib/src/phy/fec/polar/polar_encoder_pipelined.h +++ b/lib/src/phy/fec/polar/polar_encoder_pipelined.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_interleaver.c b/lib/src/phy/fec/polar/polar_interleaver.c index 116a40df9..eac6b66b2 100644 --- a/lib/src/phy/fec/polar/polar_interleaver.c +++ b/lib/src/phy/fec/polar/polar_interleaver.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/polar_rm.c b/lib/src/phy/fec/polar/polar_rm.c index 521efb66e..982e172de 100644 --- a/lib/src/phy/fec/polar/polar_rm.c +++ b/lib/src/phy/fec/polar/polar_rm.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -389,7 +389,6 @@ int srsran_polar_rm_tx_init(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_f(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -414,7 +413,6 @@ int srsran_polar_rm_rx_init_f(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_s(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -439,7 +437,6 @@ int srsran_polar_rm_rx_init_s(srsran_polar_rm_t* p) int srsran_polar_rm_rx_init_c(srsran_polar_rm_t* p) { - if (p == NULL) { return -1; } @@ -464,41 +461,74 @@ int srsran_polar_rm_rx_init_c(srsran_polar_rm_t* p) void srsran_polar_rm_tx_free(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_tx* qq = q->ptr; - free(qq->y_e); - free(qq); + if (q == NULL) { + return; } + + struct pRM_tx* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { + free(qq->y_e); + } + + free(qq); } void srsran_polar_rm_rx_free_f(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_f* qq = q->ptr; - free(qq->y_e); - // free(qq->indices); - free(qq); + if (q == NULL) { + return; } + + struct pRM_rx_f* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { + free(qq->y_e); + } + + free(qq); } void srsran_polar_rm_rx_free_s(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_s* qq = q->ptr; - free(qq->y_e); - // free(qq->indices); - free(qq); + if (q == NULL) { + return; } + + struct pRM_rx_s* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { + free(qq->y_e); + } + + free(qq); } void srsran_polar_rm_rx_free_c(srsran_polar_rm_t* q) { - if (q != NULL) { - struct pRM_rx_c* qq = q->ptr; - free(qq->y_e); - // free(qq->indices); - free(qq); + if (q == NULL) { + return; } + + struct pRM_rx_c* qq = q->ptr; + if (qq == NULL) { + return; + } + + if (qq->y_e) { + free(qq->y_e); + } + + free(qq); } int srsran_polar_rm_tx(srsran_polar_rm_t* q, @@ -538,7 +568,6 @@ int srsran_polar_rm_rx_f(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_f* pp = q->ptr; float* y = NULL; float* e = pp->e; // length E @@ -566,7 +595,6 @@ int srsran_polar_rm_rx_s(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_s* pp = q->ptr; int16_t* y = NULL; int16_t* e = pp->e; @@ -594,7 +622,6 @@ int srsran_polar_rm_rx_c(srsran_polar_rm_t* q, const uint32_t K, const uint8_t ibil) { - struct pRM_rx_c* pp = q->ptr; int8_t* y = NULL; int8_t* e = pp->e; diff --git a/lib/src/phy/fec/polar/test/CMakeLists.txt b/lib/src/phy/fec/polar/test/CMakeLists.txt index f9a834161..8f5a6c115 100644 --- a/lib/src/phy/fec/polar/test/CMakeLists.txt +++ b/lib/src/phy/fec/polar/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -40,7 +40,7 @@ function(polar_tests_lite) list(GET listE ${num} eval) list(GET listK ${num} kval) list(GET listI ${num} ival) - add_nr_test(NAME ${test_name}-s${S}-n${nval}-e${eval}-k${kval}-i${ival} + add_nr_advanced_test(NAME ${test_name}-s${S}-n${nval}-e${eval}-k${kval}-i${ival} COMMAND ${test_command} -s${S} -n${nval} -e${eval} -k${kval} -i${ival} ) endforeach() @@ -54,7 +54,7 @@ function(polar_tests) foreach(Kval RANGE 36 164 32) math(EXPR Emin "${Kval} + 1") foreach(Eval RANGE ${Emin} 8192 128) - add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i0 + add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i0 COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i0 ) endforeach() @@ -66,7 +66,7 @@ function(polar_tests) foreach(Kval RANGE 18 25) math(EXPR Emin "${Kval} + 3 + 1") foreach(Eval RANGE ${Emin} 8192 128) - add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1 + add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1 COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i1 ) endforeach() @@ -75,7 +75,7 @@ function(polar_tests) foreach(Kval RANGE 32 1023 32) math(EXPR Emin "${Kval} + 1") foreach(Eval RANGE ${Emin} 8192 128) - add_nr_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1 + add_nr_advanced_test(NAME ${test_name}-s${S}-k${Kval}-e${Eval}-n${nval}-i1 COMMAND ${test_command} -s${S} -k${Kval} -e${Eval} -n${nval} -i1 ) endforeach() diff --git a/lib/src/phy/fec/polar/test/polar_chain_test.c b/lib/src/phy/fec/polar/test/polar_chain_test.c index a0d9b8fc7..bf8710dec 100644 --- a/lib/src/phy/fec/polar/test/polar_chain_test.c +++ b/lib/src/phy/fec/polar/test/polar_chain_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -193,9 +193,9 @@ int main(int argc, char** argv) int j = 0; int snr_points = 0; - int errors_symb = 0; - int errors_symb_s = 0; - int errors_symb_c = 0; + int errors_symb = 0; + int errors_symb_s = 0; + int errors_symb_c = 0; #ifdef LV_HAVE_AVX2 int errors_symb_c_avx2 = 0; #endif @@ -217,12 +217,12 @@ int main(int argc, char** argv) double elapsed_time_enc_avx2[SNR_POINTS + 1]; // 16-bit quantizer - int16_t inf16 = (1U << 15U) - 1; - int8_t inf8 = (1U << 7U) - 1; - float gain_s = NAN; - float gain_c = NAN; + int16_t inf16 = (1U << 15U) - 1; + int8_t inf8 = (1U << 7U) - 1; + float gain_s = NAN; + float gain_c = NAN; #ifdef LV_HAVE_AVX2 - float gain_c_avx2 = NAN; + float gain_c_avx2 = NAN; #endif srsran_polar_code_t code; @@ -328,13 +328,13 @@ int main(int argc, char** argv) for (int i = 0; i < snr_points; i++) { snr_db = SNR_MIN + i * snr_inc; snr_db_vec[i] = snr_db; - var[i] = srsran_convert_dB_to_amplitude(-snr_db); + var[i] = srsran_convert_dB_to_power(-snr_db); } snr_db_vec[snr_points] = 101; // include the no noise case snr_points++; } else { snr_db_vec[0] = snr_db; - var[0] = srsran_convert_dB_to_amplitude(-snr_db); + var[0] = srsran_convert_dB_to_power(-snr_db); snr_points = 1; } diff --git a/lib/src/phy/fec/polar/test/polar_interleaver_gold.h b/lib/src/phy/fec/polar/test/polar_interleaver_gold.h index 3eddee7ff..b15697e5f 100644 --- a/lib/src/phy/fec/polar/test/polar_interleaver_gold.h +++ b/lib/src/phy/fec/polar/test/polar_interleaver_gold.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/test/polar_interleaver_test.c b/lib/src/phy/fec/polar/test/polar_interleaver_test.c index eb11096af..725c39d42 100644 --- a/lib/src/phy/fec/polar/test/polar_interleaver_test.c +++ b/lib/src/phy/fec/polar/test/polar_interleaver_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,8 +20,8 @@ */ #include "polar_interleaver_gold.h" -#include "srsran/common/test_common.h" #include "srsran/phy/fec/polar/polar_interleaver.h" +#include "srsran/support/srsran_test.h" int main(int argc, char** argv) { diff --git a/lib/src/phy/fec/polar/test/polar_sets.c b/lib/src/phy/fec/polar/test/polar_sets.c index fb9bdedd7..8f445b2c3 100644 --- a/lib/src/phy/fec/polar/test/polar_sets.c +++ b/lib/src/phy/fec/polar/test/polar_sets.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/test/polar_sets.h b/lib/src/phy/fec/polar/test/polar_sets.h index ccdbc8c4b..31e1f1414 100644 --- a/lib/src/phy/fec/polar/test/polar_sets.h +++ b/lib/src/phy/fec/polar/test/polar_sets.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/test/subchannel_allocation.c b/lib/src/phy/fec/polar/test/subchannel_allocation.c index b82c9ae2e..099baa60f 100644 --- a/lib/src/phy/fec/polar/test/subchannel_allocation.c +++ b/lib/src/phy/fec/polar/test/subchannel_allocation.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/polar/test/subchannel_allocation.h b/lib/src/phy/fec/polar/test/subchannel_allocation.h index efac019b3..d9e057cd5 100644 --- a/lib/src/phy/fec/polar/test/subchannel_allocation.h +++ b/lib/src/phy/fec/polar/test/subchannel_allocation.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/softbuffer.c b/lib/src/phy/fec/softbuffer.c index 0744b57d4..b41b5acf6 100644 --- a/lib/src/phy/fec/softbuffer.c +++ b/lib/src/phy/fec/softbuffer.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -168,6 +168,15 @@ void srsran_softbuffer_rx_reset_cb(srsran_softbuffer_rx_t* q, uint32_t nof_cb) q->tb_crc = false; } +void srsran_softbuffer_rx_reset_cb_crc(srsran_softbuffer_rx_t* q, uint32_t nof_cb) +{ + if (q == NULL || nof_cb == 0) { + return; + } + + SRSRAN_MEM_ZERO(q->cb_crc, bool, SRSRAN_MIN(q->max_cb, nof_cb)); +} + int srsran_softbuffer_tx_init(srsran_softbuffer_tx_t* q, uint32_t nof_prb) { int ret = srsran_ra_tbs_from_idx(SRSRAN_RA_NOF_TBS_IDX - 1, nof_prb); diff --git a/lib/src/phy/fec/test/CMakeLists.txt b/lib/src/phy/fec/test/CMakeLists.txt index 65e826264..da3a13058 100644 --- a/lib/src/phy/fec/test/CMakeLists.txt +++ b/lib/src/phy/fec/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/test/crc_test.c b/lib/src/phy/fec/test/crc_test.c index ecd1f153c..27d5fd166 100644 --- a/lib/src/phy/fec/test/crc_test.c +++ b/lib/src/phy/fec/test/crc_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -62,7 +62,7 @@ void parse_args(int argc, char** argv) seed = (uint32_t)strtoul(argv[optind], NULL, 0); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -96,7 +96,7 @@ int main(int argc, char** argv) data[i] = rand() % 2; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { INFO("data="); srsran_vec_fprint_b(stdout, data, num_bits); } diff --git a/lib/src/phy/fec/test/crc_test.h b/lib/src/phy/fec/test/crc_test.h index 1f5ee48fc..09627b766 100644 --- a/lib/src/phy/fec/test/crc_test.h +++ b/lib/src/phy/fec/test/crc_test.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/CMakeLists.txt b/lib/src/phy/fec/turbo/CMakeLists.txt index 6f295dbda..7b556f93b 100644 --- a/lib/src/phy/fec/turbo/CMakeLists.txt +++ b/lib/src/phy/fec/turbo/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/turbo/rm_conv.c b/lib/src/phy/fec/turbo/rm_conv.c index d274c4a49..27060af9d 100644 --- a/lib/src/phy/fec/turbo/rm_conv.c +++ b/lib/src/phy/fec/turbo/rm_conv.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/rm_turbo.c b/lib/src/phy/fec/turbo/rm_turbo.c index 3a7ba9f10..02348bdb5 100644 --- a/lib/src/phy/fec/turbo/rm_turbo.c +++ b/lib/src/phy/fec/turbo/rm_turbo.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/tc_interl_lte.c b/lib/src/phy/fec/turbo/tc_interl_lte.c index 7c0b03d1c..643e93f9f 100644 --- a/lib/src/phy/fec/turbo/tc_interl_lte.c +++ b/lib/src/phy/fec/turbo/tc_interl_lte.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/tc_interl_umts.c b/lib/src/phy/fec/turbo/tc_interl_umts.c index 571b6efdc..35b3f4c43 100644 --- a/lib/src/phy/fec/turbo/tc_interl_umts.c +++ b/lib/src/phy/fec/turbo/tc_interl_umts.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/test/CMakeLists.txt b/lib/src/phy/fec/turbo/test/CMakeLists.txt index e55c82505..fc89e5f7e 100644 --- a/lib/src/phy/fec/turbo/test/CMakeLists.txt +++ b/lib/src/phy/fec/turbo/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/fec/turbo/test/rm_conv_test.c b/lib/src/phy/fec/turbo/test/rm_conv_test.c index 4fec942a2..d3ef78c82 100644 --- a/lib/src/phy/fec/turbo/test/rm_conv_test.c +++ b/lib/src/phy/fec/turbo/test/rm_conv_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/test/rm_turbo_test.c b/lib/src/phy/fec/turbo/test/rm_turbo_test.c index f1f85b5f3..930c68bd3 100644 --- a/lib/src/phy/fec/turbo/test/rm_turbo_test.c +++ b/lib/src/phy/fec/turbo/test/rm_turbo_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/test/turbocoder_test.c b/lib/src/phy/fec/turbo/test/turbocoder_test.c index cf60cbe04..fa1bf5015 100644 --- a/lib/src/phy/fec/turbo/test/turbocoder_test.c +++ b/lib/src/phy/fec/turbo/test/turbocoder_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -44,7 +44,7 @@ void parse_args(int argc, char** argv) long_cb = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/fec/turbo/test/turbodecoder_test.c b/lib/src/phy/fec/turbo/test/turbodecoder_test.c index c642822d8..423cb0012 100644 --- a/lib/src/phy/fec/turbo/test/turbodecoder_test.c +++ b/lib/src/phy/fec/turbo/test/turbodecoder_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -103,7 +103,7 @@ void parse_args(int argc, char** argv) seed = (uint32_t)strtoul(argv[optind], NULL, 0); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -215,11 +215,11 @@ int main(int argc, char** argv) for (uint32_t i = 0; i < snr_points; i++) { ebno_db = SNR_MIN + i * ebno_inc; esno_db = ebno_db + srsran_convert_power_to_dB(1.0f / 3.0f); - var[i] = srsran_convert_dB_to_amplitude(-esno_db); + var[i] = srsran_convert_dB_to_power(-esno_db); } } else { esno_db = ebno_db + srsran_convert_power_to_dB(1.0f / 3.0f); - var[0] = srsran_convert_dB_to_amplitude(-esno_db); + var[0] = srsran_convert_dB_to_power(-esno_db); snr_points = 1; } for (uint32_t i = 0; i < snr_points; i++) { diff --git a/lib/src/phy/fec/turbo/test/turbodecoder_test.h b/lib/src/phy/fec/turbo/test/turbodecoder_test.h index 918b2fa26..21506cfd7 100644 --- a/lib/src/phy/fec/turbo/test/turbodecoder_test.h +++ b/lib/src/phy/fec/turbo/test/turbodecoder_test.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/turbocoder.c b/lib/src/phy/fec/turbo/turbocoder.c index 5a0682643..e2da0b705 100644 --- a/lib/src/phy/fec/turbo/turbocoder.c +++ b/lib/src/phy/fec/turbo/turbocoder.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/turbodecoder.c b/lib/src/phy/fec/turbo/turbodecoder.c index f3e743b97..65a953cb7 100644 --- a/lib/src/phy/fec/turbo/turbodecoder.c +++ b/lib/src/phy/fec/turbo/turbodecoder.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/turbodecoder_gen.c b/lib/src/phy/fec/turbo/turbodecoder_gen.c index 9b42982d9..aa3240f16 100644 --- a/lib/src/phy/fec/turbo/turbodecoder_gen.c +++ b/lib/src/phy/fec/turbo/turbodecoder_gen.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/turbo/turbodecoder_sse.c b/lib/src/phy/fec/turbo/turbodecoder_sse.c index 765b81bd9..1bcd8a8f5 100644 --- a/lib/src/phy/fec/turbo/turbodecoder_sse.c +++ b/lib/src/phy/fec/turbo/turbodecoder_sse.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/utils_avx2.h b/lib/src/phy/fec/utils_avx2.h index 40275c276..a4908158e 100644 --- a/lib/src/phy/fec/utils_avx2.h +++ b/lib/src/phy/fec/utils_avx2.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/fec/utils_avx512.h b/lib/src/phy/fec/utils_avx512.h index b8c6b6948..863a5757e 100644 --- a/lib/src/phy/fec/utils_avx512.h +++ b/lib/src/phy/fec/utils_avx512.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/gnb/CMakeLists.txt b/lib/src/phy/gnb/CMakeLists.txt new file mode 100644 index 000000000..994cb8c40 --- /dev/null +++ b/lib/src/phy/gnb/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +file(GLOB SOURCES "*.c") +add_library(srsran_gnb OBJECT ${SOURCES}) diff --git a/lib/src/phy/gnb/gnb_dl.c b/lib/src/phy/gnb/gnb_dl.c new file mode 100644 index 000000000..393fc6e5d --- /dev/null +++ b/lib/src/phy/gnb/gnb_dl.c @@ -0,0 +1,391 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/gnb/gnb_dl.h" +#include + +static float gnb_dl_get_norm_factor(uint32_t nof_prb) +{ + return 0.05f / sqrtf(nof_prb); +} + +static int gnb_dl_alloc_prb(srsran_gnb_dl_t* q, uint32_t new_nof_prb) +{ + if (q->max_prb < new_nof_prb) { + q->max_prb = new_nof_prb; + + for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { + if (q->sf_symbols[i] != NULL) { + free(q->sf_symbols[i]); + } + + q->sf_symbols[i] = srsran_vec_cf_malloc(SRSRAN_SLOT_LEN_RE_NR(q->max_prb)); + if (q->sf_symbols[i] == NULL) { + ERROR("Malloc"); + return SRSRAN_ERROR; + } + } + } + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_dl_init(srsran_gnb_dl_t* q, cf_t* output[SRSRAN_MAX_PORTS], const srsran_gnb_dl_args_t* args) +{ + if (!q || !output || !args) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (args->nof_tx_antennas == 0) { + ERROR("Error invalid number of antennas (%d)", args->nof_tx_antennas); + return SRSRAN_ERROR; + } + + q->nof_tx_antennas = args->nof_tx_antennas; + + if (srsran_pdsch_nr_init_enb(&q->pdsch, &args->pdsch) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (gnb_dl_alloc_prb(q, args->nof_max_prb) < SRSRAN_SUCCESS) { + ERROR("Error allocating"); + return SRSRAN_ERROR; + } + + // Check symbol size is valid + int symbol_sz = srsran_symbol_sz_from_srate(args->srate_hz, args->scs); + if (symbol_sz <= 0) { + ERROR("Error calculating symbol size from sampling rate of %.2f MHz and subcarrier spacing %s", + q->srate_hz / 1e6, + srsran_subcarrier_spacing_to_str(args->scs)); + return SRSRAN_ERROR; + } + q->symbol_sz = symbol_sz; + + // Create initial OFDM configuration + srsran_ofdm_cfg_t fft_cfg = {}; + fft_cfg.nof_prb = args->nof_max_prb; + fft_cfg.symbol_sz = (uint32_t)symbol_sz; + fft_cfg.keep_dc = true; + + // Initialise a different OFDM modulator per channel + for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { + fft_cfg.in_buffer = q->sf_symbols[i]; + fft_cfg.out_buffer = output[i]; + srsran_ofdm_tx_init_cfg(&q->fft[i], &fft_cfg); + } + + if (srsran_dmrs_sch_init(&q->dmrs, false) < SRSRAN_SUCCESS) { + ERROR("Error DMRS"); + return SRSRAN_ERROR; + } + + if (srsran_pdcch_nr_init_tx(&q->pdcch, &args->pdcch) < SRSRAN_SUCCESS) { + ERROR("Error PDCCH"); + return SRSRAN_ERROR; + } + + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_encode = true; + ssb_args.max_srate_hz = args->srate_hz; + ssb_args.min_scs = args->scs; + if (srsran_ssb_init(&q->ssb, &ssb_args) < SRSRAN_SUCCESS) { + ERROR("Error SSB"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void srsran_gnb_dl_free(srsran_gnb_dl_t* q) +{ + if (q == NULL) { + return; + } + + for (uint32_t i = 0; i < SRSRAN_MAX_PORTS; i++) { + srsran_ofdm_rx_free(&q->fft[i]); + + if (q->sf_symbols[i] != NULL) { + free(q->sf_symbols[i]); + } + } + + srsran_pdsch_nr_free(&q->pdsch); + srsran_dmrs_sch_free(&q->dmrs); + + srsran_pdcch_nr_free(&q->pdcch); + srsran_ssb_free(&q->ssb); + + SRSRAN_MEM_ZERO(q, srsran_gnb_dl_t, 1); +} + +int srsran_gnb_dl_set_carrier(srsran_gnb_dl_t* q, const srsran_carrier_nr_t* carrier) +{ + if (srsran_pdsch_nr_set_carrier(&q->pdsch, carrier) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_dmrs_sch_set_carrier(&q->dmrs, carrier) < SRSRAN_SUCCESS) { + ERROR("Error DMRS"); + return SRSRAN_ERROR; + } + + if (gnb_dl_alloc_prb(q, carrier->nof_prb) < SRSRAN_SUCCESS) { + ERROR("Error allocating"); + return SRSRAN_ERROR; + } + + if (carrier->nof_prb != q->carrier.nof_prb) { + srsran_ofdm_cfg_t fft_cfg = {}; + fft_cfg.nof_prb = carrier->nof_prb; + fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); + fft_cfg.keep_dc = true; + fft_cfg.phase_compensation_hz = carrier->dl_center_frequency_hz; + + for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { + fft_cfg.in_buffer = q->sf_symbols[i]; + srsran_ofdm_tx_init_cfg(&q->fft[i], &fft_cfg); + } + } + + q->carrier = *carrier; + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_dl_set_ssb_config(srsran_gnb_dl_t* q, const srsran_ssb_cfg_t* ssb) +{ + if (q == NULL || ssb == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (srsran_ssb_set_cfg(&q->ssb, ssb) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_dl_add_ssb(srsran_gnb_dl_t* q, const srsran_pbch_msg_nr_t* pbch_msg, uint32_t sf_idx) +{ + if (q == NULL || pbch_msg == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Skip SSB if it is not the time for it + if (!srsran_ssb_send(&q->ssb, sf_idx)) { + return SRSRAN_SUCCESS; + } + + if (srsran_ssb_add(&q->ssb, q->carrier.pci, pbch_msg, q->fft[0].cfg.out_buffer, q->fft[0].cfg.out_buffer) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_dl_set_pdcch_config(srsran_gnb_dl_t* q, + const srsran_pdcch_cfg_nr_t* cfg, + const srsran_dci_cfg_nr_t* dci_cfg) +{ + if (q == NULL || cfg == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + q->pdcch_cfg = *cfg; + + if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, &q->pdcch_cfg.coreset[0]) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_dci_nr_set_cfg(&q->dci, dci_cfg) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void srsran_gnb_dl_gen_signal(srsran_gnb_dl_t* q) +{ + if (q == NULL) { + return; + } + + float norm_factor = gnb_dl_get_norm_factor(q->pdsch.carrier.nof_prb); + + for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { + srsran_ofdm_tx_sf(&q->fft[i]); + + srsran_vec_sc_prod_cfc(q->fft[i].cfg.out_buffer, norm_factor, q->fft[i].cfg.out_buffer, (uint32_t)q->fft[i].sf_sz); + } +} + +float srsran_gnb_dl_get_maximum_signal_power_dBfs(uint32_t nof_prb) +{ + return srsran_convert_amplitude_to_dB(gnb_dl_get_norm_factor(nof_prb)) + + srsran_convert_power_to_dB((float)nof_prb * SRSRAN_NRE) + 3.0f; +} + +int srsran_gnb_dl_base_zero(srsran_gnb_dl_t* q) +{ + if (q == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + for (uint32_t i = 0; i < q->nof_tx_antennas; i++) { + srsran_vec_cf_zero(q->sf_symbols[i], SRSRAN_SLOT_LEN_RE_NR(q->carrier.nof_prb)); + } + + return SRSRAN_SUCCESS; +} + +static int +gnb_dl_pdcch_put_msg(srsran_gnb_dl_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_msg_nr_t* dci_msg) +{ + if (dci_msg->ctx.coreset_id >= SRSRAN_UE_DL_NR_MAX_NOF_CORESET || + !q->pdcch_cfg.coreset_present[dci_msg->ctx.coreset_id]) { + ERROR("Invalid CORESET ID %d", dci_msg->ctx.coreset_id); + return SRSRAN_ERROR; + } + srsran_coreset_t* coreset = &q->pdcch_cfg.coreset[dci_msg->ctx.coreset_id]; + + if (srsran_pdcch_nr_set_carrier(&q->pdcch, &q->carrier, coreset) < SRSRAN_SUCCESS) { + ERROR("Error setting PDCCH carrier/CORESET"); + return SRSRAN_ERROR; + } + + // Put DMRS + if (srsran_dmrs_pdcch_put(&q->carrier, coreset, slot_cfg, &dci_msg->ctx.location, q->sf_symbols[0]) < + SRSRAN_SUCCESS) { + ERROR("Error putting PDCCH DMRS"); + return SRSRAN_ERROR; + } + + // PDCCH Encode + if (srsran_pdcch_nr_encode(&q->pdcch, dci_msg, q->sf_symbols[0]) < SRSRAN_SUCCESS) { + ERROR("Error encoding PDCCH"); + return SRSRAN_ERROR; + } + + INFO("DCI DL NR: L=%d; ncce=%d;", dci_msg->ctx.location.L, dci_msg->ctx.location.ncce); + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_dl_pdcch_put_dl(srsran_gnb_dl_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_dl_nr_t* dci_dl) +{ + if (q == NULL || slot_cfg == NULL || dci_dl == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Pack DCI + srsran_dci_msg_nr_t dci_msg = {}; + if (srsran_dci_nr_dl_pack(&q->dci, dci_dl, &dci_msg) < SRSRAN_SUCCESS) { + ERROR("Error packing DL DCI"); + return SRSRAN_ERROR; + } + + INFO("DCI DL NR: L=%d; ncce=%d;", dci_dl->ctx.location.L, dci_dl->ctx.location.ncce); + + return gnb_dl_pdcch_put_msg(q, slot_cfg, &dci_msg); +} + +int srsran_gnb_dl_pdcch_put_ul(srsran_gnb_dl_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_dci_ul_nr_t* dci_ul) +{ + if (q == NULL || slot_cfg == NULL || dci_ul == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Pack DCI + srsran_dci_msg_nr_t dci_msg = {}; + if (srsran_dci_nr_ul_pack(&q->dci, dci_ul, &dci_msg) < SRSRAN_SUCCESS) { + ERROR("Error packing UL DCI"); + return SRSRAN_ERROR; + } + + INFO("DCI DL NR: L=%d; ncce=%d;", dci_ul->ctx.location.L, dci_ul->ctx.location.ncce); + + return gnb_dl_pdcch_put_msg(q, slot_cfg, &dci_msg); +} + +int srsran_gnb_dl_pdsch_put(srsran_gnb_dl_t* q, + const srsran_slot_cfg_t* slot, + const srsran_sch_cfg_nr_t* cfg, + uint8_t* data[SRSRAN_MAX_TB]) +{ + if (srsran_dmrs_sch_put_sf(&q->dmrs, slot, cfg, &cfg->grant, q->sf_symbols[0]) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_pdsch_nr_encode(&q->pdsch, cfg, &cfg->grant, data, q->sf_symbols) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_dl_pdsch_info(const srsran_gnb_dl_t* q, const srsran_sch_cfg_nr_t* cfg, char* str, uint32_t str_len) +{ + int len = 0; + + // Append PDSCH info + len += srsran_pdsch_nr_tx_info(&q->pdsch, cfg, &cfg->grant, &str[len], str_len - len); + + return len; +} + +int srsran_gnb_dl_pdcch_dl_info(const srsran_gnb_dl_t* q, const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len) +{ + int len = 0; + + // Append PDCCH info + len += srsran_dci_dl_nr_to_str(&q->dci, dci, &str[len], str_len - len); + + return len; +} + +int srsran_gnb_dl_pdcch_ul_info(const srsran_gnb_dl_t* q, const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) +{ + int len = 0; + + // Append PDCCH info + len += srsran_dci_ul_nr_to_str(&q->dci, dci, &str[len], str_len - len); + + return len; +} + +int srsran_gnb_dl_nzp_csi_rs_put(srsran_gnb_dl_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_resource_t* resource) +{ + if (q == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (srsran_csi_rs_nzp_put_resource(&q->carrier, slot_cfg, resource, q->sf_symbols[0]) < SRSRAN_SUCCESS) { + ERROR("Error putting NZP-CSI-RS resource"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/gnb/gnb_ul.c b/lib/src/phy/gnb/gnb_ul.c new file mode 100644 index 000000000..ab759bda9 --- /dev/null +++ b/lib/src/phy/gnb/gnb_ul.c @@ -0,0 +1,371 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/gnb/gnb_ul.h" +#include "srsran/phy/ch_estimation/dmrs_pucch.h" + +/** + * @brief Shifts FFT window a fraction of the cyclic prefix. Set to 0.0f for disabling. + * @note Increases protection against inter-symbol interference in case of synchronization error in expense of computing + * performance + */ +#define GNB_UL_NR_FFT_WINDOW_OFFSET 0.5f + +/** + * @brief Minimum PUSCH DMRS measured SINR default value + */ +#define GNB_UL_PUSCH_MIN_SNR_DEFAULT -10.0f + +static int gnb_ul_alloc_prb(srsran_gnb_ul_t* q, uint32_t new_nof_prb) +{ + if (q->max_prb < new_nof_prb) { + q->max_prb = new_nof_prb; + + srsran_chest_dl_res_free(&q->chest_pusch); + if (srsran_chest_dl_res_init(&q->chest_pusch, q->max_prb) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + srsran_chest_ul_res_free(&q->chest_pucch); + if (srsran_chest_ul_res_init(&q->chest_pucch, q->max_prb) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (q->sf_symbols[0] != NULL) { + free(q->sf_symbols[0]); + } + + q->sf_symbols[0] = srsran_vec_cf_malloc(SRSRAN_SLOT_LEN_RE_NR(q->max_prb)); + if (q->sf_symbols[0] == NULL) { + ERROR("Malloc"); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_ul_init(srsran_gnb_ul_t* q, cf_t* input, const srsran_gnb_ul_args_t* args) +{ + if (q == NULL || args == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (gnb_ul_alloc_prb(q, args->nof_max_prb) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_pusch_nr_init_gnb(&q->pusch, &args->pusch) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_pucch_nr_init(&q->pucch, &args->pucch) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_dmrs_sch_init(&q->dmrs, true) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + srsran_ofdm_cfg_t ofdm_cfg = {}; + ofdm_cfg.nof_prb = args->nof_max_prb; + ofdm_cfg.in_buffer = input; + ofdm_cfg.out_buffer = q->sf_symbols[0]; + ofdm_cfg.rx_window_offset = GNB_UL_NR_FFT_WINDOW_OFFSET; + ofdm_cfg.symbol_sz = srsran_min_symbol_sz_rb(args->nof_max_prb); + ofdm_cfg.keep_dc = true; + + if (srsran_ofdm_rx_init_cfg(&q->fft, &ofdm_cfg) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Set PUSCH minimum SNR, use default value if the given is NAN, INF or zero + q->pusch_min_snr_dB = GNB_UL_PUSCH_MIN_SNR_DEFAULT; + if (isnormal(args->pusch_min_snr_dB)) { + q->pusch_min_snr_dB = args->pusch_min_snr_dB; + } + + return SRSRAN_SUCCESS; +} + +void srsran_gnb_ul_free(srsran_gnb_ul_t* q) +{ + if (q == NULL) { + return; + } + + srsran_ofdm_tx_free(&q->fft); + srsran_pusch_nr_free(&q->pusch); + srsran_pucch_nr_free(&q->pucch); + srsran_dmrs_sch_free(&q->dmrs); + srsran_chest_dl_res_free(&q->chest_pusch); + srsran_chest_ul_res_free(&q->chest_pucch); + + if (q->sf_symbols[0] != NULL) { + free(q->sf_symbols[0]); + } + + SRSRAN_MEM_ZERO(q, srsran_gnb_ul_t, 1); +} + +int srsran_gnb_ul_set_carrier(srsran_gnb_ul_t* q, const srsran_carrier_nr_t* carrier) +{ + if (q == NULL || carrier == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + q->carrier = *carrier; + + if (gnb_ul_alloc_prb(q, carrier->nof_prb) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_pusch_nr_set_carrier(&q->pusch, carrier) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_pucch_nr_set_carrier(&q->pucch, carrier) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_dmrs_sch_set_carrier(&q->dmrs, carrier) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + srsran_ofdm_cfg_t ofdm_cfg = {}; + ofdm_cfg.nof_prb = carrier->nof_prb; + ofdm_cfg.rx_window_offset = GNB_UL_NR_FFT_WINDOW_OFFSET; + ofdm_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); + ofdm_cfg.keep_dc = true; + ofdm_cfg.phase_compensation_hz = carrier->ul_center_frequency_hz; + + if (srsran_ofdm_rx_init_cfg(&q->fft, &ofdm_cfg) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_ul_fft(srsran_gnb_ul_t* q) +{ + if (q == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + srsran_ofdm_rx_sf(&q->fft); + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_ul_get_pusch(srsran_gnb_ul_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_sch_cfg_nr_t* cfg, + const srsran_sch_grant_nr_t* grant, + srsran_pusch_res_nr_t* data) +{ + if (q == NULL || cfg == NULL || grant == NULL || data == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (srsran_dmrs_sch_estimate(&q->dmrs, slot_cfg, cfg, grant, q->sf_symbols[0], &q->chest_pusch) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Check PUSCH DMRS minimum SNR and abort PUSCH decoding if it is below the threshold + if (q->dmrs.csi.snr_dB < q->pusch_min_snr_dB) { + // Set PUSCH data as not decoded + data->tb[0].crc = false; + data->tb[0].avg_iter = NAN; + data->uci.valid = false; + return SRSRAN_SUCCESS; + } + + if (srsran_pusch_nr_decode(&q->pusch, cfg, grant, &q->chest_pusch, q->sf_symbols, data) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +static int gnb_ul_decode_pucch_format1(srsran_gnb_ul_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_pucch_nr_common_cfg_t* cfg, + const srsran_pucch_nr_resource_t* resource, + const srsran_uci_cfg_nr_t* uci_cfg, + srsran_uci_value_nr_t* uci_value) +{ + uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {}; + + // Set ACK bits + uint32_t nof_bits = SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS, uci_cfg->ack.count); + + // Set SR bits + // For a positive SR transmission using PUCCH format 1, the UE transmits the PUCCH as described in [4, TS + // 38.211] by setting b ( 0 ) = 0 . + if (nof_bits == 0 && uci_cfg->o_sr > 0) { + nof_bits = 1; + } + + // Channel estimation + if (srsran_dmrs_pucch_format1_estimate(&q->pucch, cfg, slot_cfg, resource, q->sf_symbols[0], &q->chest_pucch) < + SRSRAN_SUCCESS) { + ERROR("Error in PUCCH format 1 estimation"); + return SRSRAN_ERROR; + } + + // Actual decode + float norm_corr = 0.0f; + if (srsran_pucch_nr_format1_decode( + &q->pucch, cfg, slot_cfg, resource, &q->chest_pucch, q->sf_symbols[0], b, nof_bits, &norm_corr) < + SRSRAN_SUCCESS) { + ERROR("Error in PUCCH format 1 decoding"); + return SRSRAN_ERROR; + } + + // As format 1 with positive SR is not encoded with any payload, set SR to 1 + if (uci_cfg->sr_positive_present) { + uci_value->sr = 1; + } + + // Take valid decision + uci_value->valid = (norm_corr > 0.5f); + + // De-multiplex ACK bits + for (uint32_t i = 0; i < nof_bits; i++) { + uci_value->ack[i] = b[i]; + } + + return SRSRAN_SUCCESS; +} + +static int gnb_ul_decode_pucch_format2(srsran_gnb_ul_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_pucch_nr_common_cfg_t* cfg, + const srsran_pucch_nr_resource_t* resource, + const srsran_uci_cfg_nr_t* uci_cfg, + srsran_uci_value_nr_t* uci_value) +{ + if (srsran_dmrs_pucch_format2_estimate(&q->pucch, cfg, slot_cfg, resource, q->sf_symbols[0], &q->chest_pucch) < + SRSRAN_SUCCESS) { + ERROR("Error in PUCCH format 2 estimation"); + return SRSRAN_ERROR; + } + + if (srsran_pucch_nr_format_2_3_4_decode( + &q->pucch, cfg, slot_cfg, resource, uci_cfg, &q->chest_pucch, q->sf_symbols[0], uci_value) < SRSRAN_SUCCESS) { + ERROR("Error in PUCCH format 2 decoding"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_pucch_nr_common_cfg_t* cfg, + const srsran_pucch_nr_resource_t* resource, + const srsran_uci_cfg_nr_t* uci_cfg, + srsran_uci_value_nr_t* uci_value, + srsran_csi_trs_measurements_t* meas) +{ + if (q == NULL || slot_cfg == NULL || cfg == NULL || resource == NULL || uci_cfg == NULL || uci_value == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Estimate channel + switch (resource->format) { + case SRSRAN_PUCCH_NR_FORMAT_1: + if (gnb_ul_decode_pucch_format1(q, slot_cfg, cfg, resource, uci_cfg, uci_value) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + break; + case SRSRAN_PUCCH_NR_FORMAT_2: + if (gnb_ul_decode_pucch_format2(q, slot_cfg, cfg, resource, uci_cfg, uci_value) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + break; + case SRSRAN_PUCCH_NR_FORMAT_0: + case SRSRAN_PUCCH_NR_FORMAT_3: + case SRSRAN_PUCCH_NR_FORMAT_4: + case SRSRAN_PUCCH_NR_FORMAT_ERROR: + ERROR("Invalid or not implemented PUCCH-NR format %d", (int)resource->format); + return SRSRAN_ERROR; + } + + // Copy DMRS measurements + if (meas != NULL) { + meas->rsrp = q->chest_pucch.rsrp; + meas->rsrp_dB = q->chest_pucch.rsrp_dBfs; + meas->epre = q->chest_pucch.epre; + meas->epre_dB = q->chest_pucch.epre_dBfs; + meas->n0 = q->chest_pucch.noise_estimate; + meas->n0_dB = q->chest_pucch.noise_estimate_dbFs; + meas->snr_dB = q->chest_pucch.snr_db; + meas->cfo_hz = q->chest_pucch.cfo_hz; + meas->cfo_hz_max = NAN; // Unavailable + meas->delay_us = q->chest_pucch.ta_us; + meas->nof_re = 0; // Unavailable + } + + return SRSRAN_SUCCESS; +} + +uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, + const srsran_pucch_nr_resource_t* resource, + const srsran_uci_data_nr_t* uci_data, + const srsran_csi_trs_measurements_t* csi, + char* str, + uint32_t str_len) +{ + if (q == NULL || uci_data == NULL) { + return 0; + } + + uint32_t len = 0; + + len += srsran_pucch_nr_info(resource, uci_data, &str[len], str_len - len); + + len += srsran_csi_meas_info_short(csi, &str[len], str_len - len); + + len = srsran_print_check(str, str_len, len, "valid=%c ", uci_data->value.valid ? 'y' : 'n'); + + return len; +} + +uint32_t srsran_gnb_ul_pusch_info(srsran_gnb_ul_t* q, + const srsran_sch_cfg_nr_t* cfg, + const srsran_pusch_res_nr_t* res, + char* str, + uint32_t str_len) +{ + if (q == NULL || cfg == NULL || res == NULL) { + return 0; + } + + uint32_t len = 0; + + len += srsran_pusch_nr_rx_info(&q->pusch, cfg, &cfg->grant, res, str, str_len - len); + + // Append channel estimator info + len += srsran_csi_meas_info_short(&q->dmrs.csi, &str[len], str_len - len); + + return len; +} diff --git a/lib/src/phy/io/CMakeLists.txt b/lib/src/phy/io/CMakeLists.txt index 7d69e0f7f..ae1aea5a2 100644 --- a/lib/src/phy/io/CMakeLists.txt +++ b/lib/src/phy/io/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/io/binsource.c b/lib/src/phy/io/binsource.c index f25f990f7..cfadcddec 100644 --- a/lib/src/phy/io/binsource.c +++ b/lib/src/phy/io/binsource.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/io/filesink.c b/lib/src/phy/io/filesink.c index c28436373..f53b47cb4 100644 --- a/lib/src/phy/io/filesink.c +++ b/lib/src/phy/io/filesink.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,7 +26,7 @@ #include "srsran/phy/io/filesink.h" -int srsran_filesink_init(srsran_filesink_t* q, char* filename, srsran_datatype_t type) +int srsran_filesink_init(srsran_filesink_t* q, const char* filename, srsran_datatype_t type) { bzero(q, sizeof(srsran_filesink_t)); q->f = fopen(filename, "w"); diff --git a/lib/src/phy/io/filesource.c b/lib/src/phy/io/filesource.c index 579afa13e..b9c881539 100644 --- a/lib/src/phy/io/filesource.c +++ b/lib/src/phy/io/filesource.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,7 +26,7 @@ #include "srsran/phy/io/filesource.h" #include "srsran/phy/utils/debug.h" -int srsran_filesource_init(srsran_filesource_t* q, char* filename, srsran_datatype_t type) +int srsran_filesource_init(srsran_filesource_t* q, const char* filename, srsran_datatype_t type) { bzero(q, sizeof(srsran_filesource_t)); q->f = fopen(filename, "r"); diff --git a/lib/src/phy/io/netsink.c b/lib/src/phy/io/netsink.c index a32731aa9..a9a1ccb8b 100644 --- a/lib/src/phy/io/netsink.c +++ b/lib/src/phy/io/netsink.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,7 +39,7 @@ int srsran_netsink_init(srsran_netsink_t* q, const char* address, uint16_t port, q->sockfd = socket(AF_INET, type == SRSRAN_NETSINK_TCP ? SOCK_STREAM : SOCK_DGRAM, 0); if (q->sockfd < 0) { perror("socket"); - return -1; + return SRSRAN_ERROR; } int enable = 1; @@ -53,12 +53,15 @@ int srsran_netsink_init(srsran_netsink_t* q, const char* address, uint16_t port, #endif q->servaddr.sin_family = AF_INET; - q->servaddr.sin_addr.s_addr = inet_addr(address); + if (inet_pton(q->servaddr.sin_family, address, &q->servaddr.sin_addr) != 1) { + perror("inet_pton"); + return SRSRAN_ERROR; + } q->servaddr.sin_port = htons(port); q->connected = false; q->type = type; - return 0; + return SRSRAN_SUCCESS; } void srsran_netsink_free(srsran_netsink_t* q) @@ -73,9 +76,9 @@ int srsran_netsink_set_nonblocking(srsran_netsink_t* q) { if (fcntl(q->sockfd, F_SETFL, O_NONBLOCK)) { perror("fcntl"); - return -1; + return SRSRAN_ERROR; } - return 0; + return SRSRAN_SUCCESS; } int srsran_netsink_write(srsran_netsink_t* q, void* buffer, int nof_bytes) @@ -83,11 +86,11 @@ int srsran_netsink_write(srsran_netsink_t* q, void* buffer, int nof_bytes) if (!q->connected) { if (connect(q->sockfd, &q->servaddr, sizeof(q->servaddr)) < 0) { if (errno == ECONNREFUSED || errno == EINPROGRESS) { - return 0; + return SRSRAN_SUCCESS; } else { perror("connect"); exit(-1); - return -1; + return SRSRAN_ERROR; } } else { q->connected = true; @@ -102,10 +105,10 @@ int srsran_netsink_write(srsran_netsink_t* q, void* buffer, int nof_bytes) q->sockfd = socket(AF_INET, q->type == SRSRAN_NETSINK_TCP ? SOCK_STREAM : SOCK_DGRAM, 0); if (q->sockfd < 0) { perror("socket"); - return -1; + return SRSRAN_ERROR; } q->connected = false; - return 0; + return SRSRAN_SUCCESS; } } } diff --git a/lib/src/phy/io/netsource.c b/lib/src/phy/io/netsource.c index 7dbd5fd97..4bbf0ba7e 100644 --- a/lib/src/phy/io/netsource.c +++ b/lib/src/phy/io/netsource.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,7 +39,7 @@ int srsran_netsource_init(srsran_netsource_t* q, const char* address, uint16_t p if (q->sockfd < 0) { perror("socket"); - return -1; + return SRSRAN_ERROR; } // Make sockets reusable @@ -55,16 +55,19 @@ int srsran_netsource_init(srsran_netsource_t* q, const char* address, uint16_t p q->type = type; q->servaddr.sin_family = AF_INET; - q->servaddr.sin_addr.s_addr = inet_addr(address); + if (inet_pton(q->servaddr.sin_family, address, &q->servaddr.sin_addr) != 1) { + perror("inet_pton"); + return SRSRAN_ERROR; + } q->servaddr.sin_port = htons(port); if (bind(q->sockfd, (struct sockaddr*)&q->servaddr, sizeof(struct sockaddr_in))) { perror("bind"); - return -1; + return SRSRAN_ERROR; } q->connfd = 0; - return 0; + return SRSRAN_SUCCESS; } void srsran_netsource_free(srsran_netsource_t* q) @@ -82,9 +85,9 @@ int srsran_netsource_read(srsran_netsource_t* q, void* buffer, int nbytes) if (n == -1) { if (errno == EAGAIN) { - return 0; + return SRSRAN_SUCCESS; } else { - return -1; + return SRSRAN_ERROR; } } else { return n; @@ -97,7 +100,7 @@ int srsran_netsource_read(srsran_netsource_t* q, void* buffer, int nbytes) q->connfd = accept(q->sockfd, (struct sockaddr*)&q->cliaddr, &clilen); if (q->connfd < 0) { perror("accept"); - return -1; + return SRSRAN_ERROR; } } int n = read(q->connfd, buffer, nbytes); @@ -105,7 +108,7 @@ int srsran_netsource_read(srsran_netsource_t* q, void* buffer, int nbytes) printf("Connection closed\n"); close(q->connfd); q->connfd = 0; - return 0; + return SRSRAN_SUCCESS; } if (n == -1) { perror("read"); @@ -136,7 +139,7 @@ int srsran_netsource_set_nonblocking(srsran_netsource_t* q) perror("fcntl"); return -1; } - return 0; + return SRSRAN_SUCCESS; } int srsran_netsource_set_timeout(srsran_netsource_t* q, uint32_t microseconds) @@ -146,7 +149,7 @@ int srsran_netsource_set_timeout(srsran_netsource_t* q, uint32_t microseconds) t.tv_usec = microseconds; if (setsockopt(q->sockfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(struct timeval))) { perror("setsockopt"); - return -1; + return SRSRAN_ERROR; } - return 0; + return SRSRAN_SUCCESS; } diff --git a/lib/src/phy/mimo/CMakeLists.txt b/lib/src/phy/mimo/CMakeLists.txt index ce1425e94..13e801a31 100644 --- a/lib/src/phy/mimo/CMakeLists.txt +++ b/lib/src/phy/mimo/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/mimo/layermap.c b/lib/src/phy/mimo/layermap.c index d2100084c..2abcd4dd5 100644 --- a/lib/src/phy/mimo/layermap.c +++ b/lib/src/phy/mimo/layermap.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/mimo/precoding.c b/lib/src/phy/mimo/precoding.c index 2b047d086..6e82de0f9 100644 --- a/lib/src/phy/mimo/precoding.c +++ b/lib/src/phy/mimo/precoding.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/mimo/test/CMakeLists.txt b/lib/src/phy/mimo/test/CMakeLists.txt index aff7c9027..8a3b4ef01 100644 --- a/lib/src/phy/mimo/test/CMakeLists.txt +++ b/lib/src/phy/mimo/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/mimo/test/layermap_test.c b/lib/src/phy/mimo/test/layermap_test.c index 55e4456e4..a112a9192 100644 --- a/lib/src/phy/mimo/test/layermap_test.c +++ b/lib/src/phy/mimo/test/layermap_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/mimo/test/pmi_select_test.c b/lib/src/phy/mimo/test/pmi_select_test.c index f09792ec1..c07189121 100644 --- a/lib/src/phy/mimo/test/pmi_select_test.c +++ b/lib/src/phy/mimo/test/pmi_select_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/mimo/test/pmi_select_test.h b/lib/src/phy/mimo/test/pmi_select_test.h index 1d3fbc989..ccede08ec 100644 --- a/lib/src/phy/mimo/test/pmi_select_test.h +++ b/lib/src/phy/mimo/test/pmi_select_test.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/mimo/test/precoder_test.c b/lib/src/phy/mimo/test/precoder_test.c index 2dfea7006..3f6ccd7be 100644 --- a/lib/src/phy/mimo/test/precoder_test.c +++ b/lib/src/phy/mimo/test/precoder_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -163,10 +163,10 @@ void populate_channel(srsran_tx_scheme_t type, cf_t* h[SRSRAN_MAX_PORTS][SRSRAN_ static void awgn(cf_t* y[SRSRAN_MAX_PORTS], uint32_t n, float snr) { int i; - float std_dev = srsran_convert_dB_to_amplitude(-(snr + 3.0f)) * scaling; + float var = srsran_convert_dB_to_power(-snr) * scaling * scaling; for (i = 0; i < nof_rx_ports; i++) { - srsran_ch_awgn_c(y[i], y[i], std_dev, n); + srsran_ch_awgn_c(y[i], y[i], var, n); } } diff --git a/lib/src/phy/modem/CMakeLists.txt b/lib/src/phy/modem/CMakeLists.txt index 1df22ed14..d380a24ca 100644 --- a/lib/src/phy/modem/CMakeLists.txt +++ b/lib/src/phy/modem/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/modem/demod_hard.c b/lib/src/phy/modem/demod_hard.c index 8a654bec3..427849c0b 100644 --- a/lib/src/phy/modem/demod_hard.c +++ b/lib/src/phy/modem/demod_hard.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/demod_soft.c b/lib/src/phy/modem/demod_soft.c index 524392ab1..de3dd88b8 100644 --- a/lib/src/phy/modem/demod_soft.c +++ b/lib/src/phy/modem/demod_soft.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/hard_demod_lte.c b/lib/src/phy/modem/hard_demod_lte.c index 8f50795d8..43c02279d 100644 --- a/lib/src/phy/modem/hard_demod_lte.c +++ b/lib/src/phy/modem/hard_demod_lte.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/hard_demod_lte.h b/lib/src/phy/modem/hard_demod_lte.h index f13bdc6a1..b041024ac 100644 --- a/lib/src/phy/modem/hard_demod_lte.h +++ b/lib/src/phy/modem/hard_demod_lte.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/lte_tables.c b/lib/src/phy/modem/lte_tables.c index ff022e61b..4195cb2b6 100644 --- a/lib/src/phy/modem/lte_tables.c +++ b/lib/src/phy/modem/lte_tables.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/lte_tables.h b/lib/src/phy/modem/lte_tables.h index 436865983..8141b9b4d 100644 --- a/lib/src/phy/modem/lte_tables.h +++ b/lib/src/phy/modem/lte_tables.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/mod.c b/lib/src/phy/modem/mod.c index d88c69f4a..6f55ebcb1 100644 --- a/lib/src/phy/modem/mod.c +++ b/lib/src/phy/modem/mod.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/modem_table.c b/lib/src/phy/modem/modem_table.c index 6a9f92d68..7bf0a4d74 100644 --- a/lib/src/phy/modem/modem_table.c +++ b/lib/src/phy/modem/modem_table.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/test/CMakeLists.txt b/lib/src/phy/modem/test/CMakeLists.txt index a543c10ed..20474536f 100644 --- a/lib/src/phy/modem/test/CMakeLists.txt +++ b/lib/src/phy/modem/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/modem/test/modem_test.c b/lib/src/phy/modem/test/modem_test.c index 62d05e814..e40c95492 100644 --- a/lib/src/phy/modem/test/modem_test.c +++ b/lib/src/phy/modem/test/modem_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/modem/test/soft_demod_test.c b/lib/src/phy/modem/test/soft_demod_test.c index e7d9f5abe..a81ee61ff 100644 --- a/lib/src/phy/modem/test/soft_demod_test.c +++ b/lib/src/phy/modem/test/soft_demod_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -54,7 +54,7 @@ void parse_args(int argc, char** argv) nof_frames = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'm': switch (strtol(argv[optind], NULL, 10)) { diff --git a/lib/src/phy/phch/CMakeLists.txt b/lib/src/phy/phch/CMakeLists.txt index 226e23312..6f22a3a4e 100644 --- a/lib/src/phy/phch/CMakeLists.txt +++ b/lib/src/phy/phch/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/phch/cqi.c b/lib/src/phy/phch/cqi.c index 49ed32071..c2e43b7b6 100644 --- a/lib/src/phy/phch/cqi.c +++ b/lib/src/phy/phch/cqi.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -74,7 +74,7 @@ static int cqi_hl_subband_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_hl_subband_t* m return bit_count; } -static int cqi_ue_subband_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_ue_subband_t* msg, uint8_t* buff) +static int cqi_ue_subband_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_ue_diff_subband_t* msg, uint8_t* buff) { uint8_t* body_ptr = buff; srsran_bit_unpack(msg->wideband_cqi, &body_ptr, 4); @@ -110,7 +110,7 @@ static int cqi_format2_wideband_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_format2_w return (int)(body_ptr - buff); } -static int cqi_format2_subband_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_format2_subband_t* msg, uint8_t* buff) +static int cqi_format2_subband_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_ue_subband_t* msg, uint8_t* buff) { uint8_t* body_ptr = buff; srsran_bit_unpack(msg->subband_cqi, &body_ptr, 4); @@ -123,10 +123,10 @@ int srsran_cqi_value_pack(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, uint switch (cfg->type) { case SRSRAN_CQI_TYPE_WIDEBAND: return cqi_format2_wideband_pack(cfg, &value->wideband, buff); - case SRSRAN_CQI_TYPE_SUBBAND: - return cqi_format2_subband_pack(cfg, &value->subband, buff); case SRSRAN_CQI_TYPE_SUBBAND_UE: - return cqi_ue_subband_pack(cfg, &value->subband_ue, buff); + return cqi_format2_subband_pack(cfg, &value->subband_ue, buff); + case SRSRAN_CQI_TYPE_SUBBAND_UE_DIFF: + return cqi_ue_subband_pack(cfg, &value->subband_ue_diff, buff); case SRSRAN_CQI_TYPE_SUBBAND_HL: return cqi_hl_subband_pack(cfg, &value->subband_hl, buff); } @@ -172,7 +172,7 @@ int cqi_hl_subband_unpack(srsran_cqi_cfg_t* cfg, uint8_t* buff, srsran_cqi_hl_su return bit_count; } -int cqi_ue_subband_unpack(srsran_cqi_cfg_t* cfg, uint8_t* buff, srsran_cqi_ue_subband_t* msg) +int cqi_ue_subband_unpack(srsran_cqi_cfg_t* cfg, uint8_t* buff, srsran_cqi_ue_diff_subband_t* msg) { uint8_t* body_ptr = buff; msg->wideband_cqi = srsran_bit_pack(&body_ptr, 4); @@ -206,7 +206,7 @@ static int cqi_format2_wideband_unpack(srsran_cqi_cfg_t* cfg, uint8_t* buff, srs return 4; } -static int cqi_format2_subband_unpack(srsran_cqi_cfg_t* cfg, uint8_t* buff, srsran_cqi_format2_subband_t* msg) +static int cqi_format2_subband_unpack(srsran_cqi_cfg_t* cfg, uint8_t* buff, srsran_cqi_ue_subband_t* msg) { uint8_t* body_ptr = buff; msg->subband_cqi = srsran_bit_pack(&body_ptr, 4); @@ -219,10 +219,10 @@ int srsran_cqi_value_unpack(srsran_cqi_cfg_t* cfg, uint8_t buff[SRSRAN_CQI_MAX_B switch (cfg->type) { case SRSRAN_CQI_TYPE_WIDEBAND: return cqi_format2_wideband_unpack(cfg, buff, &value->wideband); - case SRSRAN_CQI_TYPE_SUBBAND: - return cqi_format2_subband_unpack(cfg, buff, &value->subband); case SRSRAN_CQI_TYPE_SUBBAND_UE: - return cqi_ue_subband_unpack(cfg, buff, &value->subband_ue); + return cqi_format2_subband_unpack(cfg, buff, &value->subband_ue); + case SRSRAN_CQI_TYPE_SUBBAND_UE_DIFF: + return cqi_ue_subband_unpack(cfg, buff, &value->subband_ue_diff); case SRSRAN_CQI_TYPE_SUBBAND_HL: return cqi_hl_subband_unpack(cfg, buff, &value->subband_hl); } @@ -251,17 +251,18 @@ cqi_format2_wideband_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_format2_wideband } static int -cqi_format2_subband_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_format2_subband_t* msg, char* buff, uint32_t buff_len) +cqi_format2_subband_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_ue_subband_t* msg, char* buff, uint32_t buff_len) { int n = 0; n += snprintf(buff + n, buff_len - n, ", cqi=%d", msg->subband_cqi); n += snprintf(buff + n, buff_len - n, ", label=%d", msg->subband_label); - + n += snprintf(buff + n, buff_len - n, ", sb_idx=%d", cfg->sb_idx); return n; } -static int cqi_ue_subband_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_ue_subband_t* msg, char* buff, uint32_t buff_len) +static int +cqi_ue_subband_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_ue_diff_subband_t* msg, char* buff, uint32_t buff_len) { int n = 0; @@ -301,17 +302,17 @@ int srsran_cqi_value_tostring(srsran_cqi_cfg_t* cfg, srsran_cqi_value_t* value, case SRSRAN_CQI_TYPE_WIDEBAND: ret = cqi_format2_wideband_tostring(cfg, &value->wideband, buff, buff_len); break; - case SRSRAN_CQI_TYPE_SUBBAND: - ret = cqi_format2_subband_tostring(cfg, &value->subband, buff, buff_len); - break; case SRSRAN_CQI_TYPE_SUBBAND_UE: - ret = cqi_ue_subband_tostring(cfg, &value->subband_ue, buff, buff_len); + ret = cqi_format2_subband_tostring(cfg, &value->subband_ue, buff, buff_len); + break; + case SRSRAN_CQI_TYPE_SUBBAND_UE_DIFF: + ret = cqi_ue_subband_tostring(cfg, &value->subband_ue_diff, buff, buff_len); break; case SRSRAN_CQI_TYPE_SUBBAND_HL: ret = cqi_hl_subband_tostring(cfg, &value->subband_hl, buff, buff_len); break; default: - /* Do nothing */; + /* Do nothing */; } return ret; @@ -348,10 +349,10 @@ int srsran_cqi_size(srsran_cqi_cfg_t* cfg) } } break; - case SRSRAN_CQI_TYPE_SUBBAND: + case SRSRAN_CQI_TYPE_SUBBAND_UE: size = 4 + ((cfg->subband_label_2_bits) ? 2 : 1); break; - case SRSRAN_CQI_TYPE_SUBBAND_UE: + case SRSRAN_CQI_TYPE_SUBBAND_UE_DIFF: size = 4 + 2 + cfg->L; break; case SRSRAN_CQI_TYPE_SUBBAND_HL: @@ -453,9 +454,8 @@ static bool cqi_get_N_tdd(uint32_t I_cqi_pmi, uint32_t* N_p, uint32_t* N_offset) return true; } -static bool cqi_send(uint32_t I_cqi_pmi, uint32_t tti, bool is_fdd) +static bool cqi_send(uint32_t I_cqi_pmi, uint32_t tti, bool is_fdd, uint32_t H) { - uint32_t N_p = 0; uint32_t N_offset = 0; @@ -470,7 +470,7 @@ static bool cqi_send(uint32_t I_cqi_pmi, uint32_t tti, bool is_fdd) } if (N_p) { - if ((tti - N_offset) % N_p == 0) { + if ((tti - N_offset) % (H * N_p) == 0) { return true; } } @@ -479,7 +479,6 @@ static bool cqi_send(uint32_t I_cqi_pmi, uint32_t tti, bool is_fdd) static bool ri_send(uint32_t I_cqi_pmi, uint32_t I_ri, uint32_t tti, bool is_fdd) { - uint32_t M_ri = 0; int N_offset_ri = 0; uint32_t N_p = 0; @@ -526,6 +525,95 @@ static bool ri_send(uint32_t I_cqi_pmi, uint32_t I_ri, uint32_t tti, bool is_fdd return false; } +/* Returns the subband size for higher layer-configured subband feedback, + * i.e., the number of RBs per subband as a function of the cell bandwidth + * (Table 7.2.1-3 in TS 36.213) + */ +static int cqi_hl_get_subband_size(int nof_prb) +{ + if (nof_prb < 7) { + ERROR("Error: nof_prb is invalid (< 7)"); + return SRSRAN_ERROR; + } else if (nof_prb <= 26) { + return 4; + } else if (nof_prb <= 63) { + return 6; + } else if (nof_prb <= 110) { + return 8; + } else { + ERROR("Error: nof_prb is invalid (> 110)"); + return SRSRAN_ERROR; + } +} + +/* Returns the bandwidth parts (J) + * (Table 7.2.2-2 in TS 36.213) + */ +static int cqi_hl_get_bwp_J(int nof_prb) +{ + if (nof_prb < 7) { + ERROR("Error: nof_prb is not valid (< 7)"); + return SRSRAN_ERROR; + } else if (nof_prb <= 10) { + return 1; + } else if (nof_prb <= 26) { + return 2; + } else if (nof_prb <= 63) { + return 3; + } else if (nof_prb <= 110) { + return 4; + } else { + ERROR("Error: nof_prb is not valid (> 110)"); + return SRSRAN_ERROR; + } +} + +/* Returns the number of subbands in the j-th bandwidth part + */ +static int cqi_sb_get_Nj(uint32_t j, uint32_t nof_prb) +{ + // from Table 7.2.2-2 in TS 36.213 + int J = cqi_hl_get_bwp_J(nof_prb); + int K = cqi_hl_get_subband_size(nof_prb); + + // Catch the J and k errors, and prevent undefined modulo operations + if (J <= 0 || K <= 0) { + return 0; + } + + if (J == 1) { + return (uint32_t)ceil((float)nof_prb / K); + } else { + // all bw parts have the same number of subbands except the last one + uint32_t Nj = (uint32_t)ceil((float)nof_prb / K / J); + if (j < J - 1) { + return Nj; + } else { + return Nj - 1; + } + } +} + +uint32_t srsran_cqi_get_sb_idx(uint32_t tti, + uint32_t subband_label, + const srsran_cqi_report_cfg_t* cqi_report_cfg, + const srsran_cell_t* cell) +{ + uint32_t bw_part_idx = srsran_cqi_periodic_sb_bw_part_idx(cqi_report_cfg, tti, cell->nof_prb, cell->frame_type); + return subband_label + bw_part_idx * cqi_sb_get_Nj(bw_part_idx, cell->nof_prb); +} + +int srsran_cqi_hl_get_L(int nof_prb) +{ + int subband_size = cqi_hl_get_subband_size(nof_prb); + int bwp_J = cqi_hl_get_bwp_J(nof_prb); + if (subband_size <= 0 || bwp_J <= 0) { + ERROR("Invalid parameters"); + return SRSRAN_ERROR; + } + return (int)SRSRAN_CEIL_LOG2((double)nof_prb / (subband_size * bwp_J)); +} + bool srsran_cqi_periodic_ri_send(const srsran_cqi_report_cfg_t* cfg, uint32_t tti, srsran_frame_type_t frame_type) { return cfg->periodic_configured && cfg->ri_idx_present && @@ -534,7 +622,65 @@ bool srsran_cqi_periodic_ri_send(const srsran_cqi_report_cfg_t* cfg, uint32_t tt bool srsran_cqi_periodic_send(const srsran_cqi_report_cfg_t* cfg, uint32_t tti, srsran_frame_type_t frame_type) { - return cfg->periodic_configured && cqi_send(cfg->pmi_idx, tti, frame_type == SRSRAN_FDD); + return cfg->periodic_configured && cqi_send(cfg->pmi_idx, tti, frame_type == SRSRAN_FDD, 1); +} + +uint32_t cqi_sb_get_H(const srsran_cqi_report_cfg_t* cfg, uint32_t nof_prb) +{ + uint32_t K = cfg->subband_wideband_ratio; + uint32_t J = cqi_hl_get_bwp_J(nof_prb); + + // Catch the J errors + if (J <= 0) + { + return 0; + } + + uint32_t H = J * K + 1; + return H; +} + +bool srsran_cqi_periodic_is_subband(const srsran_cqi_report_cfg_t* cfg, + uint32_t tti, + uint32_t nof_prb, + srsran_frame_type_t frame_type) +{ + uint32_t H = cqi_sb_get_H(cfg, nof_prb); + + // A periodic report is subband if it's a CQI opportunity and is not wideband + return srsran_cqi_periodic_send(cfg, tti, frame_type) && !cqi_send(cfg->pmi_idx, tti, frame_type == SRSRAN_FDD, H); +} + +uint32_t srsran_cqi_periodic_sb_bw_part_idx(const srsran_cqi_report_cfg_t* cfg, + uint32_t tti, + uint32_t nof_prb, + srsran_frame_type_t frame_type) +{ + uint32_t H = cqi_sb_get_H(cfg, nof_prb); + uint32_t N_p = 0, N_offset = 0; + + if (frame_type == SRSRAN_FDD) { + if (!cqi_get_N_fdd(cfg->pmi_idx, &N_p, &N_offset)) { + return false; + } + } else { + if (!cqi_get_N_tdd(cfg->pmi_idx, &N_p, &N_offset)) { + return false; + } + } + + assert(N_p != 0); + uint32_t x = ((tti - N_offset) / N_p) % H; + if (x > 0) { + int J = cqi_hl_get_bwp_J(nof_prb); + // Catch the J errors and prevent undefined modulo operation + if (J <= 0) { + return 0; + } + return (x - 1) % J; + } else { + return 0; + } } // CQI-to-Spectral Efficiency: 36.213 Table 7.2.3-1 @@ -601,25 +747,6 @@ uint8_t srsran_cqi_from_snr(float snr) return 0; } -/* Returns the subband size for higher layer-configured subband feedback, - * i.e., the number of RBs per subband as a function of the cell bandwidth - * (Table 7.2.1-3 in TS 36.213) - */ -static int cqi_hl_get_subband_size(int nof_prb) -{ - if (nof_prb < 7) { - return 0; - } else if (nof_prb <= 26) { - return 4; - } else if (nof_prb <= 63) { - return 6; - } else if (nof_prb <= 110) { - return 8; - } else { - return -1; - } -} - /* Returns the number of subbands to be reported in CQI measurements as * defined in clause 7.2 in TS 36.213, i.e., the N parameter */ diff --git a/lib/src/phy/phch/csi.c b/lib/src/phy/phch/csi.c index 75a1787ca..4487e3890 100644 --- a/lib/src/phy/phch/csi.c +++ b/lib/src/phy/phch/csi.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,6 +26,8 @@ #define CSI_WIDEBAND_CSI_NOF_BITS 4 +#define CSI_DEFAULT_ALPHA 0.5f + /// Implements SNRI to CQI conversion uint32_t csi_snri_db_to_cqi(srsran_csi_cqi_table_t table, float snri_db) { @@ -43,11 +45,10 @@ static bool csi_report_trigger(const srsran_csi_hl_report_cfg_t* cfg, uint32_t s return false; } -static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_t* cfg, - const srsran_csi_measurements_t* channel_meas, - const srsran_csi_measurements_t* interf_meas, - srsran_csi_report_cfg_t* report_cfg, - srsran_csi_report_value_t* report_value) +static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_t* cfg, + const srsran_csi_channel_measurements_t* channel_meas, + const srsran_csi_channel_measurements_t* interf_meas, + srsran_csi_report_value_t* report_value) { // Take SNR by default float wideband_sinr_db = channel_meas->wideband_snr_db; @@ -57,18 +58,6 @@ static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_ wideband_sinr_db = channel_meas->wideband_rsrp_dBm - interf_meas->wideband_epre_dBm; } - // Fill report configuration - report_cfg->type = cfg->type; - report_cfg->quantity = SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI; - report_cfg->freq_cfg = SRSRAN_CSI_REPORT_FREQ_WIDEBAND; - report_cfg->nof_ports = channel_meas->nof_ports; - report_cfg->K_csi_rs = channel_meas->K_csi_rs; - - // Save PUCCH resource only if periodic type - if (cfg->type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { - report_cfg->pucch_resource = cfg->periodic.resource; - } - // Fill quantified values report_value->wideband_cri_ri_pmi_cqi.cqi = csi_snri_db_to_cqi(cfg->cqi_table, wideband_sinr_db); report_value->wideband_cri_ri_pmi_cqi.ri = 0; @@ -151,48 +140,132 @@ csi_none_unpack(const srsran_csi_report_cfg_t* cfg, const uint8_t* o_csi1, srsra return cfg->K_csi_rs; } -int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, - uint32_t slot_idx, - const srsran_csi_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], - srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], - srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]) +int srsran_csi_new_nzp_csi_rs_measurement( + const srsran_csi_hl_resource_cfg_t csi_resources[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + const srsran_csi_channel_measurements_t* new_measure, + uint32_t nzp_csi_rs_id) +{ + if (csi_resources == NULL || measurements == NULL || new_measure == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Iterate all resources + for (uint32_t res_idx = 0; res_idx < SRSRAN_CSI_MAX_NOF_RESOURCES; res_idx++) { + // Skip resource is set to none + if (csi_resources[res_idx].type != SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NZP_CSI_RS_SSB) { + continue; + } + + // Look for the NZP-CSI reource set + bool present = false; + for (uint32_t i = 0; i < SRSRAN_CSI_MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_X_CONFIG && !present; i++) { + if (csi_resources[res_idx].nzp_csi_rs_ssb.nzp_csi_rs_resource_set_id_list[i] == nzp_csi_rs_id) { + present = true; + } + } + + // Skip Resource if it does not contain the NZP-CSI-RS set + if (!present) { + continue; + } + + // Filter measurements + measurements[res_idx].wideband_rsrp_dBm = + SRSRAN_VEC_SAFE_EMA(new_measure->wideband_rsrp_dBm, measurements[res_idx].wideband_rsrp_dBm, CSI_DEFAULT_ALPHA); + measurements[res_idx].wideband_epre_dBm = + SRSRAN_VEC_SAFE_EMA(new_measure->wideband_epre_dBm, measurements[res_idx].wideband_epre_dBm, CSI_DEFAULT_ALPHA); + measurements[res_idx].wideband_snr_db = + SRSRAN_VEC_SAFE_EMA(new_measure->wideband_snr_db, measurements[res_idx].wideband_snr_db, CSI_DEFAULT_ALPHA); + + // Force rest + measurements[res_idx].cri = new_measure->cri; + measurements[res_idx].K_csi_rs = new_measure->K_csi_rs; + } + + return SRSRAN_SUCCESS; +} + +int srsran_csi_reports_generate(const srsran_csi_hl_cfg_t* cfg, + const srsran_slot_cfg_t* slot_cfg, + srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_SLOT_MAX_NOF_REPORT]) { uint32_t count = 0; // Check inputs - if (cfg == NULL || measurements == NULL || report_cfg == NULL || report_value == NULL) { + if (cfg == NULL || report_cfg == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } + // Make sure report configuration is initialised to zero + SRSRAN_MEM_ZERO(report_cfg, srsran_csi_report_cfg_t, SRSRAN_CSI_SLOT_MAX_NOF_REPORT); + // Iterate every possible configured CSI report for (uint32_t i = 0; i < SRSRAN_CSI_MAX_NOF_REPORT; i++) { // Skip if report is not configured or triggered - if (!csi_report_trigger(&cfg->reports[i], slot_idx)) { + if (!csi_report_trigger(&cfg->reports[i], slot_cfg->idx)) { continue; } - // Select channel measurement - if (cfg->reports->channel_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { - ERROR("Channel measurement ID (%d) is out of range", cfg->reports->channel_meas_id); + if (count >= SRSRAN_CSI_SLOT_MAX_NOF_REPORT) { + ERROR("The number of CSI reports in the slot (%d) exceeds the maximum (%d)", + count++, + SRSRAN_CSI_SLOT_MAX_NOF_REPORT); return SRSRAN_ERROR; } - const srsran_csi_measurements_t* channel_meas = &measurements[cfg->reports->channel_meas_id]; + + // Configure report + report_cfg[count].cfg = cfg->reports[i]; + report_cfg[count].nof_ports = 1; + report_cfg[count].K_csi_rs = 1; + report_cfg[count].has_part2 = false; + count++; + } + + return (int)count; +} + +int srsran_csi_reports_quantify(const srsran_csi_report_cfg_t reports[SRSRAN_CSI_SLOT_MAX_NOF_REPORT], + const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_report_value_t report_value[SRSRAN_CSI_SLOT_MAX_NOF_REPORT]) +{ + uint32_t count = 0; + + // Check inputs + if (reports == NULL || measurements == NULL || report_value == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Iterate every possible configured CSI report + for (uint32_t i = 0; i < SRSRAN_CSI_SLOT_MAX_NOF_REPORT; i++) { + // If the report is the last one, break + if (reports->cfg.type == SRSRAN_CSI_REPORT_TYPE_NONE) { + break; + } + + // Select channel measurement + uint32_t channel_meas_id = reports[i].cfg.channel_meas_id; + if (channel_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { + ERROR("Channel measurement ID (%d) is out of range", channel_meas_id); + return SRSRAN_ERROR; + } + const srsran_csi_channel_measurements_t* channel_meas = &measurements[channel_meas_id]; // Select interference measurement - const srsran_csi_measurements_t* interf_meas = NULL; - if (cfg->reports->interf_meas_present) { - if (cfg->reports->interf_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { - ERROR("Interference measurement ID (%d) is out of range", cfg->reports->interf_meas_id); + const srsran_csi_channel_measurements_t* interf_meas = NULL; + if (reports[i].cfg.interf_meas_present) { + uint32_t interf_meas_id = reports[i].cfg.interf_meas_id; + if (interf_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { + ERROR("Interference measurement ID (%d) is out of range", interf_meas_id); return SRSRAN_ERROR; } - interf_meas = &measurements[cfg->reports->interf_meas_id]; + interf_meas = &measurements[interf_meas_id]; } // Quantify measurements according to frequency and quantity configuration - if (cfg->reports->freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && - cfg->reports->quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { - csi_wideband_cri_ri_pmi_cqi_quantify( - &cfg->reports[i], channel_meas, interf_meas, &report_cfg[count], &report_value[count]); + if (reports[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && + reports[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + csi_wideband_cri_ri_pmi_cqi_quantify(&reports[i].cfg, channel_meas, interf_meas, &report_value[count]); count++; } else { ; // Ignore other types @@ -214,9 +287,9 @@ int srsran_csi_part1_nof_bits(const srsran_csi_report_cfg_t* report_list, uint32 // Iterate all report configurations for (uint32_t i = 0; i < nof_reports; i++) { const srsran_csi_report_cfg_t* report = &report_list[i]; - if (report->quantity && report->quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + if (report->cfg.quantity && report->cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { count += csi_wideband_cri_ri_pmi_cqi_nof_bits(report); - } else if (report->quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { + } else if (report->cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { count += csi_none_nof_bits(report); } } @@ -269,15 +342,15 @@ int srsran_csi_part1_pack(const srsran_csi_report_cfg_t* report_cfg, } for (uint32_t i = 0; i < nof_reports && count < max_o_csi1; i++) { - if (report_cfg[i].freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && - report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + if (report_cfg[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && + report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { count += csi_wideband_cri_ri_pmi_cqi_pack(&report_cfg[i], &report_value[i], &o_csi1[count]); - } else if (report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { + } else if (report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { count += csi_none_pack(&report_cfg[i], &report_value[i], &o_csi1[count]); } else { ERROR("CSI frequency (%d) and quantity (%d) combination is not implemented", - report_cfg[i].freq_cfg, - report_cfg[i].quantity); + report_cfg[i].cfg.freq_cfg, + report_cfg[i].cfg.quantity); } } @@ -303,15 +376,15 @@ int srsran_csi_part1_unpack(const srsran_csi_report_cfg_t* report_cfg, } for (uint32_t i = 0; i < nof_reports && count < max_o_csi1; i++) { - if (report_cfg[i].freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && - report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + if (report_cfg[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && + report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { count += csi_wideband_cri_ri_pmi_cqi_unpack(&report_cfg[i], &o_csi1[count], &report_value[i]); - } else if (report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { + } else if (report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { count += csi_none_unpack(&report_cfg[i], &o_csi1[count], &report_value[i]); } else { ERROR("CSI frequency (%d) and quantity (%d) combination is not implemented", - report_cfg[i].freq_cfg, - report_cfg[i].quantity); + report_cfg[i].cfg.freq_cfg, + report_cfg[i].cfg.quantity); } } @@ -326,10 +399,10 @@ uint32_t srsran_csi_str(const srsran_csi_report_cfg_t* report_cfg, { uint32_t len = 0; for (uint32_t i = 0; i < nof_reports; i++) { - if (report_cfg[i].freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && - report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + if (report_cfg[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && + report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { len = srsran_print_check(str, str_len, len, "cqi=%d ", report_value[i].wideband_cri_ri_pmi_cqi.cqi); - } else if (report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { + } else if (report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { char tmp[20] = {}; srsran_vec_sprint_bin(tmp, sizeof(tmp), report_value[i].none, report_cfg->K_csi_rs); len = srsran_print_check(str, str_len, len, "csi=%s ", tmp); diff --git a/lib/src/phy/phch/dci.c b/lib/src/phy/phch/dci.c index caa5a9b78..782569f2e 100644 --- a/lib/src/phy/phch/dci.c +++ b/lib/src/phy/phch/dci.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -705,7 +705,7 @@ static int dci_format1_unpack(srsran_cell_t* cell, /* Packs DCI format 1A for compact scheduling of PDSCH words according to 36.212 5.3.3.1.3 * - * TODO: RA procedure initiated by PDCCH, TPC commands + * TODO: TPC commands */ static int dci_format1As_pack(srsran_cell_t* cell, srsran_dl_sf_cfg_t* sf, @@ -723,49 +723,64 @@ static int dci_format1As_pack(srsran_cell_t* cell, *y++ = 1; // format differentiation - if (dci->alloc_type != SRSRAN_RA_ALLOC_TYPE2) { - ERROR("Format 1A accepts type2 resource allocation only"); - return SRSRAN_ERROR; - } + // random access procedure initiated by a PDCCH order + if (dci->is_pdcch_order) { + *y++ = 0; // localized or distributed VRB assignment is always 0 for PDCCH order - *y++ = dci->type2_alloc.mode; // localized or distributed VRB assignment - - /* pack RIV according to 7.1.6.3 of 36.213 */ - uint32_t riv = dci->type2_alloc.riv; - uint32_t nb_gap = 0; - if (SRSRAN_RNTI_ISUSER(dci->rnti) && dci->type2_alloc.mode == SRSRAN_RA_TYPE2_DIST && nof_prb >= 50) { - nb_gap = 1; - *y++ = dci->type2_alloc.n_gap; - } - srsran_bit_unpack(riv, &y, riv_nbits(nof_prb) - nb_gap); - - // in format1A, MCS = TBS according to 7.1.7.2 of 36.213 - srsran_bit_unpack(dci->tb[0].mcs_idx, &y, 5); - - srsran_bit_unpack(dci->pid, &y, HARQ_PID_LEN); - - if (!SRSRAN_RNTI_ISUSER(dci->rnti)) { - if (nof_prb >= 50 && dci->type2_alloc.mode == SRSRAN_RA_TYPE2_DIST) { - *y++ = dci->type2_alloc.n_gap; - } else { - y++; // bit reserved + // RIV values all set to 1 for PDCCH order + int nof_bits = riv_nbits(cell->nof_prb); + int i = 0; + while (i < nof_bits) { + *y++ = 1; + i++; } + + srsran_bit_unpack(dci->preamble_idx, &y, 6); // preamble index + srsran_bit_unpack(dci->prach_mask_idx, &y, 4); // PRACH mask index } else { - *y++ = dci->tb[0].ndi; + if (dci->alloc_type != SRSRAN_RA_ALLOC_TYPE2) { + ERROR("Format 1A accepts type2 resource allocation only"); + return SRSRAN_ERROR; + } + + *y++ = dci->type2_alloc.mode; // localized or distributed VRB assignment + + /* pack RIV according to 7.1.6.3 of 36.213 */ + uint32_t riv = dci->type2_alloc.riv; + uint32_t nb_gap = 0; + if (SRSRAN_RNTI_ISUSER(dci->rnti) && dci->type2_alloc.mode == SRSRAN_RA_TYPE2_DIST && nof_prb >= 50) { + nb_gap = 1; + *y++ = dci->type2_alloc.n_gap; + } + srsran_bit_unpack(riv, &y, riv_nbits(nof_prb) - nb_gap); + + // in format1A, MCS = TBS according to 7.1.7.2 of 36.213 + srsran_bit_unpack(dci->tb[0].mcs_idx, &y, 5); + + srsran_bit_unpack(dci->pid, &y, HARQ_PID_LEN); + + if (!SRSRAN_RNTI_ISUSER(dci->rnti)) { + if (nof_prb >= 50 && dci->type2_alloc.mode == SRSRAN_RA_TYPE2_DIST) { + *y++ = dci->type2_alloc.n_gap; + } else { + y++; // bit reserved + } + } else { + *y++ = dci->tb[0].ndi; + } + + // rv version + srsran_bit_unpack(dci->tb[0].rv, &y, 2); + + if (SRSRAN_RNTI_ISUSER(dci->rnti)) { + // TPC not implemented + *y++ = 0; + *y++ = 0; + } else { + y++; // MSB of TPC is reserved + *y++ = dci->type2_alloc.n_prb1a; // LSB indicates N_prb_1a for TBS + } } - - // rv version - srsran_bit_unpack(dci->tb[0].rv, &y, 2); - - if (SRSRAN_RNTI_ISUSER(dci->rnti)) { - // TPC not implemented - *y++ = 0; - *y++ = 0; - } else { - y++; // MSB of TPC is reserved - *y++ = dci->type2_alloc.n_prb1a; // LSB indicates N_prb_1a for TBS - } - // Padding with zeros uint32_t n = srsran_dci_format_sizeof(cell, sf, cfg, SRSRAN_DCI_FORMAT1A); while (y - msg->payload < n) { @@ -819,16 +834,16 @@ static int dci_format1As_unpack(srsran_cell_t* cell, // This is a Random access order y += 1 + nof_bits; - dci->is_ra_order = true; - dci->ra_preamble = srsran_bit_pack(&y, 6); - dci->ra_mask_idx = srsran_bit_pack(&y, 4); + dci->is_pdcch_order = true; + dci->preamble_idx = srsran_bit_pack(&y, 6); + dci->prach_mask_idx = srsran_bit_pack(&y, 4); return SRSRAN_SUCCESS; } } } - dci->is_ra_order = false; + dci->is_pdcch_order = false; dci->alloc_type = SRSRAN_RA_ALLOC_TYPE2; dci->type2_alloc.mode = *y++; @@ -1389,10 +1404,12 @@ bool srsran_location_find(const srsran_dci_location_t* locations, uint32_t nof_l return false; } -bool srsran_location_find_ncce(const srsran_dci_location_t* locations, uint32_t nof_locations, uint32_t ncce) +bool srsran_location_find_location(const srsran_dci_location_t* locations, + uint32_t nof_locations, + const srsran_dci_location_t* location) { for (uint32_t i = 0; i < nof_locations; i++) { - if (locations[i].ncce == ncce) { + if (locations[i].ncce == location->ncce && locations[i].L == location->L) { return true; } } @@ -1551,36 +1568,46 @@ static char* freq_hop_fl_string(int freq_hop) void srsran_dci_dl_fprint(FILE* f, srsran_dci_dl_t* dci, uint32_t nof_prb) { - fprintf(f, " - Resource Allocation Type:\t\t%s\n", ra_type_string(dci->alloc_type)); - switch (dci->alloc_type) { - case SRSRAN_RA_ALLOC_TYPE0: - fprintf(f, " + Resource Block Group Size:\t\t%d\n", srsran_ra_type0_P(nof_prb)); - fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type0_alloc.rbg_bitmask); - break; - case SRSRAN_RA_ALLOC_TYPE1: - fprintf(f, " + Resource Block Group Size:\t\t%d\n", srsran_ra_type0_P(nof_prb)); - fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type1_alloc.vrb_bitmask); - fprintf(f, " + RBG Subset:\t\t\t%d\n", dci->type1_alloc.rbg_subset); - fprintf(f, " + RBG Shift:\t\t\t\t%s\n", dci->type1_alloc.shift ? "Yes" : "No"); - break; - case SRSRAN_RA_ALLOC_TYPE2: - fprintf(f, " + Type:\t\t\t\t%s\n", dci->type2_alloc.mode == SRSRAN_RA_TYPE2_LOC ? "Localized" : "Distributed"); - fprintf(f, " + Resource Indicator Value:\t\t%d\n", dci->type2_alloc.riv); - break; - } - if (dci->cif_present) { - fprintf(f, " - Carrier idx:\t\t\t\t%d\n", dci->cif); - } - fprintf(f, " - HARQ process:\t\t\t%d\n", dci->pid); - fprintf(f, " - TPC command for PUCCH:\t\t--\n"); - fprintf(f, " - Transport blocks swapped:\t\t%s\n", (dci->tb_cw_swap) ? "true" : "false"); + if (dci->is_pdcch_order) { + fprintf(f, "PDCCH order:\n"); + if (dci->cif_present) { + fprintf(f, " - Carrier idx:\t\t\t\t%d\n", dci->cif); + } + fprintf(f, " - Preamble index:\t\t%d\n", dci->preamble_idx); + fprintf(f, " - PRACH mask index:\t\t%d\n", dci->prach_mask_idx); + } else { + fprintf(f, " - Resource Allocation Type:\t\t%s\n", ra_type_string(dci->alloc_type)); + switch (dci->alloc_type) { + case SRSRAN_RA_ALLOC_TYPE0: + fprintf(f, " + Resource Block Group Size:\t\t%d\n", srsran_ra_type0_P(nof_prb)); + fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type0_alloc.rbg_bitmask); + break; + case SRSRAN_RA_ALLOC_TYPE1: + fprintf(f, " + Resource Block Group Size:\t\t%d\n", srsran_ra_type0_P(nof_prb)); + fprintf(f, " + RBG Bitmap:\t\t\t0x%x\n", dci->type1_alloc.vrb_bitmask); + fprintf(f, " + RBG Subset:\t\t\t%d\n", dci->type1_alloc.rbg_subset); + fprintf(f, " + RBG Shift:\t\t\t\t%s\n", dci->type1_alloc.shift ? "Yes" : "No"); + break; + case SRSRAN_RA_ALLOC_TYPE2: + fprintf( + f, " + Type:\t\t\t\t%s\n", dci->type2_alloc.mode == SRSRAN_RA_TYPE2_LOC ? "Localized" : "Distributed"); + fprintf(f, " + Resource Indicator Value:\t\t%d\n", dci->type2_alloc.riv); + break; + } + if (dci->cif_present) { + fprintf(f, " - Carrier idx:\t\t\t\t%d\n", dci->cif); + } + fprintf(f, " - HARQ process:\t\t\t%d\n", dci->pid); + fprintf(f, " - TPC command for PUCCH:\t\t--\n"); + fprintf(f, " - Transport blocks swapped:\t\t%s\n", (dci->tb_cw_swap) ? "true" : "false"); - for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { - fprintf(f, " - Transport block %d enabled:\t\t%s\n", i, SRSRAN_DCI_IS_TB_EN(dci->tb[i]) ? "true" : "false"); - if (SRSRAN_DCI_IS_TB_EN(dci->tb[i])) { - fprintf(f, " + Modulation and coding scheme index:\t%d\n", dci->tb[i].mcs_idx); - fprintf(f, " + New data indicator:\t\t\t%s\n", dci->tb[i].ndi ? "Yes" : "No"); - fprintf(f, " + Redundancy version:\t\t\t%d\n", dci->tb[i].rv); + for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { + fprintf(f, " - Transport block %d enabled:\t\t%s\n", i, SRSRAN_DCI_IS_TB_EN(dci->tb[i]) ? "true" : "false"); + if (SRSRAN_DCI_IS_TB_EN(dci->tb[i])) { + fprintf(f, " + Modulation and coding scheme index:\t%d\n", dci->tb[i].mcs_idx); + fprintf(f, " + New data indicator:\t\t\t%s\n", dci->tb[i].ndi ? "Yes" : "No"); + fprintf(f, " + Redundancy version:\t\t\t%d\n", dci->tb[i].rv); + } } } } @@ -1625,46 +1652,51 @@ uint32_t srsran_dci_dl_info(const srsran_dci_dl_t* dci_dl, char* info_str, uint3 n = srsran_print_check(info_str, len, n, ", cif=%d", dci_dl->cif); } - switch (dci_dl->alloc_type) { - case SRSRAN_RA_ALLOC_TYPE0: - n = srsran_print_check(info_str, len, n, ", rbg=0x%x", dci_dl->type0_alloc.rbg_bitmask); - break; - case SRSRAN_RA_ALLOC_TYPE1: - n = srsran_print_check(info_str, - len, - n, - ", vrb=0x%x, rbg_s=%d, sh=%d", - dci_dl->type1_alloc.vrb_bitmask, - dci_dl->type1_alloc.rbg_subset, - dci_dl->type1_alloc.shift); - break; - case SRSRAN_RA_ALLOC_TYPE2: - n = srsran_print_check(info_str, len, n, ", riv=%d", dci_dl->type2_alloc.riv); - break; - } + if (dci_dl->is_pdcch_order) { + n = srsran_print_check(info_str, len, n, ", preamb_idx=%d", dci_dl->preamble_idx); + n = srsran_print_check(info_str, len, n, ", prach_mask_idx=%d", dci_dl->prach_mask_idx); + } else { + switch (dci_dl->alloc_type) { + case SRSRAN_RA_ALLOC_TYPE0: + n = srsran_print_check(info_str, len, n, ", rbg=0x%x", dci_dl->type0_alloc.rbg_bitmask); + break; + case SRSRAN_RA_ALLOC_TYPE1: + n = srsran_print_check(info_str, + len, + n, + ", vrb=0x%x, rbg_s=%d, sh=%d", + dci_dl->type1_alloc.vrb_bitmask, + dci_dl->type1_alloc.rbg_subset, + dci_dl->type1_alloc.shift); + break; + case SRSRAN_RA_ALLOC_TYPE2: + n = srsran_print_check(info_str, len, n, ", riv=%d", dci_dl->type2_alloc.riv); + break; + } - n = srsran_print_check(info_str, len, n, ", pid=%d", dci_dl->pid); + n = srsran_print_check(info_str, len, n, ", pid=%d", dci_dl->pid); - n = srsran_print_check(info_str, len, n, ", mcs={"); - n = print_multi(info_str, n, len, dci_dl, 0); - n = srsran_print_check(info_str, len, n, "}"); - n = srsran_print_check(info_str, len, n, ", ndi={"); - n = print_multi(info_str, n, len, dci_dl, 2); - n = srsran_print_check(info_str, len, n, "}"); + n = srsran_print_check(info_str, len, n, ", mcs={"); + n = print_multi(info_str, n, len, dci_dl, 0); + n = srsran_print_check(info_str, len, n, "}"); + n = srsran_print_check(info_str, len, n, ", ndi={"); + n = print_multi(info_str, n, len, dci_dl, 2); + n = srsran_print_check(info_str, len, n, "}"); - if (dci_dl->format == SRSRAN_DCI_FORMAT1 || dci_dl->format == SRSRAN_DCI_FORMAT1A || - dci_dl->format == SRSRAN_DCI_FORMAT1B || dci_dl->format == SRSRAN_DCI_FORMAT2 || - dci_dl->format == SRSRAN_DCI_FORMAT2A || dci_dl->format == SRSRAN_DCI_FORMAT2B) { - n = srsran_print_check(info_str, len, n, ", tpc_pucch=%d", dci_dl->tpc_pucch); - } + if (dci_dl->format == SRSRAN_DCI_FORMAT1 || dci_dl->format == SRSRAN_DCI_FORMAT1A || + dci_dl->format == SRSRAN_DCI_FORMAT1B || dci_dl->format == SRSRAN_DCI_FORMAT2 || + dci_dl->format == SRSRAN_DCI_FORMAT2A || dci_dl->format == SRSRAN_DCI_FORMAT2B) { + n = srsran_print_check(info_str, len, n, ", tpc_pucch=%d", dci_dl->tpc_pucch); + } - if (dci_dl->is_tdd) { - n = srsran_print_check(info_str, len, n, ", dai=%d", dci_dl->dai); - } + if (dci_dl->is_tdd) { + n = srsran_print_check(info_str, len, n, ", dai=%d", dci_dl->dai); + } - if (dci_dl->format == SRSRAN_DCI_FORMAT2 || dci_dl->format == SRSRAN_DCI_FORMAT2A || - dci_dl->format == SRSRAN_DCI_FORMAT2B) { - n = srsran_print_check(info_str, len, n, ", tb_sw=%d, pinfo=%d", dci_dl->tb_cw_swap, dci_dl->pinfo); + if (dci_dl->format == SRSRAN_DCI_FORMAT2 || dci_dl->format == SRSRAN_DCI_FORMAT2A || + dci_dl->format == SRSRAN_DCI_FORMAT2B) { + n = srsran_print_check(info_str, len, n, ", tb_sw=%d, pinfo=%d", dci_dl->tb_cw_swap, dci_dl->pinfo); + } } #if SRSRAN_DCI_HEXDEBUG diff --git a/lib/src/phy/phch/dci_nbiot.c b/lib/src/phy/phch/dci_nbiot.c index 2b7d941c6..15b67ab8c 100644 --- a/lib/src/phy/phch/dci_nbiot.c +++ b/lib/src/phy/phch/dci_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/dci_nr.c b/lib/src/phy/phch/dci_nr.c index 97db2150e..abfc99640 100644 --- a/lib/src/phy/phch/dci_nr.c +++ b/lib/src/phy/phch/dci_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,15 +31,13 @@ */ #define DCI_NR_MIN_SIZE 12 -#define CEIL_LOG2(N) (((N) == 0) ? 0 : ceil(log2((double)(N)))) - static uint32_t dci_nr_freq_resource_size_type1(uint32_t N) { if (N == 0) { return 0; } - return (int)CEIL_LOG2(N * (N + 1) / 2.0); + return (int)SRSRAN_CEIL_LOG2(N * (N + 1) / 2.0); } static uint32_t dci_nr_freq_resource_size(srsran_resource_alloc_t alloc_type, uint32_t N_RBG, uint32_t N_BWP_RB) @@ -66,7 +64,7 @@ static uint32_t dci_nr_bwp_id_size(uint32_t N_BWP_RRC) N_BWP = N_BWP_RRC + 1; } - return (int)CEIL_LOG2(N_BWP); + return (int)SRSRAN_CEIL_LOG2(N_BWP); } static uint32_t dci_nr_time_res_size(uint32_t nof_time_res) @@ -75,7 +73,7 @@ static uint32_t dci_nr_time_res_size(uint32_t nof_time_res) // 4 bits are necessary for PUSCH default time resource assigment (TS 38.214 Table 6.1.2.1.1-2) nof_time_res = SRSRAN_MAX_NOF_TIME_RA; } - return (uint32_t)CEIL_LOG2(nof_time_res); + return (uint32_t)SRSRAN_CEIL_LOG2(nof_time_res); } static uint32_t dci_nr_ptrs_size(const srsran_dci_cfg_nr_t* cfg) @@ -93,6 +91,65 @@ static uint32_t dci_nr_ptrs_size(const srsran_dci_cfg_nr_t* cfg) return 2; } +static uint32_t dci_nr_dl_ports_size(const srsran_dci_cfg_nr_t* cfg) +{ + uint32_t ret = 4; + + if (cfg->pdsch_dmrs_type == srsran_dmrs_sch_type_2) { + ret++; + } + if (cfg->pdsch_dmrs_max_len == srsran_dmrs_sch_len_2) { + ret++; + } + + return ret; +} + +static uint32_t dci_nr_ul_ports_size(const srsran_dci_cfg_nr_t* cfg) +{ + // 2 bits as defined by Tables 7.3.1.1.2-6, if transform precoder is enabled, dmrs-Type=1, and maxLength=1; + if (cfg->enable_transform_precoding == true && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) { + return 2; + } + + // 4 bits as defined by Tables 7.3.1.1.2-7, if transform precoder is enabled, dmrs-Type=1, and maxLength=2; + if (cfg->enable_transform_precoding == true && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) { + return 4; + } + + // 3 bits as defined by Tables 7.3.1.1.2-8/9/10/11, if transform precoder is disabled, dmrs-Type=1, and maxLength=1 + if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) { + return 3; + } + + // 4 bits as defined by Tables 7.3.1.1.2-12/13/14/15, if transform precoder is disabled, dmrs-Type=1, and + // maxLength=2 + if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_1 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) { + return 4; + } + + // 4 bits as defined by Tables 7.3.1.1.2-16/17/18/19, if transform precoder is disabled, dmrs-Type=2, and + // maxLength=1 + if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_2 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) { + return 4; + } + + // 5 bits as defined by Tables 7.3.1.1.2-20/21/22/23, if transform precoder is disabled, dmrs-Type=2, and + // maxLength=2 + if (cfg->enable_transform_precoding == false && cfg->pusch_dmrs_type == srsran_dmrs_sch_type_2 && + cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_2) { + return 5; + } + + ERROR("Unhandled configuration"); + return 0; +} + static uint32_t dci_nr_srs_id_size(const srsran_dci_cfg_nr_t* cfg) { uint32_t N_srs = SRSRAN_MIN(1, cfg->nof_srs); @@ -101,9 +158,9 @@ static uint32_t dci_nr_srs_id_size(const srsran_dci_cfg_nr_t* cfg) for (uint32_t k = 1; k < SRSRAN_MIN(cfg->nof_ul_layers, cfg->nof_srs); k++) { N += cfg->nof_srs / k; } - return (uint32_t)CEIL_LOG2(N); + return (uint32_t)SRSRAN_CEIL_LOG2(N); } - return (uint32_t)CEIL_LOG2(N_srs); + return (uint32_t)SRSRAN_CEIL_LOG2(N_srs); } // Determines DCI format 0_0 according to TS 38.212 clause 7.3.1.1.1 @@ -167,7 +224,8 @@ static int dci_nr_format_0_0_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_ msg->ctx = dci->ctx; // Check RNTI type - if (rnti_type != srsran_rnti_type_c && rnti_type != srsran_rnti_type_cs && rnti_type != srsran_rnti_type_mcs_c) { + if (rnti_type != srsran_rnti_type_c && rnti_type != srsran_rnti_type_cs && rnti_type != srsran_rnti_type_mcs_c && + rnti_type != srsran_rnti_type_tc) { return SRSRAN_ERROR; } @@ -237,7 +295,8 @@ static int dci_nr_format_0_0_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ uint32_t N_UL_BWP_RB = SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type) ? q->cfg.bwp_ul_initial_bw : q->cfg.bwp_ul_active_bw; // Check RNTI type - if (rnti_type != srsran_rnti_type_c && rnti_type != srsran_rnti_type_cs && rnti_type != srsran_rnti_type_mcs_c) { + if (rnti_type != srsran_rnti_type_c && rnti_type != srsran_rnti_type_cs && rnti_type != srsran_rnti_type_mcs_c && + rnti_type != srsran_rnti_type_tc) { ERROR("Unsupported %s", srsran_rnti_type_str(rnti_type)); return SRSRAN_ERROR; } @@ -260,9 +319,7 @@ static int dci_nr_format_0_0_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ // Frequency domain resource assignment uint32_t N = dci_nr_freq_resource_size_type1(N_UL_BWP_RB); - if (N < N_UL_hop) { - return SRSRAN_ERROR; - } + dci->freq_domain_assigment = srsran_bit_pack(&y, N - N_UL_hop - trim); // Time domain resource assignment – 4 bits @@ -299,7 +356,7 @@ static int dci_nr_format_0_0_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ return SRSRAN_SUCCESS; } -static int dci_nr_format_0_0_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) +static uint32_t dci_nr_format_0_0_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) { uint32_t len = 0; @@ -402,12 +459,7 @@ static uint32_t dci_nr_format_0_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_ } // Antenna ports - if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { - count += 3; - } else { - ERROR("Not implemented"); - return 0; - } + count += dci_nr_ul_ports_size(cfg); // SRS request - 2 or 3 bits count += cfg->enable_sul ? 3 : 2; @@ -511,12 +563,7 @@ static int dci_nr_format_0_1_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_ } // Antenna ports - if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { - srsran_bit_unpack(dci->ports, &y, 3); - } else { - ERROR("Not implemented"); - return 0; - } + srsran_bit_unpack(dci->ports, &y, dci_nr_ul_ports_size(cfg)); // SRS request - 2 or 3 bits srsran_bit_unpack(dci->srs_request, &y, cfg->enable_sul ? 3 : 2); @@ -628,7 +675,7 @@ static int dci_nr_format_0_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ } // Antenna ports - if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { + if (!cfg->enable_transform_precoding && cfg->pusch_dmrs_max_len == srsran_dmrs_sch_len_1) { dci->ports = srsran_bit_pack(&y, 3); } else { ERROR("Not implemented"); @@ -668,7 +715,7 @@ static int dci_nr_format_0_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ return SRSRAN_SUCCESS; } -static int +static uint32_t dci_nr_format_0_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) { uint32_t len = 0; @@ -735,11 +782,8 @@ dci_nr_format_0_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci } // Antenna ports - if (!cfg->enable_transform_precoding && !cfg->pusch_dmrs_double) { + if (dci_nr_ul_ports_size(cfg)) { len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports); - } else { - ERROR("Not implemented"); - return 0; } // SRS request - 2 bits @@ -776,6 +820,112 @@ dci_nr_format_0_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci return len; } +static uint32_t dci_nr_rar_sizeof() +{ + // Fields described by TS 38.213 Table 8.2-1: Random Access Response Grant Content field size + uint32_t count = 0; + + // Frequency hopping flag - 1 bit + count += 1; + + // PUSCH frequency resource allocation - 14 bits + count += 14; + + // PUSCH time resource allocation - 4 bits + count += 4; + + // MCS - 4 bits + count += 4; + + // TPC command for PUSCH - 3 bits + count += 3; + + // CSI request - 1 bits + count += 1; + + return count; +} + +static int dci_nr_rar_pack(const srsran_dci_ul_nr_t* dci, srsran_dci_msg_nr_t* msg) +{ + // Fields described by TS 38.213 Table 8.2-1: Random Access Response Grant Content field size + uint8_t* y = msg->payload; + + // Frequency hopping flag - 1 bit + srsran_bit_unpack(dci->freq_hopping_flag, &y, 1); + + // PUSCH frequency resource allocation - 14 bits + srsran_bit_unpack(dci->freq_domain_assigment, &y, 14); + + // PUSCH time resource allocation - 4 bits + srsran_bit_unpack(dci->time_domain_assigment, &y, 4); + + // MCS - 4 bits + srsran_bit_unpack(dci->mcs, &y, 4); + + // TPC command for PUSCH - 3 bits + srsran_bit_unpack(dci->tpc, &y, 3); + + // CSI request - 1 bits + srsran_bit_unpack(dci->csi_request, &y, 1); + + return SRSRAN_SUCCESS; +} + +static int dci_nr_rar_unpack(srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci) +{ + // Fields described by TS 38.213 Table 8.2-1: Random Access Response Grant Content field size + uint8_t* y = msg->payload; + + // Copy DCI MSG fields + dci->ctx = msg->ctx; + + // Frequency hopping flag - 1 bit + dci->freq_hopping_flag = srsran_bit_pack(&y, 1); + + // PUSCH frequency resource allocation - 14 bits + dci->freq_domain_assigment = srsran_bit_pack(&y, 14); + + // PUSCH time resource allocation - 4 bits + dci->time_domain_assigment = srsran_bit_pack(&y, 4); + + // MCS -4 bits + dci->mcs = srsran_bit_pack(&y, 4); + + // TPC command for PUSCH - 3 bits + dci->tpc = srsran_bit_pack(&y, 3); + + // CSI request - 1 bits + dci->csi_request = srsran_bit_pack(&y, 1); + + return SRSRAN_SUCCESS; +} + +static uint32_t dci_nr_rar_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + // Frequency hopping flag + len = srsran_print_check(str, str_len, len, "hop=%d ", dci->freq_hopping_flag); + + // PUSCH frequency resource allocation + len = srsran_print_check(str, str_len, len, "f_alloc=0x%x ", dci->freq_domain_assigment); + + // PUSCH time resource allocation + len = srsran_print_check(str, str_len, len, "t_alloc=0x%x ", dci->time_domain_assigment); + + // Modulation and coding scheme + len = srsran_print_check(str, str_len, len, "mcs=%d ", dci->mcs); + + // TPC command for scheduled PUSCH + len = srsran_print_check(str, str_len, len, "tpc=%d ", dci->tpc); + + // CSI request + len = srsran_print_check(str, str_len, len, "csi=%d ", dci->csi_request); + + return len; +} + static uint32_t dci_nr_format_1_0_sizeof(uint32_t N_DL_BWP_RB, srsran_rnti_type_t rnti_type) { uint32_t count = 0; @@ -1088,7 +1238,7 @@ static int dci_nr_format_1_0_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ return SRSRAN_SUCCESS; } -static int dci_nr_format_1_0_to_str(const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len) +static uint32_t dci_nr_format_1_0_to_str(const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len) { uint32_t len = 0; srsran_rnti_type_t rnti_type = dci->ctx.rnti_type; @@ -1126,12 +1276,13 @@ static int dci_nr_format_1_0_to_str(const srsran_dci_dl_nr_t* dci, char* str, ui // HARQ process number – 4 bits if (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_tc) { - len = srsran_print_check(str, str_len, len, "harq_id=%d ", dci->harq_feedback); + len = srsran_print_check(str, str_len, len, "harq_id=%d ", dci->pid); } // System information indicator – 1 bit if (rnti_type == srsran_rnti_type_si) { len = srsran_print_check(str, str_len, len, "sii=%d ", dci->sii); + len = srsran_print_check(str, str_len, len, "coreset0_bw=%d ", dci->coreset0_bw); } // Downlink assignment index – 2 bits @@ -1203,7 +1354,7 @@ static uint32_t dci_nr_format_1_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_ } // ZP CSI-RS trigger - 0, 1, or 2 bits - count += (int)CEIL_LOG2(cfg->nof_aperiodic_zp + 1); + count += (int)SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1); // For transport block 1: // Modulation and coding scheme – 5 bits @@ -1246,16 +1397,10 @@ static uint32_t dci_nr_format_1_1_sizeof(const srsran_dci_cfg_nr_t* cfg, srsran_ count += 3; // PDSCH-to-HARQ_feedback timing indicator – 0, 1, 2, or 3 bits - count += (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack); + count += (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack); // Antenna port(s) – 4, 5, or 6 bits - count += 4; - if (cfg->pdsch_dmrs_type2) { - count++; - } - if (cfg->pdsch_dmrs_double) { - count++; - } + count += dci_nr_dl_ports_size(cfg); // Transmission configuration indication – 0 or 3 bits if (cfg->pdsch_tci) { @@ -1325,7 +1470,7 @@ static int dci_nr_format_1_1_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_ } // ZP CSI-RS trigger - 0, 1, or 2 bits - srsran_bit_unpack(dci->zp_csi_rs_id, &y, CEIL_LOG2(cfg->nof_aperiodic_zp + 1)); + srsran_bit_unpack(dci->zp_csi_rs_id, &y, SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1)); // For transport block 1: // Modulation and coding scheme – 5 bits @@ -1368,16 +1513,10 @@ static int dci_nr_format_1_1_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_ srsran_bit_unpack(dci->pucch_resource, &y, 3); // PDSCH-to-HARQ_feedback timing indicator – 0, 1, 2, or 3 bits - srsran_bit_unpack(dci->harq_feedback, &y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack)); + srsran_bit_unpack(dci->harq_feedback, &y, (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack)); // Antenna port(s) – 4, 5, or 6 bits - srsran_bit_unpack(dci->ports, &y, 4); - if (cfg->pdsch_dmrs_type2) { - y++; - } - if (cfg->pdsch_dmrs_double) { - y++; - } + srsran_bit_unpack(dci->ports, &y, dci_nr_dl_ports_size(cfg)); // Transmission configuration indication – 0 or 3 bits if (cfg->pdsch_tci) { @@ -1461,7 +1600,7 @@ static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ } // ZP CSI-RS trigger - 0, 1, or 2 bits - dci->zp_csi_rs_id = srsran_bit_pack(&y, CEIL_LOG2(cfg->nof_aperiodic_zp + 1)); + dci->zp_csi_rs_id = srsran_bit_pack(&y, SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1)); // For transport block 1: // Modulation and coding scheme – 5 bits @@ -1504,16 +1643,10 @@ static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ dci->pucch_resource = srsran_bit_pack(&y, 3); // PDSCH-to-HARQ_feedback timing indicator – 0, 1, 2, or 3 bits - dci->harq_feedback = srsran_bit_pack(&y, (int)CEIL_LOG2(cfg->nof_dl_to_ul_ack)); + dci->harq_feedback = srsran_bit_pack(&y, (int)SRSRAN_CEIL_LOG2(cfg->nof_dl_to_ul_ack)); // Antenna port(s) – 4, 5, or 6 bits - dci->ports = srsran_bit_pack(&y, 4); - if (cfg->pdsch_dmrs_type2) { - y++; - } - if (cfg->pdsch_dmrs_double) { - y++; - } + dci->ports = srsran_bit_pack(&y, dci_nr_dl_ports_size(cfg)); // Transmission configuration indication – 0 or 3 bits if (cfg->pdsch_tci) { @@ -1543,7 +1676,7 @@ static int dci_nr_format_1_1_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_ return SRSRAN_SUCCESS; } -static int +static uint32_t dci_nr_format_1_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len) { uint32_t len = 0; @@ -1585,7 +1718,7 @@ dci_nr_format_1_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci } // ZP CSI-RS trigger - 0, 1, or 2 bits - if (CEIL_LOG2(cfg->nof_aperiodic_zp + 1) > 0) { + if (SRSRAN_CEIL_LOG2(cfg->nof_aperiodic_zp + 1) > 0) { len = srsran_print_check(str, str_len, len, "zp_csi_rs_id=%d ", dci->zp_csi_rs_id); } @@ -1628,7 +1761,9 @@ dci_nr_format_1_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci } // Antenna port(s) – 4, 5, or 6 bits - len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports); + if (dci_nr_dl_ports_size(cfg) > 0) { + len = srsran_print_check(str, str_len, len, "ports=%d ", dci->ports); + } // Transmission configuration indication – 0 or 3 bits if (cfg->pdsch_tci) { @@ -1656,6 +1791,10 @@ dci_nr_format_1_1_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci int srsran_dci_nr_set_cfg(srsran_dci_nr_t* q, const srsran_dci_cfg_nr_t* cfg) { + if (q == NULL || cfg == NULL) { + return SRSRAN_ERROR; + } + // Reset current setup SRSRAN_MEM_ZERO(q, srsran_dci_nr_t, 1); @@ -1797,6 +1936,11 @@ uint32_t srsran_dci_nr_size(const srsran_dci_nr_t* q, srsran_search_space_type_t return q->dci_1_1_size; } + // RAR packed MSG3 DCI + if (format == srsran_dci_format_nr_rar) { + return dci_nr_rar_sizeof(); + } + // Not implemented return 0; } @@ -1825,71 +1969,12 @@ bool srsran_dci_nr_valid_direction(const srsran_dci_msg_nr_t* dci) return (dci->ctx.format == srsran_dci_format_nr_1_0); } -static int dci_nr_rar_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, srsran_dci_msg_nr_t* msg) +int srsran_dci_nr_dl_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, srsran_dci_msg_nr_t* msg) { - ERROR("Not implemented"); - return SRSRAN_ERROR; -} - -static int dci_nr_rar_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci) -{ - if (msg == NULL || dci == NULL) { + if (q == NULL || dci == NULL || msg == NULL) { return SRSRAN_ERROR; } - uint8_t* y = msg->payload; - - // Copy DCI MSG fields - dci->ctx = msg->ctx; - - // Frequency hopping flag - 1 bit - dci->freq_hopping_flag = srsran_bit_pack(&y, 1); - - // PUSCH frequency resource allocation - 14 bits - dci->freq_domain_assigment = srsran_bit_pack(&y, 14); - - // PUSCH time resource allocation - 4 bits - dci->time_domain_assigment = srsran_bit_pack(&y, 4); - - // MCS -4 bits - dci->mcs = srsran_bit_pack(&y, 4); - - // TPC command for PUSCH - 3 bits - dci->tpc = srsran_bit_pack(&y, 3); - - // CSI request - 1 bits - dci->csi_request = srsran_bit_pack(&y, 3); - - return SRSRAN_SUCCESS; -} - -static int dci_nr_rar_to_str(const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) -{ - uint32_t len = 0; - - // Frequency hopping flag - len = srsran_print_check(str, str_len, len, "hop=%d ", dci->freq_hopping_flag); - - // PUSCH frequency resource allocation - len = srsran_print_check(str, str_len, len, "f_alloc=0x%x ", dci->freq_domain_assigment); - - // PUSCH time resource allocation - len = srsran_print_check(str, str_len, len, "t_alloc=0x%x ", dci->time_domain_assigment); - - // Modulation and coding scheme - len = srsran_print_check(str, str_len, len, "mcs=%d ", dci->mcs); - - // TPC command for scheduled PUSCH - len = srsran_print_check(str, str_len, len, "tpc=%d ", dci->tpc); - - // CSI request - len = srsran_print_check(str, str_len, len, "csi=%d ", dci->csi_request); - - return len; -} - -int srsran_dci_nr_dl_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, srsran_dci_msg_nr_t* msg) -{ // Copy DCI MSG fields msg->ctx = dci->ctx; @@ -1908,8 +1993,13 @@ int srsran_dci_nr_dl_pack(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dc int srsran_dci_nr_dl_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_dl_nr_t* dci) { + if (q == NULL || dci == NULL || msg == NULL) { + return SRSRAN_ERROR; + } + // Copy DCI MSG fields - dci->ctx = msg->ctx; + dci->ctx = msg->ctx; + dci->coreset0_bw = q->cfg.coreset0_bw; // Pack DCI switch (msg->ctx.format) { @@ -1925,6 +2015,14 @@ int srsran_dci_nr_dl_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, int srsran_dci_nr_ul_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, srsran_dci_msg_nr_t* msg) { + if (msg == NULL || dci == NULL) { + return SRSRAN_ERROR; + } + + if (dci->ctx.format != srsran_dci_format_nr_rar && q == NULL) { + return SRSRAN_ERROR; + } + // Copy DCI MSG fields msg->ctx = dci->ctx; @@ -1935,7 +2033,7 @@ int srsran_dci_nr_ul_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dc case srsran_dci_format_nr_0_1: return dci_nr_format_0_1_pack(q, dci, msg); case srsran_dci_format_nr_rar: - return dci_nr_rar_pack(q, dci, msg); + return dci_nr_rar_pack(dci, msg); default: ERROR("Unsupported DCI format %d", msg->ctx.format); } @@ -1945,6 +2043,14 @@ int srsran_dci_nr_ul_pack(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dc int srsran_dci_nr_ul_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, srsran_dci_ul_nr_t* dci) { + if (msg == NULL || dci == NULL) { + return SRSRAN_ERROR; + } + + if (msg->ctx.format != srsran_dci_format_nr_rar && q == NULL) { + return SRSRAN_ERROR; + } + // Copy DCI MSG fields dci->ctx = msg->ctx; @@ -1955,19 +2061,30 @@ int srsran_dci_nr_ul_unpack(const srsran_dci_nr_t* q, srsran_dci_msg_nr_t* msg, case srsran_dci_format_nr_0_1: return dci_nr_format_0_1_unpack(q, msg, dci); case srsran_dci_format_nr_rar: - return dci_nr_rar_unpack(q, msg, dci); + return dci_nr_rar_unpack(msg, dci); default: ERROR("Unsupported DCI format %d", msg->ctx.format); } return SRSRAN_ERROR; } -int srsran_dci_ctx_to_str(const srsran_dci_ctx_t* ctx, char* str, uint32_t str_len) +uint32_t srsran_dci_ctx_to_str(const srsran_dci_ctx_t* ctx, char* str, uint32_t str_len) { + if (ctx == NULL || str == NULL) { + return 0; + } + uint32_t len = 0; // Print base - len = srsran_print_check(str, str_len, len, "rnti=%04x dci=%s ", ctx->rnti, srsran_dci_format_nr_string(ctx->format)); + len = srsran_print_check(str, + str_len, + len, + "%s-rnti=0x%04x dci=%s ss=%s ", + srsran_rnti_type_str_short(ctx->rnti_type), + ctx->rnti, + srsran_dci_format_nr_string(ctx->format), + srsran_ss_type_str(ctx->ss_type)); if (ctx->format != srsran_dci_format_nr_rar) { len = srsran_print_check(str, str_len, len, "L=%d cce=%d ", ctx->location.L, ctx->location.ncce); @@ -1976,8 +2093,12 @@ int srsran_dci_ctx_to_str(const srsran_dci_ctx_t* ctx, char* str, uint32_t str_l return len; } -int srsran_dci_ul_nr_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) +uint32_t srsran_dci_ul_nr_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* dci, char* str, uint32_t str_len) { + if (q == NULL || dci == NULL || str == NULL) { + return 0; + } + uint32_t len = 0; len += srsran_dci_ctx_to_str(&dci->ctx, &str[len], str_len - len); @@ -2001,8 +2122,12 @@ int srsran_dci_ul_nr_to_str(const srsran_dci_nr_t* q, const srsran_dci_ul_nr_t* return len; } -int srsran_dci_dl_nr_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len) +uint32_t srsran_dci_dl_nr_to_str(const srsran_dci_nr_t* q, const srsran_dci_dl_nr_t* dci, char* str, uint32_t str_len) { + if (q == NULL || dci == NULL || str == NULL) { + return SRSRAN_ERROR; + } + uint32_t len = 0; len += srsran_dci_ctx_to_str(&dci->ctx, &str[len], str_len - len); diff --git a/lib/src/phy/phch/harq_ack.c b/lib/src/phy/phch/harq_ack.c new file mode 100644 index 000000000..0a2a69dcd --- /dev/null +++ b/lib/src/phy/phch/harq_ack.c @@ -0,0 +1,362 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/phch/harq_ack.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" + +// Implements TS 38.213 Table 9.1.3-1: Value of counter DAI in DCI format 1_0 and of counter DAI or total DAI DCI format +// 1_1 +static uint32_t ue_dl_nr_V_DL_DAI(uint32_t dai) +{ + return dai + 1; +} + +static int harq_ack_gen_ack_type2(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_pdsch_ack_nr_t* ack_info, + srsran_uci_cfg_nr_t* uci_cfg) +{ + bool harq_ack_spatial_bundling = + ack_info->use_pusch ? cfg->harq_ack_spatial_bundling_pusch : cfg->harq_ack_spatial_bundling_pucch; + uint32_t m = 0; // PDCCH with DCI format 1_0 or DCI format 1_1 monitoring occasion index: lower index corresponds to + // earlier PDCCH with DCI format 1_0 or DCI format 1_1 monitoring occasion + uint32_t j = 0; + uint32_t V_temp = 0; + uint32_t V_temp2 = 0; + + uint32_t N_DL_cells = ack_info->nof_cc; // number of serving cells configured by higher layers for the UE + + // Initialise ACK bits + SRSRAN_MEM_ZERO(uci_cfg->ack.bits, srsran_harq_ack_bit_t, SRSRAN_HARQ_ACK_MAX_NOF_BITS); + + // The following code follows the exact pseudo-code provided in TS 38.213 9.1.3.1 Type-2 HARQ-ACK codebook ... + while (m < SRSRAN_UCI_NR_MAX_M) { + uint32_t c = 0; // serving cell index: lower indexes correspond to lower RRC indexes of corresponding cell + while (c < N_DL_cells) { + // Get ACK information of serving cell c for the PDCH monitoring occasion m + const srsran_harq_ack_m_t* ack = &ack_info->cc[c].m[m]; + + // Get DAI counter value + uint32_t V_DL_CDAI = ue_dl_nr_V_DL_DAI(ack->resource.v_dai_dl); + uint32_t V_DL_TDAI = ack->resource.dci_format_1_1 ? ue_dl_nr_V_DL_DAI(ack->resource.v_dai_dl) : UINT32_MAX; + + // Get ACK values + srsran_harq_ack_bit_t ack_tb0 = {}; + ack_tb0.tb0 = true; + ack_tb0.cc_idx = c; + ack_tb0.m_idx = m; + ack_tb0.pid = ack->resource.pid; + srsran_harq_ack_bit_t ack_tb1 = {}; + ack_tb1.tb1 = true; + ack_tb1.cc_idx = c; + ack_tb1.m_idx = m; + ack_tb1.pid = ack->resource.pid; + + // For a PDCCH monitoring occasion with DCI format 1_0 or DCI format 1_1 in the active DL BWP of a serving cell, + // when a UE receives a PDSCH with one transport block and the value of maxNrofCodeWordsScheduledByDCI is 2, the + // HARQ-ACK information is associated with the first transport block and the UE generates a NACK for the second + // transport block if harq-ACK-SpatialBundlingPUCCH is not provided and generates HARQ-ACK information with + // value of ACK for the second transport block if harq-ACK-SpatialBundlingPUCCH is provided. + if (cfg->max_cw_sched_dci_is_2 && !ack->second_tb_present) { + ack_tb1.tb1 = false; + } + + // if PDCCH monitoring occasion m is before an active DL BWP change on serving cell c or an active UL + // BWP change on the PCell and an active DL BWP change is not triggered by a DCI format 1_1 in PDCCH + // monitoring occasion m + if (ack->dl_bwp_changed || ack->ul_bwp_changed) { + c = c + 1; + } else { + if (ack->present) { + // Load ACK resource data into UCI info + uci_cfg->pucch.resource_id = ack_info->cc[c].m[m].resource.pucch_resource_id; + uci_cfg->pucch.n_cce_0 = ack_info->cc[c].m[m].resource.n_cce; + uci_cfg->pucch.rnti = ack_info->cc[c].m[m].resource.rnti; + + if (V_DL_CDAI <= V_temp) { + j = j + 1; + } + + V_temp = V_DL_CDAI; + + if (V_DL_TDAI == UINT32_MAX) { + V_temp2 = V_DL_CDAI; + } else { + V_temp2 = V_DL_TDAI; + } + + // if harq-ACK-SpatialBundlingPUCCH is not provided and m is a monitoring occasion for PDCCH with DCI format + // 1_0 or DCI format 1_1 and the UE is configured by maxNrofCodeWordsScheduledByDCI with reception of two + // transport blocks for at least one configured DL BWP of at least one serving cell, + if (!harq_ack_spatial_bundling && cfg->max_cw_sched_dci_is_2) { + uci_cfg->ack.bits[8 * j + 2 * (V_DL_CDAI - 1) + 0] = ack_tb0; + uci_cfg->ack.bits[8 * j + 2 * (V_DL_CDAI - 1) + 1] = ack_tb1; + } + // elseif harq-ACK-SpatialBundlingPUCCH is provided to the UE and m is a monitoring occasion for + // PDCCH with DCI format 1_1 and the UE is configured by maxNrofCodeWordsScheduledByDCI with + // reception of two transport blocks in at least one configured DL BWP of a serving cell, + else if (harq_ack_spatial_bundling && ack->resource.dci_format_1_1 && cfg->max_cw_sched_dci_is_2) { + ack_tb0.tb1 = true; + uci_cfg->ack.bits[4 * j + (V_DL_CDAI - 1)] = ack_tb0; + } + // else + else { + uci_cfg->ack.bits[4 * j + (V_DL_CDAI - 1)] = ack_tb0; + } + } + c = c + 1; + } + } + m = m + 1; + } + if (V_temp2 < V_temp) { + j = j + 1; + } + + // if harq-ACK-SpatialBundlingPUCCH is not provided to the UE and the UE is configured by + // maxNrofCodeWordsScheduledByDCI with reception of two transport blocks for at least one configured DL BWP of a + // serving cell, + if (!harq_ack_spatial_bundling && cfg->max_cw_sched_dci_is_2) { + uci_cfg->ack.count = 2 * (4 * j + V_temp2); + } else { + uci_cfg->ack.count = 4 * j + V_temp2; + } + + // Implement here SPS PDSCH reception + // ... + + return SRSRAN_SUCCESS; +} + +static int harq_ack_k1(const srsran_harq_ack_cfg_hl_t* cfg, const srsran_dci_dl_nr_t* dci_dl) +{ + // For DCI format 1_0, the PDSCH-to-HARQ_feedback timing indicator field values map to {1, 2, 3, 4, 5, 6, 7, 8} + if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) { + return (int)dci_dl->harq_feedback + 1; + } + + // For DCI format 1_1, if present, the PDSCH-to-HARQ_feedback timing indicator field values map to values for a set of + // number of slots provided by dl-DataToUL-ACK as defined in Table 9.2.3-1. + if (dci_dl->harq_feedback >= SRSRAN_MAX_NOF_DL_DATA_TO_UL || dci_dl->harq_feedback >= cfg->nof_dl_data_to_ul_ack) { + ERROR("Out-of-range PDSCH-to-HARQ feedback index (%d, max %d)", dci_dl->harq_feedback, cfg->nof_dl_data_to_ul_ack); + return SRSRAN_ERROR; + } + + return (int)cfg->dl_data_to_ul_ack[dci_dl->harq_feedback]; +} + +int srsran_harq_ack_resource(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_dci_dl_nr_t* dci_dl, + srsran_harq_ack_resource_t* pdsch_ack_resource) +{ + if (cfg == NULL || dci_dl == NULL || pdsch_ack_resource == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Calculate Data to UL ACK timing k1 + int k1 = harq_ack_k1(cfg, dci_dl); + if (k1 < SRSRAN_ERROR) { + ERROR("Error calculating K1"); + return SRSRAN_ERROR; + } + + // Fill PDSCH resource + pdsch_ack_resource->dci_format_1_1 = (dci_dl->ctx.format == srsran_dci_format_nr_1_1); + pdsch_ack_resource->k1 = k1; + pdsch_ack_resource->v_dai_dl = dci_dl->dai; + pdsch_ack_resource->rnti = dci_dl->ctx.rnti; + pdsch_ack_resource->pucch_resource_id = dci_dl->pucch_resource; + pdsch_ack_resource->n_cce = dci_dl->ctx.location.ncce; + pdsch_ack_resource->pid = dci_dl->pid; + + return SRSRAN_SUCCESS; +} + +int srsran_harq_ack_gen_uci_cfg(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_pdsch_ack_nr_t* ack_info, + srsran_uci_cfg_nr_t* uci_cfg) +{ + // Check inputs + if (cfg == NULL || ack_info == NULL || uci_cfg == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // According TS 38.213 9.1.2 Type-1 HARQ-ACK codebook determination + if (cfg->harq_ack_codebook == srsran_pdsch_harq_ack_codebook_semi_static) { + // This clause applies if the UE is configured with pdsch-HARQ-ACK-Codebook = semi-static. + ERROR("Type-1 HARQ-ACK codebook determination is NOT implemented"); + return SRSRAN_ERROR; + } + + // According TS 38.213 9.1.3 Type-2 HARQ-ACK codebook determination + if (cfg->harq_ack_codebook == srsran_pdsch_harq_ack_codebook_dynamic) { + // This clause applies if the UE is configured with pdsch-HARQ-ACK-Codebook = dynamic. + return harq_ack_gen_ack_type2(cfg, ack_info, uci_cfg); + } + + ERROR("No HARQ-ACK codebook determination is NOT implemented"); + return SRSRAN_ERROR; +} + +int srsran_harq_ack_pack(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_pdsch_ack_nr_t* ack_info, + srsran_uci_data_nr_t* uci_data) +{ + // Check inputs + if (cfg == NULL || ack_info == NULL || uci_data == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Generate configuration + if (srsran_harq_ack_gen_uci_cfg(cfg, ack_info, &uci_data->cfg) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Actual packing + for (uint32_t i = 0; i < uci_data->cfg.ack.count; i++) { + srsran_harq_ack_bit_t* ack_bit = &uci_data->cfg.ack.bits[i]; + + // Skip bit if no TB is used + if (!ack_bit->tb0 && !ack_bit->tb1) { + uci_data->value.ack[i] = 0; + continue; + } + + // Only TB0 + if (ack_bit->tb0 && !ack_bit->tb1) { + uci_data->value.ack[i] = ack_info->cc[ack_bit->cc_idx].m[ack_bit->m_idx].value[0]; + continue; + } + + // Only TB1 + if (!ack_bit->tb0 && ack_bit->tb1) { + uci_data->value.ack[i] = ack_info->cc[ack_bit->cc_idx].m[ack_bit->m_idx].value[1]; + continue; + } + + // Both TB with bundling + uci_data->value.ack[i] = ack_info->cc[ack_bit->cc_idx].m[ack_bit->m_idx].value[0]; + uci_data->value.ack[i] &= ack_info->cc[ack_bit->cc_idx].m[ack_bit->m_idx].value[1]; + } + + return SRSRAN_SUCCESS; +} + +int srsran_harq_ack_unpack(const srsran_harq_ack_cfg_hl_t* cfg, + const srsran_uci_data_nr_t* uci_data, + srsran_pdsch_ack_nr_t* ack_info) +{ + // Check inputs + if (cfg == NULL || ack_info == NULL || uci_data == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Actual packing + for (uint32_t i = 0; i < uci_data->cfg.ack.count; i++) { + const srsran_harq_ack_bit_t* ack_bit = &uci_data->cfg.ack.bits[i]; + + // Extract TB0 + if (ack_bit->tb0) { + ack_info->cc[ack_bit->cc_idx].m[ack_bit->m_idx].value[0] = uci_data->value.ack[i]; + } + + // Extract TB1 + if (ack_bit->tb1) { + ack_info->cc[ack_bit->cc_idx].m[ack_bit->m_idx].value[1] = uci_data->value.ack[i]; + } + } + + return SRSRAN_SUCCESS; +} + +int srsran_harq_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, const srsran_harq_ack_m_t* m) +{ + // Check inputs + if (ack_info == NULL || m == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Protect SCell index and extract information + if (m->resource.scell_idx >= SRSRAN_MAX_CARRIERS) { + ERROR("Serving cell index (%d) exceeds maximum", m->resource.scell_idx); + return SRSRAN_ERROR; + } + srsran_harq_ack_cc_t* cc = &ack_info->cc[m->resource.scell_idx]; + + if (cc->M >= SRSRAN_UCI_NR_MAX_M) { + ERROR("Accumulated M HARQ feedback exceeds maximum (%d)", SRSRAN_UCI_NR_MAX_M); + return SRSRAN_ERROR; + } + + // Find insertion index + uint32_t idx = cc->M; // Append at the end by default + for (uint32_t i = 0; i < cc->M; i++) { + if (cc->m[i].resource.k1 < m->resource.k1) { + idx = i; + break; + } + } + + // Increment count + cc->M += 1; + + // Make space for insertion + for (uint32_t i = cc->M - 1; i != idx; i--) { + cc->m[i] = cc->m[i - 1]; + } + + // Actual insertion + cc->m[idx] = *m; + + return SRSRAN_SUCCESS; +} + +uint32_t srsran_harq_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + if (ack_info == NULL || str == NULL) { + return 0; + } + + // Print base info + len = srsran_print_check( + str, str_len, len, "use_pusch=%c nof_cc=%d\n", ack_info->use_pusch ? 'y' : 'n', ack_info->nof_cc); + + // Iterate all carriers + for (uint32_t cc = 0; cc < ack_info->nof_cc; cc++) { + len = srsran_print_check(str, str_len, len, " CC %d: M=%d\n", cc, ack_info->cc[cc].M); + for (uint32_t m = 0; m < ack_info->cc[cc].M; m++) { + if (ack_info->cc[cc].m[m].present) { + len = srsran_print_check(str, + str_len, + len, + " m %d: k1=%d dai=%d ack=%d\n", + m, + ack_info->cc[cc].m[m].resource.k1, + ack_info->cc[cc].m[m].resource.v_dai_dl, + ack_info->cc[cc].m[m].value[0]); + } + } + } + + return len; +} \ No newline at end of file diff --git a/lib/src/phy/phch/mib_sl.c b/lib/src/phy/phch/mib_sl.c index 89496dbdc..a004e222c 100644 --- a/lib/src/phy/phch/mib_sl.c +++ b/lib/src/phy/phch/mib_sl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/npbch.c b/lib/src/phy/phch/npbch.c index 32f9be103..34547e79e 100644 --- a/lib/src/phy/phch/npbch.c +++ b/lib/src/phy/phch/npbch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/npdcch.c b/lib/src/phy/phch/npdcch.c index 0fd4da524..35f003e52 100644 --- a/lib/src/phy/phch/npdcch.c +++ b/lib/src/phy/phch/npdcch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/npdsch.c b/lib/src/phy/phch/npdsch.c index e0379f3b8..c89956047 100644 --- a/lib/src/phy/phch/npdsch.c +++ b/lib/src/phy/phch/npdsch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/pbch.c b/lib/src/phy/phch/pbch.c index af23dc2e6..b1a72e1cd 100644 --- a/lib/src/phy/phch/pbch.c +++ b/lib/src/phy/phch/pbch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -49,15 +49,11 @@ bool srsran_pbch_exists(int nframe, int nslot) return (!(nframe % 5) && nslot == 1); } -cf_t* offset_original; - int srsran_pbch_cp(cf_t* input, cf_t* output, srsran_cell_t cell, bool put) { int i; cf_t* ptr; - offset_original = input; - if (put) { ptr = input; output += cell.nof_prb * SRSRAN_NRE / 2 - 36; diff --git a/lib/src/phy/phch/pbch_msg_nr.c b/lib/src/phy/phch/pbch_msg_nr.c new file mode 100644 index 000000000..b48c56f46 --- /dev/null +++ b/lib/src/phy/phch/pbch_msg_nr.c @@ -0,0 +1,188 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsran/phy/phch/pbch_msg_nr.h" +#include "srsran/phy/utils/bit.h" +#include "srsran/phy/utils/vector.h" + +static bool pbch_msg_nr_is_mib(const srsran_pbch_msg_nr_t* msg) +{ + return msg->payload[0] == 0; +} + +bool srsran_pbch_msg_nr_is_mib(const srsran_pbch_msg_nr_t* msg) +{ + if (msg == NULL) { + return false; + } + + return pbch_msg_nr_is_mib(msg); +} + +int srsran_pbch_msg_nr_mib_pack(const srsran_mib_nr_t* mib, srsran_pbch_msg_nr_t* pbch_msg) +{ + if (mib == NULL || pbch_msg == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Copy PBCH message context + pbch_msg->sfn_4lsb = mib->sfn & 0b1111; + pbch_msg->ssb_idx = mib->ssb_idx; + pbch_msg->k_ssb_msb = mib->ssb_offset >> 4U; + pbch_msg->hrf = mib->hrf; + + // Pack MIB payload + uint8_t* y = pbch_msg->payload; + + // MIB - 1 bit + *(y++) = 0; + + // systemFrameNumber - 6 bits MSB + srsran_bit_unpack(mib->sfn >> 4U, &y, 6); + + // subCarrierSpacingCommon - 1 bit + *(y++) = (mib->scs_common == srsran_subcarrier_spacing_15kHz || mib->scs_common == srsran_subcarrier_spacing_60kHz) + ? 0 + : 1; + + // ssb-SubcarrierOffset - 4 bits + srsran_bit_unpack(mib->ssb_offset, &y, 4); + + // dmrs-TypeA-Position - 1 bit + *(y++) = (mib->dmrs_typeA_pos == srsran_dmrs_sch_typeA_pos_2) ? 0 : 1; + + // pdcch-ConfigSIB1 + // controlResourceSetZero - 4 bits + srsran_bit_unpack(mib->coreset0_idx, &y, 4); + + // searchSpaceZero - 4 bits + srsran_bit_unpack(mib->ss0_idx, &y, 4); + + // Barred - 1 bit + *(y++) = (mib->cell_barred) ? 0 : 1; + + // intraFreqReselection - 1 bit + *(y++) = (mib->intra_freq_reselection) ? 0 : 1; + + // Spare - 1 bit + srsran_bit_unpack(mib->spare, &y, 1); + + return SRSRAN_SUCCESS; +} + +int srsran_pbch_msg_nr_mib_unpack(const srsran_pbch_msg_nr_t* pbch_msg, srsran_mib_nr_t* mib) +{ + if (mib == NULL || pbch_msg == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Copy PBCH message context + mib->sfn = pbch_msg->sfn_4lsb; + mib->ssb_idx = pbch_msg->ssb_idx; + mib->hrf = pbch_msg->hrf; + mib->ssb_offset = pbch_msg->k_ssb_msb << 4U; + + // Pack MIB payload + uint8_t* y = (uint8_t*)pbch_msg->payload; + + // MIB - 1 bit + if (!pbch_msg_nr_is_mib(pbch_msg)) { + return SRSRAN_ERROR; + } + y++; + + // systemFrameNumber - 6 bits MSB + mib->sfn |= srsran_bit_pack(&y, 6) << 4U; + + // subCarrierSpacingCommon - 1 bit + mib->scs_common = *(y++) == 0 ? srsran_subcarrier_spacing_15kHz : srsran_subcarrier_spacing_30kHz; + + // ssb-SubcarrierOffset - 4 bits + mib->ssb_offset |= srsran_bit_pack(&y, 4); + + // dmrs-TypeA-Position - 1 bit + mib->dmrs_typeA_pos = *(y++) == 0 ? srsran_dmrs_sch_typeA_pos_2 : srsran_dmrs_sch_typeA_pos_3; + + // pdcch-ConfigSIB1 + // controlResourceSetZero - 4 bits + mib->coreset0_idx = srsran_bit_pack(&y, 4); + + // searchSpaceZero - 4 bits + mib->ss0_idx = srsran_bit_pack(&y, 4); + + // Barred - 1 bit + mib->cell_barred = (*(y++) == 0); + + // intraFreqReselection - 1 bit + mib->intra_freq_reselection = (*(y++) == 0); + + // Spare - 1 bit + mib->spare = srsran_bit_pack(&y, 1); + + return SRSRAN_SUCCESS; +} + +uint32_t srsran_pbch_msg_info(const srsran_pbch_msg_nr_t* msg, char* str, uint32_t str_len) +{ + if (msg == NULL || str == NULL || str_len == 0) { + return 0; + } + + uint32_t len = 0; + + len = srsran_print_check(str, str_len, len, "payload="); + + len += srsran_vec_sprint_hex(&str[len], str_len - len, (uint8_t*)msg->payload, SRSRAN_PBCH_MSG_NR_SZ); + + len = srsran_print_check(str, + str_len, + len, + " sfn_lsb=%d ssb_idx=%d k_ssb_msb=%d hrf=%d ", + msg->sfn_4lsb, + msg->ssb_idx, + msg->k_ssb_msb, + msg->hrf); + + return len; +} + +uint32_t srsran_pbch_msg_nr_mib_info(const srsran_mib_nr_t* mib, char* str, uint32_t str_len) +{ + uint32_t len = 0; + + len = srsran_print_check(str, + str_len, + len, + "sfn=%d ssb_idx=%d hrf=%c scs=%d ssb_offset=%d dmrs_typeA_pos=%s coreset0=%d ss0=%d " + "barred=%c intra_freq_reselection=%c spare=%d", + mib->sfn, + mib->ssb_idx, + mib->hrf ? 'y' : 'n', + SRSRAN_SUBC_SPACING_NR(mib->scs_common) / 1000, + mib->ssb_offset, + mib->dmrs_typeA_pos == srsran_dmrs_sch_typeA_pos_2 ? "pos2" : "pos3", + mib->coreset0_idx, + mib->ss0_idx, + mib->cell_barred ? 'y' : 'n', + mib->intra_freq_reselection ? 'y' : 'n', + mib->spare); + + return len; +} diff --git a/lib/src/phy/phch/pbch_nr.c b/lib/src/phy/phch/pbch_nr.c new file mode 100644 index 000000000..83c92a4ed --- /dev/null +++ b/lib/src/phy/phch/pbch_nr.c @@ -0,0 +1,686 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/phch/pbch_nr.h" +#include "srsran/phy/common/sequence.h" +#include "srsran/phy/fec/polar/polar_chanalloc.h" +#include "srsran/phy/fec/polar/polar_interleaver.h" +#include "srsran/phy/mimo/precoding.h" +#include "srsran/phy/modem/demod_soft.h" +#include "srsran/phy/modem/mod.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/simd.h" +#include "srsran/phy/utils/vector.h" + +#define PBCH_NR_DEBUG_TX(...) DEBUG("PBCH-NR Tx: " __VA_ARGS__) +#define PBCH_NR_DEBUG_RX(...) DEBUG("PBCH-NR Rx: " __VA_ARGS__) + +/* + * CRC Parameters + */ +#define PBCH_NR_CRC SRSRAN_LTE_CRC24C +#define PBCH_NR_CRC_LEN 24 + +/* + * Polar code N_max + */ +#define PBCH_NR_POLAR_N_MAX 9U + +/* + * Polar rate matching I_BIL + */ +#define PBCH_NR_POLAR_RM_IBIL 0 + +/* + * Number of generated payload bits, called A + */ +#define PBCH_NR_A (SRSRAN_PBCH_MSG_NR_SZ + 8) + +/* + * Number of payload bits plus CRC + */ +#define PBCH_NR_K (PBCH_NR_A + PBCH_NR_CRC_LEN) + +/* + * Number of Polar encoded bits + */ +#define PBCH_NR_N (1U << PBCH_NR_POLAR_N_MAX) + +/* + * Number of RM bits + */ +#define PBCH_NR_E (864) + +/* + * Number of symbols + */ +#define PBCH_NR_M (PBCH_NR_E / 2) + +static int pbch_nr_init_encoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args) +{ + // Skip encoder init if not requested + if (!args->enable_encode) { + return SRSRAN_SUCCESS; + } + + srsran_polar_encoder_type_t encoder_type = SRSRAN_POLAR_ENCODER_PIPELINED; + +#ifdef LV_HAVE_AVX2 + if (!args->disable_simd) { + encoder_type = SRSRAN_POLAR_ENCODER_AVX2; + } +#endif /* LV_HAVE_AVX2 */ + + if (srsran_polar_encoder_init(&q->polar_encoder, encoder_type, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) { + ERROR("Error initiating polar encoder"); + return SRSRAN_ERROR; + } + + if (srsran_polar_rm_tx_init(&q->polar_rm_tx) < SRSRAN_SUCCESS) { + ERROR("Error initiating polar RM"); + return SRSRAN_ERROR; + } + + if (srsran_modem_table_lte(&q->qpsk, SRSRAN_MOD_QPSK) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_init_decoder(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args) +{ + // Skip decoder init if not requested + if (!args->enable_decode) { + return SRSRAN_SUCCESS; + } + + srsran_polar_decoder_type_t decoder_type = SRSRAN_POLAR_DECODER_SSC_C; + +#ifdef LV_HAVE_AVX2 + if (!args->disable_simd) { + decoder_type = SRSRAN_POLAR_DECODER_SSC_C_AVX2; + } +#endif /* LV_HAVE_AVX2 */ + + if (srsran_polar_decoder_init(&q->polar_decoder, decoder_type, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) { + ERROR("Error initiating polar decoder"); + return SRSRAN_ERROR; + } + + if (srsran_polar_rm_rx_init_c(&q->polar_rm_rx) < SRSRAN_SUCCESS) { + ERROR("Error initiating polar RM"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_pbch_nr_init(srsran_pbch_nr_t* q, const srsran_pbch_nr_args_t* args) +{ + if (q == NULL || args == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!args->enable_encode && !args->enable_decode) { + ERROR("Encoder and decoder are disabled, at least one of them shall be active"); + return SRSRAN_ERROR; + } + + if (pbch_nr_init_encoder(q, args) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (pbch_nr_init_decoder(q, args) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_crc_init(&q->crc, PBCH_NR_CRC, PBCH_NR_CRC_LEN) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_polar_code_init(&q->code) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_polar_code_get(&q->code, PBCH_NR_K, PBCH_NR_E, PBCH_NR_POLAR_N_MAX) < SRSRAN_SUCCESS) { + ERROR("Error Getting polar code"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void srsran_pbch_nr_free(srsran_pbch_nr_t* q) +{ + if (q == NULL) { + return; + } + srsran_polar_encoder_free(&q->polar_encoder); + srsran_polar_decoder_free(&q->polar_decoder); + srsran_polar_rm_rx_free_c(&q->polar_rm_rx); + srsran_polar_rm_tx_free(&q->polar_rm_tx); + srsran_polar_code_free(&q->code); + srsran_modem_table_free(&q->qpsk); + SRSRAN_MEM_ZERO(q, srsran_pbch_nr_t, 1); +} + +/* + * Implements TS 38.212 Table 7.1.1-1: Value of PBCH payload interleaver pattern G ( j ) + */ +static const uint32_t G[PBCH_NR_A] = {16, 23, 18, 17, 8, 30, 10, 6, 24, 7, 0, 5, 3, 2, 1, 4, + 9, 11, 12, 13, 14, 15, 19, 20, 21, 22, 25, 26, 27, 28, 29, 31}; + +#define PBCH_SFN_PAYLOAD_BEGIN 1 +#define PBCH_SFN_PAYLOAD_LENGTH 6 +#define PBCH_SFN_2ND_LSB_G (G[PBCH_SFN_PAYLOAD_LENGTH + 2]) +#define PBCH_SFN_3RD_LSB_G (G[PBCH_SFN_PAYLOAD_LENGTH + 1]) + +static void +pbch_nr_pbch_msg_pack(const srsran_pbch_nr_cfg_t* cfg, const srsran_pbch_msg_nr_t* msg, uint8_t a[PBCH_NR_A]) +{ + // Extract actual payload size + uint32_t A_hat = SRSRAN_PBCH_MSG_NR_SZ; + + // Put actual payload + uint32_t j_sfn = 0; + uint32_t j_other = 14; + for (uint32_t i = 0; i < A_hat; i++) { + if (i >= PBCH_SFN_PAYLOAD_BEGIN && i < (PBCH_SFN_PAYLOAD_BEGIN + PBCH_SFN_PAYLOAD_LENGTH)) { + a[G[j_sfn++]] = msg->payload[i]; + } else { + a[G[j_other++]] = msg->payload[i]; + } + } + + // Put SFN in a_hat[A_hat] to a_hat[A_hat + 3] + a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 3U) & 1U); // 4th LSB of SFN + a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 2U) & 1U); // 3th LSB of SFN + a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 1U) & 1U); // 2th LSB of SFN + a[G[j_sfn++]] = (uint8_t)((msg->sfn_4lsb >> 0U) & 1U); // 1th LSB of SFN + + // Put HRF in a_hat[A_hat + 4] + a[G[10]] = (msg->hrf ? 1 : 0); + + // Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7] + if (cfg->Lmax == 64) { + a[G[11]] = (uint8_t)((msg->ssb_idx >> 5U) & 1U); // 6th bit of SSB index + a[G[12]] = (uint8_t)((msg->ssb_idx >> 4U) & 1U); // 5th bit of SSB index + a[G[13]] = (uint8_t)((msg->ssb_idx >> 3U) & 1U); // 4th bit of SSB index + } else { + a[G[11]] = (uint8_t)msg->k_ssb_msb; // 6th bit of SSB index + a[G[12]] = 0; // Reserved + a[G[13]] = 0; // Reserved + } + + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { + PBCH_NR_DEBUG_TX("Packed PBCH bits: "); + srsran_vec_fprint_byte(stdout, a, PBCH_NR_A); + } +} +static void +pbch_nr_pbch_msg_unpack(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], srsran_pbch_msg_nr_t* msg) +{ + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { + PBCH_NR_DEBUG_RX("Packed PBCH bits: "); + srsran_vec_fprint_byte(stdout, a, PBCH_NR_A); + } + + // Extract actual payload size + uint32_t A_hat = SRSRAN_PBCH_MSG_NR_SZ; + + // Get actual payload + uint32_t j_sfn = 0; + uint32_t j_other = 14; + for (uint32_t i = 0; i < A_hat; i++) { + if (i >= PBCH_SFN_PAYLOAD_BEGIN && i < (PBCH_SFN_PAYLOAD_BEGIN + PBCH_SFN_PAYLOAD_LENGTH)) { + msg->payload[i] = a[G[j_sfn++]]; + } else { + msg->payload[i] = a[G[j_other++]]; + } + } + + // Put SFN in a_hat[A_hat] to a_hat[A_hat + 3] + msg->sfn_4lsb = 0; + msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 3U); // 4th LSB of SFN + msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 2U); // 3th LSB of SFN + msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 1U); // 2th LSB of SFN + msg->sfn_4lsb |= (uint8_t)(a[G[j_sfn++]] << 0U); // 1th LSB of SFN + + // Put HRF in a_hat[A_hat + 4] + msg->hrf = (a[G[10]] == 1); + + // Put SSB related in a_hat[A_hat + 5] to a_hat[A_hat + 7] + msg->ssb_idx = cfg->ssb_idx; // Load 4 LSB + if (cfg->Lmax == 64) { + msg->ssb_idx = msg->ssb_idx & 0b111; + msg->ssb_idx |= (uint8_t)(a[G[11]] << 5U); // 6th bit of SSB index + msg->ssb_idx |= (uint8_t)(a[G[12]] << 4U); // 5th bit of SSB index + msg->ssb_idx |= (uint8_t)(a[G[13]] << 3U); // 4th bit of SSB index + } else { + msg->k_ssb_msb = a[G[11]]; + } +} + +static void pbch_nr_scramble(const srsran_pbch_nr_cfg_t* cfg, const uint8_t a[PBCH_NR_A], uint8_t a_prime[PBCH_NR_A]) +{ + uint32_t i = 0; + uint32_t j = 0; + + // Initialise sequence + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id)); + + // Select value M + uint32_t M = PBCH_NR_A - 3; + if (cfg->Lmax == 64) { + M = PBCH_NR_A - 6; + } + + // Select value v + uint32_t v = 2 * a[PBCH_SFN_3RD_LSB_G] + a[PBCH_SFN_2ND_LSB_G]; + + // Advance sequence + srsran_sequence_state_advance(&sequence_state, M * v); + + // Generate actual sequence + uint8_t c[PBCH_NR_A] = {}; + srsran_sequence_state_apply_bit(&sequence_state, c, c, PBCH_NR_A); + + while (i < PBCH_NR_A) { + uint8_t s_i = c[j]; + + // Check if i belongs to a SS/PBCH block index which is only multiplexed when L_max is 64 + bool is_ssb_idx = (i == G[11] || i == G[12] || i == G[13]) && cfg->Lmax == 64; + + // a i corresponds to any one of the bits belonging to the SS/PBCH block index, the half frame index, and 2 nd and 3 + // rd least significant bits of the system frame number + if (is_ssb_idx || i == G[10] || i == PBCH_SFN_2ND_LSB_G || i == PBCH_SFN_3RD_LSB_G) { + s_i = 0; + } else { + j++; + } + + a_prime[i] = a[i] ^ s_i; + i++; + } +} + +static int pbch_nr_polar_encode(srsran_pbch_nr_t* q, const uint8_t c[PBCH_NR_K], uint8_t d[PBCH_NR_N]) +{ + // Interleave + uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL]; + srsran_polar_interleaver_run_u8(c, c_prime, PBCH_NR_K, true); + + // Allocate channel + uint8_t allocated[PBCH_NR_N]; + srsran_polar_chanalloc_tx(c_prime, allocated, q->code.N, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set); + + // Encode bits + if (srsran_polar_encoder_encode(&q->polar_encoder, allocated, d, q->code.n) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { + PBCH_NR_DEBUG_TX("Allocated: "); + srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N); + } + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_polar_decode(srsran_pbch_nr_t* q, const int8_t d[PBCH_NR_N], uint8_t c[PBCH_NR_K]) +{ + // Decode bits + uint8_t allocated[PBCH_NR_N]; + if (srsran_polar_decoder_decode_c(&q->polar_decoder, d, allocated, q->code.n, q->code.F_set, q->code.F_set_size) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { + PBCH_NR_DEBUG_RX("Allocated: "); + srsran_vec_fprint_byte(stdout, allocated, PBCH_NR_N); + } + + // Allocate channel + uint8_t c_prime[SRSRAN_POLAR_INTERLEAVER_K_MAX_IL]; + srsran_polar_chanalloc_rx(allocated, c_prime, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set); + + // Interleave + srsran_polar_interleaver_run_u8(c_prime, c, PBCH_NR_K, false); + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_polar_rm_tx(srsran_pbch_nr_t* q, const uint8_t d[PBCH_NR_N], uint8_t o[PBCH_NR_E]) +{ + if (srsran_polar_rm_tx(&q->polar_rm_tx, d, o, q->code.n, PBCH_NR_E, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { + PBCH_NR_DEBUG_TX("d: "); + srsran_vec_fprint_byte(stdout, d, PBCH_NR_N); + } + + return SRSRAN_SUCCESS; +} + +static int pbch_nr_polar_rm_rx(srsran_pbch_nr_t* q, const int8_t llr[PBCH_NR_E], int8_t d[PBCH_NR_N]) +{ + if (srsran_polar_rm_rx_c(&q->polar_rm_rx, llr, d, PBCH_NR_E, q->code.n, PBCH_NR_K, PBCH_NR_POLAR_RM_IBIL) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Negate all LLR + for (uint32_t i = 0; i < PBCH_NR_N; i++) { + d[i] *= -1; + } + + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { + PBCH_NR_DEBUG_RX("d: "); + srsran_vec_fprint_bs(stdout, d, PBCH_NR_N); + } + + return SRSRAN_SUCCESS; +} + +static void pbch_nr_scramble_tx(const srsran_pbch_nr_cfg_t* cfg, + uint32_t ssb_idx, + const uint8_t b[PBCH_NR_E], + uint8_t b_hat[PBCH_NR_E]) +{ + // Initialise sequence + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id)); + + // Select value M + uint32_t M_bit = PBCH_NR_E; + + // Select value v + // for L max = 8 or L max = 64 , & is the three least significant bits of the SS/PBCH block index + uint32_t v = (ssb_idx & 0b111U); + if (cfg->Lmax == 4) { + // for L max = 4 , & is the two least significant bits of the SS/PBCH block index + v = ssb_idx & 0b11U; + } + + // Advance sequence + srsran_sequence_state_advance(&sequence_state, v * M_bit); + + // Apply sequence + srsran_sequence_state_apply_bit(&sequence_state, b, b_hat, PBCH_NR_E); +} + +static void pbch_nr_scramble_rx(const srsran_pbch_nr_cfg_t* cfg, + uint32_t ssb_idx, + const int8_t b_hat[PBCH_NR_E], + int8_t b[PBCH_NR_E]) +{ + // Initialise sequence + srsran_sequence_state_t sequence_state = {}; + srsran_sequence_state_init(&sequence_state, SRSRAN_SEQUENCE_MOD(cfg->N_id)); + + // Select value M + uint32_t M_bit = PBCH_NR_E; + + // Select value v + // for L max = 8 or L max = 64 , & is the three least significant bits of the SS/PBCH block index + uint32_t v = (ssb_idx & 0b111U); + if (cfg->Lmax == 4) { + // for L max = 4 , & is the two least significant bits of the SS/PBCH block index + v = ssb_idx & 0b11U; + } + + // Advance sequence + srsran_sequence_state_advance(&sequence_state, v * M_bit); + + // Apply sequence + srsran_sequence_state_apply_c(&sequence_state, b_hat, b, PBCH_NR_E); +} + +static void +pbch_nr_mapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t symbols[PBCH_NR_M], cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +{ + uint32_t count = 0; + + // PBCH DMRS shift + uint32_t v = cfg->N_id % 4; + + // Symbol 1 + for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++]; + } + + // Symbol 2 + for (uint32_t k = 0; k < 48; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++]; + } + for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++]; + } + + // Symbol 3 + for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k] = symbols[count++]; + } + + // if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { + // PBCH_NR_DEBUG_TX("Symbols: "); + // srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M); + // } +} + +static void +pbch_nr_demapping(const srsran_pbch_nr_cfg_t* cfg, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], cf_t symbols[PBCH_NR_M]) +{ + uint32_t count = 0; + + // PBCH DMRS shift + uint32_t v = cfg->N_id % 4; + + // Symbol 1 + for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + symbols[count++] = ssb_grid[1 * SRSRAN_SSB_BW_SUBC + k]; + } + + // Symbol 2 + for (uint32_t k = 0; k < 48; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k]; + } + for (uint32_t k = 192; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + symbols[count++] = ssb_grid[2 * SRSRAN_SSB_BW_SUBC + k]; + } + + // Symbol 3 + for (uint32_t k = 0; k < SRSRAN_SSB_BW_SUBC; k++) { + // Skip DMRS + if (k % 4 == v) { + continue; + } + + symbols[count++] = ssb_grid[3 * SRSRAN_SSB_BW_SUBC + k]; + } + + // if (srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { + // PBCH_NR_DEBUG_RX("Symbols: "); + // srsran_vec_fprint_c(stdout, symbols, PBCH_NR_M); + // } +} + +int srsran_pbch_nr_encode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + const srsran_pbch_msg_nr_t* msg, + cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +{ + if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // TS 38.212 7.1 Broadcast channel + // 7.1.1 PBCH payload generation + uint8_t a[PBCH_NR_A]; + pbch_nr_pbch_msg_pack(cfg, msg, a); + + // 7.1.2 Scrambling + uint8_t b[PBCH_NR_K]; + pbch_nr_scramble(cfg, a, b); + + // 7.1.3 Transport block CRC attachment + uint32_t checksum = srsran_crc_attach(&q->crc, b, PBCH_NR_A); + PBCH_NR_DEBUG_TX("checksum=%06x", checksum); + + // 7.1.4 Channel coding + uint8_t d[PBCH_NR_N]; + if (pbch_nr_polar_encode(q, b, d) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // 7.1.5 Rate matching + uint8_t f[PBCH_NR_E]; + if (pbch_nr_polar_rm_tx(q, d, f) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // TS 38.211 7.3.3 Physical broadcast channel + // 7.3.3.1 Scrambling + pbch_nr_scramble_tx(cfg, msg->ssb_idx, f, f); + + // 7.3.3.2 Modulation + cf_t symbols[PBCH_NR_M]; + srsran_mod_modulate(&q->qpsk, f, symbols, PBCH_NR_E); + + // 7.3.3.3 Mapping to physical resources + // 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block + pbch_nr_mapping(cfg, symbols, ssb_grid); + + return SRSRAN_SUCCESS; +} + +int srsran_pbch_nr_decode(srsran_pbch_nr_t* q, + const srsran_pbch_nr_cfg_t* cfg, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + const cf_t ce_grid[SRSRAN_SSB_NOF_RE], + srsran_pbch_msg_nr_t* msg) +{ + if (q == NULL || cfg == NULL || msg == NULL || ssb_grid == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // 7.3.3.3 Mapping to physical resources + // 7.4.3.1.3 Mapping of PBCH and DM-RS within an SS/PBCH block + srsran_simd_aligned cf_t symbols[PBCH_NR_M]; + pbch_nr_demapping(cfg, ssb_grid, symbols); + + srsran_simd_aligned cf_t ce[PBCH_NR_M]; + pbch_nr_demapping(cfg, ce_grid, ce); + + // Channel equalizer + if (srsran_predecoding_single(symbols, ce, symbols, NULL, PBCH_NR_M, 1.0f, 0.0f) < SRSRAN_SUCCESS) { + ERROR("Error in predecoder"); + return SRSRAN_ERROR; + } + + // Avoid NAN getting into the demodulator + for (uint32_t i = 0; i < PBCH_NR_M; i++) { + if (isnan(__real__ symbols[i]) || isnan(__imag__ symbols[i])) { + symbols[i] = 0.0f; + } + } + + // 7.3.3.2 Modulation + int8_t llr[PBCH_NR_E]; + srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, symbols, llr, PBCH_NR_M); + + // If all LLR are zero, no message could be received + if (srsran_vec_avg_power_bf(llr, PBCH_NR_E) == 0) { + SRSRAN_MEM_ZERO(msg, srsran_pbch_msg_nr_t, 1); + return SRSRAN_SUCCESS; + } + + // TS 38.211 7.3.3 Physical broadcast channel + // 7.3.3.1 Scrambling + pbch_nr_scramble_rx(cfg, cfg->ssb_idx, llr, llr); + + // 7.1.5 Rate matching + int8_t d[PBCH_NR_N]; + if (pbch_nr_polar_rm_rx(q, llr, d) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // TS 38.212 7.1 Broadcast channel + // 7.1.4 Channel coding + uint8_t b[PBCH_NR_K]; + if (pbch_nr_polar_decode(q, d, b) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // 7.1.3 Transport block CRC attachment + msg->crc = srsran_crc_match(&q->crc, b, PBCH_NR_A); + PBCH_NR_DEBUG_RX("crc=%s", msg->crc ? "OK" : "KO"); + + // 7.1.2 Scrambling + uint8_t a[PBCH_NR_A]; + pbch_nr_scramble(cfg, b, a); + + // 7.1.1 PBCH payload generation + pbch_nr_pbch_msg_unpack(cfg, a, msg); + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/phch/pcfich.c b/lib/src/phy/phch/pcfich.c index da32a45d3..c55e0a36b 100644 --- a/lib/src/phy/phch/pcfich.c +++ b/lib/src/phy/phch/pcfich.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/pdcch.c b/lib/src/phy/phch/pdcch.c index e07c3a195..e31fd85d0 100644 --- a/lib/src/phy/phch/pdcch.c +++ b/lib/src/phy/phch/pdcch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,6 +19,7 @@ * */ +#include #include #include #include @@ -45,7 +46,8 @@ float srsran_pdcch_coderate(uint32_t nof_bits, uint32_t l) { - return (float)(nof_bits + 16) / (4 * PDCCH_FORMAT_NOF_REGS(l)); + static const int nof_bits_x_symbol = 2; // QPSK + return (float)(nof_bits + 16) / (nof_bits_x_symbol * PDCCH_FORMAT_NOF_REGS(l)); } /** Initializes the PDCCH transmitter and receiver */ @@ -385,12 +387,14 @@ int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dc uint32_t nof_bits = srsran_dci_format_sizeof(&q->cell, sf, dci_cfg, msg->format); uint32_t e_bits = PDCCH_FORMAT_NOF_BITS(msg->location.L); + // Compute absolute mean of the LLRs double mean = 0; for (int i = 0; i < e_bits; i++) { mean += fabsf(q->llr[msg->location.ncce * 72 + i]); } mean /= e_bits; - if (mean > 0.3) { + + if (mean > 0.3f) { ret = srsran_pdcch_dci_decode(q, &q->llr[msg->location.ncce * 72], msg->payload, e_bits, nof_bits, &msg->rnti); if (ret == SRSRAN_SUCCESS) { msg->nof_bits = nof_bits; @@ -418,6 +422,27 @@ int srsran_pdcch_decode_msg(srsran_pdcch_t* q, srsran_dl_sf_cfg_t* sf, srsran_dc return ret; } +float srsran_pdcch_msg_corr(srsran_pdcch_t* q, srsran_dci_msg_t* msg) +{ + if (q == NULL || msg == NULL) { + return 0.0f; + } + + uint32_t E = PDCCH_FORMAT_NOF_BITS(msg->location.L); + uint32_t nof_llr = E / 2; + + // Encode same decoded message and compute correlation + srsran_pdcch_dci_encode(q, msg->payload, q->e, msg->nof_bits, E, msg->rnti); + + // Modulate + srsran_mod_modulate(&q->mod, q->e, q->d, E); + + // Correlate + cf_t corr = srsran_vec_dot_prod_conj_ccc((cf_t*)&q->llr[msg->location.ncce * 72], q->d, nof_llr); + + return cabsf(corr / nof_llr) * (float)M_SQRT1_2; +} + /** Performs PDCCH receiver processing to extract LLR for all control region. LLR bits are stored in srsran_pdcch_t * object. DCI can be decoded from given locations in successive calls to srsran_pdcch_decode_msg() */ diff --git a/lib/src/phy/phch/pdcch_nr.c b/lib/src/phy/phch/pdcch_nr.c index 673a93347..658f2e6b6 100644 --- a/lib/src/phy/phch/pdcch_nr.c +++ b/lib/src/phy/phch/pdcch_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -78,12 +78,19 @@ static int srsran_pdcch_nr_get_ncce(const srsran_coreset_t* coreset, return SRSRAN_ERROR; } + // Calculate CORESET bandiwth in physical resource blocks + uint32_t coreset_bw = srsran_coreset_get_bw(coreset); + // Every REG is 1PRB wide and a CCE is 6 REG. So, the number of N_CCE is a sixth of the bandwidth times the number of // symbols - uint32_t N_cce = srsran_coreset_get_bw(coreset) * coreset->duration / 6; + uint32_t N_cce = coreset_bw * coreset->duration / 6; if (N_cce < L) { - ERROR("Error number of CCE %d is lower than the aggregation level %d", N_cce, L); + ERROR("Error CORESET (total bandwidth of %d RBs and %d CCEs) cannot fit the aggregation level %d (%d)", + coreset_bw, + N_cce, + L, + aggregation_level); return SRSRAN_ERROR; } @@ -306,33 +313,142 @@ int srsran_pdcch_nr_set_carrier(srsran_pdcch_nr_t* q, return SRSRAN_SUCCESS; } +static int pdcch_nr_cce_to_reg_mapping_non_interleaved(const srsran_coreset_t* coreset, + const srsran_dci_location_t* dci_location, + bool rb_mask[SRSRAN_MAX_PRB_NR]) +{ + uint32_t nof_cce = 1U << dci_location->L; + uint32_t L = 6; + uint32_t nof_reg_bundle = 6 / L; + + // For each CCE j in the PDCCH transmission + for (uint32_t j = dci_location->ncce; j < dci_location->ncce + nof_cce; j++) { + // For each REG bundle i in the CCE j + for (uint32_t reg_bundle = 0; reg_bundle < nof_reg_bundle; reg_bundle++) { + // Calculate x variable + uint32_t x = (6 * j) / L + reg_bundle; + + // For non interleaved f(x) = x + uint32_t i = x; + + // For each REG in the REG bundle + uint32_t rb_start = (i * L) / coreset->duration; + uint32_t rb_end = ((i + 1) * L) / coreset->duration; + for (uint32_t rb = rb_start; rb < rb_end; rb++) { + rb_mask[rb] = true; + } + } + } + + return SRSRAN_SUCCESS; +} + +static int pdcch_nr_cce_to_reg_mapping_interleaved(const srsran_coreset_t* coreset, + const srsran_dci_location_t* dci_location, + bool rb_mask[SRSRAN_MAX_PRB_NR]) +{ + // Calculate CORESET constants + uint32_t N_CORESET_REG = coreset->duration * srsran_coreset_get_bw(coreset); + uint32_t L = pdcch_nr_bundle_size(coreset->reg_bundle_size); + uint32_t R = pdcch_nr_bundle_size(coreset->interleaver_size); + uint32_t C = N_CORESET_REG / (L * R); + uint32_t n_shift = coreset->shift_index; + + // Validate + if (N_CORESET_REG == 0 || N_CORESET_REG % (L * R) != 0 || L % coreset->duration != 0) { + ERROR("Invalid CORESET configuration N=%d; L=%d; R=%d;", N_CORESET_REG, L, R); + return 0; + } + + uint32_t nof_cce = 1U << dci_location->L; + uint32_t nof_reg_bundle = 6 / L; + + // For each CCE j in the PDCCH transmission + for (uint32_t j = dci_location->ncce; j < dci_location->ncce + nof_cce; j++) { + // For each REG bundle i in the CCE j + for (uint32_t reg_bundle = 0; reg_bundle < nof_reg_bundle; reg_bundle++) { + // Calculate x variable + uint32_t x = (6 * j) / L + reg_bundle; + + // For non interleaved f(x) = x + uint32_t r = x % R; + uint32_t c = x / R; + uint32_t i = (r * C + c + n_shift) % (N_CORESET_REG / L); + + // For each REG in the REG bundle + uint32_t rb_start = (i * L) / coreset->duration; + uint32_t rb_end = ((i + 1) * L) / coreset->duration; + for (uint32_t rb = rb_start; rb < rb_end; rb++) { + rb_mask[rb] = true; + } + } + } + + return SRSRAN_SUCCESS; +} + +int srsran_pdcch_nr_cce_to_reg_mapping(const srsran_coreset_t* coreset, + const srsran_dci_location_t* dci_location, + bool rb_mask[SRSRAN_MAX_PRB_NR]) +{ + if (coreset == NULL || dci_location == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Non-interleaved case + if (coreset->mapping_type == srsran_coreset_mapping_type_non_interleaved) { + return pdcch_nr_cce_to_reg_mapping_non_interleaved(coreset, dci_location, rb_mask); + } + + // Interleaved case + return pdcch_nr_cce_to_reg_mapping_interleaved(coreset, dci_location, rb_mask); +} + static uint32_t pdcch_nr_cp(const srsran_pdcch_nr_t* q, const srsran_dci_location_t* dci_location, cf_t* slot_grid, cf_t* symbols, bool put) { - uint32_t L = 1U << dci_location->L; + uint32_t offset_k = q->coreset.offset_rb * SRSRAN_NRE; - // Calculate begin and end sub-carrier index for the selected candidate - uint32_t k_begin = (dci_location->ncce * SRSRAN_NRE * 6) / q->coreset.duration; - uint32_t k_end = k_begin + (L * 6 * SRSRAN_NRE) / q->coreset.duration; + // Compute REG list + bool rb_mask[SRSRAN_MAX_PRB_NR] = {}; + if (srsran_pdcch_nr_cce_to_reg_mapping(&q->coreset, dci_location, rb_mask) < SRSRAN_SUCCESS) { + return 0; + } uint32_t count = 0; // Iterate over symbols for (uint32_t l = 0; l < q->coreset.duration; l++) { // Iterate over frequency resource groups - uint32_t k = 0; + uint32_t rb = 0; for (uint32_t r = 0; r < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; r++) { - if (q->coreset.freq_resources[r]) { - for (uint32_t i = r * 6 * SRSRAN_NRE; i < (r + 1) * 6 * SRSRAN_NRE; i++, k++) { - if (k >= k_begin && k < k_end && k % 4 != 1) { - if (put) { - slot_grid[q->carrier.nof_prb * SRSRAN_NRE * l + i] = symbols[count++]; - } else { - symbols[count++] = slot_grid[q->carrier.nof_prb * SRSRAN_NRE * l + i]; - } + // Skip frequency resource if not set + if (!q->coreset.freq_resources[r]) { + continue; + } + + // For each RB in the frequency resource + for (uint32_t i = r * 6; i < (r + 1) * 6; i++, rb++) { + // Skip if this RB is not marked as mapped + if (!rb_mask[rb]) { + continue; + } + + // For each RE in the RB + for (uint32_t k = i * SRSRAN_NRE; k < (i + 1) * SRSRAN_NRE; k++) { + // Skip if it is a DMRS + if (k % 4 == 1) { + continue; + } + + // Read or write in the grid + if (put) { + slot_grid[q->carrier.nof_prb * SRSRAN_NRE * l + k + offset_k] = symbols[count++]; + } else { + symbols[count++] = slot_grid[q->carrier.nof_prb * SRSRAN_NRE * l + k + offset_k]; } } } @@ -401,7 +517,7 @@ int srsran_pdcch_nr_encode(srsran_pdcch_nr_t* q, const srsran_dci_msg_nr_t* dci_ srsran_polar_interleaver_run_u8(c, c_prime, q->K, true); // Print c and c_prime (after interleaving) - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { PDCCH_INFO_TX("c="); srsran_vec_fprint_hex(stdout, c, q->K); PDCCH_INFO_TX("c_prime="); @@ -417,7 +533,7 @@ int srsran_pdcch_nr_encode(srsran_pdcch_nr_t* q, const srsran_dci_msg_nr_t* dci_ } // Print d - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { PDCCH_INFO_TX("d="); srsran_vec_fprint_byte(stdout, q->d, q->code.N); } @@ -444,7 +560,7 @@ int srsran_pdcch_nr_encode(srsran_pdcch_nr_t* q, const srsran_dci_msg_nr_t* dci_ q->meas_time_us = (uint32_t)t[0].tv_usec; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { char str[128] = {}; srsran_pdcch_nr_info(q, NULL, str, sizeof(str)); PDCCH_INFO_TX("%s", str); @@ -493,7 +609,7 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, } // Print channel estimates if enabled - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { PDCCH_DEBUG_RX("ce="); srsran_vec_fprint_c(stdout, ce->ce, q->M); } @@ -502,7 +618,7 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, srsran_predecoding_single(q->symbols, ce->ce, q->symbols, NULL, q->M, 1.0f, ce->noise_var); // Print symbols if enabled - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { PDCCH_DEBUG_RX("symbols="); srsran_vec_fprint_c(stdout, q->symbols, q->M); } @@ -533,7 +649,7 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, } // Print d - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { PDCCH_DEBUG_RX("d="); srsran_vec_fprint_bs(stdout, d, q->K); } @@ -556,7 +672,7 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, srsran_polar_interleaver_run_u8(c_prime, c, q->K, false); // Print c - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { PDCCH_INFO_RX("c_prime="); srsran_vec_fprint_hex(stdout, c_prime, q->K); PDCCH_INFO_RX("c="); @@ -577,7 +693,7 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, uint32_t checksum2 = srsran_bit_pack(&ptr, 24); res->crc = checksum1 == checksum2; - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { PDCCH_INFO_RX("CRC={%06x, %06x}; msg=", checksum1, checksum2); srsran_vec_fprint_hex(stdout, c, dci_msg->nof_bits); } @@ -591,7 +707,7 @@ int srsran_pdcch_nr_decode(srsran_pdcch_nr_t* q, q->meas_time_us = (uint32_t)t[0].tv_usec; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { char str[128] = {}; srsran_pdcch_nr_info(q, res, str, sizeof(str)); PDCCH_INFO_RX("%s", str); diff --git a/lib/src/phy/phch/pdsch.c b/lib/src/phy/phch/pdsch.c index ee7845bf8..3dc6b29d4 100644 --- a/lib/src/phy/phch/pdsch.c +++ b/lib/src/phy/phch/pdsch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/pdsch_nr.c b/lib/src/phy/phch/pdsch_nr.c index 4fcfe7b0e..1bbf77510 100644 --- a/lib/src/phy/phch/pdsch_nr.c +++ b/lib/src/phy/phch/pdsch_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -330,7 +330,7 @@ static inline int pdsch_nr_encode_codeword(srsran_pdsch_nr_t* q, return SRSRAN_ERROR; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("b="); srsran_vec_fprint_b(stdout, q->b[tb->cw_idx], tb->nof_bits); } @@ -342,7 +342,7 @@ static inline int pdsch_nr_encode_codeword(srsran_pdsch_nr_t* q, // 7.3.1.2 Modulation srsran_mod_modulate(&q->modem_tables[tb->mod], q->b[tb->cw_idx], q->d[tb->cw_idx], tb->nof_bits); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("d="); srsran_vec_fprint_c(stdout, q->d[tb->cw_idx], tb->nof_re); } @@ -446,7 +446,7 @@ static inline int pdsch_nr_decode_codeword(srsran_pdsch_nr_t* q, return SRSRAN_ERROR_OUT_OF_BOUNDS; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("d="); srsran_vec_fprint_c(stdout, q->d[tb->cw_idx], tb->nof_re); } @@ -469,7 +469,7 @@ static inline int pdsch_nr_decode_codeword(srsran_pdsch_nr_t* q, // Descrambling srsran_sequence_apply_c(llr, llr, tb->nof_bits, pdsch_nr_cinit(&q->carrier, cfg, rnti, tb->cw_idx)); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("b="); srsran_vec_fprint_b(stdout, q->b[tb->cw_idx], tb->nof_bits); } @@ -491,7 +491,7 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q, srsran_pdsch_res_nr_t* data) { // Check input pointers - if (!q || !cfg || !grant || !data || !sf_symbols) { + if (!q || !cfg || !grant || !data || !sf_symbols || !channel) { return SRSRAN_ERROR_INVALID_INPUTS; } @@ -525,7 +525,7 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q, return SRSRAN_ERROR; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("ce="); srsran_vec_fprint_c(stdout, channel->ce[0][0], nof_re); DEBUG("x="); @@ -537,8 +537,7 @@ int srsran_pdsch_nr_decode(srsran_pdsch_nr_t* q, // Antenna port demapping // ... Not implemented - srsran_predecoding_type( - q->x, channel->ce, q->d, NULL, 1, 1, 1, 0, nof_re, SRSRAN_TXSCHEME_PORT0, 1.0f, channel->noise_estimate); + srsran_predecoding_single(q->x[0], channel->ce[0][0], q->d[0], NULL, nof_re, 1.0f, channel->noise_estimate); // Layer demapping if (grant->nof_layers > 1) { @@ -578,9 +577,19 @@ static uint32_t pdsch_nr_grant_info(const srsran_pdsch_nr_t* q, } } + // Append RNTI type and id + len = + srsran_print_check(str, str_len, len, "%s-rnti=0x%x ", srsran_rnti_type_str_short(grant->rnti_type), grant->rnti); + // Append time-domain resource mapping - len = srsran_print_check( - str, str_len, len, "rnti=0x%x prb=%d:%d symb=%d:%d ", grant->rnti, first_prb, grant->nof_prb, grant->S, grant->L); + len = srsran_print_check(str, + str_len, + len, + "prb=(%d,%d) symb=(%d,%d) ", + first_prb, + first_prb + grant->nof_prb - 1, + grant->S, + grant->S + grant->L - 1); // Append TB info for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) { diff --git a/lib/src/phy/phch/phch_cfg_nr.c b/lib/src/phy/phch/phch_cfg_nr.c index 29ebf9e34..dc9ee33f1 100644 --- a/lib/src/phy/phch/phch_cfg_nr.c +++ b/lib/src/phy/phch/phch_cfg_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -238,7 +238,7 @@ static uint32_t phch_cfg_uci_to_str(const srsran_uci_cfg_nr_t* uci, char* str, u len = srsran_print_check(str, str_len, len, " beta_csi_part1_offset=%.2f\n", uci->pusch.beta_csi1_offset); len = srsran_print_check(str, str_len, len, " beta_csi_part2_offset=%.2f\n", uci->pusch.beta_csi1_offset); len = srsran_print_check(str, str_len, len, " o_csi1=%d\n", srsran_csi_part1_nof_bits(uci->csi, uci->nof_csi)); - len = srsran_print_check(str, str_len, len, " o_ack=%d\n", uci->o_ack); + len = srsran_print_check(str, str_len, len, " o_ack=%d\n", uci->ack.count); return len; } @@ -260,7 +260,7 @@ uint32_t srsran_sch_cfg_nr_info(const srsran_sch_cfg_nr_t* sch_cfg, char* str, u // Append SCH information len += phch_cfg_sch_to_str(&sch_cfg->sch_cfg, &str[len], str_len - len); - // Append SCH information + // Append reserved RE information len += phch_cfg_rvd_to_str(&sch_cfg->rvd_re, &str[len], str_len - len); // UCI configuration diff --git a/lib/src/phy/phch/phich.c b/lib/src/phy/phch/phich.c index 5054025ce..a6a91bb59 100644 --- a/lib/src/phy/phch/phich.c +++ b/lib/src/phy/phch/phich.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -195,11 +195,6 @@ int srsran_phich_decode(srsran_phich_t* q, uint32_t sf_idx = sf->tti % 10; - if (sf_idx >= SRSRAN_NOF_SF_X_FRAME) { - ERROR("Invalid nslot %d", sf_idx); - return SRSRAN_ERROR_INVALID_INPUTS; - } - if (SRSRAN_CP_ISEXT(q->cell.cp)) { if (n_phich.nseq >= SRSRAN_PHICH_EXT_NSEQUENCES) { ERROR("Invalid nseq %d", n_phich.nseq); @@ -328,11 +323,6 @@ int srsran_phich_encode(srsran_phich_t* q, uint32_t sf_idx = sf->tti % 10; - if (sf_idx >= SRSRAN_NOF_SF_X_FRAME) { - ERROR("Invalid nslot %d", sf_idx); - return SRSRAN_ERROR_INVALID_INPUTS; - } - if (SRSRAN_CP_ISEXT(q->cell.cp)) { if (n_phich.nseq >= SRSRAN_PHICH_EXT_NSEQUENCES) { ERROR("Invalid nseq %d", n_phich.nseq); diff --git a/lib/src/phy/phch/pmch.c b/lib/src/phy/phch/pmch.c index 5842d8b1a..8af4f847f 100644 --- a/lib/src/phy/phch/pmch.c +++ b/lib/src/phy/phch/pmch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -291,9 +291,7 @@ int srsran_pmch_decode(srsran_pmch_t* q, cf_t* sf_symbols[SRSRAN_MAX_PORTS], srsran_pdsch_res_t* out) { - /* Set pointers for layermapping & precoding */ uint32_t i, n; - cf_t* x[SRSRAN_MAX_LAYERS]; if (q != NULL && sf_symbols != NULL && out != NULL && cfg != NULL) { INFO("Decoding PMCH SF: %d, MBSFN area ID: 0x%x, Mod %s, TBS: %d, NofSymbols: %d, NofBitsE: %d, rv_idx: %d, " @@ -308,12 +306,6 @@ int srsran_pmch_decode(srsran_pmch_t* q, cfg->pdsch_cfg.grant.nof_prb, sf->cfi); - /* number of layers equals number of ports */ - for (i = 0; i < q->cell.nof_ports; i++) { - x[i] = q->x[i]; - } - memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSRAN_MAX_LAYERS - q->cell.nof_ports)); - uint32_t lstart = SRSRAN_NOF_CTRL_SYMBOLS(q->cell, sf->cfi); for (int j = 0; j < q->nof_rx_antennas; j++) { /* extract symbols */ @@ -384,6 +376,7 @@ int srsran_pmch_decode(srsran_pmch_t* q, void srsran_configure_pmch(srsran_pmch_cfg_t* pmch_cfg, srsran_cell_t* cell, srsran_mbsfn_cfg_t* mbsfn_cfg) { pmch_cfg->area_id = 1; + pmch_cfg->pdsch_cfg.rnti = SRSRAN_MRNTI; pmch_cfg->pdsch_cfg.grant.nof_layers = 1; pmch_cfg->pdsch_cfg.grant.nof_prb = cell->nof_prb; pmch_cfg->pdsch_cfg.grant.tb[0].mcs_idx = mbsfn_cfg->mbsfn_mcs; @@ -410,9 +403,7 @@ int srsran_pmch_encode(srsran_pmch_t* q, cf_t* sf_symbols[SRSRAN_MAX_PORTS]) { int i; - /* Set pointers for layermapping & precoding */ - cf_t* x[SRSRAN_MAX_LAYERS]; - int ret = SRSRAN_ERROR_INVALID_INPUTS; + int ret = SRSRAN_ERROR_INVALID_INPUTS; if (q != NULL && cfg != NULL) { for (i = 0; i < q->cell.nof_ports; i++) { if (sf_symbols[i] == NULL) { @@ -440,12 +431,6 @@ int srsran_pmch_encode(srsran_pmch_t* q, cfg->pdsch_cfg.grant.tb[0].nof_bits, 0); - /* number of layers equals number of ports */ - for (i = 0; i < q->cell.nof_ports; i++) { - x[i] = q->x[i]; - } - memset(&x[q->cell.nof_ports], 0, sizeof(cf_t*) * (SRSRAN_MAX_LAYERS - q->cell.nof_ports)); - // TODO: use tb_encode directly if (srsran_dlsch_encode(&q->dl_sch, &cfg->pdsch_cfg, data, q->e)) { ERROR("Error encoding TB"); diff --git a/lib/src/phy/phch/prach.c b/lib/src/phy/phch/prach.c index d46aee861..f1fe81754 100644 --- a/lib/src/phy/phch/prach.c +++ b/lib/src/phy/phch/prach.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,6 +24,7 @@ #include #include "srsran/phy/common/phy_common.h" +#include "srsran/phy/common/phy_common_nr.h" #include "srsran/phy/phch/prach.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" @@ -127,16 +128,20 @@ bool srsran_prach_tti_opportunity(srsran_prach_t* p, uint32_t current_tti, int a return false; } - if (p->is_nr) { - return srsran_prach_nr_tti_opportunity_fr1_unpaired(p->config_idx, current_tti); - } - uint32_t config_idx = p->config_idx; - if (!p->tdd_config.configured) { - return srsran_prach_tti_opportunity_config_fdd(config_idx, current_tti, allowed_subframe); + if (p->tdd_config.configured) { + if (p->is_nr) { + return srsran_prach_nr_tti_opportunity_fr1_unpaired(p->config_idx, current_tti); + } else { + return srsran_prach_tti_opportunity_config_tdd( + config_idx, p->tdd_config.sf_config, current_tti, &p->current_prach_idx); + } } else { - return srsran_prach_tti_opportunity_config_tdd( - config_idx, p->tdd_config.sf_config, current_tti, &p->current_prach_idx); + if (p->is_nr) { + return srsran_prach_nr_tti_opportunity_fr1_paired(p->config_idx, current_tti); + } else { + return srsran_prach_tti_opportunity_config_fdd(config_idx, current_tti, allowed_subframe); + } } } @@ -163,6 +168,23 @@ bool srsran_prach_tti_opportunity_config_fdd(uint32_t config_idx, uint32_t curre return false; } +bool srsran_prach_in_window_config_fdd(uint32_t config_idx, uint32_t current_tti, int allowed_subframe) +{ + if (srsran_prach_tti_opportunity_config_fdd(config_idx, current_tti, allowed_subframe)) { + return true; + } + + uint32_t preamble_format = srsran_prach_get_preamble_format(config_idx); + float T_tot = (prach_Tseq[preamble_format] + prach_Tcp[preamble_format]) * SRSRAN_LTE_TS; + uint32_t tti_dur = (uint32_t)ceilf(T_tot * 1000); + for (uint32_t i = 1; i < tti_dur; ++i) { + if (srsran_prach_tti_opportunity_config_fdd(config_idx, current_tti - i, allowed_subframe)) { + return true; + } + } + return false; +} + uint32_t srsran_prach_nof_f_idx_tdd(uint32_t config_idx, uint32_t tdd_ul_dl_config) { if (config_idx < 64 && tdd_ul_dl_config < 7) { @@ -273,6 +295,66 @@ void srsran_prach_sf_config(uint32_t config_idx, srsran_prach_sf_config_t* sf_co memcpy(sf_config, &prach_sf_config[config_idx % 16], sizeof(srsran_prach_sf_config_t)); } +const prach_nr_config_t* srsran_prach_nr_get_cfg_fr1_paired(uint32_t config_idx) +{ + if (config_idx < PRACH_NR_CFG_FR1_PAIRED_NOF_CFG) { + return &prach_nr_cfg_fr1_paired[config_idx]; + } + + ERROR("Invalid configuration index %d", config_idx); + return NULL; +} + +bool srsran_prach_nr_tti_opportunity_fr1_paired(uint32_t config_idx, uint32_t current_tti) +{ + uint32_t sfn = current_tti / SRSRAN_NOF_SF_X_FRAME; + uint32_t sf_idx = current_tti % SRSRAN_NOF_SF_X_FRAME; + + // Get configuration + const prach_nr_config_t* cfg = srsran_prach_nr_get_cfg_fr1_paired(config_idx); + if (cfg == NULL) { + return false; + } + + // Protect zero division + if (cfg->x == 0) { + ERROR("Invalid Zero value"); + return false; + } + + // Check for System Frame Number match + if (sfn % cfg->x != cfg->y) { + return false; + } + + // Protect subframe number vector access + if (cfg->nof_subframe_number > PRACH_NR_CFG_MAX_NOF_SF) { + ERROR("Invalid number of subframes (%d)", cfg->nof_subframe_number); + return false; + } + + // Check for subframe number match + for (uint32_t i = 0; i < cfg->nof_subframe_number; i++) { + if (cfg->subframe_number[i] == sf_idx) { + return true; + } + } + + // If reached here, no opportunity + return false; +} + +uint32_t srsran_prach_nr_start_symbol_fr1_paired(uint32_t config_idx) +{ + // Get configuration + const prach_nr_config_t* cfg = srsran_prach_nr_get_cfg_fr1_paired(config_idx); + if (cfg == NULL) { + return 0; + } + + return cfg->starting_symbol; +} + const prach_nr_config_t* srsran_prach_nr_get_cfg_fr1_unpaired(uint32_t config_idx) { if (config_idx < PRACH_NR_CFG_FR1_UNPAIRED_NOF_CFG) { @@ -322,12 +404,21 @@ bool srsran_prach_nr_tti_opportunity_fr1_unpaired(uint32_t config_idx, uint32_t return false; } -uint32_t srsran_prach_nr_start_symbol_fr1_unpaired(uint32_t config_idx) +uint32_t srsran_prach_nr_start_symbol(uint32_t config_idx, srsran_duplex_mode_t duplex_mode) { - // Get configuration - const prach_nr_config_t* cfg = srsran_prach_nr_get_cfg_fr1_unpaired(config_idx); - if (cfg == NULL) { - return false; + const prach_nr_config_t* cfg; + if (duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + // Get configuration + cfg = srsran_prach_nr_get_cfg_fr1_unpaired(config_idx); + if (cfg == NULL) { + return 0; + } + } else { + // Get configuration + cfg = srsran_prach_nr_get_cfg_fr1_paired(config_idx); + if (cfg == NULL) { + return 0; + } } return cfg->starting_symbol; @@ -533,7 +624,9 @@ int srsran_prach_set_cell_(srsran_prach_t* p, int ret = SRSRAN_ERROR; if (p != NULL && N_ifft_ul < 2049 && cfg->config_idx < 64 && cfg->root_seq_idx < MAX_ROOTS) { if (N_ifft_ul > p->max_N_ifft_ul) { - ERROR("PRACH: Error in set_cell(): N_ifft_ul must be lower or equal max_N_ifft_ul in init()"); + ERROR("PRACH: Error in set_cell(): N_ifft_ul (%d) must be lower or equal max_N_ifft_ul (%d) in init()", + N_ifft_ul, + p->max_N_ifft_ul); return -1; } @@ -661,7 +754,7 @@ int srsran_prach_gen(srsran_prach_t* p, uint32_t seq_index, uint32_t freq_offset uint32_t N_rb_ul = srsran_nof_prb(p->N_ifft_ul); uint32_t k_0 = freq_offset * N_RB_SC - N_rb_ul * N_RB_SC / 2 + p->N_ifft_ul / 2; uint32_t K = DELTA_F / DELTA_F_RA; - uint32_t begin = PHI + (K * k_0) + (p->is_nr ? 1 : (K / 2)); + uint32_t begin = PHI + (K * k_0) + (p->is_nr ? 0 : (K / 2)); if (6 + freq_offset > N_rb_ul) { ERROR("Error no space for PRACH: frequency offset=%d, N_rb_ul=%d", freq_offset, N_rb_ul); @@ -671,11 +764,16 @@ int srsran_prach_gen(srsran_prach_t* p, uint32_t seq_index, uint32_t freq_offset DEBUG( "N_zc: %d, N_cp: %d, N_seq: %d, N_ifft_prach=%d begin: %d", p->N_zc, p->N_cp, p->N_seq, p->N_ifft_prach, begin); - // Map dft-precoded sequence to ifft bins - memset(p->ifft_in, 0, begin * sizeof(cf_t)); - memcpy(&p->ifft_in[begin], get_precoded_dft(p, seq_index), p->N_zc * sizeof(cf_t)); - memset(&p->ifft_in[begin + p->N_zc], 0, (p->N_ifft_prach - begin - p->N_zc) * sizeof(cf_t)); + // Fill bottom guard frequency domain with zeros + srsran_vec_cf_zero(p->ifft_in, begin); + // Map dft-precoded sequence to ifft bins + srsran_vec_cf_copy(&p->ifft_in[begin], get_precoded_dft(p, seq_index), p->N_zc); + + // Fill top guard frequency domain with zeros + srsran_vec_cf_zero(&p->ifft_in[begin + p->N_zc], p->N_ifft_prach - begin - p->N_zc); + + // Generate frequency domain signal srsran_dft_run(&p->ifft, p->ifft_in, p->ifft_out); // Copy CP into buffer @@ -895,7 +993,7 @@ int srsran_prach_detect_offset(srsran_prach_t* p, uint32_t N_rb_ul = srsran_nof_prb(p->N_ifft_ul); uint32_t k_0 = freq_offset * N_RB_SC - N_rb_ul * N_RB_SC / 2 + p->N_ifft_ul / 2; uint32_t K = DELTA_F / DELTA_F_RA; - uint32_t begin = PHI + (K * k_0) + (K / 2); + uint32_t begin = PHI + (K * k_0) + (p->is_nr ? 0 : (K / 2)); memcpy(p->prach_bins, &p->signal_fft[begin], p->N_zc * sizeof(cf_t)); int loops = (p->successive_cancellation) ? SUCCESSIVE_CANCELLATION_ITS : 1; diff --git a/lib/src/phy/phch/prach_tables.h b/lib/src/phy/phch/prach_tables.h index bc0727a5e..f869888c7 100644 --- a/lib/src/phy/phch/prach_tables.h +++ b/lib/src/phy/phch/prach_tables.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -455,10 +455,29 @@ static const prach_nr_config_t prach_nr_cfg_fr1_unpaired[PRACH_NR_CFG_FR1_UNPAIR {0, 1, 0, {8}, 1, 0}, {0, 1, 0, {7}, 1, 0}, {0, 1, 0, {6}, 1, 0}, {0, 1, 0, {5}, 1, 0}, {0, 1, 0, {4}, 1, 0}, {0, 1, 0, {3}, 1, 0}, - {0, 1, 0, {2}, 1, 0}, {0, 1, 0, {1, 6}, 1, 0}, - {0, 1, 0, {1, 6}, 1, 7}, {0, 1, 0, {4, 9}, 1, 0}, - {0, 1, 0, {3, 8}, 1, 0}, {0, 1, 0, {2, 7}, 1, 0}, - {0, 1, 0, {8, 9}, 1, 0}, {0, 1, 0, {4, 8, 9}, 1, 0}, - {0, 1, 0, {3, 4, 9}, 1, 0}, {0, 1, 0, {7, 8, 9}, 1, 0}, - {0, 1, 0, {3, 4, 8, 9}, 1, 0}, {0, 1, 0, {6, 7, 8, 9}, 1, 0}, - {0, 1, 0, {1, 4, 6, 9}, 1, 0}, {0, 1, 0, {1, 3, 5, 7, 9}, 1, 0}}; + {0, 1, 0, {2}, 1, 0}, {0, 1, 0, {1, 6}, 2, 0}, + {0, 1, 0, {1, 6}, 2, 7}, {0, 1, 0, {4, 9}, 2, 0}, + {0, 1, 0, {3, 8}, 2, 0}, {0, 1, 0, {2, 7}, 2, 0}, + {0, 1, 0, {8, 9}, 2, 0}, {0, 1, 0, {4, 8, 9}, 3, 0}, + {0, 1, 0, {3, 4, 9}, 3, 0}, {0, 1, 0, {7, 8, 9}, 3, 0}, + {0, 1, 0, {3, 4, 8, 9}, 4, 0}, {0, 1, 0, {6, 7, 8, 9}, 4, 0}, + {0, 1, 0, {1, 4, 6, 9}, 4, 0}, {0, 1, 0, {1, 3, 5, 7, 9}, 5, 0}}; + +#define PRACH_NR_CFG_FR1_PAIRED_NOF_CFG 28 + +// Table 6.3.3.2-2: Random access configurations for FR1 and paired spectrum. +static const prach_nr_config_t prach_nr_cfg_fr1_paired[PRACH_NR_CFG_FR1_PAIRED_NOF_CFG] = { + {0, 16, 1, {1}, 1, 0}, {0, 16, 1, {4}, 1, 0}, + {0, 16, 1, {7}, 1, 0}, {0, 16, 1, {9}, 1, 0}, + {0, 8, 1, {1}, 1, 0}, {0, 8, 1, {4}, 1, 0}, + {0, 8, 1, {7}, 1, 0}, {0, 8, 1, {9}, 1, 0}, + {0, 4, 1, {1}, 1, 0}, {0, 4, 1, {4}, 1, 0}, + {0, 4, 1, {7}, 1, 0}, {0, 4, 1, {9}, 1, 0}, + {0, 2, 1, {1}, 1, 0}, {0, 2, 1, {4}, 1, 0}, + {0, 2, 1, {7}, 1, 0}, {0, 2, 1, {9}, 1, 0}, + {0, 1, 0, {1}, 1, 0}, {0, 1, 0, {4}, 1, 0}, + {0, 1, 0, {7}, 1, 0}, {0, 1, 0, {1, 6}, 2, 0}, + {0, 1, 0, {2, 7}, 2, 0}, {0, 1, 0, {3, 8}, 2, 0}, + {0, 1, 0, {1, 4, 7}, 3, 0}, {0, 1, 0, {2, 5, 8}, 3, 0}, + {0, 1, 0, {3, 6, 9}, 3, 0}, {0, 1, 0, {0, 2, 4, 6, 8}, 5, 0}, + {0, 1, 0, {1, 3, 5, 7, 9}, 5, 0}, {0, 1, 0, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 10, 0}}; \ No newline at end of file diff --git a/lib/src/phy/phch/prb_dl.c b/lib/src/phy/phch/prb_dl.c index 8e6d79c44..597699baf 100644 --- a/lib/src/phy/phch/prb_dl.c +++ b/lib/src/phy/phch/prb_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/prb_dl.h b/lib/src/phy/phch/prb_dl.h index 2acfe1ac6..e9bbf7225 100644 --- a/lib/src/phy/phch/prb_dl.h +++ b/lib/src/phy/phch/prb_dl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/psbch.c b/lib/src/phy/phch/psbch.c index 270260775..5e7f9b71b 100644 --- a/lib/src/phy/phch/psbch.c +++ b/lib/src/phy/phch/psbch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/pscch.c b/lib/src/phy/phch/pscch.c index 8148abddb..429b228d6 100644 --- a/lib/src/phy/phch/pscch.c +++ b/lib/src/phy/phch/pscch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/pssch.c b/lib/src/phy/phch/pssch.c index ec92751cd..282292531 100644 --- a/lib/src/phy/phch/pssch.c +++ b/lib/src/phy/phch/pssch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -210,6 +210,8 @@ int srsran_pssch_init(srsran_pssch_t* q, ERROR("Error allocating memory"); return SRSRAN_ERROR; } + srsran_vec_cf_zero(q->scfdma_symbols, q->nof_data_symbols * SRSRAN_NRE * SRSRAN_MAX_PRB); + if (srsran_dft_precoding_init(&q->dft_precoder, SRSRAN_MAX_PRB, true)) { ERROR("Error DFT precoder init"); return SRSRAN_ERROR; diff --git a/lib/src/phy/phch/pucch.c b/lib/src/phy/phch/pucch.c index 6edcddc5d..370d85e3c 100644 --- a/lib/src/phy/phch/pucch.c +++ b/lib/src/phy/phch/pucch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/pucch_cfg_nr.c b/lib/src/phy/phch/pucch_cfg_nr.c index c55446126..8734f3a04 100644 --- a/lib/src/phy/phch/pucch_cfg_nr.c +++ b/lib/src/phy/phch/pucch_cfg_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -169,7 +169,8 @@ int srsran_pucch_nr_cfg_resource_valid(const srsran_pucch_nr_resource_t* resourc return SRSRAN_ERROR; } - if (resource->intra_slot_hopping) { + // Frequency hopping is only possible with Format 1 + if (resource->intra_slot_hopping && resource->format != SRSRAN_PUCCH_NR_FORMAT_1) { ERROR("Intra-slot hopping is not implemented"); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/pucch_nr.c b/lib/src/phy/phch/pucch_nr.c index af59b778b..f4f4cf260 100644 --- a/lib/src/phy/phch/pucch_nr.c +++ b/lib/src/phy/phch/pucch_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -406,6 +406,8 @@ int srsran_pucch_nr_format1_encode(const srsran_pucch_nr_t* q, srsran_mod_modulate(&q->qpsk, b, d, 2); } + INFO("[PUCCH Format 1 Data TX] d=%+.3f%+.3f", __real__ d[0], __imag__ d[0]); + // Get group sequence uint32_t u = 0; uint32_t v = 0; @@ -414,41 +416,60 @@ int srsran_pucch_nr_format1_encode(const srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - // Calculate number of symbols carrying PUCCH (No DMRS) - uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, 0); - + // First symbol of this PUCCH transmission uint32_t l_prime = resource->start_symbol_idx; - for (uint32_t l = 1, m = 0; l < resource->nof_symbols; l += 2, m++) { - // Get start of the sequence in resource grid - cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - // Get Alpha index - uint32_t alpha_idx = 0; - if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < - SRSRAN_SUCCESS) { - return SRSRAN_ERROR; + // For each hop + for (uint32_t m_prime = 0, l = 1; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) { + // Calculate number of symbols carrying PUCCH (No DMRS) + uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, m_prime); + + // Get the starting PRB + uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb; + + // For each symbol carrying PUCCH data + for (uint32_t m = 0; m < n_pucch; m++, l += 2) { + // Get start of the sequence in resource grid + cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE]; + + // Get Alpha index + uint32_t alpha_idx = 0; + if (srsran_pucch_nr_alpha_idx(&q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, 0, &alpha_idx) < + SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence"); + return SRSRAN_ERROR; + } + + // Get w_i_m + cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSRAN_NRE]; + srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 Data TX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f z=", + m_prime, + m, + __real__ w_i_m, + __imag__ w_i_m); + srsran_vec_fprint_c(stdout, z, SRSRAN_NRE); + } + + // Put z in the grid + srsran_vec_sc_prod_ccc(z, d[0], slot_symbols_ptr, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 TX] l=%d; x=", l + l_prime); + srsran_vec_fprint_c(stdout, slot_symbols_ptr, SRSRAN_NRE); + } } - - // get r_uv sequence from LUT object - const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); - if (r_uv == NULL) { - ERROR("Getting r_uv sequence"); - return SRSRAN_ERROR; - } - - // Compute y = d(0) * r_uv - cf_t y[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(r_uv, d[0], y, SRSRAN_NRE); - - // Get w_i_m - cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); - - // Compute z(n) = w(i) * y(n) - cf_t z[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(y, w_i_m, z, SRSRAN_NRE); - - // Put z in the grid - srsran_vec_cf_copy(slot_symbols_ptr, z, SRSRAN_NRE); } return SRSRAN_SUCCESS; @@ -461,7 +482,8 @@ int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* q, srsran_chest_ul_res_t* chest_res, cf_t* slot_symbols, uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS], - uint32_t nof_bits) + uint32_t nof_bits, + float* norm_corr) { uint32_t m_cs = 0; @@ -480,8 +502,9 @@ int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - // Received symbol d - cf_t d = 0; + // Accumulates received symbol d and average power + cf_t d = 0; + float pwr_acc = 0.0f; // Get group sequence uint32_t u = 0; @@ -491,52 +514,102 @@ int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - // Calculate number of symbols carrying PUCCH (No DMRS) - uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, 0); - + // First symbol of this PUCCH transmission uint32_t l_prime = resource->start_symbol_idx; - for (uint32_t l = 1, m = 0; l < resource->nof_symbols; l += 2, m++) { - // Get start of the sequence in resource grid - cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - cf_t* ce_ptr = &chest_res->ce[(q->carrier.nof_prb * (l + l_prime) + resource->starting_prb) * SRSRAN_NRE]; - // Equalise x = w(i) * d' * r_uv(n) - cf_t x[SRSRAN_NRE]; - srsran_predecoding_single(slot_symbols_ptr, ce_ptr, x, NULL, SRSRAN_NRE, 1.0f, chest_res->noise_estimate); + // For each hop + uint32_t n_pucch_sum = 0; + for (uint32_t m_prime = 0, l = 1; m_prime < (resource->intra_slot_hopping ? 2 : 1); m_prime++) { + // Calculate number of symbols carrying PUCCH (No DMRS) + uint32_t n_pucch = pucch_nr_format1_n_pucch(resource, m_prime); - // Get Alpha index - uint32_t alpha_idx = 0; - if (srsran_pucch_nr_alpha_idx( - &q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < SRSRAN_SUCCESS) { - return SRSRAN_ERROR; + // Get the starting PRB + uint32_t starting_prb = (m_prime == 0) ? resource->starting_prb : resource->second_hop_prb; + + // For each symbol carrying PUCCH data + for (uint32_t m = 0; m < n_pucch; m++, l += 2) { + // Get start of the sequence in resource grid + cf_t* slot_symbols_ptr = &slot_symbols[(q->carrier.nof_prb * (l + l_prime) + starting_prb) * SRSRAN_NRE]; + cf_t* ce_ptr = &chest_res->ce[SRSRAN_NRE * n_pucch_sum]; + n_pucch_sum++; + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 CE RX] ce="); + srsran_vec_fprint_c(stdout, ce_ptr, SRSRAN_NRE); + } + + // Equalise x = w(i) * d' * r_uv(n) + cf_t x[SRSRAN_NRE]; + srsran_predecoding_single(slot_symbols_ptr, ce_ptr, x, NULL, SRSRAN_NRE, 1.0f, chest_res->noise_estimate); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 RX] l=%d; x=", l + l_prime); + srsran_vec_fprint_c(stdout, x, SRSRAN_NRE); + } + + // Get Alpha index + uint32_t alpha_idx = 0; + if (srsran_pucch_nr_alpha_idx( + &q->carrier, cfg, slot, l, l_prime, resource->initial_cyclic_shift, m_cs, &alpha_idx) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // get r_uv sequence from LUT object + const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); + if (r_uv == NULL) { + ERROR("Getting r_uv sequence"); + return SRSRAN_ERROR; + } + + // Get w_i_m + cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); + + // Compute z(n) = w(i) * r_uv(n) + cf_t z[SRSRAN_NRE]; + srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); + + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("[PUCCH Format 1 Data RX] m_prime=%d; m=%d; w_i_m=%+.3f%+.3f z=", + m_prime, + m, + __real__ w_i_m, + __imag__ w_i_m); + srsran_vec_fprint_c(stdout, z, SRSRAN_NRE); + } + + // Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d' + d += srsran_vec_dot_prod_conj_ccc(x, z, SRSRAN_NRE) / SRSRAN_NRE; + + // Compute and accumulate average symbol power + pwr_acc += srsran_vec_avg_power_cf(x, SRSRAN_NRE); } - - // get r_uv sequence from LUT object - const cf_t* r_uv = srsran_zc_sequence_lut_get(&q->r_uv_1prb, u, v, alpha_idx); - if (r_uv == NULL) { - ERROR("Getting r_uv sequence"); - return SRSRAN_ERROR; - } - // Get w_i_m - cf_t w_i_m = srsran_pucch_nr_format1_w(q, n_pucch, resource->time_domain_occ, m); - - // Compute z(n) = w(i) * r_uv(n) - cf_t z[SRSRAN_NRE]; - srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); - - // Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d' - d += srsran_vec_dot_prod_conj_ccc(x, z, SRSRAN_NRE); } + INFO("[PUCCH Format 1 Data RX] d=%+.3f%+.3f", __real__ d, __imag__ d); + // Demodulate d float llr[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; srsran_demod_soft_demodulate((nof_bits == 1) ? SRSRAN_MOD_BPSK : SRSRAN_MOD_QPSK, &d, llr, 1); - // Hard decision + // Hard decision based on the LLRs sign for (uint32_t i = 0; i < nof_bits; i++) { b[i] = llr[i] > 0.0f ? 1 : 0; } + // Calculate normalised correlation, it uses the absolute value of d and accumulated average power + if (norm_corr != NULL) { + // Get the number of payload symbols. As the one of every 2 symbols carry DMRS, the payload symbols is half of the + // total symbols rounding down + float nsymb = (float)SRSRAN_FLOOR(resource->nof_symbols, 2); + + // Avoid zero, INF or NAN division, set correlation to 0 in this case + if (isnormal(pwr_acc) && isnormal(nsymb)) { + *norm_corr = cabsf(d) / sqrtf(pwr_acc * nsymb); + } else { + *norm_corr = 0.0f; + } + } + return SRSRAN_SUCCESS; } @@ -562,7 +635,7 @@ static int pucch_nr_format2_encode(srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - // Calculate number of encoded symbols + // Calculate number of encoded bits int e = srsran_uci_nr_pucch_format_2_3_4_E(resource); if (e < SRSRAN_SUCCESS) { ERROR("Error selecting E"); @@ -607,8 +680,13 @@ static int pucch_nr_format2_decode(srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - // Calculate number of encoded symbols - uint32_t E = srsran_uci_nr_pucch_format_2_3_4_E(resource); + // Calculate number of encoded bits + int e = srsran_uci_nr_pucch_format_2_3_4_E(resource); + if (e < SRSRAN_SUCCESS) { + ERROR("Error selecting E"); + return SRSRAN_ERROR; + } + uint32_t E = (uint32_t)e; // Undo mapping to physical resources uint32_t l_start = resource->start_symbol_idx; @@ -628,7 +706,7 @@ static int pucch_nr_format2_decode(srsran_pucch_nr_t* q, } } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { INFO("d="); srsran_vec_fprint_c(stdout, q->d, resource->nof_symbols * resource->nof_prb * (SRSRAN_NRE - 4)); INFO("ce="); @@ -636,13 +714,13 @@ static int pucch_nr_format2_decode(srsran_pucch_nr_t* q, } // Equalise - if (srsran_predecoding_single(q->d, q->ce, q->d, NULL, E, 1.0f, chest_res->noise_estimate) < SRSRAN_SUCCESS) { + if (srsran_predecoding_single(q->d, q->ce, q->d, NULL, E / 2, 1.0f, chest_res->noise_estimate) < SRSRAN_SUCCESS) { ERROR("Error Pre-decoding"); return SRSRAN_ERROR; } // Soft-demodulate - if (srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, q->d, llr, E) < SRSRAN_SUCCESS) { + if (srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, q->d, llr, E / 2) < SRSRAN_SUCCESS) { ERROR("Error soft-demodulate"); return SRSRAN_ERROR; } @@ -771,10 +849,10 @@ static uint32_t pucch_nr_resource_info(const srsran_pucch_nr_resource_t* r, char return len; } -uint32_t srsran_pucch_nr_tx_info(const srsran_pucch_nr_resource_t* resource, - const srsran_uci_data_nr_t* uci_data, - char* str, - uint32_t str_len) +uint32_t srsran_pucch_nr_info(const srsran_pucch_nr_resource_t* resource, + const srsran_uci_data_nr_t* uci_data, + char* str, + uint32_t str_len) { uint32_t len = 0; diff --git a/lib/src/phy/phch/pucch_proc.c b/lib/src/phy/phch/pucch_proc.c index fb0e457e7..0010ba4aa 100644 --- a/lib/src/phy/phch/pucch_proc.c +++ b/lib/src/phy/phch/pucch_proc.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/pusch.c b/lib/src/phy/phch/pusch.c index 9740c4f93..efb2a1d28 100644 --- a/lib/src/phy/phch/pusch.c +++ b/lib/src/phy/phch/pusch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/pusch_nr.c b/lib/src/phy/phch/pusch_nr.c index 3669959a4..2de85a8f2 100644 --- a/lib/src/phy/phch/pusch_nr.c +++ b/lib/src/phy/phch/pusch_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -383,17 +383,15 @@ static int pusch_nr_gen_mux_uci(srsran_pusch_nr_t* q, const srsran_uci_cfg_nr_t* // if the number of HARQ-ACK information bits to be transmitted on PUSCH is 0, 1 or 2 bits uint32_t G_ack_rvd = 0; - if (cfg->o_ack <= 2) { + if (cfg->ack.count <= 2) { // the number of reserved resource elements for potential HARQ-ACK transmission is calculated according to Clause // 6.3.2.4.2.1, by setting O_ACK = 2 ; G_ack_rvd = srsran_uci_nr_pusch_ack_nof_bits(&cfg->pusch, 2); - - // Disable non reserved HARQ-ACK bits - G_ack = 0; } // Counters uint32_t m_ack_count = 0; + uint32_t m_rvd_count = 0; uint32_t m_csi1_count = 0; uint32_t m_csi2_count = 0; uint32_t m_ulsch_count = 0; @@ -420,15 +418,26 @@ static int pusch_nr_gen_mux_uci(srsran_pusch_nr_t* q, const srsran_uci_cfg_nr_t* // Compute HARQ-ACK bits multiplexing uint32_t ack_d = 0; uint32_t ack_m_re_count = 0; + uint32_t rvd_d = 0; + uint32_t rvd_m_re_count = 0; if (l >= l1) { - if (cfg->o_ack <= 2 && m_ack_count < G_ack_rvd) { - ack_d = 1; - ack_m_re_count = M_ulsch_sc; - if (G_ack_rvd - m_ack_count < M_uci_sc * Nl * Qm) { - ack_d = (M_uci_sc * Nl * Qm) / (G_ack_rvd - m_ack_count); - ack_m_re_count = SRSRAN_CEIL(G_ack_rvd - m_ack_count, Nl * Qm); + if (cfg->ack.count <= 2 && m_rvd_count < G_ack_rvd) { + rvd_d = 1; + rvd_m_re_count = M_ulsch_sc; + if (G_ack_rvd - m_rvd_count < M_uci_sc * Nl * Qm) { + rvd_d = (M_uci_sc * Nl * Qm) / (G_ack_rvd - m_rvd_count); + rvd_m_re_count = SRSRAN_CEIL(G_ack_rvd - m_rvd_count, Nl * Qm); + } + M_uci_rvd = rvd_m_re_count; + + if (m_ack_count < G_ack) { + ack_d = 1; + ack_m_re_count = M_uci_rvd; + if (G_ack - m_ack_count < M_uci_rvd * Nl * Qm) { + ack_d = (M_uci_rvd * Nl * Qm) / (G_ack - m_ack_count); + ack_m_re_count = SRSRAN_CEIL(G_ack - m_ack_count, Nl * Qm); + } } - M_uci_rvd = ack_m_re_count; } else if (m_ack_count < G_ack) { ack_d = 1; ack_m_re_count = M_ulsch_sc; @@ -469,14 +478,14 @@ static int pusch_nr_gen_mux_uci(srsran_pusch_nr_t* q, const srsran_uci_cfg_nr_t* // Leave the rest for UL-SCH uint32_t ulsch_m_re_count = M_uci_sc; - for (uint32_t i = 0, csi1_i = 0, csi2_i = 0; i < cfg->pusch.M_pusch_sc[l]; i++) { - // Check if RE is reserved for ACK + for (uint32_t i = 0, csi1_i = 0, csi2_i = 0, rvd_i = 0; i < cfg->pusch.M_pusch_sc[l]; i++) { + // Check if RE is reserved bool reserved = false; - if (ack_m_re_count != 0 && i % ack_d == 0 && m_ack_count < G_ack_rvd) { + if (rvd_m_re_count != 0 && i % rvd_d == 0 && m_rvd_count < G_ack_rvd) { reserved = true; } - if (ack_m_re_count != 0 && i % ack_d == 0 && m_ack_count < G_ack) { + if (G_ack_rvd == 0 && ack_m_re_count != 0 && i % ack_d == 0 && m_ack_count < G_ack) { for (uint32_t j = 0; j < Nl * Qm; j++) { pos_ack[m_ack_count++] = m_all_count + j; } @@ -507,14 +516,15 @@ static int pusch_nr_gen_mux_uci(srsran_pusch_nr_t* q, const srsran_uci_cfg_nr_t* // Set reserved bits only if there are ACK bits if (reserved) { - if (cfg->o_ack > 0) { + if (ack_m_re_count != 0 && rvd_i % ack_d == 0 && m_ack_count < G_ack) { for (uint32_t j = 0; j < Nl * Qm; j++) { pos_ack[m_ack_count++] = m_all_count + j; } - } else { - m_ack_count += Nl * Qm; + ack_m_re_count--; } - ack_m_re_count--; + m_rvd_count += Nl * Qm; + rvd_m_re_count--; + rvd_i++; } // Increment all bit counter @@ -540,8 +550,8 @@ static int pusch_nr_gen_mux_uci(srsran_pusch_nr_t* q, const srsran_uci_cfg_nr_t* q->G_ulsch = m_ulsch_count; // Assert Number of bits - if (G_ack_rvd != 0 && G_ack_rvd != m_ack_count && cfg->o_ack > 0) { - ERROR("Not matched %d!=%d", G_ack_rvd, m_ack_count); + if (G_ack_rvd != 0 && G_ack_rvd != m_rvd_count && cfg->ack.count <= 2) { + ERROR("Not matched %d!=%d", G_ack_rvd, m_rvd_count); } if (G_ack != 0 && G_ack != m_ack_count) { ERROR("Not matched %d!=%d", G_ack, m_ack_count); @@ -555,12 +565,12 @@ static int pusch_nr_gen_mux_uci(srsran_pusch_nr_t* q, const srsran_uci_cfg_nr_t* } // Print debug information if configured for ity - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { if (m_ulsch_count != 0) { DEBUG("UL-SCH bit positions:"); srsran_vec_fprint_i(stdout, (int*)pos_ulsch, m_ulsch_count); } - if (m_ack_count != 0 && cfg->o_ack > 0) { + if (m_ack_count != 0 && cfg->ack.count > 0) { DEBUG("HARQ-ACK bit positions [%d]:", m_ack_count); srsran_vec_fprint_i(stdout, (int*)pos_ack, m_ack_count); } @@ -661,7 +671,7 @@ static inline int pusch_nr_encode_codeword(srsran_pusch_nr_t* q, } } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("b="); srsran_vec_fprint_b(stdout, b, nof_bits); } @@ -671,7 +681,7 @@ static inline int pusch_nr_encode_codeword(srsran_pusch_nr_t* q, srsran_sequence_apply_bit(b, q->b[tb->cw_idx], nof_bits, cinit); // Special Scrambling condition - if (cfg->uci.o_ack <= 2) { + if (cfg->uci.ack.count <= 2) { for (uint32_t i = 0; i < q->G_ack; i++) { uint32_t idx = q->pos_ack[i]; if (q->g_ack[i] == (uint8_t)UCI_BIT_REPETITION) { @@ -687,7 +697,7 @@ static inline int pusch_nr_encode_codeword(srsran_pusch_nr_t* q, // 7.3.1.2 Modulation srsran_mod_modulate(&q->modem_tables[tb->mod], q->b[tb->cw_idx], q->d[tb->cw_idx], nof_bits); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("d="); srsran_vec_fprint_c(stdout, q->d[tb->cw_idx], tb->nof_re); } @@ -724,7 +734,7 @@ int srsran_pusch_nr_encode(srsran_pusch_nr_t* q, return SRSRAN_ERROR; } - // 7.3.1.1 and 7.3.1.2 + // 6.3.1.1 and 6.3.1.2 uint32_t nof_cw = 0; for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { nof_cw += grant->tb[tb].enabled ? 1 : 0; @@ -736,20 +746,23 @@ int srsran_pusch_nr_encode(srsran_pusch_nr_t* q, } } - // 7.3.1.3 Layer mapping + // 6.3.1.3 Layer mapping cf_t** x = q->d; if (grant->nof_layers > 1) { x = q->x; srsran_layermap_nr(q->d, nof_cw, x, grant->nof_layers, grant->nof_layers); } - // 7.3.1.4 Antenna port mapping + // 6.3.1.4 Transform precoding // ... Not implemented - // 7.3.1.5 Mapping to virtual resource blocks + // 6.3.1.5 Precoding // ... Not implemented - // 7.3.1.6 Mapping from virtual to physical resource blocks + // 6.3.1.6 Mapping to virtual resource blocks + // ... Not implemented + + // 6.3.1.7 Mapping from virtual to physical resource blocks int n = pusch_nr_put(q, cfg, grant, x[0], sf_symbols[0]); if (n < SRSRAN_SUCCESS) { ERROR("Putting NR PUSCH resources"); @@ -793,7 +806,7 @@ static inline int pusch_nr_decode_codeword(srsran_pusch_nr_t* q, return SRSRAN_ERROR_OUT_OF_BOUNDS; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("d="); srsran_vec_fprint_c(stdout, q->d[tb->cw_idx], tb->nof_re); } @@ -802,7 +815,7 @@ static inline int pusch_nr_decode_codeword(srsran_pusch_nr_t* q, uint32_t nof_bits = tb->nof_re * srsran_mod_bits_x_symbol(tb->mod); // Calculate HARQ-ACK bits - int n = srsran_uci_nr_pusch_ack_nof_bits(&cfg->uci.pusch, cfg->uci.o_ack); + int n = srsran_uci_nr_pusch_ack_nof_bits(&cfg->uci.pusch, cfg->uci.ack.count); if (n < SRSRAN_SUCCESS) { ERROR("Calculating G_ack"); return SRSRAN_ERROR; @@ -841,13 +854,20 @@ static inline int pusch_nr_decode_codeword(srsran_pusch_nr_t* q, // Descrambling srsran_sequence_apply_c(llr, llr, nof_bits, pusch_nr_cinit(&q->carrier, cfg, rnti, tb->cw_idx)); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("b="); srsran_vec_fprint_bs(stdout, llr, nof_bits); } // Demultiplex UCI only if necessary if (q->uci_mux) { + // As it can be HARQ-ACK takes LLRs from ULSCH, demultiplex HARQ-ACK first + int8_t* g_ack = (int8_t*)q->g_ack; + for (uint32_t i = 0; i < q->G_ack; i++) { + g_ack[i] = llr[q->pos_ack[i]]; + llr[q->pos_ack[i]] = 0; + } + // Demultiplex UL-SCH, change sign int8_t* g_ulsch = (int8_t*)q->g_ulsch; for (uint32_t i = 0; i < q->G_ulsch; i++) { @@ -857,12 +877,6 @@ static inline int pusch_nr_decode_codeword(srsran_pusch_nr_t* q, g_ulsch[i] = 0; } - // Demultiplex HARQ-ACK - int8_t* g_ack = (int8_t*)q->g_ack; - for (uint32_t i = 0; i < q->G_ack; i++) { - g_ack[i] = llr[q->pos_ack[i]]; - } - // Demultiplex CSI part 1 int8_t* g_csi1 = (int8_t*)q->g_csi1; for (uint32_t i = 0; i < q->G_csi1; i++) { @@ -921,7 +935,7 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q, srsran_pusch_res_nr_t* data) { // Check input pointers - if (!q || !cfg || !grant || !data || !sf_symbols) { + if (!q || !cfg || !grant || !data || !sf_symbols || !channel) { return SRSRAN_ERROR_INVALID_INPUTS; } @@ -966,7 +980,7 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q, return SRSRAN_ERROR; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("ce="); srsran_vec_fprint_c(stdout, channel->ce[0][0], nof_re); DEBUG("x="); @@ -978,8 +992,7 @@ int srsran_pusch_nr_decode(srsran_pusch_nr_t* q, // Antenna port demapping // ... Not implemented - srsran_predecoding_type( - q->x, channel->ce, q->d, NULL, 1, 1, 1, 0, nof_re, SRSRAN_TXSCHEME_PORT0, 1.0f, channel->noise_estimate); + srsran_predecoding_single(q->x[0], channel->ce[0][0], q->d[0], NULL, nof_re, 1.0f, channel->noise_estimate); // Layer demapping if (grant->nof_layers > 1) { @@ -1019,9 +1032,19 @@ static uint32_t pusch_nr_grant_info(const srsran_pusch_nr_t* q, } } + // Append RNTI type and id + len = + srsran_print_check(str, str_len, len, "%s-rnti=0x%x ", srsran_rnti_type_str_short(grant->rnti_type), grant->rnti); + // Append time-domain resource mapping - len = srsran_print_check( - str, str_len, len, "rnti=0x%x prb=%d:%d symb=%d:%d ", grant->rnti, first_prb, grant->nof_prb, grant->S, grant->L); + len = srsran_print_check(str, + str_len, + len, + "prb=(%d,%d) symb=(%d,%d) ", + first_prb, + first_prb + grant->nof_prb - 1, + grant->S, + grant->S + grant->L - 1); // Append TB info for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) { @@ -1053,11 +1076,12 @@ uint32_t srsran_pusch_nr_rx_info(const srsran_pusch_nr_t* q, len += pusch_nr_grant_info(q, cfg, grant, res, &str[len], str_len - len); if (res != NULL && srsran_uci_nr_total_bits(&cfg->uci) > 0) { - len = srsran_print_check(str, str_len, len, "UCI: "); srsran_uci_data_nr_t uci_data = {}; uci_data.cfg = cfg->uci; uci_data.value = res->uci; len += srsran_uci_nr_info(&uci_data, &str[len], str_len - len); + + len = srsran_print_check(str, str_len, len, "valid=%c ", res->uci.valid ? 'y' : 'n'); } if (q->meas_time_en) { diff --git a/lib/src/phy/phch/ra.c b/lib/src/phy/phch/ra.c index e0fa51dbe..b240f300b 100644 --- a/lib/src/phy/phch/ra.c +++ b/lib/src/phy/phch/ra.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/ra_dl.c b/lib/src/phy/phch/ra_dl.c index 925785136..c240988da 100644 --- a/lib/src/phy/phch/ra_dl.c +++ b/lib/src/phy/phch/ra_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/ra_dl_nr.c b/lib/src/phy/phch/ra_dl_nr.c index c462d7a89..e963c4f5e 100644 --- a/lib/src/phy/phch/ra_dl_nr.c +++ b/lib/src/phy/phch/ra_dl_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -124,7 +124,7 @@ int srsran_ra_dl_nr_time_default_A(uint32_t m, srsran_dmrs_sch_typeA_pos_t dmrs_ static void ra_dl_nr_time_hl(const srsran_sch_time_ra_t* hl_ra_cfg, srsran_sch_grant_nr_t* grant) { // Compute S and L from SLIV from higher layers - ra_helper_compute_s_and_l(SRSRAN_NSYMB_PER_SLOT_NR, hl_ra_cfg->sliv, &grant->S, &grant->L); + srsran_sliv_to_s_and_l(SRSRAN_NSYMB_PER_SLOT_NR, hl_ra_cfg->sliv, &grant->S, &grant->L); grant->k = hl_ra_cfg->k; grant->mapping = hl_ra_cfg->mapping_type; @@ -149,7 +149,8 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, // Determine which PDSCH Time domain RA configuration to apply (TS 38.214 Table 5.1.2.1.1-1) if (rnti_type == srsran_rnti_type_si && ss_type == srsran_search_space_type_common_0) { // Row 1 - ERROR("Row not implemented"); + // Note: Only Default A is supported, which corresponds SS/PBCH block and coreset mux pattern 1 + srsran_ra_dl_nr_time_default_A(m, cfg->typeA_pos, grant); } else if (rnti_type == srsran_rnti_type_si && ss_type == srsran_search_space_type_common_0A) { // Row 2 ERROR("Row not implemented"); @@ -186,7 +187,7 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, srsran_ra_dl_nr_time_default_A(m, cfg->typeA_pos, grant); } } else { - ERROR("Unhandled case %s, ss_type=%d", srsran_rnti_type_str(rnti_type), ss_type); + ERROR("Unhandled case %s, ss_type=%s", srsran_rnti_type_str(rnti_type), srsran_ss_type_str(ss_type)); } // Validate S and L parameters @@ -198,69 +199,98 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, return SRSRAN_SUCCESS; } -int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(const srsran_dmrs_sch_cfg_t* cfg, - srsran_sch_grant_nr_t* grant) +int srsran_ra_dl_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_dl_nr_t* dci, + srsran_dmrs_sch_len_t* dmrs_duration) { - if (cfg == NULL || grant == NULL) { - return SRSRAN_ERROR_INVALID_INPUTS; + // Table 7.3.1.2.2-1: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=1 + // Table 7.3.1.2.2-3: Antenna port(s) (1000 + DMRS port), dmrs-Type=2, maxLength=1 + if (cfg->dmrs_max_length == srsran_dmrs_sch_len_1) { + *dmrs_duration = srsran_dmrs_sch_len_1; + return SRSRAN_SUCCESS; } - /* According to TS 38.214 V15.10.0 5.1.6.1.3 CSI-RS for mobility: - * When receiving PDSCH scheduled by DCI format 1_0, the UE shall assume the number of DM-RS CDM groups without data - * is 1 which corresponds to CDM group 0 for the case of PDSCH with allocation duration of 2 symbols, and the UE - * shall assume that the number of DM-RS CDM groups without data is 2 which corresponds to CDM group {0,1} for all - * other cases. - */ - if (cfg->length == srsran_dmrs_sch_len_2) { - grant->nof_dmrs_cdm_groups_without_data = 1; - } else { - grant->nof_dmrs_cdm_groups_without_data = 2; - } - - return SRSRAN_SUCCESS; -} - -/* RBG size for type0 scheduling as in table 5.1.2.2.1-1 of 36.214 */ -uint32_t srsran_ra_dl_nr_type0_P(uint32_t bwp_size, bool config_is_1) -{ - if (bwp_size <= 36) { - return config_is_1 ? 2 : 4; - } else if (bwp_size <= 72) { - return config_is_1 ? 4 : 8; - } else if (bwp_size <= 144) { - return config_is_1 ? 8 : 16; - } else { - return 16; - } -} - -static int ra_freq_type0(const srsran_carrier_nr_t* carrier, - const srsran_sch_hl_cfg_nr_t* cfg, - const srsran_dci_dl_nr_t* dci_dl, - srsran_sch_grant_nr_t* grant) -{ - uint32_t P = srsran_ra_dl_nr_type0_P(carrier->nof_prb, cfg->rbg_size_cfg_1); - - uint32_t N_rbg = (int)ceilf((float)(carrier->nof_prb + (carrier->start % P)) / P); - uint32_t rbg_offset = 0; - for (uint32_t i = 0; i < N_rbg; i++) { - uint32_t rbg_size = P; - if (i == 0) { - rbg_size -= (carrier->start % P); - } else if ((i == N_rbg - 1) && ((carrier->nof_prb + carrier->start) % P) > 0) { - rbg_size = (carrier->nof_prb + carrier->start) % P; + // Table 7.3.1.2.2-2: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=2 + if (cfg->dmrs_type == srsran_dmrs_sch_type_1 && cfg->dmrs_max_length == srsran_dmrs_sch_len_2) { + // Only one codeword supported! + if (dci->ports < 12) { + *dmrs_duration = srsran_dmrs_sch_len_1; + return SRSRAN_SUCCESS; } - if (dci_dl->freq_domain_assigment & (1 << (N_rbg - i - 1))) { - for (uint32_t j = 0; j < rbg_size; j++) { - if (rbg_offset + j < carrier->nof_prb) { - grant->prb_idx[rbg_offset + j] = true; - grant->nof_prb++; - } + + if (dci->ports < 31) { + *dmrs_duration = srsran_dmrs_sch_len_2; + return SRSRAN_SUCCESS; + } + + ERROR("reserved value for ports (%d)", dci->ports); + return SRSRAN_ERROR; + } + + // Table 7.3.1.2.2-4: Antenna port(s) (1000 + DMRS port), dmrs-Type=2, maxLength=2 + if (cfg->dmrs_type == srsran_dmrs_sch_type_2 && cfg->dmrs_max_length == srsran_dmrs_sch_len_2) { + // Only one codeword supported! + if (dci->ports < 3) { + *dmrs_duration = srsran_dmrs_sch_len_1; + return SRSRAN_SUCCESS; + } + + if (dci->ports < 24) { + *dmrs_duration = srsran_dmrs_sch_len_2; + return SRSRAN_SUCCESS; + } + + ERROR("reserved value for ports (%d)", dci->ports); + return SRSRAN_ERROR; + } + + ERROR("Unhandled case"); + return SRSRAN_ERROR; +} + +int srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_dl_nr_t* dci, + uint32_t L) +{ + // According to TS 38.214 5.1.6.2 DM-RS reception procedure + switch (dci->ctx.format) { + case srsran_dci_format_nr_1_0: + // When receiving PDSCH scheduled by DCI format 1_0, the UE shall assume the number of DM-RS CDM groups + // without data is 1 which corresponds to CDM group 0 for the case of PDSCH with allocation duration of 2 symbols, + // and the UE shall assume that the number of DM-RS CDM groups without data is 2 which corresponds to CDM group + // {0,1} for all other cases. + if (L == 2) { + return 1; + } else { + return 2; } - } - rbg_offset += rbg_size; + return SRSRAN_SUCCESS; + case srsran_dci_format_nr_1_1: + // When receiving PDSCH scheduled by DCI format 1_1, the UE shall assume that the CDM groups indicated in the + // configured index from Tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of [5, TS. 38.212] contain + // potential co- scheduled downlink DM-RS and are not used for data transmission, where "1", "2" and "3" for the + // number of DM-RS CDM group(s) in Tables 7.3.1.2.2-1, 7.3.1.2.2-2, 7.3.1.2.2-3, 7.3.1.2.2-4 of [5, TS. 38.212] + // correspond to CDM group 0, {0,1}, {0,1,2}, respectively. + + // Table 7.3.1.2.2-1: Antenna port(s) (1000 + DMRS port), dmrs-Type=1, maxLength=1 + if (cfg->dmrs_type == srsran_dmrs_sch_type_1 && cfg->dmrs_max_length == srsran_dmrs_sch_len_1) { + if (dci->ports < 3) { + return 1; + } + if (dci->ports < 12) { + return 2; + } + ERROR("Invalid ports=%d;", dci->ports); + return SRSRAN_ERROR; + } + + ERROR("Unhandled case (%d, %d)", cfg->dmrs_type, cfg->dmrs_max_length); + return SRSRAN_ERROR; + default: + ERROR("Invalid DL DCI format %s", srsran_dci_format_nr_string(dci->ctx.format)); } - return 0; + + return SRSRAN_ERROR; } int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier, @@ -268,17 +298,55 @@ int srsran_ra_dl_nr_freq(const srsran_carrier_nr_t* carrier, const srsran_dci_dl_nr_t* dci_dl, srsran_sch_grant_nr_t* grant) { - if (cfg == NULL || grant == NULL || dci_dl == NULL) { + if (carrier == NULL || cfg == NULL || grant == NULL || dci_dl == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - // RA scheme - if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) { - // when the scheduling grant is received with DCI format 1_0 , then downlink resource allocation type 1 is used. - return ra_helper_freq_type1(carrier->nof_prb, dci_dl->freq_domain_assigment, grant); + // For a PDSCH scheduled with a DCI format 1_0 in any type of PDCCH common search space, regardless of which + // bandwidth part is the active bandwidth part, RB numbering starts from the lowest RB of the CORESET in which the + // DCI was received; otherwise RB numbering starts from the lowest RB in the determined downlink bandwidth part. + uint32_t start_rb = 0; + if (dci_dl->ctx.format == srsran_dci_format_nr_1_0 && SRSRAN_SEARCH_SPACE_IS_COMMON(dci_dl->ctx.ss_type)) { + start_rb = dci_dl->ctx.coreset_start_rb; } - ra_freq_type0(carrier, cfg, dci_dl, grant); - ERROR("Only DCI Format 1_0 is supported"); + // when DCI format 1_0 is decoded in any common search space in which case the size of CORESET 0 shall be used if + // CORESET 0 is configured for the cell and the size of initial DL bandwidth part shall be used if CORESET 0 is not + // configured for the cell. + uint32_t type1_bwp_sz = carrier->nof_prb; + if (SRSRAN_SEARCH_SPACE_IS_COMMON(dci_dl->ctx.ss_type) && dci_dl->coreset0_bw != 0) { + type1_bwp_sz = dci_dl->coreset0_bw; + } + + // The UE shall assume that when the scheduling grant is received with DCI format 1_0 , then downlink resource + // allocation type 1 is used. + if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) { + return ra_helper_freq_type1(type1_bwp_sz, start_rb, dci_dl->freq_domain_assigment, grant); + } + + // If the scheduling DCI is configured to indicate the downlink resource allocation type as part of the Frequency + // domain resource assignment field by setting a higher layer parameter resourceAllocation in pdsch-Config to + // 'dynamicswitch', the UE shall use downlink resource allocation type 0 or type 1 as defined by this DCI field. + if (cfg->alloc == srsran_resource_alloc_dynamic) { + ERROR("Unsupported dynamic resource allocation"); + return SRSRAN_ERROR; + } + + // Otherwise the UE shall use the downlink frequency resource allocation type as defined by the higher layer parameter + // resourceAllocation. + if (cfg->alloc == srsran_resource_alloc_type1) { + return ra_helper_freq_type1(type1_bwp_sz, start_rb, dci_dl->freq_domain_assigment, grant); + } + + if (cfg->alloc == srsran_resource_alloc_type0) { + return ra_helper_freq_type0(carrier, cfg, dci_dl->freq_domain_assigment, grant); + } + + ERROR("Unhandled case"); return SRSRAN_ERROR; } + +uint32_t srsran_ra_nr_type1_riv(uint32_t N_prb, uint32_t start_rb, uint32_t length_rb) +{ + return srsran_sliv_from_s_and_l(N_prb, start_rb, length_rb); +} diff --git a/lib/src/phy/phch/ra_helper.h b/lib/src/phy/phch/ra_helper.h index 05bed4e2f..63ea66c37 100644 --- a/lib/src/phy/phch/ra_helper.h +++ b/lib/src/phy/phch/ra_helper.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,33 +22,68 @@ #ifndef SRSRAN_RA_HELPER_H #define SRSRAN_RA_HELPER_H +#include "srsran/phy/common/sliv.h" #include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" #include -static inline void ra_helper_compute_s_and_l(uint32_t N, uint32_t v, uint32_t* S, uint32_t* L) +/* RBG size for type0 scheduling as in table 5.1.2.2.1-1 of 36.214 */ +static uint32_t ra_helper_type0_P(uint32_t bwp_size, bool config_is_1) { - uint32_t low = v % N; - uint32_t high = v / N; - if (high + 1 + low <= N) { - *S = low; - *L = high + 1; + if (bwp_size <= 36) { + return config_is_1 ? 2 : 4; + } else if (bwp_size <= 72) { + return config_is_1 ? 4 : 8; + } else if (bwp_size <= 144) { + return config_is_1 ? 8 : 16; } else { - *S = N - 1 - low; - *L = N - high + 1; + return 16; } } -static int ra_helper_freq_type1(uint32_t N_bwp_size, uint32_t riv, srsran_sch_grant_nr_t* grant) +static int ra_helper_freq_type0(const srsran_carrier_nr_t* carrier, + const srsran_sch_hl_cfg_nr_t* cfg, + uint32_t riv, + srsran_sch_grant_nr_t* grant) +{ + uint32_t P = ra_helper_type0_P(carrier->nof_prb, cfg->rbg_size_cfg_1); + + uint32_t N_rbg = (int)ceilf((float)(carrier->nof_prb + (carrier->start % P)) / P); + uint32_t rbg_offset = 0; + for (uint32_t i = 0; i < N_rbg; i++) { + uint32_t rbg_size = P; + if (i == 0) { + rbg_size -= (carrier->start % P); + } else if ((i == N_rbg - 1) && ((carrier->nof_prb + carrier->start) % P) > 0) { + rbg_size = (carrier->nof_prb + carrier->start) % P; + } + if (riv & (1 << (N_rbg - i - 1))) { + for (uint32_t j = 0; j < rbg_size; j++) { + if (rbg_offset + j < carrier->nof_prb) { + grant->prb_idx[rbg_offset + j] = true; + grant->nof_prb++; + } + } + } + rbg_offset += rbg_size; + } + return 0; +} + +static int ra_helper_freq_type1(uint32_t N_bwp_size, uint32_t start_rb, uint32_t riv, srsran_sch_grant_nr_t* grant) { uint32_t start = 0; uint32_t len = 0; - ra_helper_compute_s_and_l(N_bwp_size, riv, &start, &len); + srsran_sliv_to_s_and_l(N_bwp_size, riv, &start, &len); if (start + len > N_bwp_size) { ERROR("RIV 0x%x for BWP size %d resulted in freq=%d:%d", riv, N_bwp_size, start, len); return SRSRAN_ERROR; } + // Apply numbering start + start += start_rb; + for (uint32_t i = 0; i < start; i++) { grant->prb_idx[i] = false; } diff --git a/lib/src/phy/phch/ra_nbiot.c b/lib/src/phy/phch/ra_nbiot.c index 8e75a06b9..542226a04 100644 --- a/lib/src/phy/phch/ra_nbiot.c +++ b/lib/src/phy/phch/ra_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/ra_nr.c b/lib/src/phy/phch/ra_nr.c index 3aab83510..96368f855 100644 --- a/lib/src/phy/phch/ra_nr.c +++ b/lib/src/phy/phch/ra_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,12 +21,14 @@ #include "srsran/phy/phch/ra_nr.h" #include "srsran/phy/ch_estimation/csi_rs.h" +#include "srsran/phy/fec/cbsegm.h" #include "srsran/phy/phch/csi.h" #include "srsran/phy/phch/pdsch_nr.h" #include "srsran/phy/phch/ra_dl_nr.h" #include "srsran/phy/phch/ra_ul_nr.h" #include "srsran/phy/phch/uci_nr.h" #include "srsran/phy/utils/debug.h" +#include /* floor */ typedef struct { srsran_mod_t modulation; @@ -38,6 +40,7 @@ typedef struct { #define RA_NR_MCS_SIZE_TABLE2 28 #define RA_NR_MCS_SIZE_TABLE3 29 #define RA_NR_TBS_SIZE_TABLE 93 +#define RA_NR_CQI_TABLE_SIZE 16 #define RA_NR_BETA_OFFSET_HARQACK_SIZE 32 #define RA_NR_BETA_OFFSET_CSI_SIZE 32 @@ -139,12 +142,99 @@ static const float ra_nr_beta_offset_csi_table[RA_NR_BETA_OFFSET_CSI_SIZE] = { 4.000f, 5.000f, 6.250f, 8.000f, 10.000f, 12.625f, 15.875f, 20.000f, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN, NAN}; -typedef enum { ra_nr_table_1 = 0, ra_nr_table_2, ra_nr_table_3 } ra_nr_table_t; +typedef enum { ra_nr_table_idx_1 = 0, ra_nr_table_idx_2, ra_nr_table_idx_3 } ra_nr_table_idx_t; -static ra_nr_table_t ra_nr_select_table_pusch_noprecoding(srsran_mcs_table_t mcs_table, - srsran_dci_format_nr_t dci_format, - srsran_search_space_type_t search_space_type, - srsran_rnti_type_t rnti_type) +/** + * The table below performs the mapping of the CQI into the closest MCS, based on the corresponding spectral efficiency. + * The mapping works as follows: + * - select spectral efficiency from the CQI from tables Table 5.2.2.1-2, Table 5.2.2.1-3, or Table 5.2.2.1-4, + * TS 38.214 V15.14.0 + * - select MCS corresponding to same spectral efficiency from Table 5.1.3.1-1, Table 5.1.3.1-2, or Table 5.1.3.1-3, + * TS 38.214 V15.14.0 + * + * The array ra_nr_cqi_to_mcs_table[CQI_table_idx][MCS_table_idx][CQI] contains the MCS corresponding to CQI, based on + * the given CQI_table_idx and MCS_table_idx tables + * CQI_table_idx: 1 -> Table 5.2.2.1-2; 2 -> Table 5.2.2.1-3, 3 -> Table 5.2.2.1-4 + * MCS_table_idx: 1 -> Table 5.1.3.1-1; 2 -> Table 5.1.3.1-2; 3 -> Table 5.1.3.1-3 + */ + +static const int ra_nr_cqi_to_mcs_table[3][3][RA_NR_CQI_TABLE_SIZE] = { + /* ROW 1 - CQI Table 1 */ + {/* MCS Table 1 */ {-1, 0, 0, 2, 4, 6, 8, 11, 13, 15, 18, 20, 22, 24, 26, 28}, + /* MCS Table 2 */ {-1, 0, 0, 1, 2, 3, 4, 5, 7, 9, 11, 13, 15, 17, 19, 21}, + /* MCS Table 3 */ {-1, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 28, 28}}, + /* ROW 2 - CQI Table 2 */ + {/* MCS Table 1 */ {-1, 0, 2, 6, 11, 13, 15, 18, 20, 22, 24, 26, 28, 28, 28, 28}, + /* MCS Table 2 */ {-1, 0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27}, + /* MCS Table 3 */ {-1, 4, 8, 12, 16, 18, 20, 22, 24, 26, 28, 28, 28, 28, 28, 28}}, + /* ROW 3 - CQI Table 3 */ + {/* MCS Table 1 */ {-1, 0, 0, 0, 0, 2, 4, 6, 8, 11, 13, 15, 18, 20, 22, 24}, + /* MCS Table 2 */ {-1, 0, 0, 0, 0, 1, 2, 3, 4, 5, 7, 9, 11, 13, 15, 17}, + /* MCS Table 3 */ {-1, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28}}}; + +/** + * The CQI to Spectral efficiency table. + * The array ra_nr_cqi_to_se_table[CQI_table_idx][CQI] contains the Spectral Efficiency corresponding to CQI, based on + * the given CQI_table_idx: + * CQI_table_idx: 1 -> Table 5.2.2.1-2; 2 -> Table 5.2.2.1-3, 3 -> Table 5.2.2.1-4 + */ +static const double ra_nr_cqi_to_se_table[3][RA_NR_CQI_TABLE_SIZE] = { + /* ROW 1 - CQI Table 1 */ + {-1, + 0.1523, + 0.2344, + 0.3770, + 0.6016, + 0.8770, + 1.1758, + 1.4766, + 1.9141, + 2.4063, + 2.7305, + 3.3223, + 3.9023, + 4.5234, + 5.1152, + 5.5547}, + /* ROW 2 - CQI Table 2 */ + {-1, + 0.1523, + 0.3770, + 0.8770, + 1.4766, + 1.9141, + 2.4063, + 2.7305, + 3.3223, + 3.9023, + 4.5234, + 5.1152, + 5.5547, + 6.2266, + 6.9141, + 7.4063}, + /* ROW 3 - CQI Table 3 */ + {-1, + 0.0586, + 0.0977, + 0.1523, + 0.2344, + 0.3770, + 0.6016, + 0.8770, + 1.1758, + 1.4766, + 1.9141, + 2.4063, + 2.7305, + 3.3223, + 3.9023, + 4.5234}}; + +static ra_nr_table_idx_t ra_nr_select_table_pusch_noprecoding(srsran_mcs_table_t mcs_table, + srsran_dci_format_nr_t dci_format, + srsran_search_space_type_t search_space_type, + srsran_rnti_type_t rnti_type) { // Non-implemented parameters bool mcs_c_rnti = false; @@ -154,7 +244,7 @@ static ra_nr_table_t ra_nr_select_table_pusch_noprecoding(srsran_mcs_table_t // - CRC scrambled by C-RNTI or SP-CSI-RNTI, if (mcs_table == srsran_mcs_table_256qam && dci_format == srsran_dci_format_nr_0_1 && (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_sp_csi)) { - return ra_nr_table_2; + return ra_nr_table_idx_2; } // - the UE is not configured with MCS-C-RNTI, @@ -164,14 +254,14 @@ static ra_nr_table_t ra_nr_select_table_pusch_noprecoding(srsran_mcs_table_t if (!mcs_c_rnti && mcs_table == srsran_mcs_table_qam64LowSE && dci_format != srsran_dci_format_nr_rar && search_space_type == srsran_search_space_type_ue && (rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_sp_csi)) { - return ra_nr_table_3; + return ra_nr_table_idx_3; } // - the UE is configured with MCS-C-RNTI, and // - the PUSCH is scheduled by a PDCCH with // - CRC scrambled by MCS-C-RNTI, // if (mcs_c_rnti && dci_format != srsran_dci_format_nr_rar && rnti_type == srsran_rnti_type_mcs_c) { - // return ra_nr_table_3; + // return ra_nr_table_idx_3; // } // - mcs-Table in configuredGrantConfig is set to 'qam256', @@ -179,7 +269,7 @@ static ra_nr_table_t ra_nr_select_table_pusch_noprecoding(srsran_mcs_table_t // - if PUSCH is transmitted with configured grant // if (configured_grant_table == srsran_mcs_table_256qam && // (rnti_type == srsran_rnti_type_cs || dci_format == srsran_dci_format_nr_cg)) { - // return ra_nr_table_2; + // return ra_nr_table_idx_2; // } // - mcs-Table in configuredGrantConfig is set to 'qam64LowSE' @@ -187,27 +277,23 @@ static ra_nr_table_t ra_nr_select_table_pusch_noprecoding(srsran_mcs_table_t // - if PUSCH is transmitted with configured grant, // if (configured_grant_table == srsran_mcs_table_qam64LowSE && // (rnti_type == srsran_rnti_type_cs || dci_format == srsran_dci_format_nr_cg)) { - // return ra_nr_table_3; + // return ra_nr_table_idx_3; // } - return ra_nr_table_1; + return ra_nr_table_idx_1; } -static ra_nr_table_t ra_nr_select_table_pdsch(srsran_mcs_table_t mcs_table, - srsran_dci_format_nr_t dci_format, - srsran_search_space_type_t search_space_type, - srsran_rnti_type_t rnti_type) +static ra_nr_table_idx_t ra_nr_select_table_pdsch(srsran_mcs_table_t mcs_table, + srsran_dci_format_nr_t dci_format, + srsran_search_space_type_t search_space_type, + srsran_rnti_type_t rnti_type) { - // Non-implemented parameters - bool sps_config_mcs_table_present = false; - bool is_pdcch_sps = false; - // - the higher layer parameter mcs-Table given by PDSCH-Config is set to 'qam256', and // - the PDSCH is scheduled by a PDCCH with DCI format 1_1 with // - CRC scrambled by C-RNTI if (mcs_table == srsran_mcs_table_256qam && dci_format == srsran_dci_format_nr_1_1 && rnti_type == srsran_rnti_type_c) { - return ra_nr_table_1; + return ra_nr_table_idx_2; } // the UE is not configured with MCS-C-RNTI, @@ -216,34 +302,34 @@ static ra_nr_table_t ra_nr_select_table_pdsch(srsran_mcs_table_t mcs_tab // CRC scrambled by C - RNTI if (mcs_table == srsran_mcs_table_qam64LowSE && search_space_type == srsran_search_space_type_ue && rnti_type == srsran_rnti_type_c) { - return ra_nr_table_3; + return ra_nr_table_idx_3; } // - the UE is not configured with the higher layer parameter mcs-Table given by SPS-Config, // - the higher layer parameter mcs-Table given by PDSCH-Config is set to 'qam256', // - if the PDSCH is scheduled by a PDCCH with DCI format 1_1 with CRC scrambled by CS-RNTI or // - if the PDSCH is scheduled without corresponding PDCCH transmission using SPS-Config, - if (!sps_config_mcs_table_present && mcs_table == srsran_mcs_table_256qam && - ((dci_format == srsran_dci_format_nr_1_1 && rnti_type == srsran_rnti_type_c) || (!is_pdcch_sps))) { - return ra_nr_table_2; - } + // if (!sps_config_mcs_table_present && mcs_table == srsran_mcs_table_256qam && + // ((dci_format == srsran_dci_format_nr_1_1 && rnti_type == srsran_rnti_type_cs) || (!is_pdcch_sps))) { + // return ra_nr_table_idx_2; + // } // - the UE is configured with the higher layer parameter mcs-Table given by SPS-Config set to 'qam64LowSE' // - if the PDSCH is scheduled by a PDCCH with CRC scrambled by CS-RNTI or // - if the PDSCH is scheduled without corresponding PDCCH transmission using SPS-Config, // if (sps_config_mcs_table_present && sps_config_mcs_table == srsran_mcs_table_qam64LowSE && // (rnti_type == srsran_rnti_type_cs || is_pdcch_sps)) { - // return ra_nr_table_3; + // return ra_nr_table_idx_3; // } // else - return ra_nr_table_1; + return ra_nr_table_idx_1; } -static ra_nr_table_t ra_nr_select_table(srsran_mcs_table_t mcs_table, - srsran_dci_format_nr_t dci_format, - srsran_search_space_type_t search_space_type, - srsran_rnti_type_t rnti_type) +static ra_nr_table_idx_t ra_nr_select_table(srsran_mcs_table_t mcs_table, + srsran_dci_format_nr_t dci_format, + srsran_search_space_type_t search_space_type, + srsran_rnti_type_t rnti_type) { // Check if it is a PUSCH transmission if (dci_format == srsran_dci_format_nr_0_0 || dci_format == srsran_dci_format_nr_0_1 || @@ -281,14 +367,14 @@ double srsran_ra_nr_R_from_mcs(srsran_mcs_table_t mcs_table, srsran_rnti_type_t rnti_type, uint32_t mcs_idx) { - ra_nr_table_t table = ra_nr_select_table(mcs_table, dci_format, search_space_type, rnti_type); + ra_nr_table_idx_t table = ra_nr_select_table(mcs_table, dci_format, search_space_type, rnti_type); switch (table) { - case ra_nr_table_1: + case ra_nr_table_idx_1: return srsran_ra_nr_R_from_mcs_table1(mcs_idx) / 1024.0; - case ra_nr_table_2: + case ra_nr_table_idx_2: return srsran_ra_nr_R_from_mcs_table2(mcs_idx) / 1024.0; - case ra_nr_table_3: + case ra_nr_table_idx_3: return srsran_ra_nr_R_from_mcs_table3(mcs_idx) / 1024.0; default: ERROR("Invalid table %d", table); @@ -303,14 +389,14 @@ srsran_mod_t srsran_ra_nr_mod_from_mcs(srsran_mcs_table_t mcs_table, srsran_rnti_type_t rnti_type, uint32_t mcs_idx) { - ra_nr_table_t table = ra_nr_select_table(mcs_table, dci_format, search_space_type, rnti_type); + ra_nr_table_idx_t table = ra_nr_select_table(mcs_table, dci_format, search_space_type, rnti_type); switch (table) { - case ra_nr_table_1: + case ra_nr_table_idx_1: return srsran_ra_nr_modulation_from_mcs_table1(mcs_idx); - case ra_nr_table_2: + case ra_nr_table_idx_2: return srsran_ra_nr_modulation_from_mcs_table2(mcs_idx); - case ra_nr_table_3: + case ra_nr_table_idx_3: return srsran_ra_nr_modulation_from_mcs_table3(mcs_idx); default: ERROR("Invalid table %d", table); @@ -463,6 +549,28 @@ static int ra_nr_assert_csi_rs_dmrs_collision(const srsran_sch_cfg_nr_t* pdsch_c return SRSRAN_SUCCESS; } +uint32_t ra_nr_nof_crc_bits(uint32_t tbs, double R) +{ + srsran_cbsegm_t cbsegm = {}; + srsran_basegraph_t bg = srsran_sch_nr_select_basegraph(tbs, R); + + if (bg == BG1) { + if (srsran_cbsegm_ldpc_bg1(&cbsegm, tbs) != SRSRAN_SUCCESS) { + // This should never fail + ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tbs); + return 0; + } + } else { + if (srsran_cbsegm_ldpc_bg2(&cbsegm, tbs) != SRSRAN_SUCCESS) { + // This should never fail + ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tbs); + return 0; + } + } + + return cbsegm.C * cbsegm.L_cb + cbsegm.L_tb; +} + int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, const srsran_sch_grant_nr_t* grant, uint32_t mcs_idx, @@ -527,6 +635,7 @@ int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, uint32_t N_re_rvd = srsran_re_pattern_list_count(&pdsch_cfg->rvd_re, grant->S, grant->S + grant->L, grant->prb_idx); // Steps 2,3,4 + tb->mcs = mcs_idx; tb->tbs = (int)srsran_ra_nr_tbs(N_re, S, R, Qm, tb->N_L); tb->R = R; tb->mod = m; @@ -534,16 +643,30 @@ int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, tb->nof_bits = tb->nof_re * Qm; tb->enabled = true; + // Calculate actual rate + tb->R_prime = 0.0; + if (tb->nof_re != 0) { + tb->R_prime = (double)(tb->tbs + ra_nr_nof_crc_bits(tb->tbs, tb->R)) / (double)tb->nof_bits; + } + return SRSRAN_SUCCESS; } -static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, srsran_sch_grant_nr_t* grant, srsran_sch_cfg_nr_t* cfg) +static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, const srsran_dci_dl_nr_t* dci, srsran_sch_cfg_nr_t* cfg) { const bool dedicated_dmrs_present = - (grant->mapping == srsran_sch_mapping_type_A) ? hl_cfg->dmrs_typeA.present : hl_cfg->dmrs_typeB.present; + (cfg->grant.mapping == srsran_sch_mapping_type_A) ? hl_cfg->dmrs_typeA.present : hl_cfg->dmrs_typeB.present; - if (grant->dci_format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) { - if (grant->mapping == srsran_sch_mapping_type_A) { + if (dci->ctx.format == srsran_dci_format_nr_1_0 || !dedicated_dmrs_present) { + // The reference point for k is + // - for PDSCH transmission carrying SIB1, subcarrier 0 of the lowest-numbered common resource block in the + // CORESET configured by the PBCH + //- otherwise, subcarrier 0 in common resource block 0 + if (dci->ctx.rnti_type == srsran_rnti_type_si) { + cfg->dmrs.reference_point_k_rb = dci->ctx.coreset_start_rb; + } + + if (cfg->grant.mapping == srsran_sch_mapping_type_A) { // Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; cfg->dmrs.type = srsran_dmrs_sch_type_1; @@ -555,34 +678,37 @@ static int ra_dl_dmrs(const srsran_sch_hl_cfg_nr_t* hl_cfg, srsran_sch_grant_nr_ return SRSRAN_ERROR; } } else { - if (grant->mapping == srsran_sch_mapping_type_A) { + // Load DMRS duration + if (srsran_ra_dl_nr_nof_front_load_symbols(hl_cfg, dci, &cfg->dmrs.length) < SRSRAN_SUCCESS) { + ERROR("Loading number of front-load symbols"); + return SRSRAN_ERROR; + } + + // DMRS Type + cfg->dmrs.type = hl_cfg->dmrs_type; + + // Other DMRS configuration + if (cfg->grant.mapping == srsran_sch_mapping_type_A) { cfg->dmrs.additional_pos = hl_cfg->dmrs_typeA.additional_pos; - cfg->dmrs.type = hl_cfg->dmrs_typeA.type; - cfg->dmrs.length = hl_cfg->dmrs_typeA.length; cfg->dmrs.scrambling_id0_present = false; cfg->dmrs.scrambling_id1_present = false; } else { cfg->dmrs.additional_pos = hl_cfg->dmrs_typeB.additional_pos; - cfg->dmrs.type = hl_cfg->dmrs_typeB.type; - cfg->dmrs.length = hl_cfg->dmrs_typeB.length; cfg->dmrs.scrambling_id0_present = false; cfg->dmrs.scrambling_id1_present = false; } } // Set number of DMRS CDM groups without data - if (grant->dci_format == srsran_dci_format_nr_1_0) { - if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&cfg->dmrs, grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups"); - return SRSRAN_ERROR; - } - } else { - ERROR("Invalid case"); + int n = srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data(hl_cfg, dci, cfg->grant.L); + if (n < SRSRAN_SUCCESS) { + ERROR("Error loading number of DMRS CDM groups"); return SRSRAN_ERROR; } + cfg->grant.nof_dmrs_cdm_groups_without_data = (uint32_t)n; // Set DMRS power offset Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE - if (ra_nr_dmrs_power_offset(grant) < SRSRAN_SUCCESS) { + if (ra_nr_dmrs_power_offset(&cfg->grant) < SRSRAN_SUCCESS) { ERROR("Error setting DMRS power offset"); return SRSRAN_ERROR; } @@ -676,13 +802,14 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, // 5.1.2.3 Physical resource block (PRB) bundling // ... - pdsch_grant->nof_layers = 1; - pdsch_grant->dci_format = dci_dl->ctx.format; - pdsch_grant->rnti = dci_dl->ctx.rnti; - pdsch_grant->rnti_type = dci_dl->ctx.rnti_type; - pdsch_grant->tb[0].rv = dci_dl->rv; - pdsch_grant->tb[0].mcs = dci_dl->mcs; - pdsch_grant->tb[0].ndi = dci_dl->ndi; + pdsch_grant->nof_layers = 1; + pdsch_grant->dci_format = dci_dl->ctx.format; + pdsch_grant->rnti = dci_dl->ctx.rnti; + pdsch_grant->rnti_type = dci_dl->ctx.rnti_type; + pdsch_grant->tb[0].rv = dci_dl->rv; + pdsch_grant->tb[0].mcs = dci_dl->mcs; + pdsch_grant->tb[0].ndi = dci_dl->ndi; + pdsch_cfg->sch_cfg.mcs_table = pdsch_hl_cfg->mcs_table; // 5.1.4 PDSCH resource mapping if (ra_dl_resource_mapping(carrier, slot, pdsch_hl_cfg, pdsch_cfg) < SRSRAN_SUCCESS) { @@ -691,7 +818,7 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, } // 5.1.6.2 DM-RS reception procedure - if (ra_dl_dmrs(pdsch_hl_cfg, pdsch_grant, pdsch_cfg) < SRSRAN_SUCCESS) { + if (ra_dl_dmrs(pdsch_hl_cfg, dci_dl, pdsch_cfg) < SRSRAN_SUCCESS) { ERROR("Error selecting DMRS configuration"); return SRSRAN_ERROR; } @@ -706,15 +833,15 @@ int srsran_ra_dl_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, } static int -ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pusch_grant, srsran_sch_cfg_nr_t* cfg) +ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, const srsran_dci_ul_nr_t* dci, srsran_sch_cfg_nr_t* cfg) { - const bool dedicated_dmrs_present = (pusch_grant->mapping == srsran_sch_mapping_type_A) + const bool dedicated_dmrs_present = (cfg->grant.mapping == srsran_sch_mapping_type_A) ? pusch_hl_cfg->dmrs_typeA.present : pusch_hl_cfg->dmrs_typeB.present; - if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || pusch_grant->dci_format == srsran_dci_format_nr_rar || + if (dci->ctx.format == srsran_dci_format_nr_0_0 || dci->ctx.format == srsran_dci_format_nr_rar || !dedicated_dmrs_present) { - if (pusch_grant->mapping == srsran_sch_mapping_type_A) { + if (cfg->grant.mapping == srsran_sch_mapping_type_A) { // Absent default values are defined is TS 38.331 - DMRS-DownlinkConfig cfg->dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; cfg->dmrs.type = srsran_dmrs_sch_type_1; @@ -726,34 +853,36 @@ ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pu return SRSRAN_ERROR; } } else { - if (pusch_grant->mapping == srsran_sch_mapping_type_A) { + // DMRS duration + if (srsran_ra_ul_nr_nof_front_load_symbols(pusch_hl_cfg, dci, &cfg->dmrs.length) < SRSRAN_SUCCESS) { + ERROR("Loading number of front-load symbols"); + return SRSRAN_ERROR; + } + + // DMRS type + cfg->dmrs.type = pusch_hl_cfg->dmrs_type; + + if (cfg->grant.mapping == srsran_sch_mapping_type_A) { cfg->dmrs.additional_pos = pusch_hl_cfg->dmrs_typeA.additional_pos; - cfg->dmrs.type = pusch_hl_cfg->dmrs_typeA.type; - cfg->dmrs.length = pusch_hl_cfg->dmrs_typeA.length; cfg->dmrs.scrambling_id0_present = false; cfg->dmrs.scrambling_id1_present = false; } else { cfg->dmrs.additional_pos = pusch_hl_cfg->dmrs_typeB.additional_pos; - cfg->dmrs.type = pusch_hl_cfg->dmrs_typeB.type; - cfg->dmrs.length = pusch_hl_cfg->dmrs_typeB.length; cfg->dmrs.scrambling_id0_present = false; cfg->dmrs.scrambling_id1_present = false; } } // Set number of DMRS CDM groups without data - if (pusch_grant->dci_format == srsran_dci_format_nr_0_0 || pusch_grant->dci_format == srsran_dci_format_nr_rar) { - if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(cfg, pusch_grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups"); - return SRSRAN_ERROR; - } - } else { - ERROR("DCI format not implemented %s", srsran_dci_format_nr_string(pusch_grant->dci_format)); + int n = srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(pusch_hl_cfg, dci, cfg->grant.L); + if (n < SRSRAN_SUCCESS) { + ERROR("Error getting number of DMRS CDM groups without data"); return SRSRAN_ERROR; } + cfg->grant.nof_dmrs_cdm_groups_without_data = (uint32_t)n; // Set DMRS power offset Table 6.2.2-1: The ratio of PUSCH EPRE to DM-RS EPRE - if (ra_nr_dmrs_power_offset(pusch_grant) < SRSRAN_SUCCESS) { + if (ra_nr_dmrs_power_offset(&cfg->grant) < SRSRAN_SUCCESS) { ERROR("Error setting DMRS power offset"); return SRSRAN_ERROR; } @@ -762,6 +891,7 @@ ra_ul_dmrs(const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, srsran_sch_grant_nr_t* pu } int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, + const srsran_slot_cfg_t* slot_cfg, const srsran_sch_hl_cfg_nr_t* pusch_hl_cfg, const srsran_dci_ul_nr_t* dci_ul, srsran_sch_cfg_nr_t* pusch_cfg, @@ -787,16 +917,17 @@ int srsran_ra_ul_dci_to_grant_nr(const srsran_carrier_nr_t* carrier, // 5.1.2.3 Physical resource block (PRB) bundling // ... - pusch_grant->nof_layers = 1; - pusch_grant->dci_format = dci_ul->ctx.format; - pusch_grant->rnti = dci_ul->ctx.rnti; - pusch_grant->rnti_type = dci_ul->ctx.rnti_type; - pusch_grant->tb[0].rv = dci_ul->rv; - pusch_grant->tb[0].mcs = dci_ul->mcs; - pusch_grant->tb[0].ndi = dci_ul->ndi; + pusch_grant->nof_layers = 1; + pusch_grant->dci_format = dci_ul->ctx.format; + pusch_grant->rnti = dci_ul->ctx.rnti; + pusch_grant->rnti_type = dci_ul->ctx.rnti_type; + pusch_grant->tb[0].rv = dci_ul->rv; + pusch_grant->tb[0].mcs = dci_ul->mcs; + pusch_grant->tb[0].ndi = dci_ul->ndi; + pusch_cfg->sch_cfg.mcs_table = pusch_hl_cfg->mcs_table; // 5.1.6.2 DM-RS reception procedure - if (ra_ul_dmrs(pusch_hl_cfg, pusch_grant, pusch_cfg) < SRSRAN_SUCCESS) { + if (ra_ul_dmrs(pusch_hl_cfg, dci_ul, pusch_cfg) < SRSRAN_SUCCESS) { ERROR("Error selecting DMRS configuration"); return SRSRAN_ERROR; } @@ -823,9 +954,9 @@ static float ra_ul_beta_offset_ack_semistatic(const srsran_beta_offsets_t* beta_ // Select Beta Offset index from the number of HARQ-ACK bits uint32_t beta_offset_index = beta_offsets->ack_index1; - if (uci_cfg->o_ack > 11) { + if (uci_cfg->ack.count > 11) { beta_offset_index = beta_offsets->ack_index3; - } else if (uci_cfg->o_ack > 2) { + } else if (uci_cfg->ack.count > 2) { beta_offset_index = beta_offsets->ack_index2; } @@ -833,7 +964,7 @@ static float ra_ul_beta_offset_ack_semistatic(const srsran_beta_offsets_t* beta_ if (beta_offset_index >= RA_NR_BETA_OFFSET_HARQACK_SIZE) { ERROR("Beta offset index for HARQ-ACK (%d) for O_ack=%d exceeds table size (%d)", beta_offset_index, - uci_cfg->o_ack, + uci_cfg->ack.count, RA_NR_BETA_OFFSET_HARQACK_SIZE); return NAN; } @@ -1021,8 +1152,8 @@ int srsran_ra_ul_set_grant_uci_nr(const srsran_carrier_nr_t* carrier, // Calculate number of UCI encoded bits int Gack = 0; - if (pusch_cfg->uci.o_ack > 2) { - Gack = srsran_uci_nr_pusch_ack_nof_bits(&pusch_cfg->uci.pusch, pusch_cfg->uci.o_ack); + if (pusch_cfg->uci.ack.count > 2) { + Gack = srsran_uci_nr_pusch_ack_nof_bits(&pusch_cfg->uci.pusch, pusch_cfg->uci.ack.count); if (Gack < SRSRAN_SUCCESS) { ERROR("Error calculating Qdack"); return SRSRAN_ERROR; @@ -1039,7 +1170,127 @@ int srsran_ra_ul_set_grant_uci_nr(const srsran_carrier_nr_t* carrier, for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) { pusch_cfg->grant.tb[i].nof_bits = pusch_cfg->grant.tb[i].nof_re * srsran_mod_bits_x_symbol(pusch_cfg->grant.tb[i].mod) - Gack - Gcsi1 - Gcsi2; + + if (pusch_cfg->grant.tb[i].nof_bits > 0) { + pusch_cfg->grant.tb[i].R_prime = + (double)(pusch_cfg->grant.tb[i].tbs + + ra_nr_nof_crc_bits(pusch_cfg->grant.tb[i].tbs, pusch_cfg->grant.tb[i].R)) / + (double)pusch_cfg->grant.tb[i].nof_bits; + } else { + pusch_cfg->grant.tb[i].R_prime = NAN; + } } return SRSRAN_SUCCESS; } + +int srsran_ra_nr_cqi_to_mcs(uint8_t cqi, + srsran_csi_cqi_table_t cqi_table_idx, + srsran_mcs_table_t mcs_table, + srsran_dci_format_nr_t dci_format, + srsran_search_space_type_t search_space_type, + srsran_rnti_type_t rnti_type) +{ + if (cqi >= RA_NR_CQI_TABLE_SIZE) { + ERROR("Invalid CQI (%u)", cqi); + return -1; + } + + ra_nr_table_idx_t mcs_table_idx = ra_nr_select_table_pdsch(mcs_table, dci_format, search_space_type, rnti_type); + + return ra_nr_cqi_to_mcs_table[cqi_table_idx][mcs_table_idx][cqi]; +} + +double srsran_ra_nr_cqi_to_se(uint8_t cqi, srsran_csi_cqi_table_t cqi_table_idx) +{ + if (cqi >= RA_NR_CQI_TABLE_SIZE) { + ERROR("Invalid CQI (%u)", cqi); + return -1; + } + + return ra_nr_cqi_to_se_table[cqi_table_idx][cqi]; +} + +int srsran_ra_nr_se_to_mcs(double se_target, + srsran_mcs_table_t mcs_table, + srsran_dci_format_nr_t dci_format, + srsran_search_space_type_t search_space_type, + srsran_rnti_type_t rnti_type) +{ + // Get MCS table index to be used + ra_nr_table_idx_t mcs_table_idx = ra_nr_select_table_pdsch(mcs_table, dci_format, search_space_type, rnti_type); + + // Get MCS table and size based on mcs_table_idx + const mcs_entry_t* mcs_se_table; + size_t mcs_table_size; + switch (mcs_table_idx) { + case ra_nr_table_idx_1: + mcs_se_table = ra_nr_table1; + mcs_table_size = RA_NR_MCS_SIZE_TABLE1; + break; + case ra_nr_table_idx_2: + mcs_se_table = ra_nr_table2; + mcs_table_size = RA_NR_MCS_SIZE_TABLE2; + break; + case ra_nr_table_idx_3: + mcs_se_table = ra_nr_table3; + mcs_table_size = RA_NR_MCS_SIZE_TABLE3; + break; + default: + ERROR("Invalid MCS table index (%u)", mcs_table_idx); + return -1; + } + + // if SE is lower than min possible value, return min MCS + if (se_target <= mcs_se_table[0].S) { + return 0; + } + // if SE is greater than max possible value, return max MCS + else if (se_target >= mcs_se_table[mcs_table_size - 1].S) { + return mcs_table_size - 1; + } + + // handle monotonicity oddity between MCS 16 and 17 for MCS table 1 + if (mcs_table_idx == ra_nr_table_idx_1) { + if (se_target == mcs_se_table[17].S) { + return 17; + } else if (se_target <= mcs_se_table[16].S && se_target > mcs_se_table[17].S) { + return 16; + } + } + + /* In the following, we search for the greatest MCS value such that MCS(SE) <= target SE, where the target SE is the + * value provided as an input argument. The MCS is the vector index, the content of the vector is the SE. + * The search is performed by means of a binary-search like algorithm. At each iteration, we look for the SE in the + * left or right half of the vector, depending on the target SE. + * We stop when the lower-bound (lb) and upper-bound (ub) are two consecutive MCS values and we return the + * lower-bound, which approximates the greatest MCS value such that MCS(SE) <= target SE + * */ + size_t lb = 0; // lower-bound of MCS-to-SE vector where to perform binary search + size_t ub = mcs_table_size - 1; // upper-bound of MCS-to-SE vector where to perform binary search + while (ub > lb + 1) { + size_t mid_point = (size_t)floor(((double)(lb + ub)) / 2); + // break out of loop is there is an exact match + if (mcs_se_table[mid_point].S == se_target) { + return (int)mid_point; + } + // restrict the search to the left half of the vector + else if (se_target < mcs_se_table[mid_point].S) { + ub = mid_point; + // handle monotonicity oddity between MCS 16 and 17 for MCS table 1 + if (mcs_table_idx == ra_nr_table_idx_1 && ub == 17) { + ub = 16; + } + } + // restrict the search to the right half of the vector + else { /* se_target > mcs_se_table[mid_point].S ) */ + lb = mid_point; + // handle monotonicity oddity between MCS 16 and 17 for MCS table 1 + if (mcs_table_idx == ra_nr_table_idx_1 && lb == 16) { + lb = 17; + } + } + } + + return (int)lb; +} diff --git a/lib/src/phy/phch/ra_sl.c b/lib/src/phy/phch/ra_sl.c index bbb116b38..af8646505 100644 --- a/lib/src/phy/phch/ra_sl.c +++ b/lib/src/phy/phch/ra_sl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/ra_ul.c b/lib/src/phy/phch/ra_ul.c index 6a0c62df2..13a1090db 100644 --- a/lib/src/phy/phch/ra_ul.c +++ b/lib/src/phy/phch/ra_ul.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index 34c0d9864..62e35f915 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -82,7 +82,7 @@ int srsran_ra_ul_nr_pusch_time_resource_default_A(uint32_t scs_cfg, uint32_t m, static void ra_ul_nr_time_hl(const srsran_sch_time_ra_t* hl_ra_cfg, srsran_sch_grant_nr_t* grant) { // Compute S and L from SLIV from higher layers - ra_helper_compute_s_and_l(SRSRAN_NSYMB_PER_SLOT_NR, hl_ra_cfg->sliv, &grant->S, &grant->L); + srsran_sliv_to_s_and_l(SRSRAN_NSYMB_PER_SLOT_NR, hl_ra_cfg->sliv, &grant->S, &grant->L); grant->k = hl_ra_cfg->k; grant->mapping = hl_ra_cfg->mapping_type; @@ -152,7 +152,7 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, // Row 1 if (cfg->nof_common_time_ra == 0) { srsran_ra_ul_nr_pusch_time_resource_default_A(cfg->scs_cfg, m, grant); - } else if (m < SRSRAN_MAX_NOF_TIME_RA && m < cfg->nof_common_time_ra) { + } else if (m < cfg->nof_common_time_ra) { ra_ul_nr_time_hl(&cfg->common_time_ra[m], grant); } else { ERROR("Time domain resource selection (m=%d) exceeds the maximum value (%d)", @@ -165,7 +165,7 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, // Row 2 if (cfg->nof_common_time_ra == 0) { srsran_ra_ul_nr_pusch_time_resource_default_A(cfg->scs_cfg, m, grant); - } else if (m < SRSRAN_MAX_NOF_TIME_RA) { + } else { ra_ul_nr_time_hl(&cfg->common_time_ra[m], grant); } } else if ((rnti_type == srsran_rnti_type_c || rnti_type == srsran_rnti_type_mcs_c || @@ -205,31 +205,95 @@ int srsran_ra_ul_nr_time(const srsran_sch_hl_cfg_nr_t* cfg, return SRSRAN_SUCCESS; } -int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(const srsran_sch_cfg_nr_t* cfg, - srsran_sch_grant_nr_t* grant) +int srsran_ra_ul_nr_nof_front_load_symbols(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_ul_nr_t* dci, + srsran_dmrs_sch_len_t* dmrs_duration) { - if (cfg == NULL || grant == NULL) { + if (cfg == NULL || dci == NULL || dmrs_duration == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - /* According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure: - * For PUSCH scheduled by DCI format 0_0 or by activation DCI format 0_0 with CRC scrambled by CS-RNTI, the UE - * shall assume the number of DM-RS CDM groups without data is 1 which corresponds to CDM group 0 for the case of - * PUSCH with allocation duration of 2 or less OFDM symbols with transform precoding disabled, the UE shall assume - * that the number of DM-RS CDM groups without data is 3 which corresponds to CDM group {0,1,2} for the case of PUSCH - * scheduled by activation DCI format 0_0 and the dmrs-Type in cg-DMRS-Configuration equal to 'type2' and the PUSCH - * allocation duration being more than 2 OFDM symbols, and the UE shall assume that the number of DM-RS CDM groups - * without data is 2 which corresponds to CDM group {0,1} for all other cases. - */ - if (grant->L <= 2 && !cfg->enable_transform_precoder) { - grant->nof_dmrs_cdm_groups_without_data = 1; - // } else if (grant->L > 2 && cfg->dmrs_cg.type == srsran_dmrs_sch_type_2){ - // grant->nof_dmrs_cdm_groups_without_data = 3; - } else { - grant->nof_dmrs_cdm_groups_without_data = 2; + // Table 7.3.1.1.2-6: Antenna port(s), transform precoder is enabled, dmrs-Type=1, maxLength=1 + // Table 7.3.1.1.2-8: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=1 + // Table 7.3.1.1.2-9: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=2 + // Table 7.3.1.1.2-10: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=3 + // Table 7.3.1.1.2-11: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank=4 + // Table 7.3.1.1.2-16: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=1 + // Table 7.3.1.1.2-17: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=2 + // Table 7.3.1.1.2-18: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=3 + // Table 7.3.1.1.2-19: Antenna port(s), transform precoder is disabled, dmrs-Type=2, maxLength=1, rank=4 + if (cfg->dmrs_max_length == srsran_dmrs_sch_len_1) { + *dmrs_duration = srsran_dmrs_sch_len_1; + return SRSRAN_SUCCESS; } - return SRSRAN_SUCCESS; + // Other tables are not implemented + ERROR("Unhandled case"); + return SRSRAN_ERROR; +} + +int srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data(const srsran_sch_hl_cfg_nr_t* cfg, + const srsran_dci_ul_nr_t* dci, + uint32_t L) +{ + if (cfg == NULL || dci == NULL || L == 0) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + uint32_t rank = 1; // No other supported + + // According to TS 38.214 V15.10.0 6.2.2 UE DM-RS transmission procedure: + switch (dci->ctx.format) { + case srsran_dci_format_nr_0_0: + case srsran_dci_format_nr_rar: + // For PUSCH scheduled by DCI format 0_0 or by activation DCI format 0_0 with CRC scrambled by CS-RNTI, the UE + // shall assume the number of DM-RS CDM groups without data is 1 which corresponds to CDM group 0 for the case of + // PUSCH with allocation duration of 2 or less OFDM symbols with transform precoding disabled, the UE shall assume + // that the number of DM-RS CDM groups without data is 3 which corresponds to CDM group {0,1,2} for the case of + // PUSCH scheduled by activation DCI format 0_0 and the dmrs-Type in cg-DMRS-Configuration equal to 'type2' and + // the PUSCH allocation duration being more than 2 OFDM symbols, and the UE shall assume that the number of DM-RS + // CDM groups without data is 2 which corresponds to CDM group {0,1} for all other cases. + if (L <= 2 && !cfg->enable_transform_precoder) { + return 1; + } else if (L > 2 && cfg->dmrs_type == srsran_dmrs_sch_type_2 && dci->ctx.format == srsran_dci_format_nr_cg) { + return 3; + } else { + return 2; + } + return SRSRAN_SUCCESS; + case srsran_dci_format_nr_0_1: + // For PUSCH scheduled by DCI format 0_1, by activation DCI format 0_1 with CRC scrambled by CS-RNTI, or + // configured by configured grant Type 1 configuration, the UE shall assume the DM-RS CDM groups indicated in + // Tables 7.3.1.1.2-6 to 7.3.1.1.2-23 of Clause 7.3.1.1 of [5, TS38.212] are not used for data transmission, where + // "1", "2" and "3" for the number of DM-RS CDM group(s) correspond to CDM group 0, {0,1}, {0,1,2}, respectively. + + // Table 7.3.1.1.2-6: Antenna port(s), transform precoder is enabled, dmrs-Type=1, maxLength=1 + if (cfg->enable_transform_precoder && cfg->dmrs_type == srsran_dmrs_sch_type_1 && + cfg->dmrs_max_length == srsran_dmrs_sch_len_1 && rank == 1) { + return 2; + } + + // Table 7.3.1.1.2-8: Antenna port(s), transform precoder is disabled, dmrs-Type=1, maxLength=1, rank= 1 + if (!cfg->enable_transform_precoder && cfg->dmrs_type == srsran_dmrs_sch_type_1 && + cfg->dmrs_max_length == srsran_dmrs_sch_len_1 && rank == 1) { + if (dci->ports < 2) { + return 1; + } + + if (dci->ports < 6) { + return 2; + } + + ERROR("Invalid ports (%d)", dci->ports); + return SRSRAN_ERROR; + } + + ERROR("Unhandled configuration"); + return SRSRAN_ERROR; + default: + ERROR("Invalid UL DCI format %s", srsran_dci_format_nr_string(dci->ctx.format)); + } + + return SRSRAN_ERROR; } #define RA_UL_PUCCH_CODE_RATE_N 8 @@ -365,7 +429,7 @@ int srsran_ra_ul_nr_pucch_format_2_3_min_prb(const srsran_pucch_nr_resource_t* r } // Compute total number of UCI bits - uint32_t O_total = uci_cfg->o_ack + uci_cfg->o_sr + srsran_csi_part1_nof_bits(uci_cfg->csi, uci_cfg->nof_csi); + uint32_t O_total = uci_cfg->ack.count + uci_cfg->o_sr + srsran_csi_part1_nof_bits(uci_cfg->csi, uci_cfg->nof_csi); // Add CRC bits if any O_total += srsran_uci_nr_crc_len(O_total); @@ -379,46 +443,150 @@ int srsran_ra_ul_nr_freq(const srsran_carrier_nr_t* carrier, const srsran_dci_ul_nr_t* dci_ul, srsran_sch_grant_nr_t* grant) { - if (cfg == NULL || grant == NULL || dci_ul == NULL) { + if (carrier == NULL || cfg == NULL || grant == NULL || dci_ul == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - // RA scheme - if (dci_ul->ctx.format == srsran_dci_format_nr_0_0 || dci_ul->ctx.format == srsran_dci_format_nr_rar) { - // when the scheduling grant is received with DCI format 1_0 , then downlink resource allocation type 1 is used. - return ra_helper_freq_type1(carrier->nof_prb, dci_ul->freq_domain_assigment, grant); + // TS 38.213 PUSCH scheduled by RAR UL grant + if (dci_ul->ctx.format == srsran_dci_format_nr_rar) { + return ra_helper_freq_type1(carrier->nof_prb, 0, dci_ul->freq_domain_assigment, grant); } - ERROR("Unhandled DCI Format %s", srsran_dci_format_nr_string(dci_ul->ctx.format)); + // The UE shall assume that when the scheduling PDCCH is received with DCI format 0_0, then uplink resource + // allocation type 1 is used. + if (dci_ul->ctx.format == srsran_dci_format_nr_0_0) { + return ra_helper_freq_type1(carrier->nof_prb, 0, dci_ul->freq_domain_assigment, grant); + } + + // If the scheduling DCI is configured to indicate the uplink resource allocation type as part of the Frequency domain + // resource assignment field by setting a higher layer parameter resourceAllocation in pusch-Config to 'dynamicSwitch' + if (cfg->alloc == srsran_resource_alloc_dynamic) { + ERROR("Unsupported dynamic resource allocation"); + return SRSRAN_ERROR; + } + + // Otherwise the UE shall use the uplink frequency resource allocation type as defined by the higher layer parameter + // resourceAllocation. + if (cfg->alloc == srsran_resource_alloc_type1) { + return ra_helper_freq_type1(carrier->nof_prb, 0, dci_ul->freq_domain_assigment, grant); + } + + if (cfg->alloc == srsran_resource_alloc_type0) { + return ra_helper_freq_type0(carrier, cfg, dci_ul->freq_domain_assigment, grant); + } + + ERROR("Unhandled case"); return SRSRAN_ERROR; } +typedef struct { + srsran_pucch_nr_format_t format; + uint32_t start_symbol; + uint32_t rb_offset; + uint32_t N_cs; +} pucch_res_common_cfg_t; + +// Table 9.2.1-1 +#define PUCCH_RES_COMMON_CFG_IDX_MAX 15 +static pucch_res_common_cfg_t pucch_res_common_cfg[PUCCH_RES_COMMON_CFG_IDX_MAX + 1] = { + {SRSRAN_PUCCH_NR_FORMAT_0, 12, 0, 2}, + {SRSRAN_PUCCH_NR_FORMAT_0, 12, 0, 3}, + {SRSRAN_PUCCH_NR_FORMAT_0, 12, 3, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 10, 0, 2}, + {SRSRAN_PUCCH_NR_FORMAT_1, 10, 0, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 10, 2, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 10, 4, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 4, 0, 2}, + {SRSRAN_PUCCH_NR_FORMAT_1, 4, 0, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 4, 2, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 4, 4, 3}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 2}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 4}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 2, 4}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 4, 4}, + {SRSRAN_PUCCH_NR_FORMAT_1, 0, 0, 4}}; + // Implements TS 38.213 Table 9.2.1-1: PUCCH resource sets before dedicated PUCCH resource configuration -static int ra_ul_nr_pucch_resource_default(uint32_t r_pucch, srsran_pucch_nr_resource_t* resource) +static int ra_ul_nr_pucch_resource_default(uint32_t pucch_res_common_idx, + uint32_t N_size_bwp, + uint32_t r_pucch, + srsran_pucch_nr_resource_t* resource) { - ERROR("Not implemented"); - return SRSRAN_ERROR; + if (pucch_res_common_idx > PUCCH_RES_COMMON_CFG_IDX_MAX) { + ERROR("Invalid pucch_res_common_idx value (%d)\n", pucch_res_common_idx); + return SRSRAN_ERROR; + } + + uint32_t cs_v_2[2] = {0, 6}; + uint32_t cs_v_3[3] = {0, 4, 8}; + uint32_t cs_v_4[4] = {0, 3, 6, 9}; + + // Table 9.2.1-1 + resource->format = pucch_res_common_cfg[pucch_res_common_idx].format; + resource->start_symbol_idx = pucch_res_common_cfg[pucch_res_common_idx].start_symbol; + resource->nof_symbols = 14 - resource->start_symbol_idx; + uint32_t rb_offset_bwp = pucch_res_common_cfg[pucch_res_common_idx].rb_offset; + uint32_t N_cs = pucch_res_common_cfg[pucch_res_common_idx].N_cs; + + // Special case for cs_v_2 value + if (pucch_res_common_idx == 0) { + cs_v_2[1] = 3; + } + + // Special case for rb_offset + if (pucch_res_common_idx == 15) { + rb_offset_bwp = N_size_bwp / 4; + } + + uint32_t csv_idx = 0; + if (r_pucch / 8 == 0) { + resource->starting_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch, N_cs); + resource->second_hop_prb = N_size_bwp - 1 - resource->starting_prb; + csv_idx = r_pucch % N_cs; + } else { + resource->second_hop_prb = rb_offset_bwp + SRSRAN_FLOOR(r_pucch - 8, N_cs); + resource->starting_prb = N_size_bwp - 1 - resource->second_hop_prb; + csv_idx = (r_pucch - 8) % N_cs; + } + + switch (N_cs) { + case 2: + resource->initial_cyclic_shift = cs_v_2[csv_idx]; + break; + case 3: + resource->initial_cyclic_shift = cs_v_3[csv_idx]; + break; + case 4: + resource->initial_cyclic_shift = cs_v_4[csv_idx]; + break; + } + + return SRSRAN_SUCCESS; } static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg, - uint32_t O_uci, + const srsran_uci_cfg_nr_t* uci_cfg, uint32_t pucch_resource_id, srsran_pucch_nr_resource_t* resource) { - uint32_t N2 = cfg->sets[1].max_payload_size > 0 ? cfg->sets[1].max_payload_size : SRSRAN_UCI_NR_MAX_NOF_BITS; - uint32_t N3 = cfg->sets[2].max_payload_size > 0 ? cfg->sets[2].max_payload_size : SRSRAN_UCI_NR_MAX_NOF_BITS; + uint32_t O_uci = srsran_uci_nr_total_bits(uci_cfg); + uint32_t N2 = cfg->sets[1].max_payload_size > 0 ? cfg->sets[1].max_payload_size : SRSRAN_UCI_NR_MAX_NOF_BITS; + uint32_t N3 = cfg->sets[2].max_payload_size > 0 ? cfg->sets[2].max_payload_size : SRSRAN_UCI_NR_MAX_NOF_BITS; // If the UE transmits O UCI UCI information bits, that include HARQ-ACK information bits, the UE determines a PUCCH // resource set to be... uint32_t resource_set_id = 3; - if (O_uci <= 2 && cfg->sets[0].nof_resources > 0) { + if (uci_cfg->nof_csi == 0 && uci_cfg->ack.count <= 2) { + // a first set of PUCCH resources with pucch-ResourceSetId = 0 if O_UCI ≤ 2 including 1 or 2 HARQ-ACK + // information bits and a positive or negative SR on one SR transmission occasion if transmission of HARQ-ACK + // information and SR occurs simultaneously, or resource_set_id = 0; } else if (O_uci <= N2 && cfg->sets[1].nof_resources > 0) { resource_set_id = 1; } else if (O_uci <= N3 && cfg->sets[2].nof_resources > 0) { resource_set_id = 2; } else if (cfg->sets[3].nof_resources == 0) { - ERROR("Invalid PUCCH resource configuration, N3=%d, O_uci=%d", N3, O_uci); + ERROR("Invalid PUCCH resource configuration, N2=%d, N3=%d, O_uci=%d", N2, N3, O_uci); return SRSRAN_ERROR; } else if (O_uci > SRSRAN_UCI_NR_MAX_NOF_BITS) { ERROR("The number of UCI bits (%d), exceeds the maximum (%d)", O_uci, SRSRAN_UCI_NR_MAX_NOF_BITS); @@ -441,20 +609,18 @@ static int ra_ul_nr_pucch_resource_hl(const srsran_pucch_nr_hl_cfg_t* cfg, int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, const srsran_uci_cfg_nr_t* uci_cfg, + uint32_t N_bwp_sz, srsran_pucch_nr_resource_t* resource) { if (pucch_cfg == NULL || uci_cfg == NULL || resource == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - uint32_t O_uci = srsran_uci_nr_total_bits(uci_cfg); - // Use SR PUCCH resource // - At least one positive SR - // - up to 2 HARQ-ACK + // - No HARQ-ACK // - No CSI report - if (uci_cfg->pucch.sr_positive_present > 0 && uci_cfg->o_ack <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && - uci_cfg->nof_csi == 0) { + if (uci_cfg->sr_positive_present > 0 && uci_cfg->ack.count == 0 && uci_cfg->nof_csi == 0) { uint32_t sr_resource_id = uci_cfg->pucch.sr_resource_id; if (sr_resource_id >= SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES) { ERROR("SR resource ID (%d) exceeds the maximum ID (%d)", sr_resource_id, SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES); @@ -473,20 +639,71 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, return SRSRAN_SUCCESS; } + // Use SR PUCCH resource + // - At least one positive SR + // - up to 2 HARQ-ACK + // - No CSI report + if (uci_cfg->sr_positive_present && uci_cfg->ack.count <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && + uci_cfg->nof_csi == 0) { + uint32_t sr_resource_id = uci_cfg->pucch.sr_resource_id; + if (sr_resource_id >= SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES) { + ERROR("SR resource ID (%d) exceeds the maximum ID (%d)", sr_resource_id, SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES); + return SRSRAN_ERROR; + } + + if (!pucch_cfg->sr_resources[sr_resource_id].configured) { + ERROR("SR resource ID (%d) is not configured", sr_resource_id); + return SRSRAN_ERROR; + } + + // Select PUCCH resource for SR + srsran_pucch_nr_resource_t resource_sr = pucch_cfg->sr_resources[sr_resource_id].resource; + + // Select PUCCH resource for HARQ-ACK + srsran_pucch_nr_resource_t resource_harq = {}; + if (ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, &resource_harq) < SRSRAN_SUCCESS) { + ERROR("Error selecting HARQ-ACK resource"); + return SRSRAN_ERROR; + } + + // If a UE would transmit positive or negative SR in a resource using PUCCH format 0 and HARQ-ACK information bits + // in a resource using PUCCH format 1 in a slot, the UE transmits only a PUCCH with the HARQ-ACK information bits + // in the resource using PUCCH format 1. + if (resource_sr.format == SRSRAN_PUCCH_NR_FORMAT_0 && resource_harq.format == SRSRAN_PUCCH_NR_FORMAT_1) { + *resource = resource_harq; + return SRSRAN_SUCCESS; + } + + // If the UE would transmit positive SR in a first resource using PUCCH format 1 and at most two HARQ-ACK + // information bits in a second resource using PUCCH format 1 in a slot, the UE transmits a PUCCH with HARQ-ACK + // information bits in the first resource using PUCCH format 1 as described in Clause 9.2.3. If a UE would transmit + // negative SR in a resource using PUCCH format 1 and at most two HARQ-ACK information bits in a resource using + // PUCCH format 1 in a slot, the UE transmits a PUCCH in the resource using PUCCH format 1 for HARQ-ACK + // information as described in Clause 9.2.3. + if (resource_sr.format == SRSRAN_PUCCH_NR_FORMAT_1 && resource_harq.format == SRSRAN_PUCCH_NR_FORMAT_1) { + *resource = resource_sr; + return SRSRAN_SUCCESS; + } + + // The impossible happened... + ERROR("The impossible happened..."); + return SRSRAN_ERROR; + } + // Use format 2, 3 or 4 resource from higher layers // - Irrelevant SR opportunities // - More than 2 HARQ-ACK // - No CSI report - if (uci_cfg->o_sr > 0 && uci_cfg->o_ack > SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && uci_cfg->nof_csi == 0) { - return ra_ul_nr_pucch_resource_hl(pucch_cfg, O_uci, uci_cfg->pucch.resource_id, resource); + if (uci_cfg->o_sr > 0 && uci_cfg->ack.count > SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS && uci_cfg->nof_csi == 0) { + return ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, resource); } // Use format 2, 3 or 4 CSI report resource from higher layers // - Irrelevant SR opportunities // - No HARQ-ACK // - Single periodic CSI report - if (uci_cfg->o_ack == 0 && uci_cfg->nof_csi == 1 && uci_cfg->csi[0].type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { - *resource = uci_cfg->csi[0].pucch_resource; + if (uci_cfg->ack.count == 0 && uci_cfg->nof_csi == 1 && uci_cfg->csi[0].cfg.type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { + *resource = uci_cfg->csi[0].cfg.periodic.resource; return SRSRAN_SUCCESS; } @@ -494,10 +711,11 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, // a PUCCH resource set is provided by pucch-ResourceCommon through an index to a row of Table 9.2.1-1 for size // transmission of HARQ-ACK information on PUCCH in an initial UL BWP of N BWP PRBs. if (!pucch_cfg->enabled) { - uint32_t r_pucch = (2 * uci_cfg->pucch.n_cce_0) + 2 * uci_cfg->pucch.resource_id; - return ra_ul_nr_pucch_resource_default(r_pucch, resource); + uint32_t N_cce = SRSRAN_FLOOR(N_bwp_sz, 6); + uint32_t r_pucch = ((2 * uci_cfg->pucch.n_cce_0) / N_cce) + 2 * uci_cfg->pucch.resource_id; + return ra_ul_nr_pucch_resource_default(pucch_cfg->common.resource_common, N_bwp_sz, r_pucch, resource); } - return ra_ul_nr_pucch_resource_hl(pucch_cfg, O_uci, uci_cfg->pucch.resource_id, resource); + return ra_ul_nr_pucch_resource_hl(pucch_cfg, uci_cfg, uci_cfg->pucch.resource_id, resource); } uint32_t srsran_ra_ul_nr_nof_sr_bits(uint32_t K) diff --git a/lib/src/phy/phch/regs.c b/lib/src/phy/phch/regs.c index ecb85be65..3351ac07e 100644 --- a/lib/src/phy/phch/regs.c +++ b/lib/src/phy/phch/regs.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -616,6 +616,7 @@ int regs_num_x_symbol(uint32_t symbol, uint32_t nof_port, srsran_cp_t cp) ERROR("Invalid symbol %d", symbol); return SRSRAN_ERROR; } + return SRSRAN_ERROR; } /** diff --git a/lib/src/phy/phch/sch.c b/lib/src/phy/phch/sch.c index 4cbdc7ca9..56bb81ed9 100644 --- a/lib/src/phy/phch/sch.c +++ b/lib/src/phy/phch/sch.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,6 +32,7 @@ #include #include +#define SRSRAN_PDSCH_MIN_TDEC_ITERS 2 #define SRSRAN_PDSCH_MAX_TDEC_ITERS 10 #ifdef LV_HAVE_SSE @@ -442,8 +443,9 @@ bool decode_tb_cb(srsran_sch_t* q, crc_ptr = &q->crc_tb; } - // CRC is OK - if (!srsran_crc_checksum_byte(crc_ptr, &data[cb_idx * rlen / 8], len_crc)) { + // CRC is OK and ran the minimum number of iterations + if (!srsran_crc_checksum_byte(crc_ptr, &data[cb_idx * rlen / 8], len_crc) && + (cb_noi >= SRSRAN_PDSCH_MIN_TDEC_ITERS)) { softbuffer->cb_crc[cb_idx] = true; early_stop = true; @@ -513,54 +515,8 @@ static int decode_tb(srsran_sch_t* q, int16_t* e_bits, uint8_t* data) { - if (q != NULL && data != NULL && softbuffer != NULL && e_bits != NULL && cb_segm != NULL && Qm != 0) { - if (cb_segm->tbs == 0 || cb_segm->C == 0) { - return SRSRAN_SUCCESS; - } - - if (cb_segm->F) { - fprintf(stderr, "Error filler bits are not supported. Use standard TBS\n"); - return SRSRAN_ERROR_INVALID_INPUTS; - } - - if (cb_segm->C > softbuffer->max_cb) { - fprintf(stderr, - "Error number of CB to decode (%d) exceeds soft buffer size (%d CBs)\n", - cb_segm->C, - softbuffer->max_cb); - return SRSRAN_ERROR_INVALID_INPUTS; - } - - bool crc_ok = true; - - data[cb_segm->tbs / 8 + 0] = 0; - data[cb_segm->tbs / 8 + 1] = 0; - data[cb_segm->tbs / 8 + 2] = 0; - - // Process Codeblocks - crc_ok = decode_tb_cb(q, softbuffer, cb_segm, Qm, rv, nof_e_bits, e_bits, data); - - if (crc_ok) { - uint32_t par_rx = 0, par_tx = 0; - - // Compute transport block CRC - par_rx = srsran_crc_checksum_byte(&q->crc_tb, data, cb_segm->tbs); - - // check parity bits - par_tx = ((uint32_t)data[cb_segm->tbs / 8 + 0]) << 16 | ((uint32_t)data[cb_segm->tbs / 8 + 1]) << 8 | - ((uint32_t)data[cb_segm->tbs / 8 + 2]); - - if (par_rx == par_tx && par_rx) { - INFO("TB decoded OK"); - return SRSRAN_SUCCESS; - } else { - INFO("Error in TB parity: par_tx=0x%x, par_rx=0x%x", par_tx, par_rx); - return SRSRAN_ERROR; - } - } else { - return SRSRAN_ERROR; - } - } else { + // Check inputs + if (q == NULL || data == NULL || softbuffer == NULL || e_bits == NULL || cb_segm == NULL || Qm == 0) { ERROR("Missing inputs: data=%d, softbuffer=%d, e_bits=%d, cb_segm=%d Qm=%d", data != 0, softbuffer != 0, @@ -569,6 +525,51 @@ static int decode_tb(srsran_sch_t* q, Qm); return SRSRAN_ERROR_INVALID_INPUTS; } + + // Check segmentation is valid + if (cb_segm->tbs == 0 || cb_segm->C == 0) { + return SRSRAN_SUCCESS; + } + + if (cb_segm->F) { + fprintf(stderr, "Error filler bits are not supported. Use standard TBS\n"); + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (cb_segm->C > softbuffer->max_cb) { + fprintf(stderr, + "Error number of CB to decode (%d) exceeds soft buffer size (%d CBs)\n", + cb_segm->C, + softbuffer->max_cb); + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Process Codeblocks + bool cb_crc_ok = decode_tb_cb(q, softbuffer, cb_segm, Qm, rv, nof_e_bits, e_bits, data); + + // If any of the CBs CRC is KO + if (!cb_crc_ok) { + INFO("Error in CB parity"); + return SRSRAN_ERROR; + } + + // One CB CRC OK, means TB CRC is OK. + if (cb_segm->C == 1) { + INFO("TB decoded OK"); + return SRSRAN_SUCCESS; + } + + // Check TB CRC for whole TB + if (srsran_crc_match_byte(&q->crc_tb, data, cb_segm->tbs)) { + INFO("TB decoded OK"); + return SRSRAN_SUCCESS; + } + + // TB CRC check failed, as at least one CB had a false alarm, reset all CB CRC flags in the softbuffer + srsran_softbuffer_rx_reset_cb_crc(softbuffer, cb_segm->C); + + INFO("Error in TB parity"); + return SRSRAN_ERROR; } int srsran_dlsch_decode(srsran_sch_t* q, srsran_pdsch_cfg_t* cfg, int16_t* e_bits, uint8_t* data) diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index 838b2a4cd..4af74f771 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -74,6 +74,43 @@ uint32_t sch_nr_n_prb_lbrm(uint32_t nof_prb) return 273; } +static int sch_nr_cbsegm(srsran_basegraph_t bg, uint32_t tbs, srsran_cbsegm_t* cbsegm) +{ + if (bg == BG1) { + if (srsran_cbsegm_ldpc_bg1(cbsegm, tbs) != SRSRAN_SUCCESS) { + ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tbs); + return SRSRAN_ERROR; + } + } else { + if (srsran_cbsegm_ldpc_bg2(cbsegm, tbs) != SRSRAN_SUCCESS) { + ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tbs); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +static int sch_nr_Nref(uint32_t N_rb, srsran_mcs_table_t mcs_table, uint32_t max_mimo_layers) +{ + uint32_t N_re_lbrm = SRSRAN_MAX_NRE_NR * sch_nr_n_prb_lbrm(N_rb); + double TCR_lbrm = 948.0 / 1024.0; + uint32_t Qm_lbrm = (mcs_table == srsran_mcs_table_256qam) ? 8 : 6; + uint32_t TBS_LRBM = srsran_ra_nr_tbs(N_re_lbrm, 1.0, TCR_lbrm, Qm_lbrm, SRSRAN_MIN(4, max_mimo_layers)); + double R = 2.0 / 3.0; + srsran_basegraph_t bg = srsran_sch_nr_select_basegraph(TBS_LRBM, R); + + // Compute segmentation + srsran_cbsegm_t cbsegm = {}; + int r = sch_nr_cbsegm(bg, TBS_LRBM, &cbsegm); + if (r < SRSRAN_SUCCESS) { + ERROR("Error computing TB segmentation"); + return SRSRAN_ERROR; + } + + return (int)ceil((double)TBS_LRBM / (double)(cbsegm.C * R)); +} + int srsran_sch_nr_fill_tb_info(const srsran_carrier_nr_t* carrier, const srsran_sch_cfg_t* sch_cfg, const srsran_sch_tb_t* tb, @@ -88,16 +125,9 @@ int srsran_sch_nr_fill_tb_info(const srsran_carrier_nr_t* carrier, // Compute code block segmentation srsran_cbsegm_t cbsegm = {}; - if (bg == BG1) { - if (srsran_cbsegm_ldpc_bg1(&cbsegm, tb->tbs) != SRSRAN_SUCCESS) { - ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tb->tbs); - return SRSRAN_ERROR; - } - } else { - if (srsran_cbsegm_ldpc_bg2(&cbsegm, tb->tbs) != SRSRAN_SUCCESS) { - ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tb->tbs); - return SRSRAN_ERROR; - } + if (sch_nr_cbsegm(bg, tb->tbs, &cbsegm) < SRSRAN_SUCCESS) { + ERROR("Error calculation TB segmentation"); + return SRSRAN_ERROR; } if (cbsegm.Z > MAX_LIFTSIZE) { @@ -120,13 +150,15 @@ int srsran_sch_nr_fill_tb_info(const srsran_carrier_nr_t* carrier, cfg->Nl = tb->N_L; // Calculate Nref - uint32_t N_re_lbrm = SRSRAN_MAX_NRE_NR * sch_nr_n_prb_lbrm(carrier->nof_prb); - double TCR_lbrm = 948.0 / 1024.0; - uint32_t Qm_lbrm = (sch_cfg->mcs_table == srsran_mcs_table_256qam) ? 8 : 6; - uint32_t max_mimo_layers = SRSRAN_MAX(carrier->max_mimo_layers, 4); - uint32_t TBS_LRBM = srsran_ra_nr_tbs(N_re_lbrm, 1.0, TCR_lbrm, Qm_lbrm, max_mimo_layers); - double R = 2.0 / 3.0; - cfg->Nref = (uint32_t)ceil((double)TBS_LRBM / (double)(cbsegm.C * R)); + if (sch_cfg->limited_buffer_rm) { + int Nref = sch_nr_Nref(carrier->nof_prb, sch_cfg->mcs_table, 4); + if (Nref < SRSRAN_SUCCESS) { + ERROR("Error computing N_ref"); + } + cfg->Nref = (uint32_t)Nref; + } else { + cfg->Nref = SRSRAN_LDPC_MAX_LEN_ENCODED_CB; + } // Calculate number of code blocks after applying CBGTI... not implemented, activate all CB for (uint32_t r = 0; r < cbsegm.C; r++) { @@ -431,7 +463,7 @@ static inline int sch_nr_encode(srsran_sch_nr_t* q, // Calculate TB CRC uint32_t checksum_tb = srsran_crc_checksum_byte(crc_tb, data, tb->tbs); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("tb="); srsran_vec_fprint_byte(stdout, data, tb->tbs / 8); } @@ -466,7 +498,7 @@ static inline int sch_nr_encode(srsran_sch_nr_t* q, srsran_bit_unpack_vector(input_ptr, q->temp_cb, (int)cb_len); } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("cb%d=", r); srsran_vec_fprint_byte(stdout, input_ptr, cb_len / 8); } @@ -487,7 +519,7 @@ static inline int sch_nr_encode(srsran_sch_nr_t* q, // Encode code block srsran_ldpc_encoder_encode(encoder, q->temp_cb, rm_buffer, cfg.Kr); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("encoded="); srsran_vec_fprint_b(stdout, rm_buffer, encoder->liftN - 2 * encoder->ls); } @@ -530,6 +562,18 @@ static int sch_nr_decode(srsran_sch_nr_t* q, return SRSRAN_ERROR_INVALID_INPUTS; } + // Protect softbuffer access + if (!tb->softbuffer.rx) { + ERROR("Missing softbuffer!"); + return SRSRAN_ERROR; + } + + // Protect PDU access + if (!res->payload) { + ERROR("Missing payload pointer!"); + return SRSRAN_ERROR; + } + int8_t* input_ptr = e_bits; uint32_t nof_iter_sum = 0; @@ -561,6 +605,7 @@ static int sch_nr_decode(srsran_sch_nr_t* q, // Counter of code blocks that have matched CRC uint32_t cb_ok = 0; + res->crc = false; // For each code block... uint32_t j = 0; @@ -627,22 +672,13 @@ static int sch_nr_decode(srsran_sch_nr_t* q, nof_iter_sum += n_iter_cb; // Check if CB is all zeros - uint32_t cb_len = cfg.Kp - cfg.L_cb; - bool all_zeros = true; - for (uint32_t i = 0; i < cb_len && all_zeros; i++) { - all_zeros = (q->temp_cb[i] == 0); - } + uint32_t cb_len = cfg.Kp - cfg.L_cb; - tb->softbuffer.rx->cb_crc[r] = (ret != 0) && (!all_zeros); - SCH_INFO_RX("CB %d/%d iter=%d CRC=%s all_zeros=%s", - r, - cfg.C, - n_iter_cb, - tb->softbuffer.rx->cb_crc[r] ? "OK" : "KO", - all_zeros ? "yes" : "no"); + tb->softbuffer.rx->cb_crc[r] = (ret != 0); + SCH_INFO_RX("CB %d/%d iter=%d CRC=%s", r, cfg.C, n_iter_cb, tb->softbuffer.rx->cb_crc[r] ? "OK" : "KO"); // CB Debug trace - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("CB %d/%d:", r, cfg.C); srsran_vec_fprint_hex(stdout, q->temp_cb, cb_len); } @@ -655,6 +691,8 @@ static int sch_nr_decode(srsran_sch_nr_t* q, input_ptr += E; } + // Set average number of iterations + res->avg_iter = (float)nof_iter_sum / (float)cfg.C; // Set average number of iterations if (cfg.C > 0) { @@ -663,7 +701,6 @@ static int sch_nr_decode(srsran_sch_nr_t* q, res->avg_iter = NAN; } - // Not all CB are decoded, skip TB union and CRC check if (cb_ok != cfg.C) { return SRSRAN_SUCCESS; @@ -704,7 +741,7 @@ static int sch_nr_decode(srsran_sch_nr_t* q, SCH_INFO_RX("TB: TBS=%d; CRC={%06x, %06x}", tb->tbs, checksum1, checksum2); } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_DEBUG && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_DEBUG && !is_handler_registered()) { DEBUG("Decode: "); srsran_vec_fprint_byte(stdout, res->payload, tb->tbs / 8); } @@ -760,7 +797,7 @@ int srsran_sch_nr_tb_info(const srsran_sch_tb_t* tb, const srsran_sch_tb_res_nr_ tb->cw_idx, srsran_mod_string(tb->mod), tb->tbs / 8, - tb->R, + tb->R_prime, tb->rv); if (res != NULL) { diff --git a/lib/src/phy/phch/sci.c b/lib/src/phy/phch/sci.c index 3af1b7cd4..8ea7d8062 100644 --- a/lib/src/phy/phch/sci.c +++ b/lib/src/phy/phch/sci.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/sequences.c b/lib/src/phy/phch/sequences.c index 1fd76f43e..38c842df2 100644 --- a/lib/src/phy/phch/sequences.c +++ b/lib/src/phy/phch/sequences.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/tbs_tables.h b/lib/src/phy/phch/tbs_tables.h index 32d1fb4e6..ab7422cfc 100644 --- a/lib/src/phy/phch/tbs_tables.h +++ b/lib/src/phy/phch/tbs_tables.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/tbs_tables_nbiot.h b/lib/src/phy/phch/tbs_tables_nbiot.h index 47af10f83..e25ee2c97 100644 --- a/lib/src/phy/phch/tbs_tables_nbiot.h +++ b/lib/src/phy/phch/tbs_tables_nbiot.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 72968f832..141cc9e63 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -21,7 +21,7 @@ set(CTEST_LABELS "lib;phy;phch") ######################################################################## -# PBCH TEST +# PBCH TEST ######################################################################## add_executable(pbch_test pbch_test.c) @@ -57,15 +57,15 @@ add_executable(psbch_file_test psbch_file_test.c) target_link_libraries(psbch_file_test srsran_phy) # TM2 file tests -add_lte_test(psbch_file_test_ideal_tm2_p6_c0 psbch_file_test -p 6 -c 0 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p15_c84 psbch_file_test -p 15 -c 84 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p25_c168 psbch_file_test -p 25 -c 168 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p50_c252 psbch_file_test -p 50 -c 252 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p100_c335 psbch_file_test -p 100 -c 335 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) -add_lte_test(psbch_file_test_ideal_tm2_p50_c252_ext psbch_file_test -p 50 -c 252 -e -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6_ext.dat) +add_lte_test(psbch_file_test_ideal_tm2_p6_c0 psbch_file_test -p 6 -c 0 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p15_c84 psbch_file_test -p 15 -c 84 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p25_c168 psbch_file_test -p 25 -c 168 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p50_c252 psbch_file_test -p 50 -c 252 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p100_c335 psbch_file_test -p 100 -c 335 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) +add_lte_test(psbch_file_test_ideal_tm2_p50_c252_ext psbch_file_test -p 50 -c 252 -e -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p50_c252_s15.36e6_ext.dat) # TM4 file tests -add_lte_test(psbch_file_test_cmw_tm4_p50_c169 psbch_file_test -p 50 -c 169 -t 4 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_slss_id169.dat) +add_lte_test(psbch_file_test_cmw_tm4_p50_c169 psbch_file_test -p 50 -c 169 -t 4 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_slss_id169.dat) ######################################################################## # PSCCH TEST @@ -123,38 +123,38 @@ add_executable(pssch_pscch_file_test pssch_pscch_file_test.c) target_link_libraries(pssch_pscch_file_test srsran_phy) # TM2 file tests -add_lte_test(pssch_pscch_file_test_ideal_tm2_p100 pssch_pscch_file_test -p 100 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) -set_property(TEST pssch_pscch_file_test_ideal_tm2_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=2 num_decoded_tb=1") +add_lte_test(pssch_pscch_file_test_ideal_tm2_p100 pssch_pscch_file_test -p 100 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) +set_property(TEST pssch_pscch_file_test_ideal_tm2_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=[2,3] num_decoded_tb=1") # TM4 file tests (first SF is sf_idx = 6 such that the PSSCH sf_idx=0) -add_lte_test(pssch_pscch_file_test_ideal_tm4_p100 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -d -m 6 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) +add_lte_test(pssch_pscch_file_test_ideal_tm4_p100 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -d -m 6 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) set_property(TEST pssch_pscch_file_test_ideal_tm4_p100 PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1") -add_lte_test(pssch_pscch_test_tm4_p50_qc pssch_pscch_file_test -p 50 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_qc9150_f5.92e9_s15.36e6_50prb_20offset.dat) +add_lte_test(pssch_pscch_test_tm4_p50_qc pssch_pscch_file_test -p 50 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_qc9150_f5.92e9_s15.36e6_50prb_20offset.dat) set_property(TEST pssch_pscch_test_tm4_p50_qc PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1 num_decoded_tb=1") # Capture has a SFO offset of ~64 samples, but offsetting by 20 is enough to decode it -add_lte_test(pssch_pscch_test_tm4_p50_cmw pssch_pscch_file_test -p 50 -t 4 -o 20 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_0offset_1ms.dat) +add_lte_test(pssch_pscch_test_tm4_p50_cmw pssch_pscch_file_test -p 50 -t 4 -o 20 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_cmw500_f5.92e9_s11.52e6_50prb_0offset_1ms.dat) set_property(TEST pssch_pscch_test_tm4_p50_cmw PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=1 num_decoded_tb=1") # With PHY retransmission (3 TTI offset) first SF at sf_idx=5 -add_lte_test(pssch_pscch_test_tm4_p50_huawei pssch_pscch_file_test -p 50 -t 4 -m 5 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_huawei_s11.52e6_50prb_10prb_offset_with_retx.dat) +add_lte_test(pssch_pscch_test_tm4_p50_huawei pssch_pscch_file_test -p 50 -t 4 -m 5 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_huawei_s11.52e6_50prb_10prb_offset_with_retx.dat) set_property(TEST pssch_pscch_test_tm4_p50_huawei PROPERTY PASS_REGULAR_EXPRESSION "num_decoded_sci=2 num_decoded_tb=2") # With PHY ReTx (0 TTI offset?) -add_lte_test(pssch_pscch_test_tm4_p50_uxm1 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs12.dat) +add_lte_test(pssch_pscch_test_tm4_p50_uxm1 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs12.dat) set_property(TEST pssch_pscch_test_tm4_p50_uxm1 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=2 num_decoded_tb=2") # 100 PRB startOffset 1 MCS12 MAC padding, first SF is index 0 -add_lte_test(pssch_pscch_test_tm4_p100_uxm2 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s23.04e6_100prb_1prb_offset_mcs12_padding.dat) +add_lte_test(pssch_pscch_test_tm4_p100_uxm2 pssch_pscch_file_test -p 100 -t 4 -s 10 -n 10 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s23.04e6_100prb_1prb_offset_mcs12_padding.dat) set_property(TEST pssch_pscch_test_tm4_p100_uxm2 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=4") # 100 PRB LTE sampling rate, startOffset1 MCS12 ITS data, first SF is index 6 -add_lte_test(pssch_pscch_test_tm4_p100_uxm3 pssch_pscch_file_test -p 100 -d -t 4 -s 10 -n 10 -m 6 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s30.72e6_100prb_1prb_offset_mcs12_its.dat) +add_lte_test(pssch_pscch_test_tm4_p100_uxm3 pssch_pscch_file_test -p 100 -d -t 4 -s 10 -n 10 -m 6 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s30.72e6_100prb_1prb_offset_mcs12_its.dat) set_property(TEST pssch_pscch_test_tm4_p100_uxm3 PROPERTY PASS_REGULAR_EXPRESSION "mcs=12.*num_decoded_sci=1") # 50 PRB LTE sampling rate, startOffset0 MCS28 MAC padding, first SF is index 1 -add_lte_test(pssch_pscch_test_tm4_p50_uxm4 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -m 1 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs28_padding_5ms.dat) +add_lte_test(pssch_pscch_test_tm4_p50_uxm4 pssch_pscch_file_test -p 50 -d -t 4 -s 5 -n 10 -m 1 -i ${CMAKE_CURRENT_SOURCE_DIR}/signal_sidelink_uxm_s15.36e6_50prb_0prb_offset_mcs28_padding_5ms.dat) set_property(TEST pssch_pscch_test_tm4_p50_uxm4 PROPERTY PASS_REGULAR_EXPRESSION "mcs=28.*num_decoded_sci=5") ######################################################################## @@ -210,19 +210,39 @@ add_lte_test(phich_test_104 phich_test -p 4 -n 10 -e -l -g 1/2) add_executable(pdcch_test pdcch_test.c) target_link_libraries(pdcch_test srsran_phy) -add_lte_test(pdcch_test_6 pdcch_test -n 6) -add_lte_test(pdcch_test_15 pdcch_test -n 15) -add_lte_test(pdcch_test_25 pdcch_test -n 25) -add_lte_test(pdcch_test_50 pdcch_test -n 50) -add_lte_test(pdcch_test_75 pdcch_test -n 75) -add_lte_test(pdcch_test_100 pdcch_test -n 100) -add_lte_test(pdcch_test_6_mimo pdcch_test -n 6 -p 2) -add_lte_test(pdcch_test_15_mimo pdcch_test -n 15 -p 2) -add_lte_test(pdcch_test_25_mimo pdcch_test -n 25 -p 2) -add_lte_test(pdcch_test_50_mimo pdcch_test -n 50 -p 2) -add_lte_test(pdcch_test_75_mimo pdcch_test -n 75 -p 2) -add_lte_test(pdcch_test_100_mimo pdcch_test -n 100 -p 2) -#add_lte_test(pdcch_test_crosscarrier pdcch_test -x) +foreach (nof_prb 6 15 25 50 75 100) + # Currently, the ARM and SSE platforms srsRAN has been tested are not capable of running 100PRB. So, skip 100 PRB in + # ARM and SSE. + if ((HAVE_NEON OR NOT HAVE_AVX2) AND (${nof_prb} EQUAL 100)) + continue() + endif () + foreach (nof_ports 1 2) + foreach (cfi 1 2 3) + foreach (snr auto 15 300) + # Build PDCCH test arguments + set(pdcch_test_args -n ${nof_prb} -p ${nof_ports} -f ${cfi} -S ${snr} -R 1) + + # The current Viterbi decoder implementation for SSE and NEON does not perform good enough to pass the + # test without false detection. So, enable false alarm check in AVX2 based machines only. + if (HAVE_AVX2) + set(pdcch_test_args ${pdcch_test_args} -F) + endif () + + string(REGEX REPLACE "\ " "" test_name_args ${pdcch_test_args}) + + add_lte_test(pdcch_test${test_name_args} pdcch_test ${pdcch_test_args}) + endforeach () + endforeach () + endforeach () +endforeach () + +######################################################################## +# DCI TEST +######################################################################## + +add_executable(dci_test dci_test.c) +target_link_libraries(dci_test srsran_phy) +add_test(dci_test dci_test) ######################################################################## # PDSCH TEST @@ -465,11 +485,7 @@ add_lte_test(npdsch_npdcch_dci_formatN1_test npdsch_npdcch_file_test -c 0 -s 1 - add_executable(pusch_test pusch_test.c) target_link_libraries(pusch_test srsran_phy) -if (NOT DEFINED TEST_EXTENSION) - set(TEST_EXTENSION Normal) -endif(NOT DEFINED TEST_EXTENSION) - -if (TEST_EXTENSION STREQUAL Paranoid) +if (${ENABLE_ALL_TEST}) # All valid number of PRBs for PUSCH set(cell_n_prb_valid 1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 40 45 48 50 54 60 64 72 75 80 81 90 96 100) @@ -481,7 +497,7 @@ if (TEST_EXTENSION STREQUAL Paranoid) set(pusch_cqi none wideband) -else (TEST_EXTENSION STREQUAL Paranoid) +else () set(cell_n_prb_valid 50) set(pusch_min_mcs 0) @@ -492,7 +508,7 @@ else (TEST_EXTENSION STREQUAL Paranoid) set(pusch_cqi none wideband) -endif (TEST_EXTENSION STREQUAL Paranoid) +endif () foreach (cell_n_prb 6 15 25 50 75 100) set(pusch_cell_n_prb) @@ -578,6 +594,8 @@ add_lte_test(prach_zc0 prach_test -z 0) add_lte_test(prach_zc2 prach_test -z 2) add_lte_test(prach_zc3 prach_test -z 3) +add_nr_test(prach_nr prach_test -n 50 -f 0 -r 0 -z 0 -N 1) + add_executable(prach_test_multi prach_test_multi.c) target_link_libraries(prach_test_multi srsran_phy) @@ -624,6 +642,10 @@ if(RF_FOUND) target_link_libraries(prach_test_usrp srsran_rf srsran_phy pthread) endif(RF_FOUND) +add_executable(prach_nr_test_perf EXCLUDE_FROM_ALL prach_nr_test_perf.c) +target_link_libraries(prach_nr_test_perf srsran_phy) +# this is just for performance evaluation, not for unit testing + ######################################################################## # NR ######################################################################## @@ -632,6 +654,10 @@ add_executable(dci_nr_test dci_nr_test.c) target_link_libraries(dci_nr_test srsran_phy) add_nr_test(dci_nr_test dci_nr_test) +add_executable(mib_nr_test mib_nr_test.c) +target_link_libraries(mib_nr_test srsran_phy) +add_nr_test(mib_nr_test mib_nr_test) + add_executable(pucch_nr_test pucch_nr_test.c) target_link_libraries(pucch_nr_test srsran_phy) add_nr_test(pucch_nr_test pucch_nr_test) @@ -665,7 +691,11 @@ add_nr_test(pusch_nr_ack2_csi4_test pusch_nr_test -p 50 -m 20 -A 2 -C 4) add_nr_test(pusch_nr_ack4_csi4_test pusch_nr_test -p 50 -m 20 -A 4 -C 4) add_nr_test(pusch_nr_ack20_csi4_test pusch_nr_test -p 50 -m 20 -A 20 -C 4) +add_executable(pusch_nr_bler_test EXCLUDE_FROM_ALL pusch_nr_bler_test.c) +target_link_libraries(pusch_nr_bler_test srsran_phy) +# this is just for performance evaluation, not for unit testing add_executable(pdcch_nr_test pdcch_nr_test.c) target_link_libraries(pdcch_nr_test srsran_phy) -add_nr_test(pdcch_nr_test pdcch_nr_test) +add_nr_test(pdcch_nr_test_non_interleaved pdcch_nr_test) +add_nr_test(pdcch_nr_test_interleaved pdcch_nr_test -I) diff --git a/lib/src/phy/phch/test/dci_nbiot_test.c b/lib/src/phy/phch/test/dci_nbiot_test.c index 9ee1f44a1..b518e0c97 100644 --- a/lib/src/phy/phch/test/dci_nbiot_test.c +++ b/lib/src/phy/phch/test/dci_nbiot_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,7 +39,7 @@ void parse_args(int argc, char** argv) while ((opt = getopt(argc, argv, "cpndv")) != -1) { switch (opt) { case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/dci_nr_test.c b/lib/src/phy/phch/test/dci_nr_test.c index caf4822e5..70f272e80 100644 --- a/lib/src/phy/phch/test/dci_nr_test.c +++ b/lib/src/phy/phch/test/dci_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,10 +19,10 @@ * */ -#include "srsran/common/test_common.h" #include "srsran/phy/phch/dci_nr.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/random.h" +#include "srsran/support/srsran_test.h" #include static uint32_t nof_repetitions = 1024; @@ -64,11 +64,11 @@ static int test_52prb_base() cfg.enable_transform_precoding = false; cfg.dynamic_dual_harq_ack_codebook = false; cfg.pusch_tx_config_non_codebook = false; - cfg.pusch_dmrs_type2 = false; - cfg.pusch_dmrs_double = false; cfg.pusch_ptrs = false; cfg.pusch_dynamic_betas = false; cfg.pusch_alloc_type = srsran_resource_alloc_type1; + cfg.pusch_dmrs_type = srsran_dmrs_sch_type_1; + cfg.pusch_dmrs_max_len = srsran_dmrs_sch_len_1; // DCI 1_1 parameters cfg.nof_dl_bwp = 0; @@ -81,12 +81,12 @@ static int test_52prb_base() cfg.pdsch_rm_pattern2 = false; cfg.pdsch_2cw = false; cfg.multiple_scell = false; - cfg.pdsch_dmrs_type2 = false; - cfg.pdsch_dmrs_double = false; cfg.pdsch_tci = false; cfg.pdsch_cbg_flush = false; cfg.pdsch_dynamic_bundling = false; cfg.pdsch_alloc_type = srsran_resource_alloc_type1; + cfg.pdsch_dmrs_type = srsran_dmrs_sch_type_1; + cfg.pdsch_dmrs_max_len = srsran_dmrs_sch_len_1; // Configure DCI srsran_dci_nr_t dci = {}; @@ -99,8 +99,11 @@ static int test_52prb_base() TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_ue, srsran_dci_format_nr_1_0) == 39); TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_ue, srsran_dci_format_nr_0_1) == 36); TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_ue, srsran_dci_format_nr_1_1) == 41); + TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_rar, srsran_dci_format_nr_rar) == 27); + TESTASSERT(srsran_dci_nr_size(&dci, srsran_search_space_type_common_0, srsran_dci_format_nr_1_0) == 39); srsran_dci_ctx_t ctx = {}; + ctx.rnti = 0x1234; ctx.ss_type = srsran_search_space_type_common_3; ctx.rnti_type = srsran_rnti_type_c; @@ -202,6 +205,55 @@ static int test_52prb_base() TESTASSERT(memcmp(&dci_tx, &dci_rx, sizeof(srsran_dci_ul_nr_t)) == 0); } + // Test UL DCI RAR Packing/Unpacking and info + ctx.ss_type = srsran_search_space_type_rar; + ctx.format = srsran_dci_format_nr_rar; + for (uint32_t i = 0; i < nof_repetitions; i++) { + srsran_dci_ul_nr_t dci_tx = {}; + dci_tx.ctx = ctx; + dci_tx.freq_domain_assigment = srsran_random_uniform_int_dist(random_gen, 0, (int)(1U << 14U) - 1); // 14 bit + dci_tx.time_domain_assigment = srsran_random_uniform_int_dist(random_gen, 0, (int)(1U << 4U) - 1); // 4 bit + dci_tx.freq_hopping_flag = srsran_random_uniform_int_dist(random_gen, 0, (int)(1U << 1U) - 1); // 1 bit + dci_tx.mcs = srsran_random_uniform_int_dist(random_gen, 0, (int)(1U << 4U) - 1); // 4 bit + dci_tx.rv = 0; // unavailable + dci_tx.ndi = 0; // unavailable + dci_tx.pid = 0; // unavailable + dci_tx.tpc = srsran_random_uniform_int_dist(random_gen, 0, (int)(1U << 3U) - 1); // 3 bit + dci_tx.frequency_offset = 0; // unavailable + dci_tx.csi_request = srsran_random_uniform_int_dist(random_gen, 0, (int)(1U << 1U) - 1); // 1 bit + dci_tx.sul = 0; // unavailable + dci_tx.cc_id = 0; // unavailable + dci_tx.bwp_id = 0; // unavailable + dci_tx.dai1 = 0; // unavailable + dci_tx.dai2 = 0; // unavailable + dci_tx.srs_id = 0; // unavailable + dci_tx.ports = 0; // unavailabale + dci_tx.srs_request = 0; // unavailabale + dci_tx.cbg_info = 0; // unavailable + dci_tx.ptrs_id = 0; // unavailable + dci_tx.beta_id = 0; // unavailable + dci_tx.dmrs_id = 0; // unavailabale + dci_tx.ulsch = 0; // unavailabale + + // Pack + srsran_dci_msg_nr_t dci_msg = {}; + TESTASSERT(srsran_dci_nr_ul_pack(&dci, &dci_tx, &dci_msg) == SRSRAN_SUCCESS); + + // Unpack + srsran_dci_ul_nr_t dci_rx = {}; + TESTASSERT(srsran_dci_nr_ul_unpack(&dci, &dci_msg, &dci_rx) == SRSRAN_SUCCESS); + + // To string + char str[512]; + TESTASSERT(srsran_dci_ul_nr_to_str(&dci, &dci_tx, str, (uint32_t)sizeof(str)) != 0); + INFO("Tx: %s", str); + TESTASSERT(srsran_dci_ul_nr_to_str(&dci, &dci_rx, str, (uint32_t)sizeof(str)) != 0); + INFO("Rx: %s", str); + + // Assert + TESTASSERT(memcmp(&dci_tx, &dci_rx, sizeof(srsran_dci_ul_nr_t)) == 0); + } + // Test UL DCI 1_0 Packing/Unpacking and info ctx.format = srsran_dci_format_nr_1_0; for (uint32_t i = 0; i < nof_repetitions; i++) { @@ -255,7 +307,7 @@ static int test_52prb_base() TESTASSERT(memcmp(&dci_tx, &dci_rx, sizeof(srsran_dci_dl_nr_t)) == 0); } - // Test UL DCI 1_0 Packing/Unpacking and info + // Test UL DCI 1_1 Packing/Unpacking and info ctx.format = srsran_dci_format_nr_1_1; for (uint32_t i = 0; i < nof_repetitions; i++) { srsran_dci_dl_nr_t dci_tx = {}; @@ -308,6 +360,43 @@ static int test_52prb_base() TESTASSERT(memcmp(&dci_tx, &dci_rx, sizeof(srsran_dci_dl_nr_t)) == 0); } + // Test DL DCI 1_0 Packing/Unpacking and info for SI-RNTI + ctx.format = srsran_dci_format_nr_1_0; + ctx.rnti = 0xffff; + ctx.ss_type = srsran_search_space_type_common_0; + ctx.rnti_type = srsran_rnti_type_si; + dci.cfg.coreset0_bw = 48; + + for (uint32_t i = 0; i < nof_repetitions; i++) { + srsran_dci_dl_nr_t dci_tx = {}; + dci_tx.ctx = ctx; + dci_tx.freq_domain_assigment = 0x120; + dci_tx.time_domain_assigment = 0; + dci_tx.vrb_to_prb_mapping = 0; + dci_tx.mcs = srsran_random_uniform_int_dist(random_gen, 0, 31); + dci_tx.rv = srsran_random_uniform_int_dist(random_gen, 0, 3); + dci_tx.sii = 1; // bit set to 1 indicates SI message other than SIB1 + dci_tx.coreset0_bw = 48; + + // Pack + srsran_dci_msg_nr_t dci_msg = {}; + TESTASSERT(srsran_dci_nr_dl_pack(&dci, &dci_tx, &dci_msg) == SRSRAN_SUCCESS); + + // Unpack + srsran_dci_dl_nr_t dci_rx = {}; + TESTASSERT(srsran_dci_nr_dl_unpack(&dci, &dci_msg, &dci_rx) == SRSRAN_SUCCESS); + + // To string + char str[512]; + TESTASSERT(srsran_dci_dl_nr_to_str(&dci, &dci_tx, str, (uint32_t)sizeof(str)) != 0); + INFO("Tx: %s", str); + TESTASSERT(srsran_dci_dl_nr_to_str(&dci, &dci_rx, str, (uint32_t)sizeof(str)) != 0); + INFO("Rx: %s", str); + + // Assert + TESTASSERT(memcmp(&dci_tx, &dci_rx, sizeof(srsran_dci_dl_nr_t)) == 0); + } + return SRSRAN_SUCCESS; } @@ -324,7 +413,7 @@ static void parse_args(int argc, char** argv) while ((opt = getopt(argc, argv, "vR")) != -1) { switch (opt) { case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'R': nof_repetitions = (uint32_t)strtol(argv[optind], NULL, 10); diff --git a/lib/src/phy/phch/test/dci_test.c b/lib/src/phy/phch/test/dci_test.c new file mode 100644 index 000000000..5bff7f170 --- /dev/null +++ b/lib/src/phy/phch/test/dci_test.c @@ -0,0 +1,117 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/phch/dci.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/random.h" +#include "srsran/srsran.h" +#include "srsran/support/srsran_test.h" +#include + +#define UE_CRNTI 0x1234 + +static int test_pdcch_orders() +{ + static srsran_cell_t cell = { + 52, // nof_prb + 1, // nof_ports + 0, // cell_id + SRSRAN_CP_NORM, // cyclic prefix + SRSRAN_PHICH_NORM, // PHICH length + SRSRAN_PHICH_R_1, // PHICH resources + SRSRAN_FDD, + }; + + srsran_dl_sf_cfg_t dl_sf; + ZERO_OBJECT(dl_sf); + + srsran_dci_location_t locations[SRSRAN_NOF_SF_X_FRAME][30]; + static uint32_t cfi = 2; + + static srsran_pdcch_t pdcch; + static srsran_regs_t regs; + + if (srsran_regs_init(®s, cell)) { + ERROR("Error initiating regs"); + exit(-1); + } + + if (srsran_pdcch_init_enb(&pdcch, cell.nof_prb)) { + ERROR("Error creating PDCCH object"); + exit(-1); + } + if (srsran_pdcch_set_cell(&pdcch, ®s, cell)) { + ERROR("Error creating PDCCH object"); + exit(-1); + } + + /* Initiate valid DCI locations */ + for (int i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) { + dl_sf.cfi = cfi; + dl_sf.tti = i; + srsran_pdcch_ue_locations(&pdcch, &dl_sf, locations[i], 30, UE_CRNTI); + } + + srsran_dci_dl_t dci_tx; + bzero(&dci_tx, sizeof(srsran_dci_dl_t)); + dci_tx.rnti = UE_CRNTI; + dci_tx.location = locations[1][0]; + dci_tx.format = SRSRAN_DCI_FORMAT1A; + dci_tx.cif_present = false; + dci_tx.is_pdcch_order = true; + dci_tx.preamble_idx = 0; + dci_tx.prach_mask_idx = 0; + + srsran_dci_cfg_t cfg = {}; + cfg.cif_enabled = false; + cfg.srs_request_enabled = false; + + // Pack + srsran_dci_msg_t dci_msg = {}; + TESTASSERT(srsran_dci_msg_pack_pdsch(&cell, &dl_sf, &cfg, &dci_tx, &dci_msg) == SRSRAN_SUCCESS); + + // Unpack + srsran_dci_dl_t dci_rx = {}; + TESTASSERT(srsran_dci_msg_unpack_pdsch(&cell, &dl_sf, &cfg, &dci_msg, &dci_rx) == SRSRAN_SUCCESS); + + // To string + char str[128]; + srsran_dci_dl_info(&dci_tx, str, sizeof(str)); + printf("Tx: %s\n", str); + srsran_dci_dl_info(&dci_rx, str, sizeof(str)); + printf("Rx: %s\n", str); + + // Assert + TESTASSERT(memcmp(&dci_tx, &dci_rx, srsran_dci_format_sizeof(&cell, &dl_sf, &cfg, dci_tx.format)) == 0); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + if (test_pdcch_orders() != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + printf("Success!\n"); + + return SRSRAN_SUCCESS; +} \ No newline at end of file diff --git a/lib/src/phy/phch/test/mib_nr_test.c b/lib/src/phy/phch/test/mib_nr_test.c new file mode 100644 index 000000000..cf56eaa2f --- /dev/null +++ b/lib/src/phy/phch/test/mib_nr_test.c @@ -0,0 +1,118 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/phch/pbch_msg_nr.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/random.h" +#include "srsran/support/srsran_test.h" +#include +#include + +static uint32_t nof_repetitions = 16; +static srsran_random_t random_gen = NULL; + +static int test_packing_unpacking() +{ + for (uint32_t r = 0; r < nof_repetitions; r++) { + srsran_mib_nr_t mib = {}; + mib.sfn = srsran_random_uniform_int_dist(random_gen, 0, 1023); + mib.ssb_idx = srsran_random_uniform_int_dist(random_gen, 0, 127); + mib.hrf = srsran_random_bool(random_gen, 0.5f); + mib.scs_common = + srsran_random_bool(random_gen, 0.5f) ? srsran_subcarrier_spacing_15kHz : srsran_subcarrier_spacing_30kHz; + mib.ssb_offset = srsran_random_uniform_int_dist(random_gen, 0, 31); + mib.dmrs_typeA_pos = + srsran_random_bool(random_gen, 0.5f) ? srsran_dmrs_sch_typeA_pos_2 : srsran_dmrs_sch_typeA_pos_3; + mib.coreset0_idx = srsran_random_uniform_int_dist(random_gen, 0, 15); + mib.ss0_idx = srsran_random_uniform_int_dist(random_gen, 0, 15); + mib.cell_barred = srsran_random_bool(random_gen, 0.5f); + mib.intra_freq_reselection = srsran_random_bool(random_gen, 0.5f); + mib.spare = srsran_random_uniform_int_dist(random_gen, 0, 1); + + srsran_pbch_msg_nr_t pbch_msg = {}; + TESTASSERT(srsran_pbch_msg_nr_mib_pack(&mib, &pbch_msg) == SRSRAN_SUCCESS); + + TESTASSERT(srsran_pbch_msg_nr_is_mib(&pbch_msg)); + + srsran_mib_nr_t mib2 = {}; + TESTASSERT(srsran_pbch_msg_nr_mib_unpack(&pbch_msg, &mib2) == SRSRAN_SUCCESS); + + char str1[256]; + char str2[256]; + char strp[256]; + srsran_pbch_msg_nr_mib_info(&mib, str1, (uint32_t)sizeof(str1)); + srsran_pbch_msg_nr_mib_info(&mib2, str2, (uint32_t)sizeof(str2)); + srsran_pbch_msg_info(&pbch_msg, strp, (uint32_t)sizeof(strp)); + + if (memcmp(&mib, &mib2, sizeof(srsran_mib_nr_t)) != 0) { + ERROR("Failed packing/unpacking MIB"); + printf(" Source: %s\n", str1); + printf("Unpacked: %s\n", str2); + printf(" Packed: %s\n", strp); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +static void usage(char* prog) +{ + printf("Usage: %s [cpndv]\n", prog); + printf("\t-v Increase verbose [default none]\n"); + printf("\t-R Set number of Packing/Unpacking [default %d]\n", nof_repetitions); +} + +static void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "vR")) != -1) { + switch (opt) { + case 'v': + increase_srsran_verbose_level(); + break; + case 'R': + nof_repetitions = (uint32_t)strtol(argv[optind], NULL, 10); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + parse_args(argc, argv); + + int ret = SRSRAN_ERROR; + random_gen = srsran_random_init(1234); + + if (test_packing_unpacking() < SRSRAN_SUCCESS) { + goto clean_exit; + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + srsran_random_free(random_gen); + return ret; +} diff --git a/lib/src/phy/phch/test/npbch_file_test.c b/lib/src/phy/phch/test/npbch_file_test.c index 9e616a193..09fea1672 100644 --- a/lib/src/phy/phch/test/npbch_file_test.c +++ b/lib/src/phy/phch/test/npbch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -92,7 +92,7 @@ void parse_args(int argc, char** argv) sf_idx = (int)(strtol(argv[optind], NULL, 10) % 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'r': nf = (int)strtol(argv[optind], NULL, 10); diff --git a/lib/src/phy/phch/test/npbch_test.c b/lib/src/phy/phch/test/npbch_test.c index 284a6ab92..8d9613be3 100644 --- a/lib/src/phy/phch/test/npbch_test.c +++ b/lib/src/phy/phch/test/npbch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -58,7 +58,7 @@ void parse_args(int argc, char** argv) cell.base.id = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/npdcch_file_test.c b/lib/src/phy/phch/test/npdcch_file_test.c index 4445ec09c..c912c8ec8 100644 --- a/lib/src/phy/phch/test/npdcch_file_test.c +++ b/lib/src/phy/phch/test/npdcch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -92,7 +92,7 @@ void parse_args(int argc, char** argv) tti = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/npdcch_test.c b/lib/src/phy/phch/test/npdcch_test.c index a30be6187..0e5de8b58 100644 --- a/lib/src/phy/phch/test/npdcch_test.c +++ b/lib/src/phy/phch/test/npdcch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -74,7 +74,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/npdsch_npdcch_file_test.c b/lib/src/phy/phch/test/npdsch_npdcch_file_test.c index ce18cd482..40fef6fea 100644 --- a/lib/src/phy/phch/test/npdsch_npdcch_file_test.c +++ b/lib/src/phy/phch/test/npdsch_npdcch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -126,7 +126,7 @@ void parse_args(int argc, char** argv) snr = strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -222,8 +222,8 @@ int main(int argc, char** argv) // add some noise to the signal if (snr != -1.0) { - float nstd = powf(10.0f, -snr / 20.0f); - srsran_ch_awgn_c(buff_ptrs[0], buff_ptrs[0], nstd, nread); + float var = srsran_convert_dB_to_power(-snr); + srsran_ch_awgn_c(buff_ptrs[0], buff_ptrs[0], var, nread); } // try to decode diff --git a/lib/src/phy/phch/test/npdsch_test.c b/lib/src/phy/phch/test/npdsch_test.c index 939164170..c5d92e256 100644 --- a/lib/src/phy/phch/test/npdsch_test.c +++ b/lib/src/phy/phch/test/npdsch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -112,7 +112,7 @@ void parse_args(int argc, char** argv) expected_nof_re = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/pbch_file_test.c b/lib/src/phy/phch/test/pbch_file_test.c index 73d61820c..64439ebf4 100644 --- a/lib/src/phy/phch/test/pbch_file_test.c +++ b/lib/src/phy/phch/test/pbch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,7 +48,7 @@ uint8_t bch_payload_file[SRSRAN_BCH_PAYLOAD_LEN] = {0, 1, 1, 0, 1, 0, 0, 0, 0, 0 #define FLEN (10 * SRSRAN_SF_LEN(srsran_symbol_sz(cell.nof_prb))) srsran_filesource_t fsrc; -cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS]; +cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS]; srsran_pbch_t pbch; srsran_ofdm_t fft; srsran_chest_dl_t chest; @@ -83,7 +83,7 @@ void parse_args(int argc, char** argv) nof_frames = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'e': cell.cp = SRSRAN_CP_EXT; diff --git a/lib/src/phy/phch/test/pbch_test.c b/lib/src/phy/phch/test/pbch_test.c index 616829494..50c8ebb69 100644 --- a/lib/src/phy/phch/test/pbch_test.c +++ b/lib/src/phy/phch/test/pbch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -63,7 +63,7 @@ void parse_args(int argc, char** argv) cell.id = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/pcfich_file_test.c b/lib/src/phy/phch/test/pcfich_file_test.c index cfd97b0cb..303b829a2 100644 --- a/lib/src/phy/phch/test/pcfich_file_test.c +++ b/lib/src/phy/phch/test/pcfich_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -42,7 +42,7 @@ int flen; FILE* fmatlab = NULL; srsran_filesource_t fsrc; -cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS]; +cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS]; srsran_pcfich_t pcfich; srsran_regs_t regs; srsran_ofdm_t fft; @@ -86,7 +86,7 @@ void parse_args(int argc, char** argv) matlab_file_name = argv[optind]; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'e': cell.cp = SRSRAN_CP_EXT; diff --git a/lib/src/phy/phch/test/pcfich_test.c b/lib/src/phy/phch/test/pcfich_test.c index 01766353e..eb3741fa0 100644 --- a/lib/src/phy/phch/test/pcfich_test.c +++ b/lib/src/phy/phch/test/pcfich_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -62,7 +62,7 @@ void parse_args(int argc, char** argv) cell.id = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/pdcch_file_test.c b/lib/src/phy/phch/test/pdcch_file_test.c index 75e981bfb..9ebe85017 100644 --- a/lib/src/phy/phch/test/pdcch_file_test.c +++ b/lib/src/phy/phch/test/pdcch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,7 +48,7 @@ int max_frames = 10; srsran_dci_format_t dci_format = SRSRAN_DCI_FORMAT1A; srsran_filesource_t fsrc; srsran_pdcch_t pdcch; -cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS]; +cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS]; srsran_regs_t regs; srsran_ofdm_t fft; srsran_chest_dl_t chest; @@ -102,7 +102,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'e': cell.cp = SRSRAN_CP_EXT; diff --git a/lib/src/phy/phch/test/pdcch_nr_test.c b/lib/src/phy/phch/test/pdcch_nr_test.c index 4d159987f..527ba973b 100644 --- a/lib/src/phy/phch/test/pdcch_nr_test.c +++ b/lib/src/phy/phch/test/pdcch_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,19 +24,11 @@ #include "srsran/phy/utils/random.h" #include -static srsran_carrier_nr_t carrier = { - 1, // pci - 0, // absolute_frequency_ssb - 0, // absolute_frequency_point_a - 0, // offset_to_carrier - srsran_subcarrier_spacing_15kHz, // scs - 50, // nof_prb - 0, // start - 1 // max_mimo_layers -}; +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; -static uint16_t rnti = 0x1234; -static bool fast_sweep = true; +static uint16_t rnti = 0x1234; +static bool fast_sweep = true; +static bool interleaved = false; typedef struct { uint64_t time_us; @@ -78,16 +70,17 @@ static int test(srsran_pdcch_nr_t* tx, static void usage(char* prog) { - printf("Usage: %s [pFv] \n", prog); + printf("Usage: %s [pFIv] \n", prog); printf("\t-p Number of carrier PRB [Default %d]\n", carrier.nof_prb); printf("\t-F Fast CORESET frequency resource sweeping [Default %s]\n", fast_sweep ? "Enabled" : "Disabled"); + printf("\t-I Enable interleaved CCE-to-REG [Default %s]\n", interleaved ? "Enabled" : "Disabled"); printf("\t-v [set srsran_verbose to debug, default none]\n"); } static int parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "pFv")) != -1) { + while ((opt = getopt(argc, argv, "pFIv")) != -1) { switch (opt) { case 'p': carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); @@ -95,8 +88,11 @@ static int parse_args(int argc, char** argv) case 'F': fast_sweep ^= true; break; + case 'I': + interleaved ^= true; + break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -144,16 +140,34 @@ int main(int argc, char** argv) goto clean_exit; } - srsran_coreset_t coreset = {}; - uint32_t nof_frequency_resource = SRSRAN_MIN(SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, carrier.nof_prb / 6); + srsran_coreset_t coreset = {}; + if (interleaved) { + coreset.mapping_type = srsran_coreset_mapping_type_interleaved; + coreset.reg_bundle_size = srsran_coreset_bundle_size_n6; + coreset.interleaver_size = srsran_coreset_bundle_size_n2; + coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + coreset.shift_index = carrier.pci; + + carrier.nof_prb = 52; + carrier.pci = 500; + } + + uint32_t nof_frequency_resource = SRSRAN_MIN(SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, carrier.nof_prb / 6); for (uint32_t frequency_resources = 1; frequency_resources < (1U << nof_frequency_resource); frequency_resources = (fast_sweep) ? ((frequency_resources << 1U) | 1U) : (frequency_resources + 1)) { for (uint32_t i = 0; i < nof_frequency_resource; i++) { uint32_t mask = ((frequency_resources >> i) & 1U); coreset.freq_resources[i] = (mask == 1); } + for (coreset.duration = SRSRAN_CORESET_DURATION_MIN; coreset.duration <= SRSRAN_CORESET_DURATION_MAX; coreset.duration++) { + // Skip case if CORESET bandwidth is not enough + uint32_t N = srsran_coreset_get_bw(&coreset) * coreset.duration; + if (interleaved && N % 12 != 0) { + continue; + } + srsran_search_space_t search_space = {}; search_space.type = srsran_search_space_type_ue; search_space.formats[search_space.nof_formats++] = srsran_dci_format_nr_0_0; diff --git a/lib/src/phy/phch/test/pdcch_test.c b/lib/src/phy/phch/test/pdcch_test.c index 2571f0782..e28516ff0 100644 --- a/lib/src/phy/phch/test/pdcch_test.c +++ b/lib/src/phy/phch/test/pdcch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,73 +24,105 @@ #include #include +#include "srsran/common/test_common.h" #include "srsran/srsran.h" -srsran_cell_t cell = {.nof_prb = 6, - .nof_ports = 1, - .id = 1, - .cp = SRSRAN_CP_NORM, - .phich_resources = SRSRAN_PHICH_R_1, - .phich_length = SRSRAN_PHICH_NORM}; +// Test parameters +static uint32_t pci = 1; +static uint16_t rnti = 0x46; +static uint32_t cfi = 2; +static uint32_t nof_ports = 1; +static srsran_dci_cfg_t dci_cfg = {}; +static uint32_t nof_prb = 100; +static float snr_dB = NAN; +static uint32_t repetitions = 1; +static bool false_check = false; -uint32_t cfi = 1; -uint32_t nof_rx_ant = 1; -bool print_dci_table; -srsran_dci_cfg_t dci_cfg = {}; +// Test objects +static srsran_random_t random_gen = NULL; +static srsran_pdcch_t pdcch_tx = {}; +static srsran_pdcch_t pdcch_rx = {}; +static srsran_chest_dl_res_t chest_dl_res = {}; +static srsran_channel_awgn_t awgn = {}; +static cf_t* slot_symbols[SRSRAN_MAX_PORTS] = {}; -void usage(char* prog) +static void usage(char* prog) { - printf("Usage: %s [cfpndxv]\n", prog); - printf("\t-c cell id [Default %d]\n", cell.id); + printf("Usage: %s [pfncxv]\n", prog); + printf("\t-c cell id [Default %d]\n", pci); printf("\t-f cfi [Default %d]\n", cfi); - printf("\t-p cell.nof_ports [Default %d]\n", cell.nof_ports); - printf("\t-n cell.nof_prb [Default %d]\n", cell.nof_prb); - printf("\t-A nof_rx_ant [Default %d]\n", nof_rx_ant); - printf("\t-d Print DCI table [Default %s]\n", print_dci_table ? "yes" : "no"); + printf("\t-p cell.nof_ports [Default %d]\n", nof_ports); + printf("\t-n cell.nof_prb [Default %d]\n", nof_prb); printf("\t-x Enable/Disable Cross-scheduling [Default %s]\n", dci_cfg.cif_enabled ? "enabled" : "disabled"); + printf("\t-F False detection check [Default %s]\n", false_check ? "enabled" : "disabled"); + printf("\t-R Repetitions [Default %d]\n", repetitions); + printf("\t-S SNR in dB [Default %+.1f]\n", snr_dB); printf("\t-v [set srsran_verbose to debug, default none]\n"); } -void parse_args(int argc, char** argv) +static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "cfpndvAx")) != -1) { + while ((opt = getopt(argc, argv, "pfncxvFRS")) != -1) { switch (opt) { case 'p': - cell.nof_ports = (uint32_t)strtol(argv[optind], NULL, 10); + nof_ports = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'f': cfi = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'n': - cell.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); + nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'c': - cell.id = (uint32_t)strtol(argv[optind], NULL, 10); - break; - case 'A': - nof_rx_ant = (uint32_t)strtol(argv[optind], NULL, 10); - break; - case 'd': - print_dci_table = true; + pci = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'x': - dci_cfg.cif_enabled ^= true; + dci_cfg.cif_enabled = !dci_cfg.cif_enabled; + break; + case 'F': + false_check = !false_check; + break; + case 'R': + repetitions = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'S': + snr_dB = (float)strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); exit(-1); } } + printf("params - pci=%d; rnti=0x%04x; cfi=%d; nof_ports=%d; cif_enabled=%d; nof_prb=%d; snr_db=%+.1f; " + "repetitions=%d; false_check=%d;\n", + pci, + rnti, + cfi, + nof_ports, + dci_cfg.cif_enabled, + nof_prb, + snr_dB, + repetitions, + false_check); } -int test_dci_payload_size() +static void print_dci_msg(const char* desc, const srsran_dci_msg_t* dci_msg) +{ + printf("%srnti=0x%04x; format=%s; L=%d; ncce=%d; payload=", + desc, + rnti, + srsran_dci_format_string(dci_msg->format), + dci_msg->location.L, + dci_msg->location.ncce); + srsran_vec_fprint_byte(stdout, dci_msg->payload, dci_msg->nof_bits); +} + +static int assert_payload_size(const srsran_cell_t* cell, srsran_dl_sf_cfg_t* dl_sf) { - int i, j; - int x[5]; const srsran_dci_format_t formats[] = { SRSRAN_DCI_FORMAT0, SRSRAN_DCI_FORMAT1, SRSRAN_DCI_FORMAT1A, SRSRAN_DCI_FORMAT1C, SRSRAN_DCI_FORMAT2A}; const int prb[6] = {6, 15, 25, 50, 75, 100}; @@ -101,297 +133,290 @@ int test_dci_payload_size() {27, 33, 27, 14, 42}, {28, 39, 28, 15, 48}}; - srsran_dl_sf_cfg_t dl_sf; - ZERO_OBJECT(dl_sf); + // Skip if special options are requested + if (dci_cfg.cif_enabled || dci_cfg.multiple_csi_request_enabled) { + return SRSRAN_SUCCESS; + } - srsran_cell_t cell_test; - ZERO_OBJECT(cell_test); - cell_test.nof_ports = 1; + // Skip if MIMO is enabled + if (cell->nof_ports > 1) { + return SRSRAN_SUCCESS; + } - ZERO_OBJECT(dci_cfg); + for (uint32_t i = 0; i < 6; i++) { + if (prb[i] != cell->nof_prb) { + continue; + } + int n = prb[i]; - printf("Testing DCI payload sizes...\n"); - printf(" PRB\t0\t1\t1A\t1C\t2A\n"); - for (i = 0; i < 6; i++) { - int n = prb[i]; - cell_test.nof_prb = n; - - for (j = 0; j < 5; j++) { - x[j] = srsran_dci_format_sizeof(&cell_test, &dl_sf, &dci_cfg, formats[j]); + uint32_t x[5]; + for (uint32_t j = 0; j < 5; j++) { + x[j] = srsran_dci_format_sizeof(cell, dl_sf, &dci_cfg, formats[j]); if (x[j] != dci_sz[i][j]) { ERROR("Invalid DCI payload size for %s and %d PRB. Is %d and should be %d", srsran_dci_format_string(formats[j]), n, x[j], dci_sz[i][j]); - return -1; + return SRSRAN_ERROR; } } - printf(" %2d:\t%2d\t%2d\t%2d\t%2d\t%2d\n", n, x[0], x[1], x[2], x[3], x[4]); + return SRSRAN_SUCCESS; } - printf("Ok\n"); - if (print_dci_table) { - printf("dci_sz_table[101][4] = {\n"); - for (i = 0; i <= 100; i++) { - printf(" {"); - for (j = 0; j < 4; j++) { - cell_test.nof_prb = i; - printf("%d", srsran_dci_format_sizeof(&cell, &dl_sf, &dci_cfg, formats[j])); - if (j < 3) { - printf(", "); - } - } - if (i < 100) { - printf("},\n"); - } else { - printf("}\n"); - } - } - printf("};\n"); - } return 0; } -typedef struct { - srsran_dci_msg_t dci_tx, dci_rx; - srsran_dci_location_t dci_location; - srsran_dci_format_t dci_format; - srsran_dci_dl_t ra_dl_tx; - srsran_dci_dl_t ra_dl_rx; -} testcase_dci_t; +static const srsran_dci_format_t formats[] = {SRSRAN_DCI_FORMAT0, + SRSRAN_DCI_FORMAT1A, + SRSRAN_DCI_FORMAT1, + SRSRAN_DCI_FORMAT2A, + SRSRAN_DCI_FORMAT2, + SRSRAN_DCI_NOF_FORMATS}; + +static float get_snr_dB(uint32_t L) +{ + static const float snr_table_dB[4] = {15.0f, 6.0f, 5.0f, 0.0f}; + + if (isnormal(snr_dB) && L < 4) { + return snr_dB; + } else if (L < 4) { + return snr_table_dB[L]; + } else { + ERROR("L >= 4\n"); + return 0.0f; + } +} + +static int test_case1() +{ + uint32_t nof_re = SRSRAN_NOF_RE(pdcch_tx.cell); + + // Iterate all possible subframes + for (uint32_t f_idx = 0; formats[f_idx] != SRSRAN_DCI_NOF_FORMATS; f_idx++) { + srsran_dci_format_t format = formats[f_idx]; + struct timeval t[3] = {}; + uint64_t t_encode_us = 0; + uint64_t t_encode_count = 0; + uint64_t t_llr_us = 0; + uint64_t t_decode_us = 0; + uint64_t t_decode_count = 0; + uint32_t false_alarm_corr_count = 0; + float min_corr = INFINITY; + + for (uint32_t sf_idx = 0; sf_idx < repetitions * SRSRAN_NOF_SF_X_FRAME; sf_idx++) { + srsran_dl_sf_cfg_t dl_sf_cfg = {}; + dl_sf_cfg.cfi = cfi; + dl_sf_cfg.tti = sf_idx % 10240; + + // Generate PDCCH locations + srsran_dci_location_t locations[SRSRAN_MAX_CANDIDATES] = {}; + uint32_t locations_count = 0; + locations_count += + srsran_pdcch_common_locations(&pdcch_tx, &locations[locations_count], SRSRAN_MAX_CANDIDATES_COM, cfi); + locations_count += + srsran_pdcch_ue_locations(&pdcch_tx, &dl_sf_cfg, &locations[locations_count], SRSRAN_MAX_CANDIDATES_UE, rnti); + + // Iterate all possible locations + for (uint32_t loc = 0; loc < locations_count; loc++) { + srsran_dci_msg_t dci_tx = {}; + dci_tx.nof_bits = srsran_dci_format_sizeof(&pdcch_tx.cell, &dl_sf_cfg, &dci_cfg, format); + dci_tx.location = locations[loc]; + dci_tx.format = format; + dci_tx.rnti = rnti; + + // Assert DCI size + TESTASSERT(assert_payload_size(&pdcch_tx.cell, &dl_sf_cfg) == SRSRAN_SUCCESS); + + // Initialise resource grid for each Tx port + for (uint32_t p = 0; p < nof_ports; p++) { + srsran_vec_cf_zero(slot_symbols[p], nof_re); + } + + // Generate Tx DCI + srsran_random_bit_vector(random_gen, dci_tx.payload, dci_tx.nof_bits); + + // Encode + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_pdcch_encode(&pdcch_tx, &dl_sf_cfg, &dci_tx, slot_symbols) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_encode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + t_encode_count++; + + // Set noise level according to aggregation level + float n0_dB = -get_snr_dB(locations[loc].L); + TESTASSERT(srsran_channel_awgn_set_n0(&awgn, n0_dB) == SRSRAN_SUCCESS); + chest_dl_res.noise_estimate = srsran_convert_dB_to_power(n0_dB); + + // Apply AWGN + for (uint32_t p = 0; p < nof_ports; p++) { + srsran_channel_awgn_run_c(&awgn, slot_symbols[p], slot_symbols[p], nof_re); + } + + // Extract LLR + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_pdcch_extract_llr(&pdcch_rx, &dl_sf_cfg, &chest_dl_res, slot_symbols) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_llr_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + + // Try decoding the PDCCH in all possible locations + for (uint32_t loc_rx = 0; loc_rx < locations_count; loc_rx++) { + // Skip location if: + // - False check is disabled and Tx/Rx dont match + // - Tx aggregation level is bigger than Rx aggregation level + if ((!false_check && loc_rx != loc) || locations[loc_rx].L < locations[loc].L) { + continue; + } + + // Prepare DCI message context + srsran_dci_msg_t dci_rx = {}; + dci_rx.location = locations[loc_rx]; + dci_rx.format = format; + + // Try to decode PDCCH message + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf_cfg, &dci_cfg, &dci_rx) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_decode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + t_decode_count++; + + // Compute LLR correlation + float corr = srsran_pdcch_msg_corr(&pdcch_rx, &dci_rx); + + bool rnti_match = (dci_tx.rnti == dci_rx.rnti); + bool location_match = (loc == loc_rx); + bool payload_match = (memcmp(dci_tx.payload, dci_rx.payload, dci_tx.nof_bits) == 0); + bool corr_thr = corr > 0.5f; + + // Skip location if the decoding is not successful in a different location than transmitted + if (!location_match && !rnti_match) { + continue; + } + + // Skip location if the correlation does not surpass the threshold + if (!location_match && !corr_thr) { + false_alarm_corr_count++; + continue; + } + + // Assert correlation only if location matches + if (location_match) { + TESTASSERT(corr_thr); + if (location_match && corr < min_corr) { + min_corr = corr; + } + } + + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO || !payload_match) { + // If payload is not match and there is no logging, set logging to info and run the decoder again + if (get_srsran_verbose_level() < SRSRAN_VERBOSE_INFO) { + printf("-- Detected payload was not matched, repeating decode with INFO logs (n0: %+.1f dB, corr: %f)\n", + n0_dB, + corr); + set_srsran_verbose_level(SRSRAN_VERBOSE_INFO); + srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf_cfg, &dci_cfg, &dci_rx); + } + print_dci_msg("Tx: ", &dci_tx); + print_dci_msg("Rx: ", &dci_rx); + } + + // Assert received message + TESTASSERT(payload_match); + } + } + } + + if (!t_encode_count || !t_decode_count) { + ERROR("Error in test case 1: undefined division"); + return SRSRAN_ERROR; + } + + printf("test_case_1 - format %s - passed - %.1f usec/encode; %.1f usec/llr; %.1f usec/decode; min_corr=%f; " + "false_alarm_prob=%f;\n", + srsran_dci_format_string(format), + (double)t_encode_us / (double)(t_encode_count), + (double)t_llr_us / (double)(t_encode_count), + (double)t_decode_us / (double)(t_decode_count), + min_corr, + (double)false_alarm_corr_count / (double)t_decode_count); + } + + return SRSRAN_SUCCESS; +} int main(int argc, char** argv) { - srsran_chest_dl_res_t chest_dl_res; - srsran_pdcch_t pdcch_tx, pdcch_rx; - testcase_dci_t testcases[10]; - srsran_regs_t regs; - int i; - int nof_re; - cf_t* slot_symbols[SRSRAN_MAX_PORTS]; - int nof_dcis; - - bzero(&testcases, sizeof(testcase_dci_t) * 10); - srsran_random_t random_gen = srsran_random_init(0x1234); - - int ret = -1; + srsran_regs_t regs = {}; + int i = 0; + int ret = SRSRAN_ERROR; parse_args(argc, argv); + random_gen = srsran_random_init(0x1234); - nof_re = SRSRAN_CP_NORM_NSYMB * cell.nof_prb * SRSRAN_NRE; + // Create cell + srsran_cell_t cell = {}; + cell.nof_prb = nof_prb; + cell.nof_ports = nof_ports; + cell.cp = SRSRAN_CP_NORM; + cell.phich_resources = SRSRAN_PHICH_R_1; + cell.phich_length = SRSRAN_PHICH_NORM; - if (test_dci_payload_size()) { - exit(-1); + // Initialise channel estimates with identity matrix + if (srsran_chest_dl_res_init(&chest_dl_res, cell.nof_prb) < SRSRAN_SUCCESS) { + ERROR("Error channel estimates"); + goto quit; } - - /* init memory */ - - srsran_chest_dl_res_init(&chest_dl_res, cell.nof_prb); srsran_chest_dl_res_set_identity(&chest_dl_res); + // Allocate grid + uint32_t nof_re = SRSRAN_NOF_RE(cell); for (i = 0; i < SRSRAN_MAX_PORTS; i++) { slot_symbols[i] = srsran_vec_cf_malloc(nof_re); - if (!slot_symbols[i]) { - perror("malloc"); - exit(-1); + if (slot_symbols[i] == NULL) { + ERROR("malloc"); + goto quit; } - srsran_vec_cf_zero(slot_symbols[i], nof_re); } if (srsran_regs_init(®s, cell)) { ERROR("Error initiating regs"); - exit(-1); + goto quit; } if (srsran_pdcch_init_enb(&pdcch_tx, cell.nof_prb)) { ERROR("Error creating PDCCH object"); - exit(-1); + goto quit; } if (srsran_pdcch_set_cell(&pdcch_tx, ®s, cell)) { ERROR("Error setting cell in PDCCH object"); - exit(-1); + goto quit; } - if (srsran_pdcch_init_ue(&pdcch_rx, cell.nof_prb, nof_rx_ant)) { + if (srsran_pdcch_init_ue(&pdcch_rx, cell.nof_prb, nof_ports)) { ERROR("Error creating PDCCH object"); - exit(-1); + goto quit; } + if (srsran_pdcch_set_cell(&pdcch_rx, ®s, cell)) { ERROR("Error setting cell in PDCCH object"); - exit(-1); + goto quit; } - /* Resource allocate init */ - nof_dcis = 0; - srsran_dci_dl_t dci; - ZERO_OBJECT(dci); - dci.pid = 5; - dci.tb[0].mcs_idx = 5; - dci.tb[0].ndi = 0; - dci.tb[0].rv = 1; - dci.alloc_type = SRSRAN_RA_ALLOC_TYPE0; - dci.type0_alloc.rbg_bitmask = 0x5; - dci.cif_present = dci_cfg.cif_enabled; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); + if (srsran_channel_awgn_init(&awgn, 0x1234) < SRSRAN_SUCCESS) { + ERROR("Error init AWGN"); + goto quit; } - /* Format 1 Test case */ - if (cell.nof_ports == 1) { - testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT1; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); - } - testcases[nof_dcis].ra_dl_tx = dci; - nof_dcis++; - - /* Format 1 Test case */ - dci.tb[0].mcs_idx = 15; - testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT1; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); - } - testcases[nof_dcis].ra_dl_tx = dci; - nof_dcis++; + // Execute actual test cases + if (test_case1() < SRSRAN_SUCCESS) { + ERROR("Test case 1 failed"); + goto quit; } - /* Tx Diversity Test case */ - if (cell.nof_ports > 1) { - dci.tb[1].mcs_idx = 13; - dci.tb[1].rv = 3; - dci.tb[1].ndi = true; - testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT2A; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); - } - testcases[nof_dcis].ra_dl_tx = dci; - nof_dcis++; - } - - /* CDD Spatial Multiplexing Test case */ - if (cell.nof_ports > 1) { - dci.tb[1].mcs_idx = 28; - dci.tb[1].rv = 1; - dci.tb[1].ndi = false; - testcases[nof_dcis].dci_format = SRSRAN_DCI_FORMAT2; - if (dci_cfg.cif_enabled) { - dci.cif = (uint32_t)srsran_random_uniform_int_dist(random_gen, 0, 7); - } - testcases[nof_dcis].ra_dl_tx = dci; - nof_dcis++; - } - - srsran_dl_sf_cfg_t dl_sf; - ZERO_OBJECT(dl_sf); - dl_sf.cfi = cfi; - - for (int s = 0; s < 10; s++) { - dl_sf.tti = s; - printf("Encoding %d DCIs for sf_idx=%d\n", nof_dcis, s); - /* Execute Rx */ - for (i = 0; i < nof_dcis; i++) { - testcases[i].ra_dl_tx.rnti = (uint16_t)(1234 + i); - testcases[i].ra_dl_tx.format = testcases[i].dci_format; - - srsran_dci_msg_pack_pdsch(&cell, &dl_sf, &dci_cfg, &testcases[i].ra_dl_tx, &testcases[i].dci_tx); - srsran_dci_location_set(&testcases[i].dci_location, 0, (uint32_t)i); - - testcases[i].dci_tx.format = testcases[i].dci_format; - testcases[i].dci_tx.location = testcases[i].dci_location; - - // Enable just 1 TB per default - if (testcases[i].dci_format < SRSRAN_DCI_FORMAT2) { - for (int j = 1; j < SRSRAN_MAX_CODEWORDS; j++) { - SRSRAN_DCI_TB_DISABLE(testcases[i].ra_dl_tx.tb[j]); - } - } - - if (srsran_pdcch_encode(&pdcch_tx, &dl_sf, &testcases[i].dci_tx, slot_symbols)) { - ERROR("Error encoding DCI message"); - goto quit; - } - } - - /* Execute 'Rx' */ - if (srsran_pdcch_extract_llr(&pdcch_rx, &dl_sf, &chest_dl_res, slot_symbols)) { - ERROR("Error extracting LLRs"); - goto quit; - } - - /* Decode DCIs */ - for (i = 0; i < nof_dcis; i++) { - testcases[i].dci_rx.format = testcases[i].dci_format; - testcases[i].dci_rx.location = testcases[i].dci_location; - if (srsran_pdcch_decode_msg(&pdcch_rx, &dl_sf, &dci_cfg, &testcases[i].dci_rx)) { - ERROR("Error decoding DCI message"); - goto quit; - } - if (srsran_dci_msg_unpack_pdsch(&cell, &dl_sf, &dci_cfg, &testcases[i].dci_rx, &testcases[i].ra_dl_rx)) { - ERROR("Error unpacking DCI message"); - goto quit; - } - if (testcases[i].dci_rx.rnti >= 1234 && testcases[i].dci_rx.rnti < 1234 + nof_dcis) { - testcases[i].dci_rx.rnti -= 1234; - } else { - printf("Received invalid DCI CRC %d\n", testcases[i].dci_rx.rnti); - goto quit; - } - } - - /* Compare Tx and Rx */ - for (i = 0; i < nof_dcis; i++) { - if (memcmp(testcases[i].dci_tx.payload, testcases[i].dci_rx.payload, testcases[i].dci_tx.nof_bits)) { - printf("Error in DCI %d: Received data does not match\n", i); - goto quit; - } -#if SRSRAN_DCI_HEXDEBUG - // Ignore Hex str - bzero(testcases[i].ra_dl_rx.hex_str, sizeof(testcases[i].ra_dl_rx.hex_str)); - testcases[i].ra_dl_rx.nof_bits = 0; -#endif - // Ignore DCI location - testcases[i].ra_dl_rx.location = testcases[i].ra_dl_tx.location; - // Ignore cw_idx - for (int j = 0; j < SRSRAN_MAX_CODEWORDS; j++) { - testcases[i].ra_dl_rx.tb[j].cw_idx = testcases[i].ra_dl_tx.tb[j].cw_idx; - } - if (memcmp(&testcases[i].ra_dl_tx, &testcases[i].ra_dl_rx, sizeof(srsran_dci_dl_t))) { - uint8_t* x = (uint8_t*)&testcases[i].ra_dl_rx; - uint8_t* y = (uint8_t*)&testcases[i].ra_dl_tx; - for (int j = 0; j < sizeof(srsran_dci_dl_t); j++) { - if (x[j] != y[j]) { - printf("error in byte %d, rx=%d, tx=%d\n", j, x[j], y[j]); - } - } - printf("tx: "); - srsran_vec_fprint_byte(stdout, (uint8_t*)&testcases[i].ra_dl_tx, sizeof(srsran_dci_dl_t)); - printf("rx: "); - srsran_vec_fprint_byte(stdout, (uint8_t*)&testcases[i].ra_dl_rx, sizeof(srsran_dci_dl_t)); - printf("Error in RA %d: Received data does not match\n", i); - printf(" Field | Tx | Rx \n"); - printf("--------------+----------+----------\n"); - if (testcases[i].ra_dl_tx.cif) { - printf(" cif | %8d | %8d\n", testcases[i].ra_dl_tx.cif, testcases[i].ra_dl_rx.cif); - } - printf(" harq_process | %8d | %8d\n", testcases[i].ra_dl_tx.pid, testcases[i].ra_dl_rx.pid); - printf(" mcs_idx | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].mcs_idx, testcases[i].ra_dl_rx.tb[0].mcs_idx); - printf(" rv_idx | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].rv, testcases[i].ra_dl_rx.tb[0].rv); - printf(" ndi | %8d | %8d\n", testcases[i].ra_dl_tx.tb[0].ndi, testcases[i].ra_dl_rx.tb[0].ndi); - printf(" mcs_idx_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].mcs_idx, testcases[i].ra_dl_rx.tb[1].mcs_idx); - printf(" rv_idx_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].rv, testcases[i].ra_dl_rx.tb[1].rv); - printf(" ndi_1 | %8d | %8d\n", testcases[i].ra_dl_tx.tb[1].ndi, testcases[i].ra_dl_rx.tb[1].ndi); - printf(" tb_cw_swap | %8d | %8d\n", testcases[i].ra_dl_tx.tb_cw_swap, testcases[i].ra_dl_rx.tb_cw_swap); - printf(" sram_id | %8d | %8d\n", testcases[i].ra_dl_tx.sram_id, testcases[i].ra_dl_rx.sram_id); - printf(" pinfo | %8d | %8d\n", testcases[i].ra_dl_tx.pinfo, testcases[i].ra_dl_rx.pinfo); - printf(" pconf | %8d | %8d\n", testcases[i].ra_dl_tx.pconf, testcases[i].ra_dl_rx.pconf); - printf(" power_offset | %8d | %8d\n", testcases[i].ra_dl_tx.power_offset, testcases[i].ra_dl_rx.power_offset); - printf(" tpc_pucch | %8d | %8d\n", testcases[i].ra_dl_tx.tpc_pucch, testcases[i].ra_dl_rx.tpc_pucch); - goto quit; - } - } - } - ret = 0; + ret = SRSRAN_SUCCESS; quit: srsran_pdcch_free(&pdcch_tx); @@ -399,9 +424,12 @@ quit: srsran_chest_dl_res_free(&chest_dl_res); srsran_regs_free(®s); srsran_random_free(random_gen); + srsran_channel_awgn_free(&awgn); for (i = 0; i < SRSRAN_MAX_PORTS; i++) { - free(slot_symbols[i]); + if (slot_symbols[i]) { + free(slot_symbols[i]); + } } if (ret) { printf("Error\n"); diff --git a/lib/src/phy/phch/test/pdsch_nr_test.c b/lib/src/phy/phch/test/pdsch_nr_test.c index dd5b4367b..ac2dd0a09 100644 --- a/lib/src/phy/phch/test/pdsch_nr_test.c +++ b/lib/src/phy/phch/test/pdsch_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,16 +29,7 @@ #include #include -static srsran_carrier_nr_t carrier = { - 1, // pci - 0, // absolute_frequency_ssb - 0, // absolute_frequency_point_a - 0, // offset_to_carrier - srsran_subcarrier_spacing_15kHz, // scs - SRSRAN_MAX_PRB_NR, // nof_prb - 0, // start - 1 // max_mimo_layers -}; +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; static uint32_t n_prb = 0; // Set to 0 for steering static uint32_t mcs = 30; // Set to 30 for steering @@ -74,7 +65,7 @@ int parse_args(int argc, char** argv) carrier.max_mimo_layers = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -169,15 +160,11 @@ int main(int argc, char** argv) goto clean_exit; } - // Load number of DMRS CDM groups without data - if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups without data"); - goto clean_exit; - } - - pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; - pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; - pdsch_cfg.grant.rnti = rnti; + // Set PDSCH grant without considering any procedure + pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO + pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; + pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; + pdsch_cfg.grant.rnti = rnti; uint32_t n_prb_start = 1; uint32_t n_prb_end = carrier.nof_prb + 1; diff --git a/lib/src/phy/phch/test/pdsch_pdcch_file_test.c b/lib/src/phy/phch/test/pdsch_pdcch_file_test.c index 91fdb6530..db768cc76 100644 --- a/lib/src/phy/phch/test/pdsch_pdcch_file_test.c +++ b/lib/src/phy/phch/test/pdsch_pdcch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -105,7 +105,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'e': cell.cp = SRSRAN_CP_EXT; @@ -174,7 +174,7 @@ int main(int argc, char** argv) exit(-1); } - uint8_t* data[] = {malloc(100000)}; + uint8_t* data[SRSRAN_MAX_CODEWORDS] = {malloc(100000)}; if (!data[0]) { perror("malloc"); exit(-1); diff --git a/lib/src/phy/phch/test/pdsch_test.c b/lib/src/phy/phch/test/pdsch_test.c index d5d6bfc87..2b9995115 100644 --- a/lib/src/phy/phch/test/pdsch_test.c +++ b/lib/src/phy/phch/test/pdsch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -137,7 +137,7 @@ void parse_args(int argc, char** argv) enable_coworker = true; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'q': enable_256qam ^= true; @@ -159,7 +159,7 @@ static int check_softbits(srsran_pdsch_t* pdsch_enb, if (!pdsch_ue->llr_is_8bit && !tb_cw_swap) { // Scramble - srsran_sequence_pdsch_apply_c(pdsch_ue->e[tb], + srsran_sequence_pdsch_apply_s(pdsch_ue->e[tb], pdsch_ue->e[tb], rnti, pdsch_cfg->grant.tb[tb].cw_idx, @@ -177,6 +177,10 @@ static int check_softbits(srsran_pdsch_t* pdsch_enb, rx_bytes[i] = w; } if (memcmp(pdsch_ue->e[tb], pdsch_enb->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8) != 0) { + printf("tx="); + srsran_vec_fprint_byte(stdout, pdsch_enb->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8); + printf("rx="); + srsran_vec_fprint_byte(stdout, pdsch_ue->e[tb], pdsch_cfg->grant.tb[tb].nof_bits / 8); ret = SRSRAN_ERROR; } } @@ -204,6 +208,7 @@ int main(int argc, char** argv) srsran_chest_dl_res_t chest_res; srsran_pdsch_res_t pdsch_res[SRSRAN_MAX_CODEWORDS]; srsran_random_t random_gen = srsran_random_init(0x1234); + srsran_crc_t crc_tb; /* Initialise to zeros */ ZERO_OBJECT(softbuffers_tx); @@ -221,6 +226,7 @@ int main(int argc, char** argv) ZERO_OBJECT(chest); ZERO_OBJECT(chest_res); ZERO_OBJECT(pdsch_res); + ZERO_OBJECT(crc_tb); parse_args(argc, argv); @@ -393,11 +399,19 @@ int main(int argc, char** argv) } } + if (srsran_crc_init(&crc_tb, SRSRAN_LTE_CRC24A, 24) < SRSRAN_SUCCESS) { + ERROR("Error initiating CRC24A"); + goto quit; + } + + // Generate random data for (int tb = 0; tb < SRSRAN_MAX_CODEWORDS; tb++) { if (pdsch_cfg.grant.tb[tb].enabled) { for (int byte = 0; byte < pdsch_cfg.grant.tb[tb].tbs / 8; byte++) { data_tx[tb][byte] = (uint8_t)srsran_random_uniform_int_dist(random_gen, 0, 255); } + // Attach CRC for making sure TB with 0 CRC are detected + srsran_crc_attach_byte(&crc_tb, data_tx[tb], pdsch_cfg.grant.tb[tb].tbs - 24); } } @@ -508,10 +522,12 @@ int main(int argc, char** argv) for (int tb = 0; tb < SRSRAN_MAX_CODEWORDS; tb++) { if (pdsch_cfg.grant.tb[tb].enabled) { if (check_softbits(&pdsch_tx, &pdsch_rx, &pdsch_cfg, subframe, tb)) { - printf("TB%d: The received softbits in subframe %d DO NOT match the encoded bits (crc=%d)\n", - tb, - subframe, - pdsch_res[tb].crc); + ERROR("TB%d: The received softbits in subframe %d DO NOT match the encoded bits (crc=%d)\n", + tb, + subframe, + pdsch_res[tb].crc); + ret = SRSRAN_ERROR; + goto quit; } else { for (int byte = 0; byte < pdsch_cfg.grant.tb[tb].tbs / 8; byte++) { if (data_tx[tb][byte] != data_rx[tb][byte]) { diff --git a/lib/src/phy/phch/test/phich_file_test.c b/lib/src/phy/phch/test/phich_file_test.c index 8f8b57734..b0e67dbee 100644 --- a/lib/src/phy/phch/test/phich_file_test.c +++ b/lib/src/phy/phch/test/phich_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,7 +48,7 @@ int numsubframe = 0; FILE* fmatlab = NULL; srsran_filesource_t fsrc; -cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_CODEWORDS]; +cf_t * input_buffer, *fft_buffer[SRSRAN_MAX_PORTS]; srsran_phich_t phich; srsran_regs_t regs; srsran_ofdm_t fft; @@ -109,7 +109,7 @@ void parse_args(int argc, char** argv) cell.nof_ports = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'l': cell.cp = SRSRAN_CP_EXT; diff --git a/lib/src/phy/phch/test/phich_test.c b/lib/src/phy/phch/test/phich_test.c index 649d41e95..2b0b44b17 100644 --- a/lib/src/phy/phch/test/phich_test.c +++ b/lib/src/phy/phch/test/phich_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -87,7 +87,7 @@ void parse_args(int argc, char** argv) cell.cp = SRSRAN_CP_EXT; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/pmch_file_test.c b/lib/src/phy/phch/test/pmch_file_test.c index 470b44029..c21072315 100644 --- a/lib/src/phy/phch/test/pmch_file_test.c +++ b/lib/src/phy/phch/test/pmch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -95,7 +95,7 @@ void parse_args(int argc, char** argv) mbsfn_area_id = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'e': cell.cp = SRSRAN_CP_EXT; @@ -216,9 +216,12 @@ int main(int argc, char** argv) SRSRAN_DCI_TB_DISABLE(dci.tb[1]); srsran_ra_dl_dci_to_grant(&cell, &dl_sf, SRSRAN_TM1, false, &dci, &pmch_cfg.pdsch_cfg.grant); - srsran_pdsch_res_t pdsch_res; - pdsch_res.payload = data; - ret = srsran_ue_dl_decode_pmch(&ue_dl, &dl_sf, &pmch_cfg, &pdsch_res); + srsran_pdsch_res_t pdsch_res = {}; + pdsch_res.payload = data; + + srsran_pdsch_res_t pdsch_res_vec[SRSRAN_MAX_CODEWORDS]; + pdsch_res_vec[0] = pdsch_res; + ret = srsran_ue_dl_decode_pmch(&ue_dl, &dl_sf, &pmch_cfg, pdsch_res_vec); if (pdsch_res.crc == 1) { printf("PMCH Decoded OK!\n"); } else if (pdsch_res.crc == 0) { diff --git a/lib/src/phy/phch/test/pmch_test.c b/lib/src/phy/phch/test/pmch_test.c index 372a79e4e..09d130ea2 100644 --- a/lib/src/phy/phch/test/pmch_test.c +++ b/lib/src/phy/phch/test/pmch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -112,7 +112,7 @@ void parse_args(int argc, char** argv) nof_rx_antennas = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -143,7 +143,8 @@ int main(int argc, char** argv) srsran_chest_dl_res_t chest_dl_res; srsran_pmch_t pmch; srsran_pmch_cfg_t pmch_cfg; - srsran_ofdm_t ifft_mbsfn[SRSRAN_MAX_PORTS], fft_mbsfn[SRSRAN_MAX_PORTS]; + srsran_ofdm_t ifft_mbsfn[SRSRAN_MAX_PORTS] = {}; + srsran_ofdm_t fft_mbsfn[SRSRAN_MAX_PORTS] = {}; parse_args(argc, argv); /* Initialise to zeros */ diff --git a/lib/src/phy/phch/test/prach_nr_test_perf.c b/lib/src/phy/phch/test/prach_nr_test_perf.c new file mode 100644 index 000000000..9d34e9a04 --- /dev/null +++ b/lib/src/phy/phch/test/prach_nr_test_perf.c @@ -0,0 +1,263 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/** + * \file prach_nr_test_perf.c + * \brief Performance test for PRACH NR. + * + * This program simulates several PRACH preamble transmissions (so far, burst format 0 only) + * to estimate the probability of detection and of false alarm. The probability of detection + * is the conditional probability of detecting the preamble when the preamble is present. + * An error consists in detecting no preambles, detecting only preambles different from the + * reference one, or detecting the correct preamble with a timing error beyond tolerance. + * The probability of false alarm is the probability of detecting any preamble when input + * is only noise. + * + * The simulation setup can be controlled by means of the following arguments. + * - -N num: sets the number of experiments to \c num. + * - -n num: sets the total number of UL PRBs to \c num. + * - -f num: sets the preamble format to \c num (for now, format 0 only). + * - -s val: sets the nominal SNR to \c val dB. + * - -v : activates verbose output. + * + * Example: + * \code{.cpp} + * prach_nr_test_perf -n 52 -s -14.6 + * \endcode + * + * \todo Restricted preamble formats not implemented yet. Fading channel and SIMO. + */ + +#include +#include +#include +#include +#include + +#include "srsran/srsran.h" + +#define MAX_LEN 70176 + +static uint32_t nof_prb = 52; +static uint32_t config_idx = 0; +static int nof_runs = 100; +static float snr_dB = -14.5F; +static bool is_verbose = false; + +static void usage(char* prog) +{ + printf("Usage: %s\n", prog); + printf("\t-N Number of experiments [Default %d]\n", nof_runs); + printf("\t-n Uplink number of PRB [Default %d]\n", nof_prb); + printf("\t-f Preamble format [Default %d]\n", config_idx); + printf("\t-s SNR in dB [Default %.2f]\n", snr_dB); + printf("\t-v Activate verbose output [Default %s]\n", is_verbose ? "true" : "false"); +} + +static void parse_args(int argc, char** argv) +{ + int opt = 0; + while ((opt = getopt(argc, argv, "N:n:f:s:v")) != -1) { + switch (opt) { + case 'N': + nof_runs = (int)strtol(optarg, NULL, 10); + break; + case 'n': + nof_prb = (uint32_t)strtol(optarg, NULL, 10); + break; + case 'f': + config_idx = (uint32_t)strtol(optarg, NULL, 10); + break; + case 's': + snr_dB = strtof(optarg, NULL); + break; + case 'v': + is_verbose = true; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + parse_args(argc, argv); + if (config_idx != 0) { + ERROR("Preamble format not yet implemented"); + return SRSRAN_ERROR; + } + srsran_prach_t prach; + + const int fft_size = srsran_symbol_sz(nof_prb); + const float main_scs_kHz = 15; // UL subcarrier spacing (i.e., Delta f) + const float sampling_time_us = 1000.0F / (main_scs_kHz * (float)fft_size); + const int slot_length = 15 * fft_size; // number of samples in a slot + + if (srsran_prach_init(&prach, fft_size)) { + ERROR("Initializing PRACH"); + srsran_prach_free(&prach); + return SRSRAN_ERROR; + } + + cf_t preamble[MAX_LEN]; + srsran_vec_cf_zero(preamble, MAX_LEN); + + srsran_prach_cfg_t prach_cfg; + ZERO_OBJECT(prach_cfg); + + // Setup according to TS38.104 Section 8.4 + prach_cfg.is_nr = true; + prach_cfg.config_idx = 0; // preamble format 0 + prach_cfg.hs_flag = false; // no high speed + prach_cfg.freq_offset = 0; + prach_cfg.root_seq_idx = 22; // logical (root sequence) index i + prach_cfg.zero_corr_zone = 1; // zero correlation zone -> implies Ncs = 13 + prach_cfg.num_ra_preambles = 0; // use default + const uint32_t seq_index = 32; // sequence index "v" + const float prach_scs_kHz = 1.25F; // PRACH subcarrier spacing (i.e., Delta f^RA) + const float max_time_error_us = 1.04F; // time error tolerance + const int nof_offset_steps = 10; + + if (srsran_prach_set_cfg(&prach, &prach_cfg, nof_prb)) { + ERROR("Error initiating PRACH object"); + srsran_prach_free(&prach); + return SRSRAN_ERROR; + } + + if (srsran_prach_gen(&prach, seq_index, 0, preamble) < SRSRAN_SUCCESS) { + ERROR("Generating PRACH preamble"); + srsran_prach_free(&prach); + return SRSRAN_ERROR; + } + + const uint32_t preamble_length = prach.N_seq; + + float prach_pwr_sqrt = sqrtf(srsran_vec_avg_power_cf(preamble, preamble_length)); + if (!isnormal(prach_pwr_sqrt)) { + ERROR("PRACH preamble power is not a finite, nonzero value"); + srsran_prach_free(&prach); + return SRSRAN_ERROR; + } + srsran_vec_sc_prod_cfc(preamble, 1.0F / prach_pwr_sqrt, preamble, preamble_length); + + int vector_length = 2 * slot_length; + cf_t symbols[vector_length]; + cf_t noise_vec[vector_length]; + + uint32_t indices[64] = {0}; + float offset_est[64] = {0}; + uint32_t n_indices = 0; + + float time_offset_us = 0; + int offset_samples = 0; + float noise_var = srsran_convert_dB_to_power(-snr_dB); + int ok_detection = 0; + int missed_detection = 0; + int false_detection_signal_tmp = 0; + int false_detection_signal = 0; + int false_detection_noise = 0; + int offset_est_error = 0; + + // Timing offset base value is equivalent to N_cs/2 + const uint32_t ZC_length = prach.N_zc; // Zadoff-Chu sequence length (i.e., L_RA) + const float base_time_offset_us = (float)prach.N_cs * 1000 / (2.0F * (float)ZC_length * prach_scs_kHz); + + int step = SRSRAN_MAX(nof_runs / 100, 1); + for (int i_run = 0; i_run < nof_runs; i_run++) { + // show we are doing something + if (i_run % (20 * step) == 0) { + printf("\n"); + } + if (i_run % step == 0) { + printf("*"); + fflush(stdout); + } + + srsran_vec_cf_zero(noise_vec, vector_length); + srsran_ch_awgn_c(noise_vec, noise_vec, noise_var, vector_length); + if (is_verbose) { + float prach_pwr = srsran_vec_avg_power_cf(preamble, preamble_length); + float noise_pwr = srsran_vec_avg_power_cf(noise_vec, vector_length); + printf(" Tx power: %.3f\n", prach_pwr); + printf(" Noise power: %.3f\n", noise_pwr); + printf(" Target/measured SNR: %.3f / %.3f dB\n", snr_dB, srsran_convert_power_to_dB(prach_pwr / noise_pwr)); + } + // Cycle timing offset with a 0.1-us step starting from the base value + for (int i = 0; i < nof_offset_steps; i++) { + time_offset_us = base_time_offset_us + (float)i * 0.1F; + offset_samples = (int)roundf(time_offset_us / sampling_time_us); + srsran_vec_cf_copy(symbols, noise_vec, vector_length); + srsran_vec_sum_ccc(&symbols[offset_samples], preamble, &symbols[offset_samples], preamble_length); + + srsran_prach_detect_offset(&prach, 0, &symbols[prach.N_cp], slot_length, indices, offset_est, NULL, &n_indices); + false_detection_signal_tmp = 0; + for (int j = 0; j < n_indices; j++) { + if (indices[j] != seq_index) { + false_detection_signal_tmp++; + } else if (fabsf(offset_est[j] * 1.0e6F - time_offset_us) > max_time_error_us) { + offset_est_error++; + } else { + ok_detection++; + } + } + false_detection_signal += (n_indices > 1 || false_detection_signal_tmp == 1); + // Missed detection if no preamble was detected or no detected preamble is the right one + missed_detection += (n_indices == 0 || n_indices == false_detection_signal_tmp); + } + + srsran_prach_detect_offset(&prach, 0, &noise_vec[prach.N_cp], slot_length, indices, offset_est, NULL, &n_indices); + false_detection_noise += (n_indices > 0); + } + int total_runs = nof_offset_steps * nof_runs; + if (missed_detection + ok_detection + offset_est_error != total_runs) { + srsran_prach_free(&prach); + ERROR("Counting detection errors"); + return SRSRAN_ERROR; + } + + printf("\n\nPRACH performance test: format 0, %d PRB, AWGN channel, SNR=%.1f dB\n", nof_prb, snr_dB); + printf("\nMissed detection probability: %.3e (%d out of %d)\n", + (float)missed_detection / (float)total_runs, + missed_detection, + total_runs); + printf("Probability of timing error: %.3e (%d out of %d)\n", + (float)offset_est_error / (float)total_runs, + offset_est_error, + total_runs); + printf("Probability of OK detection: %.3e (%d out of %d)\n", + (float)ok_detection / (float)total_runs, + ok_detection, + total_runs); + printf("\nProbability of false detection with preamble: %.3e (%d out of %d)\n", + (float)false_detection_signal / (float)total_runs, + false_detection_signal, + total_runs); + printf("Probability of false detection without preamble: %.3e (%d out of %d)\n", + (float)false_detection_noise / (float)nof_runs, + false_detection_noise, + nof_runs); + + srsran_prach_free(&prach); + + printf("Done\n"); +} diff --git a/lib/src/phy/phch/test/prach_test.c b/lib/src/phy/phch/test/prach_test.c index 133c85ba3..3fe39c4f9 100644 --- a/lib/src/phy/phch/test/prach_test.c +++ b/lib/src/phy/phch/test/prach_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,6 +32,7 @@ #define MAX_LEN 70176 +static bool is_nr = false; static uint32_t nof_prb = 50; static uint32_t config_idx = 3; static uint32_t root_seq_idx = 0; @@ -45,12 +46,13 @@ static void usage(char* prog) printf("\t-f Preamble format [Default 0]\n"); printf("\t-r Root sequence index [Default 0]\n"); printf("\t-z Zero correlation zone config [Default 1]\n"); + printf("\t-N Toggle LTE/NR operation, zero for LTE, non-zero for NR [Default %s]\n", is_nr ? "NR" : "LTE"); } static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "nfrz")) != -1) { + while ((opt = getopt(argc, argv, "nfrzN")) != -1) { switch (opt) { case 'n': nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); @@ -64,6 +66,9 @@ static void parse_args(int argc, char** argv) case 'z': zero_corr_zone = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'N': + is_nr = (uint32_t)strtol(argv[optind], NULL, 10) > 0; + break; default: usage(argv[0]); exit(-1); @@ -83,6 +88,7 @@ int main(int argc, char** argv) srsran_prach_cfg_t prach_cfg; ZERO_OBJECT(prach_cfg); + prach_cfg.is_nr = is_nr; prach_cfg.config_idx = config_idx; prach_cfg.hs_flag = high_speed_flag; prach_cfg.freq_offset = 0; diff --git a/lib/src/phy/phch/test/prach_test_multi.c b/lib/src/phy/phch/test/prach_test_multi.c index 4aad36152..d8e27c0b2 100644 --- a/lib/src/phy/phch/test/prach_test_multi.c +++ b/lib/src/phy/phch/test/prach_test_multi.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/test/prach_test_usrp.c b/lib/src/phy/phch/test/prach_test_usrp.c index 5f6c4a2c2..d987112d2 100644 --- a/lib/src/phy/phch/test/prach_test_usrp.c +++ b/lib/src/phy/phch/test/prach_test_usrp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -56,9 +56,6 @@ static float uhd_rx_gain = 40, uhd_tx_gain = 60, uhd_freq = 2.4e9; static char* uhd_args = ""; static char* device_name = ""; -// SRSRAN Verbose -SRSRAN_API extern int srsran_verbose; - void usage(char* prog) { printf("Usage: %s \n", prog); @@ -145,7 +142,7 @@ void parse_args(int argc, char** argv) seq_idx = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'z': zero_corr_zone = (uint32_t)strtol(argv[optind], NULL, 10); diff --git a/lib/src/phy/phch/test/psbch_file_test.c b/lib/src/phy/phch/test/psbch_file_test.c index c1993074c..f88598829 100644 --- a/lib/src/phy/phch/test/psbch_file_test.c +++ b/lib/src/phy/phch/test/psbch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -98,7 +98,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -165,7 +165,7 @@ int main(int argc, char** argv) } srsran_chest_sl_t psbch_chest; - if (srsran_chest_sl_init(&psbch_chest, SRSRAN_SIDELINK_PSBCH, cell, sl_comm_resource_pool) != SRSRAN_SUCCESS) { + if (srsran_chest_sl_init(&psbch_chest, SRSRAN_SIDELINK_PSBCH, cell, &sl_comm_resource_pool) != SRSRAN_SUCCESS) { ERROR("Error in chest PSBCH init"); return SRSRAN_ERROR; } diff --git a/lib/src/phy/phch/test/psbch_test.c b/lib/src/phy/phch/test/psbch_test.c index 052dfe9df..68507d8a8 100644 --- a/lib/src/phy/phch/test/psbch_test.c +++ b/lib/src/phy/phch/test/psbch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -76,7 +76,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/pscch_test.c b/lib/src/phy/phch/test/pscch_test.c index beb3a2bb8..049a01a70 100644 --- a/lib/src/phy/phch/test/pscch_test.c +++ b/lib/src/phy/phch/test/pscch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -74,7 +74,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -164,7 +164,7 @@ int main(int argc, char** argv) } srsran_sci_info(&sci, sci_msg, sizeof(sci_msg)); - fprintf(stdout, "%s", sci_msg); + fprintf(stdout, "%s\n", sci_msg); if (sci.riv == riv_txed) { ret = SRSRAN_SUCCESS; } diff --git a/lib/src/phy/phch/test/pssch_pscch_file_test.c b/lib/src/phy/phch/test/pssch_pscch_file_test.c index 9bec2122b..99bdc2db0 100644 --- a/lib/src/phy/phch/test/pssch_pscch_file_test.c +++ b/lib/src/phy/phch/test/pssch_pscch_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -113,7 +113,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -175,7 +175,7 @@ int base_init() return SRSRAN_ERROR; } - if (srsran_chest_sl_init(&pscch_chest, SRSRAN_SIDELINK_PSCCH, cell, sl_comm_resource_pool) != SRSRAN_SUCCESS) { + if (srsran_chest_sl_init(&pscch_chest, SRSRAN_SIDELINK_PSCCH, cell, &sl_comm_resource_pool) != SRSRAN_SUCCESS) { ERROR("Error in PSCCH DMRS init"); return SRSRAN_ERROR; } @@ -185,7 +185,7 @@ int base_init() return SRSRAN_ERROR; } - if (srsran_chest_sl_init(&pssch_chest, SRSRAN_SIDELINK_PSSCH, cell, sl_comm_resource_pool) != SRSRAN_SUCCESS) { + if (srsran_chest_sl_init(&pssch_chest, SRSRAN_SIDELINK_PSSCH, cell, &sl_comm_resource_pool) != SRSRAN_SUCCESS) { ERROR("Error in chest PSSCH init"); return SRSRAN_ERROR; } @@ -296,7 +296,7 @@ int main(int argc, char** argv) if (srsran_pscch_decode(&pscch, equalized_sf_buffer, sci_rx, pscch_prb_start_idx) == SRSRAN_SUCCESS) { if (srsran_sci_format0_unpack(&sci, sci_rx) == SRSRAN_SUCCESS) { srsran_sci_info(&sci, sci_msg, sizeof(sci_msg)); - fprintf(stdout, "%s", sci_msg); + fprintf(stdout, "%s\n", sci_msg); sci_decoded = true; num_decoded_sci++; @@ -357,7 +357,7 @@ int main(int argc, char** argv) if (srsran_pscch_decode(&pscch, equalized_sf_buffer, sci_rx, pscch_prb_start_idx) == SRSRAN_SUCCESS) { if (srsran_sci_format1_unpack(&sci, sci_rx) == SRSRAN_SUCCESS) { srsran_sci_info(&sci, sci_msg, sizeof(sci_msg)); - fprintf(stdout, "%s", sci_msg); + fprintf(stdout, "%s\n", sci_msg); num_decoded_sci++; diff --git a/lib/src/phy/phch/test/pssch_test.c b/lib/src/phy/phch/test/pssch_test.c index 2ed202415..fa5cf5351 100644 --- a/lib/src/phy/phch/test/pssch_test.c +++ b/lib/src/phy/phch/test/pssch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -65,7 +65,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/phch/test/pucch_nr_test.c b/lib/src/phy/phch/test/pucch_nr_test.c index 104ab8c2f..74bfacb87 100644 --- a/lib/src/phy/phch/test/pucch_nr_test.c +++ b/lib/src/phy/phch/test/pucch_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,16 +31,7 @@ #include #include -static srsran_carrier_nr_t carrier = { - 1, // pci - 0, // absolute_frequency_ssb - 0, // absolute_frequency_point_a - 0, // offset_to_carrier - srsran_subcarrier_spacing_15kHz, // scs - 6, // nof_prb - 0, // start - 1 // max_mimo_layers -}; +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; static uint32_t starting_prb_stride = 4; static uint32_t starting_symbol_stride = 4; @@ -97,63 +88,71 @@ static int test_pucch_format0(srsran_pucch_nr_t* pucch, const srsran_pucch_nr_co static int test_pucch_format1(srsran_pucch_nr_t* pucch, const srsran_pucch_nr_common_cfg_t* cfg, srsran_chest_ul_res_t* chest_res, - cf_t* slot_symbols) + cf_t* slot_symbols, + bool enable_intra_slot_hopping) { srsran_slot_cfg_t slot = {}; srsran_pucch_nr_resource_t resource = {}; resource.format = SRSRAN_PUCCH_NR_FORMAT_1; + resource.intra_slot_hopping = enable_intra_slot_hopping; for (slot.idx = 0; slot.idx < SRSRAN_NSLOTS_PER_FRAME_NR(carrier.scs); slot.idx++) { for (resource.starting_prb = 0; resource.starting_prb < carrier.nof_prb; resource.starting_prb += starting_prb_stride) { - for (resource.nof_symbols = SRSRAN_PUCCH_NR_FORMAT1_MIN_NSYMB; - resource.nof_symbols <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NSYMB; - resource.nof_symbols++) { - for (resource.start_symbol_idx = 0; - resource.start_symbol_idx <= - SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_STARTSYMB, SRSRAN_NSYMB_PER_SLOT_NR - resource.nof_symbols); - resource.start_symbol_idx += starting_symbol_stride) { - for (resource.time_domain_occ = 0; resource.time_domain_occ <= SRSRAN_PUCCH_NR_FORMAT1_MAX_TOCC; - resource.time_domain_occ++) { - for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= SRSRAN_PUCCH_NR_FORMAT1_MAX_CS; - resource.initial_cyclic_shift++) { - for (uint32_t nof_bits = 1; nof_bits <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS; nof_bits++) { - for (uint32_t word = 0; word < (1U << nof_bits); word++) { - // Generate bits - uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {}; - for (uint32_t i = 0; i < nof_bits; i++) { - b[i] = (word >> i) & 1U; - } + for (resource.second_hop_prb = 0; resource.second_hop_prb < (enable_intra_slot_hopping) ? carrier.nof_prb : 0; + resource.second_hop_prb += starting_prb_stride) { + for (resource.nof_symbols = SRSRAN_PUCCH_NR_FORMAT1_MIN_NSYMB; + resource.nof_symbols <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NSYMB; + resource.nof_symbols++) { + for (resource.start_symbol_idx = 0; + resource.start_symbol_idx <= + SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_STARTSYMB, SRSRAN_NSYMB_PER_SLOT_NR - resource.nof_symbols); + resource.start_symbol_idx += starting_symbol_stride) { + for (resource.time_domain_occ = 0; resource.time_domain_occ <= SRSRAN_PUCCH_NR_FORMAT1_MAX_TOCC; + resource.time_domain_occ++) { + for (resource.initial_cyclic_shift = 0; resource.initial_cyclic_shift <= SRSRAN_PUCCH_NR_FORMAT1_MAX_CS; + resource.initial_cyclic_shift++) { + for (uint32_t nof_bits = 1; nof_bits <= SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS; nof_bits++) { + for (uint32_t word = 0; word < (1U << nof_bits); word++) { + // Generate bits + uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {}; + for (uint32_t i = 0; i < nof_bits; i++) { + b[i] = (word >> i) & 1U; + } - // Encode PUCCH - TESTASSERT(srsran_pucch_nr_format1_encode(pucch, cfg, &slot, &resource, b, nof_bits, slot_symbols) == - SRSRAN_SUCCESS); + // Encode PUCCH + TESTASSERT(srsran_pucch_nr_format1_encode( + pucch, cfg, &slot, &resource, b, nof_bits, slot_symbols) == SRSRAN_SUCCESS); - // Put DMRS - TESTASSERT(srsran_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) == - SRSRAN_SUCCESS); + // Put DMRS + TESTASSERT(srsran_dmrs_pucch_format1_put(pucch, &carrier, cfg, &slot, &resource, slot_symbols) == + SRSRAN_SUCCESS); - // Apply AWGN - srsran_channel_awgn_run_c( - &awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSRAN_NRE * SRSRAN_NSYMB_PER_SLOT_NR); + // Apply AWGN + srsran_channel_awgn_run_c( + &awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSRAN_NRE * SRSRAN_NSYMB_PER_SLOT_NR); - // Estimate channel - TESTASSERT(srsran_dmrs_pucch_format1_estimate( - pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == SRSRAN_SUCCESS); + // Estimate channel + TESTASSERT(srsran_dmrs_pucch_format1_estimate( + pucch, cfg, &slot, &resource, slot_symbols, chest_res) == SRSRAN_SUCCESS); - TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f); - TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f); - TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f); + TESTASSERT(fabsf(chest_res->rsrp_dBfs - 0.0f) < 3.0f); + TESTASSERT(fabsf(chest_res->epre_dBfs - 0.0f) < 3.0f); + TESTASSERT(fabsf(chest_res->snr_db - snr_db) < 10.0f); - // Decode PUCCH - uint8_t b_rx[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; - TESTASSERT(srsran_pucch_nr_format1_decode( - pucch, cfg, &slot, &resource, chest_res, slot_symbols, b_rx, nof_bits) == - SRSRAN_SUCCESS); + // Decode PUCCH + uint8_t b_rx[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; + TESTASSERT(srsran_pucch_nr_format1_decode( + pucch, cfg, &slot, &resource, chest_res, slot_symbols, b_rx, nof_bits, NULL) == + SRSRAN_SUCCESS); - // Check received bits - for (uint32_t i = 0; i < nof_bits; i++) { - TESTASSERT(b[i] == b_rx[i]); + // Check received bits + for (uint32_t i = 0; i < nof_bits; i++) { + if (b[i] != b_rx[i]) { + printf("aaa"); + } + TESTASSERT(b[i] == b_rx[i]); + } } } } @@ -186,13 +185,14 @@ static int test_pucch_format2(srsran_pucch_nr_t* pucch, resource.start_symbol_idx += starting_symbol_stride) { srsran_uci_cfg_nr_t uci_cfg = {}; - for (uci_cfg.o_ack = SRSRAN_PUCCH_NR_FORMAT2_MIN_NOF_BITS; uci_cfg.o_ack <= SRSRAN_UCI_NR_MAX_ACK_BITS; - uci_cfg.o_ack++) { + for (uci_cfg.ack.count = SRSRAN_PUCCH_NR_FORMAT2_MIN_NOF_BITS; + uci_cfg.ack.count <= SRSRAN_HARQ_ACK_MAX_NOF_BITS; + uci_cfg.ack.count++) { srsran_uci_value_nr_t uci_value = {}; // Maximum code rate is reserved uint32_t max_code_rate_end = SRSRAN_PUCCH_NR_MAX_CODE_RATE; - if (uci_cfg.o_ack == 11) { + if (uci_cfg.ack.count == 11) { max_code_rate_end = SRSRAN_PUCCH_NR_MAX_CODE_RATE - 1; } @@ -207,7 +207,7 @@ static int test_pucch_format2(srsran_pucch_nr_t* pucch, for (resource.starting_prb = 0; resource.starting_prb < (carrier.nof_prb - resource.nof_prb); resource.starting_prb += starting_prb_stride) { // Generate ACKs - for (uint32_t i = 0; i < uci_cfg.o_ack; i++) { + for (uint32_t i = 0; i < uci_cfg.ack.count; i++) { uci_value.ack[i] = (uint8_t)srsran_random_uniform_int_dist(random_gen, 0, 1); } @@ -224,8 +224,8 @@ static int test_pucch_format2(srsran_pucch_nr_t* pucch, &awgn, slot_symbols, slot_symbols, carrier.nof_prb * SRSRAN_NRE * SRSRAN_NSYMB_PER_SLOT_NR); // Estimate channel - TESTASSERT(srsran_dmrs_pucch_format2_estimate( - pucch, &carrier, cfg, &slot, &resource, slot_symbols, chest_res) == SRSRAN_SUCCESS); + TESTASSERT(srsran_dmrs_pucch_format2_estimate(pucch, cfg, &slot, &resource, slot_symbols, chest_res) == + SRSRAN_SUCCESS); INFO("RSRP=%+.2f; EPRE=%+.2f; SNR=%+.2f;", chest_res->rsrp_dBfs, chest_res->epre_dBfs, @@ -243,7 +243,7 @@ static int test_pucch_format2(srsran_pucch_nr_t* pucch, TESTASSERT(uci_value_rx.valid == true); // Check received ACKs - for (uint32_t i = 0; i < uci_cfg.o_ack; i++) { + for (uint32_t i = 0; i < uci_cfg.ack.count; i++) { TESTASSERT(uci_value.ack[i] == uci_value_rx.ack[i]); } } @@ -268,6 +268,9 @@ static void usage(char* prog) static void parse_args(int argc, char** argv) { + // Limit default number of RB + carrier.nof_prb = 6; + int opt; while ((opt = getopt(argc, argv, "cnfsv")) != -1) { switch (opt) { @@ -284,7 +287,7 @@ static void parse_args(int argc, char** argv) snr_db = strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -350,9 +353,13 @@ int main(int argc, char** argv) } } - // Test Format 1 + // Test Format 1 with and without intra slot frequency hopping if (format < 0 || format == 1) { - if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb) < SRSRAN_SUCCESS) { + if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb, false) < SRSRAN_SUCCESS) { + ERROR("Failed PUCCH format 1"); + goto clean_exit; + } + if (test_pucch_format1(&pucch, &common_cfg, &chest_res, slot_symb, true) < SRSRAN_SUCCESS) { ERROR("Failed PUCCH format 1"); goto clean_exit; } diff --git a/lib/src/phy/phch/test/pucch_test.c b/lib/src/phy/phch/test/pucch_test.c index 07abb118c..701c9bab7 100644 --- a/lib/src/phy/phch/test/pucch_test.c +++ b/lib/src/phy/phch/test/pucch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -75,7 +75,7 @@ void parse_args(int argc, char** argv) snr_db = strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -89,7 +89,7 @@ int test_uci_cqi_pucch(void) int ret = SRSRAN_SUCCESS; __attribute__((aligned(256))) uint8_t o_bits[SRSRAN_UCI_MAX_CQI_LEN_PUCCH] = {0}; __attribute__((aligned(256))) uint8_t e_bits[SRSRAN_UCI_CQI_CODED_PUCCH_B] = {0}; - __attribute__((aligned(256))) int16_t e_symb[SRSRAN_UCI_CQI_CODED_PUCCH_B] = {0}; + __attribute__((aligned(256))) int16_t e_symb[SRSRAN_CQI_MAX_BITS] = {0}; __attribute__((aligned(256))) uint8_t d_bits[SRSRAN_UCI_MAX_CQI_LEN_PUCCH] = {0}; srsran_uci_cqi_pucch_t uci_cqi_pucch = {0}; @@ -116,7 +116,7 @@ int test_uci_cqi_pucch(void) if (recv != cqi) { printf("Error! cqi = %d (len: %d), %X!=%X \n", cqi, nof_bits, cqi, recv); - if (srsran_verbose) { + if (get_srsran_verbose_level()) { printf("original: "); srsran_vec_fprint_b(stdout, o_bits, nof_bits); printf(" decoded: "); diff --git a/lib/src/phy/phch/test/pusch_nr_bler_test.c b/lib/src/phy/phch/test/pusch_nr_bler_test.c new file mode 100644 index 000000000..2d0bc4358 --- /dev/null +++ b/lib/src/phy/phch/test/pusch_nr_bler_test.c @@ -0,0 +1,438 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/** + * \file pusch_nr_bler_test.c + * \brief BLER and throughput test for PUSCH NR. + * + * This program simulates several PUSCH transmissions in order to estimate its performance in terms of the receiver BLER + * and throughput (expressed as a percentage of the transmitted one). Specifically, the simulation runs until 100 + * transmissions fail (or after 2,000,000 transmitted transport blocks). Failures are detected by CRC verification. + * + * The simulation setup can be controlled by means of the following arguments. + * - -p num: sets the number of granted PUSCH PRBs to \c num. + * - -T tab: sets the modulation and coding scheme table (valid options: \c 64qam, \c 256qam, \c 64qamLowSE). + * - -m mcs: sets the modulation and coding scheme index to \c mcs. + * - -L num: sets the number of transmission layers to \c num. + * - -A num: sets the number of HARQ-ACK bits to \c num. + * - -C num: sets the number of CSI bits to \c num. + * - -N num: sets the maximum number of simulated transport blocks to \c num. + * - -s val: sets the nominal SNR to \c val (in dB). + * - -f : activates full BLER simulations (Tx--Rx comparison as opposed to CRC-verification only). + * - -v : activates verbose output. + * + * Example: + * \code{.cpp} + * pusch_nr_bler_test -p 52 -m 2 -T 64qam -s -1.8 -f + * \endcode + * + */ + +#include "srsran/phy/channel/ch_awgn.h" +#include "srsran/phy/phch/pusch_nr.h" +#include "srsran/phy/phch/ra_nr.h" +#include "srsran/phy/phch/ra_ul_nr.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/random.h" +#include "srsran/phy/utils/vector.h" +#include + +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; +static uint32_t n_prb = 0; +static uint32_t mcs = 30; +static srsran_sch_cfg_nr_t pusch_cfg = {}; +static uint16_t rnti = 0x1234; +static uint32_t nof_ack_bits = 0; +static uint32_t nof_csi_bits = 0; +static uint32_t max_blocks = 2e6; // max number of simulated transport blocks +static float snr = 10; +static bool full_check = false; + +void usage(char* prog) +{ + printf("Usage: %s [pmTLACNsfv] \n", prog); + printf("\t-p Number of grant PRB [Default %d]\n", n_prb); + printf("\t-m MCS PRB [Default %d]\n", mcs); + printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n", + srsran_mcs_table_to_str(pusch_cfg.sch_cfg.mcs_table)); + printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers); + printf("\t-A Provide a number of HARQ-ACK bits [Default %d]\n", nof_ack_bits); + printf("\t-C Provide a number of CSI bits [Default %d]\n", nof_csi_bits); + printf("\t-N Maximum number of simulated transport blocks [Default %d]\n", max_blocks); + printf("\t-s Signal-to-Noise Ratio in dB [Default %.1f]\n", snr); + printf("\t-f Perform full BLER check instead of CRC only [Default %s]\n", full_check ? "true" : "false"); + printf("\t-v [set srsran_verbose to debug, default none]\n"); +} + +int parse_args(int argc, char** argv) +{ + int opt = 0; + while ((opt = getopt(argc, argv, "p:m:T:L:A:C:N:s:fv")) != -1) { + switch (opt) { + case 'p': + n_prb = (uint32_t)strtol(optarg, NULL, 10); + break; + case 'm': + mcs = (uint32_t)strtol(optarg, NULL, 10); + break; + case 'T': + pusch_cfg.sch_cfg.mcs_table = srsran_mcs_table_from_str(optarg); + break; + case 'L': + carrier.max_mimo_layers = (uint32_t)strtol(optarg, NULL, 10); + break; + case 'A': + nof_ack_bits = (uint32_t)strtol(optarg, NULL, 10); + break; + case 'C': + nof_csi_bits = (uint32_t)strtol(optarg, NULL, 10); + break; + case 'N': + max_blocks = (uint32_t)strtol(optarg, NULL, 10); + break; + case 's': + snr = strtof(optarg, NULL); + break; + case 'f': + full_check = true; + break; + case 'v': + increase_srsran_verbose_level(); + break; + default: + usage(argv[0]); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + srsran_pusch_nr_t pusch_tx = {}; + srsran_pusch_nr_t pusch_rx = {}; + srsran_chest_dl_res_t chest = {}; + srsran_random_t rand_gen = srsran_random_init(1234); + + srsran_pusch_data_nr_t data_tx = {}; + srsran_pusch_res_nr_t data_rx = {}; + cf_t* sf_symbols_tx[SRSRAN_MAX_LAYERS_NR] = {}; + cf_t* sf_symbols_rx[SRSRAN_MAX_LAYERS_NR] = {}; + + // Set default PUSCH configuration + pusch_cfg.sch_cfg.mcs_table = srsran_mcs_table_64qam; + + if (parse_args(argc, argv) < SRSRAN_SUCCESS) { + goto clean_exit; + } + + srsran_pusch_nr_args_t pusch_args = {}; + pusch_args.sch.disable_simd = false; + pusch_args.measure_evm = true; + + if (srsran_pusch_nr_init_ue(&pusch_tx, &pusch_args) < SRSRAN_SUCCESS) { + ERROR("Error initiating PUSCH for Tx"); + goto clean_exit; + } + + if (srsran_pusch_nr_init_gnb(&pusch_rx, &pusch_args) < SRSRAN_SUCCESS) { + ERROR("Error initiating SCH NR for Rx"); + goto clean_exit; + } + + if (srsran_pusch_nr_set_carrier(&pusch_tx, &carrier)) { + ERROR("Error setting SCH NR carrier"); + goto clean_exit; + } + + if (srsran_pusch_nr_set_carrier(&pusch_rx, &carrier)) { + ERROR("Error setting SCH NR carrier"); + goto clean_exit; + } + + uint32_t slot_length = SRSRAN_SLOT_LEN_RE_NR(carrier.nof_prb); + for (uint32_t i = 0; i < carrier.max_mimo_layers; i++) { + sf_symbols_tx[i] = srsran_vec_cf_malloc(slot_length); + sf_symbols_rx[i] = srsran_vec_cf_malloc(slot_length); + if (sf_symbols_tx[i] == NULL || sf_symbols_rx[i] == NULL) { + ERROR("Error malloc"); + goto clean_exit; + } + } + + for (uint32_t i = 0; i < pusch_tx.max_cw; i++) { + data_tx.payload[i] = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR); + data_rx.tb[i].payload = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR); + if (data_tx.payload[i] == NULL || data_rx.tb[i].payload == NULL) { + ERROR("Error malloc"); + goto clean_exit; + } + } + + srsran_softbuffer_tx_t softbuffer_tx = {}; + srsran_softbuffer_rx_t softbuffer_rx = {}; + + if (srsran_softbuffer_tx_init_guru(&softbuffer_tx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < + SRSRAN_SUCCESS) { + ERROR("Error init soft-buffer"); + goto clean_exit; + } + + if (srsran_softbuffer_rx_init_guru(&softbuffer_rx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < + SRSRAN_SUCCESS) { + ERROR("Error init soft-buffer"); + goto clean_exit; + } + + // Use grant default A time resources with m=0 + if (srsran_ra_ul_nr_pusch_time_resource_default_A(carrier.scs, 0, &pusch_cfg.grant) < SRSRAN_SUCCESS) { + ERROR("Error loading default grant"); + goto clean_exit; + } + + // Set PUSCH grant without considering any procedure + pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO + pusch_cfg.grant.nof_layers = carrier.max_mimo_layers; + pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; + pusch_cfg.grant.rnti = rnti; + + // Check input: PRB + if (n_prb > carrier.nof_prb) { + ERROR("Invalid number of PRB"); + goto clean_exit; + } + + // Check input: MCS + uint32_t mcs_end = pusch_cfg.sch_cfg.mcs_table == srsran_mcs_table_256qam ? 28 : 29; + if (mcs > mcs_end) { + ERROR("Invalid MCS"); + goto clean_exit; + } + + srsran_sch_hl_cfg_nr_t sch_hl_cfg = {}; + sch_hl_cfg.scaling = 1.0F; + sch_hl_cfg.beta_offsets.fix_ack = 12.625F; + sch_hl_cfg.beta_offsets.fix_csi1 = 2.25F; + sch_hl_cfg.beta_offsets.fix_csi2 = 2.25F; + + if (srsran_chest_dl_res_init(&chest, carrier.nof_prb) < SRSRAN_SUCCESS) { + ERROR("Initiating chest"); + goto clean_exit; + } + + for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) { + pusch_cfg.grant.prb_idx[n] = (n < n_prb); + } + pusch_cfg.grant.nof_prb = n_prb; + + pusch_cfg.grant.dci_format = srsran_dci_format_nr_0_0; + pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 2; + pusch_cfg.dmrs.type = srsran_dmrs_sch_type_1; + pusch_cfg.dmrs.length = srsran_dmrs_sch_len_1; + pusch_cfg.dmrs.additional_pos = srsran_dmrs_sch_add_pos_2; + if (srsran_ra_nr_fill_tb(&pusch_cfg, &pusch_cfg.grant, mcs, &pusch_cfg.grant.tb[0]) < SRSRAN_SUCCESS) { + ERROR("Error filling tb"); + goto clean_exit; + } + + uint32_t n_blocks = 0; + uint32_t n_errors = 0; + uint32_t crc_false_pos = 0; + uint32_t crc_false_neg = 0; + float evm = 0; + for (; n_blocks < max_blocks && n_errors < 100; n_blocks++) { + // Generate SCH payload + for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { + // Skip TB if no allocated + if (data_tx.payload[tb] == NULL) { + continue; + } + + // load payload with bytes + for (uint32_t i = 0; i < pusch_cfg.grant.tb[tb].tbs / 8 + 1; i++) { + data_tx.payload[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX); + } + pusch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx; + } + + // Generate HARQ ACK bits + if (nof_ack_bits > 0) { + pusch_cfg.uci.ack.count = nof_ack_bits; + for (uint32_t i = 0; i < nof_ack_bits; i++) { + data_tx.uci.ack[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1); + } + } + + // Generate CSI report bits + uint8_t csi_report_tx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {}; + uint8_t csi_report_rx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {}; + if (nof_csi_bits > 0) { + pusch_cfg.uci.csi[0].cfg.quantity = SRSRAN_CSI_REPORT_QUANTITY_NONE; + pusch_cfg.uci.csi[0].K_csi_rs = nof_csi_bits; + pusch_cfg.uci.nof_csi = 1; + data_tx.uci.csi[0].none = csi_report_tx; + for (uint32_t i = 0; i < nof_csi_bits; i++) { + csi_report_tx[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1); + } + + data_rx.uci.csi[0].none = csi_report_rx; + } + + if (srsran_ra_ul_set_grant_uci_nr(&carrier, &sch_hl_cfg, &pusch_cfg.uci, &pusch_cfg) < SRSRAN_SUCCESS) { + ERROR("Setting UCI"); + goto clean_exit; + } + + if (srsran_pusch_nr_encode(&pusch_tx, &pusch_cfg, &pusch_cfg.grant, &data_tx, sf_symbols_tx) < SRSRAN_SUCCESS) { + ERROR("Error encoding"); + goto clean_exit; + } + + float noise_var = srsran_convert_dB_to_power(-snr); + for (uint32_t i = 0; i < carrier.max_mimo_layers; i++) { + srsran_ch_awgn_c(sf_symbols_tx[i], sf_symbols_rx[i], noise_var, slot_length); + // memcpy(sf_symbols_rx[i], sf_symbols_tx[i], slot_length * sizeof(cf_t)); + } + + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) { + uint32_t nof_re_total = carrier.nof_prb * SRSRAN_NRE; + uint32_t nof_re_used = pusch_cfg.grant.nof_prb * SRSRAN_NRE; + for (int i_layer = 0; i_layer < carrier.max_mimo_layers; i_layer++) { + INFO("Layer %d", i_layer); + float tx_power = 0; + float rx_power = 0; + uint8_t n_symbols = 0; + for (int i = 0; i < SRSRAN_NSYMB_PER_SLOT_NR; i++) { + if (!pusch_tx.dmrs_re_pattern.symbol[i]) { + n_symbols++; + tx_power += srsran_vec_avg_power_cf(sf_symbols_tx[0] + i * nof_re_total, nof_re_total); + rx_power += srsran_vec_avg_power_cf(sf_symbols_rx[0] + i * nof_re_total, nof_re_total); + } + } + tx_power *= (float)nof_re_total / nof_re_used; // compensate for unused REs + INFO(" Tx power: %.3f", tx_power / n_symbols); + INFO(" Rx power: %.3f", rx_power / n_symbols); + INFO(" SNR: %.3f dB", srsran_convert_power_to_dB(tx_power / (rx_power - tx_power))); + } + } + + for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { + pusch_cfg.grant.tb[tb].softbuffer.rx = &softbuffer_rx; + srsran_softbuffer_rx_reset(pusch_cfg.grant.tb[tb].softbuffer.rx); + } + + // assume perfect channel estimation (including noise variance) + for (uint32_t i = 0; i < pusch_cfg.grant.tb->nof_re; i++) { + chest.ce[0][0][i] = 1.0F; + } + chest.nof_re = pusch_cfg.grant.tb->nof_re; + chest.noise_estimate = 2 * noise_var; + + if (srsran_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &chest, sf_symbols_rx, &data_rx) < + SRSRAN_SUCCESS) { + ERROR("Error decoding"); + goto clean_exit; + } + + evm += data_rx.evm[0]; + // Validate UL-SCH CRC check + if (!data_rx.tb[0].crc) { + n_errors++; + printf("*"); + fflush(stdout); + if (n_errors % 20 == 0) { + printf("\n"); + } + } + + if (full_check) { + // Validate by comparing payload (recall, payload is represented in bytes) + if ((memcmp(data_rx.tb[0].payload, data_tx.payload[0], pusch_cfg.grant.tb[0].tbs * sizeof(uint8_t) / 8) == 0) && + !data_rx.tb[0].crc) { + printf("\nWARNING! Codeword OK but CRC KO!\n"); + crc_false_pos++; + } else if ((memcmp(data_rx.tb[0].payload, data_tx.payload[0], pusch_cfg.grant.tb[0].tbs * sizeof(uint8_t) / 8) != + 0) && + data_rx.tb[0].crc) { + printf("\nWarning! Codeword KO but CRC OK!\n"); + crc_false_neg++; + } + } + } + char str[512]; + srsran_pusch_nr_rx_info(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &data_rx, str, (uint32_t)sizeof(str)); + + char str_extra[2048]; + srsran_sch_cfg_nr_info(&pusch_cfg, str_extra, (uint32_t)sizeof(str_extra)); + printf("\nPUSCH: %s\n%s", str, str_extra); + + printf("\nNominal SNR: %.1f dB\n", snr); + printf("Average EVM: %.3f\n", evm / n_blocks); + + printf("BLER: %.3e (%d errors out of %d blocks)\n", (double)n_errors / n_blocks, n_errors, n_blocks); + printf("Tx Throughput: %.3e Mbps -- Rx Throughput: %.3e Mbps (%.2f%%)\n", + pusch_cfg.grant.tb[0].tbs / 1e3, + (n_blocks - n_errors) / 1e3 * pusch_cfg.grant.tb[0].tbs / n_blocks, + 100.0F * (n_blocks - n_errors) / n_blocks); + + if (full_check) { + uint32_t true_errors = n_errors + crc_false_neg - crc_false_pos; + printf("CRC: missed detection/Type I err. %.2f%% (%d out of %d)", + 100.0F * crc_false_neg / true_errors, + crc_false_neg, + true_errors); + printf(" -- false alarm %.2f%% (%d out of %d)", 100.0F * crc_false_pos / n_errors, crc_false_pos, n_errors); + printf(" -- Type II err. %.2f%% (%d out of %d)\n", + 100.0F * crc_false_pos / (n_blocks - true_errors), + crc_false_pos, + n_blocks - true_errors); + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + srsran_chest_dl_res_free(&chest); + srsran_random_free(rand_gen); + srsran_pusch_nr_free(&pusch_tx); + srsran_pusch_nr_free(&pusch_rx); + for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { + if (data_tx.payload[i]) { + free(data_tx.payload[i]); + } + if (data_rx.tb[i].payload) { + free(data_rx.tb[i].payload); + } + } + for (uint32_t i = 0; i < SRSRAN_MAX_LAYERS_NR; i++) { + if (sf_symbols_tx[i]) { + free(sf_symbols_tx[i]); + } + if (sf_symbols_rx[i]) { + free(sf_symbols_rx[i]); + } + } + srsran_softbuffer_tx_free(&softbuffer_tx); + srsran_softbuffer_rx_free(&softbuffer_rx); + + return ret; +} diff --git a/lib/src/phy/phch/test/pusch_nr_test.c b/lib/src/phy/phch/test/pusch_nr_test.c index 49c5764c3..7f019e490 100644 --- a/lib/src/phy/phch/test/pusch_nr_test.c +++ b/lib/src/phy/phch/test/pusch_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,17 +28,7 @@ #include #include -static srsran_carrier_nr_t carrier = { - 1, // pci - 0, // absolute_frequency_ssb - 0, // absolute_frequency_point_a - 0, // offset_to_carrier - srsran_subcarrier_spacing_15kHz, // scs - SRSRAN_MAX_PRB_NR, // nof_prb - 0, // start - 1 // max_mimo_layers -}; - +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; static uint32_t n_prb = 0; // Set to 0 for steering static uint32_t mcs = 30; // Set to 30 for steering static srsran_sch_cfg_nr_t pusch_cfg = {}; @@ -83,7 +73,7 @@ int parse_args(int argc, char** argv) nof_csi_bits = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -175,15 +165,11 @@ int main(int argc, char** argv) goto clean_exit; } - // Load number of DMRS CDM groups without data - if (srsran_ra_ul_nr_nof_dmrs_cdm_groups_without_data_format_0_0(&pusch_cfg, &pusch_cfg.grant) < SRSRAN_SUCCESS) { - ERROR("Error loading number of DMRS CDM groups without data"); - goto clean_exit; - } - - pusch_cfg.grant.nof_layers = carrier.max_mimo_layers; - pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; - pusch_cfg.grant.rnti = rnti; + // Set PUSCH grant without considering any procedure + pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO + pusch_cfg.grant.nof_layers = carrier.max_mimo_layers; + pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; + pusch_cfg.grant.rnti = rnti; uint32_t n_prb_start = 1; uint32_t n_prb_end = carrier.nof_prb + 1; @@ -219,7 +205,7 @@ int main(int argc, char** argv) pusch_cfg.grant.dci_format = srsran_dci_format_nr_0_0; if (srsran_ra_nr_fill_tb(&pusch_cfg, &pusch_cfg.grant, mcs, &pusch_cfg.grant.tb[0]) < SRSRAN_SUCCESS) { - ERROR("Error filing tb"); + ERROR("Error filling tb"); goto clean_exit; } @@ -238,7 +224,7 @@ int main(int argc, char** argv) // Generate HARQ ACK bits if (nof_ack_bits > 0) { - pusch_cfg.uci.o_ack = nof_ack_bits; + pusch_cfg.uci.ack.count = nof_ack_bits; for (uint32_t i = 0; i < nof_ack_bits; i++) { data_tx.uci.ack[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1); } @@ -248,10 +234,10 @@ int main(int argc, char** argv) uint8_t csi_report_tx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {}; uint8_t csi_report_rx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {}; if (nof_csi_bits > 0) { - pusch_cfg.uci.csi[0].quantity = SRSRAN_CSI_REPORT_QUANTITY_NONE; - pusch_cfg.uci.csi[0].K_csi_rs = nof_csi_bits; - pusch_cfg.uci.nof_csi = 1; - data_tx.uci.csi[0].none = csi_report_tx; + pusch_cfg.uci.csi[0].cfg.quantity = SRSRAN_CSI_REPORT_QUANTITY_NONE; + pusch_cfg.uci.csi[0].K_csi_rs = nof_csi_bits; + pusch_cfg.uci.nof_csi = 1; + data_tx.uci.csi[0].none = csi_report_tx; for (uint32_t i = 0; i < nof_csi_bits; i++) { csi_report_tx[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1); } @@ -290,25 +276,49 @@ int main(int argc, char** argv) goto clean_exit; } - float mse = 0.0f; + // Check symbols Mean Square Error (MSE) uint32_t nof_re = srsran_ra_dl_nr_slot_nof_re(&pusch_cfg, &pusch_cfg.grant); - for (uint32_t i = 0; i < pusch_cfg.grant.nof_layers; i++) { - for (uint32_t j = 0; j < nof_re; j++) { - mse += cabsf(pusch_tx.d[i][j] - pusch_rx.d[i][j]); - } - } if (nof_re * pusch_cfg.grant.nof_layers > 0) { - mse = mse / (nof_re * pusch_cfg.grant.nof_layers); - } - if (mse > 0.001) { - ERROR("MSE error (%f) is too high", mse); + float mse = 0.0f; + float mse_tmp = 0.0f; for (uint32_t i = 0; i < pusch_cfg.grant.nof_layers; i++) { - printf("d_tx[%d]=", i); - srsran_vec_fprint_c(stdout, pusch_tx.d[i], nof_re); - printf("d_rx[%d]=", i); - srsran_vec_fprint_c(stdout, pusch_rx.d[i], nof_re); + for (uint32_t j = 0; j < nof_re; j++) { + mse_tmp = cabsf(pusch_tx.d[i][j] - pusch_rx.d[i][j]); + mse += mse_tmp * mse_tmp; + } + } + mse = mse / (nof_re * pusch_cfg.grant.nof_layers); + if (mse > 0.001) { + ERROR("MSE error (%f) is too high", mse); + for (uint32_t i = 0; i < pusch_cfg.grant.nof_layers; i++) { + printf("d_tx[%d]=", i); + srsran_vec_fprint_c(stdout, pusch_tx.d[i], nof_re); + printf("d_rx[%d]=", i); + srsran_vec_fprint_c(stdout, pusch_rx.d[i], nof_re); + } + goto clean_exit; + } + } + + // Check Received SCH LLR match + if (pusch_rx.G_ulsch > 0) { + for (uint32_t i = 0; i < pusch_rx.G_ulsch; i++) { + uint8_t rx_bit = (((int8_t*)pusch_rx.g_ulsch)[i]) < 0 ? 1 : 0; + if (rx_bit == 0) { + pusch_rx.g_ulsch[i] = pusch_tx.g_ulsch[i]; + } else { + pusch_rx.g_ulsch[i] = rx_bit; + } + } + if (memcmp(pusch_tx.g_ulsch, pusch_rx.g_ulsch, pusch_tx.G_ulsch) != 0) { + printf("g_ulsch_tx="); + srsran_vec_fprint_byte(stdout, pusch_tx.g_ulsch, pusch_tx.G_ulsch); + printf("g_ulsch_rx="); + srsran_vec_fprint_byte(stdout, pusch_rx.g_ulsch, pusch_tx.G_ulsch); + // srsran_vec_fprint_bs(stdout, (int8_t*)pusch_rx.g_ulsch, pusch_rx.G_ulsch); + + goto clean_exit; } - goto clean_exit; } // Validate UL-SCH CRC check @@ -359,7 +369,7 @@ int main(int argc, char** argv) } } - if (srsran_verbose >= SRSRAN_VERBOSE_INFO) { + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) { char str[512]; srsran_pusch_nr_rx_info(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &data_rx, str, (uint32_t)sizeof(str)); diff --git a/lib/src/phy/phch/test/pusch_test.c b/lib/src/phy/phch/test/pusch_test.c index 791339cea..623769e3d 100644 --- a/lib/src/phy/phch/test/pusch_test.c +++ b/lib/src/phy/phch/test/pusch_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -170,7 +170,7 @@ void parse_args(int argc, char** argv) optind++; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -182,19 +182,21 @@ void parse_args(int argc, char** argv) int main(int argc, char** argv) { srsran_random_t random_h = srsran_random_init(0); - srsran_chest_ul_res_t chest_res; - srsran_pusch_t pusch_tx; - srsran_pusch_t pusch_rx; + srsran_chest_ul_res_t chest_res = {}; + srsran_pusch_t pusch_tx = {}; + srsran_pusch_t pusch_rx = {}; uint8_t* data = NULL; uint8_t* data_rx = NULL; cf_t* sf_symbols = NULL; int ret = -1; struct timeval t[3]; - srsran_pusch_cfg_t cfg; - srsran_softbuffer_tx_t softbuffer_tx; - srsran_softbuffer_rx_t softbuffer_rx; + srsran_pusch_cfg_t cfg = {}; + srsran_softbuffer_tx_t softbuffer_tx = {}; + srsran_softbuffer_rx_t softbuffer_rx = {}; + srsran_crc_t crc_tb; ZERO_OBJECT(uci_data_tx); + ZERO_OBJECT(crc_tb); bzero(&cfg, sizeof(srsran_pusch_cfg_t)); @@ -292,9 +294,12 @@ int main(int argc, char** argv) srsran_softbuffer_tx_reset(&softbuffer_tx); srsran_softbuffer_rx_reset(&softbuffer_rx); + // Generate random data for (uint32_t i = 0; i < cfg.grant.tb.tbs / 8; i++) { data[i] = (uint8_t)srsran_random_uniform_int_dist(random_h, 0, 255); } + // Attach CRC for making sure TB with 0 CRC are detected + srsran_crc_attach_byte(&crc_tb, data, cfg.grant.tb.tbs - 24); for (uint32_t a = 0; a < uci_data_tx.cfg.ack[0].nof_acks; a++) { uci_data_tx.value.ack.ack_value[a] = (uint8_t)srsran_random_uniform_int_dist(random_h, 0, 1); @@ -353,7 +358,7 @@ int main(int argc, char** argv) ret = SRSRAN_ERROR; } else { INFO("Rx ACK (%d bits) is Ok: ", uci_data_tx.cfg.ack[0].nof_acks); - if (srsran_verbose >= SRSRAN_VERBOSE_INFO) { + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) { srsran_vec_fprint_byte(stdout, uci_data_tx.value.ack.ack_value, uci_data_tx.cfg.ack[0].nof_acks); } } diff --git a/lib/src/phy/phch/test/ra_nr_test.c b/lib/src/phy/phch/test/ra_nr_test.c index b089d56c0..cbe776d13 100644 --- a/lib/src/phy/phch/test/ra_nr_test.c +++ b/lib/src/phy/phch/test/ra_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/phch/test/sch_nr_test.c b/lib/src/phy/phch/test/sch_nr_test.c index 7cdf690d9..387365152 100644 --- a/lib/src/phy/phch/test/sch_nr_test.c +++ b/lib/src/phy/phch/test/sch_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,16 +27,7 @@ #include #include -static srsran_carrier_nr_t carrier = { - 1, // pci - 0, // absolute_frequency_ssb - 0, // absolute_frequency_point_a - 0, // offset_to_carrier - srsran_subcarrier_spacing_15kHz, // scs - SRSRAN_MAX_PRB_NR, // nof_prb - 0, // start - 1 // max_mimo_layers -}; +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; static uint32_t n_prb = 0; // Set to 0 for steering static uint32_t mcs = 30; // Set to 30 for steering @@ -80,7 +71,7 @@ int parse_args(int argc, char** argv) carrier.max_mimo_layers = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -188,12 +179,7 @@ int main(int argc, char** argv) for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) { pdsch_cfg.grant.prb_idx[n] = (n < n_prb); } - - if (srsran_ra_dl_nr_nof_dmrs_cdm_groups_without_data_format_1_0(&pdsch_cfg.dmrs, &pdsch_cfg.grant) < - SRSRAN_SUCCESS) { - ERROR("Error calculating number of DMRS CDM groups"); - goto clean_exit; - } + pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO srsran_sch_tb_t tb = {}; tb.rv = rv; diff --git a/lib/src/phy/phch/uci.c b/lib/src/phy/phch/uci.c index f14147213..d9646cc5f 100644 --- a/lib/src/phy/phch/uci.c +++ b/lib/src/phy/phch/uci.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -129,7 +129,7 @@ int16_t srsran_uci_decode_cqi_pucch(srsran_uci_cqi_pucch_t* q, uint8_t* cqi_data, uint32_t cqi_len) { - if (cqi_len < SRSRAN_UCI_MAX_CQI_LEN_PUCCH && b_bits != NULL && cqi_data != NULL) { + if (q != NULL && cqi_len < SRSRAN_UCI_MAX_CQI_LEN_PUCCH && b_bits != NULL && cqi_data != NULL) { uint32_t max_w = 0; int32_t max_corr = INT32_MIN; uint32_t nwords = 1 << SRSRAN_UCI_MAX_CQI_LEN_PUCCH; @@ -655,9 +655,10 @@ int srsran_uci_decode_ack_ri(srsran_pusch_cfg_t* cfg, uint32_t Qm = srsran_mod_bits_x_symbol(cfg->grant.tb.mod); int16_t llr_acc[32] = {}; ///< LLR accumulator - uint32_t nof_acc = - (nof_bits == 1) ? Qm : (nof_bits == 2) ? Qm * 3 : SRSRAN_FEC_BLOCK_SIZE; ///< Number of required LLR - uint32_t count_acc = 0; ///< LLR counter + uint32_t nof_acc = (nof_bits == 1) ? Qm + : (nof_bits == 2) ? Qm * 3 + : SRSRAN_FEC_BLOCK_SIZE; ///< Number of required LLR + uint32_t count_acc = 0; ///< LLR counter for (uint32_t i = 0; i < Qprime; i++) { if (is_ri) { diff --git a/lib/src/phy/phch/uci_nr.c b/lib/src/phy/phch/uci_nr.c index be0b4c92d..6dfeeef92 100644 --- a/lib/src/phy/phch/uci_nr.c +++ b/lib/src/phy/phch/uci_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -42,6 +42,40 @@ uint32_t srsran_uci_nr_crc_len(uint32_t A) return (A <= 11) ? 0 : (A < 20) ? 6 : 11; } +static inline int uci_nr_pusch_cfg_valid(const srsran_uci_nr_pusch_cfg_t* cfg) +{ + // No data pointer + if (cfg == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Unset configuration is unset + if (cfg->nof_re == 0 && cfg->nof_layers == 0 && !isnormal(cfg->R)) { + return SRSRAN_SUCCESS; + } + + // Detect invalid number of layers + if (cfg->nof_layers == 0) { + ERROR("Invalid number of layers %d", cfg->nof_layers); + return SRSRAN_ERROR; + } + + // Detect invalid number of RE + if (cfg->nof_re == 0) { + ERROR("Invalid number of RE %d", cfg->nof_re); + return SRSRAN_ERROR; + } + + // Detect invalid Rate + if (!isnormal(cfg->R)) { + ERROR("Invalid R %f", cfg->R); + return SRSRAN_ERROR; + } + + // Otherwise it is set and valid + return 1; +} + int srsran_uci_nr_init(srsran_uci_nr_t* q, const srsran_uci_nr_args_t* args) { if (q == NULL || args == NULL) { @@ -165,15 +199,15 @@ static int uci_nr_pack_ack_sr(const srsran_uci_cfg_nr_t* cfg, const srsran_uci_v int A = 0; // Append ACK bits - srsran_vec_u8_copy(&sequence[A], value->ack, cfg->o_ack); - A += cfg->o_ack; + srsran_vec_u8_copy(&sequence[A], value->ack, cfg->ack.count); + A += cfg->ack.count; // Append SR bits uint8_t* bits = &sequence[A]; srsran_bit_unpack(value->sr, &bits, cfg->o_sr); A += cfg->o_sr; - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("Packed UCI bits: "); srsran_vec_fprint_byte(stdout, sequence, A); } @@ -186,15 +220,15 @@ static int uci_nr_unpack_ack_sr(const srsran_uci_cfg_nr_t* cfg, uint8_t* sequenc int A = 0; // Append ACK bits - srsran_vec_u8_copy(value->ack, &sequence[A], cfg->o_ack); - A += cfg->o_ack; + srsran_vec_u8_copy(value->ack, &sequence[A], cfg->ack.count); + A += cfg->ack.count; // Append SR bits uint8_t* bits = &sequence[A]; value->sr = srsran_bit_pack(&bits, cfg->o_sr); A += cfg->o_sr; - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_RX("Unpacked UCI bits: "); srsran_vec_fprint_byte(stdout, sequence, A); } @@ -207,8 +241,8 @@ static int uci_nr_pack_ack_sr_csi(const srsran_uci_cfg_nr_t* cfg, const srsran_u int A = 0; // Append ACK bits - srsran_vec_u8_copy(&sequence[A], value->ack, cfg->o_ack); - A += cfg->o_ack; + srsran_vec_u8_copy(&sequence[A], value->ack, cfg->ack.count); + A += cfg->ack.count; // Append SR bits uint8_t* bits = &sequence[A]; @@ -223,7 +257,7 @@ static int uci_nr_pack_ack_sr_csi(const srsran_uci_cfg_nr_t* cfg, const srsran_u } A += n; - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("Packed UCI bits: "); srsran_vec_fprint_byte(stdout, sequence, A); } @@ -236,15 +270,15 @@ static int uci_nr_unpack_ack_sr_csi(const srsran_uci_cfg_nr_t* cfg, uint8_t* seq int A = 0; // Append ACK bits - srsran_vec_u8_copy(value->ack, &sequence[A], cfg->o_ack); - A += cfg->o_ack; + srsran_vec_u8_copy(value->ack, &sequence[A], cfg->ack.count); + A += cfg->ack.count; // Append SR bits uint8_t* bits = &sequence[A]; value->sr = srsran_bit_pack(&bits, cfg->o_sr); A += cfg->o_sr; - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_RX("Unpacked UCI bits: "); srsran_vec_fprint_byte(stdout, sequence, A); } @@ -265,17 +299,16 @@ static int uci_nr_A(const srsran_uci_cfg_nr_t* cfg) // 6.3.1.1.1 HARQ-ACK/SR only UCI bit sequence generation if (o_csi == 0) { - return cfg->o_ack + cfg->o_sr; + return cfg->ack.count + cfg->o_sr; } // 6.3.1.1.2 CSI only - if (cfg->o_ack == 0 && cfg->o_sr == 0) { + if (cfg->ack.count == 0 && cfg->o_sr == 0) { return o_csi; } // 6.3.1.1.3 HARQ-ACK/SR and CSI - ERROR("HARQ-ACK/SR and CSI encoding are not implemented"); - return SRSRAN_ERROR; + return cfg->ack.count + cfg->o_sr + o_csi; } static int uci_nr_pack_pucch(const srsran_uci_cfg_nr_t* cfg, const srsran_uci_value_nr_t* value, uint8_t* sequence) @@ -288,7 +321,7 @@ static int uci_nr_pack_pucch(const srsran_uci_cfg_nr_t* cfg, const srsran_uci_va } // 6.3.1.1.2 CSI only - if (cfg->o_ack == 0 && cfg->o_sr == 0) { + if (cfg->ack.count == 0 && cfg->o_sr == 0) { return srsran_csi_part1_pack(cfg->csi, value->csi, cfg->nof_csi, sequence, SRSRAN_UCI_NR_MAX_NOF_BITS); } @@ -306,9 +339,8 @@ static int uci_nr_unpack_pucch(const srsran_uci_cfg_nr_t* cfg, uint8_t* sequence } // 6.3.1.1.2 CSI only - if (cfg->o_ack == 0 && cfg->o_sr == 0) { - ERROR("CSI only are not implemented"); - return SRSRAN_ERROR; + if (cfg->ack.count == 0 && cfg->o_sr == 0) { + return srsran_csi_part1_unpack(cfg->csi, cfg->nof_csi, sequence, SRSRAN_UCI_NR_MAX_NOF_BITS, value->csi); } // 6.3.1.1.3 HARQ-ACK/SR and CSI @@ -370,7 +402,7 @@ static int uci_nr_encode_1bit(srsran_uci_nr_t* q, const srsran_uci_cfg_nr_t* cfg return SRSRAN_ERROR; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("One bit encoded NR-UCI; o="); srsran_vec_fprint_b(stdout, o, E); } @@ -411,7 +443,7 @@ static int uci_nr_decode_1_bit(srsran_uci_nr_t* q, // Save decoded bit q->bit_sequence[0] = (corr < 0) ? 0 : 1; - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_RX("One bit decoding NR-UCI llr="); srsran_vec_fprint_bs(stdout, llr, E); UCI_NR_INFO_RX("One bit decoding NR-UCI A=%d; E=%d; pwr=%f; corr=%f; norm=%f; thr=%f; %s", @@ -516,7 +548,7 @@ static int uci_nr_encode_2bit(srsran_uci_nr_t* q, const srsran_uci_cfg_nr_t* cfg return SRSRAN_ERROR; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("Two bit encoded NR-UCI; E=%d; o=", E); srsran_vec_fprint_b(stdout, o, E); } @@ -562,7 +594,7 @@ static int uci_nr_decode_2_bit(srsran_uci_nr_t* q, q->bit_sequence[0] = c0 ? 1 : 0; q->bit_sequence[1] = c1 ? 1 : 0; - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_RX("Two bit decoding NR-UCI llr="); srsran_vec_fprint_bs(stdout, llr, E); UCI_NR_INFO_RX("Two bit decoding NR-UCI A=%d; E=%d; Qm=%d; c0=%d; c1=%d; c2=%d %s", @@ -583,7 +615,7 @@ uci_nr_encode_3_11_bit(srsran_uci_nr_t* q, const srsran_uci_cfg_nr_t* cfg, uint3 { srsran_block_encode(q->bit_sequence, A, o, E); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("Block encoded UCI bits; o="); srsran_vec_fprint_b(stdout, o, E); } @@ -610,9 +642,11 @@ static int uci_nr_decode_3_11_bit(srsran_uci_nr_t* q, // Compute average LLR power float pwr = srsran_vec_avg_power_bf(llr, E); + + // If the power measurement is invalid (zero, NAN, INF) then consider it cannot be decoded if (!isnormal(pwr)) { - ERROR("Received all zeros"); - return SRSRAN_ERROR; + *decoded_ok = false; + return SRSRAN_SUCCESS; } // Decode @@ -624,7 +658,7 @@ static int uci_nr_decode_3_11_bit(srsran_uci_nr_t* q, // Take decoded decision with threshold *decoded_ok = (corr > q->block_code_threshold); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_RX("Block decoding NR-UCI llr="); srsran_vec_fprint_bs(stdout, llr, E); UCI_NR_INFO_RX("Block decoding NR-UCI A=%d; E=%d; pwr=%f; corr=%f; norm=%f; thr=%f; %s", @@ -688,7 +722,7 @@ uci_nr_encode_11_1706_bit(srsran_uci_nr_t* q, const srsran_uci_cfg_nr_t* cfg, ui srsran_crc_attach(crc, q->c, A_prime / C); UCI_NR_INFO_TX("Attaching %d/%d CRC%d=%" PRIx64, r, C, L, srsran_crc_checksum_get(crc)); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("Polar cb %d/%d c=", r, C); srsran_vec_fprint_byte(stdout, q->c, K_r); } @@ -696,7 +730,7 @@ uci_nr_encode_11_1706_bit(srsran_uci_nr_t* q, const srsran_uci_cfg_nr_t* cfg, ui // Allocate channel srsran_polar_chanalloc_tx(q->c, q->allocated, q->code.N, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("Polar alloc %d/%d ", r, C); srsran_vec_fprint_byte(stdout, q->allocated, q->code.N); } @@ -706,7 +740,7 @@ uci_nr_encode_11_1706_bit(srsran_uci_nr_t* q, const srsran_uci_cfg_nr_t* cfg, ui return SRSRAN_ERROR; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("Polar encoded %d/%d ", r, C); srsran_vec_fprint_byte(stdout, q->d, q->code.N); } @@ -714,7 +748,7 @@ uci_nr_encode_11_1706_bit(srsran_uci_nr_t* q, const srsran_uci_cfg_nr_t* cfg, ui // Rate matching srsran_polar_rm_tx(&q->rm_tx, q->d, &o[E_r * r], q->code.n, E_r, K_r, UCI_NR_POLAR_RM_IBIL); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_TX("Polar RM cw %d/%d ", r, C); srsran_vec_fprint_byte(stdout, &o[E_r * r], E_r); } @@ -765,7 +799,7 @@ static int uci_nr_decode_11_1706_bit(srsran_uci_nr_t* q, for (uint32_t r = 0, s = 0; r < C; r++) { uint32_t k = 0; - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_RX("Polar LLR %d/%d ", r, C); srsran_vec_fprint_bs(stdout, &llr[E_r * r], q->code.N); } @@ -780,7 +814,7 @@ static int uci_nr_decode_11_1706_bit(srsran_uci_nr_t* q, return SRSRAN_ERROR; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_RX("Polar alloc %d/%d ", r, C); srsran_vec_fprint_byte(stdout, q->allocated, q->code.N); } @@ -788,7 +822,7 @@ static int uci_nr_decode_11_1706_bit(srsran_uci_nr_t* q, // Undo channel allocation srsran_polar_chanalloc_rx(q->allocated, q->c, q->code.K, q->code.nPC, q->code.K_set, q->code.PC_set); - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { UCI_NR_INFO_RX("Polar cb %d/%d c=", r, C); srsran_vec_fprint_byte(stdout, q->c, K_r); } @@ -989,16 +1023,25 @@ uint32_t srsran_uci_nr_total_bits(const srsran_uci_cfg_nr_t* uci_cfg) return 0; } - return uci_cfg->o_ack + uci_cfg->o_sr + srsran_csi_part1_nof_bits(uci_cfg->csi, uci_cfg->nof_csi); + uint32_t o_csi = srsran_csi_part1_nof_bits(uci_cfg->csi, uci_cfg->nof_csi); + + // According to 38.213 9.2.4 UE procedure for reporting SR + // The UE transmits a PUCCH in the PUCCH resource for the corresponding SR configuration only when the UE transmits a + // positive SR + if (uci_cfg->ack.count == 0 && o_csi == 0 && !uci_cfg->sr_positive_present) { + return 0; + } + + return uci_cfg->ack.count + uci_cfg->o_sr + o_csi; } uint32_t srsran_uci_nr_info(const srsran_uci_data_nr_t* uci_data, char* str, uint32_t str_len) { uint32_t len = 0; - if (uci_data->cfg.o_ack > 0) { + if (uci_data->cfg.ack.count > 0) { char str_ack[10]; - srsran_vec_sprint_bin(str_ack, (uint32_t)sizeof(str_ack), uci_data->value.ack, uci_data->cfg.o_ack); + srsran_vec_sprint_bin(str_ack, (uint32_t)sizeof(str_ack), uci_data->value.ack, uci_data->cfg.ack.count); len = srsran_print_check(str, str_len, len, "ack=%s ", str_ack); } @@ -1015,10 +1058,6 @@ uint32_t srsran_uci_nr_info(const srsran_uci_data_nr_t* uci_data, char* str, uin static int uci_nr_pusch_Q_prime_ack(const srsran_uci_nr_pusch_cfg_t* cfg, uint32_t O_ack) { - if (cfg == NULL) { - return SRSRAN_ERROR_INVALID_INPUTS; - } - uint32_t L_ack = srsran_uci_nr_crc_len(O_ack); // Number of CRC bits uint32_t Qm = srsran_mod_bits_x_symbol(cfg->modulation); // modulation order of the PUSCH @@ -1046,14 +1085,15 @@ static int uci_nr_pusch_Q_prime_ack(const srsran_uci_nr_pusch_cfg_t* cfg, uint32 int srsran_uci_nr_pusch_ack_nof_bits(const srsran_uci_nr_pusch_cfg_t* cfg, uint32_t O_ack) { - // Check inputs - if (cfg == NULL) { + // Validate configuration + int err = uci_nr_pusch_cfg_valid(cfg); + if (err < SRSRAN_SUCCESS) { return SRSRAN_ERROR_INVALID_INPUTS; } - if (cfg->nof_layers == 0) { - ERROR("Invalid number of layers (%d)", cfg->nof_layers); - return SRSRAN_ERROR; + // Configuration is unset + if (err == 0) { + return 0; } int Q_ack_prime = uci_nr_pusch_Q_prime_ack(cfg, O_ack); @@ -1075,7 +1115,7 @@ int srsran_uci_nr_encode_pusch_ack(srsran_uci_nr_t* q, return SRSRAN_ERROR_INVALID_INPUTS; } - int A = cfg->o_ack; + int A = cfg->ack.count; // 6.3.2.1 UCI bit sequence generation // 6.3.2.1.1 HARQ-ACK @@ -1088,7 +1128,7 @@ int srsran_uci_nr_encode_pusch_ack(srsran_uci_nr_t* q, UCI_NR_INFO_TX("No HARQ-ACK to mux"); return SRSRAN_SUCCESS; } else { - srsran_vec_u8_copy(q->bit_sequence, value->ack, cfg->o_ack); + srsran_vec_u8_copy(q->bit_sequence, value->ack, cfg->ack.count); } // Compute total of encoded bits according to 6.3.2.4 Rate matching @@ -1111,12 +1151,12 @@ int srsran_uci_nr_decode_pusch_ack(srsran_uci_nr_t* q, return SRSRAN_ERROR_INVALID_INPUTS; } - int A = cfg->o_ack; + int A = cfg->ack.count; // 6.3.2.1 UCI bit sequence generation // 6.3.2.1.1 HARQ-ACK bool has_csi_part2 = srsran_csi_has_part2(cfg->csi, cfg->nof_csi); - if (cfg->pusch.K_sum == 0 && cfg->nof_csi > 1 && !has_csi_part2 && cfg->o_ack < 2) { + if (cfg->pusch.K_sum == 0 && cfg->nof_csi > 1 && !has_csi_part2 && cfg->ack.count < 2) { A = 2; } @@ -1177,17 +1217,23 @@ static int uci_nr_pusch_Q_prime_csi1(const srsran_uci_nr_pusch_cfg_t* cfg, uint3 int srsran_uci_nr_pusch_csi1_nof_bits(const srsran_uci_cfg_nr_t* cfg) { - // Check inputs - if (cfg == NULL) { + // Validate configuration + int err = uci_nr_pusch_cfg_valid(&cfg->pusch); + if (err < SRSRAN_SUCCESS) { return SRSRAN_ERROR_INVALID_INPUTS; } + // Configuration is unset + if (err == 0) { + return 0; + } + int O_csi1 = srsran_csi_part1_nof_bits(cfg->csi, cfg->nof_csi); if (O_csi1 < SRSRAN_SUCCESS) { ERROR("Errpr calculating CSI part 1 number of bits"); return SRSRAN_ERROR; } - uint32_t O_ack = SRSRAN_MAX(2, cfg->o_ack); + uint32_t O_ack = SRSRAN_MAX(2, cfg->ack.count); int Q_csi1_prime = uci_nr_pusch_Q_prime_csi1(&cfg->pusch, (uint32_t)O_csi1, O_ack); if (Q_csi1_prime < SRSRAN_SUCCESS) { diff --git a/lib/src/phy/resampling/CMakeLists.txt b/lib/src/phy/resampling/CMakeLists.txt index f6a43d6e9..a4636ce49 100644 --- a/lib/src/phy/resampling/CMakeLists.txt +++ b/lib/src/phy/resampling/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/resampling/decim.c b/lib/src/phy/resampling/decim.c index 92a4fd579..1ba3dc3b9 100644 --- a/lib/src/phy/resampling/decim.c +++ b/lib/src/phy/resampling/decim.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/resampling/interp.c b/lib/src/phy/resampling/interp.c index 31a013431..6d1bc3d35 100644 --- a/lib/src/phy/resampling/interp.c +++ b/lib/src/phy/resampling/interp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/resampling/resample_arb.c b/lib/src/phy/resampling/resample_arb.c index 56cf250e7..15cfd66d3 100644 --- a/lib/src/phy/resampling/resample_arb.c +++ b/lib/src/phy/resampling/resample_arb.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -152,11 +152,7 @@ int srsran_resample_arb_compute(srsran_resample_arb_t* q, cf_t* input, cf_t* out filter_input, srsran_resample_arb_polyfilt[(idx + 1) % SRSRAN_RESAMPLE_ARB_N], SRSRAN_RESAMPLE_ARB_M); } - if (idx == SRSRAN_RESAMPLE_ARB_N) { - *output = res1; - } else { - *output = (q->interpolate) ? (res1 + (res2 - res1) * frac) : res1; - } + *output = (q->interpolate) ? (res1 + (res2 - res1) * frac) : res1; output++; n_out++; diff --git a/lib/src/phy/resampling/resampler.c b/lib/src/phy/resampling/resampler.c index 1a3eac01d..760a21e44 100644 --- a/lib/src/phy/resampling/resampler.c +++ b/lib/src/phy/resampling/resampler.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,6 +34,11 @@ */ #define RESAMPLER_BETA 0.45 +/** + * Filter delay in multiples of ratio + */ +#define RESAMPLER_DELAY 7 + /** * The FFT size power is determined from the ratio logarithm in base 2 plus the following parameter */ @@ -70,21 +75,26 @@ int srsran_resampler_fft_init(srsran_resampler_fft_t* q, srsran_resampler_mode_t uint32_t output_fft_size = 0; uint32_t high_size = base_size * ratio; + // Select FFT/IFFT sizes filter delay and window size. For best performance and avoid aliasing, the window size shall + // be as big as the input DFT subtracting the filter length at the input rate switch (mode) { case SRSRAN_RESAMPLER_MODE_INTERPOLATE: input_fft_size = base_size; output_fft_size = high_size; + q->delay = RESAMPLER_DELAY * ratio; + q->window_sz = input_fft_size - 2 * RESAMPLER_DELAY; break; case SRSRAN_RESAMPLER_MODE_DECIMATE: default: input_fft_size = high_size; output_fft_size = base_size; + q->delay = RESAMPLER_DELAY * ratio; + q->window_sz = input_fft_size - 2 * q->delay; break; } - q->mode = mode; - q->ratio = ratio; - q->window_sz = input_fft_size / 4; + q->mode = mode; + q->ratio = ratio; q->in_buffer = srsran_vec_cf_malloc(high_size); if (q->in_buffer == NULL) { @@ -120,11 +130,20 @@ int srsran_resampler_fft_init(srsran_resampler_fft_t* q, srsran_resampler_mode_t return SRSRAN_ERROR; } + // Calculate absolute filter delay + double delay = (double)q->delay; + if (mode == SRSRAN_RESAMPLER_MODE_INTERPOLATE) { + delay = (double)(high_size - q->delay); + } + // Compute time domain filter coefficients, see raised cosine formula in section "1.2 Impulse Response" of // https://dspguru.com/dsp/reference/raised-cosine-and-root-raised-cosine-formulas/ double T = (double)1.0; for (int32_t i = 0; i < high_size; i++) { - double t = ((double)i - (double)high_size / 2.0) / (double)ratio; + // Convert to time + double t = ((double)i - delay) / (double)ratio; + + // Compute coefficient double h = 1.0 / T; if (isnormal(t)) { h = sin(M_PI * t / T); @@ -135,6 +154,11 @@ int srsran_resampler_fft_init(srsran_resampler_fft_t* q, srsran_resampler_mode_t q->in_buffer[i] = (float)h; } + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("h_%s=", q->mode == SRSRAN_RESAMPLER_MODE_INTERPOLATE ? "interp" : "decimate"); + srsran_vec_fprint_c(stdout, q->in_buffer, high_size); + } + // Compute frequency domain coefficients, since the filter is symmetrical, it does not matter whether FFT or iFFT if (mode == SRSRAN_RESAMPLER_MODE_INTERPOLATE) { srsran_dft_run_guru_c(&q->ifft); @@ -179,14 +203,11 @@ static void resampler_fft_interpolate(srsran_resampler_fft_t* q, const cf_t* inp // Execute FFT srsran_dft_run_guru_c(&q->fft); - // Replicate input spectrum - for (uint32_t i = 1; i < q->ratio; i++) { - srsran_vec_cf_copy(&q->out_buffer[q->fft.size * i], q->out_buffer, q->fft.size); + // Replicate input spectrum and filter at same time + for (uint32_t i = 0; i < q->ratio; i++) { + srsran_vec_prod_ccc(q->out_buffer, &q->filter[q->fft.size * i], &q->in_buffer[q->fft.size * i], q->fft.size); } - // Apply filtering - srsran_vec_prod_ccc(q->out_buffer, q->filter, q->in_buffer, q->ifft.size); - // Execute iFFT srsran_dft_run_guru_c(&q->ifft); } else { @@ -222,29 +243,27 @@ static void resampler_fft_decimate(srsran_resampler_fft_t* q, const cf_t* input, while (count < nsamples) { uint32_t n = SRSRAN_MIN(q->window_sz, nsamples - count); - if (input) { - // Copy input samples - srsran_vec_cf_copy(q->in_buffer, &input[count], q->window_sz); + // Copy input samples + srsran_vec_cf_copy(q->in_buffer, &input[count], n); - // Pad zeroes - srsran_vec_cf_zero(&q->in_buffer[n], q->fft.size - n); + // Pad zeroes + srsran_vec_cf_zero(&q->in_buffer[n], q->fft.size - n); - // Execute FFT - srsran_dft_run_guru_c(&q->fft); + // Execute FFT + srsran_dft_run_guru_c(&q->fft); - // Apply filtering and cut - srsran_vec_prod_ccc(q->out_buffer, q->filter, q->in_buffer, q->ifft.size / 2); - srsran_vec_prod_ccc(&q->out_buffer[q->fft.size - q->ifft.size / 2], - &q->filter[q->fft.size - q->ifft.size / 2], - &q->in_buffer[q->ifft.size / 2], - q->ifft.size / 2); + // Apply filter + srsran_vec_prod_ccc(q->out_buffer, q->filter, q->out_buffer, q->fft.size); - // Execute iFFT - srsran_dft_run_guru_c(&q->ifft); - } else { - srsran_vec_cf_zero(q->out_buffer, q->ifft.size); + // Decimate + srsran_vec_cf_copy(q->in_buffer, q->out_buffer, q->ifft.size); + for (uint32_t i = 1; i < q->ratio; i++) { + srsran_vec_sum_ccc(&q->out_buffer[q->ifft.size * i], q->in_buffer, q->in_buffer, q->ifft.size); } + // Execute iFFT + srsran_dft_run_guru_c(&q->ifft); + // Add previous state srsran_vec_sum_ccc(q->out_buffer, q->state, q->out_buffer, q->state_len); @@ -316,5 +335,5 @@ uint32_t srsran_resampler_fft_get_delay(srsran_resampler_fft_t* q) return UINT32_MAX; } - return q->ifft.size / 2; -} \ No newline at end of file + return q->delay; +} diff --git a/lib/src/phy/resampling/test/CMakeLists.txt b/lib/src/phy/resampling/test/CMakeLists.txt index 9347759c8..b08cb5d78 100644 --- a/lib/src/phy/resampling/test/CMakeLists.txt +++ b/lib/src/phy/resampling/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/resampling/test/resample_arb_bench.c b/lib/src/phy/resampling/test/resample_arb_bench.c index 208392540..b4cebf389 100644 --- a/lib/src/phy/resampling/test/resample_arb_bench.c +++ b/lib/src/phy/resampling/test/resample_arb_bench.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/resampling/test/resample_arb_test.c b/lib/src/phy/resampling/test/resample_arb_test.c index 3100e9c97..478efca27 100644 --- a/lib/src/phy/resampling/test/resample_arb_test.c +++ b/lib/src/phy/resampling/test/resample_arb_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/resampling/test/resampler_test.c b/lib/src/phy/resampling/test/resampler_test.c index 08ace27ad..c43e0291a 100644 --- a/lib/src/phy/resampling/test/resampler_test.c +++ b/lib/src/phy/resampling/test/resampler_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,6 +19,7 @@ * */ +#include "srsran/phy/channel/ch_awgn.h" #include "srsran/phy/resampling/resampler.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" @@ -29,12 +30,19 @@ static uint32_t buffer_size = 1920; static uint32_t factor = 2; static uint32_t repetitions = 2; +static enum { + WAVE_SINE = 0, + WAVE_DELTA, + WAVE_STEP, + WAVE_GAUSS, +} wave = WAVE_SINE; static void usage(char* prog) { printf("Usage: %s [sfr]\n", prog); printf("\t-s Buffer size [Default %d]\n", buffer_size); - printf("\t-f Buffer size [Default %d]\n", factor); + printf("\t-f Interpolation/Decimation factor [Default %d]\n", factor); + printf("\t-w Wave type: sine, step, delta [Default sine]\n"); printf("\t-f r [Default %d]\n", repetitions); } @@ -42,7 +50,7 @@ static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "sfr")) != -1) { + while ((opt = getopt(argc, argv, "sfrvw")) != -1) { switch (opt) { case 's': buffer_size = (uint32_t)strtol(argv[optind], NULL, 10); @@ -53,6 +61,33 @@ static void parse_args(int argc, char** argv) case 'r': repetitions = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'v': + increase_srsran_verbose_level(); + break; + case 'w': + if (strcmp(argv[optind], "sine") == 0) { + wave = WAVE_SINE; + break; + } + + if (strcmp(argv[optind], "delta") == 0) { + wave = WAVE_DELTA; + break; + } + + if (strcmp(argv[optind], "step") == 0) { + wave = WAVE_STEP; + break; + } + + if (strcmp(argv[optind], "gauss") == 0) { + wave = WAVE_GAUSS; + break; + } + + printf("Invalid wave '%s'\n", argv[optind]); + usage(argv[0]); + break; default: usage(argv[0]); exit(-1); @@ -65,6 +100,7 @@ int main(int argc, char** argv) struct timeval t[3] = {}; srsran_resampler_fft_t interp = {}; srsran_resampler_fft_t decim = {}; + srsran_channel_awgn_t awgn = {}; parse_args(argc, argv); @@ -81,7 +117,31 @@ int main(int argc, char** argv) } srsran_vec_cf_zero(src, buffer_size); - srsran_vec_gen_sine(1.0f, 0.01f, src, buffer_size / 10); + + switch (wave) { + case WAVE_SINE: + srsran_vec_gen_sine(1.0f, 0.01f, src, buffer_size / 2); + break; + case WAVE_DELTA: + src[0] = 1.0f; + break; + case WAVE_STEP: + for (uint32_t i = 0; i < buffer_size; i++) { + src[i] = 1.0f; + } + break; + case WAVE_GAUSS: + srsran_channel_awgn_init(&awgn, 0); + srsran_channel_awgn_set_n0(&awgn, 0); + srsran_channel_awgn_run_c(&awgn, src, src, buffer_size); + srsran_channel_awgn_free(&awgn); + break; + } + + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("signal="); + srsran_vec_fprint_c(stdout, src, buffer_size); + } gettimeofday(&t[1], NULL); for (uint32_t r = 0; r < repetitions; r++) { @@ -91,22 +151,26 @@ int main(int argc, char** argv) gettimeofday(&t[2], NULL); get_time_interval(t); uint64_t duration_us = (uint64_t)(t[0].tv_sec * 1000000UL + t[0].tv_usec); - printf("Done %.1f Msps\n", factor * buffer_size * repetitions / (double)duration_us); - // printf("interp="); - // srsran_vec_fprint_c(stdout, interpolated, buffer_size * factor); + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("interp="); + srsran_vec_fprint_c(stdout, interpolated, buffer_size * factor); + printf("decim="); + srsran_vec_fprint_c(stdout, decimated, buffer_size); + } // Check error - uint32_t delay = srsran_resampler_fft_get_delay(&decim) * 2; + uint32_t delay = (srsran_resampler_fft_get_delay(&decim) + srsran_resampler_fft_get_delay(&interp)) / factor; uint32_t nsamples = buffer_size - delay; srsran_vec_sub_ccc(src, &decimated[delay], interpolated, nsamples); float mse = sqrtf(srsran_vec_avg_power_cf(interpolated, nsamples)); - printf("MSE: %f\n", mse); - // printf("src="); - // srsran_vec_fprint_c(stdout, src, nsamples); - // printf("decim="); - // srsran_vec_fprint_c(stdout, &decimated[delay], nsamples); + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { + printf("recovered="); + srsran_vec_fprint_c(stdout, &decimated[delay], nsamples); + } + + printf("Done %.1f Msps; MSE: %.6f\n", factor * buffer_size * repetitions / (double)duration_us, mse); srsran_resampler_fft_free(&interp); srsran_resampler_fft_free(&decim); diff --git a/lib/src/phy/rf/CMakeLists.txt b/lib/src/phy/rf/CMakeLists.txt index a412b90fb..71b5b54cb 100644 --- a/lib/src/phy/rf/CMakeLists.txt +++ b/lib/src/phy/rf/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -23,13 +23,32 @@ if(RF_FOUND) add_library(srsran_rf_utils STATIC rf_utils.c) target_link_libraries(srsran_rf_utils srsran_phy) - # Include common RF files + # Top-level RF library sources set(SOURCES_RF "") list(APPEND SOURCES_RF rf_imp.c) - if (UHD_FOUND) + # Lists of static (builtin) and dynamic RF plugins + set(STATIC_PLUGINS "") + set(DYNAMIC_PLUGINS "") + + if (ENABLE_RF_PLUGINS) + add_definitions(-DENABLE_RF_PLUGINS) + endif (ENABLE_RF_PLUGINS) + + # RF plugins + if (UHD_FOUND AND ENABLE_UHD) add_definitions(-DENABLE_UHD) - list(APPEND SOURCES_RF rf_uhd_imp.cc) + set(SOURCES_UHD rf_uhd_imp.cc) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_uhd SHARED ${SOURCES_UHD}) + set_target_properties(srsran_rf_uhd PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_uhd) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_uhd STATIC ${SOURCES_UHD}) + list(APPEND STATIC_PLUGINS srsran_rf_uhd) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_uhd srsran_rf_utils srsran_phy ${UHD_LIBRARIES} ${Boost_LIBRARIES}) + install(TARGETS srsran_rf_uhd DESTINATION ${LIBRARY_DIR} OPTIONAL) # If found, add a macro to inform the UHD driver about the available feature if (UHD_ENABLE_X300_FW_RESET) @@ -41,51 +60,123 @@ if(RF_FOUND) if (UHD_ENABLE_CUSTOM_RFNOC) add_definitions(-DUHD_ENABLE_CUSTOM_RFNOC) endif(UHD_ENABLE_CUSTOM_RFNOC) - endif (UHD_FOUND) + endif (UHD_FOUND AND ENABLE_UHD) + if (BLADERF_FOUND AND ENABLE_BLADERF) + add_definitions(-DENABLE_BLADERF) + set(SOURCES_BLADE rf_blade_imp.c) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_blade SHARED ${SOURCES_BLADE}) + set_target_properties(srsran_rf_blade PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_blade) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_blade STATIC ${SOURCES_BLADE}) + list(APPEND STATIC_PLUGINS srsran_rf_blade) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_blade srsran_rf_utils srsran_phy ${BLADERF_LIBRARIES}) + install(TARGETS srsran_rf_blade DESTINATION ${LIBRARY_DIR} OPTIONAL) + endif (BLADERF_FOUND AND ENABLE_BLADERF) + + if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) + add_definitions(-DENABLE_SOAPYSDR) + set(SOURCES_SOAPY rf_soapy_imp.c) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_soapy SHARED ${SOURCES_SOAPY}) + set_target_properties(srsran_rf_soapy PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_soapy) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_soapy STATIC ${SOURCES_SOAPY}) + list(APPEND STATIC_PLUGINS srsran_rf_soapy) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_soapy srsran_rf_utils srsran_phy ${SOAPYSDR_LIBRARIES}) + install(TARGETS srsran_rf_soapy DESTINATION ${LIBRARY_DIR} OPTIONAL) + endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) + + if(SKIQ_FOUND AND ENABLE_SKIQ) + add_executable(skiq_pps_test skiq_pps_test.c) + target_link_libraries(skiq_pps_test ${SKIQ_LIBRARIES} rt pthread m) + add_definitions(-DENABLE_SIDEKIQ) + set(SOURCES_SKIQ rf_skiq_imp.c rf_skiq_imp_card.c rf_skiq_imp_port.c) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_skiq SHARED ${SOURCES_SKIQ}) + set_target_properties(srsran_rf_skiq PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_skiq) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_skiq STATIC ${SOURCES_SKIQ}) + list(APPEND STATIC_PLUGINS srsran_rf_skiq) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_skiq srsran_rf_utils srsran_phy ${SKIQ_LIBRARIES} rt) + install(TARGETS srsran_rf_skiq DESTINATION ${LIBRARY_DIR} OPTIONAL) + endif(SKIQ_FOUND AND ENABLE_SKIQ) + + if (ZEROMQ_FOUND AND ENABLE_ZEROMQ) + add_definitions(-DENABLE_ZEROMQ) + set(SOURCES_ZMQ rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c) + if (ENABLE_RF_PLUGINS) + add_library(srsran_rf_zmq SHARED ${SOURCES_ZMQ}) + set_target_properties(srsran_rf_zmq PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + list(APPEND DYNAMIC_PLUGINS srsran_rf_zmq) + else (ENABLE_RF_PLUGINS) + add_library(srsran_rf_zmq STATIC ${SOURCES_ZMQ}) + list(APPEND STATIC_PLUGINS srsran_rf_zmq) + endif (ENABLE_RF_PLUGINS) + target_link_libraries(srsran_rf_zmq srsran_rf_utils srsran_phy ${ZEROMQ_LIBRARIES}) + install(TARGETS srsran_rf_zmq DESTINATION ${LIBRARY_DIR} OPTIONAL) + endif (ZEROMQ_FOUND AND ENABLE_ZEROMQ) + + # Add sources of file-based RF directly to the RF library (not as a plugin) + list(APPEND SOURCES_RF rf_file_imp.c rf_file_imp_tx.c rf_file_imp_rx.c) + + # Top-level RF library + add_library(srsran_rf_object OBJECT ${SOURCES_RF}) + set_property(TARGET srsran_rf_object PROPERTY POSITION_INDEPENDENT_CODE 1) + set(TOP_RF_LIBS) + if (ENABLE_RF_PLUGINS) + # Build as shared library with optional RF plugins (optional dependencies) + if (DYNAMIC_PLUGINS) + add_dependencies(srsran_rf_object ${DYNAMIC_PLUGINS}) + endif (DYNAMIC_PLUGINS) + add_library(srsran_rf SHARED $) + target_link_libraries(srsran_rf dl) + list(APPEND TOP_RF_LIBS srsran_rf) + + # Add $ORIGIN (i.e. current location of this library) to rpath of srsran_rf. + # This ensures that it will find the plugins that reside in the same directory as the library + set_target_properties(srsran_rf PROPERTIES BUILD_RPATH "\$ORIGIN/") + set_target_properties(srsran_rf PROPERTIES INSTALL_RPATH "\$ORIGIN/") + else (ENABLE_RF_PLUGINS) + # Build as static library with built-in RF plugins (mandatory dependencies) + add_library(srsran_rf STATIC $) + target_link_libraries(srsran_rf ${STATIC_PLUGINS}) + list(APPEND TOP_RF_LIBS srsran_rf) + + # Also build as shared library with built-in RF plugins (mandatory dependencies) + add_library(srsran_rf_shared SHARED $) + target_link_libraries(srsran_rf_shared ${STATIC_PLUGINS}) + list(APPEND TOP_RF_LIBS srsran_rf_shared) + set_target_properties(srsran_rf_shared PROPERTIES OUTPUT_NAME srsran_rf) + endif (ENABLE_RF_PLUGINS) + + foreach (TOP_RF_LIB ${TOP_RF_LIBS}) + target_link_libraries(${TOP_RF_LIB} srsran_rf_utils srsran_phy) + set_target_properties(${TOP_RF_LIB} PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) + install(TARGETS ${TOP_RF_LIB} DESTINATION ${LIBRARY_DIR} OPTIONAL) + endforeach () + + # Tests if (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC) add_executable(rfnoc_test rfnoc_test.cc) target_link_libraries(rfnoc_test srsran_rf ${UHD_LIBRARIES} ${Boost_LIBRARIES} /usr/lib/x86_64-linux-gnu/libboost_system.so) message(info ${Boost_LIBRARIES}) endif (UHD_FOUND AND UHD_ENABLE_CUSTOM_RFNOC) - if (BLADERF_FOUND) - add_definitions(-DENABLE_BLADERF) - list(APPEND SOURCES_RF rf_blade_imp.c) - endif (BLADERF_FOUND) - - if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) - add_definitions(-DENABLE_SOAPYSDR) - list(APPEND SOURCES_RF rf_soapy_imp.c) - endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) - if (ZEROMQ_FOUND) - add_definitions(-DENABLE_ZEROMQ) - list(APPEND SOURCES_RF rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c) - endif (ZEROMQ_FOUND) - - add_library(srsran_rf SHARED ${SOURCES_RF}) - target_link_libraries(srsran_rf srsran_rf_utils srsran_phy) - set_target_properties(srsran_rf PROPERTIES VERSION ${SRSRAN_VERSION_STRING} SOVERSION ${SRSRAN_SOVERSION}) - - if (UHD_FOUND) - target_link_libraries(srsran_rf ${UHD_LIBRARIES}) - endif (UHD_FOUND) - - if (BLADERF_FOUND) - target_link_libraries(srsran_rf ${BLADERF_LIBRARIES}) - endif (BLADERF_FOUND) - - if (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) - target_link_libraries(srsran_rf ${SOAPYSDR_LIBRARIES}) - endif (SOAPYSDR_FOUND AND ENABLE_SOAPYSDR) - - if (ZEROMQ_FOUND) - target_link_libraries(srsran_rf ${ZEROMQ_LIBRARIES}) add_executable(rf_zmq_test rf_zmq_test.c) target_link_libraries(rf_zmq_test srsran_rf) #add_test(rf_zmq_test rf_zmq_test) endif (ZEROMQ_FOUND) - INSTALL(TARGETS srsran_rf DESTINATION ${LIBRARY_DIR}) + add_executable(rf_file_test rf_file_test.c) + target_link_libraries(rf_file_test srsran_rf) + add_test(rf_file_test rf_file_test) endif(RF_FOUND) diff --git a/lib/src/phy/rf/rf_blade_imp.c b/lib/src/phy/rf/rf_blade_imp.c index b5160ba1b..f40119a52 100644 --- a/lib/src/phy/rf/rf_blade_imp.c +++ b/lib/src/phy/rf/rf_blade_imp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,7 +24,10 @@ #include #include "rf_blade_imp.h" -#include "srsran/srsran.h" +#include "rf_plugin.h" +#include "srsran/phy/common/timestamp.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" #define UNUSED __attribute__((unused)) #define CONVERT_BUFFER_SIZE (240 * 1024) @@ -542,3 +545,44 @@ int rf_blade_send_timed(void* h, return nsamples; } + +rf_dev_t srsran_rf_dev_blade = {"bladeRF", + rf_blade_devname, + rf_blade_start_rx_stream, + rf_blade_stop_rx_stream, + rf_blade_flush_buffer, + rf_blade_has_rssi, + rf_blade_get_rssi, + rf_blade_suppress_stdout, + rf_blade_register_error_handler, + rf_blade_open, + .srsran_rf_open_multi = rf_blade_open_multi, + rf_blade_close, + rf_blade_set_rx_srate, + rf_blade_set_rx_gain, + rf_blade_set_rx_gain_ch, + rf_blade_set_tx_gain, + rf_blade_set_tx_gain_ch, + rf_blade_get_rx_gain, + rf_blade_get_tx_gain, + rf_blade_get_info, + rf_blade_set_rx_freq, + rf_blade_set_tx_srate, + rf_blade_set_tx_freq, + rf_blade_get_time, + NULL, + rf_blade_recv_with_time, + rf_blade_recv_with_time_multi, + rf_blade_send_timed, + .srsran_rf_send_timed_multi = rf_blade_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_blade; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_blade_imp.h b/lib/src/phy/rf/rf_blade_imp.h index e20a7b0bb..4605172fd 100644 --- a/lib/src/phy/rf/rf_blade_imp.h +++ b/lib/src/phy/rf/rf_blade_imp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,11 +19,16 @@ * */ +#ifndef SRSRAN_RF_BLADE_IMP_H_ +#define SRSRAN_RF_BLADE_IMP_H_ + #include "srsran/config.h" #include "srsran/phy/rf/rf.h" #define DEVNAME "bladerf" +extern rf_dev_t srsran_rf_dev_blade; + SRSRAN_API int rf_blade_open(char* args, void** handler); SRSRAN_API int rf_blade_open_multi(char* args, void** handler, uint32_t nof_channels); @@ -99,3 +104,5 @@ SRSRAN_API int rf_blade_send_timed(void* h, bool blocking, bool is_start_of_burst, bool is_end_of_burst); + +#endif /* SRSRAN_RF_BLADE_IMP_H_ */ diff --git a/lib/src/phy/rf/rf_dev.h b/lib/src/phy/rf/rf_dev.h index 647c7c5ba..705b928c6 100644 --- a/lib/src/phy/rf/rf_dev.h +++ b/lib/src/phy/rf/rf_dev.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,207 +22,58 @@ #include "srsran/phy/rf/rf.h" #include -/* RF frontend API */ -typedef struct { - const char* name; - const char* (*srsran_rf_devname)(void* h); - int (*srsran_rf_start_rx_stream)(void* h, bool now); - int (*srsran_rf_stop_rx_stream)(void* h); - void (*srsran_rf_flush_buffer)(void* h); - bool (*srsran_rf_has_rssi)(void* h); - float (*srsran_rf_get_rssi)(void* h); - void (*srsran_rf_suppress_stdout)(void* h); - void (*srsran_rf_register_error_handler)(void* h, srsran_rf_error_handler_t error_handler, void* arg); - int (*srsran_rf_open)(char* args, void** h); - int (*srsran_rf_open_multi)(char* args, void** h, uint32_t nof_channels); - int (*srsran_rf_close)(void* h); - double (*srsran_rf_set_rx_srate)(void* h, double freq); - int (*srsran_rf_set_rx_gain)(void* h, double gain); - int (*srsran_rf_set_rx_gain_ch)(void* h, uint32_t ch, double gain); - int (*srsran_rf_set_tx_gain)(void* h, double gain); - int (*srsran_rf_set_tx_gain_ch)(void* h, uint32_t ch, double gain); - double (*srsran_rf_get_rx_gain)(void* h); - double (*srsran_rf_get_tx_gain)(void* h); - srsran_rf_info_t* (*srsran_rf_get_info)(void* h); - double (*srsran_rf_set_rx_freq)(void* h, uint32_t ch, double freq); - double (*srsran_rf_set_tx_srate)(void* h, double freq); - double (*srsran_rf_set_tx_freq)(void* h, uint32_t ch, double freq); - void (*srsran_rf_get_time)(void* h, time_t* secs, double* frac_secs); - void (*srsran_rf_sync_pps)(void* h); - int (*srsran_rf_recv_with_time)(void* h, - void* data, - uint32_t nsamples, - bool blocking, - time_t* secs, - double* frac_secs); - int (*srsran_rf_recv_with_time_multi)(void* h, - void** data, - uint32_t nsamples, - bool blocking, - time_t* secs, - double* frac_secs); - int (*srsran_rf_send_timed)(void* h, - void* data, - int nsamples, - time_t secs, - double frac_secs, - bool has_time_spec, - bool blocking, - bool is_start_of_burst, - bool is_end_of_burst); - int (*srsran_rf_send_timed_multi)(void* h, - void** data, - int nsamples, - time_t secs, - double frac_secs, - bool has_time_spec, - bool blocking, - bool is_start_of_burst, - bool is_end_of_burst); -} rf_dev_t; - /* Define implementation for UHD */ #ifdef ENABLE_UHD - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_uhd = {"libsrsran_rf_uhd.so", NULL, NULL}; +#else #include "rf_uhd_imp.h" - -static rf_dev_t dev_uhd = {"UHD", - rf_uhd_devname, - rf_uhd_start_rx_stream, - rf_uhd_stop_rx_stream, - rf_uhd_flush_buffer, - rf_uhd_has_rssi, - rf_uhd_get_rssi, - rf_uhd_suppress_stdout, - rf_uhd_register_error_handler, - rf_uhd_open, - .srsran_rf_open_multi = rf_uhd_open_multi, - rf_uhd_close, - rf_uhd_set_rx_srate, - rf_uhd_set_rx_gain, - rf_uhd_set_rx_gain_ch, - rf_uhd_set_tx_gain, - rf_uhd_set_tx_gain_ch, - rf_uhd_get_rx_gain, - rf_uhd_get_tx_gain, - rf_uhd_get_info, - rf_uhd_set_rx_freq, - rf_uhd_set_tx_srate, - rf_uhd_set_tx_freq, - rf_uhd_get_time, - rf_uhd_sync_pps, - rf_uhd_recv_with_time, - rf_uhd_recv_with_time_multi, - rf_uhd_send_timed, - .srsran_rf_send_timed_multi = rf_uhd_send_timed_multi}; +static srsran_rf_plugin_t plugin_uhd = {"", NULL, &srsran_rf_dev_uhd}; +#endif #endif /* Define implementation for bladeRF */ #ifdef ENABLE_BLADERF - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_blade = {"libsrsran_rf_blade.so", NULL, NULL}; +#else #include "rf_blade_imp.h" - -static rf_dev_t dev_blade = {"bladeRF", - rf_blade_devname, - rf_blade_start_rx_stream, - rf_blade_stop_rx_stream, - rf_blade_flush_buffer, - rf_blade_has_rssi, - rf_blade_get_rssi, - rf_blade_suppress_stdout, - rf_blade_register_error_handler, - rf_blade_open, - .srsran_rf_open_multi = rf_blade_open_multi, - rf_blade_close, - rf_blade_set_rx_srate, - rf_blade_set_rx_gain, - rf_blade_set_rx_gain_ch, - rf_blade_set_tx_gain, - rf_blade_set_tx_gain_ch, - rf_blade_get_rx_gain, - rf_blade_get_tx_gain, - rf_blade_get_info, - rf_blade_set_rx_freq, - rf_blade_set_tx_srate, - rf_blade_set_tx_freq, - rf_blade_get_time, - NULL, - rf_blade_recv_with_time, - rf_blade_recv_with_time_multi, - rf_blade_send_timed, - .srsran_rf_send_timed_multi = rf_blade_send_timed_multi}; +static srsran_rf_plugin_t plugin_blade = {"", NULL, &srsran_rf_dev_blade}; +#endif #endif +/* Define implementation for SoapySDR */ #ifdef ENABLE_SOAPYSDR - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_soapy = {"libsrsran_rf_soapy.so", NULL, NULL}; +#else #include "rf_soapy_imp.h" - -static rf_dev_t dev_soapy = {"soapy", - rf_soapy_devname, - rf_soapy_start_rx_stream, - rf_soapy_stop_rx_stream, - rf_soapy_flush_buffer, - rf_soapy_has_rssi, - rf_soapy_get_rssi, - rf_soapy_suppress_stdout, - rf_soapy_register_error_handler, - rf_soapy_open, - rf_soapy_open_multi, - rf_soapy_close, - rf_soapy_set_rx_srate, - rf_soapy_set_rx_gain, - rf_soapy_set_rx_gain_ch, - rf_soapy_set_tx_gain, - rf_soapy_set_tx_gain_ch, - rf_soapy_get_rx_gain, - rf_soapy_get_tx_gain, - rf_soapy_get_info, - rf_soapy_set_rx_freq, - rf_soapy_set_tx_srate, - rf_soapy_set_tx_freq, - rf_soapy_get_time, - NULL, - rf_soapy_recv_with_time, - rf_soapy_recv_with_time_multi, - rf_soapy_send_timed, - .srsran_rf_send_timed_multi = rf_soapy_send_timed_multi}; - +static srsran_rf_plugin_t plugin_soapy = {"", NULL, &srsran_rf_dev_soapy}; +#endif #endif -/* Define implementation for UHD */ +/* Define implementation for ZeroMQ */ #ifdef ENABLE_ZEROMQ - +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_zmq = {"libsrsran_rf_zmq.so", NULL, NULL}; +#else #include "rf_zmq_imp.h" +static srsran_rf_plugin_t plugin_zmq = {"", NULL, &srsran_rf_dev_zmq}; +#endif +#endif -static rf_dev_t dev_zmq = {"zmq", - rf_zmq_devname, - rf_zmq_start_rx_stream, - rf_zmq_stop_rx_stream, - rf_zmq_flush_buffer, - rf_zmq_has_rssi, - rf_zmq_get_rssi, - rf_zmq_suppress_stdout, - rf_zmq_register_error_handler, - rf_zmq_open, - .srsran_rf_open_multi = rf_zmq_open_multi, - rf_zmq_close, - rf_zmq_set_rx_srate, - rf_zmq_set_rx_gain, - rf_zmq_set_rx_gain_ch, - rf_zmq_set_tx_gain, - rf_zmq_set_tx_gain_ch, - rf_zmq_get_rx_gain, - rf_zmq_get_tx_gain, - rf_zmq_get_info, - rf_zmq_set_rx_freq, - rf_zmq_set_tx_srate, - rf_zmq_set_tx_freq, - rf_zmq_get_time, - NULL, - rf_zmq_recv_with_time, - rf_zmq_recv_with_time_multi, - rf_zmq_send_timed, - .srsran_rf_send_timed_multi = rf_zmq_send_timed_multi}; +/* Define implementation for file-based RF */ +#include "rf_file_imp.h" +static srsran_rf_plugin_t plugin_file = {"", NULL, &srsran_rf_dev_file}; + +/* Define implementation for Sidekiq */ +#ifdef ENABLE_SIDEKIQ +#ifdef ENABLE_RF_PLUGINS +static srsran_rf_plugin_t plugin_skiq = {"libsrsran_rf_skiq.so", NULL, NULL}; +#else +#include "rf_skiq_imp.h" +static srsran_rf_plugin_t plugin_skiq = {"", NULL, &srsran_rf_dev_skiq}; +#endif #endif //#define ENABLE_DUMMY_DEV @@ -235,27 +86,36 @@ int dummy_rcv() } void dummy_fnc() {} -static rf_dev_t dev_dummy = {"dummy", dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, - dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, - dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_rcv, - dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc}; +static rf_dev_t srsran_rf_dev_dummy = { + "dummy", dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, + dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, + dummy_fnc, dummy_fnc, dummy_fnc, dummy_rcv, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc}; +static srsran_rf_plugin_t plugin_dummy = {"", NULL, &srsran_rf_dev_dummy}; + #endif -static rf_dev_t* available_devices[] = { +/** + * Collection of all currently available RF plugins + */ +static srsran_rf_plugin_t* rf_plugins[] = { #ifdef ENABLE_UHD - &dev_uhd, + &plugin_uhd, #endif #ifdef ENABLE_SOAPYSDR - &dev_soapy, + &plugin_soapy, #endif #ifdef ENABLE_BLADERF - &dev_blade, + &plugin_blade, #endif #ifdef ENABLE_ZEROMQ - &dev_zmq, + &plugin_zmq, +#endif +#ifdef ENABLE_SIDEKIQ + &plugin_skiq, #endif #ifdef ENABLE_DUMMY_DEV - &dev_dummy, + &plugin_dummy, #endif + &plugin_file, NULL}; diff --git a/lib/src/phy/rf/rf_file_imp.c b/lib/src/phy/rf/rf_file_imp.c new file mode 100644 index 000000000..c01b4f702 --- /dev/null +++ b/lib/src/phy/rf/rf_file_imp.c @@ -0,0 +1,893 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rf_file_imp.h" +#include "rf_file_imp_trx.h" +#include "rf_helper.h" +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + // Common attributes + char* devname; + srsran_rf_info_t info; + uint32_t nof_channels; + + // RF State + uint32_t srate; // radio rate configured by upper layers + uint32_t base_srate; + uint32_t decim_factor; // decimation factor between base_srate used on transport on radio's rate + double rx_gain; + uint32_t tx_freq_mhz[SRSRAN_MAX_CHANNELS]; + uint32_t rx_freq_mhz[SRSRAN_MAX_CHANNELS]; + bool tx_off; + char id[RF_PARAM_LEN]; + + // FILEs + rf_file_tx_t transmitter[SRSRAN_MAX_CHANNELS]; + rf_file_rx_t receiver[SRSRAN_MAX_CHANNELS]; + bool close_files; + + // Various sample buffers + cf_t* buffer_decimation[SRSRAN_MAX_CHANNELS]; + cf_t* buffer_tx; + + // Rx timestamp + uint64_t next_rx_ts; + + pthread_mutex_t tx_config_mutex; + pthread_mutex_t rx_config_mutex; + pthread_mutex_t decim_mutex; + pthread_mutex_t rx_gain_mutex; +} rf_file_handler_t; + +/* + * Static methods + */ + +static void update_rates(rf_file_handler_t* handler, double srate); + +void rf_file_info(char* id, const char* format, ...) +{ +#if VERBOSE + struct timeval t; + gettimeofday(&t, NULL); + va_list args; + va_start(args, format); + printf("[%s@%02ld.%06ld] ", id ? id : "file", t.tv_sec % 10, t.tv_usec); + vprintf(format, args); + va_end(args); +#else /* VERBOSE */ + // Do nothing +#endif /* VERBOSE */ +} + +void rf_file_error(char* id, const char* format, ...) +{ + struct timeval t; + gettimeofday(&t, NULL); + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +static inline int update_ts(void* h, uint64_t* ts, int nsamples, const char* dir) +{ + int ret = SRSRAN_ERROR; + + if (h && nsamples > 0) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + + (*ts) += nsamples; + + srsran_timestamp_t _ts = {}; + srsran_timestamp_init_uint64(&_ts, *ts, handler->base_srate); + rf_file_info( + handler->id, " -> next %s time after %d samples: %d + %.3f\n", dir, nsamples, _ts.full_secs, _ts.frac_secs); + + ret = SRSRAN_SUCCESS; + } + + return ret; +} + +int rf_file_handle_error(char* id, const char* text) +{ + // Not implemented + return SRSRAN_ERROR; +} + +/* + * Public methods + */ + +const char* rf_file_devname(void* h) +{ + return DEVNAME_FILE; +} + +int rf_file_start_rx_stream(void* h, bool now) +{ + return SRSRAN_SUCCESS; +} + +int rf_file_stop_rx_stream(void* h) +{ + return SRSRAN_SUCCESS; +} + +void rf_file_flush_buffer(void* h) +{ + printf("%s\n", __FUNCTION__); +} + +bool rf_file_has_rssi(void* h) +{ + return false; +} + +float rf_file_get_rssi(void* h) +{ + return 0.0; +} + +void rf_file_suppress_stdout(void* h) +{ + // do nothing +} + +void rf_file_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg) +{ + // do nothing +} + +int rf_file_open(char* args, void** h) +{ + return rf_file_open_multi(args, h, 1); +} + +int rf_file_open_multi(char* args, void** h, uint32_t nof_channels) +{ + int ret = SRSRAN_ERROR; + + FILE* rx_files[SRSRAN_MAX_CHANNELS] = {NULL}; + FILE* tx_files[SRSRAN_MAX_CHANNELS] = {NULL}; + + if (h && nof_channels <= SRSRAN_MAX_CHANNELS) { + uint32_t base_srate = FILE_BASERATE_DEFAULT_HZ; + + // parse args + if (args && strlen(args)) { + // base_srate + parse_uint32(args, "base_srate", -1, &base_srate); + } else { + fprintf(stderr, "[file] Error: RF device args are required for file-based no-RF module\n"); + goto clean_exit; + } + + for (int i = 0; i < nof_channels; i++) { + // rx_file + char rx_file[RF_PARAM_LEN] = {}; + parse_string(args, "rx_file", i, rx_file); + + // tx_file + char tx_file[RF_PARAM_LEN] = {}; + parse_string(args, "tx_file", i, tx_file); + + // initialize transmitter + if (strlen(tx_file) != 0) { + tx_files[i] = fopen(tx_file, "wb"); + if (tx_files[i] == NULL) { + fprintf(stderr, "[file] Error: opening tx_file%d: %s; %s\n", i, tx_file, strerror(errno)); + goto clean_exit; + } + } + + // initialize receiver + if (strlen(rx_file) != 0) { + rx_files[i] = fopen(rx_file, "rb"); + if (rx_files[i] == NULL) { + fprintf(stderr, "[file] Error: opening rx_file%d: %s; %s\n", i, rx_file, strerror(errno)); + goto clean_exit; + } + } + } + + // defer further initialization to open_file method + ret = rf_file_open_file(h, rx_files, tx_files, nof_channels, base_srate); + if (ret != SRSRAN_SUCCESS) { + goto clean_exit; + } + + // add flag to close all files when closing device + rf_file_handler_t* handler = (rf_file_handler_t*)(*h); + handler->close_files = true; + return ret; + } + +clean_exit: + for (int i = 0; i < nof_channels; i++) { + if (rx_files[i] != NULL) { + fclose(rx_files[i]); + } + if (tx_files[i] != NULL) { + fclose(tx_files[i]); + } + } + return ret; +} + +int rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate) +{ + int ret = SRSRAN_ERROR; + + if (h) { + *h = NULL; + + rf_file_handler_t* handler = (rf_file_handler_t*)malloc(sizeof(rf_file_handler_t)); + if (!handler) { + fprintf(stderr, "malloc: %s\n", strerror(errno)); + return SRSRAN_ERROR; + } + memset(handler, 0, sizeof(rf_file_handler_t)); + *h = handler; + handler->base_srate = base_srate; + handler->info.max_rx_gain = FILE_MAX_GAIN_DB; + handler->info.min_rx_gain = FILE_MIN_GAIN_DB; + handler->info.max_tx_gain = FILE_MAX_GAIN_DB; + handler->info.min_tx_gain = FILE_MIN_GAIN_DB; + handler->nof_channels = nof_channels; + strcpy(handler->id, "file\0"); + + rf_file_opts_t rx_opts = {}; + rf_file_opts_t tx_opts = {}; + tx_opts.id = handler->id; + rx_opts.id = handler->id; + + if (pthread_mutex_init(&handler->tx_config_mutex, NULL)) { + fprintf(stderr, "Mutex init: %s\n", strerror(errno)); + } + if (pthread_mutex_init(&handler->rx_config_mutex, NULL)) { + fprintf(stderr, "Mutex init: %s\n", strerror(errno)); + } + if (pthread_mutex_init(&handler->decim_mutex, NULL)) { + fprintf(stderr, "Mutex init: %s\n", strerror(errno)); + } + if (pthread_mutex_init(&handler->rx_gain_mutex, NULL)) { + fprintf(stderr, "Mutex init: %s\n", strerror(errno)); + } + + pthread_mutex_lock(&handler->rx_gain_mutex); + handler->rx_gain = 0.0; + pthread_mutex_unlock(&handler->rx_gain_mutex); + + // id + // TODO: set some meaningful ID in handler->id + + // rx_format, tx_format + // TODO: add other formats + rx_opts.sample_format = FILERF_TYPE_FC32; + tx_opts.sample_format = FILERF_TYPE_FC32; + + update_rates(handler, 1.92e6); + + // Create channels + for (int i = 0; i < handler->nof_channels; i++) { + if (rx_files != NULL && rx_files[i] != NULL) { + rx_opts.file = rx_files[i]; + if (rf_file_rx_open(&handler->receiver[i], rx_opts) != SRSRAN_SUCCESS) { + fprintf(stderr, "[file] Error: opening receiver\n"); + goto clean_exit; + } + } else { + // no rx_files provided + fprintf(stdout, "[file] %s rx channel %d not specified. Disabling receiver.\n", handler->id, i); + } + if (tx_files != NULL && tx_files[i] != NULL) { + tx_opts.file = tx_files[i]; + if (rf_file_tx_open(&handler->transmitter[i], tx_opts) != SRSRAN_SUCCESS) { + fprintf(stderr, "[file] Error: opening transmitter\n"); + goto clean_exit; + } + } else { + // no tx_files provided + fprintf(stdout, "[file] %s tx channel %d not specified. Disabling transmitter.\n", handler->id, i); + handler->tx_off = true; + } + + if (!handler->transmitter[i].running && !handler->receiver[i].running) { + fprintf(stderr, "[file] Error: Neither tx nor rx specificed for channel %d.\n", i); + goto clean_exit; + } + } + + // Create decimation and overflow buffer + for (uint32_t i = 0; i < handler->nof_channels; i++) { + handler->buffer_decimation[i] = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE); + if (!handler->buffer_decimation[i]) { + fprintf(stderr, "Error: allocating decimation buffer\n"); + goto clean_exit; + } + } + + handler->buffer_tx = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE); + if (!handler->buffer_tx) { + fprintf(stderr, "Error: allocating tx buffer\n"); + goto clean_exit; + } + + ret = SRSRAN_SUCCESS; + + clean_exit: + if (ret) { + rf_file_close(handler); + } + } + return ret; +} + +int rf_file_close(void* h) +{ + rf_file_handler_t* handler = (rf_file_handler_t*)h; + + rf_file_info(handler->id, "Closing ...\n"); + + // close receiver+transmitter and release related resources (except for the file handles) + for (int i = 0; i < handler->nof_channels; i++) { + rf_file_tx_close(&handler->transmitter[i]); + rf_file_rx_close(&handler->receiver[i]); + } + + // release other resources + for (uint32_t i = 0; i < handler->nof_channels; i++) { + if (handler->buffer_decimation[i]) { + free(handler->buffer_decimation[i]); + } + } + + if (handler->buffer_tx) { + free(handler->buffer_tx); + } + + pthread_mutex_destroy(&handler->tx_config_mutex); + pthread_mutex_destroy(&handler->rx_config_mutex); + pthread_mutex_destroy(&handler->decim_mutex); + pthread_mutex_destroy(&handler->rx_gain_mutex); + + // now close the files if we opened them ourselves + if (handler->close_files) { + for (int i = 0; i < handler->nof_channels; i++) { + if (handler->receiver[i].file != NULL) { + fclose(handler->receiver[i].file); + } + if (handler->transmitter[i].file != NULL) { + fclose(handler->transmitter[i].file); + } + } + } + + // Free all + free(handler); + + return SRSRAN_SUCCESS; +} + +void update_rates(rf_file_handler_t* handler, double srate) +{ + pthread_mutex_lock(&handler->decim_mutex); + if (handler) { + // Decimation must be full integer + if (((uint64_t)handler->base_srate % (uint64_t)srate) == 0) { + handler->srate = (uint32_t)srate; + handler->decim_factor = handler->base_srate / handler->srate; + } else { + fprintf(stderr, + "Error: couldn't update sample rate. %.2f is not divisible by %.2f\n", + srate / 1e6, + handler->base_srate / 1e6); + } + printf("Current sample rate is %.2f MHz with a base rate of %.2f MHz (x%d decimation)\n", + handler->srate / 1e6, + handler->base_srate / 1e6, + handler->decim_factor); + } + pthread_mutex_unlock(&handler->decim_mutex); +} + +double rf_file_set_rx_srate(void* h, double srate) +{ + double ret = 0.0; + if (h) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + update_rates(handler, srate); + ret = handler->srate; + } + return ret; +} + +int rf_file_set_rx_gain(void* h, double gain) +{ + double ret = 0.0; + if (h) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + handler->rx_gain = gain; + ret = gain; + } + return ret; +} + +int rf_file_set_rx_gain_ch(void* h, uint32_t ch, double gain) +{ + return rf_file_set_rx_gain(h, gain); +} + +int rf_file_set_tx_gain(void* h, double gain) +{ + return 0.0; +} + +int rf_file_set_tx_gain_ch(void* h, uint32_t ch, double gain) +{ + return rf_file_set_tx_gain(h, gain); +} + +double rf_file_get_rx_gain(void* h) +{ + double ret = 0.0; + if (h) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + ret = handler->rx_gain; + } + return ret; +} + +double rf_file_get_tx_gain(void* h) +{ + return 0.0; +} + +srsran_rf_info_t* rf_file_get_info(void* h) +{ + srsran_rf_info_t* info = NULL; + if (h) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + info = &handler->info; + } + return info; +} + +double rf_file_set_rx_freq(void* h, uint32_t ch, double freq) +{ + double ret = NAN; + if (h) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + pthread_mutex_lock(&handler->rx_config_mutex); + if (ch < handler->nof_channels && isnormal(freq) && freq > 0.0) { + handler->rx_freq_mhz[ch] = (uint32_t)(freq / 1e6); + ret = freq; + } + pthread_mutex_unlock(&handler->rx_config_mutex); + } + return ret; +} + +double rf_file_set_tx_srate(void* h, double srate) +{ + double ret = 0.0; + if (h) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + update_rates(handler, srate); + ret = srate; + } + return ret; +} + +double rf_file_set_tx_freq(void* h, uint32_t ch, double freq) +{ + double ret = NAN; + if (h) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + pthread_mutex_lock(&handler->tx_config_mutex); + if (ch < handler->nof_channels && isnormal(freq) && freq > 0.0) { + handler->tx_freq_mhz[ch] = (uint32_t)(freq / 1e6); + ret = freq; + } + pthread_mutex_unlock(&handler->tx_config_mutex); + } + return ret; +} + +void rf_file_get_time(void* h, time_t* secs, double* frac_secs) +{ + if (h) { + if (secs) { + *secs = 0; + } + + if (frac_secs) { + *frac_secs = 0; + } + } +} + +int rf_file_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) +{ + return rf_file_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); +} + +int rf_file_recv_with_time_multi(void* h, + void** data, + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs) +{ + int ret = SRSRAN_ERROR; + + if (h) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + + // Map ports to data buffers according to the selected frequencies + pthread_mutex_lock(&handler->rx_config_mutex); + bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used + cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched + + // For each logical channel... + for (uint32_t logical = 0; logical < handler->nof_channels; logical++) { + bool unmatched = true; + + // For each physical channel... + for (uint32_t physical = 0; physical < handler->nof_channels; physical++) { + // Consider a match if the physical channel is NOT mapped and the frequency match + if (!mapped[physical] && rf_file_rx_match_freq(&handler->receiver[physical], handler->rx_freq_mhz[logical])) { + // Not mapped and matched frequency with receiver + buffers[physical] = (cf_t*)data[logical]; + mapped[physical] = true; + unmatched = false; + break; + } + } + + // If no matching frequency found; set data to zeros + if (unmatched) { + srsran_vec_zero(data[logical], nsamples); + } + } + pthread_mutex_unlock(&handler->rx_config_mutex); + + // Protect the access to decim_factor since is a shared variable + pthread_mutex_lock(&handler->decim_mutex); + uint32_t decim_factor = handler->decim_factor; + pthread_mutex_unlock(&handler->decim_mutex); + + uint32_t nbytes = NSAMPLES2NBYTES(nsamples * decim_factor); + uint32_t nsamples_baserate = nsamples * decim_factor; + + rf_file_info(handler->id, "Rx %d samples (%d B)\n", nsamples, nbytes); + + // set timestamp for this reception + if (secs != NULL && frac_secs != NULL) { + srsran_timestamp_t ts = {}; + srsran_timestamp_init_uint64(&ts, handler->next_rx_ts, handler->base_srate); + *secs = ts.full_secs; + *frac_secs = ts.frac_secs; + } + + // return if receiver is turned off + if (!handler->receiver[0].running) { + update_ts(handler, &handler->next_rx_ts, nsamples_baserate, "rx"); + return nsamples; + } + + // Check available buffer size + if (nbytes > FILE_MAX_BUFFER_SIZE) { + fprintf(stderr, + "[file] Error: Trying to receive %d B but buffer is only %zu B at channel %d.\n", + nbytes, + FILE_MAX_BUFFER_SIZE, + 0); + goto clean_exit; + } + + // receive samples + srsran_timestamp_t ts_tx = {}, ts_rx = {}; + srsran_timestamp_init_uint64(&ts_tx, handler->transmitter[0].nsamples, handler->base_srate); + srsran_timestamp_init_uint64(&ts_rx, handler->next_rx_ts, handler->base_srate); + rf_file_info(handler->id, " - next rx time: %d + %.3f\n", ts_rx.full_secs, ts_rx.frac_secs); + rf_file_info(handler->id, " - next tx time: %d + %.3f\n", ts_tx.full_secs, ts_tx.frac_secs); + + // check for tx gap if we're also transmitting on this radio + for (int i = 0; i < handler->nof_channels; i++) { + if (handler->transmitter[i].running) { + rf_file_tx_align(&handler->transmitter[i], handler->next_rx_ts + nsamples_baserate); + } + } + + // copy from rx buffer as many samples as requested into provided buffer + bool completed = false; + int32_t count[SRSRAN_MAX_CHANNELS] = {}; + while (!completed) { + uint32_t completed_count = 0; + + // Iterate channels + for (uint32_t i = 0; i < handler->nof_channels; i++) { + cf_t* ptr = (decim_factor != 1 || buffers[i] == NULL) ? handler->buffer_decimation[i] : buffers[i]; + + // Completed condition + if (count[i] < nsamples_baserate && handler->receiver[i].running) { + // Keep receiving + int32_t n = rf_file_rx_baseband(&handler->receiver[i], &ptr[count[i]], nsamples_baserate - count[i]); + if (n > 0) { + // No error + count[i] += n; + } else { + if (n != SRSRAN_ERROR_RX_EOF) { + // Other error, exit + fprintf(stderr, "Error: receiving data.\n"); + } + ret = n; + goto clean_exit; + } + } else { + // Completed, count it + completed_count++; + } + } + + // Check if all channels are completed + completed = (completed_count == handler->nof_channels); + } + rf_file_info(handler->id, " - read %d samples.\n", NBYTES2NSAMPLES(nbytes)); + + // decimate if needed + if (decim_factor != 1) { + for (uint32_t c = 0; c < handler->nof_channels; c++) { + // skip if buffer is not available + if (buffers[c]) { + cf_t* dst = buffers[c]; + cf_t* ptr = handler->buffer_decimation[c]; + + for (uint32_t i = 0, n = 0; i < nsamples; i++) { + // Averaging decimation + cf_t avg = 0.0f; + for (int j = 0; j < decim_factor; j++, n++) { + avg += ptr[n]; + } + dst[i] = avg; // divide by decim_factor later via scale + } + + rf_file_info(handler->id, + " - re-adjust bytes due to %dx decimation %d --> %d samples)\n", + decim_factor, + nsamples_baserate, + nsamples); + } + } + } + + // Set gain + pthread_mutex_lock(&handler->rx_gain_mutex); + float scale = srsran_convert_dB_to_amplitude(handler->rx_gain); + pthread_mutex_unlock(&handler->rx_gain_mutex); + // scale shall also incorporate decim_factor + scale = scale / decim_factor; + for (uint32_t c = 0; c < handler->nof_channels; c++) { + if (buffers[c]) { + srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples); + } + } + + // update rx time + update_ts(handler, &handler->next_rx_ts, nsamples_baserate, "rx"); + } + + ret = nsamples; + +clean_exit: + + return ret; +} + +int rf_file_send_timed(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + void* _data[4] = {data, NULL, NULL, NULL}; + + return rf_file_send_timed_multi( + h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst); +} + +int rf_file_send_timed_multi(void* h, + void* data[4], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + int ret = SRSRAN_ERROR; + + if (h && data && nsamples > 0) { + rf_file_handler_t* handler = (rf_file_handler_t*)h; + + // Map ports to data buffers according to the selected frequencies + pthread_mutex_lock(&handler->tx_config_mutex); + bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used + cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched or zero transmission + + // For each logical channel... + for (uint32_t logical = 0; logical < handler->nof_channels; logical++) { + // For each physical channel... + for (uint32_t physical = 0; physical < handler->nof_channels; physical++) { + // Consider a match if the physical channel is NOT mapped and the frequency match + if (!mapped[physical] && + rf_file_tx_match_freq(&handler->transmitter[physical], handler->tx_freq_mhz[logical])) { + // Not mapped and matched frequency with receiver + buffers[physical] = (cf_t*)data[logical]; + mapped[physical] = true; + break; + } + } + } + pthread_mutex_unlock(&handler->tx_config_mutex); + + // Protect the access to decim_factor since is a shared variable + pthread_mutex_lock(&handler->decim_mutex); + uint32_t decim_factor = handler->decim_factor; + pthread_mutex_unlock(&handler->decim_mutex); + + uint32_t nbytes = NSAMPLES2NBYTES(nsamples); + uint32_t nsamples_baseband = nsamples * decim_factor; + uint32_t nbytes_baseband = NSAMPLES2NBYTES(nsamples_baseband); + if (nbytes_baseband > FILE_MAX_BUFFER_SIZE) { + fprintf(stderr, "Error: trying to transmit too many samples (%d > %zu).\n", nbytes, FILE_MAX_BUFFER_SIZE); + goto clean_exit; + } + + rf_file_info(handler->id, "Tx %d samples (%d B)\n", nsamples, nbytes); + + // return if transmitter is switched off + if (handler->tx_off) { + return SRSRAN_SUCCESS; + } + + // check if this is a tx in the future + if (has_time_spec) { + rf_file_info(handler->id, " - tx time: %d + %.3f\n", secs, frac_secs); + + srsran_timestamp_t ts = {}; + srsran_timestamp_init(&ts, secs, frac_secs); + uint64_t tx_ts = srsran_timestamp_uint64(&ts, handler->base_srate); + int num_tx_gap_samples = 0; + + for (int i = 0; i < handler->nof_channels; i++) { + if (handler->transmitter[i].running) { + num_tx_gap_samples = rf_file_tx_align(&handler->transmitter[i], tx_ts); + } + } + + if (num_tx_gap_samples < 0) { + fprintf(stderr, + "[file] Error: tx time is %.3f ms in the past (%" PRIu64 " < %" PRIu64 ")\n", + -1000.0 * num_tx_gap_samples / handler->base_srate, + tx_ts, + handler->transmitter[0].nsamples); + goto clean_exit; + } + } + + // Send base-band samples + for (int i = 0; i < handler->nof_channels; i++) { + if (buffers[i] != NULL) { + // Select buffer pointer depending on interpolation + cf_t* buf = (decim_factor != 1) ? handler->buffer_tx : buffers[i]; + + // Interpolate if required + if (decim_factor != 1) { + rf_file_info(handler->id, + " - re-adjust bytes due to %dx interpolation %d --> %d samples)\n", + decim_factor, + nsamples, + nsamples_baseband); + + int n = 0; + cf_t* src = buffers[i]; + for (int k = 0; k < nsamples; k++) { + // perform zero order hold + for (int j = 0; j < decim_factor; j++, n++) { + buf[n] = src[k]; + } + } + + if (nsamples_baseband != n) { + fprintf(stderr, + "Number of tx samples (%d) does not match with number of interpolated samples (%d)\n", + nsamples_baseband, + n); + goto clean_exit; + } + } + + int n = rf_file_tx_baseband(&handler->transmitter[i], buf, nsamples_baseband); + if (n == SRSRAN_ERROR) { + goto clean_exit; + } + } else { + int n = rf_file_tx_zeros(&handler->transmitter[i], nsamples_baseband); + if (n == SRSRAN_ERROR) { + goto clean_exit; + } + } + } + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + + return ret; +} + +rf_dev_t srsran_rf_dev_file = {"file", + rf_file_devname, + rf_file_start_rx_stream, + rf_file_stop_rx_stream, + rf_file_flush_buffer, + rf_file_has_rssi, + rf_file_get_rssi, + rf_file_suppress_stdout, + rf_file_register_error_handler, + rf_file_open, + .srsran_rf_open_multi = rf_file_open_multi, + rf_file_close, + rf_file_set_rx_srate, + rf_file_set_rx_gain, + rf_file_set_rx_gain_ch, + rf_file_set_tx_gain, + rf_file_set_tx_gain_ch, + rf_file_get_rx_gain, + rf_file_get_tx_gain, + rf_file_get_info, + rf_file_set_rx_freq, + rf_file_set_tx_srate, + rf_file_set_tx_freq, + rf_file_get_time, + NULL, + rf_file_recv_with_time, + rf_file_recv_with_time_multi, + rf_file_send_timed, + .srsran_rf_send_timed_multi = rf_file_send_timed_multi}; diff --git a/lib/src/phy/rf/rf_file_imp.h b/lib/src/phy/rf/rf_file_imp.h new file mode 100644 index 000000000..011504b97 --- /dev/null +++ b/lib/src/phy/rf/rf_file_imp.h @@ -0,0 +1,144 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RF_FILE_IMP_H +#define SRSRAN_RF_FILE_IMP_H + +#include + +#include "srsran/config.h" +#include "srsran/phy/common/phy_common.h" +#include "srsran/phy/rf/rf.h" + +#define DEVNAME_FILE "file" +#define PARAM_LEN (128) +#define PARAM_LEN_SHORT (PARAM_LEN / 2) + +extern rf_dev_t srsran_rf_dev_file; + +SRSRAN_API const char* rf_file_devname(void* h); + +SRSRAN_API int rf_file_start_rx_stream(void* h, bool now); + +// SRSRAN_API int rf_file_start_rx_stream_nsamples(void* h, uint32_t nsamples); + +SRSRAN_API int rf_file_stop_rx_stream(void* h); + +SRSRAN_API void rf_file_flush_buffer(void* h); + +SRSRAN_API bool rf_file_has_rssi(void* h); + +SRSRAN_API float rf_file_get_rssi(void* h); + +SRSRAN_API void rf_file_suppress_stdout(void* h); + +SRSRAN_API void rf_file_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg); + +/** + * @brief This function is not supported for file-based RF abstraction + * + * Use @c rf_file_open_file() to open this device + * + * @param args not used + * @param h not used + * @return SRSRAN_ERROR_INVALID_COMMAND + */ +SRSRAN_API int rf_file_open(char* args, void** h); + +/** + * @brief This function is not supported for file-based RF abstraction + * + * Use @c rf_file_open_file() to open this device + * + * @param args not used + * @param h not used + * @param nof_channels not used + * @return SRSRAN_ERROR_INVALID_COMMAND + */ +SRSRAN_API int rf_file_open_multi(char* args, void** h, uint32_t nof_channels); + +SRSRAN_API int rf_file_close(void* h); + +SRSRAN_API double rf_file_set_rx_srate(void* h, double srate); + +SRSRAN_API int rf_file_set_rx_gain(void* h, double gain); + +SRSRAN_API int rf_file_set_rx_gain_ch(void* h, uint32_t ch, double gain); + +SRSRAN_API int rf_file_set_tx_gain(void* h, double gain); + +SRSRAN_API int rf_file_set_tx_gain_ch(void* h, uint32_t ch, double gain); + +SRSRAN_API double rf_file_get_rx_gain(void* h); + +SRSRAN_API double rf_file_get_tx_gain(void* h); + +SRSRAN_API srsran_rf_info_t* rf_file_get_info(void* h); + +SRSRAN_API double rf_file_set_rx_freq(void* h, uint32_t ch, double freq); + +SRSRAN_API double rf_file_set_tx_srate(void* h, double srate); + +SRSRAN_API double rf_file_set_tx_freq(void* h, uint32_t ch, double freq); + +SRSRAN_API void rf_file_get_time(void* h, time_t* secs, double* frac_secs); + +// srsran_rf_sync_pps + +SRSRAN_API int +rf_file_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs); + +SRSRAN_API int +rf_file_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs); + +SRSRAN_API int rf_file_send_timed(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +SRSRAN_API int rf_file_send_timed_multi(void* h, + void* data[4], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +/** + * @brief Dedicated function to open a file-based RF abstraction + * @param[out] h Resulting object handle + * @param[in] rx_files List of pre-opened FILE* for each RX channel; NULL to disable + * @param[in] tx_files List of pre-opened FILE* for each TX channel; NULL to disable + * @param[in] nof_channels Number of channels per direction + * @param[in] base_srate Sample rate of RX and TX files + * @return SRSRAN_SUCCESS on success, otherwise error code + */ +SRSRAN_API int +rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate); + +#endif // SRSRAN_RF_FILE_IMP_H diff --git a/lib/src/phy/rf/rf_file_imp_rx.c b/lib/src/phy/rf/rf_file_imp_rx.c new file mode 100644 index 000000000..9349fb0e0 --- /dev/null +++ b/lib/src/phy/rf/rf_file_imp_rx.c @@ -0,0 +1,107 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rf_file_imp_trx.h" +#include +#include +#include + +int rf_file_rx_open(rf_file_rx_t* q, rf_file_opts_t opts) +{ + int ret = SRSRAN_ERROR; + + if (q) { + // Zero object + memset(q, 0, sizeof(rf_file_rx_t)); + + // Copy id + strncpy(q->id, opts.id, FILE_ID_STRLEN - 1); + q->id[FILE_ID_STRLEN - 1] = '\0'; + + // Assign file + q->file = opts.file; + + // Configure formats + q->sample_format = opts.sample_format; + q->frequency_mhz = opts.frequency_mhz; + + q->temp_buffer = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE); + if (!q->temp_buffer) { + fprintf(stderr, "Error: allocating rx buffer\n"); + goto clean_exit; + } + + q->temp_buffer_convert = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE); + if (!q->temp_buffer_convert) { + fprintf(stderr, "Error: allocating rx buffer\n"); + goto clean_exit; + } + + if (pthread_mutex_init(&q->mutex, NULL)) { + fprintf(stderr, "Error: creating mutex\n"); + goto clean_exit; + } + + q->running = true; + + ret = SRSRAN_SUCCESS; + } + +clean_exit: + return ret; +} + +int rf_file_rx_baseband(rf_file_rx_t* q, cf_t* buffer, uint32_t nsamples) +{ + uint32_t sample_sz = sizeof(cf_t); + + int ret = fread(buffer, sample_sz, nsamples, q->file); + if (ret > 0) { + return ret; + } else { + return SRSRAN_ERROR_RX_EOF; + } +} + +bool rf_file_rx_match_freq(rf_file_rx_t* q, uint32_t freq_hz) +{ + bool ret = false; + if (q) { + ret = (q->frequency_mhz == 0 || q->frequency_mhz == freq_hz); + } + return ret; +} + +void rf_file_rx_close(rf_file_rx_t* q) +{ + rf_file_info(q->id, "Closing ...\n"); + q->running = false; + + if (q->temp_buffer) { + free(q->temp_buffer); + } + + if (q->temp_buffer_convert) { + free(q->temp_buffer_convert); + } + + // not touching q->file as we don't know if we need to close it ourselves +} diff --git a/lib/src/phy/rf/rf_file_imp_trx.h b/lib/src/phy/rf/rf_file_imp_trx.h new file mode 100644 index 000000000..84c4acfa0 --- /dev/null +++ b/lib/src/phy/rf/rf_file_imp_trx.h @@ -0,0 +1,115 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RF_FILE_IMP_TRX_H +#define SRSRAN_RF_FILE_IMP_TRX_H + +#include "srsran/config.h" +#include +#include +#include +#include +#include + +/* Definitions */ +#define VERBOSE (0) +#define NSAMPLES2NBYTES(X) (((uint32_t)(X)) * sizeof(cf_t)) +#define NBYTES2NSAMPLES(X) ((X) / sizeof(cf_t)) +#define FILE_MAX_BUFFER_SIZE (NSAMPLES2NBYTES(3072000)) // 10 subframes at 20 MHz +#define FILE_TIMEOUT_MS (1000) +#define FILE_BASERATE_DEFAULT_HZ (23040000) +#define FILE_ID_STRLEN 16 +#define FILE_MAX_GAIN_DB (30.0f) +#define FILE_MIN_GAIN_DB (0.0f) + +typedef enum { FILERF_TYPE_FC32 = 0, FILERF_TYPE_SC16 } rf_file_format_t; + +typedef struct { + char id[FILE_ID_STRLEN]; + rf_file_format_t sample_format; + FILE* file; + uint64_t nsamples; + bool running; + pthread_mutex_t mutex; + cf_t* zeros; + void* temp_buffer_convert; + uint32_t frequency_mhz; + int32_t sample_offset; +} rf_file_tx_t; + +typedef struct { + char id[FILE_ID_STRLEN]; + rf_file_format_t sample_format; + FILE* file; + uint64_t nsamples; + bool running; + pthread_t thread; + pthread_mutex_t mutex; + cf_t* temp_buffer; + void* temp_buffer_convert; + uint32_t frequency_mhz; +} rf_file_rx_t; + +typedef struct { + const char* id; + rf_file_format_t sample_format; + FILE* file; + uint32_t frequency_mhz; +} rf_file_opts_t; + +/* + * Common functions + */ +SRSRAN_API void rf_file_info(char* id, const char* format, ...); + +SRSRAN_API void rf_file_error(char* id, const char* format, ...); + +SRSRAN_API int rf_file_handle_error(char* id, const char* text); + +/* + * Transmitter functions + */ +SRSRAN_API int rf_file_tx_open(rf_file_tx_t* q, rf_file_opts_t opts); + +SRSRAN_API int rf_file_tx_align(rf_file_tx_t* q, uint64_t ts); + +SRSRAN_API int rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples); + +SRSRAN_API int rf_file_tx_get_nsamples(rf_file_tx_t* q); + +SRSRAN_API int rf_file_tx_zeros(rf_file_tx_t* q, uint32_t nsamples); + +SRSRAN_API bool rf_file_tx_match_freq(rf_file_tx_t* q, uint32_t freq_hz); + +SRSRAN_API void rf_file_tx_close(rf_file_tx_t* q); + +/* + * Receiver functions + */ +SRSRAN_API int rf_file_rx_open(rf_file_rx_t* q, rf_file_opts_t opts); + +SRSRAN_API int rf_file_rx_baseband(rf_file_rx_t* q, cf_t* buffer, uint32_t nsamples); + +SRSRAN_API bool rf_file_rx_match_freq(rf_file_rx_t* q, uint32_t freq_hz); + +SRSRAN_API void rf_file_rx_close(rf_file_rx_t* q); + +#endif // SRSRAN_RF_FILE_IMP_TRX_H diff --git a/lib/src/phy/rf/rf_file_imp_tx.c b/lib/src/phy/rf/rf_file_imp_tx.c new file mode 100644 index 000000000..1b3f667ba --- /dev/null +++ b/lib/src/phy/rf/rf_file_imp_tx.c @@ -0,0 +1,198 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rf_file_imp_trx.h" +#include +#include +#include +#include +#include +#include + +int rf_file_tx_open(rf_file_tx_t* q, rf_file_opts_t opts) +{ + int ret = SRSRAN_ERROR; + + if (q) { + // Zero object + memset(q, 0, sizeof(rf_file_tx_t)); + + // Copy id + strncpy(q->id, opts.id, FILE_ID_STRLEN - 1); + q->id[FILE_ID_STRLEN - 1] = '\0'; + + // Assign file + q->file = opts.file; + + // Configure formats + q->sample_format = opts.sample_format; + q->frequency_mhz = opts.frequency_mhz; + + q->temp_buffer_convert = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE); + if (!q->temp_buffer_convert) { + fprintf(stderr, "Error: allocating tx buffer\n"); + goto clean_exit; + } + + if (pthread_mutex_init(&q->mutex, NULL)) { + fprintf(stderr, "Error: creating mutex\n"); + goto clean_exit; + } + + q->zeros = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE); + if (!q->zeros) { + fprintf(stderr, "Error: allocating zeros\n"); + goto clean_exit; + } + memset(q->zeros, 0, FILE_MAX_BUFFER_SIZE); + + q->running = true; + + ret = SRSRAN_SUCCESS; + } + +clean_exit: + return ret; +} + +static int _rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples) +{ + int n = SRSRAN_ERROR; + + // convert samples if necessary + void* buf = (buffer) ? buffer : q->zeros; + uint32_t sample_sz = sizeof(cf_t); + + if (q->sample_format == FILERF_TYPE_SC16) { + buf = q->temp_buffer_convert; + sample_sz = 2 * sizeof(short); + srsran_vec_convert_fi((float*)buffer, INT16_MAX, (short*)q->temp_buffer_convert, 2 * nsamples); + } + + size_t ret = fwrite(buf, (size_t)sample_sz, (size_t)nsamples, q->file); + if (ret < (size_t)nsamples) { + rf_file_error(q->id, + "[file] Error: transmitter expected %d bytes and sent %zd. %s.\n", + NSAMPLES2NBYTES(nsamples), + ret, + strerror(errno)); + n = SRSRAN_ERROR; + goto clean_exit; + } + + // Increment sample counter + q->nsamples += nsamples; + n = nsamples; + +clean_exit: + return n; +} + +int rf_file_tx_align(rf_file_tx_t* q, uint64_t ts) +{ + pthread_mutex_lock(&q->mutex); + + int64_t nsamples = (int64_t)ts - (int64_t)q->nsamples; + + if (nsamples > 0) { + rf_file_info(q->id, " - Detected Tx gap of %d samples.\n", nsamples); + _rf_file_tx_baseband(q, q->zeros, (uint32_t)nsamples); + } + + pthread_mutex_unlock(&q->mutex); + + return (int)nsamples; +} + +int rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples) +{ + int n; + + pthread_mutex_lock(&q->mutex); + + if (q->sample_offset > 0) { + _rf_file_tx_baseband(q, q->zeros, (uint32_t)q->sample_offset); + q->sample_offset = 0; + } else if (q->sample_offset < 0) { + n = SRSRAN_MIN(-q->sample_offset, nsamples); + buffer += n; + nsamples -= n; + q->sample_offset += n; + if (nsamples == 0) { + return n; + } + } + + n = _rf_file_tx_baseband(q, buffer, nsamples); + + pthread_mutex_unlock(&q->mutex); + + return n; +} + +int rf_file_tx_get_nsamples(rf_file_tx_t* q) +{ + pthread_mutex_lock(&q->mutex); + int ret = q->nsamples; + pthread_mutex_unlock(&q->mutex); + return ret; +} + +int rf_file_tx_zeros(rf_file_tx_t* q, uint32_t nsamples) +{ + pthread_mutex_lock(&q->mutex); + + rf_file_info(q->id, " - Tx %d Zeros.\n", nsamples); + _rf_file_tx_baseband(q, q->zeros, (uint32_t)nsamples); + + pthread_mutex_unlock(&q->mutex); + + return (int)nsamples; +} + +bool rf_file_tx_match_freq(rf_file_tx_t* q, uint32_t freq_hz) +{ + bool ret = false; + if (q) { + ret = (q->frequency_mhz == 0 || q->frequency_mhz == freq_hz); + } + return ret; +} + +void rf_file_tx_close(rf_file_tx_t* q) +{ + rf_file_info(q->id, "Closing ...\n"); + pthread_mutex_lock(&q->mutex); + q->running = false; + pthread_mutex_unlock(&q->mutex); + + pthread_mutex_destroy(&q->mutex); + + if (q->zeros) { + free(q->zeros); + } + + if (q->temp_buffer_convert) { + free(q->temp_buffer_convert); + } + + // not touching q->file as we don't know if we need to close it ourselves +} diff --git a/lib/src/phy/rf/rf_file_test.c b/lib/src/phy/rf/rf_file_test.c new file mode 100644 index 000000000..a1a92d85b --- /dev/null +++ b/lib/src/phy/rf/rf_file_test.c @@ -0,0 +1,336 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rf_file_imp.h" +#include "srsran/common/tsan_options.h" +#include "srsran/phy/common/timestamp.h" +#include "srsran/phy/utils/debug.h" +#include +#include +#include +#include +#include + +#define PRINT_SAMPLES 0 +#define COMPARE_BITS 0 +#define COMPARE_EPSILON (1e-6f) +#define NOF_RX_ANT 4 +#define NUM_SF (500) +#define SF_LEN (1920) +#define RF_BUFFER_SIZE (SF_LEN * NUM_SF) +#define TX_OFFSET_MS (4) + +static cf_t ue_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; +static cf_t enb_tx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; +static cf_t enb_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; + +static srsran_rf_t ue_radio, enb_radio; +pthread_t rx_thread; + +void* ue_rx_thread_function(void* args) +{ + char rf_args[RF_PARAM_LEN]; + strncpy(rf_args, (char*)args, RF_PARAM_LEN - 1); + rf_args[RF_PARAM_LEN - 1] = 0; + + // sleep(1); + + printf("opening rx device with args=%s\n", rf_args); + if (srsran_rf_open_devname(&ue_radio, "file", rf_args, NOF_RX_ANT)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + + // receive 5 subframes at once (i.e. mimic initial rx that receives one slot) + uint32_t num_slots = NUM_SF / 5; + uint32_t num_samps_per_slot = SF_LEN * 5; + uint32_t num_rxed_samps = 0; + for (uint32_t i = 0; i < num_slots; ++i) { + void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; + for (uint32_t c = 0; c < NOF_RX_ANT; c++) { + data_ptr[c] = &ue_rx_buffer[c][i * num_samps_per_slot]; + } + num_rxed_samps += srsran_rf_recv_with_time_multi(&ue_radio, data_ptr, num_samps_per_slot, true, NULL, NULL); + } + + printf("received %d samples.\n", num_rxed_samps); + + printf("closing ue rx device\n"); + srsran_rf_close(&ue_radio); + + return NULL; +} + +void enb_tx_function(const char* tx_args, bool timed_tx) +{ + char rf_args[RF_PARAM_LEN]; + strncpy(rf_args, tx_args, RF_PARAM_LEN - 1); + rf_args[RF_PARAM_LEN - 1] = 0; + + printf("opening tx device with args=%s\n", rf_args); + if (srsran_rf_open_devname(&enb_radio, "file", rf_args, NOF_RX_ANT)) { + fprintf(stderr, "Error opening rf\n"); + exit(-1); + } + + // generate random tx data + for (int c = 0; c < NOF_RX_ANT; c++) { + for (int i = 0; i < RF_BUFFER_SIZE; i++) { + enb_tx_buffer[c][i] = ((float)rand() / (float)RAND_MAX) + _Complex_I * ((float)rand() / (float)RAND_MAX); + } + } + + // send data subframe per subframe + uint32_t num_txed_samples = 0; + + // initial transmission without ts + void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; + for (int c = 0; c < NOF_RX_ANT; c++) { + data_ptr[c] = &enb_tx_buffer[c][num_txed_samples]; + } + int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false); + num_txed_samples += SF_LEN; + + // from here on, all transmissions are timed relative to the last rx time + srsran_timestamp_t rx_time, tx_time; + + for (uint32_t i = 0; i < NUM_SF - ((timed_tx) ? TX_OFFSET_MS : 1); ++i) { + // first recv samples + for (int c = 0; c < NOF_RX_ANT; c++) { + data_ptr[c] = enb_rx_buffer[c]; + } + srsran_rf_recv_with_time_multi(&enb_radio, data_ptr, SF_LEN, true, &rx_time.full_secs, &rx_time.frac_secs); + + // prepare data buffer + for (int c = 0; c < NOF_RX_ANT; c++) { + data_ptr[c] = &enb_tx_buffer[c][num_txed_samples]; + } + + if (timed_tx) { + // timed tx relative to receive time (this will cause a cap in the rx'ed samples at the UE resulting in 3 zero + // subframes) + srsran_timestamp_copy(&tx_time, &rx_time); + srsran_timestamp_add(&tx_time, 0, TX_OFFSET_MS * 1e-3); + ret = srsran_rf_send_timed_multi( + &enb_radio, (void**)data_ptr, SF_LEN, tx_time.full_secs, tx_time.frac_secs, true, true, false); + } else { + // normal tx + ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false); + } + if (ret != SRSRAN_SUCCESS) { + fprintf(stderr, "Error sending data\n"); + exit(-1); + } + + num_txed_samples += SF_LEN; + } + + printf("transmitted %d samples in %d subframes\n", num_txed_samples, NUM_SF); + + printf("closing tx device\n"); + srsran_rf_close(&enb_radio); +} + +int run_test(const char* rx_args, const char* tx_args, bool timed_tx) +{ + int ret = SRSRAN_ERROR; + + // make sure we can receive in slots + if (NUM_SF % 5 != 0) { + fprintf(stderr, "number of subframes must be multiple of 5\n"); + goto exit; + } + + // write to file(s) + enb_tx_function(tx_args, timed_tx); + + // read from file(s) + ue_rx_thread_function((void*)rx_args); + + // channel-wise comparison + for (int c = 0; c < NOF_RX_ANT; c++) { + // subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx) + for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) { + uint32_t sf_offet = 0; + if (timed_tx && i >= 1) { + // for timed transmission, the enb inserts 3 zero subframes after the first untimed tx + sf_offet = (TX_OFFSET_MS - 1) * SF_LEN; + } + +#if PRINT_SAMPLES + // print first 10 samples for each SF + printf("enb_tx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &enb_tx_buffer[c][i * SF_LEN], 10); + printf("ue_rx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &ue_rx_buffer[c][sf_offet + i * SF_LEN], 10); +#endif + +#if COMPARE_BITS + int d = memcmp(&ue_rx_buffer[sf_offet + i * SF_LEN], &enb_tx_buffer[i * SF_LEN], SF_LEN); + if (d) { + d = d > 0 ? d : -d; + fprintf(stderr, "data mismatch in subframe %d, sample %d\n", i, d); + printf("enb_tx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &enb_tx_buffer[i * SF_LEN + d], 10); + printf("ue_rx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &ue_rx_buffer[sf_offet + i * SF_LEN + d], 10); + goto exit; + } +#else + srsran_vec_sub_ccc(&ue_rx_buffer[c][sf_offet + i * SF_LEN], + &enb_tx_buffer[c][i * SF_LEN], + &ue_rx_buffer[c][sf_offet + i * SF_LEN], + SF_LEN); + uint32_t max_ix = srsran_vec_max_abs_ci(&ue_rx_buffer[c][sf_offet + i * SF_LEN], SF_LEN); + if (cabsf(ue_rx_buffer[c][sf_offet + i * SF_LEN + max_ix]) > COMPARE_EPSILON) { + fprintf(stderr, "data mismatch in subframe %d\n", i); + goto exit; + } +#endif + } + } + + ret = SRSRAN_SUCCESS; + +exit: + return ret; +} + +int param_test(const char* args_param, const int num_channels) +{ + char rf_args[RF_PARAM_LEN] = {}; + strncpy(rf_args, (char*)args_param, RF_PARAM_LEN - 1); + rf_args[RF_PARAM_LEN - 1] = 0; + + printf("opening tx device with args=%s\n", rf_args); + if (srsran_rf_open_devname(&enb_radio, "file", rf_args, num_channels)) { + fprintf(stderr, "Error opening rf\n"); + return SRSRAN_ERROR; + } + + srsran_rf_close(&enb_radio); + + return SRSRAN_SUCCESS; +} + +void create_file(const char* filename) +{ + FILE* f = fopen(filename, "w"); + fclose(f); +} + +void remove_file(const char* filename) +{ + remove(filename); +} + +int main() +{ + // create files for testing + create_file("rx_file0"); + create_file("rx_file1"); + create_file("rx_file2"); + create_file("rx_file3"); + + // two RX files + if (param_test("rx_file=rx_file0," + "rx_file1=rx_file1", + 2)) { + fprintf(stderr, "Param test failed!\n"); + return SRSRAN_ERROR; + } + + // multiple RX files, no channel index provided + if (param_test("rx_file=rx_file0," + "rx_file=rx_file1," + "rx_file=rx_file2," + "rx_file=rx_file3," + "base_srate=1.92e6", + 4)) { + fprintf(stderr, "Param test failed!\n"); + return SRSRAN_ERROR; + } + + // one RX, one TX and all generic options + if (param_test("rx_file0=rx_file0," + "tx_file0=tx_file0," + "base_srate=1.92e6", + 1)) { + fprintf(stderr, "Param test failed!\n"); + return SRSRAN_ERROR; + } + + // two RX, two TX + if (param_test("rx_file0=rx_file0," + "rx_file1=rx_file1," + "tx_file0=tx_file0," + "tx_file1=tx_file1", + 2)) { + fprintf(stderr, "Param test failed!\n"); + return SRSRAN_ERROR; + } + +#if NOF_RX_ANT == 1 + // single tx, single rx with continuous transmissions (no decimation, no timed tx) + if (run_test("rx_file=tx_file0,base_srate=1.92e6", "tx_file=tx_file0,base_srate=1.92e6", false) != SRSRAN_SUCCESS) { + fprintf(stderr, "Single tx, single rx test failed (no decimation, no timed tx)!\n"); + return -1; + } +#endif + + // up to 4 trx radios with continous tx (no decimation, no timed tx) + if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3,base_srate=1.92e6", + "tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3,base_srate=1.92e6", + false) != SRSRAN_SUCCESS) { + fprintf(stderr, "Multi TRx radio test failed (no decimation, no timed tx)!\n"); + return -1; + } + + // up to 4 trx radios with continous tx (with decimation, no timed tx) + if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3", + "tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3", + false) != SRSRAN_SUCCESS) { + fprintf(stderr, "Multi TRx radio test failed (with decimation, no timed tx)!\n"); + return -1; + } + + // up to 4 trx radios with continous tx (with decimation, timed tx) + if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3", + "tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3", + true) != SRSRAN_SUCCESS) { + fprintf(stderr, "Two TRx radio test failed (with decimation, timed tx)!\n"); + return -1; + } + + // clean workspace + remove_file("rx_file0"); + remove_file("rx_file1"); + remove_file("rx_file2"); + remove_file("rx_file3"); + remove_file("tx_file0"); + remove_file("tx_file1"); + remove_file("tx_file2"); + remove_file("tx_file3"); + + fprintf(stdout, "Test passed!\n"); + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/rf/rf_helper.h b/lib/src/phy/rf/rf_helper.h index 1926f4b08..e393d2b07 100644 --- a/lib/src/phy/rf/rf_helper.h +++ b/lib/src/phy/rf/rf_helper.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/rf/rf_imp.c b/lib/src/phy/rf/rf_imp.c index d056427a6..6e756385b 100644 --- a/lib/src/phy/rf/rf_imp.c +++ b/lib/src/phy/rf/rf_imp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,17 +19,19 @@ * */ -#include - #include "rf_dev.h" #include "srsran/phy/rf/rf.h" -#include "srsran/srsran.h" +#include "srsran/phy/utils/debug.h" +#include +#include int rf_get_available_devices(char** devnames, int max_strlen) { int i = 0; - while (available_devices[i]->name) { - strncpy(devnames[i], available_devices[i]->name, max_strlen); + while (rf_plugins[i] != NULL) { + if (rf_plugins[i]->rf_api != NULL) { + strncpy(devnames[i], rf_plugins[i]->rf_api->name, max_strlen); + } i++; } return i; @@ -37,12 +39,12 @@ int rf_get_available_devices(char** devnames, int max_strlen) int srsran_rf_set_rx_gain_th(srsran_rf_t* rf, double gain) { + pthread_mutex_lock(&rf->mutex); if (gain > rf->cur_rx_gain + 2 || gain < rf->cur_rx_gain - 2) { - pthread_mutex_lock(&rf->mutex); rf->new_rx_gain = gain; pthread_cond_signal(&rf->cond); - pthread_mutex_unlock(&rf->mutex); } + pthread_mutex_unlock(&rf->mutex); return SRSRAN_SUCCESS; } @@ -102,32 +104,60 @@ const char* srsran_rf_get_devname(srsran_rf_t* rf) int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uint32_t nof_channels) { rf->thread_gain_run = false; - /* Try to open the device if name is provided */ - if (devname) { - if (devname[0] != '\0') { - int i = 0; - while (available_devices[i] != NULL) { - if (!strcasecmp(available_devices[i]->name, devname)) { - rf->dev = available_devices[i]; - return available_devices[i]->srsran_rf_open_multi(args, &rf->handler, nof_channels); + + bool no_rf_devs_detected = true; + printf("Supported RF device list:"); + for (unsigned int i = 0; rf_plugins[i] && rf_plugins[i]->rf_api; i++) { + no_rf_devs_detected = false; + printf(" %s", rf_plugins[i]->rf_api->name); + } + printf("%s\n", no_rf_devs_detected ? " " : ""); + + // Try to open the device if name is provided + if (devname && devname[0] != '\0') { + int i = 0; + while (rf_plugins[i] != NULL) { + if (rf_plugins[i]->rf_api) { + if (!strcasecmp(rf_plugins[i]->rf_api->name, devname)) { + rf->dev = rf_plugins[i]->rf_api; + return rf_plugins[i]->rf_api->srsran_rf_open_multi(args, &rf->handler, nof_channels); } - i++; } - printf("Device %s not found. Switching to auto mode\n", devname); + i++; } + + ERROR("RF device '%s' not found. Please check the available srsRAN CMAKE options to verify if this device is being " + "detected in your system", + devname); + // provided device not found, abort + return SRSRAN_ERROR; } - /* If in auto mode or provided device not found, try to open in order of apperance in available_devices[] array */ + // auto-mode, try to open in order of apperance in rf_plugins[] array int i = 0; - while (available_devices[i] != NULL) { - if (!available_devices[i]->srsran_rf_open_multi(args, &rf->handler, nof_channels)) { - rf->dev = available_devices[i]; - return 0; + while (rf_plugins[i] != NULL && rf_plugins[i]->rf_api != NULL) { + printf("Trying to open RF device '%s'\n", rf_plugins[i]->rf_api->name); + if (!rf_plugins[i]->rf_api->srsran_rf_open_multi(args, &rf->handler, nof_channels)) { + rf->dev = rf_plugins[i]->rf_api; + printf("RF device '%s' successfully opened\n", rf_plugins[i]->rf_api->name); + return SRSRAN_SUCCESS; } + printf("Unable to open RF device '%s'\n", rf_plugins[i]->rf_api->name); i++; } - ERROR("No compatible RF frontend found"); - return -1; + + ERROR( + "Failed to open a RF frontend device. Please check the available srsRAN CMAKE options to verify what RF frontend " + "devices have been detected in your system"); + return SRSRAN_ERROR; +} + +int srsran_rf_open_file(srsran_rf_t* rf, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate) +{ + rf->dev = &srsran_rf_dev_file; + + // file abstraction has custom "open" function with file-related args + return rf_file_open_file(&rf->handler, rx_files, tx_files, nof_channels, base_srate); } const char* srsran_rf_name(srsran_rf_t* rf) @@ -183,9 +213,13 @@ int srsran_rf_open_multi(srsran_rf_t* h, char* args, uint32_t nof_channels) int srsran_rf_close(srsran_rf_t* rf) { // Stop gain thread + pthread_mutex_lock(&rf->mutex); if (rf->thread_gain_run) { rf->thread_gain_run = false; - pthread_cond_signal(&rf->cond); + } + pthread_mutex_unlock(&rf->mutex); + pthread_cond_signal(&rf->cond); + if (rf->thread_gain) { pthread_join(rf->thread_gain, NULL); } @@ -370,3 +404,94 @@ int srsran_rf_send_timed2(srsran_rf_t* rf, { return srsran_rf_send_timed3(rf, data, nsamples, secs, frac_secs, true, true, is_start_of_burst, is_end_of_burst); } + +#ifdef ENABLE_RF_PLUGINS +static void unload_plugin(srsran_rf_plugin_t* rf_plugin) +{ + if (rf_plugin == NULL) { + return; + } + if (rf_plugin->dl_handle != NULL) { + rf_plugin->rf_api = NULL; + dlclose(rf_plugin->dl_handle); + rf_plugin->dl_handle = NULL; + } +} + +static int load_plugin(srsran_rf_plugin_t* rf_plugin) +{ + if (rf_plugin->rf_api != NULL) { + // already loaded + return SRSRAN_SUCCESS; + } + + rf_plugin->dl_handle = dlopen(rf_plugin->plugin_name, RTLD_NOW); + if (rf_plugin->dl_handle == NULL) { + // Not an error, if loading failed due to missing dependencies. + // Flag this plugin as not available and return SUCCESS. + // Note: as this function is called before log-level is configured, use plain printf for any messages < ERROR + printf("Skipping RF plugin %s: %s\n", rf_plugin->plugin_name, dlerror()); + rf_plugin->rf_api = NULL; + return SRSRAN_SUCCESS; + } + + // clear errors + dlerror(); + char* err = NULL; + + // load symbols + int (*register_plugin)(rf_dev_t * *rf_api) = dlsym(rf_plugin->dl_handle, "register_plugin"); + if ((err = dlerror()) != NULL) { + ERROR("Error loading symbol '%s': %s", "register_plugin", err); + goto clean_exit; + } + + // register plugin + int ret = register_plugin(&rf_plugin->rf_api); + if (ret != SRSRAN_SUCCESS) { + ERROR("Failed to register RF API for plugin %s", rf_plugin->plugin_name); + goto clean_exit; + } + return SRSRAN_SUCCESS; +clean_exit: + unload_plugin(rf_plugin); + return SRSRAN_ERROR; +} +#endif /* ENABLE_RF_PLUGINS */ + +int srsran_rf_load_plugins() +{ +#ifdef ENABLE_RF_PLUGINS + for (unsigned int i = 0; rf_plugins[i]; i++) { + if (load_plugin(rf_plugins[i]) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + + printf("Active RF plugins:"); + for (unsigned int i = 0; rf_plugins[i]; i++) { + if (rf_plugins[i]->dl_handle != NULL) { + printf(" %s", rf_plugins[i]->plugin_name); + } + } + printf("\n"); + + printf("Inactive RF plugins:"); + for (unsigned int i = 0; rf_plugins[i]; i++) { + if (rf_plugins[i]->dl_handle == NULL) { + printf(" %s", rf_plugins[i]->plugin_name); + } + } + printf("\n"); + +#endif /* ENABLE_RF_PLUGINS */ + return SRSRAN_SUCCESS; +} + +// Search and load plugins when this library is loaded (shared) or right before main (static) +void __attribute__((constructor)) init() +{ + if (srsran_rf_load_plugins() != SRSRAN_SUCCESS) { + ERROR("Failed to load RF plugins"); + } +} diff --git a/lib/src/upper/rlc_am_base.cc b/lib/src/phy/rf/rf_plugin.h similarity index 67% rename from lib/src/upper/rlc_am_base.cc rename to lib/src/phy/rf/rf_plugin.h index 08d5e488b..fa2a65531 100644 --- a/lib/src/upper/rlc_am_base.cc +++ b/lib/src/phy/rf/rf_plugin.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,19 +19,19 @@ * */ -#include "srsran/upper/rlc_am_base.h" -#include +#ifndef SRSRAN_RF_PLUGIN_H +#define SRSRAN_RF_PLUGIN_H -namespace srsran { +#include "srsran/phy/rf/rf.h" -bool rlc_am_is_control_pdu(uint8_t* payload) -{ - return ((*(payload) >> 7) & 0x01) == RLC_DC_FIELD_CONTROL_PDU; +#ifdef __cplusplus +extern "C" { +#endif + +SRSRAN_API int register_plugin(rf_dev_t** rf_api); + +#ifdef __cplusplus } +#endif -bool rlc_am_is_control_pdu(byte_buffer_t* pdu) -{ - return rlc_am_is_control_pdu(pdu->msg); -} - -} // namespace srsran +#endif /* SRSRAN_RF_PLUGIN_H */ diff --git a/lib/src/phy/rf/rf_skiq_imp.c b/lib/src/phy/rf/rf_skiq_imp.c new file mode 100644 index 000000000..2408d0397 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp.c @@ -0,0 +1,991 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include + +#include + +#include "rf_helper.h" +#include "rf_plugin.h" +#include "rf_skiq_imp.h" +#include "rf_skiq_imp_card.h" + +/** + * According the document Sidekiq API 4.13.0 @ref AD9361TimestampSlip: + * Functions that will affect the timestamp: + * â€ĸ skiq_write_rx_LO_freq() + * â€ĸ skiq_write_rx_sample_rate_and_bandwidth() + * â€ĸ skiq_write_tx_LO_freq() + * â€ĸ skiq_run_tx_quadcal() + * â€ĸ skiq_write_rx_freq_tune_mode() + * â€ĸ skiq_write_tx_freq_tune_mode() + * Functions that will be affected by the timestamp slip: + * â€ĸ skiq_read_last_1pps_timestamp() + * â€ĸ skiq_receive() + * â€ĸ skiq_transmit() + * â€ĸ skiq_read_curr_rx_timestamp() + * â€ĸ skiq_read_curr_tx_timestamp() + * + * The functions mentioned on the first group above can be divided in two groups. The first group are the ones that + * require restart the tx/rx streams of cards: + * â€ĸ skiq_write_rx_sample_rate_and_bandwidth() + * + * The module assumes: + * - Tx and Rx sampling rates are equal + * - Tx/Rx shall be stopped during the configuration + * - skiq_stop_rx_streaming_multi_immediate can be called while skiq_receive is being executed + * - skiq_receive shall not be called while skiq_stop_rx_streaming_multi_immediate + * + * In order to update the sampling rate, the RF module shall: + * - Stop all cards Rx streams + * - Stop all cards Tx streams + * - Update Tx/Rx sample rates + * - Start Rx stream + * - enable Tx stream on the next transmission + * + * The second group do not require restarting the tx/rx streams. Indeed, they only affect to a single card and there is + * no interest on stalling the rest of cards stream. Because of this, the module shall suspend the affected card. + * â€ĸ skiq_write_rx_LO_freq() + * â€ĸ skiq_write_tx_LO_freq() + * + * The module assumes: + * - The Tx/Rx LO frequency is changed for a single card + * - The Tx/Rx is stalled only in selected card + * - The rest of cards shall keep operating without stalling their streams + * + * In order to update the Tx/Rx LO frequencies, the RF module shall: + * - Suspend the Tx/Rx streams of the card: + * - If receive port ring-buffer has samples, the module shall keep reading from ringbuffer; + * - Otherwise, the module shall not read from ring-buffer and write zeros in the receive buffer + * - Set the Tx/Rx LO frequency + * - Resume the reception + * + */ + +uint32_t rf_skiq_logging_level = SKIQ_LOG_INFO; + +typedef struct { + uint32_t nof_cards; + uint32_t nof_ports; + + rf_skiq_card_t cards[SKIQ_MAX_NUM_CARDS]; + + float cur_tx_gain; + + srsran_rf_info_t info; + + uint64_t next_tstamp; + + double current_srate_hz; + cf_t dummy_buffer[RF_SKIQ_DUMMY_BUFFER_SIZE]; + + pthread_mutex_t mutex_rx; ///< Makes sure receive function and sampling rate setter are not running simultaneously + +} rf_skiq_handler_t; + +void rf_skiq_suppress_stdout(void* h) +{ + SKIQ_RF_INFO("Suppressing stdout... lowering logging level to warning\n"); + rf_skiq_logging_level = SKIQ_LOG_WARNING; +} + +static bool rf_skiq_is_streaming(rf_skiq_handler_t* h) +{ + // If a single card is streaming, return true + for (uint32_t i = 0; i < h->nof_cards; i++) { + if (rf_skiq_card_is_streaming(&h->cards[i])) { + return true; + } + } + + return false; +} + +bool rf_skiq_check_synch(rf_skiq_handler_t* h) +{ + // Get first card system timestamp + int64_t ts0_sys = (int64_t)rf_skiq_card_read_sys_timestamp(&h->cards[0]); + int64_t ts0_rf = (int64_t)rf_skiq_card_read_rf_timestamp(&h->cards[0]); + SKIQ_RF_INFO(" ... Card 0 TS(sys/rf)=%ld/%ld\n", ts0_sys, ts0_rf); + + bool pass = true; + // Compare all the other card timestamps + for (uint32_t i = 1; i < h->nof_cards; i++) { + int64_t ts2_sys = (int64_t)rf_skiq_card_read_sys_timestamp(&h->cards[i]); + int64_t ts2_rf = (int64_t)rf_skiq_card_read_rf_timestamp(&h->cards[i]); + + // Use current sampling rate + double srate = h->current_srate_hz; + + // If the current srate was not set (zero, nan or Inf), read it back from the first card + if (!isnormal(srate)) { + uint32_t srate_int = 0; + if (skiq_read_rx_sample_rate(0, skiq_rx_hdl_A1, &srate_int, &srate)) { + ERROR("Error reading sampling rate\n"); + } + } + + // Make sure all cards system and RF timestamps are inside maximum allowed error window + bool card_pass = labs(ts2_sys - ts0_sys) < SKIQ_CARD_SYNC_MAX_ERROR; + card_pass = card_pass && labs(ts2_rf - ts0_rf) < (int64_t)(srate / 2); + + // It is enough that a card does not pass to fail the check + pass = pass && card_pass; + + SKIQ_RF_INFO(" ... Card %d TS(sys/rf)=(%ld/%ld) (%.4f/%.4f). %s\n", + i, + ts2_sys, + ts2_rf, + (double)labs(ts2_sys - ts0_sys) / (double)SKIQ_SYS_TIMESTAMP_FREQ, + (double)labs(ts2_rf - ts0_rf) / srate, + card_pass ? "Ok" : "KO"); + } + + return pass; +} + +/** + * Synchronizes single and multiple cards using the PPS signal. This function helper shall be used once at + * initialization. + * + * @param h SKIQ driver handler + * @return SRSRAN_SUCCESS if it is possible to synchronize boards, SRSRAN_ERROR otherwise + */ +static int rf_skiq_synch_cards(rf_skiq_handler_t* h) +{ + bool do_1pps = h->nof_cards > 1; //< PPS is required when more than one card is used + uint32_t trials = 0; //< Count PPS synchronization check trials + + // Try synchronizing timestamps of all cards up to 10 trials + do { + SKIQ_RF_INFO("Resetting system timestamp trial %d/%d\n", trials + 1, SKIQ_CARD_SYNCH_MAX_TRIALS); + + // Reset timestamp in next PPS + for (int i = 0; i < h->nof_cards; i++) { + // Sets the timestamps to the last Rx known time. + if (rf_skiq_card_update_timestamp( + &h->cards[i], do_1pps, h->cards->rx_ports->rb_tstamp_rem + h->current_srate_hz) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + + // It could be that one or more cards PPS reset was issued just after a PPS signal, so only if there is PPS + // (multiple cards), verifies that all cards are synchronised on the same PPS + if (!do_1pps) { + return SRSRAN_SUCCESS; + } + + // Wait for a second to pass + SKIQ_RF_INFO(" ... Waiting PPS to pass ...\n"); + sleep(1); + SKIQ_RF_INFO(" ... Checking:\n"); + + // Successful synchronization across boards! + if (rf_skiq_check_synch(h)) { + return SRSRAN_SUCCESS; + } + + // Increment trial count + trials++; + } while (trials < SKIQ_CARD_SYNCH_MAX_TRIALS); + + // All trials have been consumed without a Successful synchronization + ERROR("Error card synchronization failed\n"); + return SRSRAN_ERROR; +} + +void rf_skiq_register_error_handler(void* h_, srsran_rf_error_handler_t error_handler, void* arg) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + SKIQ_RF_INFO("Registering error handler...\n"); + + // Set error handler for each card + for (uint32_t i = 0; i < h->nof_cards; i++) { + rf_skiq_card_set_error_handler(&h->cards[i], error_handler, arg); + } +} + +const char* rf_skiq_devname(void* h) +{ + return "Sidekiq"; +} + +int rf_skiq_start_rx_stream(void* h_, bool now) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + rf_skiq_synch_cards(h); + + // Get current system timestamp, assume all cards are synchronized + uint64_t ts = rf_skiq_card_read_sys_timestamp(&h->cards[0]); + + // Advance a 10th of a second (100ms) + ts += SKIQ_SYS_TIMESTAMP_FREQ / 10; + + // Start streams for each card at the indicated timestamp... + for (uint32_t i = 0; i < h->nof_cards; i++) { + if (rf_skiq_card_start_rx_streaming(&h->cards[i], ts)) { + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_stop_rx_stream(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + for (int i = 0; i < h->nof_cards; i++) { + rf_skiq_card_stop_rx_streaming(&h->cards[i]); + } + + return SRSRAN_SUCCESS; +} + +static int rf_skiq_send_end_of_burst(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + for (int i = 0; i < h->nof_cards; i++) { + rf_skiq_card_end_of_burst(&h->cards[i]); + } + + return SRSRAN_SUCCESS; +} + +void rf_skiq_flush_buffer(void* h) +{ + SKIQ_RF_INFO("Flushing buffers...\n"); + + int n; + void* data[SKIQ_MAX_CHANNELS] = {}; + do { + n = rf_skiq_recv_with_time_multi(h, data, 1024, 0, NULL, NULL); + } while (n > 0); +} + +bool rf_skiq_has_rssi(void* h) +{ + return false; +} + +float rf_skiq_get_rssi(void* h) +{ + return 0.0f; +} + +int rf_skiq_open(char* args, void** h) +{ + return rf_skiq_open_multi(args, h, 1); +} + +void rf_skiq_log_msg(int32_t priority, const char* message) +{ + if (priority <= rf_skiq_logging_level) { + printf("%s", message); + } +} + +int rf_skiq_open_multi(char* args, void** h_, uint32_t nof_channels) +{ + // Check number of antennas bounds + if (nof_channels < SKIQ_MIN_CHANNELS || nof_channels > SKIQ_MAX_CHANNELS) { + ERROR("Number of channels (%d) not supported (%d-%d)\n", nof_channels, SKIQ_MIN_CHANNELS, SKIQ_MAX_CHANNELS); + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + rf_skiq_handler_t* h = (rf_skiq_handler_t*)calloc(1, sizeof(rf_skiq_handler_t)); + if (!h) { + return SRSRAN_ERROR; + } + *h_ = h; + + // Parse main parameters + parse_uint32(args, "nof_cards", 0, &h->nof_cards); + parse_uint32(args, "nof_ports", 0, &h->nof_ports); + + char log_level[RF_PARAM_LEN] = "info"; + parse_string(args, "log_level", 0, log_level); + if (strcmp(log_level, "info") == 0) { + rf_skiq_logging_level = SKIQ_LOG_INFO; + } else if (strcmp(log_level, "debug") == 0) { + rf_skiq_logging_level = SKIQ_LOG_DEBUG; + } else if (strcmp(log_level, "warn") == 0) { + rf_skiq_logging_level = SKIQ_LOG_WARNING; + } else if (strcmp(log_level, "error") == 0) { + rf_skiq_logging_level = SKIQ_LOG_ERROR; + } else { + ERROR("Error log_level %s is undefined. Options: debug, info, warn and error\n", log_level); + return SRSRAN_ERROR; + } + + // Register Logger + skiq_register_logging(&rf_skiq_log_msg); + + // Get available cards + uint8_t nof_available_cards = 0; + uint8_t available_cards[SKIQ_MAX_NUM_CARDS] = {}; + if (skiq_get_cards(skiq_xport_type_auto, &nof_available_cards, available_cards)) { + ERROR("Getting available cards\n"); + return SRSRAN_ERROR; + } + + // + if (h->nof_cards == 0 && h->nof_ports == 0) { + if (nof_channels <= (uint32_t)nof_available_cards) { + // One channel per card + h->nof_cards = nof_channels; + h->nof_ports = 1; + } else if (nof_channels <= RF_SKIQ_MAX_PORTS_CARD) { + // One channel per port + h->nof_cards = 1; + h->nof_ports = nof_channels; + } else if (nof_channels % RF_SKIQ_MAX_PORTS_CARD == 0) { + // use all ports + h->nof_cards = nof_channels / RF_SKIQ_MAX_PORTS_CARD; + h->nof_ports = RF_SKIQ_MAX_PORTS_CARD; + } else if (nof_channels % nof_available_cards == 0) { + // use all cards + h->nof_cards = nof_available_cards; + h->nof_ports = nof_channels / nof_available_cards; + } else { + ERROR("Error deducing the number of cards and ports"); + } + } else if (h->nof_ports == 0 && nof_channels % h->nof_cards == 0) { + h->nof_ports = nof_channels / h->nof_cards; + } else if (h->nof_cards == 0 && nof_channels % h->nof_ports == 0) { + h->nof_cards = nof_channels / h->nof_ports; + } + + if (h->nof_cards == 0 || h->nof_cards > nof_available_cards) { + ERROR("Error invalid number of cards %d, available %d\n", h->nof_cards, nof_available_cards); + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + if (h->nof_ports == 0 || h->nof_ports > RF_SKIQ_MAX_PORTS_CARD) { + ERROR("Error invalid number of cards %d, available %d\n", h->nof_cards, nof_available_cards); + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + // Create default port options + rf_skiq_port_opts_t port_opts = {}; + port_opts.tx_rb_size = 2048; + port_opts.rx_rb_size = 2048; + port_opts.chan_mode = (h->nof_ports > 1) ? skiq_chan_mode_dual : skiq_chan_mode_single; + port_opts.stream_mode = skiq_rx_stream_mode_balanced; + + // Parse other options + parse_uint32(args, "tx_rb_size", 0, &port_opts.tx_rb_size); + parse_uint32(args, "rx_rb_size", 0, &port_opts.rx_rb_size); + parse_string(args, "mode", 0, port_opts.stream_mode_str); + + if (strlen(port_opts.stream_mode_str) > 0) { + if (strcmp(port_opts.stream_mode_str, "low_latency") == 0) { + port_opts.stream_mode = skiq_rx_stream_mode_low_latency; + } else if (strcmp(port_opts.stream_mode_str, "balanced") == 0) { + port_opts.stream_mode = skiq_rx_stream_mode_balanced; + } else if (strcmp(port_opts.stream_mode_str, "high_tput") == 0) { + port_opts.stream_mode = skiq_rx_stream_mode_high_tput; + } else { + ERROR("Invalid mode: %s; Valid modes are: low_latency, balanced, high_tput\n", port_opts.stream_mode_str); + return SRSRAN_ERROR; + } + } + + SKIQ_RF_INFO("Opening %d SKIQ cards with %d ports...\n", h->nof_cards, h->nof_ports); + + if (pthread_mutex_init(&h->mutex_rx, NULL)) { + ERROR("Error initialising mutex\n"); + return SRSRAN_ERROR; + } + + // Initialise driver + if (skiq_init(skiq_xport_type_auto, skiq_xport_init_level_full, available_cards, h->nof_cards)) { + ERROR("Unable to initialise libsidekiq driver\n"); + return SRSRAN_ERROR; + } + + // Initialise each card + for (uint32_t i = 0; i < h->nof_cards; i++) { + if (rf_skiq_card_init(&h->cards[i], available_cards[i], h->nof_ports, &port_opts)) { + return SRSRAN_ERROR; + } + } + + // Parse default frequencies + for (uint32_t i = 0, ch = 0; i < h->nof_cards; i++) { + for (uint32_t j = 0; j < h->nof_ports; j++, ch++) { + double tx_freq = 0.0; + parse_double(args, "tx_freq", ch, &tx_freq); + + if (isnormal(tx_freq)) { + rf_skiq_set_tx_freq(h, ch, tx_freq); + } + double rx_freq = 0.0; + parse_double(args, "rx_freq", ch, &rx_freq); + + if (isnormal(rx_freq)) { + rf_skiq_set_rx_freq(h, ch, rx_freq); + } + } + } + + // Set a default gain + rf_skiq_set_rx_gain(h, SKIQ_RX_GAIN_DEFAULT_dB); + + // Parse default sample rate + double srate_hz = 0.0; + parse_double(args, "srate", 0, &srate_hz); + srate_hz = isnormal(srate_hz) ? srate_hz : SKIQ_DEFAULT_SAMPLING_RATE_HZ; + + // Set a default sampling rate, default can be too low + rf_skiq_set_tx_srate(h, srate_hz); + rf_skiq_set_rx_srate(h, srate_hz); + + return SRSRAN_SUCCESS; +} + +int rf_skiq_close(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + SKIQ_RF_INFO("Closing...\n"); + + // Ensure Tx/Rx streaming is stopped + rf_skiq_send_end_of_burst(h); + rf_skiq_stop_rx_stream(h); + + // Free all open cards + for (int i = 0; i < h->nof_cards; i++) { + rf_skiq_card_close(&h->cards[i]); + } + + // Close sidekiq SDK + skiq_exit(); + + pthread_mutex_destroy(&h->mutex_rx); + + // Deallocate object memory + if (h != NULL) { + free(h); + } + + return SRSRAN_SUCCESS; +} + +static double rf_skiq_set_srate_hz(rf_skiq_handler_t* h, double srate_hz) +{ + // If the sampling rate is not modified dont bother + if (h->current_srate_hz == srate_hz) { + return srate_hz; + } + SKIQ_RF_INFO("Setting sampling rate to %.2f MHz ...\n", srate_hz / 1e6); + + // Save streaming state + bool is_streaming = rf_skiq_is_streaming(h); + + // Stop streaming + SKIQ_RF_INFO(" ... Stop Tx/Rx streaming\n"); + rf_skiq_send_end_of_burst(h); + rf_skiq_stop_rx_stream(h); + + // Set sampling + SKIQ_RF_INFO(" ... Setting sampling rates to %.2f MHz\n", srate_hz / 1e6); + pthread_mutex_lock(&h->mutex_rx); + for (uint32_t i = 0; i < h->nof_cards; i++) { + rf_skiq_card_set_srate_hz(&h->cards[i], (uint32_t)srate_hz); + } + pthread_mutex_unlock(&h->mutex_rx); + + // Start streaming if it was started + if (is_streaming) { + SKIQ_RF_INFO(" ... Start Rx streaming\n"); + rf_skiq_start_rx_stream(h, true); + } + + // Update current sampling rate + h->current_srate_hz = srate_hz; + SKIQ_RF_INFO(" ... Done!\n"); + + return srate_hz; +} + +double rf_skiq_set_rx_srate(void* h, double sample_rate) +{ + return rf_skiq_set_srate_hz((rf_skiq_handler_t*)h, sample_rate); +} + +double rf_skiq_set_tx_srate(void* h, double sample_rate) +{ + return rf_skiq_set_srate_hz((rf_skiq_handler_t*)h, sample_rate); +} + +int rf_skiq_set_rx_gain(void* h_, double rx_gain) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + for (uint32_t i = 0; i < h->nof_cards; i++) { + rf_skiq_card_set_rx_gain_db(&h->cards[i], h->nof_ports, rx_gain); + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_set_tx_gain(void* h_, double tx_gain) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + for (uint32_t i = 0; i < h->nof_cards; i++) { + h->cur_tx_gain = rf_skiq_card_set_tx_gain_db(&h->cards[i], h->nof_ports, tx_gain); + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_set_tx_gain_ch(void* h_, uint32_t ch, double tx_gain) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + uint32_t card_idx = ch / h->nof_ports; + uint32_t port_idx = ch % h->nof_ports; + + if (card_idx >= h->nof_cards || port_idx >= h->nof_ports) { + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + tx_gain = rf_skiq_card_set_tx_gain_db(&h->cards[card_idx], port_idx, tx_gain); + + if (ch == 0) { + h->cur_tx_gain = tx_gain; + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_set_rx_gain_ch(void* h_, uint32_t ch, double rx_gain) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + uint32_t card_idx = ch / h->nof_ports; + uint32_t port_idx = ch % h->nof_ports; + + if (card_idx >= h->nof_cards || port_idx >= h->nof_ports) { + return SRSRAN_ERROR_OUT_OF_BOUNDS; + } + + rx_gain = rf_skiq_card_set_rx_gain_db(&h->cards[card_idx], port_idx, rx_gain); + + return SRSRAN_SUCCESS; +} + +double rf_skiq_get_rx_gain(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + return h->cards[0].cur_rx_gain_db; +} + +double rf_skiq_get_tx_gain(void* h_) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + return h->cur_tx_gain; +} + +srsran_rf_info_t* rf_skiq_get_info(void* h_) +{ + srsran_rf_info_t* ret = NULL; + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + if (h != NULL) { + ret = &h->info; + + ret->min_tx_gain = 0.25 * (double)h->cards[0].param.tx_param->atten_quarter_db_max; + ret->max_tx_gain = 0.25 * (double)h->cards[0].param.tx_param->atten_quarter_db_min; + ret->min_rx_gain = h->cards[0].param.rx_param[0].gain_index_min; + ret->max_rx_gain = h->cards[0].param.rx_param[0].gain_index_max; + } + + return ret; +} + +double rf_skiq_set_rx_freq(void* h_, uint32_t ch, double freq) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + uint32_t card_idx = ch / h->nof_ports; + uint32_t port_idx = ch % h->nof_ports; + +#pragma message "TODO: The Rx stream needs to stop, RF timestamp shall be aligned with other cards and start again" + + if (card_idx < h->nof_cards && port_idx < h->nof_ports) { + return rf_skiq_card_set_rx_freq_hz(&h->cards[card_idx], port_idx, freq); + } + + return freq; +} + +double rf_skiq_set_tx_freq(void* h_, uint32_t ch, double freq) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + + uint32_t card_idx = ch / h->nof_ports; + uint32_t port_idx = ch % h->nof_ports; + +#pragma message "TODO: The Rx stream needs to stop, RF timestamp shall be aligned with other cards and start again" + + if (card_idx < h->nof_cards && port_idx < h->nof_ports) { + return rf_skiq_card_set_tx_freq_hz(&h->cards[card_idx], port_idx, freq); + } + + return freq; +} + +void tstamp_to_time(rf_skiq_handler_t* h, uint64_t tstamp, time_t* secs, double* frac_secs) +{ + uint64_t srate_hz = (uint64_t)h->current_srate_hz; + + if (srate_hz == 0) { + ERROR("Warning: Sampling rate has not been set yet.\n"); + srate_hz = UINT64_MAX; + } + + if (secs) { + *secs = (time_t)tstamp / srate_hz; + } + if (frac_secs) { + uint64_t rem = tstamp % srate_hz; + *frac_secs = (double)rem / h->current_srate_hz; + } +} + +uint64_t time_to_tstamp(rf_skiq_handler_t* h, time_t secs, double frac_secs) +{ + return secs * h->current_srate_hz + frac_secs * h->current_srate_hz; +} + +void rf_skiq_get_time(void* h_, time_t* secs, double* frac_secs) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + uint64_t tstamp = rf_skiq_card_read_rf_timestamp(&h->cards[0]); + tstamp_to_time(h, tstamp, secs, frac_secs); +} + +static int +rf_skiq_discard_rx_samples(rf_skiq_handler_t* h, uint32_t card, uint32_t port, uint32_t nsamples, uint64_t* ts_start) +{ + *ts_start = 0; + while (nsamples > 0) { + uint64_t ts = 0; + + // Receive in dummy buffer + int32_t n = rf_skiq_card_receive( + &h->cards[card], port, h->dummy_buffer, SRSRAN_MIN(nsamples, RF_SKIQ_DUMMY_BUFFER_SIZE), &ts); + + // Check for error + if (n < 0) { + ERROR("An error occurred discarding %d Rx samples for channel %d:%d\n", nsamples, card, port); + return n; + } + + // Save first timestamp + if (*ts_start == 0) { + *ts_start = ts; + } + + // Decrement pending samples + nsamples -= n; + } + + return nsamples; +} + +static int rf_skiq_synch_rx_ports(rf_skiq_handler_t* h) +{ + int64_t tstamp_min = INT64_MAX; + int64_t tstamp_max = 0; + + // no need to synchronize + if (h->nof_cards * h->nof_ports < 2) { + return SRSRAN_SUCCESS; + } + + // Find minimum and maximum next timestamps + for (uint32_t card = 0; card < h->nof_cards; card++) { + // Iterate for all ports + for (uint32_t port = 0; port < h->nof_ports; port++) { + int64_t ts = (int64_t)rf_skiq_card_get_rx_timestamp(&h->cards[card], port); + + // If the card is not streaming or suspended will return TS 0; so skip + if (ts == 0UL) { + continue; + } + + // Is minimum? + tstamp_min = SRSRAN_MIN(tstamp_min, ts); + + // Is maximum? + tstamp_max = SRSRAN_MAX(tstamp_max, ts); + } + } + + // Check if any synchronization is required + if (tstamp_max == tstamp_min) { + return SRSRAN_SUCCESS; + } + + // Align all channels to the maximum timestamp + for (uint32_t card = 0; card < h->nof_cards; card++) { + // Iterate for all ports + for (uint32_t port = 0; port < h->nof_ports; port++) { + uint64_t ts = rf_skiq_card_get_rx_timestamp(&h->cards[card], port); + + // If the card is not streaming or suspended will return TS 0; so skip + if (ts == 0UL) { + continue; + } + + // Calculate number of samples + int nsamples = (int)(tstamp_max - (int64_t)ts); + + // Skip port if negative or zero (possible if stream is enabled during this time) + if (nsamples <= 0) { + continue; + } + + // Too many samples, sign of some extreme error + if (nsamples > SKIQ_PORT_SYNC_MAX_GAP) { + ERROR("too many samples to align (%d) for channel %d:%d\n", nsamples, card, port); + return SRSRAN_ERROR; + } + + ts = 0; + if (rf_skiq_discard_rx_samples(h, card, port, nsamples, &ts) < SRSRAN_SUCCESS) { + ERROR("Error occurred during Rx streams alignment."); + return SRSRAN_ERROR; + } + } + } + + return SRSRAN_SUCCESS; +} + +int rf_skiq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) +{ + return rf_skiq_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); +} + +int rf_skiq_recv_with_time_multi(void* h_, + void** data_, + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + uint64_t ts_start = 0; + cf_t** data = (cf_t**)data_; + + pthread_mutex_lock(&h->mutex_rx); + + // Perform channel synchronization + rf_skiq_synch_rx_ports(h); + + bool completed = false; + uint32_t count[SKIQ_MAX_CHANNELS] = {}; + + while (!completed) { + // Completion true by default + completed = true; + + // Iterate over cards + for (uint32_t card = 0, chan = 0; card < h->nof_cards; card++) { + // Iterate over ports + for (uint32_t port = 0; port < h->nof_ports; port++, chan++) { + // Calculate number of pending samples + uint32_t pending = nsamples - count[chan]; + + if (pending > 0) { + uint64_t ts = 0; + int n = 0; + + // If data is not provided... + if (data[chan] == NULL) { + // ... discard up to RF_SKIQ_DUMMY_BUFFER_SIZE samples + n = rf_skiq_card_receive( + &h->cards[card], port, h->dummy_buffer, SRSRAN_MIN(pending, RF_SKIQ_DUMMY_BUFFER_SIZE), &ts); + } else { + // ... read base-band + n = rf_skiq_card_receive(&h->cards[card], port, &data[chan][count[chan]], pending, &ts); + } + + // If error is detected, return it + if (n < SRSRAN_SUCCESS) { + pthread_mutex_unlock(&h->mutex_rx); + return n; + } + + // Save first valid timestamp + if (ts_start == 0) { + ts_start = ts; + } + + // Increment count for the channel + count[chan] += n; + + // Lower completed flag if at least a channel has not reach the target + if (count[chan] < nsamples) { + completed = false; + } + } + } + } + } + + pthread_mutex_unlock(&h->mutex_rx); + + // Convert u64 to srsran timestamp + tstamp_to_time(h, ts_start, secs, frac_secs); + + // No error, return number of received samples + return nsamples; +} + +int rf_skiq_send_timed(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + void* _data[SRSRAN_MAX_PORTS] = {}; + _data[0] = data; + + return rf_skiq_send_timed_multi( + h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst); +} + +int rf_skiq_send_timed_multi(void* h_, + void* data_[SRSRAN_MAX_PORTS], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst) +{ + rf_skiq_handler_t* h = (rf_skiq_handler_t*)h_; + cf_t** data = (cf_t**)data_; + + // Force timestamp only if start of burst + if (is_start_of_burst) { + if (has_time_spec) { + h->next_tstamp = time_to_tstamp(h, secs, frac_secs); + SKIQ_RF_DEBUG("[Tx SOB] ts=%ld\n", h->next_tstamp); + } else { + h->next_tstamp = rf_skiq_card_read_rf_timestamp(&h->cards[0]); + h->next_tstamp += (uint64_t)round(h->current_srate_hz / 10); // increment a 10th of a second + } + } + + uint32_t rpm = 0; + bool completed = false; + uint32_t count[SKIQ_MAX_CHANNELS] = {}; + + while (!completed) { + // Completion true by default + completed = true; + + // Iterate all cards... + for (uint32_t card = 0, chan = 0; card < h->nof_cards; card++) { + // Iterate all ports... + for (uint32_t port = 0; port < h->nof_ports; port++, chan++) { + // Calculate number of pending samples + uint32_t pending = nsamples - count[chan]; + + if (pending > 0) { + uint64_t ts = h->next_tstamp + count[chan]; + cf_t* ptr = (data[chan] == NULL) ? NULL : &data[chan][count[chan]]; + int n = rf_skiq_card_send(&h->cards[card], port, ptr, pending, ts); + if (n > SRSRAN_SUCCESS) { + count[chan] += n; + } + + if (count[chan] < nsamples) { + completed = false; + } + } + } + } + } + + // Increment timestamp + h->next_tstamp += nsamples; + + if (is_end_of_burst) { + rf_skiq_send_end_of_burst(h); + } + + return (int)rpm; +} + +rf_dev_t srsran_rf_dev_skiq = {.name = "Sidekiq", + .srsran_rf_devname = rf_skiq_devname, + .srsran_rf_start_rx_stream = rf_skiq_start_rx_stream, + .srsran_rf_stop_rx_stream = rf_skiq_stop_rx_stream, + .srsran_rf_flush_buffer = rf_skiq_flush_buffer, + .srsran_rf_has_rssi = rf_skiq_has_rssi, + .srsran_rf_get_rssi = rf_skiq_get_rssi, + .srsran_rf_suppress_stdout = rf_skiq_suppress_stdout, + .srsran_rf_register_error_handler = rf_skiq_register_error_handler, + .srsran_rf_open = rf_skiq_open, + .srsran_rf_open_multi = rf_skiq_open_multi, + .srsran_rf_close = rf_skiq_close, + .srsran_rf_set_rx_srate = rf_skiq_set_rx_srate, + .srsran_rf_set_tx_srate = rf_skiq_set_tx_srate, + .srsran_rf_set_rx_gain = rf_skiq_set_rx_gain, + .srsran_rf_set_tx_gain = rf_skiq_set_tx_gain, + .srsran_rf_set_tx_gain_ch = rf_skiq_set_tx_gain_ch, + .srsran_rf_set_rx_gain_ch = rf_skiq_set_rx_gain_ch, + .srsran_rf_get_rx_gain = rf_skiq_get_rx_gain, + .srsran_rf_get_tx_gain = rf_skiq_get_tx_gain, + .srsran_rf_get_info = rf_skiq_get_info, + .srsran_rf_set_rx_freq = rf_skiq_set_rx_freq, + .srsran_rf_set_tx_freq = rf_skiq_set_tx_freq, + .srsran_rf_get_time = rf_skiq_get_time, + .srsran_rf_recv_with_time = rf_skiq_recv_with_time, + .srsran_rf_recv_with_time_multi = rf_skiq_recv_with_time_multi, + .srsran_rf_send_timed = rf_skiq_send_timed, + .srsran_rf_send_timed_multi = rf_skiq_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_skiq; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_skiq_imp.h b/lib/src/phy/rf/rf_skiq_imp.h new file mode 100644 index 000000000..2283ed019 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp.h @@ -0,0 +1,106 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include + +#include "srsran/config.h" +#include "srsran/phy/rf/rf.h" + +extern rf_dev_t srsran_rf_dev_skiq; + +SRSRAN_API int rf_skiq_open(char* args, void** handler); + +SRSRAN_API int rf_skiq_open_multi(char* args, void** handler, uint32_t nof_rx_antennas); + +SRSRAN_API const char* rf_skiq_devname(void* h); + +SRSRAN_API int rf_skiq_close(void* h); + +SRSRAN_API int rf_skiq_start_rx_stream(void* h, bool now); + +SRSRAN_API int rf_skiq_start_rx_stream_nsamples(void* h, uint32_t nsamples); + +SRSRAN_API int rf_skiq_stop_rx_stream(void* h); + +SRSRAN_API void rf_skiq_flush_buffer(void* h); + +SRSRAN_API bool rf_skiq_has_rssi(void* h); + +SRSRAN_API float rf_skiq_get_rssi(void* h); + +SRSRAN_API void rf_skiq_set_master_clock_rate(void* h, double rate); + +SRSRAN_API bool rf_skiq_is_master_clock_dynamic(void* h); + +SRSRAN_API double rf_skiq_set_rx_srate(void* h, double freq); + +SRSRAN_API int rf_skiq_set_rx_gain(void* h, double gain); + +SRSRAN_API int rf_skiq_set_rx_gain_ch(void* h_, uint32_t ch, double rx_gain); + +SRSRAN_API double rf_skiq_get_rx_gain(void* h); + +SRSRAN_API double rf_skiq_get_tx_gain(void* h); + +SRSRAN_API srsran_rf_info_t* rf_skiq_get_info(void* h); + +SRSRAN_API void rf_skiq_suppress_stdout(void* h); + +SRSRAN_API void rf_skiq_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg); + +SRSRAN_API double rf_skiq_set_rx_freq(void* h, uint32_t ch, double freq); + +SRSRAN_API int +rf_skiq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs); + +SRSRAN_API int +rf_skiq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs); + +SRSRAN_API double rf_skiq_set_tx_srate(void* h, double freq); + +SRSRAN_API int rf_skiq_set_tx_gain(void* h, double gain); + +SRSRAN_API int rf_skiq_set_tx_gain_ch(void* h, uint32_t ch, double gain); + +SRSRAN_API double rf_skiq_set_tx_freq(void* h, uint32_t ch, double freq); + +SRSRAN_API void rf_skiq_get_time(void* h, time_t* secs, double* frac_secs); + +SRSRAN_API int rf_skiq_send_timed(void* h, + void* data, + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); + +SRSRAN_API int rf_skiq_send_timed_multi(void* h, + void* data[4], + int nsamples, + time_t secs, + double frac_secs, + bool has_time_spec, + bool blocking, + bool is_start_of_burst, + bool is_end_of_burst); diff --git a/lib/src/phy/rf/rf_skiq_imp_card.c b/lib/src/phy/rf/rf_skiq_imp_card.c new file mode 100644 index 000000000..d2ddd1a9a --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_card.c @@ -0,0 +1,566 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rf_skiq_imp_card.h" + +static void* reader_thread(void* arg) +{ + rf_skiq_card_t* q = (rf_skiq_card_t*)arg; + + while (q->state != RF_SKIQ_PORT_STATE_STOP) { + // Wait to leave idle state + if (q->state == RF_SKIQ_PORT_STATE_IDLE) { + SKIQ_RF_INFO("[Rx %d] IDLE\n", q->card); + pthread_mutex_lock(&q->mutex); + while (q->state == RF_SKIQ_PORT_STATE_IDLE) { + pthread_cond_wait(&q->cvar, &q->mutex); + } + pthread_mutex_unlock(&q->mutex); + SKIQ_RF_INFO("[Rx %d] %s\n", q->card, q->state == RF_SKIQ_PORT_STATE_STREAMING ? "STREAMING" : "STOP"); + } + + // Check exit condition + if (q->state == RF_SKIQ_PORT_STATE_STOP) { + break; + } + + skiq_rx_hdl_t curr_rx_hdl = 0; + skiq_rx_block_t* p_rx_block = NULL; + uint32_t len = 0; + skiq_rx_status_t rx_status = skiq_receive(q->card, &curr_rx_hdl, &p_rx_block, &len); + + switch (rx_status) { + case skiq_rx_status_success: + if (curr_rx_hdl < q->nof_ports && p_rx_block != NULL && len > SKIQ_RX_HEADER_SIZE_IN_BYTES) { + // Convert number of bytes into samples + uint32_t nsamples = len / 4 - SKIQ_RX_HEADER_SIZE_IN_WORDS; + + // Give block to the port + rf_skiq_rx_port_write(&q->rx_ports[curr_rx_hdl], p_rx_block, nsamples); + } else { + ERROR("Card %d received data with corrupted pointers\n", q->card); + } + break; + case skiq_rx_status_no_data: + // Do nothing + break; + case skiq_rx_status_error_generic: + ERROR("Error: Generic error occurred during skiq_receive.\n"); + break; + case skiq_rx_status_error_overrun: + ERROR("Error: overrun error occurred during skiq_receive.\n"); + break; + case skiq_rx_status_error_packet_malformed: + ERROR("Error: packet malformed error occurred during skiq_receive.\n"); + break; + case skiq_rx_status_error_card_not_active: + ERROR("Error: inactive card error occurred during skiq_receive.\n"); + break; + case skiq_rx_status_error_not_streaming: + ERROR("Error: not streaming card error occurred during skiq_receive.\n"); + break; + default: + ERROR("Error: the impossible happened during skiq_receive.\n"); + break; + } + } + + SKIQ_RF_INFO("Exiting reader thread!\n"); + + return NULL; +} + +int rf_skiq_card_init(rf_skiq_card_t* q, uint8_t card, uint8_t nof_ports, const rf_skiq_port_opts_t* opts) +{ + q->card = card; + q->nof_ports = nof_ports; + + // Reprogram FPGA to reset all states + if (skiq_prog_fpga_from_flash(card)) { + ERROR("Error programming card %d from flash\n", q->card); + return SRSRAN_ERROR; + } + + // Read card parameters + if (skiq_read_parameters(card, &q->param)) { + ERROR("Reading card %d param", card); + return SRSRAN_ERROR; + } + + // Check number of rx channels + if (q->param.rf_param.num_rx_channels < nof_ports) { + ERROR("Card %d does not support %d Rx channels", card, nof_ports); + return SRSRAN_ERROR; + } + + // Check number of tx channels + if (q->param.rf_param.num_tx_channels < nof_ports) { + ERROR("Card %d does not support %d Tx channels", card, nof_ports); + return SRSRAN_ERROR; + } + + // set a modest rx timeout + if (skiq_set_rx_transfer_timeout(card, 1000)) { + ERROR("Setting Rx transfer timeout"); + return SRSRAN_ERROR; + } + + // do not pack 12bit samples + if (skiq_write_iq_pack_mode(card, false)) { + ERROR("Setting Rx IQ pack mode"); + return SRSRAN_ERROR; + } + + // set the control output bits to include the gain + if (skiq_write_rfic_control_output_config( + card, RFIC_CONTROL_OUTPUT_MODE_GAIN_CONTROL_RXA1, RFIC_CONTROL_OUTPUT_MODE_GAIN_BITS) != 0) { + ERROR("Unable to configure card %d the RF IC control output (A1)", card); + return SRSRAN_ERROR; + } + + // set RX channel mode + if (skiq_write_chan_mode(card, q->mode)) { + ERROR("Setting card %d channel mode", card); + return SRSRAN_ERROR; + } + + // Select Rx streaming mode to low latency if the sampling rate is lower than 5MHz + if (skiq_write_rx_stream_mode(q->card, opts->stream_mode)) { + ERROR("Error setting Rx stream mode\n"); + return SRSRAN_ERROR; + } + + // initialise tx/rx ports + for (uint8_t i = 0; i < nof_ports; i++) { + if (rf_skiq_tx_port_init(&q->tx_ports[i], card, (skiq_tx_hdl_t)i, opts)) { + ERROR("Initiating card %d, Tx port %d", card, i); + return SRSRAN_ERROR; + } + + if (rf_skiq_rx_port_init(&q->rx_ports[i], card, (skiq_rx_hdl_t)i, opts)) { + ERROR("Initiating card %d, Rx port %d", card, i); + return SRSRAN_ERROR; + } + } + + if (pthread_mutex_init(&q->mutex, NULL)) { + ERROR("Initiating mutex"); + return SRSRAN_ERROR; + } + + if (pthread_cond_init(&q->cvar, NULL)) { + ERROR("Initiating cvar"); + return SRSRAN_ERROR; + } + + // Initialise thread parameters + pthread_attr_t attr; + struct sched_param param; + + param.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_init(&attr); + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + if (pthread_attr_setschedparam(&attr, ¶m)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + // Launch thread + if (pthread_create(&q->thread, &attr, reader_thread, q)) { + ERROR("Error creating reader thread with attributes (Did you miss sudo?). Trying without attributes.\n"); + + // try to create thread without attributes + pthread_attr_destroy(&attr); + if (pthread_create(&q->thread, NULL, reader_thread, q)) { + ERROR("Error creating reader thread, even without thread attributes. Exiting.\n"); + return SRSRAN_ERROR; + } + } + + // Rename thread + char thread_name[32] = {}; + if (snprintf(thread_name, sizeof(thread_name), "SKIQ Rx %d", q->card) > 0) { + pthread_setname_np(q->thread, thread_name); + } + + return SRSRAN_SUCCESS; +} + +void rf_skiq_card_set_error_handler(rf_skiq_card_t* q, srsran_rf_error_handler_t error_handler, void* arg) +{ + for (uint32_t i = 0; i < q->nof_ports; i++) { + rf_skiq_tx_port_set_error_handler(&q->tx_ports[i], error_handler, arg); + rf_skiq_rx_port_set_error_handler(&q->rx_ports[i], error_handler, arg); + } +} + +double rf_skiq_card_set_tx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db) +{ + double max_atten_dB = 0.25 * q->param.tx_param->atten_quarter_db_max; + double min_atten_dB = 0.25 * q->param.tx_param->atten_quarter_db_min; + + // Calculate attenuation: + // - 0dB attenuation -> Maximum gain; + // - 0dB gain -> Maximum attenuation; + double att_dB = max_atten_dB - gain_db; + + // Check gain range + if (att_dB < min_atten_dB || att_dB > max_atten_dB) { + ERROR("Error port %d:%d the selected gain (%.2f dB) is out of range (%.2f to %.2f dB).\n", + q->card, + port_idx, + gain_db, + min_atten_dB, + max_atten_dB); + } + + // Calculate attenuation index + uint16_t att_index = (uint16_t)floor(att_dB * 4); + + // Bound index + att_index = SRSRAN_MIN(SRSRAN_MAX(att_index, q->param.tx_param->atten_quarter_db_min), + q->param.tx_param->atten_quarter_db_max); + + // Calculate equivalent gain + double actual_gain_dB = max_atten_dB - att_index * 0.25; + + // Set gain per port + if (port_idx >= q->nof_ports) { + for (uint8_t i = 0; i < q->nof_ports; i++) { + if (skiq_write_tx_attenuation(q->card, (skiq_tx_hdl_t)i, att_index)) { + ERROR("Error setting card %d:%d Tx attenuation\n", q->card, i); + } + } + } else { + if (skiq_write_tx_attenuation(q->card, (skiq_tx_hdl_t)port_idx, att_index)) { + ERROR("Error setting card %d:%d Tx attenuation\n", q->card, port_idx); + } + } + + return actual_gain_dB; +} + +double rf_skiq_card_set_rx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db) +{ + // From Sidekiq API doc: + // + // For Sidekiq mPCIe (skiq_mpcie), Sidekiq M.2 (skiq_m2), Sidekiq Stretch (skiq_m2_2280), Sidekiq Z2 + //(skiq_z2), and Matchstiq Z3u (skiq_z3u) each increment of the gain index value results in approxi- + // mately 1 dB of gain, with approximately 76 dB of total gain available. For details on the gain table, + // refer to p. 37 of AD9361 Reference Manual UG-570 + + // Check gain range + if (gain_db < q->param.rx_param->gain_index_min || gain_db > q->param.rx_param->gain_index_max) { + ERROR("Error port %d:%d the selected gain (%.2f dB) is out of range (%d to %d dB).\n", + q->card, + port_idx, + gain_db, + q->param.rx_param->gain_index_min, + q->param.rx_param->gain_index_max); + } + + // Calculate attenuation index + uint16_t gain_idx = (uint16_t)floor(gain_db); + + if (port_idx < q->nof_ports) { + // Set single port gain + skiq_write_rx_gain(q->card, (skiq_rx_hdl_t)port_idx, gain_idx); + } else { + // Set all gains + for (int i = 0; i < q->nof_ports; i++) { + skiq_write_rx_gain(q->card, (skiq_rx_hdl_t)i, gain_idx); + } + } + + // Update current rx_gain + q->cur_rx_gain_db = gain_db; + + return gain_db; +} + +int rf_skiq_card_update_timestamp(rf_skiq_card_t* q, bool use_1pps, uint64_t new_ts) +{ + if (use_1pps) { + // Read 1pps source + skiq_1pps_source_t pps_source = skiq_1pps_source_unavailable; + if (skiq_read_1pps_source(q->card, &pps_source)) { + ERROR("Error reading card %d 1PPS source\n", q->card); + return SRSRAN_ERROR; + } + + // Make sure the source is external + if (pps_source != skiq_1pps_source_external) { + ERROR("Error card %d is not configured with external 1PPS source\n", q->card); + return SRSRAN_ERROR; + } + + // Get last time a PPS was received + uint64_t ts_sys_1pps = 0; + if (skiq_read_last_1pps_timestamp(q->card, NULL, &ts_sys_1pps)) { + ERROR("Reading card %d last 1PPS timestamp", q->card); + return SRSRAN_ERROR; + } + + // Read current system time + uint64_t ts = 0; + if (skiq_read_curr_sys_timestamp(q->card, &ts)) { + ERROR("Reading card %d system timestamp", q->card); + return SRSRAN_ERROR; + } + + // Make sure a 1PPS was received less than 2 seconds ago + if (ts - ts_sys_1pps > 2 * SKIQ_SYS_TIMESTAMP_FREQ) { + ERROR("Error card %d last PPS was received %.1f seconds ago (%ld - %ld)\n", + q->card, + (double)(ts - ts_sys_1pps) / (double)SKIQ_SYS_TIMESTAMP_FREQ, + ts, + ts_sys_1pps); + return SRSRAN_ERROR; + } + + // Set a given time in the future, a 100th of a second (10ms) + ts += SKIQ_SYS_TIMESTAMP_FREQ / 100; + + // Order that all timestamps are reseted when next 1PPS signal is received + SKIQ_RF_INFO(" ... Resetting card %d system timestamp on next PPS\n", q->card); + if (skiq_write_timestamp_update_on_1pps(q->card, ts, new_ts)) { + ERROR("Error reseting card %d timestamp on 1 PPS", q->card); + return SRSRAN_ERROR; + } + } else { + // Simply, reset timestamp + SKIQ_RF_INFO(" ... Resetting card %d system timestamp now\n", q->card); + if (skiq_update_timestamps(q->card, new_ts)) { + ERROR("Error resetting card %d timestamp", q->card); + return SRSRAN_ERROR; + } + } + return SRSRAN_SUCCESS; +} + +uint64_t rf_skiq_card_read_sys_timestamp(rf_skiq_card_t* q) +{ + uint64_t ts = 0UL; + + if (skiq_read_curr_sys_timestamp(q->card, &ts)) { + ERROR("Reading card %d system timestamp", q->card); + } + + return ts; +} + +uint64_t rf_skiq_card_read_rf_timestamp(rf_skiq_card_t* q) +{ + uint64_t ts = 0UL; + + if (skiq_read_curr_rx_timestamp(q->card, skiq_rx_hdl_A1, &ts)) { + ERROR("Reading card %d system timestamp", q->card); + } + + return ts; +} + +int rf_skiq_card_start_rx_streaming(rf_skiq_card_t* q, uint64_t timestamp) +{ + // Wrong state + if (q->state == RF_SKIQ_PORT_STATE_STOP) { + ERROR("Error starting Rx stream: wrong state (%d)\n", q->state); + return SRSRAN_ERROR; + } + + // Already enabled + if (q->state == RF_SKIQ_PORT_STATE_STREAMING) { + SKIQ_RF_INFO("Rx streams in card %d have already started\n", q->card); + return SRSRAN_SUCCESS; + } + + // Make a list with Rx handlers + skiq_rx_hdl_t rx_hdl[RF_SKIQ_MAX_PORTS_CARD]; + for (uint8_t i = 0; i < RF_SKIQ_MAX_PORTS_CARD; i++) { + rx_hdl[i] = (skiq_rx_hdl_t)i; + } + + pthread_mutex_lock(&q->mutex); + + // Start all Rx in a row + if (skiq_start_rx_streaming_multi_on_trigger(q->card, rx_hdl, q->nof_ports, skiq_trigger_src_synced, timestamp)) { + ERROR("Failed to start card %d Rx streaming\n", q->card); + pthread_mutex_unlock(&q->mutex); + return SRSRAN_ERROR; + } + + // Update state and broadcast condition variable + q->state = RF_SKIQ_PORT_STATE_STREAMING; + pthread_cond_broadcast(&q->cvar); + pthread_mutex_unlock(&q->mutex); + + SKIQ_RF_INFO("Rx streams in card %d have started\n", q->card); + + return SRSRAN_SUCCESS; +} + +int rf_skiq_card_stop_rx_streaming(rf_skiq_card_t* q) +{ + if (q->state == RF_SKIQ_PORT_STATE_STOP) { + ERROR("Error stopping Rx stream: wrong state (%d)\n", q->state); + return SRSRAN_ERROR; + } + + // Avoid stop streaming if it was not started + if (q->state == RF_SKIQ_PORT_STATE_IDLE) { + return SRSRAN_ERROR; + } + + // Make a list with Tx/Rx handlers + skiq_rx_hdl_t rx_hdl[RF_SKIQ_MAX_PORTS_CARD]; + for (uint8_t i = 0; i < RF_SKIQ_MAX_PORTS_CARD; i++) { + rx_hdl[i] = (skiq_rx_hdl_t)i; + } + + pthread_mutex_lock(&q->mutex); + + // Update state and broadcast condition variable first + q->state = RF_SKIQ_PORT_STATE_IDLE; + pthread_cond_broadcast(&q->cvar); + + // Stop all Rx in a row + if (skiq_stop_rx_streaming_multi_immediate(q->card, rx_hdl, q->nof_ports)) { + ERROR("Failed to stop card %d Rx streaming\n", q->card); + pthread_mutex_unlock(&q->mutex); + return SRSRAN_ERROR; + } + + pthread_mutex_unlock(&q->mutex); + + SKIQ_RF_INFO("Rx streams in card %d have stopped\n", q->card); + + return SRSRAN_SUCCESS; +} + +void rf_skiq_card_end_of_burst(rf_skiq_card_t* q) +{ + for (uint32_t i = 0; i < q->nof_ports; i++) { + rf_skiq_tx_port_end_of_burst(&q->tx_ports[i]); + } +} + +int rf_skiq_card_set_srate_hz(rf_skiq_card_t* q, uint32_t srate_hz) +{ + for (uint8_t i = 0; i < q->nof_ports; i++) { + // Set transmitter sampling rate + if (skiq_write_tx_sample_rate_and_bandwidth(q->card, (skiq_tx_hdl_t)i, srate_hz, srate_hz)) { + ERROR("Setting Tx sampling rate\n"); + } + + // Set receiver sampling rate + if (skiq_write_rx_sample_rate_and_bandwidth(q->card, (skiq_rx_hdl_t)i, srate_hz, srate_hz)) { + ERROR("Setting Rx sampling rate\n"); + } + + rf_skiq_rx_port_reset(&q->rx_ports[i]); + } + + return SRSRAN_SUCCESS; +} + +double rf_skiq_card_set_tx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz) +{ + q->suspend = true; + rf_skiq_tx_port_set_lo(&q->tx_ports[port_idx], (uint64_t)freq_hz); + q->suspend = false; + + return freq_hz; +} + +double rf_skiq_card_set_rx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz) +{ + q->suspend = true; + rf_skiq_rx_port_set_lo(&q->rx_ports[port_idx], (uint64_t)freq_hz); + q->suspend = false; + return freq_hz; +} + +void rf_skiq_card_close(rf_skiq_card_t* q) +{ + SKIQ_RF_INFO("Closing card %d...\n", q->card); + + // Post stop state to reader thread + q->state = RF_SKIQ_PORT_STATE_STOP; + pthread_cond_broadcast(&q->cvar); + + // Wait for reader thread to finish + pthread_join(q->thread, NULL); + + for (uint8_t i = 0; i < q->nof_ports; i++) { + rf_skiq_rx_port_free(&q->rx_ports[i]); + rf_skiq_tx_port_free(&q->tx_ports[i]); + } + + pthread_cond_destroy(&q->cvar); + pthread_mutex_destroy(&q->mutex); + + // Unlocks all cards + if (skiq_disable_cards(&q->card, 1)) { + ERROR("Unable to disable card %d\n", q->card); + } +} + +int rf_skiq_card_receive(rf_skiq_card_t* q, uint32_t port_idx, cf_t* dst, uint32_t nsamples, uint64_t* ts) +{ + // If suspended and samples are not available, then set all to zero + if (q->suspend && rf_skiq_rx_port_available(&q->rx_ports[port_idx]) == 0) { + srsran_vec_cf_zero(dst, nsamples); + *ts = 0UL; + return nsamples; + } + + return rf_skiq_rx_port_read(&q->rx_ports[port_idx], dst, nsamples, ts); +} + +uint64_t rf_skiq_card_get_rx_timestamp(rf_skiq_card_t* q, uint32_t port_idx) +{ + if (q->suspend || q->rx_ports[port_idx].rb_overflow) { + return 0UL; + } + + return rf_skiq_rx_port_get_timestamp(&q->rx_ports[port_idx]); +} + +bool rf_skiq_card_is_streaming(rf_skiq_card_t* q) +{ + return q->state == RF_SKIQ_PORT_STATE_STREAMING && !q->suspend; +} + +int rf_skiq_card_send(rf_skiq_card_t* q, uint32_t port_idx, const cf_t* data, uint32_t nsamples, uint64_t timestamp) +{ + // If suspended, do not bother the transmitter + if (q->suspend) { + return nsamples; + } + + return rf_skiq_tx_port_send(&q->tx_ports[port_idx], data, nsamples, timestamp); +} diff --git a/lib/src/phy/rf/rf_skiq_imp_card.h b/lib/src/phy/rf/rf_skiq_imp_card.h new file mode 100644 index 000000000..febd4cf45 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_card.h @@ -0,0 +1,82 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSRAN_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_ +#define SRSRAN_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_ + +#include "rf_skiq_imp_port.h" + +typedef struct { + uint8_t card; + uint8_t nof_ports; + skiq_chan_mode_t mode; + + skiq_param_t param; + rf_skiq_tx_port_t tx_ports[RF_SKIQ_MAX_PORTS_CARD]; + rf_skiq_rx_port_t rx_ports[RF_SKIQ_MAX_PORTS_CARD]; + + double cur_rx_gain_db; + bool suspend; + + uint64_t start_rx_stream_ts; + rf_skiq_port_state_t state; + pthread_mutex_t mutex; ///< Protect concurrent access to start/stop rx stream and receive + pthread_cond_t cvar; + pthread_t thread; + +} rf_skiq_card_t; + +int rf_skiq_card_init(rf_skiq_card_t* q, uint8_t card, uint8_t nof_ports, const rf_skiq_port_opts_t* opts); + +void rf_skiq_card_set_error_handler(rf_skiq_card_t* q, srsran_rf_error_handler_t error_handler, void* arg); + +double rf_skiq_card_set_tx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db); + +double rf_skiq_card_set_rx_gain_db(rf_skiq_card_t* q, uint32_t port_idx, double gain_db); + +int rf_skiq_card_update_timestamp(rf_skiq_card_t* q, bool use_1pps, uint64_t new_ts); + +uint64_t rf_skiq_card_read_sys_timestamp(rf_skiq_card_t* q); + +uint64_t rf_skiq_card_read_rf_timestamp(rf_skiq_card_t* q); + +int rf_skiq_card_start_rx_streaming(rf_skiq_card_t* q, uint64_t timestamp); + +int rf_skiq_card_stop_rx_streaming(rf_skiq_card_t* q); + +void rf_skiq_card_end_of_burst(rf_skiq_card_t* q); + +int rf_skiq_card_set_srate_hz(rf_skiq_card_t* q, uint32_t srate_hz); + +double rf_skiq_card_set_tx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz); + +double rf_skiq_card_set_rx_freq_hz(rf_skiq_card_t* q, uint32_t port_idx, double freq_hz); + +void rf_skiq_card_close(rf_skiq_card_t* q); + +int rf_skiq_card_receive(rf_skiq_card_t* q, uint32_t port_idx, cf_t* dst, uint32_t nsamples, uint64_t* ts); + +uint64_t rf_skiq_card_get_rx_timestamp(rf_skiq_card_t* q, uint32_t port_idx); + +bool rf_skiq_card_is_streaming(rf_skiq_card_t* q); + +int rf_skiq_card_send(rf_skiq_card_t* q, uint32_t port_idx, const cf_t* data, uint32_t nsamples, uint64_t timestamp); + +#endif // SRSRAN_LIB_SRC_PHY_RF_RF_SKIQ_IMP_CARD_H_ diff --git a/lib/src/phy/rf/rf_skiq_imp_cfg.h b/lib/src/phy/rf/rf_skiq_imp_cfg.h new file mode 100644 index 000000000..da3a6e766 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_cfg.h @@ -0,0 +1,119 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RF_SKIQ_IMP_CFG_H +#define SRSRAN_RF_SKIQ_IMP_CFG_H + +/** + * RF_SKIQ_MAX_PORTS_CARD sets the maximum number of ports per card + */ +#define RF_SKIQ_MAX_PORTS_CARD 2 + +/** + * SKIQ_CARD_SYNCH_MAX_TRIALS defines the maximum number of trials to synchronize multiple boards + */ +#define SKIQ_CARD_SYNCH_MAX_TRIALS 10 + +/** + * SKIQ_CARD_SYNC_MAX_ERROR sets the maximum number of system "ticks" error between boards during synchronization check. + * Consider the communication medium delay between the host and SidekIQ cards. + */ +#define SKIQ_CARD_SYNC_MAX_ERROR (SKIQ_SYS_TIMESTAMP_FREQ / 2) + +/** + * Maximum gap allowed in number of samples between ports + */ +#define SKIQ_PORT_SYNC_MAX_GAP (1024 * 1024) + +/** + * For checking the number of Tx lattes in the FPGA set the next line to the desired check period in number of blocks. + * Example: set to 1000 for checking it every 1000 blocks. + * WARNING: A low period may cause a reduction of performance in the Host-FPGA communication + */ +#define SKIQ_TX_LATES_CHECK_PERIOD (1920 * 10) + +/** + * Minimum number of channels that this RF device can reach + */ +#define SKIQ_MIN_CHANNELS (1) + +/** + * Maximum number of channels that this RF device can reach + */ +#define SKIQ_MAX_CHANNELS (SKIQ_MAX_NUM_CARDS * RF_SKIQ_MAX_PORTS_CARD) + +/** + * Dummy receive buffer size in samples + */ +#define RF_SKIQ_DUMMY_BUFFER_SIZE (1024) + +/** + * Magic word value as a ring buffer check + */ +#define SKIQ_RX_BUFFFER_MAGIC_WORD 0xABCD1234 + +/** + * Normalization value between fixed and floating point conversion + */ +#define SKIQ_NORM 2048.0 + +/** + * Default Rx gain in decibels (dB) + */ +#define SKIQ_RX_GAIN_DEFAULT_dB (+50.0f) + +/** + * Default sampling rate in samples per second (Hz) + */ +#define SKIQ_DEFAULT_SAMPLING_RATE_HZ (30.72e6) + +/** + * + */ +#define SKIQ_TX_PACKET_SIZE(N, MODE) (SKIQ_TX_PACKET_SIZE_INCREMENT_IN_WORDS * (N)-SKIQ_TX_HEADER_SIZE_IN_WORDS) + +/** + * SKIQ driver standard output MACRO + */ +extern uint32_t rf_skiq_logging_level; + +#define SKIQ_RF_INFO(...) \ + do { \ + if (rf_skiq_logging_level >= SKIQ_LOG_INFO) { \ + fprintf(stdout, "[SKIQ RF INFO] " __VA_ARGS__); \ + } \ + } while (false) + +#define SKIQ_RF_DEBUG(...) \ + do { \ + if (rf_skiq_logging_level >= SKIQ_LOG_DEBUG) { \ + fprintf(stdout, "[SKIQ RF INFO] " __VA_ARGS__); \ + } \ + } while (false) + +#define SKIQ_RF_ERROR(...) \ + do { \ + if (rf_skiq_logging_level >= SKIQ_LOG_ERROR) { \ + fprintf(stdout, "[SKIQ RF ERROR] " __VA_ARGS__); \ + } \ + } while (false) + +#endif // SRSLTE_RF_SKIQ_IMP_CFG_H diff --git a/lib/src/phy/rf/rf_skiq_imp_port.c b/lib/src/phy/rf/rf_skiq_imp_port.c new file mode 100644 index 000000000..abd344bfd --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_port.c @@ -0,0 +1,591 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rf_skiq_imp_port.h" + +static void rf_skiq_rx_port_handle_overflow(rf_skiq_rx_port_t* q) +{ + srsran_rf_error_t error = {}; + + error.type = SRSRAN_RF_ERROR_OVERFLOW; + if (q->error_handler) { + q->error_handler(q->error_handler_arg, error); + } else { + SKIQ_RF_INFO("Rx overflow detected in %d:%d\n", q->card, (int)q->hdl); + } +} + +#if SKIQ_TX_LATES_CHECK_PERIOD +static bool rf_skiq_tx_port_handle_late(rf_skiq_tx_port_t* q) +{ + // Get number of lattes from FPGA + uint32_t total_late = 0; + if (skiq_read_tx_num_late_timestamps(q->card, q->hdl, &total_late)) { + ERROR("Error reading lates from port %d:%d\n", q->card, (int)q->hdl); + } + + // Calculate number of late timestamps + uint32_t new_late = total_late; + + // Remove previous read value + if (new_late >= q->last_total_late) { + new_late = new_late - q->last_total_late; + } + + // Update latest value + q->last_total_late = total_late; + + // No late, do not report them + if (new_late == 0) { + return false; + } + + if (q->error_handler) { + srsran_rf_error_t error = {}; + error.type = SRSRAN_RF_ERROR_LATE; + error.opt = new_late; + q->error_handler(q->error_handler_arg, error); + } else { + SKIQ_RF_INFO("Port %d late events detected in %d:%d\n", new_late, q->card, (int)q->hdl); + } + + return true; +} +#endif // SKIQ_TX_LATES_CHECK_PERIOD + +static void* writer_thread(void* arg) +{ + uint64_t last_tx_ts = 0; + rf_skiq_tx_port_t* q = (rf_skiq_tx_port_t*)arg; + skiq_tx_block_t* p_tx_block = NULL; + + if (skiq_start_tx_streaming(q->card, q->hdl)) { + ERROR("Error starting Tx stream %d:%d\n", q->card, (int)q->hdl); + return NULL; + } + + q->state = RF_SKIQ_PORT_STATE_STREAMING; + + while (q->state != RF_SKIQ_PORT_STATE_STOP) { + // Read block from ring-buffer + int n = srsran_ringbuffer_read_block(&q->rb, (void**)&p_tx_block, q->p_block_nbytes, 1000); + + // Stop state is detected + if (q->state == RF_SKIQ_PORT_STATE_STOP) { + break; + } + + // If the ring buffer read resulted in timeout + if (n == SRSRAN_ERROR_TIMEOUT) { + continue; + } + + // Ignore blocks with TS=0 + if (p_tx_block->timestamp == 0) { + continue; + } + + // Check if the timestamp is the past (this can be caused by sample rate change) + if (last_tx_ts > p_tx_block->timestamp) { + + // Get current RF timestamp + uint64_t curr_tx_ts = 0UL; + skiq_read_curr_tx_timestamp(q->card, q->hdl, &curr_tx_ts); + + // Avoids giving a block to the FPGA that has already passed, otherwise it could hang forever + if (curr_tx_ts > p_tx_block->timestamp) { + SKIQ_RF_ERROR("[Tx %d:%d block] Tx block (ts=%ld) is in the past (last_tx_ts=%ld, curr_tx_ts=%ld), ignoring\n", + q->card, + (int)q->hdl, + q->p_tx_block->timestamp, + last_tx_ts, + curr_tx_ts); + continue; + } + } + last_tx_ts = p_tx_block->timestamp + q->block_size; + + // If the ring-buffer did not return with error code... + if (n > SRSRAN_SUCCESS) { + SKIQ_RF_DEBUG("[Tx %d:%d block] ts=%ld; nsamples=%d; last_tx_ts=%ld;\n", + q->card, + (int)q->hdl, + p_tx_block->timestamp, + q->block_size, + last_tx_ts); + + if (skiq_transmit(q->card, q->hdl, p_tx_block, NULL) < 0) { + ERROR("Error transmitting card %d\n", q->card); + q->state = RF_SKIQ_PORT_STATE_STOP; + } + +#if SKIQ_TX_LATES_CHECK_PERIOD + if (q->last_check_ts + SKIQ_TX_LATES_CHECK_PERIOD < p_tx_block->timestamp) { + // Handle late timestamps events + rf_skiq_tx_port_handle_late(q); + + // Update last check TS + q->last_check_ts = p_tx_block->timestamp; + } +#endif // SKIQ_TX_LATES_CHECK_PERIOD + } + } + + if (skiq_stop_tx_streaming(q->card, q->hdl)) { + ERROR("Error stopping Tx stream %d:%d\n", q->card, (int)q->hdl); + } + + SKIQ_RF_INFO("Exiting writer thread!\n"); + + return NULL; +} + +int rf_skiq_tx_port_init(rf_skiq_tx_port_t* q, uint8_t card, skiq_tx_hdl_t hdl, const rf_skiq_port_opts_t* opts) +{ + // Defines the block size in multiples of 256 words + uint32_t nof_blocks_per_packet = 4; + switch (opts->stream_mode) { + case skiq_rx_stream_mode_high_tput: + nof_blocks_per_packet = 8; + break; + case skiq_rx_stream_mode_low_latency: + nof_blocks_per_packet = 2; + break; + case skiq_rx_stream_mode_balanced: + default: + // Keep default value + break; + } + + q->card = card; + q->hdl = hdl; + q->block_size = SKIQ_TX_PACKET_SIZE(nof_blocks_per_packet, opts->chan_mode); + q->p_block_nbytes = q->block_size * 4 + SKIQ_TX_HEADER_SIZE_IN_BYTES; + + // configure the data flow mode to use timestamps + if (skiq_write_tx_data_flow_mode(card, hdl, skiq_tx_with_timestamps_data_flow_mode) != 0) { + ERROR("Setting Tx data flow mode"); + return SRSRAN_ERROR; + } + + // configure the transfer mode to synchronous + if (skiq_write_tx_transfer_mode(card, hdl, skiq_tx_transfer_mode_sync) != 0) { + ERROR("setting tx transfer mode"); + return SRSRAN_ERROR; + } + + // configure Tx block size + if (skiq_write_tx_block_size(card, hdl, q->block_size) != 0) { + ERROR("configuring Tx block size"); + return SRSRAN_ERROR; + } + + q->p_tx_block = skiq_tx_block_allocate(q->block_size); + if (q->p_tx_block == NULL) { + ERROR("Allocating Tx block"); + return SRSRAN_ERROR; + } + + // initialise ring buffer + if (srsran_ringbuffer_init(&q->rb, (int)(opts->tx_rb_size * q->p_block_nbytes))) { + ERROR("Initialising ringbuffer"); + return SRSRAN_ERROR; + } + + // Initialise thread parameters + pthread_attr_t attr; + struct sched_param param; + + param.sched_priority = sched_get_priority_max(SCHED_FIFO); + pthread_attr_init(&attr); + if (pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + if (pthread_attr_setschedparam(&attr, ¶m)) { + ERROR("Error not enough privileges to set Scheduling priority\n"); + } + + // Launch thread + if (pthread_create(&q->thread, &attr, writer_thread, q)) { + ERROR("Error creating writer thread with attributes (Did you miss sudo?). Trying without attributes.\n"); + + // try to create thread without attributes + pthread_attr_destroy(&attr); + if (pthread_create(&q->thread, NULL, writer_thread, q)) { + ERROR("Error creating writer thread, even without thread attributes. Exiting.\n"); + return SRSRAN_ERROR; + } + } + + // Rename thread + char thread_name[32] = {}; + if (snprintf(thread_name, sizeof(thread_name), "SKIQ Tx %d:%d", q->card, (int)q->hdl) > 0) { + pthread_setname_np(q->thread, thread_name); + } + + return SRSRAN_SUCCESS; +} + +void rf_skiq_tx_port_free(rf_skiq_tx_port_t* q) +{ + // Stop thread + q->state = RF_SKIQ_PORT_STATE_STOP; + + // Unlock ringbuffer + srsran_ringbuffer_write(&q->rb, (void*)q->p_tx_block, q->p_block_nbytes); + + // Wait thread to return + pthread_join(q->thread, NULL); + + if (q->p_tx_block) { + skiq_tx_block_free(q->p_tx_block); + } + srsran_ringbuffer_free(&q->rb); +} + +void rf_skiq_tx_port_end_of_burst(rf_skiq_tx_port_t* q) +{ + pthread_mutex_lock(&q->mutex); + + // Fill pending block if any, otherwise push a block with zeros + if (q->next_offset > 0) { + // Calculate pending samples to fill the block + uint32_t pending = q->block_size - q->next_offset; + + // Zero pending samples in the block + srsran_vec_i16_zero(&q->p_tx_block->data[q->next_offset * 2], 2 * pending); + + // Write block into the ring-buffer + srsran_ringbuffer_write_block(&q->rb, q->p_tx_block, q->p_block_nbytes); + + SKIQ_RF_DEBUG("[Tx EOB %d:%d] Padding offset=%d; n=%d; ts=%ld\n", + q->card, + (int)q->hdl, + q->next_offset, + pending, + q->p_tx_block->timestamp); + + // Reset next offset, so next transmission uses a new block + q->next_offset = 0; + } + + pthread_mutex_unlock(&q->mutex); +} + +int rf_skiq_tx_port_send(rf_skiq_tx_port_t* q, const cf_t* buffer, uint32_t nsamples, uint64_t ts) +{ + // Ignore transmission if the stream is not enabled + if (q->state != RF_SKIQ_PORT_STATE_STREAMING) { + return nsamples; + } + + // If no data and no block has started, early return + if (buffer == NULL && q->next_offset == 0) { + return nsamples; + } + + pthread_mutex_lock(&q->mutex); + + // Calculate destination where IQ shall be stored + int16_t* p_tx_iq = &q->p_tx_block->data[q->next_offset * 2]; + + // Calculate number of samples to take from buffer + nsamples = SRSRAN_MIN(nsamples, q->block_size - q->next_offset); + + // Set time stamp only if no offset + if (q->next_offset == 0) { + skiq_tx_set_block_timestamp(q->p_tx_block, ts); + } + + SKIQ_RF_DEBUG( + "[Tx %d:%d] Write offset=%d; nsamples=%d; ts=%ld\n", q->card, (int)q->hdl, q->next_offset, nsamples, ts); + + // Fill data ... + if (buffer == NULL) { + // ... with zeros + srsran_vec_i16_zero(p_tx_iq, 2 * nsamples); + } else { + // ... with samples, after conversion + srsran_vec_convert_conj_cs(buffer, SKIQ_NORM, p_tx_iq, nsamples); + } + q->next_offset += nsamples; + + // If the number of samples does not fill the block, return early + if (q->next_offset < q->block_size) { + pthread_mutex_unlock(&q->mutex); + return nsamples; + } + + if (srsran_ringbuffer_space(&q->rb) < q->p_block_nbytes * 2) { + ERROR("Tx buffer overflow\n"); + pthread_mutex_unlock(&q->mutex); + return nsamples; + } + + // Actual write in ring buffer + int n = srsran_ringbuffer_write_timed_block(&q->rb, q->p_tx_block, q->p_block_nbytes, 5); + + pthread_mutex_unlock(&q->mutex); + + // In case of error (e.g. timeout) return code + if (n < SRSRAN_SUCCESS) { + return n; + } + + // In case of number of bytes mismatch return error + if (n != q->p_block_nbytes) { + ERROR("Error writing in Tx buffer %d:%d\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + // Reset offset only if the block was successfully written into the ring-buffer + q->next_offset = 0; + + // Return the number of samples writen in the buffer + return nsamples; +} + +void rf_skiq_tx_port_set_error_handler(rf_skiq_tx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg) +{ + q->error_handler = error_handler; + q->error_handler_arg = arg; +} + +int rf_skiq_tx_port_set_lo(rf_skiq_tx_port_t* q, uint64_t lo_freq) +{ + // Skip setting LO frequency if it is not required + if (q->current_lo == lo_freq) { + return SRSRAN_SUCCESS; + } + + skiq_filt_t filt = (lo_freq < 3000000000UL) ? skiq_filt_0_to_3000_MHz : skiq_filt_3000_to_6000_MHz; + + if (skiq_write_tx_LO_freq(q->card, q->hdl, lo_freq)) { + ERROR("Setting card %d:%d Tx Lo frequency", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + if (skiq_write_tx_filter_path(q->card, q->hdl, filt)) { + ERROR("Setting card %d:%d Tx filter", q->card, q->hdl); + return SRSRAN_ERROR; + } + + q->current_lo = lo_freq; + + return SRSRAN_SUCCESS; +} + +int rf_skiq_rx_port_init(rf_skiq_rx_port_t* q, uint8_t card, skiq_rx_hdl_t hdl, const rf_skiq_port_opts_t* opts) +{ + q->card = card; + q->hdl = hdl; + + // enabling DC offset correction can cause an IQ impairment + if (skiq_write_rx_dc_offset_corr(card, hdl, false)) { + ERROR("Setting RX DC offset correction"); + return SRSRAN_ERROR; + } + + // set rx gain mode + if (skiq_write_rx_gain_mode(card, hdl, skiq_rx_gain_manual)) { + ERROR("Setting RX gain mode"); + return SRSRAN_ERROR; + } + + // Rx block size in bytes + int32_t rx_block_size = skiq_read_rx_block_size(q->card, opts->stream_mode) - SKIQ_RX_HEADER_SIZE_IN_BYTES; + + // initialise ring buffer + if (srsran_ringbuffer_init(&q->rb, (int)(opts->rx_rb_size * rx_block_size))) { + ERROR("Initialising ringbuffer"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void rf_skiq_rx_port_free(rf_skiq_rx_port_t* q) +{ + + srsran_ringbuffer_free(&q->rb); +} + +int rf_skiq_rx_port_available(rf_skiq_rx_port_t* q) +{ + return srsran_ringbuffer_status(&q->rb); +} + +int rf_skiq_rx_port_read(rf_skiq_rx_port_t* q, cf_t* dest, uint32_t nsamples, uint64_t* ts_start) +{ + // Detect start of new block + if (q->rb_read_rem == 0) { + skiq_header_t header = {}; + + // If ring-buffer overflow was detected... + if (q->rb_overflow) { + // Reset ring buffer + srsran_ringbuffer_reset(&q->rb); + + // Clear overflow flag + q->rb_overflow = false; + + // Set samples to zero + srsran_vec_cf_zero(dest, nsamples); + + // Set default timestamp + *ts_start = 0; + + // Since the buffer is empty, return the full amount of samples so it does not delay reception of other channels + return nsamples; + } + + // Read a packet. First the header + if (srsran_ringbuffer_read(&q->rb, &header, sizeof(skiq_header_t)) != sizeof(skiq_header_t)) { + ERROR("Error reading header from ring-buffer %d:%d corrupted\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + // Check header magic word + if (header.magic != SKIQ_RX_BUFFFER_MAGIC_WORD) { + ERROR("Error ring-buffer %d:%d corrupted\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + // Successful read + q->rb_read_rem = header.nsamples; + q->rb_tstamp_rem = header.tstamp; + } + + // Limit number of samples to the remainder of the stored packet + nsamples = SRSRAN_MIN(q->rb_read_rem, nsamples); + + // Read any remainder of a packet from the ring buffer + int n = srsran_ringbuffer_read_convert_conj(&q->rb, dest, SKIQ_NORM, nsamples); + + // Detect error in read + if (n < SRSRAN_SUCCESS) { + ERROR("Error reading packet remainder from %d:%d\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + SKIQ_RF_DEBUG("[Rx %d:%d] Read nsamples=%d/%d; ts=%ld\n", q->card, (int)q->hdl, n, nsamples, q->rb_tstamp_rem); + + // Update timestamp + *ts_start = q->rb_tstamp_rem; + + // Update reminder + q->rb_read_rem -= n; + q->rb_tstamp_rem += n; + + // Return number of read samples + return n; +} + +uint64_t rf_skiq_rx_port_get_timestamp(rf_skiq_rx_port_t* q) +{ + return q->rb_tstamp_rem; +} + +int rf_skiq_rx_port_write(rf_skiq_rx_port_t* q, const skiq_rx_block_t* p_rx_block, uint32_t nsamples) +{ + // Prepare header + skiq_header_t header = {}; + header.magic = SKIQ_RX_BUFFFER_MAGIC_WORD; + header.tstamp = p_rx_block->rf_timestamp; + header.nsamples = nsamples; + + // Ignore block if the overflow flag has risen + if (q->rb_overflow) { + return nsamples; + } + + SKIQ_RF_DEBUG("[Rx %d:%d block] ts=%ld; nsamples=%d;\n", q->card, (int)q->hdl, header.tstamp, header.nsamples); + + // Check space in the ring-buffer prior to writing + if (srsran_ringbuffer_space(&q->rb) >= sizeof(skiq_header_t) + nsamples * 4) { + // Write header + if (srsran_ringbuffer_write_block(&q->rb, &header, sizeof(skiq_header_t)) != sizeof(skiq_header_t)) { + ERROR("Writing header in Rx buffer %d:%d!\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + // Write IQ samples + if (srsran_ringbuffer_write_block(&q->rb, (uint8_t*)p_rx_block->data, (int)nsamples * 4) != nsamples * 4) { + ERROR("Writing base-band in Rx buffer %d:%d!\n", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + } else { + SKIQ_RF_INFO("Rx %d:%d ring-buffer overflow!\n", q->card, (int)q->hdl); + q->rb_overflow = true; + rf_skiq_rx_port_handle_overflow(q); + } + + // Process overload, call handle only for rising-edges + if (!q->rf_overflow && p_rx_block->overload) { + rf_skiq_rx_port_handle_overflow(q); + } + q->rf_overflow = p_rx_block->overload; + + return nsamples; +} + +void rf_skiq_rx_port_set_error_handler(rf_skiq_rx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg) +{ + q->error_handler = error_handler; + q->error_handler_arg = arg; +} + +void rf_skiq_rx_port_reset(rf_skiq_rx_port_t* q) +{ + SKIQ_RF_INFO("Rx port %d:%d reset\n", q->card, (int)q->hdl); + q->rb_read_rem = 0; + q->rb_tstamp_rem = 0; + srsran_ringbuffer_reset(&q->rb); +} + +int rf_skiq_rx_port_set_lo(rf_skiq_rx_port_t* q, uint64_t lo_freq) +{ + // Skip setting LO frequency if it is not required + if (q->current_lo == lo_freq) { + return SRSRAN_SUCCESS; + } + + skiq_filt_t filt = (lo_freq < 3000000000UL) ? skiq_filt_0_to_3000_MHz : skiq_filt_3000_to_6000_MHz; + + if (skiq_write_rx_LO_freq(q->card, q->hdl, lo_freq)) { + ERROR("Setting card %d:%d Rx Lo frequency", q->card, (int)q->hdl); + return SRSRAN_ERROR; + } + + if (skiq_write_rx_preselect_filter_path(q->card, q->hdl, filt)) { + ERROR("Setting card %d:%d Rx filter", q->card, q->hdl); + return SRSRAN_ERROR; + } + + q->current_lo = lo_freq; + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/rf/rf_skiq_imp_port.h b/lib/src/phy/rf/rf_skiq_imp_port.h new file mode 100644 index 000000000..a7031af15 --- /dev/null +++ b/lib/src/phy/rf/rf_skiq_imp_port.h @@ -0,0 +1,109 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RF_SKIQ_IMP_PORT_H +#define SRSRAN_RF_SKIQ_IMP_PORT_H + +#include +#include + +#include "rf_helper.h" +#include "rf_skiq_imp_cfg.h" +#include "rf_skiq_imp_port.h" +#include "srsran/srsran.h" + +typedef struct { + uint32_t magic; + uint64_t tstamp; + uint32_t nsamples; +} skiq_header_t; + +typedef enum { + RF_SKIQ_PORT_STATE_IDLE = 0, + RF_SKIQ_PORT_STATE_STREAMING, + RF_SKIQ_PORT_STATE_STOP +} rf_skiq_port_state_t; + +typedef struct { + uint32_t tx_rb_size; + uint32_t rx_rb_size; + skiq_chan_mode_t chan_mode; + char stream_mode_str[RF_PARAM_LEN]; + skiq_rx_stream_mode_t stream_mode; +} rf_skiq_port_opts_t; + +typedef struct { + uint8_t card; + skiq_tx_hdl_t hdl; + skiq_tx_block_t* p_tx_block; + uint32_t p_block_nbytes; // Size in bytes including header + uint32_t block_size; // Size in words (samples) + uint32_t next_offset; // Number of samples remainder + srsran_ringbuffer_t rb; + rf_skiq_port_state_t state; + pthread_t thread; + pthread_mutex_t mutex; // Protects p_tx_block + + uint64_t current_lo; + + srsran_rf_error_handler_t error_handler; + void* error_handler_arg; + +#if SKIQ_TX_LATES_CHECK_PERIOD + uint64_t last_check_ts; + uint32_t last_total_late; + uint32_t last_total_underruns; +#endif // SKIQ_TX_LATES_CHECK_PERIOD +} rf_skiq_tx_port_t; + +int rf_skiq_tx_port_init(rf_skiq_tx_port_t* q, uint8_t card, skiq_tx_hdl_t hdl, const rf_skiq_port_opts_t* opts); +void rf_skiq_tx_port_free(rf_skiq_tx_port_t* q); +void rf_skiq_tx_port_end_of_burst(rf_skiq_tx_port_t* q); +int rf_skiq_tx_port_send(rf_skiq_tx_port_t* q, const cf_t* buffer, uint32_t nsamples, uint64_t ts); +void rf_skiq_tx_port_set_error_handler(rf_skiq_tx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg); +int rf_skiq_tx_port_set_lo(rf_skiq_tx_port_t* q, uint64_t lo_freq); + +typedef struct { + uint8_t card; + skiq_rx_hdl_t hdl; + srsran_ringbuffer_t rb; + uint32_t rb_read_rem; + uint64_t rb_tstamp_rem; + bool rf_overflow; ///< Indicates an RF message was flagged with overflow + bool rb_overflow; ///< Indicates that ring-buffer is full and it needs to be flushed + + uint64_t current_lo; + + srsran_rf_error_handler_t error_handler; + void* error_handler_arg; +} rf_skiq_rx_port_t; + +int rf_skiq_rx_port_init(rf_skiq_rx_port_t* q, uint8_t card, skiq_rx_hdl_t hdl, const rf_skiq_port_opts_t* opts); +void rf_skiq_rx_port_free(rf_skiq_rx_port_t* q); +int rf_skiq_rx_port_available(rf_skiq_rx_port_t* q); +int rf_skiq_rx_port_read(rf_skiq_rx_port_t* q, cf_t* dest, uint32_t nsamples, uint64_t* ts_start); +uint64_t rf_skiq_rx_port_get_timestamp(rf_skiq_rx_port_t* q); +int rf_skiq_rx_port_write(rf_skiq_rx_port_t* q, const skiq_rx_block_t* p_rx_block, uint32_t nbytes); +void rf_skiq_rx_port_set_error_handler(rf_skiq_rx_port_t* q, srsran_rf_error_handler_t error_handler, void* arg); +void rf_skiq_rx_port_reset(rf_skiq_rx_port_t* q); +int rf_skiq_rx_port_set_lo(rf_skiq_rx_port_t* q, uint64_t lo_freq); + +#endif // SRSRAN_RF_SKIQ_IMP_PORT_H diff --git a/lib/src/phy/rf/rf_soapy_imp.c b/lib/src/phy/rf/rf_soapy_imp.c index 4af516a8c..82a66f750 100644 --- a/lib/src/phy/rf/rf_soapy_imp.c +++ b/lib/src/phy/rf/rf_soapy_imp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,15 +25,18 @@ #include #include "rf_helper.h" +#include "rf_plugin.h" #include "rf_soapy_imp.h" -#include "srsran/srsran.h" +#include "srsran/phy/common/phy_common.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" #include #include #include #include +#include #include -#include #define HAVE_ASYNC_THREAD 0 @@ -263,11 +266,15 @@ int rf_soapy_stop_tx_stream(void* h) void rf_soapy_flush_buffer(void* h) { int n; - cf_t tmp1[1024]; - cf_t tmp2[1024]; - void* data[2] = {tmp1, tmp2}; + cf_t dummy[1024]; + void* data[SRSRAN_MAX_CHANNELS] = {}; + + for (int i = 0; i < SRSRAN_MAX_CHANNELS; i++) { + data[i] = dummy; + } + do { - n = rf_soapy_recv_with_time_multi(h, data, 1024, 0, NULL, NULL); + n = rf_soapy_recv_with_time_multi(h, data, sizeof(dummy), 0, NULL, NULL); } while (n > 0); } @@ -855,7 +862,9 @@ int rf_soapy_recv_with_time_multi(void* h, int rf_soapy_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) { - return rf_soapy_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); + void* data_multi[SRSRAN_MAX_PORTS] = {NULL}; + data_multi[0] = data; + return rf_soapy_recv_with_time_multi(h, data_multi, nsamples, blocking, secs, frac_secs); } int rf_soapy_send_timed(void* h, @@ -995,3 +1004,44 @@ int rf_soapy_send_timed_multi(void* h, return n; } + +rf_dev_t srsran_rf_dev_soapy = {"soapy", + rf_soapy_devname, + rf_soapy_start_rx_stream, + rf_soapy_stop_rx_stream, + rf_soapy_flush_buffer, + rf_soapy_has_rssi, + rf_soapy_get_rssi, + rf_soapy_suppress_stdout, + rf_soapy_register_error_handler, + rf_soapy_open, + rf_soapy_open_multi, + rf_soapy_close, + rf_soapy_set_rx_srate, + rf_soapy_set_rx_gain, + rf_soapy_set_rx_gain_ch, + rf_soapy_set_tx_gain, + rf_soapy_set_tx_gain_ch, + rf_soapy_get_rx_gain, + rf_soapy_get_tx_gain, + rf_soapy_get_info, + rf_soapy_set_rx_freq, + rf_soapy_set_tx_srate, + rf_soapy_set_tx_freq, + rf_soapy_get_time, + NULL, + rf_soapy_recv_with_time, + rf_soapy_recv_with_time_multi, + rf_soapy_send_timed, + .srsran_rf_send_timed_multi = rf_soapy_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_soapy; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_soapy_imp.h b/lib/src/phy/rf/rf_soapy_imp.h index 153088923..c44851a28 100644 --- a/lib/src/phy/rf/rf_soapy_imp.h +++ b/lib/src/phy/rf/rf_soapy_imp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,11 +23,14 @@ #define SRSRAN_RF_SOAPY_IMP_H_ #include "srsran/config.h" +#include "srsran/phy/common/phy_common.h" #include "srsran/phy/rf/rf.h" #include #include #define DEVNAME_SOAPY "soapy" +extern rf_dev_t srsran_rf_dev_soapy; + SRSRAN_API int rf_soapy_open(char* args, void** handler); SRSRAN_API int rf_soapy_open_multi(char* args, void** handler, uint32_t num_requested_channels); @@ -75,8 +78,12 @@ SRSRAN_API double rf_soapy_set_rx_freq(void* h, uint32_t ch, double freq); SRSRAN_API int rf_soapy_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs); -SRSRAN_API int -rf_soapy_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs); +SRSRAN_API int rf_soapy_recv_with_time_multi(void* h, + void* data[SRSRAN_MAX_PORTS], + uint32_t nsamples, + bool blocking, + time_t* secs, + double* frac_secs); SRSRAN_API double rf_soapy_set_tx_srate(void* h, double freq); diff --git a/lib/src/phy/rf/rf_uhd_generic.h b/lib/src/phy/rf/rf_uhd_generic.h index 8e3bd87d8..c09d2b84b 100644 --- a/lib/src/phy/rf/rf_uhd_generic.h +++ b/lib/src/phy/rf/rf_uhd_generic.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -42,18 +42,18 @@ private: Debug("Making USRP object with args '" << dev_addr.to_string() << "'"); - UHD_SAFE_C_SAVE_ERROR(this, usrp = uhd::usrp::multi_usrp::make(dev_addr);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp = uhd::usrp::multi_usrp::make(dev_addr);) } uhd_error set_tx_subdev(const std::string& string) { Info("Setting tx_subdev_spec to '" << string << "'"); - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_tx_subdev_spec(string);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_tx_subdev_spec(string);) } uhd_error set_rx_subdev(const std::string& string) { Info("Setting rx_subdev_spec to '" << string << "'"); - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_rx_subdev_spec(string);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_rx_subdev_spec(string);) } uhd_error test_ad936x_device(uint32_t nof_channels) @@ -101,7 +101,7 @@ private: } if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) { - last_error = md.strerror(); + Error(md.strerror()); return UHD_ERROR_IO; } @@ -201,7 +201,7 @@ public: // Set receiver subdev spec if specified if (not rx_subdev.empty()) { - err = set_rx_subdev(tx_subdev); + err = set_rx_subdev(rx_subdev); if (err != UHD_ERROR_NONE) { return err; } @@ -245,9 +245,8 @@ public: // Otherwise, close USRP and open again usrp = nullptr; - Warning("Failed to open Rx stream '" << last_error << "', trying to open device again. " << ntrials - << " trials left. Waiting for " << FE_RX_RESET_SLEEP_TIME_MS.count() - << " ms"); + Warning("Failed to open Rx stream, trying to open device again. " + << ntrials << " trials left. Waiting for " << FE_RX_RESET_SLEEP_TIME_MS.count() << " ms"); // Sleep std::this_thread::sleep_for(FE_RX_RESET_SLEEP_TIME_MS); @@ -263,103 +262,101 @@ public: uhd_error get_mboard_name(std::string& mboard_name) override { - UHD_SAFE_C_SAVE_ERROR(this, mboard_name = usrp->get_mboard_name();) + SRSRAN_UHD_SAFE_C_LOG_ERROR(mboard_name = usrp->get_mboard_name();) } uhd_error get_mboard_sensor_names(std::vector& sensors) override { - UHD_SAFE_C_SAVE_ERROR(this, sensors = usrp->get_mboard_sensor_names();) + SRSRAN_UHD_SAFE_C_LOG_ERROR(sensors = usrp->get_mboard_sensor_names();) } uhd_error get_rx_sensor_names(std::vector& sensors) override { - UHD_SAFE_C_SAVE_ERROR(this, sensors = usrp->get_rx_sensor_names();) + SRSRAN_UHD_SAFE_C_LOG_ERROR(sensors = usrp->get_rx_sensor_names();) } uhd_error get_sensor(const std::string& sensor_name, double& sensor_value) override { - UHD_SAFE_C_SAVE_ERROR(this, sensor_value = usrp->get_mboard_sensor(sensor_name).to_real();) + SRSRAN_UHD_SAFE_C_LOG_ERROR(sensor_value = usrp->get_mboard_sensor(sensor_name).to_real();) } uhd_error get_sensor(const std::string& sensor_name, bool& sensor_value) override { - UHD_SAFE_C_SAVE_ERROR(this, sensor_value = usrp->get_mboard_sensor(sensor_name).to_bool();) + SRSRAN_UHD_SAFE_C_LOG_ERROR(sensor_value = usrp->get_mboard_sensor(sensor_name).to_bool();) } uhd_error get_rx_sensor(const std::string& sensor_name, bool& sensor_value) override { - UHD_SAFE_C_SAVE_ERROR(this, sensor_value = usrp->get_rx_sensor(sensor_name).to_bool();) + SRSRAN_UHD_SAFE_C_LOG_ERROR(sensor_value = usrp->get_rx_sensor(sensor_name).to_bool();) } uhd_error set_time_unknown_pps(const uhd::time_spec_t& timespec) override { Debug("Setting Time at next PPS..."); - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_time_unknown_pps(timespec);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_time_unknown_pps(timespec);) } uhd_error get_time_now(uhd::time_spec_t& timespec) override { - UHD_SAFE_C_SAVE_ERROR(this, timespec = usrp->get_time_now();) + SRSRAN_UHD_SAFE_C_LOG_ERROR(timespec = usrp->get_time_now();) } uhd_error set_sync_source(const std::string& sync_source, const std::string& clock_source) override { Debug("Setting PPS source to '" << sync_source << "' and clock source to '" << clock_source << "'"); #if UHD_VERSION < 3140099 - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_clock_source(clock_source); usrp->set_time_source(sync_source);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_clock_source(clock_source); usrp->set_time_source(sync_source);) #else - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_sync_source(clock_source, sync_source);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_sync_source(clock_source, sync_source);) #endif } uhd_error get_gain_range(uhd::gain_range_t& tx_gain_range, uhd::gain_range_t& rx_gain_range) override { - UHD_SAFE_C_SAVE_ERROR(this, tx_gain_range = usrp->get_tx_gain_range(); rx_gain_range = usrp->get_rx_gain_range();) + SRSRAN_UHD_SAFE_C_LOG_ERROR(tx_gain_range = usrp->get_tx_gain_range(); rx_gain_range = usrp->get_rx_gain_range();) } uhd_error set_master_clock_rate(double rate) override { Debug("Setting master clock rate to " << rate / 1e6 << " MHz"); - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_master_clock_rate(rate);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_master_clock_rate(rate);) } uhd_error set_rx_rate(double rate) override { Debug("Setting Rx Rate to " << rate / 1e6 << "MHz"); - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_rx_rate(rate);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_rx_rate(rate);) } uhd_error set_tx_rate(double rate) override { Debug("Setting Tx Rate to " << rate / 1e6 << "MHz"); - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_tx_rate(rate);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_tx_rate(rate);) } uhd_error set_command_time(const uhd::time_spec_t& timespec) override { - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_command_time(timespec);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_command_time(timespec);) } uhd_error get_rx_stream(size_t& max_num_samps) override { Debug("Creating Rx stream"); - UHD_SAFE_C_SAVE_ERROR( - this, rx_stream = nullptr; rx_stream = usrp->get_rx_stream(stream_args); - max_num_samps = rx_stream->get_max_num_samps(); - if (max_num_samps == 0UL) { - last_error = "The maximum number of receive samples is zero."; - return UHD_ERROR_VALUE; - }) + SRSRAN_UHD_SAFE_C_LOG_ERROR(rx_stream = nullptr; rx_stream = usrp->get_rx_stream(stream_args); + max_num_samps = rx_stream->get_max_num_samps(); + if (max_num_samps == 0UL) { + Error("The maximum number of receive samples is zero."); + return UHD_ERROR_VALUE; + }) } uhd_error get_tx_stream(size_t& max_num_samps) override { Debug("Creating Tx stream"); - UHD_SAFE_C_SAVE_ERROR( - this, tx_stream = nullptr; tx_stream = usrp->get_tx_stream(stream_args); - max_num_samps = tx_stream->get_max_num_samps(); - if (max_num_samps == 0UL) { - last_error = "The maximum number of transmit samples is zero."; - return UHD_ERROR_VALUE; - }) + SRSRAN_UHD_SAFE_C_LOG_ERROR(tx_stream = nullptr; tx_stream = usrp->get_tx_stream(stream_args); + max_num_samps = tx_stream->get_max_num_samps(); + if (max_num_samps == 0UL) { + Error("The maximum number of transmit samples is zero."); + return UHD_ERROR_VALUE; + }) } uhd_error set_tx_gain(size_t ch, double gain) override { Debug("Setting channel " << ch << " Tx gain to " << gain << " dB"); - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_tx_gain(gain, ch);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_tx_gain(gain, ch);) } uhd_error set_rx_gain(size_t ch, double gain) override { Debug("Setting channel " << ch << " Rx gain to " << gain << " dB"); - UHD_SAFE_C_SAVE_ERROR(this, usrp->set_rx_gain(gain, ch);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(usrp->set_rx_gain(gain, ch);) } - uhd_error get_rx_gain(double& gain) override { UHD_SAFE_C_SAVE_ERROR(this, gain = usrp->get_rx_gain();) } - uhd_error get_tx_gain(double& gain) override { UHD_SAFE_C_SAVE_ERROR(this, gain = usrp->get_tx_gain();) } + uhd_error get_rx_gain(double& gain) override { SRSRAN_UHD_SAFE_C_LOG_ERROR(gain = usrp->get_rx_gain();) } + uhd_error get_tx_gain(double& gain) override { SRSRAN_UHD_SAFE_C_LOG_ERROR(gain = usrp->get_tx_gain();) } uhd_error set_tx_freq(uint32_t ch, double target_freq, double& actual_freq) override { Debug("Setting channel " << ch << " Tx frequency to " << target_freq / 1e6 << " MHz"); @@ -378,8 +375,8 @@ public: tune_request.dsp_freq_policy = uhd::tune_request_t::POLICY_AUTO; } - UHD_SAFE_C_SAVE_ERROR(this, uhd::tune_result_t tune_result = usrp->set_tx_freq(tune_request, ch); - actual_freq = tune_result.target_rf_freq;) + SRSRAN_UHD_SAFE_C_LOG_ERROR(uhd::tune_result_t tune_result = usrp->set_tx_freq(tune_request, ch); + actual_freq = tune_result.target_rf_freq;) } uhd_error set_rx_freq(uint32_t ch, double target_freq, double& actual_freq) override { @@ -399,8 +396,8 @@ public: tune_request.dsp_freq_policy = uhd::tune_request_t::POLICY_AUTO; } - UHD_SAFE_C_SAVE_ERROR(this, uhd::tune_result_t tune_result = usrp->set_rx_freq(tune_request, ch); - actual_freq = tune_result.target_rf_freq;) + SRSRAN_UHD_SAFE_C_LOG_ERROR(uhd::tune_result_t tune_result = usrp->set_rx_freq(tune_request, ch); + actual_freq = tune_result.target_rf_freq;) } }; diff --git a/lib/src/phy/rf/rf_uhd_imp.cc b/lib/src/phy/rf/rf_uhd_imp.cc index e2da0e144..276863ccd 100644 --- a/lib/src/phy/rf/rf_uhd_imp.cc +++ b/lib/src/phy/rf/rf_uhd_imp.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,6 +19,7 @@ * */ +#include #include #include #include @@ -28,7 +29,9 @@ #include #include "rf_helper.h" -#include "srsran/srsran.h" +#include "rf_plugin.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" #include "rf_uhd_generic.h" #include "rf_uhd_imp.h" @@ -148,11 +151,15 @@ struct rf_uhd_handler_t { uint32_t nof_tx_channels = 0; std::array tx_freq = {}; std::array rx_freq = {}; + double cur_rx_gain_ch0 = 0; - srsran_rf_error_handler_t uhd_error_handler = nullptr; - void* uhd_error_handler_arg = nullptr; - rf_uhd_imp_underflow_state_t tx_state = RF_UHD_IMP_TX_STATE_START_BURST; - uhd::time_spec_t eob_ack_timeout = {}; //< Set when a Underflow/Late happens + std::mutex tx_gain_mutex; + std::array, SRSRAN_MAX_CHANNELS> tx_gain_db = {}; + + srsran_rf_error_handler_t uhd_error_handler = nullptr; + void* uhd_error_handler_arg = nullptr; + std::atomic tx_state = {RF_UHD_IMP_TX_STATE_START_BURST}; + uhd::time_spec_t eob_ack_timeout = {}; //< Set when a Underflow/Late happens double current_master_clock = 0.0; @@ -163,7 +170,7 @@ struct rf_uhd_handler_t { #if HAVE_ASYNC_THREAD // Asynchronous transmission message thread - bool async_thread_running = false; + std::atomic async_thread_running{false}; std::thread async_thread; std::mutex async_mutex; std::condition_variable async_cvar; @@ -197,15 +204,12 @@ void suppress_handler(const char* x) // do nothing } -static cf_t zero_mem[64 * 1024] = {}; - -#define print_usrp_error(h) \ - do { \ - ERROR("USRP reported the following error: %s", h->uhd->last_error.c_str()); \ - } while (false) +static std::array zero_mem = {}; // For transmitting zeros +static std::array dummy_mem = {}; // For receiving static void log_overflow(rf_uhd_handler_t* h) { + std::unique_lock lock(h->tx_mutex); if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; } @@ -220,6 +224,7 @@ static void log_overflow(rf_uhd_handler_t* h) static void log_late(rf_uhd_handler_t* h, bool is_rx) { + std::unique_lock lock(h->tx_mutex); if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; } @@ -236,9 +241,12 @@ static void log_late(rf_uhd_handler_t* h, bool is_rx) #if HAVE_ASYNC_THREAD static void log_underflow(rf_uhd_handler_t* h) { - // Flag underflow - if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { - h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; + { + std::lock_guard tx_lock(h->tx_mutex); + // Flag underflow + if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { + h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; + } } if (h->uhd_error_handler != nullptr) { srsran_rf_error_t error; @@ -252,8 +260,6 @@ static void log_underflow(rf_uhd_handler_t* h) static void log_rx_error(rf_uhd_handler_t* h) { if (h->uhd_error_handler) { - ERROR("USRP reported the following error: %s", h->uhd->last_error.c_str()); - srsran_rf_error_t error; bzero(&error, sizeof(srsran_rf_error_t)); error.type = srsran_rf_error_t::SRSRAN_RF_ERROR_RX; @@ -281,7 +287,6 @@ static void* async_thread(void* h) if (handler->uhd->is_tx_ready()) { lock.unlock(); if (handler->uhd->recv_async_msg(md, RF_UHD_IMP_ASYNCH_MSG_TIMEOUT_S, valid) != UHD_ERROR_NONE) { - print_usrp_error(handler); return nullptr; } @@ -302,6 +307,7 @@ static void* async_thread(void* h) } } else if (event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK) { // Makes sure next block will be start of burst + std::lock_guard tx_lock(handler->tx_mutex); if (handler->tx_state == RF_UHD_IMP_TX_STATE_WAIT_EOB_ACK) { handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST; } @@ -378,7 +384,6 @@ static int set_time_to_gps_time(rf_uhd_handler_t* handler) std::vector sensors; if (handler->uhd->get_mboard_sensor_names(sensors) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -404,14 +409,12 @@ static int set_time_to_gps_time(rf_uhd_handler_t* handler) // Get actual sensor value double frac_secs = 0.0; if (handler->uhd->get_sensor(sensor_name, frac_secs) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } // Get time and set printf("Setting USRP time to %fs\n", frac_secs); if (handler->uhd->set_time_unknown_pps(uhd::time_spec_t(frac_secs)) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -432,13 +435,11 @@ static int wait_sensor_locked(rf_uhd_handler_t* handler, if (is_mboard) { // motherboard sensor if (handler->uhd->get_mboard_sensor_names(sensors) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } } else { // daughterboard sensor if (handler->uhd->get_rx_sensor_names(sensors) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } } @@ -466,12 +467,10 @@ static int wait_sensor_locked(rf_uhd_handler_t* handler, // Get actual sensor value if (is_mboard) { if (handler->uhd->get_sensor(sensor_name, is_locked) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } } else { if (handler->uhd->get_rx_sensor(sensor_name, is_locked) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } } @@ -509,7 +508,7 @@ bool rf_uhd_rx_wait_lo_locked(void* h) return is_locked; } -static inline int rf_uhd_start_rx_stream_unsafe(rf_uhd_handler_t* handler) +static inline int rf_uhd_start_rx_stream_nolock(rf_uhd_handler_t* handler) { // Check if stream was not created or started if (not handler->uhd->is_rx_ready() or handler->rx_stream_enabled) { @@ -519,7 +518,6 @@ static inline int rf_uhd_start_rx_stream_unsafe(rf_uhd_handler_t* handler) // Issue stream command if (handler->uhd->start_rx_stream(RF_UHD_IMP_STREAM_DELAY_S) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -533,10 +531,10 @@ int rf_uhd_start_rx_stream(void* h, bool now) rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; std::unique_lock lock(handler->rx_mutex); - return rf_uhd_start_rx_stream_unsafe(handler); + return rf_uhd_start_rx_stream_nolock(handler); } -static inline int rf_uhd_stop_rx_stream_unsafe(rf_uhd_handler_t* handler) +static inline int rf_uhd_stop_rx_stream_nolock(rf_uhd_handler_t* handler) { // Check if stream was created or stream was not started if (not handler->uhd->is_rx_ready() or not handler->rx_stream_enabled) { @@ -546,7 +544,6 @@ static inline int rf_uhd_stop_rx_stream_unsafe(rf_uhd_handler_t* handler) // Issue stream command if (handler->uhd->stop_rx_stream() != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -560,7 +557,7 @@ int rf_uhd_stop_rx_stream(void* h) rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; std::unique_lock lock(handler->rx_mutex); - if (rf_uhd_stop_rx_stream_unsafe(handler) < SRSRAN_SUCCESS) { + if (rf_uhd_stop_rx_stream_nolock(handler) < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } @@ -580,15 +577,15 @@ void rf_uhd_flush_buffer(void* h) // Set all pointers to zero buffer for (auto& i : data) { - i = zero_mem; + i = dummy_mem.data(); } // Receive until time out uhd::rx_metadata_t md; do { - if (handler->uhd->receive(data, handler->rx_nof_samples, md, 0.0, false, rxd_samples) != UHD_ERROR_NONE) { + uint32_t nsamples = SRSRAN_MIN(handler->rx_nof_samples, (uint32_t)dummy_mem.size()); + if (handler->uhd->receive(data, nsamples, md, 0.0, false, rxd_samples) != UHD_ERROR_NONE) { log_rx_error(handler); - print_usrp_error(handler); return; } } while (rxd_samples > 0 and md.error_code == uhd::rx_metadata_t::ERROR_CODE_NONE); @@ -773,7 +770,6 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels // Make USRP if (handler->uhd->usrp_make(device_addr, nof_channels) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -794,7 +790,6 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels if (handler->devname.empty()) { std::string mboard_name; if (handler->uhd->get_mboard_name(mboard_name) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -819,7 +814,6 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels // Set sync source if (handler->uhd->set_sync_source(sync_src, clock_src) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -834,7 +828,7 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels if (clock_src != "internal") { // blocks until clock source is locked int error = wait_sensor_locked(handler, sensor_name, true, 300, is_locked); - // Print Not lock error if the return was succesful, wait_sensor_locked prints the error before returning + // Print Not lock error if the return was successful, wait_sensor_locked prints the error before returning if (not is_locked and error == SRSRAN_SUCCESS) { ERROR( "Could not lock reference clock source. Sensor: %s=%s\n", sensor_name.c_str(), is_locked ? "true" : "false"); @@ -846,11 +840,9 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels // Set default Tx/Rx rates if (handler->uhd->set_rx_rate(handler->rx_rate) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } if (handler->uhd->set_tx_rate(handler->tx_rate) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -860,12 +852,10 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels } if (handler->uhd->get_rx_stream(handler->rx_nof_samples) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } if (handler->uhd->get_tx_stream(handler->tx_nof_samples) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -874,7 +864,6 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels for (uint32_t i = 0; i < nof_channels; i++) { if (std::isnormal(handler->rx_freq[i])) { if (handler->uhd->set_rx_freq(i, handler->rx_freq[i], handler->rx_freq[i]) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } rf_uhd_rx_wait_lo_locked(handler); @@ -884,7 +873,6 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels for (uint32_t i = 0; i < nof_channels; i++) { if (std::isnormal(handler->tx_freq[i])) { if (handler->uhd->set_tx_freq(i, handler->tx_freq[i], handler->tx_freq[i]) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } } @@ -894,7 +882,6 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels uhd::gain_range_t tx_gain_range; uhd::gain_range_t rx_gain_range; if (handler->uhd->get_gain_range(tx_gain_range, rx_gain_range) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } handler->info.min_tx_gain = tx_gain_range.start(); @@ -915,7 +902,6 @@ static int uhd_init(rf_uhd_handler_t* handler, char* args, uint32_t nof_channels // Restore priorities if (uhd_set_thread_priority(0, false) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -970,13 +956,11 @@ int rf_uhd_close(void* h) return SRSRAN_SUCCESS; } -static inline void rf_uhd_set_master_clock_rate_unsafe(rf_uhd_handler_t* handler, double rate) +static inline void rf_uhd_set_master_clock_rate_nolock(rf_uhd_handler_t* handler, double rate) { // Set master clock rate if it is allowed and change is required if (handler->dynamic_master_rate and handler->current_master_clock != rate) { - if (handler->uhd->set_master_clock_rate(rate) != UHD_ERROR_NONE) { - print_usrp_error(handler); - } + handler->uhd->set_master_clock_rate(rate); handler->current_master_clock = rate; } } @@ -989,7 +973,7 @@ static inline int rf_uhd_imp_end_burst(rf_uhd_handler_t* handler) // Set buffer pointers for (int i = 0; i < SRSRAN_MAX_CHANNELS; i++) { - buffs_ptr[i] = zero_mem; + buffs_ptr[i] = zero_mem.data(); } // Set metadata @@ -999,7 +983,6 @@ static inline int rf_uhd_imp_end_burst(rf_uhd_handler_t* handler) // Actual base-band transmission if (handler->uhd->send(buffs_ptr, 0, md, RF_UHD_IMP_TRX_TIMEOUT_S, txd_samples) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -1021,20 +1004,19 @@ double rf_uhd_set_rx_srate(void* h, double freq) // Stop RX streamer if (RF_UHD_IMP_PROHIBITED_STOP_START.count(handler->devname) == 0) { - if (rf_uhd_stop_rx_stream_unsafe(handler) != SRSRAN_SUCCESS) { + if (rf_uhd_stop_rx_stream_nolock(handler) != SRSRAN_SUCCESS) { return SRSRAN_ERROR; } } // Set master clock rate if (fmod(handler->current_master_clock, freq) > 0.0) { - rf_uhd_set_master_clock_rate_unsafe(handler, 4 * freq); + rf_uhd_set_master_clock_rate_nolock(handler, 4 * freq); } if (handler->nof_rx_channels > 1) { uhd::time_spec_t timespec; if (handler->uhd->get_time_now(timespec) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } timespec += RF_UHD_IMP_TIMED_COMMAND_DELAY_S; @@ -1043,13 +1025,11 @@ double rf_uhd_set_rx_srate(void* h, double freq) // Set RX rate if (handler->uhd->set_rx_rate(freq) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } if (RF_UHD_IMP_PROHIBITED_STREAM_REMAKE.count(handler->devname) == 0) { if (handler->uhd->get_rx_stream(handler->rx_nof_samples) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } } @@ -1062,11 +1042,12 @@ double rf_uhd_set_rx_srate(void* h, double freq) double rf_uhd_set_tx_srate(void* h, double freq) { - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - std::unique_lock lock(handler->tx_mutex); + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + // Locking order should be kept the same with the async worker. #if HAVE_ASYNC_THREAD std::unique_lock lock_async(handler->async_mutex); #endif /* HAVE_ASYNC_THREAD */ + std::unique_lock lock(handler->tx_mutex); // Early return if the current rate matches and Tx stream has been created if (freq == handler->tx_rate and handler->uhd->is_tx_ready()) { @@ -1082,13 +1063,12 @@ double rf_uhd_set_tx_srate(void* h, double freq) // Set master clock rate if (fmod(handler->current_master_clock, freq) > 0.0) { - rf_uhd_set_master_clock_rate_unsafe(handler, 4 * freq); + rf_uhd_set_master_clock_rate_nolock(handler, 4 * freq); } if (handler->nof_tx_channels > 1) { uhd::time_spec_t timespec; if (handler->uhd->get_time_now(timespec) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } timespec += RF_UHD_IMP_TIMED_COMMAND_DELAY_S; @@ -1097,13 +1077,11 @@ double rf_uhd_set_tx_srate(void* h, double freq) // Set TX rate if (handler->uhd->set_tx_rate(freq) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } if (RF_UHD_IMP_PROHIBITED_STREAM_REMAKE.count(handler->devname) == 0) { if (handler->uhd->get_tx_stream(handler->tx_nof_samples) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } } @@ -1123,7 +1101,6 @@ int rf_uhd_set_rx_gain(void* h, double gain) rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; for (size_t i = 0; i < handler->nof_rx_channels; i++) { if (rf_uhd_set_rx_gain_ch(h, i, gain)) { - print_usrp_error(handler); return SRSRAN_ERROR; } } @@ -1134,9 +1111,11 @@ int rf_uhd_set_rx_gain_ch(void* h, uint32_t ch, double gain) { rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; if (handler->uhd->set_rx_gain(ch, gain) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } + if (ch == 0) { + handler->cur_rx_gain_ch0 = gain; + } return SRSRAN_SUCCESS; } @@ -1144,8 +1123,7 @@ int rf_uhd_set_tx_gain(void* h, double gain) { rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; for (size_t i = 0; i < handler->nof_tx_channels; i++) { - if (rf_uhd_set_tx_gain_ch(h, i, gain)) { - print_usrp_error(handler); + if (rf_uhd_set_tx_gain_ch(h, i, gain) < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } } @@ -1155,24 +1133,34 @@ int rf_uhd_set_tx_gain(void* h, double gain) int rf_uhd_set_tx_gain_ch(void* h, uint32_t ch, double gain) { rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - if (handler->uhd->set_tx_gain(ch, gain) != UHD_ERROR_NONE) { - print_usrp_error(handler); + if (ch >= SRSRAN_MAX_CHANNELS) { return SRSRAN_ERROR; } + + // If the transmitter is not in a burst, update the gain instantly + std::unique_lock lock(handler->tx_gain_mutex); + if (handler->tx_state != RF_UHD_IMP_TX_STATE_BURST) { + // Set gain + if (handler->uhd->set_tx_gain(ch, gain) != UHD_ERROR_NONE) { + return SRSRAN_ERROR; + } + + // Update current gains + handler->tx_gain_db[ch].second = gain; + handler->tx_gain_db[ch].first = gain; + return SRSRAN_SUCCESS; + } + + // Otherwise + handler->tx_gain_db[ch].first = gain; + return SRSRAN_SUCCESS; } double rf_uhd_get_rx_gain(void* h) { rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - double gain = 0.0; - - if (handler->uhd->get_rx_gain(gain) != UHD_ERROR_NONE) { - print_usrp_error(handler); - return SRSRAN_ERROR; - } - - return gain; + return handler->cur_rx_gain_ch0; } double rf_uhd_get_tx_gain(void* h) @@ -1181,7 +1169,6 @@ double rf_uhd_get_tx_gain(void* h) double gain = 0.0; if (handler->uhd->get_tx_gain(gain) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -1210,13 +1197,9 @@ static bool rf_uhd_set_freq_ch(rf_uhd_handler_t* handler, uint32_t ch, double& f // Set frequency if (is_tx) { - if (handler->uhd->set_tx_freq(ch, freq, curr_freq) != UHD_ERROR_NONE) { - print_usrp_error(handler); - } + handler->uhd->set_tx_freq(ch, freq, curr_freq); } else { - if (handler->uhd->set_rx_freq(ch, freq, curr_freq) != UHD_ERROR_NONE) { - print_usrp_error(handler); - } + handler->uhd->set_rx_freq(ch, freq, curr_freq); } return true; } @@ -1261,7 +1244,7 @@ void rf_uhd_get_time(void* h, time_t* secs, double* frac_secs) rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; uhd::time_spec_t timespec; if (handler->uhd->get_time_now(timespec) != UHD_ERROR_NONE) { - print_usrp_error(handler); + return; } if (secs != nullptr) { *secs = timespec.get_full_secs(); @@ -1280,9 +1263,7 @@ void rf_uhd_sync_pps(void* h) rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; uhd::time_spec_t timespec(0.0); - if (handler->uhd->set_time_unknown_pps(timespec) != UHD_ERROR_NONE) { - print_usrp_error(handler); - } + handler->uhd->set_time_unknown_pps(timespec); } int rf_uhd_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) @@ -1314,7 +1295,7 @@ int rf_uhd_recv_with_time_multi(void* h, // Start stream if not started if (not handler->rx_stream_enabled) { - if (rf_uhd_start_rx_stream_unsafe(handler) != SRSRAN_SUCCESS) { + if (rf_uhd_start_rx_stream_nolock(handler) != SRSRAN_SUCCESS) { return SRSRAN_ERROR; } } @@ -1322,17 +1303,22 @@ int rf_uhd_recv_with_time_multi(void* h, // Receive stream in multiple blocks while (rxd_samples_total < nsamples and trials < RF_UHD_IMP_MAX_RX_TRIALS) { void* buffs_ptr[SRSRAN_MAX_CHANNELS] = {}; - for (uint32_t i = 0; i < handler->nof_rx_channels; i++) { - cf_t* data_c = (cf_t*)data[i]; - buffs_ptr[i] = &data_c[rxd_samples_total]; - } size_t num_samps_left = nsamples - rxd_samples_total; - size_t num_rx_samples = (num_samps_left > handler->rx_nof_samples) ? handler->rx_nof_samples : num_samps_left; + size_t num_rx_samples = SRSRAN_MIN(handler->rx_nof_samples, num_samps_left); + + for (uint32_t i = 0; i < handler->nof_rx_channels; i++) { + if (data[i] != nullptr) { + cf_t* data_c = (cf_t*)data[i]; + buffs_ptr[i] = &data_c[rxd_samples_total]; + } else { + buffs_ptr[i] = dummy_mem.data(); + num_rx_samples = SRSRAN_MIN(num_rx_samples, (uint32_t)dummy_mem.size()); + } + } if (handler->uhd->receive(buffs_ptr, num_rx_samples, md, 1.0, false, rxd_samples) != UHD_ERROR_NONE) { log_rx_error(handler); - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -1354,7 +1340,7 @@ int rf_uhd_recv_with_time_multi(void* h, if (RF_UHD_IMP_PROHIBITED_STOP_START.count(handler->devname) == 0) { // Stop Rx stream - rf_uhd_stop_rx_stream_unsafe(handler); + rf_uhd_stop_rx_stream_nolock(handler); } return -1; @@ -1426,7 +1412,6 @@ int rf_uhd_send_timed_multi(void* h, if (is_start_of_burst) { // It gets the USRP time for transmissions without time if (handler->uhd->get_time_now(md.time_spec) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -1442,9 +1427,33 @@ int rf_uhd_send_timed_multi(void* h, cf_t* data_c[SRSRAN_MAX_CHANNELS] = {}; for (uint32_t i = 0; i < SRSRAN_MAX_CHANNELS; i++) { if (i < handler->nof_tx_channels) { - data_c[i] = (data[i] != nullptr) ? (cf_t*)(data[i]) : zero_mem; + data_c[i] = (data[i] != nullptr) ? (cf_t*)(data[i]) : zero_mem.data(); } else { - data_c[i] = zero_mem; + data_c[i] = zero_mem.data(); + } + } + + // Set RF Tx gains if a change is detected + { + std::unique_lock tx_gain_lock(handler->tx_gain_mutex); + for (uint32_t i = 0; i < handler->nof_tx_channels; i++) { + // Skip if the gain remains unchanged + if (handler->tx_gain_db[i].first == handler->tx_gain_db[i].second) { + continue; + } + + // Set the command to applied at the beginning of this transmission + if (handler->uhd->set_command_time(md.time_spec) != UHD_ERROR_NONE) { + return SRSRAN_ERROR; + } + + // Send Tx gain request + if (handler->uhd->set_tx_gain(i, handler->tx_gain_db[i].first) != UHD_ERROR_NONE) { + return SRSRAN_ERROR; + } + + // Update gain + handler->tx_gain_db[i].second = handler->tx_gain_db[i].first; } } @@ -1461,6 +1470,8 @@ int rf_uhd_send_timed_multi(void* h, // Flush receiver only if allowed if (RF_UHD_IMP_PROHIBITED_EOB_FLUSH.count(handler->devname) == 0) { + // Avoid holding the tx lock while in the rf_uhd_flush_buffer function to avoid potential deadlocks. + lock.unlock(); rf_uhd_flush_buffer(h); } @@ -1508,7 +1519,6 @@ int rf_uhd_send_timed_multi(void* h, if (handler->tx_state != RF_UHD_IMP_TX_STATE_WAIT_EOB_ACK) { // Actual transmission if (handler->uhd->send(buffs_ptr, tx_samples, md, RF_UHD_IMP_TRX_TIMEOUT_S, txd_samples) != UHD_ERROR_NONE) { - print_usrp_error(handler); return SRSRAN_ERROR; } @@ -1535,3 +1545,44 @@ int rf_uhd_send_timed_multi(void* h, return nsamples; } + +rf_dev_t srsran_rf_dev_uhd = {"UHD", + rf_uhd_devname, + rf_uhd_start_rx_stream, + rf_uhd_stop_rx_stream, + rf_uhd_flush_buffer, + rf_uhd_has_rssi, + rf_uhd_get_rssi, + rf_uhd_suppress_stdout, + rf_uhd_register_error_handler, + rf_uhd_open, + rf_uhd_open_multi, + rf_uhd_close, + rf_uhd_set_rx_srate, + rf_uhd_set_rx_gain, + rf_uhd_set_rx_gain_ch, + rf_uhd_set_tx_gain, + rf_uhd_set_tx_gain_ch, + rf_uhd_get_rx_gain, + rf_uhd_get_tx_gain, + rf_uhd_get_info, + rf_uhd_set_rx_freq, + rf_uhd_set_tx_srate, + rf_uhd_set_tx_freq, + rf_uhd_get_time, + rf_uhd_sync_pps, + rf_uhd_recv_with_time, + rf_uhd_recv_with_time_multi, + rf_uhd_send_timed, + rf_uhd_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_uhd; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_uhd_imp.h b/lib/src/phy/rf/rf_uhd_imp.h index 925a5d787..67a91da4c 100644 --- a/lib/src/phy/rf/rf_uhd_imp.h +++ b/lib/src/phy/rf/rf_uhd_imp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,6 +38,8 @@ extern "C" { #define DEVNAME_E3X0 "uhd_e3x0" #define DEVNAME_UNKNOWN "uhd_unknown" +extern rf_dev_t srsran_rf_dev_uhd; + SRSRAN_API int rf_uhd_open(char* args, void** handler); SRSRAN_API int rf_uhd_open_multi(char* args, void** handler, uint32_t nof_channels); diff --git a/lib/src/phy/rf/rf_uhd_rfnoc.h b/lib/src/phy/rf/rf_uhd_rfnoc.h index 7c860cc02..faef65939 100644 --- a/lib/src/phy/rf/rf_uhd_rfnoc.h +++ b/lib/src/phy/rf/rf_uhd_rfnoc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -108,17 +108,16 @@ private: // Destroy any previous USRP instance device3 = nullptr; - UHD_SAFE_C_SAVE_ERROR(this, device3 = uhd::device3::make(dev_addr);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(device3 = uhd::device3::make(dev_addr);) } template uhd_error parse_param(uhd::device_addr_t& args, const std::string& param, T& value, bool pop = true) { - UHD_SAFE_C_SAVE_ERROR( - this, + SRSRAN_UHD_SAFE_C_LOG_ERROR( // Check if parameter exists if (not args.has_key(param)) { - last_error = "RF-NOC requires " + param + " parameter"; + Error("RF-NOC requires " + param + " parameter"); return UHD_ERROR_KEY; } @@ -144,14 +143,14 @@ private: // Parse number of radios parse_param(args, "rfnoc_nof_radios", nof_radios); if (nof_radios == 0) { - last_error = "RF-NOC Number of radios cannot be zero"; + Error("RF-NOC Number of radios cannot be zero"); return UHD_ERROR_KEY; } // Parse number of channels per radio parse_param(args, "rfnoc_nof_channels", nof_channels); if (nof_channels == 0) { - last_error = "RF-NOC Number of channels cannot be zero"; + Error("RF-NOC Number of channels cannot be zero"); return UHD_ERROR_KEY; } @@ -167,8 +166,7 @@ private: uhd_error create_control_interfaces() { - UHD_SAFE_C_SAVE_ERROR( - this, + SRSRAN_UHD_SAFE_C_LOG_ERROR( // Create Radio control if (not loopback) { radio_ctrl.resize(nof_radios); @@ -325,9 +323,7 @@ private: nof_samples_per_packet = spp * 4 + 2 * sizeof(uint64_t); } - UHD_SAFE_C_SAVE_ERROR( - this, - + SRSRAN_UHD_SAFE_C_LOG_ERROR( // Get Tx and Rx Graph graph = device3->create_graph("graph"); @@ -367,9 +363,7 @@ private: nof_samples_per_packet = spp * 4 + 2 * sizeof(uint64_t); } - UHD_SAFE_C_SAVE_ERROR( - this, - + SRSRAN_UHD_SAFE_C_LOG_ERROR( // Get Tx and Rx Graph graph = device3->create_graph("graph"); @@ -425,10 +419,10 @@ public: // Check number of channels if (nof_channels_ % nof_channels != 0 or nof_channels_ / nof_channels > nof_radios) { - last_error = "Number of requested channels (" + std::to_string(nof_channels_) + - ") is different than the RFNOC " - "available channels (" + - std::to_string(nof_radios * nof_channels) + ")"; + Error("Number of requested channels (" + std::to_string(nof_channels_) + + ") is different than the RFNOC " + "available channels (" + + std::to_string(nof_radios * nof_channels) + ")"); return UHD_ERROR_VALUE; } @@ -470,49 +464,41 @@ public: }; uhd_error get_mboard_sensor_names(std::vector& sensors) override { - UHD_SAFE_C_SAVE_ERROR( - this, if (device3->get_tree()->exists(TREE_MBOARD_SENSORS)) { - sensors = device3->get_tree()->list(TREE_MBOARD_SENSORS); - }) + SRSRAN_UHD_SAFE_C_LOG_ERROR(if (device3->get_tree()->exists(TREE_MBOARD_SENSORS)) { + sensors = device3->get_tree()->list(TREE_MBOARD_SENSORS); + }) } uhd_error get_rx_sensor_names(std::vector& sensors) override { - UHD_SAFE_C_SAVE_ERROR( - this, + SRSRAN_UHD_SAFE_C_LOG_ERROR( if (device3->get_tree()->exists(TREE_RX_SENSORS)) { sensors = device3->get_tree()->list(TREE_RX_SENSORS); }) } uhd_error get_sensor(const std::string& sensor_name, double& sensor_value) override { - UHD_SAFE_C_SAVE_ERROR( - this, + SRSRAN_UHD_SAFE_C_LOG_ERROR( sensor_value = device3->get_tree()->access(TREE_MBOARD_SENSORS / sensor_name).get().to_real();) } uhd_error get_sensor(const std::string& sensor_name, bool& sensor_value) override { - UHD_SAFE_C_SAVE_ERROR( - this, + SRSRAN_UHD_SAFE_C_LOG_ERROR( sensor_value = device3->get_tree()->access(TREE_MBOARD_SENSORS / sensor_name).get().to_bool();) } uhd_error get_rx_sensor(const std::string& sensor_name, bool& sensor_value) override { - UHD_SAFE_C_SAVE_ERROR( - this, + SRSRAN_UHD_SAFE_C_LOG_ERROR( sensor_value = device3->get_tree()->access(TREE_RX_SENSORS / sensor_name).get().to_bool();) } uhd_error set_time_unknown_pps(const uhd::time_spec_t& timespec) override { Info("Setting time " << timespec.get_real_secs() << " at next PPS..."); - UHD_SAFE_C_SAVE_ERROR( - this, - for (auto& r - : radio_ctrl) { r->set_time_next_pps(timespec); }); + SRSRAN_UHD_SAFE_C_LOG_ERROR(for (auto& r : radio_ctrl) { r->set_time_next_pps(timespec); }); } uhd_error get_time_now(uhd::time_spec_t& timespec) override { - UHD_SAFE_C_SAVE_ERROR(this, timespec = device3->get_tree()->access(TREE_TIME_NOW).get(); - Info("-- " << timespec.get_real_secs());) + SRSRAN_UHD_SAFE_C_LOG_ERROR(timespec = device3->get_tree()->access(TREE_TIME_NOW).get(); + Info("-- " << timespec.get_real_secs());) } uhd_error set_sync_source(const std::string& sync_source, const std::string& clock_source) override { @@ -520,13 +506,12 @@ public: return UHD_ERROR_NONE; } - UHD_SAFE_C_SAVE_ERROR( - this, for (size_t radio_idx = 0; radio_idx < nof_radios; radio_idx++) { - UHD_LOG_DEBUG(radio_id[radio_idx], - "Setting PPS source to '" << sync_source << "' and clock source to '" << clock_source << "'"); - radio_ctrl[radio_idx]->set_clock_source(clock_source); - radio_ctrl[radio_idx]->set_time_source(sync_source); - }) + SRSRAN_UHD_SAFE_C_LOG_ERROR(for (size_t radio_idx = 0; radio_idx < nof_radios; radio_idx++) { + UHD_LOG_DEBUG(radio_id[radio_idx], + "Setting PPS source to '" << sync_source << "' and clock source to '" << clock_source << "'"); + radio_ctrl[radio_idx]->set_clock_source(clock_source); + radio_ctrl[radio_idx]->set_time_source(sync_source); + }) } uhd_error get_gain_range(uhd::gain_range_t& tx_gain_range, uhd::gain_range_t& rx_gain_range) override { @@ -538,29 +523,27 @@ public: uhd_error set_master_clock_rate(double rate) override { return UHD_ERROR_NONE; } uhd_error set_rx_rate(double rate) override { - UHD_SAFE_C_SAVE_ERROR( - this, for (size_t i = 0; i < nof_radios; i++) { - for (size_t j = 0; j < nof_channels; j++) { - UHD_LOG_DEBUG(ddc_id[i], "Setting channel " << j << " output rate to " << rate / 1e6 << " MHz"); - ddc_ctrl[i]->set_arg("output_rate", std::to_string(rate), j); - } - }) + SRSRAN_UHD_SAFE_C_LOG_ERROR(for (size_t i = 0; i < nof_radios; i++) { + for (size_t j = 0; j < nof_channels; j++) { + UHD_LOG_DEBUG(ddc_id[i], "Setting channel " << j << " output rate to " << rate / 1e6 << " MHz"); + ddc_ctrl[i]->set_arg("output_rate", std::to_string(rate), j); + } + }) } uhd_error set_tx_rate(double rate) override { - UHD_SAFE_C_SAVE_ERROR( - this, for (size_t i = 0; i < nof_radios; i++) { - for (size_t j = 0; j < nof_channels; j++) { - UHD_LOG_DEBUG(duc_id[i], "Setting channel " << j << " input rate to " << rate / 1e6 << " MHz"); - duc_ctrl[i]->set_arg("input_rate", std::to_string(rate), j); - } - }) + SRSRAN_UHD_SAFE_C_LOG_ERROR(for (size_t i = 0; i < nof_radios; i++) { + for (size_t j = 0; j < nof_channels; j++) { + UHD_LOG_DEBUG(duc_id[i], "Setting channel " << j << " input rate to " << rate / 1e6 << " MHz"); + duc_ctrl[i]->set_arg("input_rate", std::to_string(rate), j); + } + }) } uhd_error set_command_time(const uhd::time_spec_t& timespec) override { return UHD_ERROR_NONE; } uhd_error get_rx_stream(size_t& max_num_samps) override { - UHD_SAFE_C_SAVE_ERROR( - this, uhd::stream_args_t stream_args("fc32", "sc16"); + SRSRAN_UHD_SAFE_C_LOG_ERROR( + uhd::stream_args_t stream_args("fc32", "sc16"); stream_args.channels.resize(nof_radios * nof_channels); @@ -584,8 +567,8 @@ public: } uhd_error get_tx_stream(size_t& max_num_samps) override { - UHD_SAFE_C_SAVE_ERROR( - this, uhd::stream_args_t stream_args("fc32", "sc16"); + SRSRAN_UHD_SAFE_C_LOG_ERROR( + uhd::stream_args_t stream_args("fc32", "sc16"); stream_args.channels.resize(nof_radios * nof_channels); if (spp != 0) { stream_args.args["spp"] = std::to_string(spp); } @@ -614,7 +597,7 @@ public: uhd_error set_tx_gain(size_t ch, double gain) override { if (ch >= nof_channels * nof_radios) { - last_error = "Invalid channel index " + std::to_string(ch); + Error("Invalid channel index " + std::to_string(ch)); return UHD_ERROR_INDEX; } @@ -627,12 +610,11 @@ public: // Set the gain for the channel zero only if (channel_idx != 0) { - last_error = "None"; return UHD_ERROR_NONE; } - UHD_SAFE_C_SAVE_ERROR( - this, UHD_LOG_DEBUG(radio_id[radio_idx], "Setting TX Gain: " << gain << " dB..."); + SRSRAN_UHD_SAFE_C_LOG_ERROR( + UHD_LOG_DEBUG(radio_id[radio_idx], "Setting TX Gain: " << gain << " dB..."); radio_ctrl[radio_idx]->set_tx_gain(gain, 0); UHD_LOG_DEBUG(radio_id[radio_idx], "Actual TX Gain: " << radio_ctrl[radio_idx]->get_rx_gain(0) << " dB...");) } @@ -640,7 +622,7 @@ public: uhd_error set_rx_gain(size_t ch, double gain) override { if (ch >= nof_channels * nof_radios) { - last_error = "Invalid channel index " + std::to_string(ch); + Error("Invalid channel index " + std::to_string(ch)); return UHD_ERROR_INDEX; } @@ -653,12 +635,11 @@ public: // Set the gain for the channel zero only if (channel_idx != 0) { - last_error = "None"; return UHD_ERROR_NONE; } - UHD_SAFE_C_SAVE_ERROR( - this, UHD_LOG_DEBUG(radio_id[radio_idx], "Setting RX Gain: " << gain << " dB..."); + SRSRAN_UHD_SAFE_C_LOG_ERROR( + UHD_LOG_DEBUG(radio_id[radio_idx], "Setting RX Gain: " << gain << " dB..."); radio_ctrl[radio_idx]->set_rx_gain(gain, 0); UHD_LOG_DEBUG(radio_id[radio_idx], "Actual RX Gain: " << radio_ctrl[radio_idx]->get_rx_gain(0) << " dB...");) } @@ -668,7 +649,7 @@ public: return UHD_ERROR_NONE; } - UHD_SAFE_C_SAVE_ERROR(this, gain = radio_ctrl[0]->get_tx_gain(0);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(gain = radio_ctrl[0]->get_tx_gain(0);) } uhd_error get_rx_gain(double& gain) override { @@ -676,23 +657,22 @@ public: return UHD_ERROR_NONE; } - UHD_SAFE_C_SAVE_ERROR(this, gain = radio_ctrl[0]->get_rx_gain(0);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(gain = radio_ctrl[0]->get_rx_gain(0);) } uhd_error set_tx_freq(uint32_t ch, double target_freq, double& actual_freq) override { if (ch >= tx_freq_hz.size()) { - last_error = "Invalid channel index " + std::to_string(ch); + Error("Invalid channel index " + std::to_string(ch)); return UHD_ERROR_INDEX; } if (not std::isnormal(target_freq)) { - last_error = "Invalid TX frequency value " + std::to_string(target_freq) + " for channel " + std::to_string(ch); + Error("Invalid TX frequency value " + std::to_string(target_freq) + " for channel " + std::to_string(ch)); return UHD_ERROR_VALUE; } // Nothing to update if (std::round(tx_freq_hz[ch]) == std::round(target_freq)) { - last_error = "None"; return UHD_ERROR_NONE; } @@ -704,9 +684,7 @@ public: size_t i = ch / nof_channels; size_t j = ch % nof_channels; - UHD_SAFE_C_SAVE_ERROR( - this, - + SRSRAN_UHD_SAFE_C_LOG_ERROR( // Set Radio Tx freq if (not std::isnormal(tx_center_freq_hz[i]) and not loopback) { UHD_LOG_DEBUG(radio_id[i], @@ -731,18 +709,17 @@ public: uhd_error set_rx_freq(uint32_t ch, double target_freq, double& actual_freq) override { if (ch >= rx_freq_hz.size()) { - last_error = "Invalid channel index " + std::to_string(ch); + Error("Invalid channel index " + std::to_string(ch)); return UHD_ERROR_INDEX; } if (not std::isnormal(target_freq)) { - last_error = "Invalid TX frequency value " + std::to_string(target_freq) + " for channel " + std::to_string(ch); + Error("Invalid TX frequency value " + std::to_string(target_freq) + " for channel " + std::to_string(ch)); return UHD_ERROR_VALUE; } // Nothing to update if (std::round(rx_freq_hz[ch]) == std::round(target_freq)) { - last_error = "None"; return UHD_ERROR_NONE; } @@ -754,9 +731,7 @@ public: size_t i = ch / nof_channels; size_t j = ch % nof_channels; - UHD_SAFE_C_SAVE_ERROR( - this, - + SRSRAN_UHD_SAFE_C_LOG_ERROR( // Set Radio Tx freq if (not std::isnormal(rx_center_freq_hz[i]) and not loopback) { UHD_LOG_DEBUG(radio_id[i], "Setting RX Freq: " << target_freq / 1e6 << " MHz..."); diff --git a/lib/src/phy/rf/rf_uhd_safe.h b/lib/src/phy/rf/rf_uhd_safe.h index 1eb5fc037..3d9390da1 100644 --- a/lib/src/phy/rf/rf_uhd_safe.h +++ b/lib/src/phy/rf/rf_uhd_safe.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,17 +25,37 @@ #include #ifdef UHD_LOG_INFO +#define Error(message) UHD_LOG_ERROR("UHD RF", message) #define Warning(message) UHD_LOG_WARNING("UHD RF", message) #define Info(message) UHD_LOG_INFO("UHD RF", message) #define Debug(message) UHD_LOG_DEBUG("UHD RF", message) #define Trace(message) UHD_LOG_TRACE("UHD RF", message) #else +#define Error(message) UHD_LOG << message << std::endl #define Warning(message) UHD_LOG << message << std::endl #define Info(message) UHD_LOG << message << std::endl #define Debug(message) UHD_LOG << message << std::endl #define Trace(message) UHD_LOG << message << std::endl #endif +#define SRSRAN_UHD_SAFE_C_LOG_ERROR(...) \ + try { \ + __VA_ARGS__ \ + } catch (const uhd::exception& e) { \ + Error(e.what()); \ + return error_from_uhd_exception(&e); \ + } catch (const boost::exception& e) { \ + Error(boost::diagnostic_information(e)); \ + return UHD_ERROR_BOOSTEXCEPT; \ + } catch (const std::exception& e) { \ + Error(e.what()); \ + return UHD_ERROR_STDEXCEPT; \ + } catch (...) { \ + Error("Unrecognized exception caught."); \ + return UHD_ERROR_UNKNOWN; \ + } \ + return UHD_ERROR_NONE; + #ifdef ENABLE_UHD_X300_FW_RESET #include @@ -54,8 +74,7 @@ private: #ifdef ENABLE_UHD_X300_FW_RESET uhd_error try_usrp_x300_reset(const uhd::device_addr_t& dev_addr) { - UHD_SAFE_C_SAVE_ERROR( - this, + SRSRAN_UHD_SAFE_C_LOG_ERROR( // It is not possible to reset device if IP address is not provided if (not dev_addr.has_key("addr")) { return UHD_ERROR_NONE; } @@ -118,8 +137,6 @@ protected: } public: - std::string last_error; - virtual uhd_error usrp_make(const uhd::device_addr_t& dev_addr, uint32_t nof_channels) = 0; virtual uhd_error get_mboard_name(std::string& mboard_name) = 0; virtual uhd_error get_mboard_sensor_names(std::vector& sensors) = 0; @@ -138,19 +155,19 @@ public: return err; } - UHD_SAFE_C_SAVE_ERROR(this, uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); - stream_cmd.time_spec = time_spec; - stream_cmd.time_spec += delay; - stream_cmd.stream_now = not std::isnormal(delay); + SRSRAN_UHD_SAFE_C_LOG_ERROR(uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + stream_cmd.time_spec = time_spec; + stream_cmd.time_spec += delay; + stream_cmd.stream_now = not std::isnormal(delay); - rx_stream->issue_stream_cmd(stream_cmd);) + rx_stream->issue_stream_cmd(stream_cmd);) } uhd_error stop_rx_stream() { Debug("Stopping Rx stream"); - UHD_SAFE_C_SAVE_ERROR(this, uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); - stream_cmd.stream_now = true; - rx_stream->issue_stream_cmd(stream_cmd);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + stream_cmd.stream_now = true; + rx_stream->issue_stream_cmd(stream_cmd);) } virtual uhd_error set_sync_source(const std::string& sync_source, const std::string& clock_source) = 0; virtual uhd_error get_gain_range(uhd::gain_range_t& tx_gain_range, uhd::gain_range_t& rx_gain_range) = 0; @@ -173,12 +190,13 @@ public: const bool one_packet, size_t& nof_rxd_samples) { - UHD_SAFE_C_SAVE_ERROR(this, uhd::rx_streamer::buffs_type buffs_cpp(buffs, rx_stream->get_num_channels()); - nof_rxd_samples = rx_stream->recv(buffs_cpp, nsamps_per_buff, metadata, timeout, one_packet);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(uhd::rx_streamer::buffs_type buffs_cpp(buffs, rx_stream->get_num_channels()); + nof_rxd_samples = + rx_stream->recv(buffs_cpp, nsamps_per_buff, metadata, timeout, one_packet);) } virtual uhd_error recv_async_msg(uhd::async_metadata_t& async_metadata, double timeout, bool& valid) { - UHD_SAFE_C_SAVE_ERROR(this, valid = tx_stream->recv_async_msg(async_metadata, timeout);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(valid = tx_stream->recv_async_msg(async_metadata, timeout);) } uhd_error send(void** buffs, const size_t nsamps_per_buff, @@ -186,8 +204,8 @@ public: const double timeout, size_t& nof_txd_samples) { - UHD_SAFE_C_SAVE_ERROR(this, uhd::tx_streamer::buffs_type buffs_cpp(buffs, tx_stream->get_num_channels()); - nof_txd_samples = tx_stream->send(buffs_cpp, nsamps_per_buff, metadata, timeout);) + SRSRAN_UHD_SAFE_C_LOG_ERROR(uhd::tx_streamer::buffs_type buffs_cpp(buffs, tx_stream->get_num_channels()); + nof_txd_samples = tx_stream->send(buffs_cpp, nsamps_per_buff, metadata, timeout);) } virtual bool is_rx_ready() { return rx_stream != nullptr; } virtual bool is_tx_ready() { return tx_stream != nullptr; } diff --git a/lib/src/phy/rf/rf_utils.c b/lib/src/phy/rf/rf_utils.c index 0ac08cdd8..face9df7a 100644 --- a/lib/src/phy/rf/rf_utils.c +++ b/lib/src/phy/rf/rf_utils.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/rf/rf_zmq_imp.c b/lib/src/phy/rf/rf_zmq_imp.c index 43108e048..1b672799e 100644 --- a/lib/src/phy/rf/rf_zmq_imp.c +++ b/lib/src/phy/rf/rf_zmq_imp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,6 +21,7 @@ #include "rf_zmq_imp.h" #include "rf_helper.h" +#include "rf_plugin.h" #include "rf_zmq_imp_trx.h" #include #include @@ -42,6 +43,7 @@ typedef struct { uint32_t base_srate; uint32_t decim_factor; // decimation factor between base_srate used on transport on radio's rate double rx_gain; + double tx_gain; uint32_t tx_freq_mhz[SRSRAN_MAX_CHANNELS]; uint32_t rx_freq_mhz[SRSRAN_MAX_CHANNELS]; bool tx_off; @@ -62,9 +64,10 @@ typedef struct { pthread_mutex_t tx_config_mutex; pthread_mutex_t rx_config_mutex; pthread_mutex_t decim_mutex; + pthread_mutex_t rx_gain_mutex; } rf_zmq_handler_t; -void update_rates(rf_zmq_handler_t* handler, double srate); +static void update_rates(rf_zmq_handler_t* handler, double srate); /* * Static Atributes @@ -203,9 +206,11 @@ int rf_zmq_open_multi(char* args, void** h, uint32_t nof_channels) return SRSRAN_ERROR; } bzero(handler, sizeof(rf_zmq_handler_t)); - *h = handler; - handler->base_srate = ZMQ_BASERATE_DEFAULT_HZ; // Sample rate for 100 PRB cell - handler->rx_gain = 0.0; + *h = handler; + handler->base_srate = ZMQ_BASERATE_DEFAULT_HZ; // Sample rate for 100 PRB cell + pthread_mutex_lock(&handler->rx_gain_mutex); + handler->rx_gain = 0.0; + pthread_mutex_unlock(&handler->rx_gain_mutex); handler->info.max_rx_gain = ZMQ_MAX_GAIN_DB; handler->info.min_rx_gain = ZMQ_MIN_GAIN_DB; handler->info.max_tx_gain = ZMQ_MAX_GAIN_DB; @@ -229,6 +234,9 @@ int rf_zmq_open_multi(char* args, void** h, uint32_t nof_channels) if (pthread_mutex_init(&handler->decim_mutex, NULL)) { perror("Mutex init"); } + if (pthread_mutex_init(&handler->rx_gain_mutex, NULL)) { + perror("Mutex init"); + } // parse args if (args && strlen(args)) { @@ -281,7 +289,9 @@ int rf_zmq_open_multi(char* args, void** h, uint32_t nof_channels) } } } else { - fprintf(stderr, "[zmq] Error: RF device args are required for ZMQ no-RF module\n"); + fprintf(stderr, + "[zmq] Error: No device 'args' option has been set. Please make sure to set this option to be able to " + "use the ZMQ no-RF module\n"); goto clean_exit; } @@ -417,6 +427,7 @@ int rf_zmq_close(void* h) pthread_mutex_destroy(&handler->tx_config_mutex); pthread_mutex_destroy(&handler->rx_config_mutex); pthread_mutex_destroy(&handler->decim_mutex); + pthread_mutex_destroy(&handler->rx_gain_mutex); // Free all free(handler); @@ -472,7 +483,9 @@ int rf_zmq_set_rx_gain(void* h, double gain) { if (h) { rf_zmq_handler_t* handler = (rf_zmq_handler_t*)h; - handler->rx_gain = gain; + pthread_mutex_lock(&handler->rx_gain_mutex); + handler->rx_gain = gain; + pthread_mutex_unlock(&handler->rx_gain_mutex); } return SRSRAN_SUCCESS; } @@ -484,6 +497,12 @@ int rf_zmq_set_rx_gain_ch(void* h, uint32_t ch, double gain) int rf_zmq_set_tx_gain(void* h, double gain) { + if (h) { + rf_zmq_handler_t* handler = (rf_zmq_handler_t*)h; + pthread_mutex_lock(&handler->tx_config_mutex); + handler->tx_gain = gain; + pthread_mutex_unlock(&handler->tx_config_mutex); + } return SRSRAN_SUCCESS; } @@ -497,14 +516,23 @@ double rf_zmq_get_rx_gain(void* h) double ret = 0.0; if (h) { rf_zmq_handler_t* handler = (rf_zmq_handler_t*)h; - ret = handler->rx_gain; + pthread_mutex_lock(&handler->rx_gain_mutex); + ret = handler->rx_gain; + pthread_mutex_unlock(&handler->rx_gain_mutex); } return ret; } double rf_zmq_get_tx_gain(void* h) { - return 0.0; + float ret = NAN; + if (h) { + rf_zmq_handler_t* handler = (rf_zmq_handler_t*)h; + pthread_mutex_lock(&handler->tx_config_mutex); + ret = handler->tx_gain; + pthread_mutex_unlock(&handler->tx_config_mutex); + } + return ret; } srsran_rf_info_t* rf_zmq_get_info(void* h) @@ -606,12 +634,7 @@ int rf_zmq_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, return rf_zmq_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs); } -int rf_zmq_recv_with_time_multi(void* h, - void* data[4], - uint32_t nsamples, - bool blocking, - time_t* secs, - double* frac_secs) +int rf_zmq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs) { int ret = SRSRAN_ERROR; @@ -620,23 +643,28 @@ int rf_zmq_recv_with_time_multi(void* h, // Map ports to data buffers according to the selected frequencies pthread_mutex_lock(&handler->rx_config_mutex); + bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched - for (uint32_t i = 0; i < handler->nof_channels; i++) { - bool mapped = false; - // Find first matching frequency - for (uint32_t j = 0; j < handler->nof_channels && !mapped; j++) { - // Traverse all channels, break if mapped - if (buffers[j] == NULL && rf_zmq_rx_match_freq(&handler->receiver[j], handler->rx_freq_mhz[i])) { - // Available buffer and matched frequency with receiver - buffers[j] = (cf_t*)data[i]; - mapped = true; + // For each logical channel... + for (uint32_t logical = 0; logical < handler->nof_channels; logical++) { + bool unmatched = true; + + // For each physical channel... + for (uint32_t physical = 0; physical < handler->nof_channels; physical++) { + // Consider a match if the physical channel is NOT mapped and the frequency match + if (!mapped[physical] && rf_zmq_rx_match_freq(&handler->receiver[physical], handler->rx_freq_mhz[logical])) { + // Not mapped and matched frequency with receiver + buffers[physical] = (cf_t*)data[logical]; + mapped[physical] = true; + unmatched = false; + break; } } // If no matching frequency found; set data to zeros - if (!mapped && data[i]) { - memset(data[i], 0, sizeof(cf_t) * nsamples); + if (unmatched) { + srsran_vec_zero(data[logical], nsamples); } } pthread_mutex_unlock(&handler->rx_config_mutex); @@ -660,7 +688,7 @@ int rf_zmq_recv_with_time_multi(void* h, } // return if receiver is turned off - if (!handler->receiver[0].running) { + if (!rf_zmq_rx_is_running(&handler->receiver[0])) { update_ts(handler, &handler->next_rx_ts, nsamples_baserate, "rx"); return nsamples; } @@ -677,17 +705,17 @@ int rf_zmq_recv_with_time_multi(void* h, // receive samples srsran_timestamp_t ts_tx = {}, ts_rx = {}; - srsran_timestamp_init_uint64(&ts_tx, handler->transmitter[0].nsamples, handler->base_srate); + srsran_timestamp_init_uint64(&ts_tx, rf_zmq_tx_get_nsamples(&handler->transmitter[0]), handler->base_srate); srsran_timestamp_init_uint64(&ts_rx, handler->next_rx_ts, handler->base_srate); rf_zmq_info(handler->id, " - next rx time: %d + %.3f\n", ts_rx.full_secs, ts_rx.frac_secs); rf_zmq_info(handler->id, " - next tx time: %d + %.3f\n", ts_tx.full_secs, ts_tx.frac_secs); // Leave time for the Tx to transmit - usleep((1000000 * nsamples_baserate) / handler->base_srate); + usleep((1000000UL * nsamples_baserate) / handler->base_srate); // check for tx gap if we're also transmitting on this radio for (int i = 0; i < handler->nof_channels; i++) { - if (handler->transmitter[i].running) { + if (rf_zmq_tx_is_running(&handler->transmitter[i])) { rf_zmq_tx_align(&handler->transmitter[i], handler->next_rx_ts + nsamples_baserate); } } @@ -703,7 +731,7 @@ int rf_zmq_recv_with_time_multi(void* h, cf_t* ptr = (decim_factor != 1 || buffers[i] == NULL) ? handler->buffer_decimation[i] : buffers[i]; // Completed condition - if (count[i] < nsamples_baserate && handler->receiver[i].running) { + if (count[i] < nsamples_baserate && rf_zmq_rx_is_running(&handler->receiver[i])) { // Keep receiving int32_t n = rf_zmq_rx_baseband(&handler->receiver[i], &ptr[count[i]], nsamples_baserate); #if ZMQ_MONITOR @@ -767,7 +795,7 @@ int rf_zmq_recv_with_time_multi(void* h, for (int j = 0; j < decim_factor; j++, n++) { avg += ptr[n]; } - dst[i] = avg; + dst[i] = avg; // divide by decim_factor later via scale } rf_zmq_info(handler->id, @@ -780,7 +808,13 @@ int rf_zmq_recv_with_time_multi(void* h, } // Set gain + pthread_mutex_lock(&handler->rx_gain_mutex); float scale = srsran_convert_dB_to_amplitude(handler->rx_gain); + pthread_mutex_unlock(&handler->rx_gain_mutex); + // scale shall also incorporate decim_factor + if (decim_factor > 0) { + scale = scale / decim_factor; + } for (uint32_t c = 0; c < handler->nof_channels; c++) { if (buffers[c]) { srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples); @@ -832,22 +866,33 @@ int rf_zmq_send_timed_multi(void* h, // Map ports to data buffers according to the selected frequencies pthread_mutex_lock(&handler->tx_config_mutex); - cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched - for (uint32_t i = 0; i < handler->nof_channels; i++) { - bool mapped = false; + bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used + cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched or zero transmission - // Find first matching frequency - for (uint32_t j = 0; j < handler->nof_channels && !mapped; j++) { - // Traverse all channels, break if mapped - if (buffers[j] == NULL && rf_zmq_tx_match_freq(&handler->transmitter[j], handler->tx_freq_mhz[i])) { - // Available buffer and matched frequency with receiver - buffers[j] = (cf_t*)data[i]; - mapped = true; + // For each logical channel... + for (uint32_t logical = 0; logical < handler->nof_channels; logical++) { + // For each physical channel... + for (uint32_t physical = 0; physical < handler->nof_channels; physical++) { + // Consider a match if the physical channel is NOT mapped and the frequency match + if (!mapped[physical] && rf_zmq_tx_match_freq(&handler->transmitter[physical], handler->tx_freq_mhz[logical])) { + // Not mapped and matched frequency with receiver + buffers[physical] = (cf_t*)data[logical]; + mapped[physical] = true; + break; } } } + + // Load transmission gain + float tx_gain = srsran_convert_dB_to_amplitude(handler->tx_gain); + pthread_mutex_unlock(&handler->tx_config_mutex); + // If the Tx gain is NAN, INF or 0.0, use 1.0 + if (!isnormal(tx_gain)) { + tx_gain = 1.0f; + } + // Protect the access to decim_factor since is a shared variable pthread_mutex_lock(&handler->decim_mutex); uint32_t decim_factor = handler->decim_factor; @@ -878,7 +923,7 @@ int rf_zmq_send_timed_multi(void* h, int num_tx_gap_samples = 0; for (int i = 0; i < handler->nof_channels; i++) { - if (handler->transmitter[i].running) { + if (rf_zmq_tx_is_running(&handler->transmitter[i])) { num_tx_gap_samples = rf_zmq_tx_align(&handler->transmitter[i], tx_ts); } } @@ -888,7 +933,7 @@ int rf_zmq_send_timed_multi(void* h, "[zmq] Error: tx time is %.3f ms in the past (%" PRIu64 " < %" PRIu64 ")\n", -1000.0 * num_tx_gap_samples / handler->base_srate, tx_ts, - handler->transmitter[0].nsamples); + (uint64_t)rf_zmq_tx_get_nsamples(&handler->transmitter[0])); goto clean_exit; } } @@ -925,6 +970,10 @@ int rf_zmq_send_timed_multi(void* h, } } + // Scale according to current gain + srsran_vec_sc_prod_cfc(buf, tx_gain, buf, nsamples_baseband); + + // Finally, transmit baseband int n = rf_zmq_tx_baseband(&handler->transmitter[i], buf, nsamples_baseband); if (n == SRSRAN_ERROR) { goto clean_exit; @@ -944,3 +993,44 @@ clean_exit: return ret; } + +rf_dev_t srsran_rf_dev_zmq = {"zmq", + rf_zmq_devname, + rf_zmq_start_rx_stream, + rf_zmq_stop_rx_stream, + rf_zmq_flush_buffer, + rf_zmq_has_rssi, + rf_zmq_get_rssi, + rf_zmq_suppress_stdout, + rf_zmq_register_error_handler, + rf_zmq_open, + .srsran_rf_open_multi = rf_zmq_open_multi, + rf_zmq_close, + rf_zmq_set_rx_srate, + rf_zmq_set_rx_gain, + rf_zmq_set_rx_gain_ch, + rf_zmq_set_tx_gain, + rf_zmq_set_tx_gain_ch, + rf_zmq_get_rx_gain, + rf_zmq_get_tx_gain, + rf_zmq_get_info, + rf_zmq_set_rx_freq, + rf_zmq_set_tx_srate, + rf_zmq_set_tx_freq, + rf_zmq_get_time, + NULL, + rf_zmq_recv_with_time, + rf_zmq_recv_with_time_multi, + rf_zmq_send_timed, + .srsran_rf_send_timed_multi = rf_zmq_send_timed_multi}; + +#ifdef ENABLE_RF_PLUGINS +int register_plugin(rf_dev_t** rf_api) +{ + if (rf_api == NULL) { + return SRSRAN_ERROR; + } + *rf_api = &srsran_rf_dev_zmq; + return SRSRAN_SUCCESS; +} +#endif /* ENABLE_RF_PLUGINS */ diff --git a/lib/src/phy/rf/rf_zmq_imp.h b/lib/src/phy/rf/rf_zmq_imp.h index 0df0da872..b8b352f0d 100644 --- a/lib/src/phy/rf/rf_zmq_imp.h +++ b/lib/src/phy/rf/rf_zmq_imp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -30,6 +30,8 @@ #define DEVNAME_ZMQ "ZeroMQ" +extern rf_dev_t srsran_rf_dev_zmq; + SRSRAN_API int rf_zmq_open(char* args, void** handler); SRSRAN_API int rf_zmq_open_multi(char* args, void** handler, uint32_t nof_channels); diff --git a/lib/src/phy/rf/rf_zmq_imp_rx.c b/lib/src/phy/rf/rf_zmq_imp_rx.c index 49aca7ed0..50ed807a9 100644 --- a/lib/src/phy/rf/rf_zmq_imp_rx.c +++ b/lib/src/phy/rf/rf_zmq_imp_rx.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -30,7 +30,7 @@ static void* rf_zmq_async_rx_thread(void* h) { rf_zmq_rx_t* q = (rf_zmq_rx_t*)h; - while (q->sock && q->running) { + while (q->sock && rf_zmq_rx_is_running(q)) { int nbytes = 0; int n = SRSRAN_ERROR; uint8_t dummy = 0xFF; @@ -39,7 +39,7 @@ static void* rf_zmq_async_rx_thread(void* h) // Send request if socket type is REQUEST if (q->socket_type == ZMQ_REQ) { - while (n < 0 && q->running) { + while (n < 0 && rf_zmq_rx_is_running(q)) { rf_zmq_info(q->id, " - tx'ing rx request\n"); n = zmq_send(q->sock, &dummy, sizeof(dummy), 0); if (n < 0) { @@ -53,7 +53,7 @@ static void* rf_zmq_async_rx_thread(void* h) } // Receive baseband - for (n = (n < 0) ? 0 : -1; n < 0 && q->running;) { + for (n = (n < 0) ? 0 : -1; n < 0 && rf_zmq_rx_is_running(q);) { n = zmq_recv(q->sock, q->temp_buffer, ZMQ_MAX_BUFFER_SIZE, 0); if (n == -1) { if (rf_zmq_handle_error(q->id, "asynchronous rx baseband receive")) { @@ -77,7 +77,7 @@ static void* rf_zmq_async_rx_thread(void* h) n = -1; // Try to write in ring buffer - while (n < 0 && q->running) { + while (n < 0 && rf_zmq_rx_is_running(q)) { n = srsran_ringbuffer_write_timed(&q->ringbuffer, q->temp_buffer, nbytes, q->trx_timeout_ms); if (n == SRSRAN_ERROR_TIMEOUT && q->log_trx_timeout) { fprintf(stderr, "Error: timeout writing samples to ringbuffer after %dms\n", q->trx_timeout_ms); @@ -161,7 +161,7 @@ int rf_zmq_rx_open(rf_zmq_rx_t* q, rf_zmq_opts_t opts, void* zmq_ctx, char* sock } if (zmq_setsockopt(q->sock, ZMQ_SNDTIMEO, &timeout, sizeof(timeout)) == -1) { - fprintf(stderr, "Error: setting receive timeout on rx socket\n"); + fprintf(stderr, "Error: setting send timeout on rx socket\n"); goto clean_exit; } @@ -262,13 +262,18 @@ bool rf_zmq_rx_match_freq(rf_zmq_rx_t* q, uint32_t freq_hz) void rf_zmq_rx_close(rf_zmq_rx_t* q) { rf_zmq_info(q->id, "Closing ...\n"); + + pthread_mutex_lock(&q->mutex); q->running = false; + pthread_mutex_unlock(&q->mutex); if (q->thread) { pthread_join(q->thread, NULL); pthread_detach(q->thread); } + pthread_mutex_destroy(&q->mutex); + srsran_ringbuffer_free(&q->ringbuffer); if (q->temp_buffer) { @@ -291,3 +296,17 @@ void rf_zmq_rx_close(rf_zmq_rx_t* q) } #endif // ZMQ_MONITOR } + +bool rf_zmq_rx_is_running(rf_zmq_rx_t* q) +{ + if (!q) { + return false; + } + + bool ret = false; + pthread_mutex_lock(&q->mutex); + ret = q->running; + pthread_mutex_unlock(&q->mutex); + + return ret; +} diff --git a/lib/src/phy/rf/rf_zmq_imp_trx.h b/lib/src/phy/rf/rf_zmq_imp_trx.h index 8acc2c7d6..f05420e3e 100644 --- a/lib/src/phy/rf/rf_zmq_imp_trx.h +++ b/lib/src/phy/rf/rf_zmq_imp_trx.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -106,12 +106,16 @@ SRSRAN_API int rf_zmq_tx_align(rf_zmq_tx_t* q, uint64_t ts); SRSRAN_API int rf_zmq_tx_baseband(rf_zmq_tx_t* q, cf_t* buffer, uint32_t nsamples); +SRSRAN_API int rf_zmq_tx_get_nsamples(rf_zmq_tx_t* q); + SRSRAN_API int rf_zmq_tx_zeros(rf_zmq_tx_t* q, uint32_t nsamples); SRSRAN_API bool rf_zmq_tx_match_freq(rf_zmq_tx_t* q, uint32_t freq_hz); SRSRAN_API void rf_zmq_tx_close(rf_zmq_tx_t* q); +SRSRAN_API bool rf_zmq_tx_is_running(rf_zmq_tx_t* q); + /* * Receiver functions */ @@ -123,4 +127,6 @@ SRSRAN_API bool rf_zmq_rx_match_freq(rf_zmq_rx_t* q, uint32_t freq_hz); SRSRAN_API void rf_zmq_rx_close(rf_zmq_rx_t* q); +SRSRAN_API bool rf_zmq_rx_is_running(rf_zmq_rx_t* q); + #endif // SRSRAN_RF_ZMQ_IMP_TRX_H diff --git a/lib/src/phy/rf/rf_zmq_imp_tx.c b/lib/src/phy/rf/rf_zmq_imp_tx.c index d638e1a84..80176565c 100644 --- a/lib/src/phy/rf/rf_zmq_imp_tx.c +++ b/lib/src/phy/rf/rf_zmq_imp_tx.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,7 +66,7 @@ int rf_zmq_tx_open(rf_zmq_tx_t* q, rf_zmq_opts_t opts, void* zmq_ctx, char* sock } if (zmq_setsockopt(q->sock, ZMQ_SNDTIMEO, &timeout, sizeof(timeout)) == -1) { - fprintf(stderr, "Error: setting receive timeout on tx socket\n"); + fprintf(stderr, "Error: setting send timeout on tx socket\n"); goto clean_exit; } @@ -209,6 +209,14 @@ int rf_zmq_tx_baseband(rf_zmq_tx_t* q, cf_t* buffer, uint32_t nsamples) return n; } +int rf_zmq_tx_get_nsamples(rf_zmq_tx_t* q) +{ + pthread_mutex_lock(&q->mutex); + int ret = q->nsamples; + pthread_mutex_unlock(&q->mutex); + return ret; +} + int rf_zmq_tx_zeros(rf_zmq_tx_t* q, uint32_t nsamples) { pthread_mutex_lock(&q->mutex); @@ -232,7 +240,11 @@ bool rf_zmq_tx_match_freq(rf_zmq_tx_t* q, uint32_t freq_hz) void rf_zmq_tx_close(rf_zmq_tx_t* q) { + pthread_mutex_lock(&q->mutex); q->running = false; + pthread_mutex_unlock(&q->mutex); + + pthread_mutex_destroy(&q->mutex); if (q->zeros) { free(q->zeros); @@ -247,3 +259,17 @@ void rf_zmq_tx_close(rf_zmq_tx_t* q) q->sock = NULL; } } + +bool rf_zmq_tx_is_running(rf_zmq_tx_t* q) +{ + if (!q) { + return false; + } + + bool ret = false; + pthread_mutex_lock(&q->mutex); + ret = q->running; + pthread_mutex_unlock(&q->mutex); + + return ret; +} diff --git a/lib/src/phy/rf/rf_zmq_test.c b/lib/src/phy/rf/rf_zmq_test.c index 70820845f..2fc04f648 100644 --- a/lib/src/phy/rf/rf_zmq_test.c +++ b/lib/src/phy/rf/rf_zmq_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,21 +20,28 @@ */ #include "rf_zmq_imp.h" -#include "srsran/srsran.h" +#include "srsran/common/tsan_options.h" +#include "srsran/phy/common/timestamp.h" +#include "srsran/phy/utils/debug.h" +#include #include #include +#include #include #include -#define NOF_RX_ANT 1 +#define PRINT_SAMPLES 1 +#define COMPARE_BITS 0 +#define COMPARE_EPSILON (1e-6f) +#define NOF_RX_ANT 4 #define NUM_SF (500) #define SF_LEN (1920) #define RF_BUFFER_SIZE (SF_LEN * NUM_SF) #define TX_OFFSET_MS (4) -static cf_t ue_rx_buffer[RF_BUFFER_SIZE]; -static cf_t enb_tx_buffer[RF_BUFFER_SIZE]; -static cf_t enb_rx_buffer[RF_BUFFER_SIZE]; +static cf_t ue_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; +static cf_t enb_tx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; +static cf_t enb_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; static srsran_rf_t ue_radio, enb_radio; pthread_t rx_thread; @@ -59,13 +66,15 @@ void* ue_rx_thread_function(void* args) uint32_t num_rxed_samps = 0; for (uint32_t i = 0; i < num_slots; ++i) { void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; - data_ptr[0] = &ue_rx_buffer[i * num_samps_per_slot]; + for (uint32_t c = 0; c < NOF_RX_ANT; c++) { + data_ptr[c] = &ue_rx_buffer[c][i * num_samps_per_slot]; + } num_rxed_samps += srsran_rf_recv_with_time_multi(&ue_radio, data_ptr, num_samps_per_slot, true, NULL, NULL); } printf("received %d samples.\n", num_rxed_samps); - printf("closing ue norf device\n"); + printf("closing ue zmq device\n"); srsran_rf_close(&ue_radio); return NULL; @@ -84,8 +93,10 @@ void enb_tx_function(const char* tx_args, bool timed_tx) } // generate random tx data - for (int i = 0; i < RF_BUFFER_SIZE; i++) { - enb_tx_buffer[i] = ((float)rand() / (float)RAND_MAX) + _Complex_I * ((float)rand() / (float)RAND_MAX); + for (int c = 0; c < NOF_RX_ANT; c++) { + for (int i = 0; i < RF_BUFFER_SIZE; i++) { + enb_tx_buffer[c][i] = ((float)rand() / (float)RAND_MAX) + _Complex_I * ((float)rand() / (float)RAND_MAX); + } } // send data subframe per subframe @@ -93,8 +104,12 @@ void enb_tx_function(const char* tx_args, bool timed_tx) // initial transmission without ts void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; - data_ptr[0] = &enb_tx_buffer[num_txed_samples]; - int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false); + cf_t tx_buffer[NOF_RX_ANT][SF_LEN]; + for (int c = 0; c < NOF_RX_ANT; c++) { + memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof(cf_t)); + data_ptr[c] = &tx_buffer[c][0]; + } + int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false); num_txed_samples += SF_LEN; // from here on, all transmissions are timed relative to the last rx time @@ -102,11 +117,16 @@ void enb_tx_function(const char* tx_args, bool timed_tx) for (uint32_t i = 0; i < NUM_SF - ((timed_tx) ? TX_OFFSET_MS : 1); ++i) { // first recv samples - data_ptr[0] = enb_rx_buffer; + for (int c = 0; c < NOF_RX_ANT; c++) { + data_ptr[c] = enb_rx_buffer[c]; + } srsran_rf_recv_with_time_multi(&enb_radio, data_ptr, SF_LEN, true, &rx_time.full_secs, &rx_time.frac_secs); // prepare data buffer - data_ptr[0] = &enb_tx_buffer[num_txed_samples]; + for (int c = 0; c < NOF_RX_ANT; c++) { + memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof(cf_t)); + data_ptr[c] = &tx_buffer[c][0]; + } if (timed_tx) { // timed tx relative to receive time (this will cause a cap in the rx'ed samples at the UE resulting in 3 zero @@ -154,25 +174,46 @@ int run_test(const char* rx_args, const char* tx_args, bool timed_tx) // wait for rx thread pthread_join(rx_thread, NULL); - // subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx) - for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) { - uint32_t sf_offet = 0; - if (timed_tx && i >= 1) { - // for timed transmission, the enb inserts 3 zero subframes after the first untimed tx - sf_offet = (TX_OFFSET_MS - 1) * SF_LEN; - } + // channel-wise comparison + for (int c = 0; c < NOF_RX_ANT; c++) { + // subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx) + for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) { + uint32_t sf_offet = 0; + if (timed_tx && i >= 1) { + // for timed transmission, the enb inserts 3 zero subframes after the first untimed tx + sf_offet = (TX_OFFSET_MS - 1) * SF_LEN; + } -#if 0 - // print first 3 samples for each SF - printf("enb_tx_buffer sf%d:\n", i); - srsran_vec_fprint_c(stdout, &enb_tx_buffer[i * SF_LEN], 3); - printf("ue_rx_buffer sf%d:\n", i); - srsran_vec_fprint_c(stdout, &ue_rx_buffer[sf_offet + i * SF_LEN], 3); +#if PRINT_SAMPLES + // print first 10 samples for each SF + printf("enb_tx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &enb_tx_buffer[c][i * SF_LEN], 10); + printf("ue_rx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &ue_rx_buffer[c][sf_offet + i * SF_LEN], 10); #endif - if (memcmp(&ue_rx_buffer[sf_offet + i * SF_LEN], &enb_tx_buffer[i * SF_LEN], SF_LEN) != 0) { - fprintf(stderr, "data mismatch in subframe %d\n", i); - goto exit; +#if COMPARE_BITS + int d = memcmp(&ue_rx_buffer[sf_offet + i * SF_LEN], &enb_tx_buffer[i * SF_LEN], SF_LEN); + if (d) { + d = d > 0 ? d : -d; + fprintf(stderr, "data mismatch in subframe %d, sample %d\n", i, d); + printf("enb_tx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &enb_tx_buffer[i * SF_LEN + d], 10); + printf("ue_rx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &ue_rx_buffer[sf_offet + i * SF_LEN + d], 10); + goto exit; + } +#else + srsran_vec_sub_ccc(&ue_rx_buffer[c][sf_offet + i * SF_LEN], + &enb_tx_buffer[c][i * SF_LEN], + &ue_rx_buffer[c][sf_offet + i * SF_LEN], + SF_LEN); + uint32_t max_ix = srsran_vec_max_abs_ci(&ue_rx_buffer[c][sf_offet + i * SF_LEN], SF_LEN); + if (cabsf(ue_rx_buffer[c][sf_offet + i * SF_LEN + max_ix]) > COMPARE_EPSILON) { + fprintf(stderr, "data mismatch in subframe %d\n", i); + goto exit; + } +#endif } } @@ -201,56 +242,76 @@ int param_test(const char* args_param, const int num_channels) int main() { - // two Rx ports - if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { - fprintf(stderr, "Param test failed!\n"); - return SRSRAN_ERROR; - } + // // two Rx ports + // if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { + // fprintf(stderr, "Param test failed!\n"); + // return SRSRAN_ERROR; + // } - // multiple rx ports, no channel index provided - if (param_test("rx_port=ipc://dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,base_srate=1.92e6", 4)) { - fprintf(stderr, "Param test failed!\n"); - return SRSRAN_ERROR; - } + // // multiple rx ports, no channel index provided + // if (param_test("rx_port=ipc://dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,base_srate=1.92e6", 4)) { + // fprintf(stderr, "Param test failed!\n"); + // return SRSRAN_ERROR; + // } - // One Rx, one Tx and all generic options - if (param_test("rx_port0=tcp://" - "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test", - 1)) { - fprintf(stderr, "Param test failed!\n"); - return SRSRAN_ERROR; - } + // // One Rx, one Tx and all generic options + // if (param_test("rx_port0=tcp://" + // "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test", + // 1)) { + // fprintf(stderr, "Param test failed!\n"); + // return SRSRAN_ERROR; + // } - // 1 port, 2 antennas, MIMO freq config - if (param_test( - "tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://" - "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6", - 2)) { - fprintf(stderr, "Param test failed!\n"); - return SRSRAN_ERROR; - } + // // 1 port, 2 antennas, MIMO freq config + // if (param_test( + // "tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://" + // "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6", + // 2)) { + // fprintf(stderr, "Param test failed!\n"); + // return SRSRAN_ERROR; + // } +#if NOF_RX_ANT == 1 // single tx, single rx with continuous transmissions (no timed tx) using IPC transport if (run_test("rx_port=ipc://link1,id=ue,base_srate=1.92e6", "tx_port=ipc://link1,id=enb,base_srate=1.92e6", false) != SRSRAN_SUCCESS) { fprintf(stderr, "Single tx, single rx test failed!\n"); return -1; } +#endif - // two trx radios with continous tx (no timed tx) using TCP transport for both directions - if (run_test("tx_port=tcp://*:5554,rx_port=tcp://" - "localhost:5555,id=ue,base_srate=1.92e6,log_trx_timeout=true,trx_timeout_ms=1000", - "rx_port=tcp://localhost:5554,tx_port=tcp://*:5555,id=enb,base_srate=1.92e6", + // up to 4 trx radios with continous tx (no decimation, no timed tx) + if (run_test("tx_port=tcp://*:5554,tx_port=tcp://*:5556,tx_port=tcp://*:5558,tx_port=tcp://*:5560,rx_port=tcp://" + "localhost:5555,rx_port=tcp://localhost:5557,rx_port=tcp://localhost:5559,rx_port=tcp://" + "localhost:5561,id=ue,base_srate=1.92e6,log_trx_timeout=true,trx_timeout_ms=1000", + "rx_port=tcp://localhost:5554,rx_port=tcp://localhost:5556,rx_port=tcp://localhost:5558,rx_port=tcp://" + "localhost:5560,tx_port=tcp://*:5555,tx_port=tcp://*:5557,tx_port=tcp://*:5559,tx_port=tcp://" + "*:5561,id=enb,base_srate=1.92e6", false) != SRSRAN_SUCCESS) { - fprintf(stderr, "Two TRx radio test failed!\n"); + fprintf(stderr, "Multi TRx radio test failed!\n"); return -1; } - // two trx radios with continous tx (no timed tx) using TCP for UL (UE tx) and IPC for eNB DL (eNB tx) - if (run_test("tx_port=tcp://*:5554,rx_port=ipc://dl,id=ue,base_srate=1.92e6", - "rx_port=tcp://localhost:5554,tx_port=ipc://dl,id=enb,base_srate=1.92e6", + // up to 4 trx radios with continous tx (timed tx) using TCP for UL (UE tx) and IPC for eNB DL (eNB tx) + if (run_test("tx_port=tcp://*:5554,tx_port=tcp://*:5556,tx_port=tcp://*:5558,tx_port=tcp://*:5560,rx_port=ipc://" + "dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,id=ue,base_srate=1.92e6", + "rx_port=tcp://localhost:5554,rx_port=tcp://localhost:5556,rx_port=tcp://localhost:5558,rx_port=tcp://" + "localhost:5560,tx_port=ipc://dl0,tx_port=ipc://dl1,tx_port=ipc://dl2,tx_port=ipc://" + "dl3,id=enb,base_srate=1.92e6", true) != SRSRAN_SUCCESS) { - fprintf(stderr, "Two TRx radio test with timed tx failed!\n"); + fprintf(stderr, "Multi TRx radio test with timed tx failed!\n"); + return -1; + } + + // up to 4 trx radios with continous tx (timed tx) using TCP for UL (UE tx) and IPC for eNB DL (eNB tx) + // with decimation 23.04e6 <-> 1.92e6 + if (run_test("tx_port=tcp://*:5554,tx_port=tcp://*:5556,tx_port=tcp://*:5558,tx_port=tcp://*:5560,rx_port=ipc://" + "dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,id=ue,base_srate=23.04e6", + "rx_port=tcp://localhost:5554,rx_port=tcp://localhost:5556,rx_port=tcp://localhost:5558,rx_port=tcp://" + "localhost:5560,tx_port=ipc://dl0,tx_port=ipc://dl1,tx_port=ipc://dl2,tx_port=ipc://" + "dl3,id=enb,base_srate=23.04e6", + true) != SRSRAN_SUCCESS) { + fprintf(stderr, "Multi TRx radio test with timed tx and decimation failed!\n"); return -1; } diff --git a/lib/src/phy/rf/rfnoc_test.cc b/lib/src/phy/rf/rfnoc_test.cc index 1b3a3a59d..1854f1fd7 100644 --- a/lib/src/phy/rf/rfnoc_test.cc +++ b/lib/src/phy/rf/rfnoc_test.cc @@ -88,7 +88,6 @@ private: if (valid) { switch (async_metadata.event_code) { - case uhd::async_metadata_t::EVENT_CODE_BURST_ACK: Warning("BURST ACK") break; case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW: @@ -223,16 +222,13 @@ public: uhd::log::set_console_level(uhd::log::severity_level::trace); if (rfnoc->usrp_make(hint, config.nof_channels * config.nof_radios) != UHD_ERROR_NONE) { - Warning(rfnoc->last_error); return SRSRAN_ERROR; } if (rfnoc->set_tx_rate(config.srate_hz) != UHD_ERROR_NONE) { - Warning(rfnoc->last_error); return SRSRAN_ERROR; } if (rfnoc->set_rx_rate(config.srate_hz) != UHD_ERROR_NONE) { - Warning(rfnoc->last_error); return SRSRAN_ERROR; } @@ -248,7 +244,6 @@ public: } if (rfnoc->start_rx_stream(config.init_tx_time) != UHD_ERROR_NONE) { - Warning(rfnoc->last_error); return SRSRAN_ERROR; } diff --git a/lib/src/phy/rf/skiq_pps_test.c b/lib/src/phy/rf/skiq_pps_test.c new file mode 100644 index 000000000..6782f758d --- /dev/null +++ b/lib/src/phy/rf/skiq_pps_test.c @@ -0,0 +1,142 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include + +#define ERROR(...) fprintf(stderr, __VA_ARGS__); + +const static uint8_t nof_cards = 1; + +int main(int argc, char* argv[]) +{ + int ret = -1; + uint8_t card = 0; + + if (argc > 1) { + card = (uint8_t)strtol(argv[1], NULL, 10); + } + + uint8_t nof_available_cards = 0; + uint8_t available_cards[SKIQ_MAX_NUM_CARDS] = {}; + if (skiq_get_cards(skiq_xport_type_auto, &nof_available_cards, available_cards)) { + ERROR("Getting available cards\n"); + goto clean_exit; + } + + // Check number of cards bounds + if (nof_cards > nof_available_cards) { + ERROR("The number of cards (%d) exceeds available cards (%d)\n", nof_cards, nof_available_cards); + goto clean_exit; + } + + // Initialise driver + if (skiq_init(skiq_xport_type_auto, skiq_xport_init_level_full, &card, 1)) { + ERROR("Unable to initialise libsidekiq driver\n"); + goto clean_exit; + } + + // Programming FPGA from flash ensures the FPGA and transceiver are completely reseted + if (skiq_prog_fpga_from_flash(card)) { + ERROR("Error programming FPGA from flash\n"); + goto clean_exit; + } + + // Read 1pps source + skiq_1pps_source_t pps_source = skiq_1pps_source_unavailable; + if (skiq_read_1pps_source(card, &pps_source)) { + ERROR("Error reading card %d 1PPS source\n", card); + goto clean_exit; + } + + // Make sure the source is external + if (pps_source != skiq_1pps_source_external) { + ERROR("Error card %d is not configured with external 1PPS source\n", card); + goto clean_exit; + } + + // Sleeping 5 seconds + sleep(5); + + // Get last time a PPS was received + uint64_t ts_sys_1pps = 0; + if (skiq_read_last_1pps_timestamp(card, NULL, &ts_sys_1pps)) { + ERROR("Reading card %d last 1PPS timestamp", card); + goto clean_exit; + } + + // Read current system time + uint64_t ts = 0; + if (skiq_read_curr_sys_timestamp(card, &ts)) { + ERROR("Reading card %d system timestamp", card); + goto clean_exit; + } + + // Make sure a 1PPS was received less than 2 seconds ago + if (ts - ts_sys_1pps > 2 * SKIQ_SYS_TIMESTAMP_FREQ) { + ERROR("Error card %d last PPS was received %.1f seconds ago (ts=%ld)\n", + card, + (double)(ts - ts_sys_1pps) / (double)SKIQ_SYS_TIMESTAMP_FREQ, + ts_sys_1pps); + goto clean_exit; + } + + // Set a given time in the future, a 100th of a second (10ms) + ts += SKIQ_SYS_TIMESTAMP_FREQ / 100; + + // Reset timestamp in a near future on next PPS signal + if (skiq_write_timestamp_reset_on_1pps(card, ts)) { + ERROR("Error reseting card %d timestamp on 1 PPS", card); + goto clean_exit; + } + + // Give time to pass 1PPS + sleep(1); + + // Read current system time + if (skiq_read_curr_sys_timestamp(card, &ts)) { + ERROR("Reading card %d system timestamp", card); + goto clean_exit; + } + + // The current system timestamp should be below 2s + if (ts > 2 * SKIQ_SYS_TIMESTAMP_FREQ) { + ERROR("Timestamp of card %d is greater than 2 seconds (%.1fs)!\n", + card, + (double)ts / (double)SKIQ_SYS_TIMESTAMP_FREQ); + goto clean_exit; + } + + // Success + printf("Success!\n"); + ret = 0; + +clean_exit: + if (skiq_disable_cards(&card, 1)) { + ERROR("Unable to disable cards\n"); + } + + // Close sidekiq SDK + skiq_exit(); + + return ret; +} diff --git a/lib/src/phy/scrambling/CMakeLists.txt b/lib/src/phy/scrambling/CMakeLists.txt index f97363a9c..e29b8a1d5 100644 --- a/lib/src/phy/scrambling/CMakeLists.txt +++ b/lib/src/phy/scrambling/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/scrambling/scrambling.c b/lib/src/phy/scrambling/scrambling.c index 7a008b66f..5845ac74d 100644 --- a/lib/src/phy/scrambling/scrambling.c +++ b/lib/src/phy/scrambling/scrambling.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/scrambling/test/CMakeLists.txt b/lib/src/phy/scrambling/test/CMakeLists.txt index a36366944..393201f4d 100644 --- a/lib/src/phy/scrambling/test/CMakeLists.txt +++ b/lib/src/phy/scrambling/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/scrambling/test/scrambling_test.c b/lib/src/phy/scrambling/test/scrambling_test.c index cf8111beb..ed5a71a1f 100644 --- a/lib/src/phy/scrambling/test/scrambling_test.c +++ b/lib/src/phy/scrambling/test/scrambling_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/CMakeLists.txt b/lib/src/phy/sync/CMakeLists.txt index 2a1d54ca8..ede552621 100644 --- a/lib/src/phy/sync/CMakeLists.txt +++ b/lib/src/phy/sync/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/sync/cfo.c b/lib/src/phy/sync/cfo.c index bbfec37da..e54f2336c 100644 --- a/lib/src/phy/sync/cfo.c +++ b/lib/src/phy/sync/cfo.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/cp.c b/lib/src/phy/sync/cp.c index 0b0928e84..9f1eaab00 100644 --- a/lib/src/phy/sync/cp.c +++ b/lib/src/phy/sync/cp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/find_sss.c b/lib/src/phy/sync/find_sss.c index 272500ed1..7150c52eb 100644 --- a/lib/src/phy/sync/find_sss.c +++ b/lib/src/phy/sync/find_sss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/gen_sss.c b/lib/src/phy/sync/gen_sss.c index 36922507e..d08cfa730 100644 --- a/lib/src/phy/sync/gen_sss.c +++ b/lib/src/phy/sync/gen_sss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/npss.c b/lib/src/phy/sync/npss.c index 8fbfc3b1c..868022c58 100644 --- a/lib/src/phy/sync/npss.c +++ b/lib/src/phy/sync/npss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/nsss.c b/lib/src/phy/sync/nsss.c index eb2d98f52..914a223e9 100644 --- a/lib/src/phy/sync/nsss.c +++ b/lib/src/phy/sync/nsss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/pss.c b/lib/src/phy/sync/pss.c index 368379c5c..179db70a7 100644 --- a/lib/src/phy/sync/pss.c +++ b/lib/src/phy/sync/pss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/pss_nr.c b/lib/src/phy/sync/pss_nr.c new file mode 100644 index 000000000..99a80321b --- /dev/null +++ b/lib/src/phy/sync/pss_nr.c @@ -0,0 +1,154 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/sync/pss_nr.h" +#include "srsran/phy/utils/vector.h" + +/** + * NR PSS First Subcarrier index + */ +#define PSS_NR_SUBC_BEGIN 56 + +/** + * Calculates Sequence circular offset + */ +#define PSS_NR_SEQUENCE_M(N_id_2) ((43U * (N_id_2)) % SRSRAN_PSS_NR_LEN) + +/** + * Pregenerated modulated sequence + */ +static cf_t pss_nr_d[SRSRAN_PSS_NR_LEN] = {}; + +/** + * Sequence generation as described in TS 38.211 clause 7.4.2.2.1 + */ +__attribute__((constructor)) __attribute__((unused)) static void pss_nr_pregen() +{ + // Initialise M sequence x + uint32_t x[SRSRAN_PSS_NR_LEN + 7]; + x[6] = 1; + x[5] = 1; + x[4] = 1; + x[3] = 0; + x[2] = 1; + x[1] = 1; + x[0] = 0; + + // Generate M sequence x + for (uint32_t i = 0; i < SRSRAN_PSS_NR_LEN; i++) { + x[i + 7] = (x[i + 4] + x[i]) % 2; + } + + // Modulate M sequence d + for (uint32_t i = 0; i < SRSRAN_PSS_NR_LEN; i++) { + pss_nr_d[i] = 1.0f - 2.0f * (float)x[i]; + } +} + +int srsran_pss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_2, float beta) +{ + // Verify inputs + if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR) { + return SRSRAN_ERROR; + } + + // Calculate generation parameters + uint32_t m = PSS_NR_SEQUENCE_M(N_id_2); + uint32_t copy_sz_1 = SRSRAN_PSS_NR_LEN - m; + uint32_t grid_idx_1 = SRSRAN_PSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + PSS_NR_SUBC_BEGIN; + uint32_t grid_idx_2 = grid_idx_1 + copy_sz_1; + + // Copy sequence from offset to the end + srsran_vec_sc_prod_cfc(&pss_nr_d[m], beta, &ssb_grid[grid_idx_1], copy_sz_1); + + // Copy sequence from 0 to offset + srsran_vec_sc_prod_cfc(&pss_nr_d[0], beta, &ssb_grid[grid_idx_2], m); + + return SRSRAN_SUCCESS; +} + +int srsran_pss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_2, cf_t lse[SRSRAN_PSS_NR_LEN]) +{ + // Verify inputs + if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR || lse == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Extract PSS + srsran_vec_cf_copy( + lse, &ssb_grid[SRSRAN_PSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + PSS_NR_SUBC_BEGIN], SRSRAN_PSS_NR_LEN); + + // Estimate + uint32_t m = PSS_NR_SEQUENCE_M(N_id_2); + srsran_vec_prod_ccc(&pss_nr_d[m], lse, lse, SRSRAN_PSS_NR_LEN - m); + srsran_vec_prod_ccc(&pss_nr_d[0], &lse[SRSRAN_PSS_NR_LEN - m], &lse[SRSRAN_PSS_NR_LEN - m], m); + + return SRSRAN_SUCCESS; +} + +int srsran_pss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], float* norm_corr, uint32_t* found_N_id_2) +{ + // Verify inputs + if (ssb_grid == NULL || norm_corr == NULL || found_N_id_2 == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + const cf_t* pss_ptr = &ssb_grid[SRSRAN_PSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + PSS_NR_SUBC_BEGIN]; + + // Measure PSS region average power + float avg_power = srsran_vec_avg_power_cf(pss_ptr, SRSRAN_PSS_NR_LEN); + + // If no energy detected or invalid, consider zero correlation + if (!isnormal(avg_power)) { + *norm_corr = 0.0f; + *found_N_id_2 = 0; + return SRSRAN_SUCCESS; + } + + // Search state + float max_corr = -INFINITY; //< Stores best correlation + uint32_t N_id_2 = 0; //< Best N_id_2 + + // Iterate over all possible N_id_2 + for (uint32_t N_id_2_candidate = 0; N_id_2_candidate < SRSRAN_NOF_NID_2_NR; N_id_2_candidate++) { + uint32_t m = PSS_NR_SEQUENCE_M(N_id_2_candidate); + cf_t acc = 0.0f; + + // Correlate d sequence fist part + acc += srsran_vec_dot_prod_ccc(&pss_nr_d[m], &pss_ptr[0], SRSRAN_PSS_NR_LEN - m); + + // Correlate d sequence second part + acc += srsran_vec_dot_prod_ccc(&pss_nr_d[0], &pss_ptr[SRSRAN_PSS_NR_LEN - m], m); + + // Correlate + float corr = SRSRAN_CSQABS(acc); + if (corr > max_corr) { + N_id_2 = N_id_2_candidate; + max_corr = corr; + } + } + + // Copy found result + *norm_corr = max_corr / avg_power / SRSRAN_PSS_NR_LEN; + *found_N_id_2 = N_id_2; + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/sync/psss.c b/lib/src/phy/sync/psss.c index c6cc0462b..4c52296c9 100644 --- a/lib/src/phy/sync/psss.c +++ b/lib/src/phy/sync/psss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/refsignal_dl_sync.c b/lib/src/phy/sync/refsignal_dl_sync.c index 308817530..9112a6d2e 100644 --- a/lib/src/phy/sync/refsignal_dl_sync.c +++ b/lib/src/phy/sync/refsignal_dl_sync.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -77,8 +77,14 @@ static inline void refsignal_sf_prepare_correlation(srsran_refsignal_dl_sync_t* { uint32_t sf_len = q->ifft.sf_sz; cf_t* ptr_filt = q->conv_fft_cc.filter_fft; - memcpy(ptr_filt, q->sequences[0], sizeof(cf_t) * sf_len); - srsran_vec_cf_zero(&ptr_filt[sf_len], sf_len); + + // Put first subframe in buffer + srsran_vec_cf_copy(ptr_filt, q->sequences[0], sf_len); + + // Zero the rest of the buffer + srsran_vec_cf_zero(&ptr_filt[sf_len], q->conv_fft_cc.output_len - sf_len); + + // Make correlation sequence in frequency domain srsran_dft_run_c(&q->conv_fft_cc.filter_plan, ptr_filt, ptr_filt); } @@ -142,7 +148,7 @@ static inline void refsignal_dl_pss_sss_strength(srsran_refsignal_dl_sync_t* q, } } -int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q) +int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q, srsran_cp_t cp) { int ret = SRSRAN_ERROR_INVALID_INPUTS; @@ -180,12 +186,12 @@ int srsran_refsignal_dl_sync_init(srsran_refsignal_dl_sync_t* q) // Initiate OFDM modulator if (!ret) { - ret = srsran_ofdm_tx_init(&q->ifft, SRSRAN_CP_NORM, q->ifft_buffer_in, q->ifft_buffer_out, SRSRAN_MAX_PRB); + ret = srsran_ofdm_tx_init(&q->ifft, cp, q->ifft_buffer_in, q->ifft_buffer_out, SRSRAN_MAX_PRB); } // Set PRB if (!ret) { - ret = srsran_ofdm_tx_set_prb(&q->ifft, SRSRAN_CP_NORM, SRSRAN_MAX_PRB); + ret = srsran_ofdm_tx_set_prb(&q->ifft, cp, SRSRAN_MAX_PRB); } // Initiate FFT Convolution @@ -258,7 +264,12 @@ int srsran_refsignal_dl_sync_set_cell(srsran_refsignal_dl_sync_t* q, srsran_cell srsran_ofdm_tx_sf(&q->ifft); // Undo scaling and normalize overall power to 1 - float scale = 1.0f / nof_re; + float scale = 1.0f; + + // Avoid zero division + if (nof_re != 0) { + scale /= (float)nof_re; + } // Copy time domain signal, normalized by number of RE srsran_vec_sc_prod_cfc(q->ifft_buffer_out, scale, q->sequences[i], q->ifft.sf_sz); @@ -298,22 +309,23 @@ void srsran_refsignal_dl_sync_free(srsran_refsignal_dl_sync_t* q) } } -int srsran_refsignal_dl_sync_find_peak(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples) +int refsignal_dl_sync_find_peak(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples) { - int ret = SRSRAN_ERROR; - float peak_value = 0.0f; - int peak_idx = 0; - float rms_avg = 0; - uint32_t sf_len = q->ifft.sf_sz; + int ret = SRSRAN_ERROR; + float peak_value = 0.0f; + int peak_idx = 0; + float rms_avg = 0; + + uint32_t sf_len = q->ifft.sf_sz; + if (sf_len == 0) { + return SRSRAN_ERROR; + } // Load correlation sequence and convert to frequency domain refsignal_sf_prepare_correlation(q); - // Limit correlate for a frame or less - nsamples = SRSRAN_MIN(nsamples - sf_len, SRSRAN_NOF_SF_X_FRAME * sf_len); - // Correlation - for (int n = 0; n < nsamples; n += sf_len) { + for (uint32_t n = 0; n + q->conv_fft_cc.filter_len < nsamples; n += q->conv_fft_cc.input_len) { // Correlate, find maximum, calculate RMS and peak uint32_t imax = 0; float peak = 0.0f; @@ -336,7 +348,7 @@ int srsran_refsignal_dl_sync_find_peak(srsran_refsignal_dl_sync_t* q, cf_t* buff } // Double check sub-frame selection failure due to high PSS - if (ret > 0) { + if (ret >= 0) { float sss_strength = 0.0f; float sss_strength_false = 0.0f; refsignal_dl_pss_sss_strength(q, &buffer[peak_idx], 0, NULL, &sss_strength, &sss_strength_false); @@ -364,151 +376,154 @@ int srsran_refsignal_dl_sync_find_peak(srsran_refsignal_dl_sync_t* q, cf_t* buff return ret; } -void srsran_refsignal_dl_sync_run(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples) +int srsran_refsignal_dl_sync_run(srsran_refsignal_dl_sync_t* q, cf_t* buffer, uint32_t nsamples) { - if (q) { - uint32_t sf_len = q->ifft.sf_sz; - uint32_t sf_count = 0; - float rsrp_lin = 0.0f; - float rsrp_lin_min = +INFINITY; - float rsrp_lin_max = -INFINITY; - float rssi_lin = 0.0f; - float cfo_acc = 0.0f; - float cfo_min = +INFINITY; - float cfo_max = -INFINITY; - float sss_strength_avg = 0.0f; - float sss_strength_false_avg = 0.0f; - float rsrp_false_avg = 0.0f; - bool false_alarm = false; + if (q == NULL || buffer == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + uint32_t sf_len = q->ifft.sf_sz; + uint32_t sf_count = 0; + float rsrp_lin = 0.0f; + float rsrp_lin_min = +INFINITY; + float rsrp_lin_max = -INFINITY; + float rssi_lin = 0.0f; + float cfo_acc = 0.0f; + float cfo_min = +INFINITY; + float cfo_max = -INFINITY; + float sss_strength_avg = 0.0f; + float sss_strength_false_avg = 0.0f; + float rsrp_false_avg = 0.0f; + bool false_alarm = false; - // Stage 1: find peak - int peak_idx = srsran_refsignal_dl_sync_find_peak(q, buffer, nsamples); + // Stage 1: find peak + int peak_idx = refsignal_dl_sync_find_peak(q, buffer, nsamples); - // Stage 2: Proccess subframes - if (peak_idx >= 0) { - // Calculate initial subframe index and sample - uint32_t sf_idx_init = (2 * SRSRAN_NOF_SF_X_FRAME - peak_idx / sf_len) % SRSRAN_NOF_SF_X_FRAME; - uint32_t n_init = peak_idx % sf_len; + // Stage 2: Proccess subframes + if (peak_idx >= 0) { + // Calculate initial subframe index and sample + uint32_t sf_idx_init = SRSRAN_NOF_SF_X_FRAME - (peak_idx / sf_len) % SRSRAN_NOF_SF_X_FRAME; + uint32_t n_init = peak_idx % sf_len; - for (int sf_idx = sf_idx_init, n = n_init; n < (nsamples - sf_len + 1); - sf_idx = (sf_idx + 1) % SRSRAN_NOF_SF_X_FRAME, n += sf_len) { - cf_t* buf = &buffer[n]; + for (uint32_t sf_idx = sf_idx_init, n = n_init; n < (nsamples - sf_len + 1); + sf_idx = (sf_idx + 1) % SRSRAN_NOF_SF_X_FRAME, n += sf_len) { + cf_t* buf = &buffer[n]; - // Measure subframe rsrp, rssi and accumulate - float rsrp = 0.0f, rssi = 0.0f, cfo = 0.0f; - srsran_refsignal_dl_sync_measure_sf(q, buf, sf_idx, &rsrp, &rssi, &cfo); + // Measure subframe rsrp, rssi and accumulate + float rsrp = 0.0f, rssi = 0.0f, cfo = 0.0f; + srsran_refsignal_dl_sync_measure_sf(q, buf, sf_idx, &rsrp, &rssi, &cfo); - // Update measurements - rsrp_lin += rsrp; - rsrp_lin_min = SRSRAN_MIN(rsrp_lin_min, rsrp); - rsrp_lin_max = SRSRAN_MAX(rsrp_lin_max, rsrp); + // Update measurements + rsrp_lin += rsrp; + rsrp_lin_min = SRSRAN_MIN(rsrp_lin_min, rsrp); + rsrp_lin_max = SRSRAN_MAX(rsrp_lin_max, rsrp); - rssi_lin += rssi; + rssi_lin += rssi; - cfo_acc += cfo; - cfo_min = SRSRAN_MIN(cfo_min, cfo); - cfo_max = SRSRAN_MAX(cfo_max, cfo); + cfo_acc += cfo; + cfo_min = SRSRAN_MIN(cfo_min, cfo); + cfo_max = SRSRAN_MAX(cfo_max, cfo); - // Compute PSS/SSS strength - if (sf_idx % (SRSRAN_NOF_SF_X_FRAME / 2) == 0) { - float sss_strength = 0.0f; - float sss_strength_false = 0.0f; - refsignal_dl_pss_sss_strength(q, buf, sf_idx, NULL, &sss_strength, &sss_strength_false); + // Compute PSS/SSS strength + if (sf_idx % (SRSRAN_NOF_SF_X_FRAME / 2) == 0) { + float sss_strength = 0.0f; + float sss_strength_false = 0.0f; + refsignal_dl_pss_sss_strength(q, buf, sf_idx, NULL, &sss_strength, &sss_strength_false); - float rsrp_false = 0.0f; - srsran_refsignal_dl_sync_measure_sf(q, buf, sf_idx + 1, &rsrp_false, NULL, NULL); + float rsrp_false = 0.0f; + srsran_refsignal_dl_sync_measure_sf(q, buf, sf_idx + 1, &rsrp_false, NULL, NULL); - sss_strength_avg += sss_strength; - sss_strength_false_avg += sss_strength_false; - rsrp_false_avg += rsrp_false; - } - - // Increment counter - sf_count++; + sss_strength_avg += sss_strength; + sss_strength_false_avg += sss_strength_false; + rsrp_false_avg += rsrp_false; } - // Average measurements - if (sf_count) { - rsrp_lin /= sf_count; - rssi_lin /= sf_count; - cfo_acc /= sf_count; - sss_strength_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME); - sss_strength_false_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME); - rsrp_false_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME); - } + // Increment counter + sf_count++; + } - // RSRP conversion to dB - float rsrp_dB_min = srsran_convert_power_to_dBm(rsrp_lin_min); - float rsrp_dB_max = srsran_convert_power_to_dBm(rsrp_lin_max); - float rsrp_dB = srsran_convert_power_to_dBm(rsrp_lin); - float rsrp_false_dB = srsran_convert_power_to_dBm(rsrp_false_avg); + // Average measurements + if (sf_count) { + rsrp_lin /= sf_count; + rssi_lin /= sf_count; + cfo_acc /= sf_count; + sss_strength_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME); + sss_strength_false_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME); + rsrp_false_avg /= (2.0f * sf_count / SRSRAN_NOF_SF_X_FRAME); + } - // Stage 3: Final false alarm decision - uint32_t false_count = 0; - if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_SEVERE) { - false_alarm = true; - } else if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_MILD) { - false_count++; - } + // RSRP conversion to dB + float rsrp_dB_min = srsran_convert_power_to_dBm(rsrp_lin_min); + float rsrp_dB_max = srsran_convert_power_to_dBm(rsrp_lin_max); + float rsrp_dB = srsran_convert_power_to_dBm(rsrp_lin); + float rsrp_false_dB = srsran_convert_power_to_dBm(rsrp_false_avg); - if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_SEVERE) { - false_alarm = true; - } else if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_MILD) { - false_count++; - } + // Stage 3: Final false alarm decision + uint32_t false_count = 0; + if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_SEVERE) { + false_alarm = true; + } else if (sss_strength_avg < sss_strength_false_avg * REFSIGNAL_DL_SSS_FALSE_RATIO_MILD) { + false_count++; + } - if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_SEVERE) { - false_alarm = true; - } else if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_MILD) { - false_count++; - } + if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_SEVERE) { + false_alarm = true; + } else if (cfo_max - cfo_min > REFSIGNAL_DL_CFO_MIN_MAX_MILD) { + false_count++; + } - if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_SEVERE) { - false_alarm = true; - } else if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_MILD) { - false_count++; - } + if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_SEVERE) { + false_alarm = true; + } else if (rsrp_dB_max - rsrp_dB_min > REFSIGNAL_DL_RSRP_MIN_MAX_MILD) { + false_count++; + } - // Allow only one check fail - if (false_count > REFSIGNAL_DL_MAX_FAULT_CHECK) { - false_alarm = true; - } + if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_SEVERE) { + false_alarm = true; + } else if (rsrp_dB - rsrp_false_dB < REFSIGNAL_DL_RSRP_FALSE_RATIO_MILD) { + false_count++; + } - INFO("-- pci=%03d; rsrp_dB=(%+.1f|%+.1f|%+.1f); rsrp_max-min=%.1f; rsrp_false_ratio=%.1f; " - "cfo=(%.1f|%.1f|%.1f); cfo_max-min=%.1f; sss_ratio=%f; false_count=%d;", - q->refsignal.cell.id, - rsrp_dB_min, - rsrp_dB, - rsrp_dB_max, - rsrp_dB_max - rsrp_dB_min, - rsrp_dB - rsrp_false_dB, - cfo_min, - cfo_acc, - cfo_max, - cfo_max - cfo_min, - sss_strength_avg / sss_strength_false_avg, - false_count); + // Allow only one check fail + if (false_count > REFSIGNAL_DL_MAX_FAULT_CHECK) { + false_alarm = true; + } - if (!false_alarm) { - // Calculate in dBm - q->rsrp_dBfs = rsrp_dB; + INFO("-- pci=%03d; rsrp_dB=(%+.1f|%+.1f|%+.1f); rsrp_max-min=%.1f; rsrp_false_ratio=%.1f; " + "cfo=(%.1f|%.1f|%.1f); cfo_max-min=%.1f; sss_ratio=%f; false_count=%d;", + q->refsignal.cell.id, + rsrp_dB_min, + rsrp_dB, + rsrp_dB_max, + rsrp_dB_max - rsrp_dB_min, + rsrp_dB - rsrp_false_dB, + cfo_min, + cfo_acc, + cfo_max, + cfo_max - cfo_min, + sss_strength_avg / sss_strength_false_avg, + false_count); - // Calculate RSSI in dBm - q->rssi_dBfs = srsran_convert_power_to_dBm(rssi_lin); + if (!false_alarm) { + // Calculate in dBm + q->rsrp_dBfs = rsrp_dB; - // Calculate RSRQ - q->rsrq_dB = srsran_convert_power_to_dB(q->refsignal.cell.nof_prb) + q->rsrp_dBfs - q->rssi_dBfs; + // Calculate RSSI in dBm + q->rssi_dBfs = srsran_convert_power_to_dBm(rssi_lin); - q->found = true; - q->cfo_Hz = cfo_acc; - q->peak_index = peak_idx; - } else { - refsignal_set_results_not_found(q); - } + // Calculate RSRQ + q->rsrq_dB = srsran_convert_power_to_dB(q->refsignal.cell.nof_prb) + q->rsrp_dBfs - q->rssi_dBfs; + + q->found = true; + q->cfo_Hz = cfo_acc; + q->peak_index = peak_idx; } else { refsignal_set_results_not_found(q); } + } else { + refsignal_set_results_not_found(q); } + + return SRSRAN_SUCCESS; } void srsran_refsignal_dl_sync_measure_sf(srsran_refsignal_dl_sync_t* q, diff --git a/lib/src/phy/sync/sfo.c b/lib/src/phy/sync/sfo.c index de8096285..88be4d7e0 100644 --- a/lib/src/phy/sync/sfo.c +++ b/lib/src/phy/sync/sfo.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c new file mode 100644 index 000000000..066993295 --- /dev/null +++ b/lib/src/phy/sync/ssb.c @@ -0,0 +1,1615 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/sync/ssb.h" +#include "srsran/phy/ch_estimation/dmrs_pbch.h" +#include "srsran/phy/sync/pss_nr.h" +#include "srsran/phy/sync/sss_nr.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" +#include + +/* + * Maximum allowed maximum sampling rate error in Hz + */ +#define SSB_SRATE_MAX_ERROR_HZ 0.01 + +/* + * Maximum allowed maximum frequency error offset in Hz + */ +#define SSB_FREQ_OFFSET_MAX_ERROR_HZ 0.01 + +/* + * Correlation size in function of the symbol size. It selects a power of two number at least 8 times bigger than the + * given symbol size but not bigger than 2^13 points. + */ +#define SSB_CORR_SZ(SYMB_SZ) SRSRAN_MIN(1U << (uint32_t)ceil(log2((double)(SYMB_SZ)) + 3.0), 1U << 13U) + +/* + * Default NR-PBCH DMRS normalised correlation (RSRP/EPRE) threshold + */ +#define SSB_PBCH_DMRS_DEFAULT_CORR_THR 0.5f + +static int ssb_init_corr(srsran_ssb_t* q) +{ + // Initialise correlation only if it is enabled + if (!q->args.enable_search) { + return SRSRAN_SUCCESS; + } + + // For each PSS sequence allocate + for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2_NR; N_id_2++) { + // Allocate sequences + q->pss_seq[N_id_2] = srsran_vec_cf_malloc(q->max_corr_sz); + if (q->pss_seq[N_id_2] == NULL) { + ERROR("Malloc"); + return SRSRAN_ERROR; + } + } + + q->sf_buffer = srsran_vec_cf_malloc(q->max_ssb_sz + q->max_sf_sz); + if (q->sf_buffer == NULL) { + ERROR("Malloc"); + return SRSRAN_ERROR; + } + srsran_vec_cf_zero(q->sf_buffer, q->max_ssb_sz + q->max_sf_sz); + + return SRSRAN_SUCCESS; +} + +static int ssb_init_pbch(srsran_ssb_t* q) +{ + srsran_pbch_nr_args_t args = {}; + args.enable_encode = q->args.enable_encode; + args.enable_decode = q->args.enable_decode; + args.disable_simd = q->args.disable_polar_simd; + + if (!args.enable_encode && !args.enable_decode) { + return SRSRAN_SUCCESS; + } + + if (srsran_pbch_nr_init(&q->pbch, &args) < SRSRAN_SUCCESS) { + ERROR("Error init NR PBCH"); + return SRSRAN_SUCCESS; + } + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_init(srsran_ssb_t* q, const srsran_ssb_args_t* args) +{ + // Verify input parameters + if (q == NULL || args == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Copy arguments + q->args = *args; + + // Check if the maximum sampling rate is in range, force default otherwise + q->args.max_srate_hz = (!isnormal(q->args.max_srate_hz)) ? SRSRAN_SSB_DEFAULT_MAX_SRATE_HZ : q->args.max_srate_hz; + q->args.pbch_dmrs_thr = (!isnormal(q->args.pbch_dmrs_thr)) ? SSB_PBCH_DMRS_DEFAULT_CORR_THR : q->args.pbch_dmrs_thr; + + q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(q->args.min_scs); + q->max_sf_sz = (uint32_t)round(1e-3 * q->args.max_srate_hz); + q->max_symbol_sz = (uint32_t)round(q->args.max_srate_hz / q->scs_hz); + q->max_corr_sz = SSB_CORR_SZ(q->max_symbol_sz); + q->max_ssb_sz = SRSRAN_SSB_DURATION_NSYMB * (q->max_symbol_sz + (144 * q->max_symbol_sz) / 2048); + + // Allocate temporal data + q->tmp_time = srsran_vec_cf_malloc(q->max_corr_sz); + q->tmp_freq = srsran_vec_cf_malloc(q->max_corr_sz); + q->tmp_corr = srsran_vec_cf_malloc(q->max_corr_sz); + if (q->tmp_time == NULL || q->tmp_freq == NULL || q->tmp_corr == NULL) { + ERROR("Malloc"); + return SRSRAN_ERROR; + } + + // Allocate correlation buffers + if (ssb_init_corr(q) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // PBCH + if (ssb_init_pbch(q) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void srsran_ssb_free(srsran_ssb_t* q) +{ + if (q == NULL) { + return; + } + + if (q->tmp_time != NULL) { + free(q->tmp_time); + } + + if (q->tmp_freq != NULL) { + free(q->tmp_freq); + } + + if (q->tmp_corr != NULL) { + free(q->tmp_corr); + } + + // For each PSS sequence allocate + for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2_NR; N_id_2++) { + if (q->pss_seq[N_id_2] != NULL) { + free(q->pss_seq[N_id_2]); + } + } + + if (q->sf_buffer != NULL) { + free(q->sf_buffer); + } + + srsran_dft_plan_free(&q->ifft); + srsran_dft_plan_free(&q->fft); + srsran_dft_plan_free(&q->fft_corr); + srsran_dft_plan_free(&q->ifft_corr); + srsran_pbch_nr_free(&q->pbch); + + SRSRAN_MEM_ZERO(q, srsran_ssb_t, 1); +} + +static uint32_t ssb_first_symbol_caseA(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) +{ + // Case A - 15 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes of { 2 , 8 } + 14 ⋅ n . For + // carrier frequencies smaller than or equal to 3 GHz, n = 0 , 1 . For carrier frequencies within FR1 larger than 3 + // GHz, n = 0 , 1 , 2 , 3 . + uint32_t count = 0; + uint32_t base_indexes[2] = {2, 8}; + + uint32_t N = 2; + if (cfg->center_freq_hz > 3e9) { + N = 4; + } + + for (uint32_t n = 0; n < N; n++) { + for (uint32_t i = 0; i < 2; i++) { + indexes[count++] = base_indexes[i] + 14 * n; + } + } + + return count; +} + +static uint32_t ssb_first_symbol_caseB(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) +{ + // Case B - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n . + // For carrier frequencies smaller than or equal to 3 GHz, n = 0 . For carrier frequencies within FR1 larger than 3 + // GHz, n = 0 , 1 . + uint32_t count = 0; + uint32_t base_indexes[4] = {4, 8, 16, 20}; + + uint32_t N = 1; + if (cfg->center_freq_hz > 3e9) { + N = 2; + } + + for (uint32_t n = 0; n < N; n++) { + for (uint32_t i = 0; i < 4; i++) { + indexes[count++] = base_indexes[i] + 28 * n; + } + } + + return count; +} + +static uint32_t ssb_first_symbol_caseC(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) +{ + // Case C - 30 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 2 , 8 } +14 ⋅ n . + // - For paired spectrum operation + // - For carrier frequencies smaller than or equal to 3 GHz, n = 0 , 1 . For carrier frequencies within FR1 larger + // than 3 GHz, n = 0 , 1 , 2 , 3 . + // - For unpaired spectrum operation + // - For carrier frequencies smaller than or equal to 2.3 GHz, n = 0 , 1 . For carrier frequencies within FR1 + // larger than 2.3 GHz, n = 0 , 1 , 2 , 3 . + uint32_t count = 0; + uint32_t base_indexes[2] = {2, 8}; + + uint32_t N = 4; + if ((cfg->duplex_mode == SRSRAN_DUPLEX_MODE_FDD && cfg->center_freq_hz <= 3e9) || + (cfg->duplex_mode == SRSRAN_DUPLEX_MODE_TDD && cfg->center_freq_hz <= 2.3e9)) { + N = 2; + } + + for (uint32_t n = 0; n < N; n++) { + for (uint32_t i = 0; i < 2; i++) { + indexes[count++] = base_indexes[i] + 14 * n; + } + } + + return count; +} + +static uint32_t ssb_first_symbol_caseD(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) +{ + // Case D - 120 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes { 4 , 8 , 16 , 20 } + 28 ⋅ n . + // For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , 7 , 8 , 10 , 11 , 12 , 13 , 15 , 16 , 17 , 18 . + uint32_t count = 0; + uint32_t base_indexes[4] = {4, 8, 16, 20}; + uint32_t n_indexes[16] = {0, 1, 2, 3, 5, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18}; + + for (uint32_t j = 0; j < 16; j++) { + for (uint32_t i = 0; i < 4; i++) { + indexes[count++] = base_indexes[i] + 28 * n_indexes[j]; + } + } + + return count; +} + +static uint32_t ssb_first_symbol_caseE(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) +{ + // Case E - 240 kHz SCS: the first symbols of the candidate SS/PBCH blocks have indexes + //{ 8 , 12 , 16 , 20 , 32 , 36 , 40 , 44 } + 56 ⋅ n . For carrier frequencies within FR2, n = 0 , 1 , 2 , 3 , 5 , 6 , + // 7 , 8 . + uint32_t count = 0; + uint32_t base_indexes[8] = {8, 12, 16, 20, 32, 38, 40, 44}; + uint32_t n_indexes[8] = {0, 1, 2, 3, 5, 6, 7, 8}; + + for (uint32_t j = 0; j < 8; j++) { + for (uint32_t i = 0; i < 8; i++) { + indexes[count++] = base_indexes[i] + 56 * n_indexes[j]; + } + } + + return count; +} + +static uint32_t ssb_first_symbol(const srsran_ssb_cfg_t* cfg, uint32_t indexes[SRSRAN_SSB_NOF_CANDIDATES]) +{ + uint32_t Lmax = 0; + + switch (cfg->pattern) { + case SRSRAN_SSB_PATTERN_A: + Lmax = ssb_first_symbol_caseA(cfg, indexes); + break; + case SRSRAN_SSB_PATTERN_B: + Lmax = ssb_first_symbol_caseB(cfg, indexes); + break; + case SRSRAN_SSB_PATTERN_C: + Lmax = ssb_first_symbol_caseC(cfg, indexes); + break; + case SRSRAN_SSB_PATTERN_D: + Lmax = ssb_first_symbol_caseD(cfg, indexes); + break; + case SRSRAN_SSB_PATTERN_E: + Lmax = ssb_first_symbol_caseE(cfg, indexes); + break; + case SRSRAN_SSB_PATTERN_INVALID: + ERROR("Invalid case"); + } + return Lmax; +} + +// Modulates a given symbol l and stores the time domain signal in q->tmp_time +static void ssb_modulate_symbol(srsran_ssb_t* q, cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t l) +{ + // Select symbol in grid + cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC]; + + // Initialise frequency domain + srsran_vec_cf_zero(q->tmp_freq, q->symbol_sz); + + // Map grid into frequency domain symbol + if (q->f_offset >= SRSRAN_SSB_BW_SUBC / 2) { + srsran_vec_cf_copy(&q->tmp_freq[q->f_offset - SRSRAN_SSB_BW_SUBC / 2], ptr, SRSRAN_SSB_BW_SUBC); + } else if (q->f_offset <= -SRSRAN_SSB_BW_SUBC / 2) { + srsran_vec_cf_copy(&q->tmp_freq[q->symbol_sz + q->f_offset - SRSRAN_SSB_BW_SUBC / 2], ptr, SRSRAN_SSB_BW_SUBC); + } else { + srsran_vec_cf_copy( + &q->tmp_freq[0], &ptr[SRSRAN_SSB_BW_SUBC / 2 - q->f_offset], SRSRAN_SSB_BW_SUBC / 2 + q->f_offset); + srsran_vec_cf_copy(&q->tmp_freq[q->symbol_sz - SRSRAN_SSB_BW_SUBC / 2 + q->f_offset], + &ptr[0], + SRSRAN_SSB_BW_SUBC / 2 - q->f_offset); + } + + // Convert to time domain + srsran_dft_run_guru_c(&q->ifft); + + // Normalise output + float norm = sqrtf((float)q->symbol_sz); + if (isnormal(norm)) { + srsran_vec_sc_prod_cfc(q->tmp_time, 1.0f / norm, q->tmp_time, q->symbol_sz); + } +} + +static int ssb_setup_corr(srsran_ssb_t* q) +{ + // Skip if disabled + if (!q->args.enable_search) { + return SRSRAN_SUCCESS; + } + + // Compute new correlation size + uint32_t corr_sz = SSB_CORR_SZ(q->symbol_sz); + + // Skip if the symbol size is unchanged + if (q->corr_sz == corr_sz) { + return SRSRAN_SUCCESS; + } + q->corr_sz = corr_sz; + + // Select correlation window, return error if the correlation window is smaller than a symbol + if (corr_sz < 2 * q->symbol_sz) { + ERROR("Correlation size (%d) is not sufficient (min. %d)", corr_sz, q->symbol_sz * 2); + return SRSRAN_ERROR; + } + q->corr_window = corr_sz - q->symbol_sz; + + // Free correlation + srsran_dft_plan_free(&q->fft_corr); + srsran_dft_plan_free(&q->ifft_corr); + + // Prepare correlation FFT + if (srsran_dft_plan_guru_c(&q->fft_corr, (int)corr_sz, SRSRAN_DFT_FORWARD, q->tmp_time, q->tmp_freq, 1, 1, 1, 1, 1) < + SRSRAN_SUCCESS) { + ERROR("Error planning correlation DFT"); + return SRSRAN_ERROR; + } + if (srsran_dft_plan_guru_c( + &q->ifft_corr, (int)corr_sz, SRSRAN_DFT_BACKWARD, q->tmp_corr, q->tmp_time, 1, 1, 1, 1, 1) < SRSRAN_SUCCESS) { + ERROR("Error planning correlation DFT"); + return SRSRAN_ERROR; + } + + // Zero the time domain signal last samples + srsran_vec_cf_zero(&q->tmp_time[q->symbol_sz], q->corr_window); + + // Temporal grid + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + + // Initialise correlation sequence + for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2_NR; N_id_2++) { + // Put the PSS in SSB grid + if (srsran_pss_nr_put(ssb_grid, N_id_2, 1.0f) < SRSRAN_SUCCESS) { + ERROR("Error putting PDD N_id_2=%d", N_id_2); + return SRSRAN_ERROR; + } + + // Modulate symbol with PSS + ssb_modulate_symbol(q, ssb_grid, SRSRAN_PSS_NR_SYMBOL_IDX); + + // Convert to frequency domain + srsran_dft_run_guru_c(&q->fft_corr); + + // Copy frequency domain sequence + srsran_vec_cf_copy(q->pss_seq[N_id_2], q->tmp_freq, q->corr_sz); + } + + return SRSRAN_SUCCESS; +} + +static inline int ssb_get_t_offset(srsran_ssb_t* q, uint32_t ssb_idx) +{ + // Get baseband time offset from the begining of the half radio frame to the first symbol + if (ssb_idx >= SRSRAN_SSB_NOF_CANDIDATES) { + ERROR("Invalid SSB candidate index (%d)", ssb_idx); + return SRSRAN_ERROR; + } + + float t_offset_s = srsran_symbol_offset_s(q->l_first[ssb_idx], q->cfg.scs); + if (isnan(t_offset_s) || isinf(t_offset_s) || t_offset_s < 0.0f) { + ERROR("Invalid first symbol (l_first=%d)", q->l_first[ssb_idx]); + return SRSRAN_ERROR; + } + + return (int)round(t_offset_s * q->cfg.srate_hz); +} + +int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg) +{ + // Verify input parameters + if (q == NULL || cfg == NULL || cfg->pattern == SRSRAN_SSB_PATTERN_INVALID || + cfg->duplex_mode == SRSRAN_DUPLEX_MODE_INVALID) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Calculate subcarrier spacing in Hz + q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(cfg->scs); + + // Get first symbol + q->Lmax = ssb_first_symbol(cfg, q->l_first); + + // Calculate SSB symbol size and integer frequency offset + double freq_offset_hz = cfg->ssb_freq_hz - cfg->center_freq_hz; + uint32_t symbol_sz = (uint32_t)round(cfg->srate_hz / q->scs_hz); + q->f_offset = (int32_t)round(freq_offset_hz / q->scs_hz); + + // Calculate cyclic prefix + q->cp_sz = (144U * symbol_sz) / 2048U; + + // Calculate SSB sampling error and check + double ssb_srate_error_Hz = ((double)symbol_sz * q->scs_hz) - cfg->srate_hz; + if (fabs(ssb_srate_error_Hz) > SSB_SRATE_MAX_ERROR_HZ) { + ERROR("Invalid sampling rate (%.2f MHz)", cfg->srate_hz / 1e6); + return SRSRAN_ERROR; + } + + // Calculate SSB offset error and check + double ssb_offset_error_Hz = ((double)q->f_offset * q->scs_hz) - freq_offset_hz; + if (fabs(ssb_offset_error_Hz) > SSB_FREQ_OFFSET_MAX_ERROR_HZ) { + ERROR("SSB Offset (%.1f kHz) error exceeds maximum allowed", freq_offset_hz / 1e3); + return SRSRAN_ERROR; + } + + // Verify symbol size + if (q->max_symbol_sz < symbol_sz) { + ERROR("New symbol size (%d) exceeds maximum symbol size (%d)", symbol_sz, q->max_symbol_sz); + return SRSRAN_ERROR; + } + + // Replan iFFT + if ((q->args.enable_encode || q->args.enable_search) && q->symbol_sz != symbol_sz) { + // free the current IFFT, it internally checks if the plan was created + srsran_dft_plan_free(&q->ifft); + + // Creates DFT plan + if (srsran_dft_plan_guru_c(&q->ifft, (int)symbol_sz, SRSRAN_DFT_BACKWARD, q->tmp_freq, q->tmp_time, 1, 1, 1, 1, 1) < + SRSRAN_SUCCESS) { + ERROR("Error creating iDFT"); + return SRSRAN_ERROR; + } + } + + // Replan FFT + if ((q->args.enable_measure || q->args.enable_decode || q->args.enable_search) && q->symbol_sz != symbol_sz) { + // free the current FFT, it internally checks if the plan was created + srsran_dft_plan_free(&q->fft); + + // Creates DFT plan + if (srsran_dft_plan_guru_c(&q->fft, (int)symbol_sz, SRSRAN_DFT_FORWARD, q->tmp_time, q->tmp_freq, 1, 1, 1, 1, 1) < + SRSRAN_SUCCESS) { + ERROR("Error creating iDFT"); + return SRSRAN_ERROR; + } + } + + // Finally, copy configuration + q->cfg = *cfg; + q->symbol_sz = symbol_sz; + q->sf_sz = (uint32_t)round(1e-3 * cfg->srate_hz); + q->ssb_sz = SRSRAN_SSB_DURATION_NSYMB * (q->symbol_sz + q->cp_sz); + + // Initialise correlation + if (ssb_setup_corr(q) < SRSRAN_SUCCESS) { + ERROR("Error initialising correlation"); + return SRSRAN_ERROR; + } + + if (!isnormal(q->cfg.beta_pss)) { + q->cfg.beta_pss = SRSRAN_SSB_DEFAULT_BETA; + } + + if (!isnormal(q->cfg.beta_sss)) { + q->cfg.beta_sss = SRSRAN_SSB_DEFAULT_BETA; + } + + if (!isnormal(q->cfg.beta_pbch)) { + q->cfg.beta_pbch = SRSRAN_SSB_DEFAULT_BETA; + } + + if (!isnormal(q->cfg.beta_pbch_dmrs)) { + q->cfg.beta_pbch_dmrs = SRSRAN_SSB_DEFAULT_BETA; + } + + return SRSRAN_SUCCESS; +} + +bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx) +{ + // Verify input + if (q == NULL) { + return false; + } + + // Verify periodicity + if (q->cfg.periodicity_ms == 0) { + return false; + } + + // Check periodicity + return (sf_idx % q->cfg.periodicity_ms == 0); +} + +static int ssb_encode(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +{ + uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id); + uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id); + + // Put PSS + if (srsran_pss_nr_put(ssb_grid, N_id_2, q->cfg.beta_pss) < SRSRAN_SUCCESS) { + ERROR("Error putting PSS"); + return SRSRAN_ERROR; + } + + // Put SSS + if (srsran_sss_nr_put(ssb_grid, N_id_1, N_id_2, q->cfg.beta_sss) < SRSRAN_SUCCESS) { + ERROR("Error putting PSS"); + return SRSRAN_ERROR; + } + + // Put PBCH DMRS + srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; + pbch_dmrs_cfg.N_id = N_id; + pbch_dmrs_cfg.n_hf = msg->hrf ? 1 : 0; + pbch_dmrs_cfg.ssb_idx = msg->ssb_idx; + pbch_dmrs_cfg.L_max = q->Lmax; + pbch_dmrs_cfg.beta = 0.0f; + pbch_dmrs_cfg.scs = q->cfg.scs; + if (srsran_dmrs_pbch_put(&pbch_dmrs_cfg, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error putting PBCH DMRS"); + return SRSRAN_ERROR; + } + + // Put PBCH payload + srsran_pbch_nr_cfg_t pbch_cfg = {}; + pbch_cfg.N_id = N_id; + pbch_cfg.n_hf = msg->hrf; + pbch_cfg.ssb_idx = msg->ssb_idx; + pbch_cfg.Lmax = q->Lmax; + pbch_cfg.beta = 0.0f; + if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error encoding PBCH"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +SRSRAN_API int +srsran_ssb_put_grid(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, cf_t* re_grid, uint32_t grid_bw_sc) +{ + // Verify input parameters + if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || re_grid == NULL || + grid_bw_sc * SRSRAN_NRE < SRSRAN_SSB_BW_SUBC) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_encode) { + ERROR("SSB is not configured for encode"); + return SRSRAN_ERROR; + } + + // Put signals in SSB grid + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_encode(q, N_id, msg, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Putting SSB in grid"); + return SRSRAN_ERROR; + } + + // First symbol in the half frame + uint32_t l_first = q->l_first[msg->ssb_idx]; + + // Frequency offset fom the bottom of the grid + uint32_t f_offset = grid_bw_sc / 2 + q->f_offset - SRSRAN_SSB_BW_SUBC / 2; + + // Put SSB grid in the actual resource grid + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + srsran_vec_cf_copy( + &re_grid[grid_bw_sc * (l_first + l) + f_offset], &ssb_grid[SRSRAN_SSB_BW_SUBC * l], SRSRAN_SSB_BW_SUBC); + } + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out) +{ + // Verify input parameters + if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_encode) { + ERROR("SSB is not configured for encode"); + return SRSRAN_ERROR; + } + + // Put signals in SSB grid + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_encode(q, N_id, msg, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Putting SSB in grid"); + return SRSRAN_ERROR; + } + + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, msg->ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + ERROR("Invalid SSB candidate index"); + return SRSRAN_ERROR; + } + + // Select input/ouput pointers considering the time offset in the slot + const cf_t* in_ptr = &in[t_offset]; + cf_t* out_ptr = &out[t_offset]; + + // For each SSB symbol, modulate + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + // Map SSB in resource grid and perform IFFT + ssb_modulate_symbol(q, ssb_grid, l); + + // Phase compensation + cf_t phase_compensation = (cf_t)cexp(-I * 2.0 * M_PI * q->cfg.center_freq_hz * (double)t_offset / q->cfg.srate_hz); + srsran_vec_sc_prod_ccc(q->tmp_time, phase_compensation, q->tmp_time, q->symbol_sz); + t_offset += (int)(q->symbol_sz + q->cp_sz); + + // Add cyclic prefix to input; + srsran_vec_sum_ccc(in_ptr, &q->tmp_time[q->symbol_sz - q->cp_sz], out_ptr, q->cp_sz); + in_ptr += q->cp_sz; + out_ptr += q->cp_sz; + + // Add symbol to the input baseband + srsran_vec_sum_ccc(in_ptr, q->tmp_time, out_ptr, q->symbol_sz); + in_ptr += q->symbol_sz; + out_ptr += q->symbol_sz; + } + + return SRSRAN_SUCCESS; +} + +static int ssb_demodulate(srsran_ssb_t* q, + const cf_t* in, + uint32_t t_offset, + float coarse_cfo_hz, + cf_t ssb_grid[SRSRAN_SSB_NOF_RE]) +{ + const cf_t* in_ptr = &in[t_offset]; + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + // Advance half CP, to avoid inter symbol interference + in_ptr += SRSRAN_FLOOR(q->cp_sz, 2); + + // Copy FFT window in temporal time domain buffer + if (isnormal(coarse_cfo_hz)) { + srsran_vec_apply_cfo(in_ptr, (float)(-coarse_cfo_hz / q->cfg.srate_hz), q->tmp_time, q->symbol_sz); + } else { + srsran_vec_cf_copy(q->tmp_time, in_ptr, q->symbol_sz); + } + in_ptr += q->symbol_sz + SRSRAN_CEIL(q->cp_sz, 2); + + // Phase compensation + cf_t phase_compensation = + (cf_t)cexp(-I * 2.0 * M_PI * (q->cfg.center_freq_hz - coarse_cfo_hz) * (double)t_offset / q->cfg.srate_hz); + t_offset += q->symbol_sz + q->cp_sz; + + // Convert to frequency domain + srsran_dft_run_guru_c(&q->fft); + + // Compensate half CP delay + srsran_vec_apply_cfo(q->tmp_freq, SRSRAN_CEIL(q->cp_sz, 2) / (float)(q->symbol_sz), q->tmp_freq, q->symbol_sz); + + // Select symbol in grid + cf_t* ptr = &ssb_grid[l * SRSRAN_SSB_BW_SUBC]; + + // Map frequency domain symbol into the SSB grid + if (q->f_offset >= SRSRAN_SSB_BW_SUBC / 2) { + srsran_vec_cf_copy(ptr, &q->tmp_freq[q->f_offset - SRSRAN_SSB_BW_SUBC / 2], SRSRAN_SSB_BW_SUBC); + } else if (q->f_offset <= -SRSRAN_SSB_BW_SUBC / 2) { + srsran_vec_cf_copy(ptr, &q->tmp_freq[q->symbol_sz + q->f_offset - SRSRAN_SSB_BW_SUBC / 2], SRSRAN_SSB_BW_SUBC); + } else { + srsran_vec_cf_copy( + &ptr[SRSRAN_SSB_BW_SUBC / 2 - q->f_offset], &q->tmp_freq[0], SRSRAN_SSB_BW_SUBC / 2 + q->f_offset); + srsran_vec_cf_copy(&ptr[0], + &q->tmp_freq[q->symbol_sz - SRSRAN_SSB_BW_SUBC / 2 + q->f_offset], + SRSRAN_SSB_BW_SUBC / 2 - q->f_offset); + } + + // Normalize + float norm = sqrtf((float)q->symbol_sz); + if (isnormal(norm)) { + srsran_vec_sc_prod_ccc(ptr, conjf(phase_compensation) / norm, ptr, SRSRAN_SSB_BW_SUBC); + } + } + + return SRSRAN_SUCCESS; +} + +static int +ssb_measure(srsran_ssb_t* q, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id, srsran_csi_trs_measurements_t* meas) +{ + uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id); + uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id); + + // Extract PSS and SSS LSE + cf_t pss_lse[SRSRAN_PSS_NR_LEN]; + cf_t sss_lse[SRSRAN_SSS_NR_LEN]; + if (srsran_pss_nr_extract_lse(ssb_grid, N_id_2, pss_lse) < SRSRAN_SUCCESS || + srsran_sss_nr_extract_lse(ssb_grid, N_id_1, N_id_2, sss_lse) < SRSRAN_SUCCESS) { + ERROR("Error extracting LSE"); + return SRSRAN_ERROR; + } + + // Estimate average delay + float delay_pss_norm = srsran_vec_estimate_frequency(pss_lse, SRSRAN_PSS_NR_LEN); + float delay_sss_norm = srsran_vec_estimate_frequency(sss_lse, SRSRAN_SSS_NR_LEN); + float delay_avg_norm = (delay_pss_norm + delay_sss_norm) / 2.0f; + float delay_avg_us = 1e6f * delay_avg_norm / q->scs_hz; + + // Pre-compensate delay + cf_t ssb_grid_corrected[SRSRAN_SSB_NOF_RE]; + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + srsran_vec_apply_cfo(&ssb_grid[SRSRAN_SSB_BW_SUBC * l], + delay_avg_norm, + &ssb_grid_corrected[SRSRAN_SSB_BW_SUBC * l], + SRSRAN_SSB_BW_SUBC); + } + + // Extract LSE again + if (srsran_pss_nr_extract_lse(ssb_grid_corrected, N_id_2, pss_lse) < SRSRAN_SUCCESS || + srsran_sss_nr_extract_lse(ssb_grid_corrected, N_id_1, N_id_2, sss_lse) < SRSRAN_SUCCESS) { + ERROR("Error extracting LSE"); + return SRSRAN_ERROR; + } + + // Estimate average EPRE + float epre_pss = srsran_vec_avg_power_cf(pss_lse, SRSRAN_PSS_NR_LEN); + float epre_sss = srsran_vec_avg_power_cf(sss_lse, SRSRAN_SSS_NR_LEN); + float epre = (epre_pss + epre_sss) / 2.0f; + + // Compute correlation + cf_t corr_pss = srsran_vec_acc_cc(pss_lse, SRSRAN_PSS_NR_LEN) / SRSRAN_PSS_NR_LEN; + cf_t corr_sss = srsran_vec_acc_cc(sss_lse, SRSRAN_SSS_NR_LEN) / SRSRAN_SSS_NR_LEN; + + // Compute CFO in Hz + float distance_s = srsran_symbol_distance_s(SRSRAN_PSS_NR_SYMBOL_IDX, SRSRAN_SSS_NR_SYMBOL_IDX, q->cfg.scs); + float cfo_hz_max = 1.0f / distance_s; + float cfo_hz = cargf(corr_sss * conjf(corr_pss)) / (2.0f * M_PI) * cfo_hz_max; + + // Compute average RSRP + float rsrp_pss = SRSRAN_CSQABS(corr_pss); + float rsrp_sss = SRSRAN_CSQABS(corr_sss); + float rsrp = (rsrp_pss + rsrp_sss) / 2.0f; + + // Avoid taking log of 0 or another abnormal value + if (!isnormal(rsrp)) { + rsrp = 1e-9f; + } + + // Estimate Noise: + // - Infinite (1e9), if the EPRE or RSRP is zero + // - EPRE-RSRP if EPRE > RSRP + // - zero (1e-9), otherwise + float n0_pss = 1e-9f; + if (!isnormal(epre_pss) || !isnormal(rsrp_pss)) { + n0_pss = 1e9f; + } else if (epre_pss > rsrp_pss) { + n0_pss = epre - rsrp_pss; + } + float n0_sss = 1e-9f; + if (!isnormal(epre_sss) || !isnormal(rsrp_sss)) { + n0_sss = 1e9f; + } else if (epre_sss > rsrp_sss) { + n0_sss = epre - rsrp_sss; + } + float n0 = (n0_pss + n0_sss) / 2.0f; + + // Put measurements together + meas->epre = epre; + meas->epre_dB = srsran_convert_power_to_dB(epre); + meas->rsrp = rsrp; + meas->rsrp_dB = srsran_convert_power_to_dB(rsrp); + meas->n0 = n0; + meas->n0_dB = srsran_convert_power_to_dB(n0); + meas->snr_dB = meas->rsrp_dB - meas->n0_dB; + meas->cfo_hz = cfo_hz; + meas->cfo_hz_max = cfo_hz_max; + meas->delay_us = delay_avg_us; // Convert the delay to microseconds + meas->nof_re = SRSRAN_PSS_NR_LEN + SRSRAN_SSS_NR_LEN; + + return SRSRAN_SUCCESS; +} + +static void ssb_vec_prod_conj_circ_shift(const cf_t* a, const cf_t* b, cf_t* c, uint32_t n, int shift) +{ + uint32_t offset = (uint32_t)abs(shift); + + // Avoid negative number of samples + if (offset > n) { + srsran_vec_cf_zero(c, n); + return; + } + + // Shift is negative + if (shift < 0) { + srsran_vec_prod_conj_ccc(&a[offset], &b[0], &c[0], n - offset); + srsran_vec_prod_conj_ccc(&a[0], &b[n - offset], &c[n - offset], offset); + return; + } + + // Shift is positive + if (shift > 0) { + srsran_vec_prod_conj_ccc(&a[0], &b[offset], &c[0], n - offset); + srsran_vec_prod_conj_ccc(&a[n - offset], &b[0], &c[n - offset], offset); + return; + } + + // Shift is zero + srsran_vec_prod_conj_ccc(a, b, c, n); +} + +static int ssb_pss_search(srsran_ssb_t* q, + const cf_t* in, + uint32_t nof_samples, + uint32_t* found_N_id_2, + uint32_t* found_delay, + float* coarse_cfo_hz) +{ + // verify it is initialised + if (q->corr_sz == 0) { + return SRSRAN_ERROR; + } + + // Calculate correlation CFO coarse precision + double coarse_cfo_ref_hz = (q->cfg.srate_hz / q->corr_sz); + + // Calculate shift integer range to detect the signal with a maximum CFO equal to the SSB subcarrier spacing + int shift_range = (int)ceil(SRSRAN_SUBC_SPACING_NR(q->cfg.scs) / coarse_cfo_ref_hz); + + // Calculate the coarse shift increment for half of the subcarrier spacing + int shift_coarse_inc = shift_range / 2; + + // Correlation best sequence + float best_corr = 0; + uint32_t best_delay = 0; + uint32_t best_N_id_2 = 0; + int best_shift = 0; + + // Delay in correlation window + uint32_t t_offset = 0; + while ((t_offset + q->symbol_sz) < nof_samples) { + // Number of samples taken in this iteration + uint32_t n = q->corr_sz; + + // Detect if the correlation input exceeds the input length, take the maximum amount of samples + if (t_offset + q->corr_sz > nof_samples) { + n = nof_samples - t_offset; + } + + // Copy the amount of samples + srsran_vec_cf_copy(q->tmp_time, &in[t_offset], n); + + // Append zeros if there is space left + if (n < q->corr_sz) { + srsran_vec_cf_zero(&q->tmp_time[n], q->corr_sz - n); + } + + // Convert to frequency domain + srsran_dft_run_guru_c(&q->fft_corr); + + // Try each N_id_2 sequence + for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2_NR; N_id_2++) { + // Steer coarse frequency offset + for (int shift = -shift_range; shift <= shift_range; shift += shift_coarse_inc) { + // Actual correlation in frequency domain + ssb_vec_prod_conj_circ_shift(q->tmp_freq, q->pss_seq[N_id_2], q->tmp_corr, q->corr_sz, shift); + + // Convert to time domain + srsran_dft_run_guru_c(&q->ifft_corr); + + // Find maximum + uint32_t peak_idx = srsran_vec_max_abs_ci(q->tmp_time, q->corr_window); + + // Average power, take total power of the frequency domain signal after filtering, skip correlation window if + // value is invalid (0.0, nan or inf) + float avg_pwr_corr = srsran_vec_avg_power_cf(q->tmp_corr, q->corr_sz); + if (!isnormal(avg_pwr_corr)) { + continue; + } + + // Normalise correlation + float corr = SRSRAN_CSQABS(q->tmp_time[peak_idx]) / avg_pwr_corr / sqrtf(SRSRAN_PSS_NR_LEN); + + // Update if the correlation is better than the current best + if (best_corr < corr) { + best_corr = corr; + best_delay = peak_idx + t_offset; + best_N_id_2 = N_id_2; + best_shift = shift; + } + } + } + + // Advance time + t_offset += q->corr_window; + } + + // From the best sequence correlate in frequency domain + { + // Reset best correlation + best_corr = 0.0f; + + // Number of samples taken in this iteration + uint32_t n = q->corr_sz; + + // Detect if the correlation input exceeds the input length, take the maximum amount of samples + if (best_delay + q->corr_sz > nof_samples) { + n = nof_samples - best_delay; + } + + // Copy the amount of samples + srsran_vec_cf_copy(q->tmp_time, &in[best_delay], n); + + // Append zeros if there is space left + if (n < q->corr_sz) { + srsran_vec_cf_zero(&q->tmp_time[n], q->corr_sz - n); + } + + // Convert to frequency domain + srsran_dft_run_guru_c(&q->fft_corr); + + for (int shift = -shift_range; shift <= shift_range; shift++) { + // Actual correlation in frequency domain + ssb_vec_prod_conj_circ_shift(q->tmp_freq, q->pss_seq[best_N_id_2], q->tmp_corr, q->corr_sz, shift); + + // Calculate correlation assuming the peak is in the first sample + float corr = SRSRAN_CSQABS(srsran_vec_acc_cc(q->tmp_corr, q->corr_sz)); + + // Update if the correlation is better than the current best + if (best_corr < corr) { + best_corr = corr; + best_shift = shift; + } + } + } + + // Save findings + *found_delay = best_delay; + *found_N_id_2 = best_N_id_2; + *coarse_cfo_hz = -(float)best_shift * coarse_cfo_ref_hz; + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_csi_search(srsran_ssb_t* q, + const cf_t* in, + uint32_t nof_samples, + uint32_t* N_id, + srsran_csi_trs_measurements_t* meas) +{ + // Verify inputs + if (q == NULL || in == NULL || N_id == NULL || meas == NULL || !isnormal(q->scs_hz)) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_search) { + ERROR("SSB is not configured for search"); + return SRSRAN_ERROR; + } + + // Avoid finding a peak in a region that cannot be demodulated + if (nof_samples < (q->symbol_sz + q->cp_sz) * SRSRAN_SSB_DURATION_NSYMB) { + ERROR("Insufficient number of samples (%d/%d)", nof_samples, (q->symbol_sz + q->cp_sz) * SRSRAN_SSB_DURATION_NSYMB); + return SRSRAN_ERROR; + } + nof_samples -= (q->symbol_sz + q->cp_sz) * SRSRAN_SSB_DURATION_NSYMB; + + // Search for PSS in time domain + uint32_t N_id_2 = 0; + uint32_t t_offset = 0; + float coarse_cfo_hz = 0.0f; + if (ssb_pss_search(q, in, nof_samples, &N_id_2, &t_offset, &coarse_cfo_hz) < SRSRAN_SUCCESS) { + ERROR("Error searching for N_id_2"); + return SRSRAN_ERROR; + } + + // Remove CP offset prior demodulation + if (t_offset >= q->cp_sz) { + t_offset -= q->cp_sz; + } else { + t_offset = 0; + } + + // Make sure SSB time offset is in bounded in the input buffer + if (t_offset + q->ssb_sz > nof_samples) { + return SRSRAN_SUCCESS; + } + + // Demodulate + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, in, t_offset, coarse_cfo_hz, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error demodulating"); + return SRSRAN_ERROR; + } + + // Find best N_id_1 + uint32_t N_id_1 = 0; + float sss_corr = 0.0f; + if (srsran_sss_nr_find(ssb_grid, N_id_2, &sss_corr, &N_id_1) < SRSRAN_SUCCESS) { + ERROR("Error searching for N_id_2"); + return SRSRAN_ERROR; + } + + // Select N_id + *N_id = SRSRAN_NID_NR(N_id_1, N_id_2); + + // Measure selected N_id + if (ssb_measure(q, ssb_grid, *N_id, meas)) { + ERROR("Error measuring"); + return SRSRAN_ERROR; + } + + // Add delay to measure + meas->delay_us += (float)(1e6 * t_offset / q->cfg.srate_hz); + meas->cfo_hz -= coarse_cfo_hz; + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_csi_measure(srsran_ssb_t* q, + uint32_t N_id, + uint32_t ssb_idx, + const cf_t* in, + srsran_csi_trs_measurements_t* meas) +{ + // Verify inputs + if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || meas == NULL || !isnormal(q->scs_hz)) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_measure) { + ERROR("SSB is not configured to measure"); + return SRSRAN_ERROR; + } + + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Demodulate + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, in, (uint32_t)t_offset, 0.0f, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error demodulating"); + return SRSRAN_ERROR; + } + + // Actual measurement + if (ssb_measure(q, ssb_grid, N_id, meas)) { + ERROR("Error measuring"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +static int ssb_select_pbch(srsran_ssb_t* q, + uint32_t N_id, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + uint32_t* found_n_hf, + uint32_t* found_ssb_idx_4lsb, + srsran_dmrs_pbch_meas_t* pbch_meas) +{ + // Prepare PBCH DMRS configuration + srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; + pbch_dmrs_cfg.N_id = N_id; + pbch_dmrs_cfg.n_hf = 0; // Parameter to guess + pbch_dmrs_cfg.ssb_idx = 0; // Parameter to guess + pbch_dmrs_cfg.L_max = q->Lmax; + pbch_dmrs_cfg.beta = 0.0f; + pbch_dmrs_cfg.scs = q->cfg.scs; + + // Initialise best values + srsran_dmrs_pbch_meas_t best_meas = {}; + uint32_t best_n_hf = 0; + uint32_t best_ssb_idx = 0; + + // Iterate over all the parameters to guess and select the most suitable + for (uint32_t n_hf = 0; n_hf < 2; n_hf++) { + for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_MIN(8, q->Lmax); ssb_idx++) { + // Set parameters + pbch_dmrs_cfg.n_hf = n_hf; + pbch_dmrs_cfg.ssb_idx = ssb_idx; + + // Measure + srsran_dmrs_pbch_meas_t meas = {}; + if (srsran_dmrs_pbch_measure(&pbch_dmrs_cfg, ssb_grid, &meas) < SRSRAN_SUCCESS) { + ERROR("Error measure for n_hf=%d ssb_idx=%d", n_hf, ssb_idx); + return SRSRAN_ERROR; + } + + // Select the result with highest correlation (most suitable) + if (meas.corr > best_meas.corr) { + best_meas = meas; + best_n_hf = n_hf; + best_ssb_idx = ssb_idx; + } + } + } + + // Save findings + *found_n_hf = best_n_hf; + *found_ssb_idx_4lsb = best_ssb_idx; + *pbch_meas = best_meas; + + return SRSRAN_SUCCESS; +} + +static int ssb_decode_pbch(srsran_ssb_t* q, + uint32_t N_id, + uint32_t n_hf, + uint32_t ssb_idx, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + srsran_pbch_msg_nr_t* msg) +{ + // Prepare PBCH DMRS configuration + srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; + pbch_dmrs_cfg.N_id = N_id; + pbch_dmrs_cfg.n_hf = n_hf; + pbch_dmrs_cfg.ssb_idx = ssb_idx; + pbch_dmrs_cfg.L_max = q->Lmax; + pbch_dmrs_cfg.beta = 0.0f; + pbch_dmrs_cfg.scs = q->cfg.scs; + + // Compute PBCH channel estimates + cf_t ce[SRSRAN_SSB_NOF_RE] = {}; + if (srsran_dmrs_pbch_estimate(&pbch_dmrs_cfg, ssb_grid, ce) < SRSRAN_SUCCESS) { + ERROR("Error estimating channel"); + return SRSRAN_ERROR; + } + + // Prepare PBCH configuration + srsran_pbch_nr_cfg_t pbch_cfg = {}; + pbch_cfg.N_id = N_id; + pbch_cfg.n_hf = n_hf; + pbch_cfg.ssb_idx = ssb_idx; + pbch_cfg.Lmax = q->Lmax; + pbch_cfg.beta = 0.0f; + + // Decode + if (srsran_pbch_nr_decode(&q->pbch, &pbch_cfg, ssb_grid, ce, msg) < SRSRAN_SUCCESS) { + ERROR("Error decoding PBCH"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_decode_grid(srsran_ssb_t* q, + uint32_t N_id, + uint32_t n_hf, + uint32_t ssb_idx, + const cf_t* re_grid, + uint32_t grid_bw_sc, + srsran_pbch_msg_nr_t* msg) +{ + // Verify input parameters + if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || re_grid == NULL || grid_bw_sc < SRSRAN_SSB_BW_SUBC) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_encode) { + ERROR("SSB is not configured for encode"); + return SRSRAN_ERROR; + } + + // First symbol in the half frame + uint32_t l_first = q->l_first[ssb_idx]; + + // Frequency offset fom the bottom of the grid + uint32_t f_offset = grid_bw_sc / 2 + q->f_offset - SRSRAN_SSB_BW_SUBC / 2; + + // Get SSB grid from resource grid + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) { + srsran_vec_cf_copy( + &ssb_grid[SRSRAN_SSB_BW_SUBC * l], &re_grid[grid_bw_sc * (l_first + l) + f_offset], SRSRAN_SSB_BW_SUBC); + } + + // Decode PBCH + if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, msg) < SRSRAN_SUCCESS) { + ERROR("Error decoding"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_decode_pbch(srsran_ssb_t* q, + uint32_t N_id, + uint32_t n_hf, + uint32_t ssb_idx, + const cf_t* in, + srsran_pbch_msg_nr_t* msg) +{ + // Verify inputs + if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || in == NULL || msg == NULL || !isnormal(q->scs_hz)) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_decode) { + ERROR("SSB is not configured to decode"); + return SRSRAN_ERROR; + } + + // Select start symbol from SSB candidate index + int t_offset = ssb_get_t_offset(q, ssb_idx); + if (t_offset < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Demodulate + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, in, (uint32_t)t_offset, 0.0f, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error demodulating"); + return SRSRAN_ERROR; + } + + // Decode PBCH + if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, msg) < SRSRAN_SUCCESS) { + ERROR("Error decoding"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srsran_ssb_search_res_t* res) +{ + // Verify inputs + if (q == NULL || in == NULL || res == NULL || !isnormal(q->scs_hz)) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_search || !q->args.enable_decode) { + ERROR("SSB is not configured to search (%c) and decode (%c)", + q->args.enable_search ? 'y' : 'n', + q->args.enable_decode ? 'y' : 'n'); + return SRSRAN_ERROR; + } + + // Set the SSB search result with default value with PBCH CRC unmatched, meaning no cell is found + SRSRAN_MEM_ZERO(res, srsran_ssb_search_res_t, 1); + + // Search for PSS in time domain + uint32_t N_id_2 = 0; + uint32_t t_offset = 0; + float coarse_cfo_hz = 0.0f; + if (ssb_pss_search(q, in, nof_samples, &N_id_2, &t_offset, &coarse_cfo_hz) < SRSRAN_SUCCESS) { + ERROR("Error searching for N_id_2"); + return SRSRAN_ERROR; + } + + // Remove CP offset prior demodulation + if (t_offset >= q->cp_sz) { + t_offset -= q->cp_sz; + } else { + t_offset = 0; + } + + // Make sure SSB time offset is in bounded in the input buffer + if (t_offset + q->ssb_sz > nof_samples) { + return SRSRAN_SUCCESS; + } + + // Demodulate + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, in, t_offset, coarse_cfo_hz, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error demodulating"); + return SRSRAN_ERROR; + } + + // Find best N_id_1 + uint32_t N_id_1 = 0; + float sss_corr = 0.0f; + if (srsran_sss_nr_find(ssb_grid, N_id_2, &sss_corr, &N_id_1) < SRSRAN_SUCCESS) { + ERROR("Error searching for N_id_2"); + return SRSRAN_ERROR; + } + + // Select N_id + uint32_t N_id = SRSRAN_NID_NR(N_id_1, N_id_2); + + // Select the most suitable SSB candidate + uint32_t n_hf = 0; + uint32_t ssb_idx = 0; + srsran_dmrs_pbch_meas_t pbch_meas = {}; + if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx, &pbch_meas) < SRSRAN_SUCCESS) { + ERROR("Error selecting PBCH"); + return SRSRAN_ERROR; + } + + // Avoid decoding if the selected PBCH DMRS do not reach the minimum threshold + if (pbch_meas.corr < q->args.pbch_dmrs_thr) { + return SRSRAN_SUCCESS; + } + + // Decode PBCH + srsran_pbch_msg_nr_t pbch_msg = {}; + if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, &pbch_msg) < SRSRAN_SUCCESS) { + ERROR("Error decoding PBCH"); + return SRSRAN_ERROR; + } + + // If PBCH was not decoded, skip measurements + if (!pbch_msg.crc) { + return SRSRAN_SUCCESS; + } + + // Perform measurements from PSS and SSS + srsran_csi_trs_measurements_t measurements = {}; + if (ssb_measure(q, ssb_grid, N_id, &measurements) < SRSRAN_SUCCESS) { + ERROR("Error measuring"); + return SRSRAN_ERROR; + } + + // Save result + res->N_id = N_id; + res->t_offset = t_offset; + res->pbch_msg = pbch_msg; + res->measurements = measurements; + res->measurements.cfo_hz += coarse_cfo_hz; + + return SRSRAN_SUCCESS; +} + +static int ssb_pss_find(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, uint32_t N_id_2, uint32_t* found_delay) +{ + // verify it is initialised + if (q->corr_sz == 0) { + return SRSRAN_ERROR; + } + + // Correlation best sequence + float best_corr = 0; + uint32_t best_delay = 0; + + // Delay in correlation window + uint32_t t_offset = 0; + while ((t_offset + q->symbol_sz) < nof_samples) { + // Number of samples taken in this iteration + uint32_t n = q->corr_sz; + + // Detect if the correlation input exceeds the input length, take the maximum amount of samples + if (t_offset + q->corr_sz > nof_samples) { + n = nof_samples - t_offset; + } + + // Copy the amount of samples + srsran_vec_cf_copy(q->tmp_time, &in[t_offset], n); + + // Append zeros if there is space left + if (n < q->corr_sz) { + srsran_vec_cf_zero(&q->tmp_time[n], q->corr_sz - n); + } + + // Convert to frequency domain + srsran_dft_run_guru_c(&q->fft_corr); + + // Actual correlation in frequency domain + srsran_vec_prod_conj_ccc(q->tmp_freq, q->pss_seq[N_id_2], q->tmp_corr, q->corr_sz); + + // Convert to time domain + srsran_dft_run_guru_c(&q->ifft_corr); + + // Find maximum + uint32_t peak_idx = srsran_vec_max_abs_ci(q->tmp_time, q->corr_window); + + // Average power, skip window if value is invalid (0.0, nan or inf) + float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz); + if (!isnormal(avg_pwr_corr)) { + // Advance time + t_offset += q->corr_window; + continue; + } + + // Normalise correlation + float corr = SRSRAN_CSQABS(q->tmp_time[peak_idx]) / avg_pwr_corr / sqrtf(SRSRAN_PSS_NR_LEN); + + // Update if the correlation is better than the current best + if (best_corr < corr) { + best_corr = corr; + best_delay = peak_idx + t_offset; + } + + // Advance time + t_offset += q->corr_window; + } + + // Save findings + *found_delay = best_delay; + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_find(srsran_ssb_t* q, + const cf_t* sf_buffer, + uint32_t N_id, + srsran_csi_trs_measurements_t* meas, + srsran_pbch_msg_nr_t* pbch_msg) +{ + // Verify inputs + if (q == NULL || sf_buffer == NULL || meas == NULL || !isnormal(q->scs_hz)) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_search) { + ERROR("SSB is not configured for search"); + return SRSRAN_ERROR; + } + + // Set the PBCH message result with default value (CRC unmatched), meaning no cell is found + SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1); + + // Copy tail from previous execution into the start of this + srsran_vec_cf_copy(q->sf_buffer, &q->sf_buffer[q->sf_sz], q->ssb_sz); + + // Append new samples + srsran_vec_cf_copy(&q->sf_buffer[q->ssb_sz], sf_buffer, q->sf_sz); + + // Search for PSS in time domain + uint32_t t_offset = 0; + if (ssb_pss_find(q, q->sf_buffer, q->sf_sz + q->ssb_sz, SRSRAN_NID_2_NR(N_id), &t_offset) < SRSRAN_SUCCESS) { + ERROR("Error searching for N_id_2"); + return SRSRAN_ERROR; + } + + // Remove CP offset prior demodulation + if (t_offset >= q->cp_sz) { + t_offset -= q->cp_sz; + } else { + t_offset = 0; + } + + // Make sure SSB time offset is in bounded in the input buffer + if (t_offset > q->sf_sz) { + return SRSRAN_SUCCESS; + } + + // Demodulate + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, q->sf_buffer, t_offset, 0.0f, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error demodulating"); + return SRSRAN_ERROR; + } + + // Measure selected N_id + if (ssb_measure(q, ssb_grid, N_id, meas)) { + ERROR("Error measuring"); + return SRSRAN_ERROR; + } + + // Select the most suitable SSB candidate + uint32_t n_hf = 0; + uint32_t ssb_idx = 0; // SSB candidate index + srsran_dmrs_pbch_meas_t pbch_meas = {}; + if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx, &pbch_meas) < SRSRAN_SUCCESS) { + ERROR("Error selecting PBCH"); + return SRSRAN_ERROR; + } + + // Avoid decoding if the selected PBCH DMRS do not reach the minimum threshold + if (pbch_meas.corr < q->args.pbch_dmrs_thr) { + return SRSRAN_SUCCESS; + } + + // Calculate the SSB offset in the subframe + uint32_t ssb_offset = srsran_ssb_candidate_sf_offset(q, ssb_idx); + + // Compute PBCH channel estimates + if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, pbch_msg) < SRSRAN_SUCCESS) { + ERROR("Error decoding PBCH"); + return SRSRAN_ERROR; + } + + // SSB delay in SF + float ssb_delay_us = (float)(1e6 * (((double)t_offset - (double)q->ssb_sz - (double)ssb_offset) / q->cfg.srate_hz)); + + // Add delay to measure + meas->delay_us += ssb_delay_us; + + return SRSRAN_SUCCESS; +} + +int srsran_ssb_track(srsran_ssb_t* q, + const cf_t* sf_buffer, + uint32_t N_id, + uint32_t ssb_idx, + uint32_t n_hf, + srsran_csi_trs_measurements_t* meas, + srsran_pbch_msg_nr_t* pbch_msg) +{ + // Verify inputs + if (q == NULL || sf_buffer == NULL || meas == NULL || !isnormal(q->scs_hz)) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + if (!q->args.enable_search) { + ERROR("SSB is not configured for search"); + return SRSRAN_ERROR; + } + + // Calculate SSB offset + uint32_t t_offset = srsran_ssb_candidate_sf_offset(q, ssb_idx); + + // Demodulate + cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; + if (ssb_demodulate(q, sf_buffer, t_offset, 0.0f, ssb_grid) < SRSRAN_SUCCESS) { + ERROR("Error demodulating"); + return SRSRAN_ERROR; + } + + // Measure selected N_id + if (ssb_measure(q, ssb_grid, N_id, meas)) { + ERROR("Error measuring"); + return SRSRAN_ERROR; + } + + // Compute PBCH channel estimates + if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, pbch_msg) < SRSRAN_SUCCESS) { + ERROR("Error decoding PBCH"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +uint32_t srsran_ssb_candidate_sf_idx(const srsran_ssb_t* q, uint32_t ssb_idx, bool half_frame) +{ + if (q == NULL) { + return 0; + } + + uint32_t nof_symbols_subframe = SRSRAN_NSYMB_PER_SLOT_NR * SRSRAN_NSLOTS_PER_SF_NR(q->cfg.scs); + + return q->l_first[ssb_idx] / nof_symbols_subframe + (half_frame ? (SRSRAN_NOF_SF_X_FRAME / 2) : 0); +} + +uint32_t srsran_ssb_candidate_sf_offset(const srsran_ssb_t* q, uint32_t ssb_idx) +{ + if (q == NULL) { + return 0; + } + + uint32_t nof_symbols_subframe = SRSRAN_NSYMB_PER_SLOT_NR * SRSRAN_NSLOTS_PER_SF_NR(q->cfg.scs); + uint32_t l = q->l_first[ssb_idx] % nof_symbols_subframe; + + uint32_t cp_sz_0 = (16U * q->symbol_sz) / 2048U; + + return cp_sz_0 + l * (q->symbol_sz + q->cp_sz); +} + +uint32_t srsran_ssb_cfg_to_str(const srsran_ssb_cfg_t* cfg, char* str, uint32_t str_len) +{ + uint32_t n = 0; + + n = srsran_print_check(str, + str_len, + n, + "srate=%.2f MHz; c-freq=%.3f MHz; ss-freq=%.3f MHz; scs=%s; pattern=%s; duplex=%s;", + cfg->srate_hz / 1e6, + cfg->center_freq_hz / 1e6, + cfg->ssb_freq_hz / 1e6, + srsran_subcarrier_spacing_to_str(cfg->scs), + srsran_ssb_pattern_to_str(cfg->pattern), + cfg->duplex_mode == SRSRAN_DUPLEX_MODE_FDD ? "fdd" : "tdd"); + + if (cfg->periodicity_ms > 0) { + n = srsran_print_check(str, str_len, n, " period=%d ms;", cfg->periodicity_ms); + } + + return n; +} diff --git a/lib/src/phy/sync/sss.c b/lib/src/phy/sync/sss.c index 02b827276..db4239ecd 100644 --- a/lib/src/phy/sync/sss.c +++ b/lib/src/phy/sync/sss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/sss_nr.c b/lib/src/phy/sync/sss_nr.c new file mode 100644 index 000000000..34987cc9d --- /dev/null +++ b/lib/src/phy/sync/sss_nr.c @@ -0,0 +1,211 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/sync/sss_nr.h" +#include "srsran/phy/utils/vector.h" + +/** + * NR SSS First Subcarrier index + */ +#define SSS_NR_SUBC_BEGIN 56 + +/** + * Number of possible M1 shifts + */ +#define SSS_NR_NOF_M1 112U + +/** + * Number of possible M0 shifts + */ +#define SSS_NR_NOF_M0 SRSRAN_FLOOR(SRSRAN_NOF_NID_1_NR, SSS_NR_NOF_M1) + +/** + * Calculates Sequence circular offset M0 value + */ +#define SSS_NR_SEQUENCE_M0(N_id_1, N_id_2) \ + ((15U * SRSRAN_FLOOR(N_id_1, SSS_NR_NOF_M1) + 5 * (N_id_2)) % SRSRAN_SSS_NR_LEN) + +/** + * Calculates Sequence circular offset M1 value + */ +#define SSS_NR_SEQUENCE_M1(N_id_1) (N_id_1 % SSS_NR_NOF_M1) + +/** + * Pregenerated modulated sequences + */ +static cf_t sss_nr_d0[SRSRAN_SSS_NR_LEN] = {}; +static cf_t sss_nr_d1[SRSRAN_SSS_NR_LEN] = {}; + +/** + * Sequence generation as described in TS 38.211 clause 7.4.2.2.1 + */ +__attribute__((constructor)) __attribute__((unused)) static void sss_nr_pregen() +{ + // Initialise M sequence x0 + uint32_t x0[SRSRAN_SSS_NR_LEN + 7]; + x0[6] = 0; + x0[5] = 0; + x0[4] = 0; + x0[3] = 0; + x0[2] = 0; + x0[1] = 0; + x0[0] = 1; + + // Initialise M sequence x1 + uint32_t x1[SRSRAN_SSS_NR_LEN + 7]; + x1[6] = 0; + x1[5] = 0; + x1[4] = 0; + x1[3] = 0; + x1[2] = 0; + x1[1] = 0; + x1[0] = 1; + + // Generate M sequence x + for (uint32_t i = 0; i < SRSRAN_SSS_NR_LEN; i++) { + x0[i + 7] = (x0[i + 4] + x0[i]) % 2; + x1[i + 7] = (x1[i + 1] + x1[i]) % 2; + } + + // Modulate M sequence d + for (uint32_t i = 0; i < SRSRAN_SSS_NR_LEN; i++) { + sss_nr_d0[i] = 1.0f - 2.0f * (float)x0[i]; + sss_nr_d1[i] = 1.0f - 2.0f * (float)x1[i]; + } +} + +int srsran_sss_nr_put(cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_id_1, uint32_t N_id_2, float beta) +{ + // Verify inputs + if (ssb_grid == NULL || N_id_1 >= SRSRAN_NOF_NID_1_NR || N_id_2 >= SRSRAN_NOF_NID_2_NR) { + return SRSRAN_ERROR; + } + + // Calculate generation parameters + uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1, N_id_2); + uint32_t m1 = SSS_NR_SEQUENCE_M1(N_id_1); + uint32_t grid_idx_m0_1 = SRSRAN_SSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + SSS_NR_SUBC_BEGIN; + uint32_t grid_idx_m0_2 = grid_idx_m0_1 + (SRSRAN_SSS_NR_LEN - m0); + uint32_t grid_idx_m1_1 = SRSRAN_SSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + SSS_NR_SUBC_BEGIN; + uint32_t grid_idx_m1_2 = grid_idx_m1_1 + (SRSRAN_SSS_NR_LEN - m1); + + // Copy d0 sequence first part from m0 to the end + srsran_vec_sc_prod_cfc(&sss_nr_d0[m0], beta, &ssb_grid[grid_idx_m0_1], SRSRAN_SSS_NR_LEN - m0); + + // Copy d0 sequence second part from 0 to m0 + srsran_vec_sc_prod_cfc(&sss_nr_d0[0], beta, &ssb_grid[grid_idx_m0_2], m0); + + // Multiply d1 sequence first part from m1 to the end + srsran_vec_prod_ccc(&ssb_grid[grid_idx_m1_1], &sss_nr_d1[m1], &ssb_grid[grid_idx_m1_1], SRSRAN_SSS_NR_LEN - m1); + + // Multiply d1 sequence second part from 0 to m1 + srsran_vec_prod_ccc(&ssb_grid[grid_idx_m1_2], &sss_nr_d1[0], &ssb_grid[grid_idx_m1_2], m1); + + return SRSRAN_SUCCESS; +} + +int srsran_sss_nr_extract_lse(const cf_t* ssb_grid, uint32_t N_id_1, uint32_t N_id_2, cf_t lse[SRSRAN_SSS_NR_LEN]) +{ + // Verify inputs + if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR || lse == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Extract SSS + srsran_vec_cf_copy( + lse, &ssb_grid[SRSRAN_SSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + SSS_NR_SUBC_BEGIN], SRSRAN_SSS_NR_LEN); + + // Estimate + uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1, N_id_2); + srsran_vec_prod_ccc(&sss_nr_d0[m0], lse, lse, SRSRAN_SSS_NR_LEN - m0); + srsran_vec_prod_ccc(&sss_nr_d0[0], &lse[SRSRAN_SSS_NR_LEN - m0], &lse[SRSRAN_SSS_NR_LEN - m0], m0); + + uint32_t m1 = SSS_NR_SEQUENCE_M1(N_id_1); + srsran_vec_prod_ccc(&sss_nr_d1[m1], lse, lse, SRSRAN_SSS_NR_LEN - m1); + srsran_vec_prod_ccc(&sss_nr_d1[0], &lse[SRSRAN_SSS_NR_LEN - m1], &lse[SRSRAN_SSS_NR_LEN - m1], m1); + + return SRSRAN_SUCCESS; +} + +int srsran_sss_nr_find(const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + uint32_t N_id_2, + float* norm_corr, + uint32_t* found_N_id_1) +{ + // Verify inputs + if (ssb_grid == NULL || N_id_2 >= SRSRAN_NOF_NID_2_NR || norm_corr == NULL || found_N_id_1 == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Extract SSS ptr + const cf_t* sss_ptr = &ssb_grid[SRSRAN_SSS_NR_SYMBOL_IDX * SRSRAN_SSB_BW_SUBC + SSS_NR_SUBC_BEGIN]; + + // Measure SSS average power + float avg_power = srsran_vec_avg_power_cf(sss_ptr, SRSRAN_SSS_NR_LEN); + + // If the measured power is invalid or zero, consider no SSS signal + if (!isnormal(avg_power)) { + *norm_corr = 0.0f; + *found_N_id_1 = 0; + return SRSRAN_SUCCESS; + } + + // Search state + float max_corr = -INFINITY; //< Stores best correlation + uint32_t N_id_1 = 0; //< Best N_id_1 + + // Iterate over all M1 shifts + for (uint32_t m1 = 0; m1 < SSS_NR_NOF_M1; m1++) { + // Temporal storage of SSS after applying d1 sequence + cf_t sss_seq_m1[SRSRAN_SSS_NR_LEN]; + + // Apply d1 sequence fist part + srsran_vec_prod_ccc(&sss_ptr[0], &sss_nr_d1[m1], &sss_seq_m1[0], SRSRAN_SSS_NR_LEN - m1); + + // Apply d1 sequence second part + srsran_vec_prod_ccc(&sss_ptr[SRSRAN_SSS_NR_LEN - m1], &sss_nr_d1[0], &sss_seq_m1[SRSRAN_SSS_NR_LEN - m1], m1); + + // Iterate over all N_id_1 with the given m1 sequence + for (uint32_t N_id_1_blind = m1; N_id_1_blind < SRSRAN_NOF_NID_1_NR; N_id_1_blind += SSS_NR_NOF_M1) { + uint32_t m0 = SSS_NR_SEQUENCE_M0(N_id_1_blind, N_id_2); + cf_t acc = 0.0f; + + // Correlate d0 sequence fist part + acc += srsran_vec_dot_prod_ccc(&sss_seq_m1[0], &sss_nr_d0[m0], SRSRAN_SSS_NR_LEN - m0); + + // Correlate d0 sequence second part + acc += srsran_vec_dot_prod_ccc(&sss_seq_m1[SRSRAN_SSS_NR_LEN - m0], &sss_nr_d0[0], m0); + + // Normalise + float corr = SRSRAN_CSQABS(acc); + if (corr > max_corr) { + N_id_1 = N_id_1_blind; + max_corr = corr; + } + } + } + + // Copy found result + *norm_corr = max_corr / avg_power / SRSRAN_SSS_NR_LEN; + *found_N_id_1 = N_id_1; + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/sync/ssss.c b/lib/src/phy/sync/ssss.c index 88427a69b..68963c3f2 100644 --- a/lib/src/phy/sync/ssss.c +++ b/lib/src/phy/sync/ssss.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -266,8 +266,10 @@ int srsran_ssss_find(srsran_ssss_t* q, cf_t* input, uint32_t nof_prb, uint32_t N float peak_1_value = q->shifted_output_abs[peak_1_pos]; if ((peak_1_pos >= (q->corr_peak_pos - (symbol_sz + cp_len) - 2)) && (peak_1_pos <= (q->corr_peak_pos - (symbol_sz + cp_len) + 2))) { + // Skip. } else if ((peak_1_pos >= (q->corr_peak_pos + (symbol_sz + cp_len) - 2)) && (peak_1_pos <= (q->corr_peak_pos + (symbol_sz + cp_len) + 2))) { + // Skip. } else { q->corr_peak_pos = -1; continue; @@ -279,8 +281,10 @@ int srsran_ssss_find(srsran_ssss_t* q, cf_t* input, uint32_t nof_prb, uint32_t N float peak_2_value = q->shifted_output_abs[peak_2_pos]; if ((peak_2_pos >= (q->corr_peak_pos - (symbol_sz + cp_len) - 2)) && (peak_2_pos <= (q->corr_peak_pos - (symbol_sz + cp_len) + 2))) { + // Skip. } else if ((peak_2_pos >= (q->corr_peak_pos + (symbol_sz + cp_len) - 2)) && (peak_2_pos <= (q->corr_peak_pos + (symbol_sz + cp_len) + 2))) { + // Skip. } else { q->corr_peak_pos = -1; continue; diff --git a/lib/src/phy/sync/sync.c b/lib/src/phy/sync/sync.c index 022185cb1..ff5f55105 100644 --- a/lib/src/phy/sync/sync.c +++ b/lib/src/phy/sync/sync.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -330,7 +330,7 @@ int srsran_sync_set_N_id_1(srsran_sync_t* q, uint32_t N_id_1) generate_freq_sss(q, N_id_1); return SRSRAN_SUCCESS; } else { - ERROR("Invalid N_id_2=%d", N_id_1); + ERROR("Invalid N_id_1=%d", N_id_1); return SRSRAN_ERROR_INVALID_INPUTS; } } diff --git a/lib/src/phy/sync/sync_nbiot.c b/lib/src/phy/sync/sync_nbiot.c index 603c8c585..f080d541a 100644 --- a/lib/src/phy/sync/sync_nbiot.c +++ b/lib/src/phy/sync/sync_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/test/CMakeLists.txt b/lib/src/phy/sync/test/CMakeLists.txt index 7ef124c70..ee5f0e463 100644 --- a/lib/src/phy/sync/test/CMakeLists.txt +++ b/lib/src/phy/sync/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -95,13 +95,13 @@ add_executable(psss_file_test psss_file_test.c) target_link_libraries(psss_file_test srsran_phy) # SL TM 2 -add_test(sync_sl_test_tm2_p6_c_0 sync_sl_test -p 6 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat) -add_test(sync_sl_test_tm2_p15_c_84 sync_sl_test -p 15 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat) -add_test(sync_sl_test_tm2_p25_c_168 sync_sl_test -p 25 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) -add_test(sync_sl_test_tm2_p50_c_252 sync_sl_test -p 50 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat) -add_test(sync_sl_test_tm2_p100_c_335 sync_sl_test -p 100 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) +add_test(sync_sl_test_tm2_p6_c_0 sync_sl_test -p 6 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p6_c0_s1.92e6.dat) +add_test(sync_sl_test_tm2_p15_c_84 sync_sl_test -p 15 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p15_c84_s3.84e6.dat) +add_test(sync_sl_test_tm2_p25_c_168 sync_sl_test -p 25 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) +add_test(sync_sl_test_tm2_p50_c_252 sync_sl_test -p 50 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p50_c252_s15.36e6.dat) +add_test(sync_sl_test_tm2_p100_c_335 sync_sl_test -p 100 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p100_c335_s30.72e6.dat) # Sample offset -add_test(sync_sl_test_tm2_p25_c_168_so sync_sl_test -p 25 -d -o 300 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) +add_test(sync_sl_test_tm2_p25_c_168_so sync_sl_test -p 25 -d -o 300 -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm2_p25_c168_s7.68e6.dat) # Self-test add_test(sync_sl_test_tm2_self_test_p25_c_168 sync_sl_test -p 25 -c 168 -d) # Self-test with frequency offset @@ -110,13 +110,13 @@ add_test(sync_sl_test_tm2_self_test_p25_c_168_fo sync_sl_test -p 25 -c 168 -d -f add_test(sync_sl_test_tm2_self_test_p25_c_168_fo_so sync_sl_test -p 25 -c 168 -d -f 100 -o 3600) # SL TM 4 -add_test(sync_sl_test_tm4_p6_c_0 sync_sl_test -p 6 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p6_c0_size6_num1_cshift0_s1.92e6.dat) -add_test(sync_sl_test_tm4_p15_c_84 sync_sl_test -p 15 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p15_c84_size5_num3_cshift0_s3.84e6.dat) -add_test(sync_sl_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat) -add_test(sync_sl_test_tm4_p50_c_252 sync_sl_test -p 50 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p50_c252_size10_num5_cshift0_s15.36e6.dat) -#add_test(sync_sl_test_tm4_p100_c_335 sync_sl_test -p 100 -t 4 -d -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) +add_test(sync_sl_test_tm4_p6_c_0 sync_sl_test -p 6 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p6_c0_size6_num1_cshift0_s1.92e6.dat) +add_test(sync_sl_test_tm4_p15_c_84 sync_sl_test -p 15 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p15_c84_size5_num3_cshift0_s3.84e6.dat) +add_test(sync_sl_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat) +add_test(sync_sl_test_tm4_p50_c_252 sync_sl_test -p 50 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p50_c252_size10_num5_cshift0_s15.36e6.dat) +#add_test(sync_sl_test_tm4_p100_c_335 sync_sl_test -p 100 -t 4 -d -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p100_c335_size10_num10_cshift0_s30.72e6.dat) # Sample offset -add_test(sync_sl_test_tm4_p25_c_168_so sync_sl_test -p 25 -t 4 -d -o 300 -i ${CMAKE_HOME_DIRECTORY}/lib/src/phy/phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat ) +add_test(sync_sl_test_tm4_p25_c_168_so sync_sl_test -p 25 -t 4 -d -o 300 -i ${CMAKE_CURRENT_SOURCE_DIR}/../../phch/test/signal_sidelink_ideal_tm4_p25_c168_size5_num5_cshift0_s7.68e6.dat ) # Self-test add_test(sync_sl_test_self_test_tm4_p25_c_168 sync_sl_test -p 25 -t 4 -c 168 -d) # Self-test with frequency offset @@ -133,3 +133,56 @@ target_link_libraries(cfo_test srsran_phy) add_test(cfo_test_1 cfo_test -f 0.12345 -n 1000) add_test(cfo_test_2 cfo_test -f 0.99849 -n 1000) + + +######################################################################## +# NR TEST +######################################################################## + +add_executable(ssb_measure_test ssb_measure_test.c) +target_link_libraries(ssb_measure_test srsran_phy) + +add_executable(ssb_decode_test ssb_decode_test.c) +target_link_libraries(ssb_decode_test srsran_phy) + +add_executable(ssb_grid_test ssb_grid_test.c) +target_link_libraries(ssb_grid_test srsran_phy) + +# For 1.0 GHz and 3.5 GHz Center frequencies +foreach (CELL_FREQ 1000000 3500000) + # For each supported Cell/Carrier subcarrier spacing + foreach (CELL_SCS 15 30) + # For SSB centered at -960, 0 and 960 kHz from the center frequency + foreach (SSB_OFFSET_FREQ -960 +0 +960) + # For each supported SSB subcarrier spacing + foreach (SSB_SCS 15 30) + # For patterns A, B, C + foreach (SSB_PATTERN A B C) + # Calculate Actual SSB center frequency + math(EXPR SSB_FREQ "${CELL_FREQ}${SSB_OFFSET_FREQ}") + + # Test SSB measurements + add_nr_test(ssb_measure_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_measure_test + -F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN}) + + # Test SSB PBCH decoding + add_nr_test(ssb_decode_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_decode_test + -F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN}) + + # Test SSB grid put/get decoding + add_nr_test(ssb_grid_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_grid_test + -F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN}) + endforeach () + endforeach () + endforeach () + endforeach () +endforeach () + +add_executable(ssb_file_test ssb_file_test.c) +target_link_libraries(ssb_file_test srsran_phy) + +# File test 1 +# Captured with command: lib/examples/usrp_capture -a type=x300,clock=external,sampling_rate=46.08e6,rx_subdev_spec=B:0 -g 20 -r 46.08e6 -n 460800 -f 3502.8e6 -o /tmp/n78.fo35028.fs2304M.data +add_nr_test(ssb_file_test_tdd ssb_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/n78.fo35028.fs4608M.data -v -r 46.08e6 -f 3502.8e6 -F 3512.64e6 -n 460800 -A 500 357802 2 0 1 0) +# Capture with third-party gNB on band n3 (FDD) 15kHz SSB SCS, f_s=15.36e6, f_c=1842.5e6, f_c_ssb=1842.05e6, PCI=500 +add_nr_test(ssb_file_test_fdd ssb_file_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../../ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx6_s15.36e6.dat -v -r 15.36e6 -f 1842.5e6 -F 1842.05e6 -n 15360 -d fdd -s 15 -A 500 2200 0 0 0 0) diff --git a/lib/src/phy/sync/test/cfo_test.c b/lib/src/phy/sync/test/cfo_test.c index 446bdc820..cc0b6db12 100644 --- a/lib/src/phy/sync/test/cfo_test.c +++ b/lib/src/phy/sync/test/cfo_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/sync/test/n78.fo35028.fs4608M.data b/lib/src/phy/sync/test/n78.fo35028.fs4608M.data new file mode 100644 index 000000000..874cae656 Binary files /dev/null and b/lib/src/phy/sync/test/n78.fo35028.fs4608M.data differ diff --git a/lib/src/phy/sync/test/n78.fo367536.fs6144M.data b/lib/src/phy/sync/test/n78.fo367536.fs6144M.data new file mode 100644 index 000000000..c2ec51243 Binary files /dev/null and b/lib/src/phy/sync/test/n78.fo367536.fs6144M.data differ diff --git a/lib/src/phy/sync/test/npss_file.c b/lib/src/phy/sync/test/npss_file.c index b391e0c5d..b30170fcc 100644 --- a/lib/src/phy/sync/test/npss_file.c +++ b/lib/src/phy/sync/test/npss_file.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -90,7 +90,7 @@ void parse_args(int argc, char** argv) nof_frames = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/sync/test/npss_test.c b/lib/src/phy/sync/test/npss_test.c index 0bbd84ec7..71c281948 100644 --- a/lib/src/phy/sync/test/npss_test.c +++ b/lib/src/phy/sync/test/npss_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -54,7 +54,7 @@ void parse_args(int argc, char** argv) input_len = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose = SRSRAN_VERBOSE_DEBUG; + set_srsran_verbose_level(SRSRAN_VERBOSE_DEBUG); break; default: usage(argv[0]); diff --git a/lib/src/phy/sync/test/npss_usrp.c b/lib/src/phy/sync/test/npss_usrp.c index f0e60d74c..130b95e32 100644 --- a/lib/src/phy/sync/test/npss_usrp.c +++ b/lib/src/phy/sync/test/npss_usrp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -109,7 +109,7 @@ void parse_args(int argc, char** argv) disable_plots = true; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/sync/test/nsss_test.c b/lib/src/phy/sync/test/nsss_test.c index 9f1f5fb55..84bbcad28 100644 --- a/lib/src/phy/sync/test/nsss_test.c +++ b/lib/src/phy/sync/test/nsss_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -75,7 +75,7 @@ void parse_args(int argc, char** argv) max_num_sf = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose = SRSRAN_VERBOSE_DEBUG; + set_srsran_verbose_level(SRSRAN_VERBOSE_DEBUG); break; default: usage(argv[0]); @@ -224,7 +224,6 @@ exit: free(fft_buffer); } - return ret; } diff --git a/lib/src/phy/sync/test/nsss_usrp.c b/lib/src/phy/sync/test/nsss_usrp.c index e854a808d..1cd0baef8 100644 --- a/lib/src/phy/sync/test/nsss_usrp.c +++ b/lib/src/phy/sync/test/nsss_usrp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -93,7 +93,7 @@ void parse_args(int argc, char** argv) nof_frames = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/sync/test/pss_file.c b/lib/src/phy/sync/test/pss_file.c index 49ed7ceac..7480d474b 100644 --- a/lib/src/phy/sync/test/pss_file.c +++ b/lib/src/phy/sync/test/pss_file.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -98,7 +98,7 @@ void parse_args(int argc, char** argv) disable_plots = true; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/sync/test/pss_usrp.c b/lib/src/phy/sync/test/pss_usrp.c index 9aaad6a2c..86c603337 100644 --- a/lib/src/phy/sync/test/pss_usrp.c +++ b/lib/src/phy/sync/test/pss_usrp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -107,7 +107,7 @@ void parse_args(int argc, char** argv) disable_plots = true; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/sync/test/psss_file_test.c b/lib/src/phy/sync/test/psss_file_test.c index 152742b64..cf339f9f2 100644 --- a/lib/src/phy/sync/test/psss_file_test.c +++ b/lib/src/phy/sync/test/psss_file_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,7 +33,7 @@ #include "srsran/phy/sync/ssss.h" #include "srsran/srsran.h" -char* input_file_name; +char* input_file_name; float frequency_offset = 0.0; float snr = 100.0; srsran_cp_t cp = SRSRAN_CP_NORM; @@ -96,7 +96,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -107,27 +107,41 @@ void parse_args(int argc, char** argv) int main(int argc, char** argv) { + int ret = SRSRAN_ERROR; parse_args(argc, argv); srsran_use_standard_symbol_size(use_standard_lte_rates); - if (!input_file_name || srsran_filesource_init(&fsrc, input_file_name, SRSRAN_COMPLEX_FLOAT_BIN)) { - printf("Error opening file %s\n", input_file_name); - return SRSRAN_ERROR; + // Init buffers + cf_t* input_buffer = NULL; + cf_t* input_buffer_temp = NULL; + cf_t* sf_buffer = NULL; + + // Init PSSS + srsran_psss_t psss = {}; + if (srsran_psss_init(&psss, nof_prb, cp) < SRSRAN_SUCCESS) { + ERROR("Error initialising the PSSS"); + goto clean_exit; } - // alloc memory + if (!input_file_name || srsran_filesource_init(&fsrc, input_file_name, SRSRAN_COMPLEX_FLOAT_BIN)) { + printf("Error opening file %s\n", input_file_name); + goto clean_exit; + } + + // Allocate memory uint32_t sf_n_samples = SRSRAN_SF_LEN_PRB(nof_prb); printf("I/Q samples per subframe=%d\n", sf_n_samples); - uint32_t sf_n_re = SRSRAN_CP_NSYMB(SRSRAN_CP_NORM) * SRSRAN_NRE * 2 * nof_prb; - cf_t* sf_buffer = srsran_vec_cf_malloc(sf_n_re); + uint32_t sf_n_re = SRSRAN_CP_NSYMB(SRSRAN_CP_NORM) * SRSRAN_NRE * 2 * nof_prb; + sf_buffer = srsran_vec_cf_malloc(sf_n_re); - cf_t* input_buffer = srsran_vec_cf_malloc(sf_n_samples); - cf_t* input_buffer_temp = srsran_vec_cf_malloc(sf_n_samples); + input_buffer = srsran_vec_cf_malloc(sf_n_samples); + input_buffer_temp = srsran_vec_cf_malloc(sf_n_samples); - // init PSSS - srsran_psss_t psss = {}; - srsran_psss_init(&psss, nof_prb, cp); + if (input_buffer == NULL || input_buffer_temp == NULL || sf_buffer == NULL) { + ERROR("Error allocating buffers"); + goto clean_exit; + } struct timeval t[3]; gettimeofday(&t[1], NULL); @@ -144,7 +158,7 @@ int main(int argc, char** argv) break; } else if (samples_read != sf_n_samples) { printf("Could only read %d of %d requested samples\n", samples_read, sf_n_samples); - return SRSRAN_ERROR; + goto clean_exit; } // Find PSSS signal @@ -158,11 +172,21 @@ int main(int argc, char** argv) num_subframes++; } while (samples_read == sf_n_samples && num_subframes < max_subframes); + ret = (sync == SRSRAN_SUCCESS); + +clean_exit: srsran_filesource_free(&fsrc); srsran_psss_free(&psss); - free(input_buffer); - free(input_buffer_temp); - free(sf_buffer); - return (sync == SRSRAN_SUCCESS); + if (input_buffer != NULL) { + free(input_buffer); + } + if (input_buffer_temp != NULL) { + free(input_buffer_temp); + } + if (sf_buffer != NULL) { + free(sf_buffer); + } + + return ret; } diff --git a/lib/src/phy/sync/test/ssb_decode_test.c b/lib/src/phy/sync/test/ssb_decode_test.c new file mode 100644 index 000000000..058eab302 --- /dev/null +++ b/lib/src/phy/sync/test/ssb_decode_test.c @@ -0,0 +1,357 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/phy/channel/ch_awgn.h" +#include "srsran/phy/sync/ssb.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" +#include +#include +#include +#include + +#define SSB_DECODE_TEST_PCI_STRIDE 53 +#define SSB_DECODE_TEST_SSB_STRIDE 3 + +// NR parameters +static uint32_t carrier_nof_prb = 52; +static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; +static double carrier_freq_hz = 3.5e9 + 960e3; +static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; +static double ssb_freq_hz = 3.5e9; +static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; + +// Channel parameters +static cf_t wideband_gain = 1.0f + 0.5 * I; +static int32_t delay_n = 1; +static float cfo_hz = 1000.0f; +static float n0_dB = -10.0f; + +// Test context +static srsran_random_t random_gen = NULL; +static srsran_channel_awgn_t awgn = {}; +static double srate_hz = 0.0f; // Base-band sampling rate +static uint32_t hf_len = 0; // Half-frame length +static cf_t* buffer = NULL; // Base-band buffer + +static void usage(char* prog) +{ + printf("Usage: %s [v]\n", prog); + printf("\t-s SSB subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(ssb_scs)); + printf("\t-f SSB center frequency [default, %.3f MHz]\n", ssb_freq_hz / 1e6); + printf("\t-S cell/carrier subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(carrier_scs)); + printf("\t-F cell/carrier center frequency in Hz [default, %.3f MHz]\n", carrier_freq_hz / 1e6); + printf("\t-P SSB pattern [default, %s]\n", srsran_ssb_pattern_to_str(ssb_pattern)); + printf("\t-v [set srsran_verbose to debug, default none]\n"); +} + +static void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "SsFfPv")) != -1) { + switch (opt) { + case 's': + ssb_scs = srsran_subcarrier_spacing_from_str(argv[optind]); + if (ssb_scs == srsran_subcarrier_spacing_invalid) { + ERROR("Invalid SSB subcarrier spacing %s\n", argv[optind]); + exit(-1); + } + break; + case 'f': + ssb_freq_hz = strtod(argv[optind], NULL); + break; + case 'S': + carrier_scs = srsran_subcarrier_spacing_from_str(argv[optind]); + if (carrier_scs == srsran_subcarrier_spacing_invalid) { + ERROR("Invalid Cell/Carrier subcarrier spacing %s\n", argv[optind]); + exit(-1); + } + break; + case 'F': + carrier_freq_hz = strtod(argv[optind], NULL); + break; + case 'P': + ssb_pattern = srsran_ssb_pattern_fom_str(argv[optind]); + break; + case 'v': + increase_srsran_verbose_level(); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +static void run_channel() +{ + // Delay + for (uint32_t i = 0; i < hf_len; i++) { + buffer[i] = buffer[(i + delay_n) % hf_len]; + } + + // CFO + srsran_vec_apply_cfo(buffer, -cfo_hz / srate_hz, buffer, hf_len); + + // AWGN + srsran_channel_awgn_run_c(&awgn, buffer, buffer, hf_len); + + // Wideband gain + srsran_vec_sc_prod_ccc(buffer, wideband_gain, buffer, hf_len); +} + +static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx) +{ + // Default all to zero + SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1); + + // Generate payload + srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_MSG_NR_SZ); + + pbch_msg->ssb_idx = ssb_idx; + pbch_msg->crc = true; +} + +static int test_case_true(srsran_ssb_t* ssb) +{ + // For benchmarking purposes + uint64_t t_encode_usec = 0; + uint64_t t_decode_usec = 0; + uint64_t t_search_usec = 0; + + // SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.center_freq_hz = carrier_freq_hz; + ssb_cfg.ssb_freq_hz = ssb_freq_hz; + ssb_cfg.scs = ssb_scs; + ssb_cfg.pattern = ssb_pattern; + + TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); + + // For each PCI... + uint64_t count = 0; + for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += SSB_DECODE_TEST_PCI_STRIDE) { + for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx += SSB_DECODE_TEST_SSB_STRIDE, count++) { + struct timeval t[3] = {}; + + // Build PBCH message + srsran_pbch_msg_nr_t pbch_msg_tx = {}; + gen_pbch_msg(&pbch_msg_tx, ssb_idx); + + // Print encoded PBCH message + char str[512] = {}; + srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str)); + INFO("test_case_true - encoded pci=%d %s", pci, str); + + // Initialise baseband + srsran_vec_cf_zero(buffer, hf_len); + + // Add the SSB base-band + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg_tx, buffer, buffer) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_encode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Run channel + run_channel(); + + // Decode + gettimeofday(&t[1], NULL); + srsran_pbch_msg_nr_t pbch_msg_rx = {}; + TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, pbch_msg_tx.hrf, pbch_msg_tx.ssb_idx, buffer, &pbch_msg_rx) == + SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Print decoded PBCH message + srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str)); + INFO("test_case_true - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO"); + + // Assert PBCH message CRC + TESTASSERT(pbch_msg_rx.crc); + TESTASSERT(memcmp(&pbch_msg_rx, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0); + + // Search + srsran_ssb_search_res_t res = {}; + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_ssb_search(ssb, buffer, hf_len, &res) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_search_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Print decoded PBCH message + srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str)); + INFO("test_case_true - found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO"); + + // Assert PBCH message CRC + TESTASSERT(res.pbch_msg.crc); + TESTASSERT(memcmp(&res.pbch_msg, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0); + } + } + + if (!count) { + ERROR("Error in test case true: undefined division"); + return SRSRAN_ERROR; + } + + INFO("test_case_true - %.1f usec/encode; %.1f usec/decode; %.1f usec/decode;", + (double)t_encode_usec / (double)(count), + (double)t_decode_usec / (double)(count), + (double)t_search_usec / (double)(count)); + + return SRSRAN_SUCCESS; +} + +static int test_case_false(srsran_ssb_t* ssb) +{ + // For benchmarking purposes + uint64_t t_decode_usec = 0; + uint64_t t_search_usec = 0; + + // SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.center_freq_hz = carrier_freq_hz; + ssb_cfg.ssb_freq_hz = ssb_freq_hz; + ssb_cfg.scs = ssb_scs; + ssb_cfg.pattern = ssb_pattern; + + TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); + + // For each PCI... + uint32_t count = 0; + for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += SSB_DECODE_TEST_PCI_STRIDE, count++) { + struct timeval t[3] = {}; + + // Initialise baseband + srsran_vec_cf_zero(buffer, hf_len); + + // Channel, as it is zero it only adds noise + srsran_channel_awgn_run_c(&awgn, buffer, buffer, hf_len); + + // Decode + gettimeofday(&t[1], NULL); + srsran_pbch_msg_nr_t pbch_msg_rx = {}; + TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, false, 0, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Print decoded PBCH message + char str[512] = {}; + srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str)); + INFO("test_case_false - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO"); + + // Assert PBCH message CRC is not okay + TESTASSERT(!pbch_msg_rx.crc); + + // Search + srsran_ssb_search_res_t res = {}; + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_ssb_search(ssb, buffer, hf_len, &res) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_search_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Print decoded PBCH message + srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str)); + INFO("test_case_false - false found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO"); + + // Assert PBCH message CRC + TESTASSERT(!res.pbch_msg.crc); + } + + if (!count) { + ERROR("Error in test case true: undefined division"); + return SRSRAN_ERROR; + } + + INFO("test_case_false - %.1f usec/decode; %.1f usec/decode;", + (double)t_decode_usec / (double)(count), + (double)t_search_usec / (double)(count)); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + parse_args(argc, argv); + + random_gen = srsran_random_init(1234); + srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(carrier_nof_prb); + hf_len = (uint32_t)ceil(srate_hz * (5.0 / 1000.0)); + buffer = srsran_vec_cf_malloc(hf_len); + + srsran_ssb_t ssb = {}; + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_encode = true; + ssb_args.enable_decode = true; + ssb_args.enable_search = true; + + if (buffer == NULL) { + ERROR("Malloc"); + goto clean_exit; + } + + if (srsran_channel_awgn_init(&awgn, 0x0) < SRSRAN_SUCCESS) { + ERROR("AWGN"); + goto clean_exit; + } + + if (srsran_channel_awgn_set_n0(&awgn, n0_dB) < SRSRAN_SUCCESS) { + ERROR("AWGN"); + goto clean_exit; + } + + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + ERROR("Init"); + goto clean_exit; + } + + if (test_case_true(&ssb) != SRSRAN_SUCCESS) { + ERROR("test case failed"); + goto clean_exit; + } + + if (test_case_false(&ssb) != SRSRAN_SUCCESS) { + ERROR("test case failed"); + goto clean_exit; + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + srsran_random_free(random_gen); + srsran_ssb_free(&ssb); + + srsran_channel_awgn_free(&awgn); + + if (buffer) { + free(buffer); + } + + return ret; +} \ No newline at end of file diff --git a/lib/src/phy/sync/test/ssb_file_test.c b/lib/src/phy/sync/test/ssb_file_test.c new file mode 100644 index 000000000..9d002bfae --- /dev/null +++ b/lib/src/phy/sync/test/ssb_file_test.c @@ -0,0 +1,257 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/phy/io/filesource.h" +#include "srsran/phy/sync/ssb.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" +#include +#include +#include + +// NR parameters +static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_C; +static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; +static srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_TDD; + +// Test context +static char* filename = NULL; +static double srate_hz = 23.04e6; // Base-band sampling rate in Hz +static double center_freq_hz = NAN; // Center frequency in Hz +static double ssb_freq_hz = NAN; // SSB frequency in Hz +static uint32_t nof_samples = 0; // Number of samples + +// Assertion +static bool assert = false; +static uint32_t assert_pci = 0; +static uint32_t assert_t_offset = 0; +static uint32_t assert_sfn_lsb = 0; +static uint32_t assert_ssb_idx = 0; +static uint32_t assert_ssb_k = 0; +static uint32_t assert_hrf = 0; + +static void usage(char* prog) +{ + printf("Usage: %s -i filename [rv]\n", prog); + printf("\t-r sampling rate in Hz [Default %.2f MHz]\n", srate_hz / 1e6); + printf("\t-n number of samples [Default %d]\n", nof_samples); + printf("\t-s SSB subcarrier spacing (15, 30) [Default %s]\n", srsran_subcarrier_spacing_to_str(ssb_scs)); + printf("\t-d duplex mode [Default %s]\n", duplex_mode == SRSRAN_DUPLEX_MODE_FDD ? "FDD" : "TDD"); + printf("\t-f absolute baseband center frequency in Hz [Default %.2f MHz]\n", center_freq_hz / 1e3); + printf("\t-F absolute SSB center freuqency in Hz [Default %.2f MHz]\n", ssb_freq_hz / 1e3); + printf("\t-A Assert: PCI t_offset sfn_lsb ssb_idx ssb_k hrf"); + printf("\t-v [set srsran_verbose to debug, default none]\n"); +} + +static void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "insdrfFAv")) != -1) { + switch (opt) { + case 'i': + filename = argv[optind]; + break; + case 'n': + nof_samples = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 's': + if ((uint32_t)strtol(argv[optind], NULL, 10) == 15) { + ssb_scs = srsran_subcarrier_spacing_15kHz; + } else { + ssb_scs = srsran_subcarrier_spacing_30kHz; + } + break; + case 'd': + if (strcmp(argv[optind], "tdd") == 0) { + duplex_mode = SRSRAN_DUPLEX_MODE_TDD; + } else if (strcmp(argv[optind], "fdd") == 0) { + duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + } else { + printf("Invalid duplex mode '%s'\n", argv[optind]); + usage(argv[0]); + exit(-1); + } + break; + case 'r': + srate_hz = strtod(argv[optind], NULL); + break; + case 'f': + center_freq_hz = strtod(argv[optind], NULL); + break; + case 'F': + ssb_freq_hz = strtod(argv[optind], NULL); + break; + case 'A': + assert = true; + assert_pci = (uint32_t)strtol(argv[optind++], NULL, 10); + assert_t_offset = (uint32_t)strtol(argv[optind++], NULL, 10); + assert_sfn_lsb = (uint32_t)strtol(argv[optind++], NULL, 10); + assert_ssb_idx = (uint32_t)strtol(argv[optind++], NULL, 10); + assert_ssb_k = (uint32_t)strtol(argv[optind++], NULL, 10); + assert_hrf = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'v': + increase_srsran_verbose_level(); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +static int assert_meas(uint32_t N_id, const srsran_csi_trs_measurements_t* res) +{ + TESTASSERT(N_id == assert_pci); + return SRSRAN_SUCCESS; +} +static int assert_search(const srsran_ssb_search_res_t* res) +{ + TESTASSERT(res->N_id == assert_pci); + TESTASSERT(res->t_offset == assert_t_offset); + TESTASSERT(res->pbch_msg.sfn_4lsb == assert_sfn_lsb); + TESTASSERT(res->pbch_msg.ssb_idx == assert_ssb_idx); + TESTASSERT(res->pbch_msg.k_ssb_msb == assert_ssb_k); + TESTASSERT((res->pbch_msg.hrf ? 1 : 0) == assert_hrf); + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + srsran_filesource_t filesource = {}; + srsran_ssb_t ssb = {}; + int ret = SRSRAN_ERROR; + parse_args(argc, argv); + + if (nof_samples == 0 || !isnormal(ssb_freq_hz) || !isnormal(center_freq_hz)) { + ERROR("Invalid arguments!"); + usage(argv[0]); + return SRSRAN_ERROR; + } + + cf_t* buffer = srsran_vec_cf_malloc(nof_samples); + if (buffer == NULL) { + ERROR("Malloc"); + goto clean_exit; + } + + // Initialise SSB + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_decode = true; + ssb_args.enable_search = true; + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + ERROR("Init"); + goto clean_exit; + } + + // Configure SSB + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.center_freq_hz = center_freq_hz; + ssb_cfg.ssb_freq_hz = ssb_freq_hz; + ssb_cfg.scs = ssb_scs; + ssb_cfg.pattern = ssb_pattern; + ssb_cfg.duplex_mode = duplex_mode; + if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { + ERROR("Error setting SSB configuration"); + goto clean_exit; + } + + // Initialise file source + if (srsran_filesource_init(&filesource, filename, SRSRAN_COMPLEX_FLOAT_BIN) < SRSRAN_SUCCESS) { + ERROR("Error opening file"); + goto clean_exit; + } + + // Read baseband + if (srsran_filesource_read(&filesource, buffer, (int)nof_samples) < SRSRAN_SUCCESS) { + ERROR("Error reading from file"); + goto clean_exit; + } + + // Perform SSB-CSI Search + uint32_t N_id = 0; + srsran_csi_trs_measurements_t meas = {}; + if (srsran_ssb_csi_search(&ssb, buffer, nof_samples, &N_id, &meas) < SRSRAN_SUCCESS) { + ERROR("Error performing SSB-CSI search"); + goto clean_exit; + } + + // Print measurement + char str[512] = {}; + srsran_csi_meas_info(&meas, str, sizeof(str)); + INFO("measure - search pci=%d %s", N_id, str); + + // Assert measurement + if (assert) { + if (assert_meas(N_id, &meas)) { + ERROR("Error asserting search"); + goto clean_exit; + } + } + + // Perform SSB search + srsran_ssb_search_res_t search_res = {}; + if (srsran_ssb_search(&ssb, buffer, nof_samples, &search_res) < SRSRAN_SUCCESS) { + ERROR("Error performing SSB search"); + goto clean_exit; + } + + // Print decoded PBCH message + srsran_pbch_msg_info(&search_res.pbch_msg, str, sizeof(str)); + INFO("search - t_offset=%d pci=%d %s crc=%s", + search_res.t_offset, + search_res.N_id, + str, + search_res.pbch_msg.crc ? "OK" : "KO"); + + // unpack MIB + srsran_mib_nr_t mib = {}; + if (srsran_pbch_msg_nr_mib_unpack(&search_res.pbch_msg, &mib) < SRSRAN_SUCCESS) { + ERROR("Error unpacking PBCH-MIB"); + goto clean_exit; + } + + char mib_info[512] = {}; + srsran_pbch_msg_nr_mib_info(&mib, mib_info, sizeof(mib_info)); + INFO("PBCH-MIB: %s", mib_info); + + // Assert search + if (assert) { + if (assert_search(&search_res)) { + ERROR("Error asserting search"); + goto clean_exit; + } + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + srsran_ssb_free(&ssb); + srsran_filesource_free(&filesource); + + if (buffer) { + free(buffer); + } + + return ret; +} \ No newline at end of file diff --git a/lib/src/phy/sync/test/ssb_grid_test.c b/lib/src/phy/sync/test/ssb_grid_test.c new file mode 100644 index 000000000..09194775d --- /dev/null +++ b/lib/src/phy/sync/test/ssb_grid_test.c @@ -0,0 +1,189 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/phy/channel/ch_awgn.h" +#include "srsran/phy/sync/ssb.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" +#include +#include +#include +#include + +// NR parameters +static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; +static double carrier_freq_hz = 3.5e9 + 960e3; +static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; +static double ssb_freq_hz = 3.5e9; +static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; +static uint32_t ssb_idx = 0; // SSB candidate index to test +static uint32_t pci = 123; // N_id + +// Test context +static srsran_random_t random_gen = NULL; +static double srate_hz = 0.0f; // Base-band sampling rate +static cf_t* grid = NULL; // Resource grid +static uint32_t grid_bw_sc = 52 * SRSRAN_NRE; // Resource grid bandwidth in subcarriers + +static void usage(char* prog) +{ + printf("Usage: %s [v]\n", prog); + printf("\t-s SSB subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(ssb_scs)); + printf("\t-f SSB center frequency [default, %.3f MHz]\n", ssb_freq_hz / 1e6); + printf("\t-S cell/carrier subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(carrier_scs)); + printf("\t-F cell/carrier center frequency in Hz [default, %.3f MHz]\n", carrier_freq_hz / 1e6); + printf("\t-P SSB pattern [default, %s]\n", srsran_ssb_pattern_to_str(ssb_pattern)); + printf("\t-v [set srsran_verbose to debug, default none]\n"); +} + +static void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "SsFfPv")) != -1) { + switch (opt) { + case 's': + ssb_scs = srsran_subcarrier_spacing_from_str(argv[optind]); + if (ssb_scs == srsran_subcarrier_spacing_invalid) { + ERROR("Invalid SSB subcarrier spacing %s\n", argv[optind]); + exit(-1); + } + break; + case 'f': + ssb_freq_hz = strtod(argv[optind], NULL); + break; + case 'S': + carrier_scs = srsran_subcarrier_spacing_from_str(argv[optind]); + if (carrier_scs == srsran_subcarrier_spacing_invalid) { + ERROR("Invalid Cell/Carrier subcarrier spacing %s\n", argv[optind]); + exit(-1); + } + break; + case 'F': + carrier_freq_hz = strtod(argv[optind], NULL); + break; + case 'P': + ssb_pattern = srsran_ssb_pattern_fom_str(argv[optind]); + break; + case 'v': + increase_srsran_verbose_level(); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg) +{ + // Default all to zero + SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1); + + // Generate payload + srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_MSG_NR_SZ); + + pbch_msg->ssb_idx = ssb_idx; + pbch_msg->crc = true; +} + +static int test_case(srsran_ssb_t* ssb) +{ + // SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.center_freq_hz = carrier_freq_hz; + ssb_cfg.ssb_freq_hz = ssb_freq_hz; + ssb_cfg.scs = ssb_scs; + ssb_cfg.pattern = ssb_pattern; + + TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); + + // Build PBCH message + srsran_pbch_msg_nr_t pbch_msg_tx = {}; + gen_pbch_msg(&pbch_msg_tx); + + // Print encoded PBCH message + char str[512] = {}; + srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str)); + INFO("test_case - encoded pci=%d %s", pci, str); + + // Add the SSB base-band + TESTASSERT(srsran_ssb_put_grid(ssb, pci, &pbch_msg_tx, grid, grid_bw_sc) == SRSRAN_SUCCESS); + + // Decode + srsran_pbch_msg_nr_t pbch_msg_rx = {}; + TESTASSERT(srsran_ssb_decode_grid(ssb, pci, pbch_msg_tx.hrf, pbch_msg_tx.ssb_idx, grid, grid_bw_sc, &pbch_msg_rx) == + SRSRAN_SUCCESS); + + // Print decoded PBCH message + srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str)); + INFO("test_case - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO"); + + // Assert PBCH message CRC + TESTASSERT(pbch_msg_rx.crc); + TESTASSERT(memcmp(&pbch_msg_rx, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + parse_args(argc, argv); + + random_gen = srsran_random_init(1234); + srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(grid_bw_sc / SRSRAN_NRE); + grid = srsran_vec_cf_malloc(grid_bw_sc * SRSRAN_NSYMB_PER_SLOT_NR); + + srsran_ssb_t ssb = {}; + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_encode = true; + ssb_args.enable_decode = true; + ssb_args.enable_search = true; + + if (grid == NULL) { + ERROR("Malloc"); + goto clean_exit; + } + + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + ERROR("Init"); + goto clean_exit; + } + + if (test_case(&ssb) != SRSRAN_SUCCESS) { + ERROR("test case failed"); + goto clean_exit; + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + srsran_random_free(random_gen); + srsran_ssb_free(&ssb); + + if (grid) { + free(grid); + } + + return ret; +} \ No newline at end of file diff --git a/lib/src/phy/sync/test/ssb_measure_test.c b/lib/src/phy/sync/test/ssb_measure_test.c new file mode 100644 index 000000000..5687dec83 --- /dev/null +++ b/lib/src/phy/sync/test/ssb_measure_test.c @@ -0,0 +1,262 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/phy/channel/ch_awgn.h" +#include "srsran/phy/sync/ssb.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/vector.h" +#include +#include + +// NR parameters +static uint32_t carrier_nof_prb = 52; +static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; +static double carrier_freq_hz = 3.5e9 + 960e3; +static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; +static double ssb_freq_hz = 3.5e9; +static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; + +// Channel parameters +static int32_t delay_n = 2; +static float cfo_hz = 100.0f; +static float n0_dB = -30.0f; + +// Test context +static srsran_channel_awgn_t awgn = {}; +static double srate_hz = 0.0f; // Base-band sampling rate +static float delay_us = 0.0f; // Base-band sampling rate +static uint32_t sf_len = 0; // Subframe length +static cf_t* buffer = NULL; // Base-band buffer + +#define RSRP_MAX_ERROR 1.0f +#define EPRE_MAX_ERROR 1.0f +#define N0_MAX_ERROR 3.0f +#define SNR_MAX_ERROR 3.0f +#define CFO_MAX_ERROR (cfo_hz * 0.3f) +#define DELAY_MAX_ERROR (delay_us * 0.1f) + +static void usage(char* prog) +{ + printf("Usage: %s [v]\n", prog); + printf("\t-s SSB subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(ssb_scs)); + printf("\t-f SSB center frequency [default, %.3f MHz]\n", ssb_freq_hz / 1e6); + printf("\t-S cell/carrier subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(carrier_scs)); + printf("\t-F cell/carrier center frequency in Hz [default, %.3f MHz]\n", carrier_freq_hz / 1e6); + printf("\t-P SSB pattern [default, %s]\n", srsran_ssb_pattern_to_str(ssb_pattern)); + printf("\t-v [set srsran_verbose to debug, default none]\n"); +} + + +static void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "SsFfPv")) != -1) { + switch (opt) { + case 's': + ssb_scs = srsran_subcarrier_spacing_from_str(argv[optind]); + if (ssb_scs == srsran_subcarrier_spacing_invalid) { + ERROR("Invalid SSB subcarrier spacing %s\n", argv[optind]); + exit(-1); + } + break; + case 'f': + ssb_freq_hz = strtod(argv[optind], NULL); + break; + case 'S': + carrier_scs = srsran_subcarrier_spacing_from_str(argv[optind]); + if (carrier_scs == srsran_subcarrier_spacing_invalid) { + ERROR("Invalid Cell/Carrier subcarrier spacing %s\n", argv[optind]); + exit(-1); + } + break; + case 'F': + carrier_freq_hz = strtod(argv[optind], NULL); + break; + case 'P': + ssb_pattern = srsran_ssb_pattern_fom_str(argv[optind]); + break; + case 'v': + increase_srsran_verbose_level(); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +static void run_channel() +{ + // Delay + for (uint32_t i = 0; i < sf_len; i++) { + buffer[i] = buffer[(i + delay_n) % sf_len]; + } + + // CFO + srsran_vec_apply_cfo(buffer, cfo_hz / srate_hz, buffer, sf_len); + + // AWGN + srsran_channel_awgn_run_c(&awgn, buffer, buffer, sf_len); +} + +static int assert_measure(const srsran_csi_trs_measurements_t* meas) +{ + TESTASSERT(fabsf(meas->rsrp_dB - 0.0f) < RSRP_MAX_ERROR); + TESTASSERT(fabsf(meas->epre_dB - 0.0f) < EPRE_MAX_ERROR); + TESTASSERT(fabsf(meas->n0_dB - n0_dB) < N0_MAX_ERROR); + TESTASSERT(fabsf(meas->snr_dB + n0_dB) < SNR_MAX_ERROR); + TESTASSERT(fabsf(meas->cfo_hz - cfo_hz) < CFO_MAX_ERROR); + TESTASSERT(fabsf(meas->delay_us + delay_us) < DELAY_MAX_ERROR); + return SRSRAN_SUCCESS; +} + +static int test_case_1(srsran_ssb_t* ssb) +{ + // For benchmarking purposes + uint64_t t_add_usec = 0; + uint64_t t_find_usec = 0; + uint64_t t_meas_usec = 0; + + // SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.center_freq_hz = carrier_freq_hz; + ssb_cfg.ssb_freq_hz = ssb_freq_hz; + ssb_cfg.scs = ssb_scs; + ssb_cfg.pattern = ssb_pattern; + + TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); + + // Build PBCH message + srsran_pbch_msg_nr_t pbch_msg = {}; + + for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci++) { + struct timeval t[3] = {}; + + // Initialise baseband + srsran_vec_cf_zero(buffer, sf_len); + + // Add the SSB base-band + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_ssb_add(ssb, pci, &pbch_msg, buffer, buffer) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_add_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Run channel + run_channel(); + + // Find + gettimeofday(&t[1], NULL); + uint32_t N_id_found = 0; + srsran_csi_trs_measurements_t meas_search = {}; + TESTASSERT(srsran_ssb_csi_search(ssb, buffer, sf_len, &N_id_found, &meas_search) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_find_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Print measurement + char str[512] = {}; + srsran_csi_meas_info(&meas_search, str, sizeof(str)); + INFO("test_case_1 - search pci=%d %s", pci, str); + + // Assert find + TESTASSERT(N_id_found == pci); + + // Measure + gettimeofday(&t[1], NULL); + srsran_csi_trs_measurements_t meas = {}; + TESTASSERT(srsran_ssb_csi_measure(ssb, pci, 0, buffer, &meas) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_meas_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + srsran_csi_meas_info(&meas, str, sizeof(str)); + INFO("test_case_1 - measure pci=%d %s", pci, str); + + // Assert measurements + TESTASSERT(assert_measure(&meas) == SRSRAN_SUCCESS); + } + + INFO("test_case_1 - %.1f usec/encode; %.1f usec/search; Max srate %.1f MSps; %.1f usec/measurement", + (double)t_add_usec / (double)SRSRAN_NOF_NID_NR, + (double)t_find_usec / (double)SRSRAN_NOF_NID_NR, + (double)sf_len * (double)SRSRAN_NOF_NID_NR / (double)t_find_usec, + (double)t_meas_usec / (double)SRSRAN_NOF_NID_NR); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + parse_args(argc, argv); + + srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(carrier_nof_prb); + delay_us = 1e6f * delay_n / (float)srate_hz; + sf_len = (uint32_t)ceil(srate_hz / 1000.0); + buffer = srsran_vec_cf_malloc(sf_len); + + srsran_ssb_t ssb = {}; + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_encode = true; + ssb_args.enable_measure = true; + ssb_args.enable_search = true; + + if (buffer == NULL) { + ERROR("Malloc"); + goto clean_exit; + } + + if (srsran_channel_awgn_init(&awgn, 0x0) < SRSRAN_SUCCESS) { + ERROR("AWGN"); + goto clean_exit; + } + + if (srsran_channel_awgn_set_n0(&awgn, n0_dB) < SRSRAN_SUCCESS) { + ERROR("AWGN"); + goto clean_exit; + } + + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + ERROR("Init"); + goto clean_exit; + } + + if (test_case_1(&ssb) != SRSRAN_SUCCESS) { + ERROR("test case failed"); + goto clean_exit; + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + srsran_ssb_free(&ssb); + + srsran_channel_awgn_free(&awgn); + + if (buffer) { + free(buffer); + } + + return ret; +} \ No newline at end of file diff --git a/lib/src/phy/sync/test/sync_nbiot_test.c b/lib/src/phy/sync/test/sync_nbiot_test.c index 78ce0fad3..a783e3924 100644 --- a/lib/src/phy/sync/test/sync_nbiot_test.c +++ b/lib/src/phy/sync/test/sync_nbiot_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -72,7 +72,7 @@ void parse_args(int argc, char** argv) offset = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose = SRSRAN_VERBOSE_DEBUG; + set_srsran_verbose_level(SRSRAN_VERBOSE_DEBUG); break; default: usage(argv[0]); @@ -184,8 +184,8 @@ int main(int argc, char** argv) if (snr != -1.0) { snr -= 10.0; printf("Adding AWGN with target SNR: %.2fdB\n", snr); - float nstd = srsran_convert_dB_to_amplitude(-snr); - srsran_ch_awgn_c(fft_buffer, fft_buffer, nstd, SFLEN); + float var = srsran_convert_dB_to_power(-snr); + srsran_ch_awgn_c(fft_buffer, fft_buffer, var, SFLEN); } // look for NPSS signal diff --git a/lib/src/phy/sync/test/sync_sl_test.c b/lib/src/phy/sync/test/sync_sl_test.c index a7d64fdbb..36e7bd90b 100644 --- a/lib/src/phy/sync/test/sync_sl_test.c +++ b/lib/src/phy/sync/test/sync_sl_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -110,7 +110,7 @@ void parse_args(int argc, char** argv) } break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -175,8 +175,8 @@ int main(int argc, char** argv) // ADD CHANNEL NOISE if (snr < 50) { - float std_dev = powf(10.0f, -(snr + 3.0f) / 20.0f); - srsran_ch_awgn_c(output_buffer, output_buffer, std_dev, output_buffer_len); + float var = srsran_convert_dB_to_power(-snr); + srsran_ch_awgn_c(output_buffer, output_buffer, var, output_buffer_len); } // ADD FREQUENCY OFFSET diff --git a/lib/src/phy/sync/test/sync_test.c b/lib/src/phy/sync/test/sync_test.c index ac9e8373d..b2590581f 100644 --- a/lib/src/phy/sync/test/sync_test.c +++ b/lib/src/phy/sync/test/sync_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -65,7 +65,7 @@ void parse_args(int argc, char** argv) cp = SRSRAN_CP_EXT; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/ue/CMakeLists.txt b/lib/src/phy/ue/CMakeLists.txt index c50dfad0c..5a589d46f 100644 --- a/lib/src/phy/ue/CMakeLists.txt +++ b/lib/src/phy/ue/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/ue/test/CMakeLists.txt b/lib/src/phy/ue/test/CMakeLists.txt index ee3fb9d8b..4b80e32ce 100644 --- a/lib/src/phy/ue/test/CMakeLists.txt +++ b/lib/src/phy/ue/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -38,6 +38,10 @@ add_executable(ue_dl_nbiot_test ue_dl_nbiot_test.c) target_link_libraries(ue_dl_nbiot_test srsran_phy pthread) add_test(ue_dl_nbiot_test ue_dl_nbiot_test) +add_executable(ue_sync_nr_test ue_sync_nr_test.c) +target_link_libraries(ue_sync_nr_test srsran_phy pthread) +add_test(ue_sync_nr_test ue_sync_nr_test) + if(RF_FOUND) add_executable(ue_mib_sync_test_nbiot_usrp ue_mib_sync_test_nbiot_usrp.c) target_link_libraries(ue_mib_sync_test_nbiot_usrp srsran_phy srsran_rf pthread) @@ -50,4 +54,20 @@ if(RF_FOUND) else(SRSGUI_FOUND) add_definitions(-DDISABLE_GRAPHICS) endif(SRSGUI_FOUND) -endif(RF_FOUND) \ No newline at end of file +endif(RF_FOUND) + +add_executable(ue_dl_nr_file_test ue_dl_nr_file_test.cc) +target_link_libraries(ue_dl_nr_file_test srsran_phy srsran_common pthread) +foreach (n RANGE 0 9) + #add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234 -l 2) + #add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234 -l 2) + #add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234 -l 2) + #add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234 -l 2) + #add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234 -l 2) + #add_test(ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci1_rb25_n${n}_common_L1_ncce0.dat -i 1 -P 25 -n ${n} -R 1234 -l 2) +endforeach () +#add_test(ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat -i 1 -P 52 -n 4 -R 7f) +add_test(ue_dl_nr_pci500_rb52_si_coreset0_idx6 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci500_rb52_si_coreset0_idx6_s15.36e6.dat -S -i 500 -P 52 -n 0 -R ffff -T si -c 6 -s common0 -A 368500 -a 368410) +#add_test(ue_dl_nr_pci500_rb52_si_coreset0_idx7 ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci500_rb52_si_coreset0_idx7_s15.36e6.dat -S -i 500 -P 52 -n 0 -R ffff -T si -c 7 -s common0 -A 161200 -a 161290) +add_test(ue_dl_nr_pci500_rb52_pdsch ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci500_rb52_rnti0x100_s15.36e6.dat -S -i 500 -P 52 -N 48 -n 1 -R 0x100 -T c -s common3 -o 1 -A 368500 -a 368410 -I -t 1 13) +add_test(ue_dl_nr_pci500_rb52_rar ue_dl_nr_file_test -f ${CMAKE_CURRENT_SOURCE_DIR}/ue_dl_nr_pci500_rb52_rar_s15.36e6.dat -i 500 -P 52 -n 5 -R f -T ra -c 6 -S -s common1 -A 368500 -a 368410) diff --git a/lib/src/phy/ue/test/gen_ack_nr_test.c b/lib/src/phy/ue/test/gen_ack_nr_test.c index 0e4f922f8..8342330bd 100644 --- a/lib/src/phy/ue/test/gen_ack_nr_test.c +++ b/lib/src/phy/ue/test/gen_ack_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,57 +20,58 @@ */ #include "srsran/common/test_common.h" -#include "srsran/phy/ue/ue_dl_nr.h" +#include "srsran/phy/phch/harq_ack.h" +#include "srsran/phy/utils/debug.h" #include static int test_case_1() { // Set configuration - srsran_ue_dl_nr_harq_ack_cfg_t cfg = {}; - cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; + srsran_harq_ack_cfg_hl_t cfg = {}; + cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; // Generate ACK information srsran_pdsch_ack_nr_t ack_info = {}; ack_info.nof_cc = 1; ack_info.use_pusch = true; - srsran_pdsch_ack_m_nr_t m = {}; - m.value[0] = 1; - m.present = true; + srsran_harq_ack_m_t m = {}; + m.value[0] = 1; + m.present = true; m.resource.k1 = 8; m.resource.v_dai_dl = 0; m.value[0] = 1; m.present = true; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); m.resource.k1 = 5; m.resource.v_dai_dl = 2; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); m.resource.k1 = 6; m.resource.v_dai_dl = 1; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); m.resource.k1 = 4; m.resource.v_dai_dl = 3; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); m.resource.k1 = 3; m.resource.v_dai_dl = 0; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); // Print trace char str[512] = {}; - TESTASSERT(srsran_ue_dl_nr_ack_info(&ack_info, str, (uint32_t)sizeof(str)) > SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_info(&ack_info, str, (uint32_t)sizeof(str)) > SRSRAN_SUCCESS); INFO("%s", str); // Generate UCI data srsran_uci_data_nr_t uci_data = {}; - TESTASSERT(srsran_ue_dl_nr_gen_ack(&cfg, &ack_info, &uci_data) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_pack(&cfg, &ack_info, &uci_data) == SRSRAN_SUCCESS); // Assert UCI data - TESTASSERT(uci_data.cfg.o_ack == 5); + TESTASSERT(uci_data.cfg.ack.count == 5); return SRSRAN_SUCCESS; } @@ -78,49 +79,56 @@ static int test_case_1() static int test_case_2() { // Set configuration - srsran_ue_dl_nr_harq_ack_cfg_t cfg = {}; - cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; + srsran_harq_ack_cfg_hl_t cfg = {}; + cfg.harq_ack_codebook = srsran_pdsch_harq_ack_codebook_dynamic; // Generate ACK information srsran_pdsch_ack_nr_t ack_info = {}; ack_info.nof_cc = 1; ack_info.use_pusch = true; - srsran_pdsch_ack_m_nr_t m = {}; - m.value[0] = 1; - m.present = true; + srsran_harq_ack_m_t m = {}; + m.value[0] = 1; + m.present = true; m.resource.k1 = 7; m.resource.v_dai_dl = 1; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); m.resource.k1 = 6; m.resource.v_dai_dl = 2; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); m.resource.k1 = 8; m.resource.v_dai_dl = 0; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); m.resource.k1 = 5; m.resource.v_dai_dl = 3; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); m.resource.k1 = 4; m.resource.v_dai_dl = 0; - TESTASSERT(srsran_ue_dl_nr_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_insert_m(&ack_info, &m) == SRSRAN_SUCCESS); // Print trace char str[512] = {}; - TESTASSERT(srsran_ue_dl_nr_ack_info(&ack_info, str, (uint32_t)sizeof(str)) > SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_info(&ack_info, str, (uint32_t)sizeof(str)) > SRSRAN_SUCCESS); INFO("%s", str); // Generate UCI data srsran_uci_data_nr_t uci_data = {}; - TESTASSERT(srsran_ue_dl_nr_gen_ack(&cfg, &ack_info, &uci_data) == SRSRAN_SUCCESS); + TESTASSERT(srsran_harq_ack_pack(&cfg, &ack_info, &uci_data) == SRSRAN_SUCCESS); // Assert UCI data - TESTASSERT(uci_data.cfg.o_ack == 5); + TESTASSERT(uci_data.cfg.ack.count == 5); + + // Unpack HARQ ACK UCI data + srsran_pdsch_ack_nr_t ack_info_rx = ack_info; + TESTASSERT(srsran_harq_ack_unpack(&cfg, &uci_data, &ack_info_rx) == SRSRAN_SUCCESS); + + // Assert unpacked data + TESTASSERT(memcmp(&ack_info, &ack_info_rx, sizeof(srsran_pdsch_ack_nr_t)) == 0); return SRSRAN_SUCCESS; } @@ -137,7 +145,7 @@ static void parse_args(int argc, char** argv) while ((opt = getopt(argc, argv, "v")) != -1) { switch (opt) { case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/ue/test/gen_ack_test.c b/lib/src/phy/ue/test/gen_ack_test.c index dc24ba043..24fce29ec 100644 --- a/lib/src/phy/ue/test/gen_ack_test.c +++ b/lib/src/phy/ue/test/gen_ack_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -47,7 +47,7 @@ int fdd_tests(uint32_t max_cc) ue_dl.cell.frame_type = SRSRAN_FDD; for (uint32_t nof_cc = 1; nof_cc <= max_cc; nof_cc++) { - for (uint32_t nof_tb = 1; nof_tb <= SRSRAN_MAX_CODEWORDS; nof_tb++) { + for (uint8_t nof_tb = 1; nof_tb <= SRSRAN_MAX_CODEWORDS; nof_tb++) { for (uint32_t nof_active_cc = 1; nof_active_cc <= nof_cc; nof_active_cc++) { for (uint32_t nof_active_tb = 1; nof_active_tb <= nof_tb; nof_active_tb++) { srsran_pdsch_ack_t ack_info = {}; @@ -62,7 +62,7 @@ int fdd_tests(uint32_t max_cc) ack_info.cc[cc_idx].m[0].present = cc_idx < nof_active_cc; ack_info.cc[cc_idx].m[0].resource.n_cce = cc_idx + 1; if (ack_info.cc[cc_idx].m[0].present) { - for (uint32_t j = 0; j < nof_tb; j++) { + for (uint8_t j = 0; j < nof_tb; j++) { ack_info.cc[cc_idx].m[0].value[j] = j < nof_active_tb ? 1 : 2; } } else { diff --git a/lib/src/phy/ue/test/pucch_resource_test.c b/lib/src/phy/ue/test/pucch_resource_test.c index 1e9545eb9..68e100c91 100644 --- a/lib/src/phy/ue/test/pucch_resource_test.c +++ b/lib/src/phy/ue/test/pucch_resource_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/ue/test/ue_dl_nbiot_test.c b/lib/src/phy/ue/test/ue_dl_nbiot_test.c index 21640d39d..02bcd4cb0 100644 --- a/lib/src/phy/ue/test/ue_dl_nbiot_test.c +++ b/lib/src/phy/ue/test/ue_dl_nbiot_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -49,7 +49,7 @@ void parse_args(int argc, char** argv) cell.n_id_ncell = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/ue/test/ue_dl_nr_file_test.cc b/lib/src/phy/ue/test/ue_dl_nr_file_test.cc new file mode 100644 index 000000000..32f6bf889 --- /dev/null +++ b/lib/src/phy/ue/test/ue_dl_nr_file_test.cc @@ -0,0 +1,466 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifdef __cplusplus +#include +extern "C" { +#include "srsran/phy/io/filesource.h" +#include "srsran/phy/phch/ra_nr.h" +#include "srsran/phy/ue/ue_dl_nr.h" +#include "srsran/phy/utils/debug.h" +#include +} +#endif // __cplusplus + +#include "srsran/common/band_helper.h" +#include + +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; + +static char* filename = NULL; +static srsran_pdcch_cfg_nr_t pdcch_cfg = {}; +static srsran_sch_hl_cfg_nr_t pdsch_hl_cfg = {}; +static uint16_t rnti = 0x1234; +static srsran_rnti_type_t rnti_type = srsran_rnti_type_c; +static srsran_slot_cfg_t slot_cfg = {}; + +static srsran_filesource_t filesource = {}; +static srsran_ue_dl_nr_t ue_dl = {}; +static cf_t* buffer[SRSRAN_MAX_PORTS] = {}; +static srsran_softbuffer_rx_t softbuffer = {}; +static uint8_t* data = NULL; +static int pdsch_time_ra_start = -1; +static int pdsch_time_ra_length = -1; + +static uint32_t coreset0_idx = 0; // if ss_type=si coreset0 is used and this is the index +static uint32_t coreset_offset_rb = 0; +static bool interleaved_pdcch = false; +static uint32_t dl_arfcn = 161200; // center of the NR carrier (default at 806e6 Hz) +static uint32_t ssb_arfcn = 161290; // center of the SSB within the carrier (default at 806.45e6) + +static uint32_t coreset_n_rb = 48; +static uint32_t coreset_len = 1; +static srsran_search_space_type_t ss_type = srsran_search_space_type_common_0; + +static void usage(char* prog) +{ + printf("Usage: %s [fPivnSRTscoNlAaIt] \n", prog); + printf("\t-f File name [Default none]\n"); + printf("\t-P Number of BWP (Carrier) PRB [Default %d]\n", carrier.nof_prb); + printf("\t-i Physical cell identifier [Default %d]\n", carrier.pci); + printf("\t-n Slot index [Default %d]\n", slot_cfg.idx); + printf("\t-R RNTI in hexadecimal [Default 0x%x]\n", rnti); + printf("\t-T RNTI type (c, ra, si) [Default %s]\n", srsran_rnti_type_str(rnti_type)); + printf("\t-s Search space type (common0, common3, ue) [Default %s]\n", srsran_ss_type_str(ss_type)); + printf("\t-c Coreset0 index (only used if SS type is common0 for SIB) [Default %d]\n", coreset0_idx); + printf("\t-o Coreset RB offset [Default %d]\n", coreset_offset_rb); + printf("\t-N Coreset N_RB [Default %d]\n", coreset_n_rb); + printf("\t-l Coreset duration in symbols [Default %d]\n", coreset_len); + printf("\t-I Enable interleaved CCE-to-REG [Default %s]\n", interleaved_pdcch ? "Enabled" : "Disabled"); + printf("\t-t PDSCH time resource allocation [start symbol] [length]\n"); + printf("\t-A ARFCN of the NR carrier (center) [Default %d]\n", dl_arfcn); + printf("\t-a center of the SSB within the carrier [Default %d]\n", ssb_arfcn); + + printf("\t-S Use standard rates [Default %s]\n", srsran_symbol_size_is_standard() ? "yes" : "no"); + + printf("\t-v [set srsran_verbose to debug, default none]\n"); +} + +static int parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "fPivnSRTscoNlAaIt")) != -1) { + switch (opt) { + case 'f': + filename = argv[optind]; + break; + case 'P': + carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'i': + carrier.pci = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'v': + increase_srsran_verbose_level(); + break; + case 'n': + slot_cfg.idx = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'R': + rnti = (uint16_t)strtol(argv[optind], NULL, 16); + break; + case 't': + pdsch_time_ra_start = (int)strtol(argv[optind++], NULL, 10); + pdsch_time_ra_length = (int)strtol(argv[optind], NULL, 10); + break; + case 'T': + if (strcmp(argv[optind], "c") == 0) { + rnti_type = srsran_rnti_type_c; + } else if (strcmp(argv[optind], "ra") == 0) { + rnti_type = srsran_rnti_type_ra; + } else if (strcmp(argv[optind], "si") == 0) { + rnti_type = srsran_rnti_type_si; + } else { + printf("Invalid RNTI type '%s'\n", argv[optind]); + usage(argv[0]); + return SRSRAN_ERROR; + } + break; + case 's': + if (strcmp(argv[optind], "common0") == 0) { + ss_type = srsran_search_space_type_common_0; + } else if (strcmp(argv[optind], "common1") == 0) { + ss_type = srsran_search_space_type_common_1; + } else if (strcmp(argv[optind], "common3") == 0) { + ss_type = srsran_search_space_type_common_3; + } else if (strcmp(argv[optind], "ue") == 0) { + ss_type = srsran_search_space_type_ue; + } else { + printf("Invalid SS type '%s'\n", argv[optind]); + usage(argv[0]); + return SRSRAN_ERROR; + } + break; + case 'I': + interleaved_pdcch ^= true; + break; + case 'c': + coreset0_idx = (uint16_t)strtol(argv[optind], NULL, 10); + break; + case 'o': + coreset_offset_rb = (uint16_t)strtol(argv[optind], NULL, 10); + break; + case 'N': + coreset_n_rb = (uint16_t)strtol(argv[optind], NULL, 10); + break; + case 'l': + coreset_len = (uint16_t)strtol(argv[optind], NULL, 10); + break; + case 'A': + dl_arfcn = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'a': + ssb_arfcn = (uint32_t)strtol(argv[optind], NULL, 10); + break; + case 'S': + srsran_use_standard_symbol_size(true); + break; + default: + usage(argv[0]); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + +static int work_ue_dl(srsran_ue_dl_nr_t* ue_dl, srsran_slot_cfg_t* slot) +{ + // Run FFT + srsran_ue_dl_nr_estimate_fft(ue_dl, slot); + + // Blind search + srsran_dci_dl_nr_t dci_dl_rx = {}; + int nof_found_dci = srsran_ue_dl_nr_find_dl_dci(ue_dl, slot, rnti, rnti_type, &dci_dl_rx, 1); + if (nof_found_dci < SRSRAN_SUCCESS) { + ERROR("Error in blind search"); + return SRSRAN_ERROR; + } + + // Print PDCCH blind search candidates + for (uint32_t i = 0; i < ue_dl->pdcch_info_count; i++) { + const srsran_ue_dl_nr_pdcch_info_t* info = &ue_dl->pdcch_info[i]; + INFO("PDCCH: %s-rnti=0x%x, crst_id=%d, ss_type=%s, ncce=%d, al=%d, EPRE=%+.2f, RSRP=%+.2f, corr=%.3f; " + "nof_bits=%d; crc=%s;", + srsran_rnti_type_str_short(info->dci_ctx.rnti_type), + info->dci_ctx.rnti, + info->dci_ctx.coreset_id, + srsran_ss_type_str(info->dci_ctx.ss_type), + info->dci_ctx.location.ncce, + info->dci_ctx.location.L, + info->measure.epre_dBfs, + info->measure.rsrp_dBfs, + info->measure.norm_corr, + info->nof_bits, + info->result.crc ? "OK" : "KO"); + } + + if (nof_found_dci < 1) { + printf("No DCI found :'(\n"); + return SRSRAN_ERROR; + } + + char str[1024] = {}; + srsran_dci_dl_nr_to_str(&ue_dl->dci, &dci_dl_rx, str, (uint32_t)sizeof(str)); + printf("Found DCI: %s\n", str); + + // Convert DCI to PDSCH transmission + srsran_sch_cfg_nr_t pdsch_cfg = {}; + if (rnti_type == srsran_rnti_type_ra) { + pdsch_hl_cfg.common_time_ra[0].k = 0; + pdsch_hl_cfg.common_time_ra[0].mapping_type = srsran_sch_mapping_type_A; + pdsch_hl_cfg.common_time_ra[0].sliv = + srsran_ra_type2_to_riv(SRSRAN_NSYMB_PER_SLOT_NR - 1, 1, SRSRAN_NSYMB_PER_SLOT_NR); + pdsch_hl_cfg.nof_common_time_ra = 1; + } + if (srsran_ra_dl_dci_to_grant_nr(&carrier, slot, &pdsch_hl_cfg, &dci_dl_rx, &pdsch_cfg, &pdsch_cfg.grant) < + SRSRAN_SUCCESS) { + ERROR("Error decoding PDSCH search"); + return SRSRAN_ERROR; + } + + srsran_sch_cfg_nr_info(&pdsch_cfg, str, (uint32_t)sizeof(str)); + printf("PDSCH: %s\n", str); + + // Set softbuffer + pdsch_cfg.grant.tb[0].softbuffer.rx = &softbuffer; + + // Prepare PDSCH result + srsran_pdsch_res_nr_t pdsch_res = {}; + pdsch_res.tb[0].payload = data; + + // Decode PDSCH + if (srsran_ue_dl_nr_decode_pdsch(ue_dl, slot, &pdsch_cfg, &pdsch_res) < SRSRAN_SUCCESS) { + ERROR("Error decoding PDSCH search"); + return SRSRAN_ERROR; + } + + if (!pdsch_res.tb[0].crc) { + ERROR("Error decoding PDSCH"); + return SRSRAN_ERROR; + } + + printf("Decoded PDSCH (%d B)\n", pdsch_cfg.grant.tb[0].tbs / 8); + srsran_vec_fprint_byte(stdout, pdsch_res.tb[0].payload, pdsch_cfg.grant.tb[0].tbs / 8); + + // check payload is not all null + bool all_zero = true; + for (int i = 0; i < pdsch_cfg.grant.tb[0].tbs / 8; ++i) { + if (pdsch_res.tb[0].payload[i] != 0x0) { + all_zero = false; + break; + } + } + if (all_zero) { + ERROR("PDSCH payload is all zeros"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +// helper to avoid goto in C++ +int clean_exit(int ret) +{ + if (buffer[0] != NULL) { + free(buffer[0]); + } + if (data != NULL) { + free(data); + } + srsran_ue_dl_nr_free(&ue_dl); + srsran_filesource_free(&filesource); + srsran_softbuffer_rx_free(&softbuffer); + return ret; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + + // parse args + if (parse_args(argc, argv) < SRSRAN_SUCCESS) { + return clean_exit(ret); + } + + uint32_t sf_len = SRSRAN_SF_LEN_PRB(carrier.nof_prb); + buffer[0] = srsran_vec_cf_malloc(sf_len); + if (buffer[0] == NULL) { + ERROR("Error malloc"); + return clean_exit(ret); + } + + if (srsran_softbuffer_rx_init_guru(&softbuffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < + SRSRAN_SUCCESS) { + ERROR("Error init soft-buffer"); + return clean_exit(ret); + } + + data = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR); + if (data == NULL) { + ERROR("Error malloc"); + return clean_exit(ret); + } + + // Set default PDSCH configuration + srsran_ue_dl_nr_args_t ue_dl_args = {}; + ue_dl_args.nof_rx_antennas = 1; + ue_dl_args.pdsch.sch.disable_simd = false; + ue_dl_args.pdsch.sch.decoder_use_flooded = false; + ue_dl_args.pdsch.measure_evm = true; + ue_dl_args.pdcch.disable_simd = false; + ue_dl_args.pdcch.measure_evm = true; + ue_dl_args.nof_max_prb = carrier.nof_prb; + + // Check for filename + if (filename == NULL) { + ERROR("Filename was not provided"); + return clean_exit(ret); + } + + // Open filesource + if (srsran_filesource_init(&filesource, filename, SRSRAN_COMPLEX_FLOAT_BIN) < SRSRAN_SUCCESS) { + ERROR("Error opening filesource"); + return clean_exit(ret); + } + + // initial DCI config + srsran_dci_cfg_nr_t dci_cfg = {}; + dci_cfg.bwp_dl_initial_bw = carrier.nof_prb; + dci_cfg.bwp_ul_initial_bw = carrier.nof_prb; + dci_cfg.bwp_dl_active_bw = carrier.nof_prb; + dci_cfg.bwp_ul_active_bw = carrier.nof_prb; + dci_cfg.monitor_common_0_0 = true; + dci_cfg.monitor_0_0_and_1_0 = true; + + + // derive absolute frequencies from ARFCNs + srsran::srsran_band_helper band_helper; + carrier.ssb_center_freq_hz = band_helper.nr_arfcn_to_freq(ssb_arfcn); + carrier.dl_center_frequency_hz = band_helper.nr_arfcn_to_freq(dl_arfcn); + + srsran_coreset_t* coreset = NULL; + + // Configure CORESET + if (rnti_type == srsran_rnti_type_si || rnti_type == srsran_rnti_type_ra) { + // configure to use coreset0 + coreset = &pdcch_cfg.coreset[0]; + pdcch_cfg.coreset_present[0] = true; + + // Get pointA and SSB absolute frequencies + double pointA_abs_freq_Hz = + carrier.dl_center_frequency_hz - carrier.nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(carrier.scs) / 2; + double ssb_abs_freq_Hz = carrier.ssb_center_freq_hz; + // Calculate integer SSB to pointA frequency offset in Hz + uint32_t ssb_pointA_freq_offset_Hz = + (ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0; + + // derive coreset0 parameters + if (srsran_coreset_zero(carrier.pci, ssb_pointA_freq_offset_Hz, carrier.scs, carrier.scs, coreset0_idx, coreset) != + SRSRAN_SUCCESS) { + printf("Not possible to create CORESET Zero (ssb_scs=%s, pdcch_scs=%s, idx=%d)", + srsran_subcarrier_spacing_to_str(carrier.scs), + srsran_subcarrier_spacing_to_str(carrier.scs), + coreset0_idx); + return clean_exit(ret); + } + + // Setup PDSCH DMRS (also signaled through MIB) + pdsch_hl_cfg.typeA_pos = srsran_dmrs_sch_typeA_pos_2; + + // set coreset0 bandwidth + dci_cfg.coreset0_bw = srsran_coreset_get_bw(coreset); + } else { + // configure to use coreset1 + coreset = &pdcch_cfg.coreset[1]; + coreset->id = 1; + pdcch_cfg.coreset_present[1] = true; + coreset->duration = coreset_len; + coreset->offset_rb = coreset_offset_rb; + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { + coreset->freq_resources[i] = i < coreset_n_rb / 6; + } + if (interleaved_pdcch) { + coreset->mapping_type = srsran_coreset_mapping_type_interleaved; + coreset->reg_bundle_size = srsran_coreset_bundle_size_n6; + coreset->interleaver_size = srsran_coreset_bundle_size_n2; + coreset->precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + coreset->shift_index = carrier.pci; + } + // set coreset0 bandwidth (it is used in RA when ss_type = common3) + dci_cfg.coreset0_bw = coreset_n_rb; + + // SCH configuration parameters + if (pdsch_time_ra_start >= 0 && pdsch_time_ra_length >= 0) { + auto last_pdsch_symbol = (uint16_t)(pdsch_time_ra_start + pdsch_time_ra_length); + + if (last_pdsch_symbol > SRSRAN_NSYMB_PER_SLOT_NR) { + ERROR("incorrect PDSCH start symbol or length provided"); + return clean_exit(ret); + } + uint32_t sliv = srsran_ra_nr_type1_riv(SRSRAN_NSYMB_PER_SLOT_NR, pdsch_time_ra_start, pdsch_time_ra_length); + + pdsch_hl_cfg.nof_dedicated_time_ra = 1; + pdsch_hl_cfg.dedicated_time_ra[0].mapping_type = srsran_sch_mapping_type_A; + pdsch_hl_cfg.dedicated_time_ra[0].k = 0; + pdsch_hl_cfg.dedicated_time_ra[0].sliv = sliv; + } + } + + char coreset_info[512] = {}; + srsran_coreset_to_str(coreset, coreset_info, sizeof(coreset_info)); + INFO("Coreset parameter: %s", coreset_info); + + // Configure Search Space + srsran_search_space_t* search_space = &pdcch_cfg.search_space[0]; + pdcch_cfg.search_space_present[0] = true; + search_space->id = 0; + search_space->coreset_id = (rnti_type == srsran_rnti_type_si || rnti_type == srsran_rnti_type_ra) ? 0 : 1; + search_space->type = ss_type; + search_space->formats[0] = srsran_dci_format_nr_0_0; + search_space->formats[1] = srsran_dci_format_nr_1_0; + search_space->nof_formats = 2; + for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) { + search_space->nof_candidates[L] = srsran_pdcch_nr_max_candidates_coreset(coreset, L); + } + + // Configure RA search space + pdcch_cfg.ra_search_space_present = true; + pdcch_cfg.ra_search_space = *search_space; + pdcch_cfg.ra_search_space.type = srsran_search_space_type_common_1; + + if (srsran_ue_dl_nr_init(&ue_dl, buffer, &ue_dl_args)) { + ERROR("Error UE DL"); + return clean_exit(ret); + } + + if (srsran_ue_dl_nr_set_carrier(&ue_dl, &carrier)) { + ERROR("Error setting SCH NR carrier"); + return clean_exit(ret); + } + + // Read baseband from file + if (srsran_filesource_read(&filesource, buffer[0], (int)ue_dl.fft->sf_sz) < SRSRAN_SUCCESS) { + ERROR("Error reading baseband"); + return clean_exit(ret); + } + + if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &pdcch_cfg, &dci_cfg)) { + ERROR("Error setting CORESET"); + return clean_exit(ret); + } + + // Actual decode + ret = work_ue_dl(&ue_dl, &slot_cfg); + + // free memory and return last value of ret + return clean_exit(ret); +} diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n0_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n0_common_L1_ncce0.dat new file mode 100644 index 000000000..2ddfae789 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n0_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n1_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n1_common_L1_ncce0.dat new file mode 100644 index 000000000..8374a76d5 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n1_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n2_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n2_common_L1_ncce0.dat new file mode 100644 index 000000000..3e5240db7 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n2_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n3_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n3_common_L1_ncce0.dat new file mode 100644 index 000000000..4f33583f3 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n3_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n4_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n4_common_L1_ncce0.dat new file mode 100644 index 000000000..d5cb3e0f0 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n4_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n5_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n5_common_L1_ncce0.dat new file mode 100644 index 000000000..6053e5be9 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n5_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n6_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n6_common_L1_ncce0.dat new file mode 100644 index 000000000..2d4ca1655 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n6_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n7_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n7_common_L1_ncce0.dat new file mode 100644 index 000000000..f6fab76bf Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n7_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n8_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n8_common_L1_ncce0.dat new file mode 100644 index 000000000..8b709e9e2 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n8_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n9_common_L1_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n9_common_L1_ncce0.dat new file mode 100644 index 000000000..2f6b974bb Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci1_rb25_n9_common_L1_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat new file mode 100644 index 000000000..46ba0ffe5 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_n4_ra_L2_ncce0.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_rar_s15.36e6.dat b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_rar_s15.36e6.dat new file mode 100644 index 000000000..3b96f8307 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_rar_s15.36e6.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_rnti0x100_s15.36e6.dat b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_rnti0x100_s15.36e6.dat new file mode 100644 index 000000000..daff67532 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_rnti0x100_s15.36e6.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx6_s15.36e6.dat b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx6_s15.36e6.dat new file mode 100644 index 000000000..9c35a565f Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx6_s15.36e6.dat differ diff --git a/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx7_s15.36e6.dat b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx7_s15.36e6.dat new file mode 100644 index 000000000..e7afecae3 Binary files /dev/null and b/lib/src/phy/ue/test/ue_dl_nr_pci500_rb52_si_coreset0_idx7_s15.36e6.dat differ diff --git a/lib/src/phy/ue/test/ue_mib_sync_test_nbiot_usrp.c b/lib/src/phy/ue/test/ue_mib_sync_test_nbiot_usrp.c index 464030f0a..2642debf4 100644 --- a/lib/src/phy/ue/test/ue_mib_sync_test_nbiot_usrp.c +++ b/lib/src/phy/ue/test/ue_mib_sync_test_nbiot_usrp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -86,7 +86,7 @@ void parse_args(int argc, char** argv) nof_frames = (int)strtol(argv[optind], NULL, 10); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/ue/test/ue_sync_nr_test.c b/lib/src/phy/ue/test/ue_sync_nr_test.c new file mode 100644 index 000000000..980342638 --- /dev/null +++ b/lib/src/phy/ue/test/ue_sync_nr_test.c @@ -0,0 +1,327 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/phy/channel/ch_awgn.h" +#include "srsran/phy/channel/delay.h" +#include "srsran/phy/ue/ue_sync_nr.h" +#include "srsran/phy/utils/debug.h" +#include "srsran/phy/utils/ringbuffer.h" +#include "srsran/phy/utils/vector.h" +#include +#include + +// NR parameters +static uint32_t pci = 500; // Physical Cell Identifier +static uint32_t carrier_nof_prb = 52; // Carrier bandwidth +static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; +static double center_frequency_hz = 3.5e9; +static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_15kHz; +static double ssb_frequency_hz = 3.5e9 - 960e3; +static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_C; +static srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_TDD; + +// Test and channel parameters +static uint32_t nof_sf = 1000; // Number of subframes to test +static float cfo_hz = 100.0f; // CFO in Hz +static float n0_dB = -10.0f; // Noise floor in dB relative to full-scale +static float delay_min_us = 10.0f; // Minimum dynamic delay in microseconds +static float delay_max_us = 1000.0f; // Maximum dynamic delay in microseconds +static float delay_period_s = 60.0f; // Delay period in seconds + +// Test context +static double srate_hz = 0.0; // Base-band sampling rate +static uint32_t sf_len = 0; // Subframe length +static cf_t* buffer = NULL; // Base-band buffer +static cf_t* buffer2 = NULL; // Base-band buffer + +static void usage(char* prog) +{ + printf("Usage: %s [v]\n", prog); + printf("\t-v [set srsran_verbose to debug, default none]\n"); +} + +static void parse_args(int argc, char** argv) +{ + int opt; + while ((opt = getopt(argc, argv, "v")) != -1) { + switch (opt) { + case 'v': + increase_srsran_verbose_level(); + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +typedef struct { + uint32_t sf_idx; + uint32_t sfn; + srsran_ringbuffer_t ringbuffer; + srsran_ssb_t ssb; + srsran_timestamp_t timestamp; + srsran_channel_awgn_t awgn; + srsran_channel_delay_t delay; +} test_context_t; + +static void run_channel(test_context_t* ctx) +{ + // Delay + srsran_channel_delay_execute(&ctx->delay, buffer, buffer2, sf_len, &ctx->timestamp); + + // CFO + srsran_vec_apply_cfo(buffer2, -cfo_hz / srate_hz, buffer, sf_len); + + // AWGN + srsran_channel_awgn_run_c(&ctx->awgn, buffer, buffer, sf_len); +} + +static int test_context_init(test_context_t* ctx) +{ + SRSRAN_MEM_ZERO(ctx, test_context_t, 1); + + if (ctx == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + ctx->sfn = 1; + + if (srsran_ringbuffer_init(&ctx->ringbuffer, (int)(10 * sf_len * sizeof(cf_t))) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + srsran_ssb_args_t ssb_args = {}; + ssb_args.max_srate_hz = srate_hz; + ssb_args.min_scs = ssb_scs; + ssb_args.enable_encode = true; + if (srsran_ssb_init(&ctx->ssb, &ssb_args) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.center_freq_hz = center_frequency_hz; + ssb_cfg.ssb_freq_hz = ssb_frequency_hz; + ssb_cfg.scs = ssb_scs; + ssb_cfg.pattern = ssb_pattern; + ssb_cfg.duplex_mode = duplex_mode; + if (srsran_ssb_set_cfg(&ctx->ssb, &ssb_cfg) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (srsran_channel_delay_init(&ctx->delay, delay_min_us, delay_max_us, delay_period_s, 0, (uint32_t)srate_hz) < + SRSRAN_SUCCESS) { + ERROR("Init"); + return SRSRAN_ERROR; + } + + if (srsran_channel_awgn_init(&ctx->awgn, 0x0) < SRSRAN_SUCCESS) { + ERROR("Init"); + return SRSRAN_ERROR; + } + + if (srsran_channel_awgn_set_n0(&ctx->awgn, n0_dB) < SRSRAN_SUCCESS) { + ERROR("Init"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +static void test_context_free(test_context_t* ctx) +{ + if (ctx == NULL) { + return; + } + + srsran_ringbuffer_free(&ctx->ringbuffer); + srsran_ssb_free(&ctx->ssb); + srsran_channel_delay_free(&ctx->delay); + srsran_channel_awgn_free(&ctx->awgn); +} + +static int recv_callback(void* ptr, cf_t** rx_buffer, uint32_t nof_samples, srsran_timestamp_t* timestamp) +{ + test_context_t* ctx = (test_context_t*)ptr; + + // Check inputs + if (ctx == NULL || rx_buffer == NULL || rx_buffer[0] == NULL) { + return SRSRAN_ERROR; + } + + // Calculate the number of required bytes + int required_nbytes = (int)sizeof(cf_t) * nof_samples; + + // Execute subframe until the ringbuffer has data + while (srsran_ringbuffer_status(&ctx->ringbuffer) < required_nbytes) { + // Reset buffer + srsran_vec_cf_zero(buffer, sf_len); + + if (ctx->sf_idx % (SRSRAN_NOF_SF_X_FRAME / 2) == 0) { + // Prepare PBCH message + srsran_pbch_msg_nr_t pbch_msg = {}; + pbch_msg.ssb_idx = 0; + pbch_msg.hrf = ctx->sf_idx >= (SRSRAN_NOF_SF_X_FRAME / 2); + pbch_msg.sfn_4lsb = ctx->sfn & 0b1111U; + + // Encode SSB + if (srsran_ssb_add(&ctx->ssb, pci, &pbch_msg, buffer, buffer) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + + // Run channel + run_channel(ctx); + + // Write in the ring buffer + if (srsran_ringbuffer_write(&ctx->ringbuffer, buffer, (int)sf_len * sizeof(cf_t)) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Increment subframe index + ctx->sf_idx++; + + // Increment SFN if required + if (ctx->sf_idx >= SRSRAN_NOF_SF_X_FRAME) { + ctx->sfn = (ctx->sfn + 1) % 1024U; + ctx->sf_idx = 0; + } + } + + srsran_vec_cf_zero(buffer, sf_len); + + // Read ringbuffer + if (srsran_ringbuffer_read(&ctx->ringbuffer, rx_buffer[0], required_nbytes) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Setup timestamp + *timestamp = ctx->timestamp; + + // Advance timestamp + srsran_timestamp_add(&ctx->timestamp, 0, (float)(nof_samples / srate_hz)); + + return SRSRAN_SUCCESS; +} + +static int test_case_1(srsran_ue_sync_nr_t* ue_sync) +{ + for (uint32_t sf_idx = 0; sf_idx < nof_sf; sf_idx++) { + srsran_ue_sync_nr_outcome_t outcome = {}; + + // Prevent buffer overflow in srsran_ue_sync_nr_zerocopy + if (ue_sync->nof_rx_channels > 1) { + ERROR("Error configuring number of RX channels"); + return SRSRAN_ERROR; + } + TESTASSERT(srsran_ue_sync_nr_zerocopy(ue_sync, &buffer, &outcome) == SRSRAN_SUCCESS); + + // Print outcome + INFO("measure - zerocpy in-sync=%s sf_idx=%d sfn=%d timestamp=%f cfo_hz=%+.1f delay_us=%+.3f", + outcome.in_sync ? "y" : "n", + outcome.sf_idx, + outcome.sfn, + srsran_timestamp_real(&outcome.timestamp), + outcome.cfo_hz, + outcome.delay_us); + } + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + int ret = SRSRAN_ERROR; + parse_args(argc, argv); + + if (!isnormal(srate_hz)) { + srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(carrier_nof_prb); + } + sf_len = (uint32_t)ceil(srate_hz / 1000.0); + buffer = srsran_vec_cf_malloc(sf_len); + buffer2 = srsran_vec_cf_malloc(sf_len); + + test_context_t ctx = {}; + srsran_ue_sync_nr_t ue_sync = {}; + + if (buffer == NULL) { + ERROR("Malloc"); + goto clean_exit; + } + + if (buffer2 == NULL) { + ERROR("Malloc"); + goto clean_exit; + } + + srsran_ue_sync_nr_args_t ue_sync_args = {}; + ue_sync_args.max_srate_hz = srate_hz; + ue_sync_args.min_scs = carrier_scs; + ue_sync_args.recv_obj = &ctx; + ue_sync_args.recv_callback = &recv_callback; + ue_sync_args.disable_cfo = true; + if (srsran_ue_sync_nr_init(&ue_sync, &ue_sync_args) < SRSRAN_SUCCESS) { + ERROR("Init"); + goto clean_exit; + } + + srsran_ue_sync_nr_cfg_t ue_sync_cfg = {}; + ue_sync_cfg.ssb.srate_hz = srate_hz; + ue_sync_cfg.ssb.center_freq_hz = center_frequency_hz; + ue_sync_cfg.ssb.ssb_freq_hz = ssb_frequency_hz; + ue_sync_cfg.ssb.scs = ssb_scs; + ue_sync_cfg.ssb.pattern = ssb_pattern; + ue_sync_cfg.ssb.duplex_mode = duplex_mode; + ue_sync_cfg.N_id = pci; + if (srsran_ue_sync_nr_set_cfg(&ue_sync, &ue_sync_cfg) < SRSRAN_SUCCESS) { + ERROR("Init"); + goto clean_exit; + } + + if (test_context_init(&ctx) < SRSRAN_SUCCESS) { + ERROR("Init"); + goto clean_exit; + } + + if (test_case_1(&ue_sync) != SRSRAN_SUCCESS) { + ERROR("test case failed"); + } + + ret = SRSRAN_SUCCESS; + +clean_exit: + srsran_ue_sync_nr_free(&ue_sync); + + if (buffer) { + free(buffer); + } + + if (buffer2) { + free(buffer2); + } + + test_context_free(&ctx); + + return ret; +} diff --git a/lib/src/phy/ue/test/ue_sync_test_nbiot_usrp.c b/lib/src/phy/ue/test/ue_sync_test_nbiot_usrp.c index 7bd3e347d..7849d9640 100644 --- a/lib/src/phy/ue/test/ue_sync_test_nbiot_usrp.c +++ b/lib/src/phy/ue/test/ue_sync_test_nbiot_usrp.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -105,7 +105,7 @@ void parse_args(int argc, char** argv) disable_plots = true; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); diff --git a/lib/src/phy/ue/ue_cell_search.c b/lib/src/phy/ue/ue_cell_search.c index 11a302783..42f5b98b0 100644 --- a/lib/src/phy/ue/ue_cell_search.c +++ b/lib/src/phy/ue/ue_cell_search.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -188,6 +188,11 @@ int srsran_ue_cellsearch_set_nof_valid_frames(srsran_ue_cellsearch_t* q, uint32_ } } +void srsran_set_detect_cp(srsran_ue_cellsearch_t* q, bool enable) +{ + srsran_ue_sync_cp_en(&q->ue_sync, enable); +} + /* Decide the most likely cell based on the mode */ static void get_cell(srsran_ue_cellsearch_t* q, uint32_t nof_detected_frames, srsran_ue_cellsearch_result_t* found_cell) { diff --git a/lib/src/phy/ue/ue_cell_search_nbiot.c b/lib/src/phy/ue/ue_cell_search_nbiot.c index 6822dc7e8..ff12b4d97 100644 --- a/lib/src/phy/ue/ue_cell_search_nbiot.c +++ b/lib/src/phy/ue/ue_cell_search_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/ue/ue_dl.c b/lib/src/phy/ue/ue_dl.c index d6b674125..378b8f9da 100644 --- a/lib/src/phy/ue/ue_dl.c +++ b/lib/src/phy/ue/ue_dl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -449,8 +449,16 @@ static int dci_blind_search(srsran_ue_dl_t* q, return SRSRAN_ERROR; } + // Check if RNTI is matched if ((dci_msg[nof_dci].rnti == rnti) && (dci_msg[nof_dci].nof_bits > 0)) { - dci_msg[nof_dci].rnti = rnti; + // Compute decoded message correlation to drastically reduce false alarm probability + float corr = srsran_pdcch_msg_corr(&q->pdcch, &dci_msg[nof_dci]); + + // Skip candidate if the threshold is not reached + // 0.5 is set from pdcch_test + if (!isnormal(corr) || corr < 0.5f) { + continue; + } // Look for the messages found and apply the new format if the location is common if (search_in_common && (dci_cfg->multiple_csi_request_enabled || dci_cfg->srs_request_enabled)) { @@ -461,8 +469,8 @@ static int dci_blind_search(srsran_ue_dl_t* q, * that only the PDCCH in the common search space is transmitted by the primary cell. */ // Find a matching ncce in the common SS - if (srsran_location_find_ncce( - q->current_ss_common.loc, q->current_ss_common.nof_locations, dci_msg[nof_dci].location.ncce)) { + if (srsran_location_find_location( + q->current_ss_common.loc, q->current_ss_common.nof_locations, &dci_msg[nof_dci].location)) { srsran_dci_cfg_t cfg = *dci_cfg; srsran_dci_cfg_set_common_ss(&cfg); // if the payload size is the same that it would have in the common SS (only Format0/1A is allowed there) @@ -854,11 +862,14 @@ void srsran_ue_dl_gen_cqi_periodic(srsran_ue_dl_t* q, uci_data->cfg.cqi.ri_len = 1; uci_data->value.ri = cfg->last_ri; } else if (srsran_cqi_periodic_send(&cfg->cfg.cqi_report, tti, q->cell.frame_type)) { - if (cfg->cfg.cqi_report.format_is_subband) { + if (cfg->cfg.cqi_report.format_is_subband && + srsran_cqi_periodic_is_subband(&cfg->cfg.cqi_report, tti, q->cell.nof_prb, q->cell.frame_type)) { // TODO: Implement subband periodic reports - uci_data->cfg.cqi.type = SRSRAN_CQI_TYPE_SUBBAND; - uci_data->value.cqi.subband.subband_cqi = wideband_value; - uci_data->value.cqi.subband.subband_label = 0; + uci_data->cfg.cqi.type = SRSRAN_CQI_TYPE_SUBBAND_UE; + uci_data->value.cqi.subband_ue.subband_cqi = wideband_value; + uci_data->value.cqi.subband_ue.subband_label = tti / 100 % 2; + uci_data->cfg.cqi.L = srsran_cqi_hl_get_L(q->cell.nof_prb); + uci_data->cfg.cqi.subband_label_2_bits = uci_data->cfg.cqi.L > 1; } else { uci_data->cfg.cqi.type = SRSRAN_CQI_TYPE_WIDEBAND; uci_data->value.cqi.wideband.wideband_cqi = wideband_value; @@ -1437,7 +1448,7 @@ int srsran_ue_dl_find_and_decode(srsran_ue_dl_t* q, if (ret == 1) { // Logging - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) { char str[512]; srsran_dci_dl_info(&dci_dl[0], str, 512); INFO("PDCCH: %s, snr=%.1f dB", str, q->chest_res.snr_db); diff --git a/lib/src/phy/ue/ue_dl_nbiot.c b/lib/src/phy/ue/ue_dl_nbiot.c index edf3cb527..67aea6dfa 100644 --- a/lib/src/phy/ue/ue_dl_nbiot.c +++ b/lib/src/phy/ue/ue_dl_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -400,7 +400,7 @@ void srsran_nbiot_ue_dl_decode_sib1(srsran_nbiot_ue_dl_t* q, uint32_t current_sf // activate SIB1 grant and configure NPDSCH INFO( "%d.x: Activated SIB1 decoding in sfn=%d\n", current_sfn, srsran_nbiot_ue_dl_get_next_sib1_start(q, current_sfn)); - srsran_ra_nbiot_dl_grant_t grant; + srsran_ra_nbiot_dl_grant_t grant = {}; srsran_nbiot_ue_dl_get_sib1_grant(q, current_sfn, &grant); srsran_nbiot_ue_dl_set_grant(q, &grant); } @@ -419,7 +419,7 @@ void srsran_nbiot_ue_dl_decode_sib(srsran_nbiot_ue_dl_t* q, if (type == SRSRAN_NBIOT_SI_TYPE_SIB2) { assert(params.n == 1); // calculate SIB2 params - srsran_ra_nbiot_dl_grant_t grant; + srsran_ra_nbiot_dl_grant_t grant = {}; srsran_nbiot_ue_dl_get_sib_grant(q, hfn, sfn, params, &grant); srsran_nbiot_ue_dl_set_grant(q, &grant); INFO("%d.x: Activated SIB2 reception in hfn=%d, sfn=%d", diff --git a/lib/src/phy/ue/ue_dl_nr.c b/lib/src/phy/ue/ue_dl_nr.c index ebc83c1bf..0deceb708 100644 --- a/lib/src/phy/ue/ue_dl_nr.c +++ b/lib/src/phy/ue/ue_dl_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -168,12 +168,14 @@ int srsran_ue_dl_nr_set_carrier(srsran_ue_dl_nr_t* q, const srsran_carrier_nr_t* if (carrier->nof_prb != q->carrier.nof_prb) { for (uint32_t i = 0; i < q->nof_rx_antennas; i++) { - srsran_ofdm_cfg_t cfg = {}; - cfg.nof_prb = carrier->nof_prb; - cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); - cfg.cp = SRSRAN_CP_NORM; - cfg.keep_dc = true; - cfg.rx_window_offset = UE_DL_NR_FFT_WINDOW_OFFSET; + srsran_ofdm_cfg_t cfg = {}; + cfg.nof_prb = carrier->nof_prb; + cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); + cfg.cp = SRSRAN_CP_NORM; + cfg.keep_dc = true; + cfg.rx_window_offset = UE_DL_NR_FFT_WINDOW_OFFSET; + cfg.phase_compensation_hz = carrier->dl_center_frequency_hz; + srsran_ofdm_rx_init_cfg(&q->fft[i], &cfg); } } @@ -300,6 +302,16 @@ static int ue_dl_nr_find_dci_ncce(srsran_ue_dl_nr_t* q, return SRSRAN_ERROR; } +#if 0 + static uint32_t num_pdcch = 0; + char tmpstr[64]; + snprintf(tmpstr, 64, "pdcch_symbols%d.dat", num_pdcch); + printf("save %d syms to %s\n", q->pdcch.M / 4, tmpstr); + srsran_vec_save_file(tmpstr, q->pdcch.symbols, q->pdcch.M / 4 * sizeof(cf_t)); + // srsran_vec_fprint_c(stdout, q->pdcch.symbols, q->pdcch.M/4); + num_pdcch++; +#endif + // Save information pdcch_info->result = *pdcch_res; @@ -382,7 +394,7 @@ static int ue_dl_nr_find_dci_ss(srsran_ue_dl_nr_t* q, // Calculate possible PDCCH DCI candidates uint32_t candidates[SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR] = {}; int nof_candidates = srsran_pdcch_nr_locations_coreset( - coreset, search_space, rnti, L, SRSRAN_SLOT_NR_MOD(q->carrier.scs, slot_cfg->idx), candidates); + coreset, search_space, rnti, L, SRSRAN_SLOT_NR_MOD(q->carrier.scs, slot_cfg->idx), candidates); if (nof_candidates < SRSRAN_SUCCESS) { ERROR("Error calculating DCI candidate location"); return SRSRAN_ERROR; @@ -396,6 +408,7 @@ static int ue_dl_nr_find_dci_ss(srsran_ue_dl_nr_t* q, ctx.location.ncce = candidates[ncce_idx]; ctx.ss_type = search_space->type; ctx.coreset_id = search_space->coreset_id; + ctx.coreset_start_rb = srsran_coreset_start_rb(&q->cfg.coreset[search_space->coreset_id]); ctx.rnti_type = rnti_type; ctx.rnti = rnti; ctx.format = dci_format; @@ -573,7 +586,7 @@ int srsran_ue_dl_nr_decode_pdsch(srsran_ue_dl_nr_t* q, return SRSRAN_ERROR; } - if (SRSRAN_DEBUG_ENABLED && srsran_verbose >= SRSRAN_VERBOSE_INFO && !handler_registered) { + if (SRSRAN_DEBUG_ENABLED && get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO && !is_handler_registered()) { char str[512]; srsran_ue_dl_nr_pdsch_info(q, cfg, res, str, sizeof(str)); INFO("PDSCH: %s", str); @@ -582,268 +595,43 @@ int srsran_ue_dl_nr_decode_pdsch(srsran_ue_dl_nr_t* q, return SRSRAN_SUCCESS; } -int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, - const srsran_sch_cfg_nr_t* cfg, - const srsran_pdsch_res_nr_t* res, - char* str, - uint32_t str_len) +uint32_t srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, + const srsran_sch_cfg_nr_t* cfg, + const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS], + char* str, + uint32_t str_len) { - int len = 0; + uint32_t len = 0; // Append PDSCH info len += srsran_pdsch_nr_rx_info(&q->pdsch, cfg, &cfg->grant, res, &str[len], str_len - len); // Append channel estimator info - len = srsran_print_check(str, str_len, len, "SNR=%+.1f", q->chest.snr_db); + len += srsran_csi_meas_info_short(&q->dmrs_pdsch.csi, &str[len], str_len - len); return len; } -// Implements TS 38.213 Table 9.1.3-1: Value of counter DAI in DCI format 1_0 and of counter DAI or total DAI DCI format -// 1_1 -static uint32_t ue_dl_nr_V_DL_DAI(uint32_t dai) +int srsran_ue_dl_nr_csi_measure_trs(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_trs_measurements_t* measurement) { - return dai + 1; -} - -static int ue_dl_nr_gen_ack_type2(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, - const srsran_pdsch_ack_nr_t* ack_info, - srsran_uci_data_nr_t* uci_data) -{ - bool harq_ack_spatial_bundling = - ack_info->use_pusch ? cfg->harq_ack_spatial_bundling_pusch : cfg->harq_ack_spatial_bundling_pucch; - uint8_t* o_ack = uci_data->value.ack; - - uint32_t m = 0; // PDCCH with DCI format 1_0 or DCI format 1_1 monitoring occasion index: lower index corresponds to - // earlier PDCCH with DCI format 1_0 or DCI format 1_1 monitoring occasion - uint32_t j = 0; - uint32_t V_temp = 0; - uint32_t V_temp2 = 0; - - uint32_t N_DL_cells = ack_info->nof_cc; // number of serving cells configured by higher layers for the UE - - // The following code follows the exact pseudo-code provided in TS 38.213 9.1.3.1 Type-2 HARQ-ACK codebook ... - while (m < SRSRAN_UCI_NR_MAX_M) { - uint32_t c = 0; // serving cell index: lower indexes correspond to lower RRC indexes of corresponding cell - while (c < N_DL_cells) { - // Get ACK information of serving cell c for the PDCH monitoring occasion m - const srsran_pdsch_ack_m_nr_t* ack = &ack_info->cc[c].m[m]; - - // Get DAI counter value - uint32_t V_DL_CDAI = ue_dl_nr_V_DL_DAI(ack->resource.v_dai_dl); - uint32_t V_DL_TDAI = ack->resource.dci_format_1_1 ? ue_dl_nr_V_DL_DAI(ack->resource.v_dai_dl) : UINT32_MAX; - - // Get ACK values - uint32_t ack_tb0 = ack->value[0]; - uint32_t ack_tb1 = ack->value[1]; - - // For a PDCCH monitoring occasion with DCI format 1_0 or DCI format 1_1 in the active DL BWP of a serving cell, - // when a UE receives a PDSCH with one transport block and the value of maxNrofCodeWordsScheduledByDCI is 2, the - // HARQ-ACK information is associated with the first transport block and the UE generates a NACK for the second - // transport block if harq-ACK-SpatialBundlingPUCCH is not provided and generates HARQ-ACK information with - // value of ACK for the second transport block if harq-ACK-SpatialBundlingPUCCH is provided. - if (cfg->max_cw_sched_dci_is_2 && ack->second_tb_present) { - ack_tb1 = harq_ack_spatial_bundling ? 1 : 0; - } - - // if PDCCH monitoring occasion m is before an active DL BWP change on serving cell c or an active UL - // BWP change on the PCell and an active DL BWP change is not triggered by a DCI format 1_1 in PDCCH - // monitoring occasion m - if (ack->dl_bwp_changed || ack->ul_bwp_changed) { - c = c + 1; - } else { - if (ack->present) { - // Load ACK resource data into UCI info - uci_data->cfg.pucch.resource_id = ack_info->cc[c].m[m].resource.pucch_resource_id; - uci_data->cfg.pucch.rnti = ack_info->cc[c].m[m].resource.rnti; - - if (V_DL_CDAI <= V_temp) { - j = j + 1; - } - - V_temp = V_DL_CDAI; - - if (V_DL_TDAI == UINT32_MAX) { - V_temp2 = V_DL_CDAI; - } else { - V_temp2 = V_DL_TDAI; - } - - // if harq-ACK-SpatialBundlingPUCCH is not provided and m is a monitoring occasion for PDCCH with DCI format - // 1_0 or DCI format 1_1 and the UE is configured by maxNrofCodeWordsScheduledByDCI with reception of two - // transport blocks for at least one configured DL BWP of at least one serving cell, - if (!harq_ack_spatial_bundling && cfg->max_cw_sched_dci_is_2) { - o_ack[8 * j + 2 * (V_DL_CDAI - 1) + 0] = ack_tb0; - o_ack[8 * j + 2 * (V_DL_CDAI - 1) + 1] = ack_tb1; - } - // elseif harq-ACK-SpatialBundlingPUCCH is provided to the UE and m is a monitoring occasion for - // PDCCH with DCI format 1_1 and the UE is configured by maxNrofCodeWordsScheduledByDCI with - // reception of two transport blocks in at least one configured DL BWP of a serving cell, - else if (harq_ack_spatial_bundling && ack->resource.dci_format_1_1 && cfg->max_cw_sched_dci_is_2) { - o_ack[4 * j + (V_DL_CDAI - 1)] = ack_tb0 & ack_tb1; - } - // else - else { - o_ack[4 * j + (V_DL_CDAI - 1)] = ack_tb0; - } - } - c = c + 1; - } - } - m = m + 1; - } - if (V_temp2 < V_temp) { - j = j + 1; - } - - // if harq-ACK-SpatialBundlingPUCCH is not provided to the UE and the UE is configured by - // maxNrofCodeWordsScheduledByDCI with reception of two transport blocks for at least one configured DL BWP of a - // serving cell, - if (!harq_ack_spatial_bundling && cfg->max_cw_sched_dci_is_2) { - uci_data->cfg.o_ack = 2 * (4 * j + V_temp2); - } else { - uci_data->cfg.o_ack = 4 * j + V_temp2; - } - - // Implement here SPS PDSCH reception - // ... - - return SRSRAN_SUCCESS; -} - -int ue_dl_nr_pdsch_k1(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, const srsran_dci_dl_nr_t* dci_dl) -{ - // For DCI format 1_0, the PDSCH-to-HARQ_feedback timing indicator field values map to {1, 2, 3, 4, 5, 6, 7, 8} - if (dci_dl->ctx.format == srsran_dci_format_nr_1_0) { - return (int)dci_dl->harq_feedback + 1; - } - - // For DCI format 1_1, if present, the PDSCH-to-HARQ_feedback timing indicator field values map to values for a set of - // number of slots provided by dl-DataToUL-ACK as defined in Table 9.2.3-1. - if (dci_dl->harq_feedback >= SRSRAN_MAX_NOF_DL_DATA_TO_UL || dci_dl->harq_feedback >= cfg->nof_dl_data_to_ul_ack) { - ERROR("Out-of-range PDSCH-to-HARQ feedback index (%d, max %d)", dci_dl->harq_feedback, cfg->nof_dl_data_to_ul_ack); - return SRSRAN_ERROR; - } - - return (int)cfg->dl_data_to_ul_ack[dci_dl->harq_feedback]; -} - -int srsran_ue_dl_nr_pdsch_ack_resource(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, - const srsran_dci_dl_nr_t* dci_dl, - srsran_pdsch_ack_resource_nr_t* pdsch_ack_resource) -{ - if (cfg == NULL || dci_dl == NULL || pdsch_ack_resource == NULL) { + if (q == NULL || slot_cfg == NULL || csi_rs_nzp_set == NULL || measurement == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - // Calculate Data to UL ACK timing k1 - int k1 = ue_dl_nr_pdsch_k1(cfg, dci_dl); - if (k1 < SRSRAN_ERROR) { - ERROR("Error calculating K1"); - return SRSRAN_ERROR; - } - - // Fill PDSCH resource - pdsch_ack_resource->dci_format_1_1 = (dci_dl->ctx.format == srsran_dci_format_nr_1_1); - pdsch_ack_resource->k1 = k1; - pdsch_ack_resource->v_dai_dl = dci_dl->dai; - pdsch_ack_resource->rnti = dci_dl->ctx.rnti; - pdsch_ack_resource->pucch_resource_id = dci_dl->pucch_resource; - - return SRSRAN_SUCCESS; + return srsran_csi_rs_nzp_measure_trs(&q->carrier, slot_cfg, csi_rs_nzp_set, q->sf_symbols[0], measurement); } -int srsran_ue_dl_nr_gen_ack(const srsran_ue_dl_nr_harq_ack_cfg_t* cfg, - const srsran_pdsch_ack_nr_t* ack_info, - srsran_uci_data_nr_t* uci_data) +int srsran_ue_dl_nr_csi_measure_channel(const srsran_ue_dl_nr_t* q, + const srsran_slot_cfg_t* slot_cfg, + const srsran_csi_rs_nzp_set_t* csi_rs_nzp_set, + srsran_csi_channel_measurements_t* measurement) { - // Check inputs - if (cfg == NULL || ack_info == NULL || uci_data == NULL) { + if (q == NULL || slot_cfg == NULL || csi_rs_nzp_set == NULL || measurement == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } - // According TS 38.213 9.1.2 Type-1 HARQ-ACK codebook determination - if (cfg->harq_ack_codebook == srsran_pdsch_harq_ack_codebook_semi_static) { - // This clause applies if the UE is configured with pdsch-HARQ-ACK-Codebook = semi-static. - ERROR("Type-1 HARQ-ACK codebook determination is NOT implemented"); - return SRSRAN_ERROR; - } - - // According TS 38.213 9.1.3 Type-2 HARQ-ACK codebook determination - if (cfg->harq_ack_codebook == srsran_pdsch_harq_ack_codebook_dynamic) { - // This clause applies if the UE is configured with pdsch-HARQ-ACK-Codebook = dynamic. - return ue_dl_nr_gen_ack_type2(cfg, ack_info, uci_data); - } - - ERROR("No HARQ-ACK codebook determination is NOT implemented"); - return SRSRAN_ERROR; -} - -int srsran_ue_dl_nr_ack_insert_m(srsran_pdsch_ack_nr_t* ack_info, srsran_pdsch_ack_m_nr_t* m) -{ - // Check inputs - if (ack_info == NULL || m == NULL) { - return SRSRAN_ERROR_INVALID_INPUTS; - } - - // Protect SCell index and extract information - if (m->resource.scell_idx >= SRSRAN_MAX_CARRIERS) { - ERROR("Serving cell index (%d) exceeds maximum", m->resource.scell_idx); - return SRSRAN_ERROR; - } - srsran_pdsch_ack_cc_nr_t* cc = &ack_info->cc[m->resource.scell_idx]; - - // Find insertion index - uint32_t idx = cc->M; // Append at the end by default - for (uint32_t i = 0; i < cc->M; i++) { - if (cc->m[i].resource.k1 < m->resource.k1) { - idx = i; - break; - } - } - - // Increment count - cc->M += 1; - - // Make space for insertion - for (uint32_t i = cc->M - 1; i > idx; i--) { - cc->m[i] = cc->m[i - 1]; - } - - // Actual insertion - cc->m[idx] = *m; - - return SRSRAN_SUCCESS; -} - -uint32_t srsran_ue_dl_nr_ack_info(const srsran_pdsch_ack_nr_t* ack_info, char* str, uint32_t str_len) -{ - uint32_t len = 0; - - if (ack_info == NULL || str == NULL) { - return 0; - } - - // Print base info - len = srsran_print_check( - str, str_len, len, "use_pusch=%c nof_cc=%d\n", ack_info->use_pusch ? 'y' : 'n', ack_info->nof_cc); - - // Iterate all carriers - for (uint32_t cc = 0; cc < ack_info->nof_cc; cc++) { - len = srsran_print_check(str, str_len, len, " CC %d: M=%d\n", cc, ack_info->cc[cc].M); - for (uint32_t m = 0; m < ack_info->cc[cc].M; m++) { - if (ack_info->cc[cc].m[m].present) { - len = srsran_print_check(str, - str_len, - len, - " m %d: k1=%d dai=%d ack=%d\n", - m, - ack_info->cc[cc].m[m].resource.k1, - ack_info->cc[cc].m[m].resource.v_dai_dl, - ack_info->cc[cc].m[m].value[0]); - } - } - } - - return len; + return srsran_csi_rs_nzp_measure_channel(&q->carrier, slot_cfg, csi_rs_nzp_set, q->sf_symbols[0], measurement); } diff --git a/lib/src/phy/ue/ue_mib.c b/lib/src/phy/ue/ue_mib.c index fb6dad201..d3e95526a 100644 --- a/lib/src/phy/ue/ue_mib.c +++ b/lib/src/phy/ue/ue_mib.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/ue/ue_mib_nbiot.c b/lib/src/phy/ue/ue_mib_nbiot.c index 87207fe2f..7bf0629b7 100644 --- a/lib/src/phy/ue/ue_mib_nbiot.c +++ b/lib/src/phy/ue/ue_mib_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/ue/ue_mib_sl.c b/lib/src/phy/ue/ue_mib_sl.c index 694d2051e..847f8aa47 100644 --- a/lib/src/phy/ue/ue_mib_sl.c +++ b/lib/src/phy/ue/ue_mib_sl.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/ue/ue_sync.c b/lib/src/phy/ue/ue_sync.c index ae3a517d3..afc520edd 100644 --- a/lib/src/phy/ue/ue_sync.c +++ b/lib/src/phy/ue/ue_sync.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -375,6 +375,10 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell) } } + // Set CP for find and track objects + srsran_sync_set_cp(&q->sfind, cell.cp); + srsran_sync_set_cp(&q->strack, cell.cp); + // When Cell ID is 1000, ue_sync receives nof_avg_find_frames frames in find state and does not go to tracking // state and is used to search a cell if (cell.id == 1000) { @@ -392,9 +396,6 @@ int srsran_ue_sync_set_cell(srsran_ue_sync_t* q, srsran_cell_t cell) srsran_sync_set_cfo_ema_alpha(&q->strack, 0.1); } else { - q->sfind.cp = cell.cp; - q->strack.cp = cell.cp; - srsran_sync_set_frame_type(&q->sfind, cell.frame_type); srsran_sync_set_frame_type(&q->strack, cell.frame_type); @@ -508,6 +509,12 @@ float srsran_ue_sync_get_cfo(srsran_ue_sync_t* q) return 15000 * q->cfo_current_value; } +void srsran_ue_sync_cp_en(srsran_ue_sync_t* q, bool enabled) +{ + srsran_sync_cp_en(&q->strack, enabled); + srsran_sync_cp_en(&q->sfind, enabled); +} + void srsran_ue_sync_copy_cfo(srsran_ue_sync_t* q, srsran_ue_sync_t* src_obj) { // Copy find object internal CFO averages @@ -930,31 +937,44 @@ int srsran_ue_sync_run_find_gnss_mode(srsran_ue_sync_t* q, INFO("Calibration samples received start at %ld + %f", q->last_timestamp.full_secs, q->last_timestamp.frac_secs); // round to nearest second - srsran_timestamp_t ts_next_rx; + srsran_timestamp_t ts_next_rx, ts_next_rx_tmp, ts_tmp; srsran_timestamp_copy(&ts_next_rx, &q->last_timestamp); ts_next_rx.full_secs++; ts_next_rx.frac_secs = 0.0; - INFO("Next desired recv at %ld + %f", ts_next_rx.full_secs, ts_next_rx.frac_secs); + srsran_timestamp_copy(&ts_next_rx_tmp, &ts_next_rx); + INFO("Next desired recv at %ld + %f\n", ts_next_rx_tmp.full_secs, ts_next_rx_tmp.frac_secs); // get difference in time between second rx and now - srsran_timestamp_sub(&ts_next_rx, q->last_timestamp.full_secs, q->last_timestamp.frac_secs); - srsran_timestamp_sub(&ts_next_rx, 0, 0.001); ///< account for samples that have already been rx'ed + srsran_timestamp_sub(&ts_next_rx_tmp, q->last_timestamp.full_secs, q->last_timestamp.frac_secs); + srsran_timestamp_sub(&ts_next_rx_tmp, 0, 0.001); ///< account for samples that have already been rx'ed - uint64_t align_len = srsran_timestamp_uint64(&ts_next_rx, q->sf_len * 1000); + uint64_t align_len = srsran_timestamp_uint64(&ts_next_rx_tmp, q->sf_len * 1000); - DEBUG("Difference between first recv is %ld + %f, realigning %" PRIu64 " samples", - ts_next_rx.full_secs, - ts_next_rx.frac_secs, + DEBUG("Difference between first recv is %ld + %f, realigning %" PRIu64 " samples\n", + ts_next_rx_tmp.full_secs, + ts_next_rx_tmp.frac_secs, align_len); // receive align_len samples into dummy_buffer, make sure to not exceed buffer len uint32_t sample_count = 0; - while (sample_count < align_len) { - uint32_t actual_rx_len = SRSRAN_MIN(align_len, DUMMY_BUFFER_NUM_SAMPLES); - actual_rx_len = SRSRAN_MIN(align_len - sample_count, actual_rx_len); + while (align_len > q->sf_len) { + uint32_t actual_rx_len = SRSRAN_MIN(align_len, q->sf_len); q->recv_callback(q->stream, dummy_offset_buffer, actual_rx_len, &q->last_timestamp); - sample_count += actual_rx_len; + + srsran_timestamp_copy(&ts_tmp, &ts_next_rx); + srsran_timestamp_sub(&ts_tmp, q->last_timestamp.full_secs, q->last_timestamp.frac_secs); + srsran_timestamp_sub(&ts_tmp, 0, 0.001); ///< account for samples that have already been rx'ed + align_len = srsran_timestamp_uint64(&ts_tmp, q->sf_len * 1000); + + if (align_len > (uint64_t)q->sf_len * 1000) { + ts_next_rx.full_secs++; + ts_next_rx.frac_secs = 0.0; + srsran_timestamp_copy(&ts_tmp, &ts_next_rx); + srsran_timestamp_sub(&ts_tmp, q->last_timestamp.full_secs, q->last_timestamp.frac_secs); + srsran_timestamp_sub(&ts_tmp, 0, 0.001); ///< account for samples that have already been rx'ed + align_len = srsran_timestamp_uint64(&ts_tmp, q->sf_len * 1000); + } } DEBUG("Received %d samples during alignment", sample_count); @@ -1019,39 +1039,22 @@ int srsran_ue_sync_set_tti_from_timestamp(srsran_ue_sync_t* q, srsran_timestamp_ time_t t_cur = rx_timestamp->full_secs; DEBUG("t_cur=%ld", t_cur); - // time_t of reference UTC time on 1. Jan 1900 at 0:00 - // If we put this date in https://www.epochconverter.com it returns a negative number - time_t t_ref = {0}; -#if 0 - struct tm t = {0}; - t.tm_year = 1900; // year-1900 - t.tm_mday = 1; // first of January - // t.tm_isdst = 0; // Is DST on? 1 = yes, 0 = no, -1 = unknown - t_ref = mktime(&t); -#endif - - DEBUG("t_ref=%ld", t_ref); + // 3GPP Reference UTC time is 1. Jan 1900 at 0:00 + // If we put this date in https://www.epochconverter.com it returns a negative number (-2208988800) + // as epoch time starts at 1. Jan 1970 at 0:00 + uint64_t epoch_offset_3gpp = 2208988800; static const uint32_t MSECS_PER_SEC = 1000; - DEBUG("diff=%f", difftime(t_cur, t_ref)); - - double time_diff_secs = difftime(t_cur, t_ref); - - if (time_diff_secs < 0) { - fprintf(stderr, "Time diff between Rx timestamp and reference UTC is negative. Is the timestamp correct?\n"); - return SRSRAN_ERROR; - } - - DEBUG("time diff in s %f", time_diff_secs); + uint64_t time_3gpp_secs = t_cur + epoch_offset_3gpp; // convert to ms and add fractional part - double time_diff_msecs = time_diff_secs * MSECS_PER_SEC + rx_timestamp->frac_secs; - DEBUG("time diff in ms %f", time_diff_msecs); + uint64_t time_3gpp_msecs = (time_3gpp_secs + rx_timestamp->frac_secs) * MSECS_PER_SEC; + DEBUG("rx time with 3gpp base in ms %" PRIu64 "\n", time_3gpp_msecs); // calculate SFN and SF index according to TS 36.331 Sec. 5.10.14 - q->frame_number = ((uint32_t)floor(0.1 * (time_diff_msecs - q->sfn_offset))) % 1024; - q->sf_idx = ((uint32_t)floor(time_diff_msecs - q->sfn_offset)) % 10; + q->frame_number = (uint32_t)(((uint64_t)floor(0.1 * (time_3gpp_msecs - q->sfn_offset))) % 1024); + q->sf_idx = (uint32_t)(((uint64_t)floor(time_3gpp_msecs - q->sfn_offset)) % 10); return SRSRAN_SUCCESS; } diff --git a/lib/src/phy/ue/ue_sync_nbiot.c b/lib/src/phy/ue/ue_sync_nbiot.c index ac6357e49..ade2e30e9 100644 --- a/lib/src/phy/ue/ue_sync_nbiot.c +++ b/lib/src/phy/ue/ue_sync_nbiot.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/ue/ue_sync_nr.c b/lib/src/phy/ue/ue_sync_nr.c new file mode 100644 index 000000000..e2eae330f --- /dev/null +++ b/lib/src/phy/ue/ue_sync_nr.c @@ -0,0 +1,336 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/phy/ue/ue_sync_nr.h" +#include "srsran/phy/utils/vector.h" + +#define UE_SYNC_NR_DEFAULT_CFO_ALPHA 0.1 + +int srsran_ue_sync_nr_init(srsran_ue_sync_nr_t* q, const srsran_ue_sync_nr_args_t* args) +{ + // Check inputs + if (q == NULL || args == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Copy arguments + q->recv_obj = args->recv_obj; + q->recv_callback = args->recv_callback; + q->nof_rx_channels = args->nof_rx_channels == 0 ? 1 : args->nof_rx_channels; + q->disable_cfo = args->disable_cfo; + q->cfo_alpha = isnormal(args->cfo_alpha) ? args->cfo_alpha : UE_SYNC_NR_DEFAULT_CFO_ALPHA; + + // Initialise SSB + srsran_ssb_args_t ssb_args = {}; + ssb_args.max_srate_hz = args->max_srate_hz; + ssb_args.min_scs = args->min_scs; + ssb_args.enable_search = true; + ssb_args.enable_decode = true; + ssb_args.pbch_dmrs_thr = args->pbch_dmrs_thr; + if (srsran_ssb_init(&q->ssb, &ssb_args) < SRSRAN_SUCCESS) { + ERROR("Error SSB init"); + return SRSRAN_ERROR; + } + + // Allocate temporal buffer pointers + q->tmp_buffer = SRSRAN_MEM_ALLOC(cf_t*, q->nof_rx_channels); + if (q->tmp_buffer == NULL) { + ERROR("Error alloc"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void srsran_ue_sync_nr_free(srsran_ue_sync_nr_t* q) +{ + // Check inputs + if (q == NULL) { + return; + } + + srsran_ssb_free(&q->ssb); + + if (q->tmp_buffer) { + free(q->tmp_buffer); + } + + SRSRAN_MEM_ZERO(q, srsran_ue_sync_nr_t, 1); +} + +int srsran_ue_sync_nr_set_cfg(srsran_ue_sync_nr_t* q, const srsran_ue_sync_nr_cfg_t* cfg) +{ + // Check inputs + if (q == NULL || cfg == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Copy parameters + q->N_id = cfg->N_id; + q->srate_hz = cfg->ssb.srate_hz; + + // Calculate new subframe size + q->sf_sz = (uint32_t)round(1e-3 * q->srate_hz); + + // Configure SSB + if (srsran_ssb_set_cfg(&q->ssb, &cfg->ssb) < SRSRAN_SUCCESS) { + ERROR("Error configuring SSB"); + return SRSRAN_ERROR; + } + + // Transition to find + q->state = SRSRAN_UE_SYNC_NR_STATE_FIND; + + return SRSRAN_SUCCESS; +} + +static void ue_sync_nr_reset_feedback(srsran_ue_sync_nr_t* q) +{ + SRSRAN_MEM_ZERO(&q->feedback, srsran_csi_trs_measurements_t, 1); +} + +static void ue_sync_nr_apply_feedback(srsran_ue_sync_nr_t* q) +{ + // Skip any update if there is no feedback available + if (q->feedback.nof_re == 0) { + return; + } + + // Update number of samples + q->avg_delay_us = q->feedback.delay_us; + q->next_rf_sample_offset = (uint32_t)round((double)q->avg_delay_us * (q->srate_hz * 1e-6)); + + // Integrate CFO + if (q->disable_cfo) { + q->cfo_hz = SRSRAN_VEC_SAFE_EMA(q->feedback.cfo_hz, q->cfo_hz, q->cfo_alpha); + } else { + q->cfo_hz += q->feedback.cfo_hz * q->cfo_alpha; + } + + // Reset feedback + ue_sync_nr_reset_feedback(q); +} + +static int ue_sync_nr_update_ssb(srsran_ue_sync_nr_t* q, + const srsran_csi_trs_measurements_t* measurements, + const srsran_pbch_msg_nr_t* pbch_msg) +{ + srsran_mib_nr_t mib = {}; + if (srsran_pbch_msg_nr_mib_unpack(pbch_msg, &mib) != SRSRAN_SUCCESS) { + return SRSRAN_SUCCESS; + } + + // Reset feedback to prevent any previous erroneous measurement + ue_sync_nr_reset_feedback(q); + + // Set feedback measurement + srsran_combine_csi_trs_measurements(&q->feedback, measurements, &q->feedback); + + // Apply feedback + ue_sync_nr_apply_feedback(q); + + // Setup context + q->ssb_idx = pbch_msg->ssb_idx; + q->sf_idx = srsran_ssb_candidate_sf_idx(&q->ssb, pbch_msg->ssb_idx, pbch_msg->hrf); + q->sfn = mib.sfn; + + // Transition to track only if the measured delay is below 2.4 microseconds + if (measurements->delay_us < 2.4f) { + q->state = SRSRAN_UE_SYNC_NR_STATE_TRACK; + } + + return SRSRAN_SUCCESS; +} + +static int ue_sync_nr_run_find(srsran_ue_sync_nr_t* q, cf_t* buffer) +{ + srsran_csi_trs_measurements_t measurements = {}; + srsran_pbch_msg_nr_t pbch_msg = {}; + + // Find SSB, measure PSS/SSS and decode PBCH + if (srsran_ssb_find(&q->ssb, buffer, q->N_id, &measurements, &pbch_msg) < SRSRAN_SUCCESS) { + ERROR("Error finding SSB"); + return SRSRAN_ERROR; + } + + // If the PBCH message was NOT decoded, early return + if (!pbch_msg.crc) { + return SRSRAN_SUCCESS; + } + + return ue_sync_nr_update_ssb(q, &measurements, &pbch_msg); +} + +static int ue_sync_nr_run_track(srsran_ue_sync_nr_t* q, cf_t* buffer) +{ + srsran_csi_trs_measurements_t measurements = {}; + srsran_pbch_msg_nr_t pbch_msg = {}; + uint32_t half_frame = q->sf_idx / (SRSRAN_NOF_SF_X_FRAME / 2); + + // Check if the SSB selected candidate index shall be received in this subframe + bool is_ssb_opportunity = (q->sf_idx == srsran_ssb_candidate_sf_idx(&q->ssb, q->ssb_idx, half_frame > 0)); + + // Use SSB periodicity + if (q->ssb.cfg.periodicity_ms >= 10) { + // SFN match with the periodicity + is_ssb_opportunity = is_ssb_opportunity && (half_frame == 0) && (q->sfn % q->ssb.cfg.periodicity_ms / 10 == 0); + } + + if (!is_ssb_opportunity) { + return SRSRAN_SUCCESS; + } + + // Measure PSS/SSS and decode PBCH + if (srsran_ssb_track(&q->ssb, buffer, q->N_id, q->ssb_idx, half_frame, &measurements, &pbch_msg) < SRSRAN_SUCCESS) { + ERROR("Error finding SSB"); + return SRSRAN_ERROR; + } + + // If the PBCH message was NOT decoded, transition to find + if (!pbch_msg.crc) { + q->state = SRSRAN_UE_SYNC_NR_STATE_FIND; + return SRSRAN_SUCCESS; + } + + return ue_sync_nr_update_ssb(q, &measurements, &pbch_msg); +} + +static int ue_sync_nr_recv(srsran_ue_sync_nr_t* q, cf_t** buffer, srsran_timestamp_t* timestamp) +{ + // Verify callback and srate are valid + if (q->recv_callback == NULL && !isnormal(q->srate_hz)) { + return SRSRAN_ERROR; + } + + uint32_t buffer_offset = 0; + uint32_t nof_samples = q->sf_sz; + + if (q->next_rf_sample_offset > 0) { + // Discard a number of samples from RF + if (q->recv_callback(q->recv_obj, buffer, (uint32_t)q->next_rf_sample_offset, timestamp) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } else { + // Adjust receive buffer + buffer_offset = (uint32_t)(-q->next_rf_sample_offset); + nof_samples = (uint32_t)(q->sf_sz + q->next_rf_sample_offset); + } + q->next_rf_sample_offset = 0; + + // Select buffer offsets + for (uint32_t chan = 0; chan < q->nof_rx_channels; chan++) { + // Set buffer to NULL if not present + if (buffer[chan] == NULL) { + q->tmp_buffer[chan] = NULL; + continue; + } + + // Initialise first offset samples to zero + if (buffer_offset > 0) { + srsran_vec_cf_zero(buffer[chan], buffer_offset); + } + + // Set to sample index + q->tmp_buffer[chan] = &buffer[chan][buffer_offset]; + } + + // Receive + if (q->recv_callback(q->recv_obj, q->tmp_buffer, nof_samples, timestamp) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Compensate CFO + for (uint32_t chan = 0; chan < q->nof_rx_channels; chan++) { + if (buffer[chan] != 0 && !q->disable_cfo) { + srsran_vec_apply_cfo(buffer[chan], -q->cfo_hz / q->srate_hz, buffer[chan], (int)q->sf_sz); + } + } + + return SRSRAN_SUCCESS; +} + +int srsran_ue_sync_nr_zerocopy(srsran_ue_sync_nr_t* q, cf_t** buffer, srsran_ue_sync_nr_outcome_t* outcome) +{ + // Check inputs + if (q == NULL || buffer == NULL || outcome == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Verify callback is valid + if (q->recv_callback == NULL) { + return SRSRAN_ERROR; + } + + // Receive + if (ue_sync_nr_recv(q, buffer, &outcome->timestamp) < SRSRAN_SUCCESS) { + ERROR("Error receiving baseband"); + return SRSRAN_ERROR; + } + + // Run FSM + switch (q->state) { + case SRSRAN_UE_SYNC_NR_STATE_IDLE: + // Do nothing + break; + case SRSRAN_UE_SYNC_NR_STATE_FIND: + if (ue_sync_nr_run_find(q, buffer[0]) < SRSRAN_SUCCESS) { + ERROR("Error running find"); + return SRSRAN_ERROR; + } + break; + case SRSRAN_UE_SYNC_NR_STATE_TRACK: + if (ue_sync_nr_run_track(q, buffer[0]) < SRSRAN_SUCCESS) { + ERROR("Error running track"); + return SRSRAN_ERROR; + } + break; + } + + // Increment subframe counter + q->sf_idx++; + + // Increment SFN + if (q->sf_idx >= SRSRAN_NOF_SF_X_FRAME) { + q->sfn = (q->sfn + 1) % 1024; + q->sf_idx = 0; + } + + // Fill outcome + outcome->in_sync = (q->state == SRSRAN_UE_SYNC_NR_STATE_TRACK); + outcome->sf_idx = q->sf_idx; + outcome->sfn = q->sfn; + outcome->cfo_hz = q->cfo_hz; + outcome->delay_us = q->avg_delay_us; + + return SRSRAN_SUCCESS; +} + +int srsran_ue_sync_nr_feedback(srsran_ue_sync_nr_t* q, const srsran_csi_trs_measurements_t* measurements) +{ + if (q == NULL || measurements == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Accumulate feedback proportional to the number of elements provided by the measurement + srsran_combine_csi_trs_measurements(&q->feedback, measurements, &q->feedback); + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/phy/ue/ue_ul.c b/lib/src/phy/ue/ue_ul.c index 959438840..fa4326a21 100644 --- a/lib/src/phy/ue/ue_ul.c +++ b/lib/src/phy/ue/ue_ul.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -174,6 +174,25 @@ int srsran_ue_ul_set_cell(srsran_ue_ul_t* q, srsran_cell_t cell) return ret; } +int srsran_ue_ul_set_cfr(srsran_ue_ul_t* q, const srsran_cfr_cfg_t* cfr) +{ + if (q == NULL || cfr == NULL) { + ERROR("Error, invalid inputs"); + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Copy the cfr config into the UE + q->cfr_config = *cfr; + + // Set the cfr for the fft's + if (srsran_ofdm_set_cfr(&q->fft, &q->cfr_config) < SRSRAN_SUCCESS) { + ERROR("Error setting the CFR for the fft"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + int srsran_ue_ul_pregen_signals(srsran_ue_ul_t* q, srsran_ue_ul_cfg_t* cfg) { if (q->signals_pregenerated) { diff --git a/lib/src/phy/ue/ue_ul_nr.c b/lib/src/phy/ue/ue_ul_nr.c index a556e501b..750326e2f 100644 --- a/lib/src/phy/ue/ue_ul_nr.c +++ b/lib/src/phy/ue/ue_ul_nr.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -77,10 +77,11 @@ int srsran_ue_ul_nr_set_carrier(srsran_ue_ul_nr_t* q, const srsran_carrier_nr_t* q->carrier = *carrier; - srsran_ofdm_cfg_t fft_cfg = {}; - fft_cfg.nof_prb = carrier->nof_prb; - fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); - fft_cfg.keep_dc = true; + srsran_ofdm_cfg_t fft_cfg = {}; + fft_cfg.nof_prb = carrier->nof_prb; + fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); + fft_cfg.keep_dc = true; + fft_cfg.phase_compensation_hz = carrier->ul_center_frequency_hz; if (srsran_ofdm_tx_init_cfg(&q->ifft, &fft_cfg) < SRSRAN_SUCCESS) { ERROR("Initiating OFDM"); return SRSRAN_ERROR; @@ -104,6 +105,15 @@ int srsran_ue_ul_nr_set_carrier(srsran_ue_ul_nr_t* q, const srsran_carrier_nr_t* return SRSRAN_SUCCESS; } +void srsran_ue_ul_nr_set_freq_offset(srsran_ue_ul_nr_t* q, float freq_offset_hz) +{ + if (q == NULL) { + return; + } + + q->freq_offset_hz = -freq_offset_hz; +} + int srsran_ue_ul_nr_encode_pusch(srsran_ue_ul_nr_t* q, const srsran_slot_cfg_t* slot_cfg, const srsran_sch_cfg_nr_t* pusch_cfg, @@ -139,6 +149,12 @@ int srsran_ue_ul_nr_encode_pusch(srsran_ue_ul_nr_t* q, srsran_vec_sc_prod_cfc(q->ifft.cfg.out_buffer, 0.99f / max_peak, q->ifft.cfg.out_buffer, q->ifft.sf_sz); } + // Apply frequency offset + if (isnormal(q->freq_offset_hz)) { + srsran_vec_apply_cfo( + q->ifft.cfg.out_buffer, -q->freq_offset_hz / (1000.0f * q->ifft.sf_sz), q->ifft.cfg.out_buffer, q->ifft.sf_sz); + } + return SRSRAN_SUCCESS; } @@ -159,7 +175,7 @@ static int ue_ul_nr_encode_pucch_format1(srsran_ue_ul_nr_t* q, uint8_t b[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS] = {}; // Set ACK bits - uint32_t nof_bits = SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS, uci_data->cfg.o_ack); + uint32_t nof_bits = SRSRAN_MIN(SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS, uci_data->cfg.ack.count); for (uint32_t i = 0; i < nof_bits; i++) { b[i] = uci_data->value.ack[i]; } @@ -228,6 +244,19 @@ int srsran_ue_ul_nr_encode_pucch(srsran_ue_ul_nr_t* q, // Generate signal srsran_ofdm_tx_sf(&q->ifft); + // Normalise to peak + uint32_t max_idx = srsran_vec_max_abs_ci(q->ifft.cfg.out_buffer, q->ifft.sf_sz); + float max_peak = cabsf(q->ifft.cfg.out_buffer[max_idx]); + if (isnormal(max_peak)) { + srsran_vec_sc_prod_cfc(q->ifft.cfg.out_buffer, 0.99f / max_peak, q->ifft.cfg.out_buffer, q->ifft.sf_sz); + } + + // Apply frequency offset + if (isnormal(q->freq_offset_hz)) { + srsran_vec_apply_cfo( + q->ifft.cfg.out_buffer, -q->freq_offset_hz / (1000.0f * q->ifft.sf_sz), q->ifft.cfg.out_buffer, q->ifft.sf_sz); + } + return SRSRAN_SUCCESS; } @@ -258,6 +287,8 @@ int srsran_ue_ul_nr_pusch_info(const srsran_ue_ul_nr_t* q, // Append PDSCH info len += srsran_pusch_nr_tx_info(&q->pusch, cfg, &cfg->grant, uci_value, &str[len], str_len - len); + len = srsran_print_check(str, str_len, len, " cfo=%.0f", q->freq_offset_hz); + return len; } @@ -269,7 +300,7 @@ int srsran_ue_ul_nr_pucch_info(const srsran_pucch_nr_resource_t* resource, int len = 0; // Append PDSCH info - len += srsran_pucch_nr_tx_info(resource, uci_data, &str[len], str_len - len); + len += srsran_pucch_nr_info(resource, uci_data, &str[len], str_len - len); return len; } diff --git a/lib/src/phy/utils/CMakeLists.txt b/lib/src/phy/utils/CMakeLists.txt index da8c239f1..96eebb9a6 100644 --- a/lib/src/phy/utils/CMakeLists.txt +++ b/lib/src/phy/utils/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/utils/bit.c b/lib/src/phy/utils/bit.c index fb82e38ac..ff0476a8b 100644 --- a/lib/src/phy/utils/bit.c +++ b/lib/src/phy/utils/bit.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -736,6 +736,22 @@ void srsran_bit_unpack(uint32_t value, uint8_t** bits, int nof_bits) *bits += nof_bits; } +/** + * Unpacks nof_bits from LSBs of value in LSB order to *bits. Advances pointer past unpacked bits. + * + * @param[in] value nof_bits lowest order bits will be unpacked in MSB order + * @param[in] nof_bits Number of bits to unpack + * @param[out] bits Points to buffer pointer. The buffer pointer will be advanced by nof_bits + */ +void srsran_bit_unpack_lsb(uint32_t value, uint8_t** bits, int nof_bits) +{ + int i; + for (i = 0; i < nof_bits; i++) { + (*bits)[nof_bits - i - 1] = (value >> (nof_bits - i - 1)) & 0x1; + } + *bits += nof_bits; +} + void srsran_bit_pack_vector(uint8_t* unpacked, uint8_t* packed, int nof_bits) { uint32_t i, nbytes; diff --git a/lib/src/phy/utils/cexptab.c b/lib/src/phy/utils/cexptab.c index a4c017418..7dc95ae68 100644 --- a/lib/src/phy/utils/cexptab.c +++ b/lib/src/phy/utils/cexptab.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/utils/convolution.c b/lib/src/phy/utils/convolution.c index 691819ecf..98658bfaf 100644 --- a/lib/src/phy/utils/convolution.c +++ b/lib/src/phy/utils/convolution.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/utils/debug.c b/lib/src/phy/utils/debug.c index 018d37175..b3b7267a0 100644 --- a/lib/src/phy/utils/debug.c +++ b/lib/src/phy/utils/debug.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,8 +22,33 @@ #include "srsran/phy/utils/debug.h" #include -int srsran_verbose = 0; -int handler_registered = 0; +static int srsran_verbose = 0; +static bool handler_registered = false; + +int get_srsran_verbose_level(void) +{ + return srsran_verbose; +} + +void set_srsran_verbose_level(int level) +{ + srsran_verbose = level; +} + +void increase_srsran_verbose_level(void) +{ + srsran_verbose++; +} + +bool is_handler_registered(void) +{ + return handler_registered; +} + +void set_handler_enabled(bool enable) +{ + handler_registered = enable; +} void get_time_interval(struct timeval* tdata) { diff --git a/lib/src/phy/utils/filter.c b/lib/src/phy/utils/filter.c index 957bd814b..edfa38bcc 100644 --- a/lib/src/phy/utils/filter.c +++ b/lib/src/phy/utils/filter.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/utils/mat.c b/lib/src/phy/utils/mat.c index 0659810d8..70a13798b 100644 --- a/lib/src/phy/utils/mat.c +++ b/lib/src/phy/utils/mat.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/utils/phy_logger.c b/lib/src/phy/utils/phy_logger.c index 933ca4360..df9605cf4 100644 --- a/lib/src/phy/utils/phy_logger.c +++ b/lib/src/phy/utils/phy_logger.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,7 +38,7 @@ void srsran_phy_log_register_handler(void* ctx, phy_log_handler_t handler) { phy_log_handler = handler; callback_ctx = ctx; - handler_registered++; + set_handler_enabled(true); } void srsran_phy_log_print(phy_logger_level_t log_level, const char* format, ...) diff --git a/lib/src/phy/utils/primes.c b/lib/src/phy/utils/primes.c index b61794a24..031e4fcdb 100644 --- a/lib/src/phy/utils/primes.c +++ b/lib/src/phy/utils/primes.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/utils/random.cpp b/lib/src/phy/utils/random.cpp index 0bb4c859d..f8e411949 100644 --- a/lib/src/phy/utils/random.cpp +++ b/lib/src/phy/utils/random.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,6 +20,7 @@ */ #include "srsran/phy/utils/random.h" +#include "srsran/phy/utils/bit.h" #include #include @@ -45,6 +46,37 @@ public: return dist(*mt19937); } + void byte_vector(uint8_t* buffer, uint32_t n) + { + if (buffer == NULL || n == 0) { + return; + } + + uint32_t i = 0; + uint32_t* buffer_u32 = (uint32_t*)buffer; + for (; i < n / sizeof(uint32_t); i++) { + buffer_u32[i] = (uint32_t)(*mt19937)(); + } + i *= (uint32_t)sizeof(uint32_t); + for (; i < n; i++) { + buffer[i] = (uint8_t)((*mt19937)() & 0xffUL); + } + } + + void bit_vector(uint8_t* buffer, uint32_t n) + { + if (buffer == NULL || n == 0) { + return; + } + + uint32_t i = 0; + uint8_t* ptr = buffer; + for (; i < n / 32; i++) { + srsran_bit_unpack((uint32_t)(*mt19937)(), &ptr, 32); + } + srsran_bit_unpack((uint32_t)(*mt19937)(), &ptr, n - i * 32); + } + float gauss_dist(float sigma) { std::normal_distribution dist(sigma); @@ -122,6 +154,24 @@ bool srsran_random_bool(srsran_random_t q, float prob_true) return srsran_random_uniform_real_dist(q, 0, 1) < prob_true; } +void srsran_random_byte_vector(srsran_random_t q, uint8_t* c, uint32_t nsamples) +{ + if (q == nullptr) { + return; + } + auto* h = (random_wrap*)q; + h->byte_vector(c, nsamples); +} + +void srsran_random_bit_vector(srsran_random_t q, uint8_t* c, uint32_t nsamples) +{ + if (q == nullptr) { + return; + } + auto* h = (random_wrap*)q; + h->bit_vector(c, nsamples); +} + void srsran_random_free(srsran_random_t q) { if (q) { diff --git a/lib/src/phy/utils/re_pattern.c b/lib/src/phy/utils/re_pattern.c index 32a7ea744..3f24fe688 100644 --- a/lib/src/phy/utils/re_pattern.c +++ b/lib/src/phy/utils/re_pattern.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/utils/ringbuffer.c b/lib/src/phy/utils/ringbuffer.c index 19ac49613..c855cb038 100644 --- a/lib/src/phy/utils/ringbuffer.c +++ b/lib/src/phy/utils/ringbuffer.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -87,7 +87,11 @@ int srsran_ringbuffer_resize(srsran_ringbuffer_t* q, int capacity) int srsran_ringbuffer_status(srsran_ringbuffer_t* q) { - return q->count; + int status = 0; + pthread_mutex_lock(&q->mutex); + status = q->count; + pthread_mutex_unlock(&q->mutex); + return status; } int srsran_ringbuffer_space(srsran_ringbuffer_t* q) diff --git a/lib/src/phy/utils/test/CMakeLists.txt b/lib/src/phy/utils/test/CMakeLists.txt index ff819ec08..a44fe1280 100644 --- a/lib/src/phy/utils/test/CMakeLists.txt +++ b/lib/src/phy/utils/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/phy/utils/test/dft_test.c b/lib/src/phy/utils/test/dft_test.c index fc99148c1..4324e366f 100644 --- a/lib/src/phy/utils/test/dft_test.c +++ b/lib/src/phy/utils/test/dft_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/utils/test/mat_test.c b/lib/src/phy/utils/test/mat_test.c index a02dc627f..5ffc13b13 100644 --- a/lib/src/phy/utils/test/mat_test.c +++ b/lib/src/phy/utils/test/mat_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/phy/utils/test/re_pattern_test.c b/lib/src/phy/utils/test/re_pattern_test.c index bb077508b..aac060fc7 100644 --- a/lib/src/phy/utils/test/re_pattern_test.c +++ b/lib/src/phy/utils/test/re_pattern_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,8 +19,8 @@ * */ -#include "srsran/common/test_common.h" #include "srsran/phy/utils/re_pattern.h" +#include "srsran/support/srsran_test.h" int main(int argc, char** argv) { diff --git a/lib/src/phy/utils/test/ring_buffer_test.c b/lib/src/phy/utils/test/ring_buffer_test.c index c56d8eeea..c82454dce 100644 --- a/lib/src/phy/utils/test/ring_buffer_test.c +++ b/lib/src/phy/utils/test/ring_buffer_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsran/common/test_common.h" +#include "srsran/support/srsran_test.h" #include #include #include diff --git a/lib/src/phy/utils/test/vector_test.c b/lib/src/phy/utils/test/vector_test.c index b666bd40c..79a06aa85 100644 --- a/lib/src/phy/utils/test/vector_test.c +++ b/lib/src/phy/utils/test/vector_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -845,6 +845,38 @@ TEST( free(z); srsran_cfo_free(&srsran_cfo);) +// This test compares the clipping method used for the CFR module in its default configuration to the original CFR +// algorithm. The original algorithm can still be used by defining CFR_PEAK_EXTRACTION in the CFR module. +TEST( + srsran_vec_gen_clip_env, MALLOC(cf_t, x); MALLOC(float, x_abs); MALLOC(float, env); float thres = 0.5f; + float alpha = 0.5f; + cf_t gold = 0.0f; + + for (int i = 0; i < block_size; i++) { + x[i] = RANDOM_F(); + env[i] = 0.0f; + x_abs[i] = cabsf(x[i]); + } + + // current implementation generates an amplitude envelope which is then multiplied with the signal + TEST_CALL(srsran_vec_gen_clip_env(x_abs, thres, alpha, env, block_size)) + + // Recreates the original method for clipping the signal, skipping the low-pass filtering + for (int i = 0; i < block_size; i++) { + if (x_abs[i] <= thres) { + gold = x[i]; + } else { + cf_t peak = x[i] - (thres * x[i] / x_abs[i]); // extract the peak + gold = x[i] - alpha * peak; // subtract the peak from the signal, scaled by alpha + } + // Compare the two clipping methods by applying the envelope to x and determining the error + mse += cabsf(gold - env[i] * x[i]); + } if (isnormal(mse)) { mse /= block_size; } + + free(x); + free(x_abs); + free(env);) + int main(int argc, char** argv) { char func_names[MAX_FUNCTIONS][32]; @@ -1023,6 +1055,10 @@ int main(int argc, char** argv) test_srsran_cfo_correct_change(func_names[func_count], &timmings[func_count][size_count], block_size); func_count++; + passed[func_count][size_count] = + test_srsran_vec_gen_clip_env(func_names[func_count], &timmings[func_count][size_count], block_size); + func_count++; + sizes[size_count] = block_size; size_count++; } diff --git a/lib/src/phy/utils/vector.c b/lib/src/phy/utils/vector.c index 9fcf0587c..321d54920 100644 --- a/lib/src/phy/utils/vector.c +++ b/lib/src/phy/utils/vector.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -62,6 +62,12 @@ void srsran_vec_sub_bbb(const int8_t* x, const int8_t* y, int8_t* z, const uint3 srsran_vec_sub_bbb_simd(x, y, z, len); } +/* sum a scalar to all elements of a vector */ +void srsran_vec_sc_sum_fff(const float* x, float h, float* z, uint32_t len) +{ + srsran_vec_sc_sum_fff_simd(x, h, z, len); +} + // Noise estimation in chest_dl, interpolation void srsran_vec_sub_ccc(const cf_t* x, const cf_t* y, cf_t* z, const uint32_t len) { @@ -363,7 +369,7 @@ void srsran_vec_fprint_hex(FILE* stream, uint8_t* x, const uint32_t len) fprintf(stream, "];\n"); } -void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len) +uint32_t srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, const uint32_t len) { uint32_t i, nbytes; uint8_t byte; @@ -371,7 +377,7 @@ void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, co // check that hex string fits in buffer (every byte takes 3 characters, plus brackets) if ((3 * (len / 8 + ((len % 8) ? 1 : 0))) + 2 >= max_str_len) { ERROR("Buffer too small for printing hex string (max_str_len=%d, payload_len=%d).", max_str_len, len); - return; + return 0; } int n = 0; @@ -385,7 +391,10 @@ void srsran_vec_sprint_hex(char* str, const uint32_t max_str_len, uint8_t* x, co n += sprintf(&str[n], "%02x ", byte); } n += sprintf(&str[n], "]"); + str[n] = 0; str[max_str_len - 1] = 0; + + return n; } void srsran_vec_sprint_bin(char* str, const uint32_t max_str_len, const uint8_t* x, const uint32_t len) @@ -580,7 +589,11 @@ int32_t srsran_vec_dot_prod_sss(const int16_t* x, const int16_t* y, const uint32 float srsran_vec_avg_power_cf(const cf_t* x, const uint32_t len) { - return crealf(srsran_vec_dot_prod_conj_ccc(x, x, len)) / len; + if (!len) { + return 0; + } else { + return crealf(srsran_vec_dot_prod_conj_ccc(x, x, len)) / len; + } } float srsran_vec_avg_power_sf(const int16_t* x, const uint32_t len) @@ -627,6 +640,17 @@ float srsran_vec_avg_power_bf(const int8_t* x, const uint32_t len) return acc; } +float srsran_vec_avg_power_ff(const float* x, const uint32_t len) +{ + if (!len) { + return 0; + } else { + float pwr_symb_avg = srsran_vec_dot_prod_fff(x, x, len); + pwr_symb_avg /= (float)len; + return pwr_symb_avg; + } +} + // Correlation assumes zero-mean x and y float srsran_vec_corr_ccc(const cf_t* x, cf_t* y, const uint32_t len) { @@ -823,9 +847,9 @@ void srsran_vec_interleave_add(const cf_t* x, const cf_t* y, cf_t* z, const int srsran_vec_interleave_add_simd(x, y, z, len); } -void srsran_vec_gen_sine(cf_t amplitude, float freq, cf_t* z, int len) +cf_t srsran_vec_gen_sine(cf_t amplitude, float freq, cf_t* z, int len) { - srsran_vec_gen_sine_simd(amplitude, freq, z, len); + return srsran_vec_gen_sine_simd(amplitude, freq, z, len); } void srsran_vec_apply_cfo(const cf_t* x, float cfo, cf_t* z, int len) @@ -837,3 +861,38 @@ float srsran_vec_estimate_frequency(const cf_t* x, int len) { return srsran_vec_estimate_frequency_simd(x, len); } + +// TODO: implement with SIMD +void srsran_vec_gen_clip_env(const float* x_abs, const float thres, const float alpha, float* env, const int len) +{ + for (int i = 0; i < len; i++) { + env[i] = (x_abs[i] > thres) ? (1 - alpha) + alpha * thres / x_abs[i] : 1; + } +} + +float srsran_vec_papr_c(const cf_t* in, const int len) +{ + uint32_t max = srsran_vec_max_abs_ci(in, len); + float peak = SRSRAN_CSQABS(in[max]); + return peak / srsran_vec_avg_power_cf(in, len); +} + +float srsran_vec_acpr_c(const cf_t* x_f, const uint32_t win_pos_len, const uint32_t win_neg_len, const uint32_t len) +{ + // The adjacent channel cannot extend beyond the FFT len + const uint32_t ch_len = win_pos_len + win_neg_len; + const uint32_t adj_ch_len = ch_len > len / 2 ? len - ch_len : ch_len; + + // Integrate positive half of the signal power spectrum + float signal_pwr = srsran_vec_dot_prod_conj_ccc(x_f, x_f, win_pos_len); + // Integrate negative halt of the signal power spectrum + signal_pwr += srsran_vec_dot_prod_conj_ccc(x_f + len - win_neg_len, x_f + len - win_neg_len, win_neg_len); + + const float adj_ch_pwr = srsran_vec_dot_prod_conj_ccc(x_f + win_pos_len, x_f + win_pos_len, adj_ch_len); + + if (isnormal(signal_pwr)) { + return adj_ch_pwr / signal_pwr; + } else { + return 0; + } +} diff --git a/lib/src/phy/utils/vector_simd.c b/lib/src/phy/utils/vector_simd.c index 8528fdc0a..bbef51d15 100644 --- a/lib/src/phy/utils/vector_simd.c +++ b/lib/src/phy/utils/vector_simd.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -721,6 +721,36 @@ void srsran_vec_sub_fff_simd(const float* x, const float* y, float* z, const int } } +void srsran_vec_sc_sum_fff_simd(const float* x, float h, float* z, int len) +{ + int i = 0; + +#if SRSRAN_SIMD_F_SIZE + const simd_f_t hh = srsran_simd_f_set1(h); + if (SRSRAN_IS_ALIGNED(x) && SRSRAN_IS_ALIGNED(z)) { + for (; i < len - SRSRAN_SIMD_F_SIZE + 1; i += SRSRAN_SIMD_F_SIZE) { + simd_f_t xx = srsran_simd_f_load(&x[i]); + + simd_f_t zz = srsran_simd_f_add(xx, hh); + + srsran_simd_f_store(&z[i], zz); + } + } else { + for (; i < len - SRSRAN_SIMD_F_SIZE + 1; i += SRSRAN_SIMD_F_SIZE) { + simd_f_t xx = srsran_simd_f_loadu(&x[i]); + + simd_f_t zz = srsran_simd_f_add(xx, hh); + + srsran_simd_f_storeu(&z[i], zz); + } + } +#endif + + for (; i < len; i++) { + z[i] = x[i] + h; + } +} + cf_t srsran_vec_dot_prod_ccc_simd(const cf_t* x, const cf_t* y, const int len) { int i = 0; @@ -1386,7 +1416,7 @@ uint32_t srsran_vec_max_fi_simd(const float* x, const int len) simd_f_t a = srsran_simd_f_load(&x[i]); simd_sel_t res = srsran_simd_f_max(a, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res); + simd_max_values = srsran_simd_f_select(simd_max_values, a, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } else { @@ -1394,7 +1424,7 @@ uint32_t srsran_vec_max_fi_simd(const float* x, const int len) simd_f_t a = srsran_simd_f_loadu(&x[i]); simd_sel_t res = srsran_simd_f_max(a, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res); + simd_max_values = srsran_simd_f_select(simd_max_values, a, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } @@ -1444,7 +1474,7 @@ uint32_t srsran_vec_max_abs_fi_simd(const float* x, const int len) simd_f_t a = srsran_simd_f_abs(srsran_simd_f_load(&x[i])); simd_sel_t res = srsran_simd_f_max(a, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res); + simd_max_values = srsran_simd_f_select(simd_max_values, a, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } else { @@ -1452,7 +1482,7 @@ uint32_t srsran_vec_max_abs_fi_simd(const float* x, const int len) simd_f_t a = srsran_simd_f_abs(srsran_simd_f_loadu(&x[i])); simd_sel_t res = srsran_simd_f_max(a, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)a, res); + simd_max_values = srsran_simd_f_select(simd_max_values, a, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } @@ -1511,7 +1541,7 @@ uint32_t srsran_vec_max_ci_simd(const cf_t* x, const int len) simd_sel_t res = srsran_simd_f_max(z1, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)z1, res); + simd_max_values = srsran_simd_f_select(simd_max_values, z1, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } else { @@ -1527,7 +1557,7 @@ uint32_t srsran_vec_max_ci_simd(const cf_t* x, const int len) simd_sel_t res = srsran_simd_f_max(z1, simd_max_values); simd_max_indexes = srsran_simd_i_select(simd_max_indexes, simd_indexes, res); - simd_max_values = (simd_f_t)srsran_simd_i_select((simd_i_t)simd_max_values, (simd_i_t)z1, res); + simd_max_values = srsran_simd_f_select(simd_max_values, z1, res); simd_indexes = srsran_simd_i_add(simd_indexes, simd_inc); } } @@ -1643,7 +1673,7 @@ void srsran_vec_interleave_add_simd(const cf_t* x, const cf_t* y, cf_t* z, const } } -void srsran_vec_gen_sine_simd(cf_t amplitude, float freq, cf_t* z, int len) +cf_t srsran_vec_gen_sine_simd(cf_t amplitude, float freq, cf_t* z, int len) { const float TWOPI = 2.0f * (float)M_PI; cf_t osc = cexpf(_Complex_I * TWOPI * freq); @@ -1687,27 +1717,28 @@ void srsran_vec_gen_sine_simd(cf_t amplitude, float freq, cf_t* z, int len) phase *= osc; } + return phase; } void srsran_vec_apply_cfo_simd(const cf_t* x, float cfo, cf_t* z, int len) { const float TWOPI = 2.0f * (float)M_PI; int i = 0; + cf_t osc = cexpf(_Complex_I * TWOPI * cfo); + cf_t phase = 1.0f; #if SRSRAN_SIMD_CF_SIZE - srsran_simd_aligned cf_t _osc[SRSRAN_SIMD_CF_SIZE]; + // Load initial phases and oscillator srsran_simd_aligned cf_t _phase[SRSRAN_SIMD_CF_SIZE]; - - if (i < len - SRSRAN_SIMD_CF_SIZE + 1) { - for (int k = 0; k < SRSRAN_SIMD_CF_SIZE; k++) { - _osc[k] = cexpf(_Complex_I * TWOPI * cfo * SRSRAN_SIMD_CF_SIZE); - _phase[k] = cexpf(_Complex_I * TWOPI * cfo * k); - } + _phase[0] = phase; + for (int k = 1; k < SRSRAN_SIMD_CF_SIZE; k++) { + _phase[k] = _phase[k - 1] * osc; } - simd_cf_t _simd_osc = srsran_simd_cfi_load(_osc); + simd_cf_t _simd_osc = srsran_simd_cf_set1(_phase[SRSRAN_SIMD_CF_SIZE - 1] * osc); simd_cf_t _simd_phase = srsran_simd_cfi_load(_phase); if (SRSRAN_IS_ALIGNED(x) && SRSRAN_IS_ALIGNED(z)) { + // For aligned memory for (; i < len - SRSRAN_SIMD_CF_SIZE + 1; i += SRSRAN_SIMD_CF_SIZE) { simd_cf_t a = srsran_simd_cfi_load(&x[i]); @@ -1718,6 +1749,7 @@ void srsran_vec_apply_cfo_simd(const cf_t* x, float cfo, cf_t* z, int len) _simd_phase = srsran_simd_cf_prod(_simd_phase, _simd_osc); } } else { + // For unaligned memory for (; i < len - SRSRAN_SIMD_F_SIZE + 1; i += SRSRAN_SIMD_F_SIZE) { simd_cf_t a = srsran_simd_cfi_loadu(&x[i]); @@ -1728,9 +1760,12 @@ void srsran_vec_apply_cfo_simd(const cf_t* x, float cfo, cf_t* z, int len) _simd_phase = srsran_simd_cf_prod(_simd_phase, _simd_osc); } } + + // Stores the next phase + srsran_simd_cfi_store(_phase, _simd_phase); + phase = _phase[0]; #endif - cf_t osc = cexpf(_Complex_I * TWOPI * cfo); - cf_t phase = cexpf(_Complex_I * TWOPI * cfo * i); + for (; i < len; i++) { z[i] = x[i] * phase; diff --git a/lib/src/radio/CMakeLists.txt b/lib/src/radio/CMakeLists.txt index 77b6570ca..f2bc81d34 100644 --- a/lib/src/radio/CMakeLists.txt +++ b/lib/src/radio/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -21,7 +21,7 @@ if(RF_FOUND) add_library(srsran_radio STATIC radio.cc channel_mapping.cc) target_link_libraries(srsran_radio srsran_rf srsran_common) - INSTALL(TARGETS srsran_radio DESTINATION ${LIBRARY_DIR}) + install(TARGETS srsran_radio DESTINATION ${LIBRARY_DIR} OPTIONAL) endif(RF_FOUND) add_subdirectory(test) diff --git a/lib/src/radio/channel_mapping.cc b/lib/src/radio/channel_mapping.cc index 6a9b28cac..e8562fa7f 100644 --- a/lib/src/radio/channel_mapping.cc +++ b/lib/src/radio/channel_mapping.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,11 +35,15 @@ bool channel_mapping::allocate_freq(const uint32_t& logical_ch, const float& fre { std::lock_guard lock(mutex); - if (allocated_channels.count(logical_ch)) { - ERROR("allocate_freq: Carrier logical_ch=%d already allocated to channel=%d", - logical_ch, - allocated_channels[logical_ch].carrier_idx); - return false; + // Check if the logical channel has already been allocated + if (allocated_channels.count(logical_ch) > 0) { + // If the current channel contains the frequency, do nothing else + if (allocated_channels[logical_ch].band.contains(freq)) { + return true; + } + + // Otherwise, release logical channel before searching for a new available channel + release_freq_(logical_ch); } // Find first available channel that supports this frequency and allocated it @@ -54,12 +58,17 @@ bool channel_mapping::allocate_freq(const uint32_t& logical_ch, const float& fre return false; } +void channel_mapping::release_freq_(const uint32_t& logical_ch) +{ + available_channels.push_back(allocated_channels[logical_ch]); + allocated_channels.erase(logical_ch); +} + bool channel_mapping::release_freq(const uint32_t& logical_ch) { std::lock_guard lock(mutex); if (allocated_channels.count(logical_ch)) { - available_channels.push_back(allocated_channels[logical_ch]); - allocated_channels.erase(logical_ch); + release_freq_(logical_ch); return true; } return false; diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index a89e013e9..70cf7d04c 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,35 +23,23 @@ #include "srsran/common/standard_streams.h" #include "srsran/common/string_helpers.h" #include "srsran/config.h" +#include "srsran/support/srsran_assert.h" #include #include #include namespace srsran { -radio::radio() : zeros(nullptr) +radio::radio() { - zeros = srsran_vec_cf_malloc(SRSRAN_SF_LEN_MAX); - srsran_vec_cf_zero(zeros, SRSRAN_SF_LEN_MAX); + zeros.resize(SRSRAN_SF_LEN_MAX, 0); for (uint32_t i = 0; i < SRSRAN_MAX_CHANNELS; i++) { - dummy_buffers[i] = srsran_vec_cf_malloc(SRSRAN_SF_LEN_MAX * SRSRAN_NOF_SF_X_FRAME); - srsran_vec_cf_zero(dummy_buffers[i], SRSRAN_SF_LEN_MAX * SRSRAN_NOF_SF_X_FRAME); + dummy_buffers[i].resize(SRSRAN_SF_LEN_MAX * SRSRAN_NOF_SF_X_FRAME, 0); } } radio::~radio() { - if (zeros) { - free(zeros); - zeros = nullptr; - } - - for (uint32_t i = 0; i < SRSRAN_MAX_CHANNELS; i++) { - if (dummy_buffers[i]) { - free(dummy_buffers[i]); - } - } - for (srsran_resampler_fft_t& q : interpolators) { srsran_resampler_fft_free(&q); } @@ -124,11 +112,30 @@ int radio::init(const rf_args_t& args, phy_interface_radio* phy_) rx_channel_mapping.set_config(nof_channels_x_dev, nof_antennas); // Init and start Radios - for (uint32_t device_idx = 0; device_idx < (uint32_t)device_args_list.size(); device_idx++) { - if (not open_dev(device_idx, args.device_name, device_args_list[device_idx])) { - logger.error("Error opening RF device %d", device_idx); + if (args.device_name != "file" || device_args_list[0] != "auto") { + // regular RF device + for (uint32_t device_idx = 0; device_idx < (uint32_t)device_args_list.size(); device_idx++) { + if (not open_dev(device_idx, args.device_name, device_args_list[device_idx])) { + logger.error("Error opening RF device %d", device_idx); + return SRSRAN_ERROR; + } + } + } else { + // file-based RF device abstraction using pre-opened FILE* objects + if (args.rx_files == nullptr && args.tx_files == nullptr) { + logger.error("File-based RF device abstraction requested, but no files provided"); return SRSRAN_ERROR; } + for (uint32_t device_idx = 0; device_idx < (uint32_t)device_args_list.size(); device_idx++) { + if (not open_dev(device_idx, + &args.rx_files[device_idx * nof_channels_x_dev], + &args.tx_files[device_idx * nof_channels_x_dev], + nof_channels_x_dev, + args.srate_hz)) { + logger.error("Error opening RF device %d", device_idx); + return SRSRAN_ERROR; + } + } } is_start_of_burst = true; @@ -231,10 +238,6 @@ void radio::stop() srsran_rf_stop_rx_stream(&rf_device); } } - if (zeros) { - free(zeros); - zeros = NULL; - } if (is_initialized) { for (srsran_rf_t& rf_device : rf_devices) { srsran_rf_close(&rf_device); @@ -370,7 +373,7 @@ bool radio::rx_dev(const uint32_t& device_idx, const rf_buffer_interface& buffer // Discard channels not allocated, need to point to valid buffer for (uint32_t i = 0; i < SRSRAN_MAX_CHANNELS; i++) { - radio_buffers[i] = dummy_buffers[i]; + radio_buffers[i] = dummy_buffers[i].data(); } if (not map_channels(rx_channel_mapping, device_idx, 0, buffer, radio_buffers)) { @@ -424,7 +427,7 @@ bool radio::tx(rf_buffer_interface& buffer, const rf_timestamp_interface& tx_tim uint32_t nof_samples = buffer.get_nof_samples(); // Check that number of the interpolated samples does not exceed the buffer size - if (ratio > 1 && nof_samples * ratio > tx_buffer[0].size()) { + if (ratio > 1 && (size_t)nof_samples * (size_t)ratio > tx_buffer[0].size()) { // This is a corner case that could happen during sample rate change transitions, as it does not have a negative // impact, log it as info. fmt::memory_buffer buff; @@ -498,6 +501,29 @@ bool radio::open_dev(const uint32_t& device_idx, const std::string& device_name, return true; } +bool radio::open_dev(const uint32_t &device_idx, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate) +{ + srsran_rf_t* rf_device = &rf_devices[device_idx]; + + srsran::console("Opening channels idx %d in RF device abstraction\n", device_idx); + + if (srsran_rf_open_file(rf_device, rx_files, tx_files, nof_channels, base_srate)) { + logger.error("Error opening RF device abstraction"); + return false; + } + + // Suppress radio stdout + srsran_rf_suppress_stdout(rf_device); + + // Register handler for processing O/U/L + srsran_rf_register_error_handler(rf_device, rf_msg_callback, this); + + // Get device info + rf_info[device_idx] = *srsran_rf_get_info(rf_device); + + return true; +} + bool radio::tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, const srsran_timestamp_t& tx_time_) { uint32_t nof_samples = buffer.get_nof_samples(); @@ -539,16 +565,15 @@ bool radio::tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, cons nof_samples = nof_samples - past_nsamples; // Subtracts the number of trimmed samples // Prints discarded samples - logger.debug("Detected RF overlap of %.1f us. Discarding %d samples. Power=%+.1f dBfs", + logger.debug("Detected RF overlap of %.1f us. Discarding %d samples.", srsran_timestamp_real(&ts_overlap) * 1.0e6, - past_nsamples, - srsran_convert_power_to_dB(srsran_vec_avg_power_cf(&buffer.get(0)[nof_samples], past_nsamples))); + past_nsamples); } else if (past_nsamples < 0 and not is_start_of_burst) { // if the gap is bigger than TX_MAX_GAP_ZEROS, stop burst if (fabs(srsran_timestamp_real(&ts_overlap)) > tx_max_gap_zeros) { logger.info("Detected RF gap of %.1f us. Sending end-of-burst.", srsran_timestamp_real(&ts_overlap) * 1.0e6); - tx_end(); + tx_end_nolock(); } else { logger.debug("Detected RF gap of %.1f us. Tx'ing zeroes.", srsran_timestamp_real(&ts_overlap) * 1.0e6); // Otherwise, transmit zeros @@ -559,7 +584,7 @@ bool radio::tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, cons // Zeros transmission int ret = srsran_rf_send_timed2(rf_device, - zeros, + zeros.data(), nzeros, end_of_burst_time[device_idx].full_secs, end_of_burst_time[device_idx].frac_secs, @@ -586,7 +611,7 @@ bool radio::tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, cons // Discard channels not allocated, need to point to valid buffer for (uint32_t i = 0; i < SRSRAN_MAX_CHANNELS; i++) { - radio_buffers[i] = zeros; + radio_buffers[i] = zeros.data(); } if (not map_channels(tx_channel_mapping, device_idx, sample_offset, buffer, radio_buffers)) { @@ -601,6 +626,12 @@ bool radio::tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, cons } void radio::tx_end() +{ + std::unique_lock lock(tx_mutex); + tx_end_nolock(); +} + +void radio::tx_end_nolock() { if (!is_initialized) { return; @@ -608,7 +639,7 @@ void radio::tx_end() if (!is_start_of_burst) { for (uint32_t i = 0; i < (uint32_t)rf_devices.size(); i++) { srsran_rf_send_timed2( - &rf_devices[i], zeros, 0, end_of_burst_time[i].full_secs, end_of_burst_time[i].frac_secs, false, true); + &rf_devices[i], zeros.data(), 0, end_of_burst_time[i].full_secs, end_of_burst_time[i].frac_secs, false, true); } is_start_of_burst = true; } @@ -631,12 +662,17 @@ void radio::set_rx_freq(const uint32_t& carrier_idx, const double& freq) return; } - // First release mapping - rx_channel_mapping.release_freq(carrier_idx); - // Map carrier index to physical channel if (rx_channel_mapping.allocate_freq(carrier_idx, freq)) { channel_mapping::device_mapping_t device_mapping = rx_channel_mapping.get_device_mapping(carrier_idx); + if (device_mapping.channel_idx >= nof_channels_x_dev) { + logger.error("Invalid mapping physical channel %d to logical carrier %d on f_rx=%.1f MHz (nof_channels_x_dev=%d, device_idx=%d)", + device_mapping.channel_idx, + carrier_idx, + freq / 1e6, nof_channels_x_dev, device_mapping.device_idx); + return; + } + logger.info("Mapping RF channel %d (device=%d, channel=%d) to logical carrier %d on f_rx=%.1f MHz", device_mapping.carrier_idx, device_mapping.device_idx, @@ -648,6 +684,15 @@ void radio::set_rx_freq(const uint32_t& carrier_idx, const double& freq) cur_rx_freqs[device_mapping.carrier_idx] = freq; for (uint32_t i = 0; i < nof_antennas; i++) { channel_mapping::device_mapping_t dm = rx_channel_mapping.get_device_mapping(carrier_idx, i); + if (dm.device_idx >= rf_devices.size() or dm.channel_idx >= nof_channels_x_dev) { + logger.error("Invalid port mapping %d:%d to logical carrier %d on f_rx=%.1f MHz", + dm.device_idx, + dm.channel_idx, + carrier_idx, + freq / 1e6); + return; + } + srsran_rf_set_rx_freq(&rf_devices[dm.device_idx], dm.channel_idx, freq + freq_offset); } } else { @@ -701,6 +746,13 @@ void radio::set_rx_srate(const double& srate) } } + // Assert ratio is integer + srsran_assert(((uint32_t)cur_rx_srate % (uint32_t)srate) == 0, + "The sampling rate ratio is not integer (%.2f MHz / %.2f MHz = %.3f)", + cur_rx_srate / 1e6, + srate / 1e6, + cur_rx_srate / srate); + // Update decimators uint32_t ratio = (uint32_t)ceil(cur_rx_srate / srate); for (uint32_t ch = 0; ch < nof_channels; ch++) { @@ -749,12 +801,17 @@ void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) return; } - // First release mapping - tx_channel_mapping.release_freq(carrier_idx); - // Map carrier index to physical channel if (tx_channel_mapping.allocate_freq(carrier_idx, freq)) { channel_mapping::device_mapping_t device_mapping = tx_channel_mapping.get_device_mapping(carrier_idx); + if (device_mapping.channel_idx >= nof_channels_x_dev) { + logger.error("Invalid mapping physical channel %d to logical carrier %d on f_tx=%.1f MHz", + device_mapping.channel_idx, + carrier_idx, + freq / 1e6); + return; + } + logger.info("Mapping RF channel %d (device=%d, channel=%d) to logical carrier %d on f_tx=%.1f MHz", device_mapping.carrier_idx, device_mapping.device_idx, @@ -766,6 +823,14 @@ void radio::set_tx_freq(const uint32_t& carrier_idx, const double& freq) cur_tx_freqs[device_mapping.carrier_idx] = freq; for (uint32_t i = 0; i < nof_antennas; i++) { device_mapping = tx_channel_mapping.get_device_mapping(carrier_idx, i); + if (device_mapping.device_idx >= rf_devices.size() or device_mapping.channel_idx >= nof_channels_x_dev) { + logger.error("Invalid port mapping %d:%d to logical carrier %d on f_rx=%.1f MHz", + device_mapping.device_idx, + device_mapping.channel_idx, + carrier_idx, + freq / 1e6); + return; + } srsran_rf_set_tx_freq(&rf_devices[device_mapping.device_idx], device_mapping.channel_idx, freq + freq_offset); } @@ -937,6 +1002,13 @@ void radio::set_tx_srate(const double& srate) } } + // Assert ratio is integer + srsran_assert(((uint32_t)cur_tx_srate % (uint32_t)srate) == 0, + "The sampling rate ratio is not integer (%.2f MHz / %.2f MHz = %.3f)", + cur_rx_srate / 1e6, + srate / 1e6, + cur_rx_srate / srate); + // Update interpolators uint32_t ratio = (uint32_t)ceil(cur_tx_srate / srate); for (uint32_t ch = 0; ch < nof_channels; ch++) { @@ -967,6 +1039,7 @@ srsran_rf_info_t* radio::get_info() bool radio::get_metrics(rf_metrics_t* metrics) { + std::lock_guard lock(metrics_mutex); *metrics = rf_metrics; rf_metrics = {}; return true; @@ -978,20 +1051,27 @@ void radio::handle_rf_msg(srsran_rf_error_t error) return; } if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_OVERFLOW) { - rf_metrics.rf_o++; - rf_metrics.rf_error = true; + { + std::lock_guard lock(metrics_mutex); + rf_metrics.rf_o++; + rf_metrics.rf_error = true; + } logger.info("Overflow"); // inform PHY about overflow - phy->radio_overflow(); + if (phy != nullptr) { + phy->radio_overflow(); + } } else if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_UNDERFLOW) { + logger.info("Underflow"); + std::lock_guard lock(metrics_mutex); rf_metrics.rf_u++; rf_metrics.rf_error = true; - logger.info("Underflow"); } else if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_LATE) { + logger.info("Late (detected in %s)", error.opt ? "rx call" : "asynchronous thread"); + std::lock_guard lock(metrics_mutex); rf_metrics.rf_l++; rf_metrics.rf_error = true; - logger.info("Late (detected in %s)", error.opt ? "rx call" : "asynchronous thread"); } else if (error.type == srsran_rf_error_t::SRSRAN_RF_ERROR_RX) { logger.error("Fatal radio error occured."); phy->radio_failure(); diff --git a/lib/src/radio/test/CMakeLists.txt b/lib/src/radio/test/CMakeLists.txt index 714b8ecf6..d9c646a9d 100644 --- a/lib/src/radio/test/CMakeLists.txt +++ b/lib/src/radio/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -30,6 +30,18 @@ if(RF_FOUND) tx_port=tcp://*:2000,rx_port=tcp://localhost:2000\;tx_port=tcp://*:2001,rx_port=tcp://localhost:2001\;tx_port=tcp://*:2002,rx_port=tcp://localhost:2002\;tx_port=tcp://*:2003,rx_port=tcp://localhost:2003\; -p 4) endif (ZEROMQ_FOUND) + + add_executable(test_radio_rt_gain test_radio_rt_gain.cc) + target_link_libraries(test_radio_rt_gain + srsran_common + srsran_phy + srsran_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) + if (ZEROMQ_FOUND) + add_test(test_radio_rt_gain_zmq test_radio_rt_gain --srate=3.84e6 --dev_name=zmq --dev_args=tx_port=ipc:///tmp/test_radio_rt_gain_zmq,rx_port=ipc:///tmp/test_radio_rt_gain_zmq,base_srate=3.84e6) + endif (ZEROMQ_FOUND) + endif(RF_FOUND) diff --git a/lib/src/radio/test/benchmark_radio.cc b/lib/src/radio/test/benchmark_radio.cc index d7b26d79c..473f89d70 100644 --- a/lib/src/radio/test/benchmark_radio.cc +++ b/lib/src/radio/test/benchmark_radio.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,6 +34,7 @@ extern "C" { //#undef I // Fix complex.h #define I nastiness when using C++ #endif +#include "srsran/common/tsan_options.h" #include "srsran/phy/utils/debug.h" #include "srsran/radio/radio.h" @@ -64,6 +65,7 @@ static pthread_t radio_thread; #include "srsgui/srsgui.h" #include static pthread_t plot_thread; +static bool plot_thread_launched = false; static sem_t plot_sem; static uint32_t plot_sf_idx = 0; static plot_real_t fft_plot[SRSRAN_MAX_RADIOS] = {}; @@ -154,7 +156,7 @@ void parse_args(int argc, char** argv) fft_plot_enable ^= true; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'h': default: @@ -255,6 +257,7 @@ static int init_plots(uint32_t frame_size) perror("pthread_create"); exit(-1); } + plot_thread_launched = true; return SRSRAN_SUCCESS; } @@ -323,7 +326,10 @@ static void* radio_thread_run(void* arg) #ifdef ENABLE_GUI if (fft_plot_enable) { - init_plots(frame_size); + if (init_plots(frame_size) != SRSRAN_SUCCESS) { + ERROR("Error: Could not init plots"); + goto clean_exit; + } sleep(1); } #endif /* ENABLE_GUI */ @@ -582,18 +588,22 @@ clean_exit: srsran_dft_plan_free(&idft_plan); #ifdef ENABLE_GUI - pthread_join(plot_thread, NULL); - srsran_dft_plan_free(&dft_spectrum); - for (uint32_t r = 0; r < nof_radios; r++) { - for (uint32_t p = 0; p < nof_ports; p++) { - uint32_t plot_idx = r * nof_ports + p; - if (fft_plot_buffer[plot_idx]) { - free(fft_plot_buffer[plot_idx]); + if (fft_plot_enable) { + if (plot_thread_launched == true) { + pthread_join(plot_thread, NULL); + } + srsran_dft_plan_free(&dft_spectrum); + for (uint32_t r = 0; r < nof_radios; r++) { + for (uint32_t p = 0; p < nof_ports; p++) { + uint32_t plot_idx = r * nof_ports + p; + if (fft_plot_buffer[plot_idx]) { + free(fft_plot_buffer[plot_idx]); + } } } - } - if (fft_plot_temp) { - free(fft_plot_temp); + if (fft_plot_temp) { + free(fft_plot_temp); + } } #endif /* ENABLE_GUI */ diff --git a/lib/src/radio/test/test_radio_rt_gain.cc b/lib/src/radio/test/test_radio_rt_gain.cc new file mode 100644 index 000000000..b6c043a93 --- /dev/null +++ b/lib/src/radio/test/test_radio_rt_gain.cc @@ -0,0 +1,309 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsran/common/test_common.h" +#include "srsran/radio/radio.h" +#include +#include + +// shorten boost program options namespace +namespace bpo = boost::program_options; + +// Test arguments +struct test_args_s { + bool valid = false; + double srate_hz = 3.84e6; + double freq_hz = 3.5e9; + float rx_gain_db = 20.0f; + float tx_gain_db_begin = 0.0f; + float tx_gain_db_end = 30.0f; + float tx_gain_db_step = 1.0f; + uint32_t tx_delay_ms = 4; + uint32_t step_period_ms = 1; + uint32_t nof_repetitions = 3; + uint32_t power_ramping_ms = 50; + uint32_t pre_tx_ms = 50; // After main loop acquire a few more ms + uint32_t post_tx_ms = 50; // After main loop acquire a few more ms + std::string filename = "/tmp/baseband.iq.dat"; + std::string device_name = "zmq"; + std::string device_args = "tx_port=tcp://*:5555,rx_port=tcp://localhost:5555,base_srate=3.84e6"; + + test_args_s(int argc, char** argv) + { + bpo::options_description options; + + // clang-format off + options.add_options() + ("srate", bpo::value(&srate_hz)->default_value(srate_hz), "Sampling rate in Hz") + ("freq", bpo::value(&freq_hz)->default_value(freq_hz), "Center frequency in Hz") + ("rx_gain", bpo::value(&rx_gain_db)->default_value(rx_gain_db), "Receiver gain in dB") + ("tx_gain_begin", bpo::value(&tx_gain_db_begin)->default_value(tx_gain_db_begin), "Initial transmitter gain in dB") + ("tx_gain_end", bpo::value(&tx_gain_db_end)->default_value(tx_gain_db_end), "Final transmitter gain in dB") + ("tx_gain_step", bpo::value(&tx_gain_db_step)->default_value(tx_gain_db_step), "Step transmitter gain in dB") + ("tx_delay", bpo::value(&tx_delay_ms)->default_value(tx_delay_ms), "Delay between Rx and Tx in milliseconds") + ("step_period", bpo::value(&step_period_ms)->default_value(step_period_ms), "Transmitter gain step period in milliseconds") + ("repetitions", bpo::value(&nof_repetitions)->default_value(nof_repetitions), "Number of transmit gain steering repetitions") + ("power_ramping", bpo::value(&power_ramping_ms)->default_value(power_ramping_ms), "Transmitter initial power ramping in milliseconds") + ("pre_tx", bpo::value(&pre_tx_ms)->default_value(pre_tx_ms), "Initial acquisition time before start transmission in milliseconds") + ("post_tx", bpo::value(&pre_tx_ms)->default_value(pre_tx_ms), "Initial acquisition time after ending transmission in milliseconds") + ("filename", bpo::value(&filename)->default_value(filename), "File sink filename") + ("dev_name", bpo::value(&device_name)->default_value(device_name), "RF Device name") + ("dev_args", bpo::value(&device_args)->default_value(device_args), "RF Device arguments") + ("help", "Show this message") + ; + // clang-format on + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); + bpo::notify(vm); + valid = true; + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + } + + // help option was given or error - print usage and exit + if (vm.count("help") > 0 or not valid) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << options << std::endl << std::endl; + valid = false; + } + } +}; + +class phy_radio_listener : public srsran::phy_interface_radio +{ +private: + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); + uint32_t overflow_count = 0; + uint32_t failure_count = 0; + +public: + phy_radio_listener() {} + void radio_overflow() override + { + overflow_count++; + logger.error("Overflow"); + } + void radio_failure() override + { + failure_count++; + logger.error("Failure"); + } +}; + +class test_sink +{ +private: + srslog::basic_logger& logger = srslog::fetch_basic_logger("SINK", false); + srsran_filesink_t filesink = {}; + std::vector meas_power_dB; + +public: + test_sink(const std::string& filename) + { + if (srsran_filesink_init(&filesink, filename.c_str(), SRSRAN_COMPLEX_FLOAT_BIN) < SRSRAN_SUCCESS) { + ERROR("Error initiating filesink"); + filesink = {}; + } + } + + ~test_sink() + { + // Free filesink + srsran_filesink_free(&filesink); + + // Print measure power + printf("Measured power: "); + srsran_vec_fprint_f(stdout, meas_power_dB.data(), (int)meas_power_dB.size()); + } + + void write(std::vector& buffer, uint32_t nsamp) + { + // Measure average power + float avg_pwr = srsran_vec_avg_power_cf(buffer.data(), nsamp); + + // Push measurement in dB + meas_power_dB.push_back(srsran_convert_power_to_dB(avg_pwr)); + + // Write signal in file + srsran_filesink_write(&filesink, (void*)buffer.data(), (int)nsamp); + } +}; + +class test_source +{ +private: + // cf_t phase = 1.0f; + cf_t phase = 0.01; + float freq_norm = 0.125; + +public: + test_source() {} + + ~test_source() {} + + void generate(std::vector& buffer, uint32_t nsamp) + { + phase *= srsran_vec_gen_sine(phase, freq_norm, buffer.data(), (int)nsamp); + } +}; + +int main(int argc, char** argv) +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); + srslog::init(); + srsran::radio radio; + phy_radio_listener radio_listener; + + enum { BEFORE_RAMPING, RAMPING, GAIN_STEERING, POST_GAIN_STEERING, END } state = BEFORE_RAMPING; + + test_args_s args(argc, argv); + TESTASSERT(args.valid); + + // Calculate number of steps + TESTASSERT(std::isnormal(args.tx_gain_db_step)); + uint32_t nof_steps = (args.tx_gain_db_end - args.tx_gain_db_begin) / args.tx_gain_db_step; + + // Calculate subframe size in 1ms + TESTASSERT(std::isnormal(args.srate_hz)); + uint32_t sf_sz = (uint32_t)std::round(1e-3 * args.srate_hz); + + // Allocate baseband buffer + std::vector buffer(sf_sz); + + // Prepare radio arguments + srsran::rf_args_t rf_args = {}; + rf_args.log_level = "info"; + rf_args.srate_hz = args.srate_hz; + rf_args.dl_freq = args.freq_hz; + rf_args.ul_freq = args.freq_hz; + rf_args.rx_gain = args.rx_gain_db; + rf_args.tx_gain = args.tx_gain_db_begin; + rf_args.nof_carriers = 1; + rf_args.nof_antennas = 1; + rf_args.device_name = args.device_name; + rf_args.device_args = args.device_args; + + // Initialise radio + TESTASSERT(radio.init(rf_args, &radio_listener) == SRSRAN_SUCCESS); + + // Setup LO frequencies + radio.set_tx_freq(0, args.freq_hz); + radio.set_rx_freq(0, args.freq_hz); + + // Setup sampling rate + radio.set_tx_srate(args.srate_hz); + radio.set_rx_srate(args.srate_hz); + + // Setup initial gains + radio.set_tx_gain(args.tx_gain_db_begin); + radio.set_rx_gain(args.rx_gain_db); + + // Create signal sink + test_sink sink(args.filename); + + // Create signal source + test_source source; + + // Perform Tx/Rx + uint32_t sf_count = 0; + uint32_t repetition = 0; + while (state != END) { + switch (state) { + case BEFORE_RAMPING: + if (sf_count >= args.pre_tx_ms) { + logger.info("-- Starting power ramping stage"); + state = RAMPING; + sf_count = 0; + continue; + } + break; + case RAMPING: + if (sf_count >= args.power_ramping_ms) { + logger.info("-- Starting gain steering stage"); + state = GAIN_STEERING; + sf_count = 0; + continue; + } + break; + case GAIN_STEERING: + if (sf_count >= nof_steps * args.step_period_ms) { + repetition++; + sf_count = 0; + logger.info("-- Finished repetition %d of %d", repetition, args.nof_repetitions); + } + + if (repetition >= args.nof_repetitions) { + logger.info("-- Starting post gain steering stage"); + state = POST_GAIN_STEERING; + radio.tx_end(); + continue; + } + if (sf_count % args.step_period_ms == 0 and sf_count != 0) { + float tx_gain_db = args.tx_gain_db_begin + args.tx_gain_db_step * (sf_count / args.step_period_ms); + logger.info("-- New Tx gain %+.2f dB", tx_gain_db); + radio.set_tx_gain(tx_gain_db); + } + break; + case POST_GAIN_STEERING: + if (sf_count >= args.post_tx_ms) { + logger.info("-- Ending..."); + state = END; + sf_count = 0; + continue; + } + break; + default: + break; + } + + // Prepare reception buffers + srsran::rf_buffer_t rf_buffer = {}; + rf_buffer.set(0, buffer.data()); + rf_buffer.set_nof_samples(sf_sz); + + // Receive + srsran::rf_timestamp_t ts = {}; + TESTASSERT(radio.rx_now(rf_buffer, ts)); + + // Save signal, including the time before transmission + sink.write(buffer, sf_sz); + + if (state == RAMPING or state == GAIN_STEERING) { + // Generate transmit signal + source.generate(buffer, sf_sz); + + // Update timestamp for Tx + ts.add(1e-3 * (double)args.tx_delay_ms); + + // Transmit + radio.tx(rf_buffer, ts); + } + + // Increase SF counting + sf_count++; + } + + // Tear down radio + radio.stop(); + + return SRSRAN_SUCCESS; +} diff --git a/lib/src/upper/CMakeLists.txt b/lib/src/rlc/CMakeLists.txt similarity index 71% rename from lib/src/upper/CMakeLists.txt rename to lib/src/rlc/CMakeLists.txt index aa06021b0..14845a5f8 100644 --- a/lib/src/upper/CMakeLists.txt +++ b/lib/src/rlc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,21 +18,18 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES gtpu.cc - pdcp.cc - pdcp_entity_base.cc - pdcp_entity_lte.cc - rlc.cc +set(SOURCES rlc.cc rlc_tm.cc rlc_um_base.cc rlc_um_lte.cc + rlc_um_nr.cc rlc_am_base.cc rlc_am_lte.cc - pdcp_entity_nr.cc - rlc_um_nr.cc rlc_am_nr.cc + rlc_am_lte_packing.cc + rlc_am_nr_packing.cc bearer_mem_pool.cc) -add_library(srsran_upper STATIC ${SOURCES}) -target_link_libraries(srsran_upper srsran_common srsran_asn1) -INSTALL(TARGETS srsran_upper DESTINATION ${LIBRARY_DIR}) +add_library(srsran_rlc STATIC ${SOURCES}) +target_link_libraries(srsran_rlc srsran_common ${ATOMIC_LIBS}) +install(TARGETS srsran_rlc DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/upper/bearer_mem_pool.cc b/lib/src/rlc/bearer_mem_pool.cc similarity index 74% rename from lib/src/upper/bearer_mem_pool.cc rename to lib/src/rlc/bearer_mem_pool.cc index 9901d0bb2..8a0be17eb 100644 --- a/lib/src/upper/bearer_mem_pool.cc +++ b/lib/src/rlc/bearer_mem_pool.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,18 +19,24 @@ * */ -#include "srsran/upper/bearer_mem_pool.h" +#include "srsran/rlc/bearer_mem_pool.h" #include "srsran/adt/pool/batch_mem_pool.h" -#include "srsran/upper/rlc_am_lte.h" -#include "srsran/upper/rlc_um_lte.h" -#include "srsran/upper/rlc_um_nr.h" +#include "srsran/rlc/rlc_am_lte.h" +#include "srsran/rlc/rlc_am_nr.h" +#include "srsran/rlc/rlc_tm.h" +#include "srsran/rlc/rlc_um_lte.h" +#include "srsran/rlc/rlc_um_nr.h" namespace srsran { srsran::background_mem_pool* get_bearer_pool() { static background_mem_pool pool( - 4, std::max(std::max(sizeof(rlc_am_lte), sizeof(rlc_um_lte)), sizeof(rlc_um_nr)), 8, 8); + 4, + std::max(std::max(std::max(std::max(sizeof(rlc_am), sizeof(rlc_am)), sizeof(rlc_um_lte)), sizeof(rlc_um_nr)), + sizeof(rlc_tm)), + 8, + 8); return &pool; } diff --git a/lib/src/upper/rlc.cc b/lib/src/rlc/rlc.cc similarity index 73% rename from lib/src/upper/rlc.cc rename to lib/src/rlc/rlc.cc index f51c1d5c3..fdbb8d86a 100644 --- a/lib/src/upper/rlc.cc +++ b/lib/src/rlc/rlc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,18 +19,17 @@ * */ -#include "srsran/upper/rlc.h" +#include "srsran/rlc/rlc.h" #include "srsran/common/rwlock_guard.h" -#include "srsran/upper/rlc_am_lte.h" -#include "srsran/upper/rlc_tm.h" -#include "srsran/upper/rlc_um_lte.h" -#include "srsran/upper/rlc_um_nr.h" +#include "srsran/rlc/rlc_am_base.h" +#include "srsran/rlc/rlc_tm.h" +#include "srsran/rlc/rlc_um_lte.h" +#include "srsran/rlc/rlc_um_nr.h" namespace srsran { -rlc::rlc(const char* logname) : logger(srslog::fetch_basic_logger(logname)) +rlc::rlc(const char* logname) : logger(srslog::fetch_basic_logger(logname)), pool(byte_buffer_pool::get_instance()) { - pool = byte_buffer_pool::get_instance(); pthread_rwlock_init(&rwlock, NULL); } @@ -38,32 +37,13 @@ rlc::~rlc() { // destroy all remaining entities { - rwlock_write_guard lock(rwlock); - - for (rlc_map_t::iterator it = rlc_array.begin(); it != rlc_array.end(); ++it) { - delete (it->second); - } + srsran::rwlock_write_guard lock(rwlock); rlc_array.clear(); - - for (rlc_map_t::iterator it = rlc_array_mrb.begin(); it != rlc_array_mrb.end(); ++it) { - delete (it->second); - } rlc_array_mrb.clear(); } - pthread_rwlock_destroy(&rwlock); } -void rlc::init(srsue::pdcp_interface_rlc* pdcp_, - srsue::rrc_interface_rlc* rrc_, - srsue::rrc_interface_rlc* rrc_nr_, - srsran::timer_handler* timers_, - uint32_t lcid_) -{ - init(pdcp_, rrc_, timers_, lcid_); - rrc_nr = rrc_nr_; -} - void rlc::init(srsue::pdcp_interface_rlc* pdcp_, srsue::rrc_interface_rlc* rrc_, srsran::timer_handler* timers_, @@ -86,8 +66,8 @@ void rlc::init(srsue::pdcp_interface_rlc* pdcp_, uint32_t lcid_, bsr_callback_t bsr_callback_) { - init(pdcp_, rrc_, timers_, lcid_); bsr_callback = bsr_callback_; + init(pdcp_, rrc_, timers_, lcid_); } void rlc::reset_metrics() @@ -128,21 +108,21 @@ void rlc::get_metrics(rlc_metrics_t& m, const uint32_t nof_tti) double rx_rate_mbps = (nof_tti > 0) ? ((metrics.num_rx_pdu_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0; double tx_rate_mbps = (nof_tti > 0) ? ((metrics.num_tx_pdu_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0; - logger.info("lcid=%d, rx_rate_mbps=%4.2f (real=%4.2f), tx_rate_mbps=%4.2f (real=%4.2f)", - it->first, - rx_rate_mbps, - rx_rate_mbps_real_time, - tx_rate_mbps, - tx_rate_mbps_real_time); + logger.debug("lcid=%d, rx_rate_mbps=%4.2f (real=%4.2f), tx_rate_mbps=%4.2f (real=%4.2f)", + it->first, + rx_rate_mbps, + rx_rate_mbps_real_time, + tx_rate_mbps, + tx_rate_mbps_real_time); m.bearer[it->first] = metrics; } // Add multicast metrics for (rlc_map_t::iterator it = rlc_array_mrb.begin(); it != rlc_array_mrb.end(); ++it) { rlc_bearer_metrics_t metrics = it->second->get_metrics(); - logger.info("MCH_LCID=%d, rx_rate_mbps=%4.2f", - it->first, - (metrics.num_rx_pdu_bytes * 8 / static_cast(1e6)) / secs.count()); + logger.debug("MCH_LCID=%d, rx_rate_mbps=%4.2f", + it->first, + (metrics.num_rx_pdu_bytes * 8 / static_cast(1e6)) / secs.count()); m.bearer[it->first] = metrics; } @@ -180,11 +160,6 @@ void rlc::reset() { { rwlock_write_guard lock(rwlock); - - for (rlc_map_t::iterator it = rlc_array.begin(); it != rlc_array.end(); ++it) { - it->second->stop(); - delete (it->second); - } rlc_array.clear(); // the multicast bearer (MRB) is not removed here because eMBMS services continue to be streamed in idle mode (3GPP // TS 23.246 version 14.1.0 Release 14 section 8) @@ -273,20 +248,24 @@ bool rlc::has_data_locked(const uint32_t lcid) return has_data(lcid); } -uint32_t rlc::get_buffer_state(uint32_t lcid) +void rlc::get_buffer_state(uint32_t lcid, uint32_t& tx_queue, uint32_t& prio_tx_queue) { - uint32_t ret = 0; - rwlock_read_guard lock(rwlock); if (valid_lcid(lcid)) { if (rlc_array.at(lcid)->is_suspended()) { - ret = 0; + tx_queue = 0; + prio_tx_queue = 0; } else { - ret = rlc_array.at(lcid)->get_buffer_state(); + rlc_array.at(lcid)->get_buffer_state(tx_queue, prio_tx_queue); } } +} - return ret; +uint32_t rlc::get_buffer_state(uint32_t lcid) +{ + uint32_t tx_queue = 0, prio_tx_queue = 0; + get_buffer_state(lcid, tx_queue, prio_tx_queue); + return tx_queue + prio_tx_queue; } uint32_t rlc::get_total_mch_buffer_state(uint32_t lcid) @@ -301,7 +280,7 @@ uint32_t rlc::get_total_mch_buffer_state(uint32_t lcid) return ret; } -int rlc::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { uint32_t ret = 0; @@ -318,7 +297,7 @@ int rlc::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) return ret; } -int rlc::read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc::read_pdu_mch(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { uint32_t ret = 0; @@ -408,117 +387,102 @@ bool rlc::has_data(uint32_t lcid) } // Methods modifying the RLC array need to acquire the write-lock -void rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg) +int rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg) { rwlock_write_guard lock(rwlock); - rlc_common* rlc_entity = nullptr; - - // Check this for later rrc_nr pointer access - if (cnfg.rat == srsran::srsran_rat_t::nr && rrc_nr == nullptr) { - logger.error("Cannot add/modify RLC entity - missing rrc_nr parent pointer for rat type nr"); - return; + if (valid_lcid(lcid)) { + logger.warning("LCID %d already exists", lcid); + return SRSRAN_ERROR; } - if (cnfg.rlc_mode != rlc_mode_t::tm and rlc_array.find(lcid) != rlc_array.end()) { - if (rlc_array[lcid]->get_mode() != cnfg.rlc_mode) { - logger.info("Switching RLC entity type. Recreating it."); - rlc_array.erase(lcid); - } + std::unique_ptr rlc_entity; + + switch (cnfg.rlc_mode) { + case rlc_mode_t::tm: + rlc_entity = std::unique_ptr(new rlc_tm(logger, lcid, pdcp, rrc)); + break; + case rlc_mode_t::am: + switch (cnfg.rat) { + case srsran_rat_t::lte: + rlc_entity = std::unique_ptr(new rlc_am(cnfg.rat, logger, lcid, pdcp, rrc, timers)); + break; + case srsran_rat_t::nr: + rlc_entity = std::unique_ptr(new rlc_am(cnfg.rat, logger, lcid, pdcp, rrc, timers)); + break; + default: + logger.error("AM not supported for this RAT"); + return SRSRAN_ERROR; + } + break; + case rlc_mode_t::um: + switch (cnfg.rat) { + case srsran_rat_t::lte: + rlc_entity = std::unique_ptr(new rlc_um_lte(logger, lcid, pdcp, rrc, timers)); + break; + case srsran_rat_t::nr: + rlc_entity = std::unique_ptr(new rlc_um_nr(logger, lcid, pdcp, rrc, timers)); + break; + default: + logger.error("UM not supported for this RAT"); + return SRSRAN_ERROR; + } + break; + default: + logger.error("Cannot add RLC entity - invalid mode"); + return SRSRAN_ERROR; } - if (not valid_lcid(lcid)) { - switch (cnfg.rat) { - case srsran_rat_t::lte: - switch (cnfg.rlc_mode) { - case rlc_mode_t::tm: - rlc_entity = new rlc_tm(logger, lcid, pdcp, rrc); - break; - case rlc_mode_t::am: - rlc_entity = new rlc_am_lte(logger, lcid, pdcp, rrc, timers); - break; - case rlc_mode_t::um: - rlc_entity = new rlc_um_lte(logger, lcid, pdcp, rrc, timers); - break; - default: - logger.error("Cannot add RLC entity - invalid mode"); - return; - } - if (rlc_entity != nullptr) { - rlc_entity->set_bsr_callback(bsr_callback); - } - break; - case srsran_rat_t::nr: - switch (cnfg.rlc_mode) { - case rlc_mode_t::tm: - rlc_entity = new rlc_tm(logger, lcid, pdcp, rrc_nr); - break; - case rlc_mode_t::um: - rlc_entity = new rlc_um_nr(logger, lcid, pdcp, rrc_nr, timers); - break; - default: - logger.error("Cannot add RLC entity - invalid mode"); - return; - } - break; - default: - logger.error("RAT not supported"); - return; - } - - if (not rlc_array.insert(rlc_map_pair_t(lcid, rlc_entity)).second) { - logger.error("Error inserting RLC entity in to array."); - goto delete_and_exit; - } - - logger.info("Added %s radio bearer with LCID %d in %s", to_string(cnfg.rat), lcid, to_string(cnfg.rlc_mode)); - rlc_entity = NULL; + // make sure entity has been created + if (rlc_entity == nullptr) { + logger.error("Couldn't allocate new RLC entity"); + return SRSRAN_ERROR; } - // configure and add to array - if (cnfg.rlc_mode != rlc_mode_t::tm and rlc_array.find(lcid) != rlc_array.end()) { - if (not rlc_array.at(lcid)->configure(cnfg)) { + // configure entity + if (cnfg.rlc_mode != rlc_mode_t::tm) { + if (not rlc_entity->configure(cnfg)) { logger.error("Error configuring RLC entity."); - goto delete_and_exit; + return SRSRAN_ERROR; } } - logger.info("Configured %s radio bearer with LCID %d in %s", to_string(cnfg.rat), lcid, to_string(cnfg.rlc_mode)); + rlc_entity->set_bsr_callback(bsr_callback); -delete_and_exit: - if (rlc_entity) { - delete (rlc_entity); + if (not rlc_array.insert(rlc_map_pair_t(lcid, std::move(rlc_entity))).second) { + logger.error("Error inserting RLC entity in to array."); + return SRSRAN_ERROR; } + + logger.info("Added %s radio bearer with LCID %d in %s", to_string(cnfg.rat), lcid, to_string(cnfg.rlc_mode)); + + return SRSRAN_SUCCESS; } -void rlc::add_bearer_mrb(uint32_t lcid) +int rlc::add_bearer_mrb(uint32_t lcid) { rwlock_write_guard lock(rwlock); - rlc_common* rlc_entity = NULL; - if (not valid_lcid_mrb(lcid)) { - rlc_entity = new rlc_um_lte(logger, lcid, pdcp, rrc, timers); + std::unique_ptr rlc_entity = + std::unique_ptr(new rlc_um_lte(logger, lcid, pdcp, rrc, timers)); // configure and add to array - if (not rlc_entity->configure(rlc_config_t::mch_config())) { + if (not rlc_entity or rlc_entity->configure(rlc_config_t::mch_config()) == false) { logger.error("Error configuring RLC entity."); - goto delete_and_exit; + return SRSRAN_ERROR; } + rlc_entity->set_bsr_callback(bsr_callback); if (rlc_array_mrb.count(lcid) == 0) { - if (not rlc_array_mrb.insert(rlc_map_pair_t(lcid, rlc_entity)).second) { + if (not rlc_array_mrb.insert(rlc_map_pair_t(lcid, std::move(rlc_entity))).second) { logger.error("Error inserting RLC entity in to array."); - goto delete_and_exit; + return SRSRAN_ERROR; } } - logger.warning("Added bearer MRB%d with mode RLC_UM", lcid); - return; + logger.info("Added bearer MRB%d with mode RLC_UM", lcid); } else { logger.warning("Bearer MRB%d already created.", lcid); } -delete_and_exit: - if (rlc_entity != NULL) { - delete (rlc_entity); - } + return SRSRAN_SUCCESS; } void rlc::del_bearer(uint32_t lcid) @@ -528,7 +492,6 @@ void rlc::del_bearer(uint32_t lcid) if (valid_lcid(lcid)) { rlc_map_t::iterator it = rlc_array.find(lcid); it->second->stop(); - delete (it->second); rlc_array.erase(it); logger.info("Deleted RLC bearer with LCID %d", lcid); } else { @@ -543,9 +506,8 @@ void rlc::del_bearer_mrb(uint32_t lcid) if (valid_lcid_mrb(lcid)) { rlc_map_t::iterator it = rlc_array_mrb.find(lcid); it->second->stop(); - delete (it->second); rlc_array_mrb.erase(it); - logger.warning("Deleted RLC MRB bearer with LCID %d", lcid); + logger.info("Deleted RLC MRB bearer with LCID %d", lcid); } else { logger.error("Can't delete bearer with LCID %d. Bearer doesn't exist.", lcid); } @@ -558,9 +520,9 @@ void rlc::change_lcid(uint32_t old_lcid, uint32_t new_lcid) // make sure old LCID exists and new LCID is still free if (valid_lcid(old_lcid) && not valid_lcid(new_lcid)) { // insert old rlc entity into new LCID - rlc_map_t::iterator it = rlc_array.find(old_lcid); - rlc_common* rlc_entity = it->second; - if (not rlc_array.insert(rlc_map_pair_t(new_lcid, rlc_entity)).second) { + rlc_map_t::iterator it = rlc_array.find(old_lcid); + std::unique_ptr rlc_entity = std::move(it->second); + if (not rlc_array.insert(rlc_map_pair_t(new_lcid, std::move(rlc_entity))).second) { logger.error("Error inserting RLC entity into array."); return; } @@ -647,18 +609,15 @@ bool rlc::valid_lcid_mrb(uint32_t lcid) void rlc::update_bsr(uint32_t lcid) { if (bsr_callback) { - uint32_t tx_queue = get_buffer_state(lcid); - uint32_t retx_queue = 0; // todo: separate tx_queue and retx_queue - bsr_callback(lcid, tx_queue, retx_queue); + uint32_t tx_queue = 0, prio_tx_queue = 0; + get_buffer_state(lcid, tx_queue, prio_tx_queue); } } void rlc::update_bsr_mch(uint32_t lcid) { if (bsr_callback) { - uint32_t tx_queue = get_total_mch_buffer_state(lcid); - uint32_t retx_queue = 0; // todo: separate tx_queue and retx_queue - bsr_callback(lcid, tx_queue, retx_queue); + uint32_t tx_queue = get_total_mch_buffer_state(lcid); } } diff --git a/lib/src/rlc/rlc_am_base.cc b/lib/src/rlc/rlc_am_base.cc new file mode 100644 index 000000000..39eacbcda --- /dev/null +++ b/lib/src/rlc/rlc_am_base.cc @@ -0,0 +1,319 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/rlc/rlc_am_base.h" +#include "srsran/rlc/rlc_am_lte.h" +#include "srsran/rlc/rlc_am_nr.h" +#include + +namespace srsran { + +bool rlc_am_is_control_pdu(uint8_t* payload) +{ + return ((*(payload) >> 7) & 0x01) == RLC_DC_FIELD_CONTROL_PDU; +} + +bool rlc_am_is_control_pdu(byte_buffer_t* pdu) +{ + return rlc_am_is_control_pdu(pdu->msg); +} + +/******************************************************* + * RLC AM entity + * This entity is common between LTE and NR + * and only the TX/RX entities change between them + *******************************************************/ +rlc_am::rlc_am(srsran_rat_t rat, + srslog::basic_logger& logger, + uint32_t lcid_, + srsue::pdcp_interface_rlc* pdcp_, + srsue::rrc_interface_rlc* rrc_, + srsran::timer_handler* timers_) : + rlc_common(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_) +{ + if (rat == srsran_rat_t::lte) { + rlc_am_lte_tx* tx = new rlc_am_lte_tx(this); + rlc_am_lte_rx* rx = new rlc_am_lte_rx(this); + tx_base = std::unique_ptr(tx); + rx_base = std::unique_ptr(rx); + tx->set_rx(rx); + rx->set_tx(tx); + } else if (rat == srsran_rat_t::nr) { + rlc_am_nr_tx* tx = new rlc_am_nr_tx(this); + rlc_am_nr_rx* rx = new rlc_am_nr_rx(this); + tx_base = std::unique_ptr(tx); + rx_base = std::unique_ptr(rx); + tx->set_rx(rx); + rx->set_tx(tx); + } else { + RlcError("Invalid RAT at entity initialization"); + } +} + +bool rlc_am::configure(const rlc_config_t& cfg_) +{ + // determine bearer name and configure rx/tx objects + rb_name = rrc->get_rb_name(lcid); + + // store configuration + cfg = cfg_; + + if (not rx_base->configure(cfg)) { + RlcError("Error configuring bearer (RX)"); + return false; + } + + if (not tx_base->configure(cfg)) { + RlcError("Error configuring bearer (TX)"); + return false; + } + + if (cfg.rat == srsran_rat_t::lte) { + RlcInfo("AM LTE configured - t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " + "t_reordering=%d, t_status_prohibit=%d, tx_queue_length=%d", + cfg.am.t_poll_retx, + cfg.am.poll_pdu, + cfg.am.poll_byte, + cfg.am.max_retx_thresh, + cfg.am.t_reordering, + cfg.am.t_status_prohibit, + cfg.tx_queue_length); + } else if (cfg.rat == srsran_rat_t::nr) { + RlcInfo("AM NR configured - tx_sn_field_length=%d, rx_sn_field_length=%d, " + "t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, " + "max_retx_thresh=%d, t_reassembly=%d, t_status_prohibit=%d, tx_queue_length=%d", + to_number(cfg.am_nr.tx_sn_field_length), + to_number(cfg.am_nr.rx_sn_field_length), + cfg.am_nr.t_poll_retx, + cfg.am_nr.poll_pdu, + cfg.am_nr.poll_byte, + cfg.am_nr.max_retx_thresh, + cfg.am_nr.t_reassembly, + cfg.am_nr.t_status_prohibit, + cfg.tx_queue_length); + } else { + RlcError("Invalid RAT at entity configuration"); + } + return true; +} + +void rlc_am::stop() +{ + RlcDebug("Stopped bearer"); + tx_base->stop(); + rx_base->stop(); +} + +void rlc_am::reestablish() +{ + RlcDebug("Reestablished bearer"); + tx_base->reestablish(); // calls stop and enables tx again + rx_base->reestablish(); // calls only stop +} + +/**************************************************************************** + * PDCP interface + ***************************************************************************/ +void rlc_am::write_sdu(unique_byte_buffer_t sdu) +{ + uint32_t nof_bytes = sdu->N_bytes; + if (tx_base->write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) { + std::lock_guard lock(metrics_mutex); + metrics.num_tx_sdus++; + metrics.num_tx_sdu_bytes += nof_bytes; + } +} + +void rlc_am::discard_sdu(uint32_t discard_sn) +{ + tx_base->discard_sdu(discard_sn); + + std::lock_guard lock(metrics_mutex); + metrics.num_lost_sdus++; +} + +bool rlc_am::sdu_queue_is_full() +{ + return tx_base->sdu_queue_is_full(); +} + +/**************************************************************************** + * MAC interface + ***************************************************************************/ +bool rlc_am::has_data() +{ + return tx_base->has_data(); +} + +uint32_t rlc_am::get_buffer_state() +{ + return tx_base->get_buffer_state(); +} + +void rlc_am::get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio) +{ + tx_base->get_buffer_state(n_bytes_newtx, n_bytes_prio); + return; +} + +uint32_t rlc_am::read_pdu(uint8_t* payload, uint32_t nof_bytes) +{ + uint32_t read_bytes = tx_base->read_pdu(payload, nof_bytes); + + std::lock_guard lock(metrics_mutex); + metrics.num_tx_pdus += read_bytes > 0 ? 1 : 0; + metrics.num_tx_pdu_bytes += read_bytes; + return read_bytes; +} + +void rlc_am::write_pdu(uint8_t* payload, uint32_t nof_bytes) +{ + rx_base->write_pdu(payload, nof_bytes); + + std::lock_guard lock(metrics_mutex); + metrics.num_rx_pdus++; + metrics.num_rx_pdu_bytes += nof_bytes; +} + +/**************************************************************************** + * Metrics + ***************************************************************************/ +rlc_bearer_metrics_t rlc_am::get_metrics() +{ + // update values that aren't calculated on the fly + uint32_t latency = rx_base->get_sdu_rx_latency_ms(); + uint32_t buffered_bytes = rx_base->get_rx_buffered_bytes(); + + std::lock_guard lock(metrics_mutex); + metrics.rx_latency_ms = latency; + metrics.rx_buffered_bytes = buffered_bytes; + return metrics; +} + +void rlc_am::reset_metrics() +{ + std::lock_guard lock(metrics_mutex); + metrics = {}; +} + +/**************************************************************************** + * BSR callback + ***************************************************************************/ +void rlc_am::set_bsr_callback(bsr_callback_t callback) +{ + tx_base->set_bsr_callback(callback); +} + +/******************************************************* + * RLC AM TX entity + * This class is used for common code between the + * LTE and NR TX entities + *******************************************************/ +int rlc_am::rlc_am_base_tx::write_sdu(unique_byte_buffer_t sdu) +{ + std::lock_guard lock(mutex); + + if (!tx_enabled) { + return SRSRAN_ERROR; + } + + if (sdu.get() == nullptr) { + RlcWarning("NULL SDU pointer in write_sdu()"); + return SRSRAN_ERROR; + } + + // Get SDU info + uint32_t sdu_pdcp_sn = sdu->md.pdcp_sn; + + // Store SDU + uint8_t* msg_ptr = sdu->msg; + uint32_t nof_bytes = sdu->N_bytes; + srsran::error_type ret = tx_sdu_queue.try_write(std::move(sdu)); + if (ret) { + RlcHexInfo(msg_ptr, + nof_bytes, + "Tx SDU (%d B, PDCP_SN=%ld tx_sdu_queue_len=%d)", + nof_bytes, + sdu_pdcp_sn, + tx_sdu_queue.size()); + } else { + // in case of fail, the try_write returns back the sdu + RlcHexWarning(ret.error()->msg, + ret.error()->N_bytes, + "[Dropped SDU] Tx SDU (%d B, PDCP_SN=%ld, tx_sdu_queue_len=%d)", + ret.error()->N_bytes, + sdu_pdcp_sn, + tx_sdu_queue.size()); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +void rlc_am::rlc_am_base_tx::discard_sdu(uint32_t discard_sn) +{ + std::lock_guard lock(mutex); + + if (!tx_enabled) { + return; + } + bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) { + if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) { + tx_sdu_queue.queue.pop_func(sdu); + sdu = nullptr; + return true; + } + return false; + }); + + // Discard fails when the PDCP PDU is already in Tx window. + RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn); +} + +bool rlc_am::rlc_am_base_tx::sdu_queue_is_full() +{ + return tx_sdu_queue.is_full(); +} + +void rlc_am::rlc_am_base_tx::set_bsr_callback(bsr_callback_t callback) +{ + bsr_callback = callback; +} + +/******************************************************* + * RLC AM RX entity + * This class is used for common code between the + * LTE and NR TX entities + *******************************************************/ +void rlc_am::rlc_am_base_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes) +{ + RlcInfo("Rx PDU - N bytes %d", nof_bytes); + if (nof_bytes < 1) { + return; + } + + if (rlc_am_is_control_pdu(payload)) { + parent->tx_base->handle_control_pdu(payload, nof_bytes); + } else { + handle_data_pdu(payload, nof_bytes); + } +} +} // namespace srsran diff --git a/lib/src/upper/rlc_am_lte.cc b/lib/src/rlc/rlc_am_lte.cc similarity index 51% rename from lib/src/upper/rlc_am_lte.cc rename to lib/src/rlc/rlc_am_lte.cc index 23a182957..383fe3b30 100644 --- a/lib/src/upper/rlc_am_lte.cc +++ b/lib/src/rlc/rlc_am_lte.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,284 +19,47 @@ * */ -#include "srsran/upper/rlc_am_lte.h" -#include "srsran/common/string_helpers.h" +#include "srsran/rlc/rlc_am_lte.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/rlc/rlc_am_lte_packing.h" #include "srsran/srslog/event_trace.h" #include -#define MOD 1024 #define RX_MOD_BASE(x) (((x)-vr_r) % 1024) #define TX_MOD_BASE(x) (((x)-vt_a) % 1024) #define LCID (parent->lcid) -#define RB_NAME (parent->rb_name.c_str()) +#define MAX_SDUS_PER_PDU (128) namespace srsran { -/******************************* - * Helper methods - ******************************/ - -/** - * Logs Status PDU into provided log channel, using fmt_str as format string - */ -template -void log_rlc_am_status_pdu_to_string(srslog::log_channel& log_ch, - const char* fmt_str, - rlc_status_pdu_t* status, - Args&&... args) -{ - if (not log_ch.enabled()) { - return; - } - fmt::memory_buffer buffer; - fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->N_nack); - if (status->N_nack > 0) { - fmt::format_to(buffer, ", NACK_SN = "); - for (uint32_t i = 0; i < status->N_nack; ++i) { - if (status->nacks[i].has_so) { - fmt::format_to( - buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end); - } else { - fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn); - } - } - } - log_ch(fmt_str, std::forward(args)..., to_c_str(buffer)); -} - -/******************************* - * RLC AM Segments - ******************************/ - -int rlc_am_pdu_segment_pool::segment_resource::id() const -{ - return std::distance(parent_pool->segments.cbegin(), this); -} - -void rlc_am_pdu_segment_pool::segment_resource::release_pdcp_sn() -{ - pdcp_sn_ = invalid_pdcp_sn; - if (empty()) { - parent_pool->free_list.push_front(this); - } -} - -void rlc_am_pdu_segment_pool::segment_resource::release_rlc_sn() -{ - rlc_sn_ = invalid_rlc_sn; - if (empty()) { - parent_pool->free_list.push_front(this); - } -} - -rlc_am_pdu_segment_pool::rlc_am_pdu_segment_pool() -{ - for (segment_resource& s : segments) { - s.parent_pool = this; - free_list.push_front(&s); - } -} - -bool rlc_am_pdu_segment_pool::make_segment(rlc_amd_tx_pdu& rlc_list, pdcp_pdu_info& pdcp_list) -{ - if (not has_segments()) { - return false; - } - segment_resource* segment = free_list.pop_front(); - segment->rlc_sn_ = rlc_list.rlc_sn; - segment->pdcp_sn_ = pdcp_list.sn; - rlc_list.add_segment(*segment); - pdcp_list.add_segment(*segment); - return true; -} - -void pdcp_pdu_info::ack_segment(rlc_am_pdu_segment& segment) -{ - // remove from list - list.pop(&segment); - // signal pool that the pdcp handle is released - segment.release_pdcp_sn(); -} - -rlc_amd_tx_pdu::~rlc_amd_tx_pdu() -{ - while (not list.empty()) { - // remove from list - rlc_am_pdu_segment* segment = list.pop_front(); - // deallocate if also removed from PDCP - segment->release_rlc_sn(); - } -} - -/******************************* - * rlc_am_lte class - ******************************/ - -rlc_am_lte::rlc_am_lte(srslog::basic_logger& logger, - uint32_t lcid_, - srsue::pdcp_interface_rlc* pdcp_, - srsue::rrc_interface_rlc* rrc_, - srsran::timer_handler* timers_) : - logger(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_), tx(this), rx(this) -{} - -// Applies new configuration. Must be just reestablished or initiated -bool rlc_am_lte::configure(const rlc_config_t& cfg_) -{ - // determine bearer name and configure Rx/Tx objects - rb_name = rrc->get_rb_name(lcid); - - // store config - cfg = cfg_; - - if (not rx.configure(cfg.am)) { - logger.error("Error configuring bearer (RX)"); - return false; - } - - if (not tx.configure(cfg)) { - logger.error("Error configuring bearer (TX)"); - return false; - } - - logger.info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " - "t_reordering=%d, t_status_prohibit=%d", - rb_name.c_str(), - cfg.am.t_poll_retx, - cfg.am.poll_pdu, - cfg.am.poll_byte, - cfg.am.max_retx_thresh, - cfg.am.t_reordering, - cfg.am.t_status_prohibit); - return true; -} - -void rlc_am_lte::set_bsr_callback(bsr_callback_t callback) -{ - tx.set_bsr_callback(callback); -} - -void rlc_am_lte::empty_queue() -{ - // Drop all messages in TX SDU queue - tx.empty_queue(); -} - -void rlc_am_lte::reestablish() -{ - logger.debug("Reestablished bearer %s", rb_name.c_str()); - tx.reestablish(); // calls stop and enables tx again - rx.reestablish(); // calls only stop -} - -void rlc_am_lte::stop() -{ - logger.debug("Stopped bearer %s", rb_name.c_str()); - tx.stop(); - rx.stop(); -} - -rlc_mode_t rlc_am_lte::get_mode() -{ - return rlc_mode_t::am; -} - -uint32_t rlc_am_lte::get_bearer() -{ - return lcid; -} - -rlc_bearer_metrics_t rlc_am_lte::get_metrics() -{ - // update values that aren't calculated on the fly - metrics.rx_latency_ms = rx.get_sdu_rx_latency_ms(); - metrics.rx_buffered_bytes = rx.get_rx_buffered_bytes(); - return metrics; -} - -void rlc_am_lte::reset_metrics() -{ - metrics = {}; -} - -/**************************************************************************** - * PDCP interface - ***************************************************************************/ - -void rlc_am_lte::write_sdu(unique_byte_buffer_t sdu) -{ - if (tx.write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) { - metrics.num_tx_sdus++; - } -} - -void rlc_am_lte::discard_sdu(uint32_t discard_sn) -{ - tx.discard_sdu(discard_sn); - metrics.num_lost_sdus++; -} - -bool rlc_am_lte::sdu_queue_is_full() -{ - return tx.sdu_queue_is_full(); -} - -/**************************************************************************** - * MAC interface - ***************************************************************************/ - -bool rlc_am_lte::has_data() -{ - return tx.has_data(); -} - -uint32_t rlc_am_lte::get_buffer_state() -{ - return tx.get_buffer_state(); -} - -int rlc_am_lte::read_pdu(uint8_t* payload, uint32_t nof_bytes) -{ - int read_bytes = tx.read_pdu(payload, nof_bytes); - metrics.num_tx_pdus++; - metrics.num_tx_pdu_bytes += read_bytes; - return read_bytes; -} - -void rlc_am_lte::write_pdu(uint8_t* payload, uint32_t nof_bytes) -{ - rx.write_pdu(payload, nof_bytes); - metrics.num_rx_pdus++; - metrics.num_rx_pdu_bytes += nof_bytes; -} +using pdcp_pdu_info_lte = pdcp_pdu_info; +using rlc_amd_tx_pdu_lte = rlc_amd_tx_pdu; +using rlc_am_pdu_segment = rlc_am_pdu_segment_pool::segment_resource; /**************************************************************************** * Tx subclass implementation ***************************************************************************/ - -rlc_am_lte::rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) : +rlc_am_lte_tx::rlc_am_lte_tx(rlc_am* parent_) : parent(parent_), - logger(parent_->logger), pool(byte_buffer_pool::get_instance()), poll_retx_timer(parent_->timers->get_unique_timer()), - status_prohibit_timer(parent_->timers->get_unique_timer()) -{} - -rlc_am_lte::rlc_am_lte_tx::~rlc_am_lte_tx() {} - -void rlc_am_lte::rlc_am_lte_tx::set_bsr_callback(bsr_callback_t callback) + status_prohibit_timer(parent_->timers->get_unique_timer()), + rlc_am_base_tx(parent_->logger) { - bsr_callback = callback; + rx = dynamic_cast(parent->rx_base.get()); } -bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_) +bool rlc_am_lte_tx::configure(const rlc_config_t& cfg_) { + std::lock_guard lock(mutex); + + rb_name = parent->rb_name; + if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) { - logger.error("Configuring Tx queue length of %d PDUs too big. Maximum value is %d.", - cfg_.tx_queue_length, - MAX_SDUS_PER_RLC_PDU); + RlcError("Configuring Tx queue length of %d PDUs too big. Maximum value is %d.", + cfg_.tx_queue_length, + MAX_SDUS_PER_RLC_PDU); return false; } @@ -305,7 +68,7 @@ bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_) // check timers if (not poll_retx_timer.is_valid() or not status_prohibit_timer.is_valid()) { - logger.error("Configuring RLC AM TX: timers not configured"); + RlcError("Configuring RLC AM TX: timers not configured"); return false; } @@ -319,6 +82,8 @@ bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_) poll_retx_timer.set(static_cast(cfg.t_poll_retx), [this](uint32_t timerid) { timer_expired(timerid); }); } + // make sure Tx queue is empty before attempting to resize + empty_queue_nolock(); tx_sdu_queue.resize(cfg_.tx_queue_length); tx_enabled = true; @@ -326,11 +91,15 @@ bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_) return true; } -void rlc_am_lte::rlc_am_lte_tx::stop() +void rlc_am_lte_tx::stop() { - empty_queue(); - std::lock_guard lock(mutex); + stop_nolock(); +} + +void rlc_am_lte_tx::stop_nolock() +{ + empty_queue_nolock(); tx_enabled = false; @@ -360,10 +129,14 @@ void rlc_am_lte::rlc_am_lte_tx::stop() undelivered_sdu_info_queue.clear(); } -void rlc_am_lte::rlc_am_lte_tx::empty_queue() +void rlc_am_lte_tx::empty_queue() { std::lock_guard lock(mutex); + empty_queue_nolock(); +} +void rlc_am_lte_tx::empty_queue_nolock() +{ // deallocate all SDUs in transmit queue while (tx_sdu_queue.size() > 0) { unique_byte_buffer_t buf = tx_sdu_queue.read(); @@ -376,19 +149,20 @@ void rlc_am_lte::rlc_am_lte_tx::empty_queue() tx_sdu.reset(); } -void rlc_am_lte::rlc_am_lte_tx::reestablish() +void rlc_am_lte_tx::reestablish() { - stop(); + std::lock_guard lock(mutex); + stop_nolock(); tx_enabled = true; } -bool rlc_am_lte::rlc_am_lte_tx::do_status() +bool rlc_am_lte_tx::do_status() { - return parent->rx.get_do_status(); + return rx->get_do_status(); } // Function is supposed to return as fast as possible -bool rlc_am_lte::rlc_am_lte_tx::has_data() +bool rlc_am_lte_tx::has_data() { return (((do_status() && not status_prohibit_timer.is_running())) || // if we have a status PDU to transmit (not retx_queue.empty()) || // if we have a retransmission @@ -405,144 +179,105 @@ bool rlc_am_lte::rlc_am_lte_tx::has_data() * * @param sn The SN of the PDU to check */ -void rlc_am_lte::rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn) +void rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn) { if (tx_window[sn].retx_count == cfg.max_retx_thresh) { - logger.warning("%s Signaling max number of reTx=%d for SN=%d", RB_NAME, tx_window[sn].retx_count, sn); + RlcWarning("Signaling max number of reTx=%d for SN=%d", tx_window[sn].retx_count, sn); parent->rrc->max_retx_attempted(); srsran::pdcp_sn_vector_t pdcp_sns; for (const rlc_am_pdu_segment& segment : tx_window[sn]) { pdcp_sns.push_back(segment.pdcp_sn()); } parent->pdcp->notify_failure(parent->lcid, pdcp_sns); + + std::lock_guard lock(parent->metrics_mutex); parent->metrics.num_lost_pdus++; } } -uint32_t rlc_am_lte::rlc_am_lte_tx::get_buffer_state() +uint32_t rlc_am_lte_tx::get_buffer_state() +{ + uint32_t new_tx_queue = 0, prio_tx_queue = 0; + get_buffer_state(new_tx_queue, prio_tx_queue); + return new_tx_queue + prio_tx_queue; +} + +void rlc_am_lte_tx::get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio) { std::lock_guard lock(mutex); - uint32_t n_bytes = 0; - uint32_t n_sdus = 0; + get_buffer_state_nolock(n_bytes_newtx, n_bytes_prio); +} - logger.debug("%s Buffer state - do_status=%s, status_prohibit_running=%s (%d/%d)", - RB_NAME, - do_status() ? "yes" : "no", - status_prohibit_timer.is_running() ? "yes" : "no", - status_prohibit_timer.time_elapsed(), - status_prohibit_timer.duration()); +void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio) +{ + n_bytes_newtx = 0; + n_bytes_prio = 0; + uint32_t n_sdus = 0; + + if (not tx_enabled) { + return; + } + + RlcDebug("Buffer state - do_status=%s, status_prohibit_running=%s (%d/%d)", + do_status() ? "yes" : "no", + status_prohibit_timer.is_running() ? "yes" : "no", + status_prohibit_timer.time_elapsed(), + status_prohibit_timer.duration()); // Bytes needed for status report if (do_status() && not status_prohibit_timer.is_running()) { - n_bytes += parent->rx.get_status_pdu_length(); - logger.debug("%s Buffer state - total status report: %d bytes", RB_NAME, n_bytes); + n_bytes_prio += rx->get_status_pdu_length(); + RlcDebug("Buffer state - total status report: %d bytes", n_bytes_prio); } // Bytes needed for retx if (not retx_queue.empty()) { - rlc_amd_retx_t& retx = retx_queue.front(); - logger.debug("%s Buffer state - retx - SN=%d, Segment: %s, %d:%d", - RB_NAME, - retx.sn, - retx.is_segment ? "true" : "false", - retx.so_start, - retx.so_end); + rlc_amd_retx_lte_t& retx = retx_queue.front(); + RlcDebug("Buffer state - retx - SN=%d, Segment: %s, %d:%d", + retx.sn, + retx.is_segment ? "true" : "false", + retx.so_start, + retx.so_end); if (tx_window.has_sn(retx.sn)) { int req_bytes = required_buffer_size(retx); if (req_bytes < 0) { - logger.error("In get_buffer_state(): Removing retx.sn=%d from queue", retx.sn); + RlcError("In get_buffer_state(): Removing retx.sn=%d from queue", retx.sn); retx_queue.pop(); } else { - n_bytes += req_bytes; - logger.debug("Buffer state - retx: %d bytes", n_bytes); + n_bytes_prio += req_bytes; + RlcDebug("Buffer state - retx: %d bytes", n_bytes_prio); } } } // Bytes needed for tx SDUs - if (tx_window.size() < 1024) { + if (not window_full()) { n_sdus = tx_sdu_queue.get_n_sdus(); - n_bytes += tx_sdu_queue.size_bytes(); + n_bytes_newtx += tx_sdu_queue.size_bytes(); if (tx_sdu != NULL) { n_sdus++; - n_bytes += tx_sdu->N_bytes; + n_bytes_newtx += tx_sdu->N_bytes; } } // Room needed for header extensions? (integer rounding) if (n_sdus > 1) { - n_bytes += ((n_sdus - 1) * 1.5) + 0.5; + n_bytes_newtx += ((n_sdus - 1) * 1.5) + 0.5; } // Room needed for fixed header of data PDUs - if (n_bytes > 0 && n_sdus > 0) { - n_bytes += 2; // Two bytes for fixed header with SN length = 10 - logger.debug("%s Total buffer state - %d SDUs (%d B)", RB_NAME, n_sdus, n_bytes); + if (n_bytes_newtx > 0 && n_sdus > 0) { + n_bytes_newtx += 2; // Two bytes for fixed header with SN length = 10 + RlcDebug("Total buffer state - %d SDUs (%d B)", n_sdus, n_bytes_newtx); } - return n_bytes; + if (bsr_callback) { + RlcDebug("Calling BSR callback - %d new_tx, %d prio bytes", n_bytes_newtx, n_bytes_prio); + bsr_callback(parent->lcid, n_bytes_newtx, n_bytes_prio); + } } -int rlc_am_lte::rlc_am_lte_tx::write_sdu(unique_byte_buffer_t sdu) -{ - std::lock_guard lock(mutex); - - if (!tx_enabled) { - return SRSRAN_ERROR; - } - - if (sdu.get() == nullptr) { - logger.warning("NULL SDU pointer in write_sdu()"); - return SRSRAN_ERROR; - } - - // Get SDU info - uint32_t sdu_pdcp_sn = sdu->md.pdcp_sn; - - // Store SDU - uint8_t* msg_ptr = sdu->msg; - uint32_t nof_bytes = sdu->N_bytes; - srsran::error_type ret = tx_sdu_queue.try_write(std::move(sdu)); - if (ret) { - logger.info(msg_ptr, nof_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", RB_NAME, nof_bytes, tx_sdu_queue.size()); - } else { - // in case of fail, the try_write returns back the sdu - logger.warning(ret.error()->msg, - ret.error()->N_bytes, - "[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)", - RB_NAME, - ret.error()->N_bytes, - tx_sdu_queue.size()); - return SRSRAN_ERROR; - } - - return SRSRAN_SUCCESS; -} - -void rlc_am_lte::rlc_am_lte_tx::discard_sdu(uint32_t discard_sn) -{ - if (!tx_enabled) { - return; - } - - bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) { - if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) { - tx_sdu_queue.queue.pop_func(sdu); - sdu = nullptr; - } - return false; - }); - - // Discard fails when the PDCP PDU is already in Tx window. - logger.info("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn); -} - -bool rlc_am_lte::rlc_am_lte_tx::sdu_queue_is_full() -{ - return tx_sdu_queue.is_full(); -} - -int rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) { std::lock_guard lock(mutex); @@ -550,11 +285,11 @@ int rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) return 0; } - logger.debug("MAC opportunity - %d bytes", nof_bytes); - logger.debug("tx_window size - %zu PDUs", tx_window.size()); + RlcDebug("MAC opportunity - %d bytes", nof_bytes); + RlcDebug("tx_window size - %zu PDUs", tx_window.size()); if (not tx_enabled) { - logger.debug("RLC entity not active. Not generating PDU."); + RlcDebug("RLC entity not active. Not generating PDU."); return 0; } @@ -564,8 +299,8 @@ int rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) } // Section 5.2.2.3 in TS 36.311, if tx_window is full and retx_queue empty, retransmit PDU - if (tx_window.size() >= RLC_AM_WINDOW_SIZE && retx_queue.empty()) { - retransmit_pdu(); + if (window_full() && retx_queue.empty()) { + retransmit_pdu(vt_a); } // RETX if required @@ -580,52 +315,64 @@ int rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) return build_data_pdu(payload, nof_bytes); } -void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id) +void rlc_am_lte_tx::timer_expired(uint32_t timeout_id) { std::unique_lock lock(mutex); if (poll_retx_timer.is_valid() && poll_retx_timer.id() == timeout_id) { - logger.debug("%s Poll reTx timer expired after %dms", RB_NAME, poll_retx_timer.duration()); - // Section 5.2.2.3 in TS 36.311, schedule PDU for retransmission if - // (a) both tx and retx buffer are empty, or + RlcDebug("Poll retx timer expired after %dms", poll_retx_timer.duration()); + // Section 5.2.2.3 in TS 36.322, schedule PDU for retransmission if + // (a) both tx and retx buffer are empty (excluding tx'ed PDU waiting for ack), or // (b) no new data PDU can be transmitted (tx window is full) - if ((retx_queue.empty() && tx_sdu_queue.size() == 0) || tx_window.size() >= RLC_AM_WINDOW_SIZE) { - retransmit_pdu(); + if ((retx_queue.empty() && tx_sdu_queue.size() == 0) || window_full()) { + retransmit_pdu(vt_a); // TODO: TS says to send vt_s - 1 here } + } else if (status_prohibit_timer.is_valid() && status_prohibit_timer.id() == timeout_id) { + RlcDebug("Status prohibit timer expired after %dms", status_prohibit_timer.duration()); } - lock.unlock(); - if (bsr_callback) { - bsr_callback(parent->lcid, get_buffer_state(), 0); + uint32_t new_tx_queue = 0, prio_tx_queue = 0; + get_buffer_state_nolock(new_tx_queue, prio_tx_queue); } } -void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu() +void rlc_am_lte_tx::retransmit_pdu(uint32_t sn) { if (tx_window.empty()) { - logger.warning("%s No PDU to retransmit.", RB_NAME); + RlcWarning("No PDU to retransmit"); return; } - if (not tx_window.has_sn(vt_a)) { - logger.warning("%s Can't retransmit unexisting SN=%d.", RB_NAME, vt_a); + if (not tx_window.has_sn(sn)) { + RlcWarning("Can't retransmit unexisting SN=%d", sn); return; } // select first PDU in tx window for retransmission - rlc_amd_tx_pdu& pdu = tx_window[vt_a]; - logger.info("%s Schedule SN=%d for reTx.", RB_NAME, pdu.rlc_sn); - rlc_amd_retx_t& retx = retx_queue.push(); - retx.is_segment = false; - retx.so_start = 0; - retx.so_end = pdu.buf->N_bytes; - retx.sn = pdu.rlc_sn; + rlc_amd_tx_pdu_lte& pdu = tx_window[sn]; + + // increment retx counter and inform upper layers + pdu.retx_count++; + check_sn_reached_max_retx(sn); + + RlcInfo("Schedule SN=%d for retx", pdu.rlc_sn); + + rlc_amd_retx_lte_t& retx = retx_queue.push(); + retx.is_segment = false; + retx.so_start = 0; + retx.so_end = pdu.buf->N_bytes; + retx.sn = pdu.rlc_sn; } /**************************************************************************** * Helper functions ***************************************************************************/ +bool rlc_am_lte_tx::window_full() +{ + return TX_MOD_BASE(vt_s) >= RLC_AM_WINDOW_SIZE; +}; + /** * Called when building a RLC PDU for checking whether the poll bit needs * to be set. @@ -634,50 +381,55 @@ void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu() * * @return True if a status PDU needs to be requested, false otherwise. */ -bool rlc_am_lte::rlc_am_lte_tx::poll_required() +bool rlc_am_lte_tx::poll_required() { if (cfg.poll_pdu > 0 && pdu_without_poll > static_cast(cfg.poll_pdu)) { + RlcDebug("Poll required. Cause: PDU_WITHOUT_POLL > pollPdu."); return true; } if (cfg.poll_byte > 0 && byte_without_poll > static_cast(cfg.poll_byte)) { + RlcDebug("Poll required. Cause: BYTE_WITHOUT_POLL > pollByte."); return true; } if (poll_retx_timer.is_valid() && poll_retx_timer.is_expired()) { // re-arming of timer is handled by caller + RlcDebug("Poll required. Cause: t-PollRetransmission expired."); return true; } - if (tx_window.size() >= RLC_AM_WINDOW_SIZE) { + if (window_full()) { + RlcDebug("Poll required. Cause: TX window full."); return true; } if (tx_sdu_queue.size() == 0 && retx_queue.empty()) { + RlcDebug("Poll required. Cause: Empty TX and ReTX queues."); return true; } /* According to 5.2.2.1 in 36.322 v13.3.0 a poll should be requested if * the entire AM window is unacknowledged, i.e. no new PDU can be transmitted. - * However, it seems more appropiate to request more often if polling + * However, it seems more appropriate to request more often if polling * is disabled otherwise, e.g. every N PDUs. */ - if (cfg.poll_pdu == 0 && cfg.poll_byte == 0 && vt_s % poll_periodicity == 0) { + if (cfg.poll_pdu == 0 && cfg.poll_byte == 0 && vt_s % rlc_am::poll_periodicity == 0) { return true; } return false; } -int rlc_am_lte::rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_bytes) +int rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_bytes) { - int pdu_len = parent->rx.get_status_pdu(&tx_status, nof_bytes); - log_rlc_am_status_pdu_to_string(logger.debug, "%s", &tx_status); - if (pdu_len > 0 && nof_bytes >= static_cast(pdu_len)) { - log_rlc_am_status_pdu_to_string(logger.info, "%s Tx status PDU - %s", &tx_status, RB_NAME); - - parent->rx.reset_status(); - + RlcDebug("Generating status PDU. Nof bytes %d", nof_bytes); + int pdu_len = rx->get_status_pdu(&tx_status, nof_bytes); + if (pdu_len == SRSRAN_ERROR) { + RlcDebug("Deferred Status PDU. Cause: Failed to acquire Rx lock"); + pdu_len = 0; + } else if (pdu_len > 0 && nof_bytes >= static_cast(pdu_len)) { + log_rlc_am_status_pdu_to_string(logger.info, rb_name, "Tx status PDU - %s", &tx_status); if (cfg.t_status_prohibit > 0 && status_prohibit_timer.is_valid()) { // re-arm timer status_prohibit_timer.run(); @@ -685,22 +437,22 @@ int rlc_am_lte::rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_b debug_state(); pdu_len = rlc_am_write_status_pdu(&tx_status, payload); } else { - logger.info("%s Cannot tx status PDU - %d bytes available, %d bytes required", RB_NAME, nof_bytes, pdu_len); + RlcInfo("Cannot tx status PDU - %d bytes available, %d bytes required", nof_bytes, pdu_len); pdu_len = 0; } return pdu_len; } -int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) +int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) { // Check there is at least 1 element before calling front() if (retx_queue.empty()) { - logger.error("In build_retx_pdu(): retx_queue is empty"); + RlcError("In build_retx_pdu(): retx_queue is empty"); return -1; } - rlc_amd_retx_t retx = retx_queue.front(); + rlc_amd_retx_lte_t retx = retx_queue.front(); // Sanity check - drop any retx SNs not present in tx_window while (not tx_window.has_sn(retx.sn)) { @@ -708,21 +460,28 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt if (!retx_queue.empty()) { retx = retx_queue.front(); } else { - logger.info("In build_retx_pdu(): retx_queue is empty during sanity check, sn=%d", retx.sn); - return 0; + RlcInfo("SN=%d not in Tx window. Ignoring retx.", retx.sn); + if (tx_window.has_sn(vt_a)) { + // schedule next SN for retx + retransmit_pdu(vt_a); + retx = retx_queue.front(); + } else { + // empty tx window, can't provide retx PDU + return 0; + } } } // Is resegmentation needed? int req_size = required_buffer_size(retx); if (req_size < 0) { - logger.error("In build_retx_pdu(): Removing retx.sn=%d from queue", retx.sn); + RlcError("In build_retx_pdu(): Removing retx.sn=%d from queue", retx.sn); retx_queue.pop(); return -1; } if (retx.is_segment || req_size > static_cast(nof_bytes)) { - logger.debug("%s build_retx_pdu - resegmentation required", RB_NAME); + RlcDebug("build_retx_pdu - resegmentation required"); return build_segment(payload, nof_bytes, retx); } @@ -733,8 +492,8 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt // Set poll bit pdu_without_poll++; byte_without_poll += (tx_window[retx.sn].buf->N_bytes + rlc_am_packed_length(&new_header)); - logger.info("%s pdu_without_poll: %d", RB_NAME, pdu_without_poll); - logger.info("%s byte_without_poll: %d", RB_NAME, byte_without_poll); + RlcInfo("pdu_without_poll: %d", pdu_without_poll); + RlcInfo("byte_without_poll: %d", byte_without_poll); if (poll_required()) { new_header.p = 1; // vt_s won't change for reTx, so don't update poll_sn @@ -742,6 +501,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt byte_without_poll = 0; if (poll_retx_timer.is_valid()) { // re-arm timer (will be stopped when status PDU is received) + RlcDebug("re-arming retx timer"); poll_retx_timer.run(); } } @@ -751,27 +511,24 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt memcpy(ptr, tx_window[retx.sn].buf->msg, tx_window[retx.sn].buf->N_bytes); retx_queue.pop(); - tx_window[retx.sn].retx_count++; - check_sn_reached_max_retx(retx.sn); - logger.info(payload, - tx_window[retx.sn].buf->N_bytes, - "%s Tx PDU SN=%d (%d B) (attempt %d/%d)", - RB_NAME, - retx.sn, - tx_window[retx.sn].buf->N_bytes, - tx_window[retx.sn].retx_count + 1, - cfg.max_retx_thresh); - log_rlc_amd_pdu_header_to_string(logger.debug, new_header); + RlcHexInfo(payload, + tx_window[retx.sn].buf->N_bytes, + "Tx PDU SN=%d (%d B) (attempt %d/%d)", + retx.sn, + tx_window[retx.sn].buf->N_bytes, + tx_window[retx.sn].retx_count + 1, + cfg.max_retx_thresh); + log_rlc_amd_pdu_header_to_string(logger.debug, rb_name, "Tx PDU - %s", new_header); debug_state(); return (ptr - payload) + tx_window[retx.sn].buf->N_bytes; } -int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx) +int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_lte_t retx) { if (tx_window[retx.sn].buf == NULL) { - logger.error("In build_segment: retx.sn=%d has null buffer", retx.sn); + RlcError("In build_segment: retx.sn=%d has null buffer", retx.sn); return 0; } if (!retx.is_segment) { @@ -785,8 +542,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte pdu_without_poll++; byte_without_poll += (tx_window[retx.sn].buf->N_bytes + rlc_am_packed_length(&new_header)); - logger.info("%s pdu_without_poll: %d", RB_NAME, pdu_without_poll); - logger.info("%s byte_without_poll: %d", RB_NAME, byte_without_poll); + RlcInfo("pdu_without_poll: %d, byte_without_poll: %d", pdu_without_poll, byte_without_poll); new_header.dc = RLC_DC_FIELD_DATA_PDU; new_header.rf = 1; @@ -795,17 +551,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte new_header.lsf = 0; new_header.so = retx.so_start; new_header.N_li = 0; - new_header.p = 0; - if (poll_required()) { - logger.debug("%s setting poll bit to request status", RB_NAME); - new_header.p = 1; - // vt_s won't change for reTx, so don't update poll_sn - pdu_without_poll = 0; - byte_without_poll = 0; - if (poll_retx_timer.is_valid()) { - poll_retx_timer.run(); - } - } + new_header.p = 0; // Poll Requirements are done later after updating RETX queue uint32_t head_len = 0; uint32_t pdu_space = 0; @@ -817,10 +563,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte } if (nof_bytes <= head_len) { - logger.info("%s Cannot build a PDU segment - %d bytes available, %d bytes required for header", - RB_NAME, - nof_bytes, - head_len); + RlcInfo("Cannot build a PDU segment - %d bytes available, %d bytes required for header", nof_bytes, head_len); return 0; } @@ -911,8 +654,17 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte retx_queue.front().so_start = retx.so_end; } - tx_window[retx.sn].retx_count++; - check_sn_reached_max_retx(retx.sn); + // Check POLL requeriments for segment + if (poll_required()) { + RlcDebug("setting poll bit to request status"); + new_header.p = 1; + // vt_s won't change for reTx, so don't update poll_sn + pdu_without_poll = 0; + byte_without_poll = 0; + if (poll_retx_timer.is_valid()) { + poll_retx_timer.run(); + } + } // Write header and pdu uint8_t* ptr = payload; @@ -924,46 +676,43 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte debug_state(); int pdu_len = (ptr - payload) + len; if (pdu_len > static_cast(nof_bytes)) { - logger.error("%s Retx PDU segment length error. Available: %d, Used: %d", RB_NAME, nof_bytes, pdu_len); + RlcError("Retx PDU segment length error. Available: %d, Used: %d", nof_bytes, pdu_len); int header_len = (ptr - payload); - logger.debug("%s Retx PDU segment length error. Actual header len: %d, Payload len: %d, N_li: %d", - RB_NAME, - header_len, - len, - new_header.N_li); + RlcDebug("Retx PDU segment length error. Actual header len: %d, Payload len: %d, N_li: %d", + header_len, + len, + new_header.N_li); } - logger.info(payload, - pdu_len, - "%s Retx PDU segment SN=%d [so=%d] (%d B) (attempt %d/%d)", - RB_NAME, - retx.sn, - retx.so_start, - pdu_len, - tx_window[retx.sn].retx_count + 1, - cfg.max_retx_thresh); + RlcHexInfo(payload, + pdu_len, + "retx PDU segment SN=%d [so=%d] (%d B) (attempt %d/%d)", + retx.sn, + retx.so_start, + pdu_len, + tx_window[retx.sn].retx_count + 1, + cfg.max_retx_thresh); return pdu_len; } -int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes) +int rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes) { if (tx_sdu == NULL && tx_sdu_queue.is_empty()) { - logger.info("No data available to be sent"); + RlcInfo("No data available to be sent"); return 0; } // do not build any more PDU if window is already full - if (tx_sdu == NULL && tx_window.size() >= RLC_AM_WINDOW_SIZE) { - logger.info("Tx window full."); + if (window_full()) { + RlcInfo("Cannot build data PDU - Tx window full."); return 0; } if (nof_bytes < RLC_AM_MIN_DATA_PDU_SIZE) { - logger.info("%s Cannot build data PDU - %d bytes available but at least %d bytes are required ", - RB_NAME, - nof_bytes, - RLC_AM_MIN_DATA_PDU_SIZE); + RlcInfo("Cannot build data PDU - %d bytes available but at least %d bytes are required ", + nof_bytes, + RLC_AM_MIN_DATA_PDU_SIZE); return 0; } @@ -980,7 +729,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt } exit(-1); #else - logger.error("Fatal Error: Couldn't allocate PDU in build_data_pdu()."); + RlcError("Fatal Error: Couldn't allocate PDU in build_data_pdu()."); return 0; #endif } @@ -990,13 +739,13 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt header.sn = vt_s; if (not segment_pool.has_segments()) { - logger.info("Can't build a PDU - No segments available"); + RlcInfo("Can't build a PDU - No segments available"); return 0; } // insert newly assigned SN into window and use reference for in-place operations // NOTE: from now on, we can't return from this function anymore before increasing vt_s - rlc_amd_tx_pdu& tx_pdu = tx_window.add_pdu(header.sn); + rlc_amd_tx_pdu_lte& tx_pdu = tx_window.add_pdu(header.sn); uint32_t head_len = rlc_am_packed_length(&header); uint32_t to_move = 0; @@ -1004,7 +753,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt uint32_t pdu_space = SRSRAN_MIN(nof_bytes, pdu->get_tailroom()); uint8_t* pdu_ptr = pdu->msg; - logger.debug("%s Building PDU - pdu_space: %d, head_len: %d ", RB_NAME, pdu_space, head_len); + RlcDebug("Building PDU - pdu_space: %d, head_len: %d ", pdu_space, head_len); // Check for SDU segment if (tx_sdu != nullptr) { @@ -1016,18 +765,18 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt tx_sdu->N_bytes -= to_move; tx_sdu->msg += to_move; if (undelivered_sdu_info_queue.has_pdcp_sn(tx_sdu->md.pdcp_sn)) { - pdcp_pdu_info& pdcp_pdu = undelivered_sdu_info_queue[tx_sdu->md.pdcp_sn]; + pdcp_pdu_info_lte& pdcp_pdu = undelivered_sdu_info_queue[tx_sdu->md.pdcp_sn]; segment_pool.make_segment(tx_pdu, pdcp_pdu); if (tx_sdu->N_bytes == 0) { pdcp_pdu.fully_txed = true; } } else { // PDCP SNs for the RLC SDU has been removed from the queue - logger.warning("Couldn't find PDCP_SN=%d in SDU info queue (segment)", tx_sdu->md.pdcp_sn); + RlcWarning("Couldn't find PDCP_SN=%d in SDU info queue (segment)", tx_sdu->md.pdcp_sn); } if (tx_sdu->N_bytes == 0) { - logger.debug("%s Complete SDU scheduled for tx.", RB_NAME); + RlcDebug("Complete SDU scheduled for tx."); tx_sdu.reset(); } if (pdu_space > to_move) { @@ -1037,19 +786,17 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt } header.fi |= RLC_FI_FIELD_NOT_START_ALIGNED; // First byte does not correspond to first byte of SDU - logger.debug( - "%s Building PDU - added SDU segment from previous PDU (len:%d) - pdu_space: %d, head_len: %d header_sn=%d", - RB_NAME, - to_move, - pdu_space, - head_len, - header.sn); + RlcDebug("Building PDU - added SDU segment from previous PDU (len:%d) - pdu_space: %d, head_len: %d header_sn=%d", + to_move, + pdu_space, + head_len, + header.sn); } // Pull SDUs from queue - while (pdu_space > head_len && tx_sdu_queue.get_n_sdus() > 0 && header.N_li < RLC_AM_WINDOW_SIZE) { + while (pdu_space > head_len && tx_sdu_queue.get_n_sdus() > 0 && header.N_li < MAX_SDUS_PER_PDU) { if (not segment_pool.has_segments()) { - logger.info("Can't build a PDU segment - No segment resources available"); + RlcInfo("Can't build a PDU segment - No segment resources available"); if (pdu_ptr != pdu->msg) { break; // continue with the segments created up to this point } @@ -1080,14 +827,14 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt // store sdu info if (undelivered_sdu_info_queue.has_pdcp_sn(tx_sdu->md.pdcp_sn)) { - logger.warning("PDCP_SN=%d already marked as undelivered", tx_sdu->md.pdcp_sn); + RlcWarning("PDCP_SN=%d already marked as undelivered", tx_sdu->md.pdcp_sn); } else { - logger.debug("marking pdcp_sn=%d as undelivered (queue_len=%ld)", - tx_sdu->md.pdcp_sn, - undelivered_sdu_info_queue.nof_sdus()); + RlcDebug("marking pdcp_sn=%d as undelivered (queue_len=%ld)", + tx_sdu->md.pdcp_sn, + undelivered_sdu_info_queue.nof_sdus()); undelivered_sdu_info_queue.add_pdcp_sdu(tx_sdu->md.pdcp_sn); } - pdcp_pdu_info& pdcp_pdu = undelivered_sdu_info_queue[tx_sdu->md.pdcp_sn]; + pdcp_pdu_info_lte& pdcp_pdu = undelivered_sdu_info_queue[tx_sdu->md.pdcp_sn]; to_move = ((pdu_space - head_len) >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : pdu_space - head_len; memcpy(pdu_ptr, tx_sdu->msg, to_move); @@ -1102,7 +849,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt } if (tx_sdu->N_bytes == 0) { - logger.debug("%s Complete SDU scheduled for tx. PDCP SN=%d", RB_NAME, tx_sdu->md.pdcp_sn); + RlcDebug("Complete SDU scheduled for tx. PDCP SN=%d", tx_sdu->md.pdcp_sn); tx_sdu.reset(); } if (pdu_space > to_move) { @@ -1111,16 +858,12 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt pdu_space = 0; } - logger.debug("%s Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d ", - RB_NAME, - to_move, - pdu_space, - head_len); + RlcDebug("Building PDU - added SDU segment (len:%d) - pdu_space: %d, head_len: %d ", to_move, pdu_space, head_len); } // Make sure, at least one SDU (segment) has been added until this point if (pdu->N_bytes == 0) { - logger.error("Generated empty RLC PDU."); + RlcError("Generated empty RLC PDU."); } if (tx_sdu != NULL) { @@ -1130,10 +873,10 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt // Set Poll bit pdu_without_poll++; byte_without_poll += (pdu->N_bytes + head_len); - logger.debug("%s pdu_without_poll: %d", RB_NAME, pdu_without_poll); - logger.debug("%s byte_without_poll: %d", RB_NAME, byte_without_poll); + RlcDebug("pdu_without_poll: %d", pdu_without_poll); + RlcDebug("byte_without_poll: %d", byte_without_poll); if (poll_required()) { - logger.debug("%s setting poll bit to request status", RB_NAME); + RlcDebug("setting poll bit to request status"); header.p = 1; poll_sn = vt_s; pdu_without_poll = 0; @@ -1155,54 +898,74 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt rlc_am_write_data_pdu_header(&header, &ptr); memcpy(ptr, buffer_ptr->msg, buffer_ptr->N_bytes); int total_len = (ptr - payload) + buffer_ptr->N_bytes; - logger.info(payload, total_len, "%s Tx PDU SN=%d (%d B)", RB_NAME, header.sn, total_len); - log_rlc_amd_pdu_header_to_string(logger.debug, header); + RlcHexInfo(payload, total_len, "Tx PDU SN=%d (%d B)", header.sn, total_len); + log_rlc_amd_pdu_header_to_string(logger.debug, rb_name, "%s", header); debug_state(); return total_len; } -void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) +void rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) { if (not tx_enabled) { return; } - std::unique_lock lock(mutex); + // Local variables for handling Status PDU will be updated with lock + rlc_status_pdu_t status = {}; + uint32_t i = 0; + uint32_t vt_s_local = 0; - logger.info(payload, nof_bytes, "%s Rx control PDU", RB_NAME); + { + std::lock_guard lock(mutex); - rlc_status_pdu_t status; - rlc_am_read_status_pdu(payload, nof_bytes, &status); + RlcHexDebug(payload, nof_bytes, "Rx control PDU"); - log_rlc_am_status_pdu_to_string(logger.info, "%s Rx Status PDU: %s", &status, RB_NAME); + rlc_am_read_status_pdu(payload, nof_bytes, &status); - // Sec 5.2.2.2, stop poll reTx timer if status PDU comprises a positive _or_ negative acknowledgement - // for the RLC data PDU with sequence number poll_sn - if (poll_retx_timer.is_valid() && (TX_MOD_BASE(poll_sn) < TX_MOD_BASE(status.ack_sn))) { - logger.debug("%s Stopping pollRetx timer", RB_NAME); - poll_retx_timer.stop(); + log_rlc_am_status_pdu_to_string(logger.info, rb_name, "Rx Status PDU %s", &status); + + // make sure ACK_SN is within our Tx window + if (((MOD + status.ack_sn - vt_a) % MOD > RLC_AM_WINDOW_SIZE) || + ((MOD + vt_s - status.ack_sn) % MOD > RLC_AM_WINDOW_SIZE)) { + RlcWarning("Received invalid status PDU (ack_sn=%d, vt_a=%d, vt_s=%d). Dropping PDU.", status.ack_sn, vt_a, vt_s); + return; + } + + // Sec 5.2.2.2, stop poll reTx timer if status PDU comprises a positive _or_ negative acknowledgement + // for the RLC data PDU with sequence number poll_sn + if (poll_retx_timer.is_valid() && (TX_MOD_BASE(poll_sn) < TX_MOD_BASE(status.ack_sn))) { + RlcDebug("Stopping pollRetx timer"); + poll_retx_timer.stop(); + } + + // flush retx queue to avoid unordered SNs, we expect the Rx to request lost PDUs again + if (status.N_nack > 0) { + retx_queue.clear(); + } + + i = vt_a; + vt_s_local = vt_s; } - // flush retx queue to avoid unordered SNs, we expect the Rx to request lost PDUs again - if (status.N_nack > 0) { - retx_queue.clear(); - } - - // Handle ACKs and NACKs - bool update_vt_a = true; - uint32_t i = vt_a; - - while (TX_MOD_BASE(i) < TX_MOD_BASE(status.ack_sn) && TX_MOD_BASE(i) < TX_MOD_BASE(vt_s)) { + bool update_vt_a = true; + while (TX_MOD_BASE(i) < TX_MOD_BASE(status.ack_sn) && TX_MOD_BASE(i) < TX_MOD_BASE(vt_s_local)) { bool nack = false; for (uint32_t j = 0; j < status.N_nack; j++) { if (status.nacks[j].nack_sn == i) { nack = true; update_vt_a = false; + std::lock_guard lock(mutex); if (tx_window.has_sn(i)) { auto& pdu = tx_window[i]; + + // add to retx queue if it's not already there if (not retx_queue.has_sn(i)) { - rlc_amd_retx_t& retx = retx_queue.push(); + // increment Retx counter and inform upper layers if needed + pdu.retx_count++; + check_sn_reached_max_retx(i); + + rlc_amd_retx_lte_t& retx = retx_queue.push(); srsran_expect(tx_window[i].rlc_sn == i, "Incorrect RLC SN=%d!=%d being accessed", tx_window[i].rlc_sn, i); retx.sn = i; retx.is_segment = false; @@ -1213,8 +976,7 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no // sanity check if (status.nacks[j].so_start >= pdu.buf->N_bytes) { // print error but try to send original PDU again - logger.info( - "SO_start is larger than original PDU (%d >= %d)", status.nacks[j].so_start, pdu.buf->N_bytes); + RlcInfo("SO_start is larger than original PDU (%d >= %d)", status.nacks[j].so_start, pdu.buf->N_bytes); status.nacks[j].so_start = 0; } @@ -1229,28 +991,28 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no retx.is_segment = true; retx.so_start = status.nacks[j].so_start; } else { - logger.warning("%s invalid segment NACK received for SN %d. so_start: %d, so_end: %d, N_bytes: %d", - RB_NAME, - i, - status.nacks[j].so_start, - status.nacks[j].so_end, - pdu.buf->N_bytes); + RlcWarning("invalid segment NACK received for SN %d. so_start: %d, so_end: %d, N_bytes: %d", + i, + status.nacks[j].so_start, + status.nacks[j].so_end, + pdu.buf->N_bytes); } } } else { - logger.info("%s NACKed SN=%d already considered for retransmission", RB_NAME, i); + RlcInfo("NACKed SN=%d already considered for retransmission", i); } } else { - logger.warning("%s NACKed SN=%d already removed from Tx window", RB_NAME, i); + RlcError("NACKed SN=%d already removed from Tx window", i); } } } if (!nack) { // ACKed SNs get marked and removed from tx_window so PDCP get's only notified once + std::lock_guard lock(mutex); if (tx_window.has_sn(i)) { update_notification_ack_info(i); - logger.debug("Tx PDU SN=%zd being removed from tx window", i); + RlcDebug("Tx PDU SN=%zd being removed from tx window", i); tx_window.remove_pdu(i); } // Advance window if possible @@ -1262,15 +1024,17 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no i = (i + 1) % MOD; } - // Make sure vt_a points to valid SN - if (not tx_window.empty() && not tx_window.has_sn(vt_a)) { - logger.error("%s vt_a=%d points to invalid position in Tx window", RB_NAME, vt_a); + { + // Make sure vt_a points to valid SN + std::lock_guard lock(mutex); + if (not tx_window.empty() && not tx_window.has_sn(vt_a)) { + RlcError("vt_a=%d points to invalid position in Tx window.", vt_a); + parent->rrc->protocol_failure(); + } } debug_state(); - lock.unlock(); - // Notify PDCP without holding Tx mutex if (not notify_info_vec.empty()) { parent->pdcp->notify_delivery(parent->lcid, notify_info_vec); @@ -1283,12 +1047,12 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no * @tx_pdu: RLC PDU that was ack'ed. * @notify_info_vec: Vector which will keep track of the PDCP PDU SNs that have been fully ack'ed. */ -void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn) +void rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn) { - logger.debug("Updating ACK info: RLC SN=%d, number of notified SDU=%ld, number of undelivered SDUs=%ld", - rlc_sn, - notify_info_vec.size(), - undelivered_sdu_info_queue.nof_sdus()); + RlcDebug("Updating ACK info: RLC SN=%d, number of notified SDU=%ld, number of undelivered SDUs=%ld", + rlc_sn, + notify_info_vec.size(), + undelivered_sdu_info_queue.nof_sdus()); // Iterate over all undelivered SDUs if (not tx_window.has_sn(rlc_sn)) { return; @@ -1298,10 +1062,10 @@ void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn) for (rlc_am_pdu_segment& acked_segment : acked_pdu) { uint32_t pdcp_sn = acked_segment.pdcp_sn(); if (pdcp_sn == rlc_am_pdu_segment::invalid_pdcp_sn) { - logger.debug("ACKed segment in RLC_SN=%d already discarded in PDCP. No need to notify the PDCP.", rlc_sn); + RlcDebug("ACKed segment in RLC_SN=%d already discarded in PDCP. No need to notify the PDCP.", rlc_sn); continue; } - pdcp_pdu_info& info = undelivered_sdu_info_queue[pdcp_sn]; + pdcp_pdu_info_lte& info = undelivered_sdu_info_queue[pdcp_sn]; // Remove RLC SN from PDCP PDU undelivered list info.ack_segment(acked_segment); @@ -1312,31 +1076,31 @@ void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn) if (not notify_info_vec.full()) { notify_info_vec.push_back(pdcp_sn); } else { - logger.warning("Can't notify delivery of PDCP_SN=%d.", pdcp_sn); + RlcWarning("Can't notify delivery of PDCP_SN=%d.", pdcp_sn); } - logger.debug("Erasing SDU info: PDCP_SN=%d", pdcp_sn); + RlcDebug("Erasing SDU info: PDCP_SN=%d", pdcp_sn); undelivered_sdu_info_queue.clear_pdcp_sdu(pdcp_sn); } } } -void rlc_am_lte::rlc_am_lte_tx::debug_state() +void rlc_am_lte_tx::debug_state() { - logger.debug("%s vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d", RB_NAME, vt_a, vt_ms, vt_s, poll_sn); + RlcDebug("vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d", vt_a, vt_ms, vt_s, poll_sn); } -int rlc_am_lte::rlc_am_lte_tx::required_buffer_size(rlc_amd_retx_t retx) +int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_lte_t& retx) { if (!retx.is_segment) { if (tx_window.has_sn(retx.sn)) { if (tx_window[retx.sn].buf) { return rlc_am_packed_length(&tx_window[retx.sn].header) + tx_window[retx.sn].buf->N_bytes; } else { - logger.warning("retx.sn=%d has null ptr in required_buffer_size()", retx.sn); + RlcWarning("retx.sn=%d has null ptr in required_buffer_size()", retx.sn); return -1; } } else { - logger.warning("retx.sn=%d does not exist in required_buffer_size()", retx.sn); + RlcWarning("retx.sn=%d does not exist in required_buffer_size()", retx.sn); return -1; } } @@ -1401,24 +1165,24 @@ int rlc_am_lte::rlc_am_lte_tx::required_buffer_size(rlc_amd_retx_t retx) /**************************************************************************** * Rx subclass implementation ***************************************************************************/ - -rlc_am_lte::rlc_am_lte_rx::rlc_am_lte_rx(rlc_am_lte* parent_) : +rlc_am_lte_rx::rlc_am_lte_rx(rlc_am* parent_) : parent(parent_), pool(byte_buffer_pool::get_instance()), - logger(parent_->logger), - reordering_timer(parent_->timers->get_unique_timer()) -{} + reordering_timer(parent_->timers->get_unique_timer()), + rlc_am_base_rx(parent_, parent_->logger) +{ +} -rlc_am_lte::rlc_am_lte_rx::~rlc_am_lte_rx() {} - -bool rlc_am_lte::rlc_am_lte_rx::configure(rlc_am_config_t cfg_) +bool rlc_am_lte_rx::configure(const rlc_config_t& cfg_) { // TODO: add config checks - cfg = cfg_; + cfg = cfg_.am; + + rb_name = parent->rb_name; // check timers if (not reordering_timer.is_valid()) { - logger.error("Configuring RLC AM TX: timers not configured"); + RlcError("Configuring RLC AM TX: timers not configured"); return false; } @@ -1430,12 +1194,12 @@ bool rlc_am_lte::rlc_am_lte_rx::configure(rlc_am_config_t cfg_) return true; } -void rlc_am_lte::rlc_am_lte_rx::reestablish() +void rlc_am_lte_rx::reestablish() { stop(); } -void rlc_am_lte::rlc_am_lte_rx::stop() +void rlc_am_lte_rx::stop() { std::lock_guard lock(mutex); @@ -1461,18 +1225,41 @@ void rlc_am_lte::rlc_am_lte_rx::stop() rx_window.clear(); } +/** Called from stack thread when MAC has received a new RLC PDU + * + * @param payload Pointer to payload + * @param nof_bytes Payload length + */ +void rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) +{ + std::lock_guard lock(mutex); + + rlc_amd_pdu_header_t header = {}; + uint32_t payload_len = nof_bytes; + rlc_am_read_data_pdu_header(&payload, &payload_len, &header); + if (payload_len > nof_bytes) { + RlcInfo("Dropping corrupted PDU (%d B). Remaining length after header %d B.", nof_bytes, payload_len); + return; + } + if (header.rf != 0) { + handle_data_pdu_segment(payload, payload_len, header); + } else { + handle_data_pdu_full(payload, payload_len, header); + } +} + /** Called from stack thread when MAC has received a new RLC PDU * * @param payload Pointer to payload * @param nof_bytes Payload length * @param header Reference to PDU header (unpacked by caller) */ -void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header) +void rlc_am_lte_rx::handle_data_pdu_full(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header) { std::map::iterator it; - logger.info(payload, nof_bytes, "%s Rx data PDU SN=%d (%d B)", RB_NAME, header.sn, nof_bytes); - log_rlc_amd_pdu_header_to_string(logger.debug, header); + RlcHexInfo(payload, nof_bytes, "Rx data PDU SN=%d (%d B)", header.sn, nof_bytes); + log_rlc_amd_pdu_header_to_string(logger.debug, rb_name, "%s", header); // sanity check for segments not exceeding PDU length if (header.N_li > 0) { @@ -1480,7 +1267,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b for (uint32_t i = 0; i < header.N_li; i++) { segments_len += header.li[i]; if (segments_len > nof_bytes) { - logger.info("Dropping corrupted PDU (segments_len=%d > pdu_len=%d)", segments_len, nof_bytes); + RlcInfo("Dropping corrupted PDU (segments_len=%d > pdu_len=%d)", segments_len, nof_bytes); return; } } @@ -1488,19 +1275,19 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b if (!inside_rx_window(header.sn)) { if (header.p) { - logger.info("%s Status packet requested through polling bit", RB_NAME); + RlcInfo("Status packet requested through polling bit"); do_status = true; } - logger.info("%s SN=%d outside rx window [%d:%d] - discarding", RB_NAME, header.sn, vr_r, vr_mr); + RlcInfo("SN=%d outside rx window [%d:%d] - discarding", header.sn, vr_r, vr_mr); return; } if (rx_window.has_sn(header.sn)) { if (header.p) { - logger.info("%s Status packet requested through polling bit", RB_NAME); + RlcInfo("Status packet requested through polling bit"); do_status = true; } - logger.info("%s Discarding duplicate SN=%d", RB_NAME, header.sn); + RlcInfo("Discarding duplicate SN=%d", header.sn); return; } @@ -1512,7 +1299,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b srsran::console("Fatal Error: Couldn't allocate PDU in handle_data_pdu().\n"); exit(-1); #else - logger.error("Fatal Error: Couldn't allocate PDU in handle_data_pdu()."); + RlcError("Fatal Error: Couldn't allocate PDU in handle_data_pdu()."); rx_window.remove_pdu(header.sn); return; #endif @@ -1521,11 +1308,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b // check available space for payload if (nof_bytes > pdu.buf->get_tailroom()) { - logger.error("%s Discarding SN=%d of size %d B (available space %d B)", - RB_NAME, - header.sn, - nof_bytes, - pdu.buf->get_tailroom()); + RlcError("Discarding SN=%d of size %d B (available space %d B)", header.sn, nof_bytes, pdu.buf->get_tailroom()); return; } memcpy(pdu.buf->msg, payload, nof_bytes); @@ -1544,7 +1327,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b // Check poll bit if (header.p) { - logger.info("%s Status packet requested through polling bit", RB_NAME); + RlcInfo("Status packet requested through polling bit"); poll_received = true; // 36.322 v10 Section 5.2.3 @@ -1561,21 +1344,21 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b if (reordering_timer.is_valid()) { if (reordering_timer.is_running()) { if (vr_x == vr_r || (!inside_rx_window(vr_x) && vr_x != vr_mr)) { - logger.debug("Stopping reordering timer."); + RlcDebug("Stopping reordering timer."); reordering_timer.stop(); } else { - logger.debug("Leave reordering timer running."); + RlcDebug("Leave reordering timer running."); } debug_state(); } if (not reordering_timer.is_running()) { if (RX_MOD_BASE(vr_h) > RX_MOD_BASE(vr_r)) { - logger.debug("Starting reordering timer."); + RlcDebug("Starting reordering timer."); reordering_timer.run(); vr_x = vr_h; } else { - logger.debug("Leave reordering timer stopped."); + RlcDebug("Leave reordering timer stopped."); } debug_state(); } @@ -1584,29 +1367,26 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b debug_state(); } -void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* payload, - uint32_t nof_bytes, - rlc_amd_pdu_header_t& header) +void rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header) { std::map::iterator it; - logger.info(payload, - nof_bytes, - "%s Rx data PDU segment of SN=%d (%d B), SO=%d, N_li=%d", - RB_NAME, - header.sn, - nof_bytes, - header.so, - header.N_li); - log_rlc_amd_pdu_header_to_string(logger.debug, header); + RlcHexInfo(payload, + nof_bytes, + "Rx data PDU segment of SN=%d (%d B), SO=%d, N_li=%d", + header.sn, + nof_bytes, + header.so, + header.N_li); + log_rlc_amd_pdu_header_to_string(logger.debug, rb_name, "Rx data PDU segment %s", header); // Check inside rx window if (!inside_rx_window(header.sn)) { if (header.p) { - logger.info("%s Status packet requested through polling bit", RB_NAME); + logger.info("Status packet requested through polling bit"); do_status = true; } - logger.info("%s SN=%d outside rx window [%d:%d] - discarding", RB_NAME, header.sn, vr_r, vr_mr); + logger.info("SN=%d outside rx window [%d:%d] - discarding", header.sn, vr_r, vr_mr); return; } @@ -1623,7 +1403,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* pa } if (segment.buf->get_tailroom() < nof_bytes) { - logger.info("Dropping corrupted segment SN=%d, not enough space to fit %d B", header.sn, nof_bytes); + RlcInfo("Dropping corrupted segment SN=%d, not enough space to fit %d B", header.sn, nof_bytes); return; } @@ -1635,7 +1415,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* pa it = rx_segments.find(header.sn); if (rx_segments.end() != it) { if (header.p) { - logger.info("%s Status packet requested through polling bit", RB_NAME); + RlcInfo("Status packet requested through polling bit"); do_status = true; } @@ -1658,7 +1438,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* pa // Check poll bit if (header.p) { - logger.info("%s Status packet requested through polling bit", RB_NAME); + RlcInfo("Status packet requested through polling bit"); poll_received = true; // 36.322 v10 Section 5.2.3 @@ -1674,7 +1454,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* pa debug_state(); } -void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() +void rlc_am_lte_rx::reassemble_rx_sdus() { uint32_t len = 0; if (rx_sdu == NULL) { @@ -1684,7 +1464,7 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() srsran::console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (1)\n"); exit(-1); #else - logger.error("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (1)"); + RlcError("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (1)"); return; #endif } @@ -1696,13 +1476,13 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() for (uint32_t i = 0; i < rx_window[vr_r].header.N_li; i++) { len = rx_window[vr_r].header.li[i]; - logger.debug(rx_window[vr_r].buf->msg, - len, - "Handling segment %d/%d of length %d B of SN=%d", - i + 1, - rx_window[vr_r].header.N_li, - len, - vr_r); + RlcHexDebug(rx_window[vr_r].buf->msg, + len, + "Handling segment %d/%d of length %d B of SN=%d", + i + 1, + rx_window[vr_r].header.N_li, + len, + vr_r); // sanity check to avoid zero-size SDUs if (len == 0) { @@ -1712,7 +1492,7 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() if (rx_sdu->get_tailroom() >= len) { if ((rx_window[vr_r].buf->msg - rx_window[vr_r].buf->buffer) + len < SRSRAN_MAX_BUFFER_SIZE_BYTES) { if (rx_window[vr_r].buf->N_bytes < len) { - logger.error("Dropping corrupted SN=%d", vr_r); + RlcError("Dropping corrupted SN=%d", vr_r); rx_sdu.reset(); goto exit; } @@ -1726,12 +1506,15 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() rx_window[vr_r].buf->msg += len; rx_window[vr_r].buf->N_bytes -= len; - logger.info(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU (%d B)", RB_NAME, rx_sdu->N_bytes); + RlcHexInfo(rx_sdu->msg, rx_sdu->N_bytes, "Rx SDU (%d B)", rx_sdu->N_bytes); sdu_rx_latency_ms.push(std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - rx_sdu->get_timestamp()) .count()); parent->pdcp->write_pdu(parent->lcid, std::move(rx_sdu)); - parent->metrics.num_rx_sdus++; + { + std::lock_guard lock(parent->metrics_mutex); + parent->metrics.num_rx_sdus++; + } rx_sdu = srsran::make_byte_buffer(); if (rx_sdu == nullptr) { @@ -1739,18 +1522,18 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() srsran::console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (2)\n"); exit(-1); #else - logger.error("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (2)"); + RlcError("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (2)"); return; #endif } } else { int buf_len = rx_window[vr_r].buf->msg - rx_window[vr_r].buf->buffer; - logger.error("Cannot read %d bytes from rx_window. vr_r=%d, msg-buffer=%d B", len, vr_r, buf_len); + RlcError("Cannot read %d bytes from rx_window. vr_r=%d, msg-buffer=%d B", len, vr_r, buf_len); rx_sdu.reset(); goto exit; } } else { - logger.error("Cannot fit RLC PDU in SDU buffer, dropping both."); + RlcError("Cannot fit RLC PDU in SDU buffer, dropping both."); rx_sdu.reset(); goto exit; } @@ -1758,7 +1541,7 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() // Handle last segment len = rx_window[vr_r].buf->N_bytes; - logger.debug(rx_window[vr_r].buf->msg, len, "Handling last segment of length %d B of SN=%d", len, vr_r); + RlcHexDebug(rx_window[vr_r].buf->msg, len, "Handling last segment of length %d B of SN=%d", len, vr_r); if (rx_sdu->get_tailroom() >= len) { // store timestamp of the first segment when starting to assemble SDUs if (rx_sdu->N_bytes == 0) { @@ -1776,12 +1559,15 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() } if (rlc_am_end_aligned(rx_window[vr_r].header.fi)) { - logger.info(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU (%d B)", RB_NAME, rx_sdu->N_bytes); + RlcHexInfo(rx_sdu->msg, rx_sdu->N_bytes, "Rx SDU (%d B)", rx_sdu->N_bytes); sdu_rx_latency_ms.push(std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - rx_sdu->get_timestamp()) .count()); parent->pdcp->write_pdu(parent->lcid, std::move(rx_sdu)); - parent->metrics.num_rx_sdus++; + { + std::lock_guard lock(parent->metrics_mutex); + parent->metrics.num_rx_sdus++; + } rx_sdu = srsran::make_byte_buffer(); if (rx_sdu == NULL) { @@ -1789,7 +1575,7 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() srsran::console("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (3)\n"); exit(-1); #else - logger.error("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (3)"); + RlcError("Fatal Error: Could not allocate PDU in reassemble_rx_sdus() (3)"); return; #endif } @@ -1797,19 +1583,19 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() exit: // Move the rx_window - logger.debug("Erasing SN=%d.", vr_r); + RlcDebug("Erasing SN=%d.", vr_r); // also erase any segments of this SN std::map::iterator it; it = rx_segments.find(vr_r); if (rx_segments.end() != it) { - logger.debug("Erasing segments of SN=%d", vr_r); + RlcDebug("Erasing segments of SN=%d", vr_r); std::list::iterator segit; for (segit = it->second.segments.begin(); segit != it->second.segments.end(); ++segit) { - logger.debug(" Erasing segment of SN=%d SO=%d Len=%d N_li=%d", - segit->header.sn, - segit->header.so, - segit->buf->N_bytes, - segit->header.N_li); + RlcDebug(" Erasing segment of SN=%d SO=%d Len=%d N_li=%d", + segit->header.sn, + segit->header.so, + segit->buf->N_bytes, + segit->header.N_li); } it->second.segments.clear(); } @@ -1819,50 +1605,24 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus() } } -void rlc_am_lte::rlc_am_lte_rx::reset_status() +void rlc_am_lte_rx::reset_status() { - std::lock_guard lock(mutex); do_status = false; poll_received = false; } -bool rlc_am_lte::rlc_am_lte_rx::get_do_status() +bool rlc_am_lte_rx::get_do_status() { - return do_status; + return do_status.load(std::memory_order_relaxed); } -void rlc_am_lte::rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes) -{ - if (nof_bytes < 1) { - return; - } - - if (rlc_am_is_control_pdu(payload)) { - parent->tx.handle_control_pdu(payload, nof_bytes); - } else { - std::lock_guard lock(mutex); - rlc_amd_pdu_header_t header = {}; - uint32_t payload_len = nof_bytes; - rlc_am_read_data_pdu_header(&payload, &payload_len, &header); - if (payload_len > nof_bytes) { - logger.info("Dropping corrupted PDU (%d B). Remaining length after header %d B.", nof_bytes, payload_len); - return; - } - if (header.rf) { - handle_data_pdu_segment(payload, payload_len, header); - } else { - handle_data_pdu(payload, payload_len, header); - } - } -} - -uint32_t rlc_am_lte::rlc_am_lte_rx::get_rx_buffered_bytes() +uint32_t rlc_am_lte_rx::get_rx_buffered_bytes() { std::lock_guard lock(mutex); return rx_window.get_buffered_bytes(); } -uint32_t rlc_am_lte::rlc_am_lte_rx::get_sdu_rx_latency_ms() +uint32_t rlc_am_lte_rx::get_sdu_rx_latency_ms() { std::lock_guard lock(mutex); return sdu_rx_latency_ms.value(); @@ -1873,11 +1633,11 @@ uint32_t rlc_am_lte::rlc_am_lte_rx::get_sdu_rx_latency_ms() * * @param timeout_id */ -void rlc_am_lte::rlc_am_lte_rx::timer_expired(uint32_t timeout_id) +void rlc_am_lte_rx::timer_expired(uint32_t timeout_id) { std::lock_guard lock(mutex); if (reordering_timer.is_valid() and reordering_timer.id() == timeout_id) { - logger.debug("%s reordering timeout expiry - updating vr_ms (was %d)", RB_NAME, vr_ms); + RlcDebug("reordering timeout expiry - updating vr_ms (was %d)", vr_ms); // 36.322 v10 Section 5.1.3.2.4 vr_ms = vr_x; @@ -1899,9 +1659,14 @@ void rlc_am_lte::rlc_am_lte_rx::timer_expired(uint32_t timeout_id) } // Called from Tx object to pack status PDU that doesn't exceed a given size -int rlc_am_lte::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_pdu_size) +// If lock-acquisition fails, return -1. Otherwise it returns the length of the generated PDU. +int rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_pdu_size) { - std::lock_guard lock(mutex); + std::unique_lock lock(mutex, std::try_to_lock); + if (not lock.owns_lock()) { + return SRSRAN_ERROR; + } + status->N_nack = 0; status->ack_sn = vr_r; // start with lower edge of the rx window @@ -1918,21 +1683,22 @@ int rlc_am_lte::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const ui // make sure we don't exceed grant size if (rlc_am_packed_length(status) > max_pdu_size) { - logger.debug("Status PDU too big (%d > %d)", rlc_am_packed_length(status), max_pdu_size); + RlcDebug("Status PDU too big (%d > %d)", rlc_am_packed_length(status), max_pdu_size); if (status->N_nack >= 1 && status->N_nack < RLC_AM_WINDOW_SIZE) { - logger.debug("Removing last NACK SN=%d", status->nacks[status->N_nack].nack_sn); + RlcDebug("Removing last NACK SN=%d", status->nacks[status->N_nack].nack_sn); status->N_nack--; // make sure we don't have the current ACK_SN in the NACK list - if (rlc_am_is_valid_status_pdu(*status) == false) { - // No space to send any NACKs - logger.debug("Resetting N_nack to zero"); + if (rlc_am_is_valid_status_pdu(*status, vr_r) == false) { + // No space to send any NACKs, play safe and just ack lower edge + RlcWarning("Resetting ACK_SN and N_nack to initial state"); + status->ack_sn = vr_r; status->N_nack = 0; } } else { - logger.warning("Failed to generate small enough status PDU (packed_len=%d, max_pdu_size=%d, status->N_nack=%d)", - rlc_am_packed_length(status), - max_pdu_size, - status->N_nack); + RlcWarning("Failed to generate small enough status PDU (packed_len=%d, max_pdu_size=%d, status->N_nack=%d)", + rlc_am_packed_length(status), + max_pdu_size, + status->N_nack); return 0; } break; @@ -1940,16 +1706,22 @@ int rlc_am_lte::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const ui i = (i + 1) % MOD; } + // valid PDU could be generated + reset_status(); + return rlc_am_packed_length(status); } // Called from Tx object to obtain length of the full status PDU -int rlc_am_lte::rlc_am_lte_rx::get_status_pdu_length() +int rlc_am_lte_rx::get_status_pdu_length() { - std::lock_guard lock(mutex); - rlc_status_pdu_t status = {}; - status.ack_sn = vr_ms; - uint32_t i = vr_r; + std::unique_lock lock(mutex, std::try_to_lock); + if (not lock.owns_lock()) { + return 0; + } + rlc_status_pdu_t status = {}; + status.ack_sn = vr_ms; + uint32_t i = vr_r; while (RX_MOD_BASE(i) < RX_MOD_BASE(vr_ms) && status.N_nack < RLC_AM_WINDOW_SIZE) { if (not rx_window.has_sn(i)) { status.N_nack++; @@ -1959,7 +1731,7 @@ int rlc_am_lte::rlc_am_lte_rx::get_status_pdu_length() return rlc_am_packed_length(&status); } -void rlc_am_lte::rlc_am_lte_rx::print_rx_segments() +void rlc_am_lte_rx::print_rx_segments() { std::map::iterator it; std::stringstream ss; @@ -1971,11 +1743,11 @@ void rlc_am_lte::rlc_am_lte_rx::print_rx_segments() << " N_li: " << segit->header.N_li << std::endl; } } - logger.debug("%s", ss.str().c_str()); + RlcDebug("%s", ss.str().c_str()); } // NOTE: Preference would be to capture by value, and then move; but header is stack allocated -bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment) +bool rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment) { // Find segment insertion point in the list of segments auto it1 = pdu->segments.begin(); @@ -2045,7 +1817,7 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* header.fi |= (pdu->segments.front().header.fi & RLC_FI_FIELD_NOT_START_ALIGNED); header.fi |= (pdu->segments.back().header.fi & RLC_FI_FIELD_NOT_END_ALIGNED); - logger.debug("Starting header reconstruction of %zd segments", pdu->segments.size()); + RlcDebug("Starting header reconstruction of %zd segments", pdu->segments.size()); // Reconstruct li fields uint16_t count = 0; @@ -2053,7 +1825,7 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* uint16_t consumed_bytes = 0; // rolling sum of all allocated LIs during segment reconstruction for (it = pdu->segments.begin(); it != pdu->segments.end(); ++it) { - logger.debug(" Handling %d PDU segments", it->header.N_li); + RlcDebug(" Handling %d PDU segments", it->header.N_li); for (uint32_t i = 0; i < it->header.N_li; i++) { // variable marks total offset of each _processed_ LI of this segment uint32_t total_pdu_offset = it->header.so; @@ -2061,28 +1833,28 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* total_pdu_offset += it->header.li[k]; } - logger.debug(" - (total_pdu_offset=%d, consumed_bytes=%d, header.li[i]=%d)", - total_pdu_offset, - consumed_bytes, - header.li[i]); + RlcDebug(" - (total_pdu_offset=%d, consumed_bytes=%d, header.li[i]=%d)", + total_pdu_offset, + consumed_bytes, + header.li[i]); if (total_pdu_offset > header.li[i] && total_pdu_offset > consumed_bytes) { header.li[header.N_li] = total_pdu_offset - consumed_bytes; consumed_bytes = total_pdu_offset; - logger.debug(" - adding segment %d/%d (%d B, SO=%d, carryover=%d, count=%d)", - i + 1, - it->header.N_li, - header.li[header.N_li], - header.so, - carryover, - count); + RlcDebug(" - adding segment %d/%d (%d B, SO=%d, carryover=%d, count=%d)", + i + 1, + it->header.N_li, + header.li[header.N_li], + header.so, + carryover, + count); header.N_li++; count += it->header.li[i]; carryover = 0; } else { - logger.debug(" - Skipping segment in reTx PDU segment which is already included (%d B, SO=%d)", - it->header.li[i], - header.so); + RlcDebug(" - Skipping segment in reTx PDU segment which is already included (%d B, SO=%d)", + it->header.li[i], + header.so); } } @@ -2092,24 +1864,24 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* for (uint32_t k = 0; k < header.N_li; ++k) { carryover -= header.li[k]; } - logger.debug("Incremented carryover (it->buf->N_bytes=%d, count=%d). New carryover=%d", - it->buf->N_bytes, - count, - carryover); + RlcDebug("Incremented carryover (it->buf->N_bytes=%d, count=%d). New carryover=%d", + it->buf->N_bytes, + count, + carryover); } else { // Next segment would be too long, recalculate carryover header.N_li--; carryover = it->buf->N_bytes - (count - header.li[header.N_li]); - logger.debug("Recalculated carryover=%d (it->buf->N_bytes=%d, count=%d, header.li[header.N_li]=%d)", - carryover, - it->buf->N_bytes, - count, - header.li[header.N_li]); + RlcDebug("Recalculated carryover=%d (it->buf->N_bytes=%d, count=%d, header.li[header.N_li]=%d)", + carryover, + it->buf->N_bytes, + count, + header.li[header.N_li]); } tmpit = it; if (rlc_am_end_aligned(it->header.fi) && ++tmpit != pdu->segments.end()) { - logger.debug("Header is end-aligned, overwrite header.li[%d]=%d", header.N_li, carryover); + RlcDebug("Header is end-aligned, overwrite header.li[%d]=%d", header.N_li, carryover); header.li[header.N_li] = carryover; header.N_li++; consumed_bytes += carryover; @@ -2121,7 +1893,7 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* header.p |= it->header.p; } - logger.debug("Finished header reconstruction of %zd segments", pdu->segments.size()); + RlcDebug("Finished header reconstruction of %zd segments", pdu->segments.size()); // Copy data unique_byte_buffer_t full_pdu = srsran::make_byte_buffer(); @@ -2130,7 +1902,7 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* srsran::console("Fatal Error: Could not allocate PDU in add_segment_and_check()\n"); exit(-1); #else - logger.error("Fatal Error: Could not allocate PDU in add_segment_and_check()"); + RlcError("Fatal Error: Could not allocate PDU in add_segment_and_check()"); return false; #endif } @@ -2151,11 +1923,11 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* full_pdu->N_bytes += n; } - handle_data_pdu(full_pdu->msg, full_pdu->N_bytes, header); + handle_data_pdu_full(full_pdu->msg, full_pdu->N_bytes, header); return true; } -bool rlc_am_lte::rlc_am_lte_rx::inside_rx_window(const int16_t sn) +bool rlc_am_lte_rx::inside_rx_window(const int16_t sn) { if (RX_MOD_BASE(sn) >= RX_MOD_BASE(static_cast(vr_r)) && RX_MOD_BASE(sn) < RX_MOD_BASE(vr_mr)) { return true; @@ -2164,338 +1936,9 @@ bool rlc_am_lte::rlc_am_lte_rx::inside_rx_window(const int16_t sn) } } -void rlc_am_lte::rlc_am_lte_rx::debug_state() +void rlc_am_lte_rx::debug_state() { - logger.debug("%s vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d", RB_NAME, vr_r, vr_mr, vr_x, vr_ms, vr_h); -} - -buffered_pdcp_pdu_list::buffered_pdcp_pdu_list() : buffered_pdus(buffered_pdcp_pdu_list::buffer_size) -{ - clear(); -} - -void buffered_pdcp_pdu_list::clear() -{ - count = 0; - for (pdcp_pdu_info& b : buffered_pdus) { - b.clear(); - } -} - -/**************************************************************************** - * Header pack/unpack helper functions - * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 - ***************************************************************************/ - -// Read header from pdu struct, don't strip header -void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header) -{ - uint8_t* ptr = pdu->msg; - uint32_t n = 0; - rlc_am_read_data_pdu_header(&ptr, &n, header); -} - -// Read header from raw pointer, strip header -void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header) -{ - uint8_t ext; - uint8_t* ptr = *payload; - - header->dc = static_cast((*ptr >> 7) & 0x01); - - if (RLC_DC_FIELD_DATA_PDU == header->dc) { - // Fixed part - header->rf = ((*ptr >> 6) & 0x01); - header->p = ((*ptr >> 5) & 0x01); - header->fi = static_cast((*ptr >> 3) & 0x03); - ext = ((*ptr >> 2) & 0x01); - header->sn = (*ptr & 0x03) << 8; // 2 bits SN - ptr++; - header->sn |= (*ptr & 0xFF); // 8 bits SN - ptr++; - - if (header->rf) { - header->lsf = ((*ptr >> 7) & 0x01); - header->so = (*ptr & 0x7F) << 8; // 7 bits of SO - ptr++; - header->so |= (*ptr & 0xFF); // 8 bits of SO - ptr++; - } - - // Extension part - header->N_li = 0; - while (ext) { - if (header->N_li % 2 == 0) { - ext = ((*ptr >> 7) & 0x01); - header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI - ptr++; - header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI - header->N_li++; - } else { - ext = (*ptr >> 3) & 0x01; - header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI - ptr++; - header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI - header->N_li++; - ptr++; - } - } - - // Account for padding if N_li is odd - if (header->N_li % 2 == 1) { - ptr++; - } - - *nof_bytes -= ptr - *payload; - *payload = ptr; - } -} - -// Write header to pdu struct -void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu) -{ - uint8_t* ptr = pdu->msg; - rlc_am_write_data_pdu_header(header, &ptr); - pdu->N_bytes += ptr - pdu->msg; -} - -// Write header to pointer & move pointer -void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload) -{ - uint32_t i; - uint8_t ext = (header->N_li > 0) ? 1 : 0; - - uint8_t* ptr = *payload; - - // Fixed part - *ptr = (header->dc & 0x01) << 7; - *ptr |= (header->rf & 0x01) << 6; - *ptr |= (header->p & 0x01) << 5; - *ptr |= (header->fi & 0x03) << 3; - *ptr |= (ext & 0x01) << 2; - - *ptr |= (header->sn & 0x300) >> 8; // 2 bits SN - ptr++; - *ptr = (header->sn & 0xFF); // 8 bits SN - ptr++; - - // Segment part - if (header->rf) { - *ptr = (header->lsf & 0x01) << 7; - *ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO - ptr++; - *ptr = (header->so & 0x00FF); // 8 bits of SO - ptr++; - } - - // Extension part - i = 0; - while (i < header->N_li) { - ext = ((i + 1) == header->N_li) ? 0 : 1; - *ptr = (ext & 0x01) << 7; // 1 bit header - *ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI - ptr++; - *ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI - i++; - if (i < header->N_li) { - ext = ((i + 1) == header->N_li) ? 0 : 1; - *ptr |= (ext & 0x01) << 3; // 1 bit header - *ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI - ptr++; - *ptr = (header->li[i] & 0x0FF); // 8 bits of LI - ptr++; - i++; - } - } - // Pad if N_li is odd - if (header->N_li % 2 == 1) { - ptr++; - } - - *payload = ptr; -} - -void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status) -{ - rlc_am_read_status_pdu(pdu->msg, pdu->N_bytes, status); -} - -void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status) -{ - uint32_t i; - uint8_t ext1, ext2; - bit_buffer_t tmp; - uint8_t* ptr = tmp.msg; - - srsran_bit_unpack_vector(payload, tmp.msg, nof_bytes * 8); - tmp.N_bits = nof_bytes * 8; - - rlc_dc_field_t dc = static_cast(srsran_bit_pack(&ptr, 1)); - - if (RLC_DC_FIELD_CONTROL_PDU == dc) { - uint8_t cpt = srsran_bit_pack(&ptr, 3); // 3-bit Control PDU Type (0 == status) - if (0 == cpt) { - status->ack_sn = srsran_bit_pack(&ptr, 10); // 10 bits ACK_SN - ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1 - status->N_nack = 0; - while (ext1) { - status->nacks[status->N_nack].nack_sn = srsran_bit_pack(&ptr, 10); - ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1 - ext2 = srsran_bit_pack(&ptr, 1); // 1 bits E2 - if (ext2) { - status->nacks[status->N_nack].has_so = true; - status->nacks[status->N_nack].so_start = srsran_bit_pack(&ptr, 15); - status->nacks[status->N_nack].so_end = srsran_bit_pack(&ptr, 15); - } - status->N_nack++; - } - } - } -} - -void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu) -{ - pdu->N_bytes = rlc_am_write_status_pdu(status, pdu->msg); -} - -int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload) -{ - uint32_t i; - uint8_t ext1; - bit_buffer_t tmp; - uint8_t* ptr = tmp.msg; - - srsran_bit_unpack(RLC_DC_FIELD_CONTROL_PDU, &ptr, 1); // D/C - srsran_bit_unpack(0, &ptr, 3); // CPT (0 == STATUS) - srsran_bit_unpack(status->ack_sn, &ptr, 10); // 10 bit ACK_SN - ext1 = (status->N_nack == 0) ? 0 : 1; - srsran_bit_unpack(ext1, &ptr, 1); // E1 - for (i = 0; i < status->N_nack; i++) { - srsran_bit_unpack(status->nacks[i].nack_sn, &ptr, 10); // 10 bit NACK_SN - ext1 = ((status->N_nack - 1) == i) ? 0 : 1; - srsran_bit_unpack(ext1, &ptr, 1); // E1 - if (status->nacks[i].has_so) { - srsran_bit_unpack(1, &ptr, 1); // E2 - srsran_bit_unpack(status->nacks[i].so_start, &ptr, 15); - srsran_bit_unpack(status->nacks[i].so_end, &ptr, 15); - } else { - srsran_bit_unpack(0, &ptr, 1); // E2 - } - } - - // Pad - tmp.N_bits = ptr - tmp.msg; - uint8_t n_pad = 8 - (tmp.N_bits % 8); - srsran_bit_unpack(0, &ptr, n_pad); - tmp.N_bits = ptr - tmp.msg; - - // Pack bits - srsran_bit_pack_vector(tmp.msg, payload, tmp.N_bits); - return tmp.N_bits / 8; -} - -bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status) -{ - for (uint32_t i = 0; i < status.N_nack; ++i) { - // NACK can't be larger than ACK - if ((MOD + status.ack_sn - status.nacks[i].nack_sn) % MOD > RLC_AM_WINDOW_SIZE) { - return false; - } - // Don't NACK the ACK SN - if (status.nacks[i].nack_sn == status.ack_sn) { - return false; - } - } - return true; -} - -uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header) -{ - uint32_t len = 2; // Fixed part is 2 bytes - if (header->rf) { - len += 2; // Segment header is 2 bytes - } - len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up - return len; -} - -uint32_t rlc_am_packed_length(rlc_status_pdu_t* status) -{ - uint32_t len_bits = 15; // Fixed part is 15 bits - for (uint32_t i = 0; i < status->N_nack; i++) { - if (status->nacks[i].has_so) { - len_bits += 42; // 10 bits SN, 2 bits ext, 15 bits so_start, 15 bits so_end - } else { - len_bits += 12; // 10 bits SN, 2 bits ext - } - } - - return (len_bits + 7) / 8; // Convert to bytes - integer rounding up -} - -bool rlc_am_is_pdu_segment(uint8_t* payload) -{ - return ((*(payload) >> 6) & 0x01) == 1; -} - -void rlc_am_undelivered_sdu_info_to_string(fmt::memory_buffer& buffer, const std::vector& info_queue) -{ - fmt::format_to(buffer, "\n"); - for (const auto& pdcp_pdu : info_queue) { - fmt::format_to(buffer, "\tPDCP_SN = {}, undelivered RLC SNs = [", pdcp_pdu.sn); - for (const auto& nacked_segment : pdcp_pdu) { - fmt::format_to(buffer, "{} ", nacked_segment.rlc_sn()); - } - fmt::format_to(buffer, "]\n"); - } -} - -void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header) -{ - if (not log_ch.enabled()) { - return; - } - fmt::memory_buffer buffer; - fmt::format_to(buffer, - "[{}, RF={}, P={}, FI={}, SN={}, LSF={}, SO={}, N_li={}", - rlc_dc_field_text[header.dc], - (header.rf ? "1" : "0"), - (header.p ? "1" : "0"), - (header.fi ? "1" : "0"), - header.sn, - (header.lsf ? "1" : "0"), - header.so, - header.N_li); - if (header.N_li > 0) { - fmt::format_to(buffer, " ({}", header.li[0]); - for (uint32_t i = 1; i < header.N_li; ++i) { - fmt::format_to(buffer, ", {}", header.li[i]); - } - fmt::format_to(buffer, ")"); - } - fmt::format_to(buffer, "]"); - - log_ch("%s", to_c_str(buffer)); -} - -bool rlc_am_start_aligned(const uint8_t fi) -{ - return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); -} - -bool rlc_am_end_aligned(const uint8_t fi) -{ - return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED); -} - -bool rlc_am_is_unaligned(const uint8_t fi) -{ - return (fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); -} - -bool rlc_am_not_start_aligned(const uint8_t fi) -{ - return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); + RlcDebug("vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d", vr_r, vr_mr, vr_x, vr_ms, vr_h); } } // namespace srsran diff --git a/lib/src/rlc/rlc_am_lte_packing.cc b/lib/src/rlc/rlc_am_lte_packing.cc new file mode 100644 index 000000000..d5bc09ea8 --- /dev/null +++ b/lib/src/rlc/rlc_am_lte_packing.cc @@ -0,0 +1,321 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/rlc/rlc_am_lte_packing.h" +#include + +namespace srsran { + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1 + ***************************************************************************/ + +// Read header from pdu struct, don't strip header +void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header) +{ + uint8_t* ptr = pdu->msg; + uint32_t n = 0; + rlc_am_read_data_pdu_header(&ptr, &n, header); +} + +// Read header from raw pointer, strip header +void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header) +{ + uint8_t ext; + uint8_t* ptr = *payload; + + header->dc = static_cast((*ptr >> 7) & 0x01); + + if (RLC_DC_FIELD_DATA_PDU == header->dc) { + // Fixed part + header->rf = ((*ptr >> 6) & 0x01); + header->p = ((*ptr >> 5) & 0x01); + header->fi = static_cast((*ptr >> 3) & 0x03); + ext = ((*ptr >> 2) & 0x01); + header->sn = (*ptr & 0x03) << 8; // 2 bits SN + ptr++; + header->sn |= (*ptr & 0xFF); // 8 bits SN + ptr++; + + if (header->rf) { + header->lsf = ((*ptr >> 7) & 0x01); + header->so = (*ptr & 0x7F) << 8; // 7 bits of SO + ptr++; + header->so |= (*ptr & 0xFF); // 8 bits of SO + ptr++; + } + + // Extension part + header->N_li = 0; + while (ext) { + if (header->N_li % 2 == 0) { + ext = ((*ptr >> 7) & 0x01); + header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI + ptr++; + header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI + header->N_li++; + } else { + ext = (*ptr >> 3) & 0x01; + header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI + ptr++; + header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI + header->N_li++; + ptr++; + } + } + + // Account for padding if N_li is odd + if (header->N_li % 2 == 1) { + ptr++; + } + + *nof_bytes -= ptr - *payload; + *payload = ptr; + } +} + +// Write header to pdu struct +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu) +{ + uint8_t* ptr = pdu->msg; + rlc_am_write_data_pdu_header(header, &ptr); + pdu->N_bytes += ptr - pdu->msg; +} + +// Write header to pointer & move pointer +void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload) +{ + uint32_t i; + uint8_t ext = (header->N_li > 0) ? 1 : 0; + + uint8_t* ptr = *payload; + + // Fixed part + *ptr = (header->dc & 0x01) << 7; + *ptr |= (header->rf & 0x01) << 6; + *ptr |= (header->p & 0x01) << 5; + *ptr |= (header->fi & 0x03) << 3; + *ptr |= (ext & 0x01) << 2; + + *ptr |= (header->sn & 0x300) >> 8; // 2 bits SN + ptr++; + *ptr = (header->sn & 0xFF); // 8 bits SN + ptr++; + + // Segment part + if (header->rf) { + *ptr = (header->lsf & 0x01) << 7; + *ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO + ptr++; + *ptr = (header->so & 0x00FF); // 8 bits of SO + ptr++; + } + + // Extension part + i = 0; + while (i < header->N_li) { + ext = ((i + 1) == header->N_li) ? 0 : 1; + *ptr = (ext & 0x01) << 7; // 1 bit header + *ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI + ptr++; + *ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI + i++; + if (i < header->N_li) { + ext = ((i + 1) == header->N_li) ? 0 : 1; + *ptr |= (ext & 0x01) << 3; // 1 bit header + *ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI + ptr++; + *ptr = (header->li[i] & 0x0FF); // 8 bits of LI + ptr++; + i++; + } + } + // Pad if N_li is odd + if (header->N_li % 2 == 1) { + ptr++; + } + + *payload = ptr; +} + +void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status) +{ + rlc_am_read_status_pdu(pdu->msg, pdu->N_bytes, status); +} + +void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status) +{ + uint32_t i; + uint8_t ext1, ext2; + bit_buffer_t tmp; + uint8_t* ptr = tmp.msg; + + srsran_bit_unpack_vector(payload, tmp.msg, nof_bytes * 8); + tmp.N_bits = nof_bytes * 8; + + rlc_dc_field_t dc = static_cast(srsran_bit_pack(&ptr, 1)); + + if (RLC_DC_FIELD_CONTROL_PDU == dc) { + uint8_t cpt = srsran_bit_pack(&ptr, 3); // 3-bit Control PDU Type (0 == status) + if (0 == cpt) { + status->ack_sn = srsran_bit_pack(&ptr, 10); // 10 bits ACK_SN + ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1 + status->N_nack = 0; + while (ext1) { + status->nacks[status->N_nack].nack_sn = srsran_bit_pack(&ptr, 10); + ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1 + ext2 = srsran_bit_pack(&ptr, 1); // 1 bits E2 + if (ext2) { + status->nacks[status->N_nack].has_so = true; + status->nacks[status->N_nack].so_start = srsran_bit_pack(&ptr, 15); + status->nacks[status->N_nack].so_end = srsran_bit_pack(&ptr, 15); + } + status->N_nack++; + } + } + } +} + +void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu) +{ + pdu->N_bytes = rlc_am_write_status_pdu(status, pdu->msg); +} + +int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload) +{ + uint32_t i; + uint8_t ext1; + bit_buffer_t tmp; + uint8_t* ptr = tmp.msg; + + srsran_bit_unpack(RLC_DC_FIELD_CONTROL_PDU, &ptr, 1); // D/C + srsran_bit_unpack(0, &ptr, 3); // CPT (0 == STATUS) + srsran_bit_unpack(status->ack_sn, &ptr, 10); // 10 bit ACK_SN + ext1 = (status->N_nack == 0) ? 0 : 1; + srsran_bit_unpack(ext1, &ptr, 1); // E1 + for (i = 0; i < status->N_nack; i++) { + srsran_bit_unpack(status->nacks[i].nack_sn, &ptr, 10); // 10 bit NACK_SN + ext1 = ((status->N_nack - 1) == i) ? 0 : 1; + srsran_bit_unpack(ext1, &ptr, 1); // E1 + if (status->nacks[i].has_so) { + srsran_bit_unpack(1, &ptr, 1); // E2 + srsran_bit_unpack(status->nacks[i].so_start, &ptr, 15); + srsran_bit_unpack(status->nacks[i].so_end, &ptr, 15); + } else { + srsran_bit_unpack(0, &ptr, 1); // E2 + } + } + + // Pad + tmp.N_bits = ptr - tmp.msg; + uint8_t n_pad = 8 - (tmp.N_bits % 8); + srsran_bit_unpack(0, &ptr, n_pad); + tmp.N_bits = ptr - tmp.msg; + + // Pack bits + srsran_bit_pack_vector(tmp.msg, payload, tmp.N_bits); + return tmp.N_bits / 8; +} + +uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header) +{ + uint32_t len = 2; // Fixed part is 2 bytes + if (header->rf) { + len += 2; // Segment header is 2 bytes + } + len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up + return len; +} + +uint32_t rlc_am_packed_length(rlc_status_pdu_t* status) +{ + uint32_t len_bits = 15; // Fixed part is 15 bits + for (uint32_t i = 0; i < status->N_nack; i++) { + if (status->nacks[i].has_so) { + len_bits += 42; // 10 bits SN, 2 bits ext, 15 bits so_start, 15 bits so_end + } else { + len_bits += 12; // 10 bits SN, 2 bits ext + } + } + + return (len_bits + 7) / 8; // Convert to bytes - integer rounding up +} + +bool rlc_am_is_pdu_segment(uint8_t* payload) +{ + return ((*(payload) >> 6) & 0x01) == 1; +} + +bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min) +{ + // check if ACK_SN is inside Rx window + if ((MOD + status.ack_sn - rx_win_min) % MOD > RLC_AM_WINDOW_SIZE) { + return false; + } + + for (uint32_t i = 0; i < status.N_nack; ++i) { + // NACK can't be larger than ACK + if ((MOD + status.ack_sn - status.nacks[i].nack_sn) % MOD > RLC_AM_WINDOW_SIZE) { + return false; + } + // Don't NACK the ACK SN + if (status.nacks[i].nack_sn == status.ack_sn) { + return false; + } + } + return true; +} + +void rlc_am_undelivered_sdu_info_to_string(fmt::memory_buffer& buffer, + const std::vector >& info_queue) +{ + fmt::format_to(buffer, "\n"); + for (const auto& pdcp_pdu : info_queue) { + fmt::format_to(buffer, "\tPDCP_SN = {}, undelivered RLC SNs = [", pdcp_pdu.sn); + for (const auto& nacked_segment : pdcp_pdu) { + fmt::format_to(buffer, "{} ", nacked_segment.rlc_sn()); + } + fmt::format_to(buffer, "]\n"); + } +} + +bool rlc_am_start_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED); +} + +bool rlc_am_end_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED); +} + +bool rlc_am_is_unaligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); +} + +bool rlc_am_not_start_aligned(const uint8_t fi) +{ + return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED); +} + +} // namespace srsran diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc new file mode 100644 index 000000000..c38e90c58 --- /dev/null +++ b/lib/src/rlc/rlc_am_nr.cc @@ -0,0 +1,2022 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/rlc/rlc_am_nr.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/rlc/rlc_am_nr_packing.h" +#include "srsran/srslog/event_trace.h" +#include +#include + +namespace srsran { + +const static uint32_t max_tx_queue_size = 256; + +/**************************************************************************** + * RLC AM NR entity + ***************************************************************************/ + +/*************************************************************************** + * Tx subclass implementation + ***************************************************************************/ +rlc_am_nr_tx::rlc_am_nr_tx(rlc_am* parent_) : + parent(parent_), rlc_am_base_tx(parent_->logger), poll_retransmit_timer(parent->timers->get_unique_timer()) +{} + +bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_) +{ + cfg = cfg_.am_nr; + rb_name = parent->rb_name; + + if (cfg_.tx_queue_length > max_tx_queue_size) { + RlcError("configuring tx queue length of %d PDUs too big. Maximum value is %d.", + cfg_.tx_queue_length, + max_tx_queue_size); + return false; + } + + mod_nr = cardinality(cfg.tx_sn_field_length); + switch (cfg.tx_sn_field_length) { + case rlc_am_nr_sn_size_t::size12bits: + min_hdr_size = 2; + tx_window = std::unique_ptr >( + new rlc_ringbuffer_t); + break; + case rlc_am_nr_sn_size_t::size18bits: + min_hdr_size = 3; + tx_window = std::unique_ptr >( + new rlc_ringbuffer_t); + break; + default: + RlcError("attempt to configure unsupported tx_sn_field_length %s", to_string(cfg.tx_sn_field_length)); + return false; + } + + max_hdr_size = min_hdr_size + so_size; + + // make sure Tx queue is empty before attempting to resize + empty_queue_no_lock(); + tx_sdu_queue.resize(cfg_.tx_queue_length); + + // Check timers are valid + if (not poll_retransmit_timer.is_valid()) { + RlcError("Configuring TX: timers not configured"); + return false; + } + + // Configure t_poll_retransmission timer + if (cfg.t_poll_retx > 0) { + poll_retransmit_timer.set(static_cast(cfg.t_poll_retx), + [this](uint32_t timerid) { timer_expired(timerid); }); + } + + tx_enabled = true; + + RlcDebug("RLC AM NR configured tx entity."); + return true; +} + +bool rlc_am_nr_tx::has_data() +{ + return do_status() || // if we have a status PDU to transmit + tx_sdu_queue.get_n_sdus() != 0 || !retx_queue.empty(); // or if there is a SDU queued up for transmission +} + +/** + * Builds the RLC PDU. + * + * Called by the MAC, trough one of the PHY worker threads. + * + * \param [payload] is a pointer to the buffer that will hold the PDU. + * \param [nof_bytes] is the number of bytes the RLC is allowed to fill. + * + * \returns the number of bytes written to the payload buffer. + * \remark: This will be called multiple times from the MAC, + * while there is something to TX and enough space in the TB. + */ +uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes) +{ + std::lock_guard lock(mutex); + + if (not tx_enabled) { + RlcDebug("RLC entity not active. Not generating PDU."); + return 0; + } + RlcDebug("MAC opportunity - bytes=%d, tx_window size=%zu PDUs", nof_bytes, tx_window->size()); + + // Tx STATUS if requested + if (do_status()) { + unique_byte_buffer_t tx_pdu = srsran::make_byte_buffer(); + if (tx_pdu == nullptr) { + RlcError("Couldn't allocate PDU in %s().", __FUNCTION__); + return 0; + } + build_status_pdu(tx_pdu.get(), nof_bytes); + memcpy(payload, tx_pdu->msg, tx_pdu->N_bytes); + RlcDebug("Status PDU built - %d bytes", tx_pdu->N_bytes); + return tx_pdu->N_bytes; + } + + // Retransmit if required + if (not retx_queue.empty()) { + RlcInfo("Re-transmission required. Retransmission queue size: %d", retx_queue.size()); + return build_retx_pdu(payload, nof_bytes); + } + + // Send remaining segment, if it exists + if (sdu_under_segmentation_sn != INVALID_RLC_SN) { + if (not tx_window->has_sn(sdu_under_segmentation_sn)) { + sdu_under_segmentation_sn = INVALID_RLC_SN; + RlcError("SDU currently being segmented does not exist in tx_window. Aborting segmentation SN=%d", + sdu_under_segmentation_sn); + return 0; + } + return build_continuation_sdu_segment((*tx_window)[sdu_under_segmentation_sn], payload, nof_bytes); + } + + // Check whether there is something to TX + if (tx_sdu_queue.is_empty()) { + RlcInfo("No data available to be sent"); + return 0; + } + + return build_new_pdu(payload, nof_bytes); +} + +/** + * Builds a new RLC PDU. + * + * This will be called after checking whether control, retransmission, + * or segment PDUs needed to be transmitted first. + * + * This will read an SDU from the SDU queue, build a new PDU, and add it to the tx_window. + * SDU segmentation will be done if necessary. + * + * \param [payload] is a pointer to the buffer that will hold the PDU. + * \param [nof_bytes] is the number of bytes the RLC is allowed to fill. + * + * \returns the number of bytes written to the payload buffer. + */ +uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) +{ + if (nof_bytes <= min_hdr_size) { + RlcInfo("Not enough bytes for payload plus header. nof_bytes=%d", nof_bytes); + return 0; + } + + // do not build any more PDU if window is already full + if (tx_window->full()) { + RlcInfo("Cannot build data PDU - Tx window full."); + return 0; + } + + // Read new SDU from TX queue + unique_byte_buffer_t tx_sdu; + RlcDebug("Reading from RLC SDU queue. Queue size %d", tx_sdu_queue.size()); + do { + tx_sdu = tx_sdu_queue.read(); + } while (tx_sdu == nullptr && tx_sdu_queue.size() != 0); + + if (tx_sdu != nullptr) { + RlcDebug("Read RLC SDU - RLC_SN=%d, PDCP_SN=%d, %d bytes", st.tx_next, tx_sdu->md.pdcp_sn, tx_sdu->N_bytes); + } else { + RlcDebug("No SDUs left in the tx queue."); + return 0; + } + + // insert newly assigned SN into window and use reference for in-place operations + // NOTE: from now on, we can't return from this function anymore before increasing tx_next + rlc_amd_tx_pdu_nr& tx_pdu = tx_window->add_pdu(st.tx_next); + tx_pdu.pdcp_sn = tx_sdu->md.pdcp_sn; + tx_pdu.sdu_buf = srsran::make_byte_buffer(); + if (tx_pdu.sdu_buf == nullptr) { + RlcError("Couldn't allocate PDU in %s().", __FUNCTION__); + return 0; + } + + // Copy SDU into TX window SDU info + memcpy(tx_pdu.sdu_buf->msg, tx_sdu->msg, tx_sdu->N_bytes); + tx_pdu.sdu_buf->N_bytes = tx_sdu->N_bytes; + + // Segment new SDU if necessary + if (tx_sdu->N_bytes + min_hdr_size > nof_bytes) { + RlcInfo("trying to build PDU segment from SDU."); + return build_new_sdu_segment(tx_pdu, payload, nof_bytes); + } + + // Prepare header + rlc_am_nr_pdu_header_t hdr = {}; + hdr.dc = RLC_DC_FIELD_DATA_PDU; + hdr.p = get_pdu_poll(st.tx_next, false, tx_sdu->N_bytes); + hdr.si = rlc_nr_si_field_t::full_sdu; + hdr.sn_size = cfg.tx_sn_field_length; + hdr.sn = st.tx_next; + tx_pdu.header = hdr; + log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); + + // Write header + uint32_t len = rlc_am_nr_write_data_pdu_header(hdr, tx_sdu.get()); + if (len > nof_bytes) { + RlcError("error writing AMD PDU header"); + } + + // Update TX Next + st.tx_next = (st.tx_next + 1) % mod_nr; + + memcpy(payload, tx_sdu->msg, tx_sdu->N_bytes); + RlcDebug("wrote RLC PDU - %d bytes", tx_sdu->N_bytes); + + return tx_sdu->N_bytes; +} + +/** + * Builds a new RLC PDU segment, from a RLC SDU. + * + * \param [tx_pdu] is the tx_pdu info contained in the tx_window. + * \param [payload] is a pointer to the MAC buffer that will hold the PDU segment. + * \param [nof_bytes] is the number of bytes the RLC is allowed to fill. + * + * \returns the number of bytes written to the payload buffer. + * \remark: This functions assumes that the SDU has already been copied to tx_pdu.sdu_buf. + */ +uint32_t rlc_am_nr_tx::build_new_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes) +{ + RlcInfo("creating new SDU segment. Tx SDU (%d B), nof_bytes=%d B ", tx_pdu.sdu_buf->N_bytes, nof_bytes); + + // Sanity check: can this SDU be sent this in a single PDU? + if ((tx_pdu.sdu_buf->N_bytes + min_hdr_size) < nof_bytes) { + RlcError("calling build_new_sdu_segment(), but there are enough bytes to tx in a single PDU. Tx SDU (%d B), " + "nof_bytes=%d B ", + tx_pdu.sdu_buf->N_bytes, + nof_bytes); + return 0; + } + + // Sanity check: can this SDU be sent considering header overhead? + if (nof_bytes <= min_hdr_size) { // Small header as SO is not present + RlcInfo("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d, " + "min_hdr_size=%d", + nof_bytes, + min_hdr_size); + return 0; + } + + uint32_t segment_payload_len = nof_bytes - min_hdr_size; + + // Save SDU currently being segmented + // This needs to be done before calculating the polling bit + // To make sure we check correctly that the buffers are empty. + sdu_under_segmentation_sn = st.tx_next; + + // Prepare header + rlc_am_nr_pdu_header_t hdr = {}; + hdr.dc = RLC_DC_FIELD_DATA_PDU; + hdr.p = get_pdu_poll(st.tx_next, false, segment_payload_len); + hdr.si = rlc_nr_si_field_t::first_segment; + hdr.sn_size = cfg.tx_sn_field_length; + hdr.sn = st.tx_next; + hdr.so = 0; + tx_pdu.header = hdr; + log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); + + // Write header + uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); + if (hdr_len >= nof_bytes || hdr_len != min_hdr_size) { + RlcError("error writing AMD PDU header"); + return 0; + } + + // Copy PDU to payload + srsran_assert((hdr_len + segment_payload_len) <= nof_bytes, "Error calculating hdr_len and segment_payload_len"); + memcpy(&payload[hdr_len], tx_pdu.sdu_buf->msg, segment_payload_len); + + // Store Segment Info + rlc_amd_tx_pdu_nr::pdu_segment segment_info; + segment_info.payload_len = segment_payload_len; + tx_pdu.segment_list.push_back(segment_info); + return hdr_len + segment_payload_len; +} + +/** + * Build PDU segment for an RLC SDU that is already on-going segmentation. + * + * \param [tx_pdu] is the tx_pdu info contained in the tx_window. + * \param [payload] is a pointer to the MAC buffer that will hold the PDU segment. + * \param [nof_bytes] is the number of bytes the RLC is allowed to fill. + * + * \returns the number of bytes written to the payload buffer. + * \remark: This functions assumes that the SDU has already been copied to tx_pdu.sdu_buf. + */ +uint32_t rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes) +{ + RlcInfo("continuing SDU segment. SN=%d, Tx SDU (%d B), nof_bytes=%d B ", + sdu_under_segmentation_sn, + tx_pdu.sdu_buf->N_bytes, + nof_bytes); + + // Sanity check: is there an initial SDU segment? + if (tx_pdu.segment_list.empty()) { + RlcError("build_continuation_sdu_segment was called, but there was no initial segment. SN=%d, Tx SDU (%d B), " + "nof_bytes=%d B ", + sdu_under_segmentation_sn, + tx_pdu.sdu_buf->N_bytes, + nof_bytes); + sdu_under_segmentation_sn = INVALID_RLC_SN; + return 0; + } + + // Sanity check: can this SDU be sent considering header overhead? + if (nof_bytes <= max_hdr_size) { // Larger header size, as SO is present + RlcInfo("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d, " + "max_header_size=%d", + nof_bytes, + max_hdr_size); + return 0; + } + + // Can the rest of the SDU be sent on a single segment PDU? + const rlc_amd_tx_pdu_nr::pdu_segment& seg = tx_pdu.segment_list.back(); + uint32_t last_byte = seg.so + seg.payload_len; + RlcDebug("continuing SDU segment. SN=%d, last byte transmitted %d", tx_pdu.rlc_sn, last_byte); + + // Sanity check: last byte must be smaller than SDU size + if (last_byte > tx_pdu.sdu_buf->N_bytes) { + RlcError( + "last byte transmitted larger than SDU len. SDU len=%d B, last_byte=%d B", tx_pdu.sdu_buf->N_bytes, last_byte); + return 0; + } + + uint32_t segment_payload_full_len = tx_pdu.sdu_buf->N_bytes - last_byte + max_hdr_size; // SO is included + uint32_t segment_payload_len = tx_pdu.sdu_buf->N_bytes - last_byte; + rlc_nr_si_field_t si = {}; + + if (segment_payload_full_len > nof_bytes) { + RlcInfo("grant is not large enough for full SDU. " + "SDU bytes left %d, nof_bytes %d, ", + segment_payload_full_len, + nof_bytes); + si = rlc_nr_si_field_t::neither_first_nor_last_segment; + segment_payload_len = nof_bytes - max_hdr_size; + segment_payload_full_len = nof_bytes; + } else { + RlcInfo("grant is large enough for full SDU." + "SDU bytes left %d, nof_bytes %d, ", + segment_payload_full_len, + nof_bytes); + si = rlc_nr_si_field_t::last_segment; + sdu_under_segmentation_sn = INVALID_RLC_SN; + } + + // Prepare header + rlc_am_nr_pdu_header_t hdr = {}; + hdr.dc = RLC_DC_FIELD_DATA_PDU; + hdr.p = get_pdu_poll(st.tx_next, false, segment_payload_len); + hdr.si = si; + hdr.sn_size = cfg.tx_sn_field_length; + hdr.sn = st.tx_next; + hdr.so = last_byte; + tx_pdu.header = hdr; + log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); + + // Write header + uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); + if (hdr_len >= nof_bytes || hdr_len != max_hdr_size) { + RlcError("error writing AMD PDU header"); + return 0; + } + + // Copy PDU to payload + srsran_assert((hdr_len + segment_payload_len) <= nof_bytes, "Error calculating hdr_len and segment_payload_len"); + memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[last_byte], segment_payload_len); + + // Store PDU segment info into tx_window + rlc_amd_tx_pdu_nr::pdu_segment segment_info = {}; + segment_info.so = last_byte; + segment_info.payload_len = segment_payload_len; + tx_pdu.segment_list.push_back(segment_info); + + if (si == rlc_nr_si_field_t::neither_first_nor_last_segment) { + RlcInfo("grant is not large enough for full SDU." + "Storing SDU segment info"); + } else { + RlcInfo("grant is large enough for full SDU." + "Removing current SDU info"); + // SDU is fully TX'ed. Increment TX_NEXT + st.tx_next = (st.tx_next + 1) % mod_nr; + } + + return hdr_len + segment_payload_len; +} + +/** + * Builds a retx RLC PDU. + * + * This will use the retx_queue to get information about the RLC PDU + * being retx'ed. The retx may have been previously transmitted as + * a full SDU or an SDU segment. + * + * \param [tx_pdu] is the tx_pdu info contained in the tx_window. + * \param [payload] is a pointer to the MAC buffer that will hold the PDU segment. + * \param [nof_bytes] is the number of bytes the RLC is allowed to fill. + * + * \returns the number of bytes written to the payload buffer. + * \remark: This functions assumes that the SDU has already been copied to tx_pdu.sdu_buf. + */ +uint32_t rlc_am_nr_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes) +{ + // Check there is at least 1 element before calling front() + if (retx_queue.empty()) { + RlcError("in build_retx_pdu(): retx_queue is empty"); + return 0; + } + + rlc_amd_retx_nr_t& retx = retx_queue.front(); + + // Sanity check - drop any retx SNs not present in tx_window + while (not tx_window->has_sn(retx.sn)) { + RlcInfo("SN=%d not in tx window, probably already ACKed. Skip and remove from retx queue", retx.sn); + retx_queue.pop(); + if (!retx_queue.empty()) { + retx = retx_queue.front(); + } else { + RlcInfo("empty retx queue, cannot provide any retx PDU"); + return 0; + } + } + + RlcDebug("RETX - SN=%d, is_segment=%s, current_so=%d, so_start=%d, segment_length=%d", + retx.sn, + retx.is_segment ? "true" : "false", + retx.current_so, + retx.so_start, + retx.segment_length); + + // Is segmentation/re-segmentation required? + bool segmentation_required = is_retx_segmentation_required(retx, nof_bytes); + + if (segmentation_required) { + return build_retx_pdu_with_segmentation(retx, payload, nof_bytes); + } + return build_retx_pdu_without_segmentation(retx, payload, nof_bytes); +} + +/** + * Builds a retx RLC PDU, without requiring (re-)segmentation. + * + * The RETX PDU may be transporting a full SDU or an SDU segment. + * + * \param [retx] is the retx info contained in the retx_queue. This is passed by copy, to avoid + * issues when using retx after pop'ing it from the queue. + * \param [payload] is a pointer to the MAC buffer that will hold the PDU segment. + * \param [nof_bytes] is the number of bytes the RLC is allowed to fill. + * + * \returns the number of bytes written to the payload buffer. + * \remark this function will not update the SI. This means that if the retx is of the last + * SDU segment, the SI should already be of the `last_segment` type. + */ +uint32_t +rlc_am_nr_tx::build_retx_pdu_without_segmentation(const rlc_amd_retx_nr_t retx, uint8_t* payload, uint32_t nof_bytes) +{ + srsran_assert(tx_window->has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); + srsran_assert(not is_retx_segmentation_required(retx, nof_bytes), + "Called %s without checking if segmentation was required", + __FUNCTION__); + + // Get tx_pdu info from tx_window + rlc_amd_tx_pdu_nr& tx_pdu = (*tx_window)[retx.sn]; + + // Get expected header and payload len + uint32_t expected_hdr_len = get_retx_expected_hdr_len(retx); + uint32_t retx_payload_len = retx.is_segment ? (retx.so_start + retx.segment_length - retx.current_so) + : (*tx_window)[retx.sn].sdu_buf->N_bytes; + srsran_assert(nof_bytes >= (expected_hdr_len + retx_payload_len), + "Called %s but segmentation is required. nof_bytes=%d, expeced_hdr_len=%d, retx_payload_len=%d", + __FUNCTION__, + nof_bytes, + expected_hdr_len, + retx_payload_len); + + // Log RETX info + RlcDebug("SDU%scan be fully re-transmitted. SN=%d, nof_bytes=%d, expected_hdr_len=%d, " + "current_so=%d, so_start=%d, segment_length=%d", + retx.is_segment ? " segment " : " ", + retx.sn, + nof_bytes, + expected_hdr_len, + retx.current_so, + retx.so_start, + retx.segment_length); + + // Get RETX SN, current SO and SI + rlc_nr_si_field_t si = rlc_nr_si_field_t::full_sdu; + if (retx.is_segment) { + if (retx.current_so == 0) { + si = rlc_nr_si_field_t::first_segment; + } else if ((retx.current_so + retx_payload_len) < tx_pdu.sdu_buf->N_bytes) { + si = rlc_nr_si_field_t::neither_first_nor_last_segment; + } else { + si = rlc_nr_si_field_t::last_segment; + } + } + + // Get RETX PDU payload size + uint32_t retx_pdu_payload_size = 0; + if (not retx.is_segment) { + // RETX full SDU + retx_pdu_payload_size = (*tx_window)[retx.sn].sdu_buf->N_bytes; + } else { + // RETX SDU segment + retx_pdu_payload_size = (retx.so_start + retx.segment_length - retx.current_so); + } + + // Update RETX queue. This must be done before calculating + // the polling bit, to make sure the poll bit is calculated correctly + retx_queue.pop(); + + // Write header to payload + rlc_am_nr_pdu_header_t new_header = tx_pdu.header; + new_header.si = si; + new_header.so = retx.current_so; + new_header.p = get_pdu_poll(retx.sn, true, 0); + uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(new_header, payload); + + // Write SDU/SDU segment to payload + uint32_t pdu_bytes = hdr_len + retx_pdu_payload_size; + srsran_assert(pdu_bytes <= nof_bytes, "Error calculating hdr_len and pdu_payload_len"); + memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size); + + // Log RETX + RlcHexInfo((*tx_window)[retx.sn].sdu_buf->msg, + (*tx_window)[retx.sn].sdu_buf->N_bytes, + "Original SDU SN=%d (%d B) (attempt %d/%d)", + retx.sn, + (*tx_window)[retx.sn].sdu_buf->N_bytes, + (*tx_window)[retx.sn].retx_count + 1, + cfg.max_retx_thresh); + RlcHexInfo(payload, pdu_bytes, "RETX PDU SN=%d (%d B)", retx.sn, pdu_bytes); + log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header, rb_name); + + debug_state(); + return pdu_bytes; +} + +/** + * Builds a retx RLC PDU that requires (re-)segmentation. + * + * \param [tx_pdu] is the tx_pdu info contained in the tx_window. + * \param [payload] is a pointer to the MAC buffer that will hold the PDU segment. + * \param [nof_bytes] is the number of bytes the RLC is allowed to fill. + * + * \returns the number of bytes written to the payload buffer. + * \remark: This functions assumes that the SDU has already been copied to tx_pdu.sdu_buf. + */ +uint32_t rlc_am_nr_tx::build_retx_pdu_with_segmentation(rlc_amd_retx_nr_t& retx, uint8_t* payload, uint32_t nof_bytes) +{ + // Get tx_pdu info from tx_window + srsran_assert(tx_window->has_sn(retx.sn), "Called %s without checking retx SN", __FUNCTION__); + srsran_assert(is_retx_segmentation_required(retx, nof_bytes), + "Called %s without checking if segmentation was not required", + __FUNCTION__); + + rlc_amd_tx_pdu_nr& tx_pdu = (*tx_window)[retx.sn]; + + // Is this an SDU segment or a full SDU? + if (not retx.is_segment) { + RlcDebug("Creating SDU segment from full SDU. SN=%d Tx SDU (%d B), nof_bytes=%d B ", + retx.sn, + tx_pdu.sdu_buf->N_bytes, + nof_bytes); + + } else { + RlcDebug("Creating SDU segment from SDU segment. SN=%d, current_so=%d, so_start=%d, segment_length=%d", + retx.sn, + retx.current_so, + retx.so_start, + retx.segment_length); + } + + uint32_t expected_hdr_len = min_hdr_size; + rlc_nr_si_field_t si = rlc_nr_si_field_t::first_segment; + if (retx.current_so != 0) { + si = rlc_nr_si_field_t::neither_first_nor_last_segment; + expected_hdr_len = max_hdr_size; + } + + // Sanity check: are there enough bytes for header plus data? + if (nof_bytes <= expected_hdr_len) { + RlcInfo("Not enough bytes for RETX payload plus header. SN=%d, nof_bytes=%d, hdr_len=%d", + retx.sn, + nof_bytes, + expected_hdr_len); + return 0; + } + + // Sanity check: could this have been transmitted without segmentation? + if (nof_bytes > (tx_pdu.sdu_buf->N_bytes + expected_hdr_len)) { + RlcError("called %s, but there are enough bytes to avoid segmentation. SN=%d", __FUNCTION__, retx.sn); + return 0; + } + + // Can the RETX PDU be transmitted in a single PDU? + uint32_t retx_pdu_payload_size = nof_bytes - expected_hdr_len; + + // Write header + rlc_am_nr_pdu_header_t hdr = tx_pdu.header; + hdr.p = get_pdu_poll(retx.sn, true, 0); + hdr.so = retx.current_so; + hdr.si = si; + uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload); + if (hdr_len >= nof_bytes || hdr_len != expected_hdr_len) { + log_rlc_am_nr_pdu_header_to_string(logger.error, hdr, rb_name); + RlcError("Error writing AMD PDU header. nof_bytes=%d, hdr_len=%d", nof_bytes, hdr_len); + return 0; + } + log_rlc_am_nr_pdu_header_to_string(logger.info, hdr, rb_name); + + // Copy SDU segment into payload + srsran_assert((hdr_len + retx_pdu_payload_size) <= nof_bytes, "Error calculating hdr_len and segment_payload_len"); + memcpy(&payload[hdr_len], &tx_pdu.sdu_buf->msg[retx.current_so], retx_pdu_payload_size); + + // Store PDU segment info into tx_window + RlcDebug("Updating RETX segment info. SN=%d, is_segment=%s", retx.sn, retx.is_segment ? "true" : "false"); + if (!retx.is_segment) { + // Retx is not a segment yet + rlc_amd_tx_pdu_nr::pdu_segment seg1 = {}; + seg1.so = retx.current_so; + seg1.payload_len = retx_pdu_payload_size; + rlc_amd_tx_pdu_nr::pdu_segment seg2 = {}; + seg2.so = retx.current_so + retx_pdu_payload_size; + seg2.payload_len = retx.segment_length - retx_pdu_payload_size; + tx_pdu.segment_list.push_back(seg1); + tx_pdu.segment_list.push_back(seg2); + RlcDebug("New segment: SN=%d, SO=%d len=%d", retx.sn, seg1.so, seg1.payload_len); + RlcDebug("New segment: SN=%d, SO=%d len=%d", retx.sn, seg2.so, seg2.payload_len); + } else { + // Retx is already a segment + // Find current segment in segment list. + std::list::iterator it; + for (it = tx_pdu.segment_list.begin(); it != tx_pdu.segment_list.end(); ++it) { + if (it->so == retx.current_so) { + break; + } + } + if (it != tx_pdu.segment_list.end()) { + rlc_amd_tx_pdu_nr::pdu_segment seg1 = {}; + seg1.so = it->so; + seg1.payload_len = retx_pdu_payload_size; + rlc_amd_tx_pdu_nr::pdu_segment seg2 = {}; + seg2.so = it->so + retx_pdu_payload_size; + seg2.payload_len = it->payload_len - retx_pdu_payload_size; + + std::list::iterator begin_it = tx_pdu.segment_list.erase(it); + std::list::iterator insert_it = tx_pdu.segment_list.insert(begin_it, seg2); + std::list::iterator insert_it2 = tx_pdu.segment_list.insert(insert_it, seg1); + RlcDebug("Old segment SN=%d, SO=%d len=%d", retx.sn, retx.current_so, retx.segment_length); + RlcDebug("New segment SN=%d, SO=%d len=%d", retx.sn, seg1.so, seg1.payload_len); + RlcDebug("New segment SN=%d, SO=%d len=%d", retx.sn, seg2.so, seg2.payload_len); + } else { + RlcDebug("Could not find segment. SN=%d, SO=%d length=%d", retx.sn, retx.current_so, retx.segment_length); + } + } + + // Update retx queue + retx.is_segment = true; + retx.current_so = retx.current_so + retx_pdu_payload_size; + + RlcDebug("Updated RETX info. is_segment=%s, current_so=%d, so_start=%d, segment_length=%d", + retx.is_segment ? "true" : "false", + retx.current_so, + retx.so_start, + retx.segment_length); + + if (retx.current_so >= tx_pdu.sdu_buf->N_bytes) { + RlcError("Current SO larger or equal to SDU size when creating SDU segment. SN=%d, current SO=%d, SO_start=%d, " + "segment_length=%d", + retx.sn, + retx.current_so, + retx.so_start, + retx.segment_length); + return 0; + } + + if (retx.current_so >= retx.so_start + retx.segment_length) { + RlcError("Current SO larger than SO_start + segment_length. SN=%d, current SO=%d, SO_start=%d, segment_length=%s", + retx.sn, + retx.current_so, + retx.so_start, + retx.segment_length); + return 0; + } + + return hdr_len + retx_pdu_payload_size; +} + +bool rlc_am_nr_tx::is_retx_segmentation_required(const rlc_amd_retx_nr_t& retx, uint32_t nof_bytes) +{ + bool segmentation_required = false; + if (retx.is_segment) { + uint32_t expected_hdr_size = retx.current_so == 0 ? min_hdr_size : max_hdr_size; + if (nof_bytes < ((retx.so_start + retx.segment_length - retx.current_so) + expected_hdr_size)) { + RlcInfo("Re-segmentation required for RETX. SN=%d", retx.sn); + segmentation_required = true; + } + } else { + if (nof_bytes < ((*tx_window)[retx.sn].sdu_buf->N_bytes + min_hdr_size)) { + RlcInfo("Segmentation required for RETX. SN=%d", retx.sn); + segmentation_required = true; + } + } + return segmentation_required; +} + +uint32_t rlc_am_nr_tx::get_retx_expected_hdr_len(const rlc_amd_retx_nr_t& retx) +{ + uint32_t expected_hdr_len = min_hdr_size; + if (retx.is_segment && retx.current_so != 0) { + expected_hdr_len = max_hdr_size; + } + return expected_hdr_len; +} + +uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes) +{ + RlcInfo("generating status PDU. Bytes available:%d", nof_bytes); + rlc_am_nr_status_pdu_t status(cfg.rx_sn_field_length); // carries status of RX entity, hence use SN length of RX + int pdu_len = rx->get_status_pdu(&status, nof_bytes); + if (pdu_len == SRSRAN_ERROR) { + RlcDebug("deferred status PDU. Cause: Failed to acquire rx lock"); + pdu_len = 0; + } else if (pdu_len > 0 && nof_bytes >= static_cast(pdu_len)) { + RlcDebug("generated status PDU. Bytes:%d", pdu_len); + log_rlc_am_nr_status_pdu_to_string(logger.info, "TX status PDU - %s", &status, rb_name); + pdu_len = rlc_am_nr_write_status_pdu(status, cfg.tx_sn_field_length, payload); + } else { + RlcInfo("cannot tx status PDU - %d bytes available, %d bytes required", nof_bytes, pdu_len); + pdu_len = 0; + } + + return payload->N_bytes; +} + +void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) +{ + if (not tx_enabled) { + return; + } + + std::lock_guard lock(mutex); + rlc_am_nr_status_pdu_t status(cfg.tx_sn_field_length); + RlcHexDebug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name); + rlc_am_nr_read_status_pdu(payload, nof_bytes, cfg.tx_sn_field_length, &status); + log_rlc_am_nr_status_pdu_to_string(logger.info, "RX status PDU: %s", &status, parent->rb_name); + + /* + * Sanity check the received status report. + * Checking if the ACK_SN is inside the valid ACK_SN window (the TX window "off-by-one") + * makes sure we discard out of order status reports. + * Checking if ACK_SN > Tx_Next + 1 makes sure we do not receive a ACK/NACK for something we did not TX + * ACK_SN may be equal to TX_NEXT + 1, if not all SDU segments with SN=TX_NEXT have been transmitted. + */ + if (not valid_ack_sn(status.ack_sn)) { + RlcInfo("Received ACK with SN outside of TX_WINDOW, ignoring status report. ACK_SN=%d, TX_NEXT_ACK=%d.", + status.ack_sn, + st.tx_next_ack); + info_state(); + return; + } + + if (tx_mod_base_nr(status.ack_sn) > tx_mod_base_nr(st.tx_next + 1)) { + RlcWarning("Received ACK with SN larger than TX_NEXT, ignoring status report. SN=%d, TX_NEXT_ACK=%d, TX_NEXT=%d", + status.ack_sn, + st.tx_next_ack, + st.tx_next); + info_state(); + return; + } + + /** + * Section 5.3.3.3: Reception of a STATUS report + * - if the STATUS report comprises a positive or negative acknowledgement for the RLC SDU with sequence + * number equal to POLL_SN: + * - if t-PollRetransmit is running: + * - stop and reset t-PollRetransmit. + */ + if (tx_mod_base_nr(st.poll_sn) < tx_mod_base_nr(status.ack_sn)) { + if (poll_retransmit_timer.is_running()) { + RlcDebug("Received ACK or NACK for POLL_SN=%d. Stopping t-PollRetransmit", st.poll_sn); + poll_retransmit_timer.stop(); + } else { + RlcDebug("Received ACK or NACK for POLL_SN=%d. t-PollRetransmit already stopped", st.poll_sn); + } + } else { + RlcDebug("POLL_SN=%d > ACK_SN=%d. Not stopping t-PollRetransmit ", st.poll_sn, status.ack_sn); + } + + /* + * - if the SN of the corresponding RLC SDU falls within the range + * TX_Next_Ack <= SN < = the highest SN of the AMD PDU among the AMD PDUs submitted to lower layer: + * - consider the RLC SDU or the RLC SDU segment for which a negative acknowledgement was received for + * retransmission. + */ + // Process ACKs + uint32_t stop_sn = status.nacks.size() == 0 + ? status.ack_sn + : status.nacks[0].nack_sn; // Stop processing ACKs at the first NACK, if it exists. + for (uint32_t sn = st.tx_next_ack; tx_mod_base_nr(sn) < tx_mod_base_nr(stop_sn); sn = (sn + 1) % mod_nr) { + if (tx_window->has_sn(sn)) { + notify_info_vec.push_back((*tx_window)[sn].pdcp_sn); + retx_queue.remove_sn(sn); // remove any pending retx for that SN + tx_window->remove_pdu(sn); + st.tx_next_ack = (sn + 1) % mod_nr; + } else { + RlcError("Missing ACKed SN from TX window"); + break; + } + } + RlcDebug("Processed status report ACKs. ACK_SN=%d. Tx_Next_Ack=%d", status.ack_sn, st.tx_next_ack); + + // Process N_nacks + std::set retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates) + for (uint32_t nack_idx = 0; nack_idx < status.nacks.size(); nack_idx++) { + if (status.nacks[nack_idx].has_nack_range) { + for (uint32_t range_sn = status.nacks[nack_idx].nack_sn; + range_sn < status.nacks[nack_idx].nack_sn + status.nacks[nack_idx].nack_range; + range_sn++) { + rlc_status_nack_t nack = {}; + nack.nack_sn = range_sn; + if (status.nacks[nack_idx].has_so) { + // Apply so_start to first range item + if (range_sn == status.nacks[nack_idx].nack_sn) { + nack.so_start = status.nacks[nack_idx].so_start; + } + // Apply so_end to last range item + if (range_sn == (status.nacks[nack_idx].nack_sn + status.nacks[nack_idx].nack_range - 1)) { + nack.so_end = status.nacks[nack_idx].so_end; + } + // Enable has_so only if the offsets do not span the whole SDU + nack.has_so = (nack.so_start != 0) || (nack.so_end != rlc_status_nack_t::so_end_of_sdu); + } + handle_nack(nack, retx_sn_set); + } + } else { + handle_nack(status.nacks[nack_idx], retx_sn_set); + } + } + + // Process retx_count and inform upper layers if needed + for (uint32_t retx_sn : retx_sn_set) { + auto& pdu = (*tx_window)[retx_sn]; + // Increment retx_count + if (pdu.retx_count == RETX_COUNT_NOT_STARTED) { + // Set retx_count = 0 on first RE-transmission of associated SDU (38.322 Sec. 5.3.2) + pdu.retx_count = 0; + } else { + // Increment otherwise + pdu.retx_count++; + } + + // Inform upper layers if needed + check_sn_reached_max_retx(retx_sn); + } + + // Notify PDCP + if (not notify_info_vec.empty()) { + parent->pdcp->notify_delivery(parent->lcid, notify_info_vec); + } + notify_info_vec.clear(); +} + +void rlc_am_nr_tx::handle_nack(const rlc_status_nack_t& nack, std::set& retx_sn_set) +{ + if (tx_mod_base_nr(st.tx_next_ack) <= tx_mod_base_nr(nack.nack_sn) && + tx_mod_base_nr(nack.nack_sn) <= tx_mod_base_nr(st.tx_next)) { + RlcDebug("Handling NACK for SN=%d", nack.nack_sn); + if (tx_window->has_sn(nack.nack_sn)) { + auto& pdu = (*tx_window)[nack.nack_sn]; + + if (nack.has_so) { + // NACK'ing missing bytes in SDU segment. + // Retransmit all SDU segments within those missing bytes. + if (pdu.segment_list.empty()) { + RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack.nack_sn); + } + bool segment_found = false; + for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { + if (segm.so >= nack.so_start && segm.so <= nack.so_end) { + if (not retx_queue.has_sn(nack.nack_sn, segm.so)) { + rlc_amd_retx_nr_t& retx = retx_queue.push(); + retx.sn = nack.nack_sn; + retx.is_segment = true; + retx.so_start = segm.so; + retx.current_so = segm.so; + retx.segment_length = segm.payload_len; + retx_sn_set.insert(nack.nack_sn); + RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", + retx.sn, + retx.so_start, + retx.segment_length); + } else { + RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d", + nack.nack_sn, + segm.so, + segm.payload_len); + } + segment_found = true; + } + } + if (!segment_found) { + RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d", + nack.nack_sn, + nack.so_start, + nack.so_end); + for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) { + RlcDebug("Segments for SN=%d. SO=%d, SO_end=%d", nack.nack_sn, segm.so, segm.payload_len); + } + } + } else { + // NACK'ing full SDU. + // add to retx queue if it's not already there + if (not retx_queue.has_sn(nack.nack_sn)) { + // Have we segmented the SDU already? + if ((*tx_window)[nack.nack_sn].segment_list.empty()) { + rlc_amd_retx_nr_t& retx = retx_queue.push(); + retx.sn = nack.nack_sn; + retx.is_segment = false; + retx.so_start = 0; + retx.current_so = 0; + retx.segment_length = pdu.sdu_buf->N_bytes; + retx_sn_set.insert(nack.nack_sn); + RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn); + } else { + RlcInfo("Scheduled RETX of SDU SN=%d", nack.nack_sn); + retx_sn_set.insert(nack.nack_sn); + for (auto segm : (*tx_window)[nack.nack_sn].segment_list) { + rlc_amd_retx_nr_t& retx = retx_queue.push(); + retx.sn = nack.nack_sn; + retx.is_segment = true; + retx.so_start = segm.so; + retx.current_so = segm.so; + retx.segment_length = segm.payload_len; + RlcInfo("Scheduled RETX of SDU Segment. SN=%d, SO=%d, len=%d", retx.sn, segm.so, segm.payload_len); + } + } + } else { + RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", + nack.nack_sn, + st.tx_next_ack, + st.tx_next); + } + } + } else { + RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", + nack.nack_sn, + st.tx_next_ack, + st.tx_next); + } // TX window containts NACK SN + } else { + RlcInfo( + "RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", nack.nack_sn, st.tx_next_ack, st.tx_next); + } // NACK SN within expected range +} +/** + * Helper to check if a SN has reached the max reTx threshold + * + * Caller _must_ hold the mutex when calling the function. + * If the retx has been reached for a SN the upper layers (i.e. RRC/PDCP) will be informed. + * The SN is _not_ removed from the Tx window, so retransmissions of that SN can still occur. + * + * + * @param sn The SN of the PDU to check + */ +void rlc_am_nr_tx::check_sn_reached_max_retx(uint32_t sn) +{ + if ((*tx_window)[sn].retx_count == cfg.max_retx_thresh) { + RlcWarning("Signaling max number of reTx=%d for SN=%d", (*tx_window)[sn].retx_count, sn); + parent->rrc->max_retx_attempted(); + srsran::pdcp_sn_vector_t pdcp_sns; + pdcp_sns.push_back((*tx_window)[sn].pdcp_sn); + parent->pdcp->notify_failure(parent->lcid, pdcp_sns); + + std::lock_guard lock(parent->metrics_mutex); + parent->metrics.num_lost_pdus++; + } +} + +uint32_t rlc_am_nr_tx::get_buffer_state() +{ + uint32_t tx_queue = 0; + uint32_t prio_tx_queue = 0; + get_buffer_state(tx_queue, prio_tx_queue); + return tx_queue + prio_tx_queue; +} + +void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_prio) +{ + std::lock_guard lock(mutex); + RlcDebug("buffer state - do_status=%s", do_status() ? "yes" : "no"); + + if (!tx_enabled) { + RlcError("get_buffer_state() failed: TX is not enabled."); + return; + } + + // Bytes needed for status report + if (do_status()) { + n_bytes_prio += rx->get_status_pdu_length(); + RlcDebug("buffer state - total status report: %d bytes", n_bytes_prio); + } + + // Bytes needed for retx + for (const rlc_amd_retx_nr_t& retx : retx_queue.get_inner_queue()) { + RlcDebug("buffer state - retx - SN=%d, Segment: %s, %d:%d", + retx.sn, + retx.is_segment ? "true" : "false", + retx.so_start, + retx.so_start + retx.segment_length - 1); + if (tx_window->has_sn(retx.sn)) { + int req_bytes = retx.segment_length; + int hdr_req_bytes = (retx.is_segment && retx.current_so != 0) ? max_hdr_size : min_hdr_size; + if (req_bytes <= 0) { + RlcError("buffer state - retx - invalid length=%d for SN=%d", req_bytes, retx.sn); + } else { + n_bytes_prio += (req_bytes + hdr_req_bytes); + RlcDebug("buffer state - retx: %d bytes", n_bytes_prio); + } + } else { + RlcWarning("buffer state - retx for SN=%d is outside the tx_window", retx.sn); + } + } + + // Bytes needed for tx of the rest of the SDU that is currently under segmentation (if any) + if (sdu_under_segmentation_sn != INVALID_RLC_SN) { + if (tx_window->has_sn(sdu_under_segmentation_sn)) { + rlc_amd_tx_pdu_nr& seg_pdu = (*tx_window)[sdu_under_segmentation_sn]; + if (not seg_pdu.segment_list.empty()) { + // obtain amount of already transmitted Bytes + const rlc_amd_tx_pdu_nr::pdu_segment& seg = seg_pdu.segment_list.back(); + uint32_t last_byte = seg.so + seg.payload_len; + if (last_byte <= seg_pdu.sdu_buf->N_bytes) { + // compute remaining bytes pending for transmission + uint32_t remaining_bytes = seg_pdu.sdu_buf->N_bytes - last_byte; + n_bytes_new += remaining_bytes + max_hdr_size; + } else { + RlcError( + "buffer state - last segment of SDU under segmentation exceeds SDU len. SDU len=%d B, last_byte=%d B", + seg_pdu.sdu_buf->N_bytes, + last_byte); + } + } else { + RlcError("buffer state - SDU under segmentation has empty segment list. Ignoring SN=%d", + sdu_under_segmentation_sn); + } + } else { + sdu_under_segmentation_sn = INVALID_RLC_SN; + RlcError("buffer state - SDU under segmentation does not exist in tx_window. Aborting segmentation SN=%d", + sdu_under_segmentation_sn); + } + } + + // Bytes needed for tx SDUs in queue + uint32_t n_sdus = tx_sdu_queue.get_n_sdus(); + n_bytes_new += tx_sdu_queue.size_bytes(); + + // Room needed for fixed header of data PDUs + n_bytes_new += min_hdr_size * n_sdus; + RlcDebug("total buffer state - %d SDUs (%d B)", n_sdus, n_bytes_new + n_bytes_prio); + + if (bsr_callback) { + RlcDebug("calling BSR callback - %d new_tx, %d priority bytes", n_bytes_new, n_bytes_prio); + bsr_callback(parent->lcid, n_bytes_new, n_bytes_prio); + } +} + +/* + * Check whether the polling bit needs to be set, as specified in + * TS 38.322, section 5.3.3.2 + */ +uint8_t rlc_am_nr_tx::get_pdu_poll(uint32_t sn, bool is_retx, uint32_t sdu_bytes) +{ + RlcDebug("Checking poll bit requirements for PDU. SN=%d, retx=%s, sdu_bytes=%d, POLL_SN=%d", + sn, + is_retx ? "true" : "false", + sdu_bytes, + st.poll_sn); + /* For each AMD PDU or AMD PDU segment that has not been previoulsy tranmitted: + * - increment PDU_WITHOUT_POLL by one; + * - increment BYTE_WITHOUT_POLL by every new byte of Data field element that it maps to the Data field of the AMD + * PDU; + * - if PDU_WITHOUT_POLL >= pollPDU; or + * - if BYTE_WITHOUT_POLL >= pollByte: + * - include a poll in the AMD PDU as described below. + */ + uint8_t poll = 0; + if (!is_retx) { + st.pdu_without_poll++; + st.byte_without_poll += sdu_bytes; + if (cfg.poll_pdu > 0 && st.pdu_without_poll >= (uint32_t)cfg.poll_pdu) { + poll = 1; + RlcDebug("Setting poll bit due to PollPDU. SN=%d, POLL_SN=%d", sn, st.poll_sn); + } + if (cfg.poll_byte > 0 && st.byte_without_poll >= (uint32_t)cfg.poll_byte) { + poll = 1; + RlcDebug("Setting poll bit due to PollBYTE. SN=%d, POLL_SN=%d", sn, st.poll_sn); + } + } + + /* + * - if both the transmission buffer and the retransmission buffer becomes empty + * (excluding transmitted RLC SDUs or RLC SDU segments awaiting acknowledgements) + * after the transmission of the AMD PDU; or + * - if no new RLC SDU can be transmitted after the transmission of the AMD PDU (e.g. due to window stalling); + * - include a poll in the AMD PDU as described below. + */ + if ((tx_sdu_queue.is_empty() && retx_queue.empty() && sdu_under_segmentation_sn == INVALID_RLC_SN) || + tx_window->full()) { + RlcDebug("Setting poll bit due to empty buffers/inablity to TX. SN=%d, POLL_SN=%d", sn, st.poll_sn); + poll = 1; + } + + /* + * - If poll bit is included: + * - set PDU_WITHOUT_POLL to 0; + * - set BYTE_WITHOUT_POLL to 0. + */ + if (poll == 1) { + st.pdu_without_poll = 0; + st.byte_without_poll = 0; + /* + * - set POLL_SN to the highest SN of the AMD PDU among the AMD PDUs submitted to lower layer; + * - if t-PollRetransmit is not running: + * - start t-PollRetransmit. + * - else: + * - restart t-PollRetransmit. + */ + if (!is_retx) { + // This is not an RETX, but a new transmission + // As such it should be the highest SN submitted to the lower layers + st.poll_sn = sn; + RlcDebug("Setting new POLL_SN. POLL_SN=%d", sn); + } + if (cfg.t_poll_retx > 0) { + if (not poll_retransmit_timer.is_running()) { + poll_retransmit_timer.run(); + } else { + poll_retransmit_timer.stop(); + poll_retransmit_timer.run(); + } + RlcInfo("Started t-PollRetransmit. POLL_SN=%d", st.poll_sn); + } + } + return poll; +} + +bool rlc_am_nr_tx::do_status() +{ + return rx->get_do_status(); +} + +void rlc_am_nr_tx::reestablish() +{ + stop(); +} + +void rlc_am_nr_tx::empty_queue() +{ + std::lock_guard lock(mutex); + empty_queue_no_lock(); +} + +void rlc_am_nr_tx::empty_queue_no_lock() +{ + // deallocate all SDUs in transmit queue + while (tx_sdu_queue.size() > 0) { + unique_byte_buffer_t buf = tx_sdu_queue.read(); + } +} + +void rlc_am_nr_tx::stop() +{ + std::lock_guard lock(mutex); + empty_queue_no_lock(); + + if (parent->timers != nullptr && poll_retransmit_timer.is_valid()) { + poll_retransmit_timer.stop(); + } + + st = {}; + + sdu_under_segmentation_sn = INVALID_RLC_SN; + + // Drop all messages in TX window + tx_window->clear(); + + // Drop all messages in RETX queue + retx_queue.clear(); + + tx_enabled = false; +} + +void rlc_am_nr_tx::timer_expired(uint32_t timeout_id) +{ + std::unique_lock lock(mutex); + + // t-PollRetransmit + if (poll_retransmit_timer.is_valid() && poll_retransmit_timer.id() == timeout_id) { + RlcDebug("Poll retransmission timer expired after %dms", poll_retransmit_timer.duration()); + debug_state(); + /* + * - if both the transmission buffer and the retransmission buffer are empty + * (excluding transmitted RLC SDU or RLC SDU segment awaiting acknowledgements); or + * - if no new RLC SDU or RLC SDU segment can be transmitted (e.g. due to window stalling): + * - consider the RLC SDU with the highest SN among the RLC SDUs submitted to lower layer for + * retransmission; or + * - consider any RLC SDU which has not been positively acknowledged for retransmission. + * - include a poll in an AMD PDU as described in section 5.3.3.2. + */ + if ((tx_sdu_queue.is_empty() && retx_queue.empty()) || tx_window->full()) { + if (tx_window->empty()) { + RlcError("t-PollRetransmit expired, but the tx_window is empty. POLL_SN=%d, Tx_Next_Ack=%d, tx_window_size=%d", + st.poll_sn, + st.tx_next_ack, + tx_window->size()); + return; + } + if (not tx_window->has_sn(st.tx_next_ack)) { + RlcError("t-PollRetransmit expired, but Tx_Next_Ack is not in the tx_widow. POLL_SN=%d, Tx_Next_Ack=%d, " + "tx_window_size=%d", + st.poll_sn, + st.tx_next_ack, + tx_window->size()); + return; + } + // RETX first RLC SDU that has not been ACKed + // or first SDU segment of the first RLC SDU + // that has not been acked + rlc_amd_retx_nr_t& retx = retx_queue.push(); + retx.sn = st.tx_next_ack; + if ((*tx_window)[st.tx_next_ack].segment_list.empty()) { + // Full SDU + retx.is_segment = false; + retx.so_start = 0; + retx.segment_length = (*tx_window)[st.tx_next_ack].sdu_buf->N_bytes; + retx.current_so = 0; + } else { + // To make sure we do not mess up the segment list + // We RETX an SDU segment instead of the full SDU + // if the SDU has been segmented before. + // As we cannot know which segments have been ACKed before + // we simply RETX the first one. + retx.is_segment = true; + retx.so_start = 0; + retx.current_so = 0; + retx.segment_length = (*tx_window)[st.tx_next_ack].segment_list.begin()->payload_len; + } + RlcDebug("Retransmission because of t-PollRetransmit. RETX SN=%d, is_segment=%s, so_start=%d, segment_length=%d", + retx.sn, + retx.is_segment ? "true" : "false", + retx.so_start, + retx.segment_length); + } + return; + } +} + +/* + * Window helpers + */ +uint32_t rlc_am_nr_tx::tx_mod_base_nr(uint32_t sn) const +{ + return (sn - st.tx_next_ack) % mod_nr; +} + +uint32_t rlc_am_nr_tx::tx_window_size() const +{ + return am_window_size(cfg.tx_sn_field_length); +} + +bool rlc_am_nr_tx::inside_tx_window(uint32_t sn) const +{ + // TX_Next_Ack <= SN < TX_Next_Ack + AM_Window_Size + return tx_mod_base_nr(sn) < tx_window_size(); +} + +/* + * This function is used to check if a received status report + * as a valid ACK_SN. + * + * ACK_SN may be equal to TX_NEXT + AM_Window_Size if the PDU + * with SN=TX_NEXT+AM_Window_Size has been received by the RX + * An ACK_SN == Tx_Next_Ack doesn't ACK or NACKs any PDUs, as + * such, such a status report can be discarded. + */ +bool rlc_am_nr_tx::valid_ack_sn(uint32_t sn) const +{ + // Tx_Next_Ack < SN <= TX_Next + AM_Window_Size + return (0 < tx_mod_base_nr(sn)) && (tx_mod_base_nr(sn) <= tx_window_size()); +} + +/* + * Debug Helpers + */ +void rlc_am_nr_tx::debug_state() const +{ + RlcDebug("TX entity state: Tx_Next_Ack=%d, Tx_Next=%d, POLL_SN=%d, PDU_WITHOUT_POLL=%d, BYTE_WITHOUT_POLL=%d", + st.tx_next_ack, + st.tx_next, + st.poll_sn, + st.pdu_without_poll, + st.byte_without_poll); +} + +void rlc_am_nr_tx::info_state() const +{ + RlcInfo("TX window state: SDUs %d", tx_window->size()); + RlcInfo("TX entity state: Tx_Next_Ack=%d, Tx_Next=%d, POLL_SN=%d, PDU_WITHOUT_POLL=%d, BYTE_WITHOUT_POLL=%d", + st.tx_next_ack, + st.tx_next, + st.poll_sn, + st.pdu_without_poll, + st.byte_without_poll); +} + +void rlc_am_nr_tx::debug_window() const +{ + RlcDebug("TX window state: Tx_Next_Ack=%d, Tx_Next=%d, SDUs=%d", st.tx_next_ack, st.tx_next, tx_window->size()); +} +/**************************************************************************** + * Rx subclass implementation + ***************************************************************************/ +rlc_am_nr_rx::rlc_am_nr_rx(rlc_am* parent_) : + parent(parent_), + pool(byte_buffer_pool::get_instance()), + status_prohibit_timer(parent->timers->get_unique_timer()), + reassembly_timer(parent->timers->get_unique_timer()), + rlc_am_base_rx(parent_, parent_->logger) +{} + +bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_) +{ + cfg = cfg_.am_nr; + rb_name = parent->rb_name; + // Configure status prohibit timer + if (cfg.t_status_prohibit > 0) { + status_prohibit_timer.set(static_cast(cfg.t_status_prohibit), + [this](uint32_t timerid) { timer_expired(timerid); }); + } + + // Configure t_reassembly timer + if (cfg.t_reassembly > 0) { + reassembly_timer.set(static_cast(cfg.t_reassembly), [this](uint32_t timerid) { timer_expired(timerid); }); + } + + mod_nr = cardinality(cfg.rx_sn_field_length); + switch (cfg.rx_sn_field_length) { + case rlc_am_nr_sn_size_t::size12bits: + rx_window = std::unique_ptr >( + new rlc_ringbuffer_t); + break; + case rlc_am_nr_sn_size_t::size18bits: + rx_window = std::unique_ptr >( + new rlc_ringbuffer_t); + break; + default: + RlcError("attempt to configure unsupported rx_sn_field_length %s", to_string(cfg.rx_sn_field_length)); + return false; + } + + RlcDebug("RLC AM NR configured rx entity."); + + return true; +} + +void rlc_am_nr_rx::stop() +{ + std::lock_guard lock(mutex); + + if (parent->timers != nullptr && reassembly_timer.is_valid()) { + reassembly_timer.stop(); + } + + if (parent->timers != nullptr && status_prohibit_timer.is_valid()) { + status_prohibit_timer.stop(); + } + + st = {}; + + do_status = false; + + // Drop all messages in RX window + rx_window->clear(); +} + +void rlc_am_nr_rx::reestablish() +{ + stop(); +} + +void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) +{ + std::lock_guard lock(mutex); + + // Get AMD PDU Header + rlc_am_nr_pdu_header_t header = {}; + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header); + + RlcHexInfo(payload, nof_bytes, "Rx data PDU SN=%d (%d B)", header.sn, nof_bytes); + log_rlc_am_nr_pdu_header_to_string(logger.debug, header, rb_name); + + // Trigger polling if poll bit is set. + // We do this before checking if the PDU is inside the RX window, + // as the RX window may have advanced without the TX having received the ACKs + // This can cause a data stall, whereby the TX keeps retransmiting + // a PDU outside of the Rx window. + // Also, we do this before discarding duplicate SDUs/SDU segments + // Because t-PollRetransmit may transmit a PDU that was already + // received. + if (header.p != 0U) { + RlcInfo("status packet requested through polling bit"); + do_status = true; + } + + // Check whether SDU is within Rx Window + if (!inside_rx_window(header.sn)) { + RlcInfo("SN=%d outside rx window [%d:%d] - discarding", header.sn, st.rx_next, st.rx_next + rx_window_size()); + return; + } + + // Section 5.2.3.2.2, discard duplicate PDUs + if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) { + RlcInfo("discarding duplicate SN=%d", header.sn); + return; + } + + // Section 5.2.3.2.2, discard segments with overlapping bytes + if (rx_window->has_sn(header.sn) && header.si != rlc_nr_si_field_t::full_sdu) { + for (const auto& segm : (*rx_window)[header.sn].segments) { + uint32_t segm_last_byte = segm.header.so + segm.buf->N_bytes - 1; + uint32_t pdu_last_byte = header.so + nof_bytes - hdr_len - 1; + if ((header.so >= segm.header.so && header.so <= segm_last_byte) || + (pdu_last_byte >= segm.header.so && pdu_last_byte <= segm_last_byte)) { + RlcInfo("Got SDU segment with duplicate bytes. Discarding."); + RlcInfo("Discarded SDU segment. SN=%d, SO=%d, last_byte=%d, payload=%d", + header.sn, + header.so, + header.so + (nof_bytes - hdr_len), + (nof_bytes - hdr_len)); + RlcInfo("Overlaping with SDU segment with SN=%d, SO=%d, last_byte=%d, payload=%d", + header.sn, + segm.header.so, + segm_last_byte, + segm.buf->N_bytes); + return; + } + } + } + + // Write to rx window either full SDU or SDU segment + if (header.si == rlc_nr_si_field_t::full_sdu) { + int err = handle_full_data_sdu(header, payload, nof_bytes); + if (err != SRSRAN_SUCCESS) { + return; + } + } else { + int err = handle_segment_data_sdu(header, payload, nof_bytes); + if (err != SRSRAN_SUCCESS) { + return; + } + } + + debug_state(); + + // 5.2.3.2.3 Actions when an AMD PDU is placed in the reception buffer + /* + * - if x >= RX_Next_Highest + * - update RX_Next_Highest to x+ 1. + */ + if (rx_mod_base_nr(header.sn) >= rx_mod_base_nr(st.rx_next_highest)) { + st.rx_next_highest = (header.sn + 1) % mod_nr; + } + + /* + * - if all bytes of the RLC SDU with SN = x are received: + */ + if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) { + /* + * - reassemble the RLC SDU from AMD PDU(s) with SN = x, remove RLC headers when doing so and deliver + * the reassembled RLC SDU to upper layer; + */ + write_to_upper_layers(parent->lcid, std::move((*rx_window)[header.sn].buf)); + + /* + * - if x = RX_Highest_Status, + * - update RX_Highest_Status to the SN of the first RLC SDU with SN > current RX_Highest_Status for which not + * all bytes have been received. + */ + if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_highest_status)) { + uint32_t sn_upd = 0; + for (sn_upd = (st.rx_highest_status + 1) % mod_nr; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); + sn_upd = (sn_upd + 1) % mod_nr) { + if (rx_window->has_sn(sn_upd)) { + if (not(*rx_window)[sn_upd].fully_received) { + break; // first SDU not fully received + } + } else { + break; // first SDU not fully received + } + } + // Update to the SN of the first SDU with missing bytes. + // If it not exists, update to the end of the rx_window. + st.rx_highest_status = sn_upd; + } + /* + * - if x = RX_Next: + * - update RX_Next to the SN of the first RLC SDU with SN > current RX_Next for which not all bytes + * have been received. + */ + if (rx_mod_base_nr(header.sn) == rx_mod_base_nr(st.rx_next)) { + uint32_t sn_upd = 0; + // move rx_next forward and remove all fully received SDUs from rx_window + for (sn_upd = (st.rx_next) % mod_nr; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); + sn_upd = (sn_upd + 1) % mod_nr) { + if (rx_window->has_sn(sn_upd)) { + if (not(*rx_window)[sn_upd].fully_received) { + break; // first SDU not fully received + } + // RX_Next serves as the lower edge of the receiving window + // As such, we remove any SDU from the window if we update this value + rx_window->remove_pdu(sn_upd); + } else { + break; // first SDU not fully received + } + } + // Update to the SN of the first SDU with missing bytes. + // If it not exists, update to the end of the rx_window. + st.rx_next = sn_upd; + } + } + + if (reassembly_timer.is_running()) { + // if t-Reassembly is running: + /* + * - if RX_Next_Status_Trigger = RX_Next; or + * - if RX_Next_Status_Trigger = RX_Next + 1 and there is no missing byte segment of the SDU associated with + * SN = RX_Next before the last byte of all received segments of this SDU; or + * - if RX_Next_Status_Trigger falls outside of the receiving window and RX_Next_Status_Trigger is not equal + * to RX_Next + AM_Window_Size: + * - stop and reset t-Reassembly. + */ + bool stop_reassembly_timer = false; + if (st.rx_next_status_trigger == st.rx_next) { + stop_reassembly_timer = true; + } + if (rx_mod_base_nr(st.rx_next_status_trigger) == rx_mod_base_nr(st.rx_next + 1)) { + if (not(*rx_window)[st.rx_next].has_gap) { + stop_reassembly_timer = true; + } + } + if (not inside_rx_window(st.rx_next_status_trigger)) { + stop_reassembly_timer = true; + } + if (stop_reassembly_timer) { + reassembly_timer.stop(); + } + } + + if (not reassembly_timer.is_running()) { + // if t-Reassembly is not running (includes the case t-Reassembly is stopped due to actions above): + /* + * - if RX_Next_Highest> RX_Next +1; or + * - if RX_Next_Highest = RX_Next + 1 and there is at least one missing byte segment of the SDU associated + * with SN = RX_Next before the last byte of all received segments of this SDU: + * - start t-Reassembly; + * - set RX_Next_Status_Trigger to RX_Next_Highest. + */ + bool restart_reassembly_timer = false; + if (rx_mod_base_nr(st.rx_next_highest) > rx_mod_base_nr(st.rx_next + 1)) { + restart_reassembly_timer = true; + } + if (rx_mod_base_nr(st.rx_next_highest) == rx_mod_base_nr(st.rx_next + 1)) { + if (rx_window->has_sn(st.rx_next) && (*rx_window)[st.rx_next].has_gap) { + restart_reassembly_timer = true; + } + } + if (restart_reassembly_timer) { + reassembly_timer.run(); + st.rx_next_status_trigger = st.rx_next_highest; + } + } +} + +/* + * SDU handling helpers + */ +int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, const uint8_t* payload, uint32_t nof_bytes) +{ + uint32_t hdr_len = rlc_am_nr_packed_length(header); + // Full SDU received. Add SDU to Rx Window and copy full PDU into SDU buffer. + rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window->add_pdu(header.sn); + rx_sdu.buf = srsran::make_byte_buffer(); + if (rx_sdu.buf == nullptr) { + RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__); + rx_window->remove_pdu(header.sn); + return SRSRAN_ERROR; + } + rx_sdu.buf->set_timestamp(); + + // check available space for payload + if (nof_bytes > rx_sdu.buf->get_tailroom()) { + RlcError("discarding SN=%d of size %d B (available space %d B)", header.sn, nof_bytes, rx_sdu.buf->get_tailroom()); + rx_window->remove_pdu(header.sn); + return SRSRAN_ERROR; + } + memcpy(rx_sdu.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header + rx_sdu.buf->N_bytes = nof_bytes - hdr_len; + rx_sdu.fully_received = true; + rx_sdu.has_gap = false; + return SRSRAN_SUCCESS; +} + +int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header, + const uint8_t* payload, + uint32_t nof_bytes) +{ + if (header.si == rlc_nr_si_field_t::full_sdu) { + RlcError("called %s but the SI implies a full SDU. SN=%d", __FUNCTION__, header.sn); + return SRSRAN_ERROR; + } + + uint32_t hdr_len = rlc_am_nr_packed_length(header); + + // Log SDU segment reception + if (header.si == rlc_nr_si_field_t::first_segment) { // Check whether it's a full SDU + RlcDebug("Initial segment PDU. SN=%d.", header.sn); + } else if (header.si == rlc_nr_si_field_t::neither_first_nor_last_segment) { + RlcDebug("Middle segment PDU. SN=%d.", header.sn); + } else if (header.si == rlc_nr_si_field_t::last_segment) { + RlcDebug("Final segment PDU. SN=%d.", header.sn); + } + + // Add a new SDU to the RX window if necessary + rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window->has_sn(header.sn) ? (*rx_window)[header.sn] : rx_window->add_pdu(header.sn); + + // Create PDU segment info, to be stored later + rlc_amd_rx_pdu_nr pdu_segment = {}; + pdu_segment.header = header; + pdu_segment.buf = srsran::make_byte_buffer(); + if (pdu_segment.buf == nullptr) { + RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__); + return SRSRAN_ERROR; + } + memcpy(pdu_segment.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header + pdu_segment.buf->N_bytes = nof_bytes - hdr_len; + + // Store SDU segment. Sort by SO and check for duplicate bytes. + insert_received_segment(std::move(pdu_segment), rx_sdu.segments); + + // Check weather all segments have been received + update_segment_inventory(rx_sdu); + if (rx_sdu.fully_received) { + RlcInfo("Fully received segmented SDU. SN=%d.", header.sn); + rx_sdu.buf = srsran::make_byte_buffer(); + if (rx_sdu.buf == nullptr) { + RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__); + rx_window->remove_pdu(header.sn); + return SRSRAN_ERROR; + } + // Assemble SDU from segments + for (const auto& it : rx_sdu.segments) { + memcpy(&rx_sdu.buf->msg[rx_sdu.buf->N_bytes], it.buf->msg, it.buf->N_bytes); + rx_sdu.buf->N_bytes += it.buf->N_bytes; + } + } + return SRSRAN_SUCCESS; +} + +/* + * Status PDU + */ +uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t max_len) +{ + std::unique_lock lock(mutex, std::try_to_lock); + if (not lock.owns_lock()) { + return SRSRAN_ERROR; + } + + status->reset(); + + /* + * - for the RLC SDUs with SN such that RX_Next <= SN < RX_Highest_Status that has not been completely + * received yet, in increasing SN order of RLC SDUs and increasing byte segment order within RLC SDUs, + * starting with SN = RX_Next up to the point where the resulting STATUS PDU still fits to the total size of RLC + * PDU(s) indicated by lower layer: + */ + RlcDebug("Generating status PDU"); + for (uint32_t i = st.rx_next; rx_mod_base_nr(i) < rx_mod_base_nr(st.rx_highest_status); i = (i + 1) % mod_nr) { + if ((rx_window->has_sn(i) && (*rx_window)[i].fully_received)) { + RlcDebug("SDU SN=%d is fully received", i); + } else { + if (not rx_window->has_sn(i)) { + // No segment received, NACK the whole SDU + RlcDebug("Adding NACK for full SDU. NACK SN=%d", i); + rlc_status_nack_t nack; + nack.nack_sn = i; + nack.has_so = false; + status->push_nack(nack); + } else if (not(*rx_window)[i].fully_received) { + // Some segments were received, but not all. + // NACK non consecutive missing bytes + RlcDebug("Adding NACKs for segmented SDU. NACK SN=%d", i); + uint32_t last_so = 0; + bool last_segment_rx = false; + for (auto segm = (*rx_window)[i].segments.begin(); segm != (*rx_window)[i].segments.end(); segm++) { + if (segm->header.so != last_so) { + // Some bytes were not received + rlc_status_nack_t nack; + nack.nack_sn = i; + nack.has_so = true; + nack.so_start = last_so; + nack.so_end = segm->header.so - 1; // set to last missing byte + status->push_nack(nack); + if (nack.so_start > nack.so_end) { + // Print segment list + for (auto segm_it = (*rx_window)[i].segments.begin(); segm_it != (*rx_window)[i].segments.end(); + segm_it++) { + RlcError("Segment: segm.header.so=%d, segm.buf.N_bytes=%d", segm_it->header.so, segm_it->buf->N_bytes); + } + RlcError("Error: SO_start=%d > SO_end=%d. NACK_SN=%d. SO_start=%d, SO_end=%d, seg.so=%d", + nack.so_start, + nack.so_end, + nack.nack_sn, + nack.so_start, + nack.so_end, + segm->header.so); + srsran_assert(nack.so_start <= nack.so_end, + "Error: SO_start=%d > SO_end=%d. NACK_SN=%d", + nack.so_start, + nack.so_end, + nack.nack_sn); + } else { + RlcDebug("First/middle segment missing. NACK_SN=%d. SO_start=%d, SO_end=%d", + nack.nack_sn, + nack.so_start, + nack.so_end); + } + } + if (segm->header.si == rlc_nr_si_field_t::last_segment) { + last_segment_rx = true; + } + last_so = segm->header.so + segm->buf->N_bytes; + } // Segment loop + if (not last_segment_rx) { + rlc_status_nack_t nack; + nack.nack_sn = i; + nack.has_so = true; + nack.so_start = last_so; + nack.so_end = rlc_status_nack_t::so_end_of_sdu; + status->push_nack(nack); + RlcDebug( + "Final segment missing. NACK_SN=%d. SO_start=%d, SO_end=%d", nack.nack_sn, nack.so_start, nack.so_end); + srsran_assert(nack.so_start <= nack.so_end, "Error: SO_start > SO_end. NACK_SN=%d", nack.nack_sn); + } + } + } + } // NACK loop + + /* + * - set the ACK_SN to the SN of the next not received RLC SDU which is not + * indicated as missing in the resulting STATUS PDU. + */ + status->ack_sn = st.rx_highest_status; + + // trim PDU if necessary + if (status->packed_size > max_len) { + RlcInfo("Trimming status PDU with %d NACKs and packed_size=%d into max_len=%d", + status->nacks.size(), + status->packed_size, + max_len); + log_rlc_am_nr_status_pdu_to_string(logger.debug, "Untrimmed status PDU - %s", status, rb_name); + if (not status->trim(max_len)) { + RlcError("Failed to trim status PDU into provided space: max_len=%d", max_len); + } + } + + if (max_len != UINT32_MAX) { + // UINT32_MAX is used just to query the status PDU length + if (status_prohibit_timer.is_valid()) { + status_prohibit_timer.run(); + } + do_status = false; + } + + return status->packed_size; +} + +uint32_t rlc_am_nr_rx::get_status_pdu_length() +{ + rlc_am_nr_status_pdu_t tmp_status(cfg.rx_sn_field_length); + get_status_pdu(&tmp_status, UINT32_MAX); + return tmp_status.get_packed_size(); +} + +bool rlc_am_nr_rx::get_do_status() +{ + return do_status.load(std::memory_order_relaxed) && not status_prohibit_timer.is_running(); +} + +void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) +{ + std::unique_lock lock(mutex); + + // Status Prohibit + if (status_prohibit_timer.is_valid() && status_prohibit_timer.id() == timeout_id) { + RlcDebug("Status prohibit timer expired after %dms", status_prohibit_timer.duration()); + return; + } + + // Reassembly + if (reassembly_timer.is_valid() && reassembly_timer.id() == timeout_id) { + RlcDebug("Reassembly timer expired after %dms", reassembly_timer.duration()); + /* + * 5.2.3.2.4 Actions when t-Reassembly expires: + * - update RX_Highest_Status to the SN of the first RLC SDU with SN >= RX_Next_Status_Trigger for which not + * all bytes have been received; + * - if RX_Next_Highest> RX_Highest_Status +1: or + * - if RX_Next_Highest = RX_Highest_Status + 1 and there is at least one missing byte segment of the SDU + * associated with SN = RX_Highest_Status before the last byte of all received segments of this SDU: + * - start t-Reassembly; + * - set RX_Next_Status_Trigger to RX_Next_Highest. + */ + uint32_t sn_upd = {}; + for (sn_upd = st.rx_next_status_trigger; rx_mod_base_nr(sn_upd) < rx_mod_base_nr(st.rx_next_highest); + sn_upd = (sn_upd + 1) % mod_nr) { + if (not rx_window->has_sn(sn_upd) || (rx_window->has_sn(sn_upd) && not(*rx_window)[sn_upd].fully_received)) { + break; + } + } + st.rx_highest_status = sn_upd; + if (not valid_ack_sn(st.rx_highest_status)) { + RlcError("Rx_Highest_Status not inside RX window"); + debug_state(); + } + srsran_assert(valid_ack_sn(st.rx_highest_status), "Error: rx_highest_status assigned outside rx window"); + + bool restart_reassembly_timer = false; + if (rx_mod_base_nr(st.rx_next_highest) > rx_mod_base_nr(st.rx_highest_status + 1)) { + restart_reassembly_timer = true; + } + if (rx_mod_base_nr(st.rx_next_highest) == rx_mod_base_nr(st.rx_highest_status + 1)) { + if (rx_window->has_sn(st.rx_highest_status) && (*rx_window)[st.rx_highest_status].has_gap) { + restart_reassembly_timer = true; + } + } + if (restart_reassembly_timer) { + reassembly_timer.run(); + st.rx_next_status_trigger = st.rx_next_highest; + } + + /* 5.3.4 Status reporting: + * - The receiving side of an AM RLC entity shall trigger a STATUS report when t-Reassembly expires. + * NOTE 2: The expiry of t-Reassembly triggers both RX_Highest_Status to be updated and a STATUS report to be + * triggered, but the STATUS report shall be triggered after RX_Highest_Status is updated. + */ + do_status = true; + debug_state(); + debug_window(); + return; + } +} + +void rlc_am_nr_rx::write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu) +{ + uint32_t nof_bytes = sdu->N_bytes; + parent->pdcp->write_pdu(lcid, std::move(sdu)); + std::lock_guard lock(parent->metrics_mutex); + parent->metrics.num_rx_sdus++; + parent->metrics.num_rx_sdu_bytes += nof_bytes; +} + +/* + * Segment Helpers + */ +void rlc_am_nr_rx::insert_received_segment(rlc_amd_rx_pdu_nr segment, + std::set& segment_list) const +{ + segment_list.insert(std::move(segment)); +} + +void rlc_am_nr_rx::update_segment_inventory(rlc_amd_rx_sdu_nr_t& rx_sdu) const +{ + if (rx_sdu.segments.empty()) { + rx_sdu.fully_received = false; + rx_sdu.has_gap = false; + return; + } + + // Check for gaps and if all segments have been received + uint32_t next_byte = 0; + for (const auto& it : rx_sdu.segments) { + if (it.header.so != next_byte) { + // Found gap: set flags and return + rx_sdu.has_gap = true; + rx_sdu.fully_received = false; + return; + } + if (it.header.si == rlc_nr_si_field_t::last_segment) { + // Reached last segment without any gaps: set flags and return + rx_sdu.has_gap = false; + rx_sdu.fully_received = true; + return; + } + next_byte += it.buf->N_bytes; + } + // No gaps, but last segment not yet received + rx_sdu.has_gap = false; + rx_sdu.fully_received = false; +} + +/* + * Window Helpers + */ +uint32_t rlc_am_nr_rx::rx_mod_base_nr(uint32_t sn) const +{ + return (sn - st.rx_next) % mod_nr; +} + +uint32_t rlc_am_nr_rx::rx_window_size() const +{ + return am_window_size(cfg.rx_sn_field_length); +} + +bool rlc_am_nr_rx::inside_rx_window(uint32_t sn) const +{ + // RX_Next <= SN < RX_Next + AM_Window_Size + return rx_mod_base_nr(sn) < rx_window_size(); +} + +/* + * This function is used to check if the Rx_Highest_Status is + * valid when t-Reasseambly expires. + * + * ACK_SN may be equal to RX_NEXT + AM_Window_Size if the PDU + * with SN=RX_NEXT+AM_Window_Size has been received by the RX. + * An ACK_SN == Rx_Next should not update Rx_Highest_Status, + * it should be updated when Rx_Next is updated. + */ +bool rlc_am_nr_rx::valid_ack_sn(uint32_t sn) const +{ + // RX_Next < SN <= RX_Next + AM_Window_Size + return (0 < rx_mod_base_nr(sn)) && (rx_mod_base_nr(sn) <= rx_window_size()); +} + +/* + * Debug Helpers + */ +void rlc_am_nr_rx::debug_state() const +{ + RlcDebug("RX entity state: Rx_Next=%d, Rx_Next_Status_Trigger=%d, Rx_Highest_Status=%d, Rx_Next_Highest=%d", + st.rx_next, + st.rx_next_status_trigger, + st.rx_highest_status, + st.rx_next_highest); +} + +void rlc_am_nr_rx::debug_window() const +{ + RlcDebug( + "RX window state: Rx_Next=%d, Rx_Next_Highest=%d, SDUs %d", st.rx_next, st.rx_next_highest, rx_window->size()); +} + +/* + * Metrics + */ +uint32_t rlc_am_nr_rx::get_sdu_rx_latency_ms() +{ + return 0; +} + +uint32_t rlc_am_nr_rx::get_rx_buffered_bytes() +{ + return 0; +} +} // namespace srsran diff --git a/lib/src/rlc/rlc_am_nr_packing.cc b/lib/src/rlc/rlc_am_nr_packing.cc new file mode 100644 index 000000000..9f0594b59 --- /dev/null +++ b/lib/src/rlc/rlc_am_nr_packing.cc @@ -0,0 +1,627 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/rlc/rlc_am_nr_packing.h" +#include + +namespace srsran { + +/**************************************************************************** + * Container implementation for pack/unpack functions + ***************************************************************************/ + +rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : + sn_size(sn_size), mod_nr(cardinality(sn_size)) +{ + nacks_.reserve(RLC_AM_NR_TYP_NACKS); +} + +void rlc_am_nr_status_pdu_t::reset() +{ + cpt = rlc_am_nr_control_pdu_type_t::status_pdu; + ack_sn = INVALID_RLC_SN; + nacks_.clear(); + packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn; +} + +bool rlc_am_nr_status_pdu_t::is_continuous_sequence(const rlc_status_nack_t& left, const rlc_status_nack_t& right) const +{ + // SN must be continuous + if (right.nack_sn != ((left.has_nack_range ? left.nack_sn + left.nack_range : (left.nack_sn + 1)) % mod_nr)) { + return false; + } + + // Segments on left side (if present) must reach the end of sdu + if (left.has_so && left.so_end != rlc_status_nack_t::so_end_of_sdu) { + return false; + } + + // Segments on right side (if present) must start from the beginning + if (right.has_so && right.so_start != 0) { + return false; + } + + return true; +} + +void rlc_am_nr_status_pdu_t::push_nack(const rlc_status_nack_t& nack) +{ + if (nacks_.size() == 0) { + nacks_.push_back(nack); + packed_size_ += nack_size(nack); + return; + } + + rlc_status_nack_t& prev = nacks_.back(); + if (is_continuous_sequence(prev, nack) == false) { + nacks_.push_back(nack); + packed_size_ += nack_size(nack); + return; + } + + // expand previous NACK + // subtract size of previous NACK (add updated size later) + packed_size_ -= nack_size(prev); + + // enable and update NACK range + if (nack.has_nack_range == true) { + if (prev.has_nack_range == true) { + // [NACK range][NACK range] + prev.nack_range += nack.nack_range; + } else { + // [NACK SDU][NACK range] + prev.nack_range = nack.nack_range + 1; + prev.has_nack_range = true; + } + } else { + if (prev.has_nack_range == true) { + // [NACK range][NACK SDU] + prev.nack_range++; + } else { + // [NACK SDU][NACK SDU] + prev.nack_range = 2; + prev.has_nack_range = true; + } + } + + // enable and update segment offsets (if required) + if (nack.has_so == true) { + if (prev.has_so == false) { + // [NACK SDU][NACK segm] + prev.has_so = true; + prev.so_start = 0; + } + // [NACK SDU][NACK segm] or [NACK segm][NACK segm] + prev.so_end = nack.so_end; + } else { + if (prev.has_so == true) { + // [NACK segm][NACK SDU] + prev.so_end = rlc_status_nack_t::so_end_of_sdu; + } + // [NACK segm][NACK SDU] or [NACK SDU][NACK SDU] + } + + // add updated size + packed_size_ += nack_size(prev); +} + +bool rlc_am_nr_status_pdu_t::trim(uint32_t max_packed_size) +{ + if (max_packed_size >= packed_size_) { + // no trimming required + return true; + } + if (max_packed_size < rlc_am_nr_status_pdu_sizeof_header_ack_sn) { + // too little space for smallest possible status PDU (only header + ACK). + return false; + } + + // remove NACKs (starting from the back) until it fits into given space + // note: when removing a NACK for a segment, we have to remove all other NACKs with the same SN as well, + // see TS 38.322 Sec. 5.3.4: + // "set the ACK_SN to the SN of the next not received RLC SDU + // which is not indicated as missing in the resulting STATUS PDU." + while (nacks_.size() > 0 && (max_packed_size < packed_size_ || nacks_.back().nack_sn == ack_sn)) { + packed_size_ -= nack_size(nacks_.back()); + ack_sn = nacks_.back().nack_sn; + nacks_.pop_back(); + } + return true; +} + +void rlc_am_nr_status_pdu_t::refresh_packed_size() +{ + packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn; + for (auto nack : nacks_) { + packed_size_ += nack_size(nack); + } +} + +uint32_t rlc_am_nr_status_pdu_t::nack_size(const rlc_status_nack_t& nack) const +{ + uint32_t result = sn_size == rlc_am_nr_sn_size_t::size12bits ? rlc_am_nr_status_pdu_sizeof_nack_sn_ext_12bit_sn + : rlc_am_nr_status_pdu_sizeof_nack_sn_ext_18bit_sn; + if (nack.has_so) { + result += rlc_am_nr_status_pdu_sizeof_nack_so; + } + if (nack.has_nack_range) { + result += rlc_am_nr_status_pdu_sizeof_nack_range; + } + return result; +} + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4 + ***************************************************************************/ + +uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_pdu_header_t* header) +{ + return rlc_am_nr_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header); +} + +uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload, + const uint32_t nof_bytes, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_pdu_header_t* header) +{ + uint8_t* ptr = const_cast(payload); + + header->sn_size = sn_size; + + // Fixed part + header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01); // 1 bit D/C field + header->p = (*ptr >> 6) & 0x01; // 1 bit P flag + header->si = (rlc_nr_si_field_t)((*ptr >> 4) & 0x03); // 2 bits SI + + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + header->sn = (*ptr & 0x0F) << 8; // first 4 bits SN + ptr++; + + header->sn |= (*ptr & 0xFF); // last 8 bits SN + ptr++; + } else if (sn_size == rlc_am_nr_sn_size_t::size18bits) { + // sanity check + if ((*ptr & 0x0c) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + header->sn = (*ptr & 0x03) << 16; // first 4 bits SN + ptr++; + header->sn |= (*ptr & 0xFF) << 8; // bit 2-10 of SN + ptr++; + header->sn |= (*ptr & 0xFF); // last 8 bits SN + ptr++; + } else { + fprintf(stderr, "Unsupported SN length\n"); + return 0; + } + + // Read optional part + if (header->si == rlc_nr_si_field_t::last_segment || + header->si == rlc_nr_si_field_t::neither_first_nor_last_segment) { + // read SO + header->so = (*ptr & 0xFF) << 8; + ptr++; + header->so |= (*ptr & 0xFF); + ptr++; + } + + // return consumed bytes + return (ptr - payload); +} + +uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header) +{ + uint32_t len = 0; + if (header.si == rlc_nr_si_field_t::full_sdu || header.si == rlc_nr_si_field_t::first_segment) { + len = 2; + if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) { + len++; + } + } else { + // PDU contains SO + len = 4; + if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) { + len++; + } + } + return len; +} + +uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, uint8_t* payload) +{ + uint8_t* ptr = payload; + + // fixed header part + *ptr = (header.dc & 0x01) << 7; ///< 1 bit D/C field + *ptr |= (header.p & 0x01) << 6; ///< 1 bit P flag + *ptr |= (header.si & 0x03) << 4; ///< 2 bits SI + + if (header.sn_size == rlc_am_nr_sn_size_t::size12bits) { + // write first 4 bit of SN + *ptr |= (header.sn >> 8) & 0x0f; // 4 bit SN + ptr++; + *ptr = header.sn & 0xff; // remaining 8 bit of SN + ptr++; + } else { + // 18bit SN + *ptr |= (header.sn >> 16) & 0x3; // 2 bit SN + ptr++; + *ptr = header.sn >> 8; // bit 3 - 10 of SN + ptr++; + *ptr = (header.sn & 0xff); // remaining 8 bit of SN + ptr++; + } + + if (header.so) { + // write SO + *ptr = header.so >> 8; // first part of SO + ptr++; + *ptr = (header.so & 0xff); // second part of SO + ptr++; + } + return rlc_am_nr_packed_length(header); +} + +uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu) +{ + // Make room for the header + uint32_t len = rlc_am_nr_packed_length(header); + pdu->msg -= len; + pdu->N_bytes += len; + rlc_am_nr_write_data_pdu_header(header, pdu->msg); + return len; +} + +/**************************************************************************** + * Status PDU pack/unpack helper functions + * Ref: 3GPP TS 38.322 v16.2.0 Section 6.2.2.5 + ***************************************************************************/ + +uint32_t +rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status) +{ + return rlc_am_nr_read_status_pdu(pdu->msg, pdu->N_bytes, sn_size, status); +} + +uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, + const uint32_t nof_bytes, + const rlc_am_nr_sn_size_t sn_size, + rlc_am_nr_status_pdu_t* status) +{ + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + return rlc_am_nr_read_status_pdu_12bit_sn(payload, nof_bytes, status); + } else { // 18bit SN + return rlc_am_nr_read_status_pdu_18bit_sn(payload, nof_bytes, status); + } +} + +uint32_t +rlc_am_nr_read_status_pdu_12bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status) +{ + uint8_t* ptr = const_cast(payload); + status->reset(); + + // fixed part + status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT + + // sanity check + if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + + status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN + ptr++; + + status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN + ptr++; + + // read E1 flag + uint8_t e1 = *ptr & 0x80; + + // sanity check for reserved bits + if ((*ptr & 0x7f) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + + // all good, continue with next byte depending on E1 + ptr++; + + while (e1 != 0) { + // check buffer headroom + if (uint32_t(ptr - payload) >= nof_bytes) { + fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); + return 0; + } + + // E1 flag set, read a NACK_SN + rlc_status_nack_t nack = {}; + nack.nack_sn = (*ptr & 0xff) << 4; + ptr++; + + e1 = *ptr & 0x08; // 1 = further NACKs follow + uint8_t e2 = *ptr & 0x04; // 1 = set of {so_start, so_end} follows + uint8_t e3 = *ptr & 0x02; // 1 = NACK range follows (i.e. NACK across multiple SNs) + + // sanity check for reserved bits + if ((*ptr & 0x01) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + nack.nack_sn |= (*ptr & 0xF0) >> 4; + + ptr++; + if (e2 != 0) { + nack.has_so = true; + nack.so_start = (*ptr) << 8; + ptr++; + nack.so_start |= (*ptr); + ptr++; + nack.so_end = (*ptr) << 8; + ptr++; + nack.so_end |= (*ptr); + ptr++; + } + if (e3 != 0) { + nack.has_nack_range = true; + nack.nack_range = (*ptr); + ptr++; + } + status->push_nack(nack); + } + + return SRSRAN_SUCCESS; +} + +uint32_t +rlc_am_nr_read_status_pdu_18bit_sn(const uint8_t* payload, const uint32_t nof_bytes, rlc_am_nr_status_pdu_t* status) +{ + uint8_t* ptr = const_cast(payload); + status->reset(); + + // fixed part + status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT + + // sanity check + if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + + status->ack_sn = (*ptr & 0x0F) << 14; // upper 4 bits of SN + ptr++; + + status->ack_sn |= (*ptr & 0xFF) << 6; // center 8 bits of SN + ptr++; + + status->ack_sn |= (*ptr & 0xFC) >> 2; // lower 6 bits of SN + + // read E1 flag + uint8_t e1 = *ptr & 0x02; + + // sanity check for reserved bits + if ((*ptr & 0x01) != 0) { + fprintf(stderr, "Malformed PDU, reserved bit is set.\n"); + return 0; + } + + // all good, continue with next byte depending on E1 + ptr++; + + while (e1 != 0) { + // check buffer headroom + if (uint32_t(ptr - payload) >= nof_bytes) { + fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n"); + return 0; + } + + // E1 flag set, read a NACK_SN + rlc_status_nack_t nack = {}; + + nack.nack_sn = (*ptr & 0xFF) << 10; // upper 8 bits of SN + ptr++; + nack.nack_sn |= (*ptr & 0xFF) << 2; // center 8 bits of SN + ptr++; + nack.nack_sn |= (*ptr & 0xC0) >> 6; // lower 2 bits of SN + + e1 = *ptr & 0x20; // 1 = further NACKs follow + uint8_t e2 = *ptr & 0x10; // 1 = set of {so_start, so_end} follows + uint8_t e3 = *ptr & 0x08; // 1 = NACK range follows (i.e. NACK across multiple SNs) + + // sanity check for reserved bits + if ((*ptr & 0x07) != 0) { + fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); + return 0; + } + + ptr++; + if (e2 != 0) { + nack.has_so = true; + nack.so_start = (*ptr) << 8; + ptr++; + nack.so_start |= (*ptr); + ptr++; + nack.so_end = (*ptr) << 8; + ptr++; + nack.so_end |= (*ptr); + ptr++; + } + if (e3 != 0) { + nack.has_nack_range = true; + nack.nack_range = (*ptr); + ptr++; + } + status->push_nack(nack); + } + + return SRSRAN_SUCCESS; +} + +/** + * Write a RLC AM NR status PDU to a PDU buffer and eets the length of the generate PDU accordingly + * @param status_pdu The status PDU + * @param pdu A pointer to a unique bytebuffer + * @return SRSRAN_SUCCESS if PDU was written, SRSRAN_ERROR otherwise + */ +int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, + const rlc_am_nr_sn_size_t sn_size, + byte_buffer_t* pdu) +{ + if (sn_size == rlc_am_nr_sn_size_t::size12bits) { + return rlc_am_nr_write_status_pdu_12bit_sn(status_pdu, pdu); + } else { // 18bit SN + return rlc_am_nr_write_status_pdu_18bit_sn(status_pdu, pdu); + } +} + +int32_t rlc_am_nr_write_status_pdu_12bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu) +{ + uint8_t* ptr = pdu->msg; + + // fixed header part + *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero + + // write first 4 bit of ACK_SN + *ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN + ptr++; + *ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN + ptr++; + + // write E1 flag in octet 3 + if (status_pdu.nacks.size() > 0) { + *ptr = 0x80; + } else { + *ptr = 0x00; + } + ptr++; + + if (status_pdu.nacks.size() > 0) { + for (uint32_t i = 0; i < status_pdu.nacks.size(); i++) { + // write first 8 bit of NACK_SN + *ptr = (status_pdu.nacks[i].nack_sn >> 4) & 0xff; + ptr++; + + // write remaining 4 bits of NACK_SN + *ptr = (status_pdu.nacks[i].nack_sn & 0x0f) << 4; + // Set E1 if necessary + if (i < (uint32_t)(status_pdu.nacks.size() - 1)) { + *ptr |= 0x08; + } + + if (status_pdu.nacks[i].has_so) { + // Set E2 + *ptr |= 0x04; + } + + if (status_pdu.nacks[i].has_nack_range) { + // Set E3 + *ptr |= 0x02; + } + + ptr++; + if (status_pdu.nacks[i].has_so) { + (*ptr) = status_pdu.nacks[i].so_start >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_start; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end; + ptr++; + } + if (status_pdu.nacks[i].has_nack_range) { + (*ptr) = status_pdu.nacks[i].nack_range; + ptr++; + } + } + } + + pdu->N_bytes = ptr - pdu->msg; + + return SRSRAN_SUCCESS; +} + +int32_t rlc_am_nr_write_status_pdu_18bit_sn(const rlc_am_nr_status_pdu_t& status_pdu, byte_buffer_t* pdu) +{ + uint8_t* ptr = pdu->msg; + + // fixed header part + *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero + + *ptr |= (status_pdu.ack_sn >> 14) & 0x0F; // upper 4 bits of SN + ptr++; + *ptr = (status_pdu.ack_sn >> 6) & 0xFF; // center 8 bits of SN + ptr++; + *ptr = (status_pdu.ack_sn << 2) & 0xFC; // lower 6 bits of SN + + // set E1 flag if necessary + if (status_pdu.nacks.size() > 0) { + *ptr |= 0x02; + } + ptr++; + + if (status_pdu.nacks.size() > 0) { + for (uint32_t i = 0; i < status_pdu.nacks.size(); i++) { + *ptr = (status_pdu.nacks[i].nack_sn >> 10) & 0xFF; // upper 8 bits of SN + ptr++; + *ptr = (status_pdu.nacks[i].nack_sn >> 2) & 0xFF; // center 8 bits of SN + ptr++; + *ptr = (status_pdu.nacks[i].nack_sn << 6) & 0xC0; // lower 2 bits of SN + + if (i < (uint32_t)(status_pdu.nacks.size() - 1)) { + *ptr |= 0x20; // Set E1 + } + if (status_pdu.nacks[i].has_so) { + *ptr |= 0x10; // Set E2 + } + if (status_pdu.nacks[i].has_nack_range) { + *ptr |= 0x08; // Set E3 + } + + ptr++; + if (status_pdu.nacks[i].has_so) { + (*ptr) = status_pdu.nacks[i].so_start >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_start; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end >> 8; + ptr++; + (*ptr) = status_pdu.nacks[i].so_end; + ptr++; + } + if (status_pdu.nacks[i].has_nack_range) { + (*ptr) = status_pdu.nacks[i].nack_range; + ptr++; + } + } + } + + pdu->N_bytes = ptr - pdu->msg; + + return SRSRAN_SUCCESS; +} + +} // namespace srsran diff --git a/lib/src/upper/rlc_tm.cc b/lib/src/rlc/rlc_tm.cc similarity index 57% rename from lib/src/upper/rlc_tm.cc rename to lib/src/rlc/rlc_tm.cc index f9773576f..b415241ad 100644 --- a/lib/src/upper/rlc_tm.cc +++ b/lib/src/rlc/rlc_tm.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsran/upper/rlc_tm.h" +#include "srsran/rlc/rlc_tm.h" #include "srsran/common/common_lte.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h" @@ -30,9 +30,10 @@ rlc_tm::rlc_tm(srslog::basic_logger& logger, uint32_t lcid_, srsue::pdcp_interface_rlc* pdcp_, srsue::rrc_interface_rlc* rrc_) : - logger(logger), pdcp(pdcp_), rrc(rrc_), lcid(lcid_) + rlc_common(logger), pdcp(pdcp_), rrc(rrc_), lcid(lcid_) { - pool = byte_buffer_pool::get_instance(); + pool = byte_buffer_pool::get_instance(); + rb_name = "SRB0"; } // Warning: must call stop() to properly deallocate all buffers @@ -43,7 +44,7 @@ rlc_tm::~rlc_tm() bool rlc_tm::configure(const rlc_config_t& cnfg) { - logger.error("Attempted to configure TM RLC entity"); + RlcError("Attempted to configure TM RLC entity"); return true; } @@ -73,7 +74,7 @@ rlc_mode_t rlc_tm::get_mode() return rlc_mode_t::tm; } -uint32_t rlc_tm::get_bearer() +uint32_t rlc_tm::get_lcid() { return lcid; } @@ -89,23 +90,17 @@ void rlc_tm::write_sdu(unique_byte_buffer_t sdu) uint32_t nof_bytes = sdu->N_bytes; srsran::error_type ret = ul_queue.try_write(std::move(sdu)); if (ret) { - logger.info(msg_ptr, - nof_bytes, - "%s Tx SDU, queue size=%d, bytes=%d", - rrc->get_rb_name(lcid), - ul_queue.size(), - ul_queue.size_bytes()); + RlcHexInfo(msg_ptr, nof_bytes, "Tx SDU, queue size=%d, bytes=%d", ul_queue.size(), ul_queue.size_bytes()); } else { - logger.warning(ret.error()->msg, - ret.error()->N_bytes, - "[Dropped SDU] %s Tx SDU, queue size=%d, bytes=%d", - rrc->get_rb_name(lcid), - ul_queue.size(), - ul_queue.size_bytes()); + RlcHexWarning(ret.error()->msg, + ret.error()->N_bytes, + "[Dropped SDU] Tx SDU, queue size=%d, bytes=%d", + ul_queue.size(), + ul_queue.size_bytes()); } } else { - logger.warning("NULL SDU pointer in write_sdu()"); + RlcWarning("NULL SDU pointer in write_sdu()"); } } @@ -114,7 +109,7 @@ void rlc_tm::discard_sdu(uint32_t discard_sn) if (!tx_enabled) { return; } - logger.warning("SDU discard not implemented on RLC TM"); + RlcWarning("SDU discard not implemented on RLC TM"); } bool rlc_tm::sdu_queue_is_full() @@ -130,70 +125,90 @@ bool rlc_tm::has_data() uint32_t rlc_tm::get_buffer_state() { - return ul_queue.size_bytes(); + uint32_t newtx_queue = 0; + uint32_t prio_tx_queue = 0; + get_buffer_state(newtx_queue, prio_tx_queue); + return newtx_queue + prio_tx_queue; +} + +void rlc_tm::get_buffer_state(uint32_t& newtx_queue, uint32_t& prio_tx_queue) +{ + std::lock_guard lock(bsr_callback_mutex); + newtx_queue = ul_queue.size_bytes(); + prio_tx_queue = 0; + if (bsr_callback) { + bsr_callback(lcid, newtx_queue, prio_tx_queue); + } } rlc_bearer_metrics_t rlc_tm::get_metrics() { + std::lock_guard lock(metrics_mutex); return metrics; } void rlc_tm::reset_metrics() { + std::lock_guard lock(metrics_mutex); metrics = {}; } -int rlc_tm::read_pdu(uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_tm::read_pdu(uint8_t* payload, uint32_t nof_bytes) { uint32_t pdu_size = ul_queue.size_tail_bytes(); if (pdu_size > nof_bytes) { - logger.info("%s Tx PDU size larger than MAC opportunity (%d > %d)", rrc->get_rb_name(lcid), pdu_size, nof_bytes); - return -1; + RlcInfo("Tx PDU size larger than MAC opportunity (%d > %d)", pdu_size, nof_bytes); + return 0; } unique_byte_buffer_t buf; if (ul_queue.try_read(&buf)) { pdu_size = buf->N_bytes; memcpy(payload, buf->msg, buf->N_bytes); - logger.debug("%s Complete SDU scheduled for tx. Stack latency: %" PRIu64 " us", - rrc->get_rb_name(lcid), - (uint64_t)buf->get_latency_us().count()); - logger.info(payload, - pdu_size, - "%s Tx %s PDU, queue size=%d, bytes=%d", - rrc->get_rb_name(lcid), - srsran::to_string(rlc_mode_t::tm), - ul_queue.size(), - ul_queue.size_bytes()); + RlcDebug("Complete SDU scheduled for tx. Stack latency: %" PRIu64 " us", (uint64_t)buf->get_latency_us().count()); + RlcHexInfo(payload, + pdu_size, + "Tx %s PDU, queue size=%d, bytes=%d", + srsran::to_string(rlc_mode_t::tm), + ul_queue.size(), + ul_queue.size_bytes()); + std::lock_guard lock(metrics_mutex); metrics.num_tx_pdu_bytes += pdu_size; return pdu_size; - } else { - logger.warning("Queue empty while trying to read"); - if (ul_queue.size_bytes() > 0) { - logger.warning("Corrupted queue: empty but size_bytes > 0. Resetting queue"); - ul_queue.reset(); - } - return 0; } + + if (ul_queue.size_bytes() > 0) { + RlcWarning("Corrupted queue: empty but size_bytes > 0. Resetting queue"); + ul_queue.reset(); + } + return 0; } void rlc_tm::write_pdu(uint8_t* payload, uint32_t nof_bytes) { unique_byte_buffer_t buf = make_byte_buffer(); - if (buf) { + if (buf != nullptr) { memcpy(buf->msg, payload, nof_bytes); buf->N_bytes = nof_bytes; buf->set_timestamp(); - metrics.num_rx_pdu_bytes += nof_bytes; - metrics.num_rx_pdus++; + { + std::lock_guard lock(metrics_mutex); + metrics.num_rx_pdu_bytes += nof_bytes; + metrics.num_rx_pdus++; + } if (srsran::srb_to_lcid(srsran::lte_srb::srb0) == lcid) { rrc->write_pdu(lcid, std::move(buf)); } else { pdcp->write_pdu(lcid, std::move(buf)); } } else { - logger.error("Fatal Error: Couldn't allocate buffer in rlc_tm::write_pdu()."); + RlcError("Fatal Error: Couldn't allocate buffer in rlc_tm::write_pdu()."); } } +void rlc_tm::set_bsr_callback(bsr_callback_t callback) +{ + bsr_callback = std::move(callback); +} + } // namespace srsran diff --git a/lib/src/upper/rlc_um_base.cc b/lib/src/rlc/rlc_um_base.cc similarity index 69% rename from lib/src/upper/rlc_um_base.cc rename to lib/src/rlc/rlc_um_base.cc index 0f9406851..091dff070 100644 --- a/lib/src/upper/rlc_um_base.cc +++ b/lib/src/rlc/rlc_um_base.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsran/upper/rlc_um_base.h" +#include "srsran/rlc/rlc_um_base.h" #include "srsran/interfaces/ue_rrc_interfaces.h" #include @@ -30,7 +30,7 @@ rlc_um_base::rlc_um_base(srslog::basic_logger& logger, srsue::pdcp_interface_rlc* pdcp_, srsue::rrc_interface_rlc* rrc_, srsran::timer_handler* timers_) : - logger(logger), lcid(lcid_), pdcp(pdcp_), rrc(rrc_), timers(timers_), pool(byte_buffer_pool::get_instance()) + rlc_common(logger), lcid(lcid_), pdcp(pdcp_), rrc(rrc_), timers(timers_), pool(byte_buffer_pool::get_instance()) {} rlc_um_base::~rlc_um_base() {} @@ -51,7 +51,7 @@ rlc_mode_t rlc_um_base::get_mode() return rlc_mode_t::um; } -uint32_t rlc_um_base::get_bearer() +uint32_t rlc_um_base::get_lcid() { return lcid; } @@ -90,16 +90,19 @@ void rlc_um_base::empty_queue() void rlc_um_base::write_sdu(unique_byte_buffer_t sdu) { if (not tx_enabled || not tx) { - logger.debug("%s is currently deactivated. Dropping SDU (%d B)", rb_name.c_str(), sdu->N_bytes); + RlcDebug("RB is currently deactivated. Dropping SDU (%d B)", sdu->N_bytes); + std::lock_guard lock(metrics_mutex); metrics.num_lost_sdus++; return; } int sdu_bytes = sdu->N_bytes; //< Store SDU length for book-keeping if (tx->try_write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) { + std::lock_guard lock(metrics_mutex); metrics.num_tx_sdus++; metrics.num_tx_sdu_bytes += sdu_bytes; } else { + std::lock_guard lock(metrics_mutex); metrics.num_lost_sdus++; } } @@ -107,10 +110,11 @@ void rlc_um_base::write_sdu(unique_byte_buffer_t sdu) void rlc_um_base::discard_sdu(uint32_t discard_sn) { if (not tx_enabled || not tx) { - logger.debug("%s is currently deactivated. Ignoring SDU discard (SN=%u)", rb_name.c_str(), discard_sn); + RlcDebug("RB is currently deactivated. Ignoring SDU discard (SN=%u)", discard_sn); return; } tx->discard_sdu(discard_sn); + std::lock_guard lock(metrics_mutex); metrics.num_lost_sdus++; } @@ -133,17 +137,27 @@ bool rlc_um_base::has_data() uint32_t rlc_um_base::get_buffer_state() { - if (tx) { - return tx->get_buffer_state(); - } - return 0; + uint32_t newtx_queue = 0; + uint32_t prio_tx_queue = 0; + get_buffer_state(newtx_queue, prio_tx_queue); + return newtx_queue + prio_tx_queue; } -int rlc_um_base::read_pdu(uint8_t* payload, uint32_t nof_bytes) +void rlc_um_base::get_buffer_state(uint32_t& newtx_queue, uint32_t& prio_tx_queue) +{ + newtx_queue = 0; + prio_tx_queue = 0; + if (tx) { + newtx_queue = tx->get_buffer_state(); + } +} + +uint32_t rlc_um_base::read_pdu(uint8_t* payload, uint32_t nof_bytes) { if (tx && tx_enabled) { uint32_t len = tx->build_data_pdu(payload, nof_bytes); if (len > 0) { + std::lock_guard lock(metrics_mutex); metrics.num_tx_pdu_bytes += len; metrics.num_tx_pdus++; } @@ -155,22 +169,32 @@ int rlc_um_base::read_pdu(uint8_t* payload, uint32_t nof_bytes) void rlc_um_base::write_pdu(uint8_t* payload, uint32_t nof_bytes) { if (rx && rx_enabled) { - metrics.num_rx_pdus++; - metrics.num_rx_pdu_bytes += nof_bytes; + { + std::lock_guard lock(metrics_mutex); + metrics.num_rx_pdus++; + metrics.num_rx_pdu_bytes += nof_bytes; + } rx->handle_data_pdu(payload, nof_bytes); } } rlc_bearer_metrics_t rlc_um_base::get_metrics() { + std::lock_guard lock(metrics_mutex); return metrics; } void rlc_um_base::reset_metrics() { + std::lock_guard lock(metrics_mutex); metrics = {}; } +void rlc_um_base::set_bsr_callback(bsr_callback_t callback) +{ + tx->set_bsr_callback(std::move(callback)); +} + /**************************************************************************** * Helper functions ***************************************************************************/ @@ -243,18 +267,18 @@ bool rlc_um_base::rlc_um_base_tx::has_data() return (tx_sdu != nullptr || !tx_sdu_queue.is_empty()); } +void rlc_um_base::rlc_um_base_tx::set_bsr_callback(bsr_callback_t callback) +{ + bsr_callback = callback; +} + void rlc_um_base::rlc_um_base_tx::write_sdu(unique_byte_buffer_t sdu) { if (sdu) { - logger.info(sdu->msg, - sdu->N_bytes, - "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", - rb_name.c_str(), - sdu->N_bytes, - tx_sdu_queue.size()); + RlcHexInfo(sdu->msg, sdu->N_bytes, "Tx SDU (%d B, tx_sdu_queue_len=%d)", sdu->N_bytes, tx_sdu_queue.size()); tx_sdu_queue.write(std::move(sdu)); } else { - logger.warning("NULL SDU pointer in write_sdu()"); + RlcWarning("NULL SDU pointer in write_sdu()"); } } @@ -265,26 +289,36 @@ int rlc_um_base::rlc_um_base_tx::try_write_sdu(unique_byte_buffer_t sdu) uint32_t nof_bytes = sdu->N_bytes; srsran::error_type ret = tx_sdu_queue.try_write(std::move(sdu)); if (ret) { - logger.info( - msg_ptr, nof_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", rb_name.c_str(), nof_bytes, tx_sdu_queue.size()); + RlcHexInfo(msg_ptr, nof_bytes, "Tx SDU (%d B, tx_sdu_queue_len=%d)", nof_bytes, tx_sdu_queue.size()); return SRSRAN_SUCCESS; } else { - logger.warning(ret.error()->msg, - ret.error()->N_bytes, - "[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)", - rb_name.c_str(), - ret.error()->N_bytes, - tx_sdu_queue.size()); + RlcHexWarning(ret.error()->msg, + ret.error()->N_bytes, + "[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)", + rb_name.c_str(), + ret.error()->N_bytes, + tx_sdu_queue.size()); } } else { - logger.warning("NULL SDU pointer in write_sdu()"); + RlcWarning("NULL SDU pointer in write_sdu()"); } return SRSRAN_ERROR; } void rlc_um_base::rlc_um_base_tx::discard_sdu(uint32_t discard_sn) { - logger.warning("RLC UM: Discard SDU not implemented yet."); + std::lock_guard lock(mutex); + + bool discarded = tx_sdu_queue.apply_first([&discard_sn, this](unique_byte_buffer_t& sdu) { + if (sdu != nullptr && sdu->md.pdcp_sn == discard_sn) { + tx_sdu_queue.queue.pop_func(sdu); + sdu = nullptr; + } + return false; + }); + + // Discard fails when the PDCP PDU is already in Tx window. + RlcInfo("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn); } bool rlc_um_base::rlc_um_base_tx::sdu_queue_is_full() @@ -292,21 +326,21 @@ bool rlc_um_base::rlc_um_base_tx::sdu_queue_is_full() return tx_sdu_queue.is_full(); } -int rlc_um_base::rlc_um_base_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_um_base::rlc_um_base_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes) { unique_byte_buffer_t pdu; { std::lock_guard lock(mutex); - logger.debug("MAC opportunity - %d bytes", nof_bytes); + RlcDebug("MAC opportunity - %d bytes", nof_bytes); if (tx_sdu == nullptr && tx_sdu_queue.is_empty()) { - logger.info("No data available to be sent"); + RlcInfo("No data available to be sent"); return 0; } pdu = make_byte_buffer(); if (!pdu || pdu->N_bytes != 0) { - logger.error("Failed to allocate PDU buffer"); + RlcError("Failed to allocate PDU buffer"); return 0; } } diff --git a/lib/src/upper/rlc_um_lte.cc b/lib/src/rlc/rlc_um_lte.cc similarity index 73% rename from lib/src/upper/rlc_um_lte.cc rename to lib/src/rlc/rlc_um_lte.cc index bb5fb285a..5010e78f1 100644 --- a/lib/src/upper/rlc_um_lte.cc +++ b/lib/src/rlc/rlc_um_lte.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsran/upper/rlc_um_lte.h" +#include "srsran/rlc/rlc_um_lte.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include @@ -59,12 +59,11 @@ bool rlc_um_lte::configure(const rlc_config_t& cnfg_) return false; } - logger.info("%s configured in %s: t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits", - rb_name.c_str(), - srsran::to_string(cnfg_.rlc_mode), - cfg.um.t_reordering, - srsran::to_number(cfg.um.rx_sn_field_length), - srsran::to_number(cfg.um.tx_sn_field_length)); + RlcInfo("configured in %s - t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits", + srsran::to_string(cnfg_.rlc_mode), + cfg.um.t_reordering, + srsran::to_number(cfg.um.rx_sn_field_length), + srsran::to_number(cfg.um.tx_sn_field_length)); rx_enabled = true; tx_enabled = true; @@ -99,6 +98,10 @@ uint32_t rlc_um_lte::rlc_um_lte_tx::get_buffer_state() if (n_bytes > 0) n_bytes += (cfg.um.is_mrb) ? 2 : 3; + if (bsr_callback) { + bsr_callback(parent->get_lcid(), n_bytes, 0); + } + return n_bytes; } @@ -107,7 +110,7 @@ bool rlc_um_lte::rlc_um_lte_tx::configure(const rlc_config_t& cnfg_, std::string cfg = cnfg_; if (cfg.um.tx_mod == 0) { - logger.error("Error configuring %s RLC UM: tx_mod==0", rb_name.c_str()); + RlcError("Error configuring RLC UM - tx_mod==0"); return false; } @@ -118,10 +121,10 @@ bool rlc_um_lte::rlc_um_lte_tx::configure(const rlc_config_t& cnfg_, std::string return true; } -int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) { std::lock_guard lock(mutex); - rlc_umd_pdu_header_t header; + rlc_umd_pdu_header_t header = {}; header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED; header.sn = vt_us; header.N_li = 0; @@ -135,10 +138,7 @@ int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* int pdu_space = SRSRAN_MIN(nof_bytes, pdu->get_tailroom()); if (pdu_space <= head_len + 1) { - logger.info("%s Cannot build a PDU - %d bytes available, %d bytes required for header", - rb_name.c_str(), - nof_bytes, - head_len); + RlcInfo("Cannot build a PDU - %d bytes available, %d bytes required for header", nof_bytes, head_len); return 0; } @@ -146,8 +146,7 @@ int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* if (tx_sdu) { uint32_t space = pdu_space - head_len; to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space; - logger.debug( - "%s adding remainder of SDU segment - %d bytes of %d remaining", rb_name.c_str(), to_move, tx_sdu->N_bytes); + RlcDebug("adding remainder of SDU segment - %d bytes of %d remaining", to_move, tx_sdu->N_bytes); memcpy(pdu_ptr, tx_sdu->msg, to_move); last_li = to_move; pdu_ptr += to_move; @@ -158,12 +157,11 @@ int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* #ifdef ENABLE_TIMESTAMP auto latency_us = tx_sdu->get_latency_us().count(); mean_pdu_latency_us.push(latency_us); - logger.debug("%s Complete SDU scheduled for tx. Stack latency (last/average): %" PRIu64 "/%ld us", - rb_name.c_str(), - (uint64_t)latency_us, - (long)mean_pdu_latency_us.value()); + RlcDebug("Complete SDU scheduled for tx. Stack latency (last/average): %" PRIu64 "/%ld us", + (uint64_t)latency_us, + (long)mean_pdu_latency_us.value()); #else - logger.debug("%s Complete SDU scheduled for tx.", rb_name.c_str()); + RlcDebug("%s Complete SDU scheduled for tx.", rb_name.c_str()); #endif tx_sdu.reset(); } @@ -173,7 +171,7 @@ int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* // Pull SDUs from queue while (pdu_space > head_len + 1 && tx_sdu_queue.size() > 0) { - logger.debug("pdu_space=%d, head_len=%d", pdu_space, head_len); + RlcDebug("pdu_space=%d, head_len=%d", pdu_space, head_len); if (last_li > 0) { header.li[header.N_li++] = last_li; } @@ -186,7 +184,7 @@ int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* } tx_sdu = tx_sdu_queue.read(); to_move = (space >= tx_sdu->N_bytes) ? tx_sdu->N_bytes : space; - logger.debug("%s adding new SDU segment - %d bytes of %d remaining", rb_name.c_str(), to_move, tx_sdu->N_bytes); + RlcDebug("adding new SDU segment - %d bytes of %d remaining", to_move, tx_sdu->N_bytes); memcpy(pdu_ptr, tx_sdu->msg, to_move); last_li = to_move; pdu_ptr += to_move; @@ -197,12 +195,11 @@ int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* #ifdef ENABLE_TIMESTAMP auto latency_us = tx_sdu->get_latency_us().count(); mean_pdu_latency_us.push(latency_us); - logger.debug("%s Complete SDU scheduled for tx. Stack latency (last/average): %" PRIu64 "/%ld us", - rb_name.c_str(), - (uint64_t)latency_us, - (long)mean_pdu_latency_us.value()); + RlcDebug("Complete SDU scheduled for tx. Stack latency (last/average): %" PRIu64 "/%ld us", + (uint64_t)latency_us, + (long)mean_pdu_latency_us.value()); #else - logger.debug("%s Complete SDU scheduled for tx.", rb_name.c_str()); + RlcDebug("Complete SDU scheduled for tx."); #endif tx_sdu.reset(); } @@ -221,7 +218,7 @@ int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* rlc_um_write_data_pdu_header(&header, pdu.get()); memcpy(payload, pdu->msg, pdu->N_bytes); - logger.info(payload, pdu->N_bytes, "%s Tx PDU SN=%d (%d B)", rb_name.c_str(), header.sn, pdu->N_bytes); + RlcHexInfo(payload, pdu->N_bytes, "Tx PDU SN=%d (%d B)", header.sn, pdu->N_bytes); debug_state(); @@ -230,7 +227,7 @@ int rlc_um_lte::rlc_um_lte_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* void rlc_um_lte::rlc_um_lte_tx::debug_state() { - logger.debug("%s vt_us = %d", rb_name.c_str(), vt_us); + RlcDebug("vt_us = %d", vt_us); } void rlc_um_lte::rlc_um_lte_tx::reset() @@ -252,14 +249,16 @@ bool rlc_um_lte::rlc_um_lte_rx::configure(const rlc_config_t& cnfg_, std::string { cfg = cnfg_; + rb_name = rb_name_; + if (cfg.um.rx_mod == 0) { - logger.error("Error configuring %s RLC UM: rx_mod==0", rb_name.c_str()); + RlcError("Error configuring RLC UM: rx_mod==0"); return false; } // check timer if (not reordering_timer.is_valid()) { - logger.error("Configuring RLC UM RX: timers not configured"); + RlcError("Configuring RLC UM RX: timers not configured"); return false; } @@ -268,8 +267,6 @@ bool rlc_um_lte::rlc_um_lte_rx::configure(const rlc_config_t& cnfg_, std::string reordering_timer.set(static_cast(cfg.um.t_reordering), [this](uint32_t tid) { timer_expired(tid); }); } - rb_name = rb_name_; - return true; } @@ -308,17 +305,17 @@ void rlc_um_lte::rlc_um_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b { rlc_umd_pdu_header_t header; rlc_um_read_data_pdu_header(payload, nof_bytes, cfg.um.rx_sn_field_length, &header); - logger.info(payload, nof_bytes, "%s Rx data PDU SN=%d (%d B)", rb_name.c_str(), header.sn, nof_bytes); + RlcHexInfo(payload, nof_bytes, "Rx data PDU SN=%d (%d B)", header.sn, nof_bytes); if (RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh - cfg.um.rx_window_size) && RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur)) { - logger.info("%s SN=%d outside rx window [%d:%d] - discarding", rb_name.c_str(), header.sn, vr_ur, vr_uh); + RlcInfo("SN=%d outside rx window [%d:%d] - discarding", header.sn, vr_ur, vr_uh); return; } std::map::iterator it = rx_window.find(header.sn); if (rx_window.end() != it) { - logger.info("%s Discarding duplicate SN=%d", rb_name.c_str(), header.sn); + RlcInfo("Discarding duplicate SN=%d", header.sn); return; } @@ -326,7 +323,7 @@ void rlc_um_lte::rlc_um_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b rlc_umd_pdu_t pdu = {}; pdu.buf = make_byte_buffer(); if (!pdu.buf) { - logger.error("Discarting packet: no space in buffer pool"); + RlcError("Discarding packet: no space in buffer pool"); return; } memcpy(pdu.buf->msg, payload, nof_bytes); @@ -344,9 +341,9 @@ void rlc_um_lte::rlc_um_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b } // Reassemble and deliver SDUs, while updating vr_ur - logger.debug("Entering Reassemble from received PDU"); + RlcDebug("Entering Reassemble from received PDU"); reassemble_rx_sdus(); - logger.debug("Finished reassemble from received PDU"); + RlcDebug("Finished reassemble from received PDU"); // Update reordering variables and timers if (reordering_timer.is_running()) { @@ -370,32 +367,32 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() if (!rx_sdu) { rx_sdu = make_byte_buffer(); if (!rx_sdu) { - logger.error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); + RlcError("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); return; } } // First catch up with lower edge of reordering window while (!inside_reordering_window(vr_ur)) { - logger.debug("SN=%d is not inside reordering windows", vr_ur); + RlcDebug("SN=%d is not inside reordering windows", vr_ur); if (rx_window.end() == rx_window.find(vr_ur)) { - logger.debug("SN=%d not in rx_window. Reset received SDU", vr_ur); + RlcDebug("SN=%d not in rx_window. Reset received SDU", vr_ur); rx_sdu->clear(); } else { // Handle any SDU segments for (uint32_t i = 0; i < rx_window[vr_ur].header.N_li; i++) { int len = rx_window[vr_ur].header.li[i]; - logger.debug(rx_window[vr_ur].buf->msg, - len, - "Handling segment %d/%d of length %d B of SN=%d", - i + 1, - rx_window[vr_ur].header.N_li, - len, - vr_ur); + RlcHexDebug(rx_window[vr_ur].buf->msg, + len, + "Handling segment %d/%d of length %d B of SN=%d", + i + 1, + rx_window[vr_ur].header.N_li, + len, + vr_ur); // Check if we received a middle or end segment if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { - logger.warning("Dropping PDU %d in reassembly due to lost start segment", vr_ur); + RlcWarning("Dropping PDU %d in reassembly due to lost start segment", vr_ur); // Advance data pointers and continue with next segment rx_window[vr_ur].buf->msg += len; rx_window[vr_ur].buf->N_bytes -= len; @@ -410,18 +407,13 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() rx_window[vr_ur].buf->N_bytes -= len; if ((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu + 1) % cfg.um.rx_mod))) { - logger.warning("Dropping remainder of lost PDU (lower edge middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)", - vr_ur, - vr_ur_in_rx_sdu); + RlcWarning("Dropping remainder of lost PDU (lower edge middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)", + vr_ur, + vr_ur_in_rx_sdu); rx_sdu->clear(); metrics.num_lost_pdus++; } else { - logger.info(rx_sdu->msg, - rx_sdu->N_bytes, - "%s Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", - rb_name.c_str(), - vr_ur, - i); + RlcHexInfo(rx_sdu->msg, rx_sdu->N_bytes, "Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", vr_ur, i); rx_sdu->set_timestamp(); metrics.num_rx_sdus++; metrics.num_rx_sdu_bytes += rx_sdu->N_bytes; @@ -432,7 +424,7 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() } rx_sdu = make_byte_buffer(); if (!rx_sdu) { - logger.error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); + RlcError("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); return; } } @@ -441,22 +433,21 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() // Handle last segment if (rx_sdu->N_bytes > 0 || rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { - logger.info("Writing last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d", - vr_ur, - rx_sdu->N_bytes, - rx_window[vr_ur].buf->N_bytes); + RlcInfo("Writing last segment in SDU buffer. Lower edge vr_ur=%d, Buffer size=%d, segment size=%d", + vr_ur, + rx_sdu->N_bytes, + rx_window[vr_ur].buf->N_bytes); memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes); rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes; vr_ur_in_rx_sdu = vr_ur; if (rlc_um_end_aligned(rx_window[vr_ur].header.fi)) { if (pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { - logger.warning("Dropping remainder of lost PDU (lower edge last segments)"); + RlcWarning("Dropping remainder of lost PDU (lower edge last segments)"); rx_sdu->clear(); metrics.num_lost_pdus++; } else { - logger.info( - rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", rb_name.c_str(), vr_ur); + RlcHexInfo(rx_sdu->msg, rx_sdu->N_bytes, "Rx SDU vr_ur=%d (lower edge last segments)", vr_ur); rx_sdu->set_timestamp(); metrics.num_rx_sdus++; metrics.num_rx_sdu_bytes += rx_sdu->N_bytes; @@ -467,7 +458,7 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() } rx_sdu = make_byte_buffer(); if (!rx_sdu) { - logger.error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); + RlcError("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); return; } } @@ -484,11 +475,10 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() // Now update vr_ur until we reach an SN we haven't yet received while (rx_window.end() != rx_window.find(vr_ur)) { - logger.debug("Reassemble loop for vr_ur=%d", vr_ur); + RlcDebug("Reassemble loop for vr_ur=%d", vr_ur); if (not pdu_belongs_to_rx_sdu()) { - logger.warning( - "PDU SN=%d lost, stop reassambling SDU (vr_ur_in_rx_sdu=%d)", vr_ur_in_rx_sdu + 1, vr_ur_in_rx_sdu); + RlcInfo("PDU SN=%d lost, stop reassambling SDU (vr_ur_in_rx_sdu=%d)", vr_ur_in_rx_sdu + 1, vr_ur_in_rx_sdu); pdu_lost = false; // Reset flag to not prevent reassembling of further segments rx_sdu->clear(); } @@ -496,22 +486,22 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() // Handle any SDU segments for (uint32_t i = 0; i < rx_window[vr_ur].header.N_li; i++) { uint16_t len = rx_window[vr_ur].header.li[i]; - logger.debug("Handling SDU segment i=%d with len=%d of vr_ur=%d N_li=%d [%s]", - i, - len, - vr_ur, - rx_window[vr_ur].header.N_li, - rlc_fi_field_text[rx_window[vr_ur].header.fi]); + RlcDebug("Handling SDU segment i=%d with len=%d of vr_ur=%d N_li=%d [%s]", + i, + len, + vr_ur, + rx_window[vr_ur].header.N_li, + rlc_fi_field_text[rx_window[vr_ur].header.fi]); // Check if the first part of the PDU is a middle or end segment if (rx_sdu->N_bytes == 0 && i == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { - logger.warning( + RlcHexInfo( rx_window[vr_ur].buf->msg, len, "Dropping first %d B of SN=%d due to lost start segment", len, vr_ur); if (rx_window[vr_ur].buf->N_bytes < len) { - logger.error("Dropping remaining remainder of SN=%d too (N_bytes=%u < len=%d)", - vr_ur, - rx_window[vr_ur].buf->N_bytes, - len); + RlcError("Dropping remaining remainder of SN=%d too (N_bytes=%u < len=%d)", + vr_ur, + rx_window[vr_ur].buf->N_bytes, + len); goto clean_up_rx_window; } @@ -528,31 +518,31 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() // Check available space in SDU if ((uint32_t)len > rx_sdu->get_tailroom()) { - logger.error("Dropping PDU %d due to buffer mis-alignment (current segment len %d B, received %d B)", - vr_ur, - rx_sdu->N_bytes, - len); + RlcError("Dropping PDU %d due to buffer mis-alignment (current segment len %d B, received %d B)", + vr_ur, + rx_sdu->N_bytes, + len); rx_sdu->clear(); metrics.num_lost_pdus++; goto clean_up_rx_window; } if (not pdu_belongs_to_rx_sdu()) { - logger.info(rx_window[vr_ur].buf->msg, len, "Copying first %d bytes of new SDU", len); - logger.info("Updating vr_ur_in_rx_sdu. old=%d, new=%d", vr_ur_in_rx_sdu, vr_ur); + RlcHexInfo(rx_window[vr_ur].buf->msg, len, "Copying first %d bytes of new SDU", len); + RlcInfo("Updating vr_ur_in_rx_sdu. old=%d, new=%d", vr_ur_in_rx_sdu, vr_ur); vr_ur_in_rx_sdu = vr_ur; } else { - logger.info(rx_window[vr_ur].buf->msg, - len, - "Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, " - "vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d", - len, - rx_sdu->N_bytes, - rx_window[vr_ur].buf->N_bytes, - vr_ur_in_rx_sdu, - vr_ur, - cfg.um.rx_mod, - (vr_ur_in_rx_sdu + 1) % cfg.um.rx_mod); + RlcHexInfo(rx_window[vr_ur].buf->msg, + len, + "Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, " + "vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d", + len, + rx_sdu->N_bytes, + rx_window[vr_ur].buf->N_bytes, + vr_ur_in_rx_sdu, + vr_ur, + cfg.um.rx_mod, + (vr_ur_in_rx_sdu + 1) % cfg.um.rx_mod); } memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len); @@ -562,12 +552,7 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() vr_ur_in_rx_sdu = vr_ur; if (pdu_belongs_to_rx_sdu()) { - logger.info(rx_sdu->msg, - rx_sdu->N_bytes, - "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", - rb_name.c_str(), - vr_ur, - i); + RlcHexInfo(rx_sdu->msg, rx_sdu->N_bytes, "Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", vr_ur, i); rx_sdu->set_timestamp(); metrics.num_rx_sdus++; metrics.num_rx_sdu_bytes += rx_sdu->N_bytes; @@ -578,13 +563,13 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() } rx_sdu = make_byte_buffer(); if (!rx_sdu) { - logger.error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); + RlcError("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); return; } } else { - logger.warning("Dropping remainder of lost PDU (update vr_ur middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)", - vr_ur, - vr_ur_in_rx_sdu); + RlcWarning("Dropping remainder of lost PDU (update vr_ur middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)", + vr_ur, + vr_ur_in_rx_sdu); // Advance data pointers and continue with next segment rx_window[vr_ur].buf->msg += len; rx_window[vr_ur].buf->N_bytes -= len; @@ -596,7 +581,7 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() // Handle last segment if (rx_sdu->N_bytes == 0 && rx_window[vr_ur].header.N_li == 0 && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { - logger.warning("Dropping PDU %d during last segment handling due to lost start segment", vr_ur); + RlcWarning("Dropping PDU %d during last segment handling due to lost start segment", vr_ur); rx_sdu->clear(); metrics.num_lost_pdus++; goto clean_up_rx_window; @@ -605,31 +590,30 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() if (rx_sdu->N_bytes < SRSRAN_MAX_BUFFER_SIZE_BYTES && rx_window[vr_ur].buf->N_bytes < SRSRAN_MAX_BUFFER_SIZE_BYTES && rx_window[vr_ur].buf->N_bytes + rx_sdu->N_bytes < SRSRAN_MAX_BUFFER_SIZE_BYTES) { - logger.info(rx_window[vr_ur].buf->msg, - rx_window[vr_ur].buf->N_bytes, - "Writing last segment in SDU buffer. Updating vr_ur=%d, vr_ur_in_rx_sdu=%d, Buffer size=%d, " - "segment size=%d", - vr_ur, - vr_ur_in_rx_sdu, - rx_sdu->N_bytes, - rx_window[vr_ur].buf->N_bytes); + RlcHexInfo(rx_window[vr_ur].buf->msg, + rx_window[vr_ur].buf->N_bytes, + "Writing last segment in SDU buffer. Updating vr_ur=%d, vr_ur_in_rx_sdu=%d, Buffer size=%d, " + "segment size=%d", + vr_ur, + vr_ur_in_rx_sdu, + rx_sdu->N_bytes, + rx_window[vr_ur].buf->N_bytes); memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, rx_window[vr_ur].buf->N_bytes); rx_sdu->N_bytes += rx_window[vr_ur].buf->N_bytes; } else { - logger.error("Out of bounds while reassembling SDU buffer in UM: sdu_len=%d, window_buffer_len=%d, vr_ur=%d", - rx_sdu->N_bytes, - rx_window[vr_ur].buf->N_bytes, - vr_ur); + RlcError("Out of bounds while reassembling SDU buffer in UM: sdu_len=%d, window_buffer_len=%d, vr_ur=%d", + rx_sdu->N_bytes, + rx_window[vr_ur].buf->N_bytes, + vr_ur); } vr_ur_in_rx_sdu = vr_ur; if (rlc_um_end_aligned(rx_window[vr_ur].header.fi)) { if (pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) { - logger.warning("Dropping remainder of lost PDU (update vr_ur last segments)"); + RlcWarning("Dropping remainder of lost PDU (update vr_ur last segments)"); rx_sdu->clear(); metrics.num_lost_pdus++; } else { - logger.info( - rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (update vr_ur last segments)", rb_name.c_str(), vr_ur); + RlcHexInfo(rx_sdu->msg, rx_sdu->N_bytes, "Rx SDU vr_ur=%d (update vr_ur last segments)", vr_ur); rx_sdu->set_timestamp(); metrics.num_rx_sdus++; metrics.num_rx_sdu_bytes += rx_sdu->N_bytes; @@ -640,7 +624,7 @@ void rlc_um_lte::rlc_um_lte_rx::reassemble_rx_sdus() } rx_sdu = make_byte_buffer(); if (!rx_sdu) { - logger.error("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); + RlcError("Fatal Error: Couldn't allocate buffer in rlc_um::reassemble_rx_sdus()."); return; } } @@ -687,9 +671,9 @@ void rlc_um_lte::rlc_um_lte_rx::timer_expired(uint32_t timeout_id) { if (reordering_timer.id() == timeout_id) { // 36.322 v10 Section 5.1.2.2.4 - logger.info("%s reordering timeout expiry - updating vr_ur and reassembling", rb_name.c_str()); + RlcInfo("%s reordering timeout expiry - updating vr_ur and reassembling", rb_name.c_str()); - logger.warning("Lost PDU SN=%d", vr_ur); + RlcWarning("Lost PDU SN=%d", vr_ur); pdu_lost = true; if (rx_sdu != NULL) { @@ -698,9 +682,9 @@ void rlc_um_lte::rlc_um_lte_rx::timer_expired(uint32_t timeout_id) while (RX_MOD_BASE(vr_ur) < RX_MOD_BASE(vr_ux)) { vr_ur = (vr_ur + 1) % cfg.um.rx_mod; - logger.debug("Entering Reassemble from timeout id=%d", timeout_id); + RlcDebug("Entering Reassemble from timeout id=%d", timeout_id); reassemble_rx_sdus(); - logger.debug("Finished reassemble from timeout id=%d", timeout_id); + RlcDebug("Finished reassemble from timeout id=%d", timeout_id); } if (RX_MOD_BASE(vr_uh) > RX_MOD_BASE(vr_ur)) { @@ -718,7 +702,7 @@ void rlc_um_lte::rlc_um_lte_rx::timer_expired(uint32_t timeout_id) void rlc_um_lte::rlc_um_lte_rx::debug_state() { - logger.debug("%s vr_ur = %d, vr_ux = %d, vr_uh = %d", rb_name.c_str(), vr_ur, vr_ux, vr_uh); + RlcDebug("vr_ur = %d, vr_ux = %d, vr_uh = %d", vr_ur, vr_ux, vr_uh); } /**************************************************************************** diff --git a/lib/src/upper/rlc_um_nr.cc b/lib/src/rlc/rlc_um_nr.cc similarity index 70% rename from lib/src/upper/rlc_um_nr.cc rename to lib/src/rlc/rlc_um_nr.cc index 56bd8bafb..3ee7da553 100644 --- a/lib/src/upper/rlc_um_nr.cc +++ b/lib/src/rlc/rlc_um_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,11 +19,11 @@ * */ -#include "srsran/upper/rlc_um_nr.h" +#include "srsran/rlc/rlc_um_nr.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include -#define RX_MOD_NR_BASE(x) (((x)-RX_Next_Highest - cfg.um_nr.UM_Window_Size) % cfg.um_nr.mod) +#define RX_MOD_NR_BASE(x) (((x)-RX_Next_Highest - UM_Window_Size) % mod) namespace srsran { @@ -42,12 +42,12 @@ rlc_um_nr::~rlc_um_nr() bool rlc_um_nr::configure(const rlc_config_t& cnfg_) { - // determine bearer name and configure Rx/Tx objects - rb_name = get_rb_name(rrc, lcid, cnfg_.um.is_mrb); - // store config cfg = cnfg_; + // determine bearer name and configure Rx/Tx objects + rb_name = get_rb_name(); + rx.reset(new rlc_um_nr_rx(this)); if (not rx->configure(cfg, rb_name)) { return false; @@ -58,10 +58,10 @@ bool rlc_um_nr::configure(const rlc_config_t& cnfg_) return false; } - logger.info("%s configured in %s: sn_field_length=%u bits", - rb_name.c_str(), - srsran::to_string(cnfg_.rlc_mode), - srsran::to_number(cfg.um_nr.sn_field_length)); + RlcInfo("configured in %s: sn_field_length=%u bits, t_reassembly=%d ms", + srsran::to_string(cnfg_.rlc_mode), + srsran::to_number(cfg.um_nr.sn_field_length), + cfg.um_nr.t_reassembly_ms); rx_enabled = true; tx_enabled = true; @@ -69,6 +69,16 @@ bool rlc_um_nr::configure(const rlc_config_t& cnfg_) return true; } +/**************************************************************************** + * Logging helpers + ***************************************************************************/ +std::string rlc_um_nr::get_rb_name() const +{ + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "DRB{}", cfg.um_nr.bearer_id); + return fmt::to_string(fmtbuf); +} + /**************************************************************************** * Tx Subclass implementation ***************************************************************************/ @@ -80,7 +90,7 @@ uint32_t rlc_um_nr::rlc_um_nr_tx::get_buffer_state() std::lock_guard lock(mutex); // Bytes needed for tx SDUs - uint32_t n_sdus = tx_sdu_queue.size(); + uint32_t n_sdus = tx_sdu_queue.get_n_sdus(); uint32_t n_bytes = tx_sdu_queue.size_bytes(); if (tx_sdu) { n_sdus++; @@ -93,9 +103,13 @@ uint32_t rlc_um_nr::rlc_um_nr_tx::get_buffer_state() } // Room needed for fixed header? - if (n_bytes > 0) + if (n_bytes > 0) { n_bytes += (cfg.um.is_mrb) ? 2 : 3; + } + if (bsr_callback) { + bsr_callback(parent->get_lcid(), n_bytes, 0); + } return n_bytes; } @@ -103,10 +117,8 @@ bool rlc_um_nr::rlc_um_nr_tx::configure(const rlc_config_t& cnfg_, std::string r { cfg = cnfg_; - if (cfg.um_nr.mod == 0) { - logger.error("Error configuring %s RLC UM: tx_mod==0", rb_name.c_str()); - return false; - } + mod = (cfg.um_nr.sn_field_length == rlc_um_nr_sn_size_t::size6bits) ? 64 : 4096; + UM_Window_Size = (cfg.um_nr.sn_field_length == rlc_um_nr_sn_size_t::size6bits) ? 32 : 2048; // calculate header sizes for configured SN length rlc_um_nr_pdu_header_t header = {}; @@ -124,11 +136,11 @@ bool rlc_um_nr::rlc_um_nr_tx::configure(const rlc_config_t& cnfg_, std::string r return true; } -int rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) +uint32_t rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) { // Sanity check (we need at least 2B for a SDU) if (nof_bytes < 2) { - logger.warning("%s Cannot build a PDU with %d byte.", rb_name.c_str(), nof_bytes); + RlcWarning("Cannot build a PDU with %d byte.", nof_bytes); return 0; } @@ -143,7 +155,13 @@ int rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* p // Select segmentation information and header size if (tx_sdu == nullptr) { // Read a new SDU - tx_sdu = tx_sdu_queue.read(); + do { + tx_sdu = tx_sdu_queue.read(); + } while (tx_sdu == nullptr && tx_sdu_queue.size() != 0); + if (tx_sdu == nullptr) { + RlcDebug("Cannot build any PDU, tx_sdu_queue has no non-null SDU."); + return 0; + } next_so = 0; // Check for full SDU case @@ -165,10 +183,7 @@ int rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* p // Calculate actual header length uint32_t head_len = rlc_um_nr_packed_length(header); if (pdu_space <= head_len + 1) { - logger.info("%s Cannot build a PDU - %d bytes available, %d bytes required for header", - rb_name.c_str(), - nof_bytes, - head_len); + RlcInfo("Cannot build a PDU - %d bytes available, %d bytes required for header", nof_bytes, head_len); return 0; } @@ -177,7 +192,7 @@ int rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* p uint32_t to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space; // Log - logger.debug("%s adding %s - (%d/%d)", rb_name.c_str(), to_string(header.si).c_str(), to_move, tx_sdu->N_bytes); + RlcDebug("adding %s - (%d/%d)", to_string(header.si).c_str(), to_move, tx_sdu->N_bytes); // Move data from SDU to PDU uint8_t* pdu_ptr = pdu->msg; @@ -197,7 +212,7 @@ int rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* p // Update SN if needed if (header.si == rlc_nr_si_field_t::last_segment) { - TX_Next = (TX_Next + 1) % cfg.um_nr.mod; + TX_Next = (TX_Next + 1) % mod; next_so = 0; } @@ -210,7 +225,12 @@ int rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* p srsran_expect( ret <= nof_bytes, "Error while packing MAC PDU (more bytes written (%d) than expected (%d)!", ret, nof_bytes); - logger.info(payload, ret, "%s Tx PDU SN=%d (%d B)", rb_name.c_str(), header.sn, pdu->N_bytes); + if (header.si == rlc_nr_si_field_t::full_sdu) { + // log without SN + RlcHexInfo(payload, ret, "Tx PDU (%d B)", pdu->N_bytes); + } else { + RlcHexInfo(payload, ret, "Tx PDU SN=%d (%d B)", header.sn, pdu->N_bytes); + } debug_state(); @@ -219,7 +239,7 @@ int rlc_um_nr::rlc_um_nr_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* p void rlc_um_nr::rlc_um_nr_tx::debug_state() { - logger.debug("%s TX_Next=%d, next_so=%d", rb_name.c_str(), TX_Next, next_so); + RlcDebug("TX_Next=%d, next_so=%d", TX_Next, next_so); } void rlc_um_nr::rlc_um_nr_tx::reset() @@ -238,14 +258,14 @@ rlc_um_nr::rlc_um_nr_rx::rlc_um_nr_rx(rlc_um_base* parent_) : bool rlc_um_nr::rlc_um_nr_rx::configure(const rlc_config_t& cnfg_, std::string rb_name_) { - if (cfg.um_nr.mod == 0) { - logger.error("Error configuring %s RLC UM: rx_mod==0", rb_name.c_str()); - return false; - } + mod = (cfg.um_nr.sn_field_length == rlc_um_nr_sn_size_t::size6bits) ? 64 : 4096; + UM_Window_Size = (cfg.um_nr.sn_field_length == rlc_um_nr_sn_size_t::size6bits) ? 32 : 2048; + + rb_name = rb_name_; // check timer if (not reassembly_timer.is_valid()) { - logger.error("Configuring RLC UM NR RX: timers not configured"); + RlcError("Configuring RLC UM NR RX: timers not configured"); return false; } @@ -294,9 +314,8 @@ void rlc_um_nr::rlc_um_nr_rx::timer_expired(uint32_t timeout_id) { std::lock_guard lock(mutex); if (reassembly_timer.id() == timeout_id) { - logger.info("%s reassembly timeout expiry - updating RX_Next_Reassembly and reassembling", rb_name.c_str()); + RlcDebug("reassembly timeout expiry for SN=%d - updating RX_Next_Reassembly and reassembling", RX_Next_Reassembly); - logger.info("Lost PDU SN=%d", RX_Next_Reassembly); metrics.num_lost_pdus++; if (rx_sdu != nullptr) { @@ -306,13 +325,13 @@ void rlc_um_nr::rlc_um_nr_rx::timer_expired(uint32_t timeout_id) // update RX_Next_Reassembly to the next SN that has not been reassembled yet RX_Next_Reassembly = RX_Timer_Trigger; while (RX_MOD_NR_BASE(RX_Next_Reassembly) < RX_MOD_NR_BASE(RX_Next_Highest)) { - RX_Next_Reassembly = (RX_Next_Reassembly + 1) % cfg.um_nr.mod; + RX_Next_Reassembly = (RX_Next_Reassembly + 1) % mod; debug_state(); } // discard all segments with SN < updated RX_Next_Reassembly for (auto it = rx_window.begin(); it != rx_window.end();) { - if (it->first < RX_Next_Reassembly) { + if (RX_MOD_NR_BASE(it->first) < RX_MOD_NR_BASE(RX_Next_Reassembly)) { it = rx_window.erase(it); } else { ++it; @@ -321,8 +340,9 @@ void rlc_um_nr::rlc_um_nr_rx::timer_expired(uint32_t timeout_id) // check start of t_reassembly if (RX_MOD_NR_BASE(RX_Next_Highest) > RX_MOD_NR_BASE(RX_Next_Reassembly + 1) || - (RX_MOD_NR_BASE(RX_Next_Highest) == RX_MOD_NR_BASE(RX_Next_Reassembly + 1) && - has_missing_byte_segment(RX_Next_Reassembly))) { + ((RX_MOD_NR_BASE(RX_Next_Highest) == RX_MOD_NR_BASE(RX_Next_Reassembly + 1) && + has_missing_byte_segment(RX_Next_Reassembly)))) { + RlcDebug("starting reassembly timer for SN=%d", rb_name.c_str(), RX_Next_Reassembly); reassembly_timer.run(); RX_Timer_Trigger = RX_Next_Highest; } @@ -334,14 +354,14 @@ void rlc_um_nr::rlc_um_nr_rx::timer_expired(uint32_t timeout_id) // Sec 5.2.2.2.1 bool rlc_um_nr::rlc_um_nr_rx::sn_in_reassembly_window(const uint32_t sn) { - return (RX_MOD_NR_BASE(RX_Next_Highest - cfg.um_nr.UM_Window_Size) <= RX_MOD_NR_BASE(sn) && + return (RX_MOD_NR_BASE(RX_Next_Highest - UM_Window_Size) <= RX_MOD_NR_BASE(sn) && RX_MOD_NR_BASE(sn) < RX_MOD_NR_BASE(RX_Next_Highest)); } // Sec 5.2.2.2.2 bool rlc_um_nr::rlc_um_nr_rx::sn_invalid_for_rx_buffer(const uint32_t sn) { - return (RX_MOD_NR_BASE(RX_Next_Highest - cfg.um_nr.UM_Window_Size) <= RX_MOD_NR_BASE(sn) && + return (RX_MOD_NR_BASE(RX_Next_Highest - UM_Window_Size) <= RX_MOD_NR_BASE(sn) && RX_MOD_NR_BASE(sn) < RX_MOD_NR_BASE(RX_Next_Reassembly)); } @@ -351,7 +371,7 @@ unique_byte_buffer_t rlc_um_nr::rlc_um_nr_rx::rlc_um_nr_strip_pdu_header(const r { unique_byte_buffer_t sdu = make_byte_buffer(); if (sdu == nullptr) { - logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + RlcError("Couldn't allocate PDU in %s().", __FUNCTION__); return nullptr; } memcpy(sdu->msg, payload, nof_bytes); @@ -368,42 +388,44 @@ bool rlc_um_nr::rlc_um_nr_rx::has_missing_byte_segment(const uint32_t sn) { // is at least one missing byte segment of the RLC SDU associated with SN = RX_Next_Reassembly before the last byte of // all received segments of this RLC SDU - return true; + return (rx_window.find(sn) != rx_window.end()); } // Sect 5.2.2.2.3 void rlc_um_nr::rlc_um_nr_rx::handle_rx_buffer_update(const uint32_t sn) { if (rx_window.find(sn) != rx_window.end()) { + bool sdu_complete = false; + // iterate over received segments and try to assemble full SDU auto& pdu = rx_window.at(sn); for (auto it = pdu.segments.begin(); it != pdu.segments.end();) { - logger.debug("Have %s segment with SO=%d for SN=%d", - to_string_short(it->second.header.si).c_str(), - it->second.header.so, - it->second.header.sn); + RlcDebug("Have %s segment with SO=%d for SN=%d", + to_string_short(it->second.header.si).c_str(), + it->second.header.so, + it->second.header.sn); if (it->second.header.so == pdu.next_expected_so) { if (pdu.next_expected_so == 0) { if (pdu.sdu == nullptr) { // reuse buffer of first segment for final SDU pdu.sdu = std::move(it->second.buf); pdu.next_expected_so = pdu.sdu->N_bytes; - logger.debug("Reusing first segment of SN=%d for final SDU", it->second.header.sn); + RlcDebug("Reusing first segment of SN=%d for final SDU", it->second.header.sn); it = pdu.segments.erase(it); } else { - logger.debug("SDU buffer already allocated. Possible retransmission of first segment."); + RlcDebug("SDU buffer already allocated. Possible retransmission of first segment."); if (it->second.header.so != pdu.next_expected_so) { - logger.error("Invalid PDU. SO doesn't match. Discarting all segments of SN=%d.", sn); + RlcError("Invalid PDU. SO doesn't match. Discarding all segments of SN=%d.", sn); rx_window.erase(sn); return; } } } else { if (it->second.buf->N_bytes > pdu.sdu->get_tailroom()) { - logger.error("Cannot fit RLC PDU in SDU buffer (tailroom=%d, len=%d), dropping both. Erasing SN=%d.", - rx_sdu->get_tailroom(), - it->second.buf->N_bytes, - it->second.header.sn); + RlcError("Cannot fit RLC PDU in SDU buffer (tailroom=%d, len=%d), dropping both. Erasing SN=%d.", + rx_sdu->get_tailroom(), + it->second.buf->N_bytes, + it->second.header.sn); rx_window.erase(sn); metrics.num_lost_pdus++; return; @@ -413,26 +435,13 @@ void rlc_um_nr::rlc_um_nr_rx::handle_rx_buffer_update(const uint32_t sn) memcpy(pdu.sdu->msg + pdu.sdu->N_bytes, it->second.buf->msg, it->second.buf->N_bytes); pdu.sdu->N_bytes += it->second.buf->N_bytes; pdu.next_expected_so += it->second.buf->N_bytes; - logger.debug("Appended SO=%d of SN=%d", it->second.header.so, it->second.header.sn); + RlcDebug("Appended SO=%d of SN=%d", it->second.header.so, it->second.header.sn); it = pdu.segments.erase(it); if (pdu.next_expected_so == pdu.total_sdu_length) { - // deliver full SDU to upper layers - logger.info("Delivering %s SDU SN=%d (%d B)", rb_name.c_str(), sn, pdu.sdu->N_bytes); - pdcp->write_pdu(lcid, std::move(pdu.sdu)); - - // find next SN in rx buffer - if (sn == RX_Next_Reassembly) { - RX_Next_Reassembly = ((RX_Next_Reassembly + 1) % cfg.um_nr.mod); - while (RX_MOD_NR_BASE(RX_Next_Reassembly) < RX_MOD_NR_BASE(RX_Next_Highest)) { - RX_Next_Reassembly = (RX_Next_Reassembly + 1) % cfg.um_nr.mod; - } - logger.debug("Updating RX_Next_Reassembly=%d", RX_Next_Reassembly); - } - - // delete PDU from rx_window - rx_window.erase(sn); - return; + // entire SDU has been received, it will be passed up the stack outside the loop + sdu_complete = true; + break; } } } else { @@ -441,20 +450,43 @@ void rlc_um_nr::rlc_um_nr_rx::handle_rx_buffer_update(const uint32_t sn) } } - // check for SN outside of rx window - if (not sn_in_reassembly_window(sn)) { - // update RX_Next_highest - RX_Next_Highest = sn + 1; - logger.debug("Updating RX_Next_Highest=%d", RX_Next_Highest); + if (sdu_complete) { + // deliver full SDU to upper layers + RlcInfo("Rx SDU (%d B)", pdu.sdu->N_bytes); + pdcp->write_pdu(lcid, std::move(pdu.sdu)); + + // delete PDU from rx_window + rx_window.erase(sn); + + // find next SN in rx buffer + if (sn == RX_Next_Reassembly) { + if (rx_window.empty()) { + // no further segments received + RX_Next_Reassembly = RX_Next_Highest; + } else { + for (auto it = rx_window.begin(); it != rx_window.end(); ++it) { + RlcDebug("SN=%d has %zd segments", it->first, it->second.segments.size()); + if (RX_MOD_NR_BASE(it->first) > RX_MOD_NR_BASE(RX_Next_Reassembly)) { + RX_Next_Reassembly = it->first; + break; + } + } + } + RlcDebug("Updating RX_Next_Reassembly=%d", RX_Next_Reassembly); + } + } else if (not sn_in_reassembly_window(sn)) { + // SN outside of rx window + + RX_Next_Highest = (sn + 1) % mod; // update RX_Next_highest + RlcDebug("Updating RX_Next_Highest=%d", RX_Next_Highest); // drop all SNs outside of new rx window for (auto it = rx_window.begin(); it != rx_window.end();) { if (not sn_in_reassembly_window(it->first)) { - logger.info("%s SN: %d outside rx window [%d:%d] - discarding", - rb_name.c_str(), - it->first, - RX_Next_Highest - cfg.um_nr.UM_Window_Size, - RX_Next_Highest); + RlcInfo("SN=%d outside rx window [%d:%d] - discarding", + it->first, + RX_Next_Highest - UM_Window_Size, + RX_Next_Highest); it = rx_window.erase(it); metrics.num_lost_pdus++; } else { @@ -465,31 +497,35 @@ void rlc_um_nr::rlc_um_nr_rx::handle_rx_buffer_update(const uint32_t sn) if (not sn_in_reassembly_window(RX_Next_Reassembly)) { // update RX_Next_Reassembly to first SN that has not been reassembled and delivered for (const auto& rx_pdu : rx_window) { - if (rx_pdu.first >= RX_MOD_NR_BASE(RX_Next_Highest - cfg.um_nr.UM_Window_Size)) { + if (rx_pdu.first >= RX_MOD_NR_BASE(RX_Next_Highest - UM_Window_Size)) { RX_Next_Reassembly = rx_pdu.first; - logger.debug("Updating RX_Next_Reassembly=%d", RX_Next_Reassembly); + RlcDebug("Updating RX_Next_Reassembly=%d", RX_Next_Reassembly); break; } } } + } - if (reassembly_timer.is_running()) { - if (RX_Timer_Trigger <= RX_Next_Reassembly || - (not sn_in_reassembly_window(RX_Timer_Trigger) and RX_Timer_Trigger != RX_Next_Highest) || - ((RX_Next_Highest == RX_Next_Reassembly + 1) && not has_missing_byte_segment(sn))) { - reassembly_timer.stop(); - } + if (reassembly_timer.is_running()) { + if (RX_Timer_Trigger <= RX_Next_Reassembly || + (not sn_in_reassembly_window(RX_Timer_Trigger) and RX_Timer_Trigger != RX_Next_Highest) || + ((RX_Next_Highest == RX_Next_Reassembly + 1) && not has_missing_byte_segment(RX_Next_Reassembly))) { + RlcDebug("stopping reassembly timer"); + reassembly_timer.stop(); } + } - if (not reassembly_timer.is_running() && has_missing_byte_segment(sn)) { - if (RX_Next_Highest > RX_Next_Reassembly + 1) { - reassembly_timer.run(); - RX_Timer_Trigger = RX_Next_Highest; - } + if (not reassembly_timer.is_running()) { + if ((RX_MOD_NR_BASE(RX_Next_Highest) > RX_MOD_NR_BASE(RX_Next_Reassembly + 1)) || + ((RX_MOD_NR_BASE(RX_Next_Highest) == RX_MOD_NR_BASE(RX_Next_Reassembly + 1)) && + has_missing_byte_segment(RX_Next_Reassembly))) { + RlcDebug("Starting reassembly timer for SN=%d", sn); + reassembly_timer.run(); + RX_Timer_Trigger = RX_Next_Highest; } } } else { - logger.error("SN=%d does not exist in Rx buffer", sn); + RlcError("SN=%d does not exist in Rx buffer", sn); } } @@ -498,10 +534,7 @@ inline void rlc_um_nr::rlc_um_nr_rx::update_total_sdu_length(rlc_umd_pdu_segment { if (rx_pdu.header.si == rlc_nr_si_field_t::last_segment) { pdu_segments.total_sdu_length = rx_pdu.header.so + rx_pdu.buf->N_bytes; - logger.info("%s updating total SDU length for SN=%d to %d B", - rb_name.c_str(), - rx_pdu.header.sn, - pdu_segments.total_sdu_length); + RlcDebug("updating total SDU length for SN=%d to %d B", rx_pdu.header.sn, pdu_segments.total_sdu_length); } }; @@ -512,7 +545,7 @@ void rlc_um_nr::rlc_um_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_byt rlc_um_nr_pdu_header_t header = {}; rlc_um_nr_read_data_pdu_header(payload, nof_bytes, cfg.um_nr.sn_field_length, &header); - logger.debug(payload, nof_bytes, "RX %s Rx data PDU (%d B)", rb_name.c_str(), nof_bytes); + RlcHexDebug(payload, nof_bytes, "Rx data PDU (%d B)", nof_bytes); // check if PDU contains a SN if (header.si == rlc_nr_si_field_t::full_sdu) { @@ -520,10 +553,10 @@ void rlc_um_nr::rlc_um_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_byt unique_byte_buffer_t sdu = rlc_um_nr_strip_pdu_header(header, payload, nof_bytes); // deliver to PDCP - logger.info("Delivering %s SDU (%d B)", rb_name.c_str(), sdu->N_bytes); + RlcInfo("Rx SDU (%d B)", sdu->N_bytes); pdcp->write_pdu(lcid, std::move(sdu)); } else if (sn_invalid_for_rx_buffer(header.sn)) { - logger.info("%s Discarding SN=%d", rb_name.c_str(), header.sn); + RlcInfo("Discarding SN=%d", header.sn); // Nothing else to do here .. } else { // place PDU in receive buffer @@ -534,18 +567,21 @@ void rlc_um_nr::rlc_um_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_byt // check if this SN is already present in rx buffer if (rx_window.find(header.sn) == rx_window.end()) { // first received segment of this SN, add to rx buffer - logger.info("%s placing %s segment of SN=%d in Rx buffer", - rb_name.c_str(), + RlcHexDebug(rx_pdu.buf->msg, + rx_pdu.buf->N_bytes, + "placing %s segment of SN=%d (%d B) in Rx buffer", to_string_short(header.si).c_str(), - header.sn); + header.sn, + rx_pdu.buf->N_bytes); rlc_umd_pdu_segments_nr_t pdu_segments = {}; update_total_sdu_length(pdu_segments, rx_pdu); pdu_segments.segments.emplace(header.so, std::move(rx_pdu)); rx_window[header.sn] = std::move(pdu_segments); } else { // other segment for this SN already present, update received data - logger.info("%s updating SN=%d at SO=%d with %d B", - rb_name.c_str(), + RlcHexDebug(rx_pdu.buf->msg, + rx_pdu.buf->N_bytes, + "updating SN=%d at SO=%d with %d B", rx_pdu.header.sn, rx_pdu.header.so, rx_pdu.buf->N_bytes); @@ -568,12 +604,11 @@ void rlc_um_nr::rlc_um_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_byt void rlc_um_nr::rlc_um_nr_rx::debug_state() { - logger.debug("%s RX_Next_Reassembly=%d, RX_Timer_Trigger=%d, RX_Next_Highest=%d, t_Reassembly=%s", - rb_name.c_str(), - RX_Next_Reassembly, - RX_Timer_Trigger, - RX_Next_Highest, - reassembly_timer.is_running() ? "running" : "stopped"); + RlcDebug("RX_Next_Reassembly=%d, RX_Timer_Trigger=%d, RX_Next_Highest=%d, t_Reassembly=%s", + RX_Next_Reassembly, + RX_Timer_Trigger, + RX_Next_Highest, + reassembly_timer.is_running() ? "running" : "stopped"); } /**************************************************************************** * Header pack/unpack helper functions diff --git a/lib/src/srslog/CMakeLists.txt b/lib/src/srslog/CMakeLists.txt index 635d064be..105e42736 100644 --- a/lib/src/srslog/CMakeLists.txt +++ b/lib/src/srslog/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -42,4 +42,4 @@ find_package(Threads REQUIRED) add_library(srslog STATIC ${SOURCES}) target_link_libraries(srslog ${CMAKE_THREAD_LIBS_INIT}) -INSTALL(TARGETS srslog DESTINATION ${LIBRARY_DIR}) +install(TARGETS srslog DESTINATION ${LIBRARY_DIR} OPTIONAL) diff --git a/lib/src/srslog/backend_worker.cpp b/lib/src/srslog/backend_worker.cpp index d7119da67..989c37e15 100644 --- a/lib/src/srslog/backend_worker.cpp +++ b/lib/src/srslog/backend_worker.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,12 +33,48 @@ void backend_worker::stop() } } -void backend_worker::create_worker() +void backend_worker::set_thread_priority(backend_priority priority) const +{ + switch (priority) { + case backend_priority::normal: + break; + case backend_priority::high: { + int min = ::sched_get_priority_min(SCHED_FIFO); + if (min == -1) { + err_handler("Unable to set the backend thread priority to high, falling back to normal priority."); + return; + } + ::sched_param sch{min}; + if (::pthread_setschedparam(::pthread_self(), SCHED_FIFO, &sch)) { + err_handler("Unable to set the backend thread priority to high, falling back to normal priority."); + return; + } + break; + } + case backend_priority::very_high: { + int max = ::sched_get_priority_max(SCHED_FIFO); + int min = ::sched_get_priority_min(SCHED_FIFO); + if (max == -1 || min == -1) { + err_handler("Unable to set the backend thread priority to real time, falling back to normal priority."); + return; + } + ::sched_param sch{min + ((max - min) / 2)}; + if (::pthread_setschedparam(::pthread_self(), SCHED_FIFO, &sch)) { + err_handler("Unable to set the backend thread priority to real time, falling back to normal priority."); + return; + } + break; + } + } +} + +void backend_worker::create_worker(backend_priority priority) { assert(!running_flag && "Only one worker thread should be created"); - std::thread t([this]() { + std::thread t([this, priority]() { running_flag = true; + set_thread_priority(priority); do_work(); }); @@ -50,21 +86,24 @@ void backend_worker::create_worker() } } -void backend_worker::start() +void backend_worker::start(backend_priority priority) { // Ensure we only create the worker thread once. - std::call_once(start_once_flag, [this]() { create_worker(); }); + std::call_once(start_once_flag, [this, priority]() { create_worker(priority); }); } void backend_worker::do_work() { - assert(running_flag && "Thread entry function called without running thread"); + /// This period defines the time the worker will sleep while waiting for new entries. This is required to check the + /// termination variable periodically. + constexpr std::chrono::microseconds sleep_period{100}; while (running_flag) { - auto item = queue.timed_pop(sleep_period_ms); + auto item = queue.try_pop(); - // Spin again when the timeout expires. + // Spin while there are no new entries to process. if (!item.first) { + std::this_thread::sleep_for(sleep_period); continue; } @@ -100,11 +139,6 @@ void backend_worker::process_log_entry(detail::log_entry&& entry) assert(entry.format_func && "Invalid format function"); fmt_buffer.clear(); - // Already formatted strings in the foreground are passed to the formatter as the fmtstring. - if (entry.metadata.small_str.size()) { - entry.metadata.fmtstring = entry.metadata.small_str.data(); - } - // Save the pointer before moving the entry. auto* arg_store = entry.metadata.store; @@ -122,7 +156,7 @@ void backend_worker::process_outstanding_entries() assert(!running_flag && "Cannot process outstanding entries while thread is running"); while (true) { - auto item = queue.timed_pop(1); + auto item = queue.try_pop(); // Check if the queue is empty. if (!item.first) { diff --git a/lib/src/srslog/backend_worker.h b/lib/src/srslog/backend_worker.h index 01e708821..646bd36ce 100644 --- a/lib/src/srslog/backend_worker.h +++ b/lib/src/srslog/backend_worker.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,11 +35,6 @@ namespace srslog { /// log entries from a work queue and dispatches them to the selected sinks. class backend_worker { - /// This period defines the maximum time the worker will sleep while waiting - /// for new entries. This is required to check the termination variable - /// periodically. - static constexpr unsigned sleep_period_ms = 500; - public: backend_worker(detail::work_queue& queue, detail::dyn_arg_store_pool& arg_pool) : queue(queue), arg_pool(arg_pool), running_flag(false) @@ -53,7 +48,7 @@ public: /// Starts the backend worker thread. After returning from this function the /// secondary thread is ensured to be running. Calling this function more than /// once has no effect. - void start(); + void start(backend_priority priority); /// Stops the backend worker thread if it is running, otherwise the call has /// no effect. After returning from this function the secondary thread is @@ -87,7 +82,7 @@ public: private: /// Creates the worker thread. /// NOTE: This function should be only called once. - void create_worker(); + void create_worker(backend_priority priority); /// Entry function used by the secondary thread. void do_work(); @@ -112,6 +107,9 @@ private: } } + /// Establishes the specified thread priority for the calling thread. + void set_thread_priority(backend_priority priority) const; + private: detail::work_queue& queue; detail::dyn_arg_store_pool& arg_pool; diff --git a/lib/src/srslog/bundled/fmt/format.cc b/lib/src/srslog/bundled/fmt/format.cc index 4864ac26e..d1fc4f39b 100644 --- a/lib/src/srslog/bundled/fmt/format.cc +++ b/lib/src/srslog/bundled/fmt/format.cc @@ -25,6 +25,44 @@ int format_float(char* buf, std::size_t size, const char* format, int precision, : snprintf_ptr(buf, size, format, precision, value); } +namespace { + +/// This special mutex has priority inheritance to improve latency. +class pi_mutex +{ +public: + pi_mutex(const pi_mutex&) = delete; + pi_mutex& operator=(const pi_mutex&) = delete; + + pi_mutex() + { + ::pthread_mutexattr_t mutex_attr; + ::pthread_mutexattr_init(&mutex_attr); + ::pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT); + ::pthread_mutex_init(&m, &mutex_attr); + } + + ~pi_mutex() { ::pthread_mutex_destroy(&m); } + + /// Mutex lock. + void lock() { ::pthread_mutex_lock(&m); } + + /// Mutex unlock. + void unlock() { ::pthread_mutex_unlock(&m); } + + /// Mutex try lock. Returns true if the lock was obtained, false otherwise. + bool try_lock() { return (::pthread_mutex_trylock(&m) == 0); } + + /// Accessor to the raw mutex structure. + pthread_mutex_t* raw() { return &m; } + const pthread_mutex_t* raw() const { return &m; } + +private: + pthread_mutex_t m; +}; + +} + #define NODE_POOL_SIZE (10000u) static constexpr uint8_t memory_heap_tag = 0xAA; class dyn_node_pool @@ -49,7 +87,7 @@ public: void* alloc(std::size_t sz) { assert(sz <= dynamic_arg_list::max_pool_node_size && "Object is too large to fit in the pool"); - std::lock_guard lock(m); + std::lock_guard lock(m); if (free_list.empty()) { // Tag that this allocation was performed by the heap. auto *p = new type; @@ -70,7 +108,6 @@ public: return; } - std::lock_guard lock(m); uint8_t* base_ptr = reinterpret_cast(p) - 1; if (*base_ptr == memory_heap_tag) { // This pointer was allocated using the heap. @@ -78,13 +115,14 @@ public: return; } + std::lock_guard lock(m); free_list.push_back(base_ptr); } private: std::vector pool; std::vector free_list; - mutable std::mutex m; + mutable pi_mutex m; }; static dyn_node_pool node_pool; diff --git a/lib/src/srslog/event_trace.cpp b/lib/src/srslog/event_trace.cpp index abc793480..b1ac1cc30 100644 --- a/lib/src/srslog/event_trace.cpp +++ b/lib/src/srslog/event_trace.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -134,9 +134,5 @@ srslog::detail::scoped_complete_event::~scoped_complete_event() return; } - small_str_buffer str; - // Limit the category and name strings to a predefined length so everything fits in a small string. - fmt::format_to(str, "{:.32} {:.16}, {}", category, name, diff.count()); - str.push_back('\0'); - (*tracer)(std::move(str)); + (*tracer)("%s %s, %u", category, name, (unsigned)diff.count()); } diff --git a/lib/src/srslog/formatters/json_formatter.cpp b/lib/src/srslog/formatters/json_formatter.cpp index 282a99f4b..7efe3c08f 100644 --- a/lib/src/srslog/formatters/json_formatter.cpp +++ b/lib/src/srslog/formatters/json_formatter.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,15 @@ void json_formatter::format(detail::log_entry_metadata&& metadata, fmt::memory_b if (metadata.fmtstring) { if (metadata.store) { fmt::basic_format_args > args(*metadata.store); - fmt::vprintf(buffer, fmt::to_string_view(metadata.fmtstring), args); + try { + fmt::vprintf(buffer, fmt::to_string_view(metadata.fmtstring), args); + } catch (...) { + fmt::print(stderr, "srsLog error - Invalid format string: \"{}\"\n", metadata.fmtstring); + fmt::format_to(buffer, " -> srsLog error - Invalid format string: \"{}\"", metadata.fmtstring); +#ifdef STOP_ON_WARNING + std::abort(); +#endif + } fmt::format_to(buffer, fmt::to_string_view("\"")); } else { fmt::format_to(buffer, "{}\"", metadata.fmtstring); @@ -67,7 +75,15 @@ void json_formatter::format_context_begin(const detail::log_entry_metadata& md, if (md.store) { fmt::format_to(buffer, " \"log_entry\": \""); fmt::basic_format_args > args(*md.store); - fmt::vprintf(buffer, fmt::to_string_view(md.fmtstring), args); + try { + fmt::vprintf(buffer, fmt::to_string_view(md.fmtstring), args); + } catch (...) { + fmt::print(stderr, "srsLog error - Invalid format string: \"{}\"\n", md.fmtstring); + fmt::format_to(buffer, " -> srsLog error - Invalid format string: \"{}\"", md.fmtstring); +#ifdef STOP_ON_WARNING + std::abort(); +#endif + } fmt::format_to(buffer, "\",\n"); } else { fmt::format_to(buffer, " \"log_entry\": \"{}\",\n", md.fmtstring); diff --git a/lib/src/srslog/formatters/json_formatter.h b/lib/src/srslog/formatters/json_formatter.h index 76d7502fe..bec83387e 100644 --- a/lib/src/srslog/formatters/json_formatter.h +++ b/lib/src/srslog/formatters/json_formatter.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/srslog/formatters/text_formatter.cpp b/lib/src/srslog/formatters/text_formatter.cpp index e6ad59889..5a4bd161a 100644 --- a/lib/src/srslog/formatters/text_formatter.cpp +++ b/lib/src/srslog/formatters/text_formatter.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -52,7 +52,7 @@ static void format_metadata(const detail::log_entry_metadata& metadata, fmt::mem std::tm current_time = fmt::gmtime(std::chrono::high_resolution_clock::to_time_t(metadata.tp)); auto us_fraction = std::chrono::duration_cast(metadata.tp.time_since_epoch()).count() % 1000000u; - fmt::format_to(buffer, "{:%H:%M:%S}.{:06} ", current_time, us_fraction); + fmt::format_to(buffer, "{:%F}T{:%H:%M:%S}.{:06} ", current_time, current_time, us_fraction); // Format optional fields if present. if (!metadata.log_name.empty()) { @@ -75,7 +75,15 @@ void text_formatter::format(detail::log_entry_metadata&& metadata, fmt::memory_b if (metadata.fmtstring) { if (metadata.store) { fmt::basic_format_args > args(*metadata.store); - fmt::vprintf(buffer, fmt::to_string_view(metadata.fmtstring), args); + try { + fmt::vprintf(buffer, fmt::to_string_view(metadata.fmtstring), args); + } catch (...) { + fmt::print(stderr, "srsLog error - Invalid format string: \"{}\"\n", metadata.fmtstring); + fmt::format_to(buffer, " -> srsLog error - Invalid format string: \"{}\"", metadata.fmtstring); +#ifdef STOP_ON_WARNING + std::abort(); +#endif + } fmt::format_to(buffer, "\n"); } else { fmt::format_to(buffer, "{}\n", metadata.fmtstring); @@ -115,7 +123,15 @@ void text_formatter::format_context_end(const detail::log_entry_metadata& md, if (md.store) { fmt::format_to(buffer, "]: "); fmt::basic_format_args > args(*md.store); - fmt::vprintf(buffer, fmt::to_string_view(md.fmtstring), args); + try { + fmt::vprintf(buffer, fmt::to_string_view(md.fmtstring), args); + } catch (...) { + fmt::print(stderr, "srsLog error - Invalid format string: \"{}\"\n", md.fmtstring); + fmt::format_to(buffer, " -> srsLog error - Invalid format string: \"{}\"", md.fmtstring); +#ifdef STOP_ON_WARNING + std::abort(); +#endif + } fmt::format_to(buffer, "\n"); } else { fmt::format_to(buffer, "]: {}\n", md.fmtstring); diff --git a/lib/src/srslog/formatters/text_formatter.h b/lib/src/srslog/formatters/text_formatter.h index b630d2673..1f2b157a6 100644 --- a/lib/src/srslog/formatters/text_formatter.h +++ b/lib/src/srslog/formatters/text_formatter.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/srslog/log_backend_impl.h b/lib/src/srslog/log_backend_impl.h index 46c1a5ace..b4ff2d09c 100644 --- a/lib/src/srslog/log_backend_impl.h +++ b/lib/src/srslog/log_backend_impl.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,7 +38,7 @@ public: log_backend_impl(const log_backend_impl& other) = delete; log_backend_impl& operator=(const log_backend_impl& other) = delete; - void start() override { worker.start(); } + void start(backend_priority priority = backend_priority::normal) override { worker.start(priority); } bool push(detail::log_entry&& entry) override { diff --git a/lib/src/srslog/object_repository.h b/lib/src/srslog/object_repository.h index 26a07059d..96fd38ade 100644 --- a/lib/src/srslog/object_repository.h +++ b/lib/src/srslog/object_repository.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/srslog/sinks/buffered_file_sink.h b/lib/src/srslog/sinks/buffered_file_sink.h index 7640b043a..e825d71f3 100644 --- a/lib/src/srslog/sinks/buffered_file_sink.h +++ b/lib/src/srslog/sinks/buffered_file_sink.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/srslog/sinks/file_sink.h b/lib/src/srslog/sinks/file_sink.h index 6a30be191..70dbbb173 100644 --- a/lib/src/srslog/sinks/file_sink.h +++ b/lib/src/srslog/sinks/file_sink.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,9 +33,10 @@ namespace srslog { class file_sink : public sink { public: - file_sink(std::string name, size_t max_size, std::unique_ptr f) : + file_sink(std::string name, size_t max_size, bool force_flush, std::unique_ptr f) : sink(std::move(f)), max_size((max_size == 0) ? 0 : std::max(max_size, 4 * 1024)), + force_flush(force_flush), base_filename(std::move(name)) {} @@ -62,6 +63,10 @@ public: return err_str; } + if (force_flush) { + flush(); + } + return handler.write(buffer); } @@ -97,6 +102,7 @@ private: private: const size_t max_size; + const bool force_flush; const std::string base_filename; file_utils::file handler; size_t current_size = 0; diff --git a/lib/src/srslog/sinks/file_utils.h b/lib/src/srslog/sinks/file_utils.h index 17da58595..fe9f730aa 100644 --- a/lib/src/srslog/sinks/file_utils.h +++ b/lib/src/srslog/sinks/file_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/srslog/sinks/stream_sink.h b/lib/src/srslog/sinks/stream_sink.h index f9116f361..ed1d472d0 100644 --- a/lib/src/srslog/sinks/stream_sink.h +++ b/lib/src/srslog/sinks/stream_sink.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/srslog/sinks/syslog_sink.h b/lib/src/srslog/sinks/syslog_sink.h new file mode 100644 index 000000000..b9eaa5d7a --- /dev/null +++ b/lib/src/srslog/sinks/syslog_sink.h @@ -0,0 +1,106 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLOG_SYSLOG_SINK_H +#define SRSLOG_SYSLOG_SINK_H + +#include "srsran/srslog/shared_types.h" +#include "srsran/srslog/sink.h" +#include + +namespace srslog { + +/// This sink implementation writes to syslog. +class syslog_sink : public sink +{ +public: + syslog_sink(std::unique_ptr f, + std::string preamble_ = "", + syslog_local_type log_local_ = syslog_local_type::local0) : + sink(std::move(f)) + { + create_syslog(preamble_, syslog_translate(log_local_)); + } + + syslog_sink(const syslog_sink& other) = delete; + syslog_sink& operator=(const syslog_sink& other) = delete; + + detail::error_string write(detail::memory_buffer buffer) override + { + std::string entry(buffer.data(), buffer.size()); + if (entry.find("[E]") != std::string::npos) { + syslog(LOG_ERR, "%s", buffer.data()); + } else if (entry.find("[W]") != std::string::npos) { + syslog(LOG_WARNING, "%s", buffer.data()); + } else if (entry.find("[I]") != std::string::npos) { + syslog(LOG_INFO, "%s", buffer.data()); + } else if (entry.find("[D]") != std::string::npos) { + syslog(LOG_DEBUG, "%s", buffer.data()); + } else { + syslog(LOG_ERR, "%s", buffer.data()); + } + // openlog syslog does not return any value. + return {}; + } + + detail::error_string flush() override { return {}; } + +private: + /// Creates a new syslog + detail::error_string create_syslog(std::string preamble, int log_local) + { + if (preamble == "") { + openlog(NULL, LOG_CONS | LOG_PID | LOG_NDELAY, log_local); + } else { + openlog(preamble.c_str(), LOG_CONS | LOG_PID | LOG_NDELAY, log_local); + } + return {}; + } + + static int syslog_translate(syslog_local_type log_local) + { + switch (log_local) { + case syslog_local_type::local0: + return LOG_LOCAL0; + case syslog_local_type::local1: + return LOG_LOCAL1; + case syslog_local_type::local2: + return LOG_LOCAL2; + case syslog_local_type::local3: + return LOG_LOCAL3; + case syslog_local_type::local4: + return LOG_LOCAL4; + case syslog_local_type::local5: + return LOG_LOCAL5; + case syslog_local_type::local6: + return LOG_LOCAL6; + case syslog_local_type::local7: + return LOG_LOCAL7; + default: + return LOG_LOCAL0; + break; + } + }; +}; + +} // namespace srslog + +#endif // SRSLOG_SYSLOG_SINK_H diff --git a/lib/src/srslog/srslog.cpp b/lib/src/srslog/srslog.cpp index 5d7dd85d6..5296a9a97 100644 --- a/lib/src/srslog/srslog.cpp +++ b/lib/src/srslog/srslog.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #include "srsran/srslog/srslog.h" #include "formatters/json_formatter.h" #include "sinks/file_sink.h" +#include "sinks/syslog_sink.h" #include "srslog_instance.h" using namespace srslog; @@ -156,7 +157,10 @@ sink& srslog::fetch_stderr_sink(const std::string& id, std::unique_ptr f) +sink& srslog::fetch_file_sink(const std::string& path, + size_t max_size, + bool force_flush, + std::unique_ptr f) { assert(!path.empty() && "Empty path string"); @@ -169,7 +173,26 @@ sink& srslog::fetch_file_sink(const std::string& path, size_t max_size, std::uni auto& s = srslog_instance::get().get_sink_repo().emplace( std::piecewise_construct, std::forward_as_tuple(path), - std::forward_as_tuple(new file_sink(path, max_size, std::move(f)))); + std::forward_as_tuple(new file_sink(path, max_size, force_flush, std::move(f)))); + + return *s; +} + +sink& srslog::fetch_syslog_sink(const std::string& preamble_, + syslog_local_type log_local_, + std::unique_ptr f) +{ + std::string sink_id = preamble_ + std::to_string(static_cast(log_local_)); + if (auto* s = find_sink(sink_id)) { + return *s; + } + + //: TODO: GCC5 or lower versions emits an error if we use the new() expression + // directly, use redundant piecewise_construct instead. + auto& s = srslog_instance::get().get_sink_repo().emplace( + std::piecewise_construct, + std::forward_as_tuple(sink_id), + std::forward_as_tuple(new syslog_sink(std::move(f), preamble_, log_local_))); return *s; } @@ -190,9 +213,9 @@ bool srslog::install_custom_sink(const std::string& id, std::unique_ptr s) /// Framework configuration and control function implementations. /// -void srslog::init() +void srslog::init(backend_priority priority) { - srslog_instance::get().get_backend().start(); + srslog_instance::get().get_backend().start(priority); } void srslog::flush() @@ -377,7 +400,8 @@ sink* srslog::create_file_sink(const std::string& path, size_t max_size) .get_sink_repo() .emplace(std::piecewise_construct, std::forward_as_tuple(path), - std::forward_as_tuple(new file_sink(path, max_size, std::unique_ptr(new text_formatter)))) + std::forward_as_tuple( + new file_sink(path, max_size, false, std::unique_ptr(new text_formatter)))) .get(); } diff --git a/lib/src/srslog/srslog_c.cpp b/lib/src/srslog/srslog_c.cpp index bd033addf..abcd84761 100644 --- a/lib/src/srslog/srslog_c.cpp +++ b/lib/src/srslog/srslog_c.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -190,7 +190,7 @@ srslog_sink* srslog_fetch_stderr_sink(void) return c_cast(&fetch_stderr_sink()); } -srslog_sink* srslog_fetch_file_sink(const char* path, size_t max_size) +srslog_sink* srslog_fetch_file_sink(const char* path, size_t max_size, srslog_bool force_flush) { - return c_cast(&fetch_file_sink(path, max_size)); + return c_cast(&fetch_file_sink(path, max_size, force_flush)); } diff --git a/lib/src/srslog/srslog_instance.h b/lib/src/srslog/srslog_instance.h index 96959aa38..8cf968f4d 100644 --- a/lib/src/srslog/srslog_instance.h +++ b/lib/src/srslog/srslog_instance.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/src/support/CMakeLists.txt b/lib/src/support/CMakeLists.txt new file mode 100644 index 000000000..4c00f229c --- /dev/null +++ b/lib/src/support/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES emergency_handlers.cc + signal_handler.cc) + +add_library(support STATIC ${SOURCES}) diff --git a/lib/src/support/emergency_handlers.cc b/lib/src/support/emergency_handlers.cc new file mode 100644 index 000000000..205b56282 --- /dev/null +++ b/lib/src/support/emergency_handlers.cc @@ -0,0 +1,81 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/support/emergency_handlers.h" +#include "srsran/config.h" +#include "srsran/support/srsran_assert.h" + +namespace { + +/// Holds the callback function pointer and the associated user provided data pointer. +struct handler_instance { + std::atomic data{}; + std::atomic callback{}; +}; + +} // namespace + +// Handlers are added in a thread safe manner without using locks to avoid possible issues if a signal is emitted while +// modifying the callback array. +static constexpr unsigned max_handlers = 256; +static handler_instance registered_handlers[max_handlers]; +static std::atomic num_handlers; + +int add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data) +{ + // Reserve a slot in the array. + auto pos = num_handlers.fetch_add(1); + + // Check if we have space in the array. + if (pos >= max_handlers) { + srsran_assert(0, "Exceeded the emergency cleanup handler registered limit"); + return SRSRAN_ERROR; + } + + // Order is important here: write last the callback member as it is used to signal that the handler is valid when + // reading the array. + registered_handlers[pos].data.store(data); + registered_handlers[pos].callback.store(callback); + + return pos; +} + +void remove_emergency_cleanup_handler(int id) +{ + if (id < 0 || static_cast(id) >= num_handlers) { + srsran_assert(0, "Invalid emergency handler id"); + return; + } + + registered_handlers[id].callback.store(nullptr); +} + +void execute_emergency_cleanup_handlers() +{ + for (unsigned i = 0, e = num_handlers; i != e; ++i) { + auto callback = registered_handlers[i].callback.load(); + // Test the validity of the callback as it may have not been written yet into the array even if num_callbacks has + // been updated. + if (callback) { + callback(registered_handlers[i].data.load()); + } + } +} diff --git a/lib/include/srsran/common/signal_handler.h b/lib/src/support/signal_handler.cc similarity index 57% rename from lib/include/srsran/common/signal_handler.h rename to lib/src/support/signal_handler.cc index 03b9a356d..418a59dcd 100644 --- a/lib/include/srsran/common/signal_handler.h +++ b/lib/src/support/signal_handler.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,62 +19,47 @@ * */ -/** - * @file signal_handler.h - * @brief Common signal handling methods for all srsRAN applications. - */ - -#ifndef SRSRAN_SIGNAL_HANDLER_H -#define SRSRAN_SIGNAL_HANDLER_H - -#include "srsran/srslog/sink.h" -#include "srsran/srslog/srslog.h" -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus +#include "srsran/support/signal_handler.h" +#include "srsran/support/emergency_handlers.h" +#include +#include +#include +#include +#ifndef SRSRAN_TERM_TIMEOUT_S #define SRSRAN_TERM_TIMEOUT_S (5) +#endif -// static vars required by signal handling -static srslog::sink* log_sink = nullptr; -static bool running = true; +/// Handler called after the user interrupts the program. +static std::atomic user_handler; static void srsran_signal_handler(int signal) { switch (signal) { case SIGALRM: fprintf(stderr, "Couldn't stop after %ds. Forcing exit.\n", SRSRAN_TERM_TIMEOUT_S); - srslog::flush(); - //:TODO: refactor the sighandler, should not depend on log utilities - if (log_sink) { - log_sink->flush(); - } + execute_emergency_cleanup_handlers(); raise(SIGKILL); default: // all other registered signals try to stop the app gracefully - if (running) { - running = false; - fprintf(stdout, "Stopping ..\n"); - alarm(SRSRAN_TERM_TIMEOUT_S); + // Call the user handler if present and remove it so that further signals are treated by the default handler. + if (auto handler = user_handler.exchange(nullptr)) { + handler(); } else { - // already waiting for alarm to go off .. + return; } + fprintf(stdout, "Stopping ..\n"); + alarm(SRSRAN_TERM_TIMEOUT_S); break; } } -void srsran_register_signal_handler() +void srsran_register_signal_handler(srsran_signal_hanlder handler) { + user_handler.store(handler); + signal(SIGINT, srsran_signal_handler); signal(SIGTERM, srsran_signal_handler); signal(SIGHUP, srsran_signal_handler); signal(SIGALRM, srsran_signal_handler); } - -#ifdef __cplusplus -} -#endif // __cplusplus -#endif // SRSRAN_SIGNAL_HANDLER_H diff --git a/lib/src/system/CMakeLists.txt b/lib/src/system/CMakeLists.txt index 6a24a0c83..5b99cb804 100644 --- a/lib/src/system/CMakeLists.txt +++ b/lib/src/system/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/lib/src/system/sys_metrics_processor.cc b/lib/src/system/sys_metrics_processor.cc index ee732b91a..7523a769d 100644 --- a/lib/src/system/sys_metrics_processor.cc +++ b/lib/src/system/sys_metrics_processor.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -58,16 +58,30 @@ sys_metrics_processor::proc_stats_info::proc_stats_info() arg_end >> env_start >> env_end >> exit_code; } +/// Returns a null sys_metrics_t with the cpu count field filled. +static sys_metrics_t create_null_metrics() +{ + sys_metrics_t metrics; + + if (cpu_count > metrics_max_supported_cpu) { + return metrics; + } + + metrics.cpu_count = cpu_count; + metrics.cpu_load.fill(0.f); + return metrics; +} + sys_metrics_t sys_metrics_processor::get_metrics() { auto current_time = std::chrono::steady_clock::now(); uint32_t measure_interval_ms = std::chrono::duration_cast(current_time - last_query_time).count(); - // The time elapsed between 2 measures must be greater that 100 milliseconds. - if (measure_interval_ms < 100u) { - logger.warning("Interval less than 100ms, skipping measurement."); - return {}; + // The time elapsed between 2 measures must be greater that 10 milliseconds. + if (measure_interval_ms < 10u) { + logger.info("Interval less than 10ms, skipping measurement."); + return create_null_metrics(); } sys_metrics_t metrics; diff --git a/lib/src/upper/rlc_am_nr.cc b/lib/src/upper/rlc_am_nr.cc deleted file mode 100644 index a0cf4267f..000000000 --- a/lib/src/upper/rlc_am_nr.cc +++ /dev/null @@ -1,259 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/upper/rlc_am_nr.h" -#include - -namespace srsran { - -/**************************************************************************** - * Header pack/unpack helper functions - * Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4 - ***************************************************************************/ - -uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_pdu_header_t* header) -{ - return rlc_am_nr_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header); -} - -uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload, - const uint32_t nof_bytes, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_pdu_header_t* header) -{ - uint8_t* ptr = const_cast(payload); - - header->sn_size = sn_size; - - // Fixed part - header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01); // 1 bit D/C field - header->p = (*ptr >> 6) & 0x01; // 1 bit P flag - header->si = (rlc_nr_si_field_t)((*ptr >> 4) & 0x03); // 2 bits SI - - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - header->sn = (*ptr & 0x0F) << 8; // first 4 bits SN - ptr++; - - header->sn |= (*ptr & 0xFF); // last 8 bits SN - ptr++; - } else if (sn_size == rlc_am_nr_sn_size_t::size18bits) { - // sanity check - if ((*ptr & 0x0c) != 0) { - fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); - return 0; - } - header->sn = (*ptr & 0x03) << 16; // first 4 bits SN - ptr++; - header->sn |= (*ptr & 0xFF) << 8; // bit 2-10 of SN - ptr++; - header->sn |= (*ptr & 0xFF); // last 8 bits SN - ptr++; - } else { - fprintf(stderr, "Unsupported SN length\n"); - return 0; - } - - // Read optional part - if (header->si == rlc_nr_si_field_t::last_segment || - header->si == rlc_nr_si_field_t::neither_first_nor_last_segment) { - // read SO - header->so = (*ptr & 0xFF) << 8; - ptr++; - header->so |= (*ptr & 0xFF); - ptr++; - } - - // return consumed bytes - return (ptr - payload); -} - -uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header) -{ - uint32_t len = 0; - if (header.si == rlc_nr_si_field_t::full_sdu || header.si == rlc_nr_si_field_t::first_segment) { - len = 2; - if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) { - len++; - } - } else { - // PDU contains SO - len = 4; - if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) { - len++; - } - } - return len; -} - -uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu) -{ - // Make room for the header - uint32_t len = rlc_am_nr_packed_length(header); - pdu->msg -= len; - uint8_t* ptr = pdu->msg; - - // fixed header part - *ptr = (header.dc & 0x01) << 7; ///< 1 bit D/C field - *ptr |= (header.p & 0x01) << 6; ///< 1 bit P flag - *ptr |= (header.si & 0x03) << 4; ///< 2 bits SI - - if (header.sn_size == rlc_am_nr_sn_size_t::size12bits) { - // write first 4 bit of SN - *ptr |= (header.sn >> 8) & 0x0f; // 4 bit SN - ptr++; - *ptr = header.sn & 0xff; // remaining 8 bit of SN - ptr++; - } else { - // 18bit SN - *ptr |= (header.sn >> 16) & 0x3; // 2 bit SN - ptr++; - *ptr = header.sn >> 8; // bit 3 - 10 of SN - ptr++; - *ptr = (header.sn & 0xff); // remaining 8 bit of SN - ptr++; - } - - if (header.so) { - // write SO - *ptr = header.so >> 8; // first part of SO - ptr++; - *ptr = (header.so & 0xff); // second part of SO - ptr++; - } - - pdu->N_bytes += ptr - pdu->msg; - - return len; -} - -uint32_t -rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status) -{ - return rlc_am_nr_read_status_pdu(pdu->msg, pdu->N_bytes, sn_size, status); -} - -uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload, - const uint32_t nof_bytes, - const rlc_am_nr_sn_size_t sn_size, - rlc_am_nr_status_pdu_t* status) -{ - uint8_t* ptr = const_cast(payload); - - // fixed part - status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT - - // sanity check - if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) { - fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); - return 0; - } - - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN - ptr++; - - status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN - ptr++; - - // read E1 flag - uint8_t e1 = *ptr & 0x80; - - // sanity check for reserved bits - if ((*ptr & 0x7f) != 0) { - fprintf(stderr, "Malformed PDU, reserved bits are set.\n"); - return 0; - } - - // all good, continue with next byte depending on E1 - ptr++; - - // reset number of acks - status->N_nack = 0; - - if (e1) { - // E1 flag set, read a NACK_SN - rlc_status_nack_t nack = {}; - nack.nack_sn = (*ptr & 0xff) << 4; - ptr++; - // uint8_t len2 = (*ptr & 0xF0) >> 4; - nack.nack_sn |= (*ptr & 0xF0) >> 4; - status->nacks[status->N_nack] = nack; - - status->N_nack++; - } - } - - return SRSRAN_SUCCESS; -} - -/** - * Write a RLC AM NR status PDU to a PDU buffer and eets the length of the generate PDU accordingly - * @param status_pdu The status PDU - * @param pdu A pointer to a unique bytebuffer - * @return SRSRAN_SUCCESS if PDU was written, SRSRAN_ERROR otherwise - */ -int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu, - const rlc_am_nr_sn_size_t sn_size, - byte_buffer_t* pdu) -{ - uint8_t* ptr = pdu->msg; - - // fixed header part - *ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero - - if (sn_size == rlc_am_nr_sn_size_t::size12bits) { - // write first 4 bit of ACK_SN - *ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN - ptr++; - *ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN - ptr++; - - // write E1 flag in octet 3 - *ptr = (status_pdu.N_nack > 0) ? 0x80 : 0x00; - ptr++; - - if (status_pdu.N_nack > 0) { - // write first 8 bit of NACK_SN - *ptr = (status_pdu.nacks[0].nack_sn >> 4) & 0xff; - ptr++; - - // write remaining 4 bits of NACK_SN - *ptr = status_pdu.nacks[0].nack_sn & 0xf0; - ptr++; - } - } else { - // 18bit SN - *ptr |= (status_pdu.ack_sn >> 14) & 0x0f; // 4 bit ACK_SN - ptr++; - *ptr = status_pdu.ack_sn >> 8; // bit 3 - 10 of SN - ptr++; - *ptr = (status_pdu.ack_sn & 0xff); // remaining 6 bit of SN - ptr++; - } - - pdu->N_bytes = ptr - pdu->msg; - - return SRSRAN_SUCCESS; -} - -} // namespace srsran diff --git a/lib/test/CMakeLists.txt b/lib/test/CMakeLists.txt index e70003817..23928613c 100644 --- a/lib/test/CMakeLists.txt +++ b/lib/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -20,8 +20,8 @@ add_subdirectory(asn1) add_subdirectory(common) -add_subdirectory(mac) add_subdirectory(phy) add_subdirectory(srslog) -add_subdirectory(upper) +add_subdirectory(rlc) +add_subdirectory(pdcp) add_subdirectory(adt) diff --git a/lib/test/adt/CMakeLists.txt b/lib/test/adt/CMakeLists.txt index 3a7a034ec..5d0be9e08 100644 --- a/lib/test/adt/CMakeLists.txt +++ b/lib/test/adt/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -69,3 +69,11 @@ add_test(fsm_test fsm_test) add_executable(optional_test optional_test.cc) target_link_libraries(optional_test srsran_common) add_test(optional_test optional_test) + +add_executable(cached_alloc_test cached_alloc_test.cc) +target_link_libraries(cached_alloc_test srsran_common) +add_test(cached_alloc_test cached_alloc_test) + +add_executable(optional_array_test optional_array_test.cc) +target_link_libraries(optional_array_test srsran_common) +add_test(optional_array_test optional_array_test) diff --git a/lib/test/adt/bounded_bitset_test.cc b/lib/test/adt/bounded_bitset_test.cc index 1e1006703..67cdac643 100644 --- a/lib/test/adt/bounded_bitset_test.cc +++ b/lib/test/adt/bounded_bitset_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/adt/bounded_vector_test.cc b/lib/test/adt/bounded_vector_test.cc index c58211d87..95c537833 100644 --- a/lib/test/adt/bounded_vector_test.cc +++ b/lib/test/adt/bounded_vector_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,7 +20,7 @@ */ #include "srsran/adt/bounded_vector.h" -#include "srsran/common/test_common.h" +#include "srsran/support/srsran_test.h" namespace srsran { @@ -61,7 +61,7 @@ struct moveonly { moveonly& operator=(moveonly&&) noexcept = default; }; -int test_ctor() +void test_ctor() { // TEST: default ctor bounded_vector a; @@ -97,11 +97,9 @@ int test_ctor() bounded_vector a6(std::move(a5)); TESTASSERT(a6.size() == 7); TESTASSERT(a5.size() == 0); - - return SRSRAN_SUCCESS; } -int test_obj_add_rem() +void test_obj_add_rem() { // TEST: push_back / emplace_back bounded_vector a; @@ -163,11 +161,9 @@ int test_obj_add_rem() a2.clear(); a = std::move(a2); TESTASSERT(a.empty() and a2.empty()); - - return SRSRAN_SUCCESS; } -int test_move_only_type() +void test_move_only_type() { bounded_vector a(5); TESTASSERT(a.size() == 5); @@ -184,24 +180,21 @@ int test_move_only_type() a2.push_back(moveonly()); TESTASSERT(a2.size() == 7); - - return SRSRAN_SUCCESS; } -int assert_dtor_consistency() +void assert_dtor_consistency() { TESTASSERT(C::nof_dtor == C::nof_copy_ctor + C::nof_value_ctor + C::nof_move_ctor); - return SRSRAN_SUCCESS; } } // namespace srsran int main() { - TESTASSERT(srsran::test_ctor() == SRSRAN_SUCCESS); - TESTASSERT(srsran::test_obj_add_rem() == SRSRAN_SUCCESS); - TESTASSERT(srsran::test_move_only_type() == SRSRAN_SUCCESS); - TESTASSERT(srsran::assert_dtor_consistency() == SRSRAN_SUCCESS); + srsran::test_ctor(); + srsran::test_obj_add_rem(); + srsran::test_move_only_type(); + srsran::assert_dtor_consistency(); printf("Success\n"); return 0; } \ No newline at end of file diff --git a/lib/test/adt/cached_alloc_test.cc b/lib/test/adt/cached_alloc_test.cc new file mode 100644 index 000000000..f36ab14c0 --- /dev/null +++ b/lib/test/adt/cached_alloc_test.cc @@ -0,0 +1,113 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/adt/pool/cached_alloc.h" +#include "srsran/common/test_common.h" +#include + +void test_cached_deque_basic_operations() +{ + srsran::deque my_deque; + TESTASSERT(my_deque.empty() and my_deque.size() == 0); + my_deque.push_front(0); + my_deque.push_back(1); + TESTASSERT(my_deque.front() == 0 and my_deque.back() == 1); + TESTASSERT(my_deque.size() == 2); + + srsran::deque my_deque2(my_deque); + TESTASSERT(my_deque == my_deque2); + my_deque.clear(); + TESTASSERT(my_deque != my_deque2); + TESTASSERT(my_deque2.size() == 2 and my_deque2.back() == 1); + TESTASSERT(my_deque.empty()); + + my_deque = my_deque2; + TESTASSERT(my_deque == my_deque2); + my_deque2.clear(); + TESTASSERT(my_deque2.empty()); + + my_deque2 = std::move(my_deque); + TESTASSERT(my_deque.empty() and my_deque2.size() == 2); +} + +struct C { + C() = default; + C(C&&) noexcept = default; + C(const C&) = delete; + C& operator=(C&&) noexcept = default; + C& operator=(const C&) = delete; + + bool operator==(const C& other) { return true; } +}; + +void test_cached_queue_basic_operations() +{ + srsran::queue my_queue; + TESTASSERT(my_queue.empty()); + my_queue.push(C{}); + TESTASSERT(my_queue.size() == 1); + + srsran::queue my_queue2(std::move(my_queue)); + TESTASSERT(my_queue2.size() == 1); +} + +void cached_deque_benchmark() +{ + using std::chrono::high_resolution_clock; + using std::chrono::microseconds; + + srsran::queue my_deque; + std::queue std_deque; + high_resolution_clock::time_point tp; + + size_t N = 10000000, n_elems = 10; + + for (size_t i = 0; i < n_elems; ++i) { + my_deque.push(i); + std_deque.push(i); + } + + // NOTE: this benchmark doesnt account for when memory is fragmented + tp = high_resolution_clock::now(); + for (size_t i = n_elems; i < N; ++i) { + std_deque.push(i); + std_deque.pop(); + } + microseconds t_std = std::chrono::duration_cast(high_resolution_clock::now() - tp); + + tp = high_resolution_clock::now(); + for (size_t i = n_elems; i < N; ++i) { + my_deque.push(i); + my_deque.pop(); + } + microseconds t_cached = std::chrono::duration_cast(high_resolution_clock::now() - tp); + + fmt::print("Time elapsed: cached alloc={} usec, std alloc={} usec\n", t_cached.count(), t_std.count()); + fmt::print("queue sizes: {} {}\n", my_deque.size(), std_deque.size()); +} + +int main() +{ + test_cached_deque_basic_operations(); + test_cached_queue_basic_operations(); + cached_deque_benchmark(); + return 0; +} \ No newline at end of file diff --git a/lib/test/adt/circular_buffer_test.cc b/lib/test/adt/circular_buffer_test.cc index bac5e5318..76f97a1fd 100644 --- a/lib/test/adt/circular_buffer_test.cc +++ b/lib/test/adt/circular_buffer_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -216,7 +216,7 @@ void test_dyn_circular_buffer() TESTASSERT(C::count == 0); } -int test_queue_block_api() +void test_queue_block_api() { dyn_blocking_queue queue(100); @@ -238,31 +238,26 @@ int test_queue_block_api() queue.stop(); t.join(); - return SRSRAN_SUCCESS; } -int test_queue_block_api_2() +void test_queue_block_api_2() { std::thread t; - { - dyn_blocking_queue queue(100); + dyn_blocking_queue queue(100); - t = std::thread([&queue]() { - int count = 0; - while (queue.push_blocking(count++)) { - } - }); - - for (int i = 0; i < 10000; ++i) { - TESTASSERT(queue.pop_blocking() == i); + t = std::thread([&queue]() { + int count = 0; + while (queue.push_blocking(count++)) { } + }); - // queue dtor called + for (int i = 0; i < 10000; ++i) { + TESTASSERT(queue.pop_blocking() == i); } + queue.stop(); t.join(); - return SRSRAN_SUCCESS; } } // namespace srsran @@ -276,8 +271,8 @@ int main(int argc, char** argv) TESTASSERT(srsran::test_static_circular_buffer() == SRSRAN_SUCCESS); srsran::test_dyn_circular_buffer(); - TESTASSERT(srsran::test_queue_block_api() == SRSRAN_SUCCESS); - TESTASSERT(srsran::test_queue_block_api_2() == SRSRAN_SUCCESS); + srsran::test_queue_block_api(); + srsran::test_queue_block_api_2(); srsran::console("Success\n"); return SRSRAN_SUCCESS; } \ No newline at end of file diff --git a/lib/test/adt/circular_map_test.cc b/lib/test/adt/circular_map_test.cc index a852f8af5..523b1737e 100644 --- a/lib/test/adt/circular_map_test.cc +++ b/lib/test/adt/circular_map_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/adt/expected_test.cc b/lib/test/adt/expected_test.cc index fcc393447..81973ffcc 100644 --- a/lib/test/adt/expected_test.cc +++ b/lib/test/adt/expected_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/adt/fsm_test.cc b/lib/test/adt/fsm_test.cc index 6c3325198..da27189f2 100644 --- a/lib/test/adt/fsm_test.cc +++ b/lib/test/adt/fsm_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/adt/interval_test.cc b/lib/test/adt/interval_test.cc index c85c20806..4525e6869 100644 --- a/lib/test/adt/interval_test.cc +++ b/lib/test/adt/interval_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/adt/mem_pool_test.cc b/lib/test/adt/mem_pool_test.cc index 764d09e11..c59370655 100644 --- a/lib/test/adt/mem_pool_test.cc +++ b/lib/test/adt/mem_pool_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/adt/observer_test.cc b/lib/test/adt/observer_test.cc index 6a7b3b716..bb8f3902c 100644 --- a/lib/test/adt/observer_test.cc +++ b/lib/test/adt/observer_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/adt/optional_array_test.cc b/lib/test/adt/optional_array_test.cc new file mode 100644 index 000000000..da53190eb --- /dev/null +++ b/lib/test/adt/optional_array_test.cc @@ -0,0 +1,132 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/adt/optional_array.h" +#include "srsran/common/test_common.h" + +namespace srsran { + +void test_optional_array() +{ + optional_array table1; + TESTASSERT(table1.size() == 0 and table1.empty()); + + TESTASSERT(not table1.contains(0)); + table1.insert(0, 5); + TESTASSERT(table1.size() == 1 and not table1.empty()); + table1.erase(0); + TESTASSERT(table1.size() == 0 and table1.empty()); + table1.insert(1, 3); + table1.insert(4, 2); + TESTASSERT(table1.size() == 2); + TESTASSERT(table1[4] == 2 and table1[1] == 3); + + size_t count = 0; + int array[] = {3, 2}; + for (int e : table1) { + TESTASSERT(array[count++] == e); + } + + auto it = table1.begin(); + TESTASSERT(*it == 3); + table1.erase(it); + TESTASSERT(table1.size() == 1); +} + +void test_optional_vector() +{ + optional_vector table1; + TESTASSERT(table1.size() == 0 and table1.empty()); + + TESTASSERT(not table1.contains(0)); + table1.insert(0, 5); + TESTASSERT(table1.size() == 1 and not table1.empty()); + table1.erase(0); + TESTASSERT(table1.size() == 0 and table1.empty()); + table1.insert(1, 3); + table1.insert(4, 2); + TESTASSERT(table1.size() == 2); + TESTASSERT(table1[4] == 2 and table1[1] == 3); + + size_t count = 0; + int array[] = {3, 2}; + for (int e : table1) { + TESTASSERT(array[count++] == e); + } + + auto it = table1.begin(); + TESTASSERT(*it == 3); + table1.erase(it); + TESTASSERT(table1.size() == 1); +} + +void test_split_optional_span() +{ + constexpr size_t L = 7; + int some_list[L] = {}; + bool some_list_presence[L] = {}; + split_optional_span view(some_list, some_list_presence, L); + + TESTASSERT(view.size() == 0 and view.empty()); + TESTASSERT(view.begin() == view.end()); + TESTASSERT(not view.contains(0)); + TESTASSERT(view.find_first_empty() == L); + + view.insert(1, 1); + TESTASSERT(view.size() == 1 and not view.empty()); + TESTASSERT(view.begin() != view.end() and *view.begin() == 1); + TESTASSERT(view.contains(1)); + TESTASSERT(view[1] == 1); + TESTASSERT(view.find_first_empty() == 1); + + view.insert(3, 3); + TESTASSERT(view[3] == 3); + size_t c = 0; + for (auto& e : view) { + TESTASSERT(c == 0 ? e == 1 : e == 3); + c++; + } + TESTASSERT(view.size() == 2); + + view.erase(view.begin()); + TESTASSERT(view.size() == 1); + TESTASSERT(not view.contains(1) and view.contains(3)); + + view.clear(); + TESTASSERT(view.empty()); +} + +} // namespace srsran + +int main(int argc, char** argv) +{ + auto& test_log = srslog::fetch_basic_logger("TEST"); + test_log.set_level(srslog::basic_levels::info); + + srsran::test_init(argc, argv); + + srsran::test_optional_array(); + srsran::test_optional_vector(); + srsran::test_split_optional_span(); + + printf("Success\n"); + return SRSRAN_SUCCESS; +} diff --git a/lib/test/adt/optional_test.cc b/lib/test/adt/optional_test.cc index 116d7c878..d2bce2e60 100644 --- a/lib/test/adt/optional_test.cc +++ b/lib/test/adt/optional_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,7 +38,26 @@ void test_optional_int() TESTASSERT(opt == opt2); } +struct C { + std::unique_ptr val; + + C(int val = 0) : val(std::make_unique(val)) {} +}; + +void test_optional_move_only() +{ + optional a, b; + a.emplace(C{}); + TESTASSERT(a.has_value()); + TESTASSERT_EQ(0, *a.value().val); + TESTASSERT(not b.has_value()); + b.emplace(C{5}); + a = std::move(b); + TESTASSERT_EQ(5, *a.value().val); +} + int main() { test_optional_int(); + test_optional_move_only(); } \ No newline at end of file diff --git a/lib/test/adt/scope_exit_test.cc b/lib/test/adt/scope_exit_test.cc index 28c9116cc..a5b3083d7 100644 --- a/lib/test/adt/scope_exit_test.cc +++ b/lib/test/adt/scope_exit_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/adt/span_test.cc b/lib/test/adt/span_test.cc index c30eab9a0..8bf10d11e 100644 --- a/lib/test/adt/span_test.cc +++ b/lib/test/adt/span_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/asn1/CMakeLists.txt b/lib/test/asn1/CMakeLists.txt index 86247309a..102d4f120 100644 --- a/lib/test/asn1/CMakeLists.txt +++ b/lib/test/asn1/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -19,7 +19,7 @@ # add_executable(s1ap_asn1_test s1ap_test.cc) -target_link_libraries(s1ap_asn1_test srsran_common srsran_asn1 s1ap_asn1) +target_link_libraries(s1ap_asn1_test srsran_common srsran_asn1 s1ap_asn1 ${ATOMIC_LIBS}) add_test(s1ap_asn1_test s1ap_asn1_test) add_executable(srsran_asn1_rrc_mcch_test srsran_asn1_rrc_mcch_test.cc) @@ -54,9 +54,9 @@ add_executable(rrc_asn1_test rrc_test.cc) target_link_libraries(rrc_asn1_test rrc_asn1 asn1_utils srsran_common) add_test(rrc_asn1_test rrc_asn1_test) -add_executable(rrc_nr_asn1_test rrc_nr_test.cc) -target_link_libraries(rrc_nr_asn1_test rrc_nr_asn1 asn1_utils srsran_common) -add_test(rrc_nr_asn1_test rrc_nr_asn1_test) +add_executable(srsran_asn1_rrc_nr_test srsran_asn1_rrc_nr_test.cc) +target_link_libraries(srsran_asn1_rrc_nr_test rrc_nr_asn1 asn1_utils srsran_common srsran_mac) +add_test(srsran_asn1_rrc_nr_test srsran_asn1_rrc_nr_test) add_executable(ngap_asn1_test ngap_test.cc) target_link_libraries(ngap_asn1_test ngap_nr_asn1 srsran_common) @@ -71,3 +71,6 @@ target_link_libraries(rrc_asn1_decoder rrc_asn1) add_executable(nas_decoder nas_decoder.cc) target_link_libraries(nas_decoder srsran_asn1) + +add_executable(nas_5g_msg_test nas_5g_msg_test.cc) +target_link_libraries(nas_5g_msg_test nas_5g_msg) \ No newline at end of file diff --git a/lib/test/asn1/asn1_utils_test.cc b/lib/test/asn1/asn1_utils_test.cc index 6a680193b..cc7dd150b 100644 --- a/lib/test/asn1/asn1_utils_test.cc +++ b/lib/test/asn1/asn1_utils_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -404,7 +404,7 @@ int test_seq_of() cbit_ref borig2(&buf[0], sizeof(buf)); uint32_t fixed_list_size = 33; - std::array fixed_list; + std::array fixed_list = {}; for (uint32_t i = 0; i < fixed_list_size; ++i) { fixed_list[i] = i; } @@ -653,6 +653,21 @@ int test_big_integers() return 0; } +void test_varlength_field_pack() +{ + uint8_t buffer[128]; + bit_ref bref(&buffer[0], sizeof(buffer)); + TESTASSERT_EQ(SRSRAN_SUCCESS, bref.pack(0, 1)); + TESTASSERT_EQ(1, bref.distance()); + { + varlength_field_pack_guard guard(bref); + TESTASSERT_EQ(0, bref.distance()); + bref.pack(0, 8); + TESTASSERT_EQ(1, bref.distance_bytes()); + } + TESTASSERT_EQ(17, bref.distance()); // accounts for length determinant and 1 byte of data +} + int main() { // Setup the log spy to intercept error and warning log entries. @@ -681,6 +696,7 @@ int main() TESTASSERT(test_copy_ptr() == 0); TESTASSERT(test_enum() == 0); TESTASSERT(test_big_integers() == 0); + test_varlength_field_pack(); // TESTASSERT(test_json_writer()==0); srslog::flush(); diff --git a/lib/test/asn1/nas_5g_msg_test.cc b/lib/test/asn1/nas_5g_msg_test.cc new file mode 100644 index 000000000..40cce3138 --- /dev/null +++ b/lib/test/asn1/nas_5g_msg_test.cc @@ -0,0 +1,1598 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include + +#include "srsran/asn1/nas_5g_msg.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/nas_pcap.h" +#include "srsran/common/test_common.h" +#include "srsran/srslog/srslog.h" + +using namespace srsran::nas_5g; + +#define HAVE_PCAP 0 + +inline void print_msg(const srsran::unique_byte_buffer_t& msg) +{ + printf("\t"); + for (uint32_t i = 0; i < msg->N_bytes; i++) { + printf("0x%02x ", msg->msg[i]); + if ((i + 1) % 16 == 0) { + printf("\n\t"); + } + } + printf("\n"); +} + +inline void hex_dump(uint8_t* buf, uint32_t buf_length) +{ + printf("\t"); + for (uint32_t i = 0; i < buf_length; i++) { + printf("0x%02x ", buf[i]); + if ((i + 1) % 16 == 0) { + printf("\n\t"); + } + } + printf("\n"); +} + +int registration_request_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + uint8_t reg_request[] = {0x7e, 0x00, 0x41, 0x79, 0x00, 0x0b, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x2e, 0x02, 0xf0, 0xf0, 0x17, 0x07, 0xf0, 0xf0, 0xc0, 0xc0, 0x01, 0x80, 0x30}; + // Non-Access-Stratum 5GS (NAS)PDU + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Registration request (0x41) + // 5GS registration type + // .... 1... = Follow-On Request bit (FOR): Follow-on request pending + // .... .001 = 5GS registration type: initial registration (1) + // NAS key set identifier + // 0... .... = Type of security context flag (TSC): Native security context (for KSIAMF) + // .111 .... = NAS key set identifier: 7 + // 5GS mobile identity + // Length: 11 + // .... 0... = Odd/even indication: Even number of identity digits + // .... .010 = Type of identity: 5G-GUTI (2) + // Mobile Country Code (MCC): Unknown (0) + // Mobile Network Code (MNC): Unknown (000) + // AMF Region ID: 0 + // 0000 0000 00.. .... = AMF Set ID: 0 + // ..00 0000 = AMF Pointer: 0 + // 5G-TMSI: 0 (0x00000000) + // + // UE security capability + // Element ID: 0x2e + // Length: 2 + // 1... .... = 5G-EA0: Supported + // .1.. .... = 128-5G-EA1: Supported + // ..1. .... = 128-5G-EA2: Supported + // ...1 .... = 128-5G-EA3: Supported + // .... 0... = 5G-EA4: Not supported + // .... .0.. = 5G-EA5: Not supported + // .... ..0. = 5G-EA6: Not supported + // .... ...0 = 5G-EA7: Not supported + // 1... .... = 5G-IA0: Supported + // .1.. .... = 128-5G-IA1: Supported + // ..1. .... = 128-5G-IA2: Supported + // ...1 .... = 128-5G-IA3: Supported + // .... 0... = 5G-IA4: Not supported + // .... .0.. = 5G-IA5: Not supported + // .... ..0. = 5G-IA6: Not supported + // .... ...0 = 5G-IA7: Not supported + // UE network capability + // Element ID: 0x17 + // Length: 7 + // 1... .... = EEA0: Supported + // .1.. .... = 128-EEA1: Supported + // ..1. .... = 128-EEA2: Supported + // ...1 .... = 128-EEA3: Supported + // .... 0... = EEA4: Not supported + // .... .0.. = EEA5: Not supported + // .... ..0. = EEA6: Not supported + // .... ...0 = EEA7: Not supported + // 1... .... = EIA0: Supported + // .1.. .... = 128-EIA1: Supported + // ..1. .... = 128-EIA2: Supported + // ...1 .... = 128-EIA3: Supported + // .... 0... = EIA4: Not supported + // .... .0.. = EIA5: Not supported + // .... ..0. = EIA6: Not supported + // .... ...0 = EIA7: Not supported + // 1... .... = UEA0: Supported + // .1.. .... = UEA1: Supported + // ..0. .... = UEA2: Not supported + // ...0 .... = UEA3: Not supported + // .... 0... = UEA4: Not supported + // .... .0.. = UEA5: Not supported + // .... ..0. = UEA6: Not supported + // .... ...0 = UEA7: Not supported + // 1... .... = UCS2 support (UCS2): The UE has no preference between the use of the default alphabet and + // the use of UCS2 .1.. .... = UMTS integrity algorithm UIA1: Supported + // ..0. .... = UMTS integrity algorithm UIA2: Not supported + // ...0 .... = UMTS integrity algorithm UIA3: Not supported + // .... 0... = UMTS integrity algorithm UIA4: Not supported + // .... .0.. = UMTS integrity algorithm UIA5: Not supported + // .... ..0. = UMTS integrity algorithm UIA6: Not supported + // .... ...0 = UMTS integrity algorithm UIA7: Not supported + // 0... .... = ProSe direct discovery: Not supported + // .0.. .... = ProSe: Not supported + // ..0. .... = H.245 After SRVCC Handover: Not supported + // ...0 .... = Access class control for CSFB: Not supported + // .... 0... = LTE Positioning Protocol: Not supported + // .... .0.. = Location services (LCS) notification mechanisms: Not supported + // .... ..0. = SRVCC from E-UTRAN to cdma2000 1xCS: Not supported + // .... ...1 = Notification procedure: Supported + // 1... .... = Extended protocol configuration options: Supported + // .0.. .... = Header compression for control plane CIoT EPS optimization: Not supported + // ..0. .... = EMM-REGISTERED w/o PDN connectivity: Not supported + // ...0 .... = S1-U data transfer: Not supported + // .... 0... = User plane CIoT EPS optimization: Not supported + // .... .0.. = Control plane CIoT EPS optimization: Not supported + // .... ..0. = ProSe UE-to-network relay: Not supported + // .... ...0 = ProSe direct communication: Not supported + // 0... .... = Signalling for a maximum number of 15 EPS bearer contexts: Not supported + // .0.. .... = Service gap control: Not supported + // ..1. .... = N1 mode: Supported + // ...1 .... = Dual connectivity with NR: Supported + // .... 0... = Control plane data backoff: Not supported + // .... .0.. = Restriction on use of enhanced coverage: Not supported + // .... ..0. = V2X communication over PC5: Not supported + // .... ...0 = Multiple DRB: Not supported + + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, reg_request); +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + nas_5gs_msg nas_msg; + + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::registration_request); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::registration_request); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + registration_request_t& reg_request_msg = nas_msg.registration_request(); + TESTASSERT(reg_request_msg.registration_type_5gs.registration_type == + registration_type_5gs_t::registration_type_type_::options::initial_registration); + TESTASSERT(reg_request_msg.registration_type_5gs.follow_on_request_bit == + registration_type_5gs_t::follow_on_request_bit_type_::options::follow_on_request_pending); + TESTASSERT(reg_request_msg.ng_ksi.security_context_flag == + key_set_identifier_t::security_context_flag_type_::options::native_security_context); + TESTASSERT(reg_request_msg.ng_ksi.nas_key_set_identifier == + key_set_identifier_t::nas_key_set_identifier_type_::options::no_key_is_available_or_reserved); + TESTASSERT(reg_request_msg.mobile_identity_5gs.type() == mobile_identity_5gs_t::identity_types_::options::guti_5g); + mobile_identity_5gs_t::guti_5g_s guti_5g_ = reg_request_msg.mobile_identity_5gs.guti_5g(); + TESTASSERT(guti_5g_.amf_pointer == 0x0); + TESTASSERT(guti_5g_.amf_region_id == 0x0); + TESTASSERT(guti_5g_.amf_set_id == 0); + TESTASSERT(guti_5g_.mcc[0] == 0); + TESTASSERT(guti_5g_.mcc[1] == 0); + TESTASSERT(guti_5g_.mcc[2] == 0); + TESTASSERT(guti_5g_.mnc[0] == 0); + TESTASSERT(guti_5g_.mnc[1] == 0); + TESTASSERT(guti_5g_.mnc[2] == 0); + TESTASSERT(reg_request_msg.ue_security_capability_present == true); + TESTASSERT(reg_request_msg.ue_security_capability.ea0_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ea1_128_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ea2_128_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ea3_128_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ea3_128_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ea4_5g_supported == false); + TESTASSERT(reg_request_msg.ue_security_capability.ea5_5g_supported == false); + TESTASSERT(reg_request_msg.ue_security_capability.ea6_5g_supported == false); + TESTASSERT(reg_request_msg.ue_security_capability.ea7_5g_supported == false); + TESTASSERT(reg_request_msg.ue_security_capability.ia0_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ia1_128_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ia2_128_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ia3_128_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ia3_128_5g_supported == true); + TESTASSERT(reg_request_msg.ue_security_capability.ia4_5g_supported == false); + TESTASSERT(reg_request_msg.ue_security_capability.ia5_5g_supported == false); + TESTASSERT(reg_request_msg.ue_security_capability.ia6_5g_supported == false); + TESTASSERT(reg_request_msg.ue_security_capability.ia7_5g_supported == false); + TESTASSERT(reg_request_msg.ue_security_capability.eps_caps_present == false); + + TESTASSERT(reg_request_msg.s1_ue_network_capability_present == true); + + TESTASSERT(reg_request_msg.s1_ue_network_capability.eea0_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eea1_128_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eea2_128_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eea3_128_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eea4_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eea5_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eea6_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eea7_supported == false); + + TESTASSERT(reg_request_msg.s1_ue_network_capability.eia0_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eia1_128_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eia2_128_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eia3_128_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eia4_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eia5_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eia6_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.eia7_supported == false); + + TESTASSERT(reg_request_msg.s1_ue_network_capability.uea0_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uea1_128_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uea2_128_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uea3_128_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uea4_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uea5_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uea6_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uea7_supported == false); + + TESTASSERT(reg_request_msg.s1_ue_network_capability.ucs2_support == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uia1_128_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uia2_128_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uia3_128_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uia4_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uia5_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uia6_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.uia7_supported == false); + + TESTASSERT(reg_request_msg.s1_ue_network_capability.pro_se_dd_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.pro_se_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.h245_ash_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.acc_csfb_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.llp_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.lcs_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.srvcc_capability_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.nf_capability_supported == true); + + TESTASSERT(reg_request_msg.s1_ue_network_capability.e_pco_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.hc_cp_c_io_t_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.e_rw_o_pdn_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.s1_u_data_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.up_c_io_t_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.cp_c_io_t_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.pro_se_relay_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.pro_se_dc_supported == false); + + TESTASSERT(reg_request_msg.s1_ue_network_capability.max_15_eps_bearer_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.sgc_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.n1mode_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.dcnr_supported == true); + TESTASSERT(reg_request_msg.s1_ue_network_capability.cp_backoff_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.restrict_ec_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.v2_x_pc5_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.multiple_drb_supported == false); + + TESTASSERT(reg_request_msg.s1_ue_network_capability.nr_pc5_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.up_mt_edt_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.cp_mt_edt_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.wus_supported == false); + TESTASSERT(reg_request_msg.s1_ue_network_capability.racs_supported == false); + + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); + + printf("buf_cmp->N_bytes %d buf->N_bytes %d\n", buf_cmp->N_bytes, buf->N_bytes); + printf("\n"); + print_msg(buf); + printf("\n"); + print_msg(buf_cmp); + printf("\n"); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + return SRSRAN_SUCCESS; +} + +int registration_request_unpacking_packing_test_2(srsran::nas_pcap* pcap) +{ + // Non-Access-Stratum 5GS (NAS)PDU + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Registration request (0x41) + // 5GS registration type + // .... 1... = Follow-On Request bit (FOR): Follow-on request pending + // .... .001 = 5GS registration type: initial registration (1) + // NAS key set identifier + // 0... .... = Type of security context flag (TSC): Native security context (for KSIAMF) + // .111 .... = NAS key set identifier: 7 + // 5GS mobile identity + // Length: 54 + // 0... .... = Spare: 0 + // .000 .... = SUPI format: IMSI (0) + // .... 0... = Spare: 0 + // .... .001 = Type of identity: SUCI (1) + // Mobile Country Code (MCC): Unknown (1) + // Mobile Network Code (MNC): Unknown (01) + // Routing indicator: 17 + // .... 0010 = Protection scheme Id: ECIES scheme profile B (2) + // Home network public key identifier: 27 + // Scheme output: 03 99 7e e4 01 2d e3 6c 86 e2 29 97 c8 99 70 4b 0f 61 3a bd 6c 3b 1c 9c â€Ļ + // ECC ephemeral public key: 03 99 7e e4 01 2d e3 6c 86 e2 29 97 c8 99 70 4b 0f 61 3a bd 6c 3b 1c 9c â€Ļ + // Ciphertext: cb bd 5d 27 34 + // MAC tag: 0x1e8b9e3328184bec + // UE security capability + // Element ID: 0x2e + // Length: 2 + // 1... .... = 5G-EA0: Supported + // .0.. .... = 128-5G-EA1: Not supported + // ..0. .... = 128-5G-EA2: Not supported + // ...0 .... = 128-5G-EA3: Not supported + // .... 0... = 5G-EA4: Not supported + // .... .0.. = 5G-EA5: Not supported + // .... ..0. = 5G-EA6: Not supported + // .... ...0 = 5G-EA7: Not supported + // 0... .... = 5G-IA0: Not supported + // .0.. .... = 128-5G-IA1: Not supported + // ..1. .... = 128-5G-IA2: Supported + // ...0 .... = 128-5G-IA3: Not supported + // .... 0... = 5G-IA4: Not supported + // .... .0.. = 5G-IA5: Not supported + // .... ..0. = 5G-IA6: Not supported + // .... ...0 = 5G-IA7: Not supported + + uint8_t reg_request[] = {0x7e, 0x00, 0x41, 0x79, 0x00, 0x36, 0x01, 0x00, 0xf1, 0x10, 0x71, 0xff, 0x02, + 0x1b, 0x03, 0x99, 0x7e, 0xe4, 0x01, 0x2d, 0xe3, 0x6c, 0x86, 0xe2, 0x29, 0x97, + 0xc8, 0x99, 0x70, 0x4b, 0x0f, 0x61, 0x3a, 0xbd, 0x6c, 0x3b, 0x1c, 0x9c, 0xa7, + 0x8a, 0x4b, 0x14, 0x7e, 0x22, 0xaf, 0xb0, 0x64, 0xcb, 0xbd, 0x5d, 0x27, 0x34, + 0x1e, 0x8b, 0x9e, 0x33, 0x28, 0x18, 0x4b, 0xec, 0x2e, 0x02, 0x80, 0x20}; + + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, reg_request); +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + nas_5gs_msg nas_msg; + + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::registration_request); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::registration_request); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + registration_request_t& reg_request_msg = nas_msg.registration_request(); + TESTASSERT(reg_request_msg.registration_type_5gs.registration_type == + registration_type_5gs_t::registration_type_type_::options::initial_registration); + TESTASSERT(reg_request_msg.registration_type_5gs.follow_on_request_bit == + registration_type_5gs_t::follow_on_request_bit_type_::options::follow_on_request_pending); + TESTASSERT(reg_request_msg.ng_ksi.security_context_flag == + key_set_identifier_t::security_context_flag_type_::options::native_security_context); + TESTASSERT(reg_request_msg.ng_ksi.nas_key_set_identifier == + key_set_identifier_t::nas_key_set_identifier_type_::options::no_key_is_available_or_reserved); + TESTASSERT(reg_request_msg.mobile_identity_5gs.type() == mobile_identity_5gs_t::identity_types_::options::suci); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().supi_format.value == + mobile_identity_5gs_t::suci_s::supi_format_type_::options::imsi); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().mcc[0] == 0); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().mcc[1] == 0); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().mcc[2] == 1); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().mnc[0] == 0); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().mnc[1] == 1); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().mnc[2] == 0xf); + + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().routing_indicator[0] == 1); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().routing_indicator[1] == 7); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().routing_indicator[2] == 0xf); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().routing_indicator[3] == 0xf); + + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().protection_scheme_id == + mobile_identity_5gs_t::suci_s::protection_scheme_id_type_::options::ecies_scheme_profile_b); + TESTASSERT(reg_request_msg.mobile_identity_5gs.suci().home_network_public_key_identifier == 27); + + hex_dump(reg_request_msg.mobile_identity_5gs.suci().scheme_output.data(), + reg_request_msg.mobile_identity_5gs.suci().scheme_output.size()); + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); + + printf("buf_cmp->N_bytes %d buf->N_bytes %d\n", buf_cmp->N_bytes, buf->N_bytes); + printf("\n"); + print_msg(buf); + printf("\n"); + print_msg(buf_cmp); + printf("\n"); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + return SRSRAN_SUCCESS; +} + +int deregistration_request_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + // Non-Access-Stratum 5GS (NAS)PDU + // Security protected NAS 5GS message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0001 = Security header type: Integrity protected (1) + // Message authentication code: 0x6f0325f5 + // Sequence number: 2 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Deregistration request (UE originating) (0x45) + // De-registration type + // .... 1... = Switch off: Switch off + // .... .0.. = Re-registration required: re-registration not required + // .... ..01 = Access type: 3GPP access (1) + // NAS key set identifier + // 0... .... = Type of security context flag (TSC): Native security context (for KSIAMF) + // .000 .... = NAS key set identifier: 0 + // 5GS mobile identity + // Length: 11 + // .... 0... = Odd/even indication: Even number of identity digits + // .... .010 = Type of identity: 5G-GUTI (2) + // Mobile Country Code (MCC): Unknown (1) + // Mobile Network Code (MNC): Unknown (01) + // AMF Region ID: 202 + // 1111 1110 00.. .... = AMF Set ID: 1016 + // ..00 0000 = AMF Pointer: 0 + // 5G-TMSI: 1 (0x00000001) + // + uint8_t dereg_request[] = {0x7e, 0x01, 0x6f, 0x03, 0x25, 0xf5, 0x02, 0x7e, 0x00, 0x45, 0x09, 0x00, + 0x0b, 0x02, 0x00, 0xf1, 0x10, 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x01}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, dereg_request); +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + + nas_5gs_msg nas_msg; + + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::integrity_protected); + TESTASSERT(nas_msg.hdr.message_authentication_code == 0x6f0325f5); + TESTASSERT(nas_msg.hdr.sequence_number == 2); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::deregistration_request_ue_originating); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + deregistration_request_ue_originating_t& dereg_request_msg = nas_msg.deregistration_request_ue_originating(); + TESTASSERT(dereg_request_msg.de_registration_type.switch_off == + de_registration_type_t::switch_off_type_::options::switch_off); + TESTASSERT(dereg_request_msg.de_registration_type.re_registration_required == + de_registration_type_t::re_registration_required_type_::options::re_registration_not_required); + TESTASSERT(dereg_request_msg.de_registration_type.access_type == + de_registration_type_t::access_type_type_::options::access_3_gpp); + TESTASSERT(dereg_request_msg.ng_ksi.security_context_flag == + key_set_identifier_t::security_context_flag_type_::options::native_security_context); + TESTASSERT(dereg_request_msg.ng_ksi.nas_key_set_identifier == 0); + TESTASSERT(dereg_request_msg.mobile_identity_5gs.type() == mobile_identity_5gs_t::identity_types_::options::guti_5g); + mobile_identity_5gs_t::guti_5g_s guti_5g_ = dereg_request_msg.mobile_identity_5gs.guti_5g(); + TESTASSERT(guti_5g_.amf_pointer == 0x0); + TESTASSERT(guti_5g_.amf_region_id == 202); + TESTASSERT(guti_5g_.amf_set_id == 1016); + TESTASSERT(guti_5g_.mcc[0] == 0); + TESTASSERT(guti_5g_.mcc[1] == 0); + TESTASSERT(guti_5g_.mcc[2] == 1); + TESTASSERT(guti_5g_.mnc[0] == 0); + TESTASSERT(guti_5g_.mnc[1] == 1); + TESTASSERT(guti_5g_.mnc[2] == 0xf); + TESTASSERT(guti_5g_.tmsi_5g == 0x00000001); + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int authentication_request_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + // Non-Access-Stratum 5GS (NAS)PDU + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Authentication request (0x56) + // 0000 .... = Spare Half Octet: 0 + // NAS key set identifier - ngKSI + // .... 0... = Type of security context flag (TSC): Native security context (for KSIAMF) + // .... .000 = NAS key set identifier: 0 + // ABBA + // Length: 2 + // ABBA Contents: 0x0000 + // Authentication Parameter RAND - 5G authentication challenge + // Element ID: 0x21 + // RAND value: 16 46 24 32 75 b8 b9 c7 18 b6 05 c6 ff 03 96 71 + // Authentication Parameter AUTN (UMTS and EPS authentication challenge) - 5G authentication challenge + // Element ID: 0x20 + // Length: 16 + // AUTN value: a3 09 26 e4 2e ea 80 00 f6 87 d5 ba a2 d9 56 ed + // SQN xor AK: a3 09 26 e4 2e ea + // AMF: 80 00 + // MAC: f6 87 d5 ba a2 d9 56 ed + + uint8_t auth_request[] = {0x7e, 0x00, 0x56, 0x00, 0x02, 0x00, 0x00, 0x21, 0x16, 0x46, 0x24, 0x32, 0x75, 0xb8, + 0xb9, 0xc7, 0x18, 0xb6, 0x05, 0xc6, 0xff, 0x03, 0x96, 0x71, 0x20, 0x10, 0xa3, 0x09, + 0x26, 0xe4, 0x2e, 0xea, 0x80, 0x00, 0xf6, 0x87, 0xd5, 0xba, 0xa2, 0xd9, 0x56, 0xed}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, auth_request); + +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + + nas_5gs_msg nas_msg; + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::authentication_request); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + authentication_request_t& auth_request_msg = nas_msg.authentication_request(); + TESTASSERT(auth_request_msg.ng_ksi.security_context_flag == + key_set_identifier_t::security_context_flag_type_::options::native_security_context); + TESTASSERT(auth_request_msg.ng_ksi.nas_key_set_identifier == 0); + TESTASSERT(auth_request_msg.abba.abba_contents.size() == 2); + TESTASSERT(auth_request_msg.abba.abba_contents[0] == 0x00); + TESTASSERT(auth_request_msg.abba.abba_contents[1] == 0x00); + TESTASSERT(auth_request_msg.authentication_parameter_rand_present == true); + TESTASSERT(auth_request_msg.authentication_parameter_rand.rand[0] == 0x16); + TESTASSERT(auth_request_msg.authentication_parameter_rand.rand[15] == 0x71); + TESTASSERT(auth_request_msg.authentication_parameter_autn_present == true); + TESTASSERT(auth_request_msg.authentication_parameter_autn.autn.size() == 16); + TESTASSERT(auth_request_msg.authentication_parameter_autn.autn[0] == 0xa3); + TESTASSERT(auth_request_msg.authentication_parameter_autn.autn[15] == 0xed); + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int authentication_resp_request_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + // Non-Access-Stratum 5GS (NAS)PDU + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Authentication response (0x57) + // Authentication response parameter + // Element ID: 0x2d + // Length: 16 + // RES: a1 1f 51 a4 1d a9 b5 29 b3 3b 04 3a e1 e2 02 08 + + uint8_t auth_resp_buf[] = {0x7e, 0x00, 0x57, 0x2d, 0x10, 0xa1, 0x1f, 0x51, 0xa4, 0x1d, 0xa9, + 0xb5, 0x29, 0xb3, 0x3b, 0x04, 0x3a, 0xe1, 0xe2, 0x02, 0x08}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, auth_resp_buf); + +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + nas_5gs_msg nas_msg; + + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::authentication_response); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::authentication_response); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + authentication_response_t& auth_resp = nas_msg.authentication_response(); + + TESTASSERT(auth_resp.authentication_response_parameter_present == true); + TESTASSERT(auth_resp.authentication_response_parameter.res.size() == 16); + TESTASSERT(auth_resp.authentication_response_parameter.res[0] == 0xa1); + TESTASSERT(auth_resp.authentication_response_parameter.res[15] == 0x08); + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int security_command_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + // Non-Access-Stratum 5GS (NAS)PDU + // Security protected NAS 5GS message + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Security mode command (0x5d) + // NAS security algorithms + // 0000 .... = Type of ciphering algorithm: 5G-EA0 (null ciphering algorithm) (0) + // .... 0010 = Type of integrity protection algorithm: 128-5G-IA2 (2) + // 0000 .... = Spare Half Octet: 0 + // NAS key set identifier - ngKSI + // .... 0... = Type of security context flag (TSC): Native security context (for KSIAMF) + // .... .000 = NAS key set identifier: 0 + // UE security capability - Replayed UE security capabilities + // Length: 4 + // 1... .... = 5G-EA0: Supported + // .1.. .... = 128-5G-EA1: Supported + // ..1. .... = 128-5G-EA2: Supported + // ...1 .... = 128-5G-EA3: Supported + // .... 0... = 5G-EA4: Not supported + // .... .0.. = 5G-EA5: Not supported + // .... ..0. = 5G-EA6: Not supported + // .... ...0 = 5G-EA7: Not supported + // 0... .... = 5G-IA0: Not supported + // .1.. .... = 128-5G-IA1: Supported + // ..1. .... = 128-5G-IA2: Supported + // ...1 .... = 128-5G-IA3: Supported + // .... 0... = 5G-IA4: Not supported + // .... .0.. = 5G-IA5: Not supported + // .... ..0. = 5G-IA6: Not supported + // .... ...0 = 5G-IA7: Not supported + // 1... .... = EEA0: Supported + // .1.. .... = 128-EEA1: Supported + // ..1. .... = 128-EEA2: Supported + // ...1 .... = 128-EEA3: Supported + // .... 0... = EEA4: Not supported + // .... .0.. = EEA5: Not supported + // .... ..0. = EEA6: Not supported + // .... ...0 = EEA7: Not supported + // 0... .... = EIA0: Not supported + // .1.. .... = 128-EIA1: Supported + // ..1. .... = 128-EIA2: Supported + // ...1 .... = 128-EIA3: Supported + // .... 0... = EIA4: Not supported + // .... .0.. = EIA5: Not supported + // .... ..0. = EIA6: Not supported + // .... ...0 = EIA7: Not supported + // IMEISV request + // 1110 .... = Element ID: 0xe- + // .... 0... = Spare bit(s): 0x00 + // .... .001 = IMEISV request: IMEISV requested (1) + // Additional 5G security information + // Element ID: 0x36 + // Length: 1 + // .... 0... = Spare: 0 + // .... .0.. = Spare: 0 + // .... ..0. = Retransmission of initial NAS message request(RINMR): Not Requested + // .... ...0 = Horizontal derivation parameter (HDP): Not required + + uint8_t sec_command[] = {0x7e, 0x03, 0x53, 0x3f, 0xcb, 0x29, 0x00, 0x7e, 0x00, 0x5d, 0x02, + 0x00, 0x04, 0xf0, 0x70, 0xf0, 0x70, 0xe1, 0x36, 0x01, 0x00}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, sec_command); + +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + nas_5gs_msg nas_msg; + + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::integrity_protected_with_new_5G_nas_context); + TESTASSERT(nas_msg.hdr.message_authentication_code == 0x533fcb29); + TESTASSERT(nas_msg.hdr.sequence_number == 0); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.inner_extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.inner_security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::security_mode_command); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + security_mode_command_t& sec_command_msg = nas_msg.security_mode_command(); + TESTASSERT(sec_command_msg.ng_ksi.security_context_flag == + key_set_identifier_t::security_context_flag_type_::options::native_security_context); + TESTASSERT(sec_command_msg.ng_ksi.nas_key_set_identifier == 0); + + TESTASSERT(sec_command_msg.selected_nas_security_algorithms.ciphering_algorithm == + security_algorithms_t::ciphering_algorithm_type_::options::ea0_5g); + TESTASSERT(sec_command_msg.selected_nas_security_algorithms.integrity_protection_algorithm == + security_algorithms_t::integrity_protection_algorithm_type_::options::ia2_128_5g); + + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ea0_5g_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ea1_128_5g_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ea2_128_5g_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ea3_128_5g_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ea4_5g_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ea5_5g_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ea6_5g_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ea7_5g_supported == false); + + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ia0_5g_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ia1_128_5g_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ia2_128_5g_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ia3_128_5g_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ia4_5g_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ia5_5g_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ia6_5g_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.ia7_5g_supported == false); + + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eps_caps_present == true); + + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eea0_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eea1_128_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eea2_128_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eea3_128_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eea4_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eea5_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eea6_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eea7_supported == false); + + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eia0_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eia1_128_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eia2_128_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eia3_128_supported == true); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eia4_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eia5_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eia6_supported == false); + TESTASSERT(sec_command_msg.replayed_ue_security_capabilities.eia7_supported == false); + + TESTASSERT(sec_command_msg.imeisv_request_present == true); + TESTASSERT(sec_command_msg.imeisv_request.imeisv_request == true); + + TESTASSERT(sec_command_msg.additional_5g_security_information_present == true); + TESTASSERT(sec_command_msg.additional_5g_security_information.rinmr == false); + TESTASSERT(sec_command_msg.additional_5g_security_information.hdp == false); + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); + + printf("buf_cmp->N_bytes %d buf->N_bytes %d\n", buf_cmp->N_bytes, buf->N_bytes); + printf("\n"); + print_msg(buf); + printf("\n"); + print_msg(buf_cmp); + printf("\n"); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int security_complete_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + // Non-Access-Stratum 5GS (NAS)PDU + // Security protected NAS 5GS message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0100 = Security header type: Integrity protected and ciphered with new 5GS security context (4) + // Message authentication code: 0x4088e4e4 + // Sequence number: 0 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Security mode complete (0x5e) + // 5GS mobile identity + // Element ID: 0x77 + // Length: 9 + // .... 0... = Odd/even indication: Even number of identity digits + // .... .101 = Type of identity: IMEISV (5) + // IMEISV: 8651160458202125 + // NAS message container + // Element ID: 0x71 + // Length: 87 + // Non-Access-Stratum 5GS (NAS)PDU + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Registration request (0x41) + // 5GS registration type + // .... 1... = Follow-On Request bit (FOR): Follow-on request pending + // .... .001 = 5GS registration type: initial registration (1) + // NAS key set identifier + // 0... .... = Type of security context flag (TSC): Native security context (for KSIAMF) + // .111 .... = NAS key set identifier: 7 + // 5GS mobile identity + // Length: 54 + // 0... .... = Spare: 0 + // .000 .... = SUPI format: IMSI (0) + // .... 0... = Spare: 0 + // .... .001 = Type of identity: SUCI (1) + // Mobile Country Code (MCC): Unknown (1) + // Mobile Network Code (MNC): Unknown (01) + // Routing indicator: 17 + // .... 0010 = Protection scheme Id: ECIES scheme profile B (2) + // Home network public key identifier: 27 + // Scheme output: 03 e3 42 42 99 67 4b 24 bc 8c 8a 54 e2 f9 06 5b f6 92 09 63 b0 9e 37 26 â€Ļ + // ECC ephemeral public key: 03 e3 42 42 99 67 4b 24 bc 8c 8a 54 e2 f9 06 5b f6 92 09 63 b0 + // 9e 37 26 â€Ļ Ciphertext: 00 f9 6d 57 82 MAC tag: 0xbf257ecba4d6ce2d + // 5GMM capability + // Element ID: 0x10 + // Length: 1 + // 0... .... = Spare: 0 + // .0.. .... = Spare: 0 + // ..0. .... = Spare: 0 + // ...0 .... = Spare: 0 + // .... 0... = Spare: 0 + // .... .0.. = LTE Positioning Protocol (LPP) capability: Not Requested + // .... ..1. = HO attach: Supported + // .... ...1 = S1 mode: Requested + // UE security capability + // Element ID: 0x2e + // Length: 4 + // 1... .... = 5G-EA0: Supported + // .1.. .... = 128-5G-EA1: Supported + // ..1. .... = 128-5G-EA2: Supported + // ...1 .... = 128-5G-EA3: Supported + // .... 0... = 5G-EA4: Not supported + // .... .0.. = 5G-EA5: Not supported + // .... ..0. = 5G-EA6: Not supported + // .... ...0 = 5G-EA7: Not supported + // 0... .... = 5G-IA0: Not supported + // .1.. .... = 128-5G-IA1: Supported + // ..1. .... = 128-5G-IA2: Supported + // ...1 .... = 128-5G-IA3: Supported + // .... 0... = 5G-IA4: Not supported + // .... .0.. = 5G-IA5: Not supported + // .... ..0. = 5G-IA6: Not supported + // .... ...0 = 5G-IA7: Not supported + // 1... .... = EEA0: Supported + // .1.. .... = 128-EEA1: Supported + // ..1. .... = 128-EEA2: Supported + // ...1 .... = 128-EEA3: Supported + // .... 0... = EEA4: Not supported + // .... .0.. = EEA5: Not supported + // .... ..0. = EEA6: Not supported + // .... ...0 = EEA7: Not supported + // 0... .... = EIA0: Not supported + // .1.. .... = 128-EIA1: Supported + // ..1. .... = 128-EIA2: Supported + // ...1 .... = 128-EIA3: Supported + // .... 0... = EIA4: Not supported + // .... .0.. = EIA5: Not supported + // .... ..0. = EIA6: Not supported + // .... ...0 = EIA7: Not supported + // UE network capability + // Element ID: 0x17 + // Length: 7 + // 1... .... = EEA0: Supported + // .1.. .... = 128-EEA1: Supported + // ..1. .... = 128-EEA2: Supported + // ...1 .... = 128-EEA3: Supported + // .... 0... = EEA4: Not supported + // .... .0.. = EEA5: Not supported + // .... ..0. = EEA6: Not supported + // .... ...0 = EEA7: Not supported + // 0... .... = EIA0: Not supported + // .1.. .... = 128-EIA1: Supported + // ..1. .... = 128-EIA2: Supported + // ...1 .... = 128-EIA3: Supported + // .... 0... = EIA4: Not supported + // .... .0.. = EIA5: Not supported + // .... ..0. = EIA6: Not supported + // .... ...0 = EIA7: Not supported + // 1... .... = UEA0: Supported + // .1.. .... = UEA1: Supported + // ..0. .... = UEA2: Not supported + // ...0 .... = UEA3: Not supported + // .... 0... = UEA4: Not supported + // .... .0.. = UEA5: Not supported + // .... ..0. = UEA6: Not supported + // .... ...0 = UEA7: Not supported + // 0... .... = UCS2 support (UCS2): The UE has a preference for the default alphabet + // .1.. .... = UMTS integrity algorithm UIA1: Supported + // ..0. .... = UMTS integrity algorithm UIA2: Not supported + // ...0 .... = UMTS integrity algorithm UIA3: Not supported + // .... 0... = UMTS integrity algorithm UIA4: Not supported + // .... .0.. = UMTS integrity algorithm UIA5: Not supported + // .... ..0. = UMTS integrity algorithm UIA6: Not supported + // .... ...0 = UMTS integrity algorithm UIA7: Not supported + // 0... .... = ProSe direct discovery: Not supported + // .0.. .... = ProSe: Not supported + // ..0. .... = H.245 After SRVCC Handover: Not supported + // ...1 .... = Access class control for CSFB: Supported + // .... 0... = LTE Positioning Protocol: Not supported + // .... .0.. = Location services (LCS) notification mechanisms: Not supported + // .... ..0. = SRVCC from E-UTRAN to cdma2000 1xCS: Not supported + // .... ...1 = Notification procedure: Supported + // 1... .... = Extended protocol configuration options: Supported + // .0.. .... = Header compression for control plane CIoT EPS optimization: Not supported + // ..0. .... = EMM-REGISTERED w/o PDN connectivity: Not supported + // ...0 .... = S1-U data transfer: Not supported + // .... 0... = User plane CIoT EPS optimization: Not supported + // .... .0.. = Control plane CIoT EPS optimization: Not supported + // .... ..0. = ProSe UE-to-network relay: Not supported + // .... ...0 = ProSe direct communication: Not supported + // 1... .... = Signalling for a maximum number of 15 EPS bearer contexts: Supported + // .0.. .... = Service gap control: Not supported + // ..1. .... = N1 mode: Supported + // ...1 .... = Dual connectivity with NR: Supported + // .... 0... = Control plane data backoff: Not supported + // .... .0.. = Restriction on use of enhanced coverage: Not supported + // .... ..0. = V2X communication over PC5: Not supported + // .... ...0 = Multiple DRB: Not supported + // UE's usage setting + // Element ID: 0x18 + // Length: 1 + // .... 0... = Spare: 0 + // .... .0.. = Spare: 0 + // .... ..0. = Spare: 0 + // .... ...1 = UE's usage setting: Data centric + // LADN indication + // Element ID: 0x74 + // Length: 0 + // 5GS update type + // Element ID: 0x53 + // Length: 1 + // .... 0... = Spare: 0 + // .... .0.. = Spare: 0 + // .... ..0. = NG-RAN Radio Capability Update (NG-RAN-RCU): Not Needed + // .... ...1 = SMS requested: SMS over NAS supported + + uint8_t sec_complete[] = { + 0x7e, 0x04, 0x40, 0x88, 0xe4, 0xe4, 0x00, 0x7e, 0x00, 0x5e, 0x77, 0x00, 0x09, 0x85, 0x56, 0x11, 0x06, 0x54, 0x28, + 0x20, 0x21, 0xf5, 0x71, 0x00, 0x57, 0x7e, 0x00, 0x41, 0x79, 0x00, 0x36, 0x01, 0x00, 0xf1, 0x10, 0x71, 0xff, 0x02, + 0x1b, 0x03, 0xe3, 0x42, 0x42, 0x99, 0x67, 0x4b, 0x24, 0xbc, 0x8c, 0x8a, 0x54, 0xe2, 0xf9, 0x06, 0x5b, 0xf6, 0x92, + 0x09, 0x63, 0xb0, 0x9e, 0x37, 0x26, 0x13, 0x48, 0xf5, 0xfe, 0xdc, 0xa2, 0x42, 0x07, 0x91, 0x00, 0xf9, 0x6d, 0x57, + 0x82, 0xbf, 0x25, 0x7e, 0xcb, 0xa4, 0xd6, 0xce, 0x2d, 0x10, 0x01, 0x03, 0x2e, 0x04, 0xf0, 0x70, 0xf0, 0x70, 0x17, + 0x07, 0xf0, 0x70, 0xc0, 0x40, 0x11, 0x80, 0xb0, 0x18, 0x01, 0x01, 0x74, 0x00, 0x00, 0x53, 0x01, 0x01}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, sec_complete); + +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + nas_5gs_msg nas_msg; + + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::integrity_protected_and_ciphered_with_new_5G_nas_context); + TESTASSERT(nas_msg.hdr.message_authentication_code == 0x4088e4e4); + TESTASSERT(nas_msg.hdr.sequence_number == 0); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.inner_extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.inner_security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::security_mode_complete); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + security_mode_complete_t& sec_complete_msg = nas_msg.security_mode_complete(); + + TESTASSERT(sec_complete_msg.imeisv_present == true); + TESTASSERT(sec_complete_msg.imeisv.type() == mobile_identity_5gs_t::identity_types_::options::imeisv); + mobile_identity_5gs_t::imeisv_s imeisv = sec_complete_msg.imeisv.imeisv(); + + TESTASSERT(imeisv.odd_even_indicator == false); + TESTASSERT(imeisv.imeisv[0] == 8); + TESTASSERT(imeisv.imeisv[1] == 6); + TESTASSERT(imeisv.imeisv[2] == 5); + TESTASSERT(imeisv.imeisv[3] == 1); + TESTASSERT(imeisv.imeisv[4] == 1); + TESTASSERT(imeisv.imeisv[5] == 6); + TESTASSERT(imeisv.imeisv[6] == 0); + TESTASSERT(imeisv.imeisv[7] == 4); + TESTASSERT(imeisv.imeisv[8] == 5); + TESTASSERT(imeisv.imeisv[9] == 8); + TESTASSERT(imeisv.imeisv[10] == 2); + TESTASSERT(imeisv.imeisv[11] == 0); + TESTASSERT(imeisv.imeisv[12] == 2); + TESTASSERT(imeisv.imeisv[13] == 1); + TESTASSERT(imeisv.imeisv[14] == 2); + TESTASSERT(imeisv.imeisv[15] == 5); + + TESTASSERT(sec_complete_msg.nas_message_container_present == true); + TESTASSERT(sec_complete_msg.nas_message_container.nas_message_container.size() == 87); + nas_5gs_msg inner_message; + TESTASSERT(inner_message.unpack(sec_complete_msg.nas_message_container.nas_message_container) == SRSRAN_SUCCESS); + // TODO check content of inner message + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int registration_accept_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + // Non-Access-Stratum 5GS (NAS)PDU + // Security protected NAS 5GS message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0010 = Security header type: Integrity protected and ciphered (2) + // Message authentication code: 0xd2b078f7 + // Sequence number: 1 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Registration accept (0x42) + // 5GS registration result + // Length: 1 + // ...0 .... = NSSAA Performed: False + // .... 0... = SMS over NAS: Not Allowed + // .... .001 = 5GS registration result: 3GPP access (1) + // 5GS mobile identity - 5G-GUTI + // Element ID: 0x77 + // Length: 11 + // .... 0... = Odd/even indication: Even number of identity digits + // .... .010 = Type of identity: 5G-GUTI (2) + // Mobile Country Code (MCC): Unknown (1) + // Mobile Network Code (MNC): Unknown (01) + // AMF Region ID: 202 + // 1111 1110 00.. .... = AMF Set ID: 1016 + // ..00 0000 = AMF Pointer: 0 + // 5G-TMSI: 3 (0x00000003) + // + // 5GS tracking area identity list + // Element ID: 0x54 + // Length: 7 + // Partial tracking area list 1 + // .00. .... = Type of list: list of TACs belonging to one PLMN, with non-consecutive TAC values (0) + // ...0 0000 = Number of elements: 1 element (0) + // Mobile Country Code (MCC): Unknown (1) + // Mobile Network Code (MNC): Unknown (01) + // TAC: 1 + // NSSAI - Allowed NSSAI + // Element ID: 0x15 + // Length: 10 + // S-NSSAI 1 + // Length: 4 + // Slice/service type (SST): 1 + // Slice differentiator (SD): 66051 + // S-NSSAI 2 + // Length: 4 + // Slice/service type (SST): 1 + // Slice differentiator (SD): 1122867 + // GPRS Timer 3 - T3512 value + // Element ID: 0x5e + // Length: 1 + // GPRS Timer: 60 min + // 000. .... = Unit: value is incremented in multiples of 10 minutes (0) + // ...0 0110 = Timer value: 6 + // GPRS Timer 2 - T3502 value + // Element ID: 0x16 + // Length: 1 + // GPRS Timer: 12 min + // 001. .... = Unit: value is incremented in multiples of 1 minute (1) + // ...0 1100 = Timer value: 12 + + uint8_t reg_accept[] = {0x7e, 0x02, 0xd2, 0xb0, 0x78, 0xf7, 0x01, 0x7e, 0x00, 0x42, 0x01, 0x01, 0x77, 0x00, + 0x0b, 0xf2, 0x00, 0xf1, 0x10, 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x03, 0x54, 0x07, + 0x00, 0x00, 0xf1, 0x10, 0x00, 0x00, 0x01, 0x15, 0x0a, 0x04, 0x01, 0x01, 0x02, 0x03, + 0x04, 0x01, 0x11, 0x22, 0x33, 0x5e, 0x01, 0x06, 0x16, 0x01, 0x2c}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, reg_accept); + +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + nas_5gs_msg nas_msg; + + // Unpacking + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::integrity_protected_and_ciphered); + TESTASSERT(nas_msg.hdr.message_authentication_code == 0xd2b078f7); + TESTASSERT(nas_msg.hdr.sequence_number == 1); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.inner_extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.inner_security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::registration_accept); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + return SRSRAN_SUCCESS; +} + +int registration_complete_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + // Non-Access-Stratum 5GS (NAS)PDU + // Security protected NAS 5GS message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0010 = Security header type: Integrity protected and ciphered (2) + // Message authentication code: 0xa0b88817 + // Sequence number: 1 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Registration complete (0x43) + + uint8_t reg_complete[] = {0x7e, 0x02, 0xa0, 0xb8, 0x88, 0x17, 0x01, 0x7e, 0x00, 0x43}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, reg_complete); + +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + nas_5gs_msg nas_msg; + + // Unpacking + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::integrity_protected_and_ciphered); + TESTASSERT(nas_msg.hdr.message_authentication_code == 0xa0b88817); + TESTASSERT(nas_msg.hdr.sequence_number == 1); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.inner_extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.inner_security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::registration_complete); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +int deregistration_request_unpacking_packing_test_2(srsran::nas_pcap* pcap) +{ + // NAS-PDU: 7e 02 b1 b8 76 98 02 7e 00 45 09 00 0b f2 00 f1 10 ca fe 00 00 00 00 03 + // Non-Access-Stratum 5GS (NAS)PDU + // Security protected NAS 5GS message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0010 = Security header type: Integrity protected and ciphered (2) + // Message authentication code: 0xb1b87698 + // Sequence number: 2 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: Deregistration request (UE originating) (0x45) + // De-registration type + // .... 1... = Switch off: Switch off + // .... .0.. = Re-registration required: re-registration not required + // .... ..01 = Access type: 3GPP access (1) + // NAS key set identifier + // 0... .... = Type of security context flag (TSC): Native security context (for KSIAMF) + // .000 .... = NAS key set identifier: 0 + // 5GS mobile identity + // Length: 11 + // .... 0... = Odd/even indication: Even number of identity digits + // .... .010 = Type of identity: 5G-GUTI (2) + // Mobile Country Code (MCC): Unknown (1) + // Mobile Network Code (MNC): Unknown (01) + // AMF Region ID: 202 + // 1111 1110 00.. .... = AMF Set ID: 1016 + // ..00 0000 = AMF Pointer: 0 + // 5G-TMSI: 3 (0x00000003) + // + + uint8_t deregistration_req[] = {0x7e, 0x02, 0xb1, 0xb8, 0x76, 0x98, 0x02, 0x7e, 0x00, 0x45, 0x09, 0x00, + 0x0b, 0x02, 0x00, 0xf1, 0x10, 0xca, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x03}; + + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, deregistration_req); +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + + nas_5gs_msg nas_msg; + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::integrity_protected_and_ciphered); + TESTASSERT(nas_msg.hdr.message_authentication_code == 0xb1b87698); + TESTASSERT(nas_msg.hdr.sequence_number == 2); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.inner_extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.inner_security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::deregistration_request_ue_originating); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + deregistration_request_ue_originating_t& dereg_req_ue_o = nas_msg.deregistration_request_ue_originating(); + + TESTASSERT(dereg_req_ue_o.de_registration_type.switch_off == + de_registration_type_t::switch_off_type_::options::switch_off); + TESTASSERT(dereg_req_ue_o.de_registration_type.access_type == + de_registration_type_t::access_type_type_::options::access_3_gpp); + TESTASSERT(dereg_req_ue_o.ng_ksi.security_context_flag == + key_set_identifier_t::security_context_flag_type_::options::native_security_context); + TESTASSERT(dereg_req_ue_o.ng_ksi.nas_key_set_identifier == 0); + TESTASSERT(dereg_req_ue_o.mobile_identity_5gs.type() == mobile_identity_5gs_t::identity_types_::options::guti_5g); + mobile_identity_5gs_t::guti_5g_s guti_5g = dereg_req_ue_o.mobile_identity_5gs.guti_5g(); + + TESTASSERT(guti_5g.mcc[0] == 0); + TESTASSERT(guti_5g.mcc[1] == 0); + TESTASSERT(guti_5g.mcc[2] == 1); + TESTASSERT(guti_5g.mnc[0] == 0); + TESTASSERT(guti_5g.mnc[1] == 1); + TESTASSERT(guti_5g.mnc[2] == 0xf); + TESTASSERT(guti_5g.amf_region_id == 202); + TESTASSERT(guti_5g.amf_set_id == 1016); + TESTASSERT(guti_5g.amf_pointer == 0); + TESTASSERT(guti_5g.tmsi_5g == 0x00000003); + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); + +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int pdu_session_establishment_request_unpacking_packing_test(srsran::nas_pcap* pcap) +{ + // NAS-PDU: 7e 02 dc f9 1d 1b 02 7e 00 67 01 00 06 2e 0a 00 c1 ff ff 12 0a 81 22 04 â€Ļ + // Non-Access-Stratum 5GS (NAS)PDU + // Security protected NAS 5GS message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0010 = Security header type: Integrity protected and ciphered (2) + // Message authentication code: 0xdcf91d1b + // Sequence number: 2 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: UL NAS transport (0x67) + // 0000 .... = Spare Half Octet: 0 + // Payload container type + // .... 0001 = Payload container type: N1 SM information (1) + // Payload container + // Length: 6 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G session management messages (46) + // PDU session identity: PDU session identity value 10 (10) + // Procedure transaction identity: 0 + // Message type: PDU session establishment request (0xc1) + // Integrity protection maximum data rate + // Integrity protection maximum data rate for uplink: Full data rate (255) + // Integrity protection maximum data rate for downlink: Full data rate (255) + // PDU session identity 2 - PDU session ID + // Element ID: 0x12 + // PDU session identity: PDU session identity value 10 (10) + // Request type + // 1000 .... = Element ID: 0x8- + // .... .001 = Request type: Initial request (1) + // S-NSSAI + // Element ID: 0x22 + // Length: 4 + // Slice/service type (SST): 1 + // Slice differentiator (SD): 66051 + // DNN + // Element ID: 0x25 + // Length: 9 + // DNN: internet + + uint8_t pdu_session_bytes[] = {0x7e, 0x02, 0xdc, 0xf9, 0x1d, 0x1b, 0x02, 0x7e, 0x00, 0x67, 0x01, 0x00, 0x06, + 0x2e, 0x0a, 0x00, 0xc1, 0xff, 0xff, 0x12, 0x0a, 0x81, 0x22, 0x04, 0x01, 0x01, + 0x02, 0x03, 0x25, 0x09, 0x08, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, pdu_session_bytes); +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + + nas_5gs_msg nas_msg; + + // Unpacking + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::integrity_protected_and_ciphered); + TESTASSERT(nas_msg.hdr.message_authentication_code == 0xdcf91d1b); + TESTASSERT(nas_msg.hdr.sequence_number == 2); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.inner_extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.inner_security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::ul_nas_transport); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + ul_nas_transport_t& ul_nas = nas_msg.ul_nas_transport(); + TESTASSERT(ul_nas.payload_container_type.payload_container_type == + payload_container_type_t::Payload_container_type_type_::options::n1_sm_information); + TESTASSERT(ul_nas.payload_container.payload_container_contents.size() == 6); + TESTASSERT(ul_nas.payload_container.payload_container_contents[0] == 0x2e); + TESTASSERT(ul_nas.payload_container.payload_container_contents[5] == 0xff); + + // Unpack inner nas + nas_5gs_msg inner_nas; + TESTASSERT(inner_nas.unpack_outer_hdr(ul_nas.payload_container.payload_container_contents) == SRSRAN_SUCCESS); + TESTASSERT(inner_nas.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gsm); + TESTASSERT(inner_nas.hdr.pdu_session_identity == 10); + TESTASSERT(inner_nas.hdr.procedure_transaction_identity == 0); + TESTASSERT(inner_nas.hdr.message_type == msg_types::options::pdu_session_establishment_request); + + TESTASSERT(inner_nas.unpack(ul_nas.payload_container.payload_container_contents) == SRSRAN_SUCCESS); + pdu_session_establishment_request_t pdu_sess_est_req = inner_nas.pdu_session_establishment_request(); + TESTASSERT(pdu_sess_est_req.integrity_protection_maximum_data_rate.max_data_rate_upip_uplink == + integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_uplink_type_::options::full_data_rate); + TESTASSERT(pdu_sess_est_req.integrity_protection_maximum_data_rate.max_data_rate_upip_downlink == + integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_downlink_type_::options::full_data_rate); + + // Packing inner nas buffer + srsran::unique_byte_buffer_t buf_cmp_inner = srsran::make_byte_buffer(); + inner_nas.pack(buf_cmp_inner); + + // Compare inner nas buffer + TESTASSERT(ul_nas.payload_container.payload_container_contents.size() == buf_cmp_inner.get()->N_bytes); + TESTASSERT(memcmp(ul_nas.payload_container.payload_container_contents.data(), + buf_cmp_inner.get()->msg, + buf_cmp_inner.get()->N_bytes) == 0); + + // Outer again + TESTASSERT(ul_nas.pdu_session_id_present == true); + TESTASSERT(ul_nas.pdu_session_id.pdu_session_identity_2_value == 10); + TESTASSERT(ul_nas.request_type_present == true); + TESTASSERT(ul_nas.request_type.request_type_value == + request_type_t::Request_type_value_type_::options::initial_request); + TESTASSERT(ul_nas.s_nssai_present == true); + TESTASSERT(ul_nas.s_nssai.sst == 1); + TESTASSERT(ul_nas.s_nssai.sd == 66051); + TESTASSERT(ul_nas.dnn_present == true); + TESTASSERT(ul_nas.dnn.dnn_value.size() == 9); + TESTASSERT(ul_nas.dnn.dnn_value[0] == 0x08); + TESTASSERT(ul_nas.dnn.dnn_value[1] == 0x69); + TESTASSERT(ul_nas.dnn.dnn_value[8] == 0x74); + + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); + +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int pdu_session_est_req_accecpt(srsran::nas_pcap* pcap) +{ + // pDUSessionNAS-PDU: 7e 02 1a ca a2 92 02 7e 00 68 01 00 1d 2e 0a 00 c2 11 00 08 01 06 31 31 â€Ļ + // Non-Access-Stratum 5GS (NAS)PDU + // Security protected NAS 5GS message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0010 = Security header type: Integrity protected and ciphered (2) + // Message authentication code: 0x1acaa292 + // Sequence number: 2 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G mobility management messages (126) + // 0000 .... = Spare Half Octet: 0 + // .... 0000 = Security header type: Plain NAS message, not security protected (0) + // Message type: DL NAS transport (0x68) + // 0000 .... = Spare Half Octet: 0 + // Payload container type + // .... 0001 = Payload container type: N1 SM information (1) + // Payload container + // Length: 29 + // Plain NAS 5GS Message + // Extended protocol discriminator: 5G session management messages (46) + // PDU session identity: PDU session identity value 10 (10) + // Procedure transaction identity: 0 + // Message type: PDU session establishment accept (0xc2) + // .001 .... = Selected SSC mode: SSC mode 1 (1) + // PDU session type - Selected PDU session type + // .... .001 = PDU session type: IPv4 (1) + // QoS rules - Authorized QoS rules + // Length: 8 + // QoS rule 1 + // QoS rule identifier: 1 + // Length: 1585 + // 001. .... = Rule operation code: Create new QoS rule (1) + // ...1 .... = DQR: The QoS rule is the default QoS rule + // .... 0001 = Number of packet filters: 1 + // Packet filter 1 + // ..00 .... = Packet filter direction: Reserved (0) + // .... 0001 = Packet filter identifier: 1 + // Length: 1 + // Packet filter component 1 + // Packet filter component type: Unknown (0) + // Not dissected yet + // [Expert Info (Note/Protocol): Not dissected yet] + // [Not dissected yet] + // + // [Severity level: Note] + // [Group: Protocol] + // QoS rule precedence: 9 + // 0... .... = Spare: 0 + // .0.. .... = Spare: 0 + // ..00 0110 = Qos flow identifier: 6 + // Session-AMBR + // Length: 6 + // Unit for Session-AMBR for downlink: value is incremented in multiples of 1 Kbps (1) + // Session-AMBR for downlink: 59395 Kbps (59395) + // Unit for Session-AMBR for uplink: value is incremented in multiples of 1 Kbps (1) + // Session-AMBR for uplink: 59395 Kbps (59395) + // PDU address + // Element ID: 0x29 + // Length: 5 + // .... .001 = PDU session type: IPv4 (1) + // PDU address information: 60.60.0.1 + // PDU session identity 2 - PDU session ID + // Element ID: 0x12 + // PDU session identity: PDU session identity value 10 (10) + + uint8_t pdu_session_bytes[] = {0x7e, 0x02, 0x1a, 0xca, 0xa2, 0x92, 0x02, 0x7e, 0x00, 0x68, 0x01, + 0x00, 0x1d, 0x2e, 0x0a, 0x00, 0xc2, 0x11, 0x00, 0x08, 0x01, 0x06, + 0x31, 0x31, 0x01, 0x01, 0x00, 0x09, 0x06, 0x01, 0xe8, 0x03, 0x01, + 0xe8, 0x03, 0x29, 0x05, 0x01, 0x3c, 0x3c, 0x00, 0x01, 0x12, 0x0a}; + srsran::unique_byte_buffer_t buf; + copy_msg_to_buffer(buf, pdu_session_bytes); +#if HAVE_PCAP + pcap->write_nas(buf.get()->msg, buf.get()->N_bytes); +#endif + + nas_5gs_msg nas_msg; + + // Unpacking + TESTASSERT(nas_msg.unpack_outer_hdr(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.security_header_type == nas_5gs_hdr::integrity_protected_and_ciphered); + TESTASSERT(nas_msg.hdr.message_authentication_code == 0x1acaa292); + TESTASSERT(nas_msg.hdr.sequence_number == 2); + + TESTASSERT(nas_msg.unpack(buf) == SRSRAN_SUCCESS); + TESTASSERT(nas_msg.hdr.inner_extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gmm); + TESTASSERT(nas_msg.hdr.inner_security_header_type == nas_5gs_hdr::plain_5gs_nas_message); + TESTASSERT(nas_msg.hdr.message_type == msg_types::options::dl_nas_transport); + printf("%s\n", nas_msg.hdr.message_type.to_string()); + + dl_nas_transport_t& dl_nas = nas_msg.dl_nas_transport(); + TESTASSERT(dl_nas.payload_container_type.payload_container_type == + payload_container_type_t::Payload_container_type_type_::options::n1_sm_information); + TESTASSERT(dl_nas.payload_container.payload_container_contents.size() == 29); + TESTASSERT(dl_nas.payload_container.payload_container_contents[0] == 0x2e); + TESTASSERT(dl_nas.payload_container.payload_container_contents[28] == 0x01); + + // Unpack inner nas + nas_5gs_msg inner_nas; + TESTASSERT(inner_nas.unpack_outer_hdr(dl_nas.payload_container.payload_container_contents) == SRSRAN_SUCCESS); + TESTASSERT(inner_nas.hdr.extended_protocol_discriminator == nas_5gs_hdr::extended_protocol_discriminator_5gsm); + TESTASSERT(inner_nas.hdr.pdu_session_identity == 10); + TESTASSERT(inner_nas.hdr.procedure_transaction_identity == 0); + TESTASSERT(inner_nas.hdr.message_type == msg_types::options::pdu_session_establishment_accept); + + TESTASSERT(inner_nas.unpack(dl_nas.payload_container.payload_container_contents) == SRSRAN_SUCCESS); + pdu_session_establishment_accept_t pdu_sess_est_acc = inner_nas.pdu_session_establishment_accept(); + TESTASSERT(pdu_sess_est_acc.selected_ssc_mode.ssc_mode_value == + ssc_mode_t::SSC_mode_value_type_::options::ssc_mode_1); + TESTASSERT(pdu_sess_est_acc.selected_pdu_session_type.pdu_session_type_value == + pdu_session_type_t::PDU_session_type_value_type_::options::ipv4); + + TESTASSERT(pdu_sess_est_acc.session_ambr.unit_session_ambr_for_downlink == + session_ambr_t::unit_session_AMBR_type_::options::inc_by_1_kbps); + TESTASSERT(pdu_sess_est_acc.session_ambr.session_ambr_for_downlink == 59395); + TESTASSERT(pdu_sess_est_acc.session_ambr.unit_session_ambr_for_uplink == + session_ambr_t::unit_session_AMBR_type_::options::inc_by_1_kbps); + TESTASSERT(pdu_sess_est_acc.session_ambr.session_ambr_for_uplink == 59395); + TESTASSERT(pdu_sess_est_acc.pdu_address_present == true); + TESTASSERT(pdu_sess_est_acc.pdu_address.si6_lla == false); + TESTASSERT(pdu_sess_est_acc.pdu_address.pdu_session_type_value == + pdu_address_t::PDU_session_type_value_type_::options::ipv4); + TESTASSERT(pdu_sess_est_acc.pdu_address.ipv4[0] == 0x3c); + TESTASSERT(pdu_sess_est_acc.pdu_address.ipv4[1] == 0x3c); + TESTASSERT(pdu_sess_est_acc.pdu_address.ipv4[2] == 0x00); + TESTASSERT(pdu_sess_est_acc.pdu_address.ipv4[3] == 0x01); + + // Packing inner nas buffer + srsran::unique_byte_buffer_t buf_cmp_inner = srsran::make_byte_buffer(); + inner_nas.pack(buf_cmp_inner); + + // Compare inner nas buffer + // TESTASSERT(dl_nas.payload_container.payload_container_contents.size() == buf_cmp_inner.get()->N_bytes); + // TESTASSERT(memcmp(dl_nas.payload_container.payload_container_contents.data(), + // buf_cmp_inner.get()->msg, + // buf_cmp_inner.get()->N_bytes) == 0); + + // outer nas again + TESTASSERT(dl_nas.pdu_session_id_present == true); + TESTASSERT(dl_nas.pdu_session_id.pdu_session_identity_2_value == 10); + // Packing + srsran::unique_byte_buffer_t buf_cmp = srsran::make_byte_buffer(); + nas_msg.pack(buf_cmp); + +#if HAVE_PCAP + pcap->write_nas(buf_cmp.get()->msg, buf_cmp.get()->N_bytes); +#endif + + // Compare buffer + TESTASSERT(buf.get()->N_bytes == buf_cmp.get()->N_bytes); + TESTASSERT(memcmp(buf.get()->msg, buf_cmp.get()->msg, buf.get()->N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int main() +{ + srslog::init(); + srsran::console("Testing 5G NAS packing and unpacking\n"); +#if HAVE_PCAP + srsran::nas_pcap pcap; + pcap.open("nas_5g_msg_test.pcap", 0, srsran::srsran_rat_t::nr); + TESTASSERT(registration_request_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(registration_request_unpacking_packing_test_2(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(deregistration_request_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(authentication_request_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(authentication_resp_request_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(security_command_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(security_complete_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(registration_accept_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(registration_complete_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(deregistration_request_unpacking_packing_test_2(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(pdu_session_establishment_request_unpacking_packing_test(&pcap) == SRSRAN_SUCCESS); + TESTASSERT(pdu_session_est_req_accecpt(&pcap) == SRSRAN_SUCCESS); + pcap.close(); +#else + TESTASSERT(registration_request_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(registration_request_unpacking_packing_test_2(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(deregistration_request_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(authentication_request_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(authentication_resp_request_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(security_command_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(security_complete_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(registration_accept_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(registration_complete_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(deregistration_request_unpacking_packing_test_2(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(pdu_session_establishment_request_unpacking_packing_test(nullptr) == SRSRAN_SUCCESS); + TESTASSERT(pdu_session_est_req_accecpt(nullptr) == SRSRAN_SUCCESS); +#endif + return SRSRAN_SUCCESS; +} diff --git a/lib/test/asn1/nas_decoder.cc b/lib/test/asn1/nas_decoder.cc index 12682764f..9d2a17712 100644 --- a/lib/test/asn1/nas_decoder.cc +++ b/lib/test/asn1/nas_decoder.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/asn1/ngap_test.cc b/lib/test/asn1/ngap_test.cc index 8da16e708..e8e8f867d 100644 --- a/lib/test/asn1/ngap_test.cc +++ b/lib/test/asn1/ngap_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,39 +23,39 @@ #include "srsran/common/test_common.h" using namespace asn1; -using namespace asn1::ngap_nr; +using namespace asn1::ngap; /* TESTS */ int test_amf_upd() { - uint8_t ngap_msg[] = {0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x00, 0x11}; + uint8_t ngap_msg[] = { + 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x02, 0x80, 0x73, 0x72, 0x73, 0x72, 0x61, 0x6e}; cbit_ref bref(&ngap_msg[0], sizeof(ngap_msg)); - // 0000000A00000100010003000011 + // 0000000F00000100010008028073727372616E ngap_pdu_c pdu; - TESTASSERT(pdu.unpack(bref) == SRSASN_SUCCESS); + TESTASSERT_EQ(SRSASN_SUCCESS, pdu.unpack(bref)); - TESTASSERT(pdu.type().value == ngap_pdu_c::types_opts::init_msg); - TESTASSERT(pdu.init_msg().proc_code == 0); - TESTASSERT(pdu.init_msg().crit.value == crit_opts::reject); + TESTASSERT_EQ(ngap_pdu_c::types_opts::init_msg, pdu.type().value); + TESTASSERT_EQ(0, pdu.init_msg().proc_code); + TESTASSERT_EQ(crit_opts::reject, pdu.init_msg().crit.value); ngap_elem_procs_o::init_msg_c& init_choice = pdu.init_msg().value; - TESTASSERT(init_choice.type().value == ngap_elem_procs_o::init_msg_c::types_opts::amf_cfg_upd); + TESTASSERT_EQ(ngap_elem_procs_o::init_msg_c::types_opts::amf_cfg_upd, init_choice.type().value); amf_cfg_upd_s& amf_upd = init_choice.amf_cfg_upd(); TESTASSERT(not amf_upd.ext); - auto& amf_name = amf_upd.protocol_ies.amf_name; - TESTASSERT(amf_upd.protocol_ies.amf_name_present); - TESTASSERT(amf_name.id == 1); - TESTASSERT(amf_name.crit == crit_opts::reject); - TESTASSERT(amf_name.value.size() == 1); - TESTASSERT(amf_name.value[0] == 17); + auto& amf_name = amf_upd->amf_name; + TESTASSERT(amf_upd->amf_name_present); + TESTASSERT_EQ(1, amf_name.id); + TESTASSERT_EQ(crit_opts::reject, amf_name.crit); + TESTASSERT_EQ("srsran", amf_name.value.to_string()); - TESTASSERT(ceil(bref.distance_bytes()) == sizeof(ngap_msg)); - TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS); + TESTASSERT_EQ(sizeof(ngap_msg), ceil(bref.distance_bytes())); + TESTASSERT_EQ(SRSASN_SUCCESS, test_pack_unpack_consistency(pdu)); - // json_writer js; - // pdu.to_json(js); - // printf("PDU json: %s\n", js.to_string().c_str()); + json_writer js; + pdu.to_json(js); + printf("PDU json: %s\n", js.to_string().c_str()); return 0; } @@ -79,30 +79,29 @@ int test_ngsetup_request() ng_setup_request_s& ngsetup = pdu.init_msg().value.ng_setup_request(); TESTASSERT(not ngsetup.ext); // Field 0 - TESTASSERT(ngsetup.protocol_ies.global_ran_node_id.id == 27); - TESTASSERT(ngsetup.protocol_ies.global_ran_node_id.crit.value == crit_opts::reject); - TESTASSERT(ngsetup.protocol_ies.global_ran_node_id.value.type().value == - global_ran_node_id_c::types_opts::global_gnb_id); - global_gnb_id_s& global_gnb = ngsetup.protocol_ies.global_ran_node_id.value.global_gnb_id(); + TESTASSERT(ngsetup->global_ran_node_id.id == 27); + TESTASSERT(ngsetup->global_ran_node_id.crit.value == crit_opts::reject); + TESTASSERT(ngsetup->global_ran_node_id.value.type().value == global_ran_node_id_c::types_opts::global_gnb_id); + global_gnb_id_s& global_gnb = ngsetup->global_ran_node_id.value.global_gnb_id(); TESTASSERT(global_gnb.plmn_id.to_number() == 0xF110); TESTASSERT(global_gnb.gnb_id.type().value == gnb_id_c::types_opts::gnb_id); TESTASSERT(global_gnb.gnb_id.gnb_id().to_number() == 1); // Field 1 - TESTASSERT(ngsetup.protocol_ies.ran_node_name_present); + TESTASSERT(ngsetup->ran_node_name_present); // Field 2 - TESTASSERT(ngsetup.protocol_ies.supported_ta_list.id == 102); - TESTASSERT(ngsetup.protocol_ies.supported_ta_list.crit.value == crit_opts::reject); - TESTASSERT(ngsetup.protocol_ies.supported_ta_list.value.size() == 1); - TESTASSERT(ngsetup.protocol_ies.supported_ta_list.value[0].tac.to_number() == 0x75); - TESTASSERT(ngsetup.protocol_ies.supported_ta_list.value[0].broadcast_plmn_list.size() == 1); - auto& bcast_item = ngsetup.protocol_ies.supported_ta_list.value[0].broadcast_plmn_list[0]; + TESTASSERT(ngsetup->supported_ta_list.id == 102); + TESTASSERT(ngsetup->supported_ta_list.crit.value == crit_opts::reject); + TESTASSERT(ngsetup->supported_ta_list.value.size() == 1); + TESTASSERT(ngsetup->supported_ta_list.value[0].tac.to_number() == 0x75); + TESTASSERT(ngsetup->supported_ta_list.value[0].broadcast_plmn_list.size() == 1); + auto& bcast_item = ngsetup->supported_ta_list.value[0].broadcast_plmn_list[0]; TESTASSERT(bcast_item.plmn_id.to_number() == 0xF110); TESTASSERT(bcast_item.tai_slice_support_list.size()); TESTASSERT(bcast_item.tai_slice_support_list[0].s_nssai.sst.to_number() == 1); // Field 3 - TESTASSERT(ngsetup.protocol_ies.default_paging_drx.id == 21); - TESTASSERT(ngsetup.protocol_ies.default_paging_drx.crit.value == crit_opts::ignore); - TESTASSERT(ngsetup.protocol_ies.default_paging_drx.value.value == paging_drx_opts::v256); + TESTASSERT(ngsetup->default_paging_drx.id == 21); + TESTASSERT(ngsetup->default_paging_drx.crit.value == crit_opts::ignore); + TESTASSERT(ngsetup->default_paging_drx.value.value == paging_drx_opts::v256); TESTASSERT(ceil(bref.distance(ngap_msg) / 8.0) == sizeof(ngap_msg)); TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS); @@ -137,19 +136,18 @@ int test_ngsetup_response() ngap_elem_procs_o::successful_outcome_c::types_opts::ng_setup_resp); ng_setup_resp_s& resp = pdu.successful_outcome().value.ng_setup_resp(); // field 0 - TESTASSERT(resp.protocol_ies.amf_name.id == 1); - TESTASSERT(resp.protocol_ies.amf_name.crit.value == crit_opts::reject); - TESTASSERT(resp.protocol_ies.amf_name.value.size() == 56); - TESTASSERT(resp.protocol_ies.amf_name.value.to_string() == - "amf1.cluster1.net2.amf.5gc.mnc001.mcc001.3gppnetwork.org"); + TESTASSERT(resp->amf_name.id == 1); + TESTASSERT(resp->amf_name.crit.value == crit_opts::reject); + TESTASSERT(resp->amf_name.value.size() == 56); + TESTASSERT(resp->amf_name.value.to_string() == "amf1.cluster1.net2.amf.5gc.mnc001.mcc001.3gppnetwork.org"); // field 1 - TESTASSERT(resp.protocol_ies.served_guami_list.id == 96); - TESTASSERT(resp.protocol_ies.served_guami_list.crit.value == crit_opts::reject); - TESTASSERT(resp.protocol_ies.served_guami_list.value.size() == 1); - TESTASSERT(resp.protocol_ies.served_guami_list.value[0].guami.plmn_id.to_number() == 0xF110); - TESTASSERT(resp.protocol_ies.served_guami_list.value[0].guami.amf_region_id.to_number() == 0b111000); - TESTASSERT(resp.protocol_ies.served_guami_list.value[0].guami.amf_set_id.to_number() == 0b100010); - TESTASSERT(resp.protocol_ies.served_guami_list.value[0].guami.amf_pointer.to_number() == 0b10111); + TESTASSERT(resp->served_guami_list.id == 96); + TESTASSERT(resp->served_guami_list.crit.value == crit_opts::reject); + TESTASSERT(resp->served_guami_list.value.size() == 1); + TESTASSERT(resp->served_guami_list.value[0].guami.plmn_id.to_number() == 0xF110); + TESTASSERT(resp->served_guami_list.value[0].guami.amf_region_id.to_number() == 0b111000); + TESTASSERT(resp->served_guami_list.value[0].guami.amf_set_id.to_number() == 0b100010); + TESTASSERT(resp->served_guami_list.value[0].guami.amf_pointer.to_number() == 0b10111); // field 2 // ... @@ -181,7 +179,7 @@ int test_init_ue_msg() TESTASSERT(pdu.init_msg().proc_code == 15); TESTASSERT(pdu.init_msg().crit.value == crit_opts::ignore); TESTASSERT(pdu.init_msg().value.type().value == ngap_elem_procs_o::init_msg_c::types_opts::init_ue_msg); - auto& container = pdu.init_msg().value.init_ue_msg().protocol_ies; + auto& container = *pdu.init_msg().value.init_ue_msg(); // Field 0 TESTASSERT(container.ran_ue_ngap_id.id == 85); TESTASSERT(container.ran_ue_ngap_id.crit.value == crit_opts::reject); @@ -220,7 +218,40 @@ int test_dl_nas_transport() // Field 0 // ... // Field 1 - TESTASSERT(dl_nas.protocol_ies.nas_pdu.value.size() == 42); + TESTASSERT(dl_nas->nas_pdu.value.size() == 42); + + TESTASSERT(ceil(bref.distance(ngap_msg) / 8.0) == sizeof(ngap_msg)); + TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS); + return 0; +} + +// DL NAS transport with AMF-UE-NGAP-ID larger than 32bit +int test_dl_nas_transport2() +{ + uint8_t ngap_msg[] = {0x00, 0x04, 0x40, 0x42, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x06, 0x80, 0x03, 0x03, + 0xcf, 0x37, 0xd0, 0x00, 0x55, 0x00, 0x02, 0x00, 0x01, 0x00, 0x26, 0x00, 0x2b, 0x2a, + 0x7e, 0x00, 0x56, 0x00, 0x02, 0x00, 0x00, 0x21, 0xbc, 0x8d, 0xe5, 0x61, 0xf5, 0xb4, + 0xa7, 0x05, 0x8f, 0xdb, 0xe2, 0x3b, 0x4e, 0x21, 0xda, 0x45, 0x20, 0x10, 0x5a, 0xb8, + 0xd1, 0xdb, 0x13, 0x76, 0x80, 0x00, 0x1b, 0x1a, 0x8d, 0x3c, 0x98, 0x4c, 0x01, 0x06}; + // 00044042000003000a0006800303cf37d00055000200010026002b2a7e00560002000021bc8de561f5b4a7058fdbe23b4e21da4520105ab8d1db137680001b1a8d3c984c0106 + + cbit_ref bref(ngap_msg, sizeof(ngap_msg)); + ngap_pdu_c pdu; + TESTASSERT(pdu.unpack(bref) == SRSASN_SUCCESS); + + // Check Fields + TESTASSERT(pdu.type().value == ngap_pdu_c::types_opts::init_msg); + TESTASSERT(pdu.init_msg().proc_code == ASN1_NGAP_ID_DL_NAS_TRANSPORT); + TESTASSERT(pdu.init_msg().crit.value == crit_opts::ignore); + TESTASSERT(pdu.init_msg().value.type().value == ngap_elem_procs_o::init_msg_c::types_opts::dl_nas_transport); + + auto& dl_nas = pdu.init_msg().value.dl_nas_transport(); + // Field 0 + TESTASSERT_EQ(12948813776, dl_nas->amf_ue_ngap_id.value); + + // ... + // Field 2 + TESTASSERT(dl_nas->nas_pdu.value.size() == 42); TESTASSERT(ceil(bref.distance(ngap_msg) / 8.0) == sizeof(ngap_msg)); TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS); @@ -246,7 +277,7 @@ int test_ul_ran_status_transfer() TESTASSERT(pdu.init_msg().value.type().value == ngap_elem_procs_o::init_msg_c::types_opts::ul_nas_transport); auto& ul_nas = pdu.init_msg().value.ul_nas_transport(); // Field 1 - TESTASSERT(ul_nas.protocol_ies.nas_pdu.value.size() == 21); + TESTASSERT(ul_nas->nas_pdu.value.size() == 21); TESTASSERT(ceil(bref.distance(ngap_msg) / 8.0) == sizeof(ngap_msg)); TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS); @@ -317,10 +348,10 @@ int test_session_res_setup_request() TESTASSERT(pdu.init_msg().crit.value == crit_opts::reject); TESTASSERT(pdu.init_msg().value.type().value == ngap_elem_procs_o::init_msg_c::types_opts::pdu_session_res_setup_request); - auto& container = pdu.init_msg().value.pdu_session_res_setup_request().protocol_ies; - TESTASSERT(container.pdu_session_res_setup_list_su_req.id == ASN1_NGAP_NR_ID_PDU_SESSION_RES_SETUP_LIST_SU_REQ); - TESTASSERT(container.pdu_session_res_setup_list_su_req.value.size() == 1); - auto& item = container.pdu_session_res_setup_list_su_req.value[0]; + auto& container = pdu.init_msg().value.pdu_session_res_setup_request(); + TESTASSERT(container->pdu_session_res_setup_list_su_req.id == ASN1_NGAP_ID_PDU_SESSION_RES_SETUP_LIST_SU_REQ); + TESTASSERT(container->pdu_session_res_setup_list_su_req.value.size() == 1); + auto& item = container->pdu_session_res_setup_list_su_req.value[0]; TESTASSERT(item.pdu_session_id == 1); TESTASSERT(item.s_nssai.sst.to_number() == 0); TESTASSERT(item.pdu_session_res_setup_request_transfer.to_string() == @@ -330,10 +361,9 @@ int test_session_res_setup_request() item.pdu_session_res_setup_request_transfer.size()); pdu_session_res_setup_request_transfer_s req; TESTASSERT(req.unpack(bref2) == SRSASN_SUCCESS); - TESTASSERT(req.protocol_ies.ul_ngu_up_tnl_info.id == 139); - TESTASSERT(req.protocol_ies.ul_ngu_up_tnl_info.value.type().value == - up_transport_layer_info_c::types_opts::gtp_tunnel); - TESTASSERT(req.protocol_ies.ul_ngu_up_tnl_info.value.gtp_tunnel().transport_layer_address.to_string() == + TESTASSERT(req->ul_ngu_up_tnl_info.id == 139); + TESTASSERT(req->ul_ngu_up_tnl_info.value.type().value == up_transport_layer_info_c::types_opts::gtp_tunnel); + TESTASSERT(req->ul_ngu_up_tnl_info.value.gtp_tunnel().transport_layer_address.to_string() == "11000000101010000001000111010010"); TESTASSERT(bref2.distance_bytes() == (int)item.pdu_session_res_setup_request_transfer.size()); @@ -353,15 +383,16 @@ int main() // Start the log backend. srslog::init(); - TESTASSERT(test_amf_upd() == 0); - TESTASSERT(test_ngsetup_request() == 0); - TESTASSERT(test_ngsetup_response() == 0); - TESTASSERT(test_init_ue_msg() == 0); - TESTASSERT(test_dl_nas_transport() == 0); - TESTASSERT(test_ul_ran_status_transfer() == 0); - TESTASSERT(test_ue_context_release() == 0); - TESTASSERT(test_ue_context_release_complete() == 0); - TESTASSERT(test_session_res_setup_request() == 0); + test_amf_upd(); + test_ngsetup_request(); + test_ngsetup_response(); + test_init_ue_msg(); + test_dl_nas_transport(); + test_dl_nas_transport2(); + test_ul_ran_status_transfer(); + test_ue_context_release(); + test_ue_context_release_complete(); + test_session_res_setup_request(); srslog::flush(); diff --git a/lib/test/asn1/rrc_asn1_decoder.cc b/lib/test/asn1/rrc_asn1_decoder.cc index 46cd15db4..3cee57187 100644 --- a/lib/test/asn1/rrc_asn1_decoder.cc +++ b/lib/test/asn1/rrc_asn1_decoder.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/asn1/rrc_nr_test.cc b/lib/test/asn1/rrc_nr_test.cc deleted file mode 100644 index 21fbe26d9..000000000 --- a/lib/test/asn1/rrc_nr_test.cc +++ /dev/null @@ -1,331 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/asn1/rrc_nr.h" -#include "srsran/common/test_common.h" -#include - -using namespace asn1; -using namespace asn1::rrc_nr; - -int test_eutra_nr_capabilities() -{ - struct ue_mrdc_cap_s mrdc_cap; - band_combination_s band_combination; - struct band_params_c band_param_eutra; - band_param_eutra.set_eutra(); - band_param_eutra.eutra().ca_bw_class_dl_eutra_present = true; - band_param_eutra.eutra().ca_bw_class_ul_eutra_present = true; - band_param_eutra.eutra().band_eutra = 1; - band_param_eutra.eutra().ca_bw_class_dl_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; - band_param_eutra.eutra().ca_bw_class_ul_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; - band_combination.band_list.push_back(band_param_eutra); - struct band_params_c band_param_nr; - band_param_nr.set_nr(); - band_param_nr.nr().ca_bw_class_dl_nr_present = true; - band_param_nr.nr().ca_bw_class_ul_nr_present = true; - band_param_nr.nr().band_nr = 78; - band_param_nr.nr().ca_bw_class_dl_nr = asn1::rrc_nr::ca_bw_class_nr_opts::options::a; - band_param_nr.nr().ca_bw_class_ul_nr = asn1::rrc_nr::ca_bw_class_nr_opts::options::a; - band_combination.band_list.push_back(band_param_nr); - - mrdc_cap.rf_params_mrdc.supported_band_combination_list.push_back(band_combination); - mrdc_cap.rf_params_mrdc.supported_band_combination_list_present = true; - - mrdc_cap.rf_params_mrdc.ext = true; - - // RF Params MRDC applied_freq_band_list_filt - freq_band_info_c band_info_eutra; - band_info_eutra.set_band_info_eutra(); - band_info_eutra.band_info_eutra().ca_bw_class_dl_eutra_present = false; - band_info_eutra.band_info_eutra().ca_bw_class_ul_eutra_present = false; - band_info_eutra.band_info_eutra().band_eutra = 1; - mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_eutra); - - freq_band_info_c band_info_nr; - band_info_nr.set_band_info_nr(); - band_info_nr.band_info_nr().band_nr = 78; - mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_nr); - - mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt_present = true; - - // rf_params_mrdc supported band combination list v1540 - - band_combination_list_v1540_l* band_combination_list_v1450 = new band_combination_list_v1540_l(); - band_combination_v1540_s band_combination_v1540; - - band_params_v1540_s band_params_a; - band_params_a.srs_tx_switch_present = true; - band_params_a.srs_carrier_switch_present = false; - band_params_a.srs_tx_switch.supported_srs_tx_port_switch = - band_params_v1540_s::srs_tx_switch_s_::supported_srs_tx_port_switch_opts::not_supported; - band_combination_v1540.band_list_v1540.push_back(band_params_a); - - band_params_v1540_s band_params_b; - band_params_b.srs_tx_switch_present = true; - band_params_b.srs_tx_switch.supported_srs_tx_port_switch = - band_params_v1540_s::srs_tx_switch_s_::supported_srs_tx_port_switch_opts::t1r2; - band_params_b.srs_carrier_switch_present = false; - band_combination_v1540.band_list_v1540.push_back(band_params_b); - - // clang-format off - band_combination_v1540.ca_params_nr_v1540_present = false; - band_combination_v1540.ca_params_nr_v1540.simul_csi_reports_all_cc_present = true; - band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.max_num_simul_nzp_csi_rs_act_bwp_all_cc_present = true; - band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.max_num_simul_nzp_csi_rs_act_bwp_all_cc = 5; - band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.total_num_ports_simul_nzp_csi_rs_act_bwp_all_cc_present = true; - band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.total_num_ports_simul_nzp_csi_rs_act_bwp_all_cc = 32; - // clang-format on - band_combination_list_v1450->push_back(band_combination_v1540); - mrdc_cap.rf_params_mrdc.supported_band_combination_list_v1540.reset(band_combination_list_v1450); - - feature_set_combination_l feature_set_combination; - - feature_sets_per_band_l feature_sets_per_band; - - feature_set_c feature_set_eutra; - feature_set_eutra.set_eutra(); - feature_set_eutra.eutra().dl_set_eutra = 1; - feature_set_eutra.eutra().ul_set_eutra = 1; - feature_sets_per_band.push_back(feature_set_eutra); - - feature_set_combination.push_back(feature_sets_per_band); - - feature_set_c feature_set_nr; - feature_set_nr.set_nr(); - feature_set_nr.nr().dl_set_nr = 1; - feature_set_nr.nr().ul_set_nr = 1; - feature_sets_per_band.push_back(feature_set_nr); - - feature_set_combination.push_back(feature_sets_per_band); - - mrdc_cap.feature_set_combinations.push_back(feature_set_combination); - - mrdc_cap.feature_set_combinations_present = true; - - // Pack mrdc_cap - uint8_t buffer[1024]; - asn1::bit_ref bref(buffer, sizeof(buffer)); - mrdc_cap.pack(bref); - - TESTASSERT(test_pack_unpack_consistency(mrdc_cap) == SRSASN_SUCCESS); - - srslog::fetch_basic_logger("RRC").info( - buffer, bref.distance_bytes(), "Packed cap struct (%d bytes):", bref.distance_bytes()); - - return SRSRAN_SUCCESS; -} - -int test_ue_mrdc_capabilities() -{ - uint8_t msg[] = {0x01, 0x1c, 0x04, 0x81, 0x60, 0x00, 0x1c, 0x4d, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x40, 0x04, 0x04, 0xd0, 0x10, 0x74, 0x06, 0x14, 0xe8, 0x1b, 0x10, - 0x78, 0x00, 0x00, 0x20, 0x00, 0x10, 0x08, 0x08, 0x01, 0x00, 0x20}; - // 011c048160001c4d0000000400400404d010740614e81b107800002000100808010020 - - asn1::cbit_ref bref{msg, sizeof(msg)}; - ue_mrdc_cap_s mrdc_cap; - - TESTASSERT(mrdc_cap.unpack(bref) == SRSASN_SUCCESS); - - TESTASSERT(test_pack_unpack_consistency(mrdc_cap) == SRSASN_SUCCESS); - - return SRSRAN_SUCCESS; -} - -int test_ue_rrc_reconfiguration() -{ - uint8_t rrc_msg[] = "\x08\x81\x7c\x5c\x40\xb1\xc0\x7d\x48\x3a\x04\xc0\x3e\x01\x04\x54" - "\x1e\xb5\x00\x02\xe8\x53\x98\xdf\x46\x93\x4b\x80\x04\xd2\x69\x34" - "\x00\x00\x08\xc9\x8d\x6d\x8c\xa2\x01\xff\x00\x00\x00\x00\x01\x1b" - "\x82\x21\x00\x00\x04\x04\x00\xd1\x14\x0e\x70\x00\x00\x08\xc9\xc6" - "\xb6\xc6\x44\xa0\x00\x1e\xb8\x95\x63\xe0\x24\x94\x22\x0d\xb8\x44" - "\x70\x0c\x02\x10\xb0\x1d\x80\x48\xf1\x18\x06\xea\x00\x08\x0e\x01" - "\x25\xc0\xc8\x80\x37\x08\x42\x00\x00\x88\x16\x50\x02\x0c\x82\x00" - "\x00\x20\x69\x81\x01\x45\x0a\x00\x0e\x48\x18\x00\x01\x33\x55\x64" - "\x84\x1c\x00\x10\x40\xc2\x05\x0c\x1c\x9c\x40\x91\x42\xc6\x0d\x1c" - "\x3c\x8e\x00\x00\x32\x21\x40\x30\x20\x01\x91\x4a\x01\x82\x00\x0c" - "\x8c\x50\x0c\x18\x00\x64\x42\x80\xe1\x00\x03\x22\x94\x07\x0a\x00" - "\x19\x18\xa0\x38\x60\x00\xc8\x85\x02\xc3\x80\x06\x45\x28\x16\x20" - "\x64\x00\x41\x6c\x48\x04\x62\x82\x18\xa0\x08\xc5\x04\xb1\x60\x11" - "\x8a\x0a\x63\x00\x23\x14\x16\xc6\x80\x46\x28\x31\x8e\x00\x8c\x50" - "\x6b\x1e\x01\x18\xa0\xe6\x40\x00\x32\x31\x40\xb2\x23\x10\x0a\x08" - "\x40\x90\x86\x05\x10\x43\xcc\x3b\x2a\x6e\x4d\x01\xa4\x92\x1e\x2e" - "\xe0\x0c\x10\xe0\x00\x00\x01\x8f\xfd\x29\x49\x8c\x63\x72\x81\x60" - "\x00\x02\x19\x70\x00\x00\x00\x00\x00\x00\x52\xf0\x0f\xa0\x84\x8a" - "\xd5\x45\x00\x47\x00\x18\x00\x08\x20\x00\xe2\x10\x02\x40\x80\x70" - "\x10\x10\x84\x00\x0e\x21\x00\x1c\xb0\x0e\x04\x02\x20\x80\x01\xc4" - "\x20\x03\x96\x01\xc0\xc0\x42\x10\x00\x38\x84\x00\x73\x00\x38\x20" - "\x08\x82\x00\x07\x10\x80\x0e\x60\x00\x40\x00\x00\x04\x10\xc0\x40" - "\x80\xc1\x00\xe0\xd0\x00\x0e\x48\x10\x00\x00\x02\x00\x40\x00\x80" - "\x60\x00\x80\x90\x02\x20\x0a\x40\x00\x02\x38\x90\x11\x31\xc8"; - - uint32_t rrc_msg_len = sizeof(rrc_msg); - cbit_ref bref(&rrc_msg[0], sizeof(rrc_msg)); - rrc_recfg_s rrc_recfg; - - TESTASSERT(rrc_recfg.unpack(bref) == SRSASN_SUCCESS); - TESTASSERT(rrc_recfg.rrc_transaction_id == 0); - json_writer jw; - rrc_recfg.to_json(jw); - srslog::fetch_basic_logger("RRC").info("RRC Reconfig: \n %s", jw.to_string().c_str()); - - TESTASSERT(rrc_recfg.crit_exts.type() == asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg); - TESTASSERT(rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group_present == true); - - cell_group_cfg_s cell_group_cfg; - cbit_ref bref0(rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.data(), - rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.size()); - TESTASSERT(cell_group_cfg.unpack(bref0) == SRSASN_SUCCESS); - // json_writer jw1; - // cell_group_cfg.to_json(jw1); - // srslog::fetch_basic_logger("RRC").info("RRC Secondary Cell Group: \n %s", jw1.to_string().c_str()); - TESTASSERT(cell_group_cfg.cell_group_id == 1); - TESTASSERT(cell_group_cfg.rlc_bearer_to_add_mod_list_present == true); - TESTASSERT(cell_group_cfg.rlc_bearer_to_add_mod_list.size() == 1); - TESTASSERT(cell_group_cfg.mac_cell_group_cfg_present == true); - TESTASSERT(cell_group_cfg.phys_cell_group_cfg_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg_present == true); - return SRSRAN_SUCCESS; -} - -int test_radio_bearer_config() -{ - uint8_t rrc_msg[] = "\x14\x09\x28\x17\x87\xc0\x0c\x28"; - uint32_t rrc_msg_len = sizeof(rrc_msg); - cbit_ref bref(&rrc_msg[0], sizeof(rrc_msg)); - radio_bearer_cfg_s radio_bearer_cfg; - TESTASSERT(radio_bearer_cfg.unpack(bref) == SRSASN_SUCCESS); - // json_writer jw; - // radio_bearer_cfg.to_json(jw); - // srslog::fetch_basic_logger("RRC").info("RRC Bearer CFG Message: \n %s", jw.to_string().c_str()); - TESTASSERT(radio_bearer_cfg.drb_to_add_mod_list_present == true); - TESTASSERT(radio_bearer_cfg.drb_to_add_mod_list.size() == 1); - TESTASSERT(radio_bearer_cfg.security_cfg_present == true); - TESTASSERT(radio_bearer_cfg.security_cfg.security_algorithm_cfg_present == true); - TESTASSERT(radio_bearer_cfg.security_cfg.key_to_use_present == true); - return SRSRAN_SUCCESS; -} - -int test_cell_group_config() -{ - uint8_t cell_group_config_raw[] = "\x5c\x40\xb1\xc0\x33\xc8\x53\xe0\x12\x0f\x05\x38\x0f\x80\x41\x15" - "\x07\xad\x40\x00\xba\x14\xe6\x37\xd1\xa4\xd3\xa0\x01\x34\x9a\x5f" - "\xc0\x00\x00\x33\x63\x6c\x91\x28\x80\x7f\xc0\x00\x00\x00\x00\x46" - "\xe0\x88\x40\x00\x01\x01\x00\x34\x45\x03\x9c\x00\x00\x00\x33\x71" - "\xb6\x48\x90\x04\x00\x08\x2e\x25\x18\xf0\x02\x4a\x31\x06\xe2\x8d" - "\xb8\x44\x70\x0c\x02\x10\x38\x1d\x80\x48\xf1\x18\x5e\xea\x00\x08" - "\x0e\x01\x25\xc0\xca\x80\x01\x7f\x80\x00\x00\x00\x00\x83\x70\x88" - "\x20\x00\x08\x81\x65\x00\x20\xc8\x20\x00\x02\x06\x98\x10\x14\x50" - "\xa0\x00\xe4\x81\x80\x00\x13\x35\x56\x48\x41\xc0\x01\x04\x0c\x20" - "\x50\xc1\xc9\xc4\x09\x14\x2c\x60\xd1\xc3\xc8\xe0\x00\x03\x32\x14" - "\x03\x02\x00\x19\x94\xa0\x18\x20\x00\xcc\xc5\x00\xc1\x80\x06\x64" - "\x28\x0e\x10\x00\x33\x29\x40\x70\xa0\x01\x99\x8a\x03\x86\x00\x0c" - "\xc8\x50\x2c\x38\x00\x66\x52\x81\x62\x06\x60\x04\x16\xc4\x80\x46" - "\x48\x21\x8a\x00\x8c\x90\x4b\x16\x01\x19\x20\xa6\x30\x02\x32\x41" - "\x6c\x68\x04\x64\x83\x18\xe0\x08\xc9\x06\xb1\xe0\x11\x92\x0e\x64" - "\x00\x03\x33\x14\x0b\x22\x32\x00\xa0\x84\x09\x08\x60\x51\x04\x34" - "\x3b\x2a\x65\xcd\x01\xa4\x92\x1e\x2e\xe0\x0c\x10\xe0\x00\x00\x01" - "\x8f\xfd\x29\x49\x8c\x63\x72\x81\x60\x00\x02\x19\x70\x00\x00\x00" - "\x00\x00\x00\x62\xf0\x0f\xa0\x84\x8a\xd5\x45\x00\x47\x00\x18\x00" - "\x08\x20\x00\xe2\x10\x02\x40\x80\x70\x10\x10\x84\x00\x0e\x21\x00" - "\x1c\xb0\x0e\x04\x02\x20\x80\x01\xc4\x20\x03\x96\x01\xc0\xc0\x42" - "\x10\x00\x38\x84\x00\x73\x00\x38\x20\x08\x82\x00\x07\x10\x80\x0e" - "\x60\x00\x40\x00\x00\x04\x10\xc0\x40\x80\xc1\x00\xe0\xd0\x00\x0e" - "\x48\x10\x00\x00\x02\x00\x40\x00\x80\x60\x00\x80\x90\x02\x20\x0a" - "\x40\x00\x02\x38\x90\x11\x31\xc8"; - - asn1::SRSASN_CODE err; - - cbit_ref bref(&cell_group_config_raw[0], sizeof(cell_group_config_raw)); - cell_group_cfg_s cell_group_cfg; - - TESTASSERT(cell_group_cfg.unpack(bref) == SRSASN_SUCCESS); - - TESTASSERT(test_pack_unpack_consistency(cell_group_cfg) == SRSASN_SUCCESS); - - TESTASSERT(cell_group_cfg.sp_cell_cfg_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.serv_cell_idx_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync_present = true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci == 500); - TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present == true); - TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp_present == true); - TESTASSERT( - cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present == - true); - - TESTASSERT( - cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common.type() == - asn1::rrc_nr::setup_release_c::types_opts::setup); - - asn1::rrc_nr::rach_cfg_common_s& rach_cfg_common = - cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(); - - TESTASSERT(rach_cfg_common.rach_cfg_generic.prach_cfg_idx == 16); - TESTASSERT(rach_cfg_common.rach_cfg_generic.msg1_fdm == asn1::rrc_nr::rach_cfg_generic_s::msg1_fdm_opts::one); - TESTASSERT(rach_cfg_common.rach_cfg_generic.zero_correlation_zone_cfg == 0); - TESTASSERT(rach_cfg_common.rach_cfg_generic.preamb_rx_target_pwr == -110); - TESTASSERT(rach_cfg_common.rach_cfg_generic.preamb_trans_max == - asn1::rrc_nr::rach_cfg_generic_s::preamb_trans_max_opts::n7); - TESTASSERT(rach_cfg_common.rach_cfg_generic.pwr_ramp_step == - asn1::rrc_nr::rach_cfg_generic_s::pwr_ramp_step_opts::db4); - TESTASSERT(rach_cfg_common.rach_cfg_generic.ra_resp_win == asn1::rrc_nr::rach_cfg_generic_s::ra_resp_win_opts::sl10); - TESTASSERT(rach_cfg_common.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present == true); - - // asn1::json_writer json_writer; - // cell_group_cfg.to_json(json_writer); - // srslog::fetch_basic_logger("RRC").info("RRC Secondary Cell Group: Content: %s\n", json_writer.to_string().c_str()); - return SRSRAN_SUCCESS; -} - -int main() -{ - auto& asn1_logger = srslog::fetch_basic_logger("ASN1", false); - asn1_logger.set_level(srslog::basic_levels::debug); - asn1_logger.set_hex_dump_max_size(-1); - auto& rrc_logger = srslog::fetch_basic_logger("RRC", false); - rrc_logger.set_level(srslog::basic_levels::debug); - rrc_logger.set_hex_dump_max_size(-1); - - // Start the log backend. - srslog::init(); - - TESTASSERT(test_eutra_nr_capabilities() == SRSRAN_SUCCESS); - TESTASSERT(test_ue_mrdc_capabilities() == SRSRAN_SUCCESS); - TESTASSERT(test_ue_rrc_reconfiguration() == SRSRAN_SUCCESS); - TESTASSERT(test_radio_bearer_config() == SRSRAN_SUCCESS); - TESTASSERT(test_cell_group_config() == SRSRAN_SUCCESS); - - srslog::flush(); - - printf("Success\n"); - return 0; -} diff --git a/lib/test/asn1/rrc_nr_utils_test.cc b/lib/test/asn1/rrc_nr_utils_test.cc index b58d1390b..b7c719e3b 100644 --- a/lib/test/asn1/rrc_nr_utils_test.cc +++ b/lib/test/asn1/rrc_nr_utils_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -43,10 +43,11 @@ int test_rlc_config() rlc_cfg_asn1.to_json(jw); srslog::fetch_basic_logger("RRC").info("RLC NR Config: \n %s", jw.to_string().c_str()); - rlc_config_t rlc_cfg = make_rlc_config_t(rlc_cfg_asn1); + rlc_config_t rlc_cfg; + // We hard-code the bearer_id=1 and rb_type=DRB + TESTASSERT(make_rlc_config_t(rlc_cfg_asn1, /* bearer_id */ 1, &rlc_cfg) == SRSRAN_SUCCESS); TESTASSERT(rlc_cfg.rat == srsran_rat_t::nr); TESTASSERT(rlc_cfg.um_nr.sn_field_length == rlc_um_nr_sn_size_t::size12bits); - TESTASSERT(rlc_cfg.um_nr.UM_Window_Size == 2048); return SRSRAN_SUCCESS; } @@ -66,13 +67,14 @@ int test_mac_rach_common_config() rach_common_config_asn1.to_json(jw); srslog::fetch_basic_logger("RRC").info("MAC NR RACH Common config: \n %s", jw.to_string().c_str()); - rach_nr_cfg_t rach_nr_cfg = make_mac_rach_cfg(rach_common_config_asn1); - TESTASSERT(rach_nr_cfg.ra_responseWindow == 10); - TESTASSERT(rach_nr_cfg.ra_ContentionResolutionTimer == 64); - TESTASSERT(rach_nr_cfg.prach_ConfigurationIndex == 160); - TESTASSERT(rach_nr_cfg.PreambleReceivedTargetPower == -110); - TESTASSERT(rach_nr_cfg.preambleTransMax == 7); - TESTASSERT(rach_nr_cfg.powerRampingStep == 4); + rach_cfg_nr_t rach_cfg_nr = {}; + make_mac_rach_cfg(rach_common_config_asn1, &rach_cfg_nr); + TESTASSERT(rach_cfg_nr.ra_responseWindow == 10); + TESTASSERT(rach_cfg_nr.ra_ContentionResolutionTimer == 64); + TESTASSERT(rach_cfg_nr.prach_ConfigurationIndex == 160); + TESTASSERT(rach_cfg_nr.PreambleReceivedTargetPower == -110); + TESTASSERT(rach_cfg_nr.preambleTransMax == 7); + TESTASSERT(rach_cfg_nr.powerRampingStep == 4); return SRSRAN_SUCCESS; } @@ -97,15 +99,15 @@ int make_phy_tdd_cfg_test() tdd_ul_dl_cfg_common.pattern1.nrof_ul_slots = 2; tdd_ul_dl_cfg_common.pattern1.nrof_ul_symbols = 4; - srsran_tdd_config_nr_t srsran_tdd_config_nr; - TESTASSERT(make_phy_tdd_cfg(tdd_ul_dl_cfg_common, &srsran_tdd_config_nr) == true); + srsran_duplex_config_nr_t srsran_duplex_config_nr; + TESTASSERT(make_phy_tdd_cfg(tdd_ul_dl_cfg_common, &srsran_duplex_config_nr) == true); - TESTASSERT(srsran_tdd_config_nr.pattern1.period_ms == 10); - TESTASSERT(srsran_tdd_config_nr.pattern1.nof_dl_slots == 7); - TESTASSERT(srsran_tdd_config_nr.pattern1.nof_dl_symbols == 6); - TESTASSERT(srsran_tdd_config_nr.pattern1.nof_ul_slots == 2); - TESTASSERT(srsran_tdd_config_nr.pattern1.nof_ul_symbols == 4); - TESTASSERT(srsran_tdd_config_nr.pattern2.period_ms == 0); + TESTASSERT(srsran_duplex_config_nr.tdd.pattern1.period_ms == 10); + TESTASSERT(srsran_duplex_config_nr.tdd.pattern1.nof_dl_slots == 7); + TESTASSERT(srsran_duplex_config_nr.tdd.pattern1.nof_dl_symbols == 6); + TESTASSERT(srsran_duplex_config_nr.tdd.pattern1.nof_ul_slots == 2); + TESTASSERT(srsran_duplex_config_nr.tdd.pattern1.nof_ul_symbols == 4); + TESTASSERT(srsran_duplex_config_nr.tdd.pattern2.period_ms == 0); return SRSRAN_SUCCESS; } @@ -116,7 +118,7 @@ int make_phy_harq_ack_cfg_test() phys_cell_group_cfg_s phys_cell_group_cfg = {}; phys_cell_group_cfg.pdsch_harq_ack_codebook = phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::dynamic_value; - srsran_ue_dl_nr_harq_ack_cfg_t srsran_ue_dl_nr_harq_ack_cfg; + srsran_harq_ack_cfg_hl_t srsran_ue_dl_nr_harq_ack_cfg; TESTASSERT(make_phy_harq_ack_cfg(phys_cell_group_cfg, &srsran_ue_dl_nr_harq_ack_cfg) == true); TESTASSERT(srsran_ue_dl_nr_harq_ack_cfg.harq_ack_codebook == srsran_pdsch_harq_ack_codebook_dynamic); @@ -676,6 +678,179 @@ int make_phy_nzp_csi_rs_resource_test() return SRSRAN_SUCCESS; } +int fill_phy_pdsch_cfg_common_test() +{ + // "pdsch-ConfigCommon": + // "setup": + // "pdsch-TimeDomainAllocationList": [ + // "mappingType": "typeA", + // "startSymbolAndLength": 40 + // ] + + asn1::rrc_nr::pdsch_cfg_common_s pdsch_cfg = {}; + pdsch_cfg.pdsch_time_domain_alloc_list.resize(1); + pdsch_cfg.pdsch_time_domain_alloc_list[0].map_type = + asn1::rrc_nr::pdsch_time_domain_res_alloc_s::map_type_opts::options::type_a; + pdsch_cfg.pdsch_time_domain_alloc_list[0].k0_present = false; + pdsch_cfg.pdsch_time_domain_alloc_list[0].start_symbol_and_len = 40; + + srsran_sch_hl_cfg_nr_t pdsch; + fill_phy_pdsch_cfg_common(pdsch_cfg, &pdsch); + + TESTASSERT(pdsch.nof_common_time_ra == 1); + TESTASSERT(pdsch.common_time_ra[0].k == 0); + TESTASSERT(pdsch.common_time_ra[0].mapping_type == srsran_sch_mapping_type_A); + TESTASSERT(pdsch.common_time_ra[0].sliv == 40); + + return SRSRAN_SUCCESS; +} + +int fill_phy_pucch_cfg_common_test() +{ + // "pucch-ConfigCommon": + // "setup": + // "pucch-ResourceCommon": 11, + // "pucch-GroupHopping": "neither", + // "p0-nominal": -90 + + asn1::rrc_nr::pucch_cfg_common_s pucch_cfg = {}; + pucch_cfg.pucch_res_common_present = true; + pucch_cfg.pucch_res_common = 11; + pucch_cfg.hop_id_present = false; + pucch_cfg.p0_nominal_present = true; + pucch_cfg.p0_nominal = -90; + pucch_cfg.pucch_group_hop = pucch_cfg_common_s::pucch_group_hop_opts::neither; + + srsran_pucch_nr_common_cfg_t pucch = {}; + fill_phy_pucch_cfg_common(pucch_cfg, &pucch); + + TESTASSERT(pucch.resource_common == 11); + TESTASSERT(pucch.p0_nominal == -90); + TESTASSERT(pucch.group_hopping == SRSRAN_PUCCH_NR_GROUP_HOPPING_NEITHER); + + return SRSRAN_SUCCESS; +} + +int fill_phy_pusch_cfg_common_test() +{ + // "pusch-ConfigCommon": + // "setup": { + // "pusch-TimeDomainAllocationList": [ + // "k2": 4, + // "mappingType": "typeA", + // "startSymbolAndLength": 27 + // ], + // "p0-NominalWithGrant": -76 + + asn1::rrc_nr::pusch_cfg_common_s pusch_cfg = {}; + pusch_cfg.pusch_time_domain_alloc_list.resize(1); + pusch_cfg.pusch_time_domain_alloc_list[0].map_type = + asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::options::type_a; + pusch_cfg.pusch_time_domain_alloc_list[0].k2_present = true; + pusch_cfg.pusch_time_domain_alloc_list[0].k2 = 4; + pusch_cfg.pusch_time_domain_alloc_list[0].start_symbol_and_len = 27; + pusch_cfg.p0_nominal_with_grant_present = true; + pusch_cfg.p0_nominal_with_grant = -76; + + srsran_sch_hl_cfg_nr_t pusch; + fill_phy_pusch_cfg_common(pusch_cfg, &pusch); + + TESTASSERT(pusch.nof_common_time_ra == 1); + TESTASSERT(pusch.common_time_ra[0].k == 4); + TESTASSERT(pusch.common_time_ra[0].mapping_type == srsran_sch_mapping_type_A); + TESTASSERT(pusch.common_time_ra[0].sliv == 27); + + return SRSRAN_SUCCESS; +} + +int fill_phy_carrier_cfg_test() +{ + // "frequencyInfoDL": + // "frequencyBandList": [ + // "freqBandIndicatorNR": 3 + // ], + // "offsetToPointA": 13, + // "scs-SpecificCarrierList": [ + // "offsetToCarrier": 0, + // "subcarrierSpacing": "kHz15", + // "carrierBandwidth": 52 + // ] + // + // ... + // + // "frequencyInfoUL": + // "frequencyBandList": [ + // "freqBandIndicatorNR": 3 + // ], + // "absoluteFrequencyPointA": 348564, + // "scs-SpecificCarrierList": [ + // "offsetToCarrier": 0, + // "subcarrierSpacing": "kHz15", + // "carrierBandwidth": 52 + // ], + // "p-Max": 10 + + asn1::rrc_nr::serving_cell_cfg_common_sib_s serv_cell_cfg = {}; + serv_cell_cfg.dl_cfg_common.freq_info_dl.freq_band_list.resize(1); + serv_cell_cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present = true; + serv_cell_cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr = 3; + serv_cell_cfg.dl_cfg_common.freq_info_dl.offset_to_point_a = 13; + serv_cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list.resize(1); + serv_cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = 0; + serv_cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing = + asn1::rrc_nr::subcarrier_spacing_opts::options::khz15; + serv_cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].carrier_bw = 52; + + serv_cell_cfg.ul_cfg_common.freq_info_ul.freq_band_list.resize(1); + serv_cell_cfg.ul_cfg_common.freq_info_ul.freq_band_list[0].freq_band_ind_nr_present = true; + serv_cell_cfg.ul_cfg_common.freq_info_ul.freq_band_list[0].freq_band_ind_nr = 3; + serv_cell_cfg.ul_cfg_common.freq_info_ul.absolute_freq_point_a_present = true; + serv_cell_cfg.ul_cfg_common.freq_info_ul.absolute_freq_point_a = 348564; + serv_cell_cfg.ul_cfg_common.freq_info_ul.scs_specific_carrier_list.resize(1); + serv_cell_cfg.ul_cfg_common.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = 0; + serv_cell_cfg.ul_cfg_common.freq_info_ul.scs_specific_carrier_list[0].subcarrier_spacing = + asn1::rrc_nr::subcarrier_spacing_opts::options::khz15; + serv_cell_cfg.ul_cfg_common.freq_info_ul.scs_specific_carrier_list[0].carrier_bw = 52; + serv_cell_cfg.ul_cfg_common.freq_info_ul.p_max_present = true; + serv_cell_cfg.ul_cfg_common.freq_info_ul.p_max = 10; + + srsran_carrier_nr_t carrier_nr = {}; + fill_phy_carrier_cfg(serv_cell_cfg, &carrier_nr); + + TESTASSERT(carrier_nr.offset_to_carrier == 0); + TESTASSERT(carrier_nr.scs == srsran_subcarrier_spacing_15kHz); + TESTASSERT(carrier_nr.nof_prb == 52); + TESTASSERT(carrier_nr.ul_center_frequency_hz == 1747.5e6); + + return SRSRAN_SUCCESS; +} + +int fill_phy_ssb_cfg_test() +{ + // "ssb-PositionsInBurst": + // "inOneGroup": "10000000" + // "ssb-PeriodicityServingCell": "ms20", + + asn1::rrc_nr::serving_cell_cfg_common_sib_s serv_cell_cfg = {}; + serv_cell_cfg.ssb_periodicity_serving_cell = + asn1::rrc_nr::serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::options::ms20; + serv_cell_cfg.ssb_positions_in_burst.group_presence_present = false; + serv_cell_cfg.ssb_positions_in_burst.in_one_group.from_number(128); + + phy_cfg_nr_t::ssb_cfg_t ssb = {}; + fill_phy_ssb_cfg(serv_cell_cfg, &ssb); + + TESTASSERT(ssb.periodicity_ms == 20); + + uint64_t position_in_burst = 0; + for (uint64_t i = 0; i < 8; i++) { + position_in_burst = position_in_burst << 1 | ssb.position_in_burst[i]; + } + TESTASSERT(position_in_burst == 128); + + return SRSRAN_SUCCESS; +} + int main() { auto& asn1_logger = srslog::fetch_basic_logger("ASN1", false); @@ -704,6 +879,11 @@ int main() TESTASSERT(make_phy_pusch_scaling_test() == SRSRAN_SUCCESS); TESTASSERT(make_phy_zp_csi_rs_resource_test() == SRSRAN_SUCCESS); TESTASSERT(make_phy_nzp_csi_rs_resource_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_pdsch_cfg_common_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_pucch_cfg_common_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_pusch_cfg_common_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_carrier_cfg_test() == SRSRAN_SUCCESS); + TESTASSERT(fill_phy_ssb_cfg_test() == SRSRAN_SUCCESS); srslog::flush(); printf("Success\n"); diff --git a/lib/test/asn1/rrc_test.cc b/lib/test/asn1/rrc_test.cc index d24fffe7c..80de22e1e 100644 --- a/lib/test/asn1/rrc_test.cc +++ b/lib/test/asn1/rrc_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/asn1/s1ap_test.cc b/lib/test/asn1/s1ap_test.cc index 29d19037c..58d80f955 100644 --- a/lib/test/asn1/s1ap_test.cc +++ b/lib/test/asn1/s1ap_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,10 +48,10 @@ int test_s1setup_request() TESTASSERT(init_choice.type().value == s1ap_elem_procs_o::init_msg_c::types_opts::s1_setup_request); s1_setup_request_s& s1req = init_choice.s1_setup_request(); TESTASSERT(not s1req.ext); - TESTASSERT(s1req.protocol_ies.global_enb_id.id == ASN1_S1AP_ID_GLOBAL_ENB_ID); - TESTASSERT(s1req.protocol_ies.global_enb_id.crit.value == crit_opts::reject); - TESTASSERT(s1req.protocol_ies.global_enb_id.value.enb_id.type().value == enb_id_c::types_opts::macro_enb_id); - TESTASSERT(s1req.protocol_ies.global_enb_id.value.enb_id.macro_enb_id().to_number() == 0x0019B); + TESTASSERT(s1req->global_enb_id.id == ASN1_S1AP_ID_GLOBAL_ENB_ID); + TESTASSERT(s1req->global_enb_id.crit.value == crit_opts::reject); + TESTASSERT(s1req->global_enb_id.value.enb_id.type().value == enb_id_c::types_opts::macro_enb_id); + TESTASSERT(s1req->global_enb_id.value.enb_id.macro_enb_id().to_number() == 0x0019B); // // // json_writer js; // // pdu.to_json(js); @@ -86,10 +86,10 @@ int test_init_ctxt_setup_req() TESTASSERT(pdu.init_msg().proc_code == 9); TESTASSERT(pdu.init_msg().crit.value == crit_opts::reject); s1ap_elem_procs_o::init_msg_c& init_choice = pdu.init_msg().value; - auto& ctxt_setup = init_choice.init_context_setup_request().protocol_ies; - TESTASSERT(ctxt_setup.ue_security_cap.id == 107); - TESTASSERT(ctxt_setup.ue_security_cap.value.encryption_algorithms.to_string() == "1100000000000000"); - TESTASSERT(ctxt_setup.ue_security_cap.value.integrity_protection_algorithms.to_string() == "1100000000000000"); + auto& ctxt_setup = init_choice.init_context_setup_request(); + TESTASSERT(ctxt_setup->ue_security_cap.id == 107); + TESTASSERT(ctxt_setup->ue_security_cap.value.encryption_algorithms.to_string() == "1100000000000000"); + TESTASSERT(ctxt_setup->ue_security_cap.value.integrity_protection_algorithms.to_string() == "1100000000000000"); TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS); @@ -108,11 +108,11 @@ int test_ue_ctxt_release_req() TESTASSERT(pdu.type().value == s1ap_pdu_c::types_opts::init_msg); TESTASSERT(pdu.init_msg().proc_code == ASN1_S1AP_ID_UE_CONTEXT_RELEASE_REQUEST); - auto& req = pdu.init_msg().value.ue_context_release_request().protocol_ies; - TESTASSERT(req.mme_ue_s1ap_id.value.value == 1); - TESTASSERT(req.enb_ue_s1ap_id.value.value == 1); - TESTASSERT(req.cause.value.type().value == cause_c::types_opts::radio_network); - TESTASSERT(req.cause.value.radio_network().value == cause_radio_network_opts::user_inactivity); + auto& req = pdu.init_msg().value.ue_context_release_request(); + TESTASSERT(req->mme_ue_s1ap_id.value.value == 1); + TESTASSERT(req->enb_ue_s1ap_id.value.value == 1); + TESTASSERT(req->cause.value.type().value == cause_c::types_opts::radio_network); + TESTASSERT(req->cause.value.radio_network().value == cause_radio_network_opts::user_inactivity); TESTASSERT(test_pack_unpack_consistency(pdu) == SRSASN_SUCCESS); @@ -187,9 +187,9 @@ int test_ho_request() TESTASSERT(pdu.type().value == s1ap_pdu_c::types_opts::init_msg); TESTASSERT(pdu.init_msg().proc_code == ASN1_S1AP_ID_HO_RES_ALLOC); TESTASSERT(pdu.init_msg().crit.value == crit_opts::reject); - auto& horeq = pdu.init_msg().value.ho_request().protocol_ies; + auto& horeq = pdu.init_msg().value.ho_request(); - auto& erab_item = horeq.erab_to_be_setup_list_ho_req.value[0].value.erab_to_be_setup_item_ho_req(); + auto& erab_item = horeq->erab_to_be_setup_list_ho_req.value[0]->erab_to_be_setup_item_ho_req(); TESTASSERT(erab_item.erab_id == 5); TESTASSERT(erab_item.gtp_teid.to_string() == "b7361c56"); @@ -203,15 +203,16 @@ int test_enb_status_transfer() s1ap_pdu_c pdu; TESTASSERT(pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ENB_STATUS_TRANSFER)); - auto& enb_status_transfer = pdu.init_msg().value.enb_status_transfer().protocol_ies; - enb_status_transfer.mme_ue_s1ap_id.value.value = 1; - enb_status_transfer.enb_ue_s1ap_id.value.value = 1; - enb_status_transfer.enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list.resize(1); + auto& enb_status_transfer = pdu.init_msg().value.enb_status_transfer(); + enb_status_transfer->mme_ue_s1ap_id.value.value = 1; + enb_status_transfer->enb_ue_s1ap_id.value.value = 1; + enb_status_transfer->enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list.resize( + 1); auto& bearer = - enb_status_transfer.enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list[0]; + enb_status_transfer->enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list[0]; TESTASSERT(bearer.load_info_obj(ASN1_S1AP_ID_BEARERS_SUBJECT_TO_STATUS_TRANSFER_ITEM)); - auto& bearer_item = bearer.value.bearers_subject_to_status_transfer_item(); + auto& bearer_item = bearer->bearers_subject_to_status_transfer_item(); bearer_item.erab_id = 5; bearer_item.dl_coun_tvalue.pdcp_sn = 5; @@ -230,11 +231,10 @@ int test_enb_status_transfer() s1ap_pdu_c pdu2; TESTASSERT(pdu2.unpack(bref2) == SRSASN_SUCCESS); - auto& bearer2 = - pdu2.init_msg() - .value.enb_status_transfer() - .protocol_ies.enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list[0]; - auto& bearer_item2 = bearer2.value.bearers_subject_to_status_transfer_item(); + auto& bearer2 = pdu2.init_msg() + .value.enb_status_transfer() + ->enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list[0]; + auto& bearer_item2 = bearer2->bearers_subject_to_status_transfer_item(); TESTASSERT(bearer_item2.dl_coun_tvalue.hfn == bearer_item.dl_coun_tvalue.hfn); TESTASSERT(bearer_item2.dl_coun_tvalue.hfn == 0); @@ -264,7 +264,7 @@ int test_load_info_obj() container.erab_failed_to_setup_list_ctxt_su_res.value.resize(1); container.erab_failed_to_setup_list_ctxt_su_res.value[0].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM); - TESTASSERT(container.erab_failed_to_setup_list_ctxt_su_res.value[0].id == ASN1_S1AP_ID_ERAB_ITEM); + TESTASSERT_EQ(ASN1_S1AP_ID_ERAB_ITEM, container.erab_failed_to_setup_list_ctxt_su_res.value[0].id()); return SRSRAN_SUCCESS; } @@ -276,15 +276,15 @@ int test_initial_ctxt_setup_response() tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_INIT_CONTEXT_SETUP); // Fill in the MME and eNB IDs - auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies; - container.mme_ue_s1ap_id.value = 1; - container.enb_ue_s1ap_id.value = 1; + auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp(); + container->mme_ue_s1ap_id.value = 1; + container->enb_ue_s1ap_id.value = 1; - container.erab_setup_list_ctxt_su_res.value.resize(1); + container->erab_setup_list_ctxt_su_res.value.resize(1); // Fill in the GTP bind address for all bearers - for (uint32_t i = 0; i < container.erab_setup_list_ctxt_su_res.value.size(); ++i) { - container.erab_setup_list_ctxt_su_res.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_SETUP_ITEM_CTXT_SU_RES); - auto& item = container.erab_setup_list_ctxt_su_res.value[i].value.erab_setup_item_ctxt_su_res(); + for (uint32_t i = 0; i < container->erab_setup_list_ctxt_su_res.value.size(); ++i) { + container->erab_setup_list_ctxt_su_res.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_SETUP_ITEM_CTXT_SU_RES); + auto& item = container->erab_setup_list_ctxt_su_res.value[i]->erab_setup_item_ctxt_su_res(); item.erab_id = 1; // uint32_to_uint8(teid_in, item.gtp_teid.data()); item.transport_layer_address.resize(32); diff --git a/lib/test/asn1/srsran_asn1_nas_test.cc b/lib/test/asn1/srsran_asn1_nas_test.cc index ec2df32a8..b3ff4c243 100644 --- a/lib/test/asn1/srsran_asn1_nas_test.cc +++ b/lib/test/asn1/srsran_asn1_nas_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,19 +20,12 @@ */ #include "srsran/asn1/liblte_mme.h" +#include "srsran/common/test_common.h" #include "srsran/srslog/srslog.h" #include #include #include -#define TESTASSERT(cond) \ - { \ - if (!(cond)) { \ - std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ - return -1; \ - } \ - } - int nas_dedicated_eps_bearer_context_setup_request_test() { auto& nas_logger = srslog::fetch_basic_logger("NAS", false); @@ -112,7 +105,95 @@ int nas_dedicated_eps_bearer_context_setup_request_test() srslog::flush(); printf("Test NAS Activate Dedicated EPS Bearer Context Request successfull\n"); - return 0; + return SRSRAN_SUCCESS; +} + +int downlink_generic_nas_transport_unpacking_test() +{ + uint8_t nas_message[] = { + 0x27, 0xae, 0x80, 0xc8, 0xf9, 0x06, 0x07, 0x68, 0x01, 0x00, 0x06, 0xf0, 0x00, 0x00, 0x00, 0x08, 0x70}; + srsran::unique_byte_buffer_t buf; + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT dl_generic_nas_transport; + LIBLTE_ERROR_ENUM err; + + copy_msg_to_buffer(buf, nas_message); + err = liblte_mme_unpack_downlink_generic_nas_transport_msg((LIBLTE_BYTE_MSG_STRUCT*)buf.get(), + &dl_generic_nas_transport); + TESTASSERT(err == LIBLTE_SUCCESS); + TESTASSERT(dl_generic_nas_transport.generic_msg_cont_type == 1); + TESTASSERT(dl_generic_nas_transport.generic_msg_cont.N_bytes == 6); + TESTASSERT(dl_generic_nas_transport.add_info_present == false); + + return SRSRAN_SUCCESS; +} + +int downlink_generic_nas_transport_packing_test() +{ + uint8_t nas_message[] = { + 0x27, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0x68, 0x01, 0x00, 0x06, 0xf0, 0x00, 0x00, 0x00, 0x08, 0x70}; + uint8_t generic_msg_cont[] = {0xf0, 0x00, 0x00, 0x00, 0x08, 0x70}; + LIBLTE_BYTE_MSG_STRUCT buf = {}; + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT dl_generic_nas_transport; + LIBLTE_ERROR_ENUM err; + + dl_generic_nas_transport.generic_msg_cont_type = 1; + dl_generic_nas_transport.generic_msg_cont.N_bytes = sizeof(generic_msg_cont); + memcpy(dl_generic_nas_transport.generic_msg_cont.msg, generic_msg_cont, sizeof(generic_msg_cont)); + dl_generic_nas_transport.add_info_present = false; + + err = liblte_mme_pack_downlink_generic_nas_transport_msg( + &dl_generic_nas_transport, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, 0xffffffff, &buf); + + TESTASSERT(err == LIBLTE_SUCCESS); + TESTASSERT(buf.N_bytes == sizeof(nas_message)); + TESTASSERT(memcmp(buf.msg, nas_message, buf.N_bytes) == 0); + return SRSRAN_SUCCESS; +} + +int downlink_generic_nas_transport_with_add_info_unpacking_test() +{ + uint8_t nas_message[] = {0x27, 0xae, 0x80, 0xc8, 0xf9, 0x06, 0x07, 0x68, 0x01, 0x00, 0x06, + 0xf0, 0x00, 0x00, 0x00, 0x08, 0x70, 0x65, 0x02, 0x11, 0x11}; + srsran::unique_byte_buffer_t buf; + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT dl_generic_nas_transport; + LIBLTE_ERROR_ENUM err; + + copy_msg_to_buffer(buf, nas_message); + err = liblte_mme_unpack_downlink_generic_nas_transport_msg((LIBLTE_BYTE_MSG_STRUCT*)buf.get(), + &dl_generic_nas_transport); + TESTASSERT(err == LIBLTE_SUCCESS); + TESTASSERT(dl_generic_nas_transport.generic_msg_cont_type == 1); + TESTASSERT(dl_generic_nas_transport.generic_msg_cont.N_bytes == 6); + TESTASSERT(dl_generic_nas_transport.add_info_present == true); + TESTASSERT(dl_generic_nas_transport.add_info.N_octets == 2); + + return SRSRAN_SUCCESS; +} + +int downlink_generic_nas_transport_with_add_info_packing_test() +{ + uint8_t nas_message[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0xff, 0x07, 0x68, 0x01, 0x00, 0x06, + 0xf0, 0x00, 0x00, 0x00, 0x08, 0x70, 0x65, 0x02, 0x11, 0x11}; + uint8_t generic_msg_cont[] = {0xf0, 0x00, 0x00, 0x00, 0x08, 0x70}; + uint8_t add_info[] = {0x11, 0x11}; + LIBLTE_BYTE_MSG_STRUCT buf = {}; + LIBLTE_MME_DOWNLINK_GENERIC_NAS_TRANSPORT_MSG_STRUCT dl_generic_nas_transport; + LIBLTE_ERROR_ENUM err; + + dl_generic_nas_transport.generic_msg_cont_type = 1; + dl_generic_nas_transport.generic_msg_cont.N_bytes = sizeof(generic_msg_cont); + memcpy(dl_generic_nas_transport.generic_msg_cont.msg, generic_msg_cont, sizeof(generic_msg_cont)); + dl_generic_nas_transport.add_info_present = true; + dl_generic_nas_transport.add_info.N_octets = sizeof(add_info); + memcpy(dl_generic_nas_transport.add_info.info, add_info, sizeof(add_info)); + + err = liblte_mme_pack_downlink_generic_nas_transport_msg( + &dl_generic_nas_transport, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED, 0xffffffff, &buf); + + TESTASSERT(err == LIBLTE_SUCCESS); + TESTASSERT(buf.N_bytes == sizeof(nas_message)); + TESTASSERT(memcmp(buf.msg, nas_message, buf.N_bytes) == 0); + return SRSRAN_SUCCESS; } int main(int argc, char** argv) @@ -121,9 +202,13 @@ int main(int argc, char** argv) asn1_logger.set_level(srslog::basic_levels::debug); asn1_logger.set_hex_dump_max_size(-1); - srslog::init(); + srsran::test_init(argc, argv); - int result = nas_dedicated_eps_bearer_context_setup_request_test(); + TESTASSERT(nas_dedicated_eps_bearer_context_setup_request_test() == SRSRAN_SUCCESS); + TESTASSERT(downlink_generic_nas_transport_unpacking_test() == SRSRAN_SUCCESS); + TESTASSERT(downlink_generic_nas_transport_packing_test() == SRSRAN_SUCCESS); + TESTASSERT(downlink_generic_nas_transport_with_add_info_unpacking_test() == SRSRAN_SUCCESS); + TESTASSERT(downlink_generic_nas_transport_with_add_info_packing_test() == SRSRAN_SUCCESS); - return result; + return SRSRAN_SUCCESS; } diff --git a/lib/test/asn1/srsran_asn1_rrc_dl_ccch_test.cc b/lib/test/asn1/srsran_asn1_rrc_dl_ccch_test.cc index 1c120dc17..14e969d21 100644 --- a/lib/test/asn1/srsran_asn1_rrc_dl_ccch_test.cc +++ b/lib/test/asn1/srsran_asn1_rrc_dl_ccch_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/asn1/srsran_asn1_rrc_dl_dcch_test.cc b/lib/test/asn1/srsran_asn1_rrc_dl_dcch_test.cc index 487f98a26..e00f0b911 100644 --- a/lib/test/asn1/srsran_asn1_rrc_dl_dcch_test.cc +++ b/lib/test/asn1/srsran_asn1_rrc_dl_dcch_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/asn1/srsran_asn1_rrc_mcch_test.cc b/lib/test/asn1/srsran_asn1_rrc_mcch_test.cc index 6506055d3..38c954e71 100644 --- a/lib/test/asn1/srsran_asn1_rrc_mcch_test.cc +++ b/lib/test/asn1/srsran_asn1_rrc_mcch_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/asn1/srsran_asn1_rrc_meas_test.cc b/lib/test/asn1/srsran_asn1_rrc_meas_test.cc index 47d873a30..72d48b8cc 100644 --- a/lib/test/asn1/srsran_asn1_rrc_meas_test.cc +++ b/lib/test/asn1/srsran_asn1_rrc_meas_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,19 +19,15 @@ * */ +#include "srsran/asn1/rrc/dl_dcch_msg.h" #include "srsran/asn1/rrc/ul_dcch_msg.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/bcd_helpers.h" +#include "srsran/common/test_common.h" #include "srsran/interfaces/rrc_interface_types.h" #include -#define TESTASSERT(cond) \ - { \ - if (!(cond)) { \ - std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ - return -1; \ - } \ - } +#define JSON_OUTPUT 0 using namespace asn1; using namespace asn1::rrc; @@ -92,6 +88,136 @@ int meas_obj_test() return 0; } +int test_meas_config() +{ + // RRC reconfig with NR meas config + uint8_t tv[] = {0x20, 0x10, 0x15, 0xc0, 0x40, 0x00, 0x00, 0x96, 0x32, 0x18, 0x10, 0xa8, 0x04, 0xd6, 0xa0, + 0x10, 0x02, 0x01, 0x02, 0x18, 0x9a, 0x00, 0x03, 0x41, 0x81, 0x0e, 0x00, 0x01, 0x38, 0x00, + 0xc0, 0x40, 0x61, 0xc0, 0x00, 0x83, 0x00, 0x31, 0x02, 0x90, 0x60, 0x88, 0x00}; + + asn1::SRSASN_CODE err; + + cbit_ref bref(tv, sizeof(tv)); + + dl_dcch_msg_s recfg_msg_unpacked; + + TESTASSERT(recfg_msg_unpacked.unpack(bref) == SRSASN_SUCCESS); + + TESTASSERT(test_pack_unpack_consistency(recfg_msg_unpacked) == SRSASN_SUCCESS); + +#if JSON_OUTPUT + int unpacked_len = bref.distance_bytes(); + asn1::json_writer json_writer1; + recfg_msg_unpacked.to_json(json_writer1); + srslog::fetch_basic_logger("ASN1").info( + tv, sizeof(tv), "RRC config unpacked (%d B): \n %s", unpacked_len, json_writer1.to_string().c_str()); +#endif + + dl_dcch_msg_s recfg_msg_packed; + + recfg_msg_packed.msg.set_c1(); + recfg_msg_packed.msg.c1().set_rrc_conn_recfg(); // = dl_dcch_msg_type_c::c1_c_::types::rrc_conn_recfg; + recfg_msg_packed.msg.c1().rrc_conn_recfg().crit_exts.set_c1(); + recfg_msg_packed.msg.c1().rrc_conn_recfg().crit_exts.c1().set_rrc_conn_recfg_r8(); + recfg_msg_packed.msg.c1().rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8().meas_cfg_present = true; + + meas_cfg_s& meas_cfg = recfg_msg_packed.msg.c1().rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8().meas_cfg; + + meas_cfg.meas_obj_to_add_mod_list_present = true; + meas_cfg.meas_obj_to_add_mod_list.resize(2); + + auto& meas_obj = meas_cfg.meas_obj_to_add_mod_list[0]; + meas_obj.meas_obj_id = 1; + meas_obj.meas_obj.set_meas_obj_eutra(); + meas_obj.meas_obj.meas_obj_eutra().carrier_freq = 300; + meas_obj.meas_obj.meas_obj_eutra().allowed_meas_bw = allowed_meas_bw_opts::mbw50; + meas_obj.meas_obj.meas_obj_eutra().presence_ant_port1 = false; + meas_obj.meas_obj.meas_obj_eutra().neigh_cell_cfg.from_number(0b01); + + auto& meas_obj2 = meas_cfg.meas_obj_to_add_mod_list[1]; + meas_obj2.meas_obj_id = 2; + meas_obj2.meas_obj.set_meas_obj_nr_r15(); + meas_obj2.meas_obj.meas_obj_nr_r15().carrier_freq_r15 = 634176; + meas_obj2.meas_obj.meas_obj_nr_r15().rs_cfg_ssb_r15.meas_timing_cfg_r15.periodicity_and_offset_r15.set_sf20_r15(); + meas_obj2.meas_obj.meas_obj_nr_r15().rs_cfg_ssb_r15.meas_timing_cfg_r15.ssb_dur_r15 = + asn1::rrc::mtc_ssb_nr_r15_s::ssb_dur_r15_opts::sf1; + meas_obj2.meas_obj.meas_obj_nr_r15().rs_cfg_ssb_r15.subcarrier_spacing_ssb_r15 = + asn1::rrc::rs_cfg_ssb_nr_r15_s::subcarrier_spacing_ssb_r15_opts::khz30; + meas_obj2.meas_obj.meas_obj_nr_r15().ext = true; + meas_obj2.meas_obj.meas_obj_nr_r15().band_nr_r15.set_present(true); + meas_obj2.meas_obj.meas_obj_nr_r15().band_nr_r15.get()->set_setup() = 78; + + // report config + meas_cfg.report_cfg_to_add_mod_list_present = true; + meas_cfg.report_cfg_to_add_mod_list.resize(1); + auto& report_cfg = meas_cfg.report_cfg_to_add_mod_list[0]; + + report_cfg.report_cfg_id = 1; + report_cfg.report_cfg.set_report_cfg_inter_rat(); + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.set_event(); + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().event_id.set_event_b1_nr_r15(); + report_cfg.report_cfg.report_cfg_inter_rat() + .trigger_type.event() + .event_id.event_b1_nr_r15() + .b1_thres_nr_r15.set_nr_rsrp_r15(); + report_cfg.report_cfg.report_cfg_inter_rat() + .trigger_type.event() + .event_id.event_b1_nr_r15() + .b1_thres_nr_r15.nr_rsrp_r15() = 56; + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().event_id.event_b1_nr_r15().report_on_leave_r15 = + false; + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().hysteresis = 0; + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().time_to_trigger = time_to_trigger_opts::ms100; + + report_cfg.report_cfg.report_cfg_inter_rat().max_report_cells = 8; + report_cfg.report_cfg.report_cfg_inter_rat().report_interv = report_interv_opts::ms120; + report_cfg.report_cfg.report_cfg_inter_rat().report_amount = report_cfg_inter_rat_s::report_amount_opts::r1; + report_cfg.report_cfg.report_cfg_inter_rat().ext = true; + report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.set_present(true); + report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_rsrp = true; + report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_rsrq = true; + report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_sinr = true; + + // measIdToAddModList + meas_cfg.meas_id_to_add_mod_list_present = true; + meas_cfg.meas_id_to_add_mod_list.resize(1); + auto& meas_id = meas_cfg.meas_id_to_add_mod_list[0]; + meas_id.meas_id = 1; + meas_id.meas_obj_id = 2; + meas_id.report_cfg_id = 1; + + // quantityConfig + meas_cfg.quant_cfg_present = true; + meas_cfg.quant_cfg.quant_cfg_eutra_present = true; + meas_cfg.quant_cfg.ext = true; + meas_cfg.quant_cfg.quant_cfg_nr_list_r15.set_present(true); + meas_cfg.quant_cfg.quant_cfg_nr_list_r15.get()->resize(1); + auto& meas_quant = meas_cfg.quant_cfg.quant_cfg_nr_list_r15.get()[0]; + meas_quant[0].meas_quant_cell_nr_r15.filt_coeff_rsrp_r15_present = true; + meas_quant[0].meas_quant_cell_nr_r15.filt_coeff_rsrp_r15 = filt_coef_opts::fc3; + + // measGapConfig + meas_cfg.meas_gap_cfg_present = true; + meas_cfg.meas_gap_cfg.set_setup(); + meas_cfg.meas_gap_cfg.setup().gap_offset.set_gp0() = 16; + + uint8_t pack_buffer[1024]; + bit_ref bref2(pack_buffer, sizeof(pack_buffer)); + recfg_msg_packed.pack(bref2); + int packed_len = bref2.distance_bytes(); + TESTASSERT(sizeof(tv) == packed_len); + TESTASSERT(memcmp(pack_buffer, tv, packed_len) == 0); + +#if JSON_OUTPUT + asn1::json_writer json_writer2; + recfg_msg_packed.to_json(json_writer2); + srslog::fetch_basic_logger("ASN1").info( + pack_buffer, packed_len, "RRC config packed (%d B): \n %s", packed_len, json_writer2.to_string().c_str()); +#endif + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { auto& asn1_logger = srslog::fetch_basic_logger("ASN1", false); @@ -100,7 +226,8 @@ int main(int argc, char** argv) srslog::init(); - TESTASSERT(meas_obj_test() == 0); + TESTASSERT(meas_obj_test() == SRSRAN_SUCCESS); + TESTASSERT(test_meas_config() == SRSRAN_SUCCESS); return 0; } diff --git a/lib/test/asn1/srsran_asn1_rrc_nr_test.cc b/lib/test/asn1/srsran_asn1_rrc_nr_test.cc new file mode 100644 index 000000000..0e1281f99 --- /dev/null +++ b/lib/test/asn1/srsran_asn1_rrc_nr_test.cc @@ -0,0 +1,1639 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/asn1/rrc_nr.h" +#include "srsran/common/test_common.h" +#include + +#define JSON_OUTPUT 0 + +#define HAVE_PCAP 0 +#if HAVE_PCAP +#include "srsran/common/test_pcap.h" +#endif + +using namespace asn1; +using namespace asn1::rrc_nr; + +void test_rrc_setup_complete() +{ + uint8_t msg[] = {0x10, 0xc0, 0x10, 0x00, 0x20, 0x25, 0x97, 0xe0, 0x1e, 0x1e, 0x34, 0xb5, 0x30, 0xb7, 0xe0, 0x04, + 0x10, 0x90, 0x00, 0xbf, 0x20, 0x0f, 0x11, 0x08, 0x00, 0x10, 0x15, 0x66, 0x75, 0xf7, 0x12, 0xe0, + 0x4f, 0x07, 0x0f, 0x07, 0x07, 0x10, 0x03, 0x87, 0xe0, 0x04, 0x10, 0x90, 0x00, 0xbf, 0x20, 0x0f, + 0x11, 0x08, 0x00, 0x10, 0x15, 0x66, 0x75, 0xf7, 0x11, 0x00, 0x10, 0x32, 0xe0, 0x4f, 0x07, 0x0f, + 0x07, 0x02, 0xf0, 0x20, 0x10, 0x15, 0x20, 0x0f, 0x11, 0x00, 0x00, 0x06, 0x41, 0x70, 0x7f, 0x07, + 0x00, 0x00, 0x01, 0x88, 0x0b, 0x01, 0x80, 0x10, 0x17, 0x40, 0x00, 0x09, 0x05, 0x30, 0x10, 0x10}; + // 10c01000202597e01e1e34b530b7e004109000bf200f11080010156675f712e04f070f0707100387e004109000bf200f11080010156675f711001032e04f070f0702f0201015200f1100000641707f07000001880b0180101740000905301010 + + asn1::cbit_ref bref{msg, sizeof(msg)}; + asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; + TESTASSERT_SUCCESS(ul_dcch_msg.unpack(bref)); + + TESTASSERT_EQ(ul_dcch_msg_type_c::types_opts::c1, ul_dcch_msg.msg.type().value); + TESTASSERT_EQ(ul_dcch_msg_type_c::c1_c_::types_opts::rrc_setup_complete, ul_dcch_msg.msg.c1().type().value); + + TESTASSERT_SUCCESS(test_pack_unpack_consistency(ul_dcch_msg)); +} + +int test_eutra_nr_capabilities() +{ + struct ue_mrdc_cap_s mrdc_cap; + band_combination_s band_combination; + struct band_params_c band_param_eutra; + band_param_eutra.set_eutra(); + band_param_eutra.eutra().ca_bw_class_dl_eutra_present = true; + band_param_eutra.eutra().ca_bw_class_ul_eutra_present = true; + band_param_eutra.eutra().band_eutra = 1; + band_param_eutra.eutra().ca_bw_class_dl_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; + band_param_eutra.eutra().ca_bw_class_ul_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; + band_combination.band_list.push_back(band_param_eutra); + struct band_params_c band_param_nr; + band_param_nr.set_nr(); + band_param_nr.nr().ca_bw_class_dl_nr_present = true; + band_param_nr.nr().ca_bw_class_ul_nr_present = true; + band_param_nr.nr().band_nr = 78; + band_param_nr.nr().ca_bw_class_dl_nr = asn1::rrc_nr::ca_bw_class_nr_opts::options::a; + band_param_nr.nr().ca_bw_class_ul_nr = asn1::rrc_nr::ca_bw_class_nr_opts::options::a; + band_combination.band_list.push_back(band_param_nr); + + mrdc_cap.rf_params_mrdc.supported_band_combination_list.push_back(band_combination); + + mrdc_cap.rf_params_mrdc.ext = true; + + // RF Params MRDC applied_freq_band_list_filt + freq_band_info_c band_info_eutra; + band_info_eutra.set_band_info_eutra(); + band_info_eutra.band_info_eutra().ca_bw_class_dl_eutra_present = false; + band_info_eutra.band_info_eutra().ca_bw_class_ul_eutra_present = false; + band_info_eutra.band_info_eutra().band_eutra = 1; + mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_eutra); + + freq_band_info_c band_info_nr; + band_info_nr.set_band_info_nr(); + band_info_nr.band_info_nr().band_nr = 78; + mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_nr); + + // rf_params_mrdc supported band combination list v1540 + + band_combination_list_v1540_l* band_combination_list_v1450 = new band_combination_list_v1540_l(); + band_combination_v1540_s band_combination_v1540; + + band_params_v1540_s band_params_a; + band_params_a.srs_tx_switch_present = true; + band_params_a.srs_carrier_switch_present = false; + band_params_a.srs_tx_switch.supported_srs_tx_port_switch = + band_params_v1540_s::srs_tx_switch_s_::supported_srs_tx_port_switch_opts::not_supported; + band_combination_v1540.band_list_v1540.push_back(band_params_a); + + band_params_v1540_s band_params_b; + band_params_b.srs_tx_switch_present = true; + band_params_b.srs_tx_switch.supported_srs_tx_port_switch = + band_params_v1540_s::srs_tx_switch_s_::supported_srs_tx_port_switch_opts::t1r2; + band_params_b.srs_carrier_switch_present = false; + band_combination_v1540.band_list_v1540.push_back(band_params_b); + + // clang-format off + band_combination_v1540.ca_params_nr_v1540_present = false; + band_combination_v1540.ca_params_nr_v1540.simul_csi_reports_all_cc_present = true; + band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.max_num_simul_nzp_csi_rs_act_bwp_all_cc_present = true; + band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.max_num_simul_nzp_csi_rs_act_bwp_all_cc = 5; + band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.total_num_ports_simul_nzp_csi_rs_act_bwp_all_cc_present = true; + band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.total_num_ports_simul_nzp_csi_rs_act_bwp_all_cc = 32; + // clang-format on + band_combination_list_v1450->push_back(band_combination_v1540); + mrdc_cap.rf_params_mrdc.supported_band_combination_list_v1540.reset(band_combination_list_v1450); + + feature_set_combination_l feature_set_combination; + + feature_sets_per_band_l feature_sets_per_band; + + feature_set_c feature_set_eutra; + feature_set_eutra.set_eutra(); + feature_set_eutra.eutra().dl_set_eutra = 1; + feature_set_eutra.eutra().ul_set_eutra = 1; + feature_sets_per_band.push_back(feature_set_eutra); + + feature_set_combination.push_back(feature_sets_per_band); + + feature_set_c feature_set_nr; + feature_set_nr.set_nr(); + feature_set_nr.nr().dl_set_nr = 1; + feature_set_nr.nr().ul_set_nr = 1; + feature_sets_per_band.push_back(feature_set_nr); + + feature_set_combination.push_back(feature_sets_per_band); + + mrdc_cap.feature_set_combinations.push_back(feature_set_combination); + + // Pack mrdc_cap + uint8_t buffer[1024]; + asn1::bit_ref bref(buffer, sizeof(buffer)); + mrdc_cap.pack(bref); + + TESTASSERT(test_pack_unpack_consistency(mrdc_cap) == SRSASN_SUCCESS); + + srslog::fetch_basic_logger("RRC").info( + buffer, bref.distance_bytes(), "Packed cap struct (%d bytes):", bref.distance_bytes()); + + return SRSRAN_SUCCESS; +} + +int test_ue_mrdc_capabilities() +{ + uint8_t msg[] = {0x01, 0x1c, 0x04, 0x81, 0x60, 0x00, 0x1c, 0x4d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x40, 0x04, 0x04, 0xd0, 0x10, 0x74, 0x06, 0x14, 0xe8, 0x1b, 0x10, + 0x78, 0x00, 0x00, 0x20, 0x00, 0x10, 0x08, 0x08, 0x01, 0x00, 0x20}; + // 011c048160001c4d0000000400400404d010740614e81b107800002000100808010020 + + asn1::cbit_ref bref{msg, sizeof(msg)}; + ue_mrdc_cap_s mrdc_cap; + + TESTASSERT(mrdc_cap.unpack(bref) == SRSASN_SUCCESS); + + TESTASSERT(test_pack_unpack_consistency(mrdc_cap) == SRSASN_SUCCESS); + + return SRSRAN_SUCCESS; +} + +int test_ue_rrc_reconfiguration() +{ + uint8_t rrc_msg[] = "\x08\x81\x7c\x5c\x40\xb1\xc0\x7d\x48\x3a\x04\xc0\x3e\x01\x04\x54" + "\x1e\xb5\x00\x02\xe8\x53\x98\xdf\x46\x93\x4b\x80\x04\xd2\x69\x34" + "\x00\x00\x08\xc9\x8d\x6d\x8c\xa2\x01\xff\x00\x00\x00\x00\x01\x1b" + "\x82\x21\x00\x00\x04\x04\x00\xd1\x14\x0e\x70\x00\x00\x08\xc9\xc6" + "\xb6\xc6\x44\xa0\x00\x1e\xb8\x95\x63\xe0\x24\x94\x22\x0d\xb8\x44" + "\x70\x0c\x02\x10\xb0\x1d\x80\x48\xf1\x18\x06\xea\x00\x08\x0e\x01" + "\x25\xc0\xc8\x80\x37\x08\x42\x00\x00\x88\x16\x50\x02\x0c\x82\x00" + "\x00\x20\x69\x81\x01\x45\x0a\x00\x0e\x48\x18\x00\x01\x33\x55\x64" + "\x84\x1c\x00\x10\x40\xc2\x05\x0c\x1c\x9c\x40\x91\x42\xc6\x0d\x1c" + "\x3c\x8e\x00\x00\x32\x21\x40\x30\x20\x01\x91\x4a\x01\x82\x00\x0c" + "\x8c\x50\x0c\x18\x00\x64\x42\x80\xe1\x00\x03\x22\x94\x07\x0a\x00" + "\x19\x18\xa0\x38\x60\x00\xc8\x85\x02\xc3\x80\x06\x45\x28\x16\x20" + "\x64\x00\x41\x6c\x48\x04\x62\x82\x18\xa0\x08\xc5\x04\xb1\x60\x11" + "\x8a\x0a\x63\x00\x23\x14\x16\xc6\x80\x46\x28\x31\x8e\x00\x8c\x50" + "\x6b\x1e\x01\x18\xa0\xe6\x40\x00\x32\x31\x40\xb2\x23\x10\x0a\x08" + "\x40\x90\x86\x05\x10\x43\xcc\x3b\x2a\x6e\x4d\x01\xa4\x92\x1e\x2e" + "\xe0\x0c\x10\xe0\x00\x00\x01\x8f\xfd\x29\x49\x8c\x63\x72\x81\x60" + "\x00\x02\x19\x70\x00\x00\x00\x00\x00\x00\x52\xf0\x0f\xa0\x84\x8a" + "\xd5\x45\x00\x47\x00\x18\x00\x08\x20\x00\xe2\x10\x02\x40\x80\x70" + "\x10\x10\x84\x00\x0e\x21\x00\x1c\xb0\x0e\x04\x02\x20\x80\x01\xc4" + "\x20\x03\x96\x01\xc0\xc0\x42\x10\x00\x38\x84\x00\x73\x00\x38\x20" + "\x08\x82\x00\x07\x10\x80\x0e\x60\x00\x40\x00\x00\x04\x10\xc0\x40" + "\x80\xc1\x00\xe0\xd0\x00\x0e\x48\x10\x00\x00\x02\x00\x40\x00\x80" + "\x60\x00\x80\x90\x02\x20\x0a\x40\x00\x02\x38\x90\x11\x31\xc8"; + + uint32_t rrc_msg_len = sizeof(rrc_msg); + cbit_ref bref(&rrc_msg[0], sizeof(rrc_msg)); + rrc_recfg_s rrc_recfg; + + TESTASSERT(rrc_recfg.unpack(bref) == SRSASN_SUCCESS); + TESTASSERT(rrc_recfg.rrc_transaction_id == 0); +#if JSON_OUTPUT + json_writer jw; + rrc_recfg.to_json(jw); + srslog::fetch_basic_logger("RRC").info("RRC Reconfig: \n %s", jw.to_string().c_str()); +#endif + + TESTASSERT(rrc_recfg.crit_exts.type() == asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg); + TESTASSERT(rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.size() > 0); + + cell_group_cfg_s cell_group_cfg; + cbit_ref bref0(rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.data(), + rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.size()); + TESTASSERT(cell_group_cfg.unpack(bref0) == SRSASN_SUCCESS); +#if JSON_OUTPUT + json_writer jw1; + cell_group_cfg.to_json(jw1); + srslog::fetch_basic_logger("RRC").info("RRC Secondary Cell Group: \n %s", jw1.to_string().c_str()); +#endif + TESTASSERT(cell_group_cfg.cell_group_id == 1); + TESTASSERT(cell_group_cfg.rlc_bearer_to_add_mod_list.size() == 1); + TESTASSERT(cell_group_cfg.mac_cell_group_cfg_present == true); + TESTASSERT(cell_group_cfg.phys_cell_group_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg_present == true); + return SRSRAN_SUCCESS; +} + +int test_radio_bearer_config() +{ + uint8_t rrc_msg[] = "\x14\x09\x28\x17\x87\xc0\x0c\x28"; + cbit_ref bref(&rrc_msg[0], sizeof(rrc_msg)); + radio_bearer_cfg_s radio_bearer_cfg; + TESTASSERT(radio_bearer_cfg.unpack(bref) == SRSASN_SUCCESS); +#if JSON_OUTPUT + json_writer jw; + radio_bearer_cfg.to_json(jw); + srslog::fetch_basic_logger("RRC").info("RRC Bearer CFG Message: \n %s", jw.to_string().c_str()); +#endif + TESTASSERT(radio_bearer_cfg.drb_to_add_mod_list.size() == 1); + TESTASSERT(radio_bearer_cfg.security_cfg_present == true); + TESTASSERT(radio_bearer_cfg.security_cfg.security_algorithm_cfg_present == true); + TESTASSERT(radio_bearer_cfg.security_cfg.key_to_use_present == true); + + // full RRC reconfig pack + rrc_recfg_s reconfig; + reconfig.rrc_transaction_id = 0; + rrc_recfg_ies_s& recfg_ies = reconfig.crit_exts.set_rrc_recfg(); + + recfg_ies.radio_bearer_cfg_present = true; + recfg_ies.radio_bearer_cfg.drb_to_add_mod_list.resize(1); + + auto& drb_item = recfg_ies.radio_bearer_cfg.drb_to_add_mod_list[0]; + drb_item.drb_id = 1; + drb_item.cn_assoc_present = true; + drb_item.cn_assoc.set_eps_bearer_id() = 5; + drb_item.pdcp_cfg_present = true; + drb_item.pdcp_cfg.ciphering_disabled_present = true; + drb_item.pdcp_cfg.drb_present = true; + drb_item.pdcp_cfg.drb.pdcp_sn_size_dl_present = true; + drb_item.pdcp_cfg.drb.pdcp_sn_size_dl = asn1::rrc_nr::pdcp_cfg_s::drb_s_::pdcp_sn_size_dl_opts::len18bits; + drb_item.pdcp_cfg.drb.pdcp_sn_size_ul_present = true; + drb_item.pdcp_cfg.drb.pdcp_sn_size_ul = asn1::rrc_nr::pdcp_cfg_s::drb_s_::pdcp_sn_size_ul_opts::len18bits; + drb_item.pdcp_cfg.drb.discard_timer_present = true; + drb_item.pdcp_cfg.drb.discard_timer = asn1::rrc_nr::pdcp_cfg_s::drb_s_::discard_timer_opts::ms100; + drb_item.pdcp_cfg.drb.hdr_compress.set_not_used(); + drb_item.pdcp_cfg.t_reordering_present = true; + drb_item.pdcp_cfg.t_reordering = asn1::rrc_nr::pdcp_cfg_s::t_reordering_opts::ms0; + + recfg_ies.radio_bearer_cfg.security_cfg_present = true; + recfg_ies.radio_bearer_cfg.security_cfg.key_to_use_present = true; + recfg_ies.radio_bearer_cfg.security_cfg.key_to_use = asn1::rrc_nr::security_cfg_s::key_to_use_opts::secondary; + recfg_ies.radio_bearer_cfg.security_cfg.security_algorithm_cfg_present = true; + recfg_ies.radio_bearer_cfg.security_cfg.security_algorithm_cfg.ciphering_algorithm = ciphering_algorithm_opts::nea2; + + uint8_t buffer[1024]; + asn1::bit_ref bref_pack(buffer, sizeof(buffer)); + TESTASSERT(reconfig.pack(bref_pack) == asn1::SRSASN_SUCCESS); + TESTASSERT(test_pack_unpack_consistency(reconfig) == SRSASN_SUCCESS); + +#if JSON_OUTPUT + reconfig.to_json(jw); + srslog::fetch_basic_logger("RRC").info("RRC Reconfig Message: \n %s", jw.to_string().c_str()); +#endif + + // only pack the radio bearer config to compare against TV + asn1::bit_ref bref_pack2(buffer, sizeof(buffer)); + radio_bearer_cfg_s& radio_bearer_cfg_pack = recfg_ies.radio_bearer_cfg; + TESTASSERT(radio_bearer_cfg_pack.pack(bref_pack2) == asn1::SRSASN_SUCCESS); + +#if JSON_OUTPUT + radio_bearer_cfg_pack.to_json(jw); + srslog::fetch_basic_logger("RRC").info("Radio bearer config Message: \n %s", jw.to_string().c_str()); +#endif + + // TODO: messages don't match yet + // TESTASSERT(bref_pack2.distance_bytes() == sizeof(rrc_msg)); + // TESTASSERT(memcmp(rrc_msg, buffer, sizeof(rrc_msg)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_cell_group_config_tdd() +{ + uint8_t cell_group_config_raw[] = "\x5c\x40\xb1\xc0\x33\xc8\x53\xe0\x12\x0f\x05\x38\x0f\x80\x41\x15" + "\x07\xad\x40\x00\xba\x14\xe6\x37\xd1\xa4\xd3\xa0\x01\x34\x9a\x5f" + "\xc0\x00\x00\x33\x63\x6c\x91\x28\x80\x7f\xc0\x00\x00\x00\x00\x46" + "\xe0\x88\x40\x00\x01\x01\x00\x34\x45\x03\x9c\x00\x00\x00\x33\x71" + "\xb6\x48\x90\x04\x00\x08\x2e\x25\x18\xf0\x02\x4a\x31\x06\xe2\x8d" + "\xb8\x44\x70\x0c\x02\x10\x38\x1d\x80\x48\xf1\x18\x5e\xea\x00\x08" + "\x0e\x01\x25\xc0\xca\x80\x01\x7f\x80\x00\x00\x00\x00\x83\x70\x88" + "\x20\x00\x08\x81\x65\x00\x20\xc8\x20\x00\x02\x06\x98\x10\x14\x50" + "\xa0\x00\xe4\x81\x80\x00\x13\x35\x56\x48\x41\xc0\x01\x04\x0c\x20" + "\x50\xc1\xc9\xc4\x09\x14\x2c\x60\xd1\xc3\xc8\xe0\x00\x03\x32\x14" + "\x03\x02\x00\x19\x94\xa0\x18\x20\x00\xcc\xc5\x00\xc1\x80\x06\x64" + "\x28\x0e\x10\x00\x33\x29\x40\x70\xa0\x01\x99\x8a\x03\x86\x00\x0c" + "\xc8\x50\x2c\x38\x00\x66\x52\x81\x62\x06\x60\x04\x16\xc4\x80\x46" + "\x48\x21\x8a\x00\x8c\x90\x4b\x16\x01\x19\x20\xa6\x30\x02\x32\x41" + "\x6c\x68\x04\x64\x83\x18\xe0\x08\xc9\x06\xb1\xe0\x11\x92\x0e\x64" + "\x00\x03\x33\x14\x0b\x22\x32\x00\xa0\x84\x09\x08\x60\x51\x04\x34" + "\x3b\x2a\x65\xcd\x01\xa4\x92\x1e\x2e\xe0\x0c\x10\xe0\x00\x00\x01" + "\x8f\xfd\x29\x49\x8c\x63\x72\x81\x60\x00\x02\x19\x70\x00\x00\x00" + "\x00\x00\x00\x62\xf0\x0f\xa0\x84\x8a\xd5\x45\x00\x47\x00\x18\x00" + "\x08\x20\x00\xe2\x10\x02\x40\x80\x70\x10\x10\x84\x00\x0e\x21\x00" + "\x1c\xb0\x0e\x04\x02\x20\x80\x01\xc4\x20\x03\x96\x01\xc0\xc0\x42" + "\x10\x00\x38\x84\x00\x73\x00\x38\x20\x08\x82\x00\x07\x10\x80\x0e" + "\x60\x00\x40\x00\x00\x04\x10\xc0\x40\x80\xc1\x00\xe0\xd0\x00\x0e" + "\x48\x10\x00\x00\x02\x00\x40\x00\x80\x60\x00\x80\x90\x02\x20\x0a" + "\x40\x00\x02\x38\x90\x11\x31\xc8"; + + asn1::SRSASN_CODE err; + + cbit_ref bref(&cell_group_config_raw[0], sizeof(cell_group_config_raw)); + cell_group_cfg_s cell_group_cfg; + + TESTASSERT(cell_group_cfg.unpack(bref) == SRSASN_SUCCESS); + + TESTASSERT(test_pack_unpack_consistency(cell_group_cfg) == SRSASN_SUCCESS); + + TESTASSERT(cell_group_cfg.sp_cell_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.serv_cell_idx_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci == 500); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp_present == true); + TESTASSERT( + cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present == + true); + + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common + .is_setup()); + + asn1::rrc_nr::rach_cfg_common_s& rach_cfg_common = + cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(); + + TESTASSERT(rach_cfg_common.rach_cfg_generic.prach_cfg_idx == 16); + TESTASSERT(rach_cfg_common.rach_cfg_generic.msg1_fdm == asn1::rrc_nr::rach_cfg_generic_s::msg1_fdm_opts::one); + TESTASSERT(rach_cfg_common.rach_cfg_generic.zero_correlation_zone_cfg == 0); + TESTASSERT(rach_cfg_common.rach_cfg_generic.preamb_rx_target_pwr == -110); + TESTASSERT(rach_cfg_common.rach_cfg_generic.preamb_trans_max == + asn1::rrc_nr::rach_cfg_generic_s::preamb_trans_max_opts::n7); + TESTASSERT(rach_cfg_common.rach_cfg_generic.pwr_ramp_step == + asn1::rrc_nr::rach_cfg_generic_s::pwr_ramp_step_opts::db4); + TESTASSERT(rach_cfg_common.rach_cfg_generic.ra_resp_win == asn1::rrc_nr::rach_cfg_generic_s::ra_resp_win_opts::sl10); + TESTASSERT(rach_cfg_common.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present == true); + +#if JSON_OUTPUT + asn1::json_writer json_writer; + cell_group_cfg.to_json(json_writer); + srslog::fetch_basic_logger("RRC").info("RRC Secondary Cell Group: Content: %s\n", json_writer.to_string().c_str()); +#endif + + // pack it again + cell_group_cfg_s cell_group_cfg_pack; + + // RLC for DRB1 + cell_group_cfg_pack.rlc_bearer_to_add_mod_list.resize(1); + auto& rlc = cell_group_cfg_pack.rlc_bearer_to_add_mod_list[0]; + rlc.lc_ch_id = 1; + rlc.served_radio_bearer_present = true; + rlc.served_radio_bearer.set_drb_id(); + rlc.served_radio_bearer.drb_id() = 1; + rlc.rlc_cfg_present = true; + rlc.rlc_cfg.set_um_bi_dir(); + rlc.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present = true; + rlc.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len = sn_field_len_um_opts::size12; + rlc.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present = true; + rlc.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len = sn_field_len_um_opts::size12; + rlc.rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_opts::ms50; + + // MAC logical channel config + rlc.mac_lc_ch_cfg_present = true; + rlc.mac_lc_ch_cfg.ul_specific_params_present = true; + rlc.mac_lc_ch_cfg.ul_specific_params.prio = 11; + rlc.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = + asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::prioritised_bit_rate_opts::kbps0; + rlc.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = + asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::bucket_size_dur_opts::ms100; + rlc.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; + rlc.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 6; + rlc.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; + rlc.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; + + // mac-CellGroup-Config + cell_group_cfg_pack.mac_cell_group_cfg_present = true; + auto& mac_cell_group = cell_group_cfg_pack.mac_cell_group_cfg; + mac_cell_group.sched_request_cfg_present = true; + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list.resize(1); + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list[0].sched_request_id = 0; + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list[0].sr_trans_max = + asn1::rrc_nr::sched_request_to_add_mod_s::sr_trans_max_opts::n64; + mac_cell_group.bsr_cfg_present = true; + mac_cell_group.bsr_cfg.periodic_bsr_timer = asn1::rrc_nr::bsr_cfg_s::periodic_bsr_timer_opts::sf20; + mac_cell_group.bsr_cfg.retx_bsr_timer = asn1::rrc_nr::bsr_cfg_s::retx_bsr_timer_opts::sf320; + // Skip TAG and PHR config + + cell_group_cfg_pack.sp_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx_present = true; + + // SP Cell Dedicated config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present = true; + + // PDCCH config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg_present = true; + auto& pdcch_cfg_dedicated = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg; + pdcch_cfg_dedicated.set_setup(); + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list.resize(1); + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].ctrl_res_set_id = 2; + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].freq_domain_res.from_number( + 0b111111110000000000000000000000000000000000000); + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].dur = 1; + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].cce_reg_map_type.set_non_interleaved(); + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].precoder_granularity = + asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; + + // search spaces + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list.resize(1); + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].search_space_id = 2; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].ctrl_res_set_id_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].ctrl_res_set_id = 2; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].monitoring_slot_periodicity_and_offset_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].monitoring_slot_periodicity_and_offset.set_sl1(); + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].monitoring_symbols_within_slot_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].monitoring_symbols_within_slot.from_number( + 0b10000000000000); + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level1 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level1_opts::n0; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level2 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level2_opts::n2; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level4 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level8 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level16 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].search_space_type_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].search_space_type.set_ue_specific(); + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].search_space_type.ue_specific().dci_formats = asn1:: + rrc_nr::search_space_s::search_space_type_c_::ue_specific_s_::dci_formats_opts::formats0_minus0_and_minus1_minus0; + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg_present = true; + auto& pdsch_cfg_dedicated = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg; + + pdsch_cfg_dedicated.set_setup(); + pdsch_cfg_dedicated.setup().dmrs_dl_for_pdsch_map_type_a_present = true; + pdsch_cfg_dedicated.setup().dmrs_dl_for_pdsch_map_type_a.set_setup(); + pdsch_cfg_dedicated.setup().dmrs_dl_for_pdsch_map_type_a.setup().dmrs_add_position_present = true; + pdsch_cfg_dedicated.setup().dmrs_dl_for_pdsch_map_type_a.setup().dmrs_add_position = + asn1::rrc_nr::dmrs_dl_cfg_s::dmrs_add_position_opts::pos1; + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list.resize(1); + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list[0].tci_state_id = 0; + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list[0].qcl_type1.ref_sig.set_ssb(); + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list[0].qcl_type1.ref_sig.ssb() = 0; + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list[0].qcl_type1.qcl_type = + asn1::rrc_nr::qcl_info_s::qcl_type_opts::type_d; + pdsch_cfg_dedicated.setup().res_alloc = pdsch_cfg_s::res_alloc_opts::res_alloc_type1; + pdsch_cfg_dedicated.setup().rbg_size = asn1::rrc_nr::pdsch_cfg_s::rbg_size_opts::cfg1; + pdsch_cfg_dedicated.setup().prb_bundling_type.set_static_bundling(); + pdsch_cfg_dedicated.setup().prb_bundling_type.static_bundling().bundle_size_present = true; + pdsch_cfg_dedicated.setup().prb_bundling_type.static_bundling().bundle_size = + asn1::rrc_nr::pdsch_cfg_s::prb_bundling_type_c_::static_bundling_s_::bundle_size_opts::wideband; + + // ZP-CSI + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list.resize(1); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].zp_csi_rs_res_id = 0; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.set_row4(); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.row4().from_number(0b100); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.nrof_ports = + asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p4; + + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.first_ofdm_symbol_in_time_domain = 8; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.cdm_type = + asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::fd_cdm2; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.density.set_one(); + + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_band.start_rb = 0; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_band.nrof_rbs = 52; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset_present = true; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset.set_slots80(); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset.slots80() = 1; + pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set_present = true; + pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.set_setup(); + pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.setup().zp_csi_rs_res_set_id = 0; + pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list.resize(1); + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id = 1; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present = true; + + // UL config dedicated + // PUCCH + auto& ul_config = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg; + ul_config.init_ul_bwp_present = true; + ul_config.init_ul_bwp.pucch_cfg_present = true; + ul_config.init_ul_bwp.pucch_cfg.set_setup(); + ul_config.init_ul_bwp.pucch_cfg.setup().format2_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().format2.set_setup(); + ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25; + + // SR resources + ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list.resize(1); + auto& sr_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list[0]; + sr_res1.sched_request_res_id = 1; + sr_res1.sched_request_id = 0; + sr_res1.periodicity_and_offset_present = true; + sr_res1.periodicity_and_offset.set_sl40(); + sr_res1.periodicity_and_offset.sl40() = 7; + sr_res1.res_present = true; + sr_res1.res = 0; // only PUCCH resource we have defined so far + + // DL data + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack.resize(5); + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[0] = 8; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[1] = 7; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[2] = 6; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[3] = 5; + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[4] = 4; + + // PUCCH resources (only one format1 for the moment) + ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list.resize(1); + auto& pucch_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list[0]; + pucch_res1.pucch_res_id = 0; + pucch_res1.start_prb = 0; + pucch_res1.format.set_format1(); + pucch_res1.format.format1().init_cyclic_shift = 0; + pucch_res1.format.format1().nrof_symbols = 14; + pucch_res1.format.format1().start_symbol_idx = 0; + pucch_res1.format.format1().time_domain_occ = 0; + + // PUSCH config + ul_config.init_ul_bwp.pusch_cfg_present = true; + ul_config.init_ul_bwp.pusch_cfg.set_setup(); + auto& pusch_cfg_ded = ul_config.init_ul_bwp.pusch_cfg.setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.set_setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position = dmrs_ul_cfg_s::dmrs_add_position_opts::pos1; + // PUSH power control skipped + pusch_cfg_ded.res_alloc = pusch_cfg_s::res_alloc_opts::res_alloc_type1; + + // UCI + pusch_cfg_ded.uci_on_pusch_present = true; + pusch_cfg_ded.uci_on_pusch.set_setup(); + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets_present = true; + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.set_semi_static(); + auto& beta_offset_semi_static = pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.semi_static(); + beta_offset_semi_static.beta_offset_ack_idx1_present = true; + beta_offset_semi_static.beta_offset_ack_idx1 = 9; + beta_offset_semi_static.beta_offset_ack_idx2_present = true; + beta_offset_semi_static.beta_offset_ack_idx2 = 9; + beta_offset_semi_static.beta_offset_ack_idx3_present = true; + beta_offset_semi_static.beta_offset_ack_idx3 = 9; + beta_offset_semi_static.beta_offset_csi_part1_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part1_idx2 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx2 = 6; + pusch_cfg_ded.uci_on_pusch.setup().scaling = uci_on_pusch_s::scaling_opts::f1; + + ul_config.first_active_ul_bwp_id_present = true; + ul_config.first_active_ul_bwp_id = 0; + + // Serving cell config (only to setup) + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg.set_setup(); + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.set_setup(); + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch_present = + true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch = + pdsch_serving_cell_cfg_s::nrof_harq_processes_for_pdsch_opts::n16; + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.set_setup(); + + // nzp-CSI-RS Resource + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list.resize(1); + auto& nzp_csi_res = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list[0]; + nzp_csi_res.nzp_csi_rs_res_id = 0; + nzp_csi_res.res_map.freq_domain_alloc.set_row2(); + nzp_csi_res.res_map.freq_domain_alloc.row2().from_number(0b100000000000); + nzp_csi_res.res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res.res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res.res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res.res_map.density.set_one(); + nzp_csi_res.res_map.freq_band.start_rb = 0; + nzp_csi_res.res_map.freq_band.nrof_rbs = 52; + nzp_csi_res.pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res.scrambling_id = 0; + nzp_csi_res.periodicity_and_offset_present = true; + nzp_csi_res.periodicity_and_offset.set_slots80(); + nzp_csi_res.periodicity_and_offset.slots80() = 0; + // optional + nzp_csi_res.qcl_info_periodic_csi_rs_present = true; + nzp_csi_res.qcl_info_periodic_csi_rs = 0; + + // nzp-CSI-RS ResourceSet + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list.resize(1); + auto& nzp_csi_res_set = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list[0]; + nzp_csi_res_set.nzp_csi_res_set_id = 0; + nzp_csi_res_set.nzp_csi_rs_res.resize(1); + nzp_csi_res_set.nzp_csi_rs_res[0] = 0; + // Skip TRS info + + // CSI report config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list.resize(1); + auto& csi_report = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list[0]; + csi_report.report_cfg_id = 0; + csi_report.res_for_ch_meas = 0; + csi_report.csi_im_res_for_interference_present = true; + csi_report.csi_im_res_for_interference = 1; + csi_report.report_cfg_type.set_periodic(); + csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80(); + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 8; + csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1); + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0; + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = 17; + csi_report.report_quant.set_cri_ri_pmi_cqi(); + csi_report.report_freq_cfg_present = true; + csi_report.report_freq_cfg.cqi_format_ind_present = true; + csi_report.report_freq_cfg.cqi_format_ind = + asn1::rrc_nr::csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi; + csi_report.time_restrict_for_ch_meass = asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured; + csi_report.time_restrict_for_interference_meass = + asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured; + csi_report.group_based_beam_report.set_disabled(); + csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2; + csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; + + // Reconfig with Sync + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = 17943; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.smtc.release(); + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000; + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ss_pbch_block_pwr = 0; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dmrs_type_a_position = + asn1::rrc_nr::serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci = 500; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing = + subcarrier_spacing_opts::khz30; + + // DL config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl + .absolute_freq_ssb_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.absolute_freq_ssb = + 632640; + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.freq_band_list + .push_back(78); + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.absolute_freq_point_a = + 632316; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl + .scs_specific_carrier_list.resize(1); + auto& dl_carrier = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl + .scs_specific_carrier_list[0]; + dl_carrier.offset_to_carrier = 0; + dl_carrier.subcarrier_spacing = subcarrier_spacing_opts::khz15; + dl_carrier.carrier_bw = 52; + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.generic_params + .location_and_bw = 14025; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.generic_params + .subcarrier_spacing = subcarrier_spacing_opts::khz15; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp + .pdcch_cfg_common_present = true; + auto& pdcch_cfg_common = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common; + pdcch_cfg_common.set_setup(); + pdcch_cfg_common.setup().ext = false; + pdcch_cfg_common.setup().common_ctrl_res_set_present = true; + pdcch_cfg_common.setup().common_ctrl_res_set.ctrl_res_set_id = 1; + pdcch_cfg_common.setup().common_ctrl_res_set.freq_domain_res.from_number( + 0b111111110000000000000000000000000000000000000); + pdcch_cfg_common.setup().common_ctrl_res_set.dur = 1; + pdcch_cfg_common.setup().common_ctrl_res_set.cce_reg_map_type.set_non_interleaved(); + pdcch_cfg_common.setup().common_ctrl_res_set.precoder_granularity = + asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; + + // common search space list + pdcch_cfg_common.setup().common_search_space_list.resize(1); + pdcch_cfg_common.setup().common_search_space_list[0].search_space_id = 1; + pdcch_cfg_common.setup().common_search_space_list[0].ctrl_res_set_id_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].ctrl_res_set_id = 1; + pdcch_cfg_common.setup().common_search_space_list[0].search_space_type_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].search_space_type.set_common(); + pdcch_cfg_common.setup() + .common_search_space_list[0] + .search_space_type.common() + .dci_format0_minus0_and_format1_minus0_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level1 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level1_opts::n1; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level2 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level2_opts::n1; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level4 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level8 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level16 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; + pdcch_cfg_common.setup().common_search_space_list[0].monitoring_slot_periodicity_and_offset_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].monitoring_slot_periodicity_and_offset.set_sl1(); + pdcch_cfg_common.setup().common_search_space_list[0].monitoring_symbols_within_slot_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].monitoring_symbols_within_slot.from_number(0b10000000000000); + pdcch_cfg_common.setup().ra_search_space_present = true; + pdcch_cfg_common.setup().ra_search_space = 1; + + // PDSCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp + .pdsch_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_cfg_common + .set_setup(); + auto& pdsch_cfg_common = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp + .pdsch_cfg_common.setup(); + pdsch_cfg_common.pdsch_time_domain_alloc_list.resize(1); + pdsch_cfg_common.pdsch_time_domain_alloc_list[0].map_type = pdsch_time_domain_res_alloc_s::map_type_opts::type_a; + pdsch_cfg_common.pdsch_time_domain_alloc_list[0].start_symbol_and_len = 40; + + // UL config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.dummy = time_align_timer_opts::ms500; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul + .scs_specific_carrier_list.resize(1); + auto& ul_carrier = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul + .scs_specific_carrier_list[0]; + ul_carrier.offset_to_carrier = 0; + ul_carrier.subcarrier_spacing = subcarrier_spacing_opts::khz15; + ul_carrier.carrier_bw = 52; + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params + .location_and_bw = 14025; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params + .subcarrier_spacing = subcarrier_spacing_opts::khz15; + + // RACH config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present = + true; + auto& rach_cfg_common_pack = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common; + + rach_cfg_common_pack.set_setup(); + rach_cfg_common_pack.setup().rach_cfg_generic.prach_cfg_idx = 0; + rach_cfg_common_pack.setup().rach_cfg_generic.msg1_fdm = rach_cfg_generic_s::msg1_fdm_opts::one; + rach_cfg_common_pack.setup().rach_cfg_generic.msg1_freq_start = 1; + rach_cfg_common_pack.setup().rach_cfg_generic.zero_correlation_zone_cfg = 0; + rach_cfg_common_pack.setup().rach_cfg_generic.preamb_rx_target_pwr = -110; + rach_cfg_common_pack.setup().rach_cfg_generic.preamb_trans_max = + asn1::rrc_nr::rach_cfg_generic_s::preamb_trans_max_opts::n7; + rach_cfg_common_pack.setup().rach_cfg_generic.pwr_ramp_step = + asn1::rrc_nr::rach_cfg_generic_s::pwr_ramp_step_opts::db4; + rach_cfg_common_pack.setup().rach_cfg_generic.ra_resp_win = asn1::rrc_nr::rach_cfg_generic_s::ra_resp_win_opts::sl10; + rach_cfg_common_pack.setup().ra_contention_resolution_timer = + asn1::rrc_nr::rach_cfg_common_s::ra_contention_resolution_timer_opts::sf64; + rach_cfg_common_pack.setup().prach_root_seq_idx.set( + asn1::rrc_nr::rach_cfg_common_s::prach_root_seq_idx_c_::types_opts::l839); + rach_cfg_common_pack.setup().prach_root_seq_idx.set_l839() = 1; + rach_cfg_common_pack.setup().restricted_set_cfg = + asn1::rrc_nr::rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set; + + // PUSCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp + .pusch_cfg_common_present = true; + auto& pusch_cfg_common_pack = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.pusch_cfg_common; + pusch_cfg_common_pack.set_setup(); + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list.resize(2); + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].k2_present = true; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].k2 = 4; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].map_type = + asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::type_a; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].start_symbol_and_len = 27; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].k2_present = true; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].k2 = 3; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].map_type = + asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::type_a; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].start_symbol_and_len = 27; + pusch_cfg_common_pack.setup().p0_nominal_with_grant = -90; + + // PUCCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp + .pucch_cfg_common_present = true; + auto& pucch_cfg_common_pack = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.pucch_cfg_common; + pucch_cfg_common_pack.set_setup(); + pucch_cfg_common_pack.setup().pucch_group_hop = asn1::rrc_nr::pucch_cfg_common_s::pucch_group_hop_opts::neither; + pucch_cfg_common_pack.setup().p0_nominal_present = true; + pucch_cfg_common_pack.setup().p0_nominal = -90; + + // SSB config (optional) + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_positions_in_burst_present = true; + auto& ssb_pos_in_burst = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_positions_in_burst; + ssb_pos_in_burst.set_medium_bitmap().from_number(0b10000000); + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_periodicity_serving_cell_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_periodicity_serving_cell = + serving_cell_cfg_common_s::ssb_periodicity_serving_cell_opts::ms20; + + // TDD UL-DL config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.tdd_ul_dl_cfg_common_present = true; + auto& tdd_config = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.tdd_ul_dl_cfg_common; + tdd_config.ref_subcarrier_spacing = subcarrier_spacing_e::khz15; + tdd_config.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10; + tdd_config.pattern1.nrof_dl_slots = 6; + tdd_config.pattern1.nrof_dl_symbols = 0; + tdd_config.pattern1.nrof_ul_slots = 3; + tdd_config.pattern1.nrof_ul_symbols = 0; + + // pack only cell group info + asn1::dyn_octstring packed_cell_group; + packed_cell_group.resize(256); + asn1::bit_ref bref_pack(packed_cell_group.data(), packed_cell_group.size()); + TESTASSERT(cell_group_cfg_pack.pack(bref_pack) == asn1::SRSASN_SUCCESS); + TESTASSERT(test_pack_unpack_consistency(cell_group_cfg_pack) == SRSASN_SUCCESS); + packed_cell_group.resize(bref_pack.distance_bytes()); + +#if JSON_OUTPUT + asn1::json_writer json_writer2; + cell_group_cfg_pack.to_json(json_writer2); + srslog::fetch_basic_logger("RRC").info(packed_cell_group.data(), + packed_cell_group.size(), + "Cell group config repacked (%d B): \n %s", + packed_cell_group.size(), + json_writer2.to_string().c_str()); +#endif + +#if HAVE_PCAP + // pack full DL-DCCH with RRC reconfig for PCAP output + dl_dcch_msg_s dcch; + dcch.msg.set_c1().set_rrc_recfg(); + rrc_recfg_s& reconfig = dcch.msg.c1().rrc_recfg(); + reconfig.rrc_transaction_id = 0; + reconfig.crit_exts.set_rrc_recfg(); + rrc_recfg_ies_s& recfg_ies = reconfig.crit_exts.rrc_recfg(); + recfg_ies.secondary_cell_group_present = true; + recfg_ies.secondary_cell_group = packed_cell_group; + + asn1::dyn_octstring packed_dcch; + packed_dcch.resize(1024); + asn1::bit_ref bref_dcch_pack(packed_dcch.data(), packed_dcch.size()); + TESTASSERT(dcch.pack(bref_dcch_pack) == asn1::SRSASN_SUCCESS); + packed_dcch.resize(bref_dcch_pack.distance_bytes() + 10); + + asn1::json_writer json_writer3; + dcch.to_json(json_writer3); + srslog::fetch_basic_logger("RRC").info(packed_dcch.data(), + packed_dcch.size(), + "Full DCCH repacked (%d B): \n %s", + packed_dcch.size(), + json_writer3.to_string().c_str()); + + srsran::write_pdcp_sdu_nr(1, packed_dcch.data(), packed_dcch.size()); +#endif + + return SRSRAN_SUCCESS; +} + +int test_cell_group_config_fdd() +{ + uint8_t cell_group_config_raw[] = "\x5c\x40\xb1\xc0\x7d\x48\x3a\x04\xc0\x3e\x01\x04\x54\x1e\xb5\x80" + "\x02\xe8\x53\xb8\x9f\x46\x85\x60\xa4\x00\x40\xab\x41\x00\x00\x00" + "\xcd\x8d\xb2\x44\xa2\x01\xff\x00\x00\x00\x00\x01\x1b\x82\x21\x00" + "\x01\x24\x04\x00\xd0\x14\x6c\x00\x10\x28\x9d\xc0\x00\x00\x33\x71" + "\xb6\x48\x90\x04\x00\x08\x2e\x25\x18\xf0\x02\x4a\x31\x06\xe1\x8d" + "\xb8\x44\x70\x01\x08\x4c\x23\x06\xdd\x40\x01\x01\xc0\x24\xb8\x19" + "\x50\x00\x2f\xf0\x00\x00\x00\x00\x10\x6e\x11\x04\x00\x01\x10\x24" + "\xa0\x04\x19\x04\x00\x00\x40\xd3\x02\x02\x8a\x14\x00\x1c\x90\x30" + "\x00\x02\x66\xaa\xc9\x08\x38\x00\x20\x81\x84\x0a\x18\x39\x38\x81" + "\x22\x85\x8c\x1a\x38\x79\x10\x00\x00\x85\x00\x00\x80\x0a\x50\x00" + "\x10\x00\xc5\x00\x01\x80\x08\x50\x10\x20\x00\xa5\x01\x02\x80\x0c" + "\x50\x10\x30\x00\x85\x02\x03\x80\x0a\x50\x20\x40\xcd\x04\x01\x23" + "\x34\x12\x05\x0c\xd0\x50\x16\x33\x41\x60\x60\xcd\x06\x01\xa3\x34" + "\x1a\x07\x0c\xd0\x70\x1e\x01\x41\x00\x80\x00\xc5\x02\x08\x80\x50" + "\x4a\x04\x84\x30\x28\x42\x01\x22\x80\x14\x92\x1e\x2e\xe0\x0c\x10" + "\xe0\x00\x00\x01\xff\xd2\x94\x98\xc6\x37\x28\x16\x00\x00\x21\x97" + "\x00\x00\x00\x00\x00\x00\x06\x2f\x00\xfa\x08\x48\xad\x54\x50\x04" + "\x70\x01\x80\x00\x82\x00\x0e\x21\x7d\x24\x08\x07\x01\x01\x08\x40" + "\x00\xe2\x17\xd1\xcb\x00\xe0\x40\x22\x08\x00\x1c\x42\xfa\x39\x60" + "\x1c\x0c\x04\x21\x00\x03\x88\x5f\x47\x30\x03\x82\x00\x88\x20\x00" + "\x71\x0b\xe8\xe6\x00\x04\x00\x00\x00\x41\x0c\x04\x08\x0c\x10\x0e" + "\x0d\x00\x00\xe4\x81\x00\x00\x00\x20\x04\x00\x08\x06\x00\x08\x09" + "\x00\x22\x00\xa4\x00\x00\x23\x85\x01\x13\x1c"; + + asn1::SRSASN_CODE err; + + cbit_ref bref(&cell_group_config_raw[0], sizeof(cell_group_config_raw)); + cell_group_cfg_s cell_group_cfg; + + TESTASSERT(cell_group_cfg.unpack(bref) == SRSASN_SUCCESS); + + TESTASSERT(test_pack_unpack_consistency(cell_group_cfg) == SRSASN_SUCCESS); + + TESTASSERT(cell_group_cfg.sp_cell_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.serv_cell_idx_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci == 500); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present == true); + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp_present == true); + TESTASSERT( + cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present == + true); + + TESTASSERT(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common + .is_setup()); + + asn1::rrc_nr::rach_cfg_common_s& rach_cfg_common = + cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(); + + TESTASSERT(rach_cfg_common.rach_cfg_generic.prach_cfg_idx == 16); + TESTASSERT(rach_cfg_common.rach_cfg_generic.msg1_fdm == asn1::rrc_nr::rach_cfg_generic_s::msg1_fdm_opts::one); + TESTASSERT(rach_cfg_common.rach_cfg_generic.zero_correlation_zone_cfg == 0); + TESTASSERT(rach_cfg_common.rach_cfg_generic.preamb_rx_target_pwr == -110); + TESTASSERT(rach_cfg_common.rach_cfg_generic.preamb_trans_max == + asn1::rrc_nr::rach_cfg_generic_s::preamb_trans_max_opts::n7); + TESTASSERT(rach_cfg_common.rach_cfg_generic.pwr_ramp_step == + asn1::rrc_nr::rach_cfg_generic_s::pwr_ramp_step_opts::db4); + TESTASSERT(rach_cfg_common.rach_cfg_generic.ra_resp_win == asn1::rrc_nr::rach_cfg_generic_s::ra_resp_win_opts::sl10); + TESTASSERT(rach_cfg_common.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present == true); + +#if JSON_OUTPUT + asn1::json_writer json_writer; + cell_group_cfg.to_json(json_writer); + srslog::fetch_basic_logger("RRC").info("RRC Secondary Cell Group: Content: %s\n", json_writer.to_string().c_str()); +#endif + + // pack it again + cell_group_cfg_s cell_group_cfg_pack; + + // RLC for DRB1 + cell_group_cfg_pack.rlc_bearer_to_add_mod_list.resize(1); + auto& rlc = cell_group_cfg_pack.rlc_bearer_to_add_mod_list[0]; + rlc.lc_ch_id = 4; + rlc.served_radio_bearer_present = true; + rlc.served_radio_bearer.set_drb_id(); + rlc.served_radio_bearer.drb_id() = 1; + rlc.rlc_cfg_present = true; + rlc.rlc_cfg.set_um_bi_dir(); + rlc.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present = true; + rlc.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len = sn_field_len_um_opts::size12; + rlc.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present = true; + rlc.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len = sn_field_len_um_opts::size12; + rlc.rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_opts::ms50; + + // MAC logical channel config + rlc.mac_lc_ch_cfg_present = true; + rlc.mac_lc_ch_cfg.ul_specific_params_present = true; + rlc.mac_lc_ch_cfg.ul_specific_params.prio = 11; + rlc.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = + asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::prioritised_bit_rate_opts::kbps0; + rlc.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = + asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::bucket_size_dur_opts::ms100; + rlc.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; + rlc.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 6; + rlc.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; + rlc.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; + + // mac-CellGroup-Config + cell_group_cfg_pack.mac_cell_group_cfg_present = true; + auto& mac_cell_group = cell_group_cfg_pack.mac_cell_group_cfg; + mac_cell_group.sched_request_cfg_present = true; + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list.resize(1); + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list[0].sched_request_id = 0; + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list[0].sr_trans_max = + asn1::rrc_nr::sched_request_to_add_mod_s::sr_trans_max_opts::n64; + mac_cell_group.bsr_cfg_present = true; + mac_cell_group.bsr_cfg.periodic_bsr_timer = asn1::rrc_nr::bsr_cfg_s::periodic_bsr_timer_opts::sf20; + mac_cell_group.bsr_cfg.retx_bsr_timer = asn1::rrc_nr::bsr_cfg_s::retx_bsr_timer_opts::sf320; + // Skip TAG and PHR config + + cell_group_cfg_pack.sp_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx_present = true; + + // SP Cell Dedicated config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present = true; + + // PDCCH config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg_present = true; + auto& pdcch_cfg_dedicated = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg; + pdcch_cfg_dedicated.set_setup(); + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list.resize(1); + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].ctrl_res_set_id = 2; + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].freq_domain_res.from_number( + 0b111111110000000000000000000000000000000000000); + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].dur = 1; + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].cce_reg_map_type.set_non_interleaved(); + pdcch_cfg_dedicated.setup().ctrl_res_set_to_add_mod_list[0].precoder_granularity = + asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; + + // search spaces + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list.resize(1); + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].search_space_id = 2; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].ctrl_res_set_id_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].ctrl_res_set_id = 2; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].monitoring_slot_periodicity_and_offset_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].monitoring_slot_periodicity_and_offset.set_sl1(); + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].monitoring_symbols_within_slot_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].monitoring_symbols_within_slot.from_number( + 0b10000000000000); + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level1 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level1_opts::n0; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level2 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level2_opts::n2; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level4 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level8 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level16 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].search_space_type_present = true; + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].search_space_type.set_ue_specific(); + pdcch_cfg_dedicated.setup().search_spaces_to_add_mod_list[0].search_space_type.ue_specific().dci_formats = asn1:: + rrc_nr::search_space_s::search_space_type_c_::ue_specific_s_::dci_formats_opts::formats0_minus0_and_minus1_minus0; + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg_present = true; + auto& pdsch_cfg_dedicated = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg; + + pdsch_cfg_dedicated.set_setup(); + pdsch_cfg_dedicated.setup().dmrs_dl_for_pdsch_map_type_a_present = true; + pdsch_cfg_dedicated.setup().dmrs_dl_for_pdsch_map_type_a.set_setup(); + pdsch_cfg_dedicated.setup().dmrs_dl_for_pdsch_map_type_a.setup().dmrs_add_position_present = true; + pdsch_cfg_dedicated.setup().dmrs_dl_for_pdsch_map_type_a.setup().dmrs_add_position = + asn1::rrc_nr::dmrs_dl_cfg_s::dmrs_add_position_opts::pos1; + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list.resize(1); + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list[0].tci_state_id = 0; + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list[0].qcl_type1.ref_sig.set_ssb(); + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list[0].qcl_type1.ref_sig.ssb() = 0; + pdsch_cfg_dedicated.setup().tci_states_to_add_mod_list[0].qcl_type1.qcl_type = + asn1::rrc_nr::qcl_info_s::qcl_type_opts::type_d; + pdsch_cfg_dedicated.setup().res_alloc = pdsch_cfg_s::res_alloc_opts::res_alloc_type1; + pdsch_cfg_dedicated.setup().rbg_size = asn1::rrc_nr::pdsch_cfg_s::rbg_size_opts::cfg1; + pdsch_cfg_dedicated.setup().prb_bundling_type.set_static_bundling(); + pdsch_cfg_dedicated.setup().prb_bundling_type.static_bundling().bundle_size_present = true; + pdsch_cfg_dedicated.setup().prb_bundling_type.static_bundling().bundle_size = + asn1::rrc_nr::pdsch_cfg_s::prb_bundling_type_c_::static_bundling_s_::bundle_size_opts::wideband; + + // ZP-CSI + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list.resize(1); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].zp_csi_rs_res_id = 0; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.set_row4(); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.row4().from_number(0b100); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.nrof_ports = + asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p4; + + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.first_ofdm_symbol_in_time_domain = 8; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.cdm_type = + asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::fd_cdm2; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.density.set_one(); + + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_band.start_rb = 0; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].res_map.freq_band.nrof_rbs = 52; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset_present = true; + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset.set_slots80(); + pdsch_cfg_dedicated.setup().zp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset.slots80() = 1; + pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set_present = true; + pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.set_setup(); + pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.setup().zp_csi_rs_res_set_id = 0; + pdsch_cfg_dedicated.setup().p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list.resize(1); + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.first_active_dl_bwp_id = 0; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present = true; + + // UL config dedicated + // PUCCH + auto& ul_config = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg; + ul_config.init_ul_bwp_present = true; + ul_config.init_ul_bwp.pucch_cfg_present = true; + ul_config.init_ul_bwp.pucch_cfg.set_setup(); + ul_config.init_ul_bwp.pucch_cfg.setup().format2_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().format2.set_setup(); + ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate_present = true; + ul_config.init_ul_bwp.pucch_cfg.setup().format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25; + + // SR resources + ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list.resize(1); + auto& sr_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().sched_request_res_to_add_mod_list[0]; + sr_res1.sched_request_res_id = 1; + sr_res1.sched_request_id = 0; + sr_res1.periodicity_and_offset_present = true; + sr_res1.periodicity_and_offset.set_sl40(); + sr_res1.periodicity_and_offset.sl40() = 4; + sr_res1.res_present = true; + sr_res1.res = 16; + + // DL data + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack.resize(1); + ul_config.init_ul_bwp.pucch_cfg.setup().dl_data_to_ul_ack[0] = 4; + + // TODO? + // PUCCH resources (only one format1 for the moment) + ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list.resize(1); + auto& pucch_res1 = ul_config.init_ul_bwp.pucch_cfg.setup().res_to_add_mod_list[0]; + pucch_res1.pucch_res_id = 0; + pucch_res1.start_prb = 0; + pucch_res1.format.set_format1(); + pucch_res1.format.format1().init_cyclic_shift = 0; + pucch_res1.format.format1().nrof_symbols = 14; + pucch_res1.format.format1().start_symbol_idx = 0; + pucch_res1.format.format1().time_domain_occ = 0; + + // PUSCH config + ul_config.init_ul_bwp.pusch_cfg_present = true; + ul_config.init_ul_bwp.pusch_cfg.set_setup(); + auto& pusch_cfg_ded = ul_config.init_ul_bwp.pusch_cfg.setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.set_setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position = dmrs_ul_cfg_s::dmrs_add_position_opts::pos1; + // PUSH power control skipped + pusch_cfg_ded.res_alloc = pusch_cfg_s::res_alloc_opts::res_alloc_type1; + + // UCI + pusch_cfg_ded.uci_on_pusch_present = true; + pusch_cfg_ded.uci_on_pusch.set_setup(); + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets_present = true; + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.set_semi_static(); + auto& beta_offset_semi_static = pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.semi_static(); + beta_offset_semi_static.beta_offset_ack_idx1_present = true; + beta_offset_semi_static.beta_offset_ack_idx1 = 9; + beta_offset_semi_static.beta_offset_ack_idx2_present = true; + beta_offset_semi_static.beta_offset_ack_idx2 = 9; + beta_offset_semi_static.beta_offset_ack_idx3_present = true; + beta_offset_semi_static.beta_offset_ack_idx3 = 9; + beta_offset_semi_static.beta_offset_csi_part1_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part1_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx2 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx2 = 6; + pusch_cfg_ded.uci_on_pusch.setup().scaling = uci_on_pusch_s::scaling_opts::f1; + + ul_config.first_active_ul_bwp_id_present = true; + ul_config.first_active_ul_bwp_id = 0; + + // Serving cell config (only to setup) + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg.set_setup(); + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.set_setup(); + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch_present = + true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch = + pdsch_serving_cell_cfg_s::nrof_harq_processes_for_pdsch_opts::n16; + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.set_setup(); + + // nzp-CSI-RS Resource + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_to_add_mod_list.resize(5); + auto& nzp_csi_res = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup(); + // item 0 + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].nzp_csi_rs_res_id = 0; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.set_row2(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.row2().from_number(0b100000000000); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.nrof_ports = + asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.cdm_type = + asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.density.set_one(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.freq_band.start_rb = 0; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].scrambling_id = 500; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset.set_slots80(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset.slots80() = 1; + // optional + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[0].qcl_info_periodic_csi_rs = 0; + // item 1 + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].nzp_csi_rs_res_id = 1; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].res_map.freq_domain_alloc.set_row1(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].res_map.freq_domain_alloc.row1().from_number(0b0001); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].res_map.nrof_ports = + asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].res_map.cdm_type = + asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].res_map.density.set_three(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].res_map.freq_band.start_rb = 0; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].scrambling_id = 500; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].periodicity_and_offset_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].periodicity_and_offset.set_slots40(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].periodicity_and_offset.slots40() = 11; + // optional + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[1].qcl_info_periodic_csi_rs = 0; + // item 2 + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].nzp_csi_rs_res_id = 2; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].res_map.freq_domain_alloc.set_row1(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].res_map.freq_domain_alloc.row1().from_number(0b0001); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].res_map.nrof_ports = + asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].res_map.first_ofdm_symbol_in_time_domain = 8; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].res_map.cdm_type = + asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].res_map.density.set_three(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].res_map.freq_band.start_rb = 0; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].scrambling_id = 500; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].periodicity_and_offset_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].periodicity_and_offset.set_slots40(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].periodicity_and_offset.slots40() = 11; + // optional + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[2].qcl_info_periodic_csi_rs = 0; + // item 3 + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].nzp_csi_rs_res_id = 3; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].res_map.freq_domain_alloc.set_row1(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].res_map.freq_domain_alloc.row1().from_number(0b0001); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].res_map.nrof_ports = + asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].res_map.cdm_type = + asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].res_map.density.set_three(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].res_map.freq_band.start_rb = 0; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].scrambling_id = 500; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].periodicity_and_offset_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].periodicity_and_offset.set_slots40(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].periodicity_and_offset.slots40() = 12; + // optional + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[3].qcl_info_periodic_csi_rs = 0; + // item 4 + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].nzp_csi_rs_res_id = 4; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].res_map.freq_domain_alloc.set_row1(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].res_map.freq_domain_alloc.row1().from_number(0b0001); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].res_map.nrof_ports = + asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].res_map.first_ofdm_symbol_in_time_domain = 8; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].res_map.cdm_type = + asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].res_map.density.set_three(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].res_map.freq_band.start_rb = 0; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].scrambling_id = 500; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].periodicity_and_offset_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].periodicity_and_offset.set_slots40(); + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].periodicity_and_offset.slots40() = 12; + // optional + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res.nzp_csi_rs_res_to_add_mod_list[4].qcl_info_periodic_csi_rs = 0; + + // nzp-CSI-RS ResourceSet + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().nzp_csi_rs_res_set_to_add_mod_list.resize(2); + auto& nzp_csi_res_set = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup(); + // item 0 + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[0].nzp_csi_res_set_id = 0; + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[0].nzp_csi_rs_res.resize(1); + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[0].nzp_csi_rs_res[0] = 0; + // item 1 + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[1].nzp_csi_res_set_id = 1; + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[1].nzp_csi_rs_res.resize(4); + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[1].nzp_csi_rs_res[0] = 1; + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[1].nzp_csi_rs_res[1] = 2; + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[1].nzp_csi_rs_res[2] = 3; + nzp_csi_res_set.nzp_csi_rs_res_set_to_add_mod_list[1].nzp_csi_rs_res[3] = 4; + // Skip TRS info + + // CSI IM config + // TODO: add csi im config + + // CSI resource config + // TODO: add csi resource config + + // CSI report config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list.resize(1); + auto& csi_report = + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list[0]; + csi_report.report_cfg_id = 0; + csi_report.res_for_ch_meas = 0; + csi_report.csi_im_res_for_interference_present = true; + csi_report.csi_im_res_for_interference = 1; + csi_report.report_cfg_type.set_periodic(); + csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80(); + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 5; + csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1); + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0; + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = 17; + csi_report.report_quant.set_cri_ri_pmi_cqi(); + csi_report.report_freq_cfg_present = true; + csi_report.report_freq_cfg.cqi_format_ind_present = true; + csi_report.report_freq_cfg.cqi_format_ind = + asn1::rrc_nr::csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi; + csi_report.time_restrict_for_ch_meass = asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured; + csi_report.time_restrict_for_interference_meass = + asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured; + csi_report.group_based_beam_report.set_disabled(); + csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2; + csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; + + // Reconfig with Sync + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = 17933; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000; + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ss_pbch_block_pwr = -36; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dmrs_type_a_position = + asn1::rrc_nr::serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci = 500; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing = + subcarrier_spacing_opts::khz30; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.n_timing_advance_offset = + asn1::rrc_nr::serving_cell_cfg_common_s::n_timing_advance_offset_opts::n0; + + // DL config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl + .absolute_freq_ssb_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.absolute_freq_ssb = + 176210; + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.freq_band_list + .push_back(5); + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl.absolute_freq_point_a = + 175364; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl + .scs_specific_carrier_list.resize(1); + auto& dl_carrier = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.freq_info_dl + .scs_specific_carrier_list[0]; + dl_carrier.offset_to_carrier = 0; + dl_carrier.subcarrier_spacing = subcarrier_spacing_opts::khz15; + dl_carrier.carrier_bw = 52; + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.generic_params + .location_and_bw = 14025; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.generic_params + .subcarrier_spacing = subcarrier_spacing_opts::khz15; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp + .pdcch_cfg_common_present = true; + auto& pdcch_cfg_common = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common; + pdcch_cfg_common.set_setup(); + pdcch_cfg_common.setup().common_ctrl_res_set_present = true; + pdcch_cfg_common.setup().common_ctrl_res_set.ctrl_res_set_id = 1; + pdcch_cfg_common.setup().common_ctrl_res_set.freq_domain_res.from_number( + 0b111111110000000000000000000000000000000000000); + pdcch_cfg_common.setup().common_ctrl_res_set.dur = 1; + pdcch_cfg_common.setup().common_ctrl_res_set.cce_reg_map_type.set_non_interleaved(); + pdcch_cfg_common.setup().common_ctrl_res_set.precoder_granularity = + asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; + + // common search space list + pdcch_cfg_common.setup().common_search_space_list.resize(1); + pdcch_cfg_common.setup().common_search_space_list[0].search_space_id = 1; + pdcch_cfg_common.setup().common_search_space_list[0].ctrl_res_set_id_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].ctrl_res_set_id = 1; + pdcch_cfg_common.setup().common_search_space_list[0].search_space_type_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].search_space_type.set_common(); + pdcch_cfg_common.setup() + .common_search_space_list[0] + .search_space_type.common() + .dci_format0_minus0_and_format1_minus0_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level1 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level1_opts::n1; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level2 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level2_opts::n1; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level4 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level8 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; + pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level16 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; + pdcch_cfg_common.setup().common_search_space_list[0].monitoring_slot_periodicity_and_offset_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].monitoring_slot_periodicity_and_offset.set_sl1(); + pdcch_cfg_common.setup().common_search_space_list[0].monitoring_symbols_within_slot_present = true; + pdcch_cfg_common.setup().common_search_space_list[0].monitoring_symbols_within_slot.from_number(0b10000000000000); + pdcch_cfg_common.setup().ra_search_space_present = true; + pdcch_cfg_common.setup().ra_search_space = 1; + + // PDSCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp + .pdsch_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_cfg_common + .set_setup(); + auto& pdsch_cfg_common = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp + .pdsch_cfg_common.setup(); + pdsch_cfg_common.pdsch_time_domain_alloc_list.resize(1); + pdsch_cfg_common.pdsch_time_domain_alloc_list[0].map_type = pdsch_time_domain_res_alloc_s::map_type_opts::type_a; + pdsch_cfg_common.pdsch_time_domain_alloc_list[0].start_symbol_and_len = 40; + + // UL config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.dummy = time_align_timer_opts::ms500; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul.freq_band_list + .push_back(5); + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul.absolute_freq_point_a = + 166364; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul + .scs_specific_carrier_list.resize(1); + auto& ul_carrier = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul + .scs_specific_carrier_list[0]; + ul_carrier.offset_to_carrier = 0; + ul_carrier.subcarrier_spacing = subcarrier_spacing_opts::khz15; + ul_carrier.carrier_bw = 52; + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params + .location_and_bw = 14025; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params + .subcarrier_spacing = subcarrier_spacing_opts::khz15; + + // RACH config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common_present = + true; + auto& rach_cfg_common_pack = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common; + + rach_cfg_common_pack.set_setup(); + rach_cfg_common_pack.setup().rach_cfg_generic.prach_cfg_idx = 16; + rach_cfg_common_pack.setup().rach_cfg_generic.msg1_fdm = rach_cfg_generic_s::msg1_fdm_opts::one; + rach_cfg_common_pack.setup().rach_cfg_generic.msg1_freq_start = 1; + rach_cfg_common_pack.setup().rach_cfg_generic.zero_correlation_zone_cfg = 0; + rach_cfg_common_pack.setup().rach_cfg_generic.preamb_rx_target_pwr = -110; + rach_cfg_common_pack.setup().rach_cfg_generic.preamb_trans_max = + asn1::rrc_nr::rach_cfg_generic_s::preamb_trans_max_opts::n7; + rach_cfg_common_pack.setup().rach_cfg_generic.pwr_ramp_step = + asn1::rrc_nr::rach_cfg_generic_s::pwr_ramp_step_opts::db4; + rach_cfg_common_pack.setup().rach_cfg_generic.ra_resp_win = asn1::rrc_nr::rach_cfg_generic_s::ra_resp_win_opts::sl10; + rach_cfg_common_pack.setup().ra_contention_resolution_timer = + asn1::rrc_nr::rach_cfg_common_s::ra_contention_resolution_timer_opts::sf64; + rach_cfg_common_pack.setup().prach_root_seq_idx.set( + asn1::rrc_nr::rach_cfg_common_s::prach_root_seq_idx_c_::types_opts::l839); + rach_cfg_common_pack.setup().prach_root_seq_idx.set_l839() = 1; + rach_cfg_common_pack.setup().restricted_set_cfg = + asn1::rrc_nr::rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set; + + // PUSCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp + .pusch_cfg_common_present = true; + auto& pusch_cfg_common_pack = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.pusch_cfg_common; + pusch_cfg_common_pack.set_setup(); + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list.resize(2); + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].k2_present = true; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].k2 = 4; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].map_type = + asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::type_a; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].start_symbol_and_len = 27; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].k2_present = true; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].k2 = 3; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].map_type = + asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::type_a; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].start_symbol_and_len = 27; + pusch_cfg_common_pack.setup().p0_nominal_with_grant = -90; + + // PUCCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp + .pucch_cfg_common_present = true; + auto& pucch_cfg_common_pack = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.pucch_cfg_common; + pucch_cfg_common_pack.set_setup(); + pucch_cfg_common_pack.setup().pucch_group_hop = asn1::rrc_nr::pucch_cfg_common_s::pucch_group_hop_opts::neither; + pucch_cfg_common_pack.setup().p0_nominal_present = true; + pucch_cfg_common_pack.setup().p0_nominal = -90; + + // SSB config (optional) + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_positions_in_burst_present = true; + auto& ssb_pos_in_burst = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_positions_in_burst; + ssb_pos_in_burst.set_short_bitmap().from_number(0b1000); + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_periodicity_serving_cell_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_periodicity_serving_cell = + serving_cell_cfg_common_s::ssb_periodicity_serving_cell_opts::ms20; + + // pack only cell group info + asn1::dyn_octstring packed_cell_group; + packed_cell_group.resize(256); + asn1::bit_ref bref_pack(packed_cell_group.data(), packed_cell_group.size()); + TESTASSERT(cell_group_cfg_pack.pack(bref_pack) == asn1::SRSASN_SUCCESS); + TESTASSERT(test_pack_unpack_consistency(cell_group_cfg_pack) == SRSASN_SUCCESS); + packed_cell_group.resize(bref_pack.distance_bytes()); + +#if JSON_OUTPUT + asn1::json_writer json_writer2; + cell_group_cfg_pack.to_json(json_writer2); + srslog::fetch_basic_logger("RRC").info(packed_cell_group.data(), + packed_cell_group.size(), + "Cell group config repacked (%d B): \n %s", + packed_cell_group.size(), + json_writer2.to_string().c_str()); +#endif + +#if HAVE_PCAP + // pack full DL-DCCH with RRC reconfig for PCAP output + dl_dcch_msg_s dcch; + dcch.msg.set_c1().set_rrc_recfg(); + rrc_recfg_s& reconfig = dcch.msg.c1().rrc_recfg(); + reconfig.rrc_transaction_id = 0; + reconfig.crit_exts.set_rrc_recfg(); + rrc_recfg_ies_s& recfg_ies = reconfig.crit_exts.rrc_recfg(); + recfg_ies.secondary_cell_group_present = true; + recfg_ies.secondary_cell_group = packed_cell_group; + + asn1::dyn_octstring packed_dcch; + packed_dcch.resize(1024); + asn1::bit_ref bref_dcch_pack(packed_dcch.data(), packed_dcch.size()); + TESTASSERT(dcch.pack(bref_dcch_pack) == asn1::SRSASN_SUCCESS); + packed_dcch.resize(bref_dcch_pack.distance_bytes() + 10); + + asn1::json_writer json_writer3; + dcch.to_json(json_writer3); + srslog::fetch_basic_logger("RRC").info(packed_dcch.data(), + packed_dcch.size(), + "Full DCCH repacked (%d B): \n %s", + packed_dcch.size(), + json_writer3.to_string().c_str()); + + srsran::write_pdcp_sdu_nr(1, packed_dcch.data(), packed_dcch.size()); +#endif + + return SRSRAN_SUCCESS; +} + +int main() +{ + auto& asn1_logger = srslog::fetch_basic_logger("ASN1", false); + asn1_logger.set_level(srslog::basic_levels::debug); + asn1_logger.set_hex_dump_max_size(-1); + auto& rrc_logger = srslog::fetch_basic_logger("RRC", false); + rrc_logger.set_level(srslog::basic_levels::debug); + rrc_logger.set_hex_dump_max_size(-1); + + // Start the log backend. + srslog::init(); + +#if HAVE_PCAP + pcap_handle = std::unique_ptr(new srsran::mac_pcap()); + pcap_handle->open("srsran_asn1_rrc_nr_test.pcap"); +#endif + + test_rrc_setup_complete(); + TESTASSERT(test_eutra_nr_capabilities() == SRSRAN_SUCCESS); + TESTASSERT(test_ue_mrdc_capabilities() == SRSRAN_SUCCESS); + TESTASSERT(test_ue_rrc_reconfiguration() == SRSRAN_SUCCESS); + TESTASSERT(test_radio_bearer_config() == SRSRAN_SUCCESS); + TESTASSERT(test_cell_group_config_tdd() == SRSRAN_SUCCESS); + TESTASSERT(test_cell_group_config_fdd() == SRSRAN_SUCCESS); + + srslog::flush(); + + printf("Success\n"); + +#if HAVE_PCAP + if (pcap_handle) { + pcap_handle->close(); + } +#endif + + return SRSRAN_SUCCESS; +} diff --git a/lib/test/asn1/srsran_asn1_rrc_ul_dcch_test.cc b/lib/test/asn1/srsran_asn1_rrc_ul_dcch_test.cc index 2e7ed852f..365a30300 100644 --- a/lib/test/asn1/srsran_asn1_rrc_ul_dcch_test.cc +++ b/lib/test/asn1/srsran_asn1_rrc_ul_dcch_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -61,7 +61,7 @@ int rrc_ue_cap_info_test(srsran::mac_pcap* pcap) rrc_logger.set_level(srslog::basic_levels::debug); rrc_logger.set_hex_dump_max_size(128); - rrc_args_t args = {}; + srsue::rrc_args_t args = {}; args.feature_group = 0xe6041c00; args.nof_supported_bands = 1; args.supported_bands[0] = 8; @@ -128,7 +128,7 @@ int rrc_ue_cap_info_test(srsran::mac_pcap* pcap) int pack_fail_test() { - rrc_args_t args = {}; + srsue::rrc_args_t args = {}; args.feature_group = 0xe6041c00; args.nof_supported_bands = 1; args.supported_bands[0] = 8; diff --git a/lib/test/common/CMakeLists.txt b/lib/test/common/CMakeLists.txt index fcae468cc..70598c6f4 100644 --- a/lib/test/common/CMakeLists.txt +++ b/lib/test/common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -51,6 +51,10 @@ add_executable(test_f12345 test_f12345.cc) target_link_libraries(test_f12345 srsran_common ${CMAKE_THREAD_LIBS_INIT}) add_test(test_f12345 test_f12345) +add_executable(test_security_kdf test_security_kdf.cc) +target_link_libraries(test_security_kdf srsran_common ${CMAKE_THREAD_LIBS_INIT}) +add_test(test_security_kdf test_security_kdf) + add_executable(timeout_test timeout_test.cc) target_link_libraries(timeout_test srsran_phy ${CMAKE_THREAD_LIBS_INIT}) @@ -66,7 +70,7 @@ target_link_libraries(queue_test srsran_common ${CMAKE_THREAD_LIBS_INIT}) add_test(queue_test queue_test) add_executable(timer_test timer_test.cc) -target_link_libraries(timer_test srsran_common) +target_link_libraries(timer_test srsran_common ${ATOMIC_LIBS}) add_test(timer_test timer_test) add_executable(network_utils_test network_utils_test.cc) @@ -82,14 +86,8 @@ target_link_libraries(choice_type_test srsran_common) add_test(choice_type_test choice_type_test) add_executable(task_scheduler_test task_scheduler_test.cc) -target_link_libraries(task_scheduler_test srsran_common) +target_link_libraries(task_scheduler_test srsran_common ${ATOMIC_LIBS}) add_test(task_scheduler_test task_scheduler_test) -add_executable(pnf_dummy pnf_dummy.cc) -target_link_libraries(pnf_dummy srsran_common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) - -add_executable(pnf_bridge pnf_bridge.cc) -target_link_libraries(pnf_bridge srsran_common ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) - add_executable(mac_pcap_net_test mac_pcap_net_test.cc) target_link_libraries(mac_pcap_net_test srsran_common ${SCTP_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/lib/test/common/bcd_helpers_test.cc b/lib/test/common/bcd_helpers_test.cc index a8f506277..fe8bb818e 100644 --- a/lib/test/common/bcd_helpers_test.cc +++ b/lib/test/common/bcd_helpers_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/byte_buffer_queue_test.cc b/lib/test/common/byte_buffer_queue_test.cc index 7c80c5e41..07021249e 100644 --- a/lib/test/common/byte_buffer_queue_test.cc +++ b/lib/test/common/byte_buffer_queue_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/choice_type_test.cc b/lib/test/common/choice_type_test.cc index 582173eed..2da314416 100644 --- a/lib/test/common/choice_type_test.cc +++ b/lib/test/common/choice_type_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -89,15 +89,15 @@ int test_tagged_union() { using srsran::choice_details::tagged_union_t; tagged_union_t u; - u.construct_unsafe(5); + u.construct_unchecked(5); TESTASSERT(u.is()); TESTASSERT(u.get_unchecked() == 5); - u.destroy_unsafe(); + u.destroy_unchecked(); TESTASSERT(C::counter == 0); - u.construct_unsafe(C{}); + u.construct_unchecked(C{}); TESTASSERT(C::counter == 1); - u.destroy_unsafe(); + u.destroy_unchecked(); TESTASSERT(C::counter == 0); return SRSRAN_SUCCESS; diff --git a/lib/test/common/mac_pcap_net_test.cc b/lib/test/common/mac_pcap_net_test.cc index cd33d55ae..6c836841a 100644 --- a/lib/test/common/mac_pcap_net_test.cc +++ b/lib/test/common/mac_pcap_net_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/multiqueue_test.cc b/lib/test/common/multiqueue_test.cc index 95ad8fae0..a1c32785b 100644 --- a/lib/test/common/multiqueue_test.cc +++ b/lib/test/common/multiqueue_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,20 +21,14 @@ #include "srsran/adt/move_callback.h" #include "srsran/common/multiqueue.h" +#include "srsran/common/test_common.h" #include "srsran/common/thread_pool.h" #include #include +#include #include #include -#define TESTASSERT(cond) \ - { \ - if (!(cond)) { \ - std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ - return -1; \ - } \ - } - using namespace srsran; int test_multiqueue() @@ -44,79 +38,80 @@ int test_multiqueue() int number = 2; multiqueue_handler multiqueue; - TESTASSERT(multiqueue.nof_queues() == 0) + TESTASSERT(multiqueue.nof_queues() == 0); // test push/pop and size for one queue - int qid1 = multiqueue.add_queue(); - TESTASSERT(qid1 == 0 and multiqueue.is_queue_active(qid1)) - TESTASSERT(multiqueue.size(qid1) == 0 and multiqueue.empty(qid1)) - TESTASSERT(multiqueue.nof_queues() == 1) - TESTASSERT(multiqueue.try_push(qid1, 5).first) - TESTASSERT(multiqueue.try_push(qid1, number)) - TESTASSERT(multiqueue.size(qid1) == 2 and not multiqueue.empty(qid1)) - TESTASSERT(multiqueue.wait_pop(&number) == qid1) - TESTASSERT(number == 5) - TESTASSERT(multiqueue.wait_pop(&number) == qid1) - TESTASSERT(number == 2 and multiqueue.empty(qid1) and multiqueue.size(qid1) == 0) + queue_handle qid1 = multiqueue.add_queue(); + TESTASSERT(qid1.active()); + TESTASSERT(qid1.size() == 0 and qid1.empty()); + TESTASSERT(multiqueue.nof_queues() == 1); + TESTASSERT(qid1.try_push(5).has_value()); + TESTASSERT(qid1.try_push(number)); + TESTASSERT(qid1.size() == 2 and not qid1.empty()); + TESTASSERT(multiqueue.wait_pop(&number)); + TESTASSERT(number == 5); + TESTASSERT(multiqueue.wait_pop(&number)); + TESTASSERT(number == 2 and qid1.empty()); // test push/pop and size for two queues - int qid2 = multiqueue.add_queue(); - TESTASSERT(qid2 == 1) - TESTASSERT(multiqueue.nof_queues() == 2 and multiqueue.is_queue_active(qid1)) - TESTASSERT(multiqueue.try_push(qid2, 3).first) - TESTASSERT(multiqueue.size(qid2) == 1 and not multiqueue.empty(qid2)) - TESTASSERT(multiqueue.empty(qid1) and multiqueue.size(qid1) == 0) + queue_handle qid2 = multiqueue.add_queue(); + TESTASSERT(qid2.active()); + TESTASSERT(multiqueue.nof_queues() == 2 and qid1.active()); + TESTASSERT(qid2.try_push(3).has_value()); + TESTASSERT(qid2.size() == 1 and not qid2.empty()); + TESTASSERT(qid1.empty()); // check if erasing a queue breaks anything - multiqueue.erase_queue(qid1); - TESTASSERT(multiqueue.nof_queues() == 1 and not multiqueue.is_queue_active(qid1)) + qid1.reset(); + TESTASSERT(multiqueue.nof_queues() == 1 and not qid1.active()); qid1 = multiqueue.add_queue(); - TESTASSERT(qid1 == 0) - TESTASSERT(multiqueue.empty(qid1) and multiqueue.is_queue_active(qid1)) + TESTASSERT(qid1.empty() and qid1.active()); + TESTASSERT(qid2.size() == 1 and not qid2.empty()); multiqueue.wait_pop(&number); // check round-robin for (int i = 0; i < 10; ++i) { - TESTASSERT(multiqueue.try_push(qid1, i)) + TESTASSERT(qid1.try_push(i)); } for (int i = 20; i < 35; ++i) { - TESTASSERT(multiqueue.try_push(qid2, i)) + TESTASSERT(qid2.try_push(i)); } - TESTASSERT(multiqueue.size(qid1) == 10) - TESTASSERT(multiqueue.size(qid2) == 15) - TESTASSERT(multiqueue.wait_pop(&number) == qid1 and number == 0) - TESTASSERT(multiqueue.wait_pop(&number) == qid2 and number == 20) - TESTASSERT(multiqueue.wait_pop(&number) == qid1 and number == 1) - TESTASSERT(multiqueue.wait_pop(&number) == qid2 and number == 21) - TESTASSERT(multiqueue.size(qid1) == 8) - TESTASSERT(multiqueue.size(qid2) == 13) + TESTASSERT(qid1.size() == 10); + TESTASSERT(qid2.size() == 15); + TESTASSERT(multiqueue.wait_pop(&number) and number == 0); + TESTASSERT(multiqueue.wait_pop(&number) and number == 20); + TESTASSERT(multiqueue.wait_pop(&number) and number == 1); + TESTASSERT(multiqueue.wait_pop(&number) and number == 21); + TESTASSERT(qid1.size() == 8); + TESTASSERT(qid2.size() == 13); for (int i = 0; i < 8 * 2; ++i) { multiqueue.wait_pop(&number); } - TESTASSERT(multiqueue.size(qid1) == 0) - TESTASSERT(multiqueue.size(qid2) == 5) - TESTASSERT(multiqueue.wait_pop(&number) == qid2 and number == 30) + TESTASSERT(qid1.size() == 0); + TESTASSERT(qid2.size() == 5); + TESTASSERT(multiqueue.wait_pop(&number) and number == 30); // remove existing queues - multiqueue.erase_queue(qid1); - multiqueue.erase_queue(qid2); - TESTASSERT(multiqueue.nof_queues() == 0) + qid1.reset(); + qid2.reset(); + TESTASSERT(multiqueue.nof_queues() == 0); // check that adding a queue of different capacity works { - int qid1 = multiqueue.add_queue(); - int qid2 = multiqueue.add_queue(); + qid1 = multiqueue.add_queue(); + qid2 = multiqueue.add_queue(); // remove first queue again - multiqueue.erase_queue(qid1); - TESTASSERT(multiqueue.nof_queues() == 1) + qid1.reset(); + TESTASSERT(multiqueue.nof_queues() == 1); // add queue with non-default capacity - int qid3 = multiqueue.add_queue(10); + auto qid3 = multiqueue.add_queue(10); + TESTASSERT(qid3.capacity() == 10); // make sure neither a new queue index is returned - TESTASSERT(qid1 != qid3) - TESTASSERT(qid2 != qid3) + TESTASSERT(qid1 != qid3); + TESTASSERT(qid2 != qid3); } std::cout << "outcome: Success\n"; @@ -131,28 +126,28 @@ int test_multiqueue_threading() int capacity = 4, number = 0, start_number = 2, nof_pushes = capacity + 1; multiqueue_handler multiqueue(capacity); - int qid1 = multiqueue.add_queue(); - auto push_blocking_func = [&multiqueue](int qid, int start_value, int nof_pushes, bool* is_running) { + auto qid1 = multiqueue.add_queue(); + std::atomic t1_running = {true}; + auto push_blocking_func = [&t1_running](queue_handle* qid, int start_value, int nof_pushes) { for (int i = 0; i < nof_pushes; ++i) { - multiqueue.push(qid, start_value + i); + qid->push(start_value + i); std::cout << "t1: pushed item " << i << std::endl; } std::cout << "t1: pushed all items\n"; - *is_running = false; + t1_running = false; }; - bool t1_running = true; - std::thread t1(push_blocking_func, qid1, start_number, nof_pushes, &t1_running); + std::thread t1(push_blocking_func, &qid1, start_number, nof_pushes); // Wait for queue to fill - while ((int)multiqueue.size(qid1) != capacity) { + while ((int)qid1.size() != capacity) { usleep(1000); - TESTASSERT(t1_running) + TESTASSERT(t1_running); } for (int i = 0; i < nof_pushes; ++i) { - TESTASSERT(multiqueue.wait_pop(&number) == qid1) - TESTASSERT(number == start_number + i) + TESTASSERT(multiqueue.wait_pop(&number)); + TESTASSERT(number == start_number + i); std::cout << "main: popped item " << i << "\n"; } std::cout << "main: popped all items\n"; @@ -161,9 +156,9 @@ int test_multiqueue_threading() while (t1_running) { usleep(1000); } - TESTASSERT(multiqueue.size(qid1) == 0) + TESTASSERT(qid1.size() == 0); - multiqueue.reset(); + multiqueue.stop(); t1.join(); std::cout << "outcome: Success\n"; @@ -179,25 +174,25 @@ int test_multiqueue_threading2() int capacity = 4, start_number = 2, nof_pushes = capacity + 1; multiqueue_handler multiqueue(capacity); - int qid1 = multiqueue.add_queue(); - auto push_blocking_func = [&multiqueue](int qid, int start_value, int nof_pushes, bool* is_running) { + auto qid1 = multiqueue.add_queue(); + auto push_blocking_func = [](queue_handle* qid, int start_value, int nof_pushes, bool* is_running) { for (int i = 0; i < nof_pushes; ++i) { - multiqueue.push(qid, start_value + i); + qid->push(start_value + i); } std::cout << "t1: pushed all items\n"; *is_running = false; }; bool t1_running = true; - std::thread t1(push_blocking_func, qid1, start_number, nof_pushes, &t1_running); + std::thread t1(push_blocking_func, &qid1, start_number, nof_pushes, &t1_running); // Wait for queue to fill - while ((int)multiqueue.size(qid1) != capacity) { + while ((int)qid1.size() != capacity) { usleep(1000); - TESTASSERT(t1_running) + TESTASSERT(t1_running); } - multiqueue.reset(); + multiqueue.stop(); t1.join(); std::cout << "outcome: Success\n"; @@ -213,23 +208,95 @@ int test_multiqueue_threading3() int capacity = 4; multiqueue_handler multiqueue(capacity); - int qid1 = multiqueue.add_queue(); - auto pop_blocking_func = [&multiqueue](int qid, bool* success) { - int number = 0; - int id = multiqueue.wait_pop(&number); - *success = id < 0; + auto qid1 = multiqueue.add_queue(); + auto pop_blocking_func = [&multiqueue](bool* success) { + int number = 0; + bool ret = multiqueue.wait_pop(&number); + *success = not ret; }; bool t1_success = false; - std::thread t1(pop_blocking_func, qid1, &t1_success); + std::thread t1(pop_blocking_func, &t1_success); - TESTASSERT(not t1_success) + TESTASSERT(not t1_success); usleep(1000); - TESTASSERT(not t1_success) - TESTASSERT((int)multiqueue.size(qid1) == 0) + TESTASSERT(not t1_success); + TESTASSERT((int)qid1.size() == 0); // Should be able to unlock all - multiqueue.reset(); + multiqueue.stop(); + TESTASSERT(multiqueue.nof_queues() == 0); + TESTASSERT(not qid1.active()); + t1.join(); + TESTASSERT(t1_success); + + std::cout << "outcome: Success\n"; + std::cout << "===================================================\n"; + + return 0; +} + +int test_multiqueue_threading4() +{ + std::cout << "\n===== TEST multiqueue threading test 4: start =====\n"; + // Description: the consumer will block on popping, but the pushing from different producers + // should be sufficient to awake it when necessary + + int capacity = 4; + multiqueue_handler multiqueue(capacity); + auto qid1 = multiqueue.add_queue(); + auto qid2 = multiqueue.add_queue(); + auto qid3 = multiqueue.add_queue(); + auto qid4 = multiqueue.add_queue(); + std::mutex mutex; + int last_number = -1; + auto pop_blocking_func = [&multiqueue, &last_number, &mutex](bool* success) { + int number = 0; + while (multiqueue.wait_pop(&number)) { + std::lock_guard lock(mutex); + last_number = std::max(last_number, number); + } + *success = true; + }; + + bool t1_success = false; + std::thread t1(pop_blocking_func, &t1_success); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dist{0, 2}; + for (int i = 0; i < 10000; ++i) { + int qidx = dist(gen); + switch (qidx) { + case 0: + qid1.push(i); + break; + case 1: + qid2.push(i); + break; + case 2: + qid4.push(i); + break; + default: + break; + } + if (i % 20 == 0) { + int count = 0; + std::unique_lock lock(mutex); + while (last_number != i) { + lock.unlock(); + usleep(100); + count++; + TESTASSERT(count < 100000); + lock.lock(); + } + } + } + + // Should be able to unlock all + multiqueue.stop(); + TESTASSERT(multiqueue.nof_queues() == 0); + TESTASSERT(not qid1.active()); t1.join(); TESTASSERT(t1_success); @@ -290,9 +357,10 @@ int test_task_thread_pool2() // Description: push a very long task to all workers, and call thread_pool.stop() to check if it waits for the tasks // to be completed, and does not get stuck. - uint32_t nof_workers = 4; - uint8_t workers_started = 0, workers_finished = 0; - std::mutex mut; + uint32_t nof_workers = 4; + std::atomic workers_started{0}; + uint8_t workers_finished = 0; + std::mutex mut; task_thread_pool thread_pool(nof_workers); thread_pool.start(); @@ -302,7 +370,7 @@ int test_task_thread_pool2() std::lock_guard lock(mut); workers_started++; } - sleep(1); + std::this_thread::sleep_for(std::chrono::seconds{1}); std::lock_guard lock(mut); std::cout << "worker has finished\n"; workers_finished++; @@ -430,6 +498,7 @@ int main() TESTASSERT(test_multiqueue_threading() == 0); TESTASSERT(test_multiqueue_threading2() == 0); TESTASSERT(test_multiqueue_threading3() == 0); + TESTASSERT(test_multiqueue_threading4() == 0); TESTASSERT(test_task_thread_pool() == 0); TESTASSERT(test_task_thread_pool2() == 0); diff --git a/lib/test/common/network_utils_test.cc b/lib/test/common/network_utils_test.cc index 098c23fc5..a41f08558 100644 --- a/lib/test/common/network_utils_test.cc +++ b/lib/test/common/network_utils_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -53,7 +53,7 @@ int test_socket_handler() { auto& logger = srslog::fetch_basic_logger("S1AP", false); - int counter = 0; + std::atomic counter = {0}; srsran::unique_socket server_socket, client_socket, client_socket2; srsran::socket_manager sockhandler; @@ -61,12 +61,19 @@ int test_socket_handler() const char* server_addr = "127.0.100.1"; using namespace srsran::net_utils; - TESTASSERT(sctp_init_server(&server_socket, socket_type::seqpacket, server_addr, server_port)); + TESTASSERT(server_socket.open_socket( + srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP)); + TESTASSERT(server_socket.bind_addr(server_addr, server_port)); + TESTASSERT(server_socket.start_listen()); logger.info("Listening from fd=%d", server_socket.fd()); - TESTASSERT(sctp_init_client(&client_socket, socket_type::seqpacket, "127.0.0.1")); - TESTASSERT(sctp_init_client(&client_socket2, socket_type::seqpacket, "127.0.0.2")); + TESTASSERT(client_socket.open_socket( + srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP)); + TESTASSERT(client_socket.bind_addr("127.0.0.1", 0)); TESTASSERT(client_socket.connect_to(server_addr, server_port)); + TESTASSERT(client_socket2.open_socket( + srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP)); + TESTASSERT(client_socket2.bind_addr("127.0.0.2", 0)); TESTASSERT(client_socket2.connect_to(server_addr, server_port)); // register server Rx handler @@ -123,6 +130,24 @@ int test_socket_handler() return 0; } +int test_sctp_bind_error() +{ + srsran::unique_socket sock; + TESTASSERT(sock.open_socket(srsran::net_utils::addr_family::ipv4, + srsran::net_utils::socket_type::seqpacket, + srsran::net_utils::protocol_type::SCTP)); + TESTASSERT(not sock.bind_addr("1.1.1.1", 8000)); // Bogus IP address + // should not be able to bind + + srsran::unique_socket sock2; + TESTASSERT(sock2.open_socket(srsran::net_utils::addr_family::ipv4, + srsran::net_utils::socket_type::seqpacket, + srsran::net_utils::protocol_type::SCTP)); + TESTASSERT(sock.bind_addr("127.0.0.1", 8000)); // Good IP address + // should be able to bind + return SRSRAN_SUCCESS; +} + int main() { auto& logger = srslog::fetch_basic_logger("S1AP", false); @@ -132,6 +157,7 @@ int main() srslog::init(); TESTASSERT(test_socket_handler() == 0); + TESTASSERT(test_sctp_bind_error() == 0); return 0; } diff --git a/lib/test/common/pnf_bridge.cc b/lib/test/common/pnf_bridge.cc deleted file mode 100644 index 3f93142b6..000000000 --- a/lib/test/common/pnf_bridge.cc +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "srsran/common/basic_pnf.h" - -using namespace std; -namespace bpo = boost::program_options; - -struct pnf_args_t { - std::string gnb_vnf_addr; - std::string ue_vnf_addr; - uint16_t gnb_vnf_port; - uint16_t ue_vnf_port; - uint32_t sf_interval; - int32_t num_sf; - uint32_t tb_len; -}; - -void parse_args(pnf_args_t* args, int argc, char* argv[]) -{ - // Command line only options - bpo::options_description general("General options"); - - general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit"); - - // Command line or config file options - bpo::options_description common("Configuration options"); - - // clang-format off - common.add_options() - ("vnf.gnb_addr", bpo::value(&args->gnb_vnf_addr)->default_value("127.0.0.1"), "VNF address") - ("vnf.ue_addr", bpo::value(&args->ue_vnf_addr)->default_value("127.0.0.1"), "VNF address") - ("vnf.gnb_port", bpo::value(&args->gnb_vnf_port)->default_value(3333), "gNB VNF port") - ("vnf.ue_port", bpo::value(&args->ue_vnf_port)->default_value(3334), "UE VNF port") - ("sf_interval", bpo::value(&args->sf_interval)->default_value(1000), "Interval between subframes in us") - ("num_sf", bpo::value(&args->num_sf)->default_value(-1), "Number of subframes to signal (-1 infinity)") - ("tb_len", bpo::value(&args->tb_len)->default_value(1600), "TB lenth (0 for random size)"); - // clang-format on - - // these options are allowed on the command line - bpo::options_description cmdline_options; - cmdline_options.add(common).add(general); - - // parse the command line and store result in vm - bpo::variables_map vm; - bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); - bpo::notify(vm); - - // help option was given - print usage and exit - if (vm.count("help")) { - cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; - cout << common << endl << general << endl; - exit(0); - } -} - -bool running = true; -void sig_int_handler(int signo) -{ - printf("SIGINT received. Exiting...\n"); - if (signo == SIGINT) { - running = false; - } -} - -int main(int argc, char** argv) -{ - signal(SIGINT, sig_int_handler); - - pnf_args_t args; - parse_args(&args, argc, argv); - - srsran::srsran_basic_pnf ue_pnf("ue", args.ue_vnf_addr, args.ue_vnf_port, args.sf_interval, args.num_sf, args.tb_len); - srsran::srsran_basic_pnf gnb_pnf( - "gnb", args.gnb_vnf_addr, args.gnb_vnf_port, args.sf_interval, args.num_sf, args.tb_len); - - gnb_pnf.connect_out_rf_queue(ue_pnf.get_in_rf_queue()); - - ue_pnf.start(); - gnb_pnf.start(); - - while (running) { - for (uint32_t i = 0; i < 2; ++i) { - srsran::pnf_metrics_t metrics = (i == 0) ? ue_pnf.get_metrics() : gnb_pnf.get_metrics(); - printf("%s: RTT=%d, #Error=%d, #PDUs=%d, Total TB size=%d, Rate=%.2f Mbit/s\n", - i == 0 ? "UE" : "gNB", - metrics.avg_rtt_us, - metrics.num_timing_errors, - metrics.num_pdus, - metrics.tb_size, - metrics.tb_size * 8 / 1.0e6); - } - - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - ue_pnf.stop(); - gnb_pnf.stop(); - - return 0; -} \ No newline at end of file diff --git a/lib/test/common/pnf_dummy.cc b/lib/test/common/pnf_dummy.cc deleted file mode 100644 index 6bc2ede55..000000000 --- a/lib/test/common/pnf_dummy.cc +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "srsran/common/basic_pnf.h" - -using namespace std; -namespace bpo = boost::program_options; - -struct pnf_args_t { - std::string type; - std::string vnf_addr; - uint16_t vnf_port; - uint32_t sf_interval; - int32_t num_sf; - uint32_t tb_len; -}; - -void parse_args(pnf_args_t* args, int argc, char* argv[]) -{ - // Command line only options - bpo::options_description general("General options"); - - general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit"); - - // Command line or config file options - bpo::options_description common("Configuration options"); - - // clang-format off - common.add_options() - ("vnf.type", bpo::value(&args->type)->default_value("gnb"), "VNF instance type [gnb,ue]") - ("vnf.addr", bpo::value(&args->vnf_addr)->default_value("127.0.0.1"), "VNF address") - ("vnf.port", bpo::value(&args->vnf_port)->default_value(3333), "VNF port") - ("sf_interval", bpo::value(&args->sf_interval)->default_value(1000), "Interval between subframes in us") - ("num_sf", bpo::value(&args->num_sf)->default_value(-1), "Number of subframes to signal (-1 infinity)") - ("tb_len", bpo::value(&args->tb_len)->default_value(0), "TB lenth (0 for random size)"); - - // clang-format on - - // these options are allowed on the command line - bpo::options_description cmdline_options; - cmdline_options.add(common).add(general); - - // parse the command line and store result in vm - bpo::variables_map vm; - bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); - bpo::notify(vm); - - // help option was given - print usage and exit - if (vm.count("help")) { - cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; - cout << common << endl << general << endl; - exit(0); - } -} - -bool running = true; -void sig_int_handler(int signo) -{ - printf("SIGINT received. Exiting...\n"); - if (signo == SIGINT) { - running = false; - } -} - -int main(int argc, char** argv) -{ - signal(SIGINT, sig_int_handler); - - pnf_args_t args; - parse_args(&args, argc, argv); - - srsran::srsran_basic_pnf pnf(args.type, args.vnf_addr, args.vnf_port, args.sf_interval, args.num_sf, args.tb_len); - - pnf.start(); - - while (running) { - srsran::pnf_metrics_t metrics = pnf.get_metrics(); - printf("RTT=%d, #Error=%d, #PDUs=%d, Total TB size=%d, Rate=%.2f Mbit/s\n", - metrics.avg_rtt_us, - metrics.num_timing_errors, - metrics.num_pdus, - metrics.tb_size, - metrics.tb_size * 8 / 1e6); - - std::this_thread::sleep_for(std::chrono::seconds(1)); - } - - pnf.stop(); - - return 0; -} \ No newline at end of file diff --git a/lib/test/common/stack_procedure_test.cc b/lib/test/common/stack_procedure_test.cc index 57474bd42..955044f85 100644 --- a/lib/test/common/stack_procedure_test.cc +++ b/lib/test/common/stack_procedure_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/task_scheduler_test.cc b/lib/test/common/task_scheduler_test.cc index d6cbfc387..9eeda3046 100644 --- a/lib/test/common/task_scheduler_test.cc +++ b/lib/test/common/task_scheduler_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -52,7 +52,10 @@ int test_task_scheduler_no_pool() task_sched.notify_background_task_result([&state]() { state = task_result::external; }); }); TESTASSERT(state == task_result::null); - task_sched.run_next_task(); // runs notification + while (state != task_result::external) { + task_sched.run_pending_tasks(); // runs notification + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } TESTASSERT(state == task_result::external); return SRSRAN_SUCCESS; @@ -67,8 +70,10 @@ int test_task_scheduler_with_pool() task_sched.notify_background_task_result([&state]() { state = task_result::external; }); }); TESTASSERT(state == task_result::null); - task_sched.run_next_task(); // waits and runs notification - TESTASSERT(state == task_result::external); + while (state != task_result::external) { + task_sched.run_pending_tasks(); // runs notification + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } return SRSRAN_SUCCESS; } diff --git a/lib/test/common/test_eea1.cc b/lib/test/common/test_eea1.cc index b92178c7a..aab8e1182 100644 --- a/lib/test/common/test_eea1.cc +++ b/lib/test/common/test_eea1.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/test_eea2.cc b/lib/test/common/test_eea2.cc index cb70de321..50d1bb2c8 100644 --- a/lib/test/common/test_eea2.cc +++ b/lib/test/common/test_eea2.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/test_eea3.cc b/lib/test/common/test_eea3.cc index 270c699fd..08a771a25 100644 --- a/lib/test/common/test_eea3.cc +++ b/lib/test/common/test_eea3.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/test_eia1.cc b/lib/test/common/test_eia1.cc index dd7a16d7b..77a77c751 100644 --- a/lib/test/common/test_eia1.cc +++ b/lib/test/common/test_eia1.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/test_eia3.cc b/lib/test/common/test_eia3.cc index 83fd3a6e2..69ca5e0ef 100644 --- a/lib/test/common/test_eia3.cc +++ b/lib/test/common/test_eia3.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/test_f12345.cc b/lib/test/common/test_f12345.cc index b02c0b556..33f24e79c 100644 --- a/lib/test/common/test_f12345.cc +++ b/lib/test/common/test_f12345.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,6 +23,7 @@ #include #include "srsran/common/liblte_security.h" +#include "srsran/common/security.h" #include "srsran/common/test_common.h" /* * Prototypes @@ -159,85 +160,69 @@ int test_set_2() err_cmp = arrcmp(ak_star_o, ak_star, sizeof(ak_star)); TESTASSERT(err_cmp == 0); return SRSRAN_SUCCESS; - ; -} - -int test_set_ksg() -{ - LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; - int32 err_cmp = 0; - - uint8_t k_enb[] = {0xfe, 0x7d, 0xee, 0x80, 0x8d, 0x7f, 0x3b, 0x88, 0x2a, 0x08, 0x2c, 0xbd, 0xc8, 0x39, 0x0d, 0x12, - 0x9e, 0x5d, 0x28, 0xaf, 0x0e, 0x83, 0x22, 0xeb, 0x57, 0x3a, 0xda, 0x36, 0xf2, 0x1a, 0x5a, 0x89}; - uint8_t sk_gnb_o[32]; - uint16_t scg_counter = 0; - err_lte = liblte_security_generate_sk_gnb(k_enb, sk_gnb_o, scg_counter); - TESTASSERT(err_lte == LIBLTE_SUCCESS); - arrprint(sk_gnb_o, sizeof(sk_gnb_o)); - uint8_t sk_gnb[] = {0x45, 0xcb, 0xc3, 0xf8, 0xa8, 0x11, 0x93, 0xfd, 0x5c, 0x52, 0x29, 0x30, 0x0d, 0x59, 0xed, 0xf8, - 0x12, 0xe9, 0x98, 0xa1, 0x15, 0xec, 0x4e, 0x0c, 0xe9, 0x03, 0xba, 0x89, 0x36, 0x7e, 0x26, 0x28}; - err_cmp = arrcmp(sk_gnb_o, sk_gnb, sizeof(sk_gnb)); - TESTASSERT(err_cmp == 0); - return SRSRAN_SUCCESS; -} - -int test_set_nr_rrc_up() -{ - LIBLTE_ERROR_ENUM err_lte = LIBLTE_ERROR_INVALID_INPUTS; - int32 err_cmp = 0; - - uint8_t sk_gnb[] = {0x45, 0xcb, 0xc3, 0xf8, 0xa8, 0x11, 0x93, 0xfd, 0x5c, 0x52, 0x29, 0x30, 0x0d, 0x59, 0xed, 0xf8, - 0x12, 0xe9, 0x98, 0xa1, 0x15, 0xec, 0x4e, 0x0c, 0xe9, 0x03, 0xba, 0x89, 0x36, 0x7e, 0x26, 0x28}; - - uint8_t sk_gnb_o[32]; - uint8_t k_rrc_enc_o[32]; - uint8_t k_rrc_int_o[32]; - - err_lte = liblte_security_generate_k_nr_rrc(sk_gnb, - LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_128_EEA2, - LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_EIA0, - k_rrc_enc_o, - k_rrc_int_o); - - TESTASSERT(err_lte == LIBLTE_SUCCESS); - printf("RRC ENC output:\n"); - arrprint(&k_rrc_enc_o[0], sizeof(k_rrc_enc_o)); - uint8_t k_rrc_enc[] = {0x52, 0xa9, 0x95, 0xdf, 0xf8, 0x9b, 0xc2, 0x94, 0xbd, 0x89, 0xff, - 0xb1, 0x37, 0xa2, 0x9f, 0x24, 0x66, 0xa0, 0x9e, 0x99, 0x23, 0x86, - 0xc8, 0xd1, 0xdf, 0x78, 0x92, 0x96, 0x4c, 0x6f, 0xb5, 0x22}; - - err_cmp = arrcmp(k_rrc_enc_o, k_rrc_enc, sizeof(k_rrc_enc_o)); - - TESTASSERT(err_cmp == 0); - - uint8_t k_up_enc_o[32]; - uint8_t k_up_int_o[32]; - - err_lte = liblte_security_generate_k_nr_up(sk_gnb, - LIBLTE_SECURITY_CIPHERING_ALGORITHM_ID_128_EEA2, - LIBLTE_SECURITY_INTEGRITY_ALGORITHM_ID_EIA0, - k_up_enc_o, - k_up_int_o); - - uint8_t k_up_enc[] = {0x7c, 0xe2, 0x06, 0x70, 0xbb, 0xbc, 0xc5, 0x90, 0x40, 0x87, 0xc0, 0xd4, 0x26, 0x53, 0xc5, 0x40, - 0x15, 0x20, 0x52, 0xd3, 0xdf, 0xbc, 0x3f, 0x05, 0x86, 0x9b, 0x7f, 0x92, 0x00, 0x95, 0xbe, 0x68}; - printf("UP ENC output:\n"); - arrprint(&k_up_enc_o[0], sizeof(k_up_enc_o)); - - err_cmp = arrcmp(k_up_enc_o, k_up_enc, sizeof(k_up_enc_o)); - TESTASSERT(err_cmp == 0); - return SRSRAN_SUCCESS; } /* Own test sets */ -int main(int argc, char* argv[]) +int test_set_xor_own_set_1() { + auto& logger = srslog::fetch_basic_logger("LOG", false); + + uint8_t k[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + uint8_t rand[] = {0xf9, 0x6a, 0xe3, 0x6e, 0x2d, 0x65, 0xfa, 0x84, 0x64, 0xc4, 0x98, 0xff, 0xc8, 0x30, 0x38, 0x0f}; + + uint8_t res_o[16]; + uint8_t ck_o[16]; + uint8_t ik_o[16]; + uint8_t ak_o[6]; + + TESTASSERT(srsran::security_xor_f2345(k, rand, res_o, ck_o, ik_o, ak_o) == SRSRAN_SUCCESS); + + uint8_t res[] = {0xf9, 0x7b, 0xc1, 0x5d, 0x69, 0x30, 0x9c, 0xf3}; + uint8_t ck[] = {0x7b, 0xc1, 0x5d, 0x69, 0x30, 0x9c, 0xf3, 0xec, 0x5d, 0x32, 0x44, 0x04, 0xed, 0xd6, 0xf0, 0xf9}; + uint8_t ik[] = {0xc1, 0x5d, 0x69, 0x30, 0x9c, 0xf3, 0xec, 0x5d, 0x32, 0x44, 0x04, 0xed, 0xd6, 0xf0, 0xf9, 0x7b}; + uint8_t ak[] = {0x5d, 0x69, 0x30, 0x9c, 0xf3, 0xec}; + + logger.info(res_o, sizeof(res_o), "RES: "); + TESTASSERT(arrcmp(res_o, res, sizeof(res)) == 0); + + // CK + logger.info(ck_o, sizeof(ck_o), "CK: "); + TESTASSERT(arrcmp(ck_o, ck, sizeof(ck)) == 0); + + // IK + logger.info(ik_o, sizeof(ik_o), "IK: "); + TESTASSERT(arrcmp(ik_o, ik, sizeof(ik)) == 0); + + // AK + logger.info(ak_o, sizeof(ak_o), "AK: "); + TESTASSERT(arrcmp(ak_o, ak, sizeof(ak)) == 0); + + uint8_t sqn[] = {0x00, 0x00, 0x00, 0x00, 0x12, 0xd9}; + uint8_t amf[] = {0x90, 0x01}; + uint8_t mac_o[8]; + TESTASSERT(srsran::security_xor_f1(k, rand, sqn, amf, mac_o) == SRSRAN_SUCCESS); + + uint8_t mac[] = {0xf9, 0x7b, 0xc1, 0x5d, 0x7b, 0xe9, 0x0c, 0xf2}; + + // MAC + logger.info(mac_o, sizeof(mac_o), "MAC: "); + TESTASSERT(arrcmp(mac_o, mac, sizeof(mac)) == 0); - TESTASSERT(test_set_2() == SRSRAN_SUCCESS); - TESTASSERT(test_set_ksg() == SRSRAN_SUCCESS); - TESTASSERT(test_set_nr_rrc_up() == SRSRAN_SUCCESS); + return SRSRAN_SUCCESS; +} + +int main(int argc, char* argv[]) +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(128); + + srslog::init(); + + TESTASSERT(test_set_2() == SRSRAN_SUCCESS); + TESTASSERT(test_set_xor_own_set_1() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } diff --git a/lib/test/common/test_security_kdf.cc b/lib/test/common/test_security_kdf.cc new file mode 100644 index 000000000..473ae691b --- /dev/null +++ b/lib/test/common/test_security_kdf.cc @@ -0,0 +1,476 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/common.h" +#include "srsran/common/security.h" +#include "srsran/common/test_common.h" +#include "srsran/srslog/srslog.h" + +using namespace srsran; + +int arrcmp(uint8_t const* const a, uint8_t const* const b, uint32_t len) +{ + uint32_t i = 0; + + for (i = 0; i < len; i++) { + if (a[i] != b[i]) { + return a[i] - b[i]; + } + } + return SRSRAN_SUCCESS; +} + +int test_set_ksg() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + int err_lte = SRSRAN_ERROR; + int err_cmp = SRSRAN_ERROR; + + uint8_t k_enb[] = {0xfe, 0x7d, 0xee, 0x80, 0x8d, 0x7f, 0x3b, 0x88, 0x2a, 0x08, 0x2c, 0xbd, 0xc8, 0x39, 0x0d, 0x12, + 0x9e, 0x5d, 0x28, 0xaf, 0x0e, 0x83, 0x22, 0xeb, 0x57, 0x3a, 0xda, 0x36, 0xf2, 0x1a, 0x5a, 0x89}; + uint8_t sk_gnb_o[32]; + uint16_t scg_counter = 0; + err_lte = srsran::security_generate_sk_gnb(k_enb, scg_counter, sk_gnb_o); + TESTASSERT(err_lte == SRSRAN_SUCCESS); + logger.info(&sk_gnb_o[0], sizeof(sk_gnb_o), "sk gnb o:"); + uint8_t sk_gnb[] = {0x45, 0xcb, 0xc3, 0xf8, 0xa8, 0x11, 0x93, 0xfd, 0x5c, 0x52, 0x29, 0x30, 0x0d, 0x59, 0xed, 0xf8, + 0x12, 0xe9, 0x98, 0xa1, 0x15, 0xec, 0x4e, 0x0c, 0xe9, 0x03, 0xba, 0x89, 0x36, 0x7e, 0x26, 0x28}; + err_cmp = arrcmp(sk_gnb_o, sk_gnb, sizeof(sk_gnb)); + TESTASSERT(err_cmp == 0); + return SRSRAN_SUCCESS; +} + +int test_set_nr_rrc_up() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + int err_lte = SRSRAN_ERROR; + int err_cmp = SRSRAN_ERROR; + + uint8_t sk_gnb[] = {0x45, 0xcb, 0xc3, 0xf8, 0xa8, 0x11, 0x93, 0xfd, 0x5c, 0x52, 0x29, 0x30, 0x0d, 0x59, 0xed, 0xf8, + 0x12, 0xe9, 0x98, 0xa1, 0x15, 0xec, 0x4e, 0x0c, 0xe9, 0x03, 0xba, 0x89, 0x36, 0x7e, 0x26, 0x28}; + + uint8_t sk_gnb_o[32]; + uint8_t k_rrc_enc_o[32]; + uint8_t k_rrc_int_o[32]; + + err_lte = srsran::security_generate_k_nr_rrc(sk_gnb, + srsran::CIPHERING_ALGORITHM_ID_ENUM::CIPHERING_ALGORITHM_ID_128_EEA2, + srsran::INTEGRITY_ALGORITHM_ID_ENUM::INTEGRITY_ALGORITHM_ID_EIA0, + k_rrc_enc_o, + k_rrc_int_o); + + TESTASSERT(err_lte == SRSRAN_SUCCESS); + logger.info(&k_rrc_enc_o[0], sizeof(k_rrc_enc_o), "RRC ENC output:"); + + uint8_t k_rrc_enc[] = {0x52, 0xa9, 0x95, 0xdf, 0xf8, 0x9b, 0xc2, 0x94, 0xbd, 0x89, 0xff, + 0xb1, 0x37, 0xa2, 0x9f, 0x24, 0x66, 0xa0, 0x9e, 0x99, 0x23, 0x86, + 0xc8, 0xd1, 0xdf, 0x78, 0x92, 0x96, 0x4c, 0x6f, 0xb5, 0x22}; + + err_cmp = arrcmp(k_rrc_enc_o, k_rrc_enc, sizeof(k_rrc_enc_o)); + + TESTASSERT(err_cmp == 0); + + uint8_t k_up_enc_o[32]; + uint8_t k_up_int_o[32]; + + err_lte = srsran::security_generate_k_nr_up(sk_gnb, + srsran::CIPHERING_ALGORITHM_ID_ENUM::CIPHERING_ALGORITHM_ID_128_EEA2, + srsran::INTEGRITY_ALGORITHM_ID_ENUM::INTEGRITY_ALGORITHM_ID_EIA0, + k_up_enc_o, + k_up_int_o); + + uint8_t k_up_enc[] = {0x7c, 0xe2, 0x06, 0x70, 0xbb, 0xbc, 0xc5, 0x90, 0x40, 0x87, 0xc0, 0xd4, 0x26, 0x53, 0xc5, 0x40, + 0x15, 0x20, 0x52, 0xd3, 0xdf, 0xbc, 0x3f, 0x05, 0x86, 0x9b, 0x7f, 0x92, 0x00, 0x95, 0xbe, 0x68}; + + logger.info(&k_up_enc_o[0], sizeof(k_up_enc_o), "UP ENC output:"); + + err_cmp = arrcmp(k_up_enc_o, k_up_enc, sizeof(k_up_enc_o)); + TESTASSERT(err_cmp == 0); + return SRSRAN_SUCCESS; +} + +int test_generate_k_asme() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + + uint8_t k_asme_o[32] = {}; + uint8_t ck[] = {0x7b, 0xc1, 0x5d, 0x69, 0x30, 0x9c, 0xf3, 0xec, 0x5d, 0x32, 0x44, 0x04, 0xed, 0xd6, 0xf0, 0xf9}; + uint8_t ik[] = {0xc1, 0x5d, 0x69, 0x30, 0x9c, 0xf3, 0xec, 0x5d, 0x32, 0x44, 0x04, 0xed, 0xd6, 0xf0, 0xf9, 0x7b}; + uint8_t ak[] = {0x5d, 0x69, 0x30, 0x9c, 0xf3, 0xec}; + uint8_t sqn[] = {0x00, 0x00, 0x00, 0x00, 0x12, 0xd9}; + uint16_t mcc = 61441; // MCC 001 + uint16_t mnc = 65281; // MNC 01 + + uint8_t ak_xor_sqn[6]; + for (uint32_t i = 0; i < 6; i++) { + ak_xor_sqn[i] = sqn[i] ^ ak[i]; + } + // Generate K_asme + security_generate_k_asme(ck, ik, ak_xor_sqn, mcc, mnc, k_asme_o); + + uint8_t k_asme[] = {0xd5, 0xef, 0x4d, 0x8f, 0x33, 0x26, 0x69, 0x02, 0x29, 0x5d, 0x42, 0xf3, 0x22, 0xa2, 0xf2, 0xcf, + 0x11, 0xfb, 0x2c, 0xcc, 0x12, 0x4c, 0x09, 0xb4, 0xd8, 0x8d, 0x36, 0x15, 0x97, 0x03, 0x79, 0x90}; + + logger.info(k_asme_o, 32, "K_ASME output:"); + TESTASSERT(arrcmp(k_asme, k_asme_o, sizeof(k_asme_o)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_nas() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_nas_enc_o[32]; + uint8_t k_nas_int_o[32]; + + uint8_t k_asme[] = {0xd5, 0xef, 0x4d, 0x8f, 0x33, 0x26, 0x69, 0x02, 0x29, 0x5d, 0x42, 0xf3, 0x22, 0xa2, 0xf2, 0xcf, + 0x11, 0xfb, 0x2c, 0xcc, 0x12, 0x4c, 0x09, 0xb4, 0xd8, 0x8d, 0x36, 0x15, 0x97, 0x03, 0x79, 0x90}; + + TESTASSERT(srsran::security_generate_k_nas(k_asme, + srsran::CIPHERING_ALGORITHM_ID_ENUM::CIPHERING_ALGORITHM_ID_EEA0, + srsran::INTEGRITY_ALGORITHM_ID_ENUM::INTEGRITY_ALGORITHM_ID_128_EIA1, + k_nas_enc_o, + k_nas_int_o) == SRSRAN_SUCCESS); + + uint8_t k_nas_enc[] = {0xcf, 0x7a, 0x27, 0xbe, 0xab, 0x1e, 0x78, 0xbf, 0x5b, 0xb1, 0xf9, + 0x5b, 0x05, 0x82, 0x9a, 0x72, 0xe4, 0xcf, 0x7f, 0xec, 0xba, 0xd4, + 0xb8, 0xc3, 0x7d, 0xb4, 0xa1, 0x90, 0xe1, 0x19, 0x22, 0x5c}; + logger.info(k_nas_enc_o, 32, "K NAS ENC output:"); + TESTASSERT(arrcmp(k_nas_enc, k_nas_enc_o, sizeof(k_nas_enc_o)) == 0); + + uint8_t k_nas_int[] = {0xb8, 0x55, 0x87, 0x1d, 0x17, 0xd4, 0xa7, 0x17, 0xe9, 0x31, 0xd9, + 0xa6, 0x06, 0x9c, 0x6e, 0x54, 0xb7, 0x94, 0x08, 0xe2, 0x43, 0xdd, + 0x36, 0x7d, 0xc8, 0xc6, 0x39, 0x22, 0x95, 0xb3, 0xb4, 0x51}; + + logger.info(k_nas_int_o, 32, "K NAS INT output:"); + TESTASSERT(arrcmp(k_nas_int, k_nas_int_o, sizeof(k_nas_int_o)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_gnb() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + + as_key_t k_gnb_o; + + as_key_t k_gnb = {0x49, 0x3a, 0x16, 0xc5, 0x8b, 0x77, 0xb6, 0x27, 0xfa, 0x3f, 0x1a, 0xc6, 0x34, 0x4c, 0x18, 0x30, + 0x39, 0xf0, 0x1b, 0xa0, 0xcb, 0x76, 0x36, 0xbb, 0xcc, 0xc4, 0x36, 0x5b, 0x02, 0x3b, 0xd5, 0x62}; + + as_key_t k_amf = {0xd6, 0x55, 0xf1, 0x61, 0x42, 0x03, 0x5d, 0x4d, 0x72, 0xca, 0x39, 0x58, 0x3d, 0x22, 0x8d, 0x2d, + 0xd2, 0xec, 0x0c, 0xa7, 0x92, 0x9a, 0xd0, 0x07, 0xf5, 0x3b, 0x38, 0x2d, 0x05, 0x54, 0x44, 0x05}; + + uint32_t nas_ul_count = 0; + + TESTASSERT(srsran::security_generate_k_gnb(k_amf, nas_ul_count, k_gnb_o) == SRSRAN_SUCCESS); + TESTASSERT(k_gnb_o == k_gnb); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_enb() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_enb_o[32]; + + uint8_t k_asme[] = {0x61, 0x44, 0xc6, 0x81, 0xd1, 0xbe, 0xa9, 0xda, 0xe1, 0xb8, 0xcf, 0x6c, 0xd1, 0x0a, 0x68, 0x63, + 0x41, 0xdb, 0x80, 0x46, 0xa1, 0xe7, 0xa9, 0xab, 0x4d, 0x1e, 0xa0, 0xe3, 0x3c, 0x99, 0x4a, 0xc0}; + uint32_t nas_ul_count = 0; + + TESTASSERT(srsran::security_generate_k_enb(k_asme, nas_ul_count, k_enb_o) == SRSRAN_SUCCESS); + + uint8_t k_enb[] = {0xc4, 0xc7, 0xbc, 0x79, 0x8a, 0xb9, 0x4e, 0x3d, 0x35, 0x4c, 0xd6, 0x60, 0x8e, 0x79, 0xaa, 0x92, + 0xf5, 0x56, 0x9d, 0xf4, 0x65, 0x19, 0x50, 0x78, 0x50, 0x05, 0x1e, 0x36, 0xf0, 0x18, 0xca, 0x5f}; + + logger.info(k_enb, 32, "K ENB output:"); + TESTASSERT(arrcmp(k_enb, k_enb_o, sizeof(k_enb_o)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_rrc_keys() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_rrc_enc_o[32]; + uint8_t k_rrc_int_o[32]; + + uint8_t k_enb[] = {0xc4, 0xc7, 0xbc, 0x79, 0x8a, 0xb9, 0x4e, 0x3d, 0x35, 0x4c, 0xd6, 0x60, 0x8e, 0x79, 0xaa, 0x92, + 0xf5, 0x56, 0x9d, 0xf4, 0x65, 0x19, 0x50, 0x78, 0x50, 0x05, 0x1e, 0x36, 0xf0, 0x18, 0xca, 0x5f}; + + TESTASSERT(srsran::security_generate_k_rrc(k_enb, + srsran::CIPHERING_ALGORITHM_ID_ENUM::CIPHERING_ALGORITHM_ID_EEA0, + srsran::INTEGRITY_ALGORITHM_ID_ENUM::INTEGRITY_ALGORITHM_ID_128_EIA2, + k_rrc_enc_o, + k_rrc_int_o) == SRSRAN_SUCCESS); + + uint8_t k_rrc_enc[] = {0x23, 0xaf, 0xdd, 0x7b, 0x2e, 0x4a, 0x5a, 0x99, 0xc7, 0x78, 0x82, + 0x78, 0x59, 0xcf, 0x45, 0x14, 0x86, 0xa2, 0x75, 0x78, 0x8b, 0x6f, + 0x36, 0xa5, 0xb9, 0xb8, 0x10, 0xf5, 0xd4, 0x72, 0xa2, 0x4b}; + + logger.info(k_rrc_enc_o, 32, "K RRC ENC output:"); + TESTASSERT(arrcmp(k_rrc_enc, k_rrc_enc_o, sizeof(k_rrc_enc_o)) == 0); + + uint8_t k_rrc_int[] = {0x42, 0xf4, 0xf8, 0xc5, 0x85, 0xae, 0x89, 0x05, 0xa5, 0x0d, 0x0f, + 0x32, 0xa9, 0x79, 0xd8, 0xb6, 0xfa, 0x1d, 0x6d, 0x19, 0x40, 0xf1, + 0xd8, 0x79, 0xcf, 0x01, 0x89, 0x34, 0x2a, 0x8d, 0x73, 0xc2}; + + logger.info(k_rrc_int_o, 32, "K RRC INT output:"); + TESTASSERT(arrcmp(k_rrc_int, k_rrc_int_o, sizeof(k_rrc_int_o)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_up_keys() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_up_enc_o[32]; + uint8_t k_up_int_o[32]; + + uint8_t k_enb[] = {0xc4, 0xc7, 0xbc, 0x79, 0x8a, 0xb9, 0x4e, 0x3d, 0x35, 0x4c, 0xd6, 0x60, 0x8e, 0x79, 0xaa, 0x92, + 0xf5, 0x56, 0x9d, 0xf4, 0x65, 0x19, 0x50, 0x78, 0x50, 0x05, 0x1e, 0x36, 0xf0, 0x18, 0xca, 0x5f}; + + TESTASSERT(srsran::security_generate_k_up(k_enb, + srsran::CIPHERING_ALGORITHM_ID_ENUM::CIPHERING_ALGORITHM_ID_EEA0, + srsran::INTEGRITY_ALGORITHM_ID_ENUM::INTEGRITY_ALGORITHM_ID_128_EIA2, + k_up_enc_o, + k_up_int_o) == SRSRAN_SUCCESS); + + uint8_t k_up_enc[] = {0x22, 0xbf, 0xb5, 0x87, 0x61, 0xca, 0x1d, 0xd3, 0xb2, 0x0a, 0x28, 0x1c, 0x7e, 0xab, 0x0c, 0x0b, + 0x9c, 0x3c, 0x92, 0xe1, 0xdd, 0xc0, 0xc8, 0xc5, 0x70, 0x6c, 0xbb, 0x8f, 0x95, 0x5e, 0x82, 0x63}; + + logger.info(k_up_enc_o, 32, "K UP ENC output:"); + TESTASSERT(arrcmp(k_up_enc, k_up_enc_o, sizeof(k_up_enc_o)) == 0); + + uint8_t k_up_int[] = {0x82, 0x0d, 0xcd, 0xf3, 0xb9, 0xc9, 0x4b, 0x32, 0xf8, 0x41, 0xc2, 0x5c, 0xc8, 0x78, 0xaa, 0x07, + 0x77, 0x16, 0xc7, 0x83, 0xa5, 0x3f, 0xd3, 0xee, 0x58, 0x2f, 0xc5, 0x69, 0xe9, 0xc3, 0x3c, 0xa7}; + + logger.info(k_up_int_o, 32, "K UP INT output:"); + TESTASSERT(arrcmp(k_up_int, k_up_int_o, sizeof(k_up_int_o)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_enb_star() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_enb_star_o[32]; + + uint8_t k_enb[] = {0xc4, 0xc7, 0xbc, 0x79, 0x8a, 0xb9, 0x4e, 0x3d, 0x35, 0x4c, 0xd6, 0x60, 0x8e, 0x79, 0xaa, 0x92, + 0xf5, 0x56, 0x9d, 0xf4, 0x65, 0x19, 0x50, 0x78, 0x50, 0x05, 0x1e, 0x36, 0xf0, 0x18, 0xca, 0x5f}; + + TESTASSERT(srsran::security_generate_k_enb_star(k_enb, 32, 561, k_enb_star_o) == SRSRAN_SUCCESS); + + uint8_t k_enb_star[] = {0x6f, 0x04, 0x54, 0x57, 0x7f, 0x55, 0x5c, 0xa7, 0x6f, 0x44, 0x9c, + 0x2a, 0xc1, 0xda, 0x0f, 0x4f, 0xb7, 0xde, 0x4b, 0x94, 0x40, 0x11, + 0x0f, 0xfb, 0xd1, 0x2f, 0x3a, 0x5d, 0x4d, 0xe1, 0x73, 0x50}; + + logger.info(k_enb_star_o, 32, "K ENB STAR:"); + TESTASSERT(arrcmp(k_enb_star_o, k_enb_star, sizeof(k_enb_star)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_nh() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t nh_o[32]; + + uint8_t k_asme[] = {0xd5, 0xef, 0x4d, 0x8f, 0x33, 0x26, 0x69, 0x02, 0x29, 0x5d, 0x42, 0xf3, 0x22, 0xa2, 0xf2, 0xcf, + 0x11, 0xfb, 0x2c, 0xcc, 0x12, 0x4c, 0x09, 0xb4, 0xd8, 0x8d, 0x36, 0x15, 0x97, 0x03, 0x79, 0x90}; + + uint8_t sync[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12}; + + TESTASSERT(srsran::security_generate_nh(k_asme, sync, nh_o) == SRSRAN_SUCCESS); + + uint8_t nh[] = {0xc8, 0x55, 0x86, 0x64, 0x13, 0x07, 0x1b, 0x74, 0xab, 0xa0, 0x1c, 0x13, 0x2b, 0x94, 0x3e, 0x76, + 0x98, 0xaa, 0x5c, 0x11, 0x76, 0x0d, 0x9b, 0xb1, 0x16, 0x2a, 0xb3, 0x13, 0xb1, 0x1c, 0x60, 0x51}; + + logger.info(nh_o, 32, "NH:"); + TESTASSERT(arrcmp(nh_o, nh, sizeof(nh)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_res_star() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t res_star_o[16]; + + uint8_t ck[] = {0x3c, 0xba, 0x90, 0x25, 0x75, 0xed, 0x80, 0xcb, 0xfa, 0x36, 0x25, 0xaf, 0xf0, 0x9d, 0xaf, 0xfc}; + uint8_t ik[] = {0xba, 0x90, 0x25, 0x75, 0xed, 0x80, 0xcb, 0xfa, 0x36, 0x25, 0xaf, 0xf0, 0x9d, 0xaf, 0xfc, 0x3c}; + uint8_t rand[] = {0xfc, 0x2d, 0x98, 0xa3, 0x61, 0x20, 0x8b, 0xf7, 0x43, 0x63, 0x9c, 0x9e, 0x63, 0x2d, 0x73, 0x50}; + uint8_t res[] = {0xfc, 0x3c, 0xba, 0x90, 0x25, 0x75, 0xed, 0x80}; + std::string ssn = "5G:mnc001.mcc001.3gppnetwork.org"; + + TESTASSERT(srsran::security_generate_res_star(ck, ik, ssn.c_str(), rand, res, sizeof(res), res_star_o) == + SRSRAN_SUCCESS); + + uint8_t res_star[] = {0xb0, 0xe3, 0x5b, 0x23, 0xdb, 0xd7, 0xa1, 0x8c, 0x84, 0x8b, 0xfa, 0xd9, 0x11, 0x35, 0xe3, 0xfd}; + + logger.info(res_star_o, 16, "RES STAR:"); + TESTASSERT(arrcmp(res_star_o, res_star, sizeof(res_star)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_ausf() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_ausf_o[32]; + + uint8_t ck[] = {0x56, 0x1e, 0x05, 0xef, 0xbd, 0xf2, 0xef, 0xeb, 0x2d, 0x55, 0x8f, 0x04, 0x1c, 0x53, 0xc4, 0x45}; + uint8_t ik[] = {0x01, 0xe0, 0xf2, 0xf5, 0x53, 0x54, 0x31, 0x31, 0x2d, 0x57, 0x27, 0x98, 0x14, 0xcf, 0xcd, 0x89}; + uint8_t ak_xor_sqn[] = {0x30, 0x5e, 0xb0, 0x6b, 0x73, 0x07}; + std::string ssn = "5G:mnc070.mcc901.3gppnetwork.org"; + + TESTASSERT(srsran::security_generate_k_ausf(ck, ik, ak_xor_sqn, ssn.c_str(), k_ausf_o) == SRSRAN_SUCCESS); + + uint8_t k_ausf[] = {0xa2, 0xce, 0xb2, 0x0f, 0x79, 0x28, 0xbf, 0x15, 0x4d, 0x4b, 0x54, 0x8a, 0xee, 0x6d, 0x10, 0xa9, + 0x76, 0x01, 0x84, 0x7f, 0xd7, 0x2d, 0x2b, 0xc9, 0x01, 0x98, 0x2c, 0x08, 0x6e, 0xa0, 0xf3, 0x46}; + // TODO check the out put with k AMF + logger.info(k_ausf_o, 32, "K AUSF:"); + TESTASSERT(arrcmp(k_ausf_o, k_ausf, sizeof(k_ausf)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_seaf() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_seaf_o[32]; + + uint8_t k_ausf[] = {0xd9, 0xc8, 0xff, 0x91, 0xb6, 0x9e, 0x25, 0x0e, 0x25, 0x6e, 0x92, 0x46, 0x6a, 0xcf, 0x80, 0xa1, + 0xd8, 0x2b, 0x65, 0xf0, 0x94, 0xb7, 0xc0, 0x71, 0x19, 0x9c, 0x03, 0x12, 0xe0, 0x67, 0xff, 0x3b}; + std::string ssn = "5G:mnc070.mcc901.3gppnetwork.org"; + + TESTASSERT(srsran::security_generate_k_seaf(k_ausf, ssn.c_str(), k_seaf_o) == SRSRAN_SUCCESS); + + uint8_t k_seaf[] = {0x6c, 0x50, 0xbf, 0xa5, 0xf3, 0x2a, 0x89, 0xad, 0xe1, 0xee, 0x6c, 0x70, 0x7d, 0xe6, 0xdc, 0xfe, + 0xa0, 0x79, 0x0a, 0xfb, 0x6d, 0x14, 0xf9, 0xe5, 0x59, 0x43, 0xae, 0xda, 0x58, 0x33, 0x45, 0x48}; + + logger.info(k_seaf_o, 32, "K SEAF:"); + TESTASSERT(arrcmp(k_seaf_o, k_seaf, sizeof(k_seaf)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_amf() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_amf_o[32]; + + uint8_t k_seaf[] = {0x6c, 0x50, 0xbf, 0xa5, 0xf3, 0x2a, 0x89, 0xad, 0xe1, 0xee, 0x6c, 0x70, 0x7d, 0xe6, 0xdc, 0xfe, + 0xa0, 0x79, 0x0a, 0xfb, 0x6d, 0x14, 0xf9, 0xe5, 0x59, 0x43, 0xae, 0xda, 0x58, 0x33, 0x45, 0x48}; + + uint8_t abba[] = {0x00, 0x00}; + std::string supi = "901700000021309"; + + TESTASSERT(srsran::security_generate_k_amf(k_seaf, supi.c_str(), abba, sizeof(abba), k_amf_o) == SRSRAN_SUCCESS); + + uint8_t k_amf[] = {0x46, 0x23, 0x7e, 0xf0, 0x51, 0xd6, 0xa1, 0x6a, 0x39, 0x27, 0x52, 0x5f, 0xa6, 0x5c, 0xc3, 0x95, + 0x44, 0xcd, 0xfd, 0x79, 0x90, 0xb9, 0x46, 0xad, 0x79, 0x65, 0x30, 0xe6, 0xd7, 0x87, 0xa0, 0xda}; + + logger.info(k_amf_o, 32, "K AMF:"); + TESTASSERT(arrcmp(k_amf_o, k_amf, sizeof(k_amf)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_k_amf_2() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_amf_o[32]; + + uint8_t k_seaf[] = {0x30, 0x2a, 0xb7, 0xc9, 0x4f, 0xae, 0x97, 0xe0, 0x43, 0xb3, 0x70, 0xd3, 0xeb, 0x1f, 0x54, 0x68, + 0xf6, 0xa1, 0xd4, 0x55, 0xd6, 0x56, 0xa4, 0xca, 0xb3, 0x51, 0xea, 0xcf, 0xb9, 0xf7, 0xe1, 0x36}; + + uint8_t abba[] = {0x00, 0x00}; + std::string supi = "001010123456780"; + + TESTASSERT(srsran::security_generate_k_amf(k_seaf, supi.c_str(), abba, sizeof(abba), k_amf_o) == SRSRAN_SUCCESS); + + uint8_t k_amf[] = {0x8f, 0x2a, 0x5d, 0x6c, 0x12, 0x60, 0x8d, 0x8a, 0x0a, 0x8d, 0x33, 0x0a, 0x71, 0x9d, 0xee, 0x07, + 0x22, 0x6b, 0x47, 0x41, 0x7b, 0x66, 0x1a, 0xf5, 0x84, 0x90, 0xea, 0xb0, 0x7a, 0xa4, 0x37, 0xa4}; + + logger.info(k_amf_o, 32, "K AMF:"); + TESTASSERT(arrcmp(k_amf_o, k_amf, sizeof(k_amf)) == 0); + + return SRSRAN_SUCCESS; +} + +int test_generate_nas_5g() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + uint8_t k_nas_int_o[32]; + uint8_t k_nas_enc_o[32]; + + uint8_t k_amf[] = {0x7d, 0x86, 0x18, 0x52, 0xb4, 0x2a, 0x86, 0xb5, 0x96, 0xfe, 0x22, 0xc8, 0xf5, 0x0b, 0x9b, 0x89, + 0x5a, 0x1e, 0x21, 0x71, 0x6b, 0x61, 0xb8, 0xd1, 0x22, 0x78, 0x5e, 0x25, 0xba, 0xfc, 0x0d, 0x07}; + + TESTASSERT(srsran::security_generate_k_nas_5g(k_amf, + srsran::CIPHERING_ALGORITHM_ID_ENUM::CIPHERING_ALGORITHM_ID_EEA0, + srsran::INTEGRITY_ALGORITHM_ID_ENUM::INTEGRITY_ALGORITHM_ID_128_EIA2, + k_nas_enc_o, + k_nas_int_o) == SRSRAN_SUCCESS); + + uint8_t k_nas_enc[] = {0x83, 0x85, 0x6a, 0x01, 0x81, 0x59, 0x55, 0x21, 0xc9, 0xd3, 0x2a, + 0x19, 0x3f, 0x59, 0xc8, 0xa8, 0xfc, 0x7e, 0x99, 0xb9, 0x8b, 0x8c, + 0x04, 0x20, 0x1f, 0x4f, 0x9d, 0x81, 0x43, 0x3b, 0x6b, 0x38}; + uint8_t k_nas_int[] = {0x25, 0xc5, 0x8e, 0x27, 0xe7, 0xd2, 0xc4, 0x95, 0xf3, 0xd7, 0x35, + 0x29, 0x3a, 0x82, 0xbf, 0xfa, 0xa5, 0x3e, 0xe8, 0x84, 0xb9, 0x8d, + 0x36, 0x6b, 0x02, 0x89, 0x23, 0x1e, 0xc4, 0x19, 0x1e, 0xd3}; + + logger.info(k_nas_enc_o, 32, "k_nas_enc_o:"); + TESTASSERT(arrcmp(k_nas_enc_o, k_nas_enc, sizeof(k_nas_enc)) == 0); + logger.info(k_nas_int_o, 32, "k_nas_int_o:"); + TESTASSERT(arrcmp(k_nas_int_o, k_nas_int, sizeof(k_nas_int)) == 0); + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(256); + + srslog::init(); + + TESTASSERT(test_generate_k_asme() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_nas() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_enb() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_rrc_keys() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_up_keys() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_enb_star() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_nh() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_gnb() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_res_star() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_ausf() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_seaf() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_amf() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_k_amf_2() == SRSRAN_SUCCESS); + TESTASSERT(test_generate_nas_5g() == SRSRAN_SUCCESS); + TESTASSERT(test_set_ksg() == SRSRAN_SUCCESS); + TESTASSERT(test_set_nr_rrc_up() == SRSRAN_SUCCESS); + + return SRSRAN_SUCCESS; +} diff --git a/lib/test/common/timeout_test.cc b/lib/test/common/timeout_test.cc index 69bc198e8..4180521ea 100644 --- a/lib/test/common/timeout_test.cc +++ b/lib/test/common/timeout_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/common/timer_test.cc b/lib/test/common/timer_test.cc index 833bc7794..f2abc9274 100644 --- a/lib/test/common/timer_test.cc +++ b/lib/test/common/timer_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,8 +19,8 @@ * */ -#include "srsran/common/test_common.h" #include "srsran/common/timers.h" +#include "srsran/support/srsran_test.h" #include #include #include @@ -28,7 +28,9 @@ using namespace srsran; -int timers_test1() +static_assert(timer_handler::max_timer_duration() == 1073741823, "Invalid max duration"); + +void timers_test1() { timer_handler timers; uint32_t dur = 5; @@ -117,8 +119,6 @@ int timers_test1() } // TEST: timer dtor is called and removes "timer" from "timers" TESTASSERT(timers.nof_timers() == 0); - - return SRSRAN_SUCCESS; } /** @@ -126,7 +126,7 @@ int timers_test1() * - calling stop() early, forbids the timer from getting expired * - calling stop() after timer has expired should be a noop */ -int timers_test2() +void timers_test2() { timer_handler timers; uint32_t duration = 2; @@ -152,15 +152,13 @@ int timers_test2() // TEST 2: call utimer.stop() after it expires and assert it is still expired utimer2.stop(); TESTASSERT(utimer2.is_expired()); - - return SRSRAN_SUCCESS; } /** * Description: * - setting a new duration while the timer is already running should not stop timer, and should extend timeout */ -int timers_test3() +void timers_test3() { timer_handler timers; uint32_t duration = 5; @@ -183,24 +181,26 @@ int timers_test3() } timers.step_all(); TESTASSERT(not utimer.is_running() and utimer.is_expired()); - - return SRSRAN_SUCCESS; } struct timers_test4_ctxt { - std::vector timers; - srsran::tti_sync_cv tti_sync1; - srsran::tti_sync_cv tti_sync2; - const uint32_t duration = 1000; + std::vector timers; + srsran::tti_sync_cv tti_sync1; + srsran::tti_sync_cv tti_sync2; + const uint32_t duration = 1000; }; static void timers2_test4_thread(timers_test4_ctxt* ctx) { - std::mt19937 mt19937(4); + std::random_device rd; + std::mt19937 mt19937(rd()); std::uniform_real_distribution real_dist(0.0f, 1.0f); for (uint32_t d = 0; d < ctx->duration; d++) { // make random events for (uint32_t i = 1; i < ctx->timers.size(); i++) { + // ensure the getters always return reasonable values + TESTASSERT(ctx->timers[i].time_elapsed() <= ctx->duration); + if (0.1f > real_dist(mt19937)) { ctx->timers[i].run(); } @@ -221,85 +221,89 @@ static void timers2_test4_thread(timers_test4_ctxt* ctx) } } -int timers_test4() +void timers_test4() { - timers_test4_ctxt* ctx = new timers_test4_ctxt; timer_handler timers; + timers_test4_ctxt ctx; uint32_t nof_timers = 32; std::mt19937 mt19937(4); std::uniform_real_distribution real_dist(0.0f, 1.0f); // Generate all timers and start them for (uint32_t i = 0; i < nof_timers; i++) { - ctx->timers.push_back(timers.get_unique_timer()); - ctx->timers[i].set(ctx->duration); - ctx->timers[i].run(); + ctx.timers.push_back(timers.get_unique_timer()); + ctx.timers[i].set(ctx.duration); + ctx.timers[i].run(); } - // Create side thread - std::thread thread(timers2_test4_thread, ctx); + /* ========== multithreaded region begin =========== */ - for (uint32_t d = 0; d < ctx->duration; d++) { + // Create side thread + std::thread thread(timers2_test4_thread, &ctx); + + for (uint32_t d = 0; d < ctx.duration; d++) { // make random events for (uint32_t i = 1; i < nof_timers; i++) { + // ensure the getters always return reasonable values + TESTASSERT(ctx.timers[i].time_elapsed() <= ctx.duration); + if (0.1f > real_dist(mt19937)) { - ctx->timers[i].run(); + ctx.timers[i].run(); // restart run } if (0.1f > real_dist(mt19937)) { - ctx->timers[i].stop(); + ctx.timers[i].stop(); // stop run } if (0.1f > real_dist(mt19937)) { - ctx->timers[i].set(static_cast(ctx->duration * real_dist(mt19937))); - ctx->timers[i].run(); + ctx.timers[i].set(static_cast(ctx.duration * real_dist(mt19937))); + ctx.timers[i].run(); // start run with new duration } } - // first times, does not have event, it shall keep running - TESTASSERT(ctx->timers[0].is_running()); + // first timer does not get updated, so it shall keep running + TESTASSERT(ctx.timers[0].is_running()); // Increment time timers.step_all(); // wait second thread to finish events - ctx->tti_sync1.wait(); + ctx.tti_sync1.wait(); // assert no timer got wrong values for (uint32_t i = 0; i < nof_timers; i++) { - if (ctx->timers[i].is_running()) { - TESTASSERT(ctx->timers[i].time_elapsed() <= ctx->timers[i].duration()); + if (ctx.timers[i].is_running()) { + TESTASSERT(ctx.timers[i].time_elapsed() <= ctx.timers[i].duration()); + TESTASSERT(ctx.timers[i].duration() <= ctx.duration); } } // Start new TTI - ctx->tti_sync2.increase(); + ctx.tti_sync2.increase(); } // Finish asynchronous thread thread.join(); + /* ========== multithreaded region end =========== */ + // First timer should have expired - TESTASSERT(ctx->timers[0].is_expired()); - TESTASSERT(not ctx->timers[0].is_running()); + TESTASSERT(ctx.timers[0].is_expired()); + TESTASSERT(not ctx.timers[0].is_running()); // Run for the maximum period - for (uint32_t d = 0; d < ctx->duration; d++) { + for (uint32_t d = 0; d < ctx.duration; d++) { timers.step_all(); } // No timer should be running for (uint32_t i = 0; i < nof_timers; i++) { - TESTASSERT(not ctx->timers[i].is_running()); + TESTASSERT(not ctx.timers[i].is_running()); } - - delete ctx; - - return SRSRAN_SUCCESS; } /** * Description: Delaying a callback using the timer_handler */ -int timers_test5() +void timers_test5() { timer_handler timers; TESTASSERT(timers.nof_timers() == 0); @@ -351,25 +355,23 @@ int timers_test5() TESTASSERT(timers.nof_timers() == 1); TESTASSERT(vals.size() == 3); TESTASSERT(vals[2] == 3); - - return SRSRAN_SUCCESS; } /** * Description: Check if erasure of a running timer is safe */ -int timers_test6() +void timers_test6() { timer_handler timers; std::vector vals; - // Event: Add a timer that gets erased 1 tti after. + // Event: Add a timer that gets erased 1 tti after, and before expiring. { timer_handler::unique_timer t = timers.get_unique_timer(); t.set(2, [&vals](uint32_t tid) { vals.push_back(1); }); t.run(); - TESTASSERT(timers.nof_running_timers() == 1); + TESTASSERT(timers.nof_running_timers() == 1 and t.duration() == 2 and t.is_running()); timers.step_all(); } TESTASSERT(timers.nof_running_timers() == 0); @@ -384,7 +386,7 @@ int timers_test6() timer_handler::unique_timer t = timers.get_unique_timer(); t.set(2, [&vals](uint32_t tid) { vals.push_back(2); }); t.run(); - TESTASSERT(timers.nof_running_timers() == 1); + TESTASSERT(timers.nof_running_timers() == 1 and t.is_running()); timers.step_all(); TESTASSERT(t.time_elapsed() == 1); } @@ -396,8 +398,6 @@ int timers_test6() // TEST: The second timer's callback should be the one being called, and should be called only once timers.step_all(); TESTASSERT(vals.size() == 1 and vals[0] == 3); - - return SRSRAN_SUCCESS; } /** @@ -405,7 +405,7 @@ int timers_test6() * - check if timer update is safe when its new updated wheel position matches the previous wheel position * - multime timers can exist in the same wheel position */ -int timers_test7() +void timers_test7() { timer_handler timers; size_t wheel_size = timer_handler::get_wheel_size(); @@ -446,19 +446,17 @@ int timers_test7() TESTASSERT(not t2.is_expired() and t2.is_running()); TESTASSERT(t3.is_expired() and not t3.is_running()); TESTASSERT(timers.nof_running_timers() == 1 and timers.nof_timers() == 3); - - return SRSRAN_SUCCESS; } int main() { - TESTASSERT(timers_test1() == SRSRAN_SUCCESS); - TESTASSERT(timers_test2() == SRSRAN_SUCCESS); - TESTASSERT(timers_test3() == SRSRAN_SUCCESS); - TESTASSERT(timers_test4() == SRSRAN_SUCCESS); - TESTASSERT(timers_test5() == SRSRAN_SUCCESS); - TESTASSERT(timers_test6() == SRSRAN_SUCCESS); - TESTASSERT(timers_test7() == SRSRAN_SUCCESS); + timers_test1(); + timers_test2(); + timers_test3(); + timers_test4(); + timers_test5(); + timers_test6(); + timers_test7(); printf("Success\n"); return 0; } diff --git a/lib/test/common/tti_point_test.cc b/lib/test/common/tti_point_test.cc index 9522da9d7..59dbdb3d4 100644 --- a/lib/test/common/tti_point_test.cc +++ b/lib/test/common/tti_point_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,12 +19,13 @@ * */ -#include "srsran/common/test_common.h" +#include "srsran/common/slot_point.h" #include "srsran/common/tti_point.h" +#include "srsran/support/srsran_test.h" using srsran::tti_point; -int test_tti_type() +void test_tti_type() { // TEST: constructors tti_point tti1; @@ -71,13 +72,55 @@ int test_tti_type() TESTASSERT(tti_point{1u - 2u} == tti_point{10239}); TESTASSERT(tti_point{1u - 100u} == tti_point{10141}); TESTASSERT(tti_point{10239u + 3u} == tti_point{2}); +} - return SRSRAN_SUCCESS; +void test_nr_slot_type() +{ + // TEST: constructors + srsran::slot_point slot1; + TESTASSERT(not slot1.valid()); + srsran::slot_point slot2{0, 1, 5}; + TESTASSERT(slot2.valid() and slot2.numerology_idx() == 0 and slot2.slot_idx() == 5 and slot2.slot_idx() == 5 and + slot2.sfn() == 1); + srsran::slot_point slot3{slot2}; + TESTASSERT(slot3 == slot2); + + // TEST: comparison and difference operators + slot1 = srsran::slot_point{0, 1, 5}; + slot2 = srsran::slot_point{0, 1, 5}; + TESTASSERT(slot1 == slot2 and slot1 <= slot2 and slot1 >= slot2); + slot1++; + TESTASSERT(slot1 != slot2 and slot1 >= slot2 and slot1 > slot2 and slot2 < slot1 and slot2 <= slot1); + TESTASSERT(slot1 - slot2 == 1 and slot2 - slot1 == -1); + slot1 = srsran::slot_point{0, 2, 5}; + TESTASSERT(slot1 != slot2 and slot1 >= slot2 and slot1 > slot2 and slot2 < slot1 and slot2 <= slot1); + TESTASSERT(slot1 - slot2 == 10 and slot2 - slot1 == -10); + slot1 = srsran::slot_point{0, 1023, 5}; + TESTASSERT(slot1 != slot2 and slot1 <= slot2 and slot1 < slot2 and slot2 > slot1 and slot2 >= slot1); + TESTASSERT(slot1 - slot2 == -20 and slot2 - slot1 == 20); + + // TEST: increment/decrement operators + slot1 = srsran::slot_point{0, 1, 5}; + slot2 = srsran::slot_point{0, 1, 5}; + TESTASSERT(slot1++ == slot2); + TESTASSERT(slot2 + 1 == slot1); + TESTASSERT(++slot2 == slot1); + slot1 = srsran::slot_point{0, 1, 5}; + slot2 = srsran::slot_point{0, 1, 5}; + TESTASSERT(slot1 - 100 == slot2 - 100); + TESTASSERT(((slot1 - 100000) + 100000) == slot1); + TESTASSERT((slot1 - 10240) == slot1); + TESTASSERT((slot1 - 100).slot_idx() == 5 and (slot1 - 100).sfn() == 1015); + TESTASSERT(((slot1 - 100) + 100) == slot1); + TESTASSERT(((slot1 - 1) + 1) == slot1); + + fmt::print("[ {}]", slot1); } int main() { srslog::init(); - TESTASSERT(test_tti_type() == SRSRAN_SUCCESS); + test_tti_type(); + test_nr_slot_type(); return 0; } diff --git a/lib/test/pdcp/CMakeLists.txt b/lib/test/pdcp/CMakeLists.txt new file mode 100644 index 000000000..59a2a1d94 --- /dev/null +++ b/lib/test/pdcp/CMakeLists.txt @@ -0,0 +1,55 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(CTEST_LABELS "lib;pdcp") + +add_executable(pdcp_nr_test_tx pdcp_nr_test_tx.cc) +target_link_libraries(pdcp_nr_test_tx srsran_pdcp srsran_common) +add_nr_test(pdcp_nr_test_tx pdcp_nr_test_tx) + +add_executable(pdcp_nr_test_rx pdcp_nr_test_rx.cc) +target_link_libraries(pdcp_nr_test_rx srsran_pdcp srsran_common) +add_nr_test(pdcp_nr_test_rx pdcp_nr_test_rx) + +add_executable(pdcp_nr_test_discard_sdu pdcp_nr_test_discard_sdu.cc) +target_link_libraries(pdcp_nr_test_discard_sdu srsran_pdcp srsran_common ${ATOMIC_LIBS}) +add_nr_test(pdcp_nr_test_discard_sdu pdcp_nr_test_discard_sdu) + +add_executable(pdcp_lte_test_rx pdcp_lte_test_rx.cc) +target_link_libraries(pdcp_lte_test_rx srsran_pdcp srsran_common) +add_test(pdcp_lte_test_rx pdcp_lte_test_rx) + +add_executable(pdcp_lte_test_discard_sdu pdcp_lte_test_discard_sdu.cc) +target_link_libraries(pdcp_lte_test_discard_sdu srsran_pdcp srsran_common) +add_test(pdcp_lte_test_discard_sdu pdcp_lte_test_discard_sdu) + +add_executable(pdcp_lte_test_status_report pdcp_lte_test_status_report.cc) +target_link_libraries(pdcp_lte_test_status_report srsran_pdcp srsran_common) +add_test(pdcp_lte_test_status_report pdcp_lte_test_status_report) + +######################################################################## +# Option to run command after build (useful for remote builds) +######################################################################## +if (NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "Added custom post-build command: ${BUILD_CMD}") + add_custom_command(TARGET ip_test POST_BUILD COMMAND ${BUILD_CMD}) +else(NOT ${BUILD_CMD} STREQUAL "") + message(STATUS "No post-build command defined") +endif (NOT ${BUILD_CMD} STREQUAL "") diff --git a/lib/test/upper/pdcp_base_test.h b/lib/test/pdcp/pdcp_base_test.h similarity index 96% rename from lib/test/upper/pdcp_base_test.h rename to lib/test/pdcp/pdcp_base_test.h index a53d0e75c..52c770aae 100644 --- a/lib/test/upper/pdcp_base_test.h +++ b/lib/test/pdcp/pdcp_base_test.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -64,6 +64,8 @@ public: logger.info("Discard_count=%" PRIu64 "", discard_count); } + bool is_suspended(uint32_t lcid) { return false; } + uint64_t rx_count = 0; uint64_t discard_count = 0; @@ -84,6 +86,7 @@ public: void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) {} void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {} void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} + void notify_pdcp_integrity_error(uint32_t lcid) {} const char* get_rb_name(uint32_t lcid) { return "None"; } diff --git a/lib/test/upper/pdcp_lte_test.h b/lib/test/pdcp/pdcp_lte_test.h similarity index 99% rename from lib/test/upper/pdcp_lte_test.h rename to lib/test/pdcp/pdcp_lte_test.h index 01848d9b1..4ea781ab2 100644 --- a/lib/test/upper/pdcp_lte_test.h +++ b/lib/test/pdcp/pdcp_lte_test.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/upper/pdcp_lte_test_discard_sdu.cc b/lib/test/pdcp/pdcp_lte_test_discard_sdu.cc similarity index 99% rename from lib/test/upper/pdcp_lte_test_discard_sdu.cc rename to lib/test/pdcp/pdcp_lte_test_discard_sdu.cc index 6451cbf55..ace67643f 100644 --- a/lib/test/upper/pdcp_lte_test_discard_sdu.cc +++ b/lib/test/pdcp/pdcp_lte_test_discard_sdu.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/upper/pdcp_lte_test_rx.cc b/lib/test/pdcp/pdcp_lte_test_rx.cc similarity index 99% rename from lib/test/upper/pdcp_lte_test_rx.cc rename to lib/test/pdcp/pdcp_lte_test_rx.cc index c0bb3ecad..1645541e6 100644 --- a/lib/test/upper/pdcp_lte_test_rx.cc +++ b/lib/test/pdcp/pdcp_lte_test_rx.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/upper/pdcp_lte_test_status_report.cc b/lib/test/pdcp/pdcp_lte_test_status_report.cc similarity index 99% rename from lib/test/upper/pdcp_lte_test_status_report.cc rename to lib/test/pdcp/pdcp_lte_test_status_report.cc index 25b066ebf..0bea22a9a 100644 --- a/lib/test/upper/pdcp_lte_test_status_report.cc +++ b/lib/test/pdcp/pdcp_lte_test_status_report.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/upper/pdcp_nr_test.h b/lib/test/pdcp/pdcp_nr_test.h similarity index 94% rename from lib/test/upper/pdcp_nr_test.h rename to lib/test/pdcp/pdcp_nr_test.h index 7417a6769..95d7f7741 100644 --- a/lib/test/upper/pdcp_nr_test.h +++ b/lib/test/pdcp/pdcp_nr_test.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,18 +66,18 @@ uint8_t sdu1[] = {0x18, 0xe2}; uint8_t sdu2[] = {0xde, 0xad}; // Test PDUs for rx (generated from SDU1) -uint8_t pdu1_count0_snlen12[] = {0x80, 0x00, 0x8f, 0xe3, 0xe0, 0xdf, 0x82, 0x92}; -uint8_t pdu1_count2048_snlen12[] = {0x88, 0x00, 0x8d, 0x2c, 0x47, 0x5e, 0xb1, 0x5b}; -uint8_t pdu1_count4096_snlen12[] = {0x80, 0x00, 0x97, 0xbe, 0xa3, 0x32, 0xfa, 0x61}; -uint8_t pdu1_count4294967295_snlen12[] = {0x8f, 0xff, 0x1e, 0x47, 0xe6, 0x86, 0x28, 0x6c}; -uint8_t pdu1_count0_snlen18[] = {0x80, 0x00, 0x00, 0x8f, 0xe3, 0xe0, 0xdf, 0x82, 0x92}; -uint8_t pdu1_count131072_snlen18[] = {0x82, 0x00, 0x00, 0x15, 0x01, 0xf4, 0xb0, 0xfc, 0xc5}; -uint8_t pdu1_count262144_snlen18[] = {0x80, 0x00, 0x00, 0xc2, 0x47, 0xa8, 0xdd, 0xc0, 0x73}; -uint8_t pdu1_count4294967295_snlen18[] = {0x83, 0xff, 0xff, 0x1e, 0x47, 0xe6, 0x86, 0x28, 0x6c}; +uint8_t pdu1_count0_snlen12[] = {0x80, 0x00, 0x8f, 0xe3, 0xc7, 0x1b, 0xad, 0x14}; +uint8_t pdu1_count2048_snlen12[] = {0x88, 0x00, 0x8d, 0x2c, 0xe5, 0x38, 0xc0, 0x42}; +uint8_t pdu1_count4096_snlen12[] = {0x80, 0x00, 0x97, 0xbe, 0xee, 0x62, 0xf5, 0xe0}; +uint8_t pdu1_count4294967295_snlen12[] = {0x8f, 0xff, 0x1e, 0x47, 0xa9, 0x55, 0xa9, 0xd8}; +uint8_t pdu1_count0_snlen18[] = {0x80, 0x00, 0x00, 0x8f, 0xe3, 0x37, 0x33, 0xd5, 0x64}; +uint8_t pdu1_count131072_snlen18[] = {0x82, 0x00, 0x00, 0x15, 0x01, 0x99, 0x97, 0xe0, 0x4e}; +uint8_t pdu1_count262144_snlen18[] = {0x80, 0x00, 0x00, 0xc2, 0x47, 0xc2, 0xee, 0x46, 0xd9}; +uint8_t pdu1_count4294967295_snlen18[] = {0x83, 0xff, 0xff, 0x1e, 0x47, 0x78, 0xb8, 0x7a, 0x9f}; // Test PDUs for rx (generated from SDU2) -uint8_t pdu2_count1_snlen12[] = {0x80, 0x01, 0x5e, 0x3d, 0x64, 0xaf, 0xac, 0x7c}; -uint8_t pdu2_count1_snlen18[] = {0x80, 0x00, 0x01, 0x5e, 0x3d, 0x64, 0xaf, 0xac, 0x7c}; +uint8_t pdu2_count1_snlen12[] = {0x80, 0x01, 0x5e, 0x3d, 0x70, 0x6a, 0xa4, 0x90}; +uint8_t pdu2_count1_snlen18[] = {0x80, 0x00, 0x01, 0x5e, 0x3d, 0x93, 0xfe, 0xcc, 0x2e}; // This is the normal initial state. All state variables are set to zero pdcp_initial_state normal_init_state = {}; diff --git a/lib/test/upper/pdcp_nr_test_discard_sdu.cc b/lib/test/pdcp/pdcp_nr_test_discard_sdu.cc similarity index 98% rename from lib/test/upper/pdcp_nr_test_discard_sdu.cc rename to lib/test/pdcp/pdcp_nr_test_discard_sdu.cc index 3147f7497..0dcdb8e30 100644 --- a/lib/test/upper/pdcp_nr_test_discard_sdu.cc +++ b/lib/test/pdcp/pdcp_nr_test_discard_sdu.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/upper/pdcp_nr_test_rx.cc b/lib/test/pdcp/pdcp_nr_test_rx.cc similarity index 56% rename from lib/test/upper/pdcp_nr_test_rx.cc rename to lib/test/pdcp/pdcp_nr_test_rx.cc index 832790375..e7b0890ef 100644 --- a/lib/test/upper/pdcp_nr_test_rx.cc +++ b/lib/test/pdcp/pdcp_nr_test_rx.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,48 +22,60 @@ #include /* - * Genric function to test reception of in-sequence packets + * Generic class to test reception of in-sequence packets */ -int test_rx(std::vector events, - const pdcp_initial_state& init_state, - uint8_t pdcp_sn_len, - uint32_t n_sdus_exp, - const srsran::unique_byte_buffer_t& sdu_exp, - srslog::basic_logger& logger) - +class test_rx_helper { - srsran::pdcp_config_t cfg_rx = {1, - srsran::PDCP_RB_IS_DRB, - srsran::SECURITY_DIRECTION_DOWNLINK, - srsran::SECURITY_DIRECTION_UPLINK, - pdcp_sn_len, - srsran::pdcp_t_reordering_t::ms500, - srsran::pdcp_discard_timer_t::infinity, - false, - srsran::srsran_rat_t::nr}; +public: + pdcp_nr_test_helper pdcp_hlp_rx; + srsran::pdcp_entity_nr& pdcp_rx; + gw_dummy& gw_rx; + srsue::stack_test_dummy& stack; + srslog::basic_logger& logger; - pdcp_nr_test_helper pdcp_hlp_rx(cfg_rx, sec_cfg, logger); - srsran::pdcp_entity_nr* pdcp_rx = &pdcp_hlp_rx.pdcp; - gw_dummy* gw_rx = &pdcp_hlp_rx.gw; - srsue::stack_test_dummy* stack = &pdcp_hlp_rx.stack; - pdcp_hlp_rx.set_pdcp_initial_state(init_state); + test_rx_helper(uint8_t pdcp_sn_len, srslog::basic_logger& logger) : + pdcp_hlp_rx({1, + srsran::PDCP_RB_IS_DRB, + srsran::SECURITY_DIRECTION_DOWNLINK, + srsran::SECURITY_DIRECTION_UPLINK, + pdcp_sn_len, + srsran::pdcp_t_reordering_t::ms500, + srsran::pdcp_discard_timer_t::infinity, + false, + srsran::srsran_rat_t::nr}, + sec_cfg, + logger), + pdcp_rx(pdcp_hlp_rx.pdcp), + gw_rx(pdcp_hlp_rx.gw), + stack(pdcp_hlp_rx.stack), + logger(logger) + {} - // Generate test message and encript/decript SDU. - for (pdcp_test_event_t& event : events) { - // Decript and integrity check the PDU - pdcp_rx->write_pdu(std::move(event.pkt)); - for (uint32_t i = 0; i < event.ticks; ++i) { - stack->run_tti(); + int test_rx(std::vector events, + const pdcp_initial_state& init_state, + uint32_t n_sdus_exp, + const srsran::unique_byte_buffer_t& sdu_exp) + + { + pdcp_hlp_rx.set_pdcp_initial_state(init_state); + + // Generate test message and encrypt/decrypt SDU. + for (pdcp_test_event_t& event : events) { + // Decrypt and integrity check the PDU + pdcp_rx.write_pdu(std::move(event.pkt)); + for (uint32_t i = 0; i < event.ticks; ++i) { + stack.run_tti(); + } } - } - // Test if the number of RX packets - TESTASSERT(gw_rx->rx_count == n_sdus_exp); - srsran::unique_byte_buffer_t sdu_act = srsran::make_byte_buffer(); - gw_rx->get_last_pdu(sdu_act); - TESTASSERT(compare_two_packets(sdu_exp, sdu_act) == 0); - return 0; -} + // Test if the number of RX packets + TESTASSERT_EQ(gw_rx.rx_count, n_sdus_exp); + srsran::unique_byte_buffer_t sdu_act = srsran::make_byte_buffer(); + gw_rx.get_last_pdu(sdu_act); + TESTASSERT(compare_two_packets(sdu_exp, sdu_act) == 0); + return 0; + } +}; /* * RX Test: PDCP Entity with SN LEN = 12 and 18. @@ -83,12 +95,15 @@ int test_rx_all(srslog::basic_logger& logger) * This tests correct handling of HFN in the case of SN wraparound (SN LEN 12) */ { - std::vector test1_counts(2); // Test two packets + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("RX COUNT [4095,4096], 12 bit SN"); + test_rx_helper rx_helper(srsran::PDCP_SN_LEN_12, logger); + std::vector test1_counts(2); // Test two packets std::iota(test1_counts.begin(), test1_counts.end(), 4095); // Starting at COUNT 4095 std::vector test1_pdus = gen_expected_pdus_vector(tst_sdu1, test1_counts, srsran::PDCP_SN_LEN_12, sec_cfg, logger); pdcp_initial_state test1_init_state = {.tx_next = 4095, .rx_next = 4095, .rx_deliv = 4095, .rx_reord = 0}; - TESTASSERT(test_rx(std::move(test1_pdus), test1_init_state, srsran::PDCP_SN_LEN_12, 2, tst_sdu1, logger) == 0); + TESTASSERT(rx_helper.test_rx(std::move(test1_pdus), test1_init_state, 2, tst_sdu1) == 0); } /* * RX Test 2: PDCP Entity with SN LEN = 12 @@ -97,13 +112,16 @@ int test_rx_all(srslog::basic_logger& logger) * Packet that wraparound should be dropped, so only one packet should be received at the GW. */ { - std::vector test2_counts(2); // Test two packets + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("RX COUNT [4294967295,0], 12 bit SN"); + test_rx_helper rx_helper(srsran::PDCP_SN_LEN_12, logger); + std::vector test2_counts(2); // Test two packets std::iota(test2_counts.begin(), test2_counts.end(), 4294967295); // Starting at COUNT 4294967295 std::vector test2_pdus = gen_expected_pdus_vector(tst_sdu1, test2_counts, srsran::PDCP_SN_LEN_12, sec_cfg, logger); pdcp_initial_state test2_init_state = { .tx_next = 4294967295, .rx_next = 4294967295, .rx_deliv = 4294967295, .rx_reord = 0}; - TESTASSERT(test_rx(std::move(test2_pdus), test2_init_state, srsran::PDCP_SN_LEN_12, 1, tst_sdu1, logger) == 0); + TESTASSERT(rx_helper.test_rx(std::move(test2_pdus), test2_init_state, 1, tst_sdu1) == 0); } /* * RX Test 3: PDCP Entity with SN LEN = 18 @@ -111,12 +129,15 @@ int test_rx_all(srslog::basic_logger& logger) * This tests correct handling of HFN in the case of SN wraparound (SN LEN 18) */ { - std::vector test3_counts(2); // Test two packets + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("RX COUNT [262144,262145], 12 bit SN"); + test_rx_helper rx_helper(srsran::PDCP_SN_LEN_18, logger); + std::vector test3_counts(2); // Test two packets std::iota(test3_counts.begin(), test3_counts.end(), 262144); // Starting at COUNT 262144 std::vector test3_pdus = gen_expected_pdus_vector(tst_sdu1, test3_counts, srsran::PDCP_SN_LEN_18, sec_cfg, logger); pdcp_initial_state test3_init_state = {.tx_next = 262144, .rx_next = 262144, .rx_deliv = 262144, .rx_reord = 0}; - TESTASSERT(test_rx(std::move(test3_pdus), test3_init_state, srsran::PDCP_SN_LEN_18, 2, tst_sdu1, logger) == 0); + TESTASSERT(rx_helper.test_rx(std::move(test3_pdus), test3_init_state, 2, tst_sdu1) == 0); } /* @@ -125,20 +146,27 @@ int test_rx_all(srslog::basic_logger& logger) * This tests correct handling of COUNT in the case of [HFN|SN] wraparound */ { - std::vector test4_counts(2); // Test two packets + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("RX COUNT [4294967295,4294967296], 18 bit SN"); + test_rx_helper rx_helper(srsran::PDCP_SN_LEN_18, logger); + std::vector test4_counts(2); // Test two packets std::iota(test4_counts.begin(), test4_counts.end(), 4294967295); // Starting at COUNT 4294967295 std::vector test4_pdus = gen_expected_pdus_vector(tst_sdu1, test4_counts, srsran::PDCP_SN_LEN_18, sec_cfg, logger); pdcp_initial_state test4_init_state = { .tx_next = 4294967295, .rx_next = 4294967295, .rx_deliv = 4294967295, .rx_reord = 0}; - TESTASSERT(test_rx(std::move(test4_pdus), test4_init_state, srsran::PDCP_SN_LEN_18, 1, tst_sdu1, logger) == 0); + TESTASSERT(rx_helper.test_rx(std::move(test4_pdus), test4_init_state, 1, tst_sdu1) == 0); } /* * RX Test 5: PDCP Entity with SN LEN = 12 * Test reception of two out-of-order packets, starting at COUNT 0. + * Both packets are received and delivered before t-Reordering expires. */ { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("RX out-of-order COUNT [1,0], 12 bit SN"); + test_rx_helper rx_helper(srsran::PDCP_SN_LEN_12, logger); std::vector test5_pdus; pdcp_initial_state test5_init_state = {}; @@ -155,14 +183,21 @@ int test_rx_all(srslog::basic_logger& logger) // Write PDUs out of order (first the pdu with COUNT 1 and COUNT 0 after) test5_pdus.push_back(std::move(event_pdu2)); test5_pdus.push_back(std::move(event_pdu1)); - TESTASSERT(test_rx(std::move(test5_pdus), test5_init_state, srsran::PDCP_SN_LEN_12, 2, tst_sdu2, logger) == 0); + TESTASSERT(rx_helper.test_rx(std::move(test5_pdus), test5_init_state, 2, tst_sdu2) == 0); + TESTASSERT(rx_helper.pdcp_rx.is_reordering_timer_running() == false); + TESTASSERT(rx_helper.pdcp_rx.get_rx_deliv() == 2); + TESTASSERT(rx_helper.pdcp_rx.get_rx_reord() == 2); } /* * RX Test 6: PDCP Entity with SN LEN = 18 * Test reception of two out-of-order packets, starting at COUNT 0. + * Both packets are received and delivered before t-Reordering expires. */ { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("RX out-of-order COUNT [1,0], 18 bit SN"); + test_rx_helper rx_helper(srsran::PDCP_SN_LEN_18, logger); std::vector test6_pdus; pdcp_initial_state test6_init_state = {}; @@ -179,7 +214,10 @@ int test_rx_all(srslog::basic_logger& logger) // Write PDUs out of order (first the pdu with COUNT 1 and COUNT 0 after) test6_pdus.push_back(std::move(event_pdu2)); test6_pdus.push_back(std::move(event_pdu1)); - TESTASSERT(test_rx(std::move(test6_pdus), test6_init_state, srsran::PDCP_SN_LEN_18, 2, tst_sdu2, logger) == 0); + TESTASSERT(rx_helper.test_rx(std::move(test6_pdus), test6_init_state, 2, tst_sdu2) == 0); + TESTASSERT(rx_helper.pdcp_rx.is_reordering_timer_running() == false); + TESTASSERT(rx_helper.pdcp_rx.get_rx_deliv() == 2); + TESTASSERT(rx_helper.pdcp_rx.get_rx_reord() == 2); } /* @@ -187,6 +225,9 @@ int test_rx_all(srslog::basic_logger& logger) * Test Reception of one out-of-order packet. */ { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("RX out-of-order COUNT [1,0] t_reordering expired, 12 bit SN"); + test_rx_helper rx_helper(srsran::PDCP_SN_LEN_12, logger); std::vector test7_pdus; pdcp_initial_state test7_init_state = {}; @@ -198,7 +239,10 @@ int test_rx_all(srslog::basic_logger& logger) // Write PDUs out of order (first the pdu with COUNT 1 and COUNT 0 after) test7_pdus.push_back(std::move(event_pdu1)); - TESTASSERT(test_rx(std::move(test7_pdus), test7_init_state, srsran::PDCP_SN_LEN_12, 1, tst_sdu2, logger) == 0); + TESTASSERT(rx_helper.test_rx(std::move(test7_pdus), test7_init_state, 1, tst_sdu2) == 0); + TESTASSERT(rx_helper.pdcp_rx.is_reordering_timer_running() == false); + TESTASSERT(rx_helper.pdcp_rx.get_rx_deliv() == 2); + TESTASSERT(rx_helper.pdcp_rx.get_rx_reord() == 2); } /* @@ -206,6 +250,8 @@ int test_rx_all(srslog::basic_logger& logger) * Test reception of two duplicate PDUs, with COUNT 0. */ { + srsran::test_delimit_logger delimiter("RX duplicate COUNTs [0,0], 12 bit SN"); + test_rx_helper rx_helper(srsran::PDCP_SN_LEN_12, logger); std::vector test8_pdus; pdcp_initial_state test8_init_state = {}; @@ -222,7 +268,7 @@ int test_rx_all(srslog::basic_logger& logger) // Write PDUs out of order (first the pdu with COUNT 1 and COUNT 0 after) test8_pdus.push_back(std::move(event_pdu1)); test8_pdus.push_back(std::move(event_pdu2)); - TESTASSERT(test_rx(std::move(test8_pdus), test8_init_state, srsran::PDCP_SN_LEN_12, 1, tst_sdu1, logger) == 0); + TESTASSERT(rx_helper.test_rx(std::move(test8_pdus), test8_init_state, 1, tst_sdu1) == 0); } return 0; } diff --git a/lib/test/pdcp/pdcp_nr_test_tx.cc b/lib/test/pdcp/pdcp_nr_test_tx.cc new file mode 100644 index 000000000..fc0a26650 --- /dev/null +++ b/lib/test/pdcp/pdcp_nr_test_tx.cc @@ -0,0 +1,248 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "pdcp_nr_test.h" +#include + +/* + * Generic class to test transmission of in-sequence packets + */ +class test_tx_helper +{ +public: + pdcp_nr_test_helper pdcp_hlp_tx; + srsran::pdcp_entity_nr& pdcp_tx; + rlc_dummy& rlc_tx; + srsue::stack_test_dummy& stack; + srslog::basic_logger& logger; + + test_tx_helper(uint8_t pdcp_sn_len, srslog::basic_logger& logger) : + pdcp_hlp_tx({1, + srsran::PDCP_RB_IS_DRB, + srsran::SECURITY_DIRECTION_UPLINK, + srsran::SECURITY_DIRECTION_DOWNLINK, + pdcp_sn_len, + srsran::pdcp_t_reordering_t::ms500, + srsran::pdcp_discard_timer_t::ms500, + false, + srsran::srsran_rat_t::nr}, + sec_cfg, + logger), + pdcp_tx(pdcp_hlp_tx.pdcp), + rlc_tx(pdcp_hlp_tx.rlc), + stack(pdcp_hlp_tx.stack), + logger(logger) + {} + int test_tx(uint32_t n_packets, + const pdcp_initial_state& init_state, + uint64_t n_pdus_exp, + srsran::unique_byte_buffer_t pdu_exp) + { + pdcp_hlp_tx.set_pdcp_initial_state(init_state); + + // Run test + for (uint32_t i = 0; i < n_packets; ++i) { + // Test SDU + srsran::unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + sdu->append_bytes(sdu1, sizeof(sdu1)); + pdcp_hlp_tx.pdcp.write_sdu(std::move(sdu)); + } + + srsran::unique_byte_buffer_t pdu_act = srsran::make_byte_buffer(); + pdcp_hlp_tx.rlc.get_last_sdu(pdu_act); + + TESTASSERT(pdcp_hlp_tx.rlc.rx_count == n_pdus_exp); + TESTASSERT(compare_two_packets(pdu_act, pdu_exp) == 0); + return 0; + } +}; + +/* + * TX Test: PDCP Entity with SN LEN = 12 and 18. + * PDCP entity configured with EIA2 and EEA2 + */ +int test_tx_all(srslog::basic_logger& logger) +{ + uint64_t n_packets; + /* + * TX Test 1: PDCP Entity with SN LEN = 12 + * TX_NEXT = 0. + * Input: {0x18, 0xE2} + * Output: {0x80, 0x00, 0x8f, 0xe3, 0xc7, 0x1b, 0xad, 0x14} + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("TX COUNT 0, 12 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_12, logger); + n_packets = 1; + srsran::unique_byte_buffer_t pdu_exp_count0_len12 = srsran::make_byte_buffer(); + pdu_exp_count0_len12->append_bytes(pdu1_count0_snlen12, sizeof(pdu1_count0_snlen12)); + TESTASSERT(tx_helper.test_tx(n_packets, normal_init_state, n_packets, std::move(pdu_exp_count0_len12)) == 0); + } + /* + * TX Test 2: PDCP Entity with SN LEN = 12 + * TX_NEXT = 2048. + * Input: {0x18, 0xE2} + * Output: {0x88, 0x00, 0x8d, 0x2c, 0xe5, 0x38, 0xc0, 0x42} + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("TX COUNT 2048, 12 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_12, logger); + n_packets = 2049; + srsran::unique_byte_buffer_t pdu_exp_count2048_len12 = srsran::make_byte_buffer(); + pdu_exp_count2048_len12->append_bytes(pdu1_count2048_snlen12, sizeof(pdu1_count2048_snlen12)); + TESTASSERT(tx_helper.test_tx(n_packets, normal_init_state, n_packets, std::move(pdu_exp_count2048_len12)) == 0); + } + /* + * TX Test 3: PDCP Entity with SN LEN = 12 + * TX_NEXT = 4096. + * Input: {0x18, 0xE2} + * Output: {0x80, 0x00, 0x97, 0xbe, 0xee, 0x62, 0xf5, 0xe0} + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("TX COUNT 4096, 12 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_12, logger); + n_packets = 4097; + srsran::unique_byte_buffer_t pdu_exp_count4096_len12 = srsran::make_byte_buffer(); + pdu_exp_count4096_len12->append_bytes(pdu1_count4096_snlen12, sizeof(pdu1_count4096_snlen12)); + TESTASSERT(tx_helper.test_tx(n_packets, normal_init_state, n_packets, std::move(pdu_exp_count4096_len12)) == 0); + } + /* + * TX Test 4: PDCP Entity with SN LEN = 18 + * TX_NEXT = 0. + * Input: {0x18, 0xE2} + * Output: {0x80, 0x00, 0x00, 0x8f, 0xe3, 0x37, 0x33, 0xd5, 0x64} + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("TX COUNT 0, 18 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_18, logger); + n_packets = 1; + srsran::unique_byte_buffer_t pdu_exp_count0_len18 = srsran::make_byte_buffer(); + pdu_exp_count0_len18->append_bytes(pdu1_count0_snlen18, sizeof(pdu1_count0_snlen18)); + TESTASSERT(tx_helper.test_tx(n_packets, normal_init_state, n_packets, std::move(pdu_exp_count0_len18)) == 0); + } + + /* + * TX Test 5: PDCP Entity with SN LEN = 18 + * TX_NEXT = 131072. + * Input: {0x18, 0xE2} + * Output: {0x82, 0x00, 0x00, 0x15, 0x01, 0x99, 0x97, 0xe0, 0x4e} + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("TX COUNT 131072, 18 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_18, logger); + n_packets = 131073; + srsran::unique_byte_buffer_t pdu_exp_sn131072_len18 = srsran::make_byte_buffer(); + pdu_exp_sn131072_len18->append_bytes(pdu1_count131072_snlen18, sizeof(pdu1_count131072_snlen18)); + TESTASSERT(tx_helper.test_tx(n_packets, normal_init_state, n_packets, std::move(pdu_exp_sn131072_len18)) == 0); + } + + /* + * TX Test 6: PDCP Entity with SN LEN = 18 + * TX_NEXT = 262144. + * Input: {0x18, 0xE2} + * Output: {0x80, 0x00, 0x00, 0xc2, 0x47, 0xc2, 0xee, 0x46, 0xd9} + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("TX COUNT 262144, 18 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_18, logger); + n_packets = 262145; + srsran::unique_byte_buffer_t pdu_exp_count262144_len18 = srsran::make_byte_buffer(); + pdu_exp_count262144_len18->append_bytes(pdu1_count262144_snlen18, sizeof(pdu1_count262144_snlen18)); + TESTASSERT(tx_helper.test_tx(n_packets, normal_init_state, n_packets, std::move(pdu_exp_count262144_len18)) == 0); + } + /* + * TX Test 7: PDCP Entity with SN LEN = 12 + * Test TX at COUNT wraparound. + * Should print a warning and drop all packets after wrap around. + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("TX COUNT wrap around, 12 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_12, logger); + n_packets = 5; + srsran::unique_byte_buffer_t pdu_exp_count4294967295_len12 = srsran::make_byte_buffer(); + pdu_exp_count4294967295_len12->append_bytes(pdu1_count4294967295_snlen12, sizeof(pdu1_count4294967295_snlen12)); + TESTASSERT(tx_helper.test_tx(n_packets, near_wraparound_init_state, 1, std::move(pdu_exp_count4294967295_len12)) == + 0); + } + + /* + * TX Test 8: PDCP Entity with SN LEN = 18 + * Test TX at COUNT wraparound. + * Should print a warning and drop all packets after wraparound. + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("TX COUNT wrap around, 12 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_18, logger); + n_packets = 5; + srsran::unique_byte_buffer_t pdu_exp_count4294967295_len18 = srsran::make_byte_buffer(); + pdu_exp_count4294967295_len18->append_bytes(pdu1_count4294967295_snlen18, sizeof(pdu1_count4294967295_snlen18)); + TESTASSERT(tx_helper.test_tx(n_packets, near_wraparound_init_state, 1, std::move(pdu_exp_count4294967295_len18)) == + 0); + } + + /* + * TX Test 9: PDCP Entity with SN LEN = 12 + * Test whether discard timers are correctly stopped after receiving a notification from the RLC + */ + { + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + srsran::test_delimit_logger delimiter("Stop discard timers upon RLC notification, 12 bit SN"); + test_tx_helper tx_helper(srsran::PDCP_SN_LEN_12, logger); + n_packets = 1; + srsran::unique_byte_buffer_t pdu_exp_count0_len12 = srsran::make_byte_buffer(); + pdu_exp_count0_len12->append_bytes(pdu1_count0_snlen12, sizeof(pdu1_count0_snlen12)); + TESTASSERT(tx_helper.test_tx(n_packets, normal_init_state, n_packets, std::move(pdu_exp_count0_len12)) == 0); + TESTASSERT(tx_helper.pdcp_tx.nof_discard_timers() == 1); + tx_helper.pdcp_tx.notify_delivery({0}); + TESTASSERT(tx_helper.pdcp_tx.nof_discard_timers() == 0); + } + return SRSRAN_SUCCESS; +} + +// Setup all tests +int run_all_tests() +{ + // Setup log + auto& logger = srslog::fetch_basic_logger("PDCP NR Test TX", false); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(128); + + TESTASSERT(test_tx_all(logger) == 0); + return 0; +} + +int main() +{ + srslog::init(); + + if (run_all_tests() != SRSRAN_SUCCESS) { + fprintf(stderr, "pdcp_nr_tests_tx() failed\n"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} diff --git a/lib/test/phy/CMakeLists.txt b/lib/test/phy/CMakeLists.txt index cad583825..86d8cd436 100644 --- a/lib/test/phy/CMakeLists.txt +++ b/lib/test/phy/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -30,33 +30,35 @@ target_link_libraries(phy_dl_test srsran_phy srsran_common srsran_phy ${SEC_LIBR set(ue_dl_min_mcs 0) set(ue_dl_max_mcs 28) set(ue_dl_step_mcs 7) - -foreach (cell_n_prb 6 15 25 50 75 100) - foreach (allow_256 0 1) - foreach (ue_dl_tm 1 2 3 4) - foreach (ue_dl_mcs RANGE ${ue_dl_min_mcs} ${ue_dl_max_mcs} ${ue_dl_step_mcs}) - set(phy_dl_test_args "") - - set(phy_dl_test_args ${phy_dl_test_args} -p ${cell_n_prb}) - set(phy_dl_test_args ${phy_dl_test_args} -t ${ue_dl_tm}) - if (${allow_256}) - if (${ue_dl_mcs} EQUAL 28) - if (${cell_n_prb} EQUAL 15) - set(ue_dl_mcs 26) - else (${cell_n_prb} EQUAL 15) - set(ue_dl_mcs 27) - endif (${cell_n_prb} EQUAL 15) - endif (${ue_dl_mcs} EQUAL 28) - - set(phy_dl_test_args ${phy_dl_test_args} -q) - endif (${allow_256}) - set(phy_dl_test_args ${phy_dl_test_args} -m ${ue_dl_mcs}) - string(REGEX REPLACE "\ " "" test_name_args ${phy_dl_test_args}) - add_lte_test(phy_dl_test${test_name_args} phy_dl_test ${phy_dl_test_args}) - endforeach (ue_dl_mcs) - endforeach (ue_dl_tm) - endforeach (allow_256 0 1) -endforeach (cell_n_prb) +foreach (cp 0 1) + foreach (cell_n_prb 6 15 25 50 75 100) + foreach (allow_256 0 1) + foreach (ue_dl_tm 1 2 3 4) + foreach (ue_dl_mcs RANGE ${ue_dl_min_mcs} ${ue_dl_max_mcs} ${ue_dl_step_mcs}) + set(phy_dl_test_args "") + if(NOT ((${cp} EQUAL 1) AND (ue_dl_mcs GREATER 26))) + set(phy_dl_test_args ${phy_dl_test_args} -p ${cell_n_prb}) + set(phy_dl_test_args ${phy_dl_test_args} -t ${ue_dl_tm}) + set(phy_dl_test_args ${phy_dl_test_args} -E ${cp}) + if (${allow_256}) + if (${ue_dl_mcs} EQUAL 28) + if (${cell_n_prb} EQUAL 15) + set(ue_dl_mcs 26) + else (${cell_n_prb} EQUAL 15) + set(ue_dl_mcs 27) + endif (${cell_n_prb} EQUAL 15) + endif (${ue_dl_mcs} EQUAL 28) + set(phy_dl_test_args ${phy_dl_test_args} -q) + endif (${allow_256}) + set(phy_dl_test_args ${phy_dl_test_args} -m ${ue_dl_mcs}) + string(REGEX REPLACE "\ " "" test_name_args ${phy_dl_test_args}) + add_lte_test(phy_dl_test${test_name_args} phy_dl_test ${phy_dl_test_args}) + endif(NOT ((${cp} EQUAL 1) AND (ue_dl_mcs GREATER 26))) + endforeach (ue_dl_mcs) + endforeach (ue_dl_tm) + endforeach (allow_256 0 1) + endforeach (cell_n_prb) +endforeach (cp) add_executable(pucch_ca_test pucch_ca_test.c) target_link_libraries(pucch_ca_test srsran_phy srsran_common srsran_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) @@ -64,11 +66,25 @@ add_lte_test(pucch_ca_test pucch_ca_test) add_executable(phy_dl_nr_test phy_dl_nr_test.c) target_link_libraries(phy_dl_nr_test srsran_phy srsran_common srsran_phy ${SEC_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) -add_nr_test(phy_dl_nr_test phy_dl_nr_test -p 100 -m 28 ) -add_nr_test(phy_dl_nr_test_rvd phy_dl_nr_test -P 52 -p 52 -m 0 - -R 0 52 1 010010010010 00000000010000 - -R 0 52 1 100100100100 00000010000000) -add_nr_test(phy_dl_nr_test_cfo_delay phy_dl_nr_test -P 52 -p 52 -m 27 -C 100.0 -D 4 -n 10) -add_nr_test(phy_dl_nr_test_52prb phy_dl_nr_test -P 52 -p 52 -m 27 -T 256qam -v -d 1 1 -n 10) -add_nr_test(phy_dl_nr_test_270prb phy_dl_nr_test -P 270 -p 270 -m 27 -T 256qam -v -d 1 1 -n 10) +# Create a test case for each possible bandwidth described in TS 38.104 Table 5.3.2-1: Transmission bandwidth +# configuration N RB for FR1 +foreach(rb 25 52 79 106 133 160 216 270) + # Basic test with 25 RB grant, maximum MCS, 64QAM + add_nr_test(phy_dl_nr_test_${rb}prb phy_dl_nr_test -P ${rb} -p 25 -m 28) + + # Full BW grant, minimum MCS, with reserved RE + add_nr_test(phy_dl_nr_test_${rb}prb_rvd phy_dl_nr_test -P ${rb} -p ${rb} -m 0 + -R 0 ${rb} 1 010010010010 00000000010000 + -R 0 ${rb} 1 100100100100 00000010000000) + + # 25 RB grant with interleaved PDCCH + add_nr_test(phy_dl_nr_test_${rb}prb_interleaved phy_dl_nr_test -P ${rb} -p 25 -m 10 -I) + + # Maximum throughput with 256QAM + add_nr_test(phy_dl_nr_test_${rb}prb_256qam phy_dl_nr_test -P ${rb} -p ${rb} -m 27 -T 256qam -v -d 1 1 1 -n 10) + + # Maximum throughput with 64QAM and CFO+Delay impairments + add_nr_test(phy_dl_nr_test_${rb}prb_cfo_delay phy_dl_nr_test -P ${rb} -p ${rb} -m 27 -C 100.0 -D 4 -n 10) + +endforeach() diff --git a/lib/test/phy/phy_dl_nr_test.c b/lib/test/phy/phy_dl_nr_test.c index 985993dad..ec557c36e 100644 --- a/lib/test/phy/phy_dl_nr_test.c +++ b/lib/test/phy/phy_dl_nr_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsran/phy/enb/enb_dl_nr.h" +#include "srsran/phy/gnb/gnb_dl.h" #include "srsran/phy/phch/ra_dl_nr.h" #include "srsran/phy/phch/ra_nr.h" #include "srsran/phy/ue/ue_dl_nr.h" @@ -28,30 +28,22 @@ #include "srsran/phy/utils/vector.h" #include -static srsran_carrier_nr_t carrier = { - 501, // pci - 0, // absolute_frequency_ssb - 0, // absolute_frequency_point_a - 0, // offset_to_carrier - srsran_subcarrier_spacing_15kHz, // scs - 52, // nof_prb - 0, // start - 1 // max_mimo_layers -}; - -static uint32_t n_prb = 0; // Set to 0 for steering -static uint32_t mcs = 30; // Set to 30 for steering -static srsran_sch_cfg_nr_t pdsch_cfg = {}; -static uint32_t nof_slots = 10; -static uint32_t rv_idx = 0; -static uint32_t delay_n = 0; // Integer delay -static float cfo_hz = 0.0f; // CFO Hz -static srsran_dmrs_sch_type_t dmrs_type = srsran_dmrs_sch_type_1; -static srsran_dmrs_sch_add_pos_t dmrs_add_pos = srsran_dmrs_sch_add_pos_2; +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; +static uint32_t n_prb = 0; // Set to 0 for steering +static uint32_t mcs = 30; // Set to 30 for steering +static srsran_sch_cfg_nr_t pdsch_cfg = {}; +static uint32_t nof_slots = 10; +static uint32_t rv_idx = 0; +static uint32_t delay_n = 0; // Integer delay +static float cfo_hz = 0.0f; // CFO Hz +static srsran_dmrs_sch_type_t dmrs_type = srsran_dmrs_sch_type_1; +static srsran_dmrs_sch_add_pos_t dmrs_add_pos = srsran_dmrs_sch_add_pos_2; +static bool interleaved_pdcch = false; +static uint32_t nof_dmrs_cdm_groups_without_data = 1; static void usage(char* prog) { - printf("Usage: %s [rRPdpmnTLDCv] \n", prog); + printf("Usage: %s [rRPdpmnTILDCv] \n", prog); printf("\t-P Number of BWP (Carrier) PRB [Default %d]\n", carrier.nof_prb); printf("\t-p Number of grant PRB, set to 0 for steering [Default %d]\n", n_prb); printf("\t-n Number of slots to simulate [Default %d]\n", nof_slots); @@ -61,6 +53,7 @@ static void usage(char* prog) printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n", srsran_mcs_table_to_str(pdsch_cfg.sch_cfg.mcs_table)); printf("\t-R Reserve RE: [rb_begin] [rb_end] [rb_stride] [sc_mask] [symbol_mask]\n"); + printf("\t-I Enable interleaved CCE-to-REG [Default %s]\n", interleaved_pdcch ? "Enabled" : "Disabled"); printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers); printf("\t-D Delay signal an integer number of samples [Default %d samples]\n", delay_n); printf("\t-C Frequency shift (CFO) signal in Hz [Default %+.0f Hz]\n", cfo_hz); @@ -70,7 +63,7 @@ static void usage(char* prog) static int parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "rRPdpmnTLDCv")) != -1) { + while ((opt = getopt(argc, argv, "rRIPdpmnTLDCv")) != -1) { switch (opt) { case 'P': carrier.nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); @@ -96,7 +89,7 @@ static int parse_args(int argc, char** argv) dmrs_type = srsran_dmrs_sch_type_2; break; } - switch (strtol(argv[optind], NULL, 10)) { + switch (strtol(argv[optind++], NULL, 10)) { case 0: dmrs_add_pos = srsran_dmrs_sch_add_pos_0; break; @@ -110,6 +103,7 @@ static int parse_args(int argc, char** argv) dmrs_add_pos = srsran_dmrs_sch_add_pos_3; break; } + nof_dmrs_cdm_groups_without_data = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'T': pdsch_cfg.sch_cfg.mcs_table = srsran_mcs_table_from_str(argv[optind]); @@ -132,6 +126,9 @@ static int parse_args(int argc, char** argv) return SRSRAN_ERROR; } } break; + case 'I': + interleaved_pdcch ^= true; + break; case 'L': carrier.max_mimo_layers = (uint32_t)strtol(argv[optind], NULL, 10); break; @@ -142,7 +139,7 @@ static int parse_args(int argc, char** argv) cfo_hz = strtof(argv[optind], NULL); break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; default: usage(argv[0]); @@ -153,13 +150,13 @@ static int parse_args(int argc, char** argv) return SRSRAN_SUCCESS; } -static int work_gnb_dl(srsran_enb_dl_nr_t* enb_dl, +static int work_gnb_dl(srsran_gnb_dl_t* gnb_dl, srsran_slot_cfg_t* slot, srsran_search_space_t* search_space, srsran_dci_location_t* dci_location, uint8_t** data_tx) { - if (srsran_enb_dl_nr_base_zero(enb_dl) < SRSRAN_SUCCESS) { + if (srsran_gnb_dl_base_zero(gnb_dl) < SRSRAN_SUCCESS) { ERROR("Error setting base to zero"); return SRSRAN_ERROR; } @@ -172,6 +169,7 @@ static int work_gnb_dl(srsran_enb_dl_nr_t* enb_dl, dci_dl.ctx.location = *dci_location; dci_dl.ctx.ss_type = search_space->type; dci_dl.ctx.coreset_id = 1; + dci_dl.ctx.coreset_start_rb = 0; dci_dl.freq_domain_assigment = 0; dci_dl.time_domain_assigment = 0; dci_dl.vrb_to_prb_mapping = 0; @@ -179,18 +177,18 @@ static int work_gnb_dl(srsran_enb_dl_nr_t* enb_dl, dci_dl.rv = 0; // Put actual DCI - if (srsran_enb_dl_nr_pdcch_put(enb_dl, slot, &dci_dl) < SRSRAN_SUCCESS) { + if (srsran_gnb_dl_pdcch_put_dl(gnb_dl, slot, &dci_dl) < SRSRAN_SUCCESS) { ERROR("Error putting PDCCH"); return SRSRAN_ERROR; } // Put PDSCH transmission - if (srsran_enb_dl_nr_pdsch_put(enb_dl, slot, &pdsch_cfg, data_tx) < SRSRAN_SUCCESS) { + if (srsran_gnb_dl_pdsch_put(gnb_dl, slot, &pdsch_cfg, data_tx) < SRSRAN_SUCCESS) { ERROR("Error putting PDSCH"); return SRSRAN_ERROR; } - srsran_enb_dl_nr_gen_signal(enb_dl); + srsran_gnb_dl_gen_signal(gnb_dl); return SRSRAN_SUCCESS; } @@ -223,7 +221,7 @@ static int work_ue_dl(srsran_ue_dl_nr_t* ue_dl, srsran_slot_cfg_t* slot, srsran_ int main(int argc, char** argv) { int ret = SRSRAN_ERROR; - srsran_enb_dl_nr_t enb_dl = {}; + srsran_gnb_dl_t gnb_dl = {}; srsran_ue_dl_nr_t ue_dl = {}; srsran_pdsch_res_nr_t pdsch_res = {}; srsran_random_t rand_gen = srsran_random_init(1234); @@ -260,11 +258,12 @@ int main(int argc, char** argv) ue_dl_args.pdcch.measure_evm = true; ue_dl_args.nof_max_prb = carrier.nof_prb; - srsran_enb_dl_nr_args_t enb_dl_args = {}; - enb_dl_args.nof_tx_antennas = 1; - enb_dl_args.pdsch.sch.disable_simd = false; - enb_dl_args.pdcch.disable_simd = false; - enb_dl_args.nof_max_prb = carrier.nof_prb; + srsran_gnb_dl_args_t gnb_dl_args = {}; + gnb_dl_args.nof_tx_antennas = 1; + gnb_dl_args.pdsch.sch.disable_simd = false; + gnb_dl_args.pdcch.disable_simd = false; + gnb_dl_args.nof_max_prb = carrier.nof_prb; + gnb_dl_args.srate_hz = SRSRAN_SUBC_SPACING_NR(carrier.scs) * srsran_min_symbol_sz_rb(carrier.nof_prb); srsran_pdcch_cfg_nr_t pdcch_cfg = {}; @@ -272,8 +271,20 @@ int main(int argc, char** argv) srsran_coreset_t* coreset = &pdcch_cfg.coreset[1]; pdcch_cfg.coreset_present[1] = true; coreset->duration = 1; + + uint32_t coreset_bw_rb = carrier.nof_prb; + + if (interleaved_pdcch) { + coreset->mapping_type = srsran_coreset_mapping_type_interleaved; + coreset->reg_bundle_size = srsran_coreset_bundle_size_n6; + coreset->interleaver_size = srsran_coreset_bundle_size_n2; + coreset->precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + coreset->shift_index = carrier.pci; + coreset_bw_rb = SRSRAN_FLOOR(carrier.nof_prb, 12) * 12; + } + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { - coreset->freq_resources[i] = i < carrier.nof_prb / 6; + coreset->freq_resources[i] = i < coreset_bw_rb / 6; } // Configure Search Space @@ -294,7 +305,7 @@ int main(int argc, char** argv) goto clean_exit; } - if (srsran_enb_dl_nr_init(&enb_dl, buffer_gnb, &enb_dl_args)) { + if (srsran_gnb_dl_init(&gnb_dl, buffer_gnb, &gnb_dl_args)) { ERROR("Error UE DL"); goto clean_exit; } @@ -313,12 +324,12 @@ int main(int argc, char** argv) goto clean_exit; } - if (srsran_enb_dl_nr_set_carrier(&enb_dl, &carrier)) { + if (srsran_gnb_dl_set_carrier(&gnb_dl, &carrier)) { ERROR("Error setting SCH NR carrier"); goto clean_exit; } - if (srsran_enb_dl_nr_set_pdcch_config(&enb_dl, &pdcch_cfg, &dci_cfg)) { + if (srsran_gnb_dl_set_pdcch_config(&gnb_dl, &pdcch_cfg, &dci_cfg)) { ERROR("Error setting CORESET"); goto clean_exit; } @@ -357,7 +368,7 @@ int main(int argc, char** argv) pdsch_cfg.grant.L = 13; pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; - pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; + pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = nof_dmrs_cdm_groups_without_data; pdsch_cfg.grant.beta_dmrs = srsran_convert_dB_to_amplitude(3); pdsch_cfg.grant.rnti_type = srsran_rnti_type_c; pdsch_cfg.grant.rnti = 0x4601; @@ -396,10 +407,7 @@ int main(int argc, char** argv) if (data_tx[tb] == NULL) { continue; } - - for (uint32_t i = 0; i < pdsch_cfg.grant.tb[tb].tbs; i++) { - data_tx[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX); - } + srsran_random_byte_vector(rand_gen, data_tx[tb], pdsch_cfg.grant.tb[tb].tbs / 8); pdsch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx; } @@ -419,7 +427,7 @@ int main(int argc, char** argv) dci_location.L = L; gettimeofday(&t[1], NULL); - if (work_gnb_dl(&enb_dl, &slot, search_space, &dci_location, data_tx) < SRSRAN_ERROR) { + if (work_gnb_dl(&gnb_dl, &slot, search_space, &dci_location, data_tx) < SRSRAN_ERROR) { ERROR("Error running eNb DL"); goto clean_exit; } @@ -479,9 +487,11 @@ int main(int argc, char** argv) } } - if (srsran_verbose >= SRSRAN_VERBOSE_INFO) { - char str[512]; - srsran_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, &pdsch_res, str, (uint32_t)sizeof(str)); + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) { + char str[512]; + srsran_pdsch_res_nr_t pdsch_res_vec[SRSRAN_MAX_CODEWORDS] = {}; + pdsch_res_vec[0] = pdsch_res; + srsran_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, pdsch_res_vec, str, (uint32_t)sizeof(str)); char str_extra[2048]; srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra, (uint32_t)sizeof(str_extra)); @@ -508,7 +518,7 @@ int main(int argc, char** argv) clean_exit: srsran_random_free(rand_gen); - srsran_enb_dl_nr_free(&enb_dl); + srsran_gnb_dl_free(&gnb_dl); srsran_ue_dl_nr_free(&ue_dl); for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { if (data_tx[i]) { diff --git a/lib/test/phy/phy_dl_test.c b/lib/test/phy/phy_dl_test.c index 96a6ecf0b..b102bc545 100644 --- a/lib/test/phy/phy_dl_test.c +++ b/lib/test/phy/phy_dl_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -44,17 +44,20 @@ static bool print_dci_table; static uint32_t mcs = 20; static int cross_carrier_indicator = -1; static bool enable_256qam = false; +static float snr_db = NAN; // SNR in dB void usage(char* prog) { printf("Usage: %s [cfpndvs]\n", prog); printf("\t-c cell id [Default %d]\n", cell.id); + printf("\t-E extended Cyclic prefix [Default %d]\n", cell.cp); printf("\t-f cfi [Default %d]\n", cfi); printf("\t-p cell.nof_prb [Default %d]\n", cell.nof_prb); printf("\t-s number of subframes to simulate [Default %d]\n", nof_subframes); printf("\t-d Print DCI table [Default %s]\n", print_dci_table ? "yes" : "no"); printf("\t-t Transmission mode: 1,2,3,4 [Default %d]\n", transmission_mode + 1); printf("\t-m mcs [Default %d]\n", mcs); + printf("\t-S SNR in dB [Default %+.2f]\n", snr_db); printf("\tAdvanced parameters:\n"); if (cross_carrier_indicator >= 0) { printf("\t\t-a carrier-indicator [Default %d]\n", cross_carrier_indicator); @@ -93,7 +96,7 @@ void parse_args(int argc, char** argv) nof_rx_ant = 2; } - while ((opt = getopt(argc, argv, "cfapndvqstm")) != -1) { + while ((opt = getopt(argc, argv, "cfapndvqstmES")) != -1) { switch (opt) { case 't': transmission_mode = (uint32_t)strtol(argv[optind], NULL, 10) - 1; @@ -120,6 +123,12 @@ void parse_args(int argc, char** argv) case 's': nof_subframes = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'S': + snr_db = strtof(argv[optind], NULL); + break; + case 'E': + cell.cp = ((uint32_t)strtol(argv[optind], NULL, 10)) ? SRSRAN_CP_EXT : SRSRAN_CP_NORM; + break; case 'd': print_dci_table = true; break; @@ -128,7 +137,7 @@ void parse_args(int argc, char** argv) optind++; break; case 'v': - srsran_verbose++; + increase_srsran_verbose_level(); break; case 'q': enable_256qam = (enable_256qam) ? false : true; @@ -220,7 +229,7 @@ int work_ue(srsran_ue_dl_t* ue_dl, ue_dl_cfg->cfg.pdsch.csi_enable = false; ue_dl_cfg->cfg.pdsch.meas_evm_en = false; - if (srsran_verbose >= SRSRAN_VERBOSE_INFO) { + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) { char str[512]; srsran_dci_dl_info(&dci_dl[0], str, 512); INFO("UE PDCCH: rnti=0x%x, %s", rnti, str); @@ -244,7 +253,7 @@ int work_ue(srsran_ue_dl_t* ue_dl, return SRSRAN_ERROR; } - if (srsran_verbose >= SRSRAN_VERBOSE_INFO) { + if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) { char str[512]; srsran_pdsch_rx_info(&ue_dl_cfg->cfg.pdsch, pdsch_res, str, 512); INFO("eNb PDSCH: rnti=0x%x, %s", rnti, str); @@ -321,6 +330,8 @@ int main(int argc, char** argv) uint32_t count_failures = 0, count_tbs = 0; size_t pdsch_decode_us = 0; size_t pdsch_encode_us = 0; + srsran_channel_awgn_t awgn = {}; + float snr_db_avg = 0.0; int ret = -1; @@ -339,6 +350,11 @@ int main(int argc, char** argv) } } + if (srsran_channel_awgn_init(&awgn, 0x1234) < SRSRAN_SUCCESS) { + ERROR("Error AWGN init"); + goto quit; + } + for (int i = 0; i < SRSRAN_MAX_TB; i++) { softbuffer_tx[i] = (srsran_softbuffer_tx_t*)calloc(sizeof(srsran_softbuffer_tx_t), 1); if (!softbuffer_tx[i]) { @@ -388,6 +404,17 @@ int main(int argc, char** argv) goto quit; } + /* + * Set AWGN N0 + */ + if (isnormal(snr_db)) { + if (srsran_channel_awgn_set_n0(&awgn, srsran_enb_dl_get_maximum_signal_power_dBfs(cell.nof_prb) - snr_db) < + SRSRAN_SUCCESS) { + ERROR("Error setting N0"); + goto quit; + } + } + /* * Initialise UE */ @@ -439,13 +466,13 @@ int main(int argc, char** argv) dci.rnti = rnti; dci.is_tdd = false; dci.is_dwpts = false; - dci.is_ra_order = false; + dci.is_pdcch_order = false; dci.tb_cw_swap = false; dci.pconf = false; dci.power_offset = false; dci.tpc_pucch = false; - dci.ra_preamble = false; - dci.ra_mask_idx = false; + dci.preamble_idx = 0; + dci.prach_mask_idx = 0; dci.srs_request = false; dci.srs_request_present = false; @@ -506,9 +533,7 @@ int main(int argc, char** argv) for (uint32_t sf_idx = 0; sf_idx < nof_subframes; sf_idx++) { /* Generate random data */ for (int j = 0; j < SRSRAN_MAX_TB; j++) { - for (int i = 0; i < MAX_DATABUFFER_SIZE; i++) { - data_tx[j][i] = (uint8_t)srsran_random_uniform_int_dist(random, 0, 255); - } + srsran_random_byte_vector(random, data_tx[j], MAX_DATABUFFER_SIZE); } /* @@ -552,7 +577,10 @@ int main(int argc, char** argv) signal_buffer[0][i] = y0; signal_buffer[1][i] = y1; } + + srsran_channel_awgn_run_c(&awgn, signal_buffer[1], signal_buffer[1], SRSRAN_SF_LEN_PRB(cell.nof_prb)); } + srsran_channel_awgn_run_c(&awgn, signal_buffer[0], signal_buffer[0], SRSRAN_SF_LEN_PRB(cell.nof_prb)); /* * Run UE @@ -597,11 +625,13 @@ int main(int argc, char** argv) get_time_interval(t); pdsch_decode_us += (size_t)(t[0].tv_sec * 1e6 + t[0].tv_usec); + snr_db_avg += ue_dl->chest_res.snr_db; + for (int i = 0; i < SRSRAN_MAX_TB; i++) { if (ue_dl_cfg.cfg.pdsch.grant.tb[i].enabled) { - if (check_evm(enb_dl, ue_dl, &ue_dl_cfg, i)) { + if (!isnormal(snr_db) && check_evm(enb_dl, ue_dl, &ue_dl_cfg, i)) { count_failures++; - } else if (check_softbits(enb_dl, ue_dl, &ue_dl_cfg, sf_idx, i) != SRSRAN_SUCCESS) { + } else if (!isnormal(snr_db) && check_softbits(enb_dl, ue_dl, &ue_dl_cfg, sf_idx, i) != SRSRAN_SUCCESS) { printf("TB%d: The received softbits in subframe %d DO NOT match the encoded bits (crc=%d)\n", i, sf_idx, @@ -641,6 +671,10 @@ int main(int argc, char** argv) printf("BLER: %5.1f%%\n", (float)count_failures / (float)count_tbs * 100.0f); + if (isnormal(snr_db)) { + printf("SNR Real: %+.2f; estimated: %+.2f\n", snr_db, snr_db_avg / nof_subframes); + } + quit: srsran_enb_dl_free(enb_dl); srsran_ue_dl_free(ue_dl); @@ -677,6 +711,7 @@ quit: if (ue_dl) { free(ue_dl); } + srsran_channel_awgn_free(&awgn); if (ret) { printf("Error\n"); diff --git a/lib/test/phy/pucch_ca_test.c b/lib/test/phy/pucch_ca_test.c index 9c80ba83e..a28b5334f 100644 --- a/lib/test/phy/pucch_ca_test.c +++ b/lib/test/phy/pucch_ca_test.c @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -140,7 +140,7 @@ static int test_pucch_ca(srsran_ack_nack_feedback_mode_t ack_nack_feedback_mode, int main(int argc, char** argv) { // Set PHY lib verbose to INFO - srsran_verbose = SRSRAN_VERBOSE_INFO; + set_srsran_verbose_level(SRSRAN_VERBOSE_INFO); uint32_t nof_tb_1[SRSRAN_MAX_CARRIERS] = {1, 1, 1, 1, 1}; uint32_t nof_tb_2[SRSRAN_MAX_CARRIERS] = {2, 1, 1, 1, 1}; diff --git a/lib/test/upper/CMakeLists.txt b/lib/test/rlc/CMakeLists.txt similarity index 54% rename from lib/test/upper/CMakeLists.txt rename to lib/test/rlc/CMakeLists.txt index af0d1dcab..e1b7c7ca0 100644 --- a/lib/test/upper/CMakeLists.txt +++ b/lib/test/rlc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,77 +18,59 @@ # and at http://www.gnu.org/licenses/. # -set(CTEST_LABELS "lib;upper") +set(CTEST_LABELS "lib;rlc") add_executable(rlc_am_data_test rlc_am_data_test.cc) -target_link_libraries(rlc_am_data_test srsran_upper srsran_phy srsran_common) +target_link_libraries(rlc_am_data_test srsran_rlc srsran_phy srsran_common) add_lte_test(rlc_am_data_test rlc_am_data_test) add_executable(rlc_am_control_test rlc_am_control_test.cc) -target_link_libraries(rlc_am_control_test srsran_upper srsran_phy) +target_link_libraries(rlc_am_control_test srsran_rlc srsran_phy) add_lte_test(rlc_am_control_test rlc_am_control_test) -add_executable(rlc_am_test rlc_am_test.cc) -target_link_libraries(rlc_am_test srsran_upper srsran_phy srsran_common) -add_lte_test(rlc_am_test rlc_am_test) +add_executable(rlc_am_lte_test rlc_am_lte_test.cc) +target_link_libraries(rlc_am_lte_test srsran_rlc srsran_phy srsran_common) +add_lte_test(rlc_am_lte_test rlc_am_lte_test) + +add_executable(rlc_am_nr_test rlc_am_nr_test.cc) +target_link_libraries(rlc_am_nr_test srsran_rlc srsran_phy srsran_common) +add_nr_test(rlc_am_nr_test rlc_am_nr_test) add_executable(rlc_am_nr_pdu_test rlc_am_nr_pdu_test.cc) -target_link_libraries(rlc_am_nr_pdu_test srsran_upper srsran_phy) -add_nr_test(rlc_am_nr_pdu_test rlc_am_nr_pdu_test) +target_link_libraries(rlc_am_nr_pdu_test srsran_rlc srsran_phy srsran_mac srsran_common ) +add_nr_test(rlc_am_nr_pdu_test rlc_am_nr_pdu_test ) add_executable(rlc_stress_test rlc_stress_test.cc) -target_link_libraries(rlc_stress_test srsran_upper srsran_mac srsran_phy srsran_common ${Boost_LIBRARIES}) +target_link_libraries(rlc_stress_test srsran_rlc srsran_mac srsran_phy srsran_common ${Boost_LIBRARIES} ${ATOMIC_LIBS}) add_lte_test(rlc_am_stress_test rlc_stress_test --mode=AM --loglevel 1 --sdu_gen_delay 250) add_lte_test(rlc_um_stress_test rlc_stress_test --mode=UM --loglevel 1) add_lte_test(rlc_tm_stress_test rlc_stress_test --mode=TM --loglevel 1 --random_opp=false) add_nr_test(rlc_um6_nr_stress_test rlc_stress_test --rat NR --mode=UM6 --loglevel 1) add_nr_test(rlc_um12_nr_stress_test rlc_stress_test --rat NR --mode=UM12 --loglevel 1) +add_nr_test(rlc_am12_nr_stress_test rlc_stress_test --rat NR --mode=AM12 --loglevel 1) +add_nr_test(rlc_am12_nr_stress_test rlc_stress_test --rat NR --mode=AM18 --loglevel 1) add_executable(rlc_um_data_test rlc_um_data_test.cc) -target_link_libraries(rlc_um_data_test srsran_upper srsran_phy srsran_common) +target_link_libraries(rlc_um_data_test srsran_rlc srsran_phy srsran_common) add_test(rlc_um_data_test rlc_um_data_test) add_executable(rlc_um_test rlc_um_test.cc) -target_link_libraries(rlc_um_test srsran_upper srsran_phy) +target_link_libraries(rlc_um_test srsran_rlc srsran_phy) add_test(rlc_um_test rlc_um_test) add_executable(rlc_common_test rlc_common_test.cc) -target_link_libraries(rlc_common_test srsran_upper srsran_phy) +target_link_libraries(rlc_common_test srsran_rlc srsran_phy) add_test(rlc_common_test rlc_common_test) add_executable(rlc_um_nr_pdu_test rlc_um_nr_pdu_test.cc) -target_link_libraries(rlc_um_nr_pdu_test srsran_upper srsran_mac srsran_phy) +target_link_libraries(rlc_um_nr_pdu_test srsran_rlc srsran_mac srsran_phy) add_nr_test(rlc_um_nr_pdu_test rlc_um_nr_pdu_test) add_executable(rlc_um_nr_test rlc_um_nr_test.cc) -target_link_libraries(rlc_um_nr_test srsran_upper srsran_phy) +target_link_libraries(rlc_um_nr_test srsran_rlc srsran_phy srsran_mac srsran_common) add_nr_test(rlc_um_nr_test rlc_um_nr_test) -add_executable(pdcp_nr_test_tx pdcp_nr_test_tx.cc) -target_link_libraries(pdcp_nr_test_tx srsran_upper srsran_common) -add_nr_test(pdcp_nr_test_tx pdcp_nr_test_tx) - -add_executable(pdcp_nr_test_rx pdcp_nr_test_rx.cc) -target_link_libraries(pdcp_nr_test_rx srsran_upper srsran_common) -add_nr_test(pdcp_nr_test_rx pdcp_nr_test_rx) - -add_executable(pdcp_nr_test_discard_sdu pdcp_nr_test_discard_sdu.cc) -target_link_libraries(pdcp_nr_test_discard_sdu srsran_upper srsran_common) -add_nr_test(pdcp_nr_test_discard_sdu pdcp_nr_test_discard_sdu) - -add_executable(pdcp_lte_test_rx pdcp_lte_test_rx.cc) -target_link_libraries(pdcp_lte_test_rx srsran_upper srsran_common) -add_test(pdcp_lte_test_rx pdcp_lte_test_rx) - -add_executable(pdcp_lte_test_discard_sdu pdcp_lte_test_discard_sdu.cc) -target_link_libraries(pdcp_lte_test_discard_sdu srsran_upper srsran_common) -add_test(pdcp_lte_test_discard_sdu pdcp_lte_test_discard_sdu) - -add_executable(pdcp_lte_test_status_report pdcp_lte_test_status_report.cc) -target_link_libraries(pdcp_lte_test_status_report srsran_upper srsran_common) -add_test(pdcp_lte_test_status_report pdcp_lte_test_status_report) - ######################################################################## # Option to run command after build (useful for remote builds) ######################################################################## diff --git a/lib/test/upper/rlc_am_control_test.cc b/lib/test/rlc/rlc_am_control_test.cc similarity index 64% rename from lib/test/upper/rlc_am_control_test.cc rename to lib/test/rlc/rlc_am_control_test.cc index ebc38549a..e6c735181 100644 --- a/lib/test/upper/rlc_am_control_test.cc +++ b/lib/test/rlc/rlc_am_control_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,7 +20,7 @@ */ #include "srsran/common/test_common.h" -#include "srsran/upper/rlc_am_lte.h" +#include "srsran/rlc/rlc_am_lte.h" #include // Simple status PDU @@ -88,6 +88,45 @@ int malformed_status_pdu_test() return SRSRAN_SUCCESS; } +// Malformed PDU captured in field-test +// 22:48:03.509077 [RLC ] [I] DRB1 Tx status PDU - ACK_SN = 205, N_nack = 98, NACK_SN = +// [752][986][109][110][111][112][113][114][115][116][117][118][119][120][121][122][123][124][125][126][127][128][129] +int malformed_status_pdu_test2() +{ + uint32_t vr_a = 293; + + // Construct a status PDU that ACKs SN 205, which is outside the rx window + srsran::rlc_status_pdu_t status_pdu = {}; + status_pdu.ack_sn = 205; + status_pdu.N_nack = 2; + status_pdu.nacks[0].nack_sn = 752; + status_pdu.nacks[1].nack_sn = 986; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu, vr_a) == false); + + // 1 SN after upper edge of Rx window will fail + status_pdu.ack_sn = 806; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu, vr_a) == false); + + // The exact upper edge of Rx window should work + status_pdu.ack_sn = 805; + status_pdu.N_nack = 0; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu, vr_a) == true); + + // ACK_SN is again outside of rx_window + vr_a = 0; + status_pdu.ack_sn = 742; + status_pdu.N_nack = 0; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu, vr_a) == false); + + // ACK_SN is well within rx window + vr_a = 300; + status_pdu.ack_sn = 742; + status_pdu.N_nack = 0; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu, vr_a) == true); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { srslog::init(); @@ -95,6 +134,7 @@ int main(int argc, char** argv) TESTASSERT(simple_status_pdu_test1() == SRSRAN_SUCCESS); TESTASSERT(status_pdu_with_nacks_test1() == SRSRAN_SUCCESS); TESTASSERT(malformed_status_pdu_test() == SRSRAN_SUCCESS); + TESTASSERT(malformed_status_pdu_test2() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } diff --git a/lib/test/rlc/rlc_am_data_test.cc b/lib/test/rlc/rlc_am_data_test.cc new file mode 100644 index 000000000..74c5cb29b --- /dev/null +++ b/lib/test/rlc/rlc_am_data_test.cc @@ -0,0 +1,245 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/rlc/rlc_am_lte.h" +#include + +// Fixed header only +uint8_t pdu1[] = {0x88, 0x06}; +uint32_t PDU1_LEN = 2; + +// Fixed + 2 LI fields (each 1500) +uint8_t pdu2[] = {0x8C, 0x00, 0xDD, 0xC5, 0xDC}; +uint32_t PDU2_LEN = 5; + +// Fixed + 3 LI fields (each 1500) +uint8_t pdu3[] = {0x8C, 0x00, 0xDD, 0xCD, 0xDC, 0x5D, 0xC0}; +uint32_t PDU3_LEN = 7; + +// D/C = 1 = Data PDU +// RF = 0 = AMD PDU +// P = 0 = Status PDU is not requested +// FI = 11 = First byte of the Data field does not corresponds to the first byte of a RLC SDU, +// Last byte of the Data field does not corresponds to the last byte of a RLC SDU +// E = 1 = A set of E field and LI field follows from the octet following the fixed part of the header +// SN = 0000000010 -> SN 2 +// E = 1 +// LI1 = 1010011 1110 (1342 Dec) +// E = 0 +// LI2 = 10111011100 (1500 Dec) +uint8_t pdu4[] = {0x9C, 0x02, 0xD3, 0xE5, 0xDC}; +uint32_t PDU4_LEN = 5; + +using namespace srsran; + +int test1() +{ + srsran::rlc_amd_pdu_header_t h; + srsran::byte_buffer_t b1, b2; + + memcpy(b1.msg, &pdu1[0], PDU1_LEN); + b1.N_bytes = PDU1_LEN; + rlc_am_read_data_pdu_header(&b1, &h); + TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); + TESTASSERT(0x01 == h.fi); + TESTASSERT(0 == h.N_li); + TESTASSERT(0 == h.lsf); + TESTASSERT(0 == h.p); + TESTASSERT(0 == h.rf); + TESTASSERT(0 == h.so); + TESTASSERT(6 == h.sn); + rlc_am_write_data_pdu_header(&h, &b2); + TESTASSERT(b2.N_bytes == PDU1_LEN); + for (uint32_t i = 0; i < b2.N_bytes; i++) + TESTASSERT(b2.msg[i] == b1.msg[i]); + return SRSRAN_SUCCESS; +} + +int test2() +{ + srsran::rlc_amd_pdu_header_t h; + srsran::byte_buffer_t b1, b2; + + memcpy(b1.msg, &pdu2[0], PDU2_LEN); + b1.N_bytes = PDU2_LEN; + rlc_am_read_data_pdu_header(&b1, &h); + TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); + TESTASSERT(0x01 == h.fi); + TESTASSERT(2 == h.N_li); + TESTASSERT(1500 == h.li[0]); + TESTASSERT(1500 == h.li[1]); + TESTASSERT(0 == h.lsf); + TESTASSERT(0 == h.p); + TESTASSERT(0 == h.rf); + TESTASSERT(0 == h.so); + TESTASSERT(0 == h.sn); + rlc_am_write_data_pdu_header(&h, &b2); + TESTASSERT(b2.N_bytes == PDU2_LEN); + for (uint32_t i = 0; i < b2.N_bytes; i++) + TESTASSERT(b2.msg[i] == b1.msg[i]); + return SRSRAN_SUCCESS; +} + +int test3() +{ + srsran::rlc_amd_pdu_header_t h; + srsran::byte_buffer_t b1, b2; + + memcpy(b1.msg, &pdu3[0], PDU3_LEN); + b1.N_bytes = PDU3_LEN; + rlc_am_read_data_pdu_header(&b1, &h); + TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); + TESTASSERT(0x01 == h.fi); + TESTASSERT(3 == h.N_li); + TESTASSERT(1500 == h.li[0]); + TESTASSERT(1500 == h.li[1]); + TESTASSERT(1500 == h.li[2]); + TESTASSERT(0 == h.lsf); + TESTASSERT(0 == h.p); + TESTASSERT(0 == h.rf); + TESTASSERT(0 == h.so); + TESTASSERT(0 == h.sn); + rlc_am_write_data_pdu_header(&h, &b2); + TESTASSERT(b2.N_bytes == PDU3_LEN); + for (uint32_t i = 0; i < b2.N_bytes; i++) + TESTASSERT(b2.msg[i] == b1.msg[i]); + return SRSRAN_SUCCESS; +} + +int test4() +{ + srsran::rlc_amd_pdu_header_t h; + srsran::byte_buffer_t b1, b2; + + memcpy(b1.msg, &pdu4[0], PDU4_LEN); + b1.N_bytes = PDU4_LEN; + rlc_am_read_data_pdu_header(&b1, &h); + TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); + TESTASSERT(0x03 == h.fi); + TESTASSERT(2 == h.N_li); + TESTASSERT(1342 == h.li[0]); + TESTASSERT(1500 == h.li[1]); + TESTASSERT(0 == h.lsf); + TESTASSERT(0 == h.p); + TESTASSERT(0 == h.rf); + TESTASSERT(0 == h.so); + TESTASSERT(2 == h.sn); + rlc_am_write_data_pdu_header(&h, &b2); + TESTASSERT(b2.N_bytes == PDU4_LEN); + for (uint32_t i = 0; i < b2.N_bytes; i++) + TESTASSERT(b2.msg[i] == b1.msg[i]); + return SRSRAN_SUCCESS; +} + +int test5() +{ + uint8_t tv[] = { + 0x9a, 0x31, 0xc6, 0xb7, 0x48, 0x61, 0x6e, 0xc3, 0x97, 0x48, 0xa6, 0xe3, 0xac, 0xc0, 0x75, 0x9f, 0xb2, 0xc8, 0xed, + 0xb0, 0xad, 0xcc, 0xc2, 0x0d, 0x28, 0xdd, 0xbb, 0x4b, 0x4c, 0xc5, 0xfc, 0x52, 0x40, 0xc2, 0x09, 0x89, 0x23, 0x77, + 0x4c, 0xba, 0xbf, 0xf2, 0x9d, 0x6b, 0xb2, 0x12, 0x0b, 0x64, 0xda, 0xf8, 0x14, 0x88, 0xc4, 0xd7, 0x95, 0xec, 0xb4, + 0x50, 0x37, 0x00, 0x15, 0x33, 0x52, 0x56, 0xf5, 0x5b, 0xdf, 0x18, 0xd2, 0x2b, 0xd2, 0x92, 0x1d, 0x6f, 0xfd, 0xcf, + 0x82, 0x08, 0x33, 0x5c, 0x00, 0x48, 0xe4, 0xc4, 0x1f, 0x79, 0xb0, 0xd3, 0xca, 0xe8, 0xd3, 0xdf, 0x1b, 0x25, 0x35, + 0x11, 0x80, 0x14, 0x29, 0x52, 0x3f, 0xfc, 0xe4, 0x5c, 0x6b, 0xe2, 0x2b, 0xed, 0xea, 0x5f, 0x4a, 0xeb, 0xa7, 0x2e, + 0xaf, 0xc6, 0xa8, 0x60, 0x99, 0x72, 0x48, 0x6c, 0x51, 0x63, 0x91, 0x87, 0x74, 0x11, 0x9b, 0x9e, 0x63, 0xdb, 0x9a, + 0x48, 0x37, 0x05, 0x2a, 0x63, 0xf3, 0x14, 0xc2, 0x3d, 0xff, 0x69, 0x6b, 0xaf, 0x2f, 0x13, 0x0f, 0xc8, 0x85, 0x57, + 0x34, 0xd7, 0xba, 0xc5, 0x5e, 0x2f, 0xd6, 0xf9, 0xcd, 0x39, 0xd4, 0x67, 0x81, 0x0c, 0x6c, 0xab, 0x6f, 0x5f, 0xc0, + 0x31, 0x9c, 0xbf, 0x9a, 0x08, 0x6e, 0xc9, 0x1b, 0x7d, 0x91, 0xa1, 0xd4, 0xc5, 0x78, 0x8f, 0x8a, 0xd6, 0xbe, 0x60, + 0xcf, 0x8b, 0x99, 0xa4, 0xf2, 0x1b, 0xb0, 0x5e, 0xc6, 0x1f, 0xbe, 0x86, 0x50, 0x5a, 0x46, 0xea, 0x62, 0xb4, 0xb3, + 0x7e, 0x32, 0x44, 0x1f, 0x06, 0x09, 0x97, 0x95, 0x93, 0x6d, 0x53, 0xf3, 0x3c, 0xde, 0x8c, 0xe0, 0xd0, 0xa7, 0x90, + 0x2f, 0x6e, 0xaf, 0xed, 0xf4, 0xff, 0x47, 0x3a, 0xe9, 0xaa, 0xef, 0x9c, 0x28, 0x21, 0xe0, 0x47, 0x27, 0xe9, 0xde, + 0xbd, 0x7c, 0x4b, 0x10, 0x6f, 0x87, 0xef, 0xfc, 0x68, 0xbf, 0xa3, 0xf8, 0xee, 0x11, 0xa8, 0xdb, 0x06, 0xa7, 0x23, + 0x40, 0x91, 0xcd, 0x2f, 0x2d, 0xf5, 0x50, 0x0e, 0x3c, 0x78, 0xf7, 0x1a, 0x35, 0x74, 0x65, 0x45, 0xe3, 0xec, 0x34, + 0xdf, 0x54, 0xf4, 0x83, 0x4d, 0xe2, 0x94, 0xf5, 0xbe, 0x9a, 0x9c, 0xe1, 0xdb, 0x2d, 0xae, 0x0a, 0x5b, 0xa3, 0x5b, + 0x69, 0xdf, 0xd3, 0x60, 0xf9, 0x08, 0xd4, 0x5e, 0x4d, 0xb8, 0x4a, 0x82, 0x97, 0x9f, 0x76, 0x1a, 0xec, 0x58, 0xaf, + 0xe1, 0x16, 0x49, 0x7d, 0xf7, 0x24, 0xab, 0xa5, 0x2f, 0x06, 0x48, 0x8a, 0x6f, 0x27, 0x5d, 0xcf, 0x20, 0x65, 0xa4, + 0x7e, 0xb2, 0x5c, 0xc9, 0x34, 0xf3, 0x68, 0xaa, 0x0e, 0x54, 0x03, 0xbd, 0x35, 0x19, 0x06, 0xb2, 0x11, 0x2b, 0x5d, + 0xb6, 0x5a, 0x63, 0xff, 0xe4, 0xd2, 0x26, 0x41, 0xa2, 0x47, 0xa6, 0x46, 0xc5, 0x58, 0xa2, 0x8e, 0x8d, 0x95, 0xf6, + 0x37, 0xa3, 0x4a, 0x3a, 0x60, 0x7f, 0x54, 0x67, 0x32, 0x65, 0x92, 0x8f, 0x1b, 0xec, 0xf3, 0x1a, 0xd0, 0xc5, 0x41, + 0x11, 0x67, 0x88, 0xb7, 0xad, 0x4d, 0x0f, 0x4f, 0xdc, 0x9c, 0xe5, 0xd2, 0xd4, 0x88, 0x1d, 0x0e, 0xe9, 0x9c, 0x62, + 0x50, 0xce, 0xc7, 0xe2, 0x5e, 0xe3, 0xce, 0x51, 0xfd, 0x9e, 0x16, 0x3e, 0xaf, 0x7e, 0xc6, 0x66, 0x2b, 0x14, 0x75, + 0x7b, 0xf0, 0x12, 0x60, 0xc2, 0xe6, 0xe8, 0xdf, 0xf4, 0xd1, 0x7c, 0x57, 0x21, 0x4a, 0x1e, 0x03, 0xa8, 0x01, 0xd1, + 0xf9, 0xff, 0x6f, 0x10, 0x3d, 0x1e, 0x8e, 0x04, 0x84, 0xb9, 0x18, 0xfa, 0x34, 0x08, 0x0c, 0x94, 0xca, 0xf2, 0x7d, + 0xaa, 0xe6, 0x4e, 0x26, 0x3d, 0x70, 0x70, 0x5c, 0x73, 0x19, 0x5d, 0x45, 0x12, 0x5c, 0xb4, 0x22, 0x9a, 0xd3, 0xb0, + 0x9e, 0x57, 0x6a, 0xb6, 0x51, 0x9e, 0xbe, 0x5d, 0x33, 0x88, 0x4f, 0xb0, 0x32, 0x36, 0xfe, 0x58, 0x73, 0x6e, 0xc9, + 0xcf, 0xe2, 0xe2, 0x2d, 0x27, 0xf4, 0x89, 0xdb, 0x17, 0x23, 0xae, 0xc7, 0xc1, 0x06, 0x31, 0x77, 0x57, 0xd0, 0x35, + 0xb5, 0x03, 0xbe, 0x04, 0xb3, 0xf0, 0x3a, 0xb1, 0x49, 0xae, 0x20, 0x12, 0x7d, 0x02, 0xf4, 0xaa, 0x29, 0xe8, 0x34, + 0x04, 0xff, 0x57, 0xb3, 0xc7, 0x19, 0xb9, 0xf8, 0x90, 0x10, 0xc8, 0xc6, 0xc5, 0xcb, 0x84, 0xca, 0x7e, 0x74, 0x04, + 0x30, 0xbd, 0xb2, 0x50, 0xcf, 0x30, 0x52, 0xc3, 0xda, 0x7b, 0xac, 0x0e, 0x7f, 0xab, 0x66, 0x32, 0x72, 0x7f, 0xeb, + 0x6b, 0x0f, 0xfc, 0x33, 0xd5, 0xc1, 0xff, 0x59, 0x8b, 0x7d, 0xce, 0x90, 0xad, 0x8b, 0x42, 0xfd, 0x5b, 0x72, 0x4f, + 0x1e, 0x4d, 0xca, 0xca, 0x5b, 0x4a, 0x76, 0xc1, 0x7c, 0xe8, 0x40, 0x68, 0x53, 0x50, 0x64, 0x87, 0x25, 0x25, 0x86, + 0x7f, 0xb1, 0x03, 0x4d, 0x41, 0xb1, 0xd8, 0x83, 0xae, 0x33, 0xf6, 0xfe, 0x52, 0x43, 0xc8, 0x1c, 0x9e, 0x12, 0x92, + 0x60, 0x8f, 0x7b, 0xa0, 0xf7, 0xce, 0xf0, 0x5b, 0x55, 0x16, 0x80, 0xdb, 0x95, 0x31, 0xdf, 0xe2, 0x72, 0x90, 0xba, + 0xf6, 0x3e, 0xee, 0xec, 0x3c, 0x40, 0x2f, 0x05, 0x5c, 0xcd, 0x17, 0xef, 0x2d, 0xa6, 0x6a, 0xce, 0x9d, 0x38, 0xbe, + 0xf8, 0x8e, 0xd4, 0x79, 0x69, 0x69, 0xaa, 0x48, 0x4b, 0x1d, 0xd8, 0x06, 0x13, 0x17, 0xf4, 0xff, 0x53, 0x34, 0x2e, + 0x58, 0x90, 0xfb, 0x70, 0x7f, 0x29, 0x16, 0xe9, 0xf7, 0xb4, 0x22, 0xb5, 0xac, 0xb0, 0x8a, 0x25, 0x19, 0xf3, 0xd0, + 0x62, 0x3f, 0xed, 0x3a, 0x45, 0x00, 0x51, 0x39, 0xff, 0xa5, 0x6d, 0x2d, 0xfd, 0xfd, 0x28, 0x6d, 0x7d, 0x51, 0x84, + 0x66, 0x48, 0x38, 0x88, 0xfe, 0xe4, 0x38, 0x88, 0x0a, 0x52, 0x7b, 0xda, 0xb4, 0xba, 0xc7, 0xee, 0xff, 0xc7, 0x40, + 0x38, 0xc6, 0xe5, 0xa5, 0xf3, 0xe2, 0xa3, 0x1b, 0x50, 0x20, 0x6d, 0xd4, 0x86, 0xb5, 0x0c, 0x0f, 0xb3, 0xf0, 0x47, + 0x3b, 0xfa, 0x99, 0xb7, 0xd4, 0x4d, 0x71, 0x9b, 0x3c, 0x71, 0x62, 0x7c, 0xa9, 0x28, 0x61, 0x4f, 0x1b, 0x43, 0xf2, + 0x37, 0x93, 0x12, 0xa4, 0x67, 0x98, 0x59, 0x73, 0xa7, 0x0d, 0x64, 0xef, 0x48, 0x5e, 0x88, 0xff, 0x33, 0xd6, 0x71, + 0xce, 0x12, 0xe2, 0x31, 0x8e, 0x8b, 0x59, 0xef, 0xda, 0x75, 0x32, 0xcc, 0xac, 0xc6, 0xde, 0x50, 0x2d, 0x77, 0xa9, + 0xa1, 0x1e, 0xb6, 0x05, 0x0d, 0xff, 0x63, 0x96, 0xfe, 0x96, 0x6c, 0x6f, 0x65, 0x7e, 0x51, 0x96, 0x0c, 0xdd, 0xef, + 0xfb, 0xb7, 0x64, 0x2d, 0x84, 0x10, 0xf3, 0x62, 0x60, 0x21, 0xd9, 0x0a, 0xc2, 0xf8, 0xc0, 0xc7, 0x05, 0xbf, 0x2a, + 0x9b, 0xbe, 0xc1, 0x07, 0x2d, 0x26, 0x85, 0x7f, 0xbc, 0x91, 0x5c, 0xab, 0x8c, 0x13, 0x10, 0xba, 0x97, 0x20, 0xad, + 0xfa, 0x81, 0xce, 0xd3, 0x8b, 0x90, 0xcb, 0x4b, 0x57, 0xd1, 0x0b, 0x82, 0x6c, 0xc9, 0x43, 0x74, 0xf6, 0x69, 0xf9, + 0x75, 0x25, 0x8b, 0xd1, 0xd0, 0x17, 0xe5, 0xe0, 0xd1, 0x7c, 0x01, 0x7f, 0x76, 0x82, 0x4d, 0x4a, 0x0d, 0xde, 0x15, + 0x58, 0x35, 0xe6, 0x63, 0xb7, 0x53, 0x2c, 0xfa, 0xc7, 0x23, 0x63, 0xc0, 0x98, 0x88, 0x4b, 0x6a, 0x59, 0x63, 0x4f, + 0x39, 0x34, 0xcb, 0x3a, 0xb3, 0x42, 0xbc, 0x01, 0x8c, 0xc9, 0xdf, 0xa1, 0x22, 0x14, 0x88, 0x85, 0xcc, 0xdb, 0xb2, + 0xc6, 0xa2, 0xd5, 0x2a, 0x62, 0x6d, 0xb2, 0xae, 0xd7, 0x0b, 0x11, 0x26, 0x45, 0x45, 0xf2, 0x7f, 0xf9, 0x34, 0x3c, + 0xfa, 0xc0, 0x05, 0xd9, 0x61, 0x27, 0xed, 0xe9, 0xad, 0xb9, 0xc4, 0x5f, 0x80, 0x66, 0x34, 0xaa, 0xc9, 0xa1, 0x5c, + 0x77, 0x79, 0x68, 0x88, 0x9f, 0xad, 0xcd, 0x91, 0x2c, 0xc6, 0xc5, 0x68, 0xc0, 0x85, 0x6e, 0x99, 0xe7, 0x95, 0x87, + 0xd0, 0x42, 0x40, 0x95, 0xa1, 0xc0, 0xfb, 0xd5, 0x6a, 0xc4, 0x77, 0xc7, 0x3b, 0xf0, 0x2f, 0xc3, 0x8f, 0xdc, 0x91, + 0x21, 0x08, 0x57}; + + srsran::rlc_amd_pdu_header_t h; + srsran::byte_buffer_t b1; + + memcpy(b1.msg, tv, sizeof(tv)); + b1.N_bytes = sizeof(tv); + uint32_t nof_payload_bytes = sizeof(tv); + rlc_am_read_data_pdu_header(&b1.msg, &nof_payload_bytes, &h); + + TESTASSERT(nof_payload_bytes == 1046); + TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); + TESTASSERT(RLC_FI_FIELD_NOT_START_OR_END_ALIGNED == h.fi); + TESTASSERT(0 == h.N_li); + TESTASSERT(0 == h.lsf); + TESTASSERT(0 == h.p); + TESTASSERT(0 == h.rf); + TESTASSERT(0 == h.so); + TESTASSERT(561 == h.sn); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + srslog::init(); + + TESTASSERT(test1() == SRSRAN_SUCCESS); + TESTASSERT(test2() == SRSRAN_SUCCESS); + TESTASSERT(test3() == SRSRAN_SUCCESS); + TESTASSERT(test4() == SRSRAN_SUCCESS); + TESTASSERT(test5() == SRSRAN_SUCCESS); + + return SRSRAN_SUCCESS; +} diff --git a/lib/test/upper/rlc_am_test.cc b/lib/test/rlc/rlc_am_lte_test.cc similarity index 79% rename from lib/test/upper/rlc_am_test.cc rename to lib/test/rlc/rlc_am_lte_test.cc index 31039f186..090165c52 100644 --- a/lib/test/upper/rlc_am_test.cc +++ b/lib/test/rlc/rlc_am_lte_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,13 +19,14 @@ * */ +#include "rlc_test_common.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/rlc_pcap.h" #include "srsran/common/test_common.h" #include "srsran/common/threads.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h" -#include "srsran/upper/rlc_am_lte.h" +#include "srsran/rlc/rlc_am_lte.h" #define NBUFS 5 #define HAVE_PCAP 0 @@ -34,65 +35,10 @@ using namespace srsue; using namespace srsran; -bool rx_is_tx(const rlc_bearer_metrics_t& rlc1_metrics, const rlc_bearer_metrics_t& rlc2_metrics) -{ - if (rlc1_metrics.num_tx_pdu_bytes != rlc2_metrics.num_rx_pdu_bytes) { - return false; - } - - if (rlc2_metrics.num_tx_pdu_bytes != rlc1_metrics.num_rx_pdu_bytes) { - return false; - } - return true; -} - -class rlc_am_tester : public pdcp_interface_rlc, public rrc_interface_rlc -{ -public: - rlc_am_tester(rlc_pcap* pcap_ = NULL) : pcap(pcap_) {} - - // PDCP interface - void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu) - { - assert(lcid == 1); - sdus.push_back(std::move(sdu)); - } - void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} - void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {} - void write_pdu_pcch(unique_byte_buffer_t sdu) {} - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} - void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec) - { - assert(lcid == 1); - for (uint32_t pdcp_sn : pdcp_sn_vec) { - if (notified_counts.find(pdcp_sn) == notified_counts.end()) { - notified_counts[pdcp_sn] = 0; - } - notified_counts[pdcp_sn] += 1; - } - } - void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec) - { - assert(lcid == 1); - // TODO - } - - // RRC interface - void max_retx_attempted() { max_retx_triggered = true; } - - const char* get_rb_name(uint32_t lcid) { return ""; } - - std::vector sdus; - rlc_pcap* pcap = nullptr; - bool max_retx_triggered = false; - - std::map notified_counts; // Map of PDCP SNs to number of notifications -}; - class ul_writer : public thread { public: - ul_writer(rlc_am_lte* rlc_) : rlc(rlc_), running(false), thread("UL_WRITER") {} + ul_writer(rlc_am* rlc_) : rlc(rlc_), thread("UL_WRITER") {} ~ul_writer() { stop(); } void stop() { @@ -128,11 +74,11 @@ private: running = false; } - rlc_am_lte* rlc; - bool running; + rlc_am* rlc = nullptr; + std::atomic running = {false}; }; -int basic_test_tx(rlc_am_lte* rlc, byte_buffer_t pdu_bufs[NBUFS]) +int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS]) { // Push 5 SDUs into RLC1 unique_byte_buffer_t sdu_bufs[NBUFS]; @@ -159,12 +105,12 @@ int basic_test_tx(rlc_am_lte* rlc, byte_buffer_t pdu_bufs[NBUFS]) int basic_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); // before configuring entity TESTASSERT(0 == rlc1.get_buffer_state()); @@ -197,6 +143,7 @@ int basic_test() rlc_status_pdu_t status_check = {}; rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check); TESTASSERT(status_check.ack_sn == 5); // 5 is the last SN that was not received. + TESTASSERT(rlc_am_is_valid_status_pdu(status_check)); // Write status PDU to RLC1 rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); @@ -217,11 +164,11 @@ int basic_test() int concat_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -291,12 +238,12 @@ int concat_test() int segment_test(bool in_seq_rx) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -386,12 +333,12 @@ int segment_test(bool in_seq_rx) int retx_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -522,11 +469,11 @@ int retx_test() // Test correct upper layer signaling when maxRetx (default 4) have been reached int max_retx_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_config(); if (not rlc1.configure(rlc_cfg)) { @@ -585,12 +532,12 @@ int max_retx_test() // Purpose: test correct retx of lost segment and pollRetx timer expiration int segment_retx_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -660,6 +607,7 @@ int segment_retx_test() // Step timers again until poll Retx timeout expires for (int cnt = 0; cnt < 5; cnt++) { + TESTASSERT(rlc1.get_buffer_state() == 0); // No status transmissions until pollRetx expires timers.step_all(); } @@ -709,12 +657,12 @@ int resegment_test_1() // PDUs: | 10 | 10 | 10 | 10 | 10 | // Retx PDU segments: | 5 | 5| - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -867,11 +815,11 @@ int resegment_test_2() // PDUs: | 5 | 10 | 20 | 10 | 5 | // Retx PDU segments: | 10 | 10 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -999,11 +947,11 @@ int resegment_test_3() // PDUs: | 5 | 5| 20 | 10 | 10 | // Retx PDU segments: | 10 | 10 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -1129,11 +1077,11 @@ int resegment_test_4() // PDUs: | 5 | 5| 30 | 5 | 5| // Retx PDU segments: | 15 | 15 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -1261,11 +1209,11 @@ int resegment_test_5() // PDUs: |2|3| 40 |3|2| // Retx PDU segments: | 20 | 20 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -1387,12 +1335,12 @@ int resegment_test_6() // PDUs: |10|10|10| 270 | 54 | // Retx PDU segments: | 120 | 150 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -1554,14 +1502,14 @@ int resegment_test_7() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_test7.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, nullptr); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -1739,14 +1687,14 @@ int resegment_test_8() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_test8.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, nullptr); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -1891,14 +1839,14 @@ int resegment_test_9() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_resegment_test_9.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, nullptr); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(config)) { return SRSRAN_ERROR; @@ -2033,14 +1981,14 @@ int resegment_test_10() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_resegment_test_10.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(config)) { return SRSRAN_ERROR; @@ -2182,14 +2130,14 @@ int resegment_test_11() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_resegment_test_11.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(config)) { return SRSRAN_ERROR; @@ -2340,14 +2288,14 @@ int resegment_test_12() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_resegment_test_12.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(config)) { return SRSRAN_ERROR; @@ -2484,13 +2432,13 @@ int header_reconstruction_test(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -2547,13 +2495,13 @@ int header_reconstruction_test2(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test2.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; } @@ -2610,14 +2558,14 @@ int header_reconstruction_test3(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test3.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); // configure RLC - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; } @@ -2696,14 +2644,14 @@ int header_reconstruction_test4(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test4.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); // configure RLC - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; } @@ -2773,14 +2721,14 @@ int header_reconstruction_test5(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test5.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); // configure RLC - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; } @@ -2852,14 +2800,14 @@ int header_reconstruction_test6(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test6.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); // configure RLC - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; } @@ -2951,14 +2899,14 @@ int header_reconstruction_test7(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test7.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); // configure RLC - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; } @@ -3053,14 +3001,14 @@ int header_reconstruction_test8(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test8.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); // configure RLC - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; } @@ -3087,11 +3035,11 @@ int header_reconstruction_test8(srsran::log_sink_message_spy& spy) bool reset_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -3129,11 +3077,11 @@ bool reset_test() bool resume_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -3171,10 +3119,10 @@ bool resume_test() bool stop_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -3197,12 +3145,12 @@ bool stop_test() // be enough to fit all SNs that would need to be NACKed bool status_pdu_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { return -1; @@ -3232,9 +3180,9 @@ bool status_pdu_test() TESTASSERT(0 == rlc1.get_buffer_state()); - // Only pass 2nd and last PDUs to RLC2 + // Only pass 1st and last PDUs to RLC2 for (uint32_t i = 0; i < n_pdus; ++i) { - if (i == 0 || i == 2 || i == n_pdus - 1) { + if (i == 0 || i == n_pdus - 1) { rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); } } @@ -3301,6 +3249,179 @@ bool status_pdu_test() return SRSRAN_SUCCESS; } +// This test checks the correct handling of a sending RLC entity when an incorrect status PDU is injected. +// In this test, the receiver requests the retransmission of a SN that he has acknowledeged before. +// The incidence is reported to the upper layers. +bool incorrect_status_pdu_test() +{ + rlc_am_tester tester(true, nullptr); + srsran::timer_handler timers(8); + int len = 0; + + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + + // Push 5 SDUs into RLC1 + const uint32_t n_sdus = 10; + unique_byte_buffer_t sdu_bufs[n_sdus]; + for (uint32_t i = 0; i < n_sdus; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + // Read 5 PDUs from RLC1 (1 byte each) + const uint32_t n_pdus = n_sdus; + byte_buffer_t pdu_bufs[n_pdus]; + for (uint32_t i = 0; i < n_pdus; i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 2 byte header + 1 byte payload + pdu_bufs[i].N_bytes = len; + } + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Construct a status PDU that ACKs SN 1 + rlc_status_pdu_t status_pdu = {}; + status_pdu.ack_sn = 4; + status_pdu.N_nack = 3; + status_pdu.nacks[0].nack_sn = 0; + status_pdu.nacks[1].nack_sn = 2; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu)); + + // pack PDU and write to RLC + byte_buffer_t status_buf; + rlc_am_write_status_pdu(&status_pdu, &status_buf); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // This will remove SN=1 from the Tx window + + TESTASSERT(tester.protocol_failure_triggered == false); + + // construct a valid but conflicting status PDU that request SN=1 for retx + status_pdu.N_nack = 1; + status_pdu.nacks[0].nack_sn = 1; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu)); + + // pack and write to RLC again + rlc_am_write_status_pdu(&status_pdu, &status_buf); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + TESTASSERT(tester.protocol_failure_triggered == true); + return SRSRAN_SUCCESS; +} + +/// The test checks the correct detection of an out-of-order status PDUs +/// In contrast to the without explicitly NACK-ing specific SNs +bool incorrect_status_pdu_test2() +{ + rlc_am_tester tester(true, nullptr); + srsran::timer_handler timers(8); + int len = 0; + + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) { + return -1; + } + + // Push 10 SDUs into RLC1 + const uint32_t n_sdus = 10; + unique_byte_buffer_t sdu_bufs[n_sdus]; + for (uint32_t i = 0; i < n_sdus; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + // Read 10 PDUs from RLC1 (1 byte each) and push half of them to RLC2 + const uint32_t n_pdus = n_sdus; + byte_buffer_t pdu_bufs[n_pdus]; + for (uint32_t i = 0; i < n_pdus; i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 2 byte header + 1 byte payload + pdu_bufs[i].N_bytes = len; + if (i < 5) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } + } + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Construct a status PDU that ACKs all SNs + rlc_status_pdu_t status_pdu = {}; + status_pdu.ack_sn = 5; + status_pdu.N_nack = 0; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu)); + + // pack PDU and write to RLC + byte_buffer_t status_buf; + rlc_am_write_status_pdu(&status_pdu, &status_buf); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + TESTASSERT(tester.protocol_failure_triggered == false); + + // construct a valid but conflicting status PDU that acks a lower SN and requests SN=1 for retx + status_pdu.ack_sn = 3; + status_pdu.N_nack = 1; + status_pdu.nacks[0].nack_sn = 1; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu)); + + // pack and write to RLC again + rlc_am_write_status_pdu(&status_pdu, &status_buf); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // the PDU should be dropped + + // resend first Status PDU again + status_pdu.ack_sn = 5; + status_pdu.N_nack = 0; + TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu)); + + // pack and write to RLC again + rlc_am_write_status_pdu(&status_pdu, &status_buf); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Step timers until reordering timeout expires + int cnt = 5; + while (cnt--) { + timers.step_all(); + } + + // retransmit all outstanding PDUs + for (int i = 0; i < 5; i++) { + byte_buffer_t retx; + retx.N_bytes = rlc1.read_pdu(retx.msg, 3); + rlc2.write_pdu(retx.msg, retx.N_bytes); + + // Step timers until reordering timeout expires + int cnt = 5; + while (cnt--) { + timers.step_all(); + } + + // read status + byte_buffer_t status_buf; + status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + } + + TESTASSERT(tester.sdus.size() == n_sdus); + for (uint32_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT(tester.sdus[i]->N_bytes == 1); + } + + return SRSRAN_SUCCESS; +} + // This test checks the correct functioning of RLC reestablishment // after maxRetx attempt. bool reestablish_test() @@ -3309,15 +3430,15 @@ bool reestablish_test() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_reestablish_test.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, nullptr); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); @@ -3411,7 +3532,7 @@ bool reestablish_test() } } - TESTASSERT(tester.sdus.size() == 17); + TESTASSERT(tester.sdus.size() == 18); srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size()); @@ -3429,15 +3550,15 @@ bool discard_test() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_reestablish_test.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); - rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); - rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); @@ -3495,6 +3616,466 @@ bool discard_test() srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size()); +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSRAN_SUCCESS; +} + +// This test checks wether re-transmissions are triggered correctly in case the t-PollRetranmission expires. +// It checks if the poll retx timer is re-armed upon receiving an ACK for POLL_SN +bool poll_retx_expiry_test() +{ + rlc_config_t config = rlc_config_t::default_rlc_am_config(); + // [I] SRB1 configured: t_poll_retx=65, poll_pdu=-1, poll_byte=-1, max_retx_thresh=6, t_reordering=55, + // t_status_prohibit=0 + config.am.t_poll_retx = 65; + config.am.poll_pdu = -1; + config.am.poll_byte = -1; + config.am.max_retx_thresh = 6; + config.am.t_reordering = 55; + config.am.t_status_prohibit = 55; + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_poll_rext_expiry_test.pcap", config); + rlc_am_tester tester(true, &pcap); +#else + rlc_am_tester tester(true, NULL); +#endif + + srsran::timer_handler timers(8); + + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + if (not rlc1.configure(config)) { + return -1; + } + + if (not rlc2.configure(config)) { + return -1; + } + + // [I] SRB1 Tx SDU (135 B, tx_sdu_queue_len=1) + // [I] SRB1 Tx PDU SN=3 (91 B) + // [I] SRB1 Tx PDU SN=4 (48 B) + { + // Initial Tx + uint32_t num_tx_pdus = 1; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 135; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + TESTASSERT(pdu1 != nullptr); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 91); + + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + TESTASSERT(pdu2 != nullptr); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 48); + + // Deliver PDU2 to RLC2. PDU1 is lost + rlc2.write_pdu(pdu2->msg, pdu2->N_bytes); + } + + // Step timers until t-PollRetransmission timer expires on RLC1 + // t-Reordering timer also will expire on RLC2, so we can get an status report. + // [I] SRB1 Schedule SN=3 for reTx + for (int cnt = 0; cnt < 65; cnt++) { + timers.step_all(); + } + + uint32_t status_size = rlc2.get_buffer_state(); + srslog::flush(); + TESTASSERT(4 == status_size); + + // Read status PDU from RLC2 + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + TESTASSERT(status_buf != nullptr); + int len = rlc2.read_pdu(status_buf->msg, status_size); + status_buf->N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_status_pdu_t status_check = {}; + rlc_am_read_status_pdu(status_buf->msg, status_buf->N_bytes, &status_check); + TESTASSERT(status_check.ack_sn == 2); // 2 is the SN after the largest SN received. + TESTASSERT(status_check.N_nack == 1); // 1 PDU lost + TESTASSERT(rlc_am_is_valid_status_pdu(status_check)); + + // [I] SRB1 Retx PDU segment SN=3 [so=0] (83 B) (attempt 2/6) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 83); + } + + // [I] SRB1 Retx PDU segment SN=3 [so=79] (14 B) (attempt 2/6) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 79); + } + + // Deliver status PDU after ReTX to RLC1. This should restart t-PollRetransmission + TESTASSERT_EQ(false, rlc1.has_data()); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + TESTASSERT_EQ(true, rlc1.has_data()); + + // [I] SRB1 Retx PDU segment SN=3 [so=0] (83 B) (attempt 3/6) (received a NACK and retx...) + // [I] SRB1 Retx PDU segment SN=3 [so=79] (14 B) (attempt 3/6) + { + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + TESTASSERT(pdu1 != nullptr); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 83); + + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + TESTASSERT(pdu2 != nullptr); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 14); + } + + TESTASSERT_EQ(false, rlc1.has_data()); + + // Step timers until t-PollRetransmission timer expires on RLC1 + // [I] SRB1 Schedule SN=3 for reTx + + for (int cnt = 0; cnt < 66; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(true, rlc1.has_data()); + srslog::fetch_basic_logger("TEST").info("t-Poll Retransmssion successfully restarted."); + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSRAN_SUCCESS; +} + +bool full_window_check_test() +{ + rlc_config_t config = rlc_config_t::default_rlc_am_config(); + // [I] SRB1 configured: t_poll_retx=65, poll_pdu=-1, poll_byte=-1, max_retx_thresh=6, t_reordering=55, + // t_status_prohibit=0 + config.am.t_poll_retx = 65; + config.am.poll_pdu = -1; + config.am.poll_byte = -1; + config.am.max_retx_thresh = 6; + config.am.t_reordering = 55; + config.am.t_status_prohibit = 55; + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_poll_rext_expiry_test.pcap", config); + rlc_am_tester tester(true, &pcap); +#else + rlc_am_tester tester(true, NULL); +#endif + + srsran::timer_handler timers(8); + + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + if (not rlc1.configure(config)) { + return -1; + } + + if (not rlc2.configure(config)) { + return -1; + } + + { + // Initial Tx + uint32_t num_tx_pdus = 512; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->msg[0] = i; + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = 1; + pdu->msg[0] = i; + pdu->md.pdcp_sn = i; + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + } + } + { + // Tx one more to check the window is full + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->msg[0] = 0; + sdu->md.pdcp_sn = 512; + rlc1.write_sdu(std::move(sdu)); + + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + + // If the TX window is full, we should RETX SN=0 + rlc_amd_pdu_header_t header = {}; + rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header); + TESTASSERT_EQ(header.sn, 0); + TESTASSERT_EQ(header.N_li, 0); + TESTASSERT_EQ(header.fi, 0); + } + + // Ack one SN in the middle of the TX window. + // This is done to make sure the full window check is correct + // even if PDUs in the middle of the window are ACKed. + // ACK_SN=3, NACK_SN=0 + { + rlc_status_pdu_t status = {}; + status.ack_sn = 3; + status.N_nack = 1; + status.nacks[0].nack_sn = 0; + + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + TESTASSERT(status_buf != nullptr); + rlc_am_write_status_pdu(&status, status_buf.get()); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + + // Read RETX for SN=0 from NACK + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + + // Check RETX SN=0 + rlc_amd_pdu_header_t header = {}; + rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header); + TESTASSERT_EQ(header.sn, 0); + TESTASSERT_EQ(header.N_li, 0); + TESTASSERT_EQ(header.fi, 0); + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + } + { + // Tx more PDUs to check the window is still full + uint32_t num_tx_pdus = 2; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->msg[0] = i; + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = 1; + pdu->msg[0] = i; + pdu->md.pdcp_sn = i; + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + + // If the TX window is full, we should RETX SN=0 + rlc_amd_pdu_header_t header = {}; + rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header); + TESTASSERT_EQ(header.sn, 0); + TESTASSERT_EQ(header.N_li, 0); + TESTASSERT_EQ(header.fi, 0); + } + } + // ACK more PDUs and advance VT(A). + // New PDUs should be available to read now. + { + rlc_status_pdu_t status = {}; + status.ack_sn = 5; + status.N_nack = 0; + + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + TESTASSERT(status_buf != nullptr); + rlc_am_write_status_pdu(&status, status_buf.get()); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + + // Read new PDU + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + + // If the TX window is no longer full, we should TX a new SN (SN=512) + rlc_amd_pdu_header_t header = {}; + rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header); + TESTASSERT_EQ(header.sn, 512); + TESTASSERT_EQ(header.N_li, 0); + TESTASSERT_EQ(header.fi, 0); + } + +#if HAVE_PCAP + pcap.close(); +#endif + + return SRSRAN_SUCCESS; +} + +bool full_window_check_wraparound_test() +{ + rlc_config_t config = rlc_config_t::default_rlc_am_config(); + // [I] SRB1 configured: t_poll_retx=65, poll_pdu=-1, poll_byte=-1, max_retx_thresh=6, t_reordering=55, + // t_status_prohibit=0 + config.am.t_poll_retx = 65; + config.am.poll_pdu = -1; + config.am.poll_byte = -1; + config.am.max_retx_thresh = 6; + config.am.t_reordering = 55; + config.am.t_status_prohibit = 55; + +#if HAVE_PCAP + rlc_pcap pcap; + pcap.open("rlc_am_poll_rext_expiry_test.pcap", config); + rlc_am_tester tester(true, &pcap); +#else + rlc_am_tester tester(true, NULL); +#endif + + uint32_t pdcp_count = 0; + + srsran::timer_handler timers(8); + + rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + if (not rlc1.configure(config)) { + return -1; + } + + if (not rlc2.configure(config)) { + return -1; + } + + // Advance vt_a to 512 and vt_s to 512 as well. + { + // Initial Tx + uint32_t num_tx_pdus = 512; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->msg[0] = i; + sdu->md.pdcp_sn = pdcp_count++; + rlc1.write_sdu(std::move(sdu)); + + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + } + + // ACK all SNs to advance the TX window. + rlc_status_pdu_t status = {}; + status.ack_sn = num_tx_pdus; + status.N_nack = 0; + + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + TESTASSERT(status_buf != nullptr); + rlc_am_write_status_pdu(&status, status_buf.get()); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + } + + // Advance vt_a and vt_s to 1023 + { + // Initial Tx + uint32_t num_tx_pdus = 511; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->msg[0] = i; + sdu->md.pdcp_sn = pdcp_count++; + rlc1.write_sdu(std::move(sdu)); + + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + } + + // ACK all SNs to advance the TX window. + rlc_status_pdu_t status = {}; + status.ack_sn = 512 + num_tx_pdus; + status.N_nack = 0; + + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + TESTASSERT(status_buf != nullptr); + rlc_am_write_status_pdu(&status, status_buf.get()); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + } + + // Now, fill up the window + { + // Initial Tx + uint32_t num_tx_pdus = 512; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->msg[0] = i; + sdu->md.pdcp_sn = pdcp_count++; + rlc1.write_sdu(std::move(sdu)); + + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + } + } + { + // Tx one more to check the window is full + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->msg[0] = 0; + sdu->md.pdcp_sn = pdcp_count++; + rlc1.write_sdu(std::move(sdu)); + + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 3); + TESTASSERT(pdu->N_bytes == 3); + + // If the TX window is full, we should RETX SN=1023 + rlc_amd_pdu_header_t header = {}; + rlc_am_read_data_pdu_header(&pdu->msg, &pdu->N_bytes, &header); + TESTASSERT_EQ(header.sn, 1023); + TESTASSERT_EQ(header.N_li, 0); + TESTASSERT_EQ(header.fi, 0); + } + #if HAVE_PCAP pcap.close(); #endif @@ -3691,10 +4272,34 @@ int main(int argc, char** argv) exit(-1); }; + if (incorrect_status_pdu_test()) { + printf("incorrect_status_pdu_test failed\n"); + exit(-1); + }; + + if (incorrect_status_pdu_test2()) { + printf("incorrect_status_pdu_test2 failed\n"); + exit(-1); + }; + if (discard_test()) { printf("discard_test failed\n"); exit(-1); }; + if (poll_retx_expiry_test()) { + printf("poll_retx_expiry_test failed\n"); + exit(-1); + }; + + if (full_window_check_test()) { + printf("full_window_check_test failed\n"); + exit(-1); + }; + + if (full_window_check_wraparound_test()) { + printf("full_window_check_wraparound_test failed\n"); + exit(-1); + }; return SRSRAN_SUCCESS; } diff --git a/lib/test/rlc/rlc_am_nr_pdu_test.cc b/lib/test/rlc/rlc_am_nr_pdu_test.cc new file mode 100644 index 000000000..21688809d --- /dev/null +++ b/lib/test/rlc/rlc_am_nr_pdu_test.cc @@ -0,0 +1,3089 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/config.h" +#include "srsran/rlc/rlc.h" +#include "srsran/rlc/rlc_am_nr_packing.h" + +#include +#include +#include +#include +#include + +#define PCAP_CRNTI (0x1001) +#define PCAP_TTI (666) + +using namespace srsran; + +#include "srsran/common/mac_pcap.h" +#include "srsran/mac/mac_rar_pdu_nr.h" +#include "srsran/mac/mac_sch_pdu_nr.h" +static std::unique_ptr pcap_handle = nullptr; + +int write_pdu_to_pcap(const uint32_t lcid, const uint8_t* payload, const uint32_t len) +{ + if (pcap_handle) { + byte_buffer_t tx_buffer; + srsran::mac_sch_pdu_nr tx_pdu; + tx_pdu.init_tx(&tx_buffer, len + 10); + tx_pdu.add_sdu(lcid, payload, len); + tx_pdu.pack(); + pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + return SRSRAN_SUCCESS; + } + return SRSRAN_ERROR; +} + +template +srsran::byte_buffer_t make_pdu_and_log(const std::array& tv) +{ + srsran::byte_buffer_t pdu; + memcpy(pdu.msg, tv.data(), tv.size()); + pdu.N_bytes = tv.size(); + write_pdu_to_pcap(4, tv.data(), tv.size()); + return pdu; +} + +void corrupt_pdu_header(srsran::byte_buffer_t& pdu, const uint32_t header_len, const uint32_t payload_len) +{ + // clear header only + for (uint32_t i = 0; i < header_len; i++) { + pdu.msg[i] = 0xaa; + } + pdu.msg += header_len; + pdu.N_bytes = payload_len; +} + +// RLC AM PDU 12bit with complete SDU +int rlc_am_nr_pdu_test1() +{ + test_delimit_logger delimiter("PDU test 1"); + const int header_len = 2, payload_len = 4; + std::array tv = {0x80, 0x00, 0x11, 0x22, 0x33, 0x44}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + // unpack PDU + rlc_am_nr_pdu_header_t header = {}; + TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &header) != 0); + TESTASSERT(header.si == rlc_nr_si_field_t::full_sdu); + + // clear header + corrupt_pdu_header(pdu, header_len, payload_len); + + // pack again + TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); + TESTASSERT(pdu.N_bytes == tv.size()); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// RLC AM PDU 12bit first segment of SDU with P flag and SN 511 +int rlc_am_nr_pdu_test2() +{ + test_delimit_logger delimiter("PDU test 2"); + const int header_len = 2, payload_len = 4; + std::array tv = {0xd1, 0xff, 0x11, 0x22, 0x33, 0x44}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + // unpack PDU + rlc_am_nr_pdu_header_t header = {}; + TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &header) != 0); + TESTASSERT(header.si == rlc_nr_si_field_t::first_segment); + TESTASSERT(header.sn == 511); + TESTASSERT(header.so == 0); + + // clear header + corrupt_pdu_header(pdu, header_len, payload_len); + + // pack again + TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// RLC AM PDU 12bit last segment of SDU without P flag and SN 0x0404 and SO 0x0404 (1028) +int rlc_am_nr_pdu_test3() +{ + test_delimit_logger delimiter("PDU test 3"); + const int header_len = 4, payload_len = 4; + std::array tv = {0xa4, 0x04, 0x04, 0x04, 0x11, 0x22, 0x33, 0x44}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + // unpack PDU + rlc_am_nr_pdu_header_t header = {}; + TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &header) != 0); + TESTASSERT(header.si == rlc_nr_si_field_t::last_segment); + TESTASSERT(header.sn == 1028); + TESTASSERT(header.so == 1028); + + // clear header + corrupt_pdu_header(pdu, header_len, payload_len); + + // pack again + TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// RLC AM PDU 18bit full SDU with P flag and SN 0x100000001000000010 (131586) +int rlc_am_nr_pdu_test4() +{ + test_delimit_logger delimiter("PDU test 4"); + const int header_len = 3, payload_len = 4; + std::array tv = {0xc2, 0x02, 0x02, 0x11, 0x22, 0x33, 0x44}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + // unpack PDU + rlc_am_nr_pdu_header_t header = {}; + TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &header) != 0); + TESTASSERT(header.si == rlc_nr_si_field_t::full_sdu); + TESTASSERT(header.sn == 131586); + TESTASSERT(header.so == 0); + + // clear header + corrupt_pdu_header(pdu, header_len, payload_len); + + // pack again + TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// RLC AM PDU 18bit middle part of SDU (SO 514) without P flag and SN 131327 +int rlc_am_nr_pdu_test5() +{ + test_delimit_logger delimiter("PDU test 5"); + const int header_len = 5, payload_len = 4; + std::array tv = {0xb2, 0x00, 0xff, 0x02, 0x02, 0x11, 0x22, 0x33, 0x44}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + // unpack PDU + rlc_am_nr_pdu_header_t header = {}; + TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &header) != 0); + TESTASSERT(header.si == rlc_nr_si_field_t::neither_first_nor_last_segment); + TESTASSERT(header.sn == 131327); + TESTASSERT(header.so == 514); + + // clear header + corrupt_pdu_header(pdu, header_len, payload_len); + + // pack again + TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Malformed RLC AM PDU 18bit with reserved bits set +int rlc_am_nr_pdu_test6() +{ + test_delimit_logger delimiter("PDU test 6"); + const int header_len = 5, payload_len = 4; + std::array tv = {0xb7, 0x00, 0xff, 0x02, 0x02, 0x11, 0x22, 0x33, 0x44}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + // unpack PDU + rlc_am_nr_pdu_header_t header = {}; + TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &header) == 0); + TESTASSERT(header.sn == 0); + + return SRSRAN_SUCCESS; +} + +///< Control PDU tests (12bit SN) +// Status PDU for 12bit SN with ACK_SN=2065 and no further NACK_SN (E1 bit not set) +int rlc_am_nr_control_pdu_12bit_sn_test1() +{ + test_delimit_logger delimiter("Control PDU (12bit SN) test 1"); + const int len = 3; + std::array tv = {0x08, 0x11, 0x00}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 2065); + TESTASSERT(status_pdu.nacks.size() == 0); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 12bit SN with ACK_SN=2065 and NACK_SN=273 (E1 bit set) +int rlc_am_nr_control_pdu_12bit_sn_test2() +{ + test_delimit_logger delimiter("Control PDU (12bit SN) test 2"); + const int len = 5; + std::array tv = {0x08, 0x11, 0x80, 0x11, 0x10}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 2065); + TESTASSERT(status_pdu.nacks.size() == 1); + TESTASSERT(status_pdu.nacks[0].nack_sn == 273); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275, SO_START=5, SO_END=0xFFFF +// E1 and E2 bit set on first NACK, only E2 on second. +int rlc_am_nr_control_pdu_12bit_sn_test3() +{ + test_delimit_logger delimiter("Control PDU (12bit SN) test 3"); + const int len = 15; + std::array tv = { + 0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x34, 0x00, 0x05, 0xFF, 0xFF}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 2065); + TESTASSERT(status_pdu.nacks.size() == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 273); + TESTASSERT(status_pdu.nacks[0].so_start == 2); + TESTASSERT(status_pdu.nacks[0].so_end == 5); + TESTASSERT(status_pdu.nacks[1].nack_sn == 275); + TESTASSERT(status_pdu.nacks[1].so_start == 5); + TESTASSERT(status_pdu.nacks[1].so_end == 0xFFFF); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275 +// E1 and E2 bit set on first NACK, neither E1 or E2 on the second. +int rlc_am_nr_control_pdu_12bit_sn_test4() +{ + test_delimit_logger delimiter("Control PDU (12bit SN) test 4"); + const int len = 11; + std::array tv = {0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x30}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 2065); + TESTASSERT(status_pdu.nacks.size() == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 273); + TESTASSERT(status_pdu.nacks[0].has_so == true); + TESTASSERT(status_pdu.nacks[0].so_start == 2); + TESTASSERT(status_pdu.nacks[0].so_end == 5); + TESTASSERT(status_pdu.nacks[1].nack_sn == 275); + TESTASSERT(status_pdu.nacks[1].has_so == false); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Malformed Status PDU, with E1 still set at the end of the PDU +// 12bit SN with ACK_SN=2065, NACK_SN=273, SO_START=2, SO_END=5, NACK_SN=275, SO_START=5, SO_END=0xFFFF +// E1 and E2 bit set on first NACK, only E2 on second. +int rlc_am_nr_control_pdu_12bit_sn_test5() +{ + test_delimit_logger delimiter("Control PDU (12bit SN) test 5"); + const int len = 15; + std::array tv = { + 0x08, 0x11, 0x80, 0x11, 0x1c, 0x00, 0x02, 0x00, 0x05, 0x11, 0x3c, 0x00, 0x05, 0xFF, 0xFF}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 12bit SN with ACK_SN=2065, +// NACK range0: 3 full SDUs, NACK_SN=273..275 +// NACK range1: missing segment sequence across 4 SDUs +// starting at NACK_SN=276, SO_START=2, +// ending at NACK_SN=279, SO_END=5 +// E1 and E3 bit set on first NACK, E2 and E3 bit set on the second. +int rlc_am_nr_control_pdu_12bit_sn_test_nack_range() +{ + test_delimit_logger delimiter("Control PDU (12bit SN) test NACK range"); + const int len = 13; + std::array tv = {0x08, // D/C | 3CPT | 4ACK_SN_upper + 0x11, // 8ACK_SN_lower + 0x80, // E1 | 7R + 0x11, // 8NACK_SN_upper + 0x1a, // 4NACK_SN_lower | E1 | E2 | E3 | R + 0x03, // 8NACK_range + 0x11, // 8NACK_SN_upper + 0x46, // 4NACK_SN_lower | E1 | E2 | E3 | R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0x04}; // 8NACK_range + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size12bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 2065); + TESTASSERT(status_pdu.nacks.size() == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 273); + TESTASSERT(status_pdu.nacks[0].has_so == false); + TESTASSERT(status_pdu.nacks[0].has_nack_range == true); + TESTASSERT(status_pdu.nacks[0].nack_range == 3); + + TESTASSERT(status_pdu.nacks[1].nack_sn == 276); + TESTASSERT(status_pdu.nacks[1].has_so == true); + TESTASSERT(status_pdu.nacks[1].so_start == 2); + TESTASSERT(status_pdu.nacks[1].so_end == 5); + TESTASSERT(status_pdu.nacks[1].has_nack_range == true); + TESTASSERT(status_pdu.nacks[1].nack_range == 4); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Test merge of NACKs upon status PDU creation -- previous NACK: non-range; next NACK: non-range +int rlc_am_nr_control_pdu_test_nack_merge_sdu_sdu(rlc_am_nr_sn_size_t sn_size) +{ + test_delimit_logger delimiter("Control PDU ({} bit SN) test NACK merge: SDU + SDU", to_number(sn_size)); + + const uint16_t so_end_of_sdu = rlc_status_nack_t::so_end_of_sdu; + const uint32_t mod_nr = cardinality(sn_size); + const uint32_t min_size = 3; + const uint32_t nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + const uint32_t so_size = 4; + const uint32_t range_size = 1; + + // Case: [...][NACK SDU] + [NACK SDU] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(false, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(2, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK SDU] + [NACK SDU] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1002; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size, status_pdu.packed_size); + TESTASSERT(prev_nack == status_pdu.nacks.front()); + TESTASSERT(next_nack == status_pdu.nacks.back()); + } + + // Case: [...][NACK SDU] + [NACK SDU] (continuous: merge with previous element) -- with SN overflow + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = mod_nr - 1; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 0; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(false, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(2, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK SDU] + [NACK SDU] (non-continuous, SN gap: append as is) -- with SN overflow + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = mod_nr - 1; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size, status_pdu.packed_size); + TESTASSERT(prev_nack == status_pdu.nacks.front()); + TESTASSERT(next_nack == status_pdu.nacks.back()); + } + + // Case: [...][NACK SDU] + [NACK segm] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 50; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(next_nack.so_end, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(2, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK SDU] + [NACK segm] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1002; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 50; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK SDU] + [NACK segm] (non-continuous, SO gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 1; + next_nack.so_end = 50; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK SDU] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(prev_nack.so_start, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(so_end_of_sdu, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(2, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK segm] + [NACK SDU] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1002; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK SDU] (non-continuous, SO gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = 99; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK segm] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(prev_nack.so_start, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(next_nack.so_end, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(2, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK segm] + [NACK segm] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1002; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK segm] (non-continuous, SO gap (left): append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = 99; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK segm] (non-continuous, SO gap (right): append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 5; + next_nack.so_end = 22; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + return SRSRAN_SUCCESS; +} + +// Test merge of NACKs upon status PDU creation -- previous NACK: range; next NACK: non-range +int rlc_am_nr_control_pdu_test_nack_merge_range_sdu(rlc_am_nr_sn_size_t sn_size) +{ + test_delimit_logger delimiter("Control PDU ({} bit SN) test NACK merge: range + SDU", to_number(sn_size)); + + const uint16_t so_end_of_sdu = rlc_status_nack_t::so_end_of_sdu; + const uint32_t mod_nr = cardinality(sn_size); + const uint32_t min_size = 3; + const uint32_t nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + const uint32_t so_size = 4; + const uint32_t range_size = 1; + + // Case: [...][NACK range] + [NACK SDU] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(false, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + 1, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range] + [NACK SDU] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1006; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + range_size, status_pdu.packed_size); + TESTASSERT(prev_nack == status_pdu.nacks.front()); + TESTASSERT(next_nack == status_pdu.nacks.back()); + } + + // Case: [...][NACK range] + [NACK SDU] (continuous: merge with previous element) -- with SN overflow + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = mod_nr - 1; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 4; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(false, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + 1, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range] + [NACK SDU] (non-continuous, SN gap: append as is) -- with SN overflow + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = mod_nr - 1; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 5; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + range_size, status_pdu.packed_size); + TESTASSERT(prev_nack == status_pdu.nacks.front()); + TESTASSERT(next_nack == status_pdu.nacks.back()); + } + + // Case: [...][NACK range] + [NACK segm] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 50; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(next_nack.so_end, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + 1, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range] + [NACK segm] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1006; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 50; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + range_size + so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range] + [NACK segm] (non-continuous, SO gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 1; + next_nack.so_end = 50; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + range_size + so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK SDU] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(prev_nack.so_start, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(so_end_of_sdu, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + 1, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range+segm] + [NACK SDU] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1006; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK SDU] (non-continuous, SO gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = 99; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK SDU] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK segm] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(prev_nack.so_start, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(next_nack.so_end, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + 1, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range+segm] + [NACK segm] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1006; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK segm] (non-continuous, SO gap (left): append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = 99; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK segm] (non-continuous, SO gap (right): append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 5; + next_nack.so_end = 22; + next_nack.has_nack_range = false; + next_nack.nack_range = 0; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + return SRSRAN_SUCCESS; +} + +// Test merge of NACKs upon status PDU creation -- previous NACK: non-range; next NACK: range +int rlc_am_nr_control_pdu_test_nack_merge_sdu_range(rlc_am_nr_sn_size_t sn_size) +{ + test_delimit_logger delimiter("Control PDU ({} bit SN) test NACK merge: SDU + range", to_number(sn_size)); + + const uint16_t so_end_of_sdu = rlc_status_nack_t::so_end_of_sdu; + const uint32_t mod_nr = cardinality(sn_size); + const uint32_t min_size = 3; + const uint32_t nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + const uint32_t so_size = 4; + const uint32_t range_size = 1; + + // Case: [...][NACK SDU] + [NACK range] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(false, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(3, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK SDU] + [NACK range] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1002; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + range_size, status_pdu.packed_size); + TESTASSERT(prev_nack == status_pdu.nacks.front()); + TESTASSERT(next_nack == status_pdu.nacks.back()); + } + + // Case: [...][NACK SDU] + [NACK range] (continuous: merge with previous element) -- with SN overflow + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = mod_nr - 1; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 0; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(false, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(3, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK SDU] + [NACK range] (non-continuous, SN gap: append as is) -- with SN overflow + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = mod_nr - 1; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + range_size, status_pdu.packed_size); + TESTASSERT(prev_nack == status_pdu.nacks.front()); + TESTASSERT(next_nack == status_pdu.nacks.back()); + } + + // Case: [...][NACK SDU] + [NACK range+segm] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 50; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(next_nack.so_end, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(3, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK SDU] + [NACK range+segm] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1002; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 50; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK SDU] + [NACK range+segm] (non-continuous, SO gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK SDU] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 1; + next_nack.so_end = 50; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK range] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(prev_nack.so_start, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(so_end_of_sdu, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(3, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK segm] + [NACK range] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1002; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK range] (non-continuous, SO gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = 99; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK range+segm] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(prev_nack.so_start, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(next_nack.so_end, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(3, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK segm] + [NACK range+segm] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1002; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK range+segm] (non-continuous, SO gap (left): append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = 99; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK segm] + [NACK range+segm] (non-continuous, SO gap (right): append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = false; + prev_nack.nack_range = 0; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1001; + next_nack.has_so = true; + next_nack.so_start = 5; + next_nack.so_end = 22; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + return SRSRAN_SUCCESS; +} + +// Test merge of NACKs upon status PDU creation -- previous NACK: range; next NACK: range +int rlc_am_nr_control_pdu_test_nack_merge_range_range(rlc_am_nr_sn_size_t sn_size) +{ + test_delimit_logger delimiter("Control PDU ({} bit SN) test NACK merge: range + SDU", to_number(sn_size)); + + const uint16_t so_end_of_sdu = rlc_status_nack_t::so_end_of_sdu; + const uint32_t mod_nr = cardinality(sn_size); + const uint32_t min_size = 3; + const uint32_t nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + const uint32_t so_size = 4; + const uint32_t range_size = 1; + + // Case: [...][NACK range] + [NACK range] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(false, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + next_nack.nack_range, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range] + [NACK range] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1006; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * range_size, status_pdu.packed_size); + TESTASSERT(prev_nack == status_pdu.nacks.front()); + TESTASSERT(next_nack == status_pdu.nacks.back()); + } + + // Case: [...][NACK range] + [NACK range] (continuous: merge with previous element) -- with SN overflow + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = mod_nr - 1; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 4; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(false, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + next_nack.nack_range, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range] + [NACK range] (non-continuous, SN gap: append as is) -- with SN overflow + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = mod_nr - 1; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 5; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * range_size, status_pdu.packed_size); + TESTASSERT(prev_nack == status_pdu.nacks.front()); + TESTASSERT(next_nack == status_pdu.nacks.back()); + } + + // Case: [...][NACK range] + [NACK range+segm] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 50; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(0, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(next_nack.so_end, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + next_nack.nack_range, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range] + [NACK range+segm] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1006; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 50; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * range_size + so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range] + [NACK range+segm] (non-continuous, SO gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = false; + prev_nack.so_start = 0; + prev_nack.so_end = 0; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 1; + next_nack.so_end = 50; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * range_size + so_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK range] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(prev_nack.so_start, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(so_end_of_sdu, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + next_nack.nack_range, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range+segm] + [NACK range] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1006; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size + 2 * range_size, status_pdu.packed_size); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK range] (non-continuous, SO gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = 99; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = false; + next_nack.so_start = 0; + next_nack.so_end = 0; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + so_size + 2 * range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK range+segm] (continuous: merge with previous element) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + TESTASSERT_EQ(prev_nack.nack_sn, status_pdu.nacks.back().nack_sn); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_so); + TESTASSERT_EQ(prev_nack.so_start, status_pdu.nacks.back().so_start); + TESTASSERT_EQ(next_nack.so_end, status_pdu.nacks.back().so_end); + TESTASSERT_EQ(true, status_pdu.nacks.back().has_nack_range); + TESTASSERT_EQ(prev_nack.nack_range + next_nack.nack_range, status_pdu.nacks.back().nack_range); + } + + // Case: [...][NACK range+segm] + [NACK range+segm] (non-continuous, SN gap: append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1006; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + 2 * range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK range+segm] (non-continuous, SO gap (left): append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = 99; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 0; + next_nack.so_end = 22; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + 2 * range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + // Case: [...][NACK range+segm] + [NACK range+segm] (non-continuous, SO gap (right): append as is) + { + rlc_am_nr_status_pdu_t status_pdu(sn_size); + status_pdu.ack_sn = 2000; + TESTASSERT_EQ(0, status_pdu.nacks.size()); + + // Prepare status_pdu.nacks: [...][NACK range+segm] + rlc_status_nack_t prev_nack; + prev_nack.nack_sn = 1000; + prev_nack.has_so = true; + prev_nack.so_start = 7; + prev_nack.so_end = so_end_of_sdu; + prev_nack.has_nack_range = true; + prev_nack.nack_range = 5; + status_pdu.push_nack(prev_nack); + TESTASSERT_EQ(1, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + nack_size + so_size + range_size, status_pdu.packed_size); + + // Add next NACK: [NACK range+segm] + rlc_status_nack_t next_nack; + next_nack.nack_sn = 1005; + next_nack.has_so = true; + next_nack.so_start = 5; + next_nack.so_end = 22; + next_nack.has_nack_range = true; + next_nack.nack_range = 2; + status_pdu.push_nack(next_nack); + TESTASSERT_EQ(2, status_pdu.nacks.size()); + TESTASSERT_EQ(min_size + 2 * nack_size + 2 * so_size + 2 * range_size, status_pdu.packed_size); + TESTASSERT(status_pdu.nacks.front() == prev_nack); + TESTASSERT(status_pdu.nacks.back() == next_nack); + } + + return SRSRAN_SUCCESS; +} + +// Test status PDU for correct trimming and estimation of packed size +// 1) Test init, copy and reset +// 2) Test step-wise growth and trimming of status PDU while covering several corner cases +int rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t sn_size) +{ + test_delimit_logger delimiter("Control PDU ({} bit SN) test trimming", to_number(sn_size)); + + // status PDU with no NACKs + { + constexpr uint32_t min_size = 3; + srsran::byte_buffer_t pdu; + rlc_am_nr_status_pdu_t status_pdu(sn_size); + + status_pdu.ack_sn = 99; + TESTASSERT_EQ(status_pdu.packed_size, min_size); // minimum size + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, min_size); + + rlc_am_nr_status_pdu_t status_pdu_copy = status_pdu; + TESTASSERT_EQ(status_pdu_copy.ack_sn, 99); + TESTASSERT_EQ(status_pdu_copy.packed_size, min_size); // minimum size + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, min_size); + + status_pdu.reset(); + + status_pdu.ack_sn = 77; + TESTASSERT_EQ(status_pdu.packed_size, min_size); // should still have minimum size + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, min_size); + + TESTASSERT_EQ(status_pdu_copy.ack_sn, 99); // shouldn't have changed + TESTASSERT_EQ(status_pdu_copy.packed_size, min_size); // minimum size + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, min_size); + } + + // status PDU with multiple NACKs + // expect: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3] + { + constexpr uint32_t min_size = 3; + const uint32_t nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t so_size = 4; + constexpr uint32_t range_size = 1; + uint32_t expected_size = min_size; + srsran::byte_buffer_t pdu; + rlc_am_nr_status_pdu_t status_pdu(sn_size); + + status_pdu.ack_sn = 77; + { + rlc_status_nack_t nack; + nack.nack_sn = 12; + status_pdu.push_nack(nack); + } + expected_size += nack_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 14; + status_pdu.push_nack(nack); + } + expected_size += nack_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 17; + nack.has_so = true; + nack.so_start = 50; + nack.so_end = 99; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 17; + nack.has_so = true; + nack.so_start = 150; + nack.so_end = 199; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 17; + nack.has_so = true; + nack.so_start = 250; + nack.so_end = 299; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 19; + status_pdu.push_nack(nack); + } + expected_size += nack_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 21; + nack.has_so = true; + nack.so_start = 333; + nack.so_end = 111; + nack.has_nack_range = true; + nack.nack_range = 5; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size + range_size; + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + { + rlc_status_nack_t nack; + nack.nack_sn = 27; + nack.has_so = true; + nack.so_start = 444; + nack.so_end = 666; + nack.has_nack_range = true; + nack.nack_range = 3; + status_pdu.push_nack(nack); + } + expected_size += nack_size + so_size + range_size; + TESTASSERT_EQ(status_pdu.ack_sn, 77); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3] + + // create a copy, check content + rlc_am_nr_status_pdu_t status_pdu_copy = status_pdu; + TESTASSERT_EQ(status_pdu_copy.ack_sn, 77); + TESTASSERT_EQ(status_pdu_copy.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=77, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5][27 444:666 r3] + + // trim to much larger size: nothing should change + TESTASSERT_EQ(status_pdu.trim(expected_size * 2), true); + TESTASSERT_EQ(status_pdu.ack_sn, 77); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // trim to exact size: nothing should change + TESTASSERT_EQ(status_pdu.trim(expected_size), true); + TESTASSERT_EQ(status_pdu.ack_sn, 77); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // trim to (expected_size - 1): this should remove the last NACK and update ACK accordingly + TESTASSERT_EQ(status_pdu.trim(expected_size - 1), true); + expected_size -= nack_size + so_size + range_size; + TESTASSERT_EQ(status_pdu.ack_sn, 27); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=27, NACKs=[12][14][17 50:99][17 150:199][17 250:299][19][21 333:111 r5] + + // trim to (expected_size - last two NACKs): this should remove the last NACK and update ACK accordingly + TESTASSERT_EQ(status_pdu.trim(expected_size - (2 * nack_size + so_size + range_size)), true); + expected_size -= 2 * nack_size + so_size + range_size; + TESTASSERT_EQ(status_pdu.ack_sn, 19); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=19, NACKs=[12][14][17 50:99][17 150:199][17 250:299] + + // trim to (expected_size - 1): this should remove the last NACK and all other NACKs with the same SN + TESTASSERT_EQ(status_pdu.trim(expected_size - 1), true); + expected_size -= 3 * (nack_size + so_size); + TESTASSERT_EQ(status_pdu.ack_sn, 17); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=17, NACKs=[12][14] + + // trim to impossible size = 1: this should report a failure without changes of the PDU + TESTASSERT_EQ(status_pdu.trim(1), false); + TESTASSERT_EQ(status_pdu.ack_sn, 17); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=17, NACKs=[12][14] + + // trim to minimum size: this should remove all NACKs and update ACK to the SN of the first NACK + expected_size = min_size; + TESTASSERT_EQ(status_pdu.trim(expected_size), true); + TESTASSERT_EQ(status_pdu.ack_sn, 12); + TESTASSERT_EQ(status_pdu.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + + // current state: ACK=12, NACKs empty + + // check the copy again - should be unchanged if not a shallow copy + TESTASSERT_EQ(status_pdu_copy.ack_sn, 77); + TESTASSERT_EQ(status_pdu_copy.packed_size, expected_size); + TESTASSERT_EQ(rlc_am_nr_write_status_pdu(status_pdu_copy, sn_size, &pdu), SRSRAN_SUCCESS); + TESTASSERT_EQ(pdu.N_bytes, expected_size); + } + + return SRSRAN_SUCCESS; +} + +///< Control PDU tests (18bit SN) +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 and no further NACK_SN (E1 bit not set) +int rlc_am_nr_control_pdu_18bit_sn_test1() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 1"); + const int len = 3; + std::array tv = {0x0E, 0x66, 0x64}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.nacks.size() == 0); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set) +// and NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 +int rlc_am_nr_control_pdu_18bit_sn_test2() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 2"); + const int len = 6; + std::array tv = {0x0E, 0x66, 0x66, 0xD9, 0x99, 0x80}; + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.nacks.size() == 1); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 (E2 bit set), +// SO_START=5, SO_END=0xFFFF +int rlc_am_nr_control_pdu_18bit_sn_test3() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 3"); + const int len = 17; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11010000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x05, // 8SO_START_lower + 0xFF, // 8SO_END_upper + 0xFF}; // 8SO_END_lower + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.nacks.size() == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + TESTASSERT(status_pdu.nacks[0].has_so == true); + TESTASSERT(status_pdu.nacks[0].so_start == 2); + TESTASSERT(status_pdu.nacks[0].so_end == 5); + TESTASSERT(status_pdu.nacks[1].nack_sn == 222975); + TESTASSERT(status_pdu.nacks[1].has_so == true); + TESTASSERT(status_pdu.nacks[1].so_start == 5); + TESTASSERT(status_pdu.nacks[1].so_end == 0xFFFF); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 (E1 and E2 bit not set), +int rlc_am_nr_control_pdu_18bit_sn_test4() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 4"); + const int len = 13; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11000000}; // 2NACK_SN_lower | E1 | E2 | E3 | 3R + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 235929); + TESTASSERT(status_pdu.nacks.size() == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 222822); + TESTASSERT(status_pdu.nacks[0].has_so == true); + TESTASSERT(status_pdu.nacks[0].so_start == 2); + TESTASSERT(status_pdu.nacks[0].so_end == 5); + TESTASSERT(status_pdu.nacks[1].nack_sn == 222975); + TESTASSERT(status_pdu.nacks[1].has_so == false); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +// Malformed Status PDU, similar to test3 but with E1 still set at the end of the PDU +// Status PDU for 18bit SN with ACK_SN=235929=0x39999=0b11 1001 1001 1001 1001 (E1 bit set), +// NACK_SN=222822=0x36666=0b11 0110 0110 0110 0110 (E1 and E2 bit set), +// SO_START=2, SO_END=5, +// NACK_SN=222975=0x366ff=0b11 0110 0110 1111 1111 ([!E1!] and E2 bit set), +// SO_START=5, SO_END=0xFFFF +int rlc_am_nr_control_pdu_18bit_sn_test5() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test 5"); + const int len = 17; + std::array tv = {0b00001110, // D/C | 3CPT | 4ACK_SN_upper + 0b01100110, // 8ACK_SN_center + 0b01100110, // 6ACK_SN_lower | E1 | R + 0b11011001, // 8NACK_SN_upper + 0b10011001, // 8NACK_SN_center + 0b10110000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0b11011001, // 8NACK_SN_upper + 0b10111111, // 8NACK_SN_center + 0b11110000, // 2NACK_SN_lower | [!E1!] | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x05, // 8SO_START_lower + 0xFF, // 8SO_END_upper + 0xFF}; // 8SO_END_lower + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == 0); + + return SRSRAN_SUCCESS; +} + +// Status PDU for 18bit SN with ACK_SN=200977=0x31111=0b11 0001 0001 0001 0001, +// NACK range0: 3 full SDUs, NACK_SN=69905=0x11111=0b01 0001 0001 0001 0001 +// NACK range1: missing segment sequence across 4 SDUs +// starting at NACK_SN=69913=0x11119=0b01 0001 0001 0001 1001, SO_START=2, +// ending at NACK_SN=69916, SO_END=5 +// E1 and E3 bit set on first NACK, E2 and E3 bit set on the second. +int rlc_am_nr_control_pdu_18bit_sn_test_nack_range() +{ + test_delimit_logger delimiter("Control PDU (18bit SN) test NACK range"); + const int len = 15; + std::array tv = {0b00001100, // D/C | 3CPT | 4ACK_SN_upper + 0b01000100, // 8ACK_SN_center + 0b01000110, // 6ACK_SN_lower | E1 | R + 0b01000100, // 8NACK_SN_upper + 0b01000100, // 8NACK_SN_center + 0b01101000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x03, // 8NACK_range + 0b01000100, // 8NACK_SN_upper + 0b01000110, // 8NACK_SN_center + 0b01011000, // 2NACK_SN_lower | E1 | E2 | E3 | 3R + 0x00, // 8SO_START_upper + 0x02, // 8SO_START_lower + 0x00, // 8SO_END_upper + 0x05, // 8SO_END_lower + 0x04}; // 8NACK_range + srsran::byte_buffer_t pdu = make_pdu_and_log(tv); + + TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); + + // unpack PDU + rlc_am_nr_status_pdu_t status_pdu(srsran::rlc_am_nr_sn_size_t::size18bits); + TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &status_pdu) == SRSRAN_SUCCESS); + TESTASSERT(status_pdu.ack_sn == 200977); + TESTASSERT(status_pdu.nacks.size() == 2); + TESTASSERT(status_pdu.nacks[0].nack_sn == 69905); + TESTASSERT(status_pdu.nacks[0].has_so == false); + TESTASSERT(status_pdu.nacks[0].has_nack_range == true); + TESTASSERT(status_pdu.nacks[0].nack_range == 3); + + TESTASSERT(status_pdu.nacks[1].nack_sn == 69913); + TESTASSERT(status_pdu.nacks[1].has_so == true); + TESTASSERT(status_pdu.nacks[1].so_start == 2); + TESTASSERT(status_pdu.nacks[1].so_end == 5); + TESTASSERT(status_pdu.nacks[1].has_nack_range == true); + TESTASSERT(status_pdu.nacks[1].nack_range == 4); + + // reset status PDU + pdu.clear(); + + // pack again + TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &pdu) == SRSRAN_SUCCESS); + TESTASSERT(pdu.N_bytes == tv.size()); + + write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); + TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + static const struct option long_options[] = {{"pcap", no_argument, nullptr, 'p'}, {nullptr, 0, nullptr, 0}}; + + // Parse arguments + while (true) { + int option_index = 0; + int c = getopt_long(argc, argv, "p", long_options, &option_index); + if (c == -1) { + break; + } + + switch (c) { + case 'p': + printf("Setting up PCAP\n"); + pcap_handle = std::unique_ptr(new srsran::mac_pcap()); + pcap_handle->open("rlc_am_nr_pdu_test.pcap"); + break; + default: + fprintf(stderr, "error parsing arguments\n"); + return SRSRAN_ERROR; + } + } + + srslog::init(); + + if (rlc_am_nr_pdu_test1()) { + fprintf(stderr, "rlc_am_nr_pdu_test1() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_pdu_test2()) { + fprintf(stderr, "rlc_am_nr_pdu_test2() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_pdu_test3()) { + fprintf(stderr, "rlc_am_nr_pdu_test3() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_pdu_test4()) { + fprintf(stderr, "rlc_am_nr_pdu_test4() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_pdu_test5()) { + fprintf(stderr, "rlc_am_nr_pdu_test5() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_pdu_test6()) { + fprintf(stderr, "rlc_am_nr_pdu_test6() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test1()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test1() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test2()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test2() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test3()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test3() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test4()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test4() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test5()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test5() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_12bit_sn_test_nack_range()) { + fprintf(stderr, "rlc_am_nr_control_pdu_12bit_sn_test_nack_range() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_nack_merge_sdu_sdu(rlc_am_nr_sn_size_t::size12bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_merge_sdu_sdu(size12bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_nack_merge_range_sdu(rlc_am_nr_sn_size_t::size12bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_merge_range_sdu(size12bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_nack_merge_sdu_range(rlc_am_nr_sn_size_t::size12bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_merge_sdu_range(size12bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_nack_merge_range_range(rlc_am_nr_sn_size_t::size12bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_merge_range_range(size12bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t::size12bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_trimming(size12bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_18bit_sn_test1()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test1() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_18bit_sn_test2()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test2() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_18bit_sn_test3()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test3() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_18bit_sn_test4()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test4() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_18bit_sn_test5()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test5() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_18bit_sn_test_nack_range()) { + fprintf(stderr, "rlc_am_nr_control_pdu_18bit_sn_test_nack_range() failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_nack_merge_sdu_sdu(rlc_am_nr_sn_size_t::size18bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_merge_sdu_sdu(size18bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_nack_merge_range_sdu(rlc_am_nr_sn_size_t::size18bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_merge_range_sdu(size18bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_nack_merge_sdu_range(rlc_am_nr_sn_size_t::size18bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_merge_sdu_range(size18bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_nack_merge_range_range(rlc_am_nr_sn_size_t::size18bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_merge_range_range(size18bits) failed.\n"); + return SRSRAN_ERROR; + } + + if (rlc_am_nr_control_pdu_test_trimming(rlc_am_nr_sn_size_t::size18bits)) { + fprintf(stderr, "rlc_am_nr_control_pdu_test_trimming(size18bits) failed.\n"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc new file mode 100644 index 000000000..413cb552c --- /dev/null +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -0,0 +1,3423 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rlc_test_common.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/rlc_pcap.h" +#include "srsran/common/test_common.h" +#include "srsran/common/threads.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/rlc/rlc_am_nr.h" + +#define NBUFS 5 +#define HAVE_PCAP 0 +#define SDU_SIZE 500 + +using namespace srsue; +using namespace srsran; + +int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS], rlc_am_nr_sn_size_t sn_size) +{ + // Push 5 SDUs into RLC1 + unique_byte_buffer_t sdu_bufs[NBUFS]; + constexpr uint32_t payload_size = 1; // Give each buffer a size of 1 byte + for (int i = 0; i < NBUFS; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 1 byte + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc->write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + TESTASSERT_EQ(expect_buffer_state, rlc->get_buffer_state()); + + // Read 5 PDUs from RLC1 (1 byte each) + for (int i = 0; i < NBUFS; i++) { + uint32_t len = rlc->read_pdu(pdu_bufs[i].msg, data_pdu_size); + pdu_bufs[i].N_bytes = len; + TESTASSERT_EQ(data_pdu_size, len); + } + + TESTASSERT_EQ(0, rlc->get_buffer_state()); + return SRSRAN_SUCCESS; +} + +/* + * Test the limits of the TX/RX window checkers + */ +int window_checker_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("window checkers ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx = dynamic_cast(rlc1.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return SRSRAN_ERROR; + } + + { + // RLC1 RX_NEXT == 0 and RLC2 TX_NEXT_ACK == 0 + uint32_t sn_inside_below = 0; + uint32_t sn_inside_above = cardinality(sn_size) / 2 - 1; + uint32_t sn_outside_below = cardinality(sn_size) - 1; + uint32_t sn_outside_above = cardinality(sn_size) / 2; + TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_below)); + TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_above)); + TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_below)); + TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_above)); + TESTASSERT_EQ(true, tx->inside_tx_window(sn_inside_below)); + TESTASSERT_EQ(true, tx->inside_tx_window(sn_inside_above)); + TESTASSERT_EQ(false, tx->inside_tx_window(sn_outside_below)); + TESTASSERT_EQ(false, tx->inside_tx_window(sn_outside_above)); + } + + rlc_am_nr_rx_state_t rx_st = {}; + rx_st.rx_next = cardinality(sn_size) - 1; + ; + rlc_am_nr_tx_state_t tx_st = {}; + tx_st.tx_next_ack = cardinality(sn_size) - 1; + ; + + rx->set_rx_state(rx_st); + tx->set_tx_state(tx_st); + + { + // RX_NEXT == 4095 TX_NEXT_ACK == 4095 + uint32_t sn_inside_below = 0; + uint32_t sn_inside_above = cardinality(sn_size) / 2 - 2; + uint32_t sn_outside_below = cardinality(sn_size) - 2; + uint32_t sn_outside_above = cardinality(sn_size) / 2; + TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_below)); + TESTASSERT_EQ(true, rx->inside_rx_window(sn_inside_above)); + TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_below)); + TESTASSERT_EQ(false, rx->inside_rx_window(sn_outside_above)); + TESTASSERT_EQ(true, tx->inside_tx_window(sn_inside_below)); + TESTASSERT_EQ(true, tx->inside_tx_window(sn_inside_above)); + TESTASSERT_EQ(false, tx->inside_tx_window(sn_outside_below)); + TESTASSERT_EQ(false, tx->inside_tx_window(sn_outside_above)); + } + return SRSRAN_SUCCESS; +} + +/* + * Test is retx_segmentation required + * + */ +int retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("retx segmentation required checkers ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx = dynamic_cast(rlc1.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return SRSRAN_ERROR; + } + + unique_byte_buffer_t sdu_bufs[NBUFS]; + unique_byte_buffer_t pdu_bufs[NBUFS]; + for (int i = 0; i < NBUFS; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + pdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = 5; // Give each buffer a size of 1 byte + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + rlc1.read_pdu(pdu_bufs[i]->msg, 8); + } + + // Test full SDU retx + { + uint32_t nof_bytes = 8; + rlc_amd_retx_nr_t retx = {}; + retx.sn = 0; + retx.is_segment = false; + + tx->is_retx_segmentation_required(retx, nof_bytes); + TESTASSERT_EQ(false, tx->is_retx_segmentation_required(retx, nof_bytes)); + } + + // Test SDU retx segmentation required + { + uint32_t nof_bytes = 4; + rlc_amd_retx_nr_t retx; + retx.sn = 0; + retx.is_segment = false; + + tx->is_retx_segmentation_required(retx, nof_bytes); + TESTASSERT_EQ(true, tx->is_retx_segmentation_required(retx, nof_bytes)); + } + + // Test full SDU segment retx + { + uint32_t nof_bytes = 40; + rlc_amd_retx_nr_t retx = {}; + retx.sn = 0; + retx.is_segment = true; + retx.so_start = 4; + retx.segment_length = 2; + + tx->is_retx_segmentation_required(retx, nof_bytes); + TESTASSERT_EQ(false, tx->is_retx_segmentation_required(retx, nof_bytes)); + } + + // Test SDU segment retx segmentation required + { + uint32_t nof_bytes = 4; + rlc_amd_retx_nr_t retx = {}; + retx.sn = 0; + retx.is_segment = true; + retx.so_start = 4; + retx.segment_length = 2; + + tx->is_retx_segmentation_required(retx, nof_bytes); + TESTASSERT_EQ(true, tx->is_retx_segmentation_required(retx, nof_bytes)); + } + return SRSRAN_SUCCESS; +} + +/* + * Test the transmission and acknowledgement of 5 SDUs. + * + * Each SDU is transmitted as a single PDU. + * There are no lost PDUs, and the byte size is small, so the Poll_PDU configuration + * will trigger the status report. + * Poll PDU is configured to 4, so the 5th PDU should set the polling bit. + */ +int basic_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("basic tx/rx ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + basic_test_tx(&rlc1, pdu_bufs, sn_size); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } + + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 3); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the last SN that was not received. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check TX_NEXT_ACK + rlc_am_nr_tx_state_t st = tx1->get_tx_state(); + TESTASSERT_EQ(5, st.tx_next_ack); + TESTASSERT_EQ(0, tx1->get_tx_window_utilization()); + + // Check PDCP notifications + TESTASSERT_EQ(5, tester.notified_counts.size()); + for (uint16_t i = 0; i < tester.sdus.size(); i++) { + TESTASSERT_EQ(1, tester.sdus[i]->N_bytes); + TESTASSERT_EQ(i, *(tester.sdus[i]->msg)); + TESTASSERT_EQ(1, tester.notified_counts[i]); + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + constexpr uint32_t status_pdu_size = 3; + uint32_t total_tx_pdu_bytes = NBUFS * data_pdu_size; // NBUFS * PDU size + uint32_t total_rx_pdu_bytes = status_pdu_size; // One status PDU + + // RLC1 PDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // RLC1 SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_pdus); + TESTASSERT_EQ(1, metrics1.num_rx_pdus); // One status PDU + TESTASSERT_EQ(total_tx_pdu_bytes, metrics1.num_tx_pdu_bytes); // NBUFS * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes, metrics1.num_rx_pdu_bytes); // One status PDU + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // RLC2 PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // RLC2 SDU metrics + TESTASSERT_EQ(1, metrics2.num_tx_pdus); // One status PDU + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 SDUs + TESTASSERT_EQ(total_rx_pdu_bytes, metrics2.num_tx_pdu_bytes); // One status PDU + TESTASSERT_EQ(total_tx_pdu_bytes, metrics2.num_rx_pdu_bytes); // NBUFS * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + return SRSRAN_SUCCESS; +} + +/* + * Test the loss of a single PDU. + * NACK should be visible in the status report. + * Retx after NACK should be present too. + * No further status reports shall be issued. + */ +int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("lost PDU ({} bit SN)", to_number(sn_size)); + + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + rlc_config_t rlc2_config = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc2.configure(rlc2_config)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + basic_test_tx(&rlc1, pdu_bufs, sn_size); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be a NACK in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is an Retx of SN=3 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + } + + { + // Check correct re-transmission + byte_buffer_t retx_buf; + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); + retx_buf.N_bytes = len; + TESTASSERT_EQ(data_pdu_size, len); + + // Polling bit on the RETX should be required, as the buffers are not empty. + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); // t-StatusProhibit is still running + } + + // Step timers until t-StatusProhibit expires + for (int cnt = 0; cnt < 8; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(3, rlc2.get_buffer_state()); // t-StatusProhibit no longer running + + { + // Double check status report + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 3); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now + } + + { + // rlc2 should not issue further status PDUs as time passes (even after expiry of t_status_prohibit) + int32_t checktime = 2 * rlc2_config.am_nr.t_status_prohibit; + for (int cnt = 0; cnt < checktime; cnt++) { + timers.step_all(); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + } + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t total_tx_pdu_bytes1 = (NBUFS + 1) * data_pdu_size; // (NBUFS + 1 RETX) * PDU size + uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + status_pdu_nack_size; // Two status PDU (one with a NACK) + uint32_t total_tx_pdu_bytes2 = + 3 * status_pdu_ack_size + status_pdu_nack_size; // Three status PDU (one with a NACK, two without) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 RETX) * PDU size + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5 + 1, metrics1.num_tx_pdus); // One re-transmission + TESTASSERT_EQ(2, metrics1.num_rx_pdus); // One status PDU + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // (NBUFS + 1 RETX) * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // Three status PDUs + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without) + TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 1 Lost + 1 RETX) * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + return SRSRAN_SUCCESS; +} + +/* + * Test the loss of a single PDU with NACK duplicate + * NACK should be visible in the status report. + * + * Retx after NACK should be present too. + * No further status reports shall be issued. + */ +int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("lost PDU with NACK duplicate ({} bit SN)", to_number(sn_size)); + + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + rlc_config_t rlc2_config = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc2.configure(rlc2_config)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + basic_test_tx(&rlc1, pdu_bufs, sn_size); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Write duplicated status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is nothing pending in RLC1 + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be a NACK in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Write duplicated status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is only one Retx of SN=3 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + } + + { + // Check correct re-transmission + byte_buffer_t retx_buf; + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); + retx_buf.N_bytes = len; + TESTASSERT_EQ(data_pdu_size, len); + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Status report shoud be required, as the TX buffers are now empty. + } + + // Step timers until t-StatusProhibit expires + for (int cnt = 0; cnt < 8; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(3, rlc2.get_buffer_state()); // t-StatusProhibit no longer running + + { + // Double check status report + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 3); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now + } + + { + // rlc2 should not issue further status PDUs as time passes (even after expiry of t_status_prohibit) + int32_t checktime = 2 * rlc2_config.am_nr.t_status_prohibit; + for (int cnt = 0; cnt < checktime; cnt++) { + timers.step_all(); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + } + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t total_tx_pdu_bytes1 = (NBUFS + 1) * data_pdu_size; // (NBUFS + 1 RETX) * PDU size + uint32_t total_rx_pdu_bytes1 = 4 * status_pdu_ack_size + 2 * status_pdu_nack_size; // 4 status PDU (2 with a NACK) + uint32_t total_tx_pdu_bytes2 = + 3 * status_pdu_ack_size + status_pdu_nack_size; // Three status PDU (one with a NACK, two without) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 RETX) * PDU size + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5 + 1, metrics1.num_tx_pdus); // One re-transmission + TESTASSERT_EQ(4, metrics1.num_rx_pdus); // 4 status PDUs + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // (NBUFS + 1 RETX) * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // Three status PDUs + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (6 tx'ed, but one was lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without) + TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 1 Lost + 1 RETX) * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + return SRSRAN_SUCCESS; +} + +/* + * Test the loss of multiple PDUs. + * NACKs for all missing PDUs should be visible in buffer state -- but we enforce + * a trimmed status PDU by providing little space for the whole status PDU. + * Retx after NACK should be present too. + * Further status report shall contain the trimmed NACK. + * Another Retx after NACK should be present. + * No further status reports shall be issued. + */ +int lost_pdus_trimmed_nack_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("lost PDUs and trimmed NACKs ({} bit SN)", to_number(sn_size)); + + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + rlc_config_t rlc2_config = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc2.configure(rlc2_config)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + basic_test_tx(&rlc1, pdu_bufs, sn_size); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 1 && i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=1 and 3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(1, status_check.ack_sn); // 1 is the next expected SN (i.e. the first lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be two NACKs in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t expected_size = status_pdu_ack_size + 2 * status_pdu_nack_size; + TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2, enforce to trimming by providing little space (expected_size - 1) + // to drop the second NACK. + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, expected_size - 1); + status_buf.N_bytes = len; + expected_size = status_pdu_ack_size + 1 * status_pdu_nack_size; // only one NACK left + TESTASSERT_EQ(len, expected_size); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (after trimming) + TESTASSERT_EQ(1, status_check.nacks.size()); // Expect only one NACK left + TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // The NACK'ed SN is 1. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is an Retx of SN=1 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + } + + { + // Check correct re-transmission + byte_buffer_t retx_buf; + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); + retx_buf.N_bytes = len; + TESTASSERT_EQ(data_pdu_size, len); + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + + expected_size = status_pdu_ack_size + 1 * status_pdu_nack_size; + TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Status report should now include the chopped NACK + } + + // Step timers until t-StatusProhibit expires + for (int cnt = 0; cnt < 8; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); // t-StatusProhibit no longer running + + { + // Double check status report + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, expected_size); + status_buf.N_bytes = len; + expected_size = status_pdu_ack_size + 1 * status_pdu_nack_size; // the remaining NACK + TESTASSERT_EQ(len, expected_size); + + // Nothing else pending + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // Expect only the second NACK + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // The NACK'ed SN is 3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is an Retx of SN=3 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + } + { + // Check correct re-transmission + byte_buffer_t retx_buf; + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); + retx_buf.N_bytes = len; + TESTASSERT_EQ(data_pdu_size, len); + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + + expected_size = status_pdu_ack_size; + TESTASSERT_EQ(0, rlc2.get_buffer_state()); // Status report should have no NACKs + } + // Step timers until t-StatusProhibit expires + for (int cnt = 0; cnt < 8; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(expected_size, rlc2.get_buffer_state()); // t-StatusProhibit no longer running + { + // Double check status report + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, expected_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(0, status_check.nacks.size()); // All PDUs are acked now + } + + { + // rlc2 should not issue further status PDUs as time passes (even after expiry of t_status_prohibit) + int32_t checktime = 2 * rlc2_config.am_nr.t_status_prohibit; + for (int cnt = 0; cnt < checktime; cnt++) { + timers.step_all(); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + } + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t total_tx_pdu_bytes1 = (NBUFS + 2) * data_pdu_size; // (NBUFS + 2 RETX) * PDU size + uint32_t total_rx_pdu_bytes1 = 3 * status_pdu_ack_size + 2 * status_pdu_nack_size; // 3 status PDUs (2 with one NACK) + uint32_t total_tx_pdu_bytes2 = + 4 * status_pdu_ack_size + 2 * status_pdu_nack_size; // 4 status PDUs (2 with one NACK, two without) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 2 Lost + 2 RETX) * PDU size + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5 + 2, metrics1.num_tx_pdus); // One re-transmission + TESTASSERT_EQ(3, metrics1.num_rx_pdus); // 3 status PDUs + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // (NBUFS + 2 RETX) * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); // Two status PDU (one with a NACK) + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(4, metrics2.num_tx_pdus); // 4 status PDUs + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 PDUs (7 tx'ed, but 2 were lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without) + TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 2 Lost + 2 RETX) * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + return SRSRAN_SUCCESS; +} + +/* + * Test if retx queue is cleared of SDUs that are ACK'ed by a late/delayed ACK. + */ +int clean_retx_queue_of_acked_sdus_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("Clean retx_queue of SDUs that are ACK'ed by a late/delayed ACK ({} bit SN)", + to_number(sn_size)); + + constexpr uint32_t payload_size = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t data_pdu_size = header_size + payload_size; + uint32_t expect_buffer_state = NBUFS * data_pdu_size; + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + rlc_config_t rlc2_config = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc2.configure(rlc2_config)) { + return -1; + } + + rlc_am_nr_tx* rlc1_tx = dynamic_cast(rlc1.get_tx()); + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + basic_test_tx(&rlc1, pdu_bufs, sn_size); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is nothing pending in RLC1 + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be a NACK in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is only one Retx of SN=3 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + } + + // now we deliver the late PDU SN=3 to rlc2 + rlc2.write_pdu(pdu_bufs[3].msg, pdu_bufs[3].N_bytes); + + // Check there is only one Retx of SN=3 + TESTASSERT_EQ(data_pdu_size, rlc1.get_buffer_state()); + TESTASSERT_EQ(1, rlc1_tx->get_retx_queue_size()); + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be an ACK in the status report. + TESTASSERT_EQ(status_pdu_ack_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + uint32_t len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(0, status_check.nacks.size()); // Nothing else lost + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check the Retx of SN=3 has been removed + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + TESTASSERT_EQ(0, rlc1_tx->get_retx_queue_size()); + } + + { + // Attempt to read from rlc1 to verify there nothing to read from it + byte_buffer_t retx_buf; + uint32_t len = rlc1.read_pdu(retx_buf.msg, data_pdu_size); + retx_buf.N_bytes = len; + TESTASSERT_EQ(0, len); + } + + { + // rlc2 should not issue further status PDUs as time passes (even after expiry of t_status_prohibit) + int32_t checktime = 2 * rlc2_config.am_nr.t_status_prohibit; + for (int cnt = 0; cnt < checktime; cnt++) { + timers.step_all(); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + } + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t total_tx_pdu_bytes1 = (NBUFS)*data_pdu_size; // (NBUFS) * PDU size + uint32_t total_rx_pdu_bytes1 = 3 * status_pdu_ack_size + 1 * status_pdu_nack_size; // 3 status PDU (1 with a NACK) + uint32_t total_tx_pdu_bytes2 = + 3 * status_pdu_ack_size + status_pdu_nack_size; // Three status PDU (one with a NACK, two without) + uint32_t total_rx_pdu_bytes2 = (NBUFS)*data_pdu_size; // (NBUFS - 1 Lost + 1 Late) * PDU size + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(5, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_pdus); // 5 transmissions, no re-transmission + TESTASSERT_EQ(3, metrics1.num_rx_pdus); // 3 status PDUs + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); // (NBUFS) * PDU size + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); // 3 status PDU (1 with a NACK) + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // SDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(5, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // 3 status PDUs + TESTASSERT_EQ(5, metrics2.num_rx_pdus); // 5 transmissions, no re-transmission + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); // Three status PDU (one with a NACK, two without) + TESTASSERT_EQ(total_rx_pdu_bytes2, metrics2.num_rx_pdu_bytes); // (NBUFS - 1 Lost + 1 Late) * PDU size + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + return SRSRAN_SUCCESS; +} + +/* + * Test the basic segmentation of a single SDU. + * A single SDU of 3 bytes is segmented into 3 PDUs + */ +int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("basic segmentation ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Push 1 SDU into RLC1 + unique_byte_buffer_t sdu; + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + sdu = srsran::make_byte_buffer(); + TESTASSERT(nullptr != sdu); + sdu->msg[0] = 0; // Write the index into the buffer + sdu->N_bytes = payload_size; // Give the SDU the size of 3 bytes + sdu->md.pdcp_sn = 0; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu)); + + // Read 3 PDUs + constexpr uint16_t n_pdus = 3; + unique_byte_buffer_t pdu_bufs[n_pdus]; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + for (int i = 0; i < n_pdus; i++) { + pdu_bufs[i] = srsran::make_byte_buffer(); + TESTASSERT(nullptr != pdu_bufs[i]); + if (i == 0) { + pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, pdu_bufs[i]->N_bytes); + } else { + pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, pdu_bufs[i]->N_bytes); + } + } + + // Write 3 PDUs into RLC2 + for (int i = 0; i < n_pdus; i++) { + rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t total_rx_pdu_bytes = pdu_size_first + (n_pdus - 1) * pdu_size_continued; // 1 PDU (No SO) + 2 PDUs (with SO) + + // SDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(1, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(payload_size, metrics2.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_pdus); + TESTASSERT_EQ(n_pdus, metrics2.num_rx_pdus); // 3 PDUs + TESTASSERT_EQ(0, metrics2.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes, metrics2.num_rx_pdu_bytes); // 1 PDU (No SO) + 2 PDUs (with SO) + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + + // Check state + rlc_am_nr_tx_state_t state1_tx = tx1->get_tx_state(); + TESTASSERT_EQ(1, state1_tx.tx_next); + + return SRSRAN_SUCCESS; +} + +// This tests correct behaviour of the following flow: +// - Transmit 5 SDUs as whole PDUs +// - Loose 3rd PDU +// - Receive NACK for missing PDU +// - Retransmit lost PDU in 3 segments +// - Check metrics and state +int segment_retx_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("segment retx PDU ({} bit SN)", to_number(sn_size)); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + rlc_cnfg.am_nr.t_poll_retx = -1; + if (not rlc1.configure(rlc_cnfg)) { + return -1; + } + + if (not rlc2.configure(rlc_cnfg)) { + return -1; + } + + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Push 5 SDUs into RLC1 + unique_byte_buffer_t sdu_bufs[NBUFS]; + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (int i = 0; i < NBUFS; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t expected_buffer_state = (header_size + payload_size) * NBUFS; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + // Read 5 PDUs from RLC1 (1 byte each) + for (int i = 0; i < NBUFS; i++) { + uint32_t len = rlc1.read_pdu(pdu_bufs[i].msg, header_size + payload_size); + pdu_bufs[i].N_bytes = len; + TESTASSERT_EQ(header_size + payload_size, len); + } + + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be a NACK in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is an Retx of SN=3 + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); + } + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + { + // Re-transmit PDU in 3 segments + for (int i = 0; i < 3; i++) { + byte_buffer_t retx_buf; + uint32_t len = 0; + if (i == 0) { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, len); + } else { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, len); + } + retx_buf.N_bytes = len; + + rlc_am_nr_pdu_header_t header_check = {}; + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, sn_size, &header_check); + // Double check header. + TESTASSERT_EQ(3, header_check.sn); // Double check RETX SN + if (i == 0) { + TESTASSERT_EQ(rlc_nr_si_field_t::first_segment, header_check.si); + } else if (i == 1) { + TESTASSERT_EQ(rlc_nr_si_field_t::neither_first_nor_last_segment, header_check.si); + } else { + TESTASSERT_EQ(rlc_nr_si_field_t::last_segment, header_check.si); + } + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + } + TESTASSERT(0 == rlc1.get_buffer_state()); + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t data_pdu_size = header_size + payload_size; + uint32_t total_tx_pdu_bytes1 = NBUFS * data_pdu_size + pdu_size_first + 2 * pdu_size_continued; + uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + status_pdu_nack_size; // Two status PDU (one with a NACK) + uint32_t total_tx_pdu_bytes2 = total_rx_pdu_bytes1; + uint32_t total_rx_pdu_bytes2 = (NBUFS - 1) * data_pdu_size + pdu_size_first + 2 * pdu_size_continued; + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(15, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5 + 3, metrics1.num_tx_pdus); // 3 re-transmissions + TESTASSERT_EQ(2, metrics1.num_rx_pdus); // Two status PDU + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(15, metrics2.num_rx_sdu_bytes); // 5 SDUs, 3 bytes each + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(2, metrics2.num_tx_pdus); // Two status PDUs + TESTASSERT_EQ(7, metrics2.num_rx_pdus); // 7 PDUs (8 tx'ed, but one was lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes2, + metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data) + // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 33 + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + + // Check state + rlc_am_nr_rx_state_t state2_rx = rx2->get_rx_state(); + TESTASSERT_EQ(5, state2_rx.rx_next); + return SRSRAN_SUCCESS; +} + +// This tests correct behaviour of the following flow: +// - Transmit 5 SDUs as whole PDUs +// - Loose 3rd PDU +// - Receive NACK for missing PDU +// - Retransmit lost PDU in 3 segments +// - Loose first and last segment +// - Receive NACKs for missing segments +// - Receive duplicate of previous NACKs +// - Retransmit missing segments again, but only once! +// - Check metrics and state +int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("segment retx PDU and loose some segments ({} bit SN)", to_number(sn_size)); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Push 5 SDUs into RLC1 + unique_byte_buffer_t sdu_bufs[NBUFS]; + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (int i = 0; i < NBUFS; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t expected_buffer_state = (header_size + payload_size) * NBUFS; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + // Read 5 PDUs from RLC1 (each with a full SDU) + for (int i = 0; i < NBUFS; i++) { + uint32_t len = rlc1.read_pdu(pdu_bufs[i].msg, header_size + payload_size); + pdu_bufs[i].N_bytes = len; + TESTASSERT_EQ(header_size + payload_size, len); + } + + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Write 5 - 1 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + if (i != 3) { + rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes); // Don't write RLC_SN=3. + } + } + + // Only after t-reassembly has expired, will the status report include NACKs. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(3, status_check.ack_sn); // 3 is the next expected SN (i.e. the lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be a NACK in the status report. + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there is an Retx of SN=3 + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); + } + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + { + // Re-transmit PDU in 3 segments + for (int i = 0; i < 3; i++) { + byte_buffer_t retx_buf; + uint32_t len = 0; + if (i == 0) { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, len); + } else { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, len); + } + retx_buf.N_bytes = len; + + rlc_am_nr_pdu_header_t header_check = {}; + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, sn_size, &header_check); + // Double check header. + TESTASSERT_EQ(3, header_check.sn); // Double check RETX SN + if (i == 0) { + TESTASSERT_EQ(rlc_nr_si_field_t::first_segment, header_check.si); + } else if (i == 1) { + TESTASSERT_EQ(rlc_nr_si_field_t::neither_first_nor_last_segment, header_check.si); + } else { + TESTASSERT_EQ(rlc_nr_si_field_t::last_segment, header_check.si); + } + + // We loose the first and the last segment + if (i != 0 && i != 2) { + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + } + } + TESTASSERT(0 == rlc1.get_buffer_state()); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // t-reassembly has expired. There should be another NACK in the status report. + constexpr uint32_t status_pdu_so_size = 4; + TESTASSERT_EQ(status_pdu_ack_size + 2 * status_pdu_nack_size + 2 * status_pdu_so_size, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + 2 * status_pdu_nack_size + 2 * status_pdu_so_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(2, status_check.nacks.size()); // We lost two PDU segments. + TESTASSERT_EQ(3, status_check.nacks[0].nack_sn); // Lost PDU SN=3. + TESTASSERT_EQ(true, status_check.nacks[0].has_so); // This is a segment missing. + TESTASSERT_EQ(0, status_check.nacks[0].so_start); // Segment offset should be 0 here + TESTASSERT_EQ(0, status_check.nacks[0].so_end); // Segment end should be 0 here + TESTASSERT_EQ(true, status_check.nacks[1].has_so); // This is a segment missing. + TESTASSERT_EQ(2, status_check.nacks[1].so_start); // Segment offset should be 2 here + TESTASSERT_EQ(0xFFFF, status_check.nacks[1].so_end); // Segment end should be 0xFFFF here + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Write status PDU duplicate to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there are two Retx segments (a first one and a continued one) + TESTASSERT_EQ(pdu_size_first + pdu_size_continued, rlc1.get_buffer_state()); + } + + { + // Re-transmit the lost 2 segments + for (int i = 0; i < 2; i++) { + byte_buffer_t retx_buf; + uint32_t len = 0; + if (i == 0) { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, len); + } else { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, len); + } + retx_buf.N_bytes = len; + + rlc_am_nr_pdu_header_t header_check = {}; + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, sn_size, &header_check); + // Double check header. + TESTASSERT_EQ(3, header_check.sn); // Double check RETX SN + if (i == 0) { + TESTASSERT_EQ(rlc_nr_si_field_t::first_segment, header_check.si); + } else { + TESTASSERT_EQ(rlc_nr_si_field_t::last_segment, header_check.si); + } + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + } + TESTASSERT(0 == rlc1.get_buffer_state()); + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t data_pdu_size = header_size + payload_size; + uint32_t total_tx_pdu_bytes1 = NBUFS * data_pdu_size + 2 * pdu_size_first + 3 * pdu_size_continued; + uint32_t total_rx_pdu_bytes1 = status_pdu_ack_size + // ACK, no NACK + (status_pdu_ack_size + status_pdu_nack_size) + // ACK + NACK full SDU + 2 * (status_pdu_ack_size + 2 * status_pdu_nack_size + // 2 * (ACK + NACK two segments) + 2 * status_pdu_so_size); + uint32_t total_tx_pdu_bytes2 = status_pdu_ack_size + // ACK, no NACK + (status_pdu_ack_size + status_pdu_nack_size) + // ACK + NACK full SDU + 1 * (status_pdu_ack_size + 2 * status_pdu_nack_size + // 1 * (ACK + NACK two segments) + 2 * status_pdu_so_size); + uint32_t total_rx_pdu_bytes2 = (NBUFS - 1) * data_pdu_size + pdu_size_first + 2 * pdu_size_continued; + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(15, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + // PDU metrics + TESTASSERT_EQ(5 + 3 + 2, metrics1.num_tx_pdus); // 5 + (3 + 2) re-transmissions + TESTASSERT_EQ(4, metrics1.num_rx_pdus); // 4 status PDU + TESTASSERT_EQ(total_tx_pdu_bytes1, metrics1.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes1, metrics1.num_rx_pdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(15, metrics2.num_rx_sdu_bytes); // 5 SDUs, 3 bytes each + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // 3 status PDUs + TESTASSERT_EQ(7, metrics2.num_rx_pdus); // 7 PDUs (10 tx'ed, but 3 were lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, metrics2.num_tx_pdu_bytes); + TESTASSERT_EQ(total_rx_pdu_bytes2, + metrics2.num_rx_pdu_bytes); // 2 Bytes * (NBUFFS-1) (header size) + (NBUFFS-1) * 3 (data) + // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 33 + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + + // Check state + rlc_am_nr_rx_state_t state2_rx = rx2->get_rx_state(); + TESTASSERT_EQ(5, state2_rx.rx_next); + return SRSRAN_SUCCESS; +} + +int retx_segment_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + std::string str = "retx segment PDU (" + std::to_string(to_number(sn_size)) + " bit SN)"; + test_delimit_logger delimiter(str.c_str()); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + rlc_cnfg.am_nr.t_poll_retx = -1; + if (not rlc1.configure(rlc_cnfg)) { + return -1; + } + + if (not rlc2.configure(rlc_cnfg)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + int n_sdu_bufs = 5; + int n_pdu_bufs = 15; + + // Push 5 SDUs into RLC1 + std::vector sdu_bufs(n_sdu_bufs); + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (int i = 0; i < n_sdu_bufs; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + + // Read 15 PDUs from RLC1 + std::vector pdu_bufs(n_pdu_bufs); + for (int i = 0; i < n_pdu_bufs; i++) { + // First also test buffer state + uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size); + uint32_t remaining_full_sdus = remaining_total_bytes / payload_size; + uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size; + + uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus; + uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes); + expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + pdu_bufs[i] = srsran::make_byte_buffer(); + if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) { + // First segment, no SO + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_first, len); + } else { + // Middle or last segment, SO present + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_continued, len); + } + } + + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Write 15 - 3 PDUs into RLC2 + for (int i = 0; i < n_pdu_bufs; i++) { + if (i != 3 && i != 7 && i != 11) { + // Lose first segment of RLC_SN=1. + // Lose middle segment of RLC_SN=2. + // Lose last segment of RLC_SN=3. + rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); + } + } + + { + // Double check rx state + rlc_am_nr_rx_state_t st = rx2->get_rx_state(); + TESTASSERT_EQ(1, st.rx_next); + TESTASSERT_EQ(1, st.rx_highest_status); + TESTASSERT_EQ(2, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reordering was started + TESTASSERT_EQ(5, st.rx_next_highest); // Highest SN received + 1 + } + + // Only after t-reassembly has expired, will the status report include NACKs. + // RX_Highest_Status will be updated to to the SN + // of the first RLC SDU with SN >= RX_Next_Status_Trigger + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, 5); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(1, status_check.ack_sn); // 1 is the next expected SN (i.e. the first lost packet.) + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // After the t-Reassembly expires: + // - RX_Highest_Status is updated to the SN of the first RLC SDU with SN >= RX_Next_Status_Trigger, i.e., SN=2 + // - Because RX_Next_Highest> RX_Highest_Status +1: + // - t-Reassembly is restarted, and + // - RX_Next_Status_Trigger is set to RX_Next_Highest. + { + // Double check rx state + rlc_am_nr_rx_state_t st = rx2->get_rx_state(); + TESTASSERT_EQ(1, st.rx_next); + TESTASSERT_EQ(2, st.rx_highest_status); + TESTASSERT_EQ(5, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reassembly was started + TESTASSERT_EQ(5, st.rx_next_highest); // Highest SN received + 1 + } + + // t-reassembly has expired. Becuse RX_Highest_Status is 2 + // There should be an ACK of SN=2 and a NACK of SN=1 + constexpr uint32_t status_pdu_ack_size = 3; + uint32_t status_pdu_nack_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t status_pdu_so_size = 4; + TESTASSERT_EQ(status_pdu_ack_size + status_pdu_nack_size + status_pdu_so_size, + rlc2.get_buffer_state()); // 3 bytes for fixed header (ACK+E1) + 6 for NACK with SO = 9. + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + status_pdu_nack_size + status_pdu_so_size); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(2, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(1, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // Lost SDU on SN=1. + TESTASSERT_EQ(true, status_check.nacks[0].has_so); // It's a segment. + TESTASSERT_EQ(0, status_check.nacks[0].so_start); // First byte missing is 0. + TESTASSERT_EQ(0, status_check.nacks[0].so_end); // Last byte of the segment. + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // After the t-Reassembly expires: + // - RX_Highest_Status is updated to the SN of the first RLC SDU with SN >= RX_Next_Status_Trigger, i.e., SN=2 + // - Because RX_Next_Highest> RX_Highest_Status +1: + // - t-Reassembly is restarted, and + // - RX_Next_Status_Trigger is set to RX_Next_Highest. + { + // Double check rx state + rlc_am_nr_rx_state_t st = rx2->get_rx_state(); + TESTASSERT_EQ(1, st.rx_next); + TESTASSERT_EQ(5, st.rx_highest_status); + TESTASSERT_EQ(5, st.rx_next_status_trigger); // Rx_Next_Highest + 1, when the t-Reordering was started + TESTASSERT_EQ(5, st.rx_next_highest); // Highest SN received + 1 + } + + // t-reassembly has expired. There should be a NACK in the status report. + // There should be 3 NACKs with SO_start and SO_end + TESTASSERT_EQ(status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size), rlc2.get_buffer_state()); + { + // Read status PDU from RLC2 + byte_buffer_t status_buf; + int len = rlc2.read_pdu(status_buf.msg, status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size)); + status_buf.N_bytes = len; + + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(&status_buf, sn_size, &status_check); + TESTASSERT_EQ(5, status_check.ack_sn); // 5 is the next expected SN. + TESTASSERT_EQ(3, status_check.nacks.size()); // We lost one PDU. + TESTASSERT_EQ(1, status_check.nacks[0].nack_sn); // Lost SDU on SN=1. + TESTASSERT_EQ(true, status_check.nacks[0].has_so); // Lost SDU on SN=1. + TESTASSERT_EQ(0, status_check.nacks[0].so_start); // Lost SDU on SN=1. + TESTASSERT_EQ(0, status_check.nacks[0].so_end); // Lost SDU on SN=1. + TESTASSERT_EQ(2, status_check.nacks[1].nack_sn); // Lost SDU on SN=1. + TESTASSERT_EQ(true, status_check.nacks[1].has_so); // Lost SDU on SN=1. + TESTASSERT_EQ(1, status_check.nacks[1].so_start); // Lost SDU on SN=1. + TESTASSERT_EQ(1, status_check.nacks[1].so_end); // Lost SDU on SN=1. + TESTASSERT_EQ(3, status_check.nacks[2].nack_sn); // Lost SDU on SN=1. + TESTASSERT_EQ(true, status_check.nacks[2].has_so); // Lost SDU on SN=1. + TESTASSERT_EQ(2, status_check.nacks[2].so_start); // Lost SDU on SN=1. + TESTASSERT_EQ(0xFFFF, status_check.nacks[2].so_end); // Lost SDU on SN=1. + + // Write status PDU to RLC1 + rlc1.write_pdu(status_buf.msg, status_buf.N_bytes); + + // Check there are 3 Retx segments (a first one and two continued ones) + TESTASSERT_EQ(pdu_size_first + 2 * pdu_size_continued, rlc1.get_buffer_state()); + } + + { + // Re-transmit the 3 lost segments + for (int i = 0; i < 3; i++) { + // First also test buffer state + uint32_t remaining_segments = 3 - i; + expected_buffer_state = remaining_segments * (header_size + so_size + segment_size); + if (i == 0) { // subtract so_size, because in this setup the first retx is a "first_segment" without SO. + expected_buffer_state -= so_size; + } + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + byte_buffer_t retx_buf; + uint32_t len = 0; + if (i == 0) { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, len); + } else { + len = rlc1.read_pdu(retx_buf.msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, len); + } + retx_buf.N_bytes = len; + + rlc_am_nr_pdu_header_t header_check = {}; + uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(&retx_buf, sn_size, &header_check); + // Double check header. + if (i == 0) { + TESTASSERT_EQ(1, header_check.sn); // Double check RETX SN + TESTASSERT_EQ(rlc_nr_si_field_t::first_segment, header_check.si); + } else if (i == 1) { + TESTASSERT_EQ(2, header_check.sn); // Double check RETX SN + TESTASSERT_EQ(rlc_nr_si_field_t::neither_first_nor_last_segment, header_check.si); + } else { + TESTASSERT_EQ(3, header_check.sn); // Double check RETX SN + TESTASSERT_EQ(rlc_nr_si_field_t::last_segment, header_check.si); + } + + rlc2.write_pdu(retx_buf.msg, retx_buf.N_bytes); + } + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + } + + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + rlc_bearer_metrics_t metrics2 = rlc2.get_metrics(); + + uint32_t data_pdu_size = header_size + payload_size; + uint32_t total_tx_pdu_bytes1 = 5 * pdu_size_first + 10 * pdu_size_continued + pdu_size_first + 2 * pdu_size_continued; + uint32_t total_rx_pdu_bytes1 = 2 * status_pdu_ack_size + 3 * (status_pdu_nack_size + status_pdu_so_size); + uint32_t total_tx_pdu_bytes2 = 3 * status_pdu_ack_size + 4 * (status_pdu_nack_size + status_pdu_so_size); + uint32_t total_rx_pdu_bytes2 = 4 * pdu_size_first + 8 * pdu_size_continued + pdu_size_first + 2 * pdu_size_continued; + + // SDU metrics + TESTASSERT_EQ(5, metrics1.num_tx_sdus); + TESTASSERT_EQ(0, metrics1.num_rx_sdus); + TESTASSERT_EQ(15, metrics1.num_tx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_rx_sdu_bytes); + TESTASSERT_EQ(0, metrics1.num_lost_sdus); + + // PDU metrics + TESTASSERT_EQ(15 + 3, metrics1.num_tx_pdus); // 15 PDUs + 3 re-transmissions + TESTASSERT_EQ(2, metrics1.num_rx_pdus); // Two status PDU + TESTASSERT_EQ(total_tx_pdu_bytes1, + metrics1.num_tx_pdu_bytes); // 3 Bytes * 5 (5 PDUs without SO) + 10 * 5 (10 PDUs with SO) + // 3 (1 retx no SO) + 2 * 5 (2 retx with SO) = 78 + TESTASSERT_EQ(total_rx_pdu_bytes1, + metrics1.num_rx_pdu_bytes); // Two status PDU. One with just an ack (3 bytes) + // Another with 3 NACKs all with SO. (3 + 3*6 bytes) = 24 + TESTASSERT_EQ(0, metrics1.num_lost_sdus); // No lost SDUs + + // PDU metrics + TESTASSERT_EQ(0, metrics2.num_tx_sdus); + TESTASSERT_EQ(5, metrics2.num_rx_sdus); + TESTASSERT_EQ(0, metrics2.num_tx_sdu_bytes); + TESTASSERT_EQ(15, metrics2.num_rx_sdu_bytes); // 5 SDUs, 3 bytes each + TESTASSERT_EQ(0, metrics2.num_lost_sdus); + // SDU metrics + TESTASSERT_EQ(3, metrics2.num_tx_pdus); // 3 status PDUs + TESTASSERT_EQ(15, metrics2.num_rx_pdus); // 15 PDUs (18 tx'ed, but three were lost) + TESTASSERT_EQ(total_tx_pdu_bytes2, // Three status PDU. One with just an ack + metrics2.num_tx_pdu_bytes); // Another with 1 NACK with SO. + // Another with 3 NACKs all with SO. + TESTASSERT_EQ(total_rx_pdu_bytes2, // 3 Bytes (header + data size, without SO) * 5 (N PDUs without SO) + metrics2.num_rx_pdu_bytes); // 5 bytes (header + data size, with SO) * 10 (N PDUs with SO) + // = 81 bytes + TESTASSERT_EQ(0, metrics2.num_lost_sdus); // No lost SDUs + + // Check state + rlc_am_nr_rx_state_t state2_rx = rx2->get_rx_state(); + TESTASSERT_EQ(5, state2_rx.rx_next); + + return SRSRAN_SUCCESS; +} + +// We only increment TX_NEXT after transmitting the last segment of a SDU +// This means that we need to handle status reports where ACK_SN may be larger +// than TX_NEXT, as it may contain a NACK for the partially transmitted PDU with +// SN==TX_NEXT. +int handle_status_of_non_tx_last_segment(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("basic segmentation ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Push 1 SDU into RLC1 + unique_byte_buffer_t sdu; + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + sdu = srsran::make_byte_buffer(); + TESTASSERT(nullptr != sdu); + sdu->msg[0] = 0; // Write the index into the buffer + sdu->N_bytes = payload_size; // Give the SDU the size of 3 bytes + sdu->md.pdcp_sn = 0; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu)); + + // Read 2 PDUs. Leave last one in the tx_window. + constexpr uint16_t n_pdus = 2; + unique_byte_buffer_t pdu_bufs[n_pdus]; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + for (int i = 0; i < n_pdus; i++) { + pdu_bufs[i] = srsran::make_byte_buffer(); + TESTASSERT(nullptr != pdu_bufs[i]); + if (i == 0) { + pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, pdu_bufs[i]->N_bytes); + } else { + pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, pdu_bufs[i]->N_bytes); + } + } + + // Only middle PDU into RLC2 + // First PDU is lost to trigger status report + for (int i = 0; i < n_pdus; i++) { + if (i == 1) { + rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); + } + } + + // Advance timer to trigger status report + for (uint8_t t = 0; t < 35; t++) { + timers.step_all(); + } + TESTASSERT_NEQ(0, rlc2.get_buffer_state()); + + // Make sure RLC 1 has only the last segment to TX before getting the status report + TESTASSERT_EQ(pdu_size_continued, rlc1.get_buffer_state()); + + // Get status report from RLC 2 + // and write it to RLC 1 + { + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + status_buf->N_bytes = rlc2.read_pdu(status_buf->msg, 100); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + } + + // Make sure RLC 1 now has the last segment to TX and the RETX of the first segment + TESTASSERT_EQ(pdu_size_continued + pdu_size_first, rlc1.get_buffer_state()); + return SRSRAN_SUCCESS; +} + +// This test checks whether RLC informs upper layer when max retransmission has been reached +// due to lost SDUs as a whole +int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + int len = 0; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + test_delimit_logger delimiter("max retx lost SDU ({} bit SN)", to_number(sn_size)); + + const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc1.configure(rlc_cfg)) { + return SRSRAN_ERROR; + } + + // Push 2 SDUs into RLC1 + const uint32_t n_sdus = 2; + unique_byte_buffer_t sdu_bufs[n_sdus]; + constexpr uint32_t payload_size = 1; // Give each buffer a size of 1 byte + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (uint32_t i = 0; i < n_sdus; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 1 byte + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t pdu_size = header_size + payload_size; + + // Read 2 PDUs from RLC1 (1 byte each) + const uint32_t n_pdus = 2; + byte_buffer_t pdu_bufs[n_pdus]; + for (uint32_t i = 0; i < n_pdus; i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, pdu_size); // 2 byte header + 1 byte payload + pdu_bufs[i].N_bytes = len; + } + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Fake status PDU that ack SN=1 and nack SN=0 + rlc_am_nr_status_pdu_t fake_status(sn_size); + fake_status.ack_sn = 2; // delivered up to SN=1 + rlc_status_nack_t nack; // one SN was lost + nack.nack_sn = 0; // it was SN=0 that was lost + fake_status.push_nack(nack); + + // pack into PDU + byte_buffer_t status_pdu; + rlc_am_nr_write_status_pdu(fake_status, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu); + + // Exceed the number of tolerated retransmissions by one additional retransmission + // to trigger notification of the higher protocol layers. Note that the initial transmission + // (before starting retransmissions) does not count. See TS 38.322 Sec. 5.3.2 + for (uint32_t retx_count = 0; retx_count < rlc_cfg.am_nr.max_retx_thresh + 1; ++retx_count) { + // we've not yet reached max attempts + TESTASSERT(tester.max_retx_triggered == false); + + // Write status PDU to RLC1 + rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); + + byte_buffer_t pdu_buf; + len = rlc1.read_pdu(pdu_buf.msg, pdu_size); // 2 byte header + 1 byte payload + } + + // Now maxRetx should have been triggered + TESTASSERT(tester.max_retx_triggered == true); + + return SRSRAN_SUCCESS; +} + +// This test checks whether RLC informs upper layer when max retransmission has been reached +// due to lost SDU segments +int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + int len = 0; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + test_delimit_logger delimiter("max retx lost SDU segment ({} bit SN)", to_number(sn_size)); + + const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc1.configure(rlc_cfg)) { + return SRSRAN_ERROR; + } + + // Push 2 SDUs into RLC1 + const uint32_t n_sdus = 2; + unique_byte_buffer_t sdu_bufs[n_sdus]; + constexpr uint32_t payload_size = 20; // Give each buffer a size of 20 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (uint32_t i = 0; i < n_sdus; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 20 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size_first = 13; + constexpr uint32_t segment_size_continued = 7; + uint32_t pdu_size_first = header_size + segment_size_first; + uint32_t pdu_size_continued = header_size + so_size + segment_size_continued; + + // Read 2*2=4 PDUs from RLC1 and limit to 15 byte to force segmentation in two parts: + // Segment 1: 2 byte header + 13 byte payload; space fully used + // Segment 2: 4 byte header + 7 byte payload; space not fully used, 4 bytes left over + const uint32_t n_pdus = 4; + byte_buffer_t pdu_bufs[n_pdus]; + for (uint32_t i = 0; i < n_pdus; i++) { + len = rlc1.read_pdu(pdu_bufs[i].msg, pdu_size_first); + pdu_bufs[i].N_bytes = len; + } + + TESTASSERT(0 == rlc1.get_buffer_state()); + + // Fake status PDU that ack SN=1 and nack {SN=0 segment 0, SN=0 segment 1} + rlc_am_nr_status_pdu_t status_lost_both_segments(sn_size); + status_lost_both_segments.ack_sn = 2; // delivered up to SN=1 + + // two segments lost + { + rlc_status_nack_t nack; + nack.nack_sn = 0; // it was SN=0 that was lost + nack.has_so = true; // this NACKs a segment + nack.so_start = 0; // segment starts at (and includes) byte 0 + nack.so_end = 12; // segment ends at (and includes) byte 12 + status_lost_both_segments.push_nack(nack); + } + + { + rlc_status_nack_t nack; + nack.nack_sn = 0; // it was SN=0 that was lost + nack.has_so = true; // this NACKs a segment + nack.so_start = 13; // segment starts at (and includes) byte 13 + nack.so_end = 19; // segment ends at (and includes) byte 19 + status_lost_both_segments.push_nack(nack); + } + + // pack into PDU + byte_buffer_t status_pdu_lost_both_segments; + rlc_am_nr_write_status_pdu( + status_lost_both_segments, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu_lost_both_segments); + + // Fake status PDU that ack SN=1 and nack {SN=0 segment 1} + rlc_am_nr_status_pdu_t status_lost_second_segment(sn_size); + status_lost_second_segment.ack_sn = 2; // delivered up to SN=1 + + // one SN was lost + { + rlc_status_nack_t nack; + nack.nack_sn = 0; // it was SN=0 that was lost + nack.has_so = true; // this NACKs a segment + nack.so_start = 13; // segment starts at (and includes) byte 13 + nack.so_end = 19; // segment ends at (and includes) byte 19 + status_lost_second_segment.push_nack(nack); + } + + // pack into PDU + byte_buffer_t status_pdu_lost_second_segment; + rlc_am_nr_write_status_pdu( + status_lost_second_segment, rlc_cfg.am_nr.tx_sn_field_length, &status_pdu_lost_second_segment); + + // Exceed the number of tolerated retransmissions by one additional retransmission + // to trigger notification of the higher protocol layers. Note that the initial transmission + // (before starting retransmissions) does not count. See TS 38.322 Sec. 5.3.2 + for (uint32_t retx_count = 0; retx_count < rlc_cfg.am_nr.max_retx_thresh + 1; ++retx_count) { + byte_buffer_t pdu_buf; + + // we've not yet reached max attempts + TESTASSERT(tester.max_retx_triggered == false); + + if (retx_count < rlc_cfg.am_nr.max_retx_thresh / 2) { + // Send NACK for segment 1 and segment 2 + // Although two segments, this must count as one retransmission, + // because both segments NACK the same SDU in the same status message. + rlc1.write_pdu(status_pdu_lost_both_segments.msg, status_pdu_lost_both_segments.N_bytes); + + // read the retransmitted PDUs + len = rlc1.read_pdu(pdu_buf.msg, pdu_size_first); // 2 byte header + 13 byte payload + len = rlc1.read_pdu(pdu_buf.msg, pdu_size_first); // 4 byte header + 7 byte payload + } else { + // Send NACK for segment 2 (assume at least segment 1 was finally received) + rlc1.write_pdu(status_pdu_lost_second_segment.msg, status_pdu_lost_second_segment.N_bytes); + + // read the retransmitted PDUs + len = rlc1.read_pdu(pdu_buf.msg, pdu_size_first); // 4 byte header + 7 byte payload + } + } + + // Now maxRetx should have been triggered + TESTASSERT(tester.max_retx_triggered == true); + + return SRSRAN_SUCCESS; +} + +// This test checks the correct functioning of RLC discard functionality +int discard_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + test_delimit_logger delimiter("discard test ({} bit SN)", to_number(sn_size)); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return SRSRAN_ERROR; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return SRSRAN_ERROR; + } + + uint32_t num_tx_sdus = 1; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + uint32_t payload_size = 5; // Give each buffer a size of 5 bytes + // Test discarding the single SDU from the queue + { + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = payload_size; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + } + rlc1.discard_sdu(0); // Try to discard PDCP_SN=0 + TESTASSERT(rlc1.has_data() == false); + + num_tx_sdus = 10; + payload_size = 7; // Give each buffer a size of 7 bytes + // Test discarding two SDUs in the middle (SN=3) and end (SN=9) of the queue and read PDUs after + { + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = payload_size; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + } + TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 10 * (2B Header + 7B Payload) + rlc1.discard_sdu(3); // Try to discard PDCP_SN=3 + TESTASSERT(rlc1.has_data() == true); + TESTASSERT(rlc1.get_buffer_state() == (num_tx_sdus - 1) * (header_size + payload_size)); + rlc1.discard_sdu(9); // Try to discard PDCP_SN=9 + TESTASSERT(rlc1.has_data() == true); + TESTASSERT(rlc1.get_buffer_state() == (num_tx_sdus - 2) * (header_size + payload_size)); + + num_tx_sdus = 8; + { + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + uint32_t len = rlc1.read_pdu(pdu->msg, 50); // sufficient space to read without segmentation + pdu->N_bytes = len; + TESTASSERT((header_size + payload_size) == len); + // Check that we don't have any SN gaps + rlc_am_nr_pdu_header_t header = {}; + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &header); + TESTASSERT(header.sn == i); + } + } + TESTASSERT(rlc1.has_data() == false); + srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size()); + + num_tx_sdus = 3; + payload_size = 7; // Give each buffer a size of 7 bytes + // Test discarding non-existing SDU from the queue + { + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = payload_size; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = i; // Write the index into the buffer + } + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + } + TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 3 * (2B Header + 7B Payload) + rlc1.discard_sdu(8); // Try to discard PDCP_SN=8, which doesn't exist + TESTASSERT(rlc1.get_buffer_state() == num_tx_sdus * (header_size + payload_size)); // 3 * (2B Header + 7B Payload) + + return SRSRAN_SUCCESS; +} + +// Test p bit set on new TX with PollPDU +int poll_pdu(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("pollPDU test ({} bit SN)", to_number(sn_size)); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.tx_sn_field_length = sn_size; // Number of bits used for tx (UL) sequence number + rlc_cnfg.am_nr.rx_sn_field_length = sn_size; // Number of bits used for rx (DL) sequence number + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; + + // Test p bit set on new TX with PollPDU + { + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + // pollPDU == 4 + uint32_t num_tx_sdus = 6; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + uint32_t num_tx_pdus = 6; + uint32_t pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, pdu_size); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &hdr); + if (i != 3 && i != 5) { // P bit set for PollPDU and for empty TX queue + TESTASSERT_EQ(0, hdr.p); + } else { + TESTASSERT_EQ(1, hdr.p); + } + } + } + return SRSRAN_SUCCESS; +} + +// Test p bit set on new TX with PollBYTE +int poll_byte(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("pollBYTE test ({} bit SN)", to_number(sn_size)); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.tx_sn_field_length = sn_size; // Number of bits used for tx (UL) sequence number + rlc_cnfg.am_nr.rx_sn_field_length = sn_size; // Number of bits used for rx (DL) sequence number + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; + + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + // pollByte == 3000 + uint32_t num_tx_sdus = 4; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = i == 0 ? 2999 : 1; + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + uint32_t num_tx_pdus = num_tx_sdus; + uint32_t small_pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; + uint32_t large_pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3001 : 3002; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + uint32_t nof_bytes = i == 0 ? large_pdu_size : small_pdu_size; + pdu->N_bytes = rlc1.read_pdu(pdu->msg, nof_bytes); + TESTASSERT_EQ(nof_bytes, pdu->N_bytes); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), rlc_am_nr_sn_size_t::size18bits, &hdr); + if (i != 1 && i != 3) { + TESTASSERT_EQ(0, hdr.p); + } else { + TESTASSERT_EQ(1, hdr.p); + } + } + return SRSRAN_SUCCESS; +} + +// Test p bit set on RETXes that cause an empty retx queue. +int poll_retx(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("poll retx test ({} bit SN)", to_number(sn_size)); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + + rlc_config_t rlc_cnfg = {}; + rlc_cnfg.rat = srsran_rat_t::nr; + rlc_cnfg.rlc_mode = rlc_mode_t::am; + rlc_cnfg.am_nr.tx_sn_field_length = sn_size; // Number of bits used for tx (UL) sequence number + rlc_cnfg.am_nr.rx_sn_field_length = sn_size; // Number of bits used for rx (DL) sequence number + rlc_cnfg.am_nr.poll_pdu = 4; + rlc_cnfg.am_nr.poll_byte = 3000; + rlc_cnfg.am_nr.t_status_prohibit = 8; + rlc_cnfg.am_nr.max_retx_thresh = 8; + rlc_cnfg.am_nr.t_reassembly = 35; + + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + + // pollPDU == 4 + { + uint32_t num_tx_sdus = 5; + for (uint32_t i = 0; i < num_tx_sdus; ++i) { + // Write SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 1; + sdu->md.pdcp_sn = i; + rlc1.write_sdu(std::move(sdu)); + } + } + { + // Read 3 PDUs and NACK the second one + uint32_t num_tx_pdus = 3; + uint32_t pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, pdu_size); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &hdr); + TESTASSERT_EQ(0, hdr.p); + } + } + { + unique_byte_buffer_t status_pdu = srsran::make_byte_buffer(); + TESTASSERT(status_pdu != nullptr); + rlc_am_nr_status_pdu_t status(rlc_am_nr_sn_size_t::size12bits); + status.ack_sn = 2; + { + rlc_status_nack_t nack; + nack.nack_sn = 1; // SN=1 needs RETX + status.push_nack(nack); + } + rlc_am_nr_write_status_pdu(status, rlc_cnfg.am_nr.tx_sn_field_length, status_pdu.get()); + rlc1.write_pdu(status_pdu->msg, status_pdu->N_bytes); + } + { + // Read 2 PDUs, + uint32_t num_tx_pdus = 3; + uint32_t pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, pdu_size); + TESTASSERT_EQ(pdu_size, pdu->N_bytes); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &hdr); + if (i == 0) { + TESTASSERT_EQ(0, hdr.p); // No poll since pollPDU is not incremented for RETX + TESTASSERT_EQ(1, hdr.sn); + } else { + TESTASSERT_EQ(1, hdr.p); // poll set because of pollPDU for SN=3 and empty buffer on SN=4 + } + } + } + { + unique_byte_buffer_t status_pdu = srsran::make_byte_buffer(); + TESTASSERT(status_pdu != nullptr); + rlc_am_nr_status_pdu_t status(rlc_am_nr_sn_size_t::size12bits); + status.ack_sn = 4; + { + rlc_status_nack_t nack; + nack.nack_sn = 1; // SN=1 needs RETX + status.push_nack(nack); + } + rlc_am_nr_write_status_pdu(status, rlc_cnfg.am_nr.tx_sn_field_length, status_pdu.get()); + rlc1.write_pdu(status_pdu->msg, status_pdu->N_bytes); + } + { + // Read 1 RETX PDU. Empty retx buffer, so poll should be set + uint32_t num_tx_pdus = 1; + uint32_t pdu_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 3 : 4; + for (uint32_t i = 0; i < num_tx_pdus; ++i) { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, pdu_size); + TESTASSERT_EQ(pdu_size, pdu->N_bytes); + rlc_am_nr_pdu_header_t hdr; + rlc_am_nr_read_data_pdu_header(pdu.get(), sn_size, &hdr); + if (i == 0) { + TESTASSERT_EQ(1, hdr.p); // Poll set because of empty retx buffer + TESTASSERT_EQ(1, hdr.sn); + } + } + } + return SRSRAN_SUCCESS; +} + +// This test checks whether re-transmissions are triggered correctly in case the t-PollRetranmission expires. +// It checks if the poll retx timer is re-armed upon receiving an ACK for POLL_SN +bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("poll retx expiry test ({} bit SN)", to_number(sn_size)); + + srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100); + srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100); + + rlc_config_t rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(); + + rlc_cnfg.am_nr.tx_sn_field_length = sn_size; // Number of bits used for tx (UL) sequence number + rlc_cnfg.am_nr.rx_sn_field_length = sn_size; // Number of bits used for rx (DL) sequence number + rlc_cnfg.am_nr.t_poll_retx = 65; + rlc_cnfg.am_nr.poll_pdu = -1; + rlc_cnfg.am_nr.poll_byte = -1; + rlc_cnfg.am_nr.max_retx_thresh = 6; + rlc_cnfg.am_nr.t_status_prohibit = 55; + + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + if (not rlc1.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + if (not rlc2.configure(rlc_cnfg)) { + return SRSRAN_ERROR; + } + + unsigned hdr_no_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + unsigned hdr_with_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 4 : 5; + unsigned ack_size = 3; + unsigned nack_size_no_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + unsigned nack_size_with_so = sn_size == rlc_am_nr_sn_size_t::size12bits ? (2 + 4) : (3 + 4); + // Tx SDU with 135 B of data + // Read it in two PDU segments, so=0 (89B of data) + // and so=89 (46B of data) + { + // TX a single SDU + unique_byte_buffer_t sdu = srsran::make_byte_buffer(); + TESTASSERT(sdu != nullptr); + sdu->N_bytes = 135; + for (uint32_t k = 0; k < sdu->N_bytes; ++k) { + sdu->msg[k] = 0; // Write the index into the buffer + } + sdu->md.pdcp_sn = 0; + rlc1.write_sdu(std::move(sdu)); + + // Read two PDUs. The last PDU should trigger polling, as it + // is the last SDU segment in the buffer. + uint32_t pdu1_size = 89 + hdr_no_so; + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + TESTASSERT(pdu1 != nullptr); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, pdu1_size); // 89 bytes payload + + uint32_t pdu2_size = 46 + hdr_with_so; + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + TESTASSERT(pdu2 != nullptr); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, pdu2_size); // 46 bytes payload + + // Deliver PDU2 to RLC2. PDU1 is lost + rlc2.write_pdu(pdu2->msg, pdu2->N_bytes); + + // Double-check polling status in PDUs + rlc_am_nr_pdu_header_t hdr1 = {}; + rlc_am_nr_read_data_pdu_header(pdu1.get(), sn_size, &hdr1); + rlc_am_nr_pdu_header_t hdr2 = {}; + rlc_am_nr_read_data_pdu_header(pdu2.get(), sn_size, &hdr2); + TESTASSERT_EQ(0, hdr1.p); + TESTASSERT_EQ(1, hdr2.p); + } + + // Step timers until t-PollRetransmit timer expires on RLC1 + // t-PollRetransmit will schedule SN=0, so=0, payload_len=89 for RETX + // t-Reordering timer also will expire on RLC2, meaning we will also get a status report. + TESTASSERT_EQ(false, rlc1.has_data()); + for (int cnt = 0; cnt < 65; cnt++) { + timers.step_all(); + } + + // Make sure that the SDU segment was scheduled for RETX + TESTASSERT_EQ(89 + hdr_no_so, rlc1.get_buffer_state()); + + // Further segment RETX segment + // First SDU segment (81B of data) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 81 + hdr_no_so); + } + // Second SDU segment (8B of data) + { + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT(pdu != nullptr); + pdu->N_bytes = rlc1.read_pdu(pdu->msg, 8 + hdr_with_so); + } + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Read status PDU from RLC2 (triggered previously from t-Reordering) + // ACK=1, NACKs=1 + // NACK_SN[0].sn=0, NACK_SN[0].so_start=0, NACK_SN[0].so_end=89 + uint32_t status_size = rlc2.get_buffer_state(); + TESTASSERT_EQ(ack_size + nack_size_with_so, status_size); + + // Read status PDU from RLC2 + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + TESTASSERT(status_buf != nullptr); + int len = rlc2.read_pdu(status_buf->msg, status_size); + status_buf->N_bytes = len; + + TESTASSERT(0 == rlc2.get_buffer_state()); + + // Assert status is correct + rlc_am_nr_status_pdu_t status_check(sn_size); + rlc_am_nr_read_status_pdu(status_buf.get(), sn_size, &status_check); + TESTASSERT(status_check.ack_sn == 1); // SN=1 is first SN missing without a NACK + TESTASSERT(status_check.nacks.size() == 1); // 1 PDU lost + TESTASSERT(status_check.nacks[0].nack_sn == 0); // SN=0 + TESTASSERT(status_check.nacks[0].so_start == 0); // SN=0 + TESTASSERT_EQ(88, status_check.nacks[0].so_end); // SN=0 + + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + // Deliver status PDU after ReTX to RLC1. This should restart t-PollRetransmission + // It NACKs SDU segment 0:81 and 81:89 + TESTASSERT_EQ(false, rlc1.has_data()); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + TESTASSERT_EQ(true, rlc1.has_data()); + + // [I] SRB1 Retx SDU segment (81 B of data) + // [I] SRB1 Retx PDU segment (8 B of data) + { + unique_byte_buffer_t pdu1 = srsran::make_byte_buffer(); + TESTASSERT(pdu1 != nullptr); + pdu1->N_bytes = rlc1.read_pdu(pdu1->msg, 81 + hdr_no_so); + + unique_byte_buffer_t pdu2 = srsran::make_byte_buffer(); + TESTASSERT(pdu2 != nullptr); + pdu2->N_bytes = rlc1.read_pdu(pdu2->msg, 8 + hdr_with_so); + } + + TESTASSERT_EQ(false, rlc1.has_data()); // We don't have any more data + + // Step timers until t-PollRetransmission timer expires on RLC1 + // [I] SRB1 Schedule SN=3 for reTx + for (int cnt = 0; cnt < 66; cnt++) { + timers.step_all(); + } + TESTASSERT_EQ(81 + hdr_no_so, rlc1.get_buffer_state()); + srslog::fetch_basic_logger("TEST").info("t-PollRetransmssion successfully restarted."); + + return SRSRAN_SUCCESS; +} + +int rx_nack_range_no_so_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + std::string str = "Rx NACK range test (" + std::to_string(to_number(sn_size)) + " bit SN)"; + test_delimit_logger delimiter(str.c_str()); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + rlc_cnfg.am_nr.t_poll_retx = -1; + if (not rlc1.configure(rlc_cnfg)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + int n_sdu_bufs = 5; + int n_pdu_bufs = 15; + + // Push 5 SDUs into RLC1 + std::vector sdu_bufs(n_sdu_bufs); + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (int i = 0; i < n_sdu_bufs; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + + // Read 15 PDUs from RLC1 + std::vector pdu_bufs(n_pdu_bufs); + for (int i = 0; i < n_pdu_bufs; i++) { + // First also test buffer state + uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size); + uint32_t remaining_full_sdus = remaining_total_bytes / payload_size; + uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size; + + uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus; + uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes); + expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + pdu_bufs[i] = srsran::make_byte_buffer(); + if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) { + // First segment, no SO + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_first, len); + } else { + // Middle or last segment, SO present + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_continued, len); + } + } + + // Deliver dummy status report with nack range betwen PDU 6 and 10. + rlc_am_nr_status_pdu_t status(sn_size); + status.ack_sn = 5; + rlc_status_nack_t nack = {}; + nack.nack_sn = 1; + nack.has_nack_range = true; + nack.nack_range = 3; + status.push_nack(nack); + byte_buffer_t status_pdu; + rlc_am_nr_write_status_pdu(status, sn_size, &status_pdu); + + rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); + + TESTASSERT_EQ(3 * pdu_size_first + 6 * pdu_size_continued, rlc1.get_buffer_state()); + return SRSRAN_SUCCESS; +} + +int rx_nack_range_with_so_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + std::string str = "Rx NACK range test (" + std::to_string(to_number(sn_size)) + " bit SN)"; + test_delimit_logger delimiter(str.c_str()); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + rlc_cnfg.am_nr.t_poll_retx = -1; + if (not rlc1.configure(rlc_cnfg)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + int n_sdu_bufs = 5; + int n_pdu_bufs = 15; + + // Push 5 SDUs into RLC1 + std::vector sdu_bufs(n_sdu_bufs); + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (int i = 0; i < n_sdu_bufs; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + + // Read 15 PDUs from RLC1 + std::vector pdu_bufs(n_pdu_bufs); + for (int i = 0; i < n_pdu_bufs; i++) { + // First also test buffer state + uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size); + uint32_t remaining_full_sdus = remaining_total_bytes / payload_size; + uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size; + + uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus; + uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes); + expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + pdu_bufs[i] = srsran::make_byte_buffer(); + if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) { + // First segment, no SO + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_first, len); + } else { + // Middle or last segment, SO present + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_continued, len); + } + } + + // Deliver dummy status report with nack range betwen PDU 6 and 10. + rlc_am_nr_status_pdu_t status(sn_size); + status.ack_sn = 5; + + rlc_status_nack_t nack = {}; + nack.nack_sn = 1; + nack.has_nack_range = true; + nack.nack_range = 3; + nack.has_so = true; + nack.so_start = 2; + nack.so_end = 0; + status.push_nack(nack); + byte_buffer_t status_pdu; + rlc_am_nr_write_status_pdu(status, sn_size, &status_pdu); + + rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); + + TESTASSERT_EQ(2 * pdu_size_first + 3 * pdu_size_continued, rlc1.get_buffer_state()); + return SRSRAN_SUCCESS; +} + +int rx_nack_range_with_so_starting_with_full_sdu_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + std::string str = + "Rx NACK range test with SO starting with full SDU (" + std::to_string(to_number(sn_size)) + " bit SN)"; + test_delimit_logger delimiter(str.c_str()); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + rlc_cnfg.am_nr.t_poll_retx = -1; + if (not rlc1.configure(rlc_cnfg)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + int n_sdu_bufs = 5; + int n_pdu_bufs = 15; + + // Push 5 SDUs into RLC1 + std::vector sdu_bufs(n_sdu_bufs); + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (int i = 0; i < n_sdu_bufs; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_whole = header_size + payload_size; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + + // Read 15 PDUs from RLC1 + std::vector pdu_bufs(n_pdu_bufs); + for (int i = 0; i < n_pdu_bufs; i++) { + // First also test buffer state + uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size); + uint32_t remaining_full_sdus = remaining_total_bytes / payload_size; + uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size; + + uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus; + uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes); + expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + pdu_bufs[i] = srsran::make_byte_buffer(); + if (i == 3) { + // Special handling for SDU SN=1 (i==3): send as a whole, not segmented + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_whole); // 2 bytes for header + 3 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_whole, len); + // update i to skip 2 segments + i += 2; + } else { + if (i == 0 || i == 6 || i == 9 || i == 12) { + // First segment, no SO + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_first, len); + } else { + // Middle or last segment, SO present + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_continued, len); + } + } + } + + // Deliver dummy status report with nack range betwen PDU 4 and 10. + rlc_am_nr_status_pdu_t status(sn_size); + status.ack_sn = 5; + + rlc_status_nack_t nack = {}; + nack.nack_sn = 1; + nack.has_nack_range = true; + nack.nack_range = 3; + nack.has_so = true; + nack.so_start = 0; + nack.so_end = 0; + status.push_nack(nack); + byte_buffer_t status_pdu; + rlc_am_nr_write_status_pdu(status, sn_size, &status_pdu); + + rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); + + TESTASSERT_EQ(pdu_size_whole + 2 * pdu_size_first + 2 * pdu_size_continued, rlc1.get_buffer_state()); + return SRSRAN_SUCCESS; +} + +int rx_nack_range_with_so_ending_with_full_sdu_test(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + std::string str = + "Rx NACK range test with SO starting with full SDU (" + std::to_string(to_number(sn_size)) + " bit SN)"; + test_delimit_logger delimiter(str.c_str()); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + rlc_cnfg.am_nr.t_poll_retx = -1; + if (not rlc1.configure(rlc_cnfg)) { + return -1; + } + + // after configuring entity + TESTASSERT(0 == rlc1.get_buffer_state()); + + int n_sdu_bufs = 5; + int n_pdu_bufs = 15; + + // Push 5 SDUs into RLC1 + std::vector sdu_bufs(n_sdu_bufs); + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (int i = 0; i < n_sdu_bufs; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + sdu_bufs[i]->msg[0] = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_whole = header_size + payload_size; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + + // Read 15 PDUs from RLC1 + std::vector pdu_bufs(n_pdu_bufs); + for (int i = 0; i < n_pdu_bufs; i++) { + // First also test buffer state + uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size); + uint32_t remaining_full_sdus = remaining_total_bytes / payload_size; + uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size; + + uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus; + uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes); + expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu; + TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state()); + + pdu_bufs[i] = srsran::make_byte_buffer(); + if (i == 9) { + // Special handling for SDU SN=3 (i==9): send as a whole, not segmented + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_whole); // 2 bytes for header + 3 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_whole, len); + // update i to skip 2 segments + i += 2; + } else { + if (i == 0 || i == 3 || i == 6 || i == 12) { + // First segment, no SO + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_first, len); + } else { + // Middle or last segment, SO present + uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload + pdu_bufs[i]->N_bytes = len; + TESTASSERT_EQ(pdu_size_continued, len); + } + } + } + + // Deliver dummy status report with nack range betwen PDU 6 and 12. + rlc_am_nr_status_pdu_t status(sn_size); + status.ack_sn = 5; + + rlc_status_nack_t nack = {}; + nack.nack_sn = 1; + nack.has_nack_range = true; + nack.nack_range = 3; + nack.has_so = true; + nack.so_start = 2; + nack.so_end = rlc_status_nack_t::so_end_of_sdu; + status.push_nack(nack); + byte_buffer_t status_pdu; + rlc_am_nr_write_status_pdu(status, sn_size, &status_pdu); + + rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes); + + TESTASSERT_EQ(1 * pdu_size_first + 3 * pdu_size_continued + pdu_size_whole, rlc1.get_buffer_state()); + return SRSRAN_SUCCESS; +} + +int out_of_order_status(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("out of order status report ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + basic_test_tx(&rlc1, pdu_bufs, sn_size); + + // Status 1, ACK SN=2, NACK_SN = 1 + rlc_am_nr_status_pdu_t status1(sn_size); + status1.ack_sn = 2; + { + rlc_status_nack_t nack = {}; + nack.nack_sn = 1; + status1.push_nack(nack); + } + + // Status 2, ACK SN=5, NACK SN = 3 + rlc_am_nr_status_pdu_t status2(sn_size); + status2.ack_sn = 5; + { + rlc_status_nack_t nack = {}; + nack.nack_sn = 3; + status2.push_nack(nack); + } + + // pack into PDU + byte_buffer_t status1_pdu; + rlc_am_nr_write_status_pdu(status1, sn_size, &status1_pdu); + + // pack into PDU + byte_buffer_t status2_pdu; + rlc_am_nr_write_status_pdu(status2, sn_size, &status2_pdu); + + // Write status 2 to RLC1 + rlc1.write_pdu(status2_pdu.msg, status2_pdu.N_bytes); + + // Check TX_NEXT_ACK + { + rlc_am_nr_tx_state_t st = tx1->get_tx_state(); + TESTASSERT_EQ(3, st.tx_next_ack); // SN=3 was nacked on status report 2 + TESTASSERT_EQ(2, tx1->get_tx_window_utilization()); // 2 PDUs still in TX_WINDOW + } + // Write status 1 to RLC1 + rlc1.write_pdu(status1_pdu.msg, status1_pdu.N_bytes); + + // Check TX_NEXT_ACK + { + rlc_am_nr_tx_state_t st = tx1->get_tx_state(); + TESTASSERT_EQ(3, st.tx_next_ack); + TESTASSERT_EQ(2, tx1->get_tx_window_utilization()); + } + // Check statistics + rlc_bearer_metrics_t metrics1 = rlc1.get_metrics(); + + return SRSRAN_SUCCESS; +} + +// If we lose the status report +int lost_status_and_advanced_rx_window(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("Lost status report and advance RX window ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc1.configure(cfg)) { + return -1; + } + if (not rlc2.configure(cfg)) { + return -1; + } + uint32_t mod_nr = cardinality(cfg.am_nr.tx_sn_field_length); + + // Fill up the RX window + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (uint32_t sn = 0; sn < 10; ++sn) { + // Write SDU + unique_byte_buffer_t sdu_buf = srsran::make_byte_buffer(); + sdu_buf->msg[0] = sn; // Write the index into the buffer + sdu_buf->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_buf->md.pdcp_sn = sn; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_buf)); + + // Read PDU + unique_byte_buffer_t pdu_buf = srsran::make_byte_buffer(); + pdu_buf->N_bytes = rlc1.read_pdu(pdu_buf->msg, 100); + + // Write PDU into RLC 2 + // We receive all PDUs + rlc2.write_pdu(pdu_buf->msg, pdu_buf->N_bytes); + } + + // We got the polling bit, so we generate the status report. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + + // Read status PDU + { + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + status_buf->N_bytes = rlc2.read_pdu(status_buf->msg, 3); + } + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // We do not write the status report into RLC 1 + // We step trought the timers to let t-PollRetransmission expire + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + for (int t = 0; t < 45; t++) { + timers.step_all(); + } + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); + + // Read RETX of POLL_SN and check if it triggered the + // Status report + { + unique_byte_buffer_t pdu_buf = srsran::make_byte_buffer(); + pdu_buf->N_bytes = rlc1.read_pdu(pdu_buf->msg, 100); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + rlc2.write_pdu(pdu_buf->msg, pdu_buf->N_bytes); + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + } + + return SRSRAN_SUCCESS; +} + +int full_rx_window_t_reassembly_expiry(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(false, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("Full RX window and t-Reassmbly expiry test ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc1.configure(cfg)) { + return -1; + } + if (not rlc2.configure(cfg)) { + return -1; + } + uint32_t mod_nr = cardinality(cfg.am_nr.tx_sn_field_length); + + // Fill up the RX window + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (uint32_t sn = 0; sn < am_window_size(sn_size); ++sn) { + // Write SDU + unique_byte_buffer_t sdu_buf = srsran::make_byte_buffer(); + sdu_buf->msg[0] = sn; // Write the index into the buffer + sdu_buf->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_buf->md.pdcp_sn = sn; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_buf)); + + // Read PDU + unique_byte_buffer_t pdu_buf = srsran::make_byte_buffer(); + pdu_buf->N_bytes = rlc1.read_pdu(pdu_buf->msg, 100); + + // Write PDUs into RLC 2 + // Do not write SN=0 to fill up the RX window + if (sn != 0) { + rlc2.write_pdu(pdu_buf->msg, pdu_buf->N_bytes); + } + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // Read status PDU + { + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + status_buf->N_bytes = rlc2.read_pdu(status_buf->msg, 1000); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); + } + // Check Rx_Status_Highest + { + rlc_am_nr_rx_state_t st = rx2->get_rx_state(); + TESTASSERT_EQ(2048, st.rx_highest_status); + } + + return SRSRAN_SUCCESS; +} + +int main() +{ + // Setup the log message spy to intercept error and warning log entries from RLC + if (!srslog::install_custom_sink(srsran::log_sink_message_spy::name(), + std::unique_ptr( + new srsran::log_sink_message_spy(srslog::get_default_log_formatter())))) { + return SRSRAN_ERROR; + } + + auto* spy = static_cast(srslog::find_sink(srsran::log_sink_message_spy::name())); + if (spy == nullptr) { + return SRSRAN_ERROR; + } + srslog::set_default_sink(*spy); + + auto& logger_rlc1 = srslog::fetch_basic_logger("RLC_AM_1", *spy, false); + auto& logger_rlc2 = srslog::fetch_basic_logger("RLC_AM_2", *spy, false); + logger_rlc1.set_hex_dump_max_size(100); + logger_rlc2.set_hex_dump_max_size(100); + logger_rlc1.set_level(srslog::basic_levels::debug); + logger_rlc2.set_level(srslog::basic_levels::debug); + + // start log back-end + srslog::init(); + std::initializer_list sn_sizes = {rlc_am_nr_sn_size_t::size12bits, + rlc_am_nr_sn_size_t::size18bits}; + for (auto sn_size : sn_sizes) { + TESTASSERT(window_checker_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(retx_segmentation_required_checker_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(basic_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(lost_pdu_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(lost_pdu_duplicated_nack_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(lost_pdus_trimmed_nack_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(clean_retx_queue_of_acked_sdus_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(basic_segmentation_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(segment_retx_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(segment_retx_and_loose_segments_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(retx_segment_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(handle_status_of_non_tx_last_segment(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(max_retx_lost_sdu_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(max_retx_lost_segments_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(discard_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(poll_pdu(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(poll_byte(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(poll_retx(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(poll_retx_expiry(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(rx_nack_range_no_so_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(rx_nack_range_with_so_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(rx_nack_range_with_so_starting_with_full_sdu_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(rx_nack_range_with_so_ending_with_full_sdu_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(out_of_order_status(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(lost_status_and_advanced_rx_window(sn_size) == SRSRAN_SUCCESS); + } + TESTASSERT(full_rx_window_t_reassembly_expiry(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); + return SRSRAN_SUCCESS; +} diff --git a/lib/test/upper/rlc_common_test.cc b/lib/test/rlc/rlc_common_test.cc similarity index 98% rename from lib/test/upper/rlc_common_test.cc rename to lib/test/rlc/rlc_common_test.cc index f2d1172bb..b0ccfddec 100644 --- a/lib/test/upper/rlc_common_test.cc +++ b/lib/test/rlc/rlc_common_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsran/upper/rlc.h" +#include "srsran/rlc/rlc.h" #include #define TESTASSERT(cond) \ @@ -63,6 +63,7 @@ public: // RRC interface void max_retx_attempted() {} + void protocol_failure() {} const char* get_rb_name(uint32_t lcid) { return "TestRB"; } void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; } diff --git a/lib/test/rlc/rlc_stress_test.cc b/lib/test/rlc/rlc_stress_test.cc new file mode 100644 index 000000000..207c89dcf --- /dev/null +++ b/lib/test/rlc/rlc_stress_test.cc @@ -0,0 +1,402 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rlc_stress_test.h" +#include "srsran/common/block_queue.h" +#include "srsran/common/crash_handler.h" +#include "srsran/common/rlc_pcap.h" +#include "srsran/common/test_common.h" +#include "srsran/common/threads.h" +#include "srsran/common/tsan_options.h" +#include "srsran/rlc/rlc.h" +#include +#include +#include +#include +#include +#include +#include + +#include "srsran/common/mac_pcap.h" +#include "srsran/mac/mac_sch_pdu_nr.h" + +static std::unique_ptr pcap_handle = nullptr; +/*********************** + * MAC tester class + ***********************/ +void mac_dummy::run_thread() +{ + srsran::move_task_t task; + while (run_enable) { + // Downlink direction first (RLC1->RLC2) + run_tti(rlc1, rlc2, true); + + // UL direction (RLC2->RLC1) + run_tti(rlc2, rlc1, false); + + // step timer + timers->step_all(); + + if (pending_tasks.try_pop(&task)) { + task(); + } + } + if (pending_tasks.try_pop(&task)) { + task(); + } +} + +void mac_dummy::run_tti(srsue::rlc_interface_mac* tx_rlc, srsue::rlc_interface_mac* rx_rlc, bool is_dl) +{ + std::vector pdu_list; + + // Run Tx + run_tx_tti(tx_rlc, rx_rlc, pdu_list); + + // Reverse PDUs + std::reverse(pdu_list.begin(), pdu_list.end()); + + // Run Rx + run_rx_tti(tx_rlc, rx_rlc, is_dl, pdu_list); +} + +void mac_dummy::run_tx_tti(srsue::rlc_interface_mac* tx_rlc, + srsue::rlc_interface_mac* rx_rlc, + std::vector& pdu_list) +{ + // Generate A number of MAC PDUs + for (uint32_t i = 0; i < args.nof_pdu_tti; i++) { + // Create PDU unique buffer + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + printf("Fatal Error: Could not allocate PDU in %s\n", __FUNCTION__); + exit(-1); + } + + // Get MAC PDU size + float factor = 1.0f; + if (args.random_opp) { + factor = 0.5f + real_dist(mt19937); + } + int opp_size = static_cast(args.avg_opp_size * factor); + + // Request data to transmit + uint32_t buf_state = tx_rlc->get_buffer_state(lcid); + if (buf_state > 0) { + pdu->N_bytes = tx_rlc->read_pdu(lcid, pdu->msg, opp_size); + + // Push PDU in the list + pdu_list.push_back(std::move(pdu)); + } + } +} + +void mac_dummy::run_rx_tti(srsue::rlc_interface_mac* tx_rlc, + srsue::rlc_interface_mac* rx_rlc, + bool is_dl, + std::vector& pdu_list) +{ + // Sleep if necessary + if (args.pdu_tx_delay_usec > 0) { + std::this_thread::sleep_for(std::chrono::microseconds(args.pdu_tx_delay_usec)); + } + + auto it = pdu_list.begin(); // PDU iterator + bool skip_action = false; // Avoid discarding a duplicated or duplicating a discarded + + while (it != pdu_list.end()) { + // Get PDU unique buffer + srsran::unique_byte_buffer_t& pdu = *it; + + // Drop + float rnd = real_dist(mt19937); + if (std::isnan(rnd) || (((rnd > args.pdu_drop_rate) || skip_action) && pdu->N_bytes > 0)) { + uint32_t pdu_len = pdu->N_bytes; + + // Cut + if ((real_dist(mt19937) < args.pdu_cut_rate)) { + int cut_pdu_len = static_cast(pdu_len * real_dist(mt19937)); + logger.info("Cutting MAC PDU len (%d B -> %d B)", pdu_len, cut_pdu_len); + pdu_len = cut_pdu_len; + } + + // Write PDU in RX + rx_rlc->write_pdu(lcid, pdu->msg, pdu_len); + + // Write PCAP + write_pdu_to_pcap(pcap_handle, is_dl, 4, pdu->msg, pdu_len); // Only handles NR rat + if (is_dl) { + pcap->write_dl_ccch(pdu->msg, pdu_len); + } else { + pcap->write_ul_ccch(pdu->msg, pdu_len); + } + } else { + logger.info(pdu->msg, pdu->N_bytes, "Dropping RLC PDU (%d B)", pdu->N_bytes); + skip_action = true; // Avoid drop duplicating this PDU + } + + // Duplicate + if (real_dist(mt19937) > args.pdu_duplicate_rate || skip_action) { + it++; + skip_action = false; // Allow action on the next PDU + } else { + logger.info(pdu->msg, pdu->N_bytes, "Duplicating RLC PDU (%d B)", pdu->N_bytes); + skip_action = true; // Avoid drop of this PDU + } + } +} + +/*********************** + * RLC tester class + ***********************/ +// PDCP interface +void rlc_tester::write_pdu(uint32_t rx_lcid, srsran::unique_byte_buffer_t sdu) +{ + assert(rx_lcid == lcid); + if (args.mode != "AM") { + // Only AM will guarantee to deliver SDUs, take first byte as reference for other modes + next_expected_sdu = sdu->msg[0]; + } + + // check SDU content (consider faster alternative) + for (uint32_t i = 0; i < sdu->N_bytes; ++i) { + if (sdu->msg[i] != next_expected_sdu) { + logger.error(sdu->msg, + sdu->N_bytes, + "Received malformed SDU with size %d, expected data 0x%X", + sdu->N_bytes, + next_expected_sdu); + fprintf(stderr, "Received malformed SDU with size %d, expected data 0x%X\n", sdu->N_bytes, next_expected_sdu); + fprintf(stdout, "Received malformed SDU with size %d, expected data 0x%X\n", sdu->N_bytes, next_expected_sdu); + + std::this_thread::sleep_for(std::chrono::seconds(1)); // give some time to flush logs + exit(-1); + } + } + next_expected_sdu += 1; + rx_pdus++; +} + +void rlc_tester::run_thread() +{ + uint32_t pdcp_sn = 0; + uint32_t sdu_size = 0; + uint8_t payload = 0x0; // increment for each SDU + while (run_enable) { + // SDU queue is full, don't assign PDCP SN + if (rlc_pdcp->sdu_queue_is_full(lcid)) { + continue; + } + + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n"); + // backoff for a bit + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + continue; + } + pdu->md.pdcp_sn = pdcp_sn; + + // random or fixed SDU size + if (args.sdu_size < 1) { + sdu_size = int_dist(mt19937); + } else { + sdu_size = args.sdu_size; + } + + for (uint32_t i = 0; i < sdu_size; i++) { + pdu->msg[i] = payload; + } + pdu->N_bytes = sdu_size; + payload++; + + rlc_pdcp->write_sdu(lcid, std::move(pdu)); + pdcp_sn = (pdcp_sn + 1) % max_pdcp_sn; + if (args.sdu_gen_delay_usec > 0) { + std::this_thread::sleep_for(std::chrono::microseconds(args.sdu_gen_delay_usec)); + } + } +} + +void stress_test(stress_test_args_t args) +{ + auto log_sink = + (args.log_filename == "stdout") ? srslog::create_stdout_sink() : srslog::create_file_sink(args.log_filename); + if (!log_sink) { + return; + } + srslog::log_channel* chan = srslog::create_log_channel("main_channel", *log_sink); + if (!chan) { + return; + } + srslog::set_default_sink(*log_sink); + + auto& log1 = srslog::fetch_basic_logger("RLC_1", false); + log1.set_level(static_cast(args.log_level)); + log1.set_hex_dump_max_size(args.log_hex_limit); + auto& log2 = srslog::fetch_basic_logger("RLC_2", false); + log2.set_level(static_cast(args.log_level)); + log2.set_hex_dump_max_size(args.log_hex_limit); + + srsran::rlc_pcap pcap; + uint32_t lcid = 1; + + srsran::rlc_config_t cnfg_ = {}; + if (args.rat == "LTE") { + if (args.mode == "AM") { + // config RLC AM bearer + cnfg_ = srsran::rlc_config_t::default_rlc_am_config(); + cnfg_.am.max_retx_thresh = args.max_retx; + } else if (args.mode == "UM") { + // config UM bearer + cnfg_ = srsran::rlc_config_t::default_rlc_um_config(); + } else if (args.mode == "TM") { + // use default LCID in TM + lcid = 0; + } else { + std::cout << "Unsupported RLC mode " << args.mode << ", exiting." << std::endl; + exit(-1); + } + + if (args.write_pcap) { + pcap.open("rlc_stress_test.pcap", cnfg_); + } + } else if (args.rat == "NR") { + if (args.mode == "UM6") { + cnfg_ = srsran::rlc_config_t::default_rlc_um_nr_config(6); + } else if (args.mode == "UM12") { + cnfg_ = srsran::rlc_config_t::default_rlc_um_nr_config(12); + } else if (args.mode == "AM12") { + cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(12); + cnfg_.am_nr.max_retx_thresh = args.max_retx; + } else if (args.mode == "AM18") { + cnfg_ = srsran::rlc_config_t::default_rlc_am_nr_config(18); + cnfg_.am_nr.max_retx_thresh = args.max_retx; + } else { + std::cout << "Unsupported RLC mode " << args.mode << ", exiting." << std::endl; + exit(-1); + } + + if (args.write_pcap) { + pcap_handle = std::unique_ptr(new srsran::mac_pcap()); + pcap_handle->open("rlc_stress_test_nr.pcap"); + } + } else { + std::cout << "Unsupported RAT mode " << args.rat << ", exiting." << std::endl; + exit(-1); + } + + // generate random seed if needed + uint32_t seed = 0; + if (not args.zero_seed) { + std::random_device rd; + seed = rd(); + } + + if (args.seed != 0) { + seed = args.seed; + } + + srsran::timer_handler timers(8); + + srsran::rlc rlc1(log1.id().c_str()); + srsran::rlc rlc2(log2.id().c_str()); + + rlc_tester tester1(&rlc1, "tester1", args, lcid, seed); + rlc_tester tester2(&rlc2, "tester2", args, lcid, seed); + mac_dummy mac(&rlc1, &rlc2, args, lcid, &timers, &pcap, seed); + + rlc1.init(&tester1, &tester1, &timers, 0); + rlc2.init(&tester2, &tester2, &timers, 0); + + // only add AM and UM bearers + if (args.mode != "TM") { + rlc1.add_bearer(lcid, cnfg_); + rlc2.add_bearer(lcid, cnfg_); + } + + printf("Starting test ... Seed: %u\n", seed); + + tester1.start(7); + if (!args.single_tx) { + tester2.start(7); + } + mac.start(); + + // wait until test is over + std::this_thread::sleep_for(std::chrono::seconds(args.test_duration_sec)); + + srslog::flush(); + fflush(stdout); + printf("Test finished, tearing down ..\n"); + + // Stop RLC instances first to release blocking writers + mac.enqueue_task([&rlc1, &rlc2]() { + rlc1.stop(); + rlc2.stop(); + }); + + printf("RLC entities stopped.\n"); + // Stop upper layer writers + tester1.stop(); + tester2.stop(); + + printf("Writers stopped.\n"); + + mac.stop(); + if (args.write_pcap) { + pcap.close(); + } + + srsran::rlc_metrics_t metrics = {}; + rlc1.get_metrics(metrics, 1); + + printf("RLC1 received %" PRIu64 " SDUs in %ds (%.2f/s), Tx=%" PRIu64 " B, Rx=%" PRIu64 " B\n", + tester1.get_nof_rx_pdus(), + args.test_duration_sec, + static_cast(tester1.get_nof_rx_pdus() / args.test_duration_sec), + metrics.bearer[lcid].num_tx_pdu_bytes, + metrics.bearer[lcid].num_rx_pdu_bytes); + rlc_bearer_metrics_print(metrics.bearer[lcid]); + + rlc2.get_metrics(metrics, 1); + printf("RLC2 received %" PRIu64 " SDUs in %ds (%.2f/s), Tx=%" PRIu64 " B, Rx=%" PRIu64 " B\n", + tester2.get_nof_rx_pdus(), + args.test_duration_sec, + static_cast(tester2.get_nof_rx_pdus() / args.test_duration_sec), + metrics.bearer[lcid].num_tx_pdu_bytes, + metrics.bearer[lcid].num_rx_pdu_bytes); + rlc_bearer_metrics_print(metrics.bearer[lcid]); +} + +int main(int argc, char** argv) +{ + srsran_debug_handle_crash(argc, argv); + + stress_test_args_t args = {}; + parse_args(&args, argc, argv); + + srslog::init(); + + stress_test(args); + + exit(0); +} diff --git a/lib/test/rlc/rlc_stress_test.h b/lib/test/rlc/rlc_stress_test.h new file mode 100644 index 000000000..cad2eb33c --- /dev/null +++ b/lib/test/rlc/rlc_stress_test.h @@ -0,0 +1,297 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/block_queue.h" +#include "srsran/common/crash_handler.h" +#include "srsran/common/rlc_pcap.h" +#include "srsran/common/test_common.h" +#include "srsran/common/threads.h" +#include "srsran/common/tsan_options.h" +#include "srsran/rlc/rlc.h" +#include +#include +#include +#include +#include +#include +#include + +#include "srsran/common/mac_pcap.h" +#include "srsran/mac/mac_sch_pdu_nr.h" + +inline int write_pdu_to_pcap(const std::unique_ptr& pcap_handle, + const bool is_dl, + const uint32_t lcid, + const uint8_t* payload, + const uint32_t len) +{ + const uint32_t PCAP_CRNTI = 0x1001; + const uint32_t PCAP_TTI = 666; + if (pcap_handle) { + srsran::byte_buffer_t tx_buffer; + srsran::mac_sch_pdu_nr tx_pdu; + tx_pdu.init_tx(&tx_buffer, len + 10); + tx_pdu.add_sdu(lcid, payload, len); + tx_pdu.pack(); + if (is_dl) { + pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } else { + pcap_handle->write_ul_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + } + + return SRSRAN_SUCCESS; + } + return SRSRAN_ERROR; +} + +typedef struct { + std::string rat; + std::string mode; + int32_t sdu_size; + uint32_t test_duration_sec; + float pdu_drop_rate; + float pdu_cut_rate; + float pdu_duplicate_rate; + uint32_t sdu_gen_delay_usec; + uint32_t pdu_tx_delay_usec; + uint32_t log_level; + bool single_tx; + bool write_pcap; + uint32_t avg_opp_size; + bool random_opp; + bool zero_seed; + uint32_t seed; + uint32_t nof_pdu_tti; + uint32_t max_retx; + int32_t log_hex_limit; + std::string log_filename; + uint32_t min_sdu_size; + uint32_t max_sdu_size; +} stress_test_args_t; + +void parse_args(stress_test_args_t* args, int argc, char* argv[]) +{ + namespace bpo = boost::program_options; + // Command line only options + bpo::options_description general("General options"); + + general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit"); + + // clang-format off + + // Command line or config file options + bpo::options_description common("Configuration options"); + common.add_options() + ("rat", bpo::value(&args->rat)->default_value("LTE"), "The RLC version to use (LTE/NR)") + ("mode", bpo::value(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM for LTE) (UM6/UM12/AM12/AM18 for NR)") + ("duration", bpo::value(&args->test_duration_sec)->default_value(5), "Duration (sec)") + ("sdu_size", bpo::value(&args->sdu_size)->default_value(-1), "Size of SDUs (-1 means random)") + ("random_opp", bpo::value(&args->random_opp)->default_value(true), "Whether to generate random MAC opportunities") + ("avg_opp_size", bpo::value(&args->avg_opp_size)->default_value(1505), "Size of the MAC opportunity (if not random)") + ("sdu_gen_delay", bpo::value(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)") + ("pdu_tx_delay", bpo::value(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)") + ("pdu_drop_rate", bpo::value(&args->pdu_drop_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped") + ("pdu_cut_rate", bpo::value(&args->pdu_cut_rate)->default_value(0.0), "Rate at which RLC PDUs are chopped in length") + ("pdu_duplicate_rate", bpo::value(&args->pdu_duplicate_rate)->default_value(0.0), "Rate at which RLC PDUs are duplicated") + ("loglevel", bpo::value(&args->log_level)->default_value((int)srslog::basic_levels::debug), "Log level (1=Error,2=Warning,3=Info,4=Debug)") + ("log_filename", bpo::value(&args->log_filename)->default_value("stdout"), "Filename to save log to") + ("singletx", bpo::value(&args->single_tx)->default_value(false), "If set to true, only one node is generating data") + ("pcap", bpo::value(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file") + ("zeroseed", bpo::value(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero") + ("seed", bpo::value(&args->seed)->default_value(0), "Seed to use in run. 0 means the seed is randomly generated") + ("max_retx", bpo::value(&args->max_retx)->default_value(32), "Maximum number of RLC retransmission attempts") + ("nof_pdu_tti", bpo::value(&args->nof_pdu_tti)->default_value(1), "Number of PDUs processed in a TTI") + ("log_hex_limit", bpo::value(&args->log_hex_limit)->default_value(-1), "Maximum bytes in hex log") + ("min_sdu_size", bpo::value(&args->min_sdu_size)->default_value(5), "Minimum SDU size") + ("max_sdu_size", bpo::value(&args->max_sdu_size)->default_value(1500), "Maximum SDU size"); + // clang-format on + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(general); + + // parse the command line and store result in vm + bpo::variables_map vm; + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); + bpo::notify(vm); + + // help option was given - print usage and exit + if (vm.count("help") > 0) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << common << std::endl << general << std::endl; + exit(0); + } + + if (args->log_level > 4) { + args->log_level = 4; + printf("Set log level to %d (%s)\n", + args->log_level, + srslog::basic_level_to_string(static_cast(args->log_level))); + } + + // convert mode to upper case + for (auto& c : args->mode) { + c = toupper(c); + } +} + +class mac_dummy : public srsran::thread +{ +public: + mac_dummy(srsue::rlc_interface_mac* rlc1_, + srsue::rlc_interface_mac* rlc2_, + stress_test_args_t args_, + uint32_t lcid_, + srsran::timer_handler* timers_, + srsran::rlc_pcap* pcap_, + uint32_t seed_) : + run_enable(true), + rlc1(rlc1_), + rlc2(rlc2_), + args(args_), + pcap(pcap_), + lcid(lcid_), + timers(timers_), + logger(srslog::fetch_basic_logger("MAC", false)), + thread("MAC_DUMMY"), + real_dist(0.0, 1.0), + mt19937(seed_) + { + logger.set_level(static_cast(args.log_level)); + logger.set_hex_dump_max_size(args.log_hex_limit); + } + + void stop() + { + run_enable = false; + wait_thread_finish(); + } + + void enqueue_task(srsran::move_task_t task) { pending_tasks.push(std::move(task)); } + +private: + void run_thread() final; + + void run_tti(srsue::rlc_interface_mac* tx_rlc, srsue::rlc_interface_mac* rx_rlc, bool is_dl); + + void run_tx_tti(srsue::rlc_interface_mac* tx_rlc, + srsue::rlc_interface_mac* rx_rlc, + std::vector& pdu_list); + + void run_rx_tti(srsue::rlc_interface_mac* tx_rlc, + srsue::rlc_interface_mac* rx_rlc, + bool is_dl, + std::vector& pdu_list); + srsue::rlc_interface_mac* rlc1 = nullptr; + srsue::rlc_interface_mac* rlc2 = nullptr; + + std::atomic run_enable = {false}; + stress_test_args_t args = {}; + srsran::rlc_pcap* pcap = nullptr; + uint32_t lcid = 0; + srslog::basic_logger& logger; + srsran::timer_handler* timers = nullptr; + + srsran::block_queue pending_tasks; + + std::mt19937 mt19937; + std::uniform_real_distribution real_dist; +}; + +class rlc_tester : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc, public srsran::thread +{ +public: + rlc_tester(srsue::rlc_interface_pdcp* rlc_pdcp_, + std::string name_, + stress_test_args_t args_, + uint32_t lcid_, + uint32_t seed_) : + logger(srslog::fetch_basic_logger(name_.c_str(), false)), + rlc_pdcp(rlc_pdcp_), + name(name_), + args(args_), + lcid(lcid_), + thread("RLC_TESTER"), + int_dist(args_.min_sdu_size, args_.max_sdu_size), + mt19937(seed_) + { + logger.set_level(static_cast(args.log_level)); + logger.set_hex_dump_max_size(args_.log_hex_limit); + } + + void stop() + { + run_enable = false; + wait_thread_finish(); + } + + // PDCP interface + void write_pdu(uint32_t rx_lcid, srsran::unique_byte_buffer_t sdu) final; + void write_pdu_bcch_bch(srsran::unique_byte_buffer_t sdu) final {} + void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t sdu) final {} + void write_pdu_pcch(srsran::unique_byte_buffer_t sdu) final {} + void write_pdu_mch(uint32_t lcid_, srsran::unique_byte_buffer_t sdu) final {} + void notify_delivery(uint32_t lcid_, const srsran::pdcp_sn_vector_t& pdcp_sns) final {} + void notify_failure(uint32_t lcid_, const srsran::pdcp_sn_vector_t& pdcp_sns) final {} + + // RRC interface + void max_retx_attempted() final + { + fprintf( + stderr, + "Maximum number of RLC retransmission reached. Consider increasing threshold or lowering channel drop rate."); + logger.error( + "Maximum number of RLC retransmission reached. Consider increasing threshold or lowering channel drop rate."); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); + } + void protocol_failure() final + { + logger.error("RLC protocol error detected."); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); + } + const char* get_rb_name(uint32_t lcid) final { return "DRB1"; } + + uint64_t get_nof_rx_pdus() const { return rx_pdus; } + +private: + const static size_t max_pdcp_sn = 262143U; // 18bit SN + void run_thread() final; + + std::atomic run_enable = {true}; + + /// Tx uses thread-local PDCP SN to set SDU content, the Rx uses this variable to check received SDUs + uint8_t next_expected_sdu = 0; + uint64_t rx_pdus = 0; + uint32_t lcid = 0; + srslog::basic_logger& logger; + + std::string name; + + stress_test_args_t args = {}; + + srsue::rlc_interface_pdcp* rlc_pdcp = nullptr; // used by run_thread to push PDCP SDUs to RLC + + std::mt19937 mt19937; + std::uniform_int_distribution<> int_dist; +}; + diff --git a/lib/test/upper/rlc_test_common.h b/lib/test/rlc/rlc_test_common.h similarity index 51% rename from lib/test/upper/rlc_test_common.h rename to lib/test/rlc/rlc_test_common.h index 7eb3f1d86..90086b752 100644 --- a/lib/test/upper/rlc_test_common.h +++ b/lib/test/rlc/rlc_test_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,8 +23,10 @@ #define SRSRAN_RLC_TEST_COMMON_H #include "srsran/common/byte_buffer.h" +#include "srsran/common/rlc_pcap.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/rlc/rlc_metrics.h" #include namespace srsran { @@ -32,10 +34,10 @@ namespace srsran { class rlc_um_tester : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc { public: - rlc_um_tester() {} + rlc_um_tester() = default; // PDCP interface - void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu) + void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu) final { // check length if (lcid != 3 && sdu->N_bytes != expected_sdu_len) { @@ -56,15 +58,16 @@ public: // srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); sdus.push_back(std::move(sdu)); } - void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} - void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {} - void write_pdu_pcch(unique_byte_buffer_t sdu) {} - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { sdus.push_back(std::move(sdu)); } - void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) {} - void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) {} + void write_pdu_bcch_bch(unique_byte_buffer_t sdu) final {} + void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) final {} + void write_pdu_pcch(unique_byte_buffer_t sdu) final {} + void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final { sdus.push_back(std::move(sdu)); } + void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) final {} + void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) final {} // RRC interface - void max_retx_attempted() {} + void max_retx_attempted() final {} + void protocol_failure() final {} const char* get_rb_name(uint32_t lcid) { return ""; } void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; } @@ -75,6 +78,65 @@ public: uint32_t expected_sdu_len = 0; }; +class rlc_am_tester : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc +{ +public: + explicit rlc_am_tester(bool save_sdus_, rlc_pcap* pcap_) : save_sdus(save_sdus_), pcap(pcap_) {} + + // PDCP interface + void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu) + { + assert(lcid == 1); + if (save_sdus) { + sdus.push_back(std::move(sdu)); + } + } + void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} + void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {} + void write_pdu_pcch(unique_byte_buffer_t sdu) {} + void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} + void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec) + { + assert(lcid == 1); + for (uint32_t pdcp_sn : pdcp_sn_vec) { + if (notified_counts.find(pdcp_sn) == notified_counts.end()) { + notified_counts[pdcp_sn] = 0; + } + notified_counts[pdcp_sn] += 1; + } + } + void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec) + { + assert(lcid == 1); + // TODO + } + + // RRC interface + void max_retx_attempted() { max_retx_triggered = true; } + void protocol_failure() { protocol_failure_triggered = true; } + + const char* get_rb_name(uint32_t lcid) { return ""; } + + std::vector sdus; + rlc_pcap* pcap = nullptr; + bool max_retx_triggered = false; + bool protocol_failure_triggered = false; + bool save_sdus = true; + + std::map notified_counts; // Map of PDCP SNs to number of notifications +}; + +bool rx_is_tx(const rlc_bearer_metrics_t& rlc1_metrics, const rlc_bearer_metrics_t& rlc2_metrics) +{ + if (rlc1_metrics.num_tx_pdu_bytes != rlc2_metrics.num_rx_pdu_bytes) { + return false; + } + + if (rlc2_metrics.num_tx_pdu_bytes != rlc1_metrics.num_rx_pdu_bytes) { + return false; + } + return true; +} } // namespace srsran #endif // SRSRAN_RLC_TEST_COMMON_H diff --git a/lib/test/upper/rlc_um_data_test.cc b/lib/test/rlc/rlc_um_data_test.cc similarity index 95% rename from lib/test/upper/rlc_um_data_test.cc rename to lib/test/rlc/rlc_um_data_test.cc index 6cd105be2..a64773372 100644 --- a/lib/test/upper/rlc_um_data_test.cc +++ b/lib/test/rlc/rlc_um_data_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,7 +20,7 @@ */ #include "srsran/common/test_common.h" -#include "srsran/upper/rlc_um_lte.h" +#include "srsran/rlc/rlc_um_lte.h" #include // Fixed header only diff --git a/lib/test/upper/rlc_um_nr_pdu_test.cc b/lib/test/rlc/rlc_um_nr_pdu_test.cc similarity index 99% rename from lib/test/upper/rlc_um_nr_pdu_test.cc rename to lib/test/rlc/rlc_um_nr_pdu_test.cc index 90bed0137..98e34477d 100644 --- a/lib/test/upper/rlc_um_nr_pdu_test.cc +++ b/lib/test/rlc/rlc_um_nr_pdu_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,8 +20,8 @@ */ #include "srsran/config.h" -#include "srsran/upper/rlc.h" -#include "srsran/upper/rlc_um_nr.h" +#include "srsran/rlc/rlc.h" +#include "srsran/rlc/rlc_um_nr.h" #include #include diff --git a/lib/test/upper/rlc_um_nr_test.cc b/lib/test/rlc/rlc_um_nr_test.cc similarity index 85% rename from lib/test/upper/rlc_um_nr_test.cc rename to lib/test/rlc/rlc_um_nr_test.cc index 6bebc89a8..adab72697 100644 --- a/lib/test/upper/rlc_um_nr_test.cc +++ b/lib/test/rlc/rlc_um_nr_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,23 +20,16 @@ */ #include "rlc_test_common.h" +#include "srsran/common/test_common.h" #include "srsran/config.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" -#include "srsran/upper/rlc.h" -#include "srsran/upper/rlc_um_nr.h" +#include "srsran/rlc/rlc.h" +#include "srsran/rlc/rlc_um_nr.h" #include #include #include -#define TESTASSERT(cond) \ - { \ - if (!(cond)) { \ - std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ - return -1; \ - } \ - } - #define PCAP 0 #define PCAP_CRNTI (0x1001) #define PCAP_TTI (666) @@ -44,9 +37,9 @@ using namespace srsran; #if PCAP -#include "srsran/common/mac_nr_pcap.h" -#include "srsran/mac/mac_nr_pdu.h" -static std::unique_ptr pcap_handle = nullptr; +#include "srsran/common/mac_pcap.h" +#include "srsran/mac/mac_sch_pdu_nr.h" +static std::unique_ptr pcap_handle = nullptr; #endif int write_pdu_to_pcap(const uint32_t lcid, const uint8_t* payload, const uint32_t len) @@ -54,11 +47,11 @@ int write_pdu_to_pcap(const uint32_t lcid, const uint8_t* payload, const uint32_ #if PCAP if (pcap_handle) { byte_buffer_t tx_buffer; - srsran::mac_nr_sch_pdu tx_pdu; + srsran::mac_sch_pdu_nr tx_pdu; tx_pdu.init_tx(&tx_buffer, len + 10); tx_pdu.add_sdu(lcid, payload, len); tx_pdu.pack(); - pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); + pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); return SRSRAN_SUCCESS; } #endif @@ -93,7 +86,7 @@ public: logger2.set_hex_dump_max_size(-1); // configure RLC entities - rlc_config_t cnfg = rlc_config_t::default_rlc_um_nr_config(6); + rlc_config_t cnfg = rlc_config_t::default_rlc_um_nr_config(12); if (rlc1.configure(cnfg) != true) { fprintf(stderr, "Couldn't configure RLC1 object\n"); } @@ -488,6 +481,11 @@ int rlc_um_nr_test7() TESTASSERT(ctxt.tester.sdus.at(i)->N_bytes == sdu_size); } + // let t-reassembly expire + while (ctxt.timers.nof_running_timers() != 0) { + ctxt.timers.step_all(); + } + rlc_bearer_metrics_t rlc2_metrics = ctxt.rlc2.get_metrics(); TESTASSERT(rlc2_metrics.num_lost_pdus == 1); @@ -563,10 +561,75 @@ int rlc_um_nr_test8() return SRSRAN_SUCCESS; } +// Similar to rlc_um_nr_test8() but out-of-order PDUs have SNs (from multiple SDUs) +int rlc_um_nr_test9() +{ + rlc_um_nr_test_context1 ctxt; + + const uint32_t num_sdus = 2; + const uint32_t sdu_size = 20; + + ctxt.tester.set_expected_sdu_len(sdu_size); + + // Push SDUs into RLC1 + byte_buffer_pool* pool = byte_buffer_pool::get_instance(); + unique_byte_buffer_t sdu_bufs[num_sdus]; + for (uint32_t i = 0; i < num_sdus; i++) { + sdu_bufs[i] = srsran::make_byte_buffer(); + // Write the index into the buffer + for (uint32_t k = 0; k < sdu_size; ++k) { + sdu_bufs[i]->msg[k] = i; + } + sdu_bufs[i]->N_bytes = sdu_size; + ctxt.rlc1.write_sdu(std::move(sdu_bufs[i])); + } + + // Read PDUs from RLC1 with grant smaller than SDU size + const uint32_t max_num_pdus = 10; + uint32_t num_pdus = 0; + unique_byte_buffer_t pdu_bufs[max_num_pdus]; + + while (ctxt.rlc1.get_buffer_state() != 0 && num_pdus < max_num_pdus) { + pdu_bufs[num_pdus] = srsran::make_byte_buffer(); + int len = ctxt.rlc1.read_pdu(pdu_bufs[num_pdus]->msg, 10); // 3 bytes for header + payload + pdu_bufs[num_pdus]->N_bytes = len; + + // write PCAP + write_pdu_to_pcap(4, pdu_bufs[num_pdus]->msg, pdu_bufs[num_pdus]->N_bytes); + + num_pdus++; + } + + TESTASSERT(num_pdus == 6); + + // Write all PDUs such that the middle section of SN=0 is received after the start section of SN=1 + ctxt.rlc2.write_pdu(pdu_bufs[0]->msg, pdu_bufs[0]->N_bytes); + // skip 2nd PDU which is the middle section of SN=0 + ctxt.rlc2.write_pdu(pdu_bufs[2]->msg, pdu_bufs[2]->N_bytes); + ctxt.rlc2.write_pdu(pdu_bufs[3]->msg, pdu_bufs[3]->N_bytes); + + // now feed the middle part of SN=0 + ctxt.rlc2.write_pdu(pdu_bufs[1]->msg, pdu_bufs[1]->N_bytes); + + // and the rest of SN=1 + ctxt.rlc2.write_pdu(pdu_bufs[4]->msg, pdu_bufs[4]->N_bytes); + ctxt.rlc2.write_pdu(pdu_bufs[5]->msg, pdu_bufs[5]->N_bytes); + + TESTASSERT(num_sdus == ctxt.tester.get_num_sdus()); + for (uint32_t i = 0; i < ctxt.tester.sdus.size(); i++) { + TESTASSERT(ctxt.tester.sdus.at(i)->N_bytes == sdu_size); + TESTASSERT(*(ctxt.tester.sdus[i]->msg) == i); + } + + TESTASSERT(ctxt.timers.nof_running_timers() == 0); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { #if PCAP - pcap_handle = std::unique_ptr(new srsran::mac_nr_pcap()); + pcap_handle = std::unique_ptr(new srsran::mac_pcap()); pcap_handle->open("rlc_um_nr_test.pcap"); #endif @@ -615,5 +678,14 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } + if (rlc_um_nr_test9()) { + fprintf(stderr, "rlc_um_nr_test9() failed.\n"); + return SRSRAN_ERROR; + } + +#if PCAP + pcap_handle->close(); +#endif + return SRSRAN_SUCCESS; } diff --git a/lib/test/upper/rlc_um_test.cc b/lib/test/rlc/rlc_um_test.cc similarity index 99% rename from lib/test/upper/rlc_um_test.cc rename to lib/test/rlc/rlc_um_test.cc index 778f0d999..2116927b2 100644 --- a/lib/test/upper/rlc_um_test.cc +++ b/lib/test/rlc/rlc_um_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,7 +20,7 @@ */ #include "rlc_test_common.h" -#include "srsran/upper/rlc_um_lte.h" +#include "srsran/rlc/rlc_um_lte.h" #include #define TESTASSERT(cond) \ diff --git a/lib/test/srslog/CMakeLists.txt b/lib/test/srslog/CMakeLists.txt index 1034df5cf..e08a878ec 100644 --- a/lib/test/srslog/CMakeLists.txt +++ b/lib/test/srslog/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,6 +18,9 @@ # and at http://www.gnu.org/licenses/. # +add_executable(srslog_frontend_latency benchmarks/frontend_latency.cpp) +target_link_libraries(srslog_frontend_latency srslog) + add_executable(srslog_test srslog_test.cpp) target_link_libraries(srslog_test srslog) add_test(srslog_test srslog_test) @@ -44,6 +47,10 @@ target_include_directories(file_sink_test PUBLIC ../../) target_link_libraries(file_sink_test srslog) add_test(file_sink_test file_sink_test) +add_executable(syslog_sink_test syslog_sink_test.cpp) +target_include_directories(syslog_sink_test PUBLIC ../../) +target_link_libraries(syslog_sink_test srslog) + add_executable(file_utils_test file_utils_test.cpp) target_include_directories(file_utils_test PUBLIC ../../) target_link_libraries(file_utils_test srslog) diff --git a/lib/test/srslog/any_test.cpp b/lib/test/srslog/any_test.cpp index ad4a1ac59..e30a00bfa 100644 --- a/lib/test/srslog/any_test.cpp +++ b/lib/test/srslog/any_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/srslog/benchmarks/frontend_latency.cpp b/lib/test/srslog/benchmarks/frontend_latency.cpp new file mode 100644 index 000000000..98cde4924 --- /dev/null +++ b/lib/test/srslog/benchmarks/frontend_latency.cpp @@ -0,0 +1,146 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/srslog/srslog.h" +#include +#include +#include + +using namespace srslog; + +static constexpr unsigned num_iterations = 4000; +static constexpr unsigned num_entries_per_iter = 40; + +namespace { + +/// This helper class checks if there has been context switches between its construction and destruction for the caller +/// thread. +class context_switch_checker +{ +public: + explicit context_switch_checker(std::atomic& counter) : counter(counter) + { + ::getrusage(RUSAGE_THREAD, &before); + } + + ~context_switch_checker() + { + ::rusage after{}; + ::getrusage(RUSAGE_THREAD, &after); + unsigned diff = (after.ru_nvcsw - before.ru_nvcsw) + (after.ru_nivcsw - before.ru_nivcsw); + if (diff) { + counter.fetch_add(diff, std::memory_order_relaxed); + } + } + +private: + ::rusage before{}; + std::atomic& counter; +}; + +} // namespace + +/// Busy waits in the calling thread for the specified amount of time. +static void busy_wait(std::chrono::milliseconds interval) +{ + auto begin = std::chrono::steady_clock::now(); + auto end = begin + interval; + + while (std::chrono::steady_clock::now() < end) { + } +} + +/// Worker function used for each thread of the benchmark to generate and measure the time taken for each log entry. +static void run_thread(log_channel& c, std::vector& results, std::atomic& ctx_counter) +{ + for (unsigned iter = 0; iter != num_iterations; ++iter) { + context_switch_checker ctx_checker(ctx_counter); + + auto begin = std::chrono::steady_clock::now(); + for (unsigned entry_num = 0; entry_num != num_entries_per_iter; ++entry_num) { + double d = entry_num; + c("SRSLOG latency benchmark: int: %u, double: %f, string: %s", iter, d, "test"); + } + auto end = std::chrono::steady_clock::now(); + + results.push_back(std::chrono::duration_cast(end - begin).count() / num_entries_per_iter); + + busy_wait(std::chrono::milliseconds(4)); + } +} + +/// This function runs the latency benchmark generating log entries using the specified number of threads. +static void benchmark(unsigned num_threads) +{ + std::vector > thread_results; + thread_results.resize(num_threads); + for (auto& v : thread_results) { + v.reserve(num_iterations); + } + + auto& s = srslog::fetch_file_sink("srslog_latency_benchmark.txt"); + auto& channel = srslog::fetch_log_channel("bench", s, {}); + + srslog::init(); + + std::vector workers; + workers.reserve(num_threads); + + std::atomic ctx_counter(0); + for (unsigned i = 0; i != num_threads; ++i) { + workers.emplace_back(run_thread, std::ref(channel), std::ref(thread_results[i]), std::ref(ctx_counter)); + } + for (auto& w : workers) { + w.join(); + } + + std::vector results; + results.reserve(num_threads * num_iterations); + for (const auto& v : thread_results) { + results.insert(results.end(), v.begin(), v.end()); + } + std::sort(results.begin(), results.end()); + + fmt::print("SRSLOG Frontend Latency Benchmark - logging with {} thread{}\n" + "All values in nanoseconds\n" + "Percentiles: | 50th | 75th | 90th | 99th | 99.9th | Worst |\n" + " |{:6}|{:6}|{:6}|{:6}|{:8}|{:7}|\n" + "Context switches: {} in {} of generated entries\n\n", + num_threads, + (num_threads > 1) ? "s" : "", + results[static_cast(results.size() * 0.5)], + results[static_cast(results.size() * 0.75)], + results[static_cast(results.size() * 0.9)], + results[static_cast(results.size() * 0.99)], + results[static_cast(results.size() * 0.999)], + results.back(), + ctx_counter, + num_threads * num_iterations * num_entries_per_iter); +} + +int main() +{ + for (auto n : {1, 2, 4}) { + benchmark(n); + } + + return 0; +} diff --git a/lib/test/srslog/context_test.cpp b/lib/test/srslog/context_test.cpp index 5d31fecc8..22cb9a294 100644 --- a/lib/test/srslog/context_test.cpp +++ b/lib/test/srslog/context_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/srslog/event_trace_test.cpp b/lib/test/srslog/event_trace_test.cpp index 744c4d78c..f3b0dae3e 100644 --- a/lib/test/srslog/event_trace_test.cpp +++ b/lib/test/srslog/event_trace_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,7 +34,7 @@ namespace { class backend_spy : public detail::log_backend { public: - void start() override {} + void start(srslog::backend_priority priority) override {} bool push(detail::log_entry&& entry) override { diff --git a/lib/test/srslog/file_sink_test.cpp b/lib/test/srslog/file_sink_test.cpp index 88a582af0..52e01df4a 100644 --- a/lib/test/srslog/file_sink_test.cpp +++ b/lib/test/srslog/file_sink_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,7 +31,7 @@ static constexpr char log_filename[] = "file_sink_test.log"; static bool when_data_is_written_to_file_then_contents_are_valid() { file_test_utils::scoped_file_deleter deleter(log_filename); - file_sink file(log_filename, 0, std::unique_ptr(new test_dummies::log_formatter_dummy)); + file_sink file(log_filename, 0, false, std::unique_ptr(new test_dummies::log_formatter_dummy)); std::vector entries; for (unsigned i = 0; i != 10; ++i) { @@ -54,7 +54,7 @@ class file_sink_subclass : public file_sink { public: file_sink_subclass(std::string name, size_t max_size) : - file_sink(std::move(name), max_size, std::unique_ptr(new test_dummies::log_formatter_dummy)) + file_sink(std::move(name), max_size, false, std::unique_ptr(new test_dummies::log_formatter_dummy)) {} uint32_t get_num_of_files() const { return get_file_index(); } diff --git a/lib/test/srslog/file_test_utils.h b/lib/test/srslog/file_test_utils.h index 57405a56b..412db1efb 100644 --- a/lib/test/srslog/file_test_utils.h +++ b/lib/test/srslog/file_test_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/srslog/file_utils_test.cpp b/lib/test/srslog/file_utils_test.cpp index a444e6d3a..5a4efa825 100644 --- a/lib/test/srslog/file_utils_test.cpp +++ b/lib/test/srslog/file_utils_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/srslog/json_formatter_test.cpp b/lib/test/srslog/json_formatter_test.cpp index 7e3d5a340..c2a8a731b 100644 --- a/lib/test/srslog/json_formatter_test.cpp +++ b/lib/test/srslog/json_formatter_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,7 @@ static detail::log_entry_metadata build_log_entry_metadata(fmt::dynamic_format_a store->push_back(88); } - return {tp, {10, true}, "Text %d", store, "ABC", 'Z', small_str_buffer()}; + return {tp, {10, true}, "Text %d", store, "ABC", 'Z'}; } static bool when_fully_filled_log_entry_then_everything_is_formatted() diff --git a/lib/test/srslog/log_backend_test.cpp b/lib/test/srslog/log_backend_test.cpp index 7dfbf4caa..d6b4a19ce 100644 --- a/lib/test/srslog/log_backend_test.cpp +++ b/lib/test/srslog/log_backend_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -89,7 +89,7 @@ static detail::log_entry build_log_entry(sink* s, fmt::dynamic_format_arg_store< return {s, [](detail::log_entry_metadata&& metadata, fmt::memory_buffer& buffer) {}, - {tp, {0, false}, "Text %d", store, "", '\0', small_str_buffer()}}; + {tp, {0, false}, "Text %d", store, "", '\0'}}; } static bool when_backend_is_not_started_then_pushed_log_entries_are_ignored() diff --git a/lib/test/srslog/log_channel_test.cpp b/lib/test/srslog/log_channel_test.cpp index 8ef9a4172..81315bb3e 100644 --- a/lib/test/srslog/log_channel_test.cpp +++ b/lib/test/srslog/log_channel_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -69,7 +69,7 @@ namespace { class backend_spy : public detail::log_backend { public: - void start() override {} + void start(srslog::backend_priority priority) override {} bool push(detail::log_entry&& entry) override { @@ -279,30 +279,6 @@ static bool when_logging_with_context_and_message_then_filled_in_log_entry_is_pu return true; } -static bool when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend() -{ - backend_spy backend; - test_dummies::sink_dummy s; - - log_channel log("id", s, backend); - - small_str_buffer buf; - fmt::format_to(buf, "A {} {} {}", 1, 2, 3); - buf.push_back('\0'); - log(std::move(buf)); - - ASSERT_EQ(backend.push_invocation_count(), 1); - - const detail::log_entry& entry = backend.last_entry(); - ASSERT_EQ(&s, entry.s); - ASSERT_NE(entry.format_func, nullptr); - ASSERT_NE(entry.metadata.tp.time_since_epoch().count(), 0); - ASSERT_EQ(entry.metadata.hex_dump.empty(), true); - ASSERT_EQ(std::string(entry.metadata.small_str.data()), "A 1 2 3"); - - return true; -} - int main() { TEST_FUNCTION(when_log_channel_is_created_then_id_matches_expected_value); @@ -315,7 +291,6 @@ int main() TEST_FUNCTION(when_hex_array_length_is_less_than_hex_log_max_size_then_array_length_is_used); TEST_FUNCTION(when_logging_with_context_then_filled_in_log_entry_is_pushed_into_the_backend); TEST_FUNCTION(when_logging_with_context_and_message_then_filled_in_log_entry_is_pushed_into_the_backend); - TEST_FUNCTION(when_logging_with_small_string_then_filled_in_log_entry_is_pushed_into_the_backend); return 0; } diff --git a/lib/test/srslog/logger_test.cpp b/lib/test/srslog/logger_test.cpp index ffd62e400..34791a52d 100644 --- a/lib/test/srslog/logger_test.cpp +++ b/lib/test/srslog/logger_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/srslog/srslog_test.cpp b/lib/test/srslog/srslog_test.cpp index 484d5b51b..082a3be86 100644 --- a/lib/test/srslog/srslog_test.cpp +++ b/lib/test/srslog/srslog_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/srslog/syslog_sink_test.cpp b/lib/test/srslog/syslog_sink_test.cpp new file mode 100644 index 000000000..692a94404 --- /dev/null +++ b/lib/test/srslog/syslog_sink_test.cpp @@ -0,0 +1,72 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "src/srslog/sinks/syslog_sink.h" +#include "srsran/srslog/srslog.h" +#include "test_dummies.h" +#include "testing_helpers.h" + +#include +#include + +using namespace srslog; + +/// Syslog sink name. +static constexpr char sink_name[] = "srslog_syslog_sink"; + +static bool find_string_infile(std::string filename, std::string pattern) +{ + std::ifstream file(filename); + std::string line; + bool found = false; + + if (file.is_open()) { + while (std::getline(file, line)) { + if (line.find(pattern) != std::string::npos) { // WILL SEARCH 2015-1113 in file + found = true; + } + } + } else { + printf("WARNING: Could not open file %s", filename.c_str()); + } + return found; +} + +static bool syslog_basic_test() +{ + syslog_sink syslog_sink(get_default_log_formatter()); + + // Build a 1000 byte entry. + std::string entry(1000, 'a'); + + syslog_sink.write(detail::memory_buffer(entry)); + + syslog_sink.flush(); + + ASSERT_EQ(find_string_infile("/var/log/syslog", entry), true); + return true; +} + +int main() +{ + TEST_FUNCTION(syslog_basic_test); + return 0; +} \ No newline at end of file diff --git a/lib/test/srslog/test_dummies.h b/lib/test/srslog/test_dummies.h index 5ce58f122..dcbddb011 100644 --- a/lib/test/srslog/test_dummies.h +++ b/lib/test/srslog/test_dummies.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,6 +23,7 @@ #define TEST_DUMMIES #include "srsran/srslog/detail/log_backend.h" +#include "srsran/srslog/shared_types.h" #include "srsran/srslog/sink.h" namespace test_dummies { @@ -76,7 +77,7 @@ public: class backend_dummy : public srslog::detail::log_backend { public: - void start() override {} + void start(srslog::backend_priority priority) override {} bool push(srslog::detail::log_entry&& entry) override { return true; } diff --git a/lib/test/srslog/testing_helpers.h b/lib/test/srslog/testing_helpers.h index 32deb6801..ddb917a01 100644 --- a/lib/test/srslog/testing_helpers.h +++ b/lib/test/srslog/testing_helpers.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/lib/test/srslog/text_formatter_test.cpp b/lib/test/srslog/text_formatter_test.cpp index efb0f1044..08ab0b4ed 100644 --- a/lib/test/srslog/text_formatter_test.cpp +++ b/lib/test/srslog/text_formatter_test.cpp @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,7 @@ static detail::log_entry_metadata build_log_entry_metadata(fmt::dynamic_format_a store->push_back(88); } - return {tp, {10, true}, "Text %d", store, "ABC", 'Z', small_str_buffer()}; + return {tp, {10, true}, "Text %d", store, "ABC", 'Z'}; } static bool when_fully_filled_log_entry_then_everything_is_formatted() @@ -46,7 +46,7 @@ static bool when_fully_filled_log_entry_then_everything_is_formatted() fmt::dynamic_format_arg_store store; text_formatter{}.format(build_log_entry_metadata(&store), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n"; + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n"; ASSERT_EQ(result, expected); @@ -62,7 +62,7 @@ static bool when_log_entry_without_name_is_passed_then_name_is_not_formatted() fmt::memory_buffer buffer; text_formatter{}.format(std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [Z] [ 10] Text 88\n"; + std::string expected = "1970-01-01T00:00:00.050000 [Z] [ 10] Text 88\n"; ASSERT_EQ(result, expected); @@ -78,7 +78,7 @@ static bool when_log_entry_without_tag_is_passed_then_tag_is_not_formatted() fmt::memory_buffer buffer; text_formatter{}.format(std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [ 10] Text 88\n"; + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [ 10] Text 88\n"; ASSERT_EQ(result, expected); @@ -94,7 +94,7 @@ static bool when_log_entry_without_context_is_passed_then_context_is_not_formatt fmt::memory_buffer buffer; text_formatter{}.format(std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] Text 88\n"; + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] Text 88\n"; ASSERT_EQ(result, expected); @@ -111,7 +111,7 @@ static bool when_log_entry_with_hex_dump_is_passed_then_hex_dump_is_formatted() fmt::memory_buffer buffer; text_formatter{}.format(std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n" + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] [ 10] Text 88\n" " 0000: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f\n" " 0010: 10 11 12 13\n"; @@ -185,7 +185,7 @@ static bool when_log_entry_with_only_context_is_passed_then_context_is_formatted fmt::memory_buffer buffer; text_formatter{}.format_ctx(ctx, std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] Context dump for " + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] [ 10] Context dump for " "\"Complex Context\"\n" " > List: sector_list\n" " > Set: sector_metrics\n" @@ -227,7 +227,7 @@ static bool when_log_entry_with_context_and_message_is_passed_then_context_is_fo fmt::memory_buffer buffer; text_formatter{}.format_ctx(ctx, std::move(entry), buffer); std::string result = fmt::to_string(buffer); - std::string expected = "00:00:00.050000 [ABC ] [Z] [ 10] [[sector_metrics_type: event, " + std::string expected = "1970-01-01T00:00:00.050000 [ABC ] [Z] [ 10] [[sector_metrics_type: event, " "sector_metrics_sector_id: 1, [ue_container_Throughput: 1.2 MB/s, " "ue_container_Address: 10.20.30.40, [RF_SNR: 5.1 dB, RF_PWR: -11 " "dBm][RF_SNR: 10.1 dB, RF_PWR: -20 dBm]][ue_container_Throughput: 10.2 " diff --git a/lib/test/upper/pdcp_nr_test_tx.cc b/lib/test/upper/pdcp_nr_test_tx.cc deleted file mode 100644 index 322414543..000000000 --- a/lib/test/upper/pdcp_nr_test_tx.cc +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ -#include "pdcp_nr_test.h" -#include - -/* - * Genric function to test transmission of in-sequence packets - */ -int test_tx(uint32_t n_packets, - const pdcp_initial_state& init_state, - uint8_t pdcp_sn_len, - uint64_t n_pdus_exp, - srsran::unique_byte_buffer_t pdu_exp, - srslog::basic_logger& logger) -{ - srsran::pdcp_config_t cfg = {1, - srsran::PDCP_RB_IS_DRB, - srsran::SECURITY_DIRECTION_UPLINK, - srsran::SECURITY_DIRECTION_DOWNLINK, - pdcp_sn_len, - srsran::pdcp_t_reordering_t::ms500, - srsran::pdcp_discard_timer_t::infinity, - false, - srsran::srsran_rat_t::nr}; - - pdcp_nr_test_helper pdcp_hlp(cfg, sec_cfg, logger); - srsran::pdcp_entity_nr* pdcp = &pdcp_hlp.pdcp; - rlc_dummy* rlc = &pdcp_hlp.rlc; - - pdcp_hlp.set_pdcp_initial_state(init_state); - - // Run test - for (uint32_t i = 0; i < n_packets; ++i) { - // Test SDU - srsran::unique_byte_buffer_t sdu = srsran::make_byte_buffer(); - sdu->append_bytes(sdu1, sizeof(sdu1)); - pdcp->write_sdu(std::move(sdu)); - } - - srsran::unique_byte_buffer_t pdu_act = srsran::make_byte_buffer(); - rlc->get_last_sdu(pdu_act); - - TESTASSERT(rlc->rx_count == n_pdus_exp); - TESTASSERT(compare_two_packets(pdu_act, pdu_exp) == 0); - return 0; -} - -/* - * TX Test: PDCP Entity with SN LEN = 12 and 18. - * PDCP entity configured with EIA2 and EEA2 - */ -int test_tx_all(srslog::basic_logger& logger) -{ - uint64_t n_packets; - /* - * TX Test 1: PDCP Entity with SN LEN = 12 - * TX_NEXT = 0. - * Input: {0x18, 0xE2} - * Output: PDCP Header {0x80, 0x00}, Ciphered Text {0x8f, 0xe3}, MAC-I {0xe0, 0xdf, 0x82, 0x92} - */ - n_packets = 1; - srsran::unique_byte_buffer_t pdu_exp_count0_len12 = srsran::make_byte_buffer(); - pdu_exp_count0_len12->append_bytes(pdu1_count0_snlen12, sizeof(pdu1_count0_snlen12)); - TESTASSERT( - test_tx( - n_packets, normal_init_state, srsran::PDCP_SN_LEN_12, n_packets, std::move(pdu_exp_count0_len12), logger) == - 0); - - /* - * TX Test 2: PDCP Entity with SN LEN = 12 - * TX_NEXT = 2048. - * Input: {0x18, 0xE2} - * Output: PDCP Header {0x88, 0x00}, Ciphered Text {0x8d, 0x2c}, MAC-I {0x47, 0x5e, 0xb1, 0x5b} - */ - n_packets = 2049; - srsran::unique_byte_buffer_t pdu_exp_count2048_len12 = srsran::make_byte_buffer(); - pdu_exp_count2048_len12->append_bytes(pdu1_count2048_snlen12, sizeof(pdu1_count2048_snlen12)); - TESTASSERT(test_tx(n_packets, - normal_init_state, - srsran::PDCP_SN_LEN_12, - n_packets, - std::move(pdu_exp_count2048_len12), - logger) == 0); - - /* - * TX Test 3: PDCP Entity with SN LEN = 12 - * TX_NEXT = 4096. - * Input: {0x18, 0xE2} - * Output: PDCP Header {0x80,0x00}, Ciphered Text {0x97, 0xbe}, MAC-I {0xa3, 0x32, 0xfa, 0x61} - */ - n_packets = 4097; - srsran::unique_byte_buffer_t pdu_exp_count4096_len12 = srsran::make_byte_buffer(); - pdu_exp_count4096_len12->append_bytes(pdu1_count4096_snlen12, sizeof(pdu1_count4096_snlen12)); - TESTASSERT(test_tx(n_packets, - normal_init_state, - srsran::PDCP_SN_LEN_12, - n_packets, - std::move(pdu_exp_count4096_len12), - logger) == 0); - - /* - * TX Test 4: PDCP Entity with SN LEN = 18 - * TX_NEXT = 0. - * Input: {0x18, 0xE2} - * Output: PDCP Header {0x80, 0x80, 0x00}, Ciphered Text {0x8f, 0xe3}, MAC-I {0xe0, 0xdf, 0x82, 0x92} - */ - n_packets = 1; - srsran::unique_byte_buffer_t pdu_exp_count0_len18 = srsran::make_byte_buffer(); - pdu_exp_count0_len18->append_bytes(pdu1_count0_snlen18, sizeof(pdu1_count0_snlen18)); - TESTASSERT( - test_tx( - n_packets, normal_init_state, srsran::PDCP_SN_LEN_18, n_packets, std::move(pdu_exp_count0_len18), logger) == - 0); - - /* - * TX Test 5: PDCP Entity with SN LEN = 18 - * TX_NEXT = 131072. - * Input: {0x18, 0xE2} - * Output: PDCP Header {0x82, 0x00, 0x00}, Ciphered Text {0x15, 0x01}, MAC-I {0xf4, 0xb0, 0xfc, 0xc5} - */ - n_packets = 131073; - srsran::unique_byte_buffer_t pdu_exp_sn131072_len18 = srsran::make_byte_buffer(); - pdu_exp_sn131072_len18->append_bytes(pdu1_count131072_snlen18, sizeof(pdu1_count131072_snlen18)); - TESTASSERT( - test_tx( - n_packets, normal_init_state, srsran::PDCP_SN_LEN_18, n_packets, std::move(pdu_exp_sn131072_len18), logger) == - 0); - - /* - * TX Test 6: PDCP Entity with SN LEN = 18 - * TX_NEXT = 262144. - * Input: {0x18, 0xE2} - * Output: PDCP Header {0x80, 0x00, 0x00}, Ciphered Text {0xc2, 0x47}, MAC-I {0xa8, 0xdd, 0xc0, 0x73} - */ - n_packets = 262145; - srsran::unique_byte_buffer_t pdu_exp_count262144_len18 = srsran::make_byte_buffer(); - pdu_exp_count262144_len18->append_bytes(pdu1_count262144_snlen18, sizeof(pdu1_count262144_snlen18)); - TESTASSERT(test_tx(n_packets, - normal_init_state, - srsran::PDCP_SN_LEN_18, - n_packets, - std::move(pdu_exp_count262144_len18), - logger) == 0); - - /* - * TX Test 7: PDCP Entity with SN LEN = 12 - * Test TX at COUNT wraparound. - * Should print a warning and drop all packets after wraparound. - */ - n_packets = 5; - srsran::unique_byte_buffer_t pdu_exp_count4294967295_len12 = srsran::make_byte_buffer(); - pdu_exp_count4294967295_len12->append_bytes(pdu1_count4294967295_snlen12, sizeof(pdu1_count4294967295_snlen12)); - TESTASSERT(test_tx(n_packets, - near_wraparound_init_state, - srsran::PDCP_SN_LEN_12, - 1, - std::move(pdu_exp_count4294967295_len12), - logger) == 0); - - /* - * TX Test 8: PDCP Entity with SN LEN = 18 - * Test TX at COUNT wraparound. - * Should print a warning and drop all packets after wraparound. - */ - n_packets = 5; - srsran::unique_byte_buffer_t pdu_exp_count4294967295_len18 = srsran::make_byte_buffer(); - pdu_exp_count4294967295_len18->append_bytes(pdu1_count4294967295_snlen18, sizeof(pdu1_count4294967295_snlen18)); - TESTASSERT(test_tx(n_packets, - near_wraparound_init_state, - srsran::PDCP_SN_LEN_18, - 1, - std::move(pdu_exp_count4294967295_len18), - logger) == 0); - return 0; -} - -// Setup all tests -int run_all_tests() -{ - // Setup log - auto& logger = srslog::fetch_basic_logger("PDCP NR Test TX", false); - logger.set_level(srslog::basic_levels::debug); - logger.set_hex_dump_max_size(128); - - TESTASSERT(test_tx_all(logger) == 0); - return 0; -} - -int main() -{ - srslog::init(); - - if (run_all_tests() != SRSRAN_SUCCESS) { - fprintf(stderr, "pdcp_nr_tests_tx() failed\n"); - return SRSRAN_ERROR; - } - return SRSRAN_SUCCESS; -} diff --git a/lib/test/upper/rlc_am_data_test.cc b/lib/test/upper/rlc_am_data_test.cc deleted file mode 100644 index 9f2df3d20..000000000 --- a/lib/test/upper/rlc_am_data_test.cc +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/common/test_common.h" -#include "srsran/upper/rlc_am_lte.h" -#include - -// Fixed header only -uint8_t pdu1[] = {0x88, 0x06}; -uint32_t PDU1_LEN = 2; - -// Fixed + 2 LI fields (each 1500) -uint8_t pdu2[] = {0x8C, 0x00, 0xDD, 0xC5, 0xDC}; -uint32_t PDU2_LEN = 5; - -// Fixed + 3 LI fields (each 1500) -uint8_t pdu3[] = {0x8C, 0x00, 0xDD, 0xCD, 0xDC, 0x5D, 0xC0}; -uint32_t PDU3_LEN = 7; - -// D/C = 1 = Data PDU -// RF = 0 = AMD PDU -// P = 0 = Status PDU is not requested -// FI = 11 = First byte of the Data field does not corresponds to the first byte of a RLC SDU, -// Last byte of the Data field does not corresponds to the last byte of a RLC SDU -// E = 1 = A set of E field and LI field follows from the octet following the fixed part of the header -// SN = 0000000010 -> SN 2 -// E = 1 -// LI1 = 1010011 1110 (1342 Dec) -// E = 0 -// LI2 = 10111011100 (1500 Dec) -uint8_t pdu4[] = {0x9C, 0x02, 0xD3, 0xE5, 0xDC}; -uint32_t PDU4_LEN = 5; - -using namespace srsran; - -int test1() -{ - srsran::rlc_amd_pdu_header_t h; - srsran::byte_buffer_t b1, b2; - - memcpy(b1.msg, &pdu1[0], PDU1_LEN); - b1.N_bytes = PDU1_LEN; - rlc_am_read_data_pdu_header(&b1, &h); - TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); - TESTASSERT(0x01 == h.fi); - TESTASSERT(0 == h.N_li); - TESTASSERT(0 == h.lsf); - TESTASSERT(0 == h.p); - TESTASSERT(0 == h.rf); - TESTASSERT(0 == h.so); - TESTASSERT(6 == h.sn); - rlc_am_write_data_pdu_header(&h, &b2); - TESTASSERT(b2.N_bytes == PDU1_LEN); - for (uint32_t i = 0; i < b2.N_bytes; i++) - TESTASSERT(b2.msg[i] == b1.msg[i]); - return SRSRAN_SUCCESS; -} - -int test2() -{ - srsran::rlc_amd_pdu_header_t h; - srsran::byte_buffer_t b1, b2; - - memcpy(b1.msg, &pdu2[0], PDU2_LEN); - b1.N_bytes = PDU2_LEN; - rlc_am_read_data_pdu_header(&b1, &h); - TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); - TESTASSERT(0x01 == h.fi); - TESTASSERT(2 == h.N_li); - TESTASSERT(1500 == h.li[0]); - TESTASSERT(1500 == h.li[1]); - TESTASSERT(0 == h.lsf); - TESTASSERT(0 == h.p); - TESTASSERT(0 == h.rf); - TESTASSERT(0 == h.so); - TESTASSERT(0 == h.sn); - rlc_am_write_data_pdu_header(&h, &b2); - TESTASSERT(b2.N_bytes == PDU2_LEN); - for (uint32_t i = 0; i < b2.N_bytes; i++) - TESTASSERT(b2.msg[i] == b1.msg[i]); - return SRSRAN_SUCCESS; -} - -int test3() -{ - srsran::rlc_amd_pdu_header_t h; - srsran::byte_buffer_t b1, b2; - - memcpy(b1.msg, &pdu3[0], PDU3_LEN); - b1.N_bytes = PDU3_LEN; - rlc_am_read_data_pdu_header(&b1, &h); - TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); - TESTASSERT(0x01 == h.fi); - TESTASSERT(3 == h.N_li); - TESTASSERT(1500 == h.li[0]); - TESTASSERT(1500 == h.li[1]); - TESTASSERT(1500 == h.li[2]); - TESTASSERT(0 == h.lsf); - TESTASSERT(0 == h.p); - TESTASSERT(0 == h.rf); - TESTASSERT(0 == h.so); - TESTASSERT(0 == h.sn); - rlc_am_write_data_pdu_header(&h, &b2); - TESTASSERT(b2.N_bytes == PDU3_LEN); - for (uint32_t i = 0; i < b2.N_bytes; i++) - TESTASSERT(b2.msg[i] == b1.msg[i]); - return SRSRAN_SUCCESS; -} - -int test4() -{ - srsran::rlc_amd_pdu_header_t h; - srsran::byte_buffer_t b1, b2; - - memcpy(b1.msg, &pdu4[0], PDU4_LEN); - b1.N_bytes = PDU4_LEN; - rlc_am_read_data_pdu_header(&b1, &h); - TESTASSERT(RLC_DC_FIELD_DATA_PDU == h.dc); - TESTASSERT(0x03 == h.fi); - TESTASSERT(2 == h.N_li); - TESTASSERT(1342 == h.li[0]); - TESTASSERT(1500 == h.li[1]); - TESTASSERT(0 == h.lsf); - TESTASSERT(0 == h.p); - TESTASSERT(0 == h.rf); - TESTASSERT(0 == h.so); - TESTASSERT(2 == h.sn); - rlc_am_write_data_pdu_header(&h, &b2); - TESTASSERT(b2.N_bytes == PDU4_LEN); - for (uint32_t i = 0; i < b2.N_bytes; i++) - TESTASSERT(b2.msg[i] == b1.msg[i]); - return SRSRAN_SUCCESS; -} - -int main(int argc, char** argv) -{ - srslog::init(); - - TESTASSERT(test1() == SRSRAN_SUCCESS); - TESTASSERT(test2() == SRSRAN_SUCCESS); - TESTASSERT(test3() == SRSRAN_SUCCESS); - TESTASSERT(test4() == SRSRAN_SUCCESS); - - return SRSRAN_SUCCESS; -} diff --git a/lib/test/upper/rlc_am_nr_pdu_test.cc b/lib/test/upper/rlc_am_nr_pdu_test.cc deleted file mode 100644 index 832e7540f..000000000 --- a/lib/test/upper/rlc_am_nr_pdu_test.cc +++ /dev/null @@ -1,347 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/config.h" -#include "srsran/upper/rlc.h" -#include "srsran/upper/rlc_am_nr.h" - -#include -#include -#include -#include - -#define TESTASSERT(cond) \ - { \ - if (!(cond)) { \ - std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ - return -1; \ - } \ - } - -#define PCAP 0 -#define PCAP_CRNTI (0x1001) -#define PCAP_TTI (666) - -using namespace srsran; - -#if PCAP -#include "srsran/common/mac_nr_pcap.h" -#include "srsran/mac/mac_nr_pdu.h" -static std::unique_ptr pcap_handle = nullptr; -#endif - -int write_pdu_to_pcap(const uint32_t lcid, const uint8_t* payload, const uint32_t len) -{ -#if PCAP - if (pcap_handle) { - byte_buffer_t tx_buffer; - srsran::mac_nr_sch_pdu tx_pdu; - tx_pdu.init_tx(&tx_buffer, len + 10); - tx_pdu.add_sdu(lcid, payload, len); - tx_pdu.pack(); - pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); - return SRSRAN_SUCCESS; - } -#endif - return SRSRAN_ERROR; -} - -template -srsran::byte_buffer_t make_pdu_and_log(const std::array& tv) -{ - srsran::byte_buffer_t pdu; - memcpy(pdu.msg, tv.data(), tv.size()); - pdu.N_bytes = tv.size(); - write_pdu_to_pcap(4, tv.data(), tv.size()); - return pdu; -} - -void corrupt_pdu_header(srsran::byte_buffer_t& pdu, const uint32_t header_len, const uint32_t payload_len) -{ - // clear header only - for (uint32_t i = 0; i < header_len; i++) { - pdu.msg[i] = 0xaa; - } - pdu.msg += header_len; - pdu.N_bytes = payload_len; -} - -// RLC AM PDU 12bit with complete SDU -int rlc_am_nr_pdu_test1() -{ - const int header_len = 2, payload_len = 4; - std::array tv = {0x80, 0x00, 0x11, 0x22, 0x33, 0x44}; - srsran::byte_buffer_t pdu = make_pdu_and_log(tv); - - // unpack PDU - rlc_am_nr_pdu_header_t header = {}; - TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &header) != 0); - TESTASSERT(header.si == rlc_nr_si_field_t::full_sdu); - - // clear header - corrupt_pdu_header(pdu, header_len, payload_len); - - // pack again - TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); - TESTASSERT(pdu.N_bytes == tv.size()); - TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); - - return SRSRAN_SUCCESS; -} - -// RLC AM PDU 12bit first segment of SDU with P flag and SN 511 -int rlc_am_nr_pdu_test2() -{ - const int header_len = 2, payload_len = 4; - std::array tv = {0xd1, 0xff, 0x11, 0x22, 0x33, 0x44}; - srsran::byte_buffer_t pdu = make_pdu_and_log(tv); - - // unpack PDU - rlc_am_nr_pdu_header_t header = {}; - TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &header) != 0); - TESTASSERT(header.si == rlc_nr_si_field_t::first_segment); - TESTASSERT(header.sn == 511); - TESTASSERT(header.so == 0); - - // clear header - corrupt_pdu_header(pdu, header_len, payload_len); - - // pack again - TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); - TESTASSERT(pdu.N_bytes == tv.size()); - - write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); - - TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); - - return SRSRAN_SUCCESS; -} - -// RLC AM PDU 12bit last segment of SDU without P flag and SN 0x0404 and SO 0x0404 (1028) -int rlc_am_nr_pdu_test3() -{ - const int header_len = 4, payload_len = 4; - std::array tv = {0xa4, 0x04, 0x04, 0x04, 0x11, 0x22, 0x33, 0x44}; - srsran::byte_buffer_t pdu = make_pdu_and_log(tv); - - // unpack PDU - rlc_am_nr_pdu_header_t header = {}; - TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &header) != 0); - TESTASSERT(header.si == rlc_nr_si_field_t::last_segment); - TESTASSERT(header.sn == 1028); - TESTASSERT(header.so == 1028); - - // clear header - corrupt_pdu_header(pdu, header_len, payload_len); - - // pack again - TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); - TESTASSERT(pdu.N_bytes == tv.size()); - - write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); - - TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); - - return SRSRAN_SUCCESS; -} - -// RLC AM PDU 18bit full SDU with P flag and SN 0x100000001000000010 (131586) -int rlc_am_nr_pdu_test4() -{ - const int header_len = 3, payload_len = 4; - std::array tv = {0xc2, 0x02, 0x02, 0x11, 0x22, 0x33, 0x44}; - srsran::byte_buffer_t pdu = make_pdu_and_log(tv); - - // unpack PDU - rlc_am_nr_pdu_header_t header = {}; - TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &header) != 0); - TESTASSERT(header.si == rlc_nr_si_field_t::full_sdu); - TESTASSERT(header.sn == 131586); - TESTASSERT(header.so == 0); - - // clear header - corrupt_pdu_header(pdu, header_len, payload_len); - - // pack again - TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); - TESTASSERT(pdu.N_bytes == tv.size()); - - write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); - - TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); - - return SRSRAN_SUCCESS; -} - -// RLC AM PDU 18bit middle part of SDU (SO 514) without P flag and SN 131327 -int rlc_am_nr_pdu_test5() -{ - const int header_len = 5, payload_len = 4; - std::array tv = {0xb2, 0x00, 0xff, 0x02, 0x02, 0x11, 0x22, 0x33, 0x44}; - srsran::byte_buffer_t pdu = make_pdu_and_log(tv); - - // unpack PDU - rlc_am_nr_pdu_header_t header = {}; - TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &header) != 0); - TESTASSERT(header.si == rlc_nr_si_field_t::neither_first_nor_last_segment); - TESTASSERT(header.sn == 131327); - TESTASSERT(header.so == 514); - - // clear header - corrupt_pdu_header(pdu, header_len, payload_len); - - // pack again - TESTASSERT(rlc_am_nr_write_data_pdu_header(header, &pdu) == header_len); - TESTASSERT(pdu.N_bytes == tv.size()); - - write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); - - TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); - - return SRSRAN_SUCCESS; -} - -// Malformed RLC AM PDU 18bit with reserved bits set -int rlc_am_nr_pdu_test6() -{ - const int header_len = 5, payload_len = 4; - std::array tv = {0xb7, 0x00, 0xff, 0x02, 0x02, 0x11, 0x22, 0x33, 0x44}; - srsran::byte_buffer_t pdu = make_pdu_and_log(tv); - - // unpack PDU - rlc_am_nr_pdu_header_t header = {}; - TESTASSERT(rlc_am_nr_read_data_pdu_header(&pdu, srsran::rlc_am_nr_sn_size_t::size18bits, &header) == 0); - TESTASSERT(header.sn == 0); - - return SRSRAN_SUCCESS; -} - -///< Control PDU tests -// Status PDU for 12bit SN with ACK_SN=2065 and no further NACK_SN (E1 bit not set) -int rlc_am_nr_control_pdu_test1() -{ - const int len = 3; - std::array tv = {0x08, 0x11, 0x00}; - srsran::byte_buffer_t pdu = make_pdu_and_log(tv); - - TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); - - // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; - TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); - TESTASSERT(status_pdu.ack_sn == 2065); - TESTASSERT(status_pdu.N_nack == 0); - - // reset status PDU - pdu.clear(); - - // pack again - TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); - TESTASSERT(pdu.N_bytes == tv.size()); - - write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); - - TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); - - return SRSRAN_SUCCESS; -} - -// Status PDU for 12bit SN with ACK_SN=2065 and NACK_SN=273 (E1 bit set) -int rlc_am_nr_control_pdu_test2() -{ - const int len = 5; - std::array tv = {0x08, 0x11, 0x80, 0x11, 0x10}; - srsran::byte_buffer_t pdu = make_pdu_and_log(tv); - - TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true); - - // unpack PDU - rlc_am_nr_status_pdu_t status_pdu = {}; - TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS); - TESTASSERT(status_pdu.ack_sn == 2065); - TESTASSERT(status_pdu.N_nack == 1); - TESTASSERT(status_pdu.nacks[0].nack_sn == 273); - - // reset status PDU - pdu.clear(); - - // pack again - TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS); - // TESTASSERT(pdu.N_bytes == tv.size()); - - write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes); - - TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0); - - return SRSRAN_SUCCESS; -} - -int main(int argc, char** argv) -{ -#if PCAP - pcap_handle = std::unique_ptr(new srsran::mac_nr_pcap()); - pcap_handle->open("rlc_am_nr_pdu_test.pcap"); -#endif - - srslog::init(); - - if (rlc_am_nr_pdu_test1()) { - fprintf(stderr, "rlc_am_nr_pdu_test1() failed.\n"); - return SRSRAN_ERROR; - } - - if (rlc_am_nr_pdu_test2()) { - fprintf(stderr, "rlc_am_nr_pdu_test2() failed.\n"); - return SRSRAN_ERROR; - } - - if (rlc_am_nr_pdu_test3()) { - fprintf(stderr, "rlc_am_nr_pdu_test3() failed.\n"); - return SRSRAN_ERROR; - } - - if (rlc_am_nr_pdu_test4()) { - fprintf(stderr, "rlc_am_nr_pdu_test4() failed.\n"); - return SRSRAN_ERROR; - } - - if (rlc_am_nr_pdu_test5()) { - fprintf(stderr, "rlc_am_nr_pdu_test5() failed.\n"); - return SRSRAN_ERROR; - } - - if (rlc_am_nr_pdu_test6()) { - fprintf(stderr, "rlc_am_nr_pdu_test6() failed.\n"); - return SRSRAN_ERROR; - } - - if (rlc_am_nr_control_pdu_test1()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test1() failed.\n"); - return SRSRAN_ERROR; - } - - if (rlc_am_nr_control_pdu_test2()) { - fprintf(stderr, "rlc_am_nr_control_pdu_test2() failed.\n"); - return SRSRAN_ERROR; - } - - return SRSRAN_SUCCESS; -} diff --git a/lib/test/upper/rlc_stress_test.cc b/lib/test/upper/rlc_stress_test.cc deleted file mode 100644 index b565303fe..000000000 --- a/lib/test/upper/rlc_stress_test.cc +++ /dev/null @@ -1,597 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/common/block_queue.h" -#include "srsran/common/crash_handler.h" -#include "srsran/common/rlc_pcap.h" -#include "srsran/common/test_common.h" -#include "srsran/common/threads.h" -#include "srsran/upper/rlc.h" -#include -#include -#include -#include -#include -#include -#include - -#define LOG_HEX_LIMIT (-1) - -#define PCAP_CRNTI (0x1001) -#define PCAP_TTI (666) - -#include "srsran/common/mac_pcap.h" -#include "srsran/mac/mac_sch_pdu_nr.h" -static std::unique_ptr pcap_handle = nullptr; - -int write_pdu_to_pcap(const bool is_dl, const uint32_t lcid, const uint8_t* payload, const uint32_t len) -{ - if (pcap_handle) { - srsran::byte_buffer_t tx_buffer; - srsran::mac_sch_pdu_nr tx_pdu; - tx_pdu.init_tx(&tx_buffer, len + 10); - tx_pdu.add_sdu(lcid, payload, len); - tx_pdu.pack(); - if (is_dl) { - pcap_handle->write_dl_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); - } else { - pcap_handle->write_ul_crnti_nr(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI); - } - - return SRSRAN_SUCCESS; - } - return SRSRAN_ERROR; -} - -using namespace std; -using namespace srsue; -using namespace srsran; -namespace bpo = boost::program_options; - -#define MIN_SDU_SIZE (5) -#define MAX_SDU_SIZE (1500) - -typedef struct { - std::string rat; - std::string mode; - int32_t sdu_size; - uint32_t test_duration_sec; - float pdu_drop_rate; - float pdu_cut_rate; - float pdu_duplicate_rate; - uint32_t sdu_gen_delay_usec; - uint32_t pdu_tx_delay_usec; - uint32_t log_level; - bool single_tx; - bool write_pcap; - uint32_t avg_opp_size; - bool random_opp; - bool zero_seed; - uint32_t nof_pdu_tti; - uint32_t max_retx; -} stress_test_args_t; - -void parse_args(stress_test_args_t* args, int argc, char* argv[]) -{ - // Command line only options - bpo::options_description general("General options"); - - general.add_options()("help,h", "Produce help message")("version,v", "Print version information and exit"); - - // clang-format off - - // Command line or config file options - bpo::options_description common("Configuration options"); - common.add_options() - ("rat", bpo::value(&args->rat)->default_value("LTE"), "The RLC version to use (LTE/NR)") - ("mode", bpo::value(&args->mode)->default_value("AM"), "Whether to test RLC acknowledged or unacknowledged mode (AM/UM for LTE) (UM6/UM12 for NR)") - ("duration", bpo::value(&args->test_duration_sec)->default_value(5), "Duration (sec)") - ("sdu_size", bpo::value(&args->sdu_size)->default_value(-1), "Size of SDUs (-1 means random)") - ("random_opp", bpo::value(&args->random_opp)->default_value(true), "Whether to generate random MAC opportunities") - ("avg_opp_size", bpo::value(&args->avg_opp_size)->default_value(1505), "Size of the MAC opportunity (if not random)") - ("sdu_gen_delay", bpo::value(&args->sdu_gen_delay_usec)->default_value(0), "SDU generation delay (usec)") - ("pdu_tx_delay", bpo::value(&args->pdu_tx_delay_usec)->default_value(0), "Delay in MAC for transfering PDU from tx'ing RLC to rx'ing RLC (usec)") - ("pdu_drop_rate", bpo::value(&args->pdu_drop_rate)->default_value(0.1), "Rate at which RLC PDUs are dropped") - ("pdu_cut_rate", bpo::value(&args->pdu_cut_rate)->default_value(0.0), "Rate at which RLC PDUs are chopped in length") - ("pdu_duplicate_rate", bpo::value(&args->pdu_duplicate_rate)->default_value(0.0), "Rate at which RLC PDUs are duplicated") - ("loglevel", bpo::value(&args->log_level)->default_value((int)srslog::basic_levels::debug), "Log level (1=Error,2=Warning,3=Info,4=Debug)") - ("singletx", bpo::value(&args->single_tx)->default_value(false), "If set to true, only one node is generating data") - ("pcap", bpo::value(&args->write_pcap)->default_value(false), "Whether to write all RLC PDU to PCAP file") - ("zeroseed", bpo::value(&args->zero_seed)->default_value(false), "Whether to initialize random seed to zero") - ("max_retx", bpo::value(&args->max_retx)->default_value(32), "Maximum number of RLC retransmission attempts") - ("nof_pdu_tti", bpo::value(&args->nof_pdu_tti)->default_value(1), "Number of PDUs processed in a TTI"); - // clang-format on - - // these options are allowed on the command line - bpo::options_description cmdline_options; - cmdline_options.add(common).add(general); - - // parse the command line and store result in vm - bpo::variables_map vm; - bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).run(), vm); - bpo::notify(vm); - - // help option was given - print usage and exit - if (vm.count("help") > 0) { - cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; - cout << common << endl << general << endl; - exit(0); - } - - if (args->log_level > 4) { - args->log_level = 4; - printf("Set log level to %d (%s)\n", - args->log_level, - srslog::basic_level_to_string(static_cast(args->log_level))); - } - - // convert mode to upper case - for (auto& c : args->mode) { - c = toupper(c); - } -} - -class mac_dummy : public srsran::thread -{ -public: - mac_dummy(rlc_interface_mac* rlc1_, - rlc_interface_mac* rlc2_, - stress_test_args_t args_, - uint32_t lcid_, - timer_handler* timers_, - rlc_pcap* pcap_, - uint32_t seed_) : - run_enable(true), - rlc1(rlc1_), - rlc2(rlc2_), - args(args_), - pcap(pcap_), - lcid(lcid_), - timers(timers_), - logger(srslog::fetch_basic_logger("MAC", false)), - thread("MAC_DUMMY"), - real_dist(0.0, 1.0), - mt19937(seed_) - { - logger.set_level(static_cast(args.log_level)); - logger.set_hex_dump_max_size(LOG_HEX_LIMIT); - } - - void stop() - { - run_enable = false; - wait_thread_finish(); - } - - void enqueue_task(srsran::move_task_t task) { pending_tasks.push(std::move(task)); } - -private: - void run_tx_tti(rlc_interface_mac* tx_rlc, rlc_interface_mac* rx_rlc, std::vector& pdu_list) - { - // Generate A number of MAC PDUs - for (uint32_t i = 0; i < args.nof_pdu_tti; i++) { - // Create PDU unique buffer - unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (!pdu) { - printf("Fatal Error: Could not allocate PDU in mac_reader::run_thread\n"); - exit(-1); - } - - // Get MAC PDU size - float factor = 1.0f; - if (args.random_opp) { - factor = 0.5f + real_dist(mt19937); - } - int opp_size = static_cast(args.avg_opp_size * factor); - - // Request data to transmit - uint32_t buf_state = tx_rlc->get_buffer_state(lcid); - if (buf_state > 0) { - int read = tx_rlc->read_pdu(lcid, pdu->msg, opp_size); - pdu->N_bytes = read; - - // Push PDU in the list - pdu_list.push_back(std::move(pdu)); - } - } - } - - void run_rx_tti(rlc_interface_mac* tx_rlc, - rlc_interface_mac* rx_rlc, - bool is_dl, - std::vector& pdu_list) - { - // Sleep if necessary - if (args.pdu_tx_delay_usec > 0) { - std::this_thread::sleep_for(std::chrono::microseconds(args.pdu_tx_delay_usec)); - } - - auto it = pdu_list.begin(); // PDU iterator - bool skip_action = false; // Avoid discarding a duplicated or duplicating a discarded - - while (it != pdu_list.end()) { - // Get PDU unique buffer - unique_byte_buffer_t& pdu = *it; - - // Drop - float rnd = real_dist(mt19937); - if (std::isnan(rnd) || (((rnd > args.pdu_drop_rate) || skip_action) && pdu->N_bytes > 0)) { - uint32_t pdu_len = pdu->N_bytes; - - // Cut - if ((real_dist(mt19937) < args.pdu_cut_rate)) { - int cut_pdu_len = static_cast(pdu_len * real_dist(mt19937)); - logger.info("Cutting MAC PDU len (%d B -> %d B)", pdu_len, cut_pdu_len); - pdu_len = cut_pdu_len; - } - - // Write PDU in RX - rx_rlc->write_pdu(lcid, pdu->msg, pdu_len); - - // Write PCAP - write_pdu_to_pcap(is_dl, 4, pdu->msg, pdu_len); // Only handles NR rat - if (is_dl) { - pcap->write_dl_ccch(pdu->msg, pdu_len); - } else { - pcap->write_ul_ccch(pdu->msg, pdu_len); - } - } else { - logger.warning(pdu->msg, pdu->N_bytes, "Dropping RLC PDU (%d B)", pdu->N_bytes); - skip_action = true; // Avoid drop duplicating this PDU - } - - // Duplicate - if (real_dist(mt19937) > args.pdu_duplicate_rate || skip_action) { - it++; - skip_action = false; // Allow action on the next PDU - } else { - logger.warning(pdu->msg, pdu->N_bytes, "Duplicating RLC PDU (%d B)", pdu->N_bytes); - skip_action = true; // Avoid drop of this PDU - } - } - } - - void run_tti(rlc_interface_mac* tx_rlc, rlc_interface_mac* rx_rlc, bool is_dl) - { - std::vector pdu_list; - - // Run Tx - run_tx_tti(tx_rlc, rx_rlc, pdu_list); - - // Reverse PDUs - std::reverse(pdu_list.begin(), pdu_list.end()); - - // Run Rx - run_rx_tti(tx_rlc, rx_rlc, is_dl, pdu_list); - } - - void run_thread() override - { - srsran::move_task_t task; - while (run_enable) { - // Downlink direction first (RLC1->RLC2) - run_tti(rlc1, rlc2, true); - - // UL direction (RLC2->RLC1) - run_tti(rlc2, rlc1, false); - - // step timer - timers->step_all(); - - if (pending_tasks.try_pop(&task)) { - task(); - } - } - if (pending_tasks.try_pop(&task)) { - task(); - } - } - - rlc_interface_mac* rlc1 = nullptr; - rlc_interface_mac* rlc2 = nullptr; - - bool run_enable = false; - stress_test_args_t args = {}; - rlc_pcap* pcap = nullptr; - uint32_t lcid = 0; - srslog::basic_logger& logger; - srsran::timer_handler* timers = nullptr; - - srsran::block_queue pending_tasks; - - std::mt19937 mt19937; - std::uniform_real_distribution real_dist; -}; - -class rlc_tester : public pdcp_interface_rlc, public rrc_interface_rlc, public srsran::thread -{ -public: - rlc_tester(rlc_interface_pdcp* rlc_pdcp_, - std::string name_, - stress_test_args_t args_, - uint32_t lcid_, - uint32_t seed_) : - logger(srslog::fetch_basic_logger(name_.c_str(), false)), - rlc_pdcp(rlc_pdcp_), - name(name_), - args(args_), - lcid(lcid_), - thread("RLC_TESTER"), - int_dist(MIN_SDU_SIZE, MAX_SDU_SIZE), - mt19937(seed_) - { - logger.set_level(srslog::basic_levels::error); - logger.set_hex_dump_max_size(LOG_HEX_LIMIT); - } - - void stop() - { - run_enable = false; - wait_thread_finish(); - } - - // PDCP interface - void write_pdu(uint32_t rx_lcid, unique_byte_buffer_t sdu) - { - assert(rx_lcid == lcid); - if (args.mode != "AM") { - // Only AM will guarantee to deliver SDUs, take first byte as reference for other modes - next_expected_sdu = sdu->msg[0]; - } - - // check SDU content (consider faster alternative) - for (uint32_t i = 0; i < sdu->N_bytes; ++i) { - if (sdu->msg[i] != next_expected_sdu) { - logger.error(sdu->msg, - sdu->N_bytes, - "Received malformed SDU with size %d, expected data 0x%X", - sdu->N_bytes, - next_expected_sdu); - fprintf(stderr, "Received malformed SDU with size %d\n", sdu->N_bytes); - fprintf(stdout, "Received malformed SDU with size %d\n", sdu->N_bytes); - std::this_thread::sleep_for(std::chrono::seconds(1)); // give some time to flush logs - exit(-1); - } - } - next_expected_sdu += 1; - rx_pdus++; - } - void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} - void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {} - void write_pdu_pcch(unique_byte_buffer_t sdu) {} - void write_pdu_mch(uint32_t lcid_, srsran::unique_byte_buffer_t sdu) {} - void notify_delivery(uint32_t lcid_, const srsran::pdcp_sn_vector_t& pdcp_sns) {} - void notify_failure(uint32_t lcid_, const srsran::pdcp_sn_vector_t& pdcp_sns) {} - - // RRC interface - void max_retx_attempted() - { - logger.error( - "Maximum number of RLC retransmission reached. Consider increasing threshold or lowering channel drop rate."); - std::this_thread::sleep_for(std::chrono::seconds(1)); - exit(1); - } - const char* get_rb_name(uint32_t rx_lcid) { return "DRB1"; } - - int get_nof_rx_pdus() { return rx_pdus; } - -private: - const static size_t max_pdcp_sn = 262143u; // 18bit SN - void run_thread() - { - uint32_t pdcp_sn = 0; - uint32_t sdu_size = 0; - uint8_t payload = 0x0; // increment for each SDU - while (run_enable) { - // SDU queue is full, don't assign PDCP SN - if (rlc_pdcp->sdu_queue_is_full(lcid)) { - continue; - } - - unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (pdu == NULL) { - printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n"); - // backoff for a bit - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - pdu->md.pdcp_sn = pdcp_sn; - - // random or fixed SDU size - if (args.sdu_size < 1) { - sdu_size = int_dist(mt19937); - } else { - sdu_size = args.sdu_size; - } - - for (uint32_t i = 0; i < sdu_size; i++) { - pdu->msg[i] = payload; - } - pdu->N_bytes = sdu_size; - payload++; - - rlc_pdcp->write_sdu(lcid, std::move(pdu)); - pdcp_sn = (pdcp_sn + 1) % max_pdcp_sn; - if (args.sdu_gen_delay_usec > 0) { - std::this_thread::sleep_for(std::chrono::microseconds(args.sdu_gen_delay_usec)); - } - } - } - - bool run_enable = true; - /// Tx uses thread-local PDCP SN to set SDU content, the Rx uses this variable to check received SDUs - uint8_t next_expected_sdu = 0; - uint64_t rx_pdus = 0; - uint32_t lcid = 0; - srslog::basic_logger& logger; - - std::string name; - - stress_test_args_t args = {}; - - rlc_interface_pdcp* rlc_pdcp = nullptr; // used by run_thread to push PDCP SDUs to RLC - - std::mt19937 mt19937; - std::uniform_int_distribution<> int_dist; -}; - -void stress_test(stress_test_args_t args) -{ - auto& log1 = srslog::fetch_basic_logger("RLC_1", false); - log1.set_level(static_cast(args.log_level)); - log1.set_hex_dump_max_size(LOG_HEX_LIMIT); - auto& log2 = srslog::fetch_basic_logger("RLC_2", false); - log2.set_level(static_cast(args.log_level)); - log2.set_hex_dump_max_size(LOG_HEX_LIMIT); - - rlc_pcap pcap; - uint32_t lcid = 1; - - rlc_config_t cnfg_ = {}; - if (args.rat == "LTE") { - if (args.mode == "AM") { - // config RLC AM bearer - cnfg_ = rlc_config_t::default_rlc_am_config(); - cnfg_.am.max_retx_thresh = args.max_retx; - } else if (args.mode == "UM") { - // config UM bearer - cnfg_ = rlc_config_t::default_rlc_um_config(); - } else if (args.mode == "TM") { - // use default LCID in TM - lcid = 0; - } else { - cout << "Unsupported RLC mode " << args.mode << ", exiting." << endl; - exit(-1); - } - - if (args.write_pcap) { - pcap.open("rlc_stress_test.pcap", cnfg_); - } - } else if (args.rat == "NR") { - if (args.mode == "UM6") { - cnfg_ = rlc_config_t::default_rlc_um_nr_config(6); - } else if (args.mode == "UM12") { - cnfg_ = rlc_config_t::default_rlc_um_nr_config(12); - } else { - cout << "Unsupported RLC mode " << args.mode << ", exiting." << endl; - exit(-1); - } - - if (args.write_pcap) { - pcap_handle = std::unique_ptr(new srsran::mac_pcap()); - pcap_handle->open("rlc_stress_test_nr.pcap"); - } - } else { - cout << "Unsupported RAT mode " << args.rat << ", exiting." << endl; - exit(-1); - } - - // generate random seed if needed - uint32_t seed = 0; - if (not args.zero_seed) { - std::random_device rd; - seed = rd(); - } - - srsran::timer_handler timers(8); - - rlc rlc1(log1.id().c_str()); - rlc rlc2(log2.id().c_str()); - - rlc_tester tester1(&rlc1, "tester1", args, lcid, seed); - rlc_tester tester2(&rlc2, "tester2", args, lcid, seed); - mac_dummy mac(&rlc1, &rlc2, args, lcid, &timers, &pcap, seed); - - rlc1.init(&tester1, &tester1, &timers, 0); - rlc2.init(&tester2, &tester2, &timers, 0); - - // only add AM and UM bearers - if (args.mode != "TM") { - rlc1.add_bearer(lcid, cnfg_); - rlc2.add_bearer(lcid, cnfg_); - } - - printf("Starting test ..\n"); - - tester1.start(7); - if (!args.single_tx) { - tester2.start(7); - } - mac.start(); - - // wait until test is over - std::this_thread::sleep_for(std::chrono::seconds(args.test_duration_sec)); - - printf("Test finished, tearing down ..\n"); - - // Stop RLC instances first to release blocking writers - mac.enqueue_task([&rlc1, &rlc2]() { - rlc1.stop(); - rlc2.stop(); - }); - - printf("RLC entities stopped.\n"); - - // Stop upper layer writers - tester1.stop(); - tester2.stop(); - - printf("Writers stopped.\n"); - - mac.stop(); - if (args.write_pcap) { - pcap.close(); - } - - rlc_metrics_t metrics = {}; - rlc1.get_metrics(metrics, 1); - - printf("RLC1 received %d SDUs in %ds (%.2f/s), Tx=%" PRIu64 " B, Rx=%" PRIu64 " B\n", - tester1.get_nof_rx_pdus(), - args.test_duration_sec, - static_cast(tester1.get_nof_rx_pdus() / args.test_duration_sec), - metrics.bearer[lcid].num_tx_pdu_bytes, - metrics.bearer[lcid].num_rx_pdu_bytes); - rlc_bearer_metrics_print(metrics.bearer[lcid]); - - rlc2.get_metrics(metrics, 1); - printf("RLC2 received %d SDUs in %ds (%.2f/s), Tx=%" PRIu64 " B, Rx=%" PRIu64 " B\n", - tester2.get_nof_rx_pdus(), - args.test_duration_sec, - static_cast(tester2.get_nof_rx_pdus() / args.test_duration_sec), - metrics.bearer[lcid].num_tx_pdu_bytes, - metrics.bearer[lcid].num_rx_pdu_bytes); - rlc_bearer_metrics_print(metrics.bearer[lcid]); -} - -int main(int argc, char** argv) -{ - srsran_debug_handle_crash(argc, argv); - - stress_test_args_t args = {}; - parse_args(&args, argc, argv); - - srslog::init(); - - stress_test(args); - - exit(0); -} diff --git a/run-clang-format-diff.sh b/run-clang-format-diff.sh index 4059b88e8..905e66ce6 100755 --- a/run-clang-format-diff.sh +++ b/run-clang-format-diff.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsenb/CMakeLists.txt b/srsenb/CMakeLists.txt index 979dc7295..b2a4b5e70 100644 --- a/srsenb/CMakeLists.txt +++ b/srsenb/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -53,6 +53,6 @@ add_subdirectory(test) # Default configuration files ######################################################################## install(FILES enb.conf.example DESTINATION ${DATA_DIR}) -install(FILES drb.conf.example DESTINATION ${DATA_DIR}) -install(FILES rr.conf.example DESTINATION ${DATA_DIR}) -install(FILES sib.conf.example DESTINATION ${DATA_DIR}) \ No newline at end of file +install(FILES rb.conf.example DESTINATION ${DATA_DIR}) +install(FILES rr.conf.example DESTINATION ${DATA_DIR}) +install(FILES sib.conf.example DESTINATION ${DATA_DIR}) diff --git a/srsenb/drb.conf.example b/srsenb/drb.conf.example deleted file mode 100644 index 55a1a1491..000000000 --- a/srsenb/drb.conf.example +++ /dev/null @@ -1,54 +0,0 @@ - -// All times are in ms. Use -1 for infinity, where available - -qci_config = ( - -{ - qci=7; - pdcp_config = { - discard_timer = -1; - pdcp_sn_size = 12; - } - rlc_config = { - ul_um = { - sn_field_length = 10; - }; - dl_um = { - sn_field_length = 10; - t_reordering = 45; - }; - }; - logical_channel_config = { - priority = 13; - prioritized_bit_rate = -1; - bucket_size_duration = 100; - log_chan_group = 2; - }; -}, -{ - qci=9; - pdcp_config = { - discard_timer = 150; - status_report_required = true; - } - rlc_config = { - ul_am = { - t_poll_retx = 120; - poll_pdu = 64; - poll_byte = 750; - max_retx_thresh = 16; - }; - dl_am = { - t_reordering = 50; - t_status_prohibit = 50; - }; - }; - logical_channel_config = { - priority = 11; - prioritized_bit_rate = -1; - bucket_size_duration = 100; - log_chan_group = 3; - }; -} - -); diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index 19716f130..5e8eb88a2 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -12,6 +12,7 @@ # gtp_bind_addr: Local IP address to bind for GTP connection # gtp_advertise_addr: IP address of eNB to advertise for DL GTP-U Traffic # s1c_bind_addr: Local IP address to bind for S1AP connection +# s1c_bind_port: Source port for S1AP connection (0 means any) # n_prb: Number of Physical Resource Blocks (6,15,25,50,75,100) # tm: Transmission mode 1-4 (TM1 default) # nof_ports: Number of Tx ports (1 port default, set to 2 for TM2/3/4) @@ -24,41 +25,42 @@ mnc = 01 mme_addr = 127.0.1.100 gtp_bind_addr = 127.0.1.1 s1c_bind_addr = 127.0.1.1 +s1c_bind_port = 0 n_prb = 50 #tm = 4 #nof_ports = 2 ##################################################################### -# eNB configuration files +# eNB configuration files # # sib_config: SIB1, SIB2 and SIB3 configuration file -# note: when enabling mbms, use the sib.conf.mbsfn configuration file which includes SIB13 +# note: When enabling MBMS, use the sib.conf.mbsfn configuration file which includes SIB13 # rr_config: Radio Resources configuration file -# drb_config: DRB configuration file +# rb_config: SRB/DRB configuration file ##################################################################### [enb_files] sib_config = sib.conf rr_config = rr.conf -drb_config = drb.conf +rb_config = rb.conf ##################################################################### # RF configuration # # dl_earfcn: EARFCN code for DL (only valid if a single cell is configured in rr.conf) -# tx_gain: Transmit gain (dB). +# tx_gain: Transmit gain (dB). # rx_gain: Optional receive gain (dB). If disabled, AGC if enabled # # Optional parameters: # dl_freq: Override DL frequency corresponding to dl_earfcn # ul_freq: Override UL frequency corresponding to dl_earfcn (must be set if dl_freq is set) -# device_name: Device driver family. -# Supported options: "auto" (uses first found), "UHD", "bladeRF", "soapy" or "zmq". +# device_name: Device driver family +# Supported options: "auto" (uses first driver found), "UHD", "bladeRF", "soapy", "zmq" or "Sidekiq" # device_args: Arguments for the device driver. Options are "auto" or any string. # Default for UHD: "recv_frame_size=9232,send_frame_size=9232" # Default for bladeRF: "" # time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay # from antenna to timestamp insertion. -# Default "auto". B210 USRP: 100 samples, bladeRF: 27. +# Default "auto". B210 USRP: 100 samples, bladeRF: 27 ##################################################################### [rf] #dl_earfcn = 3350 @@ -84,48 +86,50 @@ rx_gain = 40 ##################################################################### # Packet capture configuration # -# MAC-layer packets are captured to file a the compact format decoded -# by the Wireshark. For decoding, use the UDP dissector and the UDP +# MAC-layer packets are captured to a file in the compact format which can +# be decoded by Wireshark. For decoding, use the UDP dissector and the UDP # heuristic dissection. Edit the preferences (Edit > Preferences > # Protocols > DLT_USER) for DLT_USER to add an entry for DLT=149 with # Protocol=udp. Further, enable the heuristic dissection in UDP under: # Analyze > Enabled Protocols > MAC-LTE > mac_lte_udp and MAC-NR > mac_nr_udp # For more information see: https://wiki.wireshark.org/MAC-LTE -# Configuring this Wireshark preferences is needed for decoding the MAC PCAP -# files as well as for the live network capture option. +# Configuring this Wireshark preferences is needed for decoding the MAC PCAP +# files as well as for the live network capture option. # # Please note that this setting will by default only capture MAC # frames on dedicated channels, and not SIB. You have to build with # WRITE_SIB_PCAP enabled in srsenb/src/stack/mac/mac.cc if you want # SIB to be part of the MAC pcap file. # -# S1AP Packets are captured to file in the compact format decoded by -# the Wireshark s1ap dissector and with DLT 150. +# S1AP Packets are captured to a file in the compact format which can +# be decoded by the Wireshark s1ap dissector with DLT 150. # To use the dissector, edit the preferences for DLT_USER to # add an entry with DLT=150, Payload Protocol=s1ap. # -# mac_enable: Enable MAC layer packet captures (true/false) -# mac_filename: File path to use for packet captures +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for LTE MAC packet captures +# nr_filename: File path to use for NR MAC packet captures # s1ap_enable: Enable or disable the PCAP. # s1ap_filename: File name where to save the PCAP. # # mac_net_enable: Enable MAC layer packet captures sent over the network (true/false default: false) # bind_ip: Bind IP address for MAC network trace (default: "0.0.0.0") # bind_port: Bind port for MAC network trace (default: 5687) -# client_ip: Client IP address for MAC network trace (default "127.0.0.1") +# client_ip: Client IP address for MAC network trace (default: "127.0.0.1") # client_port Client IP address for MAC network trace (default: 5847) ##################################################################### [pcap] -enable = false -filename = /tmp/enb.pcap -s1ap_enable = false -s1ap_filename = /tmp/enb_s1ap.pcap +#enable = false +#filename = /tmp/enb_mac.pcap +#nr_filename = /tmp/enb_mac_nr.pcap +#s1ap_enable = false +#s1ap_filename = /tmp/enb_s1ap.pcap -mac_net_enable = false -bind_ip = 0.0.0.0 -bind_port = 5687 -client_ip = 127.0.0.1 -client_port = 5847 +#mac_net_enable = false +#bind_ip = 0.0.0.0 +#bind_port = 5687 +#client_ip = 127.0.0.1 +#client_port = 5847 ##################################################################### # Log configuration @@ -160,19 +164,40 @@ enable = false # Scheduler configuration options # # sched_policy: User MAC scheduling policy (E.g. time_rr, time_pf) +# min_aggr_level: Optional minimum aggregation level index (l=log2(L) can be 0, 1, 2 or 3) # max_aggr_level: Optional maximum aggregation level index (l=log2(L) can be 0, 1, 2 or 3) +# adaptive_aggr_level: Boolean flag to enable/disable adaptive aggregation level based on target BLER # pdsch_mcs: Optional fixed PDSCH MCS (ignores reported CQIs if specified) -# pdsch_max_mcs: Optional PDSCH MCS limit +# pdsch_max_mcs: Optional PDSCH MCS limit # pusch_mcs: Optional fixed PUSCH MCS (ignores reported CQIs if specified) -# pusch_max_mcs: Optional PUSCH MCS limit -# min_nof_ctrl_symbols: Minimum number of control symbols -# max_nof_ctrl_symbols: Maximum number of control symbols +# pusch_max_mcs: Optional PUSCH MCS limit +# min_nof_ctrl_symbols: Minimum number of control symbols +# max_nof_ctrl_symbols: Maximum number of control symbols +# pucch_multiplex_enable: Allow PUCCH HARQ to collide with PUSCH and other PUCCH +# pucch_harq_max_rb: Maximum number of RB to be used for PUCCH on the edges of the grid. +# If defined and greater than 0, the scheduler will avoid DL PDCCH allocations if +# PUCCH HARQ falls outside this region +# target_bler: Target BLER (in decimal) to achieve via adaptive link +# max_delta_dl_cqi: Maximum shift in CQI for adaptive DL link +# max_delta_ul_snr: Maximum shift in UL SNR for adaptive UL link +# adaptive_dl_mcs_step_size: Step size or learning rate used in adaptive DL MCS link +# adaptive_ul_mcs_step_size: Step size or learning rate used in adaptive UL MCS link +# min_tpc_tti_interval: Minimum TTI interval between TPCs different than 1 +# ul_snr_avg_alpha: Exponential Average alpha coefficient used in estimation of UL SNR +# init_ul_snr_value: Initial UL SNR value used for computing MCS in the first UL grant +# init_dl_cqi: DL CQI value used before any CQI report is available to the eNB +# max_sib_coderate: Upper bound on SIB and RAR grants coderate +# pdcch_cqi_offset: CQI offset in derivation of PDCCH aggregation level +# nr_pdsch_mcs: Optional fixed NR PDSCH MCS (ignores reported CQIs if specified) +# nr_pusch_mcs: Optional fixed NR PUSCH MCS (ignores reported CQIs if specified) # ##################################################################### [scheduler] #policy = time_pf #policy_args = 2 -#max_aggr_level = -1 +#min_aggr_level = 0 +#max_aggr_level = 3 +#adaptive_aggr_level = false #pdsch_mcs = -1 #pdsch_max_mcs = -1 #pusch_mcs = -1 @@ -180,14 +205,28 @@ enable = false #min_nof_ctrl_symbols = 1 #max_nof_ctrl_symbols = 3 #pucch_multiplex_enable = false +#pucch_harq_max_rb = 0 +#target_bler = 0.05 +#max_delta_dl_cqi = 5 +#max_delta_ul_snr = 5 +#adaptive_dl_mcs_step_size = 0.001 +#adaptive_ul_mcs_step_size = 0.001 +#min_tpc_tti_interval = 1 +#ul_snr_avg_alpha=0.05 +#init_ul_snr_value=5 +#init_dl_cqi=5 +#max_sib_coderate=0.3 +#pdcch_cqi_offset=0 +nr_pdsch_mcs=28 +#nr_pusch_mcs=28 ##################################################################### # eMBMS configuration options # # enable: Enable MBMS transmission in the eNB -# m1u_multiaddr: Multicast addres the M1-U socket will register to -# m1u_if_addr: Address of the inteferface the M1-U interface will listen for multicast packets. -# mcs: Modulation and Coding scheme for MBMS traffic. +# m1u_multiaddr: Multicast address the M1-U socket will register to +# m1u_if_addr: Address of the interface the M1-U interface will listen to for multicast packets +# mcs: Modulation and Coding scheme for MBMS traffic # ##################################################################### [embms] @@ -200,7 +239,7 @@ enable = false ##################################################################### # Channel emulator options: -# enable: Enable/Disable internal Downlink/Uplink channel emulator +# enable: Enable/disable internal Downlink/Uplink channel emulator # # -- AWGN Generator # awgn.enable: Enable/disable AWGN generator @@ -213,8 +252,8 @@ enable = false # -- Delay Emulator delay(t) = delay_min + (delay_max - delay_min) * (1 + sin(2pi*t/period)) / 2 # Maximum speed [m/s]: (delay_max - delay_min) * pi * 300 / period # delay.enable: Enable/disable delay simulator -# delay.period_s: Delay period in seconds. -# delay.init_time_s: Delay initial time in seconds. +# delay.period_s: Delay period in seconds +# delay.init_time_s: Delay initial time in seconds # delay.maximum_us: Maximum delay in microseconds # delay.minumum_us: Minimum delay in microseconds # @@ -224,7 +263,7 @@ enable = false # rlf.t_off_ms: Time for Off state of the channel (ms) # # -- High Speed Train Doppler model simulator -# hst.enable: Enable/Disable HST simulator +# hst.enable: Enable/disable HST simulator # hst.period_s: HST simulation period in seconds # hst.fd_hz: Doppler frequency in Hz # hst.init_time_s: Initial time in seconds @@ -287,30 +326,73 @@ enable = false #fd_hz = -750.0 #init_time_s = 0.0 +##################################################################### +# CFR configuration options +# +# The CFR module provides crest factor reduction for the transmitted signal. +# +# enable: Enable or disable the CFR. Default: disabled +# +# mode: manual: CFR threshold is set by cfr_manual_thres (default). +# auto_ema: CFR threshold is adaptive based on the signal PAPR. Power avg. with Exponential Moving Average. +# The time constant of the averaging can be tweaked with the ema_alpha parameter. +# auto_cma: CFR threshold is adaptive based on the signal PAPR. Power avg. with Cumulative Moving Average. +# Use with care, as CMA's increasingly slow response may be unsuitable for most use cases. +# +# strength: Ratio between amplitude-limited vs unprocessed signal (0 to 1). Default: 1 +# manual_thres: Fixed manual clipping threshold for CFR manual mode. Default: 0.5 +# auto_target_papr: Signal PAPR target (in dB) in CFR auto modes. output PAPR can be higher due to peak smoothing. Default: 8 +# ema_alpha: Alpha coefficient for the power average in auto_ema mode. Default: 1/7 +# +##################################################################### +[cfr] +#enable = false +#mode = manual +#manual_thres = 0.5 +#strength = 1 +#auto_target_papr = 8 +#ema_alpha = 0.0143 ##################################################################### # Expert configuration options # -# pusch_max_its: Maximum number of turbo decoder iterations (Default 4) -# pusch_8bit_decoder: Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental) -# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 3) -# metrics_period_secs: Sets the period at which metrics are requested from the eNB. +# pusch_max_its: Maximum number of turbo decoder iterations (default: 4) +# nr_pusch_max_its: Maximum number of LDPC iterations for NR (Default 10) +# pusch_8bit_decoder: Use 8-bit for LLR representation and turbo decoder trellis computation (experimental) +# nof_phy_threads: Selects the number of PHY threads (maximum: 4, minimum: 1, default: 3) +# metrics_period_secs: Sets the period at which metrics are requested from the eNB # metrics_csv_enable: Write eNB metrics to CSV file. -# metrics_csv_filename: File path to use for CSV metrics. -# tracing_enable: Write source code tracing information to a file. -# tracing_filename: File path to use for tracing information. -# tracing_buffcapacity: Maximum capacity in bytes the tracing framework can store. -# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# metrics_csv_filename: File path to use for CSV metrics +# report_json_enable: Write eNB report to JSON file (default: disabled) +# report_json_filename: Report JSON filename (default: /tmp/enb_report.json) +# report_json_asn1_oct: Prints ASN1 messages encoded as an octet string instead of plain text in the JSON report file +# alarms_log_enable: Enable Alarms logging (default: disabled) +# alarms_filename: Alarms logging filename (default: /tmp/alarms.log) +# tracing_enable: Write source code tracing information to a file +# tracing_filename: File path to use for tracing information +# tracing_buffcapacity: Maximum capacity in bytes the tracing framework can store +# stdout_ts_enable: Prints once per second the timestamp into stdout # tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) -# rrc_inactivity_timer Inactivity timeout used to remove UE context from RRC (in milliseconds). +# rrc_inactivity_timer Inactivity timeout used to remove UE context from RRC (in milliseconds) +# max_mac_dl_kos: Maximum number of consecutive KOs in DL before triggering the UE's release (default: 100) +# max_mac_ul_kos: Maximum number of consecutive KOs in UL before triggering the UE's release (default: 100) # max_prach_offset_us: Maximum allowed RACH offset (in us) -# nof_prealloc_ues: Number of UE memory resources to preallocate during eNB initialization for faster UE creation (Default 8) -# eea_pref_list: Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1). -# eia_pref_list: Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0). -# +# nof_prealloc_ues: Number of UE memory resources to preallocate during eNB initialization for faster UE creation (default: 8) +# rlf_release_timer_ms: Time taken by eNB to release UE context after it detects an RLF +# eea_pref_list: Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1) +# eia_pref_list: Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0) +# gtpu_tunnel_timeout: Time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU (0 for no timer) +# ts1_reloc_prep_timeout: S1AP TS 36.413 TS1RelocPrep Expiry Timeout value in milliseconds +# ts1_reloc_overall_timeout: S1AP TS 36.413 TS1RelocOverall Expiry Timeout value in milliseconds +# rlf_release_timer_ms: Time taken by eNB to release UE context after it detects a RLF +# rlf_min_ul_snr_estim: SNR threshold in dB below which the enb is notified with RLF ko +# s1_setup_max_retries: Maximum amount of retries to setup the S1AP connection. If this value is exceeded, an alarm is written to the log. -1 means infinity. +# s1_connect_timer: Connection Retry Timer for S1 connection (seconds) +# rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings ##################################################################### [expert] #pusch_max_its = 8 # These are half iterations +#nr_pusch_max_its = 10 #pusch_8bit_decoder = false #nof_phy_threads = 3 #metrics_period_secs = 1 @@ -318,16 +400,30 @@ enable = false #metrics_csv_filename = /tmp/enb_metrics.csv #report_json_enable = true #report_json_filename = /tmp/enb_report.json +#report_json_asn1_oct = false #alarms_log_enable = true #alarms_filename = /tmp/enb_alarms.log #tracing_enable = true #tracing_filename = /tmp/enb_tracing.log #tracing_buffcapacity = 1000000 -#pregenerate_signals = false +#stdout_ts_enable = false #tx_amplitude = 0.6 #rrc_inactivity_timer = 30000 -#max_nof_kos = 100 +#max_mac_dl_kos = 100 +#max_mac_ul_kos = 100 #max_prach_offset_us = 30 #nof_prealloc_ues = 8 +#rlf_release_timer_ms = 4000 +#lcid_padding = 3 #eea_pref_list = EEA0, EEA2, EEA1 #eia_pref_list = EIA2, EIA1, EIA0 +#gtpu_tunnel_timeout = 0 +#extended_cp = false +#ts1_reloc_prep_timeout = 10000 +#ts1_reloc_overall_timeout = 10000 +#rlf_release_timer_ms = 4000 +#rlf_min_ul_snr_estim = -2 +#s1_setup_max_retries = -1 +#s1_connect_timer = 10 +#rx_gain_offset = 62 +#mac_prach_bi = 0 diff --git a/srsenb/hdr/cfg_parser.h b/srsenb/hdr/cfg_parser.h index 845ca5a9e..bb9e4890a 100644 --- a/srsenb/hdr/cfg_parser.h +++ b/srsenb/hdr/cfg_parser.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/hdr/common/common_enb.h b/srsenb/hdr/common/common_enb.h index 431719dda..83bab1f6c 100644 --- a/srsenb/hdr/common/common_enb.h +++ b/srsenb/hdr/common/common_enb.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/hdr/common/rnti_pool.h b/srsenb/hdr/common/rnti_pool.h index 6332b226b..c0c888744 100644 --- a/srsenb/hdr/common/rnti_pool.h +++ b/srsenb/hdr/common/rnti_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index 802922990..546682a77 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -40,6 +40,8 @@ #include "srsenb/hdr/stack/enb_stack_base.h" #include "srsenb/hdr/stack/rrc/rrc_config.h" +#include "srsenb/hdr/stack/mac/sched_interface.h" +#include "srsgnb/hdr/stack/gnb_stack_nr.h" #include "srsran/common/bcd_helpers.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/interfaces_common.h" @@ -47,7 +49,8 @@ #include "srsran/common/security.h" #include "srsran/interfaces/enb_command_interface.h" #include "srsran/interfaces/enb_metrics_interface.h" -#include "srsran/interfaces/sched_interface.h" +#include "srsran/interfaces/enb_time_interface.h" +#include "srsran/interfaces/enb_x2_interfaces.h" #include "srsran/interfaces/ue_interfaces.h" #include "srsran/srslog/srslog.h" #include "srsran/system/sys_metrics_processor.h" @@ -71,7 +74,7 @@ struct enb_args_t { struct enb_files_t { std::string sib_config; std::string rr_config; - std::string drb_config; + std::string rb_config; }; struct log_args_t { @@ -94,6 +97,7 @@ struct general_args_t { std::string metrics_csv_filename; bool report_json_enable; std::string report_json_filename; + bool report_json_asn1_oct; bool alarms_log_enable; std::string alarms_filename; bool print_buffer_state; @@ -104,6 +108,8 @@ struct general_args_t { std::string eea_pref_list; uint32_t max_mac_dl_kos; uint32_t max_mac_ul_kos; + uint32_t gtpu_indirect_tunnel_timeout; + uint32_t rlf_release_timer_ms; }; struct all_args_t { @@ -115,6 +121,7 @@ struct all_args_t { general_args_t general; phy_args_t phy; stack_args_t stack; + gnb_stack_args_t nr_stack; }; struct rrc_cfg_t; @@ -123,7 +130,7 @@ struct rrc_cfg_t; Main eNB class *******************************************************************************/ -class enb : public enb_metrics_interface, enb_command_interface +class enb : public enb_metrics_interface, enb_command_interface, enb_time_interface { public: enb(srslog::sink& log_sink); @@ -144,24 +151,33 @@ public: // eNodeB command interface void cmd_cell_gain(uint32_t cell_id, float gain) override; + void cmd_cell_measure() override; + + void toggle_padding() override; + + void tti_clock() override; + private: const static int ENB_POOL_SIZE = 1024 * 10; - int parse_args(const all_args_t& args_, rrc_cfg_t& rrc_cfg); + int parse_args(const all_args_t& args_, rrc_cfg_t& rrc_cfg_, rrc_nr_cfg_t& rrc_cfg_nr_); srslog::sink& log_sink; srslog::basic_logger& enb_log; - all_args_t args = {}; - bool started = false; + all_args_t args = {}; + std::atomic started = {false}; - phy_cfg_t phy_cfg = {}; - rrc_cfg_t rrc_cfg = {}; + phy_cfg_t phy_cfg = {}; + rrc_cfg_t rrc_cfg = {}; + rrc_nr_cfg_t rrc_nr_cfg = {}; // eNB components - std::unique_ptr stack = nullptr; - std::unique_ptr radio = nullptr; - std::unique_ptr phy = nullptr; + std::unique_ptr x2; + std::unique_ptr eutra_stack = nullptr; + std::unique_ptr nr_stack = nullptr; + std::unique_ptr radio = nullptr; + std::unique_ptr phy = nullptr; // System metrics processor. srsran::sys_metrics_processor sys_proc; diff --git a/srsenb/hdr/metrics_csv.h b/srsenb/hdr/metrics_csv.h index 3b5fa2ec1..6c2f645aa 100644 --- a/srsenb/hdr/metrics_csv.h +++ b/srsenb/hdr/metrics_csv.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -41,11 +41,10 @@ namespace srsenb { class metrics_csv : public srsran::metrics_listener { public: - metrics_csv(std::string filename); + metrics_csv(std::string filename, enb_metrics_interface* enb_); ~metrics_csv(); void set_metrics(const enb_metrics_t& m, const uint32_t period_usec); - void set_handle(enb_metrics_interface* enb_); void stop(); private: diff --git a/srsenb/hdr/metrics_json.h b/srsenb/hdr/metrics_json.h index 5fc2df2e5..8d8411732 100644 --- a/srsenb/hdr/metrics_json.h +++ b/srsenb/hdr/metrics_json.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,10 +35,9 @@ namespace srsenb { class metrics_json : public srsran::metrics_listener { public: - metrics_json(srslog::log_channel& c) : log_c(c) {} + metrics_json(srslog::log_channel& c, enb_metrics_interface* enb_) : log_c(c), enb(enb_) {} void set_metrics(const enb_metrics_t& m, const uint32_t period_usec) override; - void set_handle(enb_metrics_interface* enb_); void stop() override {} private: diff --git a/srsenb/hdr/metrics_stdout.h b/srsenb/hdr/metrics_stdout.h index ed2d35313..24796dff3 100644 --- a/srsenb/hdr/metrics_stdout.h +++ b/srsenb/hdr/metrics_stdout.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -46,13 +46,13 @@ public: void stop(){}; private: + void set_metrics_helper(uint32_t num_ue, const mac_metrics_t& mac, const std::vector& phy, bool is_nr); std::string float_to_string(float f, int digits, int field_width = 6); - std::string int_to_hex_string(int value, int field_width); std::string float_to_eng_string(float f, int digits); - bool do_print; - uint8_t n_reports; - enb_metrics_interface* enb; + std::atomic do_print = {false}; + uint8_t n_reports = 0; + enb_metrics_interface* enb = nullptr; }; } // namespace srsenb diff --git a/srsenb/hdr/parser.h b/srsenb/hdr/parser.h index 82c4c07f9..9b94ac447 100644 --- a/srsenb/hdr/parser.h +++ b/srsenb/hdr/parser.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -475,6 +475,12 @@ int opt_number_to_enum(EnumType& enum_val, bool& presence_flag, Setting& root, c return parse_opt_field(enum_val, root, name, number_to_enum, &presence_flag); } +template +int default_number_to_enum(EnumType& enum_val, Setting& root, const char* name, typename EnumType::options default_val) +{ + return parse_default_field(enum_val, root, name, EnumType(default_val), number_to_enum); +} + } // namespace asn1_parsers } // namespace srsenb diff --git a/srsenb/hdr/phy/enb_phy_base.h b/srsenb/hdr/phy/enb_phy_base.h index 767dfde0b..0a5ba137b 100644 --- a/srsenb/hdr/phy/enb_phy_base.h +++ b/srsenb/hdr/phy/enb_phy_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -47,6 +47,8 @@ public: virtual void get_metrics(std::vector& m) = 0; virtual void cmd_cell_gain(uint32_t cell_idx, float gain_db) = 0; + + virtual void cmd_cell_measure() = 0; }; } // namespace srsenb diff --git a/srsenb/hdr/phy/lte/cc_worker.h b/srsenb/hdr/phy/lte/cc_worker.h index d7b7828cd..cd297c10c 100644 --- a/srsenb/hdr/phy/lte/cc_worker.h +++ b/srsenb/hdr/phy/lte/cc_worker.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -69,7 +69,7 @@ private: int encode_pdsch(stack_interface_phy_lte::dl_sched_grant_t* grants, uint32_t nof_grants); int encode_pmch(stack_interface_phy_lte::dl_sched_grant_t* grant, srsran_mbsfn_cfg_t* mbsfn_cfg); - void decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_grant, + bool decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_grant, srsran_ul_cfg_t& ul_cfg, srsran_pusch_res_t& pusch_res); void decode_pusch(stack_interface_phy_lte::ul_sched_grant_t* grants, uint32_t nof_pusch); @@ -109,7 +109,7 @@ private: void metrics_read(phy_metrics_t* metrics); void metrics_dl(uint32_t mcs); void metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters); - void metrics_ul_pucch(float sinr); + void metrics_ul_pucch(float rssi, float ni, float sinr); uint32_t get_rnti() const { return rnti; } private: diff --git a/srsenb/hdr/phy/lte/sf_worker.h b/srsenb/hdr/phy/lte/sf_worker.h index b9944c7aa..743e9b060 100644 --- a/srsenb/hdr/phy/lte/sf_worker.h +++ b/srsenb/hdr/phy/lte/sf_worker.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -41,7 +41,7 @@ public: void init(phy_common* phy); cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx); - void set_time(uint32_t tti_, uint32_t tx_worker_cnt_, const srsran::rf_timestamp_t& tx_time_); + void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx); int add_rnti(uint16_t rnti, uint32_t cc_idx); void rem_rnti(uint16_t rnti); @@ -68,12 +68,9 @@ private: bool running = false; std::mutex work_mutex; - uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0; - uint32_t t_rx = 0, t_tx_dl = 0, t_tx_ul = 0; - uint32_t tx_worker_cnt = 0; - srsran::rf_timestamp_t tx_time = {}; - - std::vector > cc_workers; + uint32_t tti_rx = 0, tti_tx_dl = 0, tti_tx_ul = 0; + std::vector > cc_workers; + srsran::phy_common_interface::worker_context_t context = {}; srsran_softbuffer_tx_t temp_mbsfn_softbuffer = {}; }; diff --git a/srsenb/hdr/phy/lte/worker_pool.h b/srsenb/hdr/phy/lte/worker_pool.h index f657dd109..eb6df2795 100644 --- a/srsenb/hdr/phy/lte/worker_pool.h +++ b/srsenb/hdr/phy/lte/worker_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/hdr/phy/nr/cc_worker.h b/srsenb/hdr/phy/nr/cc_worker.h deleted file mode 100644 index 835db67a6..000000000 --- a/srsenb/hdr/phy/nr/cc_worker.h +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSENB_NR_CC_WORKER_H -#define SRSENB_NR_CC_WORKER_H - -#include "srsran/interfaces/gnb_interfaces.h" -#include "srsran/interfaces/rrc_nr_interface_types.h" -#include "srsran/phy/enb/enb_dl_nr.h" -#include "srsran/srslog/srslog.h" -#include "srsran/srsran.h" -#include -#include - -namespace srsenb { -namespace nr { - -typedef struct { - uint32_t nof_carriers; - srsran_enb_dl_nr_args_t dl; -} phy_nr_args_t; - -class phy_nr_state -{ -public: - phy_nr_args_t args = {}; - srsran::phy_cfg_nr_t cfg = {}; - - phy_nr_state() - { - args.nof_carriers = 1; - args.dl.nof_max_prb = 100; - args.dl.nof_tx_antennas = 1; - args.dl.pdsch.measure_evm = true; - args.dl.pdsch.measure_time = true; - args.dl.pdsch.sch.disable_simd = false; - } -}; - -class cc_worker -{ -public: - cc_worker(uint32_t cc_idx, srslog::basic_logger& logger, phy_nr_state* phy_state_); - ~cc_worker(); - - bool set_carrier(const srsran_carrier_nr_t* carrier); - void set_tti(uint32_t tti); - - cf_t* get_tx_buffer(uint32_t antenna_idx); - cf_t* get_rx_buffer(uint32_t antenna_idx); - uint32_t get_buffer_len(); - - bool work_dl(const srsran_slot_cfg_t& dl_slot_cfg, stack_interface_phy_nr::dl_sched_t& dl_grants); - -private: - int encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants); - int encode_pdcch_dl(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants); - - srsran_slot_cfg_t dl_slot_cfg = {}; - uint32_t cc_idx = 0; - std::array tx_buffer = {}; - std::array rx_buffer = {}; - uint32_t buffer_sz = 0; - phy_nr_state* phy_state; - srsran_enb_dl_nr_t enb_dl = {}; - srslog::basic_logger& logger; -}; - -} // namespace nr -} // namespace srsenb - -#endif // SRSENB_NR_CC_WORKER_H diff --git a/srsenb/hdr/phy/nr/sf_worker.h b/srsenb/hdr/phy/nr/sf_worker.h deleted file mode 100644 index bcd838134..000000000 --- a/srsenb/hdr/phy/nr/sf_worker.h +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSUE_NR_PHCH_WORKER_H -#define SRSUE_NR_PHCH_WORKER_H - -#include "cc_worker.h" -#include "srsenb/hdr/phy/phy_common.h" -#include "srsran/common/thread_pool.h" -#include "srsran/srslog/srslog.h" - -namespace srsenb { -namespace nr { - -/** - * The sf_worker class handles the PHY processing, UL and DL procedures associated with 1 subframe. - * It contains multiple cc_worker objects, one for each component carrier which may be executed in - * one or multiple threads. - * - * A sf_worker object is executed by a thread within the thread_pool. - */ - -class sf_worker final : public srsran::thread_pool::worker -{ -public: - sf_worker(phy_common* phy_, phy_nr_state* phy_state_, srslog::basic_logger& logger); - ~sf_worker(); - - bool set_carrier_unlocked(uint32_t cc_idx, const srsran_carrier_nr_t* carrier_); - - /* Functions used by main PHY thread */ - cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx); - cf_t* get_buffer_tx(uint32_t cc_idx, uint32_t antenna_idx); - uint32_t get_buffer_len(); - void set_tti(uint32_t tti); - -private: - /* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */ - void work_imp() override; - - std::vector > cc_workers; - - phy_common* phy = nullptr; - phy_nr_state* phy_state = nullptr; - srslog::basic_logger& logger; - - // Temporal attributes - srsran_softbuffer_tx_t softbuffer_tx = {}; - std::vector data; -}; - -} // namespace nr -} // namespace srsenb - -#endif // SRSUE_NR_PHCH_WORKER_H diff --git a/srsenb/hdr/phy/nr/slot_worker.h b/srsenb/hdr/phy/nr/slot_worker.h new file mode 100644 index 000000000..dfa1c9a90 --- /dev/null +++ b/srsenb/hdr/phy/nr/slot_worker.h @@ -0,0 +1,131 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_NR_SLOT_WORKER_H +#define SRSENB_NR_SLOT_WORKER_H + +#include "srsran/common/thread_pool.h" +#include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/interfaces/phy_common_interface.h" +#include "srsran/srslog/srslog.h" +#include "srsran/srsran.h" + +namespace srsenb { +namespace nr { + +/** + * The slot_worker class handles the PHY processing, UL and DL procedures associated with 1 slot. + * + * A slot_worker object is executed by a thread within the thread_pool. + */ + +class slot_worker final : public srsran::thread_pool::worker +{ +public: + /** + * @brief Slot worker synchronization interface + */ + class sync_interface + { + public: + /** + * @brief Wait for the worker to start DL scheduler + * @param w Worker pointer + */ + virtual void wait(slot_worker* w) = 0; + + /** + * @brief Releases the current worker + */ + virtual void release() = 0; + }; + + struct args_t { + uint32_t cell_index = 0; + uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR; + uint32_t nof_tx_ports = 1; + uint32_t nof_rx_ports = 1; + uint32_t rf_port = 0; + srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz; + uint32_t pusch_max_its = 10; + float pusch_min_snr_dB = -10.0f; + double srate_hz = 0.0; + }; + + slot_worker(srsran::phy_common_interface& common_, + stack_interface_phy_nr& stack_, + sync_interface& sync_, + srslog::basic_logger& logger); + ~slot_worker(); + + bool init(const args_t& args); + + bool set_common_cfg(const srsran_carrier_nr_t& carrier, + const srsran_pdcch_cfg_nr_t& pdcch_cfg_, + const srsran_ssb_cfg_t& ssb_cfg_); + + /* Functions used by main PHY thread */ + cf_t* get_buffer_rx(uint32_t antenna_idx); + cf_t* get_buffer_tx(uint32_t antenna_idx); + uint32_t get_buffer_len(); + void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx); + +private: + /** + * @brief Inherited from thread_pool::worker. Function called every slot to run the DL/UL processing + */ + void work_imp() override; + + /** + * @brief Retrieves the scheduling results for the UL processing and performs reception + * @return True if no error occurs, false otherwise + */ + bool work_ul(); + + /** + * @brief Retrieves the scheduling results for the DL processing and performs transmission + * @return True if no error occurs, false otherwise + */ + bool work_dl(); + + srsran::phy_common_interface& common; + stack_interface_phy_nr& stack; + srslog::basic_logger& logger; + sync_interface& sync; + + uint32_t sf_len = 0; + uint32_t cell_index = 0; + uint32_t rf_port = 0; + srsran_slot_cfg_t dl_slot_cfg = {}; + srsran_slot_cfg_t ul_slot_cfg = {}; + srsran::phy_common_interface::worker_context_t context = {}; + srsran_pdcch_cfg_nr_t pdcch_cfg = {}; + srsran_gnb_dl_t gnb_dl = {}; + srsran_gnb_ul_t gnb_ul = {}; + std::vector tx_buffer; ///< Baseband transmit buffers + std::vector rx_buffer; ///< Baseband receive buffers + std::mutex mutex; ///< Protect concurrent access from workers (and main process that inits the class) +}; + +} // namespace nr +} // namespace srsenb + +#endif // SRSENB_NR_PHCH_WORKER_H diff --git a/srsenb/hdr/phy/nr/worker_pool.h b/srsenb/hdr/phy/nr/worker_pool.h index 4d26bfdba..66f2d35bd 100644 --- a/srsenb/hdr/phy/nr/worker_pool.h +++ b/srsenb/hdr/phy/nr/worker_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,33 +19,111 @@ * */ -#ifndef SRSUE_NR_WORKER_POOL_H -#define SRSUE_NR_WORKER_POOL_H +#ifndef SRSENB_NR_WORKER_POOL_H +#define SRSENB_NR_WORKER_POOL_H -#include "sf_worker.h" +#include "slot_worker.h" +#include "srsenb/hdr/phy/phy_interfaces.h" +#include "srsenb/hdr/phy/prach_worker.h" #include "srsran/common/thread_pool.h" +#include "srsran/common/tti_sempahore.h" +#include "srsran/interfaces/enb_mac_interfaces.h" +#include "srsran/interfaces/gnb_interfaces.h" namespace srsenb { namespace nr { -class worker_pool +class worker_pool final : private slot_worker::sync_interface { - srsran::thread_pool pool; - std::vector > workers; - phy_nr_state phy_state; +private: + srsran::tti_semaphore slot_sync; ///< Slot synchronization semaphore + void wait(slot_worker* w) override { slot_sync.wait(w); } + void release() override { slot_sync.release(); } + + class prach_stack_adaptor_t : public stack_interface_phy_lte + { + private: + stack_interface_phy_nr& stack; + + public: + prach_stack_adaptor_t(stack_interface_phy_nr& stack_) : stack(stack_) + { + // Do nothing + } + + int sr_detected(uint32_t tti, uint16_t rnti) override { return 0; } + void rach_detected(uint32_t tti, uint32_t primary_cc_idx, uint32_t preamble_idx, uint32_t time_adv) override + { + stack_interface_phy_nr::rach_info_t rach_info = {}; + rach_info.slot_index = tti; + rach_info.preamble = preamble_idx; + rach_info.time_adv = time_adv; + + stack.rach_detected(rach_info); + } + int ri_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t ri_value) override { return 0; } + int pmi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t pmi_value) override { return 0; } + int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override { return 0; } + int sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t sb_idx, uint32_t cqi_value) override + { + return 0; + } + int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) override { return 0; } + int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override { return 0; } + int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) override { return 0; } + int crc_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) override { return 0; } + int push_pdu(uint32_t tti_rx, + uint16_t rnti, + uint32_t enb_cc_idx, + uint32_t nof_bytes, + bool crc_res, + uint32_t ul_nof_prbs) override + { + return 0; + } + int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override { return 0; } + int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) override { return 0; } + int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override { return 0; } + void set_sched_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) override {} + }; + + srsran::phy_common_interface& common; + stack_interface_phy_nr& stack; + srslog::sink& log_sink; + srsran::thread_pool pool; + std::vector > workers; + prach_worker_pool prach; + uint32_t current_tti = 0; ///< Current TTI, read and write from same thread + srslog::basic_logger& logger; + prach_stack_adaptor_t prach_stack_adaptor; + uint32_t nof_prach_workers = 0; + double srate_hz = 0.0; ///< Current sampling rate in Hz public: - sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } + struct args_t { + double srate_hz = 0.0; + uint32_t nof_phy_threads = 3; + uint32_t nof_prach_workers = 0; + uint32_t prio = 52; + uint32_t pusch_max_its = 10; + float pusch_min_snr_dB = -10; + srsran::phy_log_args_t log = {}; + }; + slot_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } - worker_pool(uint32_t max_workers); - bool init(const phy_args_t& args, phy_common* common, srslog::sink& log_sink, int prio); - sf_worker* wait_worker(uint32_t tti); - sf_worker* wait_worker_id(uint32_t id); - void start_worker(sf_worker* w); - void stop(); + worker_pool(srsran::phy_common_interface& common, + stack_interface_phy_nr& stack, + srslog::sink& log_sink, + uint32_t max_workers); + bool init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_list); + slot_worker* wait_worker(uint32_t tti); + slot_worker* wait_worker_id(uint32_t id); + void start_worker(slot_worker* w); + void stop(); + int set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg); }; } // namespace nr } // namespace srsenb -#endif // SRSUE_NR_WORKER_POOL_H +#endif // SRSENB_NR_WORKER_POOL_H diff --git a/srsenb/hdr/phy/phy.h b/srsenb/hdr/phy/phy.h index 35d21a45b..bad9693f5 100644 --- a/srsenb/hdr/phy/phy.h +++ b/srsenb/hdr/phy/phy.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,6 +27,7 @@ #include "srsenb/hdr/phy/enb_phy_base.h" #include "srsran/common/trace.h" #include "srsran/interfaces/enb_metrics_interface.h" +#include "srsran/interfaces/enb_time_interface.h" #include "srsran/interfaces/radio_interfaces.h" #include "srsran/radio/radio.h" #include "srsran/srslog/srslog.h" @@ -34,7 +35,10 @@ namespace srsenb { -class phy final : public enb_phy_base, public phy_interface_stack_lte, public srsran::phy_interface_radio +class phy final : public enb_phy_base, + public phy_interface_stack_lte, + public phy_interface_stack_nr, + public srsran::phy_interface_radio { public: phy(srslog::sink& log_sink); @@ -43,7 +47,14 @@ public: int init(const phy_args_t& args, const phy_cfg_t& cfg, srsran::radio_interface_phy* radio_, - stack_interface_phy_lte* stack_); + stack_interface_phy_lte* stack_lte_, + stack_interface_phy_nr& stack_nr_, + enb_time_interface* enb_); + int init(const phy_args_t& args, + const phy_cfg_t& cfg, + srsran::radio_interface_phy* radio_, + stack_interface_phy_lte* stack_, + enb_time_interface* enb_); void stop() override; std::string get_type() override { return "lte"; }; @@ -64,12 +75,15 @@ public: void get_metrics(std::vector& metrics) override; void cmd_cell_gain(uint32_t cell_id, float gain_db) override; + void cmd_cell_measure() override; void radio_overflow() override{}; void radio_failure() override{}; void srsran_phy_logger(phy_logger_level_t log_level, char* str); + int set_common_cfg(const common_cfg_t& common_cfg) override; + private: srsran::phy_cfg_mbsfn_t mbsfn_config = {}; uint32_t nof_workers = 0; @@ -86,17 +100,24 @@ private: srslog::basic_logger& phy_log; srslog::basic_logger& phy_lib_log; - lte::worker_pool lte_workers; - nr::worker_pool nr_workers; - phy_common workers_common; - prach_worker_pool prach; - txrx tx_rx; + lte::worker_pool lte_workers; + std::unique_ptr nr_workers; + phy_common workers_common; + prach_worker_pool prach; + txrx tx_rx; bool initialized = false; - srsran_prach_cfg_t prach_cfg = {}; + srsran_prach_cfg_t prach_cfg = {}; + common_cfg_t common_cfg = {}; void parse_common_config(const phy_cfg_t& cfg); + int init_lte(const phy_args_t& args, + const phy_cfg_t& cfg, + srsran::radio_interface_phy* radio_, + stack_interface_phy_lte* stack_, + enb_time_interface* enb_); + int init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_phy_nr& stack); }; } // namespace srsenb diff --git a/srsenb/hdr/phy/phy_common.h b/srsenb/hdr/phy/phy_common.h index 8b0ae1d04..c6f5fc1cd 100644 --- a/srsenb/hdr/phy/phy_common.h +++ b/srsenb/hdr/phy/phy_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -30,6 +30,7 @@ #include "srsran/common/thread_pool.h" #include "srsran/common/threads.h" #include "srsran/interfaces/enb_metrics_interface.h" +#include "srsran/interfaces/phy_common_interface.h" #include "srsran/interfaces/radio_interfaces.h" #include "srsran/phy/channel/channel.h" #include "srsran/radio/radio.h" @@ -40,7 +41,7 @@ namespace srsenb { -class phy_common +class phy_common : public srsran::phy_common_interface { public: phy_common() = default; @@ -65,34 +66,47 @@ public: * @param tx_time timestamp to transmit samples * @param is_nr flag is true if it is called from NR */ - void worker_end(void* tx_sem_id, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr = false); + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; // Common objects phy_args_t params = {}; - uint32_t get_nof_carriers_lte() { return static_cast(cell_list_lte.size()); }; - uint32_t get_nof_carriers_nr() { return static_cast(cell_list_nr.size()); }; - uint32_t get_nof_carriers() { return static_cast(cell_list_lte.size() + cell_list_nr.size()); }; + uint32_t get_nof_carriers_lte() { return static_cast(cell_list_lte.size()); } + uint32_t get_nof_carriers_nr() { return static_cast(cell_list_nr.size()); } + uint32_t get_nof_carriers() { return static_cast(cell_list_lte.size() + cell_list_nr.size()); } uint32_t get_nof_prb(uint32_t cc_idx) { uint32_t ret = 0; - if (cc_idx < cell_list_lte.size()) { - ret = cell_list_lte[cc_idx].cell.nof_prb; + if (cc_idx >= get_nof_carriers()) { + // invalid CC index + return ret; } + if (cc_idx < cell_list_lte.size()) { + ret = cell_list_lte[cc_idx].cell.nof_prb; + } else if (cc_idx >= cell_list_lte.size()) { + // offset CC index by all LTE carriers + cc_idx -= cell_list_lte.size(); + if (cc_idx < cell_list_nr.size()) { + ret = cell_list_nr[cc_idx].carrier.nof_prb; + } + } return ret; - }; + } uint32_t get_nof_ports(uint32_t cc_idx) { uint32_t ret = 0; if (cc_idx < cell_list_lte.size()) { ret = cell_list_lte[cc_idx].cell.nof_ports; + } else if ((cc_idx == 0 || cc_idx == 1) && !cell_list_nr.empty()) { + // one RF port for basic NSA/SA config + ret = 1; } return ret; - }; + } uint32_t get_nof_rf_channels() { uint32_t count = 0; @@ -117,11 +131,11 @@ public: cc_idx -= cell_list_lte.size(); if (cc_idx < cell_list_nr.size()) { - ret = cell_list_nr[cc_idx].ul_freq_hz; + ret = cell_list_nr[cc_idx].carrier.ul_center_frequency_hz; } return ret; - }; + } double get_dl_freq_hz(uint32_t cc_idx) { double ret = 0.0; @@ -132,11 +146,26 @@ public: cc_idx -= cell_list_lte.size(); if (cc_idx < cell_list_nr.size()) { - ret = cell_list_nr[cc_idx].dl_freq_hz; + ret = cell_list_nr[cc_idx].carrier.dl_center_frequency_hz; } return ret; - }; + } + double get_ssb_freq_hz(uint32_t cc_idx) + { + double ret = 0.0; + + if (cc_idx < cell_list_lte.size()) { + ret = cell_list_lte[cc_idx].dl_freq_hz; + } + + cc_idx -= cell_list_lte.size(); + if (cc_idx < cell_list_nr.size()) { + ret = cell_list_nr[cc_idx].carrier.ssb_center_freq_hz; + } + + return ret; + } uint32_t get_rf_port(uint32_t cc_idx) { uint32_t ret = 0; @@ -151,7 +180,7 @@ public: } return ret; - }; + } srsran_cell_t get_cell(uint32_t cc_idx) { srsran_cell_t c = {}; @@ -159,16 +188,46 @@ public: c = cell_list_lte[cc_idx].cell; } return c; - }; - srsran_carrier_nr_t get_cell_nr(uint32_t cc_idx) + } + + void set_cell_measure_trigger() { - srsran_carrier_nr_t c = {}; - if (cc_idx < cell_list_nr.size()) { - c = cell_list_nr[cc_idx].carrier; + // Trigger on LTE cell + for (auto it_lte = cell_list_lte.begin(); it_lte != cell_list_lte.end(); ++it_lte) { + it_lte->dl_measure = true; } - return c; - }; + // Trigger on NR cell + for (auto it_nr = cell_list_nr.begin(); it_nr != cell_list_nr.end(); ++it_nr) { + it_nr->dl_measure = true; + } + } + + bool get_cell_measure_trigger(uint32_t cc_idx) + { + if (cc_idx < cell_list_lte.size()) { + return cell_list_lte.at(cc_idx).dl_measure; + } + + cc_idx -= cell_list_lte.size(); + if (cc_idx < cell_list_nr.size()) { + return cell_list_nr.at(cc_idx).dl_measure; + } + + return false; + } + + void clear_cell_measure_trigger(uint32_t cc_idx) + { + if (cc_idx < cell_list_lte.size()) { + cell_list_lte.at(cc_idx).dl_measure = false; + } + + cc_idx -= cell_list_lte.size(); + if (cc_idx < cell_list_nr.size()) { + cell_list_nr.at(cc_idx).dl_measure = false; + } + } void set_cell_gain(uint32_t cell_id, float gain_db) { @@ -178,6 +237,7 @@ public: // Check if the lte cell was found; if (it_lte != cell_list_lte.end()) { + std::lock_guard lock(cell_gain_mutex); it_lte->gain_db = gain_db; return; } @@ -188,6 +248,7 @@ public: // Check if the nr cell was found; if (it_nr != cell_list_nr.end()) { + std::lock_guard lock(cell_gain_mutex); it_nr->gain_db = gain_db; return; } @@ -197,6 +258,7 @@ public: float get_cell_gain(uint32_t cc_idx) { + std::lock_guard lock(cell_gain_mutex); if (cc_idx < cell_list_lte.size()) { return cell_list_lte.at(cc_idx).gain_db; } @@ -209,6 +271,11 @@ public: return 0.0f; } + // Common CFR configuration + srsran_cfr_cfg_t cfr_config = {}; + void set_cfr_config(srsran_cfr_cfg_t cfr_cfg) { cfr_config = cfr_cfg; } + srsran_cfr_cfg_t get_cfr_config() { return cfr_config; } + // Common Physical Uplink DMRS configuration srsran_refsignal_dmrs_pusch_cfg_t dmrs_pusch_cfg = {}; @@ -228,7 +295,7 @@ public: void set_mch_period_stop(uint32_t stop); // Getters and setters for ul grants which need to be shared between workers - const stack_interface_phy_lte::ul_sched_list_t& get_ul_grants(uint32_t tti); + const stack_interface_phy_lte::ul_sched_list_t get_ul_grants(uint32_t tti); void set_ul_grants(uint32_t tti, const stack_interface_phy_lte::ul_sched_list_t& ul_grants); void clear_grants(uint16_t rnti); @@ -239,20 +306,20 @@ private: phy_cell_cfg_list_t cell_list_lte; phy_cell_cfg_list_nr_t cell_list_nr; + std::mutex cell_gain_mutex; bool have_mtch_stop = false; - pthread_mutex_t mtch_mutex = {}; - pthread_cond_t mtch_cvar = {}; + std::mutex mtch_mutex; + std::condition_variable mtch_cvar; srsran::phy_cfg_mbsfn_t mbsfn = {}; bool sib13_configured = false; bool mcch_configured = false; uint8_t mch_table[40] = {}; uint8_t mcch_table[10] = {}; uint32_t mch_period_stop = 0; + srsran::rf_buffer_t tx_buffer = {}; bool is_mch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti); bool is_mcch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti); - srsran::rf_buffer_t nr_tx_buffer; - bool nr_tx_buffer_ready = false; }; } // namespace srsenb diff --git a/srsenb/hdr/phy/phy_interfaces.h b/srsenb/hdr/phy/phy_interfaces.h index 9d7d25228..1f8be02ab 100644 --- a/srsenb/hdr/phy/phy_interfaces.h +++ b/srsenb/hdr/phy/phy_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,10 +22,11 @@ #ifndef SRSENB_PHY_INTERFACES_H_ #define SRSENB_PHY_INTERFACES_H_ +#include "srsgnb/hdr/phy/phy_nr_interfaces.h" #include "srsran/asn1/rrc/rr_common.h" #include "srsran/common/interfaces_common.h" #include "srsran/phy/channel/channel.h" -#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/srsran.h" #include #include @@ -40,43 +41,42 @@ struct phy_cell_cfg_t { uint32_t root_seq_idx; uint32_t num_ra_preambles; float gain_db; + bool dl_measure; }; -struct phy_cell_cfg_nr_t { - srsran_carrier_nr_t carrier; - uint32_t rf_port; - uint32_t cell_id; - double dl_freq_hz; - double ul_freq_hz; - uint32_t root_seq_idx; - uint32_t num_ra_preambles; - float gain_db; -}; +typedef std::vector phy_cell_cfg_list_t; -typedef std::vector phy_cell_cfg_list_t; -typedef std::vector phy_cell_cfg_list_nr_t; +struct cfr_args_t { + bool enable = false; + srsran_cfr_mode_t mode = SRSRAN_CFR_THR_MANUAL; + float manual_thres = 0.5f; + float strength = 1.0f; + float auto_target_papr = 8.0f; + float ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB; +}; struct phy_args_t { std::string type; srsran::phy_log_args_t log; - float max_prach_offset_us = 10; - int pusch_max_its = 10; - bool pusch_8bit_decoder = false; - float tx_amplitude = 1.0f; - uint32_t nof_phy_threads = 1; - std::string equalizer_mode = "mmse"; - float estimator_fil_w = 1.0f; - bool pusch_meas_epre = true; - bool pusch_meas_evm = false; - bool pusch_meas_ta = true; - bool pucch_meas_ta = true; - uint32_t nof_prach_threads = 1; - + float rx_gain_offset = 62; + float max_prach_offset_us = 10; + uint32_t pusch_max_its = 10; + uint32_t nr_pusch_max_its = 10; + bool pusch_8bit_decoder = false; + float tx_amplitude = 1.0f; + uint32_t nof_phy_threads = 1; + std::string equalizer_mode = "mmse"; + float estimator_fil_w = 1.0f; + bool pusch_meas_epre = true; + bool pusch_meas_evm = false; + bool pusch_meas_ta = true; + bool pucch_meas_ta = true; + uint32_t nof_prach_threads = 1; + bool extended_cp = false; srsran::channel::args_t dl_channel_args; srsran::channel::args_t ul_channel_args; - - srsran::vnf_args_t vnf_args; + cfr_args_t cfr_args; }; struct phy_cfg_t { @@ -90,6 +90,8 @@ struct phy_cfg_t { asn1::rrc::pusch_cfg_common_s pusch_cnfg; asn1::rrc::pucch_cfg_common_s pucch_cnfg; asn1::rrc::srs_ul_cfg_common_c srs_ul_cnfg; + + srsran_cfr_cfg_t cfr_config; }; } // namespace srsenb diff --git a/srsenb/hdr/phy/phy_metrics.h b/srsenb/hdr/phy/phy_metrics.h index 8307f55bb..ec4db076f 100644 --- a/srsenb/hdr/phy/phy_metrics.h +++ b/srsenb/hdr/phy/phy_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,24 +22,30 @@ #ifndef SRSENB_PHY_METRICS_H #define SRSENB_PHY_METRICS_H +#include + namespace srsenb { // PHY metrics per user struct ul_metrics_t { - float n; - float pusch_sinr; - float pucch_sinr; - float rssi; - float turbo_iters; - float mcs; - int n_samples; - int n_samples_pucch; + float n; + float pusch_sinr; + float pusch_rssi; + int64_t pusch_tpc; + float pucch_sinr; + float pucch_rssi; + float pucch_ni; + float turbo_iters; + float mcs; + int n_samples; + int n_samples_pucch; }; struct dl_metrics_t { float mcs; - int n_samples; + int64_t pucch_tpc; + int n_samples; }; struct phy_metrics_t { diff --git a/srsenb/hdr/phy/phy_ue_db.h b/srsenb/hdr/phy/phy_ue_db.h index bae1f9942..7d629637a 100644 --- a/srsenb/hdr/phy/phy_ue_db.h +++ b/srsenb/hdr/phy/phy_ue_db.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -392,6 +392,15 @@ public: const srsran_uci_cfg_t& uci_cfg, const srsran_uci_value_t& uci_value); + static void send_cqi_data(uint32_t tti, + uint16_t rnti, + uint32_t cqi_cc_idx, + const srsran_cqi_cfg_t& cqi_cfg, + const srsran_cqi_value_t& cqi_value, + const srsran_cqi_report_cfg_t& cqi_report_cfg, + const srsran_cell_t& cell, + stack_interface_phy_lte* stack); + /** * Set the latest UL Transport Block resource allocation for a given RNTI, eNb cell/carrier and UL HARQ process * identifier. diff --git a/srsenb/hdr/phy/prach_worker.h b/srsenb/hdr/phy/prach_worker.h index 6b302686c..2e1012851 100644 --- a/srsenb/hdr/phy/prach_worker.h +++ b/srsenb/hdr/phy/prach_worker.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/hdr/phy/txrx.h b/srsenb/hdr/phy/txrx.h index 716c9be63..acdba471a 100644 --- a/srsenb/hdr/phy/txrx.h +++ b/srsenb/hdr/phy/txrx.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,6 +27,7 @@ #include "srsenb/hdr/phy/lte/worker_pool.h" #include "srsenb/hdr/phy/nr/worker_pool.h" #include "srsran/config.h" +#include "srsran/interfaces/enb_time_interface.h" #include "srsran/phy/channel/channel.h" #include "srsran/radio/radio.h" #include @@ -37,19 +38,19 @@ class txrx final : public srsran::thread { public: txrx(srslog::basic_logger& logger); - bool init(stack_interface_phy_lte* stack_, + bool init(enb_time_interface* enb_, srsran::radio_interface_phy* radio_handler, lte::worker_pool* lte_workers_, - nr::worker_pool* nr_workers_, phy_common* worker_com, prach_worker_pool* prach_, uint32_t prio); + bool set_nr_workers(nr::worker_pool* nr_workers_); void stop(); private: void run_thread() override; - stack_interface_phy_lte* stack = nullptr; + enb_time_interface* enb = nullptr; srsran::radio_interface_phy* radio_h = nullptr; srslog::basic_logger& logger; lte::worker_pool* lte_workers = nullptr; @@ -61,8 +62,6 @@ private: // Main system TTI counter uint32_t tti = 0; - uint32_t tx_worker_cnt = 0; - uint32_t nof_workers = 0; std::atomic running; }; diff --git a/srsenb/hdr/phy/vnf_phy_nr.h b/srsenb/hdr/phy/vnf_phy_nr.h deleted file mode 100644 index a4895fe36..000000000 --- a/srsenb/hdr/phy/vnf_phy_nr.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSGNB_NR_VNF_PHY_H -#define SRSGNB_NR_VNF_PHY_H - -#include "srsenb/hdr/phy/enb_phy_base.h" -#include "srsenb/hdr/phy/phy_common.h" -#include "srsran/common/basic_vnf.h" -#include "srsran/interfaces/enb_metrics_interface.h" -#include "srsran/interfaces/gnb_interfaces.h" -#include "srsran/interfaces/radio_interfaces.h" - -namespace srsenb { - -struct nr_phy_cfg_t { - // TODO: add cell and RRC configs -}; - -class vnf_phy_nr : public srsenb::enb_phy_base, public srsenb::phy_interface_stack_nr -{ -public: - vnf_phy_nr() = default; - ~vnf_phy_nr(); - - int init(const srsenb::phy_args_t& args, const nr_phy_cfg_t& cfg, srsenb::stack_interface_phy_nr* stack_); - void stop() override; - - std::string get_type() override { return "vnf"; }; - - void start_plot() override; - - void get_metrics(std::vector& metrics) override; - - // MAC interface - int dl_config_request(const dl_config_request_t& request) override; - int tx_request(const tx_request_t& request) override; - - void cmd_cell_gain(uint32_t cell_idx, float gain_db) override - { - // Do nothing - } - -private: - std::unique_ptr vnf = nullptr; - - bool initialized = false; - - void parse_config(const nr_phy_cfg_t& cfg); -}; - -} // namespace srsenb - -#endif // SRSGNB_NR_VNF_PHY_H diff --git a/srsenb/hdr/stack/enb_stack_base.h b/srsenb/hdr/stack/enb_stack_base.h index 4d3164b84..31752bfa8 100644 --- a/srsenb/hdr/stack/enb_stack_base.h +++ b/srsenb/hdr/stack/enb_stack_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -68,17 +68,9 @@ typedef struct { int stack_hex_limit; } stack_log_args_t; -// Expert arguments to create GW without core NW typedef struct { - std::string ip_addr; - srsue::gw_args_t gw_args; - uint8_t drb_lcid; - uint16_t rnti; -} core_less_args_t; - -typedef struct { - std::string type; uint32_t sync_queue_size; // Max allowed difference between PHY and Stack clocks (in TTI) + uint32_t gtpu_indirect_tunnel_timeout_msec; mac_args_t mac; s1ap_args_t s1ap; pcap_args_t mac_pcap; @@ -86,7 +78,6 @@ typedef struct { pcap_args_t s1ap_pcap; stack_log_args_t log; embms_args_t embms; - core_less_args_t coreless; } stack_args_t; struct stack_metrics_t; @@ -100,8 +91,11 @@ public: virtual void stop() = 0; + virtual void toggle_padding() = 0; // eNB metrics interface virtual bool get_metrics(stack_metrics_t* metrics) = 0; + + virtual void tti_clock() = 0; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/enb_stack_lte.h b/srsenb/hdr/stack/enb_stack_lte.h index f7bd635e3..c4dd2bb06 100644 --- a/srsenb/hdr/stack/enb_stack_lte.h +++ b/srsenb/hdr/stack/enb_stack_lte.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,29 +29,34 @@ #include "mac/mac.h" #include "rrc/rrc.h" +#include "s1ap/s1ap.h" #include "srsran/common/task_scheduler.h" #include "upper/gtpu.h" #include "upper/pdcp.h" #include "upper/rlc.h" -#include "upper/s1ap.h" #include "enb_stack_base.h" +#include "srsran/common/bearer_manager.h" #include "srsran/common/mac_pcap_net.h" #include "srsran/interfaces/enb_interfaces.h" #include "srsran/srslog/srslog.h" namespace srsenb { -class enb_stack_lte final : public enb_stack_base, public stack_interface_phy_lte, public srsran::thread +class gtpu_pdcp_adapter; + +class enb_stack_lte final : public enb_stack_base, + public stack_interface_phy_lte, + public rrc_eutra_interface_rrc_nr, + public srsran::thread { public: enb_stack_lte(srslog::sink& log_sink); ~enb_stack_lte() final; // eNB stack base interface - int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_, phy_interface_stack_lte* phy_); - int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_); - void stop() final; + int init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_, phy_interface_stack_lte* phy_, x2_interface* x2_); + void stop() final; std::string get_type() final; bool get_metrics(stack_metrics_t* metrics) final; @@ -73,6 +78,10 @@ public: { return mac.cqi_info(tti, rnti, cc_idx, cqi_value); } + int sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t sb_idx, uint32_t cqi_value) final + { + return mac.sb_cqi_info(tti, rnti, cc_idx, sb_idx, cqi_value); + } int snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) final { return mac.snr_info(tti_rx, rnti, cc_idx, snr_db, ch); @@ -86,9 +95,14 @@ public: { return mac.crc_info(tti, rnti, enb_cc_idx, nof_bytes, crc_res); } - int push_pdu(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) final + int push_pdu(uint32_t tti, + uint16_t rnti, + uint32_t enb_cc_idx, + uint32_t nof_bytes, + bool crc_res, + uint32_t grant_nof_prbs) final { - return mac.push_pdu(tti, rnti, enb_cc_idx, nof_bytes, crc_res); + return mac.push_pdu(tti, rnti, enb_cc_idx, nof_bytes, crc_res, grant_nof_prbs); } int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) final { return mac.get_dl_sched(tti, dl_sched_res); } int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) final @@ -100,8 +114,39 @@ public: { mac.set_sched_dl_tti_mask(tti_mask, nof_sfs); } + void toggle_padding() override { mac.toggle_padding(); } void tti_clock() override; + // rrc_eutra_interface_rrc_nr + void sgnb_addition_ack(uint16_t eutra_rnti, sgnb_addition_ack_params_t params) final + { + x2_task_queue.push([this, eutra_rnti, params]() { rrc.sgnb_addition_ack(eutra_rnti, params); }); + } + void sgnb_addition_reject(uint16_t eutra_rnti) final + { + x2_task_queue.push([this, eutra_rnti]() { rrc.sgnb_addition_reject(eutra_rnti); }); + } + void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) final + { + x2_task_queue.push([this, eutra_rnti, nr_rnti]() { rrc.sgnb_addition_complete(eutra_rnti, nr_rnti); }); + } + void sgnb_inactivity_timeout(uint16_t eutra_rnti) final + { + x2_task_queue.push([this, eutra_rnti]() { rrc.sgnb_inactivity_timeout(eutra_rnti); }); + } + void set_activity_user(uint16_t eutra_rnti) final + { + // Note: RRC processes activity asynchronously, so there is no need to use x2_task_queue + rrc.set_activity_user(eutra_rnti); + } + void sgnb_release_ack(uint16_t eutra_rnti) final + { + x2_task_queue.push([this, eutra_rnti]() { rrc.sgnb_release_ack(eutra_rnti); }); + } + + // gtpu_interface_pdcp + void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu); + private: static const int STACK_MAIN_THREAD_PRIO = 4; // thread loop @@ -113,8 +158,6 @@ private: stack_args_t args = {}; rrc_cfg_t rrc_cfg = {}; - srsran::socket_manager rx_sockets; - srslog::basic_logger& mac_logger; srslog::basic_logger& rlc_logger; srslog::basic_logger& pdcp_logger; @@ -130,7 +173,11 @@ private: // task handling srsran::task_scheduler task_sched; - srsran::task_queue_handle enb_task_queue, gtpu_task_queue, mme_task_queue, sync_task_queue; + srsran::task_queue_handle enb_task_queue, sync_task_queue, metrics_task_queue, x2_task_queue; + + // bearer management + enb_bearer_manager bearers; // helper to manage mapping between EPS and radio bearers + std::unique_ptr gtpu_adapter; srsenb::mac mac; srsenb::rlc rlc; @@ -143,7 +190,7 @@ private: phy_interface_stack_lte* phy = nullptr; // state - bool started = false; + std::atomic started{false}; srsran::dyn_blocking_queue pending_stack_metrics; }; diff --git a/srsenb/hdr/stack/gnb_stack_nr.h b/srsenb/hdr/stack/gnb_stack_nr.h deleted file mode 100644 index c6edb170f..000000000 --- a/srsenb/hdr/stack/gnb_stack_nr.h +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -/****************************************************************************** - * File: gnb_stack_nr.h - * Description: L2/L3 gNB stack class. - *****************************************************************************/ - -#ifndef SRSRAN_GNB_STACK_NR_H -#define SRSRAN_GNB_STACK_NR_H - -#include "srsenb/hdr/stack/mac/mac_nr.h" -#include "srsenb/hdr/stack/rrc/rrc_nr.h" -#include "srsenb/hdr/stack/upper/pdcp_nr.h" -#include "srsenb/hdr/stack/upper/rlc_nr.h" -#include "upper/gtpu.h" -#include "upper/s1ap.h" -#include "upper/sdap.h" - -#include "enb_stack_base.h" -#include "srsenb/hdr/enb.h" -#include "srsran/interfaces/gnb_interfaces.h" - -// This is needed for GW -#include "srsran/interfaces/ue_interfaces.h" -#include "srsue/hdr/stack/upper/gw.h" - -namespace srsenb { - -class gnb_stack_nr final : public srsenb::enb_stack_base, - public stack_interface_phy_nr, - public stack_interface_mac, - public srsue::stack_interface_gw, - public srsran::thread -{ -public: - explicit gnb_stack_nr(); - ~gnb_stack_nr() final; - - int init(const srsenb::stack_args_t& args_, const rrc_nr_cfg_t& rrc_cfg_, phy_interface_stack_nr* phy_); - int init(const srsenb::stack_args_t& args_, const rrc_nr_cfg_t& rrc_cfg_); - - // eNB stack base interface - void stop() final; - std::string get_type() final; - bool get_metrics(srsenb::stack_metrics_t* metrics) final; - - // GW srsue stack_interface_gw dummy interface - bool is_registered() { return true; }; - bool start_service_request() { return true; }; - - // PHY->MAC interface - int sf_indication(const uint32_t tti); - int rx_data_indication(rx_data_ind_t& grant); - - // Temporary GW interface - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu); - bool is_lcid_enabled(uint32_t lcid); - bool switch_on(); - void run_tti(uint32_t tti); - - // MAC interface to trigger processing of received PDUs - void process_pdus() final; - -private: - void run_thread() final; - void run_tti_impl(uint32_t tti); - - // args - srsenb::stack_args_t args = {}; - phy_interface_stack_nr* phy = nullptr; - - srslog::basic_logger& rlc_logger; - - // task scheduling - static const int STACK_MAIN_THREAD_PRIO = 4; - srsran::task_scheduler task_sched; - srsran::task_multiqueue::queue_handle sync_task_queue, ue_task_queue, gw_task_queue, mac_task_queue; - - // derived - std::unique_ptr m_mac; - std::unique_ptr m_rlc; - std::unique_ptr m_pdcp; - std::unique_ptr m_sdap; - std::unique_ptr m_rrc; - std::unique_ptr m_gw; - // std::unique_ptr m_ngap; - // std::unique_ptr m_gtpu; - - // state - bool running = false; - uint32_t current_tti = 10240; -}; - -} // namespace srsenb - -#endif // SRSRAN_GNB_STACK_NR_H diff --git a/srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h b/srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h new file mode 100644 index 000000000..acdbef29c --- /dev/null +++ b/srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h @@ -0,0 +1,113 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_BASE_UE_BUFFER_MANAGER_H +#define SRSRAN_BASE_UE_BUFFER_MANAGER_H + +#include "sched_config.h" +#include "srsran/adt/span.h" +#include "srsran/common/common_lte.h" +#include "srsran/common/common_nr.h" +#include "srsran/srslog/srslog.h" +#include "srsran/support/srsran_assert.h" + +namespace srsenb { + +/** + * Class to handle UE DL+UL RLC and MAC buffers state + */ +template +class base_ue_buffer_manager +{ +protected: + const static uint32_t MAX_LC_ID = isNR ? (srsran::MAX_NR_NOF_BEARERS - 1) : srsran::MAX_LTE_LCID; + const static uint32_t MAX_LCG_ID = isNR ? 7 : 3; // Should import from sched_interface and sched_nr_interface + const static uint32_t MAX_SRB_LC_ID = isNR ? srsran::MAX_NR_SRB_ID : srsran::MAX_LTE_SRB_ID; + const static uint32_t MAX_NOF_LCIDS = MAX_LC_ID + 1; + const static uint32_t MAX_NOF_LCGS = MAX_LCG_ID + 1; + constexpr static uint32_t pbr_infinity = -1; + +public: + explicit base_ue_buffer_manager(uint16_t rnti, srslog::basic_logger& logger_); + + // Bearer configuration + void config_lcids(srsran::const_span bearer_cfg_list); + void config_lcid(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg); + + // Buffer Status update + int ul_bsr(uint32_t lcg_id, uint32_t val); + int dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t prio_tx_queue); + + // Configuration getters + uint16_t get_rnti() const { return rnti; } + bool is_bearer_active(uint32_t lcid) const { return get_cfg(lcid).is_active(); } + bool is_bearer_ul(uint32_t lcid) const { return get_cfg(lcid).is_ul(); } + bool is_bearer_dl(uint32_t lcid) const { return get_cfg(lcid).is_dl(); } + const mac_lc_ch_cfg_t& get_cfg(uint32_t lcid) const + { + srsran_assert(is_lcid_valid(lcid), "Provided LCID=%d is above limit=%d", lcid, MAX_LC_ID); + return channels[lcid].cfg; + } + + /// DL newtx buffer status for given LCID (no RLC overhead included) + int get_dl_tx(uint32_t lcid) const { return is_bearer_dl(lcid) ? channels[lcid].buf_tx : 0; } + + /// DL high prio tx buffer status for given LCID (no RLC overhead included) + int get_dl_prio_tx(uint32_t lcid) const { return is_bearer_dl(lcid) ? channels[lcid].buf_prio_tx : 0; } + + /// Sum of DL RLC newtx and high prio tx buffer status for given LCID (no RLC overhead included) + int get_dl_tx_total(uint32_t lcid) const { return get_dl_tx(lcid) + get_dl_prio_tx(lcid); } + + /// Sum of DL RLC newtx and high prio buffer status for all LCIDS + int get_dl_tx_total() const; + + // UL BSR methods + bool is_lcg_active(uint32_t lcg) const; + int get_bsr(uint32_t lcg) const; + int get_bsr() const; + const std::array& get_bsr_state() const { return lcg_bsr; } + + static bool is_lcid_valid(uint32_t lcid) { return lcid <= MAX_LC_ID; } + static bool is_lcg_valid(uint32_t lcg) { return lcg <= MAX_LCG_ID; } + +protected: + ~base_ue_buffer_manager() = default; + + bool config_lcid_internal(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg); + + srslog::basic_logger& logger; + uint16_t rnti; + + struct logical_channel { + mac_lc_ch_cfg_t cfg; + int buf_tx = 0; + int buf_prio_tx = 0; + int Bj = 0; + int bucket_size = 0; + }; + std::array channels; + + std::array lcg_bsr; +}; + +} // namespace srsenb + +#endif // SRSRAN_BASE_UE_BUFFER_MANAGER_H diff --git a/srsenb/hdr/stack/mac/mac_metrics.h b/srsenb/hdr/stack/mac/common/mac_metrics.h similarity index 74% rename from srsenb/hdr/stack/mac/mac_metrics.h rename to srsenb/hdr/stack/mac/common/mac_metrics.h index f698320d3..cde33695e 100644 --- a/srsenb/hdr/stack/mac/mac_metrics.h +++ b/srsenb/hdr/stack/mac/common/mac_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -30,6 +30,7 @@ namespace srsenb { /// MAC metrics per user struct mac_ue_metrics_t { uint16_t rnti; + uint32_t pci; uint32_t nof_tti; uint32_t cc_idx; int tx_pkts; @@ -44,12 +45,31 @@ struct mac_ue_metrics_t { float dl_ri; float dl_pmi; float phr; + float dl_cqi_offset; + float ul_snr_offset; + + // NR-only UL PHY metrics + float pusch_sinr; + float pucch_sinr; + float ul_rssi; + float fec_iters; + float dl_mcs; + int dl_mcs_samples; + float ul_mcs; + int ul_mcs_samples; +}; +/// MAC misc information for each cc. +struct mac_cc_info_t { + /// PCI value. + uint32_t pci; + /// RACH preamble counter per cc. + uint32_t cc_rach_counter; }; /// Main MAC metrics. struct mac_metrics_t { - /// RACH preamble counter per cc. - std::vector cc_rach_counter; + /// Per CC info. + std::vector cc_info; /// Per UE MAC metrics. std::vector ues; }; diff --git a/srsenb/hdr/stack/mac/common/sched_config.h b/srsenb/hdr/stack/mac/common/sched_config.h new file mode 100644 index 000000000..29ffd0877 --- /dev/null +++ b/srsenb/hdr/stack/mac/common/sched_config.h @@ -0,0 +1,68 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_CONFIG_H +#define SRSRAN_SCHED_CONFIG_H + +#include + +namespace srsenb { + +/** + * Structure used in UE logical channel configuration + */ +struct mac_lc_ch_cfg_t { + enum direction_t { IDLE = 0, UL, DL, BOTH } direction = IDLE; + int priority = 1; // channel priority (1 is highest) + uint32_t bsd = 1000; // msec + uint32_t pbr = -1; // prioritised bit rate + int group = 0; // logical channel group + + bool is_active() const { return direction != IDLE; } + bool is_dl() const { return direction == DL or direction == BOTH; } + bool is_ul() const { return direction == UL or direction == BOTH; } + bool operator==(const mac_lc_ch_cfg_t& other) const + { + return direction == other.direction and priority == other.priority and bsd == other.bsd and pbr == other.pbr and + group == other.group; + } + bool operator!=(const mac_lc_ch_cfg_t& other) const { return not(*this == other); } +}; + +inline const char* to_string(mac_lc_ch_cfg_t::direction_t dir) +{ + switch (dir) { + case mac_lc_ch_cfg_t::direction_t::IDLE: + return "idle"; + case mac_lc_ch_cfg_t::direction_t::BOTH: + return "bi-dir"; + case mac_lc_ch_cfg_t::direction_t::DL: + return "DL"; + case mac_lc_ch_cfg_t::direction_t::UL: + return "UL"; + default: + return "unrecognized direction"; + } +} + +} // namespace srsenb + +#endif // SRSRAN_SCHED_CONFIG_H diff --git a/srsenb/hdr/stack/mac/mac.h b/srsenb/hdr/stack/mac/mac.h index 2bcc0c449..a71cd787b 100644 --- a/srsenb/hdr/stack/mac/mac.h +++ b/srsenb/hdr/stack/mac/mac.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,6 +23,8 @@ #define SRSENB_MAC_H #include "sched.h" +#include "sched_interface.h" +#include "srsenb/hdr/common/rnti_pool.h" #include "srsenb/hdr/stack/mac/schedulers/sched_time_rr.h" #include "srsran/adt/circular_map.h" #include "srsran/adt/pool/batch_mem_pool.h" @@ -34,7 +36,6 @@ #include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_metrics_interface.h" #include "srsran/interfaces/enb_rrc_interface_types.h" -#include "srsran/interfaces/sched_interface.h" #include "srsran/srslog/srslog.h" #include "ta.h" #include "ue.h" @@ -64,11 +65,13 @@ public: int ri_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t ri_value) override; int pmi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t pmi_value) override; int cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi_value) override; + int sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t sb_idx, uint32_t cqi_value) override; int snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr, ul_channel_t ch) override; int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override; int ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) override; int crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) override; - int push_pdu(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res) override; + int push_pdu(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc_res, uint32_t ul_nof_prbs) + override; int get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res) override; int get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res) override; @@ -81,28 +84,30 @@ public: /******** Interface from RRC (RRC -> MAC) ****************/ /* Provides cell configuration including SIB periodicity, etc. */ - int cell_cfg(const std::vector& cell_cfg) override; - void reset() override; + int cell_cfg(const std::vector& cell_cfg) override; /* Manages UE scheduling context */ - int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) override; + int ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t* cfg) override; int ue_rem(uint16_t rnti) override; - int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, sched_interface::ue_cfg_t* cfg) override; + int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, const sched_interface::ue_cfg_t& cfg) override; // Indicates that the PHY config dedicated has been enabled or not void phy_config_enabled(uint16_t rnti, bool enabled) override; /* Manages UE bearers and associated configuration */ - int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) override; + int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, mac_lc_ch_cfg_t* cfg) override; int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) override; int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) override; /* Handover-related */ uint16_t reserve_new_crnti(const sched_interface::ue_cfg_t& ue_cfg) override; - bool process_pdus(); - void get_metrics(mac_metrics_t& metrics); + + void toggle_padding(); + + void add_padding(); + void write_mcch(const srsran::sib2_mbms_t* sib2_, const srsran::sib13_t* sib13_, const srsran::mcch_msg_t* mcch_, @@ -110,13 +115,19 @@ public: const uint8_t mcch_payload_length) override; private: - static const uint32_t cfi = 3; + bool check_ue_active(uint16_t rnti); + uint16_t allocate_ue(uint32_t enb_cc_idx); + bool is_valid_rnti_unprotected(uint16_t rnti); - bool check_ue_exists(uint16_t rnti); - uint16_t allocate_rnti(); - uint16_t allocate_ue(); - - std::mutex rnti_mutex; + /* helper function for PDCCH orders */ + /** + * @brief Checks if the current RACH is a RACH triggered by a PDCCH order. + * + * @param[in] preamble_idx RACH preamble idx + * @param rnti is the rnti where the crnti of the RACH is written + * @return true if this is a RACH triggered by a PDCCH order, otherwise it returns false + */ + bool is_pending_pdcch_order_prach(const uint32_t preamble_idx, uint16_t& rnti); srslog::basic_logger& logger; @@ -145,12 +156,9 @@ private: sched_interface::dl_pdu_mch_t mch = {}; /* Map of active UEs */ - rnti_map_t > ue_db; - std::map > ues_to_rem; - uint16_t last_rnti = 70; - - srsran::static_blocking_queue, 32> ue_pool; ///< Pool of pre-allocated UE objects - void prealloc_ue(uint32_t nof_ue); + static const uint16_t FIRST_RNTI = 0x46; + rnti_map_t > ue_db; + std::atomic ue_counter{0}; uint8_t* assemble_rar(sched_interface::dl_sched_rar_grant_t* grants, uint32_t enb_cc_idx, @@ -166,12 +174,12 @@ private: const static int NOF_BCCH_DLSCH_MSG = sched_interface::MAX_SIBS; const static int pcch_payload_buffer_len = 1024; - typedef struct { + struct common_buffers_t { uint8_t pcch_payload_buffer[pcch_payload_buffer_len] = {}; srsran_softbuffer_tx_t bcch_softbuffer_tx[NOF_BCCH_DLSCH_MSG] = {}; srsran_softbuffer_tx_t pcch_softbuffer_tx = {}; srsran_softbuffer_tx_t rar_softbuffer_tx = {}; - } common_buffers_t; + }; std::vector common_buffers; @@ -185,12 +193,16 @@ private: uint8_t mtch_payload_buffer[mtch_payload_len] = {}; // pointer to MAC PCAP object - srsran::mac_pcap* pcap = nullptr; - srsran::mac_pcap_net* pcap_net = nullptr; + srsran::mac_pcap* pcap = nullptr; + srsran::mac_pcap_net* pcap_net = nullptr; + bool do_padding = false; // Number of rach preambles detected for a cc. std::vector detected_rachs; + // PDCCH order + std::vector pending_po_prachs = {}; + // Softbuffer pool std::unique_ptr > softbuffer_pool; }; diff --git a/srsenb/hdr/stack/mac/mac_nr.h b/srsenb/hdr/stack/mac/mac_nr.h deleted file mode 100644 index 3de27970a..000000000 --- a/srsenb/hdr/stack/mac/mac_nr.h +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSENB_MAC_NR_H -#define SRSENB_MAC_NR_H - -#include "srsran/common/block_queue.h" -#include "srsran/common/mac_pcap.h" -#include "srsran/mac/mac_sch_pdu_nr.h" - -#include "srsenb/hdr/stack/enb_stack_base.h" -#include "srsran/interfaces/enb_metrics_interface.h" -#include "srsran/interfaces/gnb_interfaces.h" - -namespace srsenb { - -struct mac_nr_args_t { - srsenb::pcap_args_t pcap; - - // params for the dummy user - srsenb::sched_interface::sched_args_t sched; - uint16_t rnti; - uint32_t drb_lcid; - - // Add args - std::string log_level; - uint32_t log_hex_limit; - uint32_t tb_size = 64; -}; - -class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_rlc_nr -{ -public: - mac_nr(); - ~mac_nr(); - - int init(const mac_nr_args_t& args_, - phy_interface_stack_nr* phy, - stack_interface_mac* stack_, - rlc_interface_mac_nr* rlc_, - rrc_interface_mac_nr* rrc_); - void stop(); - - void get_metrics(srsenb::mac_metrics_t& metrics); - - // MAC interface for RRC - int cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg); - int read_pdu_bcch_bch(uint8_t* payload); - - // MAC interface for RLC - // TODO: - int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) { return 0; } - - // Interface for PHY - int sf_indication(const uint32_t tti); - int rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& grant); - - void process_pdus(); - -private: - void get_dl_config(const uint32_t tti, - phy_interface_stack_nr::dl_config_request_t& config_request, - phy_interface_stack_nr::tx_request_t& tx_request); - - // PDU processing - int handle_pdu(srsran::unique_byte_buffer_t pdu); - - // Interaction with other components - phy_interface_stack_nr* phy_h = nullptr; - stack_interface_mac* stack_h = nullptr; - rlc_interface_mac_nr* rlc_h = nullptr; - rrc_interface_mac_nr* rrc_h = nullptr; - - std::unique_ptr pcap = nullptr; - mac_nr_args_t args = {}; - srslog::basic_logger& logger; - - bool started = false; - - srsenb::sched_interface::cell_cfg_t cfg = {}; - - // BCH buffers - struct sib_info_t { - uint32_t index; - uint32_t periodicity; - srsran::unique_byte_buffer_t payload; - }; - std::vector bcch_dlsch_payload; - srsran::unique_byte_buffer_t bcch_bch_payload = nullptr; - - // UE-specific buffer - srsran::mac_sch_pdu_nr ue_tx_pdu; - std::vector ue_tx_buffer; - srsran::block_queue - ue_rx_pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc) - - srsran::unique_byte_buffer_t ue_rlc_buffer; - - srsran::mac_sch_pdu_nr ue_rx_pdu; -}; - -} // namespace srsenb - -#endif // SRSENB_MAC_NR_H diff --git a/srsenb/hdr/stack/mac/sched.h b/srsenb/hdr/stack/mac/sched.h index e89b47acd..8b5c7e517 100644 --- a/srsenb/hdr/stack/mac/sched.h +++ b/srsenb/hdr/stack/mac/sched.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,12 +23,12 @@ #define SRSENB_SCHEDULER_H #include "sched_grid.h" +#include "sched_interface.h" #include "sched_ue.h" -#include "srsran/interfaces/sched_interface.h" +#include "srsenb/hdr/common/common_enb.h" #include #include #include -#include namespace srsenb { @@ -56,13 +56,13 @@ public: void phy_config_enabled(uint16_t rnti, bool enabled); - int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const ue_bearer_cfg_t& cfg) final; + int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const mac_lc_ch_cfg_t& cfg) final; int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) final; uint32_t get_ul_buffer(uint16_t rnti) final; uint32_t get_dl_buffer(uint16_t rnti) final; - int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) final; + int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t prio_tx_queue) final; int dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code, uint32_t nof_cmds = 1) final; int dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) final; @@ -70,21 +70,25 @@ public: int dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t ri_value) final; int dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t pmi_value) final; int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi_value) final; + int dl_sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t sb_idx, uint32_t cqi_value) final; int ul_crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, bool crc) final; int ul_sr_info(uint32_t tti, uint16_t rnti) override; int ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) final; - int ul_phr(uint16_t rnti, int phr) final; + int ul_phr(uint16_t rnti, int phr, uint32_t ul_nof_prb) final; int ul_snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code) final; int dl_sched(uint32_t tti, uint32_t enb_cc_idx, dl_sched_res_t& sched_result) final; int ul_sched(uint32_t tti, uint32_t enb_cc_idx, ul_sched_res_t& sched_result) final; + int set_pdcch_order(uint32_t enb_cc_idx, dl_sched_po_info_t pdcch_order_info) final; + /* Custom functions */ - void set_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) final; - std::array get_enb_ue_cc_map(uint16_t rnti) final; - std::array get_scell_activation_mask(uint16_t rnti) final; - int ul_buffer_add(uint16_t rnti, uint32_t lcid, uint32_t bytes) final; + void set_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) final; + std::array get_enb_ue_cc_map(uint16_t rnti) final; + std::array get_enb_ue_activ_cc_map(uint16_t rnti) final; + int ul_buffer_add(uint16_t rnti, uint32_t lcid, uint32_t bytes) final; + int metrics_read(uint16_t rnti, mac_ue_metrics_t& metrics); class carrier_sched; @@ -100,7 +104,7 @@ protected: sched_args_t sched_cfg = {}; std::vector sched_cell_params; - std::map > ue_db; + rnti_map_t > ue_db; // independent schedulers for each carrier std::vector > carrier_schedulers; diff --git a/srsenb/hdr/stack/mac/sched_carrier.h b/srsenb/hdr/stack/mac/sched_carrier.h index 39998d594..a543f8f9c 100644 --- a/srsenb/hdr/stack/mac/sched_carrier.h +++ b/srsenb/hdr/stack/mac/sched_carrier.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,6 +24,7 @@ #include "sched.h" #include "schedulers/sched_base.h" +#include "srsran/adt/pool/cached_alloc.h" #include "srsran/srslog/srslog.h" namespace srsenb { @@ -34,16 +35,17 @@ class ra_sched; class sched::carrier_sched { public: - explicit carrier_sched(rrc_interface_mac* rrc_, - std::map >* ue_db_, - uint32_t enb_cc_idx_, - sched_result_ringbuffer* sched_results_); + explicit carrier_sched(rrc_interface_mac* rrc_, + sched_ue_list* ue_db_, + uint32_t enb_cc_idx_, + sched_result_ringbuffer* sched_results_); ~carrier_sched(); void reset(); void carrier_cfg(const sched_cell_params_t& sched_params_); void set_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs); const cc_sched_result& generate_tti_result(srsran::tti_point tti_rx); int dl_rach_info(dl_sched_rar_info_t rar_info); + int pdcch_order_info(dl_sched_po_info_t pdcch_order_info); // getters const ra_sched* get_ra_sched() const { return ra_sched_ptr.get(); } @@ -57,13 +59,15 @@ private: int alloc_ul_users(sf_sched* tti_sched); //! Get sf_sched for a given TTI sf_sched* get_sf_sched(srsran::tti_point tti_rx); + //! Schedule PDCCH orders + void pdcch_order_sched(sf_sched* tti_sched); // args - const sched_cell_params_t* cc_cfg = nullptr; - srslog::basic_logger& logger; - rrc_interface_mac* rrc = nullptr; - std::map >* ue_db = nullptr; - const uint32_t enb_cc_idx; + const sched_cell_params_t* cc_cfg = nullptr; + srslog::basic_logger& logger; + rrc_interface_mac* rrc = nullptr; + sched_ue_list* ue_db = nullptr; + const uint32_t enb_cc_idx; // Subframe scheduling logic srsran::circular_array sf_scheds; @@ -76,6 +80,11 @@ private: std::unique_ptr bc_sched_ptr; std::unique_ptr ra_sched_ptr; std::unique_ptr sched_algo; + + // pending pdcch orders + std::vector pending_pdcch_orders; + + uint32_t po_aggr_level = 2; }; //! Broadcast (SIB + paging) scheduler @@ -131,9 +140,9 @@ private: const sched_cell_params_t* cc_cfg = nullptr; sched_ue_list* ue_db = nullptr; - std::deque pending_rars; - uint32_t rar_aggr_level = 2; - static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4 + srsran::deque pending_rars; + uint32_t rar_aggr_level = 2; + static const uint32_t PRACH_RAR_OFFSET = 3; // TS 36.321 Sec. 5.1.4 }; } // namespace srsenb diff --git a/srsenb/hdr/stack/mac/sched_common.h b/srsenb/hdr/stack/mac/sched_common.h index 136f29c61..ade102777 100644 --- a/srsenb/hdr/stack/mac/sched_common.h +++ b/srsenb/hdr/stack/mac/sched_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,113 +22,51 @@ #ifndef SRSRAN_SCHED_COMMON_H #define SRSRAN_SCHED_COMMON_H -#include "srsran/adt/bounded_bitset.h" -#include "srsran/common/tti_point.h" -#include "srsran/interfaces/sched_interface.h" +/** + * File used for all functions and types common to the LTE and NR schedulers + */ namespace srsenb { -/*********************** - * Constants - **********************/ - -constexpr float tti_duration_ms = 1; -constexpr uint32_t NOF_AGGR_LEVEL = 4; - -/*********************** - * Helper Types - **********************/ - -/// List of CCE start positions in PDCCH -using cce_position_list = srsran::bounded_vector; - -/// Map {L} -> list of CCE positions -using cce_cfi_position_table = std::array; - -/// Map {cfi, L} -> list of CCE positions -using cce_sf_position_table = std::array, SRSRAN_NOF_CFI>; - -/// Map {sf, cfi, L} -> list of CCE positions -using cce_frame_position_table = std::array; - -/// structs to bundle together all the sched arguments, and share them with all the sched sub-components -class sched_cell_params_t +/// Error code of alloc attempt +enum class alloc_result { + success, + sch_collision, + no_cch_space, + no_sch_space, + no_rnti_opportunity, + invalid_grant_params, + invalid_coderate, + no_grant_space, + other_cause +}; +inline const char* to_string(alloc_result result) { - struct regs_deleter { - void operator()(srsran_regs_t* p); - }; - -public: - bool set_cfg(uint32_t enb_cc_idx_, - const sched_interface::cell_cfg_t& cfg_, - const sched_interface::sched_args_t& sched_args); - // convenience getters - uint32_t nof_prbs_to_rbgs(uint32_t nof_prbs) const { return srsran::ceil_div(nof_prbs, P); } - uint32_t nof_prb() const { return cfg.cell.nof_prb; } - uint32_t get_dl_lb_nof_re(tti_point tti_tx_dl, uint32_t nof_prbs_alloc) const; - uint32_t get_dl_nof_res(srsran::tti_point tti_tx_dl, const srsran_dci_dl_t& dci, uint32_t cfi) const; - - uint32_t enb_cc_idx = 0; - sched_interface::cell_cfg_t cfg = {}; - srsran_pucch_cfg_t pucch_cfg_common = {}; - const sched_interface::sched_args_t* sched_cfg = nullptr; - std::unique_ptr regs; - cce_sf_position_table common_locations = {}; - cce_frame_position_table rar_locations = {}; - std::array nof_cce_table = {}; ///< map cfix -> nof cces in PDCCH - uint32_t P = 0; - uint32_t nof_rbgs = 0; - - using dl_nof_re_table = srsran::bounded_vector< - std::array, SRSRAN_NOF_SLOTS_PER_SF>, SRSRAN_NOF_SF_X_FRAME>, - SRSRAN_MAX_PRB>; - using dl_lb_nof_re_table = std::array, SRSRAN_NOF_SF_X_FRAME>; - - /// Table of nof REs - dl_nof_re_table nof_re_table; - /// Cached computation of Lower bound of nof REs - dl_lb_nof_re_table nof_re_lb_table; -}; - -//! Bitmask used for CCE allocations -using pdcch_mask_t = srsran::bounded_bitset; - -//! Bitmask that stores the allocared DL RBGs -using rbgmask_t = srsran::bounded_bitset<25, true>; - -//! Bitmask that stores the allocated UL PRBs -using prbmask_t = srsran::bounded_bitset<100, true>; - -//! Struct to express a {min,...,max} range of RBGs -struct prb_interval; -struct rbg_interval : public srsran::interval { - using interval::interval; - static rbg_interval rbgmask_to_rbgs(const rbgmask_t& mask); -}; - -/// Struct to express a {min,...,max} range of PRBs -struct prb_interval : public srsran::interval { - using interval::interval; - static prb_interval rbgs_to_prbs(const rbg_interval& rbgs, uint32_t cell_nof_prb); - static prb_interval riv_to_prbs(uint32_t riv, uint32_t nof_prbs, int nof_vrbs = -1); -}; - -/// Type of Allocation stored in PDSCH/PUSCH -enum class alloc_type_t { DL_BC, DL_PCCH, DL_RAR, DL_DATA, UL_DATA }; -inline bool is_dl_ctrl_alloc(alloc_type_t a) -{ - return a == alloc_type_t::DL_BC or a == alloc_type_t::DL_PCCH or a == alloc_type_t::DL_RAR; + switch (result) { + case alloc_result::success: + return "success"; + case alloc_result::sch_collision: + return "Collision with existing SCH allocations"; + case alloc_result::other_cause: + return "error"; + case alloc_result::no_cch_space: + return "No space available in PUCCH or PDCCH"; + case alloc_result::no_sch_space: + return "Requested number of PRBs not available"; + case alloc_result::no_rnti_opportunity: + return "rnti cannot be allocated (e.g. already allocated, no data, meas gap collision, carrier inactive, etc.)"; + case alloc_result::invalid_grant_params: + return "invalid grant arguments (e.g. invalid prb mask)"; + case alloc_result::invalid_coderate: + return "Effective coderate exceeds threshold"; + case alloc_result::no_grant_space: + return "Max number of allocations reached"; + default: + break; + } + return "unknown error"; } } // namespace srsenb -namespace fmt { - -template <> -struct formatter : public formatter > {}; -template <> -struct formatter : public formatter > {}; - -} // namespace fmt - #endif // SRSRAN_SCHED_COMMON_H diff --git a/srsenb/hdr/stack/mac/sched_grid.h b/srsenb/hdr/stack/mac/sched_grid.h index b626c9b41..6c232ede0 100644 --- a/srsenb/hdr/stack/mac/sched_grid.h +++ b/srsenb/hdr/stack/mac/sched_grid.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,7 +22,8 @@ #ifndef SRSRAN_SCHED_GRID_H #define SRSRAN_SCHED_GRID_H -#include "lib/include/srsran/interfaces/sched_interface.h" +#include "sched_common.h" +#include "sched_interface.h" #include "sched_phy_ch/sched_result.h" #include "sched_phy_ch/sf_cch_allocator.h" #include "sched_ue.h" @@ -33,20 +34,6 @@ namespace srsenb { -/// Error code of alloc attempt -enum class alloc_result { - success, - sch_collision, - no_cch_space, - no_sch_space, - no_rnti_opportunity, - invalid_grant_params, - invalid_coderate, - no_grant_space, - other_cause -}; -const char* to_string(alloc_result res); - struct sf_sched_result { tti_point tti_rx; std::vector enb_cc_list; @@ -163,6 +150,9 @@ public: struct bc_alloc_t : public ctrl_alloc_t { sched_interface::dl_sched_bc_t bc_grant; }; + struct po_alloc_t : public ctrl_alloc_t { + sched_interface::dl_sched_po_t po_grant; + }; struct dl_alloc_t { size_t dci_idx; uint16_t rnti; @@ -196,12 +186,14 @@ public: alloc_result alloc_sib(uint32_t aggr_lvl, uint32_t sib_idx, uint32_t sib_ntx, rbg_interval rbgs); alloc_result alloc_paging(uint32_t aggr_lvl, uint32_t paging_payload, rbg_interval rbgs); alloc_result alloc_rar(uint32_t aggr_lvl, const pending_rar_t& rar_grant, rbg_interval rbgs, uint32_t nof_grants); + alloc_result + alloc_pdcch_order(const sched_interface::dl_sched_po_info_t& po_cfg, uint32_t aggr_lvl, rbg_interval rbgs); bool reserve_dl_rbgs(uint32_t rbg_start, uint32_t rbg_end) { return tti_alloc.reserve_dl_rbgs(rbg_start, rbg_end); } // UL alloc methods alloc_result alloc_msg3(sched_ue* user, const sched_interface::dl_sched_rar_grant_t& rargrant); alloc_result - alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_t::type_t alloc_type, bool is_msg3 = false, int msg3_mcs = -1); + alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_t::type_t alloc_type, bool is_msg3 = false, int msg3_mcs = -1); alloc_result reserve_ul_prbs(const prbmask_t& ulmask, bool strict) { return tti_alloc.reserve_ul_prbs(ulmask, strict); @@ -236,15 +228,16 @@ private: sched_ue_list& ue_list); // consts - const sched_cell_params_t* cc_cfg = nullptr; + const sched_cell_params_t* cc_cfg = nullptr; + sf_sched_result* cc_results = nullptr; ///< Results of other CCs for the same Subframe srslog::basic_logger& logger; - sf_sched_result* cc_results; ///< Results of other CCs for the same Subframe // internal state sf_grid_t tti_alloc; srsran::bounded_vector bc_allocs; srsran::bounded_vector rar_allocs; + srsran::bounded_vector po_allocs; srsran::bounded_vector data_allocs; srsran::bounded_vector ul_data_allocs; uint32_t last_msg3_prb = 0, max_msg3_prb = 0; diff --git a/srsenb/hdr/stack/mac/sched_helpers.h b/srsenb/hdr/stack/mac/sched_helpers.h index 33adadac8..6a726739b 100644 --- a/srsenb/hdr/stack/mac/sched_helpers.h +++ b/srsenb/hdr/stack/mac/sched_helpers.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,8 +22,8 @@ #ifndef SRSRAN_SCHED_HELPERS_H #define SRSRAN_SCHED_HELPERS_H -#include "srsenb/hdr/stack/mac/sched_common.h" -#include "srsran/interfaces/sched_interface.h" +#include "sched_interface.h" +#include "srsenb/hdr/stack/mac/sched_lte_common.h" #include "srsran/srslog/srslog.h" namespace srsenb { @@ -47,74 +47,6 @@ inline uint32_t get_nof_retx(uint32_t rv_idx) return nof_retxs[rv_idx % 4]; } -/// convert cell nof PRBs to nof RBGs -inline uint32_t cell_nof_prb_to_rbg(uint32_t nof_prbs) -{ - switch (nof_prbs) { - case 6: - return 6; - case 15: - return 8; - case 25: - return 13; - case 50: - return 17; - case 75: - return 19; - case 100: - return 25; - default: - srslog::fetch_basic_logger("MAC").error("Provided nof PRBs not valid"); - return 0; - } -} - -/// convert cell nof RBGs to nof PRBs -inline uint32_t cell_nof_rbg_to_prb(uint32_t nof_rbgs) -{ - switch (nof_rbgs) { - case 6: - return 6; - case 8: - return 15; - case 13: - return 25; - case 17: - return 50; - case 19: - return 75; - case 25: - return 100; - default: - srslog::fetch_basic_logger("MAC").error("Provided nof PRBs not valid"); - return 0; - } -} - -/** - * Count number of PRBs present in a DL RBG mask - * @param cell_nof_prb cell nof prbs - * @param P cell ratio prb/rbg - * @param bitmask DL RBG mask - * @return number of prbs - */ -inline uint32_t count_prb_per_tb(const rbgmask_t& bitmask) -{ - uint32_t Nprb = cell_nof_rbg_to_prb(bitmask.size()); - uint32_t P = srsran_ra_type0_P(Nprb); - uint32_t nof_prb = P * bitmask.count(); - if (bitmask.test(bitmask.size() - 1)) { - nof_prb -= bitmask.size() * P - Nprb; - } - return nof_prb; -} - -inline uint32_t count_prb_per_tb_approx(uint32_t nof_rbgs, uint32_t cell_nof_prb) -{ - uint32_t P = srsran_ra_type0_P(cell_nof_prb); - return std::min(nof_rbgs * P, cell_nof_prb); -} - cce_frame_position_table generate_cce_location_table(uint16_t rnti, const sched_cell_params_t& cell_cfg); /** @@ -146,16 +78,11 @@ inline uint32_t get_tbs_bytes(uint32_t mcs, uint32_t nof_alloc_prb, bool use_tbs /// Find lowest DCI aggregation level supported by the UE spectral efficiency uint32_t get_aggr_level(uint32_t nof_bits, uint32_t dl_cqi, + uint32_t min_aggr_lvl, uint32_t max_aggr_lvl, uint32_t cell_nof_prb, bool use_tbs_index_alt); -/******************************************************* - * RB mask helper functions - *******************************************************/ - -bool is_contiguous(const rbgmask_t& mask); - /******************************************************* * sched_interface helper functions *******************************************************/ @@ -178,8 +105,6 @@ void log_phich_cc_results(srslog::basic_logger& logger, uint32_t enb_cc_idx, const sched_interface::ul_sched_res_t& result); -const char* to_string(sched_interface::ue_bearer_cfg_t::direction_t dir); - } // namespace srsenb #endif // SRSRAN_SCHED_HELPERS_H diff --git a/lib/include/srsran/interfaces/sched_interface.h b/srsenb/hdr/stack/mac/sched_interface.h similarity index 75% rename from lib/include/srsran/interfaces/sched_interface.h rename to srsenb/hdr/stack/mac/sched_interface.h index 90ab7577e..5df6da1bc 100644 --- a/lib/include/srsran/interfaces/sched_interface.h +++ b/srsenb/hdr/stack/mac/sched_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,6 +19,7 @@ * */ +#include "common/sched_config.h" #include "srsran/adt/bounded_vector.h" #include "srsran/common/common.h" #include "srsran/srsran.h" @@ -45,6 +46,7 @@ public: const static int MAX_DATA_LIST = 32; const static int MAX_RAR_LIST = 8; const static int MAX_BC_LIST = 8; + const static int MAX_PO_LIST = 8; const static int MAX_RLC_PDU_LIST = 8; const static int MAX_PHICH_LIST = 8; @@ -54,16 +56,30 @@ public: } cell_cfg_sib_t; struct sched_args_t { - std::string sched_policy = "time_pf"; - std::string sched_policy_args = "2"; - int pdsch_mcs = -1; - int pdsch_max_mcs = 28; - int pusch_mcs = -1; - int pusch_max_mcs = 28; - uint32_t min_nof_ctrl_symbols = 1; - uint32_t max_nof_ctrl_symbols = 3; - int max_aggr_level = 3; - bool pucch_mux_enabled = false; + std::string sched_policy = "time_pf"; + std::string sched_policy_args = "2"; + int pdsch_mcs = -1; + int pdsch_max_mcs = 28; + int pusch_mcs = -1; + int pusch_max_mcs = 28; + uint32_t min_nof_ctrl_symbols = 1; + uint32_t max_nof_ctrl_symbols = 3; + int min_aggr_level = 0; + int max_aggr_level = 3; + bool adaptive_aggr_level = false; + bool pucch_mux_enabled = false; + int pucch_harq_max_rb = 0; + float target_bler = 0.05; + float max_delta_dl_cqi = 5; + float max_delta_ul_snr = 5; + float adaptive_dl_mcs_step_size = 0.001; + float adaptive_ul_mcs_step_size = 0.001; + uint32_t min_tpc_tti_interval = 1; + float ul_snr_avg_alpha = 0.05; + int init_ul_snr_value = 5; + int init_dl_cqi = 5; + float max_sib_coderate = 0.8; + int pdcch_cqi_offset = 0; }; struct cell_cfg_t { @@ -80,6 +96,7 @@ public: /* pusch configuration */ srsran_pusch_hopping_cfg_t pusch_hopping_cfg; float target_pusch_ul_sinr; + int min_phr_thres; bool enable_phr_handling; bool enable_64qam; @@ -99,7 +116,6 @@ public: uint32_t nrb_cqi; uint32_t ncs_an; - uint32_t initial_dl_cqi; uint32_t srs_subframe_config; uint32_t srs_subframe_offset; @@ -113,14 +129,6 @@ public: std::vector scell_list; }; - struct ue_bearer_cfg_t { - int priority = 1; - uint32_t bsd = 1000; // msec - uint32_t pbr = -1; - int group = 0; - enum direction_t { IDLE = 0, UL, DL, BOTH } direction = IDLE; - }; - struct ant_info_ded_t { enum class tx_mode_t { tm1, tm2, tm3, tm4, tm5, tm6, tm7, tm8_v920, nulltype } tx_mode = tx_mode_t::tm1; enum class codebook_t { @@ -150,7 +158,7 @@ public: bool continuous_pusch = false; srsran_uci_offset_cfg_t uci_offset = {15, 12, 10}; srsran_pucch_cfg_t pucch_cfg = {}; - std::array ue_bearers = {}; + std::array ue_bearers = {}; std::vector supported_cc_list; ///< list of UE supported CCs. First index for PCell ant_info_ded_t dl_ant_info; bool use_tbs_index_alt = false; @@ -217,20 +225,31 @@ public: typedef struct { srsran_dci_dl_t dci; - enum bc_type { BCCH, PCCH } type; - uint32_t index; - uint32_t tbs; - } dl_sched_bc_t; + struct dl_sched_po_info_t { + uint32_t preamble_idx; + uint32_t prach_mask_idx; + uint16_t crnti; + }; + + typedef struct { + srsran_dci_dl_t dci; + uint32_t tbs; + uint16_t crnti; + uint32_t preamble_idx; + uint32_t prach_mask_idx; + } dl_sched_po_t; + struct dl_sched_res_t { uint32_t cfi; srsran::bounded_vector data; srsran::bounded_vector rar; srsran::bounded_vector bc; + srsran::bounded_vector po; }; typedef struct { @@ -255,7 +274,7 @@ public: virtual bool ue_exists(uint16_t rnti) = 0; /* Manages UE bearers and associated configuration */ - virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const ue_bearer_cfg_t& cfg) = 0; + virtual int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const mac_lc_ch_cfg_t& cfg) = 0; virtual int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) = 0; virtual uint32_t get_ul_buffer(uint16_t rnti) = 0; @@ -269,10 +288,10 @@ public: * @param rnti user rnti * @param lc_id logical channel id for which the buffer update is concerned * @param tx_queue number of pending bytes for new DL RLC transmissions - * @param retx_queue number of pending bytes concerning RLC retransmissions + * @param prio_tx_queue number of pending bytes concerning RLC retransmissions and status PDUs * @return error code */ - virtual int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) = 0; + virtual int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t prio_tx_queue) = 0; /** * Enqueue MAC CEs for DL transmission @@ -285,28 +304,32 @@ public: virtual int dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code, uint32_t nof_cmds) = 0; /* DL information */ - virtual int dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) = 0; - virtual int dl_rach_info(uint32_t enb_cc_idx, dl_sched_rar_info_t rar_info) = 0; - virtual int dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t ri_value) = 0; - virtual int dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t pmi_value) = 0; - virtual int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi_value) = 0; + virtual int dl_ack_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) = 0; + virtual int dl_rach_info(uint32_t enb_cc_idx, dl_sched_rar_info_t rar_info) = 0; + virtual int dl_ri_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t ri_value) = 0; + virtual int dl_pmi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t pmi_value) = 0; + virtual int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi_value) = 0; + virtual int dl_sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t sb_idx, uint32_t cqi) = 0; /* UL information */ virtual int ul_crc_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, bool crc) = 0; virtual int ul_sr_info(uint32_t tti, uint16_t rnti) = 0; virtual int ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) = 0; - virtual int ul_phr(uint16_t rnti, int phr) = 0; + virtual int ul_phr(uint16_t rnti, int phr, uint32_t ul_nof_prb) = 0; virtual int ul_snr_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code) = 0; /* Run Scheduler for this tti */ virtual int dl_sched(uint32_t tti, uint32_t enb_cc_idx, dl_sched_res_t& sched_result) = 0; virtual int ul_sched(uint32_t tti, uint32_t enb_cc_idx, ul_sched_res_t& sched_result) = 0; + /* PDCCH order */ + virtual int set_pdcch_order(uint32_t enb_cc_idx, dl_sched_po_info_t pdcch_order_info) = 0; + /* Custom */ - virtual void set_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) = 0; - virtual std::array get_enb_ue_cc_map(uint16_t rnti) = 0; - virtual std::array get_scell_activation_mask(uint16_t rnti) = 0; - virtual int ul_buffer_add(uint16_t rnti, uint32_t lcid, uint32_t bytes) = 0; + virtual void set_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) = 0; + virtual std::array get_enb_ue_cc_map(uint16_t rnti) = 0; + virtual std::array get_enb_ue_activ_cc_map(uint16_t rnti) = 0; + virtual int ul_buffer_add(uint16_t rnti, uint32_t lcid, uint32_t bytes) = 0; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/mac/sched_lte_common.h b/srsenb/hdr/stack/mac/sched_lte_common.h new file mode 100644 index 000000000..6793814ca --- /dev/null +++ b/srsenb/hdr/stack/mac/sched_lte_common.h @@ -0,0 +1,103 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_LTE_COMMON_H +#define SRSRAN_SCHED_LTE_COMMON_H + +#include "sched_interface.h" +#include "srsran/adt/bounded_bitset.h" +#include "srsran/common/tti_point.h" + +namespace srsenb { + +/*********************** + * Constants + **********************/ + +constexpr float tti_duration_ms = 1; +constexpr uint32_t NOF_AGGR_LEVEL = 4; + +/*********************** + * Helper Types + **********************/ + +/// List of CCE start positions in PDCCH +using cce_position_list = srsran::bounded_vector; + +/// Map {L} -> list of CCE positions +using cce_cfi_position_table = std::array; + +/// Map {cfi, L} -> list of CCE positions +using cce_sf_position_table = std::array, SRSRAN_NOF_CFI>; + +/// Map {sf, cfi, L} -> list of CCE positions +using cce_frame_position_table = std::array; + +/// structs to bundle together all the sched arguments, and share them with all the sched sub-components +class sched_cell_params_t +{ + struct regs_deleter { + void operator()(srsran_regs_t* p); + }; + +public: + bool set_cfg(uint32_t enb_cc_idx_, + const sched_interface::cell_cfg_t& cfg_, + const sched_interface::sched_args_t& sched_args); + // convenience getters + uint32_t nof_prbs_to_rbgs(uint32_t nof_prbs) const { return srsran::ceil_div(nof_prbs, P); } + uint32_t nof_prb() const { return cfg.cell.nof_prb; } + uint32_t get_dl_lb_nof_re(tti_point tti_tx_dl, uint32_t nof_prbs_alloc) const; + uint32_t get_dl_nof_res(srsran::tti_point tti_tx_dl, const srsran_dci_dl_t& dci, uint32_t cfi) const; + + uint32_t enb_cc_idx = 0; + sched_interface::cell_cfg_t cfg = {}; + srsran_pucch_cfg_t pucch_cfg_common = {}; + const sched_interface::sched_args_t* sched_cfg = nullptr; + std::unique_ptr regs; + cce_sf_position_table common_locations = {}; + cce_frame_position_table rar_locations = {}; + std::array nof_cce_table = {}; ///< map cfix -> nof cces in PDCCH + uint32_t P = 0; + uint32_t nof_rbgs = 0; + + using dl_nof_re_table = srsran::bounded_vector< + std::array, SRSRAN_NOF_SLOTS_PER_SF>, SRSRAN_NOF_SF_X_FRAME>, + SRSRAN_MAX_PRB>; + using dl_lb_nof_re_table = std::array, SRSRAN_NOF_SF_X_FRAME>; + + /// Table of nof REs + dl_nof_re_table nof_re_table; + /// Cached computation of Lower bound of nof REs + dl_lb_nof_re_table nof_re_lb_table; +}; + +/// Type of Allocation stored in PDSCH/PUSCH +enum class alloc_type_t { DL_BC, DL_PCCH, DL_RAR, DL_PDCCH_ORDER, DL_DATA, UL_DATA }; +inline bool is_dl_ctrl_alloc(alloc_type_t a) +{ + return a == alloc_type_t::DL_BC or a == alloc_type_t::DL_PCCH or a == alloc_type_t::DL_RAR or + a == alloc_type_t::DL_PDCCH_ORDER; +} + +} // namespace srsenb + +#endif // SRSRAN_SCHED_LTE_COMMON_H diff --git a/srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h b/srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h index b0b692e89..db006753a 100644 --- a/srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h +++ b/srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,7 +22,8 @@ #ifndef SRSRAN_SCHED_DCI_H #define SRSRAN_SCHED_DCI_H -#include "../sched_common.h" +#include "../sched_lte_common.h" +#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h" #include "srsran/adt/bounded_vector.h" namespace srsenb { @@ -55,6 +56,19 @@ tbs_info compute_mcs_and_tbs(uint32_t nof_prb, bool ulqam64_enabled, bool use_tbs_index_alt); +/** + * Compute MCS, TBS based on maximum coderate, N_prb + * \remark See TS 36.213 - Table 7.1.7.1-1/1A + * @return resulting TBS (in bytes) and mcs. TBS=-1 if no valid solution was found. + */ +tbs_info compute_mcs_and_tbs(uint32_t nof_prb, + uint32_t nof_re, + float max_coderate, + uint32_t max_mcs, + bool is_ul, + bool ulqam64_enabled, + bool use_tbs_index_alt); + /** * Compute lowest MCS, TBS based on CQI, N_prb that satisfies TBS >= req_bytes (best effort) * \remark See TS 36.213 - Table 7.1.7.1-1/1A @@ -99,6 +113,11 @@ bool generate_rar_dci(sched_interface::dl_sched_rar_t& rar, const sched_cell_params_t& cell_params, uint32_t current_cfi); +void generate_pdcch_order_dci(sched_interface::dl_sched_po_t& pdcch_order, + tti_point tti_tx_dl, + const sched_cell_params_t& cell_params, + uint32_t current_cfi); + void log_broadcast_allocation(const sched_interface::dl_sched_bc_t& bc, rbg_interval rbg_range, const sched_cell_params_t& cell_params); @@ -109,6 +128,10 @@ void log_rar_allocation(const sched_interface::dl_sched_rar_t& rar, void log_rar_allocation(const sched_interface::dl_sched_rar_t& rar, rbg_interval rbg_range); +void log_po_allocation(const sched_interface::dl_sched_po_t& pdcch_order, + rbg_interval rbg_range, + const sched_cell_params_t& cell_params); + } // namespace srsenb #endif // SRSRAN_SCHED_DCI_H diff --git a/srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h b/srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h new file mode 100644 index 000000000..2cd38f9de --- /dev/null +++ b/srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h @@ -0,0 +1,183 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_PHY_RESOURCE_H +#define SRSRAN_SCHED_PHY_RESOURCE_H + +#include "srsran/adt/bounded_bitset.h" +#include "srsran/adt/interval.h" +#include "srsran/support/srsran_assert.h" +extern "C" { +#include "srsran/phy/phch/ra.h" +} + +// Description: This file defines the types associated with representing the allocation masks/intervals for RBGs, PRBs +// and PDCCH CCEs, and provides some function helpers and algorithms to handle these types. + +constexpr uint32_t MAX_NOF_RBGS = 25; +constexpr uint32_t MAX_NOF_PRBS = 100; +constexpr uint32_t MAX_NOF_CCES = 128; + +namespace srsenb { + +/// convert cell nof PRBs to nof RBGs +inline uint32_t cell_nof_prb_to_rbg(uint32_t nof_prbs) +{ + switch (nof_prbs) { + case 6: + return 6; + case 15: + return 8; + case 25: + return 13; + case 50: + return 17; + case 75: + return 19; + case 100: + return 25; + default: + srsran_assertion_failure("Provided nof PRBs not valid"); + } + return 0; +} + +/// convert cell nof RBGs to nof PRBs +inline uint32_t cell_nof_rbg_to_prb(uint32_t nof_rbgs) +{ + switch (nof_rbgs) { + case 6: + return 6; + case 8: + return 15; + case 13: + return 25; + case 17: + return 50; + case 19: + return 75; + case 25: + return 100; + default: + srsran_assertion_failure("Provided nof PRBs not valid"); + } + return 0; +} + +/// Bitmask used for CCE allocations +using pdcch_mask_t = srsran::bounded_bitset; + +/// Bitmask that stores the allocared DL RBGs +using rbgmask_t = srsran::bounded_bitset; + +/// Bitmask that stores the allocated UL PRBs +using prbmask_t = srsran::bounded_bitset; + +/// Struct to express a {min,...,max} range of RBGs +struct prb_interval; +struct rbg_interval : public srsran::interval { + using interval::interval; + static rbg_interval find_first_interval(const rbgmask_t& mask); + static rbg_interval prbs_to_rbgs(const prb_interval& prbs, uint32_t cell_nof_prb); +}; + +/// Struct to express a {min,...,max} range of PRBs +struct prb_interval : public srsran::interval { + using interval::interval; + static prb_interval rbgs_to_prbs(const rbg_interval& rbgs, uint32_t cell_nof_prb) + { + uint32_t P = srsran_ra_type0_P(cell_nof_prb); + return prb_interval{rbgs.start() * P, std::min(rbgs.stop() * P, cell_nof_prb)}; + } + static prb_interval riv_to_prbs(uint32_t riv, uint32_t nof_prbs, int nof_vrbs = -1); +}; + +inline rbg_interval rbg_interval::prbs_to_rbgs(const prb_interval& prbs, uint32_t cell_nof_prb) +{ + uint32_t P = srsran_ra_type0_P(cell_nof_prb); + return rbg_interval{prbs.start() / P, srsran::ceil_div(prbs.stop(), P)}; +} + +/******************************************************* + * helper functions + *******************************************************/ + +/// If the RBG mask one bits are all contiguous +inline bool is_contiguous(const rbgmask_t& mask) +{ + return rbg_interval::find_first_interval(mask).length() == mask.count(); +} + +/// Count number of PRBs present in a DL RBG mask +inline uint32_t count_prb_per_tb(const rbgmask_t& bitmask) +{ + uint32_t Nprb = cell_nof_rbg_to_prb(bitmask.size()); + uint32_t P = srsran_ra_type0_P(Nprb); + uint32_t nof_prb = P * bitmask.count(); + if (bitmask.test(bitmask.size() - 1)) { + nof_prb -= bitmask.size() * P - Nprb; + } + return nof_prb; +} + +/// Estimate of number of PRBs in DL grant given Nof RBGs +inline uint32_t count_prb_per_tb_approx(uint32_t nof_rbgs, uint32_t cell_nof_prb) +{ + uint32_t P = srsran_ra_type0_P(cell_nof_prb); + return std::min(nof_rbgs * P, cell_nof_prb); +} + +/** + * Finds a contiguous interval of "zeroed"/available RBG resources + * @param max_nof_rbgs maximum number of RBGs + * @param current_mask bitmask of occupied RBGs, used to search for available RBGs + * @return interval with found RBGs. If a valid interval wasn't found, interval.length() == 0 + */ +rbg_interval find_empty_rbg_interval(uint32_t max_nof_rbgs, const rbgmask_t& current_mask); + +/** + * Finds a bitmask of "zeroed"/available RBG resources + * @param max_nof_rbgs maximum number of RBGs + * @param current_mask bitmask of occupied RBGs, used to search for available RBGs + * @return bitmask of found RBGs. If a valid mask wasn't found, bitmask::size() == 0 + */ +rbgmask_t find_available_rbgmask(uint32_t max_nof_rbgs, bool is_contiguous, const rbgmask_t& current_mask); + +/** + * Finds a range of L contiguous PRBs that are empty + * @param L Max length of the requested UL PRBs + * @param current_mask input PRB mask where to search for available PRBs + * @return found interval of PRBs + */ +prb_interval find_contiguous_ul_prbs(uint32_t L, const prbmask_t& current_mask); + +} // namespace srsenb + +namespace fmt { + +template <> +struct formatter : public formatter > {}; +template <> +struct formatter : public formatter > {}; + +} // namespace fmt + +#endif // SRSRAN_SCHED_PHY_RESOURCE_H diff --git a/srsenb/hdr/stack/mac/sched_phy_ch/sched_result.h b/srsenb/hdr/stack/mac/sched_phy_ch/sched_result.h index 248298124..5dda21f5b 100644 --- a/srsenb/hdr/stack/mac/sched_phy_ch/sched_result.h +++ b/srsenb/hdr/stack/mac/sched_phy_ch/sched_result.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,7 +22,8 @@ #ifndef SRSRAN_SCHED_RESULT_H #define SRSRAN_SCHED_RESULT_H -#include "../sched_common.h" +#include "../sched_lte_common.h" +#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h" namespace srsenb { diff --git a/srsenb/hdr/stack/mac/sched_phy_ch/sf_cch_allocator.h b/srsenb/hdr/stack/mac/sched_phy_ch/sf_cch_allocator.h index e8e3203ae..2e6796916 100644 --- a/srsenb/hdr/stack/mac/sched_phy_ch/sf_cch_allocator.h +++ b/srsenb/hdr/stack/mac/sched_phy_ch/sf_cch_allocator.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "../sched_common.h" +#include "../sched_lte_common.h" #include "sched_result.h" #ifndef SRSRAN_PDCCH_SCHED_H diff --git a/srsenb/hdr/stack/mac/sched_ue.h b/srsenb/hdr/stack/mac/sched_ue.h index 2d9b0f9c4..58464400f 100644 --- a/srsenb/hdr/stack/mac/sched_ue.h +++ b/srsenb/hdr/stack/mac/sched_ue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,16 +22,16 @@ #ifndef SRSENB_SCHEDULER_UE_H #define SRSENB_SCHEDULER_UE_H -#include "sched_common.h" -#include "srsran/srslog/srslog.h" -#include -#include - +#include "sched_lte_common.h" #include "sched_ue_ctrl/sched_lch.h" #include "sched_ue_ctrl/sched_ue_cell.h" #include "sched_ue_ctrl/tpc.h" +#include "srsenb/hdr/common/common_enb.h" +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" +#include "srsran/srslog/srslog.h" #include -#include +#include +#include namespace srsenb { @@ -42,8 +42,7 @@ typedef enum { UCI_PUSCH_NONE = 0, UCI_PUSCH_CQI, UCI_PUSCH_ACK, UCI_PUSCH_ACK_C */ class sched_ue { - using ue_cfg_t = sched_interface::ue_cfg_t; - using bearer_cfg_t = sched_interface::ue_bearer_cfg_t; + using ue_cfg_t = sched_interface::ue_cfg_t; public: sched_ue(uint16_t rnti, const std::vector& cell_list_params_, const ue_cfg_t& cfg); @@ -58,18 +57,19 @@ public: void phy_config_enabled(tti_point tti_rx, bool enabled); void set_cfg(const ue_cfg_t& cfg); - void set_bearer_cfg(uint32_t lc_id, const bearer_cfg_t& cfg); + void set_bearer_cfg(uint32_t lc_id, const mac_lc_ch_cfg_t& cfg); void rem_bearer(uint32_t lc_id); void dl_buffer_state(uint8_t lc_id, uint32_t tx_queue, uint32_t retx_queue); void ul_buffer_state(uint8_t lcg_id, uint32_t bsr); - void ul_phr(int phr); + void ul_phr(int phr, uint32_t grant_nof_prb); void mac_buffer_state(uint32_t ce_code, uint32_t nof_cmds); void set_ul_snr(tti_point tti_rx, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code); void set_dl_ri(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t ri); void set_dl_pmi(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t ri); void set_dl_cqi(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t cqi); + void set_dl_sb_cqi(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t sb_idx, uint32_t cqi); int set_ack_info(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack); void set_ul_crc(tti_point tti_rx, uint32_t enb_cc_idx, bool crc_res); @@ -83,6 +83,7 @@ public: const ue_cfg_t& get_ue_cfg() const { return cfg; } uint32_t get_aggr_level(uint32_t enb_cc_idx, uint32_t nof_bits); void ul_buffer_add(uint8_t lcid, uint32_t bytes); + void metrics_read(mac_ue_metrics_t& metrics); /******************************************************* * Functions used by scheduler metric objects @@ -92,10 +93,11 @@ public: /// Get total pending bytes to be transmitted in DL. /// The amount of CEs to transmit depends on whether enb_cc_idx is UE's PCell - uint32_t get_pending_dl_bytes(uint32_t enb_cc_idx); - rbg_interval get_required_dl_rbgs(uint32_t enb_cc_idx); - uint32_t get_pending_dl_rlc_data() const; - uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const; + uint32_t get_pending_dl_bytes(uint32_t enb_cc_idx); + srsran::interval get_requested_dl_bytes(uint32_t enb_cc_idx); + rbg_interval get_required_dl_rbgs(uint32_t enb_cc_idx); + uint32_t get_pending_dl_rlc_data() const; + uint32_t get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs = -1) const; uint32_t get_pending_ul_data_total(tti_point tti_tx_ul, int this_enb_cc_idx); uint32_t get_pending_ul_new_data(tti_point tti_tx_ul, int this_enb_cc_idx); @@ -147,10 +149,9 @@ public: bool pdsch_enabled(tti_point tti_rx, uint32_t enb_cc_idx) const; bool pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_pdcch) const; + bool phich_enabled(tti_point tti_rx, uint32_t enb_cc_idx) const; private: - srsran::interval get_requested_dl_bytes(uint32_t enb_cc_idx); - bool is_sr_triggered(); tbs_info allocate_new_dl_mac_pdu(sched_interface::dl_sched_data_t* data, @@ -163,7 +164,7 @@ private: tbs_info compute_mcs_and_tbs(uint32_t enb_cc_idx, tti_point tti_tx_dl, - uint32_t nof_alloc_prbs, + const rbgmask_t& rbgs, uint32_t cfi, const srsran_dci_dl_t& dci); @@ -220,7 +221,7 @@ private: std::vector cells; ///< List of eNB cells that may be configured/activated/deactivated for the UE }; -using sched_ue_list = std::map >; +using sched_ue_list = rnti_map_t >; } // namespace srsenb diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h new file mode 100644 index 000000000..b8afc159a --- /dev/null +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h @@ -0,0 +1,254 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_DL_CQI_H +#define SRSRAN_SCHED_DL_CQI_H + +#include "srsenb/hdr/stack/mac/sched_helpers.h" +#include "srsenb/hdr/stack/mac/sched_lte_common.h" +#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h" +#include "srsran/adt/accumulators.h" +#include "srsran/common/common_lte.h" +#include "srsran/phy/phch/cqi.h" + +namespace srsenb { + +/** + * Class that handles DL CQI state of a given {rnti,sector} + * - The cell bandwidth is divided into J parts. J = f(nof_cell_prbs) + * - UE reports wideband CQI every H.Np msec, where Np is the CQI period and H=JK + 1, where K is configured in RRC + * - Thus, for K==0, only wideband CQI is active + */ +class sched_dl_cqi +{ +public: + sched_dl_cqi(uint32_t cell_nof_prb_, uint32_t K_, uint32_t init_dl_cqi) : + cell_nof_prb(cell_nof_prb_), + cell_nof_rbg(cell_nof_prb_to_rbg(cell_nof_prb_)), + K(K_), + wb_cqi_avg(init_dl_cqi), + bp_list(nof_bandwidth_parts(cell_nof_prb_), bandwidth_part_context(init_dl_cqi)), + subband_cqi(std::max(1, srsran_cqi_hl_get_no_subbands(cell_nof_prb)), 0) + { + srsran_assert(K <= 4, "K=%d outside of {0, 4}", K); + srsran_assert(K == 0 or cell_nof_prb_ > 6, "K > 0 not allowed for nof_prbs=6"); + } + + /// Set K value from upper layers. See TS 36.331, CQI-ReportPeriodic + void set_K(uint32_t K_) + { + srsran_assert(K <= 4, "K=%d outside of {0, 4}", K); + srsran_assert(K == 0 or cell_nof_prb > 6, "K > 0 not allowed for nof_prbs=6"); + K = K_; + } + + /// Update wideband CQI + void cqi_wb_info(tti_point tti, uint32_t cqi_value) + { + if (cqi_value > 0) { + last_pos_cqi_tti = tti; + } + + last_wb_tti = tti; + wb_cqi_avg = static_cast(cqi_value); + } + + /// Update subband CQI for subband "sb_index" + void cqi_sb_info(tti_point tti, uint32_t sb_index, uint32_t cqi_value) + { + if (cqi_value > 0) { + last_pos_cqi_tti = tti; + } + + uint32_t bp_idx = get_bp_index(sb_index); + bp_list[bp_idx].last_feedback_tti = tti; + bp_list[bp_idx].last_cqi_subband_idx = sb_index; + bp_list[bp_idx].cqi_val = static_cast(cqi_value); + + // just cap all sub-bands in the same bandwidth part + srsran::interval interv = get_bp_sb_indexes(bp_idx); + for (uint32_t sb_index2 = interv.start(); sb_index2 < interv.stop(); ++sb_index2) { + subband_cqi[sb_index2] = bp_list[bp_idx].cqi_val; + } + } + + /// Resets CQI to provided value + void reset_cqi(uint32_t dl_cqi) + { + last_pos_cqi_tti = {}; + last_wb_tti = {}; + wb_cqi_avg = dl_cqi; + for (bandwidth_part_context& bp : bp_list) { + bp = bandwidth_part_context(dl_cqi); + } + } + + int get_avg_cqi() const { return get_grant_avg_cqi(rbg_interval(0, cell_nof_rbg)); } + + /// Get CQI of RBG + int get_rbg_cqi(uint32_t rbg) const + { + if (not subband_cqi_enabled()) { + return static_cast(wb_cqi_avg); + } + uint32_t sb_idx = rbg_to_sb_index(rbg); + return get_subband_cqi_(sb_idx); + } + + /// Get average CQI in given RBG interval + int get_grant_avg_cqi(rbg_interval interv) const + { + if (not subband_cqi_enabled()) { + return static_cast(wb_cqi_avg); + } + float cqi = 0; + uint32_t sbstart = rbg_to_sb_index(interv.start()), sbend = rbg_to_sb_index(interv.stop() - 1) + 1; + for (uint32_t sb = sbstart; sb < sbend; ++sb) { + cqi += get_subband_cqi_(sb); + } + return static_cast(cqi / (sbend - sbstart)); + } + + /// Get average CQI in given PRB interval + int get_grant_avg_cqi(prb_interval prb_interv) const + { + return get_grant_avg_cqi(rbg_interval::prbs_to_rbgs(prb_interv, cell_nof_prb)); + } + + /// Get average CQI in given RBG mask + int get_grant_avg_cqi(const rbgmask_t& mask) const + { + if (not subband_cqi_enabled()) { + return static_cast(wb_cqi_avg); + } + float cqi = 0; + uint32_t count = 0; + for (int rbg = mask.find_lowest(0, mask.size()); rbg != -1; rbg = mask.find_lowest(rbg, mask.size())) { + uint32_t sb = rbg_to_sb_index(rbg); + cqi += get_subband_cqi_(sb); + count++; + rbg = static_cast(((sb + 1U) * cell_nof_rbg + N() - 1U) / N()); // skip to next subband index + } + return static_cast(cqi / count); + } + + /// Get CQI-optimal RBG mask with at most "req_rbgs" RBGs + rbgmask_t get_optim_rbgmask(uint32_t req_rbgs, bool max_min_flag = true) const + { + rbgmask_t rbgmask(cell_nof_rbg); + return get_optim_rbgmask(rbgmask, req_rbgs, max_min_flag); + } + rbgmask_t get_optim_rbgmask(const rbgmask_t& dl_mask, uint32_t req_rbgs, bool max_min_flag = true) const; + + /// TS 36.321, 7.2.2 - Parameter N + uint32_t nof_subbands() const { return subband_cqi.size(); } + + /// TS 36.321, 7.2.2 - Parameter J + uint32_t nof_bandwidth_parts() const { return bp_list.size(); } + + bool subband_cqi_enabled() const { return K > 0; } + + bool is_cqi_info_received() const { return last_pos_cqi_tti.is_valid(); } + + tti_point last_cqi_info_tti() const { return last_pos_cqi_tti; } + + int get_wb_cqi_info() const { return wb_cqi_avg; } + + uint32_t rbg_to_sb_index(uint32_t rbg_index) const { return rbg_index * N() / cell_nof_rbg; } + + /// Get CQI of given subband index + int get_subband_cqi(uint32_t subband_index) const + { + if (not subband_cqi_enabled()) { + return get_wb_cqi_info(); + } + return bp_list[get_bp_index(subband_index)].last_feedback_tti.is_valid() ? subband_cqi[subband_index] : wb_cqi_avg; + } + +private: + static const uint32_t max_subband_size = 8; + static const uint32_t max_nof_subbands = 13; + static const uint32_t max_bandwidth_parts = 4; + + /// TS 36.321, Table 7.2.2-2 + static uint32_t nof_bandwidth_parts(uint32_t nof_prb) + { + static const uint32_t nrb_size = 6u; + static const uint32_t nrb[] = {0, 2, 2, 3, 4, 4}; + uint32_t index = srsran::lte_cell_nof_prb_to_index(nof_prb); + + srsran_assert(index < nrb_size, "nrb index out of bounds"); + + // Fix error out of bounds, returns the array's first element by default. + index = (index < nrb_size) ? index : 0; + + return nrb[index]; + } + + uint32_t J() const { return nof_bandwidth_parts(); } + uint32_t N() const { return nof_subbands(); } + + uint32_t get_bp_index(uint32_t sb_index) const { return sb_index * J() / N(); } + + uint32_t prb_to_sb_index(uint32_t prb_index) const { return prb_index * N() / cell_nof_prb; } + + srsran::interval get_bp_sb_indexes(uint32_t bp_idx) const + { + return srsran::interval{bp_idx * N() / J(), (bp_idx + 1) * N() / J()}; + } + + float get_subband_cqi_(uint32_t sb_idx) const + { + return bp_list[get_bp_index(sb_idx)].last_feedback_tti.is_valid() ? subband_cqi[sb_idx] : wb_cqi_avg; + } + + uint32_t cell_nof_prb; + uint32_t cell_nof_rbg; + uint32_t K; ///< set in RRC + + /// context of bandwidth part + struct bandwidth_part_context { + tti_point last_feedback_tti{}; + uint32_t last_cqi_subband_idx; + float cqi_val; + + explicit bandwidth_part_context(uint32_t init_dl_cqi) : cqi_val(init_dl_cqi), last_cqi_subband_idx(max_nof_subbands) + {} + }; + + tti_point last_pos_cqi_tti; + + tti_point last_wb_tti; + float wb_cqi_avg; + + srsran::bounded_vector bp_list; + srsran::bounded_vector subband_cqi; +}; + +/// Get {RBG indexs, CQI} tuple which correspond to the set RBG with the lowest CQI +rbgmask_t find_min_cqi_rbgs(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi, int& cqi); + +/// Returns the same RBG mask, but with the RBGs with the lowest CQI reset +rbgmask_t remove_min_cqi_rbgs(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi); + +} // namespace srsenb + +#endif // SRSRAN_SCHED_DL_CQI_H diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h index d6363622c..3e157080b 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_harq.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,9 +22,9 @@ #ifndef SRSENB_SCHEDULER_HARQ_H #define SRSENB_SCHEDULER_HARQ_H +#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/adt/bounded_bitset.h" #include "srsran/common/tti_point.h" -#include "srsran/interfaces/sched_interface.h" #include "srsran/srslog/srslog.h" namespace srsenb { @@ -45,6 +45,7 @@ public: srsran::tti_point get_tti() const; bool get_ndi(uint32_t tb_idx) const; uint32_t max_nof_retx() const; + int get_mcs(uint32_t tb_idx) const { return last_mcs[tb_idx]; } protected: void new_tx_common(uint32_t tb_idx, srsran::tti_point tti, int mcs, int tbs, uint32_t max_retx_); @@ -55,17 +56,17 @@ protected: enum ack_t { NACK, ACK }; - srslog::basic_logger* logger; - bool ack_state[SRSRAN_MAX_TB]; - bool active[SRSRAN_MAX_TB]; - std::array ndi = {}; - uint32_t id; - uint32_t max_retx = 5; - uint32_t n_rtx[SRSRAN_MAX_TB]; - uint32_t tx_cnt[SRSRAN_MAX_TB]; - srsran::tti_point tti; - int last_mcs[SRSRAN_MAX_TB]; - int last_tbs[SRSRAN_MAX_TB]; + srslog::basic_logger* logger = nullptr; + std::array ack_state = {}; + std::array active = {}; + std::array ndi = {}; + uint32_t id = 0; + uint32_t max_retx = 5; + std::array n_rtx = {}; + std::array tx_cnt = {}; + std::array last_mcs = {}; + std::array last_tbs = {}; + srsran::tti_point tti; }; class dl_harq_proc : public harq_proc @@ -114,12 +115,15 @@ public: uint32_t get_pending_data() const; bool has_pending_phich() const; bool pop_pending_phich(); + void request_pdcch(); + void retx_skipped(); private: prb_interval allocation; int pending_data; - bool pending_phich = false; - bool is_msg3_ = false; + bool pending_phich = false; + bool is_msg3_ = false; + bool pdcch_requested = false; }; class harq_entity @@ -156,9 +160,9 @@ public: * @param tti_rx tti the DL ACK was received * @param tb_idx TB index for the given ACK * @param ack true for ACK and false for NACK - * @return pair with pid and size of TB of the DL harq that was ACKed + * @return tuple with pid, TBS and MCS of the DL harq that was ACKed */ - std::pair set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack); + std::tuple set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack); //! Get UL Harq for a given tti_tx_ul ul_harq_proc* get_ul_harq(tti_point tti_tx_ul); @@ -169,7 +173,7 @@ public: int set_ul_crc(srsran::tti_point tti_tx_ul, uint32_t tb_idx, bool ack_); //! Resets pending harq ACKs and cleans UL Harqs with maxretx == 0 - void reset_pending_data(srsran::tti_point tti_rx); + void finish_tti(srsran::tti_point tti_rx); private: dl_harq_proc* get_oldest_dl_harq(tti_point tti_tx_dl); diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h index 14449e406..55ec67db4 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_lch.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,70 +22,58 @@ #ifndef SRSRAN_SCHED_LCH_H #define SRSRAN_SCHED_LCH_H -#include "srsran/interfaces/sched_interface.h" +#include "srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h" +#include "srsenb/hdr/stack/mac/sched_interface.h" +#include "srsran/adt/pool/cached_alloc.h" #include "srsran/mac/pdu.h" #include "srsran/srslog/srslog.h" -#include namespace srsenb { -class lch_ue_manager +class lch_ue_manager : private base_ue_buffer_manager { - constexpr static uint32_t pbr_infinity = -1; - constexpr static uint32_t MAX_LC = sched_interface::MAX_LC; + using base_type = base_ue_buffer_manager; public: - lch_ue_manager() : logger(srslog::fetch_basic_logger("MAC")) {} + explicit lch_ue_manager(uint16_t rnti) : base_ue_buffer_manager(rnti, srslog::fetch_basic_logger("MAC")) {} void set_cfg(const sched_interface::ue_cfg_t& cfg_); void new_tti(); - void config_lcid(uint32_t lcg_id, const sched_interface::ue_bearer_cfg_t& bearer_cfg); - void ul_bsr(uint8_t lcg_id, uint32_t bsr); + // Inherited methods from ue_buffer_manager base class + using base_type::config_lcid; + using base_type::get_bsr; + using base_type::get_bsr_state; + using base_type::get_dl_prio_tx; + using base_type::get_dl_tx; + using base_type::get_dl_tx_total; + using base_type::is_bearer_active; + using base_type::is_bearer_dl; + using base_type::is_bearer_ul; + using base_type::is_lcg_active; + + void dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t prio_tx_queue); + void ul_bsr(uint32_t lcg_id, uint32_t val); void ul_buffer_add(uint8_t lcid, uint32_t bytes); - void dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t retx_queue); int alloc_rlc_pdu(sched_interface::dl_sched_pdu_t* lcid, int rem_bytes); - bool is_bearer_active(uint32_t lcid) const; - bool is_bearer_ul(uint32_t lcid) const; - bool is_bearer_dl(uint32_t lcid) const; - bool has_pending_dl_txs() const; - int get_dl_tx_total() const; - int get_dl_tx_total(uint32_t lcid) const { return get_dl_tx(lcid) + get_dl_retx(lcid); } int get_dl_tx_total_with_overhead(uint32_t lcid) const; - int get_dl_tx(uint32_t lcid) const; int get_dl_tx_with_overhead(uint32_t lcid) const; - int get_dl_retx(uint32_t lcid) const; - int get_dl_retx_with_overhead(uint32_t lcid) const; + int get_dl_prio_tx_with_overhead(uint32_t lcid) const; - bool is_lcg_active(uint32_t lcg) const; - int get_bsr(uint32_t lcid) const; - int get_bsr_with_overhead(uint32_t lcid) const; - int get_max_prio_lcid() const; - - const std::array& get_bsr_state() const; + int get_bsr_with_overhead(uint32_t lcid) const; + int get_max_prio_lcid() const; // Control Element Command queue using ce_cmd = srsran::dl_sch_lcid; - std::deque pending_ces; + srsran::deque pending_ces; private: - struct ue_bearer_t { - sched_interface::ue_bearer_cfg_t cfg = {}; - int bucket_size = 0; - int buf_tx = 0; - int buf_retx = 0; - int Bj = 0; - }; - - int alloc_retx_bytes(uint8_t lcid, int rem_bytes); + int alloc_prio_tx_bytes(uint8_t lcid, int rem_bytes); int alloc_tx_bytes(uint8_t lcid, int rem_bytes); - size_t prio_idx = 0; - srslog::basic_logger& logger; - std::array lch = {}; - std::array lcg_bsr = {}; + size_t prio_idx = 0; }; /** diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h index 9c619bd7f..3c86e1b7e 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/sched_ue_cell.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,7 +22,8 @@ #ifndef SRSRAN_SCHED_UE_CELL_H #define SRSRAN_SCHED_UE_CELL_H -#include "../sched_common.h" +#include "../sched_lte_common.h" +#include "sched_dl_cqi.h" #include "sched_harq.h" #include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h" #include "tpc.h" @@ -41,7 +42,8 @@ struct sched_ue_cell { void clear_feedback(); void finish_tti(tti_point tti_rx); - void set_dl_cqi(tti_point tti_rx, uint32_t dl_cqi_); + int set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_); + int set_dl_sb_cqi(tti_point tti_rx, uint32_t sb_idx, uint32_t dl_cqi_); bool configured() const { return ue_cc_idx >= 0; } int get_ue_cc_idx() const { return ue_cc_idx; } @@ -50,6 +52,18 @@ struct sched_ue_cell { const ue_cc_cfg* get_ue_cc_cfg() const { return configured() ? &ue_cfg->supported_cc_list[ue_cc_idx] : nullptr; } const sched_interface::ue_cfg_t* get_ue_cfg() const { return configured() ? ue_cfg : nullptr; } cc_st cc_state() const { return cc_state_; } + float get_ul_snr_offset() const { return ul_snr_coeff; } + float get_dl_cqi_offset() const { return dl_cqi_coeff; } + + int get_dl_cqi() const; + int get_dl_cqi(const rbgmask_t& rbgs) const; + int get_ul_cqi() const; + + uint32_t get_aggr_level(uint32_t nof_bits) const; + + int set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack); + int set_ul_crc(tti_point tti_rx, bool crc_res); + int set_ul_snr(tti_point tti_rx, float ul_snr, uint32_t ul_ch_code); const uint16_t rnti; @@ -66,23 +80,22 @@ struct sched_ue_cell { tpc tpc_fsm; /// UCI Feedback - uint32_t dl_ri = 0; - tti_point dl_ri_tti_rx{}; - uint32_t dl_pmi = 0; - tti_point dl_pmi_tti_rx{}; - uint32_t dl_cqi = 1; - tti_point dl_cqi_tti_rx{0}; - uint32_t ul_cqi = 1; - tti_point ul_cqi_tti_rx{}; - bool dl_cqi_rx = false; + const sched_dl_cqi& dl_cqi() const { return dl_cqi_ctxt; } + uint32_t dl_ri = 0; + tti_point dl_ri_tti_rx{}; + uint32_t dl_pmi = 0; + tti_point dl_pmi_tti_rx{}; + tti_point ul_cqi_tti_rx{}; uint32_t max_mcs_dl = 28, max_mcs_ul = 28; uint32_t max_aggr_level = 3; int fixed_mcs_ul = 0, fixed_mcs_dl = 0; private: - srslog::basic_logger& logger; + void check_cc_activation(uint32_t dl_cqi); + // args + srslog::basic_logger& logger; const sched_interface::ue_cfg_t* ue_cfg = nullptr; tti_point cfg_tti; int ue_cc_idx = -1; @@ -90,17 +103,28 @@ private: // state tti_point current_tti; cc_st cc_state_ = cc_st::idle; + + // CQI + float ul_delta_inc = 0, ul_delta_dec = 0; + float dl_delta_inc = 0, dl_delta_dec = 0; + float dl_cqi_coeff = 0, ul_snr_coeff = 0; + float max_cqi_coeff = -5, max_snr_coeff = 5; + + sched_dl_cqi dl_cqi_ctxt; }; /************************************************************* * TBS/MCS derivation ************************************************************/ +/// Compute DL grant optimal TBS and MCS given UE cell context and DL grant parameters tbs_info cqi_to_tbs_dl(const sched_ue_cell& cell, - uint32_t nof_prb, + const rbgmask_t& rbgs, uint32_t nof_re, srsran_dci_format_t dci_format, - int req_bytes = -1); + uint32_t req_bytes = std::numeric_limits::max()); + +/// Compute UL grant optimal TBS and MCS given UE cell context and UL grant parameters tbs_info cqi_to_tbs_ul(const sched_ue_cell& cell, uint32_t nof_prb, uint32_t nof_re, int req_bytes = -1, int explicit_mcs = -1); @@ -110,6 +134,19 @@ int get_required_prb_dl(const sched_ue_cell& cell, uint32_t req_bytes); uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes); +tbs_info compute_mcs_and_tbs_lower_bound(const sched_ue_cell& ue_cell, + tti_point tti_tx_dl, + const rbgmask_t& rbg_mask, + srsran_dci_format_t dci_format); + +bool find_optimal_rbgmask(const sched_ue_cell& ue_cell, + tti_point tti_tx_dl, + const rbgmask_t& dl_mask, + srsran_dci_format_t dci_format, + srsran::interval req_bytes, + tbs_info& tb, + rbgmask_t& newtxmask); + } // namespace srsenb #endif // SRSRAN_SCHED_UE_CELL_H diff --git a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h index e191ffefb..23c670251 100644 --- a/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h +++ b/srsenb/hdr/stack/mac/sched_ue_ctrl/tpc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -44,18 +44,27 @@ public: static constexpr uint32_t PUSCH_CODE = 0, PUCCH_CODE = 1; static constexpr int PHR_NEG_NOF_PRB = 1; - explicit tpc(uint32_t cell_nof_prb, - float target_pucch_snr_dB_ = -1.0, - float target_pusch_sn_dB_ = -1.0, - bool phr_handling_flag_ = false) : + explicit tpc(uint16_t rnti_, + uint32_t cell_nof_prb, + float target_pucch_snr_dB_ = -1.0, + float target_pusch_sn_dB_ = -1.0, + bool phr_handling_flag_ = false, + int min_phr_thres_ = 0, + uint32_t min_tpc_tti_interval_ = 1, + float ul_snr_avg_alpha = 0.05, + int init_ul_snr_value = 5) : + rnti(rnti_), nof_prb(cell_nof_prb), target_pucch_snr_dB(target_pucch_snr_dB_), target_pusch_snr_dB(target_pusch_sn_dB_), - snr_estim_list({ul_ch_snr_estim{target_pusch_snr_dB}, ul_ch_snr_estim{target_pucch_snr_dB}}), - phr_handling_flag(phr_handling_flag_) - { - max_prbs_cached = nof_prb; - } + min_phr_thres(min_phr_thres_), + snr_estim_list( + {ul_ch_snr_estim{ul_snr_avg_alpha, init_ul_snr_value}, ul_ch_snr_estim{ul_snr_avg_alpha, init_ul_snr_value}}), + phr_handling_flag(phr_handling_flag_), + max_prbs_cached(nof_prb), + min_tpc_tti_interval(min_tpc_tti_interval_), + logger(srslog::fetch_basic_logger("MAC")) + {} void set_cfg(float target_pusch_snr_dB_, float target_pucch_snr_dB_) { target_pucch_snr_dB = target_pucch_snr_dB_; @@ -64,52 +73,64 @@ public: void set_snr(float snr, uint32_t ul_ch_code) { + static const float MIN_UL_SNR = -4.0f; + if (snr < MIN_UL_SNR) { + // Assume signal was not sent + return; + } if (ul_ch_code < nof_ul_ch_code) { snr_estim_list[ul_ch_code].pending_snr = snr; } } - void set_phr(int phr_) + void set_phr(int phr_, uint32_t grant_nof_prbs) { last_phr = phr_; for (auto& ch_snr : snr_estim_list) { - ch_snr.phr_flag = false; + ch_snr.acc_tpc_phr_values = 0; } // compute and cache the max nof UL PRBs that avoids overflowing PHR if (phr_handling_flag) { max_prbs_cached = PHR_NEG_NOF_PRB; + int phr_x_prb = std::roundf(last_phr + 10.0F * log10f(grant_nof_prbs)); // get what the PHR would be if Nprb=1 for (int n = nof_prb; n > PHR_NEG_NOF_PRB; --n) { - if (last_phr >= 10 * log10(n)) { + if (phr_x_prb >= 10 * log10f(n) + min_phr_thres) { max_prbs_cached = n; break; } } + logger.info("SCHED: rnti=0x%x received PHR=%d for UL Nprb=%d. Max UL Nprb is now=%d", + rnti, + phr_, + grant_nof_prbs, + max_prbs_cached); } } void new_tti() { - for (size_t chidx = 0; chidx < 2; ++chidx) { + tti_count++; + for (size_t chidx = 0; chidx < nof_ul_ch_code; ++chidx) { float target_snr_dB = chidx == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; auto& ch_snr = snr_estim_list[chidx]; - if (target_snr_dB < 0) { - ch_snr.pending_delta = 0; - continue; - } // Enqueue pending UL Channel SNR measurement if (ch_snr.pending_snr == null_snr) { ch_snr.last_snr_sample_count++; ch_snr.acc_tpc_values += ch_snr.win_tpc_values.oldest(); + ch_snr.acc_tpc_phr_values += ch_snr.win_tpc_values.oldest(); } else { ch_snr.acc_tpc_values = 0; ch_snr.snr_avg.push(ch_snr.pending_snr, ch_snr.last_snr_sample_count); + ch_snr.last_snr_sample = ch_snr.pending_snr; ch_snr.last_snr_sample_count = 1; } ch_snr.pending_snr = null_snr; // Enqueue PUSCH/PUCCH TPC sent in last TTI (zero for both Delta_PUSCH/Delta_PUCCH=0 and TPC not sent) - ch_snr.win_tpc_values.push(ch_snr.pending_delta); + if (target_snr_dB >= 0) { + ch_snr.win_tpc_values.push(ch_snr.pending_delta); + } ch_snr.pending_delta = 0; } } @@ -130,6 +151,8 @@ public: uint32_t max_ul_prbs() const { return max_prbs_cached; } + float get_ul_snr_estim(uint32_t ul_ch_code = PUSCH_CODE) const { return snr_estim_list[ul_ch_code].snr_avg.value(); } + private: uint8_t encode_tpc_delta(int8_t delta) { @@ -143,39 +166,71 @@ private: case 3: return 3; default: - srslog::fetch_basic_logger("MAC").warning("Invalid TPC delta value=%d", delta); + logger.warning("Invalid TPC delta value=%d", delta); return 1; } } uint8_t encode_tpc(uint32_t cc) { - float target_snr_dB = cc == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; - auto& ch_snr = snr_estim_list[cc]; + auto& ch_snr = snr_estim_list[cc]; assert(ch_snr.pending_delta == 0); // ensure called once per {cc,tti} + + float target_snr_dB = cc == PUSCH_CODE ? target_pusch_snr_dB : target_pucch_snr_dB; if (target_snr_dB < 0) { - // undefined target sinr case. - ch_snr.pending_delta = 0; - } else { - // target SINR is finite and there is power headroom - float diff = target_snr_dB - ch_snr.snr_avg.value(); - diff -= ch_snr.win_tpc_values.value() + ch_snr.acc_tpc_values; - int8_t diff_round = roundf(diff); - if (diff_round >= 1) { - ch_snr.pending_delta = diff_round > 3 ? 3 : 1; - } else if (diff_round <= -1) { - ch_snr.pending_delta = -1; + // undefined target SINR case + return encode_tpc_delta(0); + } + + // limitation of TPC based on PHR + int max_delta = 3; + int eff_phr = last_phr; + if (cc == PUSCH_CODE and last_phr != undefined_phr) { + eff_phr -= ch_snr.win_tpc_values.value() + ch_snr.acc_tpc_phr_values; + max_delta = std::min(max_delta, eff_phr); + } + + float snr = ch_snr.last_snr_sample; + // In case of periodicity of TPCs is > 1 tti, use average SNR to compute SNR diff + if (min_tpc_tti_interval > 1) { + ch_snr.tpc_snr_avg.push(snr); + if ((tti_count - ch_snr.last_tpc_tti_count) < min_tpc_tti_interval) { + // more time required before sending next TPC + return encode_tpc_delta(0); } - if (last_phr <= 0) { - // In case there is no headroom, forbid power increases - ch_snr.pending_delta = std::min(ch_snr.pending_delta, 0); + snr = ch_snr.tpc_snr_avg.value(); + } + + float diff = target_snr_dB - snr; + diff -= ch_snr.win_tpc_values.value() + ch_snr.acc_tpc_values; + ch_snr.pending_delta = std::max(std::min((int)floorf(diff), max_delta), -1); + ch_snr.pending_delta = (ch_snr.pending_delta == 2) ? 1 : ch_snr.pending_delta; + if (ch_snr.pending_delta != 0) { + if (min_tpc_tti_interval > 1) { + ch_snr.last_tpc_tti_count = tti_count; + ch_snr.tpc_snr_avg.reset(); } + logger.debug("TPC: rnti=0x%x, %s command=%d, last SNR=%d, SNR average=%f, diff_acc=%f, eff_phr=%d", + rnti, + cc == PUSCH_CODE ? "PUSCH" : "PUCCH", + encode_tpc_delta(ch_snr.pending_delta), + ch_snr.last_snr_sample, + ch_snr.snr_avg.value(), + diff, + eff_phr); } return encode_tpc_delta(ch_snr.pending_delta); } - uint32_t nof_prb; - float target_pucch_snr_dB, target_pusch_snr_dB; - bool phr_handling_flag; + uint16_t rnti; + uint32_t nof_prb; + uint32_t min_tpc_tti_interval = 1; + float target_pucch_snr_dB, target_pusch_snr_dB; + int min_phr_thres; + bool phr_handling_flag; + srslog::basic_logger& logger; + + // state + uint32_t tti_count = 0; // PHR-related variables int last_phr = undefined_phr; @@ -183,20 +238,24 @@ private: // SNR estimation struct ul_ch_snr_estim { - // flag used in undefined target SINR case - bool phr_flag = false; // pending new snr sample float pending_snr = srsran::null_sliding_average::null_value(); // SNR average estimation with irregular sample spacing uint32_t last_snr_sample_count = 1; // jump in spacing srsran::exp_average_irreg_sampling snr_avg; + int last_snr_sample; // Accumulation of past TPC commands - srsran::sliding_sum win_tpc_values; - int pending_delta = 0; - int acc_tpc_values = 0; + srsran::sliding_sum win_tpc_values; + int acc_tpc_values = 0; + int acc_tpc_phr_values = 0; + int8_t pending_delta = 0; + uint32_t last_tpc_tti_count = 0; + srsran::rolling_average tpc_snr_avg; // average of SNRs since last TPC != 1 - explicit ul_ch_snr_estim(float target_ul_snr = -1) : - snr_avg(0.1, target_ul_snr), win_tpc_values(FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS) + explicit ul_ch_snr_estim(float exp_avg_alpha, int initial_snr) : + snr_avg(exp_avg_alpha, initial_snr), + win_tpc_values(FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS), + last_snr_sample(initial_snr) {} }; std::array snr_estim_list; diff --git a/srsenb/hdr/stack/mac/schedulers/sched_base.h b/srsenb/hdr/stack/mac/schedulers/sched_base.h index dcf0ec1ce..2ab353581 100644 --- a/srsenb/hdr/stack/mac/schedulers/sched_base.h +++ b/srsenb/hdr/stack/mac/schedulers/sched_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,6 +23,7 @@ #define SRSRAN_SCHED_BASE_H #include "srsenb/hdr/stack/mac/sched_grid.h" +#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h" namespace srsenb { @@ -43,25 +44,6 @@ protected: /**************** Helper methods ****************/ -rbg_interval find_empty_rbg_interval(uint32_t max_nof_rbgs, const rbgmask_t& current_mask); - -/** - * Finds a bitmask of available RBG resources for a given UE in a greedy fashion - * @param ue UE being allocated - * @param is_contiguous whether to find a contiguous range of RBGs - * @param current_mask bitmask of occupied RBGs, where to search for available RBGs - * @return bitmask of found RBGs. If a valid mask wasn't found, bitmask::size() == 0 - */ -rbgmask_t compute_rbgmask_greedy(uint32_t max_nof_rbgs, bool is_contiguous, const rbgmask_t& current_mask); - -/** - * Finds a range of L contiguous PRBs that are empty - * @param L Size of the requested UL PRBs - * @param current_mask input prb mask where to search for available PRBs - * @return found interval of PRBs - */ -prb_interval find_contiguous_ul_prbs(uint32_t L, const prbmask_t& current_mask); - const dl_harq_proc* get_dl_retx_harq(sched_ue& user, sf_sched* tti_sched); const dl_harq_proc* get_dl_newtx_harq(sched_ue& user, sf_sched* tti_sched); const ul_harq_proc* get_ul_retx_harq(sched_ue& user, sf_sched* tti_sched); diff --git a/srsenb/hdr/stack/mac/schedulers/sched_time_pf.h b/srsenb/hdr/stack/mac/schedulers/sched_time_pf.h index a94bd3086..7901cc0ef 100644 --- a/srsenb/hdr/stack/mac/schedulers/sched_time_pf.h +++ b/srsenb/hdr/stack/mac/schedulers/sched_time_pf.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -73,7 +73,7 @@ private: uint32_t ul_nof_samples = 0; }; - srsran::static_circular_map ue_history_db; + rnti_map_t ue_history_db; struct ue_dl_prio_compare { bool operator()(const ue_ctxt* lhs, const ue_ctxt* rhs) const; diff --git a/srsenb/hdr/stack/mac/schedulers/sched_time_rr.h b/srsenb/hdr/stack/mac/schedulers/sched_time_rr.h index 9b972d142..0de7e9346 100644 --- a/srsenb/hdr/stack/mac/schedulers/sched_time_rr.h +++ b/srsenb/hdr/stack/mac/schedulers/sched_time_rr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/hdr/stack/mac/ta.h b/srsenb/hdr/stack/mac/ta.h index 8bdeb6134..95712353f 100644 --- a/srsenb/hdr/stack/mac/ta.h +++ b/srsenb/hdr/stack/mac/ta.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -76,6 +76,8 @@ private: float ta_us; ///< TA measurement in microseconds } ta_meas_t; + std::mutex mutex; + uint32_t meas_t_ms = 0; ///< Time counter in milliseconds uint32_t meas_count = 0; ///< Number of measures in the buffer uint32_t meas_idx = 0; ///< Next mesurement index in the buffer @@ -206,6 +208,7 @@ public: */ void start() { + std::lock_guard lock(mutex); // Transition to idle only if the current state is idle if (state == state_idle) { state = state_measure; @@ -220,6 +223,7 @@ public: */ uint32_t push_value(float ta_us) { + std::lock_guard lock(mutex); // Put measurement if state is measurement if (state == state_measure) { // Set measurement @@ -247,6 +251,7 @@ public: */ uint32_t tick() { + std::lock_guard lock(mutex); // Increase measurement timestamp counter meas_t_ms++; diff --git a/srsenb/hdr/stack/mac/ue.h b/srsenb/hdr/stack/mac/ue.h index 79e8a0f0e..47b73f1c1 100644 --- a/srsenb/hdr/stack/mac/ue.h +++ b/srsenb/hdr/stack/mac/ue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,7 +22,8 @@ #ifndef SRSENB_UE_H #define SRSENB_UE_H -#include "mac_metrics.h" +#include "common/mac_metrics.h" +#include "sched_interface.h" #include "srsran/adt/circular_array.h" #include "srsran/adt/circular_map.h" #include "srsran/adt/pool/pool_interface.h" @@ -30,7 +31,6 @@ #include "srsran/common/mac_pcap.h" #include "srsran/common/mac_pcap_net.h" #include "srsran/common/tti_point.h" -#include "srsran/interfaces/sched_interface.h" #include "srsran/mac/pdu.h" #include "srsran/mac/pdu_queue.h" #include "srsran/srslog/srslog.h" @@ -45,6 +45,7 @@ class rrc_interface_mac; class rlc_interface_mac; class phy_interface_stack_lte; +/// Class to manage the allocation, deallocation & access to UE carrier DL + UL softbuffers struct ue_cc_softbuffers { // List of Tx softbuffers for all HARQ processes of one carrier using cc_softbuffer_tx_list_t = std::vector; @@ -68,39 +69,36 @@ struct ue_cc_softbuffers { srsran_softbuffer_rx_t& get_rx(uint32_t tti) { return softbuffer_rx_list.at(tti % nof_rx_harq_proc); } }; +/// Class to manage the allocation, deallocation & access to pending UL HARQ buffers class cc_used_buffers_map { public: - explicit cc_used_buffers_map(srsran::pdu_queue& shared_pdu_queue_); + explicit cc_used_buffers_map(); ~cc_used_buffers_map(); + void clear() { pdu_map.clear(); } + uint8_t* request_pdu(tti_point tti, uint32_t len); - bool push_pdu(tti_point tti, uint32_t len); + srsran::unique_byte_buffer_t release_pdu(tti_point tti); void clear_old_pdus(tti_point current_tti); - bool try_deallocate_pdu(tti_point tti); - - void clear(); - uint8_t*& operator[](tti_point tti); bool has_tti(tti_point tti) const; private: - void remove_pdu(tti_point tti); - srslog::basic_logger* logger; - srsran::pdu_queue* shared_pdu_queue; + std::mutex mutex; - srsran::static_circular_map pdu_map; + srsran::static_circular_map pdu_map; }; class cc_buffer_handler { public: - explicit cc_buffer_handler(srsran::pdu_queue& shared_pdu_queue_); + explicit cc_buffer_handler(); ~cc_buffer_handler(); void reset(); @@ -126,15 +124,15 @@ private: // buffers cc_used_buffers_map rx_used_buffers; - // One buffer per TB per HARQ process and per carrier is needed for each UE. + // One buffer per TB per DL HARQ process and per carrier is needed for each UE. std::array, SRSRAN_FDD_NOF_HARQ> tx_payload_buffer; }; -class ue : public srsran::read_pdu_interface, public srsran::pdu_queue::process_callback, public mac_ta_ue_interface +class ue : public srsran::read_pdu_interface, public mac_ta_ue_interface { public: ue(uint16_t rnti, - uint32_t nof_prb, + uint32_t enb_cc_idx, sched_interface* sched, rrc_interface_mac* rrc_, rlc_interface_mac* rlc, @@ -144,50 +142,55 @@ public: srsran::obj_pool_itf* softbuffer_pool); virtual ~ue(); - void reset(); - void start_pcap(srsran::mac_pcap* pcap_); - void start_pcap_net(srsran::mac_pcap_net* pcap_net_); + void reset(); + void start_pcap(srsran::mac_pcap* pcap_); + void start_pcap_net(srsran::mac_pcap_net* pcap_net_); + void ue_cfg(const sched_interface::ue_cfg_t& ue_cfg); + void set_tti(uint32_t tti); - uint16_t get_rnti() { return rnti; } + uint16_t get_rnti() const { return rnti; } uint32_t set_ta(int ta) override; void start_ta() { ta_fsm.start(); }; uint32_t set_ta_us(float ta_us) { return ta_fsm.push_value(ta_us); }; void tic(); + void trigger_padding(int lcid); + void set_active(bool active) { active_state.store(active, std::memory_order_relaxed); } + bool is_active() const { return active_state.load(std::memory_order_relaxed); } - uint8_t* generate_pdu(uint32_t ue_cc_idx, + uint8_t* generate_pdu(uint32_t enb_cc_idx, uint32_t harq_pid, uint32_t tb_idx, const sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST], uint32_t nof_pdu_elems, uint32_t grant_size); - uint8_t* - generate_mch_pdu(uint32_t harq_pid, sched_interface::dl_pdu_mch_t sched, uint32_t nof_pdu_elems, uint32_t grant_size); + uint8_t* generate_mch_pdu(uint32_t harq_pid, + const sched_interface::dl_pdu_mch_t& sched, + uint32_t nof_pdu_elems, + uint32_t grant_size); - srsran_softbuffer_tx_t* - get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx); - srsran_softbuffer_rx_t* get_rx_softbuffer(const uint32_t ue_cc_idx, const uint32_t tti); + srsran_softbuffer_tx_t* get_tx_softbuffer(uint32_t enb_cc_idx, uint32_t harq_process, uint32_t tb_idx); + srsran_softbuffer_rx_t* get_rx_softbuffer(uint32_t enb_cc_idx, uint32_t tti); - bool process_pdus(); - uint8_t* request_buffer(uint32_t tti, uint32_t ue_cc_idx, const uint32_t len); - void process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel) override; - void push_pdu(uint32_t tti, uint32_t ue_cc_idx, uint32_t len); - void deallocate_pdu(uint32_t tti, uint32_t ue_cc_idx); - void clear_old_buffers(uint32_t tti); + uint8_t* request_buffer(uint32_t tti, uint32_t enb_cc_idx, uint32_t len); + void process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t ue_cc_idx, uint32_t grant_nof_prbs); + srsran::unique_byte_buffer_t release_pdu(uint32_t tti, uint32_t enb_cc_idx); + void clear_old_buffers(uint32_t tti); - void metrics_read(mac_ue_metrics_t* metrics_); - void metrics_rx(bool crc, uint32_t tbs); - void metrics_tx(bool crc, uint32_t tbs); - void metrics_phr(float phr); - void metrics_dl_ri(uint32_t dl_cqi); - void metrics_dl_pmi(uint32_t dl_cqi); - void metrics_dl_cqi(uint32_t dl_cqi); - void metrics_cnt(); + std::mutex metrics_mutex = {}; + void metrics_read(mac_ue_metrics_t* metrics_); + void metrics_rx(bool crc, uint32_t tbs); + void metrics_tx(bool crc, uint32_t tbs); + void metrics_phr(float phr); + void metrics_dl_ri(uint32_t dl_cqi); + void metrics_dl_pmi(uint32_t dl_cqi); + void metrics_dl_cqi(uint32_t dl_cqi); + void metrics_cnt(); - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) final; + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) final; private: void allocate_sdu(srsran::sch_pdu* pdu, uint32_t lcid, uint32_t sdu_len); - bool process_ce(srsran::sch_subh* subh); + bool process_ce(srsran::sch_subh* subh, uint32_t grant_nof_prbs); void allocate_ce(srsran::sch_pdu* pdu, uint32_t lcid); rlc_interface_mac* rlc = nullptr; @@ -196,13 +199,15 @@ private: srslog::basic_logger& logger; sched_interface* sched = nullptr; - srsran::mac_pcap* pcap = nullptr; - srsran::mac_pcap_net* pcap_net = nullptr; - uint64_t conres_id = 0; - uint16_t rnti = 0; - uint32_t last_tti = 0; + srsran::mac_pcap* pcap = nullptr; + srsran::mac_pcap_net* pcap_net = nullptr; + uint64_t conres_id = 0; + uint16_t rnti = 0; + std::atomic last_tti{0}; uint32_t nof_failures = 0; + std::atomic active_state{true}; + uint32_t phr_counter = 0; uint32_t dl_cqi_counter = 0; uint32_t dl_ri_counter = 0; @@ -215,17 +220,13 @@ private: ta ta_fsm; // For UL there are multiple buffers per PID and are managed by pdu_queue - srsran::pdu_queue pdus; - srsran::sch_pdu mac_msg_dl, mac_msg_ul; - srsran::mch_pdu mch_mac_msg_dl; + srsran::sch_pdu mac_msg_dl, mac_msg_ul; + srsran::mch_pdu mch_mac_msg_dl; srsran::bounded_vector cc_buffers; // Mutexes std::mutex mutex; - std::mutex rx_buffers_mutex; - - const uint8_t UL_CC_IDX = 0; ///< Passed to write CC index in PCAP (TODO: use actual CC idx) }; } // namespace srsenb diff --git a/srsenb/hdr/stack/rrc/mac_controller.h b/srsenb/hdr/stack/rrc/mac_controller.h index 3bfc01a0f..61f4040e9 100644 --- a/srsenb/hdr/stack/rrc/mac_controller.h +++ b/srsenb/hdr/stack/rrc/mac_controller.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,8 +24,8 @@ #include "rrc_bearer_cfg.h" #include "rrc_cell_cfg.h" +#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/interfaces/rrc_interface_types.h" -#include "srsran/interfaces/sched_interface.h" #include namespace srsenb { @@ -64,16 +64,16 @@ public: const srsran::rrc_ue_capabilities_t& uecaps); void handle_ho_prep(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep); - void handle_max_retx(); + void set_radio_bearer_state(mac_lc_ch_cfg_t::direction_t dir); const ue_cfg_t& get_ue_sched_cfg() const { return current_sched_ue_cfg; } bool is_crnti_set() const { return crnti_set; } void set_scell_activation(const std::bitset& scell_mask); + void set_srb2_activation(bool active); void set_drb_activation(bool active); - enum proc_stage_t : int8_t { config_tx, config_complete, other }; - void update_mac(proc_stage_t stage); + void update_mac(); private: int apply_basic_conn_cfg(const asn1::rrc::rr_cfg_ded_s& rr_cfg); diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index cb1ef1d92..fbb722a7d 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,12 +28,17 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsenb/hdr/common/rnti_pool.h" #include "srsran/adt/circular_buffer.h" +#include "srsran/common/bearer_manager.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" #include "srsran/common/stack_procedure.h" #include "srsran/common/task_scheduler.h" #include "srsran/common/timeout.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" +#include "srsran/interfaces/enb_rrc_interface_pdcp.h" +#include "srsran/interfaces/enb_rrc_interface_rlc.h" +#include "srsran/interfaces/enb_rrc_interface_s1ap.h" +#include "srsran/interfaces/enb_x2_interfaces.h" #include "srsran/srslog/srslog.h" #include @@ -45,21 +50,16 @@ class rlc_interface_rrc; class mac_interface_rrc; class phy_interface_rrc_lte; -static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", - "WAIT FOR CON SETUP COMPLETE", - "WAIT FOR SECURITY MODE COMPLETE", - "WAIT FOR UE CAPABILITIY INFORMATION", - "WAIT FOR CON RECONF COMPLETE", - "RRC CONNECTED", - "RELEASE REQUEST"}; +class paging_manager; class rrc final : public rrc_interface_pdcp, public rrc_interface_mac, public rrc_interface_rlc, - public rrc_interface_s1ap + public rrc_interface_s1ap, + public rrc_eutra_interface_rrc_nr { public: - explicit rrc(srsran::task_sched_handle task_sched_); + explicit rrc(srsran::task_sched_handle task_sched_, enb_bearer_manager& manager_); ~rrc(); int32_t init(const rrc_cfg_t& cfg_, @@ -70,6 +70,15 @@ public: s1ap_interface_rrc* s1ap, gtpu_interface_rrc* gtpu); + int32_t init(const rrc_cfg_t& cfg_, + phy_interface_rrc_lte* phy, + mac_interface_rrc* mac, + rlc_interface_rrc* rlc, + pdcp_interface_rrc* pdcp, + s1ap_interface_rrc* s1ap, + gtpu_interface_rrc* gtpu, + rrc_nr_interface_rrc* rrc_nr); + void stop(); void get_metrics(rrc_metrics_t& m); void tti_clock(); @@ -84,8 +93,9 @@ public: uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override; // rrc_interface_rlc - void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) override; + void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size) override; void max_retx_attempted(uint16_t rnti) override; + void protocol_failure(uint16_t rnti) override; // rrc_interface_s1ap void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) override; @@ -109,7 +119,7 @@ public: asn1::s1ap::cause_c& cause) override; bool release_erabs(uint32_t rnti) override; int release_erab(uint16_t rnti, uint16_t erab_id) override; - void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& UEPagingID) override; + void add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) override; void ho_preparation_complete(uint16_t rnti, rrc::ho_prep_result result, const asn1::s1ap::ho_cmd_s& msg, @@ -121,60 +131,64 @@ public: int notify_ue_erab_updates(uint16_t rnti, srsran::const_byte_span nas_pdu) override; + // rrc_eutra_interface_rrc_nr + void sgnb_addition_ack(uint16_t eutra_rnti, const sgnb_addition_ack_params_t params) override; + void sgnb_addition_reject(uint16_t eutra_rnti) override; + void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) override; + void sgnb_inactivity_timeout(uint16_t eutra_rnti) override; + void sgnb_release_ack(uint16_t eutra_rnti) override; + // rrc_interface_pdcp void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override; + void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) override; uint32_t get_nof_users(); // logging - typedef enum { Rx = 0, Tx, toS1AP, fromS1AP } direction_t; + enum direction_t { Rx = 0, Tx, toS1AP, fromS1AP }; template - void log_rrc_message(const std::string& source, - const direction_t dir, - const srsran::byte_buffer_t* pdu, - const T& msg, - const std::string& msg_type) - { - log_rrc_message(source, dir, srsran::make_span(*pdu), msg, msg_type); - } - template - void log_rrc_message(const std::string& source, - const direction_t dir, + void log_rrc_message(const direction_t dir, + uint16_t rnti, + uint32_t lcid, srsran::const_byte_span pdu, const T& msg, - const std::string& msg_type) + const char* msg_type) { - static const char* dir_str[] = {"Rx", "Tx", "S1AP Tx", "S1AP Rx"}; + log_rxtx_pdu_impl(dir, rnti, lcid, pdu, msg_type); if (logger.debug.enabled()) { asn1::json_writer json_writer; msg.to_json(json_writer); - logger.debug( - pdu.data(), pdu.size(), "%s - %s %s (%zd B)", source.c_str(), dir_str[dir], msg_type.c_str(), pdu.size()); logger.debug("Content:\n%s", json_writer.to_string().c_str()); - } else if (logger.info.enabled()) { - logger.info("%s - %s %s (%zd B)", source.c_str(), dir_str[dir], msg_type.c_str(), pdu.size()); } } + template + void log_broadcast_rrc_message(uint16_t rnti, srsran::const_byte_span pdu, const T& msg, const char* msg_type) + { + log_rrc_message(Tx, rnti, -1, pdu, msg, msg_type); + } + + class ue; private: - class ue; // args srsran::task_sched_handle task_sched; - phy_interface_rrc_lte* phy = nullptr; - mac_interface_rrc* mac = nullptr; - rlc_interface_rrc* rlc = nullptr; - pdcp_interface_rrc* pdcp = nullptr; - gtpu_interface_rrc* gtpu = nullptr; - s1ap_interface_rrc* s1ap = nullptr; + enb_bearer_manager& bearer_manager; + phy_interface_rrc_lte* phy = nullptr; + mac_interface_rrc* mac = nullptr; + rlc_interface_rrc* rlc = nullptr; + pdcp_interface_rrc* pdcp = nullptr; + gtpu_interface_rrc* gtpu = nullptr; + s1ap_interface_rrc* s1ap = nullptr; + rrc_nr_interface_rrc* rrc_nr = nullptr; srslog::basic_logger& logger; // derived params std::unique_ptr cell_common_list; // state - std::unique_ptr cell_res_list; - std::map > users; // NOTE: has to have fixed addr - std::map pending_paging; + std::unique_ptr cell_res_list; + std::map > users; // NOTE: has to have fixed addr + std::unique_ptr pending_paging; void process_release_complete(uint16_t rnti); void rem_user(uint16_t rnti); @@ -183,29 +197,31 @@ private: int pack_mcch(); void config_mac(); - void parse_ul_dcch(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu); - void parse_ul_ccch(uint16_t rnti, srsran::unique_byte_buffer_t pdu); + void parse_ul_dcch(ue& ue, uint32_t lcid, srsran::unique_byte_buffer_t pdu); + void parse_ul_ccch(ue& ue, srsran::unique_byte_buffer_t pdu); void send_rrc_connection_reject(uint16_t rnti); - uint32_t paging_tti = INVALID_TTI; - srsran::byte_buffer_t byte_buf_paging; - const static int mcch_payload_len = 3000; - int current_mcch_length = 0; - uint8_t mcch_payload_buffer[mcch_payload_len] = {}; - typedef struct { + const static int mcch_payload_len = 3000; + int current_mcch_length = 0; + uint8_t mcch_payload_buffer[mcch_payload_len] = {}; + struct rrc_pdu { uint16_t rnti; uint32_t lcid; uint32_t arg; srsran::unique_byte_buffer_t pdu; - } rrc_pdu; + }; + void log_rx_pdu_fail(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu, const char* cause); + void + log_rxtx_pdu_impl(direction_t dir, uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu, const char* msg_type); - const static uint32_t LCID_EXIT = 0xffff0000; - const static uint32_t LCID_REM_USER = 0xffff0001; - const static uint32_t LCID_REL_USER = 0xffff0002; - const static uint32_t LCID_ACT_USER = 0xffff0004; - const static uint32_t LCID_RTX_USER = 0xffff0005; - const static uint32_t LCID_RADLINK_DL = 0xffff0006; - const static uint32_t LCID_RADLINK_UL = 0xffff0007; + const static uint32_t LCID_EXIT = 0xffff0000; + const static uint32_t LCID_REM_USER = 0xffff0001; + const static uint32_t LCID_REL_USER = 0xffff0002; + const static uint32_t LCID_ACT_USER = 0xffff0004; + const static uint32_t LCID_RLC_RTX = 0xffff0005; + const static uint32_t LCID_RADLINK_DL = 0xffff0006; + const static uint32_t LCID_RADLINK_UL = 0xffff0007; + const static uint32_t LCID_PROT_FAIL = 0xffff0008; bool running = false; srsran::dyn_blocking_queue rx_pdu_queue; @@ -217,8 +233,6 @@ private: asn1::rrc::sib_type7_s sib7; void rem_user_thread(uint16_t rnti); - - std::mutex paging_mutex; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h b/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h index 16a9a5743..682624eb4 100644 --- a/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h +++ b/srsenb/hdr/stack/rrc/rrc_bearer_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,6 +35,16 @@ class security_cfg_handler { public: explicit security_cfg_handler(const rrc_cfg_t& cfg_) : cfg(&cfg_), logger(srslog::fetch_basic_logger("RRC")) {} + explicit security_cfg_handler(const security_cfg_handler& other) : logger(srslog::fetch_basic_logger("RRC")) + { + cfg = other.cfg; + k_enb_present = other.k_enb_present; + security_capabilities = other.security_capabilities; + std::copy(other.k_enb, other.k_enb + 32, k_enb); + sec_cfg = other.sec_cfg; + ncc = other.ncc; + } + security_cfg_handler& operator=(const security_cfg_handler& other) { cfg = other.cfg; @@ -92,12 +102,12 @@ public: /// Called after RRCReestablishmentComplete, to add E-RABs of old rnti void reestablish_bearers(bearer_cfg_handler&& old_rnti_bearers); - int add_erab(uint8_t erab_id, - const asn1::s1ap::erab_level_qos_params_s& qos, - const asn1::bounded_bitstring<1, 160, true, true>& addr, - uint32_t teid_out, - srsran::const_span nas_pdu, - asn1::s1ap::cause_c& cause); + int addmod_erab(uint8_t erab_id, + const asn1::s1ap::erab_level_qos_params_s& qos, + const asn1::bounded_bitstring<1, 160, true, true>& addr, + uint32_t teid_out, + srsran::const_span nas_pdu, + asn1::s1ap::cause_c& cause); int release_erab(uint8_t erab_id); void release_erabs(); int modify_erab(uint8_t erab_id, diff --git a/srsenb/hdr/stack/rrc/rrc_cell_cfg.h b/srsenb/hdr/stack/rrc/rrc_cell_cfg.h index e8494b56c..108e59e33 100644 --- a/srsenb/hdr/stack/rrc/rrc_cell_cfg.h +++ b/srsenb/hdr/stack/rrc/rrc_cell_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -82,10 +82,10 @@ public: const static uint32_t N_PUCCH_MAX_PRB = 4; // Maximum number of PRB to use for PUCCH ACK/NACK in CS mode const static uint32_t N_PUCCH_MAX_RES = 3 * SRSRAN_NRE * N_PUCCH_MAX_PRB; - uint32_t next_measgap_offset = 0; - pucch_idx_sched_t sr_sched = {}; - pucch_idx_sched_t cqi_sched = {}; - std::array n_pucch_cs_used = {}; + pucch_idx_sched_t sr_sched = {}; + pucch_idx_sched_t cqi_sched = {}; + std::array n_pucch_cs_used = {}; + std::array meas_gap_alloc_map = {}; }; /** Storage of CQI/SR/PUCCH CS resources across multiple frequencies and for multiple users */ @@ -136,8 +136,9 @@ public: const enb_cell_common_list& enb_common_list); ~ue_cell_ded_list(); - ue_cell_ded* add_cell(uint32_t enb_cc_idx); + ue_cell_ded* add_cell(uint32_t enb_cc_idx, bool init_pucch = true); bool rem_last_cell(); + bool init_pucch_pcell(); bool set_cells(const std::vector& enb_cc_idxs); ue_cell_ded* get_ue_cc_idx(uint32_t ue_cc_idx) { return (ue_cc_idx < nof_cells()) ? &cell_list[ue_cc_idx] : nullptr; } diff --git a/srsenb/hdr/stack/rrc/rrc_config.h b/srsenb/hdr/stack/rrc/rrc_config.h index f8b32363d..5b2ed34cf 100644 --- a/srsenb/hdr/stack/rrc/rrc_config.h +++ b/srsenb/hdr/stack/rrc/rrc_config.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -40,12 +40,30 @@ struct rrc_cfg_sr_t { }; struct rrc_cfg_qci_t { - bool configured = false; + bool configured = false; + int enb_dl_max_retx_thres = -1; asn1::rrc::lc_ch_cfg_s::ul_specific_params_s_ lc_cfg; asn1::rrc::pdcp_cfg_s pdcp_cfg; asn1::rrc::rlc_cfg_c rlc_cfg; }; +struct srb_cfg_t { + int enb_dl_max_retx_thres = -1; + asn1::rrc::srb_to_add_mod_s::rlc_cfg_c_ rlc_cfg; +}; + +// Parameter required for NR cell measurement handling +struct rrc_endc_cfg_t { + bool act_from_b1_event; + uint32_t abs_frequency_ssb; + uint32_t nr_band; + using ssb_nr_cfg = asn1::rrc::mtc_ssb_nr_r15_s; + using ssb_rs_cfg = asn1::rrc::rs_cfg_ssb_nr_r15_s; + ssb_nr_cfg::periodicity_and_offset_r15_c_ ssb_period_offset; + ssb_nr_cfg::ssb_dur_r15_e_ ssb_duration; + ssb_rs_cfg::subcarrier_spacing_ssb_r15_e_ ssb_ssc; +}; + struct rrc_cfg_t { uint32_t enb_id; ///< Required to pack SIB1 // Per eNB SIBs @@ -67,9 +85,13 @@ struct rrc_cfg_t { bool meas_cfg_present = false; srsran_cell_t cell; cell_list_t cell_list; - cell_list_t cell_list_nr; - uint32_t max_mac_dl_kos; - uint32_t max_mac_ul_kos; + uint32_t num_nr_cells = 0; /// number of configured NR cells (used to configure RF) + uint32_t max_mac_dl_kos; + uint32_t max_mac_ul_kos; + uint32_t rlf_release_timer_ms; + srb_cfg_t srb1_cfg; + srb_cfg_t srb2_cfg; + rrc_endc_cfg_t endc_cfg; }; constexpr uint32_t UE_PCELL_CC_IDX = 0; diff --git a/srsenb/hdr/stack/rrc/rrc_config_common.h b/srsenb/hdr/stack/rrc/rrc_config_common.h index 78e9bab3b..be1bf6f2e 100644 --- a/srsenb/hdr/stack/rrc/rrc_config_common.h +++ b/srsenb/hdr/stack/rrc/rrc_config_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,9 +37,10 @@ inline const char* to_string(rrc_cfg_cqi_mode_t mode) struct rrc_cfg_cqi_t { uint32_t sf_mapping[80]; uint32_t nof_subframes; - uint32_t nof_prb; uint32_t period; uint32_t m_ri; + bool is_subband_enabled; + uint32_t subband_k; bool simultaneousAckCQI; rrc_cfg_cqi_mode_t mode; }; diff --git a/srsenb/hdr/stack/rrc/rrc_endc.h b/srsenb/hdr/stack/rrc/rrc_endc.h new file mode 100644 index 000000000..ce429e01a --- /dev/null +++ b/srsenb/hdr/stack/rrc/rrc_endc.h @@ -0,0 +1,186 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_RRC_ENDC_H +#define SRSENB_RRC_ENDC_H + +#include "rrc.h" +#include "rrc_ue.h" +#include "srsran/adt/fsm.h" +#include + +namespace srsenb { + +/** + * @brief This procedure handles the secondary node (SgNB) addition for + * EUTRA-NR Dual connectivity (ENDC) + * + */ + +class rrc::ue::rrc_endc : public srsran::fsm_t +{ +public: + // public events called from EUTRA-RRC + struct sgnb_add_req_sent_ev {}; + + /// called when 5G RRC accepted new user + struct sgnb_add_req_ack_ev { + sgnb_addition_ack_params_t params; + }; + struct sgnb_add_req_reject_ev {}; + /// Called when Reconf fails + struct rrc_reest_rx_ev {}; + + /** + * @brief Non-standard event sent from NR-RRC to EUTRA when UE has attached to NR cell + * + * sent after Reconfig complete and contention resolution on NR side + */ + struct sgnb_add_complete_ev { + uint16_t nr_rnti; /// RNTI assigned to UE on NR carrier + }; + + /** + * @brief Called from EUTRA-RRC when EN-DC (for UE) shall be removed/released + * + */ + struct sgnb_rel_req_ev { + uint16_t nr_rnti; /// RNTI assigned to UE on NR carrier + }; + + /** + * @brief Event sent from NR-RRC to EUTRA when UE has been removed from SgNB + * + * sent in response to SgNB Release Request + */ + struct sgnb_rel_req_ack_ev {}; + + /** + * @brief in case of non supported NR carrier in UE capabilities or the NR reconfiguration failed + */ + struct disable_endc_ev {}; + + rrc_endc(srsenb::rrc::ue* outer_ue, const rrc_endc_cfg_t& endc_cfg_); + ~rrc_endc(); + + bool fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg); + void handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps); + void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg); + + void start_sgnb_release(); + + bool is_endc_supported(); + +private: + // Send SgNB addition request to gNB (either triggered through MeasReport or upon start) + void start_sgnb_addition(); + + bool is_endc_activation_running() const { return not is_in_state(); } + + rrc::ue* rrc_ue = nullptr; + rrc* rrc_enb = nullptr; + srslog::basic_logger& logger; + + // nr ids to deactivate in second reconfig + uint32_t nr_meas_obj_id = 0; + uint32_t nr_report_cfg_id = 0; + uint32_t nr_meas_id = 0; + + // vars + rrc_endc_cfg_t endc_cfg = {}; + uint16_t nr_rnti = SRSRAN_INVALID_RNTI; // C-RNTI assigned to UE on NR side + asn1::rrc::rrc_conn_recfg_complete_s pending_recfg_complete; + + // fixed ENDC variables + const uint32_t lcid_drb_nr = 4; + + // internal events + struct rrc_recfg_sent_ev {}; + + // states + struct endc_deactivated_st {}; // initial state where user is served over EUTRA only + struct wait_sgnb_add_req_resp_st {}; + struct prepare_recfg_st { + sgnb_addition_ack_params_t sgnb_config; // Store NR cell group config, etc. + + void enter(rrc_endc* f, const sgnb_add_req_ack_ev& ev); + + explicit prepare_recfg_st(rrc_endc* parent_); + + private: + srslog::basic_logger& logger; + }; + struct wait_add_complete_st {}; // user needs to complete RA procedure and send C-RNTI CE + struct endc_activated_st {}; // user has enabled EN-DC successfully and is currently served + struct wait_sgnb_rel_req_resp_st {}; // release EN-DC + struct endc_disabled_st {}; // EN-DC disabled + + // FSM guards + bool requires_rel_req(const sgnb_rel_req_ev& ev); + + // FSM transition handlers + void handle_sgnb_add_req_ack(wait_sgnb_add_req_resp_st& s, const sgnb_add_req_ack_ev& ev); + void handle_sgnb_rel_req(const sgnb_rel_req_ev& ev); + void handle_rrc_reest(endc_activated_st& s, const rrc_reest_rx_ev& ev); + void handle_endc_disabled(const disable_endc_ev& ev); + +protected: + // states + state_list + states{this, + endc_deactivated_st{}, + wait_sgnb_add_req_resp_st{}, + prepare_recfg_st{this}, + wait_add_complete_st{}, + endc_activated_st{}, + wait_sgnb_rel_req_resp_st{}, + endc_disabled_st{}}; + + // transitions + using fsm = rrc_endc; + // clang-format off + using transitions = transition_table< + // Start Target Event Action Guard + // +---------------------------+--------------------------+------------------------+------------------------------+-------------------------+ + row< endc_deactivated_st, wait_sgnb_add_req_resp_st, sgnb_add_req_sent_ev, nullptr >, + // +---------------------------+--------------------------+------------------------+------------------------------+-------------------------+ + row< wait_sgnb_add_req_resp_st, prepare_recfg_st, sgnb_add_req_ack_ev, &fsm::handle_sgnb_add_req_ack >, + row< wait_sgnb_add_req_resp_st, endc_deactivated_st, sgnb_add_req_reject_ev >, + row< prepare_recfg_st, wait_add_complete_st, rrc_recfg_sent_ev >, + row< wait_add_complete_st, endc_activated_st, sgnb_add_complete_ev >, + upd< endc_activated_st, rrc_reest_rx_ev, &fsm::handle_rrc_reest >, + // +---------------------------+--------------------------+------------------------+------------------------------+-------------------------+ + to_state< wait_sgnb_rel_req_resp_st, sgnb_rel_req_ev, &fsm::handle_sgnb_rel_req, &fsm::requires_rel_req >, + row< wait_sgnb_rel_req_resp_st, endc_deactivated_st, sgnb_rel_req_ack_ev >, + to_state< endc_disabled_st, disable_endc_ev, &fsm::handle_endc_disabled > + >; + // clang-format on +}; + +} // namespace srsenb + +#endif // SRSENB_RRC_ENDC_H diff --git a/srsenb/hdr/stack/rrc/rrc_metrics.h b/srsenb/hdr/stack/rrc/rrc_metrics.h index c5a92788c..e15de48ff 100644 --- a/srsenb/hdr/stack/rrc/rrc_metrics.h +++ b/srsenb/hdr/stack/rrc/rrc_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,6 +33,7 @@ typedef enum { RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE, RRC_STATE_WAIT_FOR_SECURITY_MODE_COMPLETE, RRC_STATE_WAIT_FOR_UE_CAP_INFO, + RRC_STATE_WAIT_FOR_UE_CAP_INFO_ENDC, /* only entered for UEs with NSA support */ RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE, RRC_STATE_REESTABLISHMENT_COMPLETE, RRC_STATE_REGISTERED, diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index a3b679764..6c159b272 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,7 +66,8 @@ private: asn1::rrc::meas_cfg_s* diff_meas_cfg); // Handover from source cell - bool start_ho_preparation(uint32_t target_eci, uint8_t measobj_id, bool fwd_direct_path_available); + bool + start_ho_preparation(uint32_t target_eci, uint16_t target_tac, uint8_t measobj_id, bool fwd_direct_path_available); // Handover to target cell void fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s& msg, @@ -90,6 +91,7 @@ private: // events struct ho_meas_report_ev { uint32_t target_eci = 0; + uint16_t target_tac = 0; const asn1::rrc::meas_obj_to_add_mod_s* meas_obj = nullptr; bool direct_fwd_path = false; }; diff --git a/srsenb/hdr/stack/rrc/rrc_nr.h b/srsenb/hdr/stack/rrc/rrc_nr.h deleted file mode 100644 index 211ae5176..000000000 --- a/srsenb/hdr/stack/rrc/rrc_nr.h +++ /dev/null @@ -1,164 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSENB_RRC_NR_H -#define SRSENB_RRC_NR_H - -#include "rrc_config_common.h" -#include "rrc_metrics.h" -#include "srsenb/hdr/stack/enb_stack_base.h" -#include "srsran/asn1/rrc_nr.h" -#include "srsran/common/block_queue.h" -#include "srsran/common/buffer_pool.h" -#include "srsran/common/common.h" -#include "srsran/common/task_scheduler.h" -#include "srsran/common/threads.h" -#include "srsran/common/timeout.h" -#include "srsran/interfaces/gnb_interfaces.h" -#include -#include - -namespace srsenb { - -enum class rrc_nr_state_t { RRC_IDLE, RRC_INACTIVE, RRC_CONNECTED }; - -// TODO: Make this common to NR and LTE -struct rrc_nr_cfg_sr_t { - uint32_t period; - // asn1::rrc::sched_request_cfg_c::setup_s_::dsr_trans_max_e_ dsr_max; - uint32_t nof_prb; - uint32_t sf_mapping[80]; - uint32_t nof_subframes; -}; - -struct rrc_nr_cfg_t { - asn1::rrc_nr::mib_s mib; - asn1::rrc_nr::sib1_s sib1; - asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_item_c_ sibs[ASN1_RRC_NR_MAX_SIB]; - uint32_t nof_sibs; - rrc_nr_cfg_sr_t sr_cfg; - rrc_cfg_cqi_t cqi_cfg; - srsran_cell_t cell; - - std::string log_level; - uint32_t log_hex_limit; - - srsenb::core_less_args_t coreless; -}; - -class rrc_nr final : public rrc_interface_pdcp_nr, - public rrc_interface_mac_nr, - public rrc_interface_rlc_nr, - public rrc_interface_ngap_nr -{ -public: - explicit rrc_nr(srsran::timer_handler* timers_); - - int32_t init(const rrc_nr_cfg_t& cfg, - phy_interface_stack_nr* phy, - mac_interface_rrc_nr* mac, - rlc_interface_rrc_nr* rlc, - pdcp_interface_rrc_nr* pdcp, - ngap_interface_rrc_nr* ngap_, - gtpu_interface_rrc_nr* gtpu); - - void stop(); - - void get_metrics(srsenb::rrc_metrics_t& m); - - rrc_nr_cfg_t update_default_cfg(const rrc_nr_cfg_t& rrc_cfg); - void add_user(uint16_t rnti); - void config_mac(); - int32_t generate_sibs(); - int read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) final; - int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) final; - - // RLC interface - // TODO - void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) {} - void max_retx_attempted(uint16_t rnti) {} - const char* get_rb_name(uint32_t lcid) { return "invalid"; } - // PDCP interface - void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; - - class ue - { - public: - ue(rrc_nr* parent_, uint16_t rnti_); - - void send_connection_setup(); - void send_dl_ccch(asn1::rrc_nr::dl_ccch_msg_s* dl_dcch_msg); - - // getters - bool is_connected() { return state == rrc_nr_state_t::RRC_CONNECTED; } - bool is_idle() { return state == rrc_nr_state_t::RRC_IDLE; } - bool is_inactive() { return state == rrc_nr_state_t::RRC_INACTIVE; } - - // setters - - private: - rrc_nr* parent; - uint16_t rnti; - - // state - rrc_nr_state_t state = rrc_nr_state_t::RRC_IDLE; - uint8_t transaction_id = 0; - srsran::timer_handler::unique_timer rrc_setup_periodic_timer; - }; - -private: - rrc_nr_cfg_t cfg = {}; - - // interfaces - phy_interface_stack_nr* phy = nullptr; - mac_interface_rrc_nr* mac = nullptr; - rlc_interface_rrc_nr* rlc = nullptr; - pdcp_interface_rrc_nr* pdcp = nullptr; - gtpu_interface_rrc_nr* gtpu = nullptr; - ngap_interface_rrc_nr* ngap = nullptr; - - // args - srsran::timer_handler* timers = nullptr; - - // derived - uint32_t slot_dur_ms = 0; - srslog::basic_logger& logger; - - // vars - std::map > users; - bool running = false; - std::vector sib_buffer; - srsran::unique_byte_buffer_t mib_buffer = nullptr; - - uint32_t nof_si_messages = 0; - - // Private Methods - void handle_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu); - - // logging - typedef enum { Rx = 0, Tx } direction_t; - template - void log_rrc_message(const std::string& source, direction_t dir, const srsran::byte_buffer_t* pdu, const T& msg); -}; - -} // namespace srsenb - -#endif // SRSENB_RRC_NR_H \ No newline at end of file diff --git a/srsenb/hdr/stack/rrc/rrc_paging.h b/srsenb/hdr/stack/rrc/rrc_paging.h new file mode 100644 index 000000000..8a5c71f1e --- /dev/null +++ b/srsenb/hdr/stack/rrc/rrc_paging.h @@ -0,0 +1,264 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_PAGING_H +#define SRSRAN_RRC_PAGING_H + +#include "srsran/adt/pool/cached_alloc.h" +#include "srsran/adt/span.h" +#include "srsran/asn1/rrc/paging.h" +#include "srsran/common/tti_point.h" + +namespace srsenb { + +/** + * Class that handles the buffering of paging records and encoding of PCCH messages. + * It's thread-safe, and, assuming that threads contend for pending PCCH messages using different subframe indexes, + * should rarely blocking on mutexes + */ +class paging_manager +{ +public: + paging_manager(uint32_t default_paging_cycle_, float nb_) : + T(default_paging_cycle_), + Nb(static_cast((float)T * nb_)), + N(std::min(T, Nb)), + Ns(std::max(1U, static_cast(nb_))), + logger(srslog::fetch_basic_logger("RRC")) + { + for (subframe_info& sf_obj : sf_pending_pcch) { + sf_obj.pending_paging.resize(T); + for (pcch_info& pcch : sf_obj.pending_paging) { + pcch.pcch_msg.msg.set_c1().paging().paging_record_list_present = true; + } + } + } + + /// add new IMSI paging record + bool add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi); + + /// add new TMSI paging record + bool add_tmsi_paging(uint32_t ueid, uint8_t mmec, srsran::const_byte_span m_tmsi); + + /// Get how many bytes are required to fit the pending PCCH message. + size_t pending_pcch_bytes(tti_point tti_tx_dl); + + /** + * Invoke "callable" for PCCH indexed by tti_tx_dl in a mutexed context. + * Callable signature is bool(const_byte_span pdu, const pcch_msg& msg, bool is_first_tx) + * - "pdu" encoded ASN1 PCCH message + * - "msg" PCCH message in ASN1 form + * - "is_first_tx" tells if the PDU hasn't been transmitted yet. This may be useful to log the PCCH only for one + * of the carriers + * - the return should be true if the PDU is being transmitted, and false otherwise + */ + template + bool read_pdu_pcch(tti_point tti_tx_dl, const Callable& callable); + +private: + struct pcch_info { + tti_point tti_tx_dl; + asn1::rrc::pcch_msg_s pcch_msg; + srsran::unique_byte_buffer_t pdu; + + bool is_tx() const { return tti_tx_dl.is_valid(); } + bool empty() const { return pdu == nullptr; } + void clear() + { + tti_tx_dl = tti_point(); + pcch_msg.msg.c1().paging().paging_record_list.clear(); + pdu.reset(); + } + }; + const static size_t nof_paging_subframes = 4; + + bool add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record); + + static int get_sf_idx_key(uint32_t sf_idx) + { + switch (sf_idx) { + case 0: + return 0; + case 4: + return 1; + case 5: + return 2; + case 9: + return 3; + default: + break; + } + return -1; + } + + // args + uint32_t T; + uint32_t Nb; + uint32_t N; + uint32_t Ns; + srslog::basic_logger& logger; + + struct subframe_info { + mutable std::mutex mutex; + srsran::deque transmitted_pcch; + std::vector pending_paging; + }; + + std::array sf_pending_pcch; +}; + +bool paging_manager::add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi) +{ + asn1::rrc::paging_record_s paging_elem; + paging_elem.ue_id.set_imsi().resize(imsi.size()); + std::copy(imsi.begin(), imsi.end(), paging_elem.ue_id.imsi().begin()); + paging_elem.cn_domain = asn1::rrc::paging_record_s::cn_domain_e_::ps; + return add_paging_record(ueid, paging_elem); +} + +bool paging_manager::add_tmsi_paging(uint32_t ueid, uint8_t mmec, srsran::const_byte_span m_tmsi) +{ + asn1::rrc::paging_record_s paging_elem; + paging_elem.ue_id.set_s_tmsi().mmec.from_number(mmec); + uint32_t m_tmsi_val = 0; + for (uint32_t i = 0; i < m_tmsi.size(); i++) { + m_tmsi_val |= m_tmsi[i] << (8u * (m_tmsi.size() - i - 1u)); + } + paging_elem.ue_id.s_tmsi().m_tmsi.from_number(m_tmsi_val); + paging_elem.cn_domain = asn1::rrc::paging_record_s::cn_domain_e_::ps; + return add_paging_record(ueid, paging_elem); +} + +/// \remark See TS 36.304, Section 7 +bool paging_manager::add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record) +{ + constexpr static const int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}}; + + ueid = ((uint32_t)ueid) % 1024; + uint32_t i_s = (ueid / N) % Ns; + + int sf_idx = sf_pattern[i_s % 4][(Ns - 1) % 4]; + if (sf_idx < 0) { + logger.error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d", Ns, i_s, ueid); + return false; + } + size_t sf_key = static_cast(get_sf_idx_key(sf_idx)); + + subframe_info& locked_sf = sf_pending_pcch[sf_key]; + std::lock_guard lock(locked_sf.mutex); + + size_t sfn_cycle_idx = ((size_t)T / (size_t)N) * (size_t)(ueid % N); + pcch_info& pending_pcch = locked_sf.pending_paging[sfn_cycle_idx]; + auto& record_list = pending_pcch.pcch_msg.msg.c1().paging().paging_record_list; + + if (record_list.size() >= ASN1_RRC_MAX_PAGE_REC) { + logger.warning("Failed to add new paging record for ueid=%d. Cause: no paging record space left.", ueid); + return false; + } + if (pending_pcch.is_tx()) { + logger.error("Adding Paging records to ueid=%d PCCH that has been already sent but not cleared.", ueid); + pending_pcch.clear(); + } + + if (pending_pcch.pdu == nullptr) { + pending_pcch.pdu = srsran::make_byte_buffer(); + if (pending_pcch.pdu == nullptr) { + logger.warning("Failed to add new paging record for ueid=%d. Cause: No buffers available", ueid); + return false; + } + } + + record_list.push_back(paging_record); + + asn1::bit_ref bref(pending_pcch.pdu->msg, pending_pcch.pdu->get_tailroom()); + if (pending_pcch.pcch_msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { + logger.error("Failed to pack PCCH message"); + pending_pcch.clear(); + return false; + } + pending_pcch.pdu->N_bytes = (uint32_t)bref.distance_bytes(); + + return true; +} + +size_t paging_manager::pending_pcch_bytes(tti_point tti_tx_dl) +{ + int sf_key = get_sf_idx_key(tti_tx_dl.sf_idx()); + if (sf_key < 0) { + // tti_tx_dl is not in a paging subframe + return 0; + } + + subframe_info& locked_sf = sf_pending_pcch[static_cast(sf_key)]; + std::unique_lock lock(locked_sf.mutex, std::try_to_lock); + if (not lock.owns_lock()) { + // If the scheduler fails to lock, it will postpone the PCCH transmission to the next paging cycle + return 0; + } + + // clear old PCCH that has been transmitted at this point + while (not locked_sf.transmitted_pcch.empty() and locked_sf.transmitted_pcch.front()->tti_tx_dl < tti_tx_dl) { + locked_sf.transmitted_pcch.front()->clear(); + locked_sf.transmitted_pcch.pop_front(); + } + + const pcch_info& pending_pcch = locked_sf.pending_paging[tti_tx_dl.sfn() % T]; + if (pending_pcch.empty()) { + return 0; + } + return pending_pcch.pdu->size(); +} + +template +bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& func) +{ + int sf_key = get_sf_idx_key(tti_tx_dl.sf_idx()); + if (sf_key < 0) { + logger.warning("%s was called for tti=%d, which is not a paging subframe index", __FUNCTION__, tti_tx_dl.to_uint()); + return false; + } + + subframe_info& locked_sf = sf_pending_pcch[static_cast(sf_key)]; + std::lock_guard lock(locked_sf.mutex); + + pcch_info& pending_pcch = locked_sf.pending_paging[tti_tx_dl.sfn() % T]; + + if (pending_pcch.empty()) { + logger.warning("read_pdu_pdcch(...) called for tti=%d, but there is no pending pcch message", tti_tx_dl.to_uint()); + return false; + } + + // Call callable for existing PCCH pdu + if (func(*pending_pcch.pdu, pending_pcch.pcch_msg, not pending_pcch.is_tx())) { + if (not pending_pcch.is_tx()) { + // first tx. Enqueue in list of transmitted pcch. We do not erase the PCCH yet because it may be transmitted + // by other carriers + pending_pcch.tti_tx_dl = tti_tx_dl; + locked_sf.transmitted_pcch.push_back(&pending_pcch); + } + return true; + } + return false; +} + +} // namespace srsenb + +#endif // SRSRAN_RRC_PAGING_H diff --git a/srsenb/hdr/stack/rrc/rrc_ue.h b/srsenb/hdr/stack/rrc/rrc_ue.h index 2b26dbd32..d2bc2d580 100644 --- a/srsenb/hdr/stack/rrc/rrc_ue.h +++ b/srsenb/hdr/stack/rrc/rrc_ue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,6 +25,7 @@ #include "mac_controller.h" #include "rrc.h" #include "srsran/adt/pool/batch_mem_pool.h" +#include "srsran/asn1/rrc/uecap.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/pdcp_interface_types.h" @@ -34,6 +35,14 @@ class rrc::ue { public: class rrc_mobility; + class rrc_endc; + enum activity_timeout_type_t { + MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs + UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout) + MSG5_RX_TIMEOUT_T300, ///< UE timeout for receiving RRCConnectionSetupComplete + MSG5_RX_TIMEOUT_T301, ///< UE timeout for receiving RRCReestablishmentComplete + nulltype + }; ue(rrc* outer_rrc, uint16_t rnti, const sched_interface::ue_cfg_t& ue_cfg); ~ue(); @@ -41,21 +50,19 @@ public: bool is_connected(); bool is_idle(); - typedef enum { - MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs - UE_INACTIVITY_TIMEOUT, ///< UE inactivity timeout (usually bigger than reestablishment timeout) - nulltype - } activity_timeout_type_t; - std::string to_string(const activity_timeout_type_t& type); - void set_activity_timeout(const activity_timeout_type_t type); - void set_rlf_timeout(); - void set_activity(); + void set_activity_timeout(activity_timeout_type_t type); + void set_activity(bool enabled = true); void set_radiolink_dl_state(bool crc_res); void set_radiolink_ul_state(bool crc_res); void activity_timer_expired(const activity_timeout_type_t type); - void rlf_timer_expired(); - void max_retx_reached(); + void rlf_timer_expired(uint32_t timeout_id); + void max_rlc_retx_reached(); + void protocol_failure(); + void deactivate_bearers() { mac_ctrl.set_radio_bearer_state(mac_lc_ch_cfg_t::IDLE); } + + // Init PUCCH resources for PCell + bool init_pucch(); rrc_state_t get_state(); void get_metrics(rrc_ue_metrics_t& ue_metrics) const; @@ -71,6 +78,7 @@ public: error_unknown_rnti, radio_conn_with_ue_lost, msg3_timeout, + fail_in_radio_interface_proc, unspecified }; @@ -83,7 +91,7 @@ public: bool phy_cfg_updated = true, srsran::const_byte_span nas_pdu = {}); void send_security_mode_command(); - void send_ue_cap_enquiry(); + void send_ue_cap_enquiry(const std::vector& rats); void send_ue_info_req(); void parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); @@ -110,7 +118,7 @@ public: void handle_rrc_reconf_complete(asn1::rrc::rrc_conn_recfg_complete_s* msg, srsran::unique_byte_buffer_t pdu); void handle_security_mode_complete(asn1::rrc::security_mode_complete_s* msg); void handle_security_mode_failure(asn1::rrc::security_mode_fail_s* msg); - bool handle_ue_cap_info(asn1::rrc::ue_cap_info_s* msg); + int handle_ue_cap_info(asn1::rrc::ue_cap_info_s* msg); void handle_ue_init_ctxt_setup_req(const asn1::s1ap::init_context_setup_request_s& msg); bool handle_ue_ctxt_mod_req(const asn1::s1ap::ue_context_mod_request_s& msg); void handle_ue_info_resp(const asn1::rrc::ue_info_resp_r9_s& msg, srsran::unique_byte_buffer_t pdu); @@ -121,7 +129,6 @@ public: bool has_erab(uint32_t erab_id) const { return bearer_list.get_erabs().count(erab_id) > 0; } int get_erab_addr_in(uint16_t erab_id, transp_addr_t& addr_in, uint32_t& teid_in) const; - bool setup_erabs(const asn1::s1ap::erab_to_be_setup_list_ctxt_su_req_l& e); bool release_erabs(); int release_erab(uint32_t erab_id); int setup_erab(uint16_t erab_id, @@ -157,18 +164,24 @@ public: void save_ul_message(srsran::unique_byte_buffer_t pdu) { last_ul_msg = std::move(pdu); } + const ue_cell_ded_list& get_cell_list() const { return ue_cell_list; } + uint16_t rnti = 0; rrc* parent = nullptr; bool connect_notified = false; unique_rnti_ptr mobility_handler; + unique_rnti_ptr endc_handler; bool is_csfb = false; private: - // args - srsran::timer_handler::unique_timer activity_timer; - srsran::timer_handler::unique_timer rlf_timer; + srsran::unique_timer activity_timer; // for basic DL/UL activity timeout + + /// Radio link failure handling uses distinct timers for PHY (DL and UL) and RLC signaled RLF + srsran::unique_timer phy_dl_rlf_timer; // can be stopped through recovered DL activity + srsran::unique_timer phy_ul_rlf_timer; // can be stopped through recovered UL activity + srsran::unique_timer rlc_rlf_timer; // can only be stoped through UE reestablishment /// cached ASN1 fields for RRC config update checking, and ease of context transfer during HO ue_var_cfg_t current_ue_cfg; @@ -184,7 +197,6 @@ private: uint32_t rlf_cnt = 0; uint8_t transaction_id = 0; rrc_state_t state = RRC_STATE_IDLE; - uint16_t old_reest_rnti = SRSRAN_INVALID_RNTI; std::map old_reest_pdcp_state = {}; bool rlf_info_pending = false; diff --git a/srsenb/hdr/stack/rrc/ue_meas_cfg.h b/srsenb/hdr/stack/rrc/ue_meas_cfg.h index 2a784400a..2386ea4ba 100644 --- a/srsenb/hdr/stack/rrc/ue_meas_cfg.h +++ b/srsenb/hdr/stack/rrc/ue_meas_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/hdr/stack/rrc/ue_rr_cfg.h b/srsenb/hdr/stack/rrc/ue_rr_cfg.h index 2a0553936..7dd59ab2f 100644 --- a/srsenb/hdr/stack/rrc/ue_rr_cfg.h +++ b/srsenb/hdr/stack/rrc/ue_rr_cfg.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -54,18 +54,18 @@ class bearer_cfg_handler; struct ue_var_cfg_t; /// Fill RadioResourceConfigDedicated with data known at the RRCSetup/Reestablishment stage -void fill_rr_cfg_ded_setup(asn1::rrc::rr_cfg_ded_s& rr_cfg, - const rrc_cfg_t& enb_cfg, - const ue_cell_ded_list& ue_cell_list); +int fill_rr_cfg_ded_setup(asn1::rrc::rr_cfg_ded_s& rr_cfg, + const rrc_cfg_t& enb_cfg, + const ue_cell_ded_list& ue_cell_list); /// Apply Reconf updates and update current state -void apply_reconf_updates(asn1::rrc::rrc_conn_recfg_r8_ies_s& recfg_r8, - ue_var_cfg_t& current_ue_cfg, - const rrc_cfg_t& enb_cfg, - const ue_cell_ded_list& ue_cell_list, - bearer_cfg_handler& bearers, - const srsran::rrc_ue_capabilities_t& ue_caps, - bool phy_cfg_updated); +int apply_reconf_updates(asn1::rrc::rrc_conn_recfg_r8_ies_s& recfg_r8, + ue_var_cfg_t& current_ue_cfg, + const rrc_cfg_t& enb_cfg, + const ue_cell_ded_list& ue_cell_list, + bearer_cfg_handler& bearers, + const srsran::rrc_ue_capabilities_t& ue_caps, + bool phy_cfg_updated); /// Apply radioResourceConfigDedicated updates to the current UE RRC configuration void apply_rr_cfg_ded_diff(asn1::rrc::rr_cfg_ded_s& current_rr_cfg_ded, diff --git a/srsenb/hdr/stack/upper/s1ap.h b/srsenb/hdr/stack/s1ap/s1ap.h similarity index 93% rename from srsenb/hdr/stack/upper/s1ap.h rename to srsenb/hdr/stack/s1ap/s1ap.h index 0ed6edbac..39e9d5277 100644 --- a/srsenb/hdr/stack/upper/s1ap.h +++ b/srsenb/hdr/stack/s1ap/s1ap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -64,9 +64,6 @@ public: using erab_id_list = srsran::bounded_vector; using erab_item_list = srsran::bounded_vector; - static const uint32_t ts1_reloc_prep_timeout_ms = 10000; - static const uint32_t ts1_reloc_overall_timeout_ms = 10000; - s1ap(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger, srsran::socket_manager_itf* rx_socket_handler); @@ -89,10 +86,11 @@ public: bool user_exists(uint16_t rnti) override; void user_mod(uint16_t old_rnti, uint16_t new_rnti) override; bool user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_radio) override; - void ue_ctxt_setup_complete(uint16_t rnti) override; + void notify_rrc_reconf_complete(uint16_t rnti) override; bool is_mme_connected() override; bool send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -119,7 +117,7 @@ public: // Stack interface bool - handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); + handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); void start_pcap(srsran::s1ap_pcap* pcap_); private: @@ -134,6 +132,7 @@ private: rrc_interface_s1ap* rrc = nullptr; s1ap_args_t args; srslog::basic_logger& logger; + srslog::log_channel& alarms_channel; srsran::task_sched_handle task_sched; srsran::task_queue_handle mme_task_queue; srsran::socket_manager_itf* rx_socket_handler; @@ -177,6 +176,8 @@ private: bool handle_erabreleasecommand(const asn1::s1ap::erab_release_cmd_s& msg); bool handle_uecontextmodifyrequest(const asn1::s1ap::ue_context_mod_request_s& msg); + void ue_ctxt_setup_complete(uint16_t rnti); + // handover /** * Source eNB Handler for S1AP "HANDOVER PREPARATION FAILURE" Message @@ -221,6 +222,7 @@ private: struct ts1_reloc_prep_expired {}; ho_prep_proc_t(s1ap::ue* ue_); srsran::proc_outcome_t init(uint32_t target_eci_, + uint16_t target_tac_, srsran::plmn_id_t target_plmn_, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -237,6 +239,7 @@ private: s1ap* s1ap_ptr = nullptr; uint32_t target_eci = 0; + uint16_t target_tac = 0; srsran::plmn_id_t target_plmn; srsran::unique_byte_buffer_t rrc_container; const asn1::s1ap::ho_cmd_s* ho_cmd_msg = nullptr; @@ -256,22 +259,28 @@ private: uint32_t m_tmsi = 0, uint8_t mmec = 0); void ue_ctxt_setup_complete(); + void notify_rrc_reconf_complete(); bool send_erab_setup_response(const erab_id_list& erabs_setup, const erab_item_list& erabs_failed); bool send_erab_release_response(const erab_id_list& erabs_released, const erab_item_list& erabs_failed); bool send_erab_modify_response(const erab_id_list& erabs_modified, const erab_item_list& erabs_failed); bool send_erab_release_indication(const std::vector& erabs_successfully_released); bool send_ue_cap_info_indication(srsran::unique_byte_buffer_t ue_radio_cap); + /// TS 36.413 8.4.5 - Handover Cancellation + void send_ho_cancel(const asn1::s1ap::cause_c& cause); + bool was_uectxtrelease_requested() const { return release_requested; } void set_state(s1ap_proc_id_t state, const erab_id_list& erabs_updated, const erab_item_list& erabs_failed_to_update); + s1ap_proc_id_t get_state() const { return current_state; } ue_ctxt_t ctxt = {}; uint16_t stream_id = 1; private: bool send_ho_required(uint32_t target_eci_, + uint16_t target_tac_, srsran::plmn_id_t target_plmn_, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -286,6 +295,7 @@ private: bool release_requested = false; srsran::unique_timer ts1_reloc_prep; ///< TS1_{RELOCprep} - max time for HO preparation srsran::unique_timer ts1_reloc_overall; ///< TS1_{RELOCOverall} + srsran::unique_timer overall_procedure_timeout; // Procedure state s1ap_proc_id_t current_state = s1ap_proc_id_t::nulltype; @@ -329,13 +339,18 @@ private: bool success = false; enum class cause_t { timeout, failure } cause; }; + struct s1connectresult { + bool success = false; + }; explicit s1_setup_proc_t(s1ap* s1ap_) : s1ap_ptr(s1ap_) {} srsran::proc_outcome_t init(); srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + srsran::proc_outcome_t react(const s1connectresult& event); srsran::proc_outcome_t react(const s1setupresult& event); - void then(const srsran::proc_state_t& result) const; + void then(const srsran::proc_state_t& result); const char* name() const { return "MME Connection"; } + uint16_t connect_count = 0; private: srsran::proc_outcome_t start_mme_connection(); diff --git a/srsenb/hdr/stack/upper/s1ap_metrics.h b/srsenb/hdr/stack/s1ap/s1ap_metrics.h similarity index 95% rename from srsenb/hdr/stack/upper/s1ap_metrics.h rename to srsenb/hdr/stack/s1ap/s1ap_metrics.h index 5beeaf683..728ceeaa8 100644 --- a/srsenb/hdr/stack/upper/s1ap_metrics.h +++ b/srsenb/hdr/stack/s1ap/s1ap_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/hdr/stack/upper/gtpu.h b/srsenb/hdr/stack/upper/gtpu.h index 1d1724369..e97f5339f 100644 --- a/srsenb/hdr/stack/upper/gtpu.h +++ b/srsenb/hdr/stack/upper/gtpu.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,6 +20,7 @@ */ #include +#include #include #include "srsenb/hdr/common/common_enb.h" @@ -45,7 +46,6 @@ struct gtpu_header_t; namespace srsenb { class pdcp_interface_gtpu; -class stack_interface_gtpu_lte; class gtpu_tunnel_manager { @@ -58,16 +58,16 @@ class gtpu_tunnel_manager public: // A UE should have <= 3 DRBs active, and each DRB should have two tunnels active at the same time at most - const static size_t MAX_TUNNELS_PER_UE = 6; + const static size_t MAX_TUNNELS_PER_UE = 10; - enum class tunnel_state { pdcp_active, buffering, forward_to, forwarded_from }; + enum class tunnel_state { pdcp_active, buffering, forward_to, forwarded_from, inactive }; struct tunnel { - uint16_t rnti = SRSRAN_INVALID_RNTI; - uint32_t lcid = srsran::INVALID_LCID; - uint32_t teid_in = 0; - uint32_t teid_out = 0; - uint32_t spgw_addr = 0; + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t eps_bearer_id = srsran::INVALID_EPS_BEARER_ID; + uint32_t teid_in = 0; + uint32_t teid_out = 0; + uint32_t spgw_addr = 0; tunnel_state state = tunnel_state::pdcp_active; srsran::unique_timer rx_timer; @@ -86,38 +86,41 @@ public: } }; - struct lcid_tunnel { - uint32_t lcid; + struct bearer_teid_pair { + uint32_t eps_bearer_id; uint32_t teid; - bool operator<(const lcid_tunnel& other) const + bool operator<(const bearer_teid_pair& other) const { - return lcid < other.lcid or (lcid == other.lcid and teid < other.teid); + return eps_bearer_id < other.eps_bearer_id or (eps_bearer_id == other.eps_bearer_id and teid < other.teid); + } + bool operator==(const bearer_teid_pair& other) const + { + return eps_bearer_id == other.eps_bearer_id and teid == other.teid; } - bool operator==(const lcid_tunnel& other) const { return lcid == other.lcid and teid == other.teid; } }; - using ue_lcid_tunnel_list = srsran::bounded_vector; + using ue_bearer_tunnel_list = srsran::bounded_vector; explicit gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger); - void init(pdcp_interface_gtpu* pdcp_); + void init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_); - bool has_teid(uint32_t teid) const { return tunnels.contains(teid); } - const tunnel* find_tunnel(uint32_t teid); - ue_lcid_tunnel_list* find_rnti_tunnels(uint16_t rnti); - srsran::span find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid); + bool has_teid(uint32_t teid) const { return tunnels.contains(teid); } + const tunnel* find_tunnel(uint32_t teid); + ue_bearer_tunnel_list* find_rnti_tunnels(uint16_t rnti); + srsran::span find_rnti_bearer_tunnels(uint16_t rnti, uint32_t eps_bearer_id); - const tunnel* add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr); + const tunnel* add_tunnel(uint16_t rnti, uint32_t eps_bearer_id, uint32_t teidout, uint32_t spgw_addr); bool update_rnti(uint16_t old_rnti, uint16_t new_rnti); void activate_tunnel(uint32_t teid); void suspend_tunnel(uint32_t teid); + void deactivate_tunnel(uint32_t teid); void set_tunnel_priority(uint32_t first_teid, uint32_t second_teid); void handle_rx_pdcp_sdu(uint32_t teid); void buffer_pdcp_sdu(uint32_t teid, uint32_t pdcp_sn, srsran::unique_byte_buffer_t sdu); void setup_forwarding(uint32_t rx_teid, uint32_t tx_teid); bool remove_tunnel(uint32_t teid); - bool remove_bearer(uint16_t rnti, uint32_t lcid); bool remove_rnti(uint16_t rnti); private: @@ -125,11 +128,12 @@ private: using tunnel_ctxt_it = typename tunnel_list_t::iterator; srsran::task_sched_handle task_sched; - pdcp_interface_gtpu* pdcp = nullptr; + const gtpu_args_t* gtpu_args = nullptr; + pdcp_interface_gtpu* pdcp = nullptr; srslog::basic_logger& logger; - rnti_map_t ue_teidin_db; - tunnel_list_t tunnels; + std::unordered_map ue_teidin_db; + tunnel_list_t tunnels; }; using gtpu_tunnel_state = gtpu_tunnel_manager::tunnel_state; @@ -143,27 +147,23 @@ public: srsran::socket_manager_itf* rx_socket_handler_); ~gtpu(); - int init(std::string gtp_bind_addr_, - std::string mme_addr_, - std::string m1u_multiaddr_, - std::string m1u_if_addr_, - pdcp_interface_gtpu* pdcp_, - bool enable_mbsfn = false); + int init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_); void stop(); // gtpu_interface_rrc srsran::expected add_bearer(uint16_t rnti, - uint32_t lcid, + uint32_t eps_bearer_id, uint32_t addr, uint32_t teid_out, + uint32_t& addr_in, const bearer_props* props = nullptr) override; void set_tunnel_status(uint32_t teidin, bool dl_active) override; - void rem_bearer(uint16_t rnti, uint32_t lcid) override; + void rem_bearer(uint16_t rnti, uint32_t eps_bearer_id) override; void mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) override; void rem_user(uint16_t rnti) override; // gtpu_interface_pdcp - void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override; + void write_pdu(uint16_t rnti, uint32_t eps_bearer_id, srsran::unique_byte_buffer_t pdu) override; // stack interface void handle_gtpu_s1u_rx_packet(srsran::unique_byte_buffer_t pdu, const sockaddr_in& addr); @@ -177,7 +177,7 @@ private: srsran::socket_manager_itf* rx_socket_handler = nullptr; srsran::task_queue_handle gtpu_queue; - bool enable_mbsfn = false; + gtpu_args_t args; std::string gtp_bind_addr; std::string mme_addr; srsenb::pdcp_interface_gtpu* pdcp = nullptr; @@ -204,9 +204,9 @@ private: std::string m1u_multiaddr; std::string m1u_if_addr; - bool initiated = false; - int m1u_sd = -1; - int lcid_counter = 0; + bool initiated = false; + int m1u_sd = -1; + int bearer_counter = 0; }; m1u_handler m1u; diff --git a/srsenb/hdr/stack/upper/gtpu_pdcp_adapter.h b/srsenb/hdr/stack/upper/gtpu_pdcp_adapter.h new file mode 100644 index 000000000..88b8c442f --- /dev/null +++ b/srsenb/hdr/stack/upper/gtpu_pdcp_adapter.h @@ -0,0 +1,87 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_GTPU_PDCP_ADAPTER_H +#define SRSRAN_GTPU_PDCP_ADAPTER_H + +#include "srsran/common/bearer_manager.h" +#include "srsran/interfaces/enb_gtpu_interfaces.h" +#include "srsran/srslog/logger.h" + +namespace srsenb { + +class gtpu_pdcp_adapter final : public gtpu_interface_pdcp, public pdcp_interface_gtpu +{ +public: + gtpu_pdcp_adapter(srslog::basic_logger& logger_, + pdcp_interface_gtpu* pdcp_lte, + pdcp_interface_gtpu* pdcp_nr, + gtpu* gtpu_, + enb_bearer_manager& bearers_) : + logger(logger_), pdcp_lte_obj(pdcp_lte), pdcp_nr_obj(pdcp_nr), gtpu_obj(gtpu_), bearers(&bearers_) + {} + + /// Converts LCID to EPS-BearerID and sends corresponding PDU to GTPU + void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override + { + auto bearer = bearers->get_lcid_bearer(rnti, lcid); + if (not bearer.is_valid()) { + logger.error("Bearer rnti=0x%x, lcid=%d not found", rnti, lcid); + return; + } + gtpu_obj->write_pdu(rnti, bearer.eps_bearer_id, std::move(pdu)); + } + void write_sdu(uint16_t rnti, uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) override + { + auto bearer = bearers->get_radio_bearer(rnti, eps_bearer_id); + // route SDU to PDCP entity + if (bearer.rat == srsran::srsran_rat_t::lte) { + pdcp_lte_obj->write_sdu(rnti, bearer.lcid, std::move(sdu), pdcp_sn); + } else if (bearer.rat == srsran::srsran_rat_t::nr) { + pdcp_nr_obj->write_sdu(rnti, bearer.lcid, std::move(sdu), pdcp_sn); + } else { + logger.warning("Can't deliver SDU for EPS bearer %d. Dropping it.", eps_bearer_id); + } + } + std::map get_buffered_pdus(uint16_t rnti, uint32_t eps_bearer_id) override + { + auto bearer = bearers->get_radio_bearer(rnti, eps_bearer_id); + // route SDU to PDCP entity + if (bearer.rat == srsran::srsran_rat_t::lte) { + return pdcp_lte_obj->get_buffered_pdus(rnti, bearer.lcid); + } else if (bearer.rat == srsran::srsran_rat_t::nr) { + return pdcp_nr_obj->get_buffered_pdus(rnti, bearer.lcid); + } + logger.error("Bearer rnti=0x%x, eps-BearerID=%d not found", rnti, eps_bearer_id); + return {}; + } + +private: + srslog::basic_logger& logger; + gtpu* gtpu_obj = nullptr; + pdcp_interface_gtpu* pdcp_lte_obj = nullptr; + pdcp_interface_gtpu* pdcp_nr_obj = nullptr; + enb_bearer_manager* bearers = nullptr; +}; + +} // namespace srsenb + +#endif // SRSRAN_GTPU_PDCP_ADAPTER_H diff --git a/srsenb/hdr/stack/upper/pdcp.h b/srsenb/hdr/stack/upper/pdcp.h index a46ec42a5..f558cc473 100644 --- a/srsenb/hdr/stack/upper/pdcp.h +++ b/srsenb/hdr/stack/upper/pdcp.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -53,13 +53,14 @@ public: void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} // pdcp_interface_rrc + void set_enabled(uint16_t rnti, uint32_t lcid, bool enabled) override; void reset(uint16_t rnti) override; void add_user(uint16_t rnti) override; void rem_user(uint16_t rnti) override; void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) override; - void add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cnfg) override; + void add_bearer(uint16_t rnti, uint32_t lcid, const srsran::pdcp_config_t& cnfg) override; void del_bearer(uint16_t rnti, uint32_t lcid) override; - void config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t cfg_sec) override; + void config_security(uint16_t rnti, uint32_t lcid, const srsran::as_security_config_t& cfg_sec) override; void enable_integrity(uint16_t rnti, uint32_t lcid) override; void enable_encryption(uint16_t rnti, uint32_t lcid) override; bool get_bearer_state(uint16_t rnti, uint32_t lcid, srsran::pdcp_lte_state_t* state) override; @@ -85,6 +86,7 @@ private: void discard_sdu(uint32_t lcid, uint32_t discard_sn); bool rb_is_um(uint32_t lcid); bool sdu_queue_is_full(uint32_t lcid); + bool is_suspended(uint32_t lcid); }; class user_interface_gtpu : public srsue::gw_interface_pdcp @@ -108,6 +110,7 @@ private: void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu); void write_pdu_pcch(srsran::unique_byte_buffer_t pdu); void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} + void notify_pdcp_integrity_error(uint32_t lcid); const char* get_rb_name(uint32_t lcid); }; diff --git a/srsenb/hdr/stack/upper/pdcp_nr.h b/srsenb/hdr/stack/upper/pdcp_nr.h deleted file mode 100644 index 19a7b4211..000000000 --- a/srsenb/hdr/stack/upper/pdcp_nr.h +++ /dev/null @@ -1,125 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/interfaces/gnb_interfaces.h" -#include "srsran/interfaces/ue_gw_interfaces.h" -#include "srsran/interfaces/ue_rlc_interfaces.h" -#include "srsran/upper/pdcp.h" -#include - -#ifndef SRSENB_PDCP_NR_H -#define SRSENB_PDCP_NR_H - -namespace srsenb { - -struct pdcp_nr_args_t { - std::string log_level; - uint32_t log_hex_limit; -}; - -class pdcp_nr : public pdcp_interface_rlc_nr, public pdcp_interface_sdap_nr, public pdcp_interface_rrc_nr -{ -public: - explicit pdcp_nr(srsran::task_sched_handle task_sched_, const char* logname); - virtual ~pdcp_nr() = default; - void init(const pdcp_nr_args_t& args_, - rlc_interface_pdcp_nr* rlc_, - rrc_interface_pdcp_nr* rrc_, - sdap_interface_pdcp_nr* gtpu_); - void stop(); - - // pdcp_interface_rlc_nr - void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu); - void notify_delivery(uint16_t rnti, uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn); - void notify_failure(uint16_t rnti, uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn); - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} - - // pdcp_interface_rrc_nr - void reset(uint16_t rnti) final; - void add_user(uint16_t rnti) final; - void rem_user(uint16_t rnti) final; - void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) final; - void add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cnfg) final; - void config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t sec_cfg) final; - void enable_integrity(uint16_t rnti, uint32_t lcid) final; - void enable_encryption(uint16_t rnti, uint32_t lcid) final; - -private: - class user_interface_rlc final : public srsue::rlc_interface_pdcp - { - public: - uint16_t rnti; - srsenb::rlc_interface_pdcp_nr* rlc; - // rlc_interface_pdcp_nr - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final; - void discard_sdu(uint32_t lcid, uint32_t discard_sn) final; - bool rb_is_um(uint32_t lcid) final; - bool sdu_queue_is_full(uint32_t lcid) final; - }; - - class user_interface_sdap : public srsue::gw_interface_pdcp - { - public: - uint16_t rnti; - srsenb::sdap_interface_pdcp_nr* sdap; - // gw_interface_pdcp - void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final {} - }; - - class user_interface_rrc : public srsue::rrc_interface_pdcp - { - public: - uint16_t rnti; - srsenb::rrc_interface_pdcp_nr* rrc; - // rrc_interface_pdcp_nr - void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; - void write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) final; - void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) final; - void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) final; - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final {} - const char* get_rb_name(uint32_t lcid) final; - }; - - class user_interface - { - public: - user_interface_rlc rlc_itf; - user_interface_sdap sdap_itf; - user_interface_rrc rrc_itf; - std::unique_ptr pdcp; - }; - - // args - pdcp_nr_args_t m_args = {}; - rlc_interface_pdcp_nr* m_rlc = nullptr; - rrc_interface_pdcp_nr* m_rrc = nullptr; - sdap_interface_pdcp_nr* m_sdap = nullptr; - - std::map users; - - srsran::task_sched_handle task_sched; - srslog::basic_logger& logger; -}; - -} // namespace srsenb - -#endif // SRSENB_PDCP_NR_H diff --git a/srsenb/hdr/stack/upper/rlc.h b/srsenb/hdr/stack/upper/rlc.h index d64ec1b9f..60d49d4fc 100644 --- a/srsenb/hdr/stack/upper/rlc.h +++ b/srsenb/hdr/stack/upper/rlc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,8 +23,8 @@ #include "srsran/interfaces/enb_metrics_interface.h" #include "srsran/interfaces/enb_rlc_interfaces.h" #include "srsran/interfaces/ue_interfaces.h" +#include "srsran/rlc/rlc.h" #include "srsran/srslog/srslog.h" -#include "srsran/upper/rlc.h" #include #ifndef SRSENB_RLC_H @@ -56,12 +56,13 @@ public: void clear_buffer(uint16_t rnti); void add_user(uint16_t rnti); void rem_user(uint16_t rnti); - void add_bearer(uint16_t rnti, uint32_t lcid, srsran::rlc_config_t cnfg); + void add_bearer(uint16_t rnti, uint32_t lcid, const srsran::rlc_config_t& cnfg); void add_bearer_mrb(uint16_t rnti, uint32_t lcid); void del_bearer(uint16_t rnti, uint32_t lcid); bool has_bearer(uint16_t rnti, uint32_t lcid); bool suspend_bearer(uint16_t rnti, uint32_t lcid); bool resume_bearer(uint16_t rnti, uint32_t lcid); + bool is_suspended(uint16_t rnti, uint32_t lcid); void reestablish(uint16_t rnti) final; // rlc_interface_pdcp @@ -74,7 +75,6 @@ public: // rlc_interface_mac int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); - void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size); private: class user_interface : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc @@ -88,6 +88,7 @@ private: void write_pdu_pcch(srsran::unique_byte_buffer_t sdu); void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} void max_retx_attempted(); + void protocol_failure(); const char* get_rb_name(uint32_t lcid); uint16_t rnti; diff --git a/srsenb/hdr/stack/upper/rlc_nr.h b/srsenb/hdr/stack/upper/rlc_nr.h deleted file mode 100644 index 2b9261f96..000000000 --- a/srsenb/hdr/stack/upper/rlc_nr.h +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSENB_RLC_NR_H -#define SRSENB_RLC_NR_H - -#include "srsran/interfaces/gnb_interfaces.h" -#include "srsran/upper/rlc.h" -#include - -namespace srsenb { - -typedef struct { - uint32_t lcid; - uint32_t plmn; - uint16_t mtch_stop; - uint8_t* payload; -} mch_service_t; - -class rlc_nr final : public rlc_interface_mac_nr, public rlc_interface_rrc_nr, public rlc_interface_pdcp_nr -{ -public: - explicit rlc_nr(const char* logname); - void init(pdcp_interface_rlc_nr* pdcp_, - rrc_interface_rlc_nr* rrc_, - mac_interface_rlc_nr* mac_, - srsran::timer_handler* timers_); - void stop(); - - // rlc_interface_rrc_nr - void clear_buffer(uint16_t rnti); - void add_user(uint16_t rnti); - void rem_user(uint16_t rnti); - void add_bearer(uint16_t rnti, uint32_t lcid, srsran::rlc_config_t cnfg); - void add_bearer_mrb(uint16_t rnti, uint32_t lcid); - - // rlc_interface_pdcp_nr - void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu); - bool rb_is_um(uint16_t rnti, uint32_t lcid); - bool sdu_queue_is_full(uint16_t rnti, uint32_t lcid); - const char* get_rb_name(uint32_t lcid); - - // rlc_interface_mac_nr - int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); - // void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t* payload); - void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); - void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size); - -private: - class user_interface : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc - { - public: - void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu); - void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns); - void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns); - void write_pdu_bcch_bch(srsran::unique_byte_buffer_t sdu); - void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t sdu); - void write_pdu_pcch(srsran::unique_byte_buffer_t sdu); - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} - void max_retx_attempted() final; - const char* get_rb_name(uint32_t lcid) final; - uint16_t rnti; - - srsenb::pdcp_interface_rlc_nr* m_pdcp = nullptr; - srsenb::rrc_interface_rlc_nr* m_rrc = nullptr; - std::unique_ptr m_rlc; - rlc_nr* parent = nullptr; - }; - - // args - srsran::timer_handler* timers = nullptr; - mac_interface_rlc_nr* m_mac = nullptr; - pdcp_interface_rlc_nr* m_pdcp = nullptr; - rrc_interface_rlc_nr* m_rrc = nullptr; - srslog::basic_logger& logger; - - // state - std::map users; - std::vector mch_services; -}; - -} // namespace srsenb - -#endif // SRSENB_RLC_NR_H diff --git a/srsenb/hdr/x2_adapter.h b/srsenb/hdr/x2_adapter.h new file mode 100644 index 000000000..24fab4f26 --- /dev/null +++ b/srsenb/hdr/x2_adapter.h @@ -0,0 +1,161 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/** + * @brief Dummy X2AP implementation + * + * Dummy X2 adapter to facilitate communication between EUTRA RRC and NR RRC + * for EN-DC procedures. + * + * The class uses direct function calls instead of real X2AP ASN1 encoded + * messages. It mainly focuses on Sec 9.1.4 of TS 36.423 Rel. 15.11 + * for E-UTRAN-NR Dual Connectivity Procedures, i.e. SgNB-* + * + * It furthermore provide an interface for the GTPU adapter to + * write DL PDUs, which it then forwards to the NR PDCP. + * + * It also provides a method to allow the eNB to foward timing + * signal, i.e. TTI tics, to the NR stack. + */ + +#ifndef SRSENB_X2_ADAPTER_H +#define SRSENB_X2_ADAPTER_H + +#include "srsgnb/hdr/stack/gnb_stack_nr.h" +#include "srsran/interfaces/enb_x2_interfaces.h" +#include "stack/enb_stack_lte.h" + +namespace srsenb { + +class x2_adapter final : public x2_interface +{ +public: + x2_adapter(enb_stack_lte* eutra_stack_, gnb_stack_nr* nr_stack_) : + nr_stack(nr_stack_), eutra_stack(eutra_stack_), logger(srslog::fetch_basic_logger("X2")) + {} + + /// rrc_nr_interface_rrc + void sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) override + { + if (nr_stack == nullptr) { + logger.error("SgNB Addition Requested for inexistent NR stack"); + return; + } + nr_stack->sgnb_addition_request(eutra_rnti, params); + } + void sgnb_reconfiguration_complete(uint16_t eutra_rnti, const asn1::dyn_octstring& reconfig_response) override + { + if (nr_stack == nullptr) { + logger.error("SgNB Addition Requested for inexistent NR stack"); + return; + } + nr_stack->sgnb_reconfiguration_complete(eutra_rnti, reconfig_response); + } + + /// rrc_eutra_interface_rrc_nr + void sgnb_addition_ack(uint16_t eutra_rnti, sgnb_addition_ack_params_t params) override + { + if (eutra_stack == nullptr) { + return; + } + eutra_stack->sgnb_addition_ack(eutra_rnti, params); + } + void sgnb_addition_reject(uint16_t eutra_rnti) override + { + if (eutra_stack == nullptr) { + return; + } + eutra_stack->sgnb_addition_reject(eutra_rnti); + } + void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) override + { + if (eutra_stack == nullptr) { + return; + } + eutra_stack->sgnb_addition_complete(eutra_rnti, nr_rnti); + } + void sgnb_inactivity_timeout(uint16_t eutra_rnti) override + { + if (eutra_stack == nullptr) { + return; + } + eutra_stack->sgnb_inactivity_timeout(eutra_rnti); + } + + void sgnb_release_ack(uint16_t eutra_rnti) override + { + if (eutra_stack == nullptr) { + return; + } + eutra_stack->sgnb_release_ack(eutra_rnti); + } + + void set_activity_user(uint16_t eutra_rnti) override + { + if (eutra_stack == nullptr) { + return; + } + eutra_stack->set_activity_user(eutra_rnti); + } + + void sgnb_release_request(uint16_t nr_rnti) override + { + if (nr_stack == nullptr) { + logger.error("SgNB Addition Requested for inexistent NR stack"); + return; + } + nr_stack->sgnb_release_request(nr_rnti); + } + + // pdcp_interface_gtpu + void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) override + { + if (nr_stack == nullptr) { + return; + } + nr_stack->write_sdu(rnti, lcid, std::move(sdu), pdcp_sn); + } + std::map get_buffered_pdus(uint16_t rnti, uint32_t lcid) override + { + if (nr_stack == nullptr) { + return {}; + } + return nr_stack->get_buffered_pdus(rnti, lcid); + } + + // gtpu_interface_pdcp + void write_pdu(uint16_t rnti, uint32_t bearer_id, srsran::unique_byte_buffer_t pdu) override + { + if (eutra_stack == nullptr) { + return; + } + eutra_stack->write_pdu(rnti, bearer_id, std::move(pdu)); + } + +private: + enb_stack_lte* eutra_stack = nullptr; + gnb_stack_nr* nr_stack = nullptr; + srslog::basic_logger& logger; +}; + +} // namespace srsenb + +#endif // SRSENB_X2_ADAPTER_H diff --git a/srsenb/rb.conf.example b/srsenb/rb.conf.example new file mode 100644 index 000000000..010599f29 --- /dev/null +++ b/srsenb/rb.conf.example @@ -0,0 +1,187 @@ +// All times are in ms. Use -1 for infinity, where available + +// 4G Section + +// srb1_config = { +// rlc_config = { +// ul_am = { +// t_poll_retx = 45; +// poll_pdu = -1; +// poll_byte = -1; +// max_retx_thresh = 4; +// }; +// dl_am = { +// t_reordering = 35; +// t_status_prohibit = 0; +// }; +// enb_specific = { +// dl_max_retx_thresh = 32; +// }; +// }; +// } + +// srb2_config = { +// rlc_config = { +// ul_am = { +// t_poll_retx = 45; +// poll_pdu = -1; +// poll_byte = -1; +// max_retx_thresh = 4; +// }; +// dl_am = { +// t_reordering = 35; +// t_status_prohibit = 0; +// }; +// enb_specific = { +// dl_max_retx_thresh = 32; +// }; +// }; +// } + +qci_config = ( +{ + qci = 7; + pdcp_config = { + discard_timer = -1; + pdcp_sn_size = 12; + } + rlc_config = { + ul_um = { + sn_field_length = 10; + }; + dl_um = { + sn_field_length = 10; + t_reordering = 45; + }; + }; + logical_channel_config = { + priority = 13; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 2; + }; + enb_specific = { + dl_max_retx_thresh = 32; + }; +}, +{ + qci = 9; + pdcp_config = { + discard_timer = 150; + status_report_required = true; + } + rlc_config = { + ul_am = { + t_poll_retx = 120; + poll_pdu = 64; + poll_byte = 750; + max_retx_thresh = 16; + }; + dl_am = { + t_reordering = 50; + t_status_prohibit = 50; + }; + }; + logical_channel_config = { + priority = 11; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 3; + }; + enb_specific = { + dl_max_retx_thresh = 32; + }; +} +); + +// 5G Section +srb1_5g_config = { + rlc_config = { + ul_am = { + sn_field_len = 12; + t_poll_retx = 45; + poll_pdu = -1; + poll_byte = -1; + max_retx_thres = 8; + }; + dl_am = { + sn_field_len = 12; + t_reassembly = 35; + t_status_prohibit = 10; + }; + }; +} + +srb2_5g_config = { + rlc_config = { + ul_am = { + sn_field_len = 12; + t_poll_retx = 45; + poll_pdu = -1; + poll_byte = -1; + max_retx_thres = 8; + }; + dl_am = { + sn_field_len = 12; + t_reassembly = 35; + t_status_prohibit = 10; + }; + }; +} + +five_qi_config = ( +{ + five_qi = 7; + pdcp_nr_config = { + drb = { + pdcp_sn_size_ul = 18; + pdcp_sn_size_dl = 18; + discard_timer = 50; + integrity_protection = false; + status_report = false; + }; + t_reordering = 50; + }; + rlc_config = { + um_bi_dir = { + ul_um = { + sn_field_len = 12; + }; + dl_um = { + sn_field_len = 12; + t_reassembly = 50; + }; + }; + }; +}, +{ + five_qi = 9; + pdcp_nr_config = { + drb = { + pdcp_sn_size_ul = 18; + pdcp_sn_size_dl = 18; + discard_timer = 50; + integrity_protection = false; + status_report = false; + }; + t_reordering = 50; + }; + rlc_config = { + am = { + ul_am = { + sn_field_len = 12; + t_poll_retx = 50; + poll_pdu = 4; + poll_byte = 3000; + max_retx_thres = 4; + }; + dl_am = { + sn_field_len = 12; + t_reassembly = 50; + t_status_prohibit = 50; + }; + }; + }; +} +); + diff --git a/srsenb/rr.conf.example b/srsenb/rr.conf.example index 1390f179f..78b2df154 100644 --- a/srsenb/rr.conf.example +++ b/srsenb/rr.conf.example @@ -45,8 +45,8 @@ phy_cnfg = simultaneousAckCQI = true; period = 40; // in ms //subframe = [0, 10, 20, 30]; // Optional vector of subframe indices every period where CQI resources will be allocated (default uses all) - nof_prb = 1; m_ri = 8; // RI period in CQI period + //subband_k = 1; // If enabled and > 0, configures sub-band CQI reporting and defines K (see 36.213 7.2.2). If disabled, configures wideband CQI }; }; @@ -62,9 +62,13 @@ cell_list = //ul_earfcn = 21400; ho_active = false; //meas_gap_period = 0; // 0 (inactive), 40 or 80 + //meas_gap_offset_subframe = [6, 12, 18, 24, 30]; // target_pusch_sinr = -1; // target_pucch_sinr = -1; + // enable_phr_handling = false; + // min_phr_thres = 0; // allowed_meas_bw = 6; + // t304 = 2000; // in msec. possible values: 50, 100, 150, 200, 500, 1000, 2000 // CA cells scell_list = ( @@ -80,17 +84,34 @@ cell_list = pci = 2; //direct_forward_path_available = false; //allowed_meas_bw = 6; + //cell_individual_offset = 0; } ); - // ReportCfg (only A3 supported) - meas_report_desc = { - a3_report_type = "RSRP"; - a3_offset = 6; - a3_hysteresis = 0; - a3_time_to_trigger = 480; - rsrq_config = 4; - }; + // Select measurement report configuration (all reports are combined with all measurement objects) + meas_report_desc = + ( + { + eventA = 3 + a3_offset = 6; + hysteresis = 0; + time_to_trigger = 480; + trigger_quant = "RSRP"; + max_report_cells = 1; + report_interv = 120; + report_amount = 1; + } + ); + meas_quant_desc = { + // averaging filter coefficient + rsrq_config = 4; + rsrp_config = 4; + }; } // Add here more cells ); + +nr_cell_list = +( + // no NR cells +); \ No newline at end of file diff --git a/srsenb/sib.conf.example b/srsenb/sib.conf.example index 0d4e60aa4..bafdcb689 100644 --- a/srsenb/sib.conf.example +++ b/srsenb/sib.conf.example @@ -1,3 +1,12 @@ +##################################################################### +# sib1 configuration options (See TS 36.331) +# +# additional_plmns: A list of additional PLMN identities. +# mcc: MCC +# mnc: MNC +# cell_reserved_for_oper: One of "reserved" or "notReserved", default is "notReserved" +# +##################################################################### sib1 = { intra_freq_reselection = "Allowed"; @@ -137,12 +146,130 @@ sib3 = } }; +##################################################################### +# sib5 configuration options (See TS 36.331) +# Contains information relevant for inter-frequency cell re-selection. +# Must be added to sib1::sched_info::si_mapping_info array parameter to be transmitted +# +# inter_freq_carrier_freq_list: A list of neighbouring inter-frequencies. +# dl_carrier_freq: The EARFCN for the EUTRA carrier frequency. +# q_rx_lev_min: Minimum received RSRP level in the E-UTRA cell, ([field_val] * 2) = [level in dBm]. +# p_max: Optional maximum allowed transmission power for the neighbouring E-UTRA cells on this carrier frequency. +# t_resel_eutra: Cell reselection timer (seconds). +# t_resel_eutra_sf: Optional speed dependent ScalingFactor for t_resel_eutra. +# sf_medium: Scaling factor if the UE is in Medium Mobility state, one of "0.25", "0.5", "0.75" or "1.0". +# sf_high: Scaling factor if the UE is in High Mobility state, one of "0.25", "0.5", "0.75" or "1.0". +# thresh_x_high: Srclev threshold (dB) to select to a higher-priority RAT/Frequency. +# thresh_x_low: Srclev threshold (dB) to select to a lower-priority RAT/Frequency. +# allowed_meas_bw: Maximum allowed measurement bandwidth on a carrier frequency . +# presence_ant_port_1: whether all the neighbouring cells use Antenna Port 1. +# cell_resel_prio: Optional absolute priority of the carrier frequency group. +# neigh_cell_cfg: Information related to MBSFN and TDD UL/DL configuration of neighbour cells. +# q_offset_freq: Frequency specific offset for equal priority E-UTRAN frequencies. +# inter_freq_neigh_cell_list: A List of inter-frequency neighbouring cells with specific cell re-selection parameters. +# phys_cell_id: Physical layer identity of the cell. +# q_offset_cell: Cell spcific offset. +# inter_freq_black_cell_list: A List of blacklisted inter-frequency neighbouring cells. +# start: The lowest physical cell identity in the range. +# range: The number of physical cell identities in the range. +# +##################################################################### +sib5 = +{ + inter_freq_carrier_freq_list = + ( + { + dl_carrier_freq = 1450; + q_rx_lev_min = -70; + t_resel_eutra = 2; + t_resel_eutra_sf = { + sf_medium = "0.25"; + sf_high = "1.0"; + }; + thresh_x_high = 3; + thresh_x_low = 2; + allowed_meas_bw = 75; + presence_ant_port_1 = True; + cell_resel_prio = 4; + neigh_cell_cfg = 2; + q_offset_freq = -6; + inter_freq_neigh_cell_list = + ( + { + phys_cell_id = 500; + q_offset_cell = 2; + } + ); + inter_freq_black_cell_list = + ( + { + start = 123; + range = 4; + } + ); + } + ); +}; + +##################################################################### +# sib6 configuration options (See TS 36.331) +# Contains UTRA neighbor information for inter-rat handover. +# Must be added to sib1::sched_info::si_mapping_info array parameter to be transmitted +# +# t_resel_utra: Cell reselection timer (seconds) +# t_resel_utra_sf: Optional speed dependent ScalingFactor for t_resel_utra. +# sf_medium: Scaling factor if the UE is in Medium Mobility state, one of "0.25", "0.5", "0.75" or "1.0". +# sf_high: Scaling factor if the UE is in High Mobility state, one of "0.25", "0.5", "0.75" or "1.0". +# carrier_freq_list_utra_fdd / carrier_freq_list_utra_tdd: A list of carrier frequencies of UTRA FDD / TDD. +# carrier_freq: The UARFCN for the UTRA carrier frequency. +# cell_resel_prio: Optional absolute priority of the carrier frequency group. +# thresh_x_high: Srclev threshold (dB) to select to a higher-priority RAT/Frequency. +# thresh_x_low: Srclev threshold (dB) to select to a lower-priority RAT/Frequency. +# q_rx_lev_min: Minimum receive level in UTRA cell, ([field_val] * 2) + 1 = [level in dBm]. +# p_max_utra: The maximum allowed transmission power on the (uplink) carrier frequency. +# q_qual_min: Minimum required quality leve in UTRA cell, applicable only for FDD cells. +# +##################################################################### +sib6 = +{ + t_resel_utra = 1; + t_resel_utra_sf = { + sf_medium = "0.25"; + sf_high = "1.0"; + } + carrier_freq_list_utra_fdd = + ( + { + carrier_freq = 9613; + cell_resel_prio = 6; + thresh_x_high = 3; + thresh_x_low = 2; + q_rx_lev_min = -50; + p_max_utra = 4; + q_qual_min = -10; + } + ); + carrier_freq_list_utra_tdd = + ( + { + carrier_freq = 9505; + thresh_x_high = 1; + thresh_x_low = 2; + q_rx_lev_min = -50; + p_max_utra = -3; + } + ); +}; + ##################################################################### # sib7 configuration options (See TS 36.331) # Contains GERAN neighbor information for CSFB and inter-rat handover. # Must be added to sib1::sched_info::si_mapping_info array parameter to be transmitted # # t_resel_geran: Cell reselection timer (seconds) +# t_resel_geran_sf: Optional speed dependent ScalingFactor for t_resel_geran. +# sf_medium: Scaling factor if the UE is in Medium Mobility state, one of "0.25", "0.5", "0.75" or "1.0". +# sf_high: Scaling factor if the UE is in High Mobility state, one of "0.25", "0.5", "0.75" or "1.0". # carrier_freqs_info_list: A list of carrier frequency groups. # cell_resel_prio: Absolute priority of the carrier frequency group # ncc_permitted: 8-bit bitmap of NCC carriers permitted for monitoring diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 144e00302..63687972e 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -34,14 +34,14 @@ if (RPATH) endif (RPATH) add_library(enb_cfg_parser STATIC parser.cc enb_cfg_parser.cc) -target_link_libraries(enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) +target_link_libraries(enb_cfg_parser srsran_common srsgnb_rrc_config_utils ${LIBCONFIGPP_LIBRARIES}) add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) -set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_upper srsenb_mac srsenb_rrc srslog system) -set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_upper srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog system) +set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_s1ap srsenb_upper srsenb_mac srsenb_rrc srslog system) +set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_gtpu srsran_rlc srsran_pdcp srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog support system) -set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_phy srsgnb_stack srsgnb_upper srsgnb_mac srsgnb_rrc) +set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_stack srsgnb_ngap srsgnb_mac srsgnb_rrc) set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) target_link_libraries(srsenb ${SRSENB_SOURCES} @@ -66,4 +66,4 @@ else(NOT ${BUILDENB_CMD} STREQUAL "") message(STATUS "No post-build-ENB command defined") endif (NOT ${BUILDENB_CMD} STREQUAL "") -install(TARGETS srsenb DESTINATION ${RUNTIME_DIR}) +install(TARGETS srsenb DESTINATION ${RUNTIME_DIR} OPTIONAL) diff --git a/srsenb/src/common/CMakeLists.txt b/srsenb/src/common/CMakeLists.txt index 2e8c6981d..ade120f93 100644 --- a/srsenb/src/common/CMakeLists.txt +++ b/srsenb/src/common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsenb/src/common/rnti_pool.cc b/srsenb/src/common/rnti_pool.cc index 1b095597e..1c8afcddd 100644 --- a/srsenb/src/common/rnti_pool.cc +++ b/srsenb/src/common/rnti_pool.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,14 +21,23 @@ #include "srsenb/hdr/common/rnti_pool.h" #include "srsenb/hdr/common/common_enb.h" +#include "srsenb/hdr/stack/mac/ue.h" +#include "srsenb/hdr/stack/rrc/rrc_endc.h" +#include "srsenb/hdr/stack/rrc/rrc_mobility.h" +#include "srsenb/hdr/stack/rrc/rrc_ue.h" #include "srsran/adt/pool/circular_stack_pool.h" +#include "srsran/rlc/rlc.h" +#include "srsran/upper/pdcp.h" namespace srsenb { +const static size_t UE_MEM_BLOCK_SIZE = 1024 + sizeof(ue) + sizeof(rrc::ue) + sizeof(rrc::ue::rrc_mobility) + + sizeof(rrc::ue::rrc_endc) + sizeof(srsran::rlc) + sizeof(srsran::pdcp); + srsran::circular_stack_pool* get_rnti_pool() { static std::unique_ptr > pool( - new srsran::circular_stack_pool(8, 32768, 4)); + new srsran::circular_stack_pool(8, UE_MEM_BLOCK_SIZE, 4)); return pool.get(); } diff --git a/srsenb/src/enb.cc b/srsenb/src/enb.cc index bff489806..e0f714f4f 100644 --- a/srsenb/src/enb.cc +++ b/srsenb/src/enb.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,10 +20,10 @@ */ #include "srsenb/hdr/enb.h" -#include "srsenb/hdr/phy/vnf_phy_nr.h" #include "srsenb/hdr/stack/enb_stack_lte.h" -#include "srsenb/hdr/stack/gnb_stack_nr.h" +#include "srsenb/hdr/x2_adapter.h" #include "srsenb/src/enb_cfg_parser.h" +#include "srsgnb/hdr/stack/gnb_stack_nr.h" #include "srsran/build_info.h" #include "srsran/common/enb_events.h" #include "srsran/radio/radio_null.h" @@ -40,7 +40,8 @@ enb::enb(srslog::sink& log_sink) : enb::~enb() { - stack.reset(); + eutra_stack.reset(); + nr_stack.reset(); } int enb::init(const all_args_t& args_) @@ -52,7 +53,7 @@ int enb::init(const all_args_t& args_) enb_log.info("%s", get_build_string().c_str()); // Validate arguments - if (parse_args(args_, rrc_cfg)) { + if (parse_args(args_, rrc_cfg, rrc_nr_cfg)) { srsran::console("Error processing arguments.\n"); return SRSRAN_ERROR; } @@ -60,92 +61,89 @@ int enb::init(const all_args_t& args_) srsran::byte_buffer_pool::get_instance()->enable_logger(true); // Create layers - if (args.stack.type == "lte") { - std::unique_ptr lte_stack(new enb_stack_lte(log_sink)); - if (!lte_stack) { - srsran::console("Error creating eNB stack.\n"); + std::unique_ptr tmp_eutra_stack; + if (not rrc_cfg.cell_list.empty()) { + // add EUTRA stack + tmp_eutra_stack.reset(new enb_stack_lte(log_sink)); + if (tmp_eutra_stack == nullptr) { + srsran::console("Error creating EUTRA stack.\n"); return SRSRAN_ERROR; } - - std::unique_ptr lte_radio = std::unique_ptr(new srsran::radio); - if (!lte_radio) { - srsran::console("Error creating radio multi instance.\n"); - return SRSRAN_ERROR; - } - - std::unique_ptr lte_phy = std::unique_ptr(new srsenb::phy(log_sink)); - if (!lte_phy) { - srsran::console("Error creating LTE PHY instance.\n"); - return SRSRAN_ERROR; - } - - // Init Radio - if (lte_radio->init(args.rf, lte_phy.get())) { - srsran::console("Error initializing radio.\n"); - return SRSRAN_ERROR; - } - - // Only Init PHY if radio couldn't be initialized - if (ret == SRSRAN_SUCCESS) { - if (lte_phy->init(args.phy, phy_cfg, lte_radio.get(), lte_stack.get())) { - srsran::console("Error initializing PHY.\n"); - ret = SRSRAN_ERROR; - } - } - - // Only init Stack if both radio and PHY could be initialized - if (ret == SRSRAN_SUCCESS) { - if (lte_stack->init(args.stack, rrc_cfg, lte_phy.get()) != SRSRAN_SUCCESS) { - srsran::console("Error initializing stack.\n"); - ret = SRSRAN_ERROR; - } - } - - stack = std::move(lte_stack); - phy = std::move(lte_phy); - radio = std::move(lte_radio); - - } else if (args.stack.type == "nr") { - std::unique_ptr nr_stack(new srsenb::gnb_stack_nr); - std::unique_ptr nr_radio(new srsran::radio_null); - std::unique_ptr nr_phy(new srsenb::vnf_phy_nr); - - // Init layers - if (nr_radio->init(args.rf, nullptr)) { - srsran::console("Error initializing radio.\n"); - return SRSRAN_ERROR; - } - - // TODO: where do we put this? - srsenb::nr_phy_cfg_t nr_phy_cfg = {}; - - args.phy.vnf_args.type = "gnb"; - args.phy.vnf_args.log_level = args.phy.log.phy_level; - args.phy.vnf_args.log_hex_limit = args.phy.log.phy_hex_limit; - if (nr_phy->init(args.phy, nr_phy_cfg, nr_stack.get())) { - srsran::console("Error initializing PHY.\n"); - return SRSRAN_ERROR; - } - - // Same here, where do we put this? - srsenb::rrc_nr_cfg_t rrc_nr_cfg = {}; - rrc_nr_cfg.coreless = args.stack.coreless; - - if (nr_stack->init(args.stack, rrc_nr_cfg, nr_phy.get())) { - srsran::console("Error initializing stack.\n"); - return SRSRAN_ERROR; - } - - stack = std::move(nr_stack); - phy = std::move(nr_phy); - radio = std::move(nr_radio); } + std::unique_ptr tmp_nr_stack; + if (not rrc_nr_cfg.cell_list.empty()) { + // add NR stack + tmp_nr_stack.reset(new gnb_stack_nr(log_sink)); + if (tmp_nr_stack == nullptr) { + srsran::console("Error creating NR stack.\n"); + return SRSRAN_ERROR; + } + } + + // If NR and EUTRA stacks were initiated, create an X2 adapter between the two. + if (tmp_nr_stack != nullptr and tmp_eutra_stack != nullptr) { + x2.reset(new x2_adapter(tmp_eutra_stack.get(), tmp_nr_stack.get())); + } + + // Radio and PHY are RAT agnostic + std::unique_ptr tmp_radio = std::unique_ptr(new srsran::radio); + if (tmp_radio == nullptr) { + srsran::console("Error creating radio multi instance.\n"); + return SRSRAN_ERROR; + } + + std::unique_ptr tmp_phy = std::unique_ptr(new srsenb::phy(log_sink)); + if (tmp_phy == nullptr) { + srsran::console("Error creating PHY instance.\n"); + return SRSRAN_ERROR; + } + + // initialize layers, if they exist + if (tmp_eutra_stack) { + if (tmp_eutra_stack->init(args.stack, rrc_cfg, tmp_phy.get(), x2.get()) != SRSRAN_SUCCESS) { + srsran::console("Error initializing EUTRA stack.\n"); + ret = SRSRAN_ERROR; + } + } + + if (tmp_nr_stack) { + if (tmp_nr_stack->init(args.nr_stack, rrc_nr_cfg, tmp_phy.get(), x2.get()) != SRSRAN_SUCCESS) { + srsran::console("Error initializing NR stack.\n"); + ret = SRSRAN_ERROR; + } + } + + // Init Radio + if (tmp_radio->init(args.rf, tmp_phy.get())) { + srsran::console("Error initializing radio.\n"); + return SRSRAN_ERROR; + } + + // Only Init PHY if radio could be initialized + if (ret == SRSRAN_SUCCESS) { + if (tmp_phy->init(args.phy, phy_cfg, tmp_radio.get(), tmp_eutra_stack.get(), *tmp_nr_stack, this)) { + srsran::console("Error initializing PHY.\n"); + ret = SRSRAN_ERROR; + } + } + + if (tmp_eutra_stack) { + eutra_stack = std::move(tmp_eutra_stack); + } + if (tmp_nr_stack) { + nr_stack = std::move(tmp_nr_stack); + } + phy = std::move(tmp_phy); + radio = std::move(tmp_radio); + started = true; // set to true in any case to allow stopping the eNB if an error happened // Now that everything is setup, log sector start events. + const std::string& sib9_hnb_name = + rrc_cfg.sibs[8].sib9().hnb_name_present ? rrc_cfg.sibs[8].sib9().hnb_name.to_string() : ""; for (unsigned i = 0, e = rrc_cfg.cell_list.size(); i != e; ++i) { - event_logger::get().log_sector_start(i, rrc_cfg.cell_list[i].pci, rrc_cfg.cell_list[i].cell_id); + event_logger::get().log_sector_start(i, rrc_cfg.cell_list[i].pci, rrc_cfg.cell_list[i].cell_id, sib9_hnb_name); } if (ret == SRSRAN_SUCCESS) { @@ -167,28 +165,34 @@ void enb::stop() phy->stop(); } - if (stack) { - stack->stop(); - } - if (radio) { radio->stop(); } + if (eutra_stack) { + eutra_stack->stop(); + } + + if (nr_stack) { + nr_stack->stop(); + } + // Now that everything is teared down, log sector stop events. + const std::string& sib9_hnb_name = + rrc_cfg.sibs[8].sib9().hnb_name_present ? rrc_cfg.sibs[8].sib9().hnb_name.to_string() : ""; for (unsigned i = 0, e = rrc_cfg.cell_list.size(); i != e; ++i) { - event_logger::get().log_sector_stop(i, rrc_cfg.cell_list[i].pci, rrc_cfg.cell_list[i].cell_id); + event_logger::get().log_sector_stop(i, rrc_cfg.cell_list[i].pci, rrc_cfg.cell_list[i].cell_id, sib9_hnb_name); } started = false; } } -int enb::parse_args(const all_args_t& args_, rrc_cfg_t& _rrc_cfg) +int enb::parse_args(const all_args_t& args_, rrc_cfg_t& rrc_cfg_, rrc_nr_cfg_t& rrc_cfg_nr_) { // set member variable args = args_; - return enb_conf_sections::parse_cfg_files(&args, &_rrc_cfg, &phy_cfg); + return enb_conf_sections::parse_cfg_files(&args, &rrc_cfg_, &rrc_cfg_nr_, &phy_cfg); } void enb::start_plot() @@ -203,10 +207,18 @@ void enb::print_pool() bool enb::get_metrics(enb_metrics_t* m) { + if (!started) { + return false; + } radio->get_metrics(&m->rf); phy->get_metrics(m->phy); - stack->get_metrics(&m->stack); - m->running = started; + if (eutra_stack) { + eutra_stack->get_metrics(&m->stack); + } + if (nr_stack) { + nr_stack->get_metrics(&m->nr_stack); + } + m->running = true; m->sys = sys_proc.get_metrics(); return true; } @@ -216,6 +228,11 @@ void enb::cmd_cell_gain(uint32_t cell_id, float gain) phy->cmd_cell_gain(cell_id, gain); } +void enb::cmd_cell_measure() +{ + phy->cmd_cell_measure(); +} + std::string enb::get_build_mode() { return std::string(srsran_get_build_mode()); @@ -236,4 +253,27 @@ std::string enb::get_build_string() return ss.str(); } +void enb::toggle_padding() +{ + if (!started) { + return; + } + if (eutra_stack) { + eutra_stack->toggle_padding(); + } +} + +void enb::tti_clock() +{ + if (!started) { + return; + } + if (eutra_stack) { + eutra_stack->tti_clock(); + } + if (nr_stack) { + nr_stack->tti_clock(); + } +} + } // namespace srsenb diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index bf1fe4b72..ac3a90b58 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,9 +21,12 @@ #include "enb_cfg_parser.h" #include "srsenb/hdr/enb.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h" #include "srsran/asn1/rrc_utils.h" +#include "srsran/common/band_helper.h" #include "srsran/common/multiqueue.h" #include "srsran/phy/common/phy_common.h" +#include "srsran/rrc/rrc_common.h" #include #define HANDLEPARSERCODE(cond) \ @@ -53,6 +56,17 @@ using namespace asn1::rrc; namespace srsenb { +template +bool contains_value(T value, const std::initializer_list& list) +{ + for (auto& v : list) { + if (v == value) { + return true; + } + } + return false; +} + bool sib_is_present(const sched_info_list_l& l, sib_type_e sib_num) { for (uint32_t i = 0; i < l.size(); i++) { @@ -65,13 +79,46 @@ bool sib_is_present(const sched_info_list_l& l, sib_type_e sib_num) return false; } +int field_additional_plmns::parse(libconfig::Setting& root) +{ + if (root.getLength() > ASN1_RRC_MAX_PLMN_MINUS1_R14) { + ERROR("PLMN-IdentityList cannot have more than %d entries", ASN1_RRC_MAX_PLMN_R11); + return SRSRAN_ERROR; + } + // Reserve the first place to the primary PLMN, see "SystemInformationBlockType1 field descriptions" in TS 36.331 + data->plmn_id_list.resize((uint32_t)root.getLength() + 1); + for (uint32_t i = 0; i < data->plmn_id_list.size() - 1; i++) { + std::string mcc_str, mnc_str; + if (!root[i].lookupValue("mcc", mcc_str)) { + ERROR("Missing field mcc in additional_plmn=%d\n", i); + return SRSRAN_ERROR; + } + + if (!root[i].lookupValue("mnc", mnc_str)) { + ERROR("Missing field mnc in additional_plmn=%d\n", i); + return SRSRAN_ERROR; + } + + srsran::plmn_id_t plmn; + if (plmn.from_string(mcc_str + mnc_str) == SRSRAN_ERROR) { + ERROR("Could not convert %s to a plmn_id in additional_plmn=%d", (mcc_str + mnc_str).c_str(), i); + return SRSRAN_ERROR; + } + srsran::to_asn1(&data->plmn_id_list[i + 1].plmn_id, plmn); + if (not parse_enum_by_str(data->plmn_id_list[i + 1].cell_reserved_for_oper, "cell_reserved_for_oper", root[i])) { + data->plmn_id_list[i + 1].cell_reserved_for_oper = plmn_id_info_s::cell_reserved_for_oper_e_::not_reserved; + } + } + return 0; +} + int field_sched_info::parse(libconfig::Setting& root) { data->sched_info_list.resize((uint32_t)root.getLength()); for (uint32_t i = 0; i < data->sched_info_list.size(); i++) { if (not parse_enum_by_number(data->sched_info_list[i].si_periodicity, "si_periodicity", root[i])) { fprintf(stderr, "Missing field si_periodicity in sched_info=%d\n", i); - return -1; + return SRSRAN_ERROR; } if (root[i].exists("si_mapping_info")) { data->sched_info_list[i].sib_map_info.resize((uint32_t)root[i]["si_mapping_info"].getLength()); @@ -82,12 +129,12 @@ int field_sched_info::parse(libconfig::Setting& root) data->sched_info_list[i].sib_map_info[j].value = (sib_type_e::options)(sib_index - 3); } else { fprintf(stderr, "Invalid SIB index %d for si_mapping_info=%d in sched_info=%d\n", sib_index, j, i); - return -1; + return SRSRAN_ERROR; } } } else { fprintf(stderr, "Number of si_mapping_info values exceeds maximum (%d)\n", ASN1_RRC_MAX_SIB); - return -1; + return SRSRAN_ERROR; } } else { data->sched_info_list[i].sib_map_info.resize(0); @@ -103,13 +150,13 @@ int field_intra_neigh_cell_list::parse(libconfig::Setting& root) for (uint32_t i = 0; i < data->intra_freq_neigh_cell_list.size() && i < ASN1_RRC_MAX_CELL_INTRA; i++) { if (not parse_enum_by_number(data->intra_freq_neigh_cell_list[i].q_offset_cell, "q_offset_range", root[i])) { fprintf(stderr, "Missing field q_offset_range in neigh_cell=%d\n", i); - return -1; + return SRSRAN_ERROR; } int phys_cell_id = 0; if (!root[i].lookupValue("phys_cell_id", phys_cell_id)) { fprintf(stderr, "Missing field phys_cell_id in neigh_cell=%d\n", i); - return -1; + return SRSRAN_ERROR; } data->intra_freq_neigh_cell_list[i].pci = (uint16)phys_cell_id; } @@ -119,31 +166,286 @@ int field_intra_neigh_cell_list::parse(libconfig::Setting& root) int field_intra_black_cell_list::parse(libconfig::Setting& root) { data->intra_freq_black_cell_list.resize((uint32_t)root.getLength()); - data->intra_freq_black_cell_list_present = data->intra_freq_neigh_cell_list.size() > 0; + data->intra_freq_black_cell_list_present = data->intra_freq_black_cell_list.size() > 0; for (uint32_t i = 0; i < data->intra_freq_black_cell_list.size() && i < ASN1_RRC_MAX_CELL_BLACK; i++) { if (not parse_enum_by_number(data->intra_freq_black_cell_list[i].range, "range", root[i])) { fprintf(stderr, "Missing field range in black_cell=%d\n", i); - return -1; + return SRSRAN_ERROR; } data->intra_freq_black_cell_list[i].range_present = true; int start = 0; if (!root[i].lookupValue("start", start)) { fprintf(stderr, "Missing field start in black_cell=%d\n", i); - return -1; + return SRSRAN_ERROR; } data->intra_freq_black_cell_list[i].start = (uint16)start; } return 0; } +int field_inter_freq_carrier_freq_list::parse(libconfig::Setting& root) +{ + data->inter_freq_carrier_freq_list.resize((uint32_t)root.getLength()); + for (uint32_t i = 0; i < data->inter_freq_carrier_freq_list.size() && i < ASN1_RRC_MAX_FREQ; i++) { + unsigned int dl_carrier_freq = 0; + if (!root[i].lookupValue("dl_carrier_freq", dl_carrier_freq)) { + ERROR("Missing field `dl_carrier_freq` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].dl_carrier_freq = dl_carrier_freq; + + int q_rx_lev_min = 0; + if (!root[i].lookupValue("q_rx_lev_min", q_rx_lev_min)) { + ERROR("Missing field `q_rx_lev_min` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].q_rx_lev_min = q_rx_lev_min; + + int p_max = 0; + if (root[i].lookupValue("p_max", p_max)) { + data->inter_freq_carrier_freq_list[i].p_max_present = true; + data->inter_freq_carrier_freq_list[i].p_max = p_max; + } + + unsigned int t_resel_eutra = 0; + if (!root[i].lookupValue("t_resel_eutra", t_resel_eutra)) { + ERROR("Missing field `t_resel_eutra` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].t_resel_eutra = t_resel_eutra; + + if (root[i].exists("t_resel_eutra_sf")) { + data->inter_freq_carrier_freq_list[i].t_resel_eutra_sf_present = true; + + field_asn1_enum_number_str sf_medium( + "sf_medium", &data->inter_freq_carrier_freq_list[i].t_resel_eutra_sf.sf_medium); + if (sf_medium.parse(root[i]["t_resel_eutra_sf"])) { + ERROR("Error parsing `sf_medium` in inter_freq_carrier_freq_list=%d t_resel_eutra_sf", i); + return SRSRAN_ERROR; + } + + field_asn1_enum_number_str sf_high( + "sf_high", &data->inter_freq_carrier_freq_list[i].t_resel_eutra_sf.sf_high); + if (sf_high.parse(root[i]["t_resel_eutra_sf"])) { + ERROR("Error parsing `sf_high` in inter_freq_carrier_freq_list=%d t_resel_eutra_sf", i); + return SRSRAN_ERROR; + } + } + + unsigned int thresh_x_high = 0; + if (!root[i].lookupValue("thresh_x_high", thresh_x_high)) { + ERROR("Missing field `thresh_x_high` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].thresh_x_high = thresh_x_high; + + unsigned int thresh_x_low = 0; + if (!root[i].lookupValue("thresh_x_low", thresh_x_low)) { + ERROR("Missing field `thresh_x_low` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].thresh_x_low = thresh_x_low; + + field_asn1_enum_number allowed_meas_bw( + "allowed_meas_bw", &data->inter_freq_carrier_freq_list[i].allowed_meas_bw); + if (allowed_meas_bw.parse(root[i])) { + ERROR("Error parsing `allowed_meas_bw` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + + bool presence_ant_port1 = 0; + if (!root[i].lookupValue("presence_ant_port_1", presence_ant_port1)) { + ERROR("Missing field `presence_ant_port_1` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + data->inter_freq_carrier_freq_list[i].presence_ant_port1 = presence_ant_port1; + + unsigned int cell_resel_prio = 0; + if (root[i].lookupValue("cell_resel_prio", cell_resel_prio)) { + data->inter_freq_carrier_freq_list[i].cell_resel_prio_present = true; + data->inter_freq_carrier_freq_list[i].cell_resel_prio = cell_resel_prio; + } + + field_asn1_enum_number q_offset_freq( + "q_offset_freq", &data->inter_freq_carrier_freq_list[i].q_offset_freq); + if (!q_offset_freq.parse(root[i])) { + data->inter_freq_carrier_freq_list[i].q_offset_freq_present = true; + } + + field_asn1_bitstring_number, uint8_t> neigh_cell_cfg( + "neigh_cell_cfg", &data->inter_freq_carrier_freq_list[i].neigh_cell_cfg); + if (neigh_cell_cfg.parse(root[i])) { + ERROR("Error parsing `neigh_cell_cfg` in inter_freq_carrier_freq_list=%d", i); + return SRSRAN_ERROR; + } + + if (root[i].exists("inter_freq_neigh_cell_list")) { + auto inter_neigh_cell_list_parser = new field_inter_freq_neigh_cell_list(&data->inter_freq_carrier_freq_list[i]); + HANDLEPARSERCODE(inter_neigh_cell_list_parser->parse(root[i]["inter_freq_neigh_cell_list"])); + } + + if (root[i].exists("inter_freq_black_cell_list")) { + auto inter_black_cell_list_parser = new field_inter_freq_black_cell_list(&data->inter_freq_carrier_freq_list[i]); + HANDLEPARSERCODE(inter_black_cell_list_parser->parse(root[i]["inter_freq_black_cell_list"])); + } + } + return 0; +} + +int field_inter_freq_neigh_cell_list::parse(libconfig::Setting& root) +{ + data->inter_freq_neigh_cell_list.resize((uint32_t)root.getLength()); + data->inter_freq_neigh_cell_list_present = data->inter_freq_neigh_cell_list.size() > 0; + for (uint32_t i = 0; i < data->inter_freq_neigh_cell_list.size() && i < ASN1_RRC_MAX_CELL_BLACK; i++) { + if (not parse_enum_by_number(data->inter_freq_neigh_cell_list[i].q_offset_cell, "q_offset_cell", root[i])) { + ERROR("Missing field q_offset_cell in neigh_cell=%d\n", i); + return SRSRAN_ERROR; + } + + unsigned int phys_cell_id = 0; + if (!root[i].lookupValue("phys_cell_id", phys_cell_id)) { + ERROR("Missing field phys_cell_id in neigh_cell=%d\n", i); + return SRSRAN_ERROR; + } + data->inter_freq_neigh_cell_list[i].pci = (uint16)phys_cell_id; + } + return 0; +} + +int field_inter_freq_black_cell_list::parse(libconfig::Setting& root) +{ + data->inter_freq_black_cell_list.resize((uint32_t)root.getLength()); + data->inter_freq_black_cell_list_present = data->inter_freq_black_cell_list.size() > 0; + for (uint32_t i = 0; i < data->inter_freq_black_cell_list.size() && i < ASN1_RRC_MAX_CELL_BLACK; i++) { + if (not parse_enum_by_number(data->inter_freq_black_cell_list[i].range, "range", root[i])) { + ERROR("Missing field range in black_cell=%d\n", i); + return SRSRAN_ERROR; + } + data->inter_freq_black_cell_list[i].range_present = true; + + unsigned int start = 0; + if (!root[i].lookupValue("start", start)) { + ERROR("Missing field start in black_cell=%d\n", i); + return SRSRAN_ERROR; + } + data->inter_freq_black_cell_list[i].start = (uint16)start; + } + return 0; +} + +int field_carrier_freq_list_utra_fdd::parse(libconfig::Setting& root) +{ + data->carrier_freq_list_utra_fdd.resize((uint32_t)root.getLength()); + data->carrier_freq_list_utra_fdd_present = data->carrier_freq_list_utra_fdd.size() > 0; + for (uint32_t i = 0; i < data->carrier_freq_list_utra_fdd.size() && i < ASN1_RRC_MAX_UTRA_FDD_CARRIER; i++) { + unsigned int carrier_freq = 0; + if (!root[i].lookupValue("carrier_freq", carrier_freq)) { + fprintf(stderr, "Missing field `carrier_freq` in carrier_freq_list_utra_fdd=%d\n", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_fdd[i].carrier_freq = carrier_freq; + + unsigned int cell_resel_prio = 0; + if (root[i].lookupValue("cell_resel_prio", cell_resel_prio)) { + data->carrier_freq_list_utra_fdd[i].cell_resel_prio_present = true; + data->carrier_freq_list_utra_fdd[i].cell_resel_prio = cell_resel_prio; + } + + unsigned int thresh_x_high = 0; + if (!root[i].lookupValue("thresh_x_high", thresh_x_high)) { + ERROR("Missing field `thresh_x_high` in carrier_freq_list_utra_fdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_fdd[i].thresh_x_high = thresh_x_high; + + unsigned int thresh_x_low = 0; + if (!root[i].lookupValue("thresh_x_low", thresh_x_low)) { + ERROR("Missing field `thresh_x_low` in carrier_freq_list_utra_fdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_fdd[i].thresh_x_low = thresh_x_low; + + int q_rx_lev_min = 0; + if (!root[i].lookupValue("q_rx_lev_min", q_rx_lev_min)) { + ERROR("Missing field `q_rx_lev_min` in carrier_freq_list_utra_fdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_fdd[i].q_rx_lev_min = q_rx_lev_min; + + int p_max_utra = 0; + if (!root[i].lookupValue("p_max_utra", p_max_utra)) { + ERROR("Missing field `p_max_utra` in carrier_freq_list_utra_fdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_fdd[i].p_max_utra = p_max_utra; + + int q_qual_min = 0; + if (!root[i].lookupValue("q_qual_min", q_qual_min)) { + ERROR("Missing field `q_qual_min` in carrier_freq_list_utra_fdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_fdd[i].q_qual_min = q_qual_min; + } + return 0; +} + +int field_carrier_freq_list_utra_tdd::parse(libconfig::Setting& root) +{ + data->carrier_freq_list_utra_tdd.resize((uint32_t)root.getLength()); + data->carrier_freq_list_utra_tdd_present = data->carrier_freq_list_utra_tdd.size() > 0; + for (uint32_t i = 0; i < data->carrier_freq_list_utra_tdd.size() && i < ASN1_RRC_MAX_UTRA_TDD_CARRIER; i++) { + unsigned int carrier_freq = 0; + if (!root[i].lookupValue("carrier_freq", carrier_freq)) { + fprintf(stderr, "Missing field `carrier_freq` in carrier_freq_list_utra_tdd=%d\n", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_tdd[i].carrier_freq = carrier_freq; + + unsigned int cell_resel_prio = 0; + if (root[i].lookupValue("cell_resel_prio", cell_resel_prio)) { + data->carrier_freq_list_utra_tdd[i].cell_resel_prio_present = true; + data->carrier_freq_list_utra_tdd[i].cell_resel_prio = cell_resel_prio; + } + + unsigned int thresh_x_high = 0; + if (!root[i].lookupValue("thresh_x_high", thresh_x_high)) { + ERROR("Missing field `thresh_x_high` in carrier_freq_list_utra_tdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_tdd[i].thresh_x_high = thresh_x_high; + + unsigned int thresh_x_low = 0; + if (!root[i].lookupValue("thresh_x_low", thresh_x_low)) { + ERROR("Missing field `thresh_x_low` in carrier_freq_list_utra_tdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_tdd[i].thresh_x_low = thresh_x_low; + + int q_rx_lev_min = 0; + if (!root[i].lookupValue("q_rx_lev_min", q_rx_lev_min)) { + ERROR("Missing field `q_rx_lev_min` in carrier_freq_list_utra_tdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_tdd[i].q_rx_lev_min = q_rx_lev_min; + + int p_max_utra = 0; + if (!root[i].lookupValue("p_max_utra", p_max_utra)) { + ERROR("Missing field `p_max_utra` in carrier_freq_list_utra_tdd=%d", i); + return SRSRAN_ERROR; + } + data->carrier_freq_list_utra_tdd[i].p_max_utra = p_max_utra; + } + return 0; +} + int field_carrier_freqs_info_list::parse(libconfig::Setting& root) { data->carrier_freqs_info_list.resize((uint32_t)root.getLength()); data->carrier_freqs_info_list_present = data->carrier_freqs_info_list.size() > 0; if (data->carrier_freqs_info_list.size() > ASN1_RRC_MAX_GNFG) { ERROR("CarrierFreqsInfoGERAN cannot have more than %d entries", ASN1_RRC_MAX_GNFG); - return -1; + return SRSRAN_ERROR; } for (uint32_t i = 0; i < data->carrier_freqs_info_list.size(); i++) { int cell_resel_prio; @@ -161,28 +463,28 @@ int field_carrier_freqs_info_list::parse(libconfig::Setting& root) field_asn1_bitstring_number, uint8_t> ncc_permitted( "ncc_permitted", &data->carrier_freqs_info_list[i].common_info.ncc_permitted); if (ncc_permitted.parse(root[i])) { - ERROR("Error parsing `ncc_permitted` in carrier_freqs_info_lsit=%d", i); - return -1; + ERROR("Error parsing `ncc_permitted` in carrier_freqs_info_list=%d", i); + return SRSRAN_ERROR; } int q_rx_lev_min = 0; if (!root[i].lookupValue("q_rx_lev_min", q_rx_lev_min)) { ERROR("Missing field `q_rx_lev_min` in carrier_freqs_info_list=%d", i); - return -1; + return SRSRAN_ERROR; } data->carrier_freqs_info_list[i].common_info.q_rx_lev_min = q_rx_lev_min; int thresh_x_high = 0; if (!root[i].lookupValue("thresh_x_high", thresh_x_high)) { ERROR("Missing field `thresh_x_high` in carrier_freqs_info_list=%d", i); - return -1; + return SRSRAN_ERROR; } data->carrier_freqs_info_list[i].common_info.thresh_x_high = thresh_x_high; int thresh_x_low = 0; if (!root[i].lookupValue("thresh_x_low", thresh_x_low)) { ERROR("Missing field `thresh_x_low` in carrier_freqs_info_list=%d", i); - return -1; + return SRSRAN_ERROR; } data->carrier_freqs_info_list[i].common_info.thresh_x_low = thresh_x_low; @@ -195,7 +497,7 @@ int field_carrier_freqs_info_list::parse(libconfig::Setting& root) &data->carrier_freqs_info_list[i].carrier_freqs.band_ind); if (band_ind.parse(root[i])) { ERROR("Error parsing `band_ind` in carrier_freqs_info_list=%d", i); - return -1; + return SRSRAN_ERROR; } data->carrier_freqs_info_list[i].carrier_freqs.following_arfcns.set_explicit_list_of_arfcns(); @@ -211,12 +513,12 @@ int field_carrier_freqs_info_list::parse(libconfig::Setting& root) exp_l[j] = (short unsigned int)arfcn; } else { fprintf(stderr, "Invalid ARFCN %d in for carrier_freqs_info_list=%d explicit_list_of_arfcns\n", i, j); - return -1; + return SRSRAN_ERROR; } } } else { fprintf(stderr, "Number of ARFCN in explicit_list_of_arfcns exceeds maximum (%d)\n", 31); - return -1; + return SRSRAN_ERROR; } } else { exp_l.resize(0); @@ -262,7 +564,7 @@ int mbsfn_sf_cfg_list_parser::parse(Setting& root) } if (len > 1) { fprintf(stderr, "Only mbsfnSubframeConfigListLengths of size 1 are supported\n"); - return -1; + return SRSRAN_ERROR; } *enabled = true; mbsfn_list->resize(len); @@ -304,53 +606,53 @@ int mbsfn_area_info_list_parser::parse(Setting& root) &mbsfn_item->non_mbsfn_region_len); if (fieldlen.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing non_mbsfn_region_length\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_str repeat( "mcch_repetition_period", &mbsfn_item->mcch_cfg_r9.mcch_repeat_period_r9); if (repeat.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mcch_repetition_period\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_str mod( "mcch_modification_period", &mbsfn_item->mcch_cfg_r9.mcch_mod_period_r9); if (mod.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mcch_modification_period\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_str sig("signalling_mcs", &mbsfn_item->mcch_cfg_r9.sig_mcs_r9); if (sig.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing signalling_mcs\n"); - return -1; + return SRSRAN_ERROR; } parser::field areaid("mbsfn_area_id", &mbsfn_item->mbsfn_area_id_r9); if (areaid.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mbsfn_area_id\n"); - return -1; + return SRSRAN_ERROR; } parser::field notif_ind("notification_indicator", &mbsfn_item->notif_ind_r9); if (notif_ind.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing notification_indicator\n"); - return -1; + return SRSRAN_ERROR; } parser::field offset("mcch_offset", &mbsfn_item->mcch_cfg_r9.mcch_offset_r9); if (offset.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mcch_offset\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_bitstring_number, uint8_t> alloc_info("sf_alloc_info", &mbsfn_item->mcch_cfg_r9.sf_alloc_info_r9); if (alloc_info.parse(root["mbsfn_area_info_list"])) { fprintf(stderr, "Error parsing mbsfn_area_info_list\n"); - return -1; + return SRSRAN_ERROR; } return 0; @@ -384,17 +686,83 @@ int phr_cnfg_parser::parse(libconfig::Setting& root) mac_main_cfg_s::phr_cfg_c_::setup_s_& s = phr_cfg->setup(); if (not parse_enum_by_str(s.dl_pathloss_change, "dl_pathloss_change", root["phr_cnfg"])) { - return -1; + return SRSRAN_ERROR; } if (not parse_enum_by_number(s.periodic_phr_timer, "periodic_phr_timer", root["phr_cnfg"])) { - return -1; + return SRSRAN_ERROR; } if (not parse_enum_by_number(s.prohibit_phr_timer, "prohibit_phr_timer", root["phr_cnfg"])) { - return -1; + return SRSRAN_ERROR; } return 0; } +int field_srb::parse(libconfig::Setting& root) +{ + // Parse RLC AM section + rlc_cfg_c* rlc_cfg = &cfg.rlc_cfg.set_explicit_value(); + if (root.exists("ul_am") && root.exists("dl_am")) { + rlc_cfg->set_am(); + } + + // RLC-UM Should not exist section + if (root.exists("ul_um") || root.exists("dl_um")) { + ERROR("Error SRBs must be AM."); + return SRSRAN_ERROR; + } + + // Parse RLC-AM section + if (root.exists("ul_am")) { + ul_am_rlc_s* am_rlc = &rlc_cfg->am().ul_am_rlc; + + field_asn1_enum_number t_poll_retx("t_poll_retx", &am_rlc->t_poll_retx); + if (t_poll_retx.parse(root["ul_am"])) { + ERROR("Error can't find t_poll_retx in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number poll_pdu("poll_pdu", &am_rlc->poll_pdu); + if (poll_pdu.parse(root["ul_am"])) { + ERROR("Error can't find poll_pdu in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number poll_byte("poll_byte", &am_rlc->poll_byte); + if (poll_byte.parse(root["ul_am"])) { + ERROR("Error can't find poll_byte in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number max_retx_thresh("max_retx_thresh", &am_rlc->max_retx_thres); + if (max_retx_thresh.parse(root["ul_am"])) { + ERROR("Error can't find max_retx_thresh in section ul_am"); + return SRSRAN_ERROR; + } + } + + if (root.exists("dl_am")) { + dl_am_rlc_s* am_rlc = &rlc_cfg->am().dl_am_rlc; + + field_asn1_enum_number t_reordering("t_reordering", &am_rlc->t_reordering); + if (t_reordering.parse(root["dl_am"])) { + ERROR("Error can't find t_reordering in section dl_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number t_status_prohibit("t_status_prohibit", &am_rlc->t_status_prohibit); + if (t_status_prohibit.parse(root["dl_am"])) { + ERROR("Error can't find t_status_prohibit in section dl_am"); + return SRSRAN_ERROR; + } + } + + if (root.exists("enb_specific")) { + cfg.enb_dl_max_retx_thres = (int)root["enb_specific"]["dl_max_retx_thresh"]; + } + + return 0; +} + int field_qci::parse(libconfig::Setting& root) { auto nof_qci = (uint32_t)root.getLength(); @@ -407,7 +775,7 @@ int field_qci::parse(libconfig::Setting& root) // Parse PDCP section if (!q.exists("pdcp_config")) { fprintf(stderr, "Error section pdcp_config not found for qci=%d\n", qci); - return -1; + return SRSRAN_ERROR; } rrc_cfg_qci_t qcicfg; @@ -436,7 +804,7 @@ int field_qci::parse(libconfig::Setting& root) rlc_cfg->set_um_uni_dir_dl(); } else { fprintf(stderr, "Invalid combination of UL/DL UM/AM for qci=%d\n", qci); - return -1; + return SRSRAN_ERROR; } // Parse RLC-UM section @@ -451,7 +819,7 @@ int field_qci::parse(libconfig::Setting& root) field_asn1_enum_number sn_field_len("sn_field_length", &um_rlc->sn_field_len); if (sn_field_len.parse(q["rlc_config"]["ul_um"])) { ERROR("Error can't find sn_field_length in section ul_um"); - return -1; + return SRSRAN_ERROR; } } @@ -466,13 +834,13 @@ int field_qci::parse(libconfig::Setting& root) field_asn1_enum_number sn_field_len("sn_field_length", &um_rlc->sn_field_len); if (sn_field_len.parse(q["rlc_config"]["dl_um"])) { ERROR("Error can't find sn_field_length in section dl_um"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number t_reordering("t_reordering", &um_rlc->t_reordering); if (t_reordering.parse(q["rlc_config"]["dl_um"])) { ERROR("Error can't find t_reordering in section dl_um"); - return -1; + return SRSRAN_ERROR; } } @@ -483,26 +851,26 @@ int field_qci::parse(libconfig::Setting& root) field_asn1_enum_number t_poll_retx("t_poll_retx", &am_rlc->t_poll_retx); if (t_poll_retx.parse(q["rlc_config"]["ul_am"])) { ERROR("Error can't find t_poll_retx in section ul_am"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number poll_pdu("poll_pdu", &am_rlc->poll_pdu); if (poll_pdu.parse(q["rlc_config"]["ul_am"])) { ERROR("Error can't find poll_pdu in section ul_am"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number poll_byte("poll_byte", &am_rlc->poll_byte); if (poll_byte.parse(q["rlc_config"]["ul_am"])) { ERROR("Error can't find poll_byte in section ul_am"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number max_retx_thresh("max_retx_thresh", &am_rlc->max_retx_thres); if (max_retx_thresh.parse(q["rlc_config"]["ul_am"])) { ERROR("Error can't find max_retx_thresh in section ul_am"); - return -1; + return SRSRAN_ERROR; } } @@ -512,20 +880,20 @@ int field_qci::parse(libconfig::Setting& root) field_asn1_enum_number t_reordering("t_reordering", &am_rlc->t_reordering); if (t_reordering.parse(q["rlc_config"]["dl_am"])) { ERROR("Error can't find t_reordering in section dl_am"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number t_status_prohibit("t_status_prohibit", &am_rlc->t_status_prohibit); if (t_status_prohibit.parse(q["rlc_config"]["dl_am"])) { ERROR("Error can't find t_status_prohibit in section dl_am"); - return -1; + return SRSRAN_ERROR; } } // Parse logical channel configuration section if (!q.exists("logical_channel_config")) { fprintf(stderr, "Error section logical_channel_config not found for qci=%d\n", qci); - return -1; + return SRSRAN_ERROR; } lc_ch_cfg_s::ul_specific_params_s_* lc_cfg = &qcicfg.lc_cfg; @@ -533,35 +901,296 @@ int field_qci::parse(libconfig::Setting& root) parser::field priority("priority", &lc_cfg->prio); if (priority.parse(q["logical_channel_config"])) { ERROR("Error can't find logical_channel_config in section priority"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number prioritised_bit_rate( "prioritized_bit_rate", &lc_cfg->prioritised_bit_rate); if (prioritised_bit_rate.parse(q["logical_channel_config"])) { fprintf(stderr, "Error can't find prioritized_bit_rate in section logical_channel_config\n"); - return -1; + return SRSRAN_ERROR; } field_asn1_enum_number bucket_size_duration( "bucket_size_duration", &lc_cfg->bucket_size_dur); if (bucket_size_duration.parse(q["logical_channel_config"])) { ERROR("Error can't find bucket_size_duration in section logical_channel_config"); - return -1; + return SRSRAN_ERROR; } parser::field log_chan_group("log_chan_group", &lc_cfg->lc_ch_group); lc_cfg->lc_ch_group_present = not log_chan_group.parse(q["logical_channel_config"]); qcicfg.configured = true; + + if (q.exists("enb_specific")) { + qcicfg.enb_dl_max_retx_thres = (int)q["enb_specific"]["dl_max_retx_thresh"]; + } + cfg.insert(std::make_pair(qci, qcicfg)); } return 0; } +int field_5g_srb::parse(libconfig::Setting& root) +{ + // Parse RLC AM section + asn1::rrc_nr::rlc_cfg_c* rlc_cfg = &cfg.rlc_cfg; + if (root.exists("ul_am") && root.exists("dl_am")) { + rlc_cfg->set_am(); + cfg.present = true; + } + + // RLC-UM must not exist in this section + if (root.exists("ul_um") || root.exists("dl_um")) { + ERROR("Error SRBs must be AM."); + return SRSRAN_ERROR; + } + + // Parse RLC-AM section + if (root.exists("ul_am")) { + asn1::rrc_nr::ul_am_rlc_s& ul_am_rlc = rlc_cfg->am().ul_am_rlc; + + // SN length + field_asn1_enum_number rlc_sn_size_ul("sn_field_len", &ul_am_rlc.sn_field_len); + if (rlc_sn_size_ul.parse(root["ul_am"]) == SRSRAN_ERROR) { + ul_am_rlc.sn_field_len_present = false; + } else { + ul_am_rlc.sn_field_len_present = true; + } + + field_asn1_enum_number t_poll_retx("t_poll_retx", &ul_am_rlc.t_poll_retx); + if (t_poll_retx.parse(root["ul_am"])) { + ERROR("Error can't find t_poll_retx in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number poll_pdu("poll_pdu", &ul_am_rlc.poll_pdu); + if (poll_pdu.parse(root["ul_am"])) { + ERROR("Error can't find poll_pdu in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number poll_byte("poll_byte", &ul_am_rlc.poll_byte); + if (poll_byte.parse(root["ul_am"])) { + ERROR("Error can't find poll_byte in section ul_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number max_retx_thresh("max_retx_thres", + &ul_am_rlc.max_retx_thres); + if (max_retx_thresh.parse(root["ul_am"])) { + ERROR("Error can't find max_retx_thresh in section ul_am"); + return SRSRAN_ERROR; + } + } + + if (root.exists("dl_am")) { + asn1::rrc_nr::dl_am_rlc_s& dl_am_rlc = rlc_cfg->am().dl_am_rlc; + + // SN length + field_asn1_enum_number rlc_sn_size_ul("sn_field_len", &dl_am_rlc.sn_field_len); + if (rlc_sn_size_ul.parse(root["dl_am"]) == SRSRAN_ERROR) { + dl_am_rlc.sn_field_len_present = false; + } else { + dl_am_rlc.sn_field_len_present = true; + } + + field_asn1_enum_number t_reassembly("t_reassembly", &dl_am_rlc.t_reassembly); + if (t_reassembly.parse(root["dl_am"])) { + ERROR("Error can't find t_reordering in section dl_am"); + return SRSRAN_ERROR; + } + + field_asn1_enum_number t_status_prohibit("t_status_prohibit", + &dl_am_rlc.t_status_prohibit); + if (t_status_prohibit.parse(root["dl_am"])) { + ERROR("Error can't find t_status_prohibit in section dl_am"); + return SRSRAN_ERROR; + } + } + + return 0; +} + +int field_five_qi::parse(libconfig::Setting& root) +{ + uint32_t nof_five_qi = (uint32_t)root.getLength(); + for (uint32_t i = 0; i < nof_five_qi; i++) { + libconfig::Setting& q = root[i]; + + uint32_t five_qi = q["five_qi"]; + + rrc_nr_cfg_five_qi_t five_qi_cfg; + + // Parse PDCP section + if (!q.exists("pdcp_nr_config")) { + fprintf(stderr, "Error section pdcp_nr_config not found for 5qi=%d\n", five_qi); + return SRSRAN_ERROR; + } + libconfig::Setting& pdcp_nr = q["pdcp_nr_config"]; + asn1::rrc_nr::pdcp_cfg_s* pdcp_cfg = &five_qi_cfg.pdcp_cfg; + + // Get PDCP-NR DRB configs + if (!pdcp_nr.exists("drb")) { + fprintf(stderr, "Error section drb not found for 5QI=%d\n", five_qi); + return SRSRAN_ERROR; + } + libconfig::Setting& drb = pdcp_nr["drb"]; + asn1::rrc_nr::pdcp_cfg_s::drb_s_* drb_cfg = &pdcp_cfg->drb; + pdcp_cfg->drb_present = true; + + // PDCP SN size UL + field_asn1_enum_number pdcp_sn_size_ul( + "pdcp_sn_size_ul", &drb_cfg->pdcp_sn_size_ul); + if (pdcp_sn_size_ul.parse(drb) == SRSRAN_ERROR) { + drb_cfg->pdcp_sn_size_ul_present = false; + } else { + drb_cfg->pdcp_sn_size_ul_present = true; + } + + // PDCP SN size DL + field_asn1_enum_number pdcp_sn_size_dl( + "pdcp_sn_size_dl", &drb_cfg->pdcp_sn_size_dl); + if (pdcp_sn_size_dl.parse(drb) == SRSRAN_ERROR) { + drb_cfg->pdcp_sn_size_dl_present = false; + } else { + drb_cfg->pdcp_sn_size_dl_present = true; + } + + // Discard timer + field_asn1_enum_number discard_timer("discard_timer", + &drb_cfg->discard_timer); + if (discard_timer.parse(drb) == -1) { + drb_cfg->discard_timer_present = false; + } else { + drb_cfg->discard_timer_present = true; + } + + parser::field status_report_required("status_report_required", &drb_cfg->status_report_required_present); + status_report_required.parse(drb); + + parser::field out_of_order_delivery("out_of_order_delivery", &drb_cfg->out_of_order_delivery_present); + out_of_order_delivery.parse(drb); + + parser::field integrity_protection("integrity_protection", &drb_cfg->integrity_protection_present); + integrity_protection.parse(drb); + + drb_cfg->hdr_compress.set_not_used(); + // Finish DRB config + + // t_Reordering + field_asn1_enum_number t_reordering("t_reordering", + &pdcp_cfg->t_reordering); + if (t_reordering.parse(pdcp_nr) == SRSRAN_ERROR) { + pdcp_cfg->t_reordering_present = false; + } else { + pdcp_cfg->t_reordering_present = true; + } + + // Parse RLC section + if (!q.exists("rlc_config")) { + fprintf(stderr, "Error section rlc_config not found for 5qi=%d\n", five_qi); + return SRSRAN_ERROR; + } + libconfig::Setting& rlc = q["rlc_config"]; + asn1::rrc_nr::rlc_cfg_c* rlc_cfg = &five_qi_cfg.rlc_cfg; + if (rlc.exists("um_uni_dir_ul") || rlc.exists("um_uni_dir_dl")) { + // Sanity check: RLC UM uni-directional is not supported. + fprintf(stderr, "Error uni-directional UM not supported. 5QI=%d\n", five_qi); + return SRSRAN_ERROR; + } + + if (rlc.exists("am")) { + rlc_cfg->set_am(); + } else if (rlc.exists("um_bi_dir")) { + rlc_cfg->set_um_bi_dir(); + } else { + fprintf(stderr, "Invalid combination of UL/DL UM/AM for 5QI=%d\n", five_qi); + return SRSRAN_ERROR; + } + + // Parse RLC-AM section + if (rlc_cfg->type() == asn1::rrc_nr::rlc_cfg_c::types::am) { + libconfig::Setting& rlc_am = rlc["am"]; + libconfig::Setting& rlc_am_ul = rlc_am["ul_am"]; + libconfig::Setting& rlc_am_dl = rlc_am["dl_am"]; + asn1::rrc_nr::ul_am_rlc_s& ul_am_cfg = rlc_cfg->am().ul_am_rlc; + asn1::rrc_nr::dl_am_rlc_s& dl_am_cfg = rlc_cfg->am().dl_am_rlc; + + // RLC AM UL + // SN length + field_asn1_enum_number rlc_sn_size_ul("sn_field_len", &ul_am_cfg.sn_field_len); + if (rlc_sn_size_ul.parse(rlc_am_ul) == SRSRAN_ERROR) { + ul_am_cfg.sn_field_len_present = false; + } else { + ul_am_cfg.sn_field_len_present = true; + } + // t-PollRetx + field_asn1_enum_number rlc_t_poll_retx("t_poll_retx", &ul_am_cfg.t_poll_retx); + rlc_t_poll_retx.parse(rlc_am_ul); + // pollPDU + field_asn1_enum_number rlc_poll_pdu("poll_pdu", &ul_am_cfg.poll_pdu); + rlc_poll_pdu.parse(rlc_am_ul); + // pollBYTE + field_asn1_enum_number rlc_poll_bytes("poll_byte", &ul_am_cfg.poll_byte); + rlc_poll_bytes.parse(rlc_am_ul); + // maxRetxThreshold + field_asn1_enum_number rlc_max_retx_thres( + "max_retx_thres", &ul_am_cfg.max_retx_thres); + rlc_max_retx_thres.parse(rlc_am_ul); + + // RLC AM DL + // SN length + field_asn1_enum_number rlc_sn_size_dl("sn_field_len", &dl_am_cfg.sn_field_len); + if (rlc_sn_size_dl.parse(rlc_am_dl) == SRSRAN_ERROR) { + dl_am_cfg.sn_field_len_present = false; + } else { + dl_am_cfg.sn_field_len_present = true; + } + // t-reassembly + field_asn1_enum_number rlc_t_reassembly("t_reassembly", &dl_am_cfg.t_reassembly); + rlc_t_reassembly.parse(rlc_am_dl); + // t-statusProhibit + field_asn1_enum_number rlc_status_prohibit("t_status_prohibit", + &dl_am_cfg.t_status_prohibit); + rlc_status_prohibit.parse(rlc_am_dl); + } else if (rlc_cfg->type() == asn1::rrc_nr::rlc_cfg_c::types::um_bi_dir) { + libconfig::Setting& rlc_um = rlc["um_bi_dir"]; + libconfig::Setting& rlc_um_ul = rlc_um["ul_um"]; + libconfig::Setting& rlc_um_dl = rlc_um["dl_um"]; + asn1::rrc_nr::ul_um_rlc_s& ul_um_cfg = rlc_cfg->um_bi_dir().ul_um_rlc; + asn1::rrc_nr::dl_um_rlc_s& dl_um_cfg = rlc_cfg->um_bi_dir().dl_um_rlc; + + // RLC UM UL + // SN field length + field_asn1_enum_number rlc_sn_size_ul("sn_field_len", &ul_um_cfg.sn_field_len); + if (rlc_sn_size_ul.parse(rlc_um_ul) == SRSRAN_ERROR) { + ul_um_cfg.sn_field_len_present = false; + } else { + ul_um_cfg.sn_field_len_present = true; + } + + // RLC UM DL + // SN field length + field_asn1_enum_number rlc_sn_size_dl("sn_field_len", &dl_um_cfg.sn_field_len); + if (rlc_sn_size_dl.parse(rlc_um_dl) == SRSRAN_ERROR) { + dl_um_cfg.sn_field_len_present = false; + } else { + dl_um_cfg.sn_field_len_present = true; + } + // t-reassembly + field_asn1_enum_number rlc_t_reassembly_dl("t_reassembly", &dl_um_cfg.t_reassembly); + rlc_t_reassembly_dl.parse(rlc_um_dl); + } + + cfg.insert(std::make_pair(five_qi, five_qi_cfg)); + } + return 0; +} namespace rr_sections { -int parse_rr(all_args_t* args_, rrc_cfg_t* rrc_cfg_) +int parse_rr(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_) { /* Transmission mode config section */ if (args_->enb.transmission_mode < 1 || args_->enb.transmission_mode > 4) { @@ -662,20 +1291,28 @@ int parse_rr(all_args_t* args_, rrc_cfg_t* rrc_cfg_) "mode", &rrc_cfg_->cqi_cfg.mode, rrc_cfg_cqi_mode_text, RRC_CFG_CQI_MODE_N_ITEMS)); cqi_report_cnfg.add_field(new parser::field("period", &rrc_cfg_->cqi_cfg.period)); cqi_report_cnfg.add_field(new parser::field("m_ri", &rrc_cfg_->cqi_cfg.m_ri)); - cqi_report_cnfg.add_field(new parser::field("nof_prb", &rrc_cfg_->cqi_cfg.nof_prb)); + cqi_report_cnfg.add_field( + new parser::field("subband_k", &rrc_cfg_->cqi_cfg.subband_k, &rrc_cfg_->cqi_cfg.is_subband_enabled)); cqi_report_cnfg.add_field(new parser::field("simultaneousAckCQI", &rrc_cfg_->cqi_cfg.simultaneousAckCQI)); cqi_report_cnfg.add_field(new field_sf_mapping(rrc_cfg_->cqi_cfg.sf_mapping, &rrc_cfg_->cqi_cfg.nof_subframes, 1)); - /* RRC config section */ - parser::section rrc_cnfg("cell_list"); - rrc_cnfg.set_optional(&rrc_cfg_->meas_cfg_present); - rrc_cnfg.add_field(new rr_sections::cell_list_section(args_, rrc_cfg_)); + // EUTRA RRC and cell config section + parser::section cell_cnfg("cell_list"); + cell_cnfg.set_optional(&rrc_cfg_->meas_cfg_present); + cell_cnfg.add_field(new rr_sections::cell_list_section(args_, rrc_cfg_)); + + // NR RRC and cell config section + bool nr_cell_cnfg_present = false; + parser::section nr_cell_cnfg("nr_cell_list"); + nr_cell_cnfg.set_optional(&nr_cell_cnfg_present); + nr_cell_cnfg.add_field(new rr_sections::nr_cell_list_section(args_, rrc_nr_cfg_, rrc_cfg_)); // Run parser with two sections parser p(args_->enb_files.rr_config); p.add_section(&mac_cnfg); p.add_section(&phy_cfg_); - p.add_section(&rrc_cnfg); + p.add_section(&cell_cnfg); + p.add_section(&nr_cell_cnfg); return p.parse(); } @@ -684,42 +1321,133 @@ static int parse_meas_cell_list(rrc_meas_cfg_t* meas_cfg, Setting& root) { meas_cfg->meas_cells.resize(root.getLength()); for (uint32_t i = 0; i < meas_cfg->meas_cells.size(); ++i) { - auto& cell = meas_cfg->meas_cells[i]; - cell.earfcn = root[i]["dl_earfcn"]; - cell.pci = (unsigned int)root[i]["pci"] % SRSRAN_NUM_PCI; - cell.eci = (unsigned int)root[i]["eci"]; - cell.q_offset = 0; // LIBLTE_RRC_Q_OFFSET_RANGE_DB_0; // TODO + auto& cell = meas_cfg->meas_cells[i]; + cell.earfcn = root[i]["dl_earfcn"]; + cell.pci = (unsigned int)root[i]["pci"] % SRSRAN_NUM_PCI; + cell.eci = (unsigned int)root[i]["eci"]; parse_default_field(cell.direct_forward_path_available, root[i], "direct_forward_path_available", false); parse_default_field(cell.allowed_meas_bw, root[i], "allowed_meas_bw", 6u); + asn1_parsers::default_number_to_enum( + cell.cell_individual_offset, root[i], "cell_individual_offset", asn1::rrc::q_offset_range_opts::db0); + parse_default_field(cell.tac, root[i], "tac", -1); srsran_assert(srsran::is_lte_cell_nof_prb(cell.allowed_meas_bw), "Invalid measurement Bandwidth"); } return 0; } -static int parse_meas_report_desc(rrc_meas_cfg_t* meas_cfg, Setting& root) +static int parse_meas_report_desc(rrc_meas_cfg_t* meas_cfg, Setting& cellroot) { - // NOTE: For now, only support one meas_report for all cells. - // TODO: for a1 - // TODO: for a2 - // meas report parsing - meas_cfg->meas_reports.resize(1); - asn1::rrc::report_cfg_eutra_s& meas_item = meas_cfg->meas_reports[0]; - HANDLEPARSERCODE(asn1_parsers::str_to_enum(meas_item.trigger_quant, root["a3_report_type"])); - auto& event = meas_item.trigger_type.set_event(); - event.event_id.set_event_a3().report_on_leave = false; - event.event_id.event_a3().a3_offset = (int)root["a3_offset"]; - event.hysteresis = (int)root["a3_hysteresis"]; - meas_item.max_report_cells = 1; // TODO: parse - meas_item.report_amount.value = report_cfg_eutra_s::report_amount_e_::r1; // TODO: parse - meas_item.report_interv.value = report_interv_e::ms120; // TODO: parse - meas_item.report_quant.value = report_cfg_eutra_s::report_quant_opts::both; // TODO: parse + // NOTE: Events A1, A2, A3 and A4 are supported. A3 and A4 will be configured for all neighbour cells + + Setting& root = cellroot["meas_report_desc"]; + + meas_cfg->meas_reports.resize(root.getLength()); + for (int i = 0; i < root.getLength(); i++) { + asn1::rrc::report_cfg_eutra_s& meas_item = meas_cfg->meas_reports[i]; + + // Parse trigger quantity before event + HANDLEPARSERCODE(asn1_parsers::str_to_enum(meas_item.trigger_quant, root[i]["trigger_quant"])); + + auto& event = meas_item.trigger_type.set_event(); + + // Configure event + switch ((int)root[i]["eventA"]) { + case 1: + if (!root[i].exists("a1_thresh")) { + ERROR("Missing a1_thresh field for A1 event\n"); + return SRSRAN_ERROR; + } + if (meas_item.trigger_quant == report_cfg_eutra_s::trigger_quant_opts::rsrp) { + event.event_id.set_event_a1().a1_thres.set_thres_rsrp() = + rrc_value_to_range(srsran::quant_rsrp, (int)root[i]["a1_thresh"]); + } else { + event.event_id.set_event_a1().a1_thres.set_thres_rsrq() = + rrc_value_to_range(srsran::quant_rsrq, (int)root[i]["a1_thresh"]); + } + break; + case 2: + if (!root[i].exists("a2_thresh")) { + ERROR("Missing a2_thresh field for A2 event\n"); + return SRSRAN_ERROR; + } + if (meas_item.trigger_quant == report_cfg_eutra_s::trigger_quant_opts::rsrp) { + event.event_id.set_event_a2().a2_thres.set_thres_rsrp() = + rrc_value_to_range(srsran::quant_rsrp, (int)root[i]["a2_thresh"]); + } else { + event.event_id.set_event_a2().a2_thres.set_thres_rsrq() = + rrc_value_to_range(srsran::quant_rsrq, (int)root[i]["a2_thresh"]); + } + break; + case 3: + if (!root[i].exists("a3_offset")) { + ERROR("Missing a3_offset field for A3 event\n"); + return SRSRAN_ERROR; + } + event.event_id.set_event_a3().report_on_leave = false; + event.event_id.event_a3().a3_offset = (int)root[i]["a3_offset"]; + break; + case 4: + if (!root[i].exists("a4_thresh")) { + ERROR("Missing a4_thresh field for A4 event\n"); + return SRSRAN_ERROR; + } + if (meas_item.trigger_quant == report_cfg_eutra_s::trigger_quant_opts::rsrp) { + event.event_id.set_event_a4().a4_thres.set_thres_rsrp() = + rrc_value_to_range(srsran::quant_rsrp, (int)root[i]["a4_thresh"]); + } else { + event.event_id.set_event_a4().a4_thres.set_thres_rsrq() = + rrc_value_to_range(srsran::quant_rsrq, (int)root[i]["a4_thresh"]); + } + break; + case 5: + // a5-threshold1 + if (!root[i].exists("a5_thresh1")) { + ERROR("Missing a5_thresh1 field for A5 event\n"); + return SRSRAN_ERROR; + } + if (meas_item.trigger_quant == report_cfg_eutra_s::trigger_quant_opts::rsrp) { + event.event_id.set_event_a5().a5_thres1.set_thres_rsrp() = + rrc_value_to_range(srsran::quant_rsrp, (int)root[i]["a5_thresh1"]); + } else { + event.event_id.set_event_a5().a5_thres1.set_thres_rsrq() = + rrc_value_to_range(srsran::quant_rsrq, (int)root[i]["a5_thresh1"]); + } + + // a5-threshold2 + if (!root[i].exists("a5_thresh2")) { + ERROR("Missing a5_thresh2 field for A5 event\n"); + return SRSRAN_ERROR; + } + if (meas_item.trigger_quant == report_cfg_eutra_s::trigger_quant_opts::rsrp) { + event.event_id.set_event_a5().a5_thres2.set_thres_rsrp() = + rrc_value_to_range(srsran::quant_rsrp, (int)root[i]["a5_thresh2"]); + } else { + event.event_id.set_event_a5().a5_thres2.set_thres_rsrq() = + rrc_value_to_range(srsran::quant_rsrq, (int)root[i]["a5_thresh2"]); + } + break; + default: + ERROR("Invalid or unsupported event A%d in meas_report_desc (only A1-A5 are supported)\n", + (int)root[i]["eventA"]); + return SRSRAN_ERROR; + } + + // Configure common variables + event.hysteresis = (int)root[i]["hysteresis"]; + HANDLEPARSERCODE(asn1_parsers::number_to_enum(event.time_to_trigger, root[i]["time_to_trigger"])); + meas_item.report_quant.value = report_cfg_eutra_s::report_quant_opts::both; // TODO: parse + meas_item.max_report_cells = (int)root[i]["max_report_cells"]; + HANDLEPARSERCODE(asn1_parsers::number_to_enum(meas_item.report_interv, root[i]["report_interv"])); + HANDLEPARSERCODE(asn1_parsers::number_to_enum(meas_item.report_amount, root[i]["report_amount"])); + } + // quant coeff parsing auto& quant = meas_cfg->quant_cfg; - HANDLEPARSERCODE(asn1_parsers::number_to_enum(event.time_to_trigger, root["a3_time_to_trigger"])); - HANDLEPARSERCODE( - asn1_parsers::opt_number_to_enum(quant.filt_coef_rsrp, quant.filt_coef_rsrp_present, root, "rsrp_config")); - HANDLEPARSERCODE( - asn1_parsers::opt_number_to_enum(quant.filt_coef_rsrq, quant.filt_coef_rsrq_present, root, "rsrq_config")); + + HANDLEPARSERCODE(asn1_parsers::opt_number_to_enum( + quant.filt_coef_rsrp, quant.filt_coef_rsrp_present, cellroot["meas_quant_desc"], "rsrp_config")); + HANDLEPARSERCODE(asn1_parsers::opt_number_to_enum( + quant.filt_coef_rsrq, quant.filt_coef_rsrq_present, cellroot["meas_quant_desc"], "rsrq_config")); return SRSRAN_SUCCESS; } @@ -751,56 +1479,181 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root) HANDLEPARSERCODE(parse_required_field(cell_cfg.cell_id, cellroot, "cell_id")); HANDLEPARSERCODE(parse_required_field(cell_cfg.tac, cellroot, "tac")); HANDLEPARSERCODE(parse_required_field(cell_cfg.pci, cellroot, "pci")); + parse_default_field(cell_cfg.tx_gain, cellroot, "tx_gain", 0.0); cell_cfg.pci = cell_cfg.pci % SRSRAN_NUM_PCI; HANDLEPARSERCODE(parse_required_field(cell_cfg.dl_earfcn, cellroot, "dl_earfcn")); + parse_default_field(cell_cfg.dl_freq_hz, cellroot, "dl_freq", 0.0); // will be derived from DL EARFCN If not set + parse_default_field(cell_cfg.ul_freq_hz, cellroot, "ul_freq", 0.0); // will be derived from DL EARFCN If not set parse_default_field(cell_cfg.ul_earfcn, cellroot, "ul_earfcn", 0u); // will be derived from DL EARFCN If not set parse_default_field( cell_cfg.root_seq_idx, cellroot, "root_seq_idx", rrc_cfg->sibs[1].sib2().rr_cfg_common.prach_cfg.root_seq_idx); - parse_default_field(cell_cfg.initial_dl_cqi, cellroot, "initial_dl_cqi", 5u); parse_default_field(cell_cfg.meas_cfg.meas_gap_period, cellroot, "meas_gap_period", 0u); + if (cellroot.exists("meas_gap_offset_subframe")) { + cell_cfg.meas_cfg.meas_gap_offset_subframe.resize(cellroot["meas_gap_offset_subframe"].getLength()); + for (uint32_t j = 0; j < (uint32_t)cellroot["meas_gap_offset_subframe"].getLength(); ++j) { + cell_cfg.meas_cfg.meas_gap_offset_subframe[j] = (uint32_t)cellroot["meas_gap_offset_subframe"][j]; + srsran_assert(cell_cfg.meas_cfg.meas_gap_offset_subframe[j] < cell_cfg.meas_cfg.meas_gap_period, + "meas gap offsets must be smaller than meas gap period"); + } + } HANDLEPARSERCODE(parse_default_field(cell_cfg.target_pusch_sinr_db, cellroot, "target_pusch_sinr", -1)); HANDLEPARSERCODE(parse_default_field(cell_cfg.target_pucch_sinr_db, cellroot, "target_pucch_sinr", -1)); HANDLEPARSERCODE(parse_default_field(cell_cfg.enable_phr_handling, cellroot, "enable_phr_handling", false)); + HANDLEPARSERCODE(parse_default_field(cell_cfg.min_phr_thres, cellroot, "min_phr_thres", 0)); parse_default_field(cell_cfg.meas_cfg.allowed_meas_bw, cellroot, "allowed_meas_bw", 6u); srsran_assert(srsran::is_lte_cell_nof_prb(cell_cfg.meas_cfg.allowed_meas_bw), "Invalid measurement Bandwidth"); + HANDLEPARSERCODE(asn1_parsers::default_number_to_enum( + cell_cfg.t304, cellroot, "t304", asn1::rrc::mob_ctrl_info_s::t304_opts::ms2000)); if (cellroot.exists("ho_active") and cellroot["ho_active"]) { HANDLEPARSERCODE(parse_meas_cell_list(&cell_cfg.meas_cfg, cellroot["meas_cell_list"])); if (not cellroot.exists("meas_report_desc")) { ERROR("PARSER ERROR: \"ho_active\" is set to true, but field \"meas_report_desc\" doesn't exist.\n"); - return -1; + return SRSRAN_ERROR; } - HANDLEPARSERCODE(parse_meas_report_desc(&cell_cfg.meas_cfg, cellroot["meas_report_desc"])); + HANDLEPARSERCODE(parse_meas_report_desc(&cell_cfg.meas_cfg, cellroot)); } if (cellroot.exists("scell_list")) { HANDLEPARSERCODE(parse_scell_list(cell_cfg, cellroot)); } - std::string type = "lte"; - if (cellroot.exists("type")) { - cellroot.lookupValue("type", type); - } - if (type == "lte") { - rrc_cfg->cell_list.push_back(cell_cfg); - } else if (type == "nr") { - rrc_cfg->cell_list_nr.push_back(cell_cfg); - } + rrc_cfg->cell_list.push_back(cell_cfg); } // Configuration check + // counter for every RF port used by the eNB to avoid misconfiguration/mapping of cells + uint32_t next_rf_port = 0; for (auto it = rrc_cfg->cell_list.begin(); it != rrc_cfg->cell_list.end(); it++) { + // Make sure RF ports are assigned in order + if (it->rf_port != next_rf_port) { + ERROR("RF ports need to be in order starting with 0 (%d != %d)", it->rf_port, next_rf_port); + return SRSRAN_ERROR; + } + next_rf_port++; + for (auto it2 = it + 1; it2 != rrc_cfg->cell_list.end(); it2++) { // Check RF port is not repeated if (it->rf_port == it2->rf_port) { ERROR("Repeated RF port for multiple cells"); - return -1; + return SRSRAN_ERROR; } // Check cell ID is not repeated if (it->cell_id == it2->cell_id) { ERROR("Repeated Cell identifier"); - return -1; + return SRSRAN_ERROR; + } + } + } + + return SRSRAN_SUCCESS; +} + +static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cfg_t* rrc_cfg_eutra, Setting& root) +{ + for (uint32_t n = 0; n < (uint32_t)root.getLength(); ++n) { + auto& cellroot = root[n]; + + rrc_cell_cfg_nr_t cell_cfg = {}; + generate_default_nr_cell(cell_cfg); + + parse_opt_field(cell_cfg.phy_cell.rf_port, cellroot, "rf_port"); + HANDLEPARSERCODE(parse_required_field(cell_cfg.phy_cell.carrier.pci, cellroot, "pci")); + HANDLEPARSERCODE(parse_required_field(cell_cfg.phy_cell.cell_id, cellroot, "cell_id")); + HANDLEPARSERCODE(parse_opt_field(cell_cfg.coreset0_idx, cellroot, "coreset0_idx")); + HANDLEPARSERCODE(parse_required_field(cell_cfg.prach_root_seq_idx, cellroot, "root_seq_idx")); + HANDLEPARSERCODE(parse_required_field(cell_cfg.tac, cellroot, "tac")); + + cell_cfg.phy_cell.carrier.pci = cell_cfg.phy_cell.carrier.pci % SRSRAN_NOF_NID_NR; + HANDLEPARSERCODE(parse_required_field(cell_cfg.dl_arfcn, cellroot, "dl_arfcn")); + parse_opt_field(cell_cfg.ul_arfcn, cellroot, "ul_arfcn"); + HANDLEPARSERCODE(parse_required_field(cell_cfg.band, cellroot, "band")); + // frequencies get derived from ARFCN + + // TODO: Add further cell-specific parameters + + rrc_cfg_nr->cell_list.push_back(cell_cfg); + } + + srsran::srsran_band_helper band_helper; + // Configuration check + // counter for every RF port used by the eNB to avoid misconfiguration/mapping of cells + uint32_t next_rf_port = rrc_cfg_eutra->cell_list.size(); + for (auto it = rrc_cfg_nr->cell_list.begin(); it != rrc_cfg_nr->cell_list.end(); ++it) { + // Make sure RF ports are assigned in order + if (it->phy_cell.rf_port != next_rf_port) { + ERROR("RF ports need to be in order starting with 0 (%d != %d)", it->phy_cell.rf_port, next_rf_port); + return SRSRAN_ERROR; + } + next_rf_port++; + + // check against other NR cells + for (auto it2 = it + 1; it2 != rrc_cfg_nr->cell_list.end(); it2++) { + // Check RF port is not repeated + if (it->phy_cell.rf_port == it2->phy_cell.rf_port) { + ERROR("Repeated RF port for multiple cells"); + return SRSRAN_ERROR; + } + + // Check cell PCI not repeated + if (it->phy_cell.carrier.pci == it2->phy_cell.carrier.pci) { + ERROR("Repeated cell PCI"); + return SRSRAN_ERROR; + } + + // Check cell PCI and cell ID is not repeated + if (it->phy_cell.cell_id == it2->phy_cell.cell_id) { + ERROR("Repeated Cell identifier"); + return SRSRAN_ERROR; + } + } + + // also check RF port against EUTRA cells + for (auto it_eutra = rrc_cfg_eutra->cell_list.begin(); it_eutra != rrc_cfg_eutra->cell_list.end(); ++it_eutra) { + // Check RF port is not repeated + if (it->phy_cell.rf_port == it_eutra->rf_port) { + ERROR("Repeated RF port for multiple cells"); + return SRSRAN_ERROR; + } + } + + // Check if dl_arfcn is valid for the given band + bool dl_arfcn_valid = false; + std::vector bands = band_helper.get_bands_nr(it->dl_arfcn); + for (uint32_t band_idx = 0; band_idx < bands.size(); band_idx++) { + if (bands.at(band_idx) == it->band) { + dl_arfcn_valid = true; + } + } + if (!dl_arfcn_valid) { + if (not bands.empty()) { + std::stringstream ss; + for (uint32_t& band : bands) { + ss << band << " "; + } + ERROR("DL ARFCN (%d) does not belong to band (%d). Recommended bands: %s", + it->dl_arfcn, + it->band, + ss.str().c_str()); + return SRSRAN_ERROR; + } + ERROR("DL ARFCN (%d) is not valid for the specified band (%d)", it->dl_arfcn, it->band); + return SRSRAN_ERROR; + } + + if (it->ul_arfcn != 0) { + // Check if ul_arfcn is valid for the given band + bool ul_arfcn_valid = false; + std::vector ul_bands = band_helper.get_bands_nr(it->ul_arfcn); + for (uint32_t band_idx = 0; band_idx < ul_bands.size(); band_idx++) { + if (ul_bands.at(band_idx) == it->band) { + ul_arfcn_valid = true; + } + } + if (!ul_arfcn_valid) { + ERROR("UL ARFCN (%d) is not valid for the specified band (%d)", it->ul_arfcn, it->band); + return SRSRAN_ERROR; } } } @@ -814,6 +1667,12 @@ int cell_list_section::parse(libconfig::Setting& root) return 0; } +int nr_cell_list_section::parse(libconfig::Setting& root) +{ + HANDLEPARSERCODE(parse_nr_cell_list(args, nr_rrc_cfg, eutra_rrc_cfg, root)); + return 0; +} + } // namespace rr_sections namespace enb_conf_sections { @@ -821,7 +1680,7 @@ namespace enb_conf_sections { int parse_cell_cfg(all_args_t* args_, srsran_cell_t* cell) { cell->frame_type = SRSRAN_FDD; - cell->cp = SRSRAN_CP_NORM; + cell->cp = args_->phy.extended_cp ? SRSRAN_CP_EXT : SRSRAN_CP_NORM; cell->nof_ports = args_->enb.nof_ports; cell->nof_prb = args_->enb.n_prb; // PCI not configured yet @@ -849,7 +1708,31 @@ int parse_cell_cfg(all_args_t* args_, srsran_cell_t* cell) return SRSRAN_SUCCESS; } -int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_) +// Parse the relevant CFR configuration params +int parse_cfr_args(all_args_t* args, srsran_cfr_cfg_t* cfr_config) +{ + cfr_config->cfr_enable = args->phy.cfr_args.enable; + cfr_config->cfr_mode = args->phy.cfr_args.mode; + cfr_config->alpha = args->phy.cfr_args.strength; + cfr_config->manual_thr = args->phy.cfr_args.manual_thres; + cfr_config->max_papr_db = args->phy.cfr_args.auto_target_papr; + cfr_config->ema_alpha = args->phy.cfr_args.ema_alpha; + + if (!srsran_cfr_params_valid(cfr_config)) { + fprintf(stderr, + "Invalid CFR parameters: cfr_mode=%d, alpha=%.2f, manual_thr=%.2f, \n " + "max_papr_db=%.2f, ema_alpha=%.2f\n", + cfr_config->cfr_mode, + cfr_config->alpha, + cfr_config->manual_thr, + cfr_config->max_papr_db, + cfr_config->ema_alpha); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + +int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t* phy_cfg_) { // Parse config files srsran_cell_t cell_common_cfg = {}; @@ -881,7 +1764,7 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_) } try { - if (rr_sections::parse_rr(args_, rrc_cfg_) != SRSRAN_SUCCESS) { + if (rr_sections::parse_rr(args_, rrc_cfg_, rrc_nr_cfg_) != SRSRAN_SUCCESS) { fprintf(stderr, "Error parsing Radio Resources configuration\n"); return SRSRAN_ERROR; } @@ -894,70 +1777,139 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_) } try { - if (drb_sections::parse_drb(args_, rrc_cfg_) != SRSRAN_SUCCESS) { - fprintf(stderr, "Error parsing DRB configuration\n"); + if (rb_sections::parse_rb(args_, rrc_cfg_, rrc_nr_cfg_) != SRSRAN_SUCCESS) { + fprintf(stderr, "Error parsing RB configuration\n"); return SRSRAN_ERROR; } } catch (const SettingTypeException& stex) { - fprintf(stderr, "Error parsing DRB configuration: %s\n", stex.getPath()); + fprintf(stderr, "Error parsing RB configuration: %s\n", stex.getPath()); return SRSRAN_ERROR; } catch (const ConfigException& cex) { - fprintf(stderr, "Error parsing DRB configuration\n"); + fprintf(stderr, "Error parsing RB configuration\n"); return SRSRAN_ERROR; } + // update number of NR cells + rrc_cfg_->num_nr_cells = rrc_nr_cfg_->cell_list.size(); + args_->rf.nof_carriers = rrc_cfg_->cell_list.size() + rrc_nr_cfg_->cell_list.size(); + ASSERT_VALID_CFG(args_->rf.nof_carriers > 0, "There must be at least one NR or LTE cell"); + if (rrc_nr_cfg_->cell_list.size() > 0) { + // NR cells available. + if (rrc_cfg_->cell_list.size() == 0) { + // SA mode. + rrc_nr_cfg_->is_standalone = true; + } else { + // NSA mode. + rrc_nr_cfg_->is_standalone = false; + } + } + // Set fields derived from others, and check for correctness of the parsed configuration - return enb_conf_sections::set_derived_args(args_, rrc_cfg_, phy_cfg_, cell_common_cfg); + if (enb_conf_sections::set_derived_args(args_, rrc_cfg_, phy_cfg_, cell_common_cfg) != SRSRAN_SUCCESS) { + fprintf(stderr, "Error deriving EUTRA cell parameters\n"); + return SRSRAN_ERROR; + } + + // do the same for NR + if (enb_conf_sections::set_derived_args_nr(args_, rrc_nr_cfg_, phy_cfg_) != SRSRAN_SUCCESS) { + fprintf(stderr, "Error deriving NR cell parameters\n"); + return SRSRAN_ERROR; + } + + // update number of NR cells + if (rrc_nr_cfg_->cell_list.size() > 0) { + // NR cells available. + if (rrc_nr_cfg_->is_standalone) { + // SA mode. Update NGAP args + args_->nr_stack.ngap.cell_id = rrc_nr_cfg_->cell_list[0].phy_cell.cell_id; + args_->nr_stack.ngap.tac = rrc_nr_cfg_->cell_list[0].tac; + // take equivalent S1AP params to update NGAP params + args_->nr_stack.ngap.gnb_name = args_->stack.s1ap.enb_name; + args_->nr_stack.ngap.gnb_id = args_->enb.enb_id; + args_->nr_stack.ngap.mcc = args_->stack.s1ap.mcc; + args_->nr_stack.ngap.mnc = args_->stack.s1ap.mnc; + args_->nr_stack.ngap.gtp_bind_addr = args_->stack.s1ap.gtp_bind_addr; + args_->nr_stack.ngap.gtp_advertise_addr = args_->stack.s1ap.gtp_advertise_addr; + args_->nr_stack.ngap.amf_addr = args_->stack.s1ap.mme_addr; + args_->nr_stack.ngap.ngc_bind_addr = args_->stack.s1ap.gtp_bind_addr; + + // Parse NIA/NEA preference list (use same as LTE for now) + for (uint32_t i = 0; i < rrc_cfg_->eea_preference_list.size(); i++) { + rrc_nr_cfg_->nea_preference_list[i] = (srsran::CIPHERING_ALGORITHM_ID_NR_ENUM)rrc_cfg_->eea_preference_list[i]; + rrc_nr_cfg_->nia_preference_list[i] = (srsran::INTEGRITY_ALGORITHM_ID_NR_ENUM)rrc_cfg_->eia_preference_list[i]; + } + + } else { + // NSA mode. + // update EUTRA RRC params for ENDC + rrc_cfg_->endc_cfg.abs_frequency_ssb = rrc_nr_cfg_->cell_list.at(0).ssb_absolute_freq_point; + rrc_cfg_->endc_cfg.nr_band = rrc_nr_cfg_->cell_list.at(0).band; + rrc_cfg_->endc_cfg.ssb_period_offset.set_sf10_r15(); + rrc_cfg_->endc_cfg.ssb_duration = asn1::rrc::mtc_ssb_nr_r15_s::ssb_dur_r15_opts::sf1; + rrc_cfg_->endc_cfg.ssb_ssc = asn1::rrc::rs_cfg_ssb_nr_r15_s::subcarrier_spacing_ssb_r15_opts::khz15; + rrc_cfg_->endc_cfg.act_from_b1_event = true; // ENDC will only be activated from B1 measurment + } + } + + // Parse CFR args + if (parse_cfr_args(args_, &phy_cfg_->cfr_config) < SRSRAN_SUCCESS) { + fprintf(stderr, "Error parsing CFR configuration\n"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; } int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_, const srsran_cell_t& cell_cfg_) { // Sanity checks - ASSERT_VALID_CFG(not rrc_cfg_->cell_list.empty(), "No cell specified in rr.conf."); ASSERT_VALID_CFG(args_->stack.mac.nof_prealloc_ues <= SRSENB_MAX_UES, "mac.nof_prealloc_ues=%d must be within [0, %d]", args_->stack.mac.nof_prealloc_ues, SRSENB_MAX_UES); - // Check for a forced DL EARFCN or frequency (only valid for a single cell config (Xico's favorite feature)) - if (rrc_cfg_->cell_list.size() == 1) { - auto& cfg = rrc_cfg_->cell_list.at(0); - if (args_->enb.dl_earfcn > 0 and args_->enb.dl_earfcn != cfg.dl_earfcn) { - cfg.dl_earfcn = args_->enb.dl_earfcn; - ERROR("Force DL EARFCN for cell PCI=%d to %d", cfg.pci, cfg.dl_earfcn); - } - if (args_->rf.dl_freq > 0) { - cfg.dl_freq_hz = args_->rf.dl_freq; - ERROR("Force DL freq for cell PCI=%d to %f MHz", cfg.pci, cfg.dl_freq_hz / 1e6f); - } - if (args_->rf.ul_freq > 0) { - cfg.ul_freq_hz = args_->rf.ul_freq; - ERROR("Force UL freq for cell PCI=%d to %f MHz", cfg.pci, cfg.ul_freq_hz / 1e6f); - } - } else { - // If more than one cell is defined, single EARFCN or DL freq will be ignored - if (args_->enb.dl_earfcn > 0 || args_->rf.dl_freq > 0) { - INFO("Multiple cells defined in rr.conf. Ignoring single EARFCN and/or frequency config."); + // Check for a forced DL EARFCN or frequency (only valid for a single cell config + if (rrc_cfg_->cell_list.size() > 0) { + if (rrc_cfg_->cell_list.size() == 1) { + auto& cfg = rrc_cfg_->cell_list.at(0); + if (args_->enb.dl_earfcn > 0 and args_->enb.dl_earfcn != cfg.dl_earfcn) { + cfg.dl_earfcn = args_->enb.dl_earfcn; + ERROR("Force DL EARFCN for cell PCI=%d to %d", cfg.pci, cfg.dl_earfcn); + } + if (args_->rf.dl_freq > 0) { + cfg.dl_freq_hz = args_->rf.dl_freq; + ERROR("Force DL freq for cell PCI=%d to %f MHz", cfg.pci, cfg.dl_freq_hz / 1e6f); + } + if (args_->rf.ul_freq > 0) { + cfg.ul_freq_hz = args_->rf.ul_freq; + ERROR("Force UL freq for cell PCI=%d to %f MHz", cfg.pci, cfg.ul_freq_hz / 1e6f); + } + } else { + // If more than one cell is defined, single EARFCN or DL freq will be ignored + if (args_->enb.dl_earfcn > 0 || args_->rf.dl_freq > 0) { + INFO("Multiple cells defined in rr.conf. Ignoring single EARFCN and/or frequency config."); + } } + + // set config for RRC's base cell + rrc_cfg_->cell = cell_cfg_; + + // Set S1AP related params from cell list + args_->stack.s1ap.enb_id = args_->enb.enb_id; + args_->stack.s1ap.cell_id = rrc_cfg_->cell_list.at(0).cell_id; + args_->stack.s1ap.tac = rrc_cfg_->cell_list.at(0).tac; } - // set config for RRC's base cell - rrc_cfg_->cell = cell_cfg_; - - // Set S1AP related params from cell list - args_->stack.s1ap.enb_id = args_->enb.enb_id; - args_->stack.s1ap.cell_id = rrc_cfg_->cell_list.at(0).cell_id; - args_->stack.s1ap.tac = rrc_cfg_->cell_list.at(0).tac; - // Create dedicated cell configuration from RRC configuration for (auto it = rrc_cfg_->cell_list.begin(); it != rrc_cfg_->cell_list.end(); ++it) { - auto& cfg = *it; + cell_cfg_t& cfg = *it; phy_cell_cfg_t phy_cell_cfg = {}; phy_cell_cfg.cell = cell_cfg_; phy_cell_cfg.cell.id = cfg.pci; phy_cell_cfg.cell_id = cfg.cell_id; phy_cell_cfg.root_seq_idx = cfg.root_seq_idx; phy_cell_cfg.rf_port = cfg.rf_port; + phy_cell_cfg.gain_db = cfg.tx_gain; phy_cell_cfg.num_ra_preambles = rrc_cfg_->sibs[1].sib2().rr_cfg_common.rach_cfg_common.preamb_info.nof_ra_preambs.to_number(); @@ -965,6 +1917,10 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ phy_cell_cfg.dl_freq_hz = cfg.dl_freq_hz; } else { phy_cell_cfg.dl_freq_hz = 1e6 * srsran_band_fd(cfg.dl_earfcn); + if (phy_cell_cfg.dl_freq_hz == 0.0) { + ERROR("Couldn't derive DL frequency for EARFCN=%d", cfg.dl_earfcn); + return SRSRAN_ERROR; + } } if (cfg.ul_freq_hz > 0) { @@ -974,6 +1930,10 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ cfg.ul_earfcn = srsran_band_ul_earfcn(cfg.dl_earfcn); } phy_cell_cfg.ul_freq_hz = 1e6 * srsran_band_fu(cfg.ul_earfcn); + if (phy_cell_cfg.ul_freq_hz == 0.0) { + ERROR("Couldn't derive UL frequency for EARFCN=%d", cfg.ul_earfcn); + return SRSRAN_ERROR; + } } for (auto scell_it = cfg.scell_list.begin(); scell_it != cfg.scell_list.end();) { @@ -991,6 +1951,13 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ } } + for (meas_cell_cfg_t& meas_cell : cfg.meas_cfg.meas_cells) { + if (meas_cell.tac < 0) { + // if meas cell TAC was not set, use current cell TAC. + meas_cell.tac = cfg.tac; + } + } + // Check if the enb cells PCIs won't lead to PSS detection issues auto is_pss_collision = [&cfg](const cell_cfg_t& c) { return c.pci % 3 == cfg.pci % 3 and c.dl_earfcn == cfg.dl_earfcn; @@ -1006,37 +1973,6 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ phy_cfg_->phy_cell_cfg.push_back(phy_cell_cfg); } - // Create NR dedicated cell configuration from RRC configuration - for (auto it = rrc_cfg_->cell_list_nr.begin(); it != rrc_cfg_->cell_list_nr.end(); ++it) { - auto& cfg = *it; - phy_cell_cfg_nr_t phy_cell_cfg = {}; - phy_cell_cfg.carrier.max_mimo_layers = cell_cfg_.nof_ports; - phy_cell_cfg.carrier.nof_prb = cell_cfg_.nof_prb; - phy_cell_cfg.carrier.pci = cfg.pci; - phy_cell_cfg.cell_id = cfg.cell_id; - phy_cell_cfg.root_seq_idx = cfg.root_seq_idx; - phy_cell_cfg.rf_port = cfg.rf_port; - phy_cell_cfg.num_ra_preambles = - rrc_cfg_->sibs[1].sib2().rr_cfg_common.rach_cfg_common.preamb_info.nof_ra_preambs.to_number(); - - if (cfg.dl_freq_hz > 0) { - phy_cell_cfg.dl_freq_hz = cfg.dl_freq_hz; - } else { - phy_cell_cfg.dl_freq_hz = 1e6 * srsran_band_fd(cfg.dl_earfcn); - } - - if (cfg.ul_freq_hz > 0) { - phy_cell_cfg.ul_freq_hz = cfg.ul_freq_hz; - } else { - if (cfg.ul_earfcn == 0) { - cfg.ul_earfcn = srsran_band_ul_earfcn(cfg.dl_earfcn); - } - phy_cell_cfg.ul_freq_hz = 1e6 * srsran_band_fu(cfg.ul_earfcn); - } - - phy_cfg_->phy_cell_cfg_nr.push_back(phy_cell_cfg); - } - if (args_->enb.transmission_mode == 1) { phy_cfg_->pdsch_cnfg.p_b = 0; // Default TM1 rrc_cfg_->sibs[1].sib2().rr_cfg_common.pdsch_cfg_common.p_b = 0; @@ -1114,7 +2050,8 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ } // Check PUCCH and PRACH configuration - uint32_t nrb_pucch = std::max(rrc_cfg_->sr_cfg.nof_prb, rrc_cfg_->cqi_cfg.nof_prb); + uint32_t nrb_pucch = + std::max(rrc_cfg_->sr_cfg.nof_prb, (uint32_t)rrc_cfg_->sibs[1].sib2().rr_cfg_common.pucch_cfg_common.nrb_cqi); uint32_t prach_freq_offset = rrc_cfg_->sibs[1].sib2().rr_cfg_common.prach_cfg.prach_cfg_info.prach_freq_offset; if (args_->enb.n_prb > 6) { uint32_t lower_bound = nrb_pucch; @@ -1146,7 +2083,6 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ } // Patch certain args that are not exposed yet - args_->rf.nof_carriers = rrc_cfg_->cell_list.size() + rrc_cfg_->cell_list_nr.size(); args_->rf.nof_antennas = args_->enb.nof_ports; // MAC needs to know the cell bandwidth to dimension softbuffers @@ -1156,8 +2092,9 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ rrc_cfg_->enb_id = args_->stack.s1ap.enb_id; // Set max number of KOs - rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_kos; - rrc_cfg_->max_mac_ul_kos = args_->general.max_mac_ul_kos; + rrc_cfg_->max_mac_dl_kos = args_->general.max_mac_dl_kos; + rrc_cfg_->max_mac_ul_kos = args_->general.max_mac_ul_kos; + rrc_cfg_->rlf_release_timer_ms = args_->general.rlf_release_timer_ms; // Set sync queue capacity to 1 for ZMQ if (args_->rf.device_name == "zmq") { @@ -1171,6 +2108,86 @@ int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_ return SRSRAN_SUCCESS; } +/** + * @brief Set the derived args for the NR RRC and PHY config + * + * Mainly configures the RRC parameter based on the arguments and config files + * read. Since for NSA we are still using a commong PHY between EUTRA and NR + * the PHY configuration is also updated accordingly. + * + * @param args_ + * @param nr_rrc_cfg_ + * @param phy_cfg_ + * @return int + */ +int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t* phy_cfg_) +{ + // Use helper class to derive NR carrier parameters + srsran::srsran_band_helper band_helper; + + // we only support one NR cell + if (rrc_nr_cfg_->cell_list.size() > 1) { + ERROR("Only a single NR cell supported."); + return SRSRAN_ERROR; + } + + rrc_nr_cfg_->inactivity_timeout_ms = args_->general.rrc_inactivity_timer; + + // Create NR dedicated cell configuration from RRC configuration + for (auto& cfg : rrc_nr_cfg_->cell_list) { + cfg.phy_cell.carrier.max_mimo_layers = args_->enb.nof_ports; + + // NR cells have the same bandwidth as EUTRA cells, adjust PRB sizes + switch (args_->enb.n_prb) { + case 25: + cfg.phy_cell.carrier.nof_prb = 25; + break; + case 50: + cfg.phy_cell.carrier.nof_prb = 52; + break; + case 100: + cfg.phy_cell.carrier.nof_prb = 106; + break; + default: + ERROR("The only accepted number of PRB is: 25, 50, 100"); + return SRSRAN_ERROR; + } + + // phy_cell_cfg.root_seq_idx = cfg.root_seq_idx; + + // PDSCH + cfg.pdsch_rs_power = phy_cfg_->pdsch_cnfg.ref_sig_pwr; + } + rrc_nr_cfg_->enb_id = args_->enb.enb_id; + rrc_nr_cfg_->mcc = args_->stack.s1ap.mcc; + rrc_nr_cfg_->mnc = args_->stack.s1ap.mnc; + + // Derive cross-dependent cell params + if (set_derived_nr_rrc_params(*rrc_nr_cfg_) != SRSRAN_SUCCESS) { + ERROR("Failed to derive NR cell params."); + return SRSRAN_ERROR; + } + + // Update PHY with RRC cell configs + for (auto& cfg : rrc_nr_cfg_->cell_list) { + phy_cfg_->phy_cell_cfg_nr.push_back(cfg.phy_cell); + } + + // MAC-NR PCAP options + args_->nr_stack.mac.pcap.enable = args_->stack.mac_pcap.enable; + args_->nr_stack.log = args_->stack.log; + + // Sanity check for unsupported/untested configuration + for (auto& cfg : rrc_nr_cfg_->cell_list) { + if (cfg.phy_cell.carrier.nof_prb != 52) { + ERROR("Only 10 MHz bandwidth supported."); + return SRSRAN_ERROR; + } + } + + return SRSRAN_SUCCESS; +} + } // namespace enb_conf_sections namespace sib_sections { @@ -1186,6 +2203,13 @@ int parse_sib1(std::string filename, sib_type1_s* data) sib1.add_field(make_asn1_enum_number_parser("si_window_length", &data->si_win_len)); sib1.add_field(new parser::field("system_info_value_tag", &data->sys_info_value_tag)); + // additional_plmns subsection uses a custom field class + parser::section additional_plmns("additional_plmns"); + sib1.add_subsection(&additional_plmns); + bool dummy_bool = true; + additional_plmns.set_optional(&dummy_bool); + additional_plmns.add_field(new field_additional_plmns(&data->cell_access_related_info)); + // sched_info subsection uses a custom field class parser::section sched_info("sched_info"); sib1.add_subsection(&sched_info); @@ -1232,7 +2256,7 @@ int parse_sib2(std::string filename, sib_type2_s* data) acbarring_data.add_field( make_asn1_enum_number_str_parser("factor", &data->ac_barr_info.ac_barr_for_mo_data.ac_barr_factor)); - acbarring_data.add_field(make_asn1_enum_number_parser("fime", &data->ac_barr_info.ac_barr_for_mo_data.ac_barr_time)); + acbarring_data.add_field(make_asn1_enum_number_parser("time", &data->ac_barr_info.ac_barr_for_mo_data.ac_barr_time)); acbarring_data.add_field(make_asn1_bitstring_number_parser( "for_special_ac", &data->ac_barr_info.ac_barr_for_mo_data.ac_barr_for_special_ac)); @@ -1469,12 +2493,62 @@ int parse_sib4(std::string filename, sib_type4_s* data) return parser::parse_section(std::move(filename), &sib4); } +int parse_sib5(std::string filename, sib_type5_s* data) +{ + parser::section sib5("sib5"); + + // interFreqCarrierFreqList + parser::section inter_freq_carrier_freq_list("inter_freq_carrier_freq_list"); + sib5.add_subsection(&inter_freq_carrier_freq_list); + bool dummy_bool = false; + inter_freq_carrier_freq_list.set_optional(&dummy_bool); + inter_freq_carrier_freq_list.add_field(new field_inter_freq_carrier_freq_list(data)); + + return parser::parse_section(std::move(filename), &sib5); +} + +int parse_sib6(std::string filename, sib_type6_s* data) +{ + parser::section sib6("sib6"); + + // t-ReselectionUTRA + sib6.add_field(new parser::field("t_resel_utra", &data->t_resel_utra)); + + // t-ReselectionUTRA-SF + parser::section t_resel_utra_sf("t_resel_utra_sf"); + sib6.add_subsection(&t_resel_utra_sf); + t_resel_utra_sf.set_optional(&data->t_resel_utra_sf_present); + t_resel_utra_sf.add_field(make_asn1_enum_number_str_parser("sf_medium", &data->t_resel_utra_sf.sf_medium)); + t_resel_utra_sf.add_field(make_asn1_enum_number_str_parser("sf_high", &data->t_resel_utra_sf.sf_high)); + + // carrierFreqListUTRA-FDD + parser::section carrier_freq_list_utra_fdd("carrier_freq_list_utra_fdd"); + sib6.add_subsection(&carrier_freq_list_utra_fdd); + bool dummy_bool = false; + carrier_freq_list_utra_fdd.set_optional(&dummy_bool); + carrier_freq_list_utra_fdd.add_field(new field_carrier_freq_list_utra_fdd(data)); + + // carrierFreqListUTRA-TDD + parser::section carrier_freq_list_utra_tdd("carrier_freq_list_utra_tdd"); + sib6.add_subsection(&carrier_freq_list_utra_tdd); + carrier_freq_list_utra_tdd.set_optional(&dummy_bool); + carrier_freq_list_utra_tdd.add_field(new field_carrier_freq_list_utra_tdd(data)); + + return parser::parse_section(std::move(filename), &sib6); +} + int parse_sib7(std::string filename, sib_type7_s* data) { parser::section sib7("sib7"); sib7.add_field(new parser::field("t_resel_geran", &data->t_resel_geran)); - // TODO: t_resel_geran_sf + + parser::section t_resel_geran_sf("t_resel_geran_sf"); + sib7.add_subsection(&t_resel_geran_sf); + t_resel_geran_sf.set_optional(&data->t_resel_geran_sf_present); + + t_resel_geran_sf.add_field(make_asn1_enum_number_str_parser("sf_medium", &data->t_resel_geran_sf.sf_medium)); + t_resel_geran_sf.add_field(make_asn1_enum_number_str_parser("sf_high", &data->t_resel_geran_sf.sf_high)); data->carrier_freqs_info_list_present = true; parser::section geran_neigh("carrier_freqs_info_list"); @@ -1513,7 +2587,7 @@ int parse_sib9(std::string filename, sib_type9_s* data) } return 0; } else { - return -1; + return SRSRAN_ERROR; } } @@ -1546,6 +2620,8 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co sib_type2_s* sib2 = &rrc_cfg_->sibs[1].set_sib2(); sib_type3_s* sib3 = &rrc_cfg_->sibs[2].set_sib3(); sib_type4_s* sib4 = &rrc_cfg_->sibs[3].set_sib4(); + sib_type5_s* sib5 = &rrc_cfg_->sibs[4].set_sib5(); + sib_type6_s* sib6 = &rrc_cfg_->sibs[5].set_sib6(); sib_type7_s* sib7 = &rrc_cfg_->sibs[6].set_sib7(); sib_type9_s* sib9 = &rrc_cfg_->sibs[8].set_sib9(); sib_type13_r9_s* sib13 = &rrc_cfg_->sibs[12].set_sib13_v920(); @@ -1559,19 +2635,22 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co std::string mnc_str; if (not srsran::mnc_to_string(args_->stack.s1ap.mnc, &mnc_str)) { ERROR("The provided mnc=%d is not valid", args_->stack.s1ap.mnc); - return -1; + return SRSRAN_ERROR; } std::string mcc_str; if (not srsran::mcc_to_string(args_->stack.s1ap.mcc, &mcc_str)) { ERROR("The provided mnc=%d is not valid", args_->stack.s1ap.mcc); - return -1; + return SRSRAN_ERROR; } sib_type1_s::cell_access_related_info_s_* cell_access = &sib1->cell_access_related_info; - cell_access->plmn_id_list.resize(1); + // In case additional PLMNs were given, resizing will remove them + if (cell_access->plmn_id_list.size() == 0) { + cell_access->plmn_id_list.resize(1); + } srsran::plmn_id_t plmn; if (plmn.from_string(mcc_str + mnc_str) == SRSRAN_ERROR) { ERROR("Could not convert %s to a plmn_id", (mcc_str + mnc_str).c_str()); - return -1; + return SRSRAN_ERROR; } srsran::to_asn1(&cell_access->plmn_id_list[0].plmn_id, plmn); cell_access->plmn_id_list[0].cell_reserved_for_oper = plmn_id_info_s::cell_reserved_for_oper_e_::not_reserved; @@ -1597,7 +2676,7 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co // verify SIB13 is available if (not sib_is_present(sib1->sched_info_list, sib_type_e::sib_type13_v920)) { fprintf(stderr, "SIB13 not present in sched_info.\n"); - return -1; + return SRSRAN_ERROR; } } @@ -1615,6 +2694,20 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co } } + // Generate SIB5 if defined in mapping info + if (sib_is_present(sib1->sched_info_list, sib_type_e::sib_type5)) { + if (sib_sections::parse_sib5(args_->enb_files.sib_config, sib5) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + + // Generate SIB6 if defined in mapping info + if (sib_is_present(sib1->sched_info_list, sib_type_e::sib_type6)) { + if (sib_sections::parse_sib6(args_->enb_files.sib_config, sib6) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + // Generate SIB7 if defined in mapping info if (sib_is_present(sib1->sched_info_list, sib_type_e::sib_type7)) { if (sib_sections::parse_sib7(args_->enb_files.sib_config, sib7) != SRSRAN_SUCCESS) { @@ -1647,15 +2740,76 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co } // namespace sib_sections -namespace drb_sections { +namespace rb_sections { -int parse_drb(all_args_t* args_, rrc_cfg_t* rrc_cfg_) +int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_) { + parser::section srb1("srb1_config"); + bool srb1_present = false; + srb1.set_optional(&srb1_present); + + parser::section srb1_rlc_cfg("rlc_config"); + srb1.add_subsection(&srb1_rlc_cfg); + srb1_rlc_cfg.add_field(new field_srb(rrc_cfg_->srb1_cfg)); + + parser::section srb2("srb2_config"); + bool srb2_present = false; + srb2.set_optional(&srb2_present); + + parser::section srb2_rlc_cfg("rlc_config"); + srb2.add_subsection(&srb2_rlc_cfg); + srb2_rlc_cfg.add_field(new field_srb(rrc_cfg_->srb2_cfg)); + parser::section qci("qci_config"); qci.add_field(new field_qci(rrc_cfg_->qci_cfg)); - return parser::parse_section(args_->enb_files.drb_config, &qci); + + parser::section srb1_5g("srb1_5g_config"); + bool srb1_5g_present = false; + srb1_5g.set_optional(&srb1_5g_present); + + parser::section srb1_5g_rlc_cfg("rlc_config"); + srb1_5g.add_subsection(&srb1_5g_rlc_cfg); + srb1_5g_rlc_cfg.add_field(new field_5g_srb(rrc_nr_cfg_->srb1_cfg)); + + parser::section srb2_5g("srb2_5g_config"); + bool srb2_5g_present = false; + srb2_5g.set_optional(&srb2_5g_present); + + parser::section srb2_5g_rlc_cfg("rlc_config"); + srb2_5g.add_subsection(&srb2_5g_rlc_cfg); + srb2_5g_rlc_cfg.add_field(new field_5g_srb(rrc_nr_cfg_->srb2_cfg)); + + parser::section five_qi("five_qi_config"); + five_qi.add_field(new field_five_qi(rrc_nr_cfg_->five_qi_cfg)); + + // Run parser with two sections + parser p(args_->enb_files.rb_config); + p.add_section(&srb1); + p.add_section(&srb2); + p.add_section(&qci); + p.add_section(&srb1_5g); + p.add_section(&srb2_5g); + p.add_section(&five_qi); + + int ret = p.parse(); + if (not srb1_present) { + rrc_cfg_->srb1_cfg.rlc_cfg.set_default_value(); + } + if (not srb2_present) { + rrc_cfg_->srb2_cfg.rlc_cfg.set_default_value(); + } + + if (!srb1_5g_present || !srb2_5g_present) { + fprintf(stderr, "Optional 5G SRB configuration is not supported yet.\n"); + fprintf(stderr, "Please specify 5G SRB1 and SRB2 configuration.\n"); + return SRSRAN_ERROR; + } + rrc_nr_cfg_->srb1_cfg.present = srb1_5g_present; + rrc_nr_cfg_->srb2_cfg.present = srb1_5g_present; + + return ret; } -} // namespace drb_sections +} // namespace rb_sections } // namespace srsenb diff --git a/srsenb/src/enb_cfg_parser.h b/srsenb/src/enb_cfg_parser.h index 963690d7b..adbd4eb48 100644 --- a/srsenb/src/enb_cfg_parser.h +++ b/srsenb/src/enb_cfg_parser.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,8 +19,8 @@ * */ -#ifndef ENB_CFG_PARSER_SIB1_H -#define ENB_CFG_PARSER_SIB1_H +#ifndef ENB_CFG_PARSER_H +#define ENB_CFG_PARSER_H #include "srsenb/hdr/parser.h" #include @@ -31,6 +31,7 @@ #include #include "srsenb/hdr/stack/rrc/rrc.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config.h" #include "srsran/asn1/asn1_utils.h" namespace srsenb { @@ -39,6 +40,7 @@ using namespace libconfig; struct all_args_t; struct phy_cfg_t; +struct rrc_nr_cfg_t; bool sib_is_present(const asn1::rrc::sched_info_list_l& l, asn1::rrc::sib_type_e sib_num); @@ -46,8 +48,11 @@ bool sib_is_present(const asn1::rrc::sched_info_list_l& l, asn1::rrc::sib_type_e namespace enb_conf_sections { int parse_cell_cfg(all_args_t* args_, srsran_cell_t* cell); -int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_); +int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_cfg_nr_, phy_cfg_t* phy_cfg_); int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_, const srsran_cell_t& cell_cfg_); +int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t* phy_cfg_); +bool is_valid_arfcn(uint32_t band, uint32_t dl_arfcn); +std::string valid_arfcns_to_string(uint32_t band); } // namespace enb_conf_sections @@ -58,6 +63,8 @@ int parse_sib1(std::string filename, asn1::rrc::sib_type1_s* data); int parse_sib2(std::string filename, asn1::rrc::sib_type2_s* data); int parse_sib3(std::string filename, asn1::rrc::sib_type3_s* data); int parse_sib4(std::string filename, asn1::rrc::sib_type4_s* data); +int parse_sib5(std::string filename, asn1::rrc::sib_type5_s* data); +int parse_sib6(std::string filename, asn1::rrc::sib_type6_s* data); int parse_sib7(std::string filename, asn1::rrc::sib_type7_s* data); int parse_sib9(std::string filename, asn1::rrc::sib_type9_s* data); int parse_sib13(std::string filename, asn1::rrc::sib_type13_r9_s* data); @@ -65,11 +72,12 @@ int parse_sibs(all_args_t* args_, rrc_cfg_t* rrc_cfg_, srsenb::phy_cfg_t* phy_co } // namespace sib_sections -// drb.conf parsing -namespace drb_sections { +// rb.conf parsing +namespace rb_sections { -int parse_drb(all_args_t* args, rrc_cfg_t* rrc_cfg); -} // namespace drb_sections +int parse_rb(all_args_t* args, rrc_cfg_t* rrc_cfg, rrc_nr_cfg_t* rrc_nr_cfg); + +} // namespace rb_sections // rr.conf parsing namespace rr_sections { @@ -84,15 +92,43 @@ public: int parse(Setting& root) override; - const char* get_name() override { return "meas_cell_list"; } + const char* get_name() override { return "cell_list"; } private: rrc_cfg_t* rrc_cfg; all_args_t* args; }; +class nr_cell_list_section final : public parser::field_itf +{ +public: + explicit nr_cell_list_section(all_args_t* all_args_, rrc_nr_cfg_t* nr_rrc_cfg_, rrc_cfg_t* eutra_rrc_cfg_) : + args(all_args_), nr_rrc_cfg(nr_rrc_cfg_), eutra_rrc_cfg(eutra_rrc_cfg_) + {} + + int parse(Setting& root) override; + + const char* get_name() override { return "nr_cell_list"; } + +private: + rrc_nr_cfg_t* nr_rrc_cfg; + rrc_cfg_t* eutra_rrc_cfg; + all_args_t* args; +}; + } // namespace rr_sections +class field_additional_plmns final : public parser::field_itf +{ +public: + explicit field_additional_plmns(asn1::rrc::sib_type1_s::cell_access_related_info_s_* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "additional_plmns"; } + +private: + asn1::rrc::sib_type1_s::cell_access_related_info_s_* data; +}; + class field_sched_info final : public parser::field_itf { public: @@ -126,6 +162,61 @@ private: asn1::rrc::sib_type4_s* data; }; +class field_inter_freq_carrier_freq_list final : public parser::field_itf +{ +public: + explicit field_inter_freq_carrier_freq_list(asn1::rrc::sib_type5_s* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "inter_freq_carrier_freq_list"; } + +private: + asn1::rrc::sib_type5_s* data; +}; + +class field_inter_freq_neigh_cell_list final : public parser::field_itf +{ +public: + explicit field_inter_freq_neigh_cell_list(asn1::rrc::inter_freq_carrier_freq_info_s* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "inter_freq_neigh_cell_list"; } + +private: + asn1::rrc::inter_freq_carrier_freq_info_s* data; +}; + +class field_inter_freq_black_cell_list final : public parser::field_itf +{ +public: + explicit field_inter_freq_black_cell_list(asn1::rrc::inter_freq_carrier_freq_info_s* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "inter_freq_black_cell_list"; } + +private: + asn1::rrc::inter_freq_carrier_freq_info_s* data; +}; + +class field_carrier_freq_list_utra_fdd final : public parser::field_itf +{ +public: + explicit field_carrier_freq_list_utra_fdd(asn1::rrc::sib_type6_s* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "carrier_freq_list_utra_fdd"; } + +private: + asn1::rrc::sib_type6_s* data; +}; + +class field_carrier_freq_list_utra_tdd final : public parser::field_itf +{ +public: + explicit field_carrier_freq_list_utra_tdd(asn1::rrc::sib_type6_s* data_) { data = data_; } + int parse(Setting& root) override; + const char* get_name() override { return "carrier_freq_list_utra_tdd"; } + +private: + asn1::rrc::sib_type6_s* data; +}; + class field_carrier_freqs_info_list final : public parser::field_itf { public: @@ -155,11 +246,23 @@ private: uint32_t default_offset; }; +class field_srb final : public parser::field_itf +{ +public: + explicit field_srb(srb_cfg_t& cfg_) : cfg(cfg_) {} + const char* get_name() override { return "field_srb"; } + + int parse(Setting& root) override; + +private: + srb_cfg_t& cfg; +}; + class field_qci final : public parser::field_itf { public: explicit field_qci(std::map& cfg_) : cfg(cfg_) {} - const char* get_name() override { return "field_cqi"; } + const char* get_name() override { return "field_qci"; } int parse(Setting& root) override; @@ -167,6 +270,29 @@ private: std::map& cfg; }; +class field_5g_srb final : public parser::field_itf +{ +public: + explicit field_5g_srb(srb_5g_cfg_t& cfg_) : cfg(cfg_) {} + const char* get_name() override { return "field_5g_srb"; } + + int parse(Setting& root) override; + +private: + srb_5g_cfg_t& cfg; +}; + +class field_five_qi final : public parser::field_itf +{ +public: + explicit field_five_qi(std::map& cfg_) : cfg(cfg_) {} + const char* get_name() override { return "field_five_qi"; } + + int parse(Setting& root) override; + +private: + std::map& cfg; +}; // ASN1 parsers class field_asn1 : public parser::field_itf @@ -556,4 +682,4 @@ private: }; } // namespace srsenb -#endif +#endif // ENB_CFG_PARSER_H diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index d7b841abe..336ee2b65 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,14 +23,17 @@ #include #include #include +#include #include #include "srsran/common/common_helper.h" #include "srsran/common/config_file.h" #include "srsran/common/crash_handler.h" -#include "srsran/common/signal_handler.h" +#include "srsran/common/tsan_options.h" #include "srsran/srslog/event_trace.h" #include "srsran/srslog/srslog.h" +#include "srsran/support/emergency_handlers.h" +#include "srsran/support/signal_handler.h" #include #include @@ -52,13 +55,17 @@ namespace bpo = boost::program_options; /********************************************************************** * Program arguments processing ***********************************************************************/ -string config_file; +string config_file; +static bool stdout_ts_enable = false; +static srslog::sink* log_sink = nullptr; +static std::atomic running = {true}; void parse_args(all_args_t* args, int argc, char* argv[]) { string mcc; string mnc; string enb_id; + string cfr_mode; bool use_standard_lte_rates = false; // Command line only options @@ -72,8 +79,6 @@ void parse_args(all_args_t* args, int argc, char* argv[]) // Command line or config file options bpo::options_description common("Configuration options"); common.add_options() - - ("enb.stack", bpo::value(&args->stack.type)->default_value("lte"), "Type of the upper stack [lte, nr]") ("enb.enb_id", bpo::value(&enb_id)->default_value("0x0"), "eNodeB ID") ("enb.name", bpo::value(&args->stack.s1ap.enb_name)->default_value("srsenb01"), "eNodeB Name") ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") @@ -82,6 +87,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("enb.gtp_bind_addr", bpo::value(&args->stack.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") ("enb.gtp_advertise_addr", bpo::value(&args->stack.s1ap.gtp_advertise_addr)->default_value(""), "IP address of eNB to advertise for DL GTP-U Traffic") ("enb.s1c_bind_addr", bpo::value(&args->stack.s1ap.s1c_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for S1AP connection") + ("enb.s1c_bind_port", bpo::value(&args->stack.s1ap.s1c_bind_port)->default_value(0), "Source port for S1AP connection (0 means any)") ("enb.n_prb", bpo::value(&args->enb.n_prb)->default_value(25), "Number of PRB") ("enb.nof_ports", bpo::value(&args->enb.nof_ports)->default_value(1), "Number of ports") ("enb.tm", bpo::value(&args->enb.transmission_mode)->default_value(1), "Transmission mode (1-8)") @@ -89,7 +95,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("enb_files.sib_config", bpo::value(&args->enb_files.sib_config)->default_value("sib.conf"), "SIB configuration files") ("enb_files.rr_config", bpo::value(&args->enb_files.rr_config)->default_value("rr.conf"), "RR configuration files") - ("enb_files.drb_config", bpo::value(&args->enb_files.drb_config)->default_value("drb.conf"), "DRB configuration files") + ("enb_files.rb_config", bpo::value(&args->enb_files.rb_config)->default_value("rb.conf"), "SRB/DRB configuration files") ("rf.dl_earfcn", bpo::value(&args->enb.dl_earfcn)->default_value(0), "Force Downlink EARFCN for single cell") ("rf.srate", bpo::value(&args->rf.srate_hz)->default_value(0.0), "Force Tx and Rx sampling rate in Hz") @@ -137,9 +143,12 @@ void parse_args(all_args_t* args, int argc, char* argv[]) /* PCAP */ ("pcap.enable", bpo::value(&args->stack.mac_pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") - ("pcap.filename", bpo::value(&args->stack.mac_pcap.filename)->default_value("enb_mac.pcap"), "MAC layer capture filename") + ("pcap.filename", bpo::value(&args->stack.mac_pcap.filename)->default_value("/tmp/enb_mac.pcap"), "MAC layer capture filename") + ("pcap.nr_filename", bpo::value(&args->nr_stack.mac.pcap.filename)->default_value("/tmp/enb_mac_nr.pcap"), "NR MAC layer capture filename") ("pcap.s1ap_enable", bpo::value(&args->stack.s1ap_pcap.enable)->default_value(false), "Enable S1AP packet captures for wireshark") - ("pcap.s1ap_filename", bpo::value(&args->stack.s1ap_pcap.filename)->default_value("enb_s1ap.pcap"), "S1AP layer capture filename") + ("pcap.s1ap_filename", bpo::value(&args->stack.s1ap_pcap.filename)->default_value("/tmp/enb_s1ap.pcap"), "S1AP layer capture filename") + ("pcap.ngap_enable", bpo::value(&args->nr_stack.ngap_pcap.enable)->default_value(false), "Enable NGAP packet captures for wireshark") + ("pcap.ngap_filename", bpo::value(&args->nr_stack.ngap_pcap.filename)->default_value("/tmp/enb_ngap.pcap"), "NGAP layer capture filename") ("pcap.mac_net_enable", bpo::value(&args->stack.mac_pcap_net.enable)->default_value(false), "Enable MAC network captures") ("pcap.bind_ip", bpo::value(&args->stack.mac_pcap_net.bind_ip)->default_value("0.0.0.0"), "Bind IP address for MAC network trace") ("pcap.bind_port", bpo::value(&args->stack.mac_pcap_net.bind_port)->default_value(5687), "Bind port for MAC network trace") @@ -153,10 +162,25 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("scheduler.pdsch_max_mcs", bpo::value(&args->stack.mac.sched.pdsch_max_mcs)->default_value(-1), "Optional PDSCH MCS limit") ("scheduler.pusch_mcs", bpo::value(&args->stack.mac.sched.pusch_mcs)->default_value(-1), "Optional fixed PUSCH MCS (ignores reported CQIs if specified)") ("scheduler.pusch_max_mcs", bpo::value(&args->stack.mac.sched.pusch_max_mcs)->default_value(-1), "Optional PUSCH MCS limit") - ("scheduler.max_aggr_level", bpo::value(&args->stack.mac.sched.max_aggr_level)->default_value(-1), "Optional maximum aggregation level index (l=log2(L)) ") + ("scheduler.min_aggr_level", bpo::value(&args->stack.mac.sched.min_aggr_level)->default_value(0), "Optional minimum aggregation level index (l=log2(L)) ") + ("scheduler.max_aggr_level", bpo::value(&args->stack.mac.sched.max_aggr_level)->default_value(3), "Optional maximum aggregation level index (l=log2(L)) ") + ("scheduler.adaptive_aggr_level", bpo::value(&args->stack.mac.sched.adaptive_aggr_level)->default_value(false), "Boolean flag to enable/disable adaptive aggregation level based on target BLER") ("scheduler.max_nof_ctrl_symbols", bpo::value(&args->stack.mac.sched.max_nof_ctrl_symbols)->default_value(3), "Number of control symbols") ("scheduler.min_nof_ctrl_symbols", bpo::value(&args->stack.mac.sched.min_nof_ctrl_symbols)->default_value(1), "Minimum number of control symbols") ("scheduler.pucch_multiplex_enable", bpo::value(&args->stack.mac.sched.pucch_mux_enabled)->default_value(false), "Enable PUCCH multiplexing") + ("scheduler.pucch_harq_max_rb", bpo::value(&args->stack.mac.sched.pucch_harq_max_rb)->default_value(0), "Maximum number of RB to be used for PUCCH on the edges of the grid") + ("scheduler.target_bler", bpo::value(&args->stack.mac.sched.target_bler)->default_value(0.05), "Target BLER (in decimal) to achieve via adaptive link") + ("scheduler.max_delta_dl_cqi", bpo::value(&args->stack.mac.sched.max_delta_dl_cqi)->default_value(5.0), "Maximum shift in CQI for adaptive DL link") + ("scheduler.max_delta_ul_snr", bpo::value(&args->stack.mac.sched.max_delta_ul_snr)->default_value(5.0), "Maximum shift in UL SNR for adaptive UL link") + ("scheduler.adaptive_dl_mcs_step_size", bpo::value(&args->stack.mac.sched.adaptive_dl_mcs_step_size)->default_value(0.001), "Step size or learning rate used in adaptive DL MCS link") + ("scheduler.adaptive_ul_mcs_step_size", bpo::value(&args->stack.mac.sched.adaptive_ul_mcs_step_size)->default_value(0.001), "Step size or learning rate used in adaptive UL MCS link") + ("scheduler.min_tpc_tti_interval", bpo::value(&args->stack.mac.sched.min_tpc_tti_interval)->default_value(1), "Minimum TTI interval between positive or negative TPCs") + ("scheduler.ul_snr_avg_alpha", bpo::value(&args->stack.mac.sched.ul_snr_avg_alpha)->default_value(0.05), "Exponential Average alpha coefficient used in estimation of UL SNR") + ("scheduler.init_ul_snr_value", bpo::value(&args->stack.mac.sched.init_ul_snr_value)->default_value(5), "Initial UL SNR value used for computing MCS in the first UL grant") + ("scheduler.init_dl_cqi", bpo::value(&args->stack.mac.sched.init_dl_cqi)->default_value(5), "DL CQI value used before any CQI report is available to the eNB") + ("scheduler.max_sib_coderate", bpo::value(&args->stack.mac.sched.max_sib_coderate)->default_value(0.8), "Upper bound on SIB and RAR grants coderate") + ("scheduler.pdcch_cqi_offset", bpo::value(&args->stack.mac.sched.pdcch_cqi_offset)->default_value(0), "CQI offset in derivation of PDCCH aggregation level") + /* Downlink Channel emulator section */ @@ -198,35 +222,60 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("channel.ul.hst.fd_hz", bpo::value(&args->phy.ul_channel_args.hst_fd_hz)->default_value(+750.0f), "Doppler frequency in Hz") ("channel.ul.hst.init_time_s", bpo::value(&args->phy.ul_channel_args.hst_init_time_s)->default_value(0), "Initial time in seconds") + /* CFR section */ + ("cfr.enable", bpo::value(&args->phy.cfr_args.enable)->default_value(args->phy.cfr_args.enable), "CFR enable") + ("cfr.mode", bpo::value(&cfr_mode)->default_value("manual"), "CFR mode") + ("cfr.manual_thres", bpo::value(&args->phy.cfr_args.manual_thres)->default_value(args->phy.cfr_args.manual_thres), "Fixed manual clipping threshold for CFR manual mode") + ("cfr.strength", bpo::value(&args->phy.cfr_args.strength)->default_value(args->phy.cfr_args.strength), "CFR ratio between amplitude-limited vs original signal (0 to 1)") + ("cfr.auto_target_papr", bpo::value(&args->phy.cfr_args.auto_target_papr)->default_value(args->phy.cfr_args.auto_target_papr), "Signal PAPR target (in dB) in CFR auto modes") + ("cfr.ema_alpha", bpo::value(&args->phy.cfr_args.ema_alpha)->default_value(args->phy.cfr_args.ema_alpha), "Alpha coefficient for the power average in auto_ema mode (0 to 1)") + /* Expert section */ - ("expert.metrics_period_secs", bpo::value(&args->general.metrics_period_secs)->default_value(1.0), "Periodicity for metrics in seconds") - ("expert.metrics_csv_enable", bpo::value(&args->general.metrics_csv_enable)->default_value(false), "Write metrics to CSV file") - ("expert.metrics_csv_filename", bpo::value(&args->general.metrics_csv_filename)->default_value("/tmp/enb_metrics.csv"), "Metrics CSV filename") - ("expert.pusch_max_its", bpo::value(&args->phy.pusch_max_its)->default_value(8), "Maximum number of turbo decoder iterations") - ("expert.pusch_8bit_decoder", bpo::value(&args->phy.pusch_8bit_decoder)->default_value(false), "Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental)") - ("expert.pusch_meas_evm", bpo::value(&args->phy.pusch_meas_evm)->default_value(false), "Enable/Disable PUSCH EVM measure") - ("expert.tx_amplitude", bpo::value(&args->phy.tx_amplitude)->default_value(0.6), "Transmit amplitude factor") - ("expert.nof_phy_threads", bpo::value(&args->phy.nof_phy_threads)->default_value(3), "Number of PHY threads") - ("expert.nof_prach_threads", bpo::value(&args->phy.nof_prach_threads)->default_value(1), "Number of PRACH workers per carrier. Only 1 or 0 is supported") - ("expert.max_prach_offset_us", bpo::value(&args->phy.max_prach_offset_us)->default_value(30), "Maximum allowed RACH offset (in us)") - ("expert.equalizer_mode", bpo::value(&args->phy.equalizer_mode)->default_value("mmse"), "Equalizer mode") + ("expert.metrics_period_secs", bpo::value(&args->general.metrics_period_secs)->default_value(1.0), "Periodicity for metrics in seconds.") + ("expert.metrics_csv_enable", bpo::value(&args->general.metrics_csv_enable)->default_value(false), "Write metrics to CSV file.") + ("expert.metrics_csv_filename", bpo::value(&args->general.metrics_csv_filename)->default_value("/tmp/enb_metrics.csv"), "Metrics CSV filename.") + ("expert.pusch_max_its", bpo::value(&args->phy.pusch_max_its)->default_value(8), "Maximum number of turbo decoder iterations for LTE.") + ("expert.pusch_8bit_decoder", bpo::value(&args->phy.pusch_8bit_decoder)->default_value(false), "Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental).") + ("expert.pusch_meas_evm", bpo::value(&args->phy.pusch_meas_evm)->default_value(false), "Enable/Disable PUSCH EVM measure.") + ("expert.tx_amplitude", bpo::value(&args->phy.tx_amplitude)->default_value(0.6), "Transmit amplitude factor.") + ("expert.nof_phy_threads", bpo::value(&args->phy.nof_phy_threads)->default_value(3), "Number of PHY threads.") + ("expert.nof_prach_threads", bpo::value(&args->phy.nof_prach_threads)->default_value(1), "Number of PRACH workers per carrier. Only 1 or 0 is supported.") + ("expert.max_prach_offset_us", bpo::value(&args->phy.max_prach_offset_us)->default_value(30), "Maximum allowed RACH offset (in us).") + ("expert.equalizer_mode", bpo::value(&args->phy.equalizer_mode)->default_value("mmse"), "Equalizer mode.") ("expert.estimator_fil_w", bpo::value(&args->phy.estimator_fil_w)->default_value(0.1), "Chooses the coefficients for the 3-tap channel estimator centered filter.") ("expert.lte_sample_rates", bpo::value(&use_standard_lte_rates)->default_value(false), "Whether to use default LTE sample rates instead of shorter variants.") - ("expert.report_json_enable", bpo::value(&args->general.report_json_enable)->default_value(false), "Write eNB report to JSON file") - ("expert.report_json_filename", bpo::value(&args->general.report_json_filename)->default_value("/tmp/enb_report.json"), "Report JSON filename") - ("expert.alarms_log_enable", bpo::value(&args->general.alarms_log_enable)->default_value(false), "Log alarms") - ("expert.alarms_filename", bpo::value(&args->general.alarms_filename)->default_value("/tmp/enb_alarms.log"), "Alarms filename") - ("expert.tracing_enable", bpo::value(&args->general.tracing_enable)->default_value(false), "Events tracing") - ("expert.tracing_filename", bpo::value(&args->general.tracing_filename)->default_value("/tmp/enb_tracing.log"), "Tracing events filename") - ("expert.tracing_buffcapacity", bpo::value(&args->general.tracing_buffcapacity)->default_value(1000000), "Tracing buffer capcity") + ("expert.report_json_enable", bpo::value(&args->general.report_json_enable)->default_value(false), "Write eNB report to JSON file (default disabled).") + ("expert.report_json_filename", bpo::value(&args->general.report_json_filename)->default_value("/tmp/enb_report.json"), "Report JSON filename (default /tmp/enb_report.json).") + ("expert.report_json_asn1_oct", bpo::value(&args->general.report_json_asn1_oct)->default_value(false), "Prints ASN1 messages encoded as an octet string instead of plain text in the JSON report file.") + ("expert.alarms_log_enable", bpo::value(&args->general.alarms_log_enable)->default_value(false), "Enable Alarms logging (default diabled).") + ("expert.alarms_filename", bpo::value(&args->general.alarms_filename)->default_value("/tmp/enb_alarms.log"), "Alarms logging filename (default /tmp/alarms.log).") + ("expert.tracing_enable", bpo::value(&args->general.tracing_enable)->default_value(false), "Events tracing.") + ("expert.tracing_filename", bpo::value(&args->general.tracing_filename)->default_value("/tmp/enb_tracing.log"), "Tracing events filename.") + ("expert.tracing_buffcapacity", bpo::value(&args->general.tracing_buffcapacity)->default_value(1000000), "Tracing buffer capcity.") + ("expert.stdout_ts_enable", bpo::value(&stdout_ts_enable)->default_value(false), "Prints once per second the timestamp into stdout.") ("expert.rrc_inactivity_timer", bpo::value(&args->general.rrc_inactivity_timer)->default_value(30000), "Inactivity timer in ms.") - ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") + ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds.") ("expert.eea_pref_list", bpo::value(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") ("expert.eia_pref_list", bpo::value(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).") - ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization") - ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release") - ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release") + ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization.") + ("expert.lcid_padding", bpo::value(&args->stack.mac.lcid_padding)->default_value(3), "LCID on which to put MAC padding") + ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release (default 100).") + ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release (default 100).") + ("expert.gtpu_tunnel_timeout", bpo::value(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(0), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU (0 for infinity).") + ("expert.rlf_release_timer_ms", bpo::value(&args->general.rlf_release_timer_ms)->default_value(4000), "Time taken by eNB to release UE context after it detects an RLF.") + ("expert.extended_cp", bpo::value(&args->phy.extended_cp)->default_value(false), "Use extended cyclic prefix") + ("expert.ts1_reloc_prep_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_prep_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocPrep Expiry Timeout value in milliseconds.") + ("expert.ts1_reloc_overall_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_overall_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocOverall Expiry Timeout value in milliseconds.") + ("expert.rlf_min_ul_snr_estim", bpo::value(&args->stack.mac.rlf_min_ul_snr_estim)->default_value(-2), "SNR threshold in dB below which the eNB is notified with rlf ko.") + ("expert.max_s1_setup_retries", bpo::value(&args->stack.s1ap.max_s1_setup_retries)->default_value(-1), "Max S1 setup retries") + ("expert.s1_connect_timer", bpo::value(&args->stack.s1ap.s1_connect_timer)->default_value(10), "Connection Retry Timer for S1 connection (seconds)") + ("expert.sctp_reuse_addr", bpo::value(&args->stack.s1ap.sctp_reuse_addr)->default_value(false), "Use SO_REUSE_ADDR on S1-C interface.") + ("expert.sctp_rto_max", bpo::value(&args->stack.s1ap.sctp_rto_max)->default_value(6000), "SCTP maximum RTO.") + ("expert.sctp_init_max_attempts", bpo::value(&args->stack.s1ap.sctp_init_max_attempts)->default_value(3), "Maximum SCTP init attempts.") + ("expert.sctp_max_init_timeo)", bpo::value(&args->stack.s1ap.sctp_max_init_timeo)->default_value(5000), "Maximum SCTP init timeout.") + ("expert.rx_gain_offset", bpo::value(&args->phy.rx_gain_offset)->default_value(62), "RX Gain offset to add to rx_gain to calibrate RSRP readings") + ("expert.mac_prach_bi", bpo::value(&args->stack.mac.prach_bi)->default_value(0), "Backoff Indicator to reduce contention in the PRACH channel") // eMBMS section ("embms.enable", bpo::value(&args->stack.embms.enable)->default_value(false), "Enables MBMS in the eNB") @@ -235,22 +284,10 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("embms.mcs", bpo::value(&args->stack.embms.mcs)->default_value(20), "Modulation and Coding scheme of MBMS traffic.") // NR section - ("scheduler.tb_len", bpo::value(&args->stack.mac.nr_tb_size)->default_value(1520), "Default TB size") - - // VNF params - ("vnf.type", bpo::value(&args->phy.vnf_args.type)->default_value("gnb"), "VNF instance type [gnb,ue]") - ("vnf.addr", bpo::value(&args->phy.vnf_args.bind_addr)->default_value("localhost"), "Address to bind VNF interface") - ("vnf.port", bpo::value(&args->phy.vnf_args.bind_port)->default_value(3333), "Bind port") - ("log.vnf_level", bpo::value(&args->phy.vnf_args.log_level), "VNF log level") - ("log.vnf_hex_limit", bpo::value(&args->phy.vnf_args.log_hex_limit), "VNF log hex dump limit") - - // Arguments for coreless operation - ("coreless.ip_devname", bpo::value(&args->stack.coreless.gw_args.tun_dev_name)->default_value("tun1"), "Name of the TUN device") - ("coreless.ip_address", bpo::value(&args->stack.coreless.ip_addr)->default_value("192.168.1.1"), "IP address of the TUN device") - ("coreless.ip_netmask", bpo::value(&args->stack.coreless.gw_args.tun_dev_netmask)->default_value("255.255.255.0"), "Netmask of the TUN device") - ("coreless.drb_lcid", bpo::value(&args->stack.coreless.drb_lcid)->default_value(4), "LCID of the dummy DRB") - ("coreless.rnti", bpo::value(&args->stack.coreless.rnti)->default_value(1234), "RNTI of the dummy user") - ; + ("scheduler.nr_pdsch_mcs", bpo::value(&args->nr_stack.mac.sched_cfg.fixed_dl_mcs)->default_value(28), "Fixed NR DL MCS (-1 for dynamic).") + ("scheduler.nr_pusch_mcs", bpo::value(&args->nr_stack.mac.sched_cfg.fixed_ul_mcs)->default_value(28), "Fixed NR UL MCS (-1 for dynamic).") + ("expert.nr_pusch_max_its", bpo::value(&args->phy.nr_pusch_max_its)->default_value(10), "Maximum number of LDPC iterations for NR.") + ; // Positional options - config file location bpo::options_description position("Positional options"); @@ -320,6 +357,12 @@ void parse_args(all_args_t* args, int argc, char* argv[]) if (!srsran::string_to_mnc(mnc, &args->stack.s1ap.mnc)) { cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; } + if (!srsran::string_to_mcc(mcc, &args->nr_stack.ngap.mcc)) { + cout << "Error parsing enb.mcc:" << mcc << " - must be a 3-digit string." << endl; + } + if (!srsran::string_to_mnc(mnc, &args->nr_stack.ngap.mnc)) { + cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; + } if (args->stack.embms.enable) { if (args->stack.mac.sched.max_nof_ctrl_symbols == 3) { @@ -342,7 +385,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) // Convert eNB Id std::size_t pos = {}; try { - args->enb.enb_id = std::stoi(enb_id, &pos, 0); + args->enb.enb_id = std::stoul(enb_id, &pos, 0); } catch (...) { cout << "Error parsing enb.enb_id: " << enb_id << "." << endl; exit(1); @@ -352,6 +395,13 @@ void parse_args(all_args_t* args, int argc, char* argv[]) exit(1); } + // parse the CFR mode string + args->phy.cfr_args.mode = srsran_cfr_str2mode(cfr_mode.c_str()); + if (args->phy.cfr_args.mode == SRSRAN_CFR_THR_INVALID) { + cout << "Error, invalid CFR mode: " << cfr_mode << endl; + exit(1); + } + // Apply all_level to any unset layers if (vm.count("log.all_level")) { if (!vm.count("log.rf_level")) { @@ -425,8 +475,8 @@ void parse_args(all_args_t* args, int argc, char* argv[]) exit(1); } - if (!config_exists(args->enb_files.drb_config, "drb.conf")) { - cout << "Failed to read DRB configuration file " << args->enb_files.drb_config << " - exiting" << endl; + if (!config_exists(args->enb_files.rb_config, "rb.conf")) { + cout << "Failed to read RB configuration file " << args->enb_files.rb_config << " - exiting" << endl; exit(1); } @@ -434,6 +484,74 @@ void parse_args(all_args_t* args, int argc, char* argv[]) } static bool do_metrics = false; +static bool do_padding = false; + +static void execute_cmd(metrics_stdout* metrics, srsenb::enb_command_interface* control, const string& cmd_line) +{ + vector cmd; + srsran::string_parse_list(cmd_line, ' ', cmd); + if (cmd[0] == "t") { + do_metrics = !do_metrics; + if (do_metrics) { + cout << "Enter t to stop trace." << endl; + } else { + cout << "Enter t to restart trace." << endl; + } + metrics->toggle_print(do_metrics); + } else if (cmd[0] == "m") { + // Trigger cell measurements + control->cmd_cell_measure(); + } else if (cmd[0] == "sleep") { + if (cmd.size() != 2) { + cout << "Usage: " << cmd[0] << " [number of seconds]" << endl; + return; + } + int nseconds = srsran::string_cast(cmd[1]); + if (nseconds <= 0) { + return; + } + std::this_thread::sleep_for(std::chrono::seconds(nseconds)); + } else if (cmd[0] == "p") { + do_padding = !do_padding; + if (do_padding) { + cout << "Enter p to stop padding." << endl; + } else { + cout << "Enter p to restart padding." << endl; + } + control->toggle_padding(); + } else if (cmd[0] == "q") { + raise(SIGTERM); + } else if (cmd[0] == "cell_gain") { + if (cmd.size() != 3) { + cout << "Usage: " << cmd[0] << " [cell identifier] [gain in dB]" << endl; + return; + } + + // Parse command arguments + uint32_t cell_id = srsran::string_cast(cmd[1]); + float gain_db = srsran::string_cast(cmd[2]); + + // Set cell gain + control->cmd_cell_gain(cell_id, gain_db); + } else if (cmd[0] == "flush") { + if (cmd.size() != 1) { + cout << "Usage: " << cmd[0] << endl; + return; + } + srslog::flush(); + cout << "Flushed log file buffers" << endl; + } else { + cout << "Available commands: " << endl; + cout << " t: starts console trace" << endl; + cout << " m: downlink signal measurements" << endl; + cout << " q: quit srsenb" << endl; + cout << " cell_gain: set relative cell gain" << endl; + cout << " sleep: pauses the commmand line operation for a given time in seconds" << endl; + cout << " p: starts MAC padding" << endl; + cout << " flush: flushes the buffers for the log file" << endl; + cout << endl; + } +} static void* input_loop(metrics_stdout* metrics, srsenb::enb_command_interface* control) { @@ -448,36 +566,11 @@ static void* input_loop(metrics_stdout* metrics, srsenb::enb_command_interface* cout << "Closing stdin thread." << endl; break; } else if (not input_line.empty()) { - vector cmd; - srsran::string_parse_list(input_line, ' ', cmd); - if (cmd[0] == "t") { - do_metrics = !do_metrics; - if (do_metrics) { - cout << "Enter t to stop trace." << endl; - } else { - cout << "Enter t to restart trace." << endl; - } - metrics->toggle_print(do_metrics); - } else if (cmd[0] == "q") { - raise(SIGTERM); - } else if (cmd[0] == "cell_gain") { - if (cmd.size() != 3) { - cout << "Usage: " << cmd[0] << " [cell identifier] [gain in dB]" << endl; - continue; - } + list cmd_list; + srsran::string_parse_list(input_line, ';', cmd_list); - // Parse command arguments - uint32_t cell_id = srsran::string_cast(cmd[1]); - float gain_db = srsran::string_cast(cmd[2]); - - // Set cell gain - control->cmd_cell_gain(cell_id, gain_db); - } else { - cout << "Available commands: " << endl; - cout << " t: starts console trace" << endl; - cout << " q: quit srsenb" << endl; - cout << " cell_gain: set relative cell gain" << endl; - cout << endl; + for (const string& cmd : cmd_list) { + execute_cmd(metrics, control, cmd); } } } @@ -491,9 +584,26 @@ static size_t fixup_log_file_maxsize(int x) return (x < 0) ? 0 : size_t(x) * 1024u; } +extern "C" void srsran_dft_exit(); +static void emergency_cleanup_handler(void* data) +{ + srslog::flush(); + if (log_sink) { + log_sink->flush(); + } + srsran_dft_exit(); +} + +static void signal_handler() +{ + running = false; +} + int main(int argc, char* argv[]) { - srsran_register_signal_handler(); + srsran_register_signal_handler(signal_handler); + add_emergency_cleanup_handler(emergency_cleanup_handler, nullptr); + all_args_t args = {}; srsran::metrics_hub metricshub; metrics_stdout metrics_screen; @@ -510,7 +620,7 @@ int main(int argc, char* argv[]) : srslog::fetch_file_sink(args.log.filename, fixup_log_file_maxsize(args.log.file_max_size))); // Alarms log channel creation. - srslog::sink& alarm_sink = srslog::fetch_file_sink(args.general.alarms_filename); + srslog::sink& alarm_sink = srslog::fetch_file_sink(args.general.alarms_filename, 0, true); srslog::log_channel& alarms_channel = srslog::fetch_log_channel("alarms", alarm_sink, {"ALRM", '\0', false}); alarms_channel.set_enabled(args.general.alarms_log_enable); @@ -533,13 +643,20 @@ int main(int argc, char* argv[]) // Set up the JSON log channel used by metrics and events. srslog::sink& json_sink = - srslog::fetch_file_sink(args.general.report_json_filename, 0, srslog::create_json_formatter()); + srslog::fetch_file_sink(args.general.report_json_filename, 0, false, srslog::create_json_formatter()); srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {}); json_channel.set_enabled(args.general.report_json_enable); // Configure the event logger just before starting the eNB class. if (args.general.report_json_enable) { - event_logger::configure(json_channel); + event_logger::asn1_output_format format = (args.general.report_json_asn1_oct) + ? event_logger::asn1_output_format::octets + : event_logger::asn1_output_format::text; + event_logger::configure(json_channel, format); + } + + if (mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE) == -1) { + srsran::console("Failed to `mlockall`: {}", errno); } // Create eNB @@ -554,16 +671,14 @@ int main(int argc, char* argv[]) metricshub.add_listener(&metrics_screen); metrics_screen.set_handle(enb.get()); - srsenb::metrics_csv metrics_file(args.general.metrics_csv_filename); + srsenb::metrics_csv metrics_file(args.general.metrics_csv_filename, enb.get()); if (args.general.metrics_csv_enable) { metricshub.add_listener(&metrics_file); - metrics_file.set_handle(enb.get()); } - srsenb::metrics_json json_metrics(json_channel); + srsenb::metrics_json json_metrics(json_channel, enb.get()); if (args.general.report_json_enable) { metricshub.add_listener(&json_metrics); - json_metrics.set_handle(enb.get()); } // create input thread @@ -574,7 +689,8 @@ int main(int argc, char* argv[]) enb->start_plot(); } } - int cnt = 0; + int cnt = 0; + int ts_cnt = 0; while (running) { if (args.general.print_buffer_state) { cnt++; @@ -583,6 +699,16 @@ int main(int argc, char* argv[]) enb->print_pool(); } } + if (stdout_ts_enable) { + if (++ts_cnt == 100) { + ts_cnt = 0; + char buff[64]; + std::time_t t = std::time(nullptr); + if (std::strftime(buff, sizeof(buff), "%FT%T", std::gmtime(&t))) { + std::cout << buff << '\n'; + } + } + } std::this_thread::sleep_for(std::chrono::milliseconds(10)); } input.join(); diff --git a/srsenb/src/metrics_csv.cc b/srsenb/src/metrics_csv.cc index 2ce0ede81..9384f4403 100644 --- a/srsenb/src/metrics_csv.cc +++ b/srsenb/src/metrics_csv.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -36,7 +36,8 @@ using namespace std; namespace srsenb { -metrics_csv::metrics_csv(std::string filename) : n_reports(0), metrics_report_period(1.0), enb(NULL) +metrics_csv::metrics_csv(std::string filename, enb_metrics_interface* enb_) : + n_reports(0), metrics_report_period(1.0), enb(enb_) { file.open(filename.c_str(), std::ios_base::out); } @@ -46,11 +47,6 @@ metrics_csv::~metrics_csv() stop(); } -void metrics_csv::set_handle(enb_metrics_interface* enb_) -{ - enb = enb_; -} - void metrics_csv::stop() { if (file.is_open()) { diff --git a/srsenb/src/metrics_json.cc b/srsenb/src/metrics_json.cc index 2c1028f07..2ffba0bb8 100644 --- a/srsenb/src/metrics_json.cc +++ b/srsenb/src/metrics_json.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,11 +24,6 @@ using namespace srsenb; -void metrics_json::set_handle(enb_metrics_interface* enb_) -{ - enb = enb_; -} - namespace { /// Bearer container metrics. @@ -59,6 +54,13 @@ DECLARE_METRIC("dl_bitrate", metric_dl_bitrate, float, ""); DECLARE_METRIC("dl_bler", metric_dl_bler, float, ""); DECLARE_METRIC("ul_snr", metric_ul_snr, float, ""); DECLARE_METRIC("ul_mcs", metric_ul_mcs, float, ""); +DECLARE_METRIC("ul_pusch_rssi", metric_ul_pusch_rssi, float, ""); +DECLARE_METRIC("ul_pucch_rssi", metric_ul_pucch_rssi, float, ""); +DECLARE_METRIC("ul_pucch_ni", metric_ul_pucch_ni, float, ""); +DECLARE_METRIC("ul_pusch_tpc", metric_ul_pusch_tpc, int64_t, ""); +DECLARE_METRIC("ul_pucch_tpc", metric_ul_pucch_tpc, int64_t, ""); +DECLARE_METRIC("dl_cqi_offset", metric_dl_cqi_offset, float, ""); +DECLARE_METRIC("ul_snr_offset", metric_ul_snr_offset, float, ""); DECLARE_METRIC("ul_bitrate", metric_ul_bitrate, float, ""); DECLARE_METRIC("ul_bler", metric_ul_bler, float, ""); DECLARE_METRIC("ul_phr", metric_ul_phr, float, ""); @@ -69,6 +71,13 @@ DECLARE_METRIC_SET("ue_container", metric_ue_rnti, metric_dl_cqi, metric_dl_mcs, + metric_ul_pusch_rssi, + metric_ul_pucch_rssi, + metric_ul_pucch_ni, + metric_ul_pusch_tpc, + metric_ul_pucch_tpc, + metric_dl_cqi_offset, + metric_ul_snr_offset, metric_dl_bitrate, metric_dl_bler, metric_ul_snr, @@ -79,19 +88,20 @@ DECLARE_METRIC_SET("ue_container", metric_bsr, mlist_bearers); -/// Sector container metrics. -DECLARE_METRIC("sector_id", metric_sector_id, uint32_t, ""); -DECLARE_METRIC("sector_rach", metric_sector_rach, uint32_t, ""); +/// Cell container metrics. +DECLARE_METRIC("carrier_id", metric_carrier_id, uint32_t, ""); +DECLARE_METRIC("pci", metric_pci, uint32_t, ""); +DECLARE_METRIC("nof_rach", metric_nof_rach, uint32_t, ""); DECLARE_METRIC_LIST("ue_list", mlist_ues, std::vector); -DECLARE_METRIC_SET("sector_container", mset_sector_container, metric_sector_id, metric_sector_rach, mlist_ues); +DECLARE_METRIC_SET("cell_container", mset_cell_container, metric_carrier_id, metric_pci, metric_nof_rach, mlist_ues); /// Metrics root object. DECLARE_METRIC("type", metric_type_tag, std::string, ""); DECLARE_METRIC("timestamp", metric_timestamp_tag, double, ""); -DECLARE_METRIC_LIST("sector_list", mlist_sector, std::vector); +DECLARE_METRIC_LIST("cell_list", mlist_cell, std::vector); /// Metrics context. -using metric_context_t = srslog::build_context_type; +using metric_context_t = srslog::build_context_type; } // namespace @@ -101,24 +111,40 @@ static void fill_ue_metrics(mset_ue_container& ue, const enb_metrics_t& m, unsig ue.write(m.stack.mac.ues[i].rnti); ue.write(std::max(0.1f, m.stack.mac.ues[i].dl_cqi)); if (!std::isnan(m.phy[i].dl.mcs)) { - ue.write(std::max(0.1f, m.phy[i].dl.mcs)); + ue.write(m.phy[i].dl.mcs); } - if (m.stack.mac.ues[i].tx_brate > 0) { + if (m.stack.mac.ues[i].tx_brate > 0 && m.stack.mac.ues[i].nof_tti > 0) { ue.write( std::max(0.1f, (float)m.stack.mac.ues[i].tx_brate / (m.stack.mac.ues[i].nof_tti * 0.001f))); } if (m.stack.mac.ues[i].tx_pkts > 0 && m.stack.mac.ues[i].tx_errors > 0) { - ue.write(std::max(0.1f, (float)100 * m.stack.mac.ues[i].tx_errors / m.stack.mac.ues[i].tx_pkts)); + ue.write((float)100 * m.stack.mac.ues[i].tx_errors / m.stack.mac.ues[i].tx_pkts); } if (!std::isnan(m.phy[i].ul.pusch_sinr)) { - ue.write(std::max(0.1f, m.phy[i].ul.pusch_sinr)); + ue.write(m.phy[i].ul.pusch_sinr); + } + if (!std::isnan(m.phy[i].ul.pusch_rssi)) { + ue.write(m.phy[i].ul.pusch_rssi); + } + if (!std::isnan(m.phy[i].ul.pucch_rssi)) { + ue.write(m.phy[i].ul.pucch_rssi); + } + if (!std::isnan(m.phy[i].ul.pucch_ni)) { + ue.write(m.phy[i].ul.pucch_ni); + } + ue.write(m.phy[i].ul.pusch_tpc); + ue.write(m.phy[i].dl.pucch_tpc); + if (!std::isnan(m.stack.mac.ues[i].dl_cqi_offset)) { + ue.write(m.stack.mac.ues[i].dl_cqi_offset); + } + if (!std::isnan(m.stack.mac.ues[i].ul_snr_offset)) { + ue.write(m.stack.mac.ues[i].ul_snr_offset); } if (!std::isnan(m.phy[i].ul.mcs)) { - ue.write(std::max(0.1f, m.phy[i].ul.mcs)); + ue.write(m.phy[i].ul.mcs); } - if (m.stack.mac.ues[i].rx_brate > 0) { - ue.write( - std::max(0.1f, (float)m.stack.mac.ues[i].rx_brate / (m.stack.mac.ues[i].nof_tti * 0.001f))); + if (m.stack.mac.ues[i].rx_brate > 0 && m.stack.mac.ues[i].nof_tti > 0) { + ue.write((float)m.stack.mac.ues[i].rx_brate / (m.stack.mac.ues[i].nof_tti * 0.001f)); } if (m.stack.mac.ues[i].rx_pkts > 0 && m.stack.mac.ues[i].rx_errors > 0) { ue.write(std::max(0.1f, (float)100 * m.stack.mac.ues[i].rx_errors / m.stack.mac.ues[i].rx_pkts)); @@ -179,7 +205,7 @@ void metrics_json::set_metrics(const enb_metrics_t& m, const uint32_t period_use if (!enb) { return; } - if (m.stack.mac.cc_rach_counter.empty()) { + if (m.stack.mac.cc_info.empty()) { return; } @@ -187,27 +213,28 @@ void metrics_json::set_metrics(const enb_metrics_t& m, const uint32_t period_use // Fill root object. ctx.write("metrics"); - auto& sector_list = ctx.get(); - sector_list.resize(m.stack.mac.cc_rach_counter.size()); + auto& cell_list = ctx.get(); + cell_list.resize(m.stack.mac.cc_info.size()); - // For each sector... - for (unsigned cc_idx = 0, e = sector_list.size(); cc_idx != e; ++cc_idx) { - auto& sector = sector_list[cc_idx]; - sector.write(cc_idx); - sector.write(m.stack.mac.cc_rach_counter[cc_idx]); + // For each cell... + for (unsigned cc_idx = 0, e = cell_list.size(); cc_idx != e; ++cc_idx) { + auto& cell = cell_list[cc_idx]; + cell.write(cc_idx); + cell.write(m.stack.mac.cc_info[cc_idx].cc_rach_counter); + cell.write(m.stack.mac.cc_info[cc_idx].pci); - // For each UE in this sector... + // For each UE in this cell... for (unsigned i = 0; i != m.stack.rrc.ues.size(); ++i) { if (!has_valid_metric_ranges(m, i)) { continue; } - // Only record UEs that belong to this sector. + // Only record UEs that belong to this cell. if (m.stack.mac.ues[i].cc_idx != cc_idx) { continue; } - sector.get().emplace_back(); - fill_ue_metrics(sector.get().back(), m, i); + cell.get().emplace_back(); + fill_ue_metrics(cell.get().back(), m, i); } } diff --git a/srsenb/src/metrics_stdout.cc b/srsenb/src/metrics_stdout.cc index 1644ae4af..bb333913e 100644 --- a/srsenb/src/metrics_stdout.cc +++ b/srsenb/src/metrics_stdout.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -74,6 +74,110 @@ void metrics_stdout::toggle_print(bool b) do_print = b; } +// Define iszero() here since it's not defined in some platforms +static bool iszero(float x) +{ + return fabsf(x) < 2 * DBL_EPSILON; +} + +void metrics_stdout::set_metrics_helper(uint32_t num_ue, + const mac_metrics_t& mac, + const std::vector& phy, + bool is_nr) +{ + for (size_t i = 0; i < num_ue; i++) { + // make sure we have stats for MAC and PHY layer too + if (i >= mac.ues.size() || ((i >= phy.size()) && !is_nr)) { + break; + } + if (mac.ues[i].tx_errors > mac.ues[i].tx_pkts) { + fmt::print("tx caution errors {} > {}\n", mac.ues[i].tx_errors, mac.ues[i].tx_pkts); + } + if (mac.ues[i].rx_errors > mac.ues[i].rx_pkts) { + fmt::print("rx caution errors {} > {}\n", mac.ues[i].rx_errors, mac.ues[i].rx_pkts); + } + + fmt::print("{:>3.5}", (is_nr) ? "nr" : "lte"); + fmt::print(" {:>4}", mac.ues[i].pci); + fmt::print("{:>5x}", mac.ues[i].rnti); + if (not iszero(mac.ues[i].dl_cqi)) { + fmt::print(" {:>3}", int(mac.ues[i].dl_cqi)); + } else { + fmt::print(" {:>3.3}", "n/a"); + } + fmt::print(" {:>1}", int(mac.ues[i].dl_ri)); + float dl_mcs = (is_nr) ? mac.ues[i].dl_mcs : phy[i].dl.mcs; + if (not isnan(dl_mcs)) { + fmt::print(" {:>2}", int(dl_mcs)); + } else { + fmt::print(" {:>2}", 0); + } + if (mac.ues[i].tx_brate > 0) { + fmt::print(" {:>6.6}", float_to_eng_string((float)mac.ues[i].tx_brate / (mac.ues[i].nof_tti * 1e-3), 1)); + } else { + fmt::print(" {:>6}", 0); + } + fmt::print(" {:>4}", mac.ues[i].tx_pkts - mac.ues[i].tx_errors); + fmt::print(" {:>4}", mac.ues[i].tx_errors); + if (mac.ues[i].tx_pkts > 0 && mac.ues[i].tx_errors) { + fmt::print(" {:>3}%", int((float)100 * mac.ues[i].tx_errors / mac.ues[i].tx_pkts)); + } else { + fmt::print(" {:>3}%", 0); + } + + fmt::print(" |"); + + auto clamp_sinr = [](float sinr) { + if (sinr > 99.9f) { + return 99.9f; + } + if (sinr < -99.9f) { + return -99.9f; + } + return sinr; + }; + float pusch_sinr = (is_nr) ? mac.ues[i].pusch_sinr : phy[i].ul.pusch_sinr; + if (not isnan(pusch_sinr) and not iszero(pusch_sinr)) { + fmt::print(" {:>5.1f}", clamp_sinr(pusch_sinr)); + } else { + fmt::print(" {:>5.5}", "n/a"); + } + float pucch_sinr = (is_nr) ? mac.ues[i].pucch_sinr : phy[i].ul.pucch_sinr; + if (not isnan(pucch_sinr) and not iszero(pucch_sinr)) { + fmt::print(" {:>5.1f}", clamp_sinr(pucch_sinr)); + } else { + fmt::print(" {:>5.5}", "n/a"); + } + int phr = mac.ues[i].phr; + if (not isnan(phr)) { + fmt::print(" {:>2}", phr); + } else { + fmt::print(" {:>2}", 0); + } + float ul_mcs = (is_nr) ? mac.ues[i].ul_mcs : phy[i].ul.mcs; + if (not isnan(ul_mcs)) { + fmt::print(" {:>2}", int(ul_mcs)); + } else { + fmt::print(" {:>2}", 0); + } + if (mac.ues[i].rx_brate > 0) { + fmt::print(" {:>6.6}", float_to_eng_string((float)mac.ues[i].rx_brate / (mac.ues[i].nof_tti * 1e-3), 1)); + } else { + fmt::print(" {:>6}", 0); + } + fmt::print(" {:>4}", mac.ues[i].rx_pkts - mac.ues[i].rx_errors); + fmt::print(" {:>4}", mac.ues[i].rx_errors); + + if (mac.ues[i].rx_pkts > 0 && mac.ues[i].rx_errors > 0) { + fmt::print(" {:>3}%", int((float)100 * mac.ues[i].rx_errors / mac.ues[i].rx_pkts)); + } else { + fmt::print(" {:>3}%", 0); + } + fmt::print(" {:>6.6}", float_to_eng_string(mac.ues[i].ul_buffer, 2)); + fmt::print("\n"); + } +} + void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t period_usec) { if (!do_print || enb == nullptr) { @@ -81,99 +185,24 @@ void metrics_stdout::set_metrics(const enb_metrics_t& metrics, const uint32_t pe } if (metrics.rf.rf_error) { - printf("RF status: O=%d, U=%d, L=%d\n", metrics.rf.rf_o, metrics.rf.rf_u, metrics.rf.rf_l); + fmt::print("RF status: O={}, U={}, L={}\n", metrics.rf.rf_o, metrics.rf.rf_u, metrics.rf.rf_l); } - if (metrics.stack.rrc.ues.size() == 0) { + if (metrics.stack.rrc.ues.size() == 0 && metrics.nr_stack.mac.ues.size() == 0) { return; } - std::ios::fmtflags f(cout.flags()); // For avoiding Coverity defect: Not restoring ostream format - if (++n_reports > 10) { n_reports = 0; - cout << endl; - cout << "------DL-------------------------------UL--------------------------------------------" << endl; - cout << "rnti cqi ri mcs brate ok nok (%) pusch pucch phr mcs brate ok nok (%) bsr" << endl; + fmt::print("\n"); + fmt::print( + " -----------------DL----------------|-------------------------UL-------------------------\n"); + fmt::print( + "rat pci rnti cqi ri mcs brate ok nok (%) | pusch pucch phr mcs brate ok nok (%) bsr\n"); } - for (size_t i = 0; i < metrics.stack.rrc.ues.size(); i++) { - // make sure we have stats for MAC and PHY layer too - if (i >= metrics.stack.mac.ues.size() || i >= metrics.phy.size()) { - break; - } - if (metrics.stack.mac.ues[i].tx_errors > metrics.stack.mac.ues[i].tx_pkts) { - printf("tx caution errors %d > %d\n", metrics.stack.mac.ues[i].tx_errors, metrics.stack.mac.ues[i].tx_pkts); - } - if (metrics.stack.mac.ues[i].rx_errors > metrics.stack.mac.ues[i].rx_pkts) { - printf("rx caution errors %d > %d\n", metrics.stack.mac.ues[i].rx_errors, metrics.stack.mac.ues[i].rx_pkts); - } - - cout << int_to_hex_string(metrics.stack.mac.ues[i].rnti, 4) << " "; - cout << float_to_string(SRSRAN_MAX(0.1, metrics.stack.mac.ues[i].dl_cqi), 1, 3); - cout << float_to_string(metrics.stack.mac.ues[i].dl_ri, 1, 4); - if (not isnan(metrics.phy[i].dl.mcs)) { - cout << float_to_string(SRSRAN_MAX(0.1, metrics.phy[i].dl.mcs), 1, 4); - } else { - cout << float_to_string(0, 2, 4); - } - if (metrics.stack.mac.ues[i].tx_brate > 0) { - cout << float_to_eng_string( - SRSRAN_MAX(0.1, (float)metrics.stack.mac.ues[i].tx_brate / (metrics.stack.mac.ues[i].nof_tti * 1e-3)), 1); - } else { - cout << float_to_string(0, 1, 6) << ""; - } - cout << std::setw(5) << metrics.stack.mac.ues[i].tx_pkts - metrics.stack.mac.ues[i].tx_errors; - cout << std::setw(5) << metrics.stack.mac.ues[i].tx_errors; - if (metrics.stack.mac.ues[i].tx_pkts > 0 && metrics.stack.mac.ues[i].tx_errors) { - cout << float_to_string( - SRSRAN_MAX(0.1, (float)100 * metrics.stack.mac.ues[i].tx_errors / metrics.stack.mac.ues[i].tx_pkts), 1, 4) - << "%"; - } else { - cout << float_to_string(0, 1, 4) << "%"; - } - cout << " "; - - if (not isnan(metrics.phy[i].ul.pusch_sinr)) { - cout << float_to_string(SRSRAN_MAX(0.1, metrics.phy[i].ul.pusch_sinr), 2, 5); - } else { - cout << float_to_string(0, 2, 5); - } - - if (not isnan(metrics.phy[i].ul.pucch_sinr)) { - cout << float_to_string(SRSRAN_MAX(0.1, metrics.phy[i].ul.pucch_sinr), 2, 5); - } else { - cout << float_to_string(0, 2, 5); - } - cout << " "; - - cout << float_to_string(metrics.stack.mac.ues[i].phr, 2, 5); - if (not isnan(metrics.phy[i].ul.mcs)) { - cout << float_to_string(SRSRAN_MAX(0.1, metrics.phy[i].ul.mcs), 1, 4); - } else { - cout << float_to_string(0, 1, 4); - } - if (metrics.stack.mac.ues[i].rx_brate > 0) { - cout << float_to_eng_string( - SRSRAN_MAX(0.1, (float)metrics.stack.mac.ues[i].rx_brate / (metrics.stack.mac.ues[i].nof_tti * 1e-3)), 1); - } else { - cout << float_to_string(0, 1) << ""; - } - cout << std::setw(5) << metrics.stack.mac.ues[i].rx_pkts - metrics.stack.mac.ues[i].rx_errors; - cout << std::setw(5) << metrics.stack.mac.ues[i].rx_errors; - - if (metrics.stack.mac.ues[i].rx_pkts > 0 && metrics.stack.mac.ues[i].rx_errors > 0) { - cout << float_to_string( - SRSRAN_MAX(0.1, (float)100 * metrics.stack.mac.ues[i].rx_errors / metrics.stack.mac.ues[i].rx_pkts), 1, 4) - << "%"; - } else { - cout << float_to_string(0, 1, 4) << "%"; - } - cout << float_to_eng_string(metrics.stack.mac.ues[i].ul_buffer, 2); - cout << endl; - } - - cout.flags(f); // For avoiding Coverity defect: Not restoring ostream format + set_metrics_helper(metrics.stack.rrc.ues.size(), metrics.stack.mac, metrics.phy, false); + set_metrics_helper(metrics.nr_stack.mac.ues.size(), metrics.nr_stack.mac, metrics.phy, true); } std::string metrics_stdout::float_to_string(float f, int digits, int field_width) @@ -184,7 +213,7 @@ std::string metrics_stdout::float_to_string(float f, int digits, int field_width f = 0.0; precision = digits - 1; } else { - precision = digits - (int)(log10f(fabs(f)) - 2 * DBL_EPSILON); + precision = digits - (int)(log10f(fabs(f + 0.0001)) - 2 * DBL_EPSILON); } if (precision == -1) { precision = 0; @@ -193,13 +222,6 @@ std::string metrics_stdout::float_to_string(float f, int digits, int field_width return os.str(); } -std::string metrics_stdout::int_to_hex_string(int value, int field_width) -{ - std::ostringstream os; - os << std::hex << std::setw(field_width) << value; - return os.str(); -} - std::string metrics_stdout::float_to_eng_string(float f, int digits) { const int degree = (f == 0.0) ? 0 : lrint(floor(log10f(fabs(f)) / 3)); diff --git a/srsenb/src/parser.cc b/srsenb/src/parser.cc index ae51ab8a6..76f90215a 100644 --- a/srsenb/src/parser.cc +++ b/srsenb/src/parser.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/src/phy/CMakeLists.txt b/srsenb/src/phy/CMakeLists.txt index a69775f99..fc24841ff 100644 --- a/srsenb/src/phy/CMakeLists.txt +++ b/srsenb/src/phy/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -22,8 +22,7 @@ set(SOURCES lte/cc_worker.cc lte/sf_worker.cc lte/worker_pool.cc - nr/cc_worker.cc - nr/sf_worker.cc + nr/slot_worker.cc nr/worker_pool.cc phy.cc phy_common.cc @@ -32,8 +31,6 @@ set(SOURCES txrx.cc) add_library(srsenb_phy STATIC ${SOURCES}) -add_library(srsgnb_phy STATIC vnf_phy_nr.cc) - -if(ENABLE_GUI AND SRSGUI_FOUND) +if (ENABLE_GUI AND SRSGUI_FOUND) target_link_libraries(srsenb_phy ${SRSGUI_LIBRARIES}) -endif() +endif () diff --git a/srsenb/src/phy/lte/cc_worker.cc b/srsenb/src/phy/lte/cc_worker.cc index 2f1ef9def..14d6c8ff5 100644 --- a/srsenb/src/phy/lte/cc_worker.cc +++ b/srsenb/src/phy/lte/cc_worker.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,6 +19,8 @@ * */ +#include + #include "srsran/common/threads.h" #include "srsran/srsran.h" @@ -86,11 +88,12 @@ FILE* f; void cc_worker::init(phy_common* phy_, uint32_t cc_idx_) { - phy = phy_; - cc_idx = cc_idx_; - srsran_cell_t cell = phy_->get_cell(cc_idx); - uint32_t nof_prb = phy_->get_nof_prb(cc_idx); - uint32_t sf_len = SRSRAN_SF_LEN_PRB(nof_prb); + phy = phy_; + cc_idx = cc_idx_; + srsran_cell_t cell = phy_->get_cell(cc_idx); + uint32_t nof_prb = phy_->get_nof_prb(cc_idx); + uint32_t sf_len = SRSRAN_SF_LEN_PRB(nof_prb); + srsran_cfr_cfg_t cfr_config = phy_->get_cfr_config(); // Init cell here for (uint32_t p = 0; p < phy->get_nof_ports(cc_idx); p++) { @@ -115,6 +118,10 @@ void cc_worker::init(phy_common* phy_, uint32_t cc_idx_) ERROR("Error initiating ENB DL (cc=%d)", cc_idx); return; } + if (srsran_enb_dl_set_cfr(&enb_dl, &cfr_config) < SRSRAN_SUCCESS) { + ERROR("Error setting the CFR"); + return; + } if (srsran_enb_ul_init(&enb_ul, signal_buffer_rx[0], nof_prb)) { ERROR("Error initiating ENB UL"); return; @@ -196,8 +203,6 @@ void cc_worker::rem_rnti(uint16_t rnti) if (ue_db.count(rnti)) { delete ue_db[rnti]; ue_db.erase(rnti); - } else { - Error("Removing user: rnti=0x%x does not exist\n", rnti); } } @@ -262,9 +267,23 @@ void cc_worker::work_dl(const srsran_dl_sf_cfg_t& dl_sf_cfg, srsran_vec_sc_prod_cfc(signal_buffer_tx[i], scale, signal_buffer_tx[i], sf_len); } } + + // Measure PAPR if flag was triggered + bool cell_meas_flag = phy->get_cell_measure_trigger(cc_idx); + if (cell_meas_flag) { + uint32_t sf_len = SRSRAN_SF_LEN_PRB(enb_dl.cell.nof_prb); + for (uint32_t i = 0; i < enb_dl.cell.nof_ports; i++) { + // PAPR measure + float papr_db = 10.0f * log10(srsran_vec_papr_c(signal_buffer_tx[i], sf_len)); + std::cout << "Cell #" << cc_idx << " port #" << i << " PAPR = " << std::setprecision(4) << papr_db << " dB " + << std::endl; + } + // clear measurement flag on cell + phy->clear_cell_measure_trigger(cc_idx); + } } -void cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_grant, +bool cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_grant, srsran_ul_cfg_t& ul_cfg, srsran_pusch_res_t& pusch_res) { @@ -272,18 +291,19 @@ void cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_ // Invalid RNTI if (rnti == SRSRAN_INVALID_RNTI) { - return; + return false; } // RNTI does not exist if (ue_db.count(rnti) == 0) { - return; + return false; } // Get UE configuration if (phy->ue_db.get_ul_config(rnti, cc_idx, ul_cfg) < SRSRAN_SUCCESS) { - Error("Error retrieving UL configuration for RNTI %x and CC %d", rnti, cc_idx); - return; + // It could happen that the UL configuration is missing due to intra-enb HO which is not an error + Info("Failed retrieving UL configuration for cc=%d rnti=0x%x", cc_idx, rnti); + return false; } // Fill UCI configuration @@ -294,7 +314,7 @@ void cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_ srsran_pusch_grant_t& grant = ul_cfg.pusch.grant; if (srsran_ra_ul_dci_to_grant(&enb_ul.cell, &ul_sf, &ul_cfg.hopping, &ul_grant.dci, &grant)) { Error("Computing PUSCH dci for RNTI %x", rnti); - return; + return false; } // Handle Format0 adaptive retx @@ -303,7 +323,7 @@ void cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_ int rv_idx = grant.tb.rv; if (phy->ue_db.get_last_ul_tb(rnti, cc_idx, ul_grant.pid, grant.tb) < SRSRAN_SUCCESS) { Error("Error retrieving last UL TB for RNTI %x, CC %d, PID %d", rnti, cc_idx, ul_grant.pid); - return; + return false; } grant.tb.rv = rv_idx; Info("Adaptive retx: rnti=0x%x, pid=%d, rv_idx=%d, mcs=%d, old_tbs=%d", @@ -324,7 +344,7 @@ void cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_ if (pusch_res.data) { if (srsran_enb_ul_get_pusch(&enb_ul, &ul_sf, &ul_cfg.pusch, &pusch_res)) { Error("Decoding PUSCH for RNTI %x", rnti); - return; + return false; } } // Save PHICH scheduling for this user. Each user can have just 1 PUSCH dci per TTI @@ -352,8 +372,12 @@ void cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_ // Save statistics only if data was provided if (ul_grant.data != nullptr) { // Save metrics stats - ue_db[rnti]->metrics_ul(ul_grant.dci.tb.mcs_idx, 0, enb_ul.chest_res.snr_db, pusch_res.avg_iterations_block); + ue_db[rnti]->metrics_ul(ul_grant.dci.tb.mcs_idx, + enb_ul.chest_res.epre_dBfs - phy->params.rx_gain_offset, + enb_ul.chest_res.snr_db, + pusch_res.avg_iterations_block); } + return true; } void cc_worker::decode_pusch(stack_interface_phy_lte::ul_sched_grant_t* grants, uint32_t nof_pusch) @@ -368,14 +392,17 @@ void cc_worker::decode_pusch(stack_interface_phy_lte::ul_sched_grant_t* grants, srsran_ul_cfg_t ul_cfg = {}; // Decodes PUSCH for the given grant - decode_pusch_rnti(ul_grant, ul_cfg, pusch_res); + if (!decode_pusch_rnti(ul_grant, ul_cfg, pusch_res)) { + return; + } // Notify MAC new received data and HARQ Indication value if (ul_grant.data != nullptr) { // Inform MAC about the CRC result phy->stack->crc_info(tti_rx, rnti, cc_idx, ul_cfg.pusch.grant.tb.tbs / 8, pusch_res.crc); // Push PDU buffer - phy->stack->push_pdu(tti_rx, rnti, cc_idx, ul_cfg.pusch.grant.tb.tbs / 8, pusch_res.crc); + phy->stack->push_pdu( + tti_rx, rnti, cc_idx, ul_cfg.pusch.grant.tb.tbs / 8, pusch_res.crc, ul_cfg.pusch.grant.L_prb); // Logging if (logger.info.enabled()) { char str[512]; @@ -436,7 +463,11 @@ int cc_worker::decode_pucch() } // Save metrics - ue_db[rnti]->metrics_ul_pucch(pucch_res.snr_db); + if (pucch_res.detected) { + ue_db[rnti]->metrics_ul_pucch(pucch_res.rssi_dbFs - phy->params.rx_gain_offset, + pucch_res.ni_dbFs - -phy->params.rx_gain_offset, + pucch_res.snr_db); + } } } } @@ -472,7 +503,7 @@ int cc_worker::encode_pdcch_ul(stack_interface_phy_lte::ul_sched_grant_t* grants } if (SRSRAN_RNTI_ISUSER(grants[i].dci.rnti)) { - if (srsran_enb_dl_location_is_common_ncce(&enb_dl, grants[i].dci.location.ncce) && + if (srsran_enb_dl_location_is_common_ncce(&enb_dl, &grants[i].dci.location) && phy->ue_db.is_pcell(grants[i].dci.rnti, cc_idx)) { // Disable extended CSI request and SRS request in common SS srsran_dci_cfg_set_common_ss(&dci_cfg); @@ -488,7 +519,7 @@ int cc_worker::encode_pdcch_ul(stack_interface_phy_lte::ul_sched_grant_t* grants if (logger.info.enabled()) { char str[512]; srsran_dci_ul_info(&grants[i].dci, str, 512); - logger.info("PDCCH: cc=%d, %s, tti_tx_dl=%d", cc_idx, str, tti_tx_dl); + logger.info("PDCCH: cc=%d, rnti=0x%x, %s, tti_tx_dl=%d", cc_idx, grants[i].dci.rnti, str, tti_tx_dl); } } } @@ -507,8 +538,10 @@ int cc_worker::encode_pdcch_dl(stack_interface_phy_lte::dl_sched_grant_t* grants continue; } + // Detect if the DCI location is in common SS, if that is the case, flag it as common SS + // This makes possible UE specific DCI fields to be disabled, so it uses a fallback DCI size if (SRSRAN_RNTI_ISUSER(grants[i].dci.rnti) && grants[i].dci.format == SRSRAN_DCI_FORMAT1A) { - if (srsran_enb_dl_location_is_common_ncce(&enb_dl, grants[i].dci.location.ncce) && + if (srsran_enb_dl_location_is_common_ncce(&enb_dl, &grants[i].dci.location) && phy->ue_db.is_pcell(grants[i].dci.rnti, cc_idx)) { srsran_dci_cfg_set_common_ss(&dci_cfg); } @@ -523,7 +556,7 @@ int cc_worker::encode_pdcch_dl(stack_interface_phy_lte::dl_sched_grant_t* grants // Logging char str[512]; srsran_dci_dl_info(&grants[i].dci, str, 512); - logger.info("PDCCH: cc=%d, %s, tti_tx_dl=%d", cc_idx, str, tti_tx_dl); + logger.info("PDCCH: cc=%d, rnti=0x%x, %s, tti_tx_dl=%d", cc_idx, grants[i].dci.rnti, str, tti_tx_dl); } } } @@ -638,7 +671,7 @@ void cc_worker::ue::metrics_read(phy_metrics_t* metrics_) if (metrics_) { *metrics_ = metrics; } - bzero(&metrics, sizeof(phy_metrics_t)); + metrics = {}; } void cc_worker::ue::metrics_dl(uint32_t mcs) @@ -649,15 +682,23 @@ void cc_worker::ue::metrics_dl(uint32_t mcs) void cc_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, float turbo_iters) { + if (isnan(rssi)) { + rssi = 0; + } metrics.ul.mcs = SRSRAN_VEC_CMA((float)mcs, metrics.ul.mcs, metrics.ul.n_samples); metrics.ul.pusch_sinr = SRSRAN_VEC_CMA((float)sinr, metrics.ul.pusch_sinr, metrics.ul.n_samples); - metrics.ul.rssi = SRSRAN_VEC_CMA((float)rssi, metrics.ul.rssi, metrics.ul.n_samples); + metrics.ul.pusch_rssi = SRSRAN_VEC_CMA((float)rssi, metrics.ul.pusch_rssi, metrics.ul.n_samples); metrics.ul.turbo_iters = SRSRAN_VEC_CMA((float)turbo_iters, metrics.ul.turbo_iters, metrics.ul.n_samples); metrics.ul.n_samples++; } -void cc_worker::ue::metrics_ul_pucch(float sinr) +void cc_worker::ue::metrics_ul_pucch(float rssi, float ni, float sinr) { + if (isnan(rssi)) { + rssi = 0; + } + metrics.ul.pucch_rssi = SRSRAN_VEC_CMA((float)rssi, metrics.ul.pucch_rssi, metrics.ul.n_samples_pucch); + metrics.ul.pucch_ni = SRSRAN_VEC_CMA((float)ni, metrics.ul.pucch_ni, metrics.ul.n_samples_pucch); metrics.ul.pucch_sinr = SRSRAN_VEC_CMA((float)sinr, metrics.ul.pucch_sinr, metrics.ul.n_samples_pucch); metrics.ul.n_samples_pucch++; } diff --git a/srsenb/src/phy/lte/sf_worker.cc b/srsenb/src/phy/lte/sf_worker.cc index 15c1fe73d..cf0e170a2 100644 --- a/srsenb/src/phy/lte/sf_worker.cc +++ b/srsenb/src/phy/lte/sf_worker.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -110,21 +110,16 @@ cf_t* sf_worker::get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx) return cc_workers[cc_idx]->get_buffer_rx(antenna_idx); } -void sf_worker::set_time(uint32_t tti_, uint32_t tx_worker_cnt_, const srsran::rf_timestamp_t& tx_time_) +void sf_worker::set_context(const srsran::phy_common_interface::worker_context_t& w_ctx) { - tti_rx = tti_; + tti_rx = w_ctx.sf_idx; tti_tx_dl = TTI_ADD(tti_rx, FDD_HARQ_DELAY_UL_MS); tti_tx_ul = TTI_RX_ACK(tti_rx); - t_tx_dl = TTIMOD(tti_tx_dl); - t_rx = TTIMOD(tti_rx); - t_tx_ul = TTIMOD(tti_tx_ul); - - tx_worker_cnt = tx_worker_cnt_; - tx_time.copy(tx_time_); + context.copy(w_ctx); for (auto& w : cc_workers) { - w->set_tti(tti_); + w->set_tti(w_ctx.sf_idx); } } @@ -161,14 +156,10 @@ void sf_worker::work_imp() // Get Transmission buffers srsran::rf_buffer_t tx_buffer = {}; - for (uint32_t cc = 0; cc < phy->get_nof_carriers_lte(); cc++) { - for (uint32_t ant = 0; ant < phy->get_nof_ports(0); ant++) { - tx_buffer.set(cc, ant, phy->get_nof_ports(0), cc_workers[cc]->get_buffer_tx(ant)); - } - } + tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB(phy->get_nof_prb(0))); if (!running) { - phy->worker_end(this, tx_buffer, tx_time); + phy->worker_end(context, true, tx_buffer); return; } @@ -176,9 +167,9 @@ void sf_worker::work_imp() srsran_sf_t sf_type = phy->is_mbsfn_sf(&mbsfn_cfg, tti_tx_dl) ? SRSRAN_SF_MBSFN : SRSRAN_SF_NORM; // Uplink grants to receive this TTI - stack_interface_phy_lte::ul_sched_list_t ul_grants = phy->get_ul_grants(t_rx); + stack_interface_phy_lte::ul_sched_list_t ul_grants = phy->get_ul_grants(tti_rx); // Uplink grants to transmit this tti and receive in the future - stack_interface_phy_lte::ul_sched_list_t ul_grants_tx = phy->get_ul_grants(t_tx_ul); + stack_interface_phy_lte::ul_sched_list_t ul_grants_tx = phy->get_ul_grants(tti_tx_ul); // Downlink grants to transmit this TTI stack_interface_phy_lte::dl_sched_list_t dl_grants(phy->get_nof_carriers_lte()); @@ -193,8 +184,8 @@ void sf_worker::work_imp() ul_sf.tti = tti_rx; // Set UL grant availability prior to any UL processing - if (phy->ue_db.set_ul_grant_available(tti_rx, ul_grants)) { - Error("Error setting UL grants. Some grant's RNTI does not exist."); + if (phy->ue_db.set_ul_grant_available(tti_rx, ul_grants) < SRSRAN_SUCCESS) { + Info("Failed setting UL grants. Some grant's RNTI does not exist."); } // Process UL @@ -206,26 +197,22 @@ void sf_worker::work_imp() if (sf_type == SRSRAN_SF_NORM) { if (stack->get_dl_sched(tti_tx_dl, dl_grants) < 0) { Error("Getting DL scheduling from MAC"); - phy->worker_end(this, tx_buffer, tx_time); + phy->worker_end(context, true, tx_buffer); return; } } else { dl_grants[0].cfi = mbsfn_cfg.non_mbsfn_region_length; if (stack->get_mch_sched(tti_tx_dl, mbsfn_cfg.is_mcch, dl_grants)) { Error("Getting MCH packets from MAC"); - phy->worker_end(this, tx_buffer, tx_time); + phy->worker_end(context, true, tx_buffer); return; } } - // Make sure CFI is in the right range - dl_grants[0].cfi = SRSRAN_MAX(dl_grants[0].cfi, 1); - dl_grants[0].cfi = SRSRAN_MIN(dl_grants[0].cfi, 3); - // Get UL scheduling for the TX TTI from MAC if (stack->get_ul_sched(tti_tx_ul, ul_grants_tx) < 0) { Error("Getting UL scheduling from MAC"); - phy->worker_end(this, tx_buffer, tx_time); + phy->worker_end(context, true, tx_buffer); return; } @@ -239,17 +226,26 @@ void sf_worker::work_imp() // Process DL for (uint32_t cc = 0; cc < cc_workers.size(); cc++) { + // Select CFI and make sure it is in the right range dl_sf.cfi = dl_grants[cc].cfi; + dl_sf.cfi = SRSRAN_MAX(dl_sf.cfi, 1); + dl_sf.cfi = SRSRAN_MIN(dl_sf.cfi, 3); + cc_workers[cc]->work_dl(dl_sf, dl_grants[cc], ul_grants_tx[cc], &mbsfn_cfg); } // Save grants - phy->set_ul_grants(t_tx_ul, ul_grants_tx); - phy->set_ul_grants(t_rx, ul_grants); + phy->set_ul_grants(tti_tx_ul, ul_grants_tx); + + // Set or combine RF ports + for (uint32_t cc = 0; cc < phy->get_nof_carriers_lte(); cc++) { + for (uint32_t ant = 0; ant < phy->get_nof_ports(0); ant++) { + tx_buffer.set_combine(phy->get_rf_port(cc), ant, phy->get_nof_ports(0), cc_workers[cc]->get_buffer_tx(ant)); + } + } Debug("Sending to radio"); - tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB(phy->get_nof_prb(0))); - phy->worker_end(this, tx_buffer, tx_time); + phy->worker_end(context, true, tx_buffer); #ifdef DEBUG_WRITE_FILE fwrite(signal_buffer_tx, SRSRAN_SF_LEN_PRB(phy->cell.nof_prb) * sizeof(cf_t), 1, f); @@ -281,15 +277,19 @@ uint32_t sf_worker::get_metrics(std::vector& metrics) for (uint32_t r = 0; r < cnt; r++) { phy_metrics_t* m = &metrics[r]; phy_metrics_t* m_ = &metrics_[r]; - m->dl.mcs = SRSRAN_VEC_PMA(m->dl.mcs, m->dl.n_samples, m_->dl.mcs, m_->dl.n_samples); + m->dl.mcs = SRSRAN_VEC_SAFE_PMA(m->dl.mcs, m->dl.n_samples, m_->dl.mcs, m_->dl.n_samples); m->dl.n_samples += m_->dl.n_samples; - m->ul.n = SRSRAN_VEC_PMA(m->ul.n, m->ul.n_samples, m_->ul.n, m_->ul.n_samples); - m->ul.pusch_sinr = SRSRAN_VEC_PMA(m->ul.pusch_sinr, m->ul.n_samples, m_->ul.pusch_sinr, m_->ul.n_samples); + m->ul.n = SRSRAN_VEC_SAFE_PMA(m->ul.n, m->ul.n_samples, m_->ul.n, m_->ul.n_samples); + m->ul.pusch_sinr = SRSRAN_VEC_SAFE_PMA(m->ul.pusch_sinr, m->ul.n_samples, m_->ul.pusch_sinr, m_->ul.n_samples); m->ul.pucch_sinr = - SRSRAN_VEC_PMA(m->ul.pucch_sinr, m->ul.n_samples_pucch, m_->ul.pucch_sinr, m_->ul.n_samples_pucch); - m->ul.mcs = SRSRAN_VEC_PMA(m->ul.mcs, m->ul.n_samples, m_->ul.mcs, m_->ul.n_samples); - m->ul.rssi = SRSRAN_VEC_PMA(m->ul.rssi, m->ul.n_samples, m_->ul.rssi, m_->ul.n_samples); - m->ul.turbo_iters = SRSRAN_VEC_PMA(m->ul.turbo_iters, m->ul.n_samples, m_->ul.turbo_iters, m_->ul.n_samples); + SRSRAN_VEC_SAFE_PMA(m->ul.pucch_sinr, m->ul.n_samples_pucch, m_->ul.pucch_sinr, m_->ul.n_samples_pucch); + m->ul.mcs = SRSRAN_VEC_SAFE_PMA(m->ul.mcs, m->ul.n_samples, m_->ul.mcs, m_->ul.n_samples); + m->ul.pusch_rssi = SRSRAN_VEC_SAFE_PMA(m->ul.pusch_rssi, m->ul.n_samples, m_->ul.pusch_rssi, m_->ul.n_samples); + m->ul.pucch_rssi = + SRSRAN_VEC_SAFE_PMA(m->ul.pucch_rssi, m->ul.n_samples_pucch, m_->ul.pucch_rssi, m_->ul.n_samples_pucch); + m->ul.pucch_ni = + SRSRAN_VEC_SAFE_PMA(m->ul.pucch_ni, m->ul.n_samples_pucch, m_->ul.pucch_ni, m_->ul.n_samples_pucch); + m->ul.turbo_iters = SRSRAN_VEC_SAFE_PMA(m->ul.turbo_iters, m->ul.n_samples, m_->ul.turbo_iters, m_->ul.n_samples); m->ul.n_samples += m_->ul.n_samples; m->ul.n_samples_pucch += m_->ul.n_samples_pucch; } diff --git a/srsenb/src/phy/lte/worker_pool.cc b/srsenb/src/phy/lte/worker_pool.cc index cdcf1df1e..2d6f30574 100644 --- a/srsenb/src/phy/lte/worker_pool.cc +++ b/srsenb/src/phy/lte/worker_pool.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/src/phy/nr/cc_worker.cc b/srsenb/src/phy/nr/cc_worker.cc deleted file mode 100644 index b9b4e49fe..000000000 --- a/srsenb/src/phy/nr/cc_worker.cc +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsenb/hdr/phy/nr/cc_worker.h" -#include "srsran/srsran.h" - -namespace srsenb { -namespace nr { -cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, phy_nr_state* phy_state_) : - cc_idx(cc_idx_), phy_state(phy_state_), logger(log) -{ - cf_t* buffer_c[SRSRAN_MAX_PORTS] = {}; - - // Allocate buffers - buffer_sz = SRSRAN_SF_LEN_PRB(phy_state->args.dl.nof_max_prb); - for (uint32_t i = 0; i < phy_state_->args.dl.nof_tx_antennas; i++) { - tx_buffer[i] = srsran_vec_cf_malloc(buffer_sz); - rx_buffer[i] = srsran_vec_cf_malloc(buffer_sz); - buffer_c[i] = tx_buffer[i]; - } - - if (srsran_enb_dl_nr_init(&enb_dl, buffer_c, &phy_state_->args.dl)) { - ERROR("Error initiating UE DL NR"); - return; - } -} - -cc_worker::~cc_worker() -{ - srsran_enb_dl_nr_free(&enb_dl); - for (cf_t* p : rx_buffer) { - if (p != nullptr) { - free(p); - } - } - for (cf_t* p : tx_buffer) { - if (p != nullptr) { - free(p); - } - } -} - -bool cc_worker::set_carrier(const srsran_carrier_nr_t* carrier) -{ - if (srsran_enb_dl_nr_set_carrier(&enb_dl, carrier) < SRSRAN_SUCCESS) { - ERROR("Error setting carrier"); - return false; - } - - srsran_coreset_t coreset = {}; - coreset.freq_resources[0] = true; // Enable the bottom 6 PRB for PDCCH - coreset.duration = 2; - - srsran_dci_cfg_nr_t dci_cfg = phy_state->cfg.get_dci_cfg(); - if (srsran_enb_dl_nr_set_pdcch_config(&enb_dl, &phy_state->cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) { - ERROR("Error setting coreset"); - return false; - } - - return true; -} - -void cc_worker::set_tti(uint32_t tti) -{ - dl_slot_cfg.idx = TTI_ADD(tti, FDD_HARQ_DELAY_UL_MS); -} - -cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx) -{ - if (antenna_idx >= phy_state->args.dl.nof_tx_antennas) { - return nullptr; - } - - return tx_buffer.at(antenna_idx); -} - -cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx) -{ - if (antenna_idx >= phy_state->args.dl.nof_tx_antennas) { - return nullptr; - } - - return rx_buffer.at(antenna_idx); -} - -uint32_t cc_worker::get_buffer_len() -{ - return tx_buffer.size(); -} - -int cc_worker::encode_pdcch_dl(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants) -{ - for (uint32_t i = 0; i < nof_grants; i++) { - // Get PHY config for UE - // ... - - // Put actual DCI - if (srsran_enb_dl_nr_pdcch_put(&enb_dl, &dl_slot_cfg, &grants[i].dci) < SRSRAN_SUCCESS) { - ERROR("Error putting PDCCH"); - return SRSRAN_ERROR; - } - - if (logger.info.enabled()) { - logger.info("PDCCH: cc=%d, ...", cc_idx); - } - } - - return SRSRAN_SUCCESS; -} - -int cc_worker::encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants) -{ - for (uint32_t i = 0; i < nof_grants; i++) { - // Get PHY config for UE - // ... - srsran_sch_hl_cfg_nr_t pdsch_hl_cfg = {}; - srsran_sch_cfg_nr_t pdsch_cfg = {}; - - // Compute DL grant - if (srsran_ra_dl_dci_to_grant_nr( - &enb_dl.carrier, &dl_slot_cfg, &pdsch_hl_cfg, &grants[i].dci, &pdsch_cfg, &pdsch_cfg.grant) < - SRSRAN_SUCCESS) { - ERROR("Computing DL grant"); - return false; - } - - // Set soft buffer - for (uint32_t j = 0; j < SRSRAN_MAX_CODEWORDS; j++) { - pdsch_cfg.grant.tb[j].softbuffer.tx = grants[i].softbuffer_tx[j]; - } - - if (srsran_enb_dl_nr_pdsch_put(&enb_dl, &dl_slot_cfg, &pdsch_cfg, grants[i].data) < SRSRAN_SUCCESS) { - ERROR("Error putting PDSCH"); - return false; - } - - // Logging - if (logger.info.enabled()) { - char str[512]; - srsran_enb_dl_nr_pdsch_info(&enb_dl, &pdsch_cfg, str, sizeof(str)); - logger.info("PDSCH: cc=%d, %s", cc_idx, str); - } - } - - return SRSRAN_SUCCESS; -} - -bool cc_worker::work_dl(const srsran_slot_cfg_t& dl_sf_cfg, stack_interface_phy_nr::dl_sched_t& dl_grants) -{ - // Reset resource grid - if (srsran_enb_dl_nr_base_zero(&enb_dl) < SRSRAN_SUCCESS) { - ERROR("Error setting base to zero"); - return SRSRAN_ERROR; - } - - // Put DL grants to resource grid. PDSCH data will be encoded as well. - encode_pdcch_dl(dl_grants.pdsch, dl_grants.nof_grants); - encode_pdsch(dl_grants.pdsch, dl_grants.nof_grants); - - // Generate signal - srsran_enb_dl_nr_gen_signal(&enb_dl); - - return true; -} - -} // namespace nr -} // namespace srsenb diff --git a/srsenb/src/phy/nr/sf_worker.cc b/srsenb/src/phy/nr/sf_worker.cc deleted file mode 100644 index 4352868fb..000000000 --- a/srsenb/src/phy/nr/sf_worker.cc +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsenb/hdr/phy/nr/sf_worker.h" - -namespace srsenb { -namespace nr { -sf_worker::sf_worker(phy_common* phy_, phy_nr_state* phy_state_, srslog::basic_logger& logger) : - phy(phy_), phy_state(phy_state_), logger(logger) -{ - for (uint32_t i = 0; i < phy_state->args.nof_carriers; i++) { - cc_worker* w = new cc_worker(i, logger, phy_state); - cc_workers.push_back(std::unique_ptr(w)); - } - - if (srsran_softbuffer_tx_init_guru(&softbuffer_tx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < - SRSRAN_SUCCESS) { - ERROR("Error init soft-buffer"); - return; - } - data.resize(SRSRAN_SCH_NR_MAX_NOF_CB_LDPC * SRSRAN_LDPC_MAX_LEN_ENCODED_CB / 8); - srsran_vec_u8_zero(data.data(), SRSRAN_SCH_NR_MAX_NOF_CB_LDPC * SRSRAN_LDPC_MAX_LEN_ENCODED_CB / 8); - snprintf((char*)data.data(), SRSRAN_SCH_NR_MAX_NOF_CB_LDPC * SRSRAN_LDPC_MAX_LEN_ENCODED_CB / 8, "hello world!"); -} - -sf_worker::~sf_worker() -{ - srsran_softbuffer_tx_free(&softbuffer_tx); -} - -bool sf_worker::set_carrier_unlocked(uint32_t cc_idx, const srsran_carrier_nr_t* carrier_) -{ - if (cc_idx >= cc_workers.size()) { - return false; - } - - return cc_workers.at(cc_idx)->set_carrier(carrier_); -} - -cf_t* sf_worker::get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx) -{ - if (cc_idx >= cc_workers.size()) { - return nullptr; - } - - return cc_workers.at(cc_idx)->get_rx_buffer(antenna_idx); -} - -cf_t* sf_worker::get_buffer_tx(uint32_t cc_idx, uint32_t antenna_idx) -{ - if (cc_idx >= cc_workers.size()) { - return nullptr; - } - - return cc_workers.at(cc_idx)->get_tx_buffer(antenna_idx); -} - -uint32_t sf_worker::get_buffer_len() -{ - return cc_workers.at(0)->get_buffer_len(); -} - -void sf_worker::set_tti(uint32_t tti) -{ - logger.set_context(tti); - for (auto& w : cc_workers) { - w->set_tti(tti); - } -} - -void sf_worker::work_imp() -{ - // Get Transmission buffers - srsran::rf_buffer_t tx_buffer = {}; - srsran::rf_timestamp_t dummy_ts = {}; - for (uint32_t cc = 0; cc < phy->get_nof_carriers_nr(); cc++) { - for (uint32_t ant = 0; ant < phy->get_nof_ports(0); ant++) { - tx_buffer.set(cc, ant, phy->get_nof_ports(0), cc_workers[cc]->get_tx_buffer(ant)); - } - } - - // Configure user - phy_state->cfg.pdsch.rbg_size_cfg_1 = false; - - // Fill grant (this comes from the scheduler) - srsran_slot_cfg_t dl_cfg = {}; - stack_interface_phy_nr::dl_sched_t grants = {}; - - grants.nof_grants = 1; - grants.pdsch[0].data[0] = data.data(); - grants.pdsch[0].softbuffer_tx[0] = &softbuffer_tx; - srsran_softbuffer_tx_reset(&softbuffer_tx); - - grants.pdsch[0].dci.ctx.rnti = 0x1234; - grants.pdsch[0].dci.ctx.format = srsran_dci_format_nr_1_0; - - grants.pdsch[0].dci.freq_domain_assigment = 0x1FFF; - grants.pdsch[0].dci.time_domain_assigment = 0; - grants.pdsch[0].dci.mcs = 27; - - grants.pdsch[0].dci.ctx.ss_type = srsran_search_space_type_ue; - grants.pdsch[0].dci.ctx.coreset_id = 1; - grants.pdsch[0].dci.ctx.location.L = 0; - grants.pdsch[0].dci.ctx.location.ncce = 0; - - for (auto& w : cc_workers) { - w->work_dl(dl_cfg, grants); - } - - phy->worker_end(this, tx_buffer, dummy_ts, true); -} - -} // namespace nr -} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/phy/nr/slot_worker.cc b/srsenb/src/phy/nr/slot_worker.cc new file mode 100644 index 000000000..7938d74a9 --- /dev/null +++ b/srsenb/src/phy/nr/slot_worker.cc @@ -0,0 +1,480 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsenb/hdr/phy/nr/slot_worker.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" + +//#define DEBUG_WRITE_FILE + +#ifdef DEBUG_WRITE_FILE +FILE* f; +static uint32_t num_slots = 0; +static uint32_t slots_to_dump = 10; +#endif + +namespace srsenb { +namespace nr { +slot_worker::slot_worker(srsran::phy_common_interface& common_, + stack_interface_phy_nr& stack_, + sync_interface& sync_, + srslog::basic_logger& logger_) : + common(common_), stack(stack_), sync(sync_), logger(logger_) +{ + // Do nothing +} + +bool slot_worker::init(const args_t& args) +{ + std::lock_guard lock(mutex); + + // Calculate subframe length + sf_len = (uint32_t)(args.srate_hz / 1000.0); + + // Copy common configurations + cell_index = args.cell_index; + rf_port = args.rf_port; + + // Allocate Tx buffers + tx_buffer.resize(args.nof_tx_ports); + for (uint32_t i = 0; i < args.nof_tx_ports; i++) { + tx_buffer[i] = srsran_vec_cf_malloc(sf_len); + if (tx_buffer[i] == nullptr) { + logger.error("Error allocating Tx buffer"); + return false; + } + } + + // Allocate Rx buffers + rx_buffer.resize(args.nof_rx_ports); + for (uint32_t i = 0; i < args.nof_rx_ports; i++) { + rx_buffer[i] = srsran_vec_cf_malloc(sf_len); + if (rx_buffer[i] == nullptr) { + logger.error("Error allocating Rx buffer"); + return false; + } + } + + // Prepare DL arguments + srsran_gnb_dl_args_t dl_args = {}; + dl_args.pdsch.measure_time = true; + dl_args.pdsch.max_layers = args.nof_tx_ports; + dl_args.pdsch.max_prb = args.nof_max_prb; + dl_args.nof_tx_antennas = args.nof_tx_ports; + dl_args.nof_max_prb = args.nof_max_prb; + dl_args.srate_hz = args.srate_hz; + + // Initialise DL + if (srsran_gnb_dl_init(&gnb_dl, tx_buffer.data(), &dl_args) < SRSRAN_SUCCESS) { + logger.error("Error gNb DL init"); + return false; + } + + // Prepare UL arguments + srsran_gnb_ul_args_t ul_args = {}; + ul_args.pusch.measure_time = true; + ul_args.pusch.measure_evm = true; + ul_args.pusch.max_layers = args.nof_rx_ports; + ul_args.pusch.sch.max_nof_iter = args.pusch_max_its; + ul_args.pusch.max_prb = args.nof_max_prb; + ul_args.nof_max_prb = args.nof_max_prb; + ul_args.pusch_min_snr_dB = args.pusch_min_snr_dB; + + // Initialise UL + if (srsran_gnb_ul_init(&gnb_ul, rx_buffer[0], &ul_args) < SRSRAN_SUCCESS) { + logger.error("Error gNb DL init"); + return false; + } + +#ifdef DEBUG_WRITE_FILE + const char* filename = "nr_baseband.dat"; + printf("Opening %s to dump baseband\n", filename); + f = fopen(filename, "w"); +#endif + + return true; +} + +slot_worker::~slot_worker() +{ + for (auto& b : tx_buffer) { + if (b) { + free(b); + b = nullptr; + } + } + for (auto& b : rx_buffer) { + if (b) { + free(b); + b = nullptr; + } + } + srsran_gnb_dl_free(&gnb_dl); + srsran_gnb_ul_free(&gnb_ul); +} + +cf_t* slot_worker::get_buffer_rx(uint32_t antenna_idx) +{ + std::lock_guard lock(mutex); + if (antenna_idx >= (uint32_t)rx_buffer.size()) { + return nullptr; + } + + return rx_buffer[antenna_idx]; +} + +cf_t* slot_worker::get_buffer_tx(uint32_t antenna_idx) +{ + std::lock_guard lock(mutex); + if (antenna_idx >= (uint32_t)tx_buffer.size()) { + return nullptr; + } + + return tx_buffer[antenna_idx]; +} + +uint32_t slot_worker::get_buffer_len() +{ + return sf_len; +} + +void slot_worker::set_context(const srsran::phy_common_interface::worker_context_t& w_ctx) +{ + logger.set_context(w_ctx.sf_idx); + ul_slot_cfg.idx = w_ctx.sf_idx; + dl_slot_cfg.idx = TTI_ADD(w_ctx.sf_idx, FDD_HARQ_DELAY_UL_MS); + context.copy(w_ctx); +} + +bool slot_worker::work_ul() +{ + stack_interface_phy_nr::ul_sched_t* ul_sched = stack.get_ul_sched(ul_slot_cfg); + if (ul_sched == nullptr) { + logger.error("Error retrieving UL scheduling"); + return false; + } + + if (ul_sched->pucch.empty() && ul_sched->pusch.empty()) { + // early exit if nothing has been scheduled + return true; + } + + // Demodulate + if (srsran_gnb_ul_fft(&gnb_ul) < SRSRAN_SUCCESS) { + logger.error("Error in demodulation"); + return false; + } + + // For each PUCCH... + for (stack_interface_phy_nr::pucch_t& pucch : ul_sched->pucch) { + srsran::bounded_vector + pucch_info(pucch.candidates.size()); + + // For each candidate decode PUCCH + for (uint32_t i = 0; i < (uint32_t)pucch.candidates.size(); i++) { + pucch_info[i].uci_data.cfg = pucch.candidates[i].uci_cfg; + + // Decode PUCCH + if (srsran_gnb_ul_get_pucch(&gnb_ul, + &ul_slot_cfg, + &pucch.pucch_cfg, + &pucch.candidates[i].resource, + &pucch_info[i].uci_data.cfg, + &pucch_info[i].uci_data.value, + &pucch_info[i].csi) < SRSRAN_SUCCESS) { + logger.error("Error getting PUCCH"); + return false; + } + } + + // Find most suitable PUCCH candidate + uint32_t best_candidate = 0; + for (uint32_t i = 1; i < (uint32_t)pucch_info.size(); i++) { + // Select candidate if exceeds the previous best candidate SNR + if (pucch_info[i].csi.snr_dB > pucch_info[best_candidate].csi.snr_dB) { + best_candidate = i; + } + } + + // Inform stack + if (stack.pucch_info(ul_slot_cfg, pucch_info[best_candidate]) < SRSRAN_SUCCESS) { + logger.error("Error pushing PUCCH information to stack"); + return false; + } + + // Log PUCCH decoding + if (logger.info.enabled()) { + std::array str; + srsran_gnb_ul_pucch_info(&gnb_ul, + &pucch.candidates[0].resource, + &pucch_info[best_candidate].uci_data, + &pucch_info[best_candidate].csi, + str.data(), + (uint32_t)str.size()); + + logger.info("PUCCH: %s", str.data()); + } + } + + // For each PUSCH... + for (stack_interface_phy_nr::pusch_t& pusch : ul_sched->pusch) { + // Prepare PUSCH + stack_interface_phy_nr::pusch_info_t pusch_info = {}; + pusch_info.uci_cfg = pusch.sch.uci; + pusch_info.pid = pusch.pid; + pusch_info.rnti = pusch.sch.grant.rnti; + pusch_info.pdu = srsran::make_byte_buffer(); + if (pusch_info.pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } + pusch_info.pdu->N_bytes = pusch.sch.grant.tb[0].tbs / 8; + pusch_info.pusch_data.tb[0].payload = pusch_info.pdu->data(); + + // Decode PUSCH + if (srsran_gnb_ul_get_pusch(&gnb_ul, &ul_slot_cfg, &pusch.sch, &pusch.sch.grant, &pusch_info.pusch_data) < + SRSRAN_SUCCESS) { + logger.error("Error getting PUSCH"); + return false; + } + + // Extract DMRS information + pusch_info.csi = gnb_ul.dmrs.csi; + + // Inform stack + if (stack.pusch_info(ul_slot_cfg, pusch_info) < SRSRAN_SUCCESS) { + logger.error("Error pushing PUSCH information to stack"); + return false; + } + + // Log PUSCH decoding + if (logger.info.enabled()) { + std::array str; + srsran_gnb_ul_pusch_info(&gnb_ul, &pusch.sch, &pusch_info.pusch_data, str.data(), (uint32_t)str.size()); + + if (logger.debug.enabled()) { + std::array str_extra = {}; + srsran_sch_cfg_nr_info(&pusch.sch, str_extra.data(), (uint32_t)str_extra.size()); + logger.info("PUSCH: %s\n%s", str.data(), str_extra.data()); + } else { + logger.info("PUSCH: %s", str.data()); + } + } + } + + return true; +} + +bool slot_worker::work_dl() +{ + // The Scheduler interface needs to be called synchronously, wait for the sync to be available + sync.wait(this); + + // Retrieve Scheduling for the current processing DL slot + const stack_interface_phy_nr::dl_sched_t* dl_sched_ptr = stack.get_dl_sched(dl_slot_cfg); + + // Releases synchronization lock and allow next worker to retrieve scheduling results + sync.release(); + + // Abort DL processing if the scheduling returned an invalid pointer + if (dl_sched_ptr == nullptr) { + return false; + } + + if (srsran_gnb_dl_base_zero(&gnb_dl) < SRSRAN_SUCCESS) { + logger.error("Error zeroing RE grid"); + return false; + } + + // Encode PDCCH for DL transmissions + for (const stack_interface_phy_nr::pdcch_dl_t& pdcch : dl_sched_ptr->pdcch_dl) { + // Set PDCCH configuration, including DCI dedicated + if (srsran_gnb_dl_set_pdcch_config(&gnb_dl, &pdcch_cfg, &pdcch.dci_cfg) < SRSRAN_SUCCESS) { + logger.error("PDCCH: Error setting DL configuration"); + return false; + } + + // Put PDCCH message + if (srsran_gnb_dl_pdcch_put_dl(&gnb_dl, &dl_slot_cfg, &pdcch.dci) < SRSRAN_SUCCESS) { + logger.error("PDCCH: Error putting DL message"); + return false; + } + + // Log PDCCH information + if (logger.info.enabled()) { + std::array str = {}; + srsran_gnb_dl_pdcch_dl_info(&gnb_dl, &pdcch.dci, str.data(), (uint32_t)str.size()); + logger.info("PDCCH: cc=%d %s tti_tx=%d", cell_index, str.data(), dl_slot_cfg.idx); + } + } + + // Encode PDCCH for UL transmissions + for (const stack_interface_phy_nr::pdcch_ul_t& pdcch : dl_sched_ptr->pdcch_ul) { + // Set PDCCH configuration, including DCI dedicated + if (srsran_gnb_dl_set_pdcch_config(&gnb_dl, &pdcch_cfg, &pdcch.dci_cfg) < SRSRAN_SUCCESS) { + logger.error("PDCCH: Error setting DL configuration"); + return false; + } + + // Put PDCCH message + if (srsran_gnb_dl_pdcch_put_ul(&gnb_dl, &dl_slot_cfg, &pdcch.dci) < SRSRAN_SUCCESS) { + logger.error("PDCCH: Error putting DL message"); + return false; + } + + // Log PDCCH information + if (logger.info.enabled()) { + std::array str = {}; + srsran_gnb_dl_pdcch_ul_info(&gnb_dl, &pdcch.dci, str.data(), (uint32_t)str.size()); + logger.info("PDCCH: cc=%d %s tti_tx=%d", cell_index, str.data(), dl_slot_cfg.idx); + } + } + + // Encode PDSCH + for (const stack_interface_phy_nr::pdsch_t& pdsch : dl_sched_ptr->pdsch) { + // convert MAC to PHY buffer data structures + uint8_t* data[SRSRAN_MAX_TB] = {}; + for (uint32_t i = 0; i < SRSRAN_MAX_TB; ++i) { + if (pdsch.data[i] != nullptr) { + data[i] = pdsch.data[i]->msg; + } + } + + // Put PDSCH message + if (srsran_gnb_dl_pdsch_put(&gnb_dl, &dl_slot_cfg, &pdsch.sch, data) < SRSRAN_SUCCESS) { + logger.error("PDSCH: Error putting DL message"); + return false; + } + + // Log PDSCH information + if (logger.info.enabled()) { + std::array str = {}; + srsran_gnb_dl_pdsch_info(&gnb_dl, &pdsch.sch, str.data(), (uint32_t)str.size()); + + if (logger.debug.enabled()) { + std::array str_extra = {}; + srsran_sch_cfg_nr_info(&pdsch.sch, str_extra.data(), (uint32_t)str_extra.size()); + logger.info("PDSCH: cc=%d %s tti_tx=%d\n%s", cell_index, str.data(), dl_slot_cfg.idx, str_extra.data()); + } else { + logger.info("PDSCH: cc=%d %s tti_tx=%d", cell_index, str.data(), dl_slot_cfg.idx); + } + } + } + + // Put NZP-CSI-RS + for (const srsran_csi_rs_nzp_resource_t& nzp_csi_rs : dl_sched_ptr->nzp_csi_rs) { + if (srsran_gnb_dl_nzp_csi_rs_put(&gnb_dl, &dl_slot_cfg, &nzp_csi_rs) < SRSRAN_SUCCESS) { + logger.error("NZP-CSI-RS: Error putting signal"); + return false; + } + } + + // Generate baseband signal + srsran_gnb_dl_gen_signal(&gnb_dl); + + // Add SSB to the baseband signal + for (const stack_interface_phy_nr::ssb_t& ssb : dl_sched_ptr->ssb) { + if (srsran_gnb_dl_add_ssb(&gnb_dl, &ssb.pbch_msg, dl_slot_cfg.idx) < SRSRAN_SUCCESS) { + logger.error("SSB: Error putting signal"); + return false; + } + } + + return true; +} + +void slot_worker::work_imp() +{ + // Inform Scheduler about new slot + stack.slot_indication(dl_slot_cfg); + + // Get Transmission buffers + uint32_t nof_ant = (uint32_t)tx_buffer.size(); + srsran::rf_buffer_t tx_rf_buffer = {}; + tx_rf_buffer.set_nof_samples(sf_len); + for (uint32_t a = 0; a < nof_ant; a++) { + tx_rf_buffer.set(rf_port, a, nof_ant, tx_buffer[a]); + } + + // Process uplink + if (not work_ul()) { + // Wait and release synchronization + sync.wait(this); + sync.release(); + common.worker_end(context, false, tx_rf_buffer); + return; + } + + // Process downlink + if (not work_dl()) { + common.worker_end(context, false, tx_rf_buffer); + return; + } + + common.worker_end(context, true, tx_rf_buffer); + +#ifdef DEBUG_WRITE_FILE + if (num_slots++ < slots_to_dump) { + printf("Writing slot %d\n", dl_slot_cfg.idx); + fwrite(tx_rf_buffer.get(0), tx_rf_buffer.get_nof_samples() * sizeof(cf_t), 1, f); + } else if (num_slots == slots_to_dump) { + printf("Baseband signaled dump finished. Please close app.\n"); + fclose(f); + } +#endif +} + +bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, + const srsran_pdcch_cfg_nr_t& pdcch_cfg_, + const srsran_ssb_cfg_t& ssb_cfg_) +{ + std::lock_guard lock(mutex); + // Set gNb DL carrier + if (srsran_gnb_dl_set_carrier(&gnb_dl, &carrier) < SRSRAN_SUCCESS) { + logger.error("Error setting DL carrier"); + return false; + } + + // Configure SSB + if (srsran_gnb_dl_set_ssb_config(&gnb_dl, &ssb_cfg_) < SRSRAN_SUCCESS) { + logger.error("Error setting SSB"); + return false; + } + + // Set gNb UL carrier + if (srsran_gnb_ul_set_carrier(&gnb_ul, &carrier) < SRSRAN_SUCCESS) { + logger.error("Error setting UL carrier (pci=%d, nof_prb=%d, max_mimo_layers=%d)", + carrier.pci, + carrier.nof_prb, + carrier.max_mimo_layers); + return false; + } + + pdcch_cfg = pdcch_cfg_; + + // Update subframe length + sf_len = SRSRAN_SF_LEN_PRB_NR(carrier.nof_prb); + + return true; +} + +} // namespace nr +} // namespace srsenb diff --git a/srsenb/src/phy/nr/worker_pool.cc b/srsenb/src/phy/nr/worker_pool.cc index befe9801f..56d43507d 100644 --- a/srsenb/src/phy/nr/worker_pool.cc +++ b/srsenb/src/phy/nr/worker_pool.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,51 +19,161 @@ * */ #include "srsenb/hdr/phy/nr/worker_pool.h" +#include "srsran/common/band_helper.h" namespace srsenb { namespace nr { -worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers) {} - -bool worker_pool::init(const phy_args_t& args, phy_common* common, srslog::sink& log_sink, int prio) +worker_pool::worker_pool(srsran::phy_common_interface& common_, + stack_interface_phy_nr& stack_, + srslog::sink& log_sink_, + uint32_t max_workers) : + pool(max_workers, "NR-"), + common(common_), + stack(stack_), + log_sink(log_sink_), + logger(srslog::fetch_basic_logger("PHY-NR", log_sink)), + prach_stack_adaptor(stack_) { - // Add workers to workers pool and start threads + // Do nothing +} + +bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_list) +{ + nof_prach_workers = args.nof_prach_workers; + + // Calculate sampling rate in Hz + if (not std::isnormal(args.srate_hz)) { + srate_hz = SRSRAN_SUBC_SPACING_NR(cell_list[0].carrier.scs) * srsran_min_symbol_sz_rb(cell_list[0].carrier.nof_prb); + } else { + srate_hz = args.srate_hz; + } + + // Configure logger srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level); + logger.set_level(log_level); + + // Add workers to workers pool and start threads for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - auto& log = srslog::fetch_basic_logger(fmt::format("PHY{}-NR", i), log_sink); + auto& log = srslog::fetch_basic_logger(fmt::format("{}PHY{}-NR", args.log.id_preamble, i), log_sink); log.set_level(log_level); log.set_hex_dump_max_size(args.log.phy_hex_limit); - auto w = new sf_worker(common, &phy_state, log); - pool.init_worker(i, w, prio); - workers.push_back(std::unique_ptr(w)); + auto w = new slot_worker(common, stack, *this, log); + pool.init_worker(i, w, args.prio); + workers.push_back(std::unique_ptr(w)); - srsran_carrier_nr_t c = common->get_cell_nr(0); - w->set_carrier_unlocked(0, &c); + slot_worker::args_t w_args = {}; + uint32_t cell_index = 0; + w_args.cell_index = cell_index; + w_args.nof_max_prb = cell_list[cell_index].carrier.nof_prb; + w_args.nof_tx_ports = cell_list[cell_index].carrier.max_mimo_layers; + w_args.nof_rx_ports = cell_list[cell_index].carrier.max_mimo_layers; + w_args.rf_port = cell_list[cell_index].rf_port; + w_args.srate_hz = srate_hz; + w_args.pusch_max_its = args.pusch_max_its; + w_args.pusch_min_snr_dB = args.pusch_min_snr_dB; + + if (not w->init(w_args)) { + return false; + } } return true; } -void worker_pool::start_worker(sf_worker* w) +void worker_pool::start_worker(slot_worker* w) { + // Push worker into synchronization queue + slot_sync.push(w); + + // Feed PRACH detection before start processing + prach.new_tti(0, current_tti, w->get_buffer_rx(0)); + + // Start actual worker pool.start_worker(w); } -sf_worker* worker_pool::wait_worker(uint32_t tti) +slot_worker* worker_pool::wait_worker(uint32_t tti) { - return (sf_worker*)pool.wait_worker(tti); + slot_worker* w = (slot_worker*)pool.wait_worker(tti); + + // Save current TTI + current_tti = tti; + + // Return worker + return w; } -sf_worker* worker_pool::wait_worker_id(uint32_t id) +slot_worker* worker_pool::wait_worker_id(uint32_t id) { - return (sf_worker*)pool.wait_worker_id(id); + return (slot_worker*)pool.wait_worker_id(id); } void worker_pool::stop() { pool.stop(); + prach.stop(); +} + +int worker_pool::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg) +{ + // Best effort to convert NR carrier into LTE cell + srsran_cell_t cell = {}; + int ret = srsran_carrier_to_cell(&common_cfg.carrier, &cell); + if (ret < SRSRAN_SUCCESS) { + logger.error("Converting carrier to cell for PRACH (%d)", ret); + return SRSRAN_ERROR; + } + + // Best effort to set up NR-PRACH config reused for NR + srsran_prach_cfg_t prach_cfg = common_cfg.prach; + uint32_t lte_nr_prach_offset = (common_cfg.carrier.nof_prb - cell.nof_prb) / 2; + if (prach_cfg.freq_offset < lte_nr_prach_offset) { + logger.error("prach_cfg.freq_offset=%d is not compatible with LTE", prach_cfg.freq_offset); + return SRSRAN_ERROR; + } + prach_cfg.freq_offset -= lte_nr_prach_offset; + prach_cfg.is_nr = true; + prach_cfg.tdd_config.configured = (common_cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD); + + // Set the PRACH configuration + prach.init(0, cell, prach_cfg, &prach_stack_adaptor, logger, 0, nof_prach_workers); + prach.set_max_prach_offset_us(1000); + + // Setup SSB sampling rate and scaling + srsran_ssb_cfg_t ssb_cfg = common_cfg.ssb; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.scaling = + srsran_convert_dB_to_amplitude(srsran_gnb_dl_get_maximum_signal_power_dBfs(common_cfg.carrier.nof_prb)); + + // Print SSB configuration, helps debugging gNb and UE + if (logger.info.enabled()) { + std::array ssb_cfg_str = {}; + srsran_ssb_cfg_to_str(&ssb_cfg, ssb_cfg_str.data(), (uint32_t)ssb_cfg_str.size()); + logger.info("Setting SSB configuration %s", ssb_cfg_str.data()); + } + + // For each worker set configuration + for (uint32_t i = 0; i < pool.get_nof_workers(); i++) { + // Reserve worker from pool + slot_worker* w = (slot_worker*)pool.wait_worker_id(i); + if (w == nullptr) { + // Skip worker if invalid pointer + continue; + } + + // Setup worker common configuration + if (not w->set_common_cfg(common_cfg.carrier, common_cfg.pdcch, ssb_cfg)) { + return SRSRAN_ERROR; + } + + // Release worker + w->release(); + } + + return SRSRAN_SUCCESS; } } // namespace nr -} // namespace srsenb \ No newline at end of file +} // namespace srsenb diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index 4a825b554..cc52b78ef 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,17 +19,17 @@ * */ +#include "srsenb/hdr/phy/phy.h" +#include "srsran/common/band_helper.h" +#include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/threads.h" #include #include #include #include #include -#include #include -#include "srsenb/hdr/phy/phy.h" -#include "srsran/common/threads.h" - #define Error(fmt, ...) \ if (SRSRAN_DEBUG_ENABLED) \ phy_log.error(fmt, ##__VA_ARGS__) @@ -76,7 +76,6 @@ phy::phy(srslog::sink& log_sink) : phy_log(srslog::fetch_basic_logger("PHY", log_sink)), phy_lib_log(srslog::fetch_basic_logger("PHY_LIB", log_sink)), lte_workers(MAX_WORKERS), - nr_workers(MAX_WORKERS), workers_common(), nof_workers(0), tx_rx(phy_log) @@ -95,7 +94,7 @@ void phy::parse_common_config(const phy_cfg_t& cfg) prach_cfg.root_seq_idx = cfg.prach_cnfg.root_seq_idx; prach_cfg.zero_corr_zone = cfg.prach_cnfg.prach_cfg_info.zero_correlation_zone_cfg; prach_cfg.freq_offset = cfg.prach_cnfg.prach_cfg_info.prach_freq_offset; - prach_cfg.num_ra_preambles = cfg.phy_cell_cfg.at(0).num_ra_preambles; + prach_cfg.num_ra_preambles = cfg.phy_cell_cfg.empty() ? 0 : cfg.phy_cell_cfg.at(0).num_ra_preambles; // DMRS workers_common.dmrs_pusch_cfg.cyclic_shift = cfg.pusch_cnfg.ul_ref_sigs_pusch.cyclic_shift; workers_common.dmrs_pusch_cfg.delta_ss = cfg.pusch_cnfg.ul_ref_sigs_pusch.group_assign_pusch; @@ -106,7 +105,48 @@ void phy::parse_common_config(const phy_cfg_t& cfg) int phy::init(const phy_args_t& args, const phy_cfg_t& cfg, srsran::radio_interface_phy* radio_, - stack_interface_phy_lte* stack_) + stack_interface_phy_lte* stack_lte_, + stack_interface_phy_nr& stack_nr_, + enb_time_interface* enb_) +{ + if (init_lte(args, cfg, radio_, stack_lte_, enb_) != SRSRAN_SUCCESS) { + phy_log.error("Couldn't initialize LTE PHY"); + return SRSRAN_ERROR; + } + + if (init_nr(args, cfg, stack_nr_) != SRSRAN_SUCCESS) { + phy_log.error("Couldn't initialize NR PHY"); + return SRSRAN_ERROR; + } + + tx_rx.init(enb_, radio, <e_workers, &workers_common, &prach, SF_RECV_THREAD_PRIO); + initialized = true; + + return SRSRAN_SUCCESS; +} + +int phy::init(const phy_args_t& args, + const phy_cfg_t& cfg, + srsran::radio_interface_phy* radio_, + stack_interface_phy_lte* stack_lte_, + enb_time_interface* enb_) +{ + if (init_lte(args, cfg, radio_, stack_lte_, enb_) != SRSRAN_SUCCESS) { + phy_log.error("Couldn't initialize LTE PHY"); + return SRSRAN_ERROR; + } + + tx_rx.init(enb_, radio, <e_workers, &workers_common, &prach, SF_RECV_THREAD_PRIO); + initialized = true; + + return SRSRAN_SUCCESS; +} + +int phy::init_lte(const phy_args_t& args, + const phy_cfg_t& cfg, + srsran::radio_interface_phy* radio_, + stack_interface_phy_lte* stack_lte_, + enb_time_interface* enb_) { if (cfg.phy_cell_cfg.size() > SRSRAN_MAX_CARRIERS) { phy_log.error( @@ -114,12 +154,11 @@ int phy::init(const phy_args_t& args, return SRSRAN_ERROR; } - mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE); - // Add PHY lib log. - srslog::basic_levels log_lvl = srslog::str_to_basic_level(args.log.phy_lib_level); + srslog::basic_levels lib_log_lvl = srslog::str_to_basic_level(args.log.phy_lib_level); + srslog::basic_levels log_lvl = srslog::str_to_basic_level(args.log.phy_level); - phy_lib_log.set_level(log_lvl); + phy_lib_log.set_level(lib_log_lvl); phy_lib_log.set_hex_dump_max_size(args.log.phy_hex_limit); if (log_lvl != srslog::basic_levels::none) { srsran_phy_log_register_handler(this, srsran_phy_handler); @@ -130,11 +169,14 @@ int phy::init(const phy_args_t& args, phy_log.set_hex_dump_max_size(args.log.phy_hex_limit); radio = radio_; - nof_workers = args.nof_phy_threads; + nof_workers = cfg.phy_cell_cfg.empty() ? 0 : args.nof_phy_threads; workers_common.params = args; - workers_common.init(cfg.phy_cell_cfg, cfg.phy_cell_cfg_nr, radio, stack_); + workers_common.init(cfg.phy_cell_cfg, cfg.phy_cell_cfg_nr, radio, stack_lte_); + if (cfg.cfr_config.cfr_enable) { + workers_common.set_cfr_config(cfg.cfr_config); + } parse_common_config(cfg); @@ -142,23 +184,20 @@ int phy::init(const phy_args_t& args, if (not cfg.phy_cell_cfg.empty()) { lte_workers.init(args, &workers_common, log_sink, WORKERS_THREAD_PRIO); } - if (not cfg.phy_cell_cfg_nr.empty()) { - nr_workers.init(args, &workers_common, log_sink, WORKERS_THREAD_PRIO); - } // For each carrier, initialise PRACH worker for (uint32_t cc = 0; cc < cfg.phy_cell_cfg.size(); cc++) { prach_cfg.root_seq_idx = cfg.phy_cell_cfg[cc].root_seq_idx; - prach.init( - cc, cfg.phy_cell_cfg[cc].cell, prach_cfg, stack_, phy_log, PRACH_WORKER_THREAD_PRIO, args.nof_prach_threads); + prach.init(cc, + cfg.phy_cell_cfg[cc].cell, + prach_cfg, + stack_lte_, + phy_log, + PRACH_WORKER_THREAD_PRIO, + args.nof_prach_threads); } prach.set_max_prach_offset_us(args.max_prach_offset_us); - // Warning this must be initialized after all workers have been added to the pool - tx_rx.init(stack_, radio, <e_workers, &nr_workers, &workers_common, &prach, SF_RECV_THREAD_PRIO); - - initialized = true; - return SRSRAN_SUCCESS; } @@ -168,7 +207,9 @@ void phy::stop() tx_rx.stop(); workers_common.stop(); lte_workers.stop(); - nr_workers.stop(); + if (nr_workers != nullptr) { + nr_workers->stop(); + } prach.stop(); initialized = false; @@ -220,20 +261,28 @@ void phy::get_metrics(std::vector& metrics) metrics[j].ul.n_samples_pucch += metrics_tmp[j].ul.n_samples_pucch; metrics[j].ul.mcs += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.mcs; metrics[j].ul.n += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.n; - metrics[j].ul.rssi += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.rssi; + metrics[j].ul.pusch_rssi += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.pusch_rssi; metrics[j].ul.pusch_sinr += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.pusch_sinr; + metrics[j].ul.pucch_rssi += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_rssi; + metrics[j].ul.pucch_ni += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_ni; metrics[j].ul.pucch_sinr += metrics_tmp[j].ul.n_samples_pucch * metrics_tmp[j].ul.pucch_sinr; metrics[j].ul.turbo_iters += metrics_tmp[j].ul.n_samples * metrics_tmp[j].ul.turbo_iters; } } for (uint32_t j = 0; j < metrics.size(); j++) { - metrics[j].dl.mcs /= metrics[j].dl.n_samples; - metrics[j].ul.mcs /= metrics[j].ul.n_samples; - metrics[j].ul.n /= metrics[j].ul.n_samples; - metrics[j].ul.rssi /= metrics[j].ul.n_samples; - metrics[j].ul.pusch_sinr /= metrics[j].ul.n_samples; - metrics[j].ul.pucch_sinr /= metrics[j].ul.n_samples_pucch; - metrics[j].ul.turbo_iters /= metrics[j].ul.n_samples; + if (metrics[j].dl.n_samples > 0) { + metrics[j].dl.mcs /= metrics[j].dl.n_samples; + } + if (metrics[j].ul.n_samples > 0) { + metrics[j].ul.mcs /= metrics[j].ul.n_samples; + metrics[j].ul.n /= metrics[j].ul.n_samples; + metrics[j].ul.pusch_rssi /= metrics[j].ul.n_samples; + metrics[j].ul.pusch_sinr /= metrics[j].ul.n_samples; + metrics[j].ul.pucch_rssi /= metrics[j].ul.n_samples_pucch; + metrics[j].ul.pucch_ni /= metrics[j].ul.n_samples_pucch; + metrics[j].ul.pucch_sinr /= metrics[j].ul.n_samples_pucch; + metrics[j].ul.turbo_iters /= metrics[j].ul.n_samples; + } } } @@ -243,6 +292,11 @@ void phy::cmd_cell_gain(uint32_t cell_id, float gain_db) workers_common.set_cell_gain(cell_id, gain_db); } +void phy::cmd_cell_measure() +{ + workers_common.set_cell_measure_trigger(); +} + /***** RRC->PHY interface **********/ void phy::set_config(uint16_t rnti, const phy_rrc_cfg_list_t& phy_cfg_list) @@ -304,7 +358,48 @@ void phy::configure_mbsfn(srsran::sib2_mbms_t* sib2, srsran::sib13_t* sib13, con // Start GUI void phy::start_plot() { - lte_workers[0]->start_plot(); + if (lte_workers.get_nof_workers() > 0) { + lte_workers[0]->start_plot(); + } +} + +int phy::init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_phy_nr& stack) +{ + if (cfg.phy_cell_cfg_nr.empty()) { + return SRSRAN_SUCCESS; + } + + nr_workers = std::unique_ptr(new nr::worker_pool(workers_common, stack, log_sink, MAX_WORKERS)); + + nr::worker_pool::args_t worker_args = {}; + worker_args.nof_phy_threads = args.nof_phy_threads; + worker_args.log.phy_level = args.log.phy_level; + worker_args.log.phy_hex_limit = args.log.phy_hex_limit; + worker_args.pusch_max_its = args.nr_pusch_max_its; + + if (not nr_workers->init(worker_args, cfg.phy_cell_cfg_nr)) { + return SRSRAN_ERROR; + } + + tx_rx.set_nr_workers(nr_workers.get()); + + if (nr_workers->set_common_cfg(common_cfg)) { + phy_log.error("Couldn't set common PHY config"); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int phy::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg_) +{ + if (nr_workers.get() == nullptr) { + // if nr_workers are not initialized yet, store the configuration in the phy + common_cfg = common_cfg_; + return SRSRAN_SUCCESS; + } + + return nr_workers->set_common_cfg(common_cfg); } } // namespace srsenb diff --git a/srsenb/src/phy/phy_common.cc b/srsenb/src/phy/phy_common.cc index 4f6c471ea..6282e13ef 100644 --- a/srsenb/src/phy/phy_common.cc +++ b/srsenb/src/phy/phy_common.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -50,15 +50,14 @@ bool phy_common::init(const phy_cell_cfg_list_t& cell_list_, cell_list_lte = cell_list_; cell_list_nr = cell_list_nr_; - pthread_mutex_init(&mtch_mutex, nullptr); - pthread_cond_init(&mtch_cvar, nullptr); // Instantiate DL channel emulator if (params.dl_channel_args.enable) { + int channel_prbs = (cell_list_lte.empty()) ? cell_list_nr[0].carrier.nof_prb : cell_list_lte[0].cell.nof_prb; dl_channel = srsran::channel_ptr( new srsran::channel(params.dl_channel_args, get_nof_rf_channels(), srslog::fetch_basic_logger("PHY"))); - dl_channel->set_srate((uint32_t)srsran_sampling_freq_hz(cell_list_lte[0].cell.nof_prb)); - dl_channel->set_signal_power_dBfs(srsran_enb_dl_get_maximum_signal_power_dBfs(cell_list_lte[0].cell.nof_prb)); + dl_channel->set_srate((uint32_t)srsran_sampling_freq_hz(channel_prbs)); + dl_channel->set_signal_power_dBfs(srsran_enb_dl_get_maximum_signal_power_dBfs(channel_prbs)); } // Create grants @@ -67,7 +66,13 @@ bool phy_common::init(const phy_cell_cfg_list_t& cell_list_, } // Set UE PHY data-base stack and configuration - ue_db.init(stack, params, cell_list_lte); + if (!cell_list_lte.empty()) { + ue_db.init(stack, params, cell_list_lte); + } + if (mcch_configured) { + build_mch_table(); + build_mcch_table(); + } reset(); return true; @@ -94,7 +99,7 @@ void phy_common::clear_grants(uint16_t rnti) } } -const stack_interface_phy_lte::ul_sched_list_t& phy_common::get_ul_grants(uint32_t tti) +const stack_interface_phy_lte::ul_sched_list_t phy_common::get_ul_grants(uint32_t tti) { std::lock_guard lock(grant_mutex); return ul_grants[tti]; @@ -113,38 +118,50 @@ void phy_common::set_ul_grants(uint32_t tti, const stack_interface_phy_lte::ul_s * Each worker uses this function to indicate that all processing is done and data is ready for transmission or * there is no transmission at all (tx_enable). In that case, the end of burst message will be sent to the radio */ -void phy_common::worker_end(void* tx_sem_id, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr) +void phy_common::worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) { // Wait for the green light to transmit in the current TTI - semaphore.wait(tx_sem_id); + semaphore.wait(w_ctx.worker_ptr); - // If this is for NR, save Tx buffers... - if (is_nr) { - nr_tx_buffer = buffer; - nr_tx_buffer_ready = true; + // For combine buffer with previous buffers + if (tx_enable) { + tx_buffer.set_nof_samples(buffer.get_nof_samples()); + tx_buffer.set_combine(buffer); + } + + // If the current worker is not the last one, skip transmission + if (not w_ctx.last) { + if (tx_enable) { + reset_last_worker(); + } + + // Release semaphore and let next worker to get in semaphore.release(); + + // Wait for the last worker to finish + if (tx_enable) { + wait_last_worker(); + } + return; } - // ... otherwise, append NR base-band from saved buffer if available - if (nr_tx_buffer_ready) { - uint32_t j = 0; - for (uint32_t i = 0; i < SRSRAN_MAX_CHANNELS; i++) { - if (buffer.get(i) == nullptr) { - buffer.set(i, nr_tx_buffer.get(j)); - j++; - } - } - nr_tx_buffer_ready = false; - } + // Add current time alignment + srsran::rf_timestamp_t tx_time = w_ctx.tx_time; // get transmit time from the last worker // Run DL channel emulator if created if (dl_channel) { - dl_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), buffer.get_nof_samples(), tx_time.get(0)); + dl_channel->run(tx_buffer.to_cf_t(), tx_buffer.to_cf_t(), tx_buffer.get_nof_samples(), tx_time.get(0)); } // Always transmit on single radio - radio->tx(buffer, tx_time); + radio->tx(tx_buffer, tx_time); + + // Reset transmit buffer + tx_buffer = {}; + + // Notify this is the last worker + last_worker(); // Allow next TTI to transmit semaphore.release(); @@ -152,19 +169,15 @@ void phy_common::worker_end(void* tx_sem_id, srsran::rf_buffer_t& buffer, srsran void phy_common::set_mch_period_stop(uint32_t stop) { - pthread_mutex_lock(&mtch_mutex); + std::lock_guard lock(mtch_mutex); have_mtch_stop = true; mch_period_stop = stop; - pthread_cond_signal(&mtch_cvar); - pthread_mutex_unlock(&mtch_mutex); + mtch_cvar.notify_one(); } void phy_common::configure_mbsfn(srsran::phy_cfg_mbsfn_t* cfg) { - mbsfn = *cfg; - - build_mch_table(); - build_mcch_table(); + mbsfn = *cfg; sib13_configured = true; mcch_configured = true; } @@ -279,9 +292,11 @@ bool phy_common::is_mch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti) uint32_t mbsfn_per_frame = mbsfn.mcch.pmch_info_list[0].sf_alloc_end / +enum_to_number(mbsfn.mcch.pmch_info_list[0].mch_sched_period); uint32_t sf_alloc_idx = frame_alloc_idx * mbsfn_per_frame + ((sf < 4) ? sf - 1 : sf - 3); + std::unique_lock lock(mtch_mutex); while (!have_mtch_stop) { - pthread_cond_wait(&mtch_cvar, &mtch_mutex); + mtch_cvar.wait(lock); } + lock.unlock(); for (uint32_t i = 0; i < mbsfn.mcch.nof_pmch_info; i++) { if (sf_alloc_idx <= mch_period_stop) { cfg->mbsfn_mcs = mbsfn.mcch.pmch_info_list[i].data_mcs; diff --git a/srsenb/src/phy/phy_ue_db.cc b/srsenb/src/phy/phy_ue_db.cc index 685227951..e82e045dd 100644 --- a/srsenb/src/phy/phy_ue_db.cc +++ b/srsenb/src/phy/phy_ue_db.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -615,6 +615,45 @@ int phy_ue_db::fill_uci_cfg(uint32_t tti, return uci_required ? 1 : SRSRAN_SUCCESS; } +void phy_ue_db::send_cqi_data(uint32_t tti, + uint16_t rnti, + uint32_t cqi_cc_idx, + const srsran_cqi_cfg_t& cqi_cfg, + const srsran_cqi_value_t& cqi_value, + const srsran_cqi_report_cfg_t& cqi_report_cfg, + const srsran_cell_t& cell, + stack_interface_phy_lte* stack) +{ + uint8_t stack_value = 0; + switch (cqi_cfg.type) { + case SRSRAN_CQI_TYPE_WIDEBAND: + stack_value = cqi_value.wideband.wideband_cqi; + stack->cqi_info(tti, rnti, cqi_cc_idx, stack_value); + break; + case SRSRAN_CQI_TYPE_SUBBAND_UE: + stack_value = cqi_value.subband_ue.subband_cqi; + stack->sb_cqi_info(tti, + rnti, + cqi_cc_idx, + srsran_cqi_get_sb_idx(tti, cqi_value.subband_ue.subband_label, &cqi_report_cfg, &cell), + stack_value); + break; + case SRSRAN_CQI_TYPE_SUBBAND_HL: + stack_value = cqi_value.subband_hl.wideband_cqi_cw0; + // Todo: change interface + stack->cqi_info(tti, rnti, cqi_cc_idx, stack_value); + break; + case SRSRAN_CQI_TYPE_SUBBAND_UE_DIFF: + stack_value = cqi_value.subband_ue_diff.wideband_cqi; + stack->sb_cqi_info(tti, + rnti, + cqi_cc_idx, + cqi_value.subband_ue_diff.position_subband, + stack_value + cqi_value.subband_ue_diff.subband_diff_cqi); + break; + } +} + int phy_ue_db::send_uci_data(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, @@ -643,7 +682,8 @@ int phy_ue_db::send_uci_data(uint32_t tti, // Get ACK info srsran_pdsch_ack_t& pdsch_ack = ue.pdsch_ack[tti]; - srsran_enb_dl_get_ack(&cell_cfg_list->at(ue.cell_info[0].enb_cc_idx).cell, &uci_cfg, &uci_value, &pdsch_ack); + const srsran_cell_t& cell = cell_cfg_list->at(ue.cell_info[0].enb_cc_idx).cell; + srsran_enb_dl_get_ack(&cell, &uci_cfg, &uci_value, &pdsch_ack); // Iterate over the ACK information for (uint32_t ue_cc_idx = 0; ue_cc_idx < SRSRAN_MAX_CARRIERS; ue_cc_idx++) { @@ -672,22 +712,7 @@ int phy_ue_db::send_uci_data(uint32_t tti, if (uci_value.cqi.data_crc) { // Channel quality indicator itself if (uci_cfg.cqi.data_enable) { - uint8_t cqi_value = 0; - switch (uci_cfg.cqi.type) { - case SRSRAN_CQI_TYPE_WIDEBAND: - cqi_value = uci_value.cqi.wideband.wideband_cqi; - break; - case SRSRAN_CQI_TYPE_SUBBAND: - cqi_value = uci_value.cqi.subband.subband_cqi; - break; - case SRSRAN_CQI_TYPE_SUBBAND_HL: - cqi_value = uci_value.cqi.subband_hl.wideband_cqi_cw0; - break; - case SRSRAN_CQI_TYPE_SUBBAND_UE: - cqi_value = uci_value.cqi.subband_ue.wideband_cqi; - break; - } - stack->cqi_info(tti, rnti, cqi_cc_idx, cqi_value); + send_cqi_data(tti, rnti, cqi_cc_idx, uci_cfg.cqi, uci_value.cqi, ue.cell_info[0].phy_cfg.dl_cfg.cqi_report, cell, stack); } // Precoding Matrix indicator (TM4) @@ -749,6 +774,7 @@ int phy_ue_db::get_last_ul_tb(uint16_t rnti, uint32_t enb_cc_idx, uint32_t pid, int phy_ue_db::set_ul_grant_available(uint32_t tti, const stack_interface_phy_lte::ul_sched_list_t& ul_sched_list) { + int ret = SRSRAN_SUCCESS; std::lock_guard lock(mutex); // Reset all available grants flags for the given TTI @@ -766,12 +792,14 @@ int phy_ue_db::set_ul_grant_available(uint32_t tti, const stack_interface_phy_lt uint16_t rnti = ul_sched_grant.dci.rnti; // Check that eNb Cell/Carrier is active for the given RNTI if (_assert_active_enb_cc(rnti, enb_cc_idx) != SRSRAN_SUCCESS) { - return SRSRAN_ERROR; + ret = SRSRAN_ERROR; + srslog::fetch_basic_logger("PHY").info("Error setting grant for rnti=0x%x, cc=%d", rnti, enb_cc_idx); + continue; } // Rise Grant available flag ue_db[rnti].cell_info[_get_ue_cc_idx(rnti, enb_cc_idx)].is_grant_available[tti] = true; } } - return SRSRAN_SUCCESS; + return ret; } diff --git a/srsenb/src/phy/prach_worker.cc b/srsenb/src/phy/prach_worker.cc index 5a857774b..0ab6b2f6b 100644 --- a/srsenb/src/phy/prach_worker.cc +++ b/srsenb/src/phy/prach_worker.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -61,14 +61,18 @@ int prach_worker::init(const srsran_cell_t& cell_, #if defined(ENABLE_GUI) and ENABLE_PRACH_GUI char title[32] = {}; - snprintf(title, sizeof(title), "PRACH buffer %d", cc_idx); + snprintf(title, sizeof(title), "PRACH buffer %s %d", prach.is_nr ? "NR" : "LTE", cc_idx); sdrgui_init(); plot_real_init(&plot_real); plot_real_setTitle(&plot_real, title); plot_real_setXAxisAutoScale(&plot_real, true); plot_real_setYAxisAutoScale(&plot_real, true); - plot_real_addToWindowGrid(&plot_real, (char*)"PRACH", 0, cc_idx); + if (prach.is_nr) { + plot_real_addToWindowGrid(&plot_real, (char*)"PRACH-NR", 1, cc_idx); + } else { + plot_real_addToWindowGrid(&plot_real, (char*)"PRACH", 0, cc_idx); + } #endif // defined(ENABLE_GUI) and ENABLE_PRACH_GUI return 0; diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc index e452a41f7..c2cfd8fb8 100644 --- a/srsenb/src/phy/txrx.cc +++ b/srsenb/src/phy/txrx.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,11 +21,11 @@ #include +#include "srsenb/hdr/phy/txrx.h" +#include "srsran/common/band_helper.h" #include "srsran/common/threads.h" #include "srsran/srsran.h" -#include "srsenb/hdr/phy/txrx.h" - #define Error(fmt, ...) \ if (SRSRAN_DEBUG_ENABLED) \ logger.error(fmt, ##__VA_ARGS__) @@ -48,24 +48,19 @@ txrx::txrx(srslog::basic_logger& logger) : thread("TXRX"), logger(logger), runni /* Do nothing */ } -bool txrx::init(stack_interface_phy_lte* stack_, +bool txrx::init(enb_time_interface* enb_, srsran::radio_interface_phy* radio_h_, lte::worker_pool* lte_workers_, - nr::worker_pool* nr_workers_, phy_common* worker_com_, prach_worker_pool* prach_, uint32_t prio_) { - stack = stack_; - radio_h = radio_h_; - lte_workers = lte_workers_; - nr_workers = nr_workers_; - worker_com = worker_com_; - prach = prach_; - tx_worker_cnt = 0; - running = true; - - nof_workers = lte_workers->get_nof_workers(); + enb = enb_; + radio_h = radio_h_; + lte_workers = lte_workers_; + worker_com = worker_com_; + prach = prach_; + running = true; // Instantiate UL channel emulator if (worker_com->params.ul_channel_args.enable) { @@ -77,6 +72,12 @@ bool txrx::init(stack_interface_phy_lte* stack_, return true; } +bool txrx::set_nr_workers(nr::worker_pool* nr_workers_) +{ + nr_workers = nr_workers_; + return true; +} + void txrx::stop() { if (running) { @@ -93,6 +94,8 @@ void txrx::run_thread() float samp_rate = srsran_sampling_freq_hz(worker_com->get_nof_prb(0)); + srsran::srsran_band_helper band_helper; + // Configure radio radio_h->set_rx_srate(samp_rate); radio_h->set_tx_srate(samp_rate); @@ -102,11 +105,23 @@ void txrx::run_thread() double tx_freq_hz = worker_com->get_dl_freq_hz(cc_idx); double rx_freq_hz = worker_com->get_ul_freq_hz(cc_idx); uint32_t rf_port = worker_com->get_rf_port(cc_idx); - srsran::console("Setting frequency: DL=%.1f Mhz, UL=%.1f MHz for cc_idx=%d nof_prb=%d\n", - tx_freq_hz / 1e6f, - rx_freq_hz / 1e6f, - cc_idx, - worker_com->get_nof_prb(cc_idx)); + if (cc_idx < worker_com->get_nof_carriers_lte()) { + srsran::console("Setting frequency: DL=%.1f Mhz, UL=%.1f MHz for cc_idx=%d nof_prb=%d\n", + tx_freq_hz / 1e6f, + rx_freq_hz / 1e6f, + cc_idx, + worker_com->get_nof_prb(cc_idx)); + } else { + srsran::console( + "Setting frequency: DL=%.1f Mhz, DL_SSB=%.2f Mhz (SSB-ARFCN=%d), UL=%.1f MHz for cc_idx=%d nof_prb=%d\n", + tx_freq_hz / 1e6f, + worker_com->get_ssb_freq_hz(cc_idx) / 1e6f, + band_helper.freq_to_nr_arfcn(worker_com->get_ssb_freq_hz(cc_idx)), + rx_freq_hz / 1e6f, + cc_idx, + worker_com->get_nof_prb(cc_idx)); + } + radio_h->set_tx_freq(rf_port, tx_freq_hz); radio_h->set_rx_freq(rf_port, rx_freq_hz); } @@ -136,8 +151,8 @@ void txrx::run_thread() } } - nr::sf_worker* nr_worker = nullptr; - if (worker_com->get_nof_carriers_nr() > 0) { + nr::slot_worker* nr_worker = nullptr; + if (nr_workers != nullptr and worker_com->get_nof_carriers_nr() > 0) { nr_worker = nr_workers->wait_worker(tti); if (nr_worker == nullptr) { running = false; @@ -156,12 +171,16 @@ void txrx::run_thread() buffer.set(rf_port, p, worker_com->get_nof_ports(0), lte_worker->get_buffer_rx(cc_lte, p)); } } - for (uint32_t cc_nr = 0; cc_nr < worker_com->get_nof_carriers_lte(); cc_nr++, cc++) { + for (uint32_t cc_nr = 0; cc_nr < worker_com->get_nof_carriers_nr(); cc_nr++, cc++) { uint32_t rf_port = worker_com->get_rf_port(cc); for (uint32_t p = 0; p < worker_com->get_nof_ports(cc); p++) { - // WARNING: The number of ports for all cells must be the same - buffer.set(rf_port, p, worker_com->get_nof_ports(0), nr_worker->get_buffer_rx(cc_nr, p)); + // WARNING: + // - The number of ports for all cells must be the same + // - Only one NR cell is currently supported + if (nr_worker != nullptr) { + buffer.set(rf_port, p, worker_com->get_nof_ports(0), nr_worker->get_buffer_rx(p)); + } } } } @@ -176,34 +195,49 @@ void txrx::run_thread() // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time timestamp.add(FDD_HARQ_DELAY_UL_MS * 1e-3); - Debug("Setting TTI=%d, tx_mutex=%d, tx_time=%ld:%f to worker %d", + Debug("Setting TTI=%d, tx_time=%ld:%f to worker %d", tti, - tx_worker_cnt, timestamp.get(0).full_secs, timestamp.get(0).frac_secs, - lte_worker->get_id()); - - lte_worker->set_time(tti, tx_worker_cnt, timestamp); - tx_worker_cnt = (tx_worker_cnt + 1) % nof_workers; + lte_worker ? lte_worker->get_id() : 0); // Trigger prach worker execution for (uint32_t cc = 0; cc < worker_com->get_nof_carriers_lte(); cc++) { prach->new_tti(cc, tti, buffer.get(worker_com->get_rf_port(cc), 0, worker_com->get_nof_ports(0))); } - // Launch NR worker only if available + // Set NR worker context and start if (nr_worker != nullptr) { - nr_worker->set_tti(tti); + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = nr_worker; + context.last = (lte_worker == nullptr); // Set last if standalone + context.tx_time.copy(timestamp); + + nr_worker->set_context(context); + + // Start NR worker processing worker_com->semaphore.push(nr_worker); nr_workers->start_worker(nr_worker); } - // Trigger phy worker execution - worker_com->semaphore.push(lte_worker); - lte_workers->start_worker(lte_worker); + // Set LTE worker context and start + if (lte_worker != nullptr) { + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = lte_worker; + context.last = true; + context.tx_time.copy(timestamp); - // Advance stack in time - stack->tti_clock(); + lte_worker->set_context(context); + + // Start LTE worker processing + worker_com->semaphore.push(lte_worker); + lte_workers->start_worker(lte_worker); + } + + // Advance in time + enb->tti_clock(); } } diff --git a/srsenb/src/phy/vnf_phy_nr.cc b/srsenb/src/phy/vnf_phy_nr.cc deleted file mode 100644 index 42ac6dc37..000000000 --- a/srsenb/src/phy/vnf_phy_nr.cc +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include -#include - -#include "srsenb/hdr/phy/vnf_phy_nr.h" -#include "srsran/common/basic_vnf_api.h" - -using namespace std; - -namespace srsenb { - -vnf_phy_nr::~vnf_phy_nr() -{ - stop(); -} - -void vnf_phy_nr::parse_config(const nr_phy_cfg_t& cfg) {} - -int vnf_phy_nr::init(const srsenb::phy_args_t& args, const nr_phy_cfg_t& cfg, srsenb::stack_interface_phy_nr* stack_) -{ - mlockall(MCL_CURRENT | MCL_FUTURE); - - // create VNF - vnf = std::unique_ptr(new srsran::srsran_basic_vnf(args.vnf_args, stack_)); - - initialized = true; - - return SRSRAN_SUCCESS; -} - -void vnf_phy_nr::stop() -{ - if (initialized) { - vnf->stop(); - initialized = false; - } -} - -// Start GUI -void vnf_phy_nr::start_plot() {} - -void vnf_phy_nr::get_metrics(std::vector& metrics) {} - -int vnf_phy_nr::dl_config_request(const dl_config_request_t& request) -{ - // prepare DL config request over basic API and send - return vnf->dl_config_request(request); -} - -int vnf_phy_nr::tx_request(const tx_request_t& request) -{ - // send Tx request over basic API - return vnf->tx_request(request); -} - -} // namespace srsenb diff --git a/srsenb/src/stack/CMakeLists.txt b/srsenb/src/stack/CMakeLists.txt index cb1997ec4..463182e83 100644 --- a/srsenb/src/stack/CMakeLists.txt +++ b/srsenb/src/stack/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -20,12 +20,9 @@ add_subdirectory(mac) add_subdirectory(rrc) +add_subdirectory(s1ap) add_subdirectory(upper) set(SOURCES enb_stack_lte.cc) -add_library(srsenb_stack STATIC ${SOURCES}) -target_link_libraries(srsenb_stack) - -add_library(srsgnb_stack STATIC gnb_stack_nr.cc) -target_link_libraries(srsgnb_stack srsue_upper) +add_library(srsenb_stack STATIC ${SOURCES}) \ No newline at end of file diff --git a/srsenb/src/stack/enb_stack_lte.cc b/srsenb/src/stack/enb_stack_lte.cc index 5dc28a2fb..c8c3c560e 100644 --- a/srsenb/src/stack/enb_stack_lte.cc +++ b/srsenb/src/stack/enb_stack_lte.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,9 +22,11 @@ #include "srsenb/hdr/stack/enb_stack_lte.h" #include "srsenb/hdr/common/rnti_pool.h" #include "srsenb/hdr/enb.h" +#include "srsenb/hdr/stack/upper/gtpu_pdcp_adapter.h" #include "srsran/interfaces/enb_metrics_interface.h" +#include "srsran/interfaces/enb_x2_interfaces.h" +#include "srsran/rlc/bearer_mem_pool.h" #include "srsran/srslog/event_trace.h" -#include "srsran/upper/bearer_mem_pool.h" using namespace srsran; @@ -43,16 +45,15 @@ enb_stack_lte::enb_stack_lte(srslog::sink& log_sink) : pdcp(&task_sched, pdcp_logger), mac(&task_sched, mac_logger), rlc(rlc_logger), - gtpu(&task_sched, gtpu_logger, &rx_sockets), - s1ap(&task_sched, s1ap_logger, &rx_sockets), - rrc(&task_sched), + gtpu(&task_sched, gtpu_logger, &get_rx_io_manager()), + s1ap(&task_sched, s1ap_logger, &get_rx_io_manager()), + rrc(&task_sched, bearers), mac_pcap(), pending_stack_metrics(64) { get_background_workers().set_nof_workers(2); - enb_task_queue = task_sched.make_task_queue(); - mme_task_queue = task_sched.make_task_queue(); - gtpu_task_queue = task_sched.make_task_queue(); + enb_task_queue = task_sched.make_task_queue(); + metrics_task_queue = task_sched.make_task_queue(); // sync_queue is added in init() } @@ -66,20 +67,14 @@ std::string enb_stack_lte::get_type() return "lte"; } -int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_, phy_interface_stack_lte* phy_) -{ - phy = phy_; - if (init(args_, rrc_cfg_)) { - return SRSRAN_ERROR; - } - - return SRSRAN_SUCCESS; -} - -int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) +int enb_stack_lte::init(const stack_args_t& args_, + const rrc_cfg_t& rrc_cfg_, + phy_interface_stack_lte* phy_, + x2_interface* x2_) { args = args_; rrc_cfg = rrc_cfg_; + phy = phy_; // Init RNTI and bearer memory pools reserve_rnti_memblocks(args.mac.nof_prealloc_ues); @@ -88,9 +83,6 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) // setup logging for each layer mac_logger.set_level(srslog::str_to_basic_level(args.log.mac_level)); - mac_logger.set_hex_dump_max_size(args.log.mac_hex_limit); - - // Init logs rlc_logger.set_level(srslog::str_to_basic_level(args.log.rlc_level)); pdcp_logger.set_level(srslog::str_to_basic_level(args.log.pdcp_level)); rrc_logger.set_level(srslog::str_to_basic_level(args.log.rrc_level)); @@ -98,6 +90,7 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) s1ap_logger.set_level(srslog::str_to_basic_level(args.log.s1ap_level)); stack_logger.set_level(srslog::str_to_basic_level(args.log.stack_level)); + mac_logger.set_hex_dump_max_size(args.log.mac_hex_limit); rlc_logger.set_hex_dump_max_size(args.log.rlc_hex_limit); pdcp_logger.set_hex_dump_max_size(args.log.pdcp_hex_limit); rrc_logger.set_hex_dump_max_size(args.log.rrc_hex_limit); @@ -107,7 +100,7 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) // Set up pcap and trace if (args.mac_pcap.enable) { - mac_pcap.open(args.mac_pcap.filename.c_str()); + mac_pcap.open(args.mac_pcap.filename); mac.start_pcap(&mac_pcap); } @@ -127,14 +120,22 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) // add sync queue sync_task_queue = task_sched.make_task_queue(args.sync_queue_size); - // Init all layers + // add x2 queue + if (x2_ != nullptr) { + x2_task_queue = task_sched.make_task_queue(); + } + + // setup bearer managers + gtpu_adapter.reset(new gtpu_pdcp_adapter(stack_logger, &pdcp, x2_, >pu, bearers)); + + // Init all LTE layers if (!mac.init(args.mac, rrc_cfg.cell_list, phy, &rlc, &rrc)) { stack_logger.error("Couldn't initialize MAC"); return SRSRAN_ERROR; } rlc.init(&pdcp, &rrc, &mac, task_sched.get_timer_handler()); - pdcp.init(&rlc, &rrc, >pu); - if (rrc.init(rrc_cfg, phy, &mac, &rlc, &pdcp, &s1ap, >pu) != SRSRAN_SUCCESS) { + pdcp.init(&rlc, &rrc, gtpu_adapter.get()); + if (rrc.init(rrc_cfg, phy, &mac, &rlc, &pdcp, &s1ap, >pu, x2_) != SRSRAN_SUCCESS) { stack_logger.error("Couldn't initialize RRC"); return SRSRAN_ERROR; } @@ -142,12 +143,15 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) stack_logger.error("Couldn't initialize S1AP"); return SRSRAN_ERROR; } - if (gtpu.init(args.s1ap.gtp_bind_addr, - args.s1ap.mme_addr, - args.embms.m1u_multiaddr, - args.embms.m1u_if_addr, - &pdcp, - args.embms.enable)) { + + gtpu_args_t gtpu_args; + gtpu_args.embms_enable = args.embms.enable; + gtpu_args.embms_m1u_multiaddr = args.embms.m1u_multiaddr; + gtpu_args.embms_m1u_if_addr = args.embms.m1u_if_addr; + gtpu_args.mme_addr = args.s1ap.mme_addr; + gtpu_args.gtp_bind_addr = args.s1ap.gtp_bind_addr; + gtpu_args.indirect_tunnel_timeout_msec = args.gtpu_indirect_tunnel_timeout_msec; + if (gtpu.init(gtpu_args, gtpu_adapter.get()) != SRSRAN_SUCCESS) { stack_logger.error("Couldn't initialize GTPU"); return SRSRAN_ERROR; } @@ -160,12 +164,13 @@ int enb_stack_lte::init(const stack_args_t& args_, const rrc_cfg_t& rrc_cfg_) void enb_stack_lte::tti_clock() { - sync_task_queue.push([this]() { tti_clock_impl(); }); + if (started.load(std::memory_order_relaxed)) { + sync_task_queue.push([this]() { tti_clock_impl(); }); + } } void enb_stack_lte::tti_clock_impl() { - trace_complete_event("enb_stack_lte::tti_clock_impl", "total_time"); task_sched.tic(); rrc.tti_clock(); } @@ -180,7 +185,7 @@ void enb_stack_lte::stop() void enb_stack_lte::stop_impl() { - rx_sockets.stop(); + get_rx_io_manager().stop(); s1ap.stop(); gtpu.stop(); @@ -210,7 +215,7 @@ void enb_stack_lte::stop_impl() bool enb_stack_lte::get_metrics(stack_metrics_t* metrics) { // use stack thread to query metrics - auto ret = enb_task_queue.try_push([this]() { + auto ret = metrics_task_queue.try_push([this]() { stack_metrics_t metrics{}; mac.get_metrics(metrics.mac); if (not metrics.mac.ues.empty()) { @@ -224,7 +229,7 @@ bool enb_stack_lte::get_metrics(stack_metrics_t* metrics) } }); - if (ret.first) { + if (ret.has_value()) { // wait for result *metrics = pending_stack_metrics.pop_blocking(); return true; @@ -234,9 +239,18 @@ bool enb_stack_lte::get_metrics(stack_metrics_t* metrics) void enb_stack_lte::run_thread() { - while (started) { + while (started.load(std::memory_order_relaxed)) { task_sched.run_next_task(); } } +void enb_stack_lte::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) +{ + // call GTPU adapter to map to EPS bearer + auto task = [this, rnti, lcid](srsran::unique_byte_buffer_t& pdu) { + gtpu_adapter->write_pdu(rnti, lcid, std::move(pdu)); + }; + x2_task_queue.push(std::bind(task, std::move(pdu))); +} + } // namespace srsenb diff --git a/srsenb/src/stack/gnb_stack_nr.cc b/srsenb/src/stack/gnb_stack_nr.cc deleted file mode 100644 index 6974e0fd4..000000000 --- a/srsenb/src/stack/gnb_stack_nr.cc +++ /dev/null @@ -1,196 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsenb/hdr/stack/gnb_stack_nr.h" -#include "srsran/srsran.h" -#include - -namespace srsenb { - -gnb_stack_nr::gnb_stack_nr() : task_sched{512, 128}, thread("gNB"), rlc_logger(srslog::fetch_basic_logger("RLC-NR")) -{ - m_mac.reset(new mac_nr()); - m_rlc.reset(new rlc_nr("RLC-NR")); - m_pdcp.reset(new pdcp_nr(&task_sched, "PDCP-NR")); - m_rrc.reset(new rrc_nr(task_sched.get_timer_handler())); - m_sdap.reset(new sdap()); - m_gw.reset(new srsue::gw()); - // m_gtpu.reset(new srsenb::gtpu()); - - ue_task_queue = task_sched.make_task_queue(); - sync_task_queue = task_sched.make_task_queue(); - gw_task_queue = task_sched.make_task_queue(); - mac_task_queue = task_sched.make_task_queue(); -} - -gnb_stack_nr::~gnb_stack_nr() -{ - stop(); -} - -std::string gnb_stack_nr::get_type() -{ - return "nr"; -} - -int gnb_stack_nr::init(const srsenb::stack_args_t& args_, const rrc_nr_cfg_t& rrc_cfg_, phy_interface_stack_nr* phy_) -{ - phy = phy_; - if (init(args_, rrc_cfg_)) { - return SRSRAN_ERROR; - } - return SRSRAN_SUCCESS; -} - -int gnb_stack_nr::init(const srsenb::stack_args_t& args_, const rrc_nr_cfg_t& rrc_cfg_) -{ - args = args_; - - // verify configuration correctness - // gtpu_log.init("GTPU", logger); - // gtpu_log.set_level(args.log.gtpu_level); - // gtpu_log.set_hex_limit(args.log.gtpu_hex_limit); - - // Init all layers - mac_nr_args_t mac_args = {}; - mac_args.log_level = args.log.mac_level; - mac_args.log_hex_limit = args.log.mac_hex_limit; - mac_args.pcap = args.mac_pcap; - mac_args.sched = args.mac.sched; - mac_args.tb_size = args.mac.nr_tb_size; - mac_args.rnti = args.coreless.rnti; - m_mac->init(mac_args, phy, this, m_rlc.get(), m_rrc.get()); - - rlc_logger.set_level(srslog::str_to_basic_level(args.log.rlc_level)); - rlc_logger.set_hex_dump_max_size(args.log.rlc_hex_limit); - m_rlc->init(m_pdcp.get(), m_rrc.get(), m_mac.get(), task_sched.get_timer_handler()); - - pdcp_nr_args_t pdcp_args = {}; - pdcp_args.log_level = args.log.pdcp_level; - pdcp_args.log_hex_limit = args.log.pdcp_hex_limit; - m_pdcp->init(pdcp_args, m_rlc.get(), m_rrc.get(), m_sdap.get()); - - m_rrc->init(rrc_cfg_, phy, m_mac.get(), m_rlc.get(), m_pdcp.get(), nullptr, nullptr); - - m_sdap->init(m_pdcp.get(), nullptr, m_gw.get()); - - m_gw->init(args.coreless.gw_args, this); - char* err_str = nullptr; - if (m_gw->setup_if_addr(5, - args.coreless.drb_lcid, - LIBLTE_MME_PDN_TYPE_IPV4, - htonl(inet_addr(args.coreless.ip_addr.c_str())), - nullptr, - err_str)) { - printf("Error configuring TUN interface\n"); - } - - // TODO: add NGAP - // m_gtpu->init(args.s1ap.gtp_bind_addr, args.s1ap.mme_addr, - // args.expert.m1u_multiaddr, args.expert.m1u_if_addr, nullptr, >pu_log, - // args.expert.enable_mbsfn); - - running = true; - - start(STACK_MAIN_THREAD_PRIO); - - return SRSRAN_SUCCESS; -} - -void gnb_stack_nr::stop() -{ - if (running) { - m_gw->stop(); - // m_gtpu->stop(); - m_rrc->stop(); - m_pdcp->stop(); - m_mac->stop(); - - srsran::get_background_workers().stop(); - running = false; - } -} - -bool gnb_stack_nr::switch_on() -{ - // Nothing to be done here - return true; -} - -void gnb_stack_nr::run_thread() -{ - while (running) { - task_sched.run_next_task(); - } -} - -void gnb_stack_nr::run_tti(uint32_t tti) -{ - current_tti = tti; - sync_task_queue.push([this, tti]() { run_tti_impl(tti); }); -} - -void gnb_stack_nr::run_tti_impl(uint32_t tti) -{ - // m_ngap->run_tti(); - task_sched.tic(); -} - -void gnb_stack_nr::process_pdus() -{ - mac_task_queue.push([this]() { m_mac->process_pdus(); }); -} - -/******************************************************** - * - * Interface for upper layer timers - * - *******************************************************/ - -bool gnb_stack_nr::get_metrics(srsenb::stack_metrics_t* metrics) -{ - m_mac->get_metrics(metrics->mac); - m_rrc->get_metrics(metrics->rrc); - return true; -} - -int gnb_stack_nr::sf_indication(const uint32_t tti) -{ - return m_mac->sf_indication(tti); -} - -int gnb_stack_nr::rx_data_indication(rx_data_ind_t& grant) -{ - return m_mac->rx_data_indication(grant); -} - -// Temporary GW interface -void gnb_stack_nr::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) -{ - m_pdcp->write_sdu(args.coreless.rnti, lcid, std::move(sdu)); -} - -bool gnb_stack_nr::is_lcid_enabled(uint32_t lcid) -{ - return (lcid == args.coreless.drb_lcid); -} - -} // namespace srsenb diff --git a/srsenb/src/stack/mac/CMakeLists.txt b/srsenb/src/stack/mac/CMakeLists.txt index 44e2c93c8..3b2ff77d0 100644 --- a/srsenb/src/stack/mac/CMakeLists.txt +++ b/srsenb/src/stack/mac/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,11 +18,13 @@ # and at http://www.gnu.org/licenses/. # +add_subdirectory(common) + add_subdirectory(schedulers) set(SOURCES mac.cc ue.cc sched.cc sched_carrier.cc sched_grid.cc sched_ue_ctrl/sched_harq.cc sched_ue.cc - sched_ue_ctrl/sched_lch.cc sched_ue_ctrl/sched_ue_cell.cc sched_phy_ch/sf_cch_allocator.cc sched_phy_ch/sched_dci.cc sched_helpers.cc) + sched_ue_ctrl/sched_lch.cc sched_ue_ctrl/sched_ue_cell.cc sched_ue_ctrl/sched_dl_cqi.cc + sched_phy_ch/sf_cch_allocator.cc sched_phy_ch/sched_dci.cc sched_phy_ch/sched_phy_resource.cc + sched_helpers.cc) add_library(srsenb_mac STATIC ${SOURCES} $) - -set(SOURCES mac_nr.cc) -add_library(srsgnb_mac STATIC ${SOURCES}) +target_link_libraries(srsenb_mac srsenb_mac_common) \ No newline at end of file diff --git a/srsenb/src/stack/mac/common/CMakeLists.txt b/srsenb/src/stack/mac/common/CMakeLists.txt new file mode 100644 index 000000000..ec49004fe --- /dev/null +++ b/srsenb/src/stack/mac/common/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES base_ue_buffer_manager.cc) +add_library(srsenb_mac_common STATIC ${SOURCES}) diff --git a/srsenb/src/stack/mac/common/base_ue_buffer_manager.cc b/srsenb/src/stack/mac/common/base_ue_buffer_manager.cc new file mode 100644 index 000000000..cf486cdf7 --- /dev/null +++ b/srsenb/src/stack/mac/common/base_ue_buffer_manager.cc @@ -0,0 +1,186 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h" +#include "srsran/adt/bounded_vector.h" +#include "srsran/common/string_helpers.h" +#include "srsran/srslog/bundled/fmt/format.h" +#include "srsran/srslog/bundled/fmt/ranges.h" +extern "C" { +#include "srsran/config.h" +} + +namespace srsenb { + +template +base_ue_buffer_manager::base_ue_buffer_manager(uint16_t rnti_, srslog::basic_logger& logger_) : + logger(logger_), rnti(rnti_) +{ + std::fill(lcg_bsr.begin(), lcg_bsr.end(), 0); +} + +template +void base_ue_buffer_manager::config_lcids(srsran::const_span bearer_cfg_list) +{ + bool log_enabled = logger.info.enabled(); + srsran::bounded_vector changed_list; + + for (uint32_t lcid = 0; is_lcid_valid(lcid); ++lcid) { + if (config_lcid_internal(lcid, bearer_cfg_list[lcid]) and log_enabled) { + // add to the changed_list the lcids that have been updated with new parameters + changed_list.push_back(lcid); + } + } + + // Log configurations of the LCIDs for which there were param updates + if (not changed_list.empty()) { + fmt::memory_buffer fmtbuf; + for (uint32_t i = 0; i < changed_list.size(); ++i) { + uint32_t lcid = changed_list[i]; + fmt::format_to(fmtbuf, + "{}{{lcid={}, mode={}, prio={}, lcg={}}}", + i > 0 ? ", " : "", + lcid, + to_string(channels[lcid].cfg.direction), + channels[lcid].cfg.priority, + channels[lcid].cfg.group); + } + logger.info("SCHED: rnti=0x%x, new lcid configuration: [%s]", rnti, srsran::to_c_str(fmtbuf)); + } +} + +template +void base_ue_buffer_manager::config_lcid(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg) +{ + bool cfg_changed = config_lcid_internal(lcid, bearer_cfg); + if (cfg_changed) { + logger.info("SCHED: rnti=0x%x, lcid=%d configured: mode=%s, prio=%d, lcg=%d", + rnti, + lcid, + to_string(channels[lcid].cfg.direction), + channels[lcid].cfg.priority, + channels[lcid].cfg.group); + } +} + +/** + * @brief configure MAC logical channel. The function checks if the configuration is valid + * and whether there was any change compared to previous value + * @return true if the lcid was updated with new parameters. False in case of case of error or no update. + */ +template +bool base_ue_buffer_manager::config_lcid_internal(uint32_t lcid, const mac_lc_ch_cfg_t& bearer_cfg) +{ + if (not is_lcid_valid(lcid)) { + logger.warning("SCHED: Configuring rnti=0x%x bearer with invalid lcid=%d", rnti, lcid); + return false; + } + if (not is_lcg_valid(bearer_cfg.group)) { + logger.warning( + "SCHED: Configuring rnti=0x%x bearer with invalid logical channel group id=%d", rnti, bearer_cfg.group); + return false; + } + + // update bearer config + if (bearer_cfg != channels[lcid].cfg) { + channels[lcid].cfg = bearer_cfg; + if (channels[lcid].cfg.pbr == pbr_infinity) { + channels[lcid].bucket_size = std::numeric_limits::max(); + channels[lcid].Bj = std::numeric_limits::max(); + } else { + channels[lcid].bucket_size = channels[lcid].cfg.bsd * channels[lcid].cfg.pbr; + channels[lcid].Bj = 0; + } + return true; + } + return false; +} + +template +int base_ue_buffer_manager::get_dl_tx_total() const +{ + int sum = 0; + for (size_t lcid = 0; is_lcid_valid(lcid); ++lcid) { + sum += get_dl_tx_total(lcid); + } + return sum; +} + +template +bool base_ue_buffer_manager::is_lcg_active(uint32_t lcg) const +{ + if (lcg == 0) { + return true; + } + for (uint32_t lcid = 0; is_lcid_valid(lcid); ++lcid) { + if (is_bearer_ul(lcid) and channels[lcid].cfg.group == (int)lcg) { + return true; + } + } + return false; +} + +template +int base_ue_buffer_manager::get_bsr(uint32_t lcg) const +{ + return is_lcg_active(lcg) ? lcg_bsr[lcg] : 0; +} + +template +int base_ue_buffer_manager::get_bsr() const +{ + uint32_t count = 0; + for (uint32_t lcg = 0; is_lcg_valid(lcg); ++lcg) { + if (is_lcg_active(lcg)) { + count += lcg_bsr[lcg]; + } + } + return count; +} + +template +int base_ue_buffer_manager::ul_bsr(uint32_t lcg_id, uint32_t val) +{ + if (not is_lcg_valid(lcg_id)) { + logger.warning("SCHED: The provided lcg_id=%d for rnti=0x%x is not valid", lcg_id, rnti); + return SRSRAN_ERROR; + } + lcg_bsr[lcg_id] = val; + return SRSRAN_SUCCESS; +} + +template +int base_ue_buffer_manager::dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t prio_tx_queue) +{ + if (not is_lcid_valid(lcid)) { + logger.warning("The provided lcid=%d is not valid", lcid); + return SRSRAN_ERROR; + } + channels[lcid].buf_prio_tx = prio_tx_queue; + channels[lcid].buf_tx = tx_queue; + return SRSRAN_SUCCESS; +} + +// Explicit instantiation +template class base_ue_buffer_manager; +template class base_ue_buffer_manager; + +} // namespace srsenb diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index c6c449e8e..9d61f1c83 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,7 +29,7 @@ #include "srsran/common/time_prof.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" #include "srsran/srslog/event_trace.h" // #define WRITE_SIB_PCAP @@ -41,6 +41,7 @@ mac::mac(srsran::ext_task_sched_handle task_sched_, srslog::basic_logger& logger logger(logger), rar_payload(), common_buffers(SRSRAN_MAX_CARRIERS), task_sched(task_sched_) { pthread_rwlock_init(&rwlock, nullptr); + stack_task_queue = task_sched.make_task_queue(); } mac::~mac() @@ -56,19 +57,13 @@ bool mac::init(const mac_args_t& args_, rrc_interface_mac* rrc) { started = false; - - if (not phy or not rlc) { - return false; - } - phy_h = phy; - rlc_h = rlc; - rrc_h = rrc; + phy_h = phy; + rlc_h = rlc; + rrc_h = rrc; args = args_; cells = cells_; - stack_task_queue = task_sched.make_task_queue(); - scheduler.init(rrc, args.sched); // Init softbuffer for SI messages @@ -84,8 +79,6 @@ bool mac::init(const mac_args_t& args_, srsran_softbuffer_tx_init(&cc.rar_softbuffer_tx, args.nof_prb); } - reset(); - // Initiate common pool of softbuffers uint32_t nof_prb = args.nof_prb; auto init_softbuffers = [nof_prb](void* ptr) { @@ -95,9 +88,6 @@ bool mac::init(const mac_args_t& args_, softbuffer_pool.reset(new srsran::background_obj_pool( 8, 8, args.nof_prealloc_ues, init_softbuffers, recycle_softbuffers)); - // Pre-alloc UE objects for first attaching users - prealloc_ue(10); - detected_rachs.resize(cells.size()); started = true; @@ -118,23 +108,12 @@ void mac::stop() srsran_softbuffer_tx_free(&cc.pcch_softbuffer_tx); srsran_softbuffer_tx_free(&cc.rar_softbuffer_tx); } - ue_pool.stop(); } } -// Implement Section 5.9 -void mac::reset() -{ - logger.info("Resetting MAC"); - - last_rnti = 70; - - /* Setup scheduler */ - scheduler.reset(); -} - void mac::start_pcap(srsran::mac_pcap* pcap_) { + srsran::rwlock_read_guard lock(rwlock); pcap = pcap_; // Set pcap in all UEs for UL messages for (auto& u : ue_db) { @@ -144,6 +123,7 @@ void mac::start_pcap(srsran::mac_pcap* pcap_) void mac::start_pcap_net(srsran::mac_pcap_net* pcap_net_) { + srsran::rwlock_read_guard lock(rwlock); pcap_net = pcap_net_; // Set pcap in all UEs for UL messages for (auto& u : ue_db) { @@ -156,11 +136,12 @@ void mac::start_pcap_net(srsran::mac_pcap_net* pcap_net_) * RLC interface * *******************************************************/ + int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) { srsran::rwlock_read_guard lock(rwlock); int ret = -1; - if (ue_db.contains(rnti)) { + if (check_ue_active(rnti)) { if (rnti != SRSRAN_MRNTI) { ret = scheduler.dl_rlc_buffer_state(rnti, lc_id, tx_queue, retx_queue); } else { @@ -171,34 +152,20 @@ int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint } ret = 0; } - } else { - logger.error("User rnti=0x%x not found", rnti); } return ret; } -int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) +int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, mac_lc_ch_cfg_t* cfg) { - int ret = -1; srsran::rwlock_read_guard lock(rwlock); - if (ue_db.contains(rnti)) { - ret = scheduler.bearer_ue_cfg(rnti, lc_id, *cfg); - } else { - logger.error("User rnti=0x%x not found", rnti); - } - return ret; + return check_ue_active(rnti) ? scheduler.bearer_ue_cfg(rnti, lc_id, *cfg) : -1; } int mac::bearer_ue_rem(uint16_t rnti, uint32_t lc_id) { srsran::rwlock_read_guard lock(rwlock); - int ret = -1; - if (ue_db.contains(rnti)) { - ret = scheduler.bearer_ue_rem(rnti, lc_id); - } else { - logger.error("User rnti=0x%x not found", rnti); - } - return ret; + return check_ue_active(rnti) ? scheduler.bearer_ue_rem(rnti, lc_id) : -1; } void mac::phy_config_enabled(uint16_t rnti, bool enabled) @@ -207,26 +174,26 @@ void mac::phy_config_enabled(uint16_t rnti, bool enabled) } // Update UE configuration -int mac::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) +int mac::ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t* cfg) { srsran::rwlock_read_guard lock(rwlock); - - auto it = ue_db.find(rnti); - ue* ue_ptr = nullptr; - if (it == ue_db.end()) { - logger.error("User rnti=0x%x not found", rnti); + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } - ue_ptr = it->second.get(); + ue* ue_ptr = ue_db[rnti].get(); // Start TA FSM in UE entity ue_ptr->start_ta(); // Update Scheduler configuration - if (cfg != nullptr and scheduler.ue_cfg(rnti, *cfg) == SRSRAN_ERROR) { - logger.error("Registering new UE rnti=0x%x to SCHED", rnti); - return SRSRAN_ERROR; + if (cfg) { + if (scheduler.ue_cfg(rnti, *cfg) == SRSRAN_ERROR) { + logger.error("Registering UE rnti=0x%x to SCHED", rnti); + return SRSRAN_ERROR; + } + ue_ptr->ue_cfg(*cfg); } + return SRSRAN_SUCCESS; } @@ -235,10 +202,9 @@ int mac::ue_rem(uint16_t rnti) { // Remove UE from the perspective of L2/L3 { - srsran::rwlock_write_guard lock(rwlock); - if (ue_db.contains(rnti)) { - ues_to_rem[rnti] = std::move(ue_db[rnti]); - ue_db.erase(rnti); + srsran::rwlock_read_guard lock(rwlock); + if (check_ue_active(rnti)) { + ue_db[rnti]->set_active(false); } else { logger.error("User rnti=0x%x not found", rnti); return SRSRAN_ERROR; @@ -250,32 +216,27 @@ int mac::ue_rem(uint16_t rnti) // Note: Let any pending retx ACK to arrive, so that PHY recognizes rnti task_sched.defer_callback(FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS, [this, rnti]() { phy_h->rem_rnti(rnti); - ues_to_rem.erase(rnti); + srsran::rwlock_write_guard lock(rwlock); + ue_db.erase(rnti); logger.info("User rnti=0x%x removed from MAC/PHY", rnti); }); return SRSRAN_SUCCESS; } // Called after Msg3 -int mac::ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, sched_interface::ue_cfg_t* cfg) +int mac::ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, const sched_interface::ue_cfg_t& cfg) { srsran::rwlock_read_guard lock(rwlock); - if (temp_crnti != crnti) { - // if C-RNTI is changed, it corresponds to older user. Handover scenario. - ue_db[crnti]->reset(); - } else { + if (temp_crnti == crnti) { // Schedule ConRes Msg4 scheduler.dl_mac_buffer_state(crnti, (uint32_t)srsran::dl_sch_lcid::CON_RES_ID); } - int ret = ue_cfg(crnti, cfg); - if (ret != SRSRAN_SUCCESS) { - return ret; - } - return ret; + return ue_cfg(crnti, &cfg); } int mac::cell_cfg(const std::vector& cell_cfg_) { + srsran::rwlock_write_guard lock(rwlock); cell_config = cell_cfg_; return scheduler.cell_cfg(cell_config); } @@ -289,9 +250,33 @@ void mac::get_metrics(mac_metrics_t& metrics) continue; } metrics.ues.emplace_back(); - u.second->metrics_read(&metrics.ues.back()); + auto& ue_metrics = metrics.ues.back(); + + u.second->metrics_read(&ue_metrics); + scheduler.metrics_read(u.first, ue_metrics); + ue_metrics.pci = (ue_metrics.cc_idx < cell_config.size()) ? cell_config[ue_metrics.cc_idx].cell.id : 0; + } + metrics.cc_info.resize(detected_rachs.size()); + for (unsigned cc = 0, e = detected_rachs.size(); cc != e; ++cc) { + metrics.cc_info[cc].cc_rach_counter = detected_rachs[cc]; + metrics.cc_info[cc].pci = (cc < cell_config.size()) ? cell_config[cc].cell.id : 0; + } +} + +void mac::toggle_padding() +{ + do_padding = !do_padding; +} + +void mac::add_padding() +{ + srsran::rwlock_read_guard lock(rwlock); + for (auto it = ue_db.begin(); it != ue_db.end(); ++it) { + uint16_t cur_rnti = it->first; + auto ue = it; + scheduler.dl_rlc_buffer_state(ue->first, args.lcid_padding, 20e6, 0); + ue->second->trigger_padding(args.lcid_padding); } - metrics.cc_rach_counter = detected_rachs; } /******************************************************** @@ -305,7 +290,7 @@ int mac::ack_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t logger.set_context(tti_rx); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -322,7 +307,7 @@ int mac::crc_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t logger.set_context(tti_rx); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -335,29 +320,45 @@ int mac::crc_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t return scheduler.ul_crc_info(tti_rx, rnti, enb_cc_idx, crc); } -int mac::push_pdu(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, uint32_t nof_bytes, bool crc) +int mac::push_pdu(uint32_t tti_rx, + uint16_t rnti, + uint32_t enb_cc_idx, + uint32_t nof_bytes, + bool crc, + uint32_t ul_nof_prbs) { srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } - std::array enb_ue_cc_map = scheduler.get_enb_ue_cc_map(rnti); - if (enb_ue_cc_map[enb_cc_idx] < 0) { - logger.error("User rnti=0x%x is not activated for carrier %d", rnti, enb_cc_idx); + srsran::unique_byte_buffer_t pdu = ue_db[rnti]->release_pdu(tti_rx, enb_cc_idx); + if (pdu == nullptr) { + logger.warning("Could not find MAC UL PDU for rnti=0x%x, cc=%d, tti=%d", rnti, enb_cc_idx, tti_rx); return SRSRAN_ERROR; } - uint32_t ue_cc_idx = enb_ue_cc_map[enb_cc_idx]; // push the pdu through the queue if received correctly if (crc) { logger.info("Pushing PDU rnti=0x%x, tti_rx=%d, nof_bytes=%d", rnti, tti_rx, nof_bytes); - ue_db[rnti]->push_pdu(tti_rx, ue_cc_idx, nof_bytes); - stack_task_queue.push([this]() { process_pdus(); }); + srsran_expect(nof_bytes == pdu->size(), + "Inconsistent PDU length for rnti=0x%x, tti_rx=%d (%d!=%d)", + rnti, + tti_rx, + nof_bytes, + (int)pdu->size()); + auto process_pdu_task = [this, rnti, enb_cc_idx, ul_nof_prbs](srsran::unique_byte_buffer_t& pdu) { + srsran::rwlock_read_guard lock(rwlock); + if (check_ue_active(rnti)) { + ue_db[rnti]->process_pdu(std::move(pdu), enb_cc_idx, ul_nof_prbs); + } else { + logger.debug("Discarding PDU rnti=0x%x", rnti); + } + }; + stack_task_queue.try_push(std::bind(process_pdu_task, std::move(pdu))); } else { - logger.debug("Discarting PDU rnti=0x%x, tti_rx=%d, nof_bytes=%d", rnti, tti_rx, nof_bytes); - ue_db[rnti]->deallocate_pdu(tti_rx, ue_cc_idx); + logger.debug("Discarding PDU rnti=0x%x, tti_rx=%d, nof_bytes=%d", rnti, tti_rx, nof_bytes); } return SRSRAN_SUCCESS; } @@ -367,7 +368,7 @@ int mac::ri_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t ri_v logger.set_context(tti); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -382,7 +383,7 @@ int mac::pmi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t pmi logger.set_context(tti); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -397,7 +398,7 @@ int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi logger.set_context(tti); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } @@ -407,15 +408,30 @@ int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t cqi return SRSRAN_SUCCESS; } +int mac::sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t sb_idx, uint32_t cqi_value) +{ + logger.set_context(tti); + srsran::rwlock_read_guard lock(rwlock); + + if (not check_ue_active(rnti)) { + return SRSRAN_ERROR; + } + + scheduler.dl_sb_cqi_info(tti, rnti, enb_cc_idx, sb_idx, cqi_value); + return SRSRAN_SUCCESS; +} + int mac::snr_info(uint32_t tti_rx, uint16_t rnti, uint32_t enb_cc_idx, float snr, ul_channel_t ch) { logger.set_context(tti_rx); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } + rrc_h->set_radiolink_ul_state(rnti, snr >= args.rlf_min_ul_snr_estim); + return scheduler.ul_snr_info(tti_rx, rnti, enb_cc_idx, snr, (uint32_t)ch); } @@ -423,13 +439,13 @@ int mac::ta_info(uint32_t tti, uint16_t rnti, float ta_us) { srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } uint32_t nof_ta_count = ue_db[rnti]->set_ta_us(ta_us); - if (nof_ta_count) { - scheduler.dl_mac_buffer_state(rnti, (uint32_t)srsran::dl_sch_lcid::TA_CMD, nof_ta_count); + if (nof_ta_count > 0) { + return scheduler.dl_mac_buffer_state(rnti, (uint32_t)srsran::dl_sch_lcid::TA_CMD, nof_ta_count); } return SRSRAN_SUCCESS; } @@ -439,65 +455,64 @@ int mac::sr_detected(uint32_t tti, uint16_t rnti) logger.set_context(tti); srsran::rwlock_read_guard lock(rwlock); - if (not check_ue_exists(rnti)) { + if (not check_ue_active(rnti)) { return SRSRAN_ERROR; } return scheduler.ul_sr_info(tti, rnti); } -uint16_t mac::allocate_rnti() +bool mac::is_valid_rnti_unprotected(uint16_t rnti) { - std::lock_guard lock(rnti_mutex); - - // Assign a c-rnti - uint16_t rnti = last_rnti++; - if (last_rnti >= 60000) { - last_rnti = 70; + if (not started) { + logger.info("RACH ignored as eNB is being shutdown"); + return false; } - - return rnti; + if (not ue_db.has_space(rnti)) { + logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti); + return false; + } + return true; } -uint16_t mac::allocate_ue() +uint16_t mac::allocate_ue(uint32_t enb_cc_idx) { - ue* inserted_ue = nullptr; - do { - // Get pre-allocated UE object - std::unique_ptr ue_ptr; - if (not ue_pool.try_pop(ue_ptr)) { - logger.error("UE pool empty. Ignoring RACH attempt."); - return SRSRAN_INVALID_RNTI; - } - uint16_t rnti = ue_ptr->get_rnti(); + ue* inserted_ue = nullptr; + uint16_t rnti = SRSRAN_INVALID_RNTI; - // Add UE to map + do { + // Assign new RNTI + rnti = FIRST_RNTI + (ue_counter.fetch_add(1, std::memory_order_relaxed) % 60000); + + // Pre-check if rnti is valid { - srsran::rwlock_write_guard lock(rwlock); - if (not started) { - logger.info("RACH ignored as eNB is being shutdown"); - return SRSRAN_INVALID_RNTI; - } - if (ue_db.size() >= SRSENB_MAX_UES) { + srsran::rwlock_read_guard read_lock(rwlock); + if (ue_db.full()) { logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", SRSENB_MAX_UES); return SRSRAN_INVALID_RNTI; } - auto ret = ue_db.insert(rnti, std::move(ue_ptr)); - if (ret) { - inserted_ue = ret.value()->second.get(); - } else { - logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti); + if (not is_valid_rnti_unprotected(rnti)) { + continue; } } - // Allocate one new UE object in advance - srsran::get_background_workers().push_task([this]() { prealloc_ue(1); }); + // Allocate and initialize UE object + unique_rnti_ptr ue_ptr = make_rnti_obj( + rnti, rnti, enb_cc_idx, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get()); + // Add UE to rnti map + srsran::rwlock_write_guard rw_lock(rwlock); + if (not is_valid_rnti_unprotected(rnti)) { + continue; + } + auto ret = ue_db.insert(rnti, std::move(ue_ptr)); + if (ret.has_value()) { + inserted_ue = ret.value()->second.get(); + } else { + logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti); + } } while (inserted_ue == nullptr); - // RNTI allocation was successful - uint16_t rnti = inserted_ue->get_rnti(); - // Set PCAP if available if (pcap != nullptr) { inserted_ue->start_pcap(pcap); @@ -510,19 +525,32 @@ uint16_t mac::allocate_ue() return rnti; } -uint16_t mac::reserve_new_crnti(const sched_interface::ue_cfg_t& ue_cfg) +bool mac::is_pending_pdcch_order_prach(const uint32_t preamble_idx, uint16_t& rnti) { - uint16_t rnti = allocate_ue(); + for (auto it = pending_po_prachs.begin(); it != pending_po_prachs.end();) { + auto& pending_po_prach = *it; + if (pending_po_prach.preamble_idx == preamble_idx) { + rnti = pending_po_prach.crnti; + // delete pending PDCCH PRACH from vector + it = pending_po_prachs.erase(it); + return true; + } + ++it; + } + return false; +} + +uint16_t mac::reserve_new_crnti(const sched_interface::ue_cfg_t& uecfg) +{ + uint16_t rnti = allocate_ue(uecfg.supported_cc_list[0].enb_cc_idx); if (rnti == SRSRAN_INVALID_RNTI) { return rnti; } // Add new user to the scheduler so that it can RX/TX SRB0 - if (scheduler.ue_cfg(rnti, ue_cfg) != SRSRAN_SUCCESS) { - logger.error("Registering new user rnti=0x%x to SCHED", rnti); + if (ue_cfg(rnti, &uecfg) != SRSRAN_SUCCESS) { return SRSRAN_INVALID_RNTI; } - return rnti; } @@ -532,12 +560,17 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx logger.set_context(tti); auto rach_tprof_meas = rach_tprof.start(); - uint16_t rnti = allocate_ue(); - if (rnti == SRSRAN_INVALID_RNTI) { - return; - } + stack_task_queue.push([this, tti, enb_cc_idx, preamble_idx, time_adv, rach_tprof_meas]() mutable { + uint16_t rnti = 0; + // check if this is a PRACH from a PDCCH order + bool is_po_prach = is_pending_pdcch_order_prach(preamble_idx, rnti); + if (!is_po_prach) { + rnti = allocate_ue(enb_cc_idx); + if (rnti == SRSRAN_INVALID_RNTI) { + return; + } + } - stack_task_queue.push([this, rnti, tti, enb_cc_idx, preamble_idx, time_adv, rach_tprof_meas]() mutable { rach_tprof_meas.defer_stop(); // Generate RAR data sched_interface::dl_sched_rar_info_t rar_info = {}; @@ -550,59 +583,66 @@ void mac::rach_detected(uint32_t tti, uint32_t enb_cc_idx, uint32_t preamble_idx // Log this event. ++detected_rachs[enb_cc_idx]; - // Add new user to the scheduler so that it can RX/TX SRB0 - sched_interface::ue_cfg_t ue_cfg = {}; - ue_cfg.supported_cc_list.emplace_back(); - ue_cfg.supported_cc_list.back().active = true; - ue_cfg.supported_cc_list.back().enb_cc_idx = enb_cc_idx; - ue_cfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - ue_cfg.supported_cc_list[0].dl_cfg.tm = SRSRAN_TM1; - if (scheduler.ue_cfg(rnti, ue_cfg) != SRSRAN_SUCCESS) { - logger.error("Registering new user rnti=0x%x to SCHED", rnti); - ue_rem(rnti); - return; - } + // If this is a PRACH from a PDCCH order, the user already exists + if (not is_po_prach) { + // Add new user to the scheduler so that it can RX/TX SRB0 + sched_interface::ue_cfg_t uecfg = {}; + uecfg.supported_cc_list.emplace_back(); + uecfg.supported_cc_list.back().active = true; + uecfg.supported_cc_list.back().enb_cc_idx = enb_cc_idx; + uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH; + uecfg.supported_cc_list[0].dl_cfg.tm = SRSRAN_TM1; + if (ue_cfg(rnti, &uecfg) != SRSRAN_SUCCESS) { + return; + } - // Register new user in RRC - if (rrc_h->add_user(rnti, ue_cfg) == SRSRAN_ERROR) { - ue_rem(rnti); - return; + // Register new user in RRC + if (rrc_h->add_user(rnti, uecfg) == SRSRAN_ERROR) { + ue_rem(rnti); + return; + } } // Trigger scheduler RACH scheduler.dl_rach_info(enb_cc_idx, rar_info); - logger.info( - "RACH: tti=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x", tti, enb_cc_idx, preamble_idx, time_adv, rnti); - srsran::console("RACH: tti=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", + auto get_pci = [this, enb_cc_idx]() { + srsran::rwlock_read_guard lock(rwlock); + return (enb_cc_idx < cell_config.size()) ? cell_config[enb_cc_idx].cell.id : 0; + }; + uint32_t pci = get_pci(); + logger.info("%sRACH: tti=%d, cc=%d, pci=%d, preamble=%d, offset=%d, temp_crnti=0x%x", + (is_po_prach) ? "PDCCH order " : "", + tti, + enb_cc_idx, + pci, + preamble_idx, + time_adv, + rnti); + srsran::console("%sRACH: tti=%d, cc=%d, pci=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", + (is_po_prach) ? "PDCCH order " : "", tti, enb_cc_idx, + pci, preamble_idx, time_adv, rnti); }); } -void mac::prealloc_ue(uint32_t nof_ue) -{ - for (uint32_t i = 0; i < nof_ue; i++) { - std::unique_ptr ptr = std::unique_ptr(new ue( - allocate_rnti(), args.nof_prb, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get())); - if (not ue_pool.try_push(std::move(ptr))) { - logger.info("Cannot preallocate more UEs as pool is full"); - return; - } - } -} - int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list) { if (!started) { return 0; } - trace_complete_event("mac::get_dl_sched", "total_time"); + trace_threshold_complete_event("mac::get_dl_sched", "total_time", std::chrono::microseconds(100)); logger.set_context(TTI_SUB(tti_tx_dl, FDD_HARQ_DELAY_UL_MS)); + if (do_padding) { + add_padding(); + } + + srsran::rwlock_read_guard lock(rwlock); for (uint32_t enb_cc_idx = 0; enb_cc_idx < cell_config.size(); enb_cc_idx++) { // Run scheduler with current info @@ -615,68 +655,62 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list) int n = 0; dl_sched_t* dl_sched_res = &dl_sched_res_list[enb_cc_idx]; - { - srsran::rwlock_read_guard lock(rwlock); + // Copy data grants + for (uint32_t i = 0; i < sched_result.data.size(); i++) { + uint32_t tb_count = 0; - // Copy data grants - for (uint32_t i = 0; i < sched_result.data.size(); i++) { - uint32_t tb_count = 0; + // Get UE + uint16_t rnti = sched_result.data[i].dci.rnti; - // Get UE - uint16_t rnti = sched_result.data[i].dci.rnti; + if (ue_db.contains(rnti)) { + // Copy dci info + dl_sched_res->pdsch[n].dci = sched_result.data[i].dci; - if (ue_db.contains(rnti)) { - // Copy dci info - dl_sched_res->pdsch[n].dci = sched_result.data[i].dci; + for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { + dl_sched_res->pdsch[n].softbuffer_tx[tb] = + ue_db[rnti]->get_tx_softbuffer(enb_cc_idx, sched_result.data[i].dci.pid, tb); - for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { - dl_sched_res->pdsch[n].softbuffer_tx[tb] = - ue_db[rnti]->get_tx_softbuffer(sched_result.data[i].dci.ue_cc_idx, sched_result.data[i].dci.pid, tb); - - // If the Rx soft-buffer is not given, abort transmission - if (dl_sched_res->pdsch[n].softbuffer_tx[tb] == nullptr) { - continue; - } - - if (sched_result.data[i].nof_pdu_elems[tb] > 0) { - /* Get PDU if it's a new transmission */ - dl_sched_res->pdsch[n].data[tb] = ue_db[rnti]->generate_pdu(sched_result.data[i].dci.ue_cc_idx, - sched_result.data[i].dci.pid, - tb, - sched_result.data[i].pdu[tb], - sched_result.data[i].nof_pdu_elems[tb], - sched_result.data[i].tbs[tb]); - - if (!dl_sched_res->pdsch[n].data[tb]) { - logger.error("Error! PDU was not generated (rnti=0x%04x, tb=%d)", rnti, tb); - } - - if (pcap) { - pcap->write_dl_crnti( - dl_sched_res->pdsch[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti_tx_dl, enb_cc_idx); - } - if (pcap_net) { - pcap_net->write_dl_crnti( - dl_sched_res->pdsch[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti_tx_dl, enb_cc_idx); - } - } else { - /* TB not enabled OR no data to send: set pointers to NULL */ - dl_sched_res->pdsch[n].data[tb] = nullptr; - } - - tb_count++; + // If the Rx soft-buffer is not given, abort transmission + if (dl_sched_res->pdsch[n].softbuffer_tx[tb] == nullptr) { + continue; } - // Count transmission if at least one TB has succesfully added - if (tb_count > 0) { - n++; + if (sched_result.data[i].nof_pdu_elems[tb] > 0) { + /* Get PDU if it's a new transmission */ + dl_sched_res->pdsch[n].data[tb] = ue_db[rnti]->generate_pdu(enb_cc_idx, + sched_result.data[i].dci.pid, + tb, + sched_result.data[i].pdu[tb], + sched_result.data[i].nof_pdu_elems[tb], + sched_result.data[i].tbs[tb]); + + if (!dl_sched_res->pdsch[n].data[tb]) { + logger.error("Error! PDU was not generated (rnti=0x%04x, tb=%d)", rnti, tb); + } + + if (pcap) { + pcap->write_dl_crnti( + dl_sched_res->pdsch[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti_tx_dl, enb_cc_idx); + } + if (pcap_net) { + pcap_net->write_dl_crnti( + dl_sched_res->pdsch[n].data[tb], sched_result.data[i].tbs[tb], rnti, true, tti_tx_dl, enb_cc_idx); + } + } else { + /* TB not enabled OR no data to send: set pointers to NULL */ + dl_sched_res->pdsch[n].data[tb] = nullptr; } - } else { - logger.warning("Invalid DL scheduling result. User 0x%x does not exist", rnti); + + tb_count++; } - } - // No more uses of shared ue_db beyond here + // Count transmission if at least one TB has successfully added + if (tb_count > 0) { + n++; + } + } else { + logger.warning("Invalid DL scheduling result. User 0x%x does not exist", rnti); + } } // Copy RAR grants @@ -736,7 +770,7 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list) } else { dl_sched_res->pdsch[n].softbuffer_tx[0] = &common_buffers[enb_cc_idx].pcch_softbuffer_tx; dl_sched_res->pdsch[n].data[0] = common_buffers[enb_cc_idx].pcch_payload_buffer; - rlc_h->read_pdu_pcch(common_buffers[enb_cc_idx].pcch_payload_buffer, pcch_payload_buffer_len); + rrc_h->read_pdu_pcch(tti_tx_dl, common_buffers[enb_cc_idx].pcch_payload_buffer, pcch_payload_buffer_len); if (pcap) { pcap->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.bc[i].tbs, true, tti_tx_dl, enb_cc_idx); @@ -749,6 +783,24 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list) n++; } + // Copy PDCCH order grants + for (uint32_t i = 0; i < sched_result.po.size(); i++) { + uint16_t rnti = sched_result.po[i].dci.rnti; + if (ue_db.contains(rnti)) { + // Copy dci info + dl_sched_res->pdsch[n].dci = sched_result.po[i].dci; + if (pcap) { + pcap->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.po[i].tbs, true, tti_tx_dl, enb_cc_idx); + } + if (pcap_net) { + pcap_net->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.po[i].tbs, true, tti_tx_dl, enb_cc_idx); + } + n++; + } else { + logger.warning("Invalid PDCCH order scheduling result. User 0x%x does not exist", rnti); + } + } + dl_sched_res->nof_grants = n; // Number of CCH symbols @@ -756,11 +808,8 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list) } // Count number of TTIs for all active users - { - srsran::rwlock_read_guard lock(rwlock); - for (auto& u : ue_db) { - u.second->metrics_cnt(); - } + for (auto& u : ue_db) { + u.second->metrics_cnt(); } return SRSRAN_SUCCESS; @@ -801,7 +850,8 @@ void mac::build_mch_sched(uint32_t tbs) int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res_list) { - dl_sched_t* dl_sched_res = &dl_sched_res_list[0]; + srsran::rwlock_read_guard lock(rwlock); + dl_sched_t* dl_sched_res = &dl_sched_res_list[0]; logger.set_context(tti); srsran_ra_tb_t mcs = {}; srsran_ra_tb_t mcs_data = {}; @@ -831,7 +881,6 @@ int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res ue_db[SRSRAN_MRNTI]->metrics_tx(true, mcs.tbs); dl_sched_res->pdsch[0].data[0] = ue_db[SRSRAN_MRNTI]->generate_mch_pdu(tti % SRSRAN_FDD_NOF_HARQ, mch, mch.num_mtch_sched + 1, mcs.tbs / 8); - } else { uint32_t current_lcid = 1; uint32_t mtch_index = 0; @@ -884,6 +933,9 @@ uint8_t* mac::assemble_rar(sched_interface::dl_sched_rar_grant_t* grants, srsran::rar_pdu* pdu = &rar_pdu_msg[rar_idx]; rar_payload[enb_cc_idx][rar_idx].clear(); pdu->init_tx(&rar_payload[enb_cc_idx][rar_idx], pdu_len); + if (args.prach_bi > 0 and args.prach_bi <= 12) { + pdu->set_backoff(args.prach_bi); + } for (uint32_t i = 0; i < nof_grants; i++) { srsran_dci_rar_pack(&grants[i].grant, grant_buffer); if (pdu->new_subh()) { @@ -914,6 +966,8 @@ int mac::get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res_list) logger.set_context(TTI_SUB(tti_tx_ul, FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS)); + srsran::rwlock_read_guard lock(rwlock); + // Execute UE FSMs (e.g. TA) for (auto& ue : ue_db) { ue.second->tic(); @@ -929,52 +983,45 @@ int mac::get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res_list) return SRSRAN_ERROR; } - { - srsran::rwlock_read_guard lock(rwlock); + // Copy DCI grants + phy_ul_sched_res->nof_grants = 0; + int n = 0; + for (uint32_t i = 0; i < sched_result.pusch.size(); i++) { + if (sched_result.pusch[i].tbs > 0) { + // Get UE + uint16_t rnti = sched_result.pusch[i].dci.rnti; - // Copy DCI grants - phy_ul_sched_res->nof_grants = 0; - int n = 0; - for (uint32_t i = 0; i < sched_result.pusch.size(); i++) { - if (sched_result.pusch[i].tbs > 0) { - // Get UE - uint16_t rnti = sched_result.pusch[i].dci.rnti; + if (ue_db.contains(rnti)) { + // Copy grant info + phy_ul_sched_res->pusch[n].current_tx_nb = sched_result.pusch[i].current_tx_nb; + phy_ul_sched_res->pusch[n].pid = TTI_RX(tti_tx_ul) % SRSRAN_FDD_NOF_HARQ; + phy_ul_sched_res->pusch[n].needs_pdcch = sched_result.pusch[i].needs_pdcch; + phy_ul_sched_res->pusch[n].dci = sched_result.pusch[i].dci; + phy_ul_sched_res->pusch[n].softbuffer_rx = ue_db[rnti]->get_rx_softbuffer(enb_cc_idx, tti_tx_ul); - if (ue_db.contains(rnti)) { - // Copy grant info - phy_ul_sched_res->pusch[n].current_tx_nb = sched_result.pusch[i].current_tx_nb; - phy_ul_sched_res->pusch[n].pid = TTI_RX(tti_tx_ul) % SRSRAN_FDD_NOF_HARQ; - phy_ul_sched_res->pusch[n].needs_pdcch = sched_result.pusch[i].needs_pdcch; - phy_ul_sched_res->pusch[n].dci = sched_result.pusch[i].dci; - phy_ul_sched_res->pusch[n].softbuffer_rx = - ue_db[rnti]->get_rx_softbuffer(sched_result.pusch[i].dci.ue_cc_idx, tti_tx_ul); - - // If the Rx soft-buffer is not given, abort reception - if (phy_ul_sched_res->pusch[n].softbuffer_rx == nullptr) { - continue; - } - - if (sched_result.pusch[n].current_tx_nb == 0) { - srsran_softbuffer_rx_reset_tbs(phy_ul_sched_res->pusch[n].softbuffer_rx, sched_result.pusch[i].tbs * 8); - } - phy_ul_sched_res->pusch[n].data = - ue_db[rnti]->request_buffer(tti_tx_ul, sched_result.pusch[i].dci.ue_cc_idx, sched_result.pusch[i].tbs); - if (phy_ul_sched_res->pusch[n].data) { - phy_ul_sched_res->nof_grants++; - } else { - logger.error("Grant for rnti=0x%x could not be allocated due to lack of buffers", rnti); - } - n++; - } else { - logger.warning("Invalid UL scheduling result. User 0x%x does not exist", rnti); + // If the Rx soft-buffer is not given, abort reception + if (phy_ul_sched_res->pusch[n].softbuffer_rx == nullptr) { + logger.warning("Failed to retrieve UL softbuffer for tti=%d, cc=%d", tti_tx_ul, enb_cc_idx); + continue; } + if (sched_result.pusch[n].current_tx_nb == 0) { + srsran_softbuffer_rx_reset_tbs(phy_ul_sched_res->pusch[n].softbuffer_rx, sched_result.pusch[i].tbs * 8); + } + phy_ul_sched_res->pusch[n].data = + ue_db[rnti]->request_buffer(tti_tx_ul, enb_cc_idx, sched_result.pusch[i].tbs); + if (phy_ul_sched_res->pusch[n].data) { + phy_ul_sched_res->nof_grants++; + } else { + logger.error("Grant for rnti=0x%x could not be allocated due to lack of buffers", rnti); + } + n++; } else { - logger.warning("Grant %d for rnti=0x%x has zero TBS", i, sched_result.pusch[i].dci.rnti); + logger.warning("Invalid UL scheduling result. User 0x%x does not exist", rnti); } + } else { + logger.warning("Grant %d for rnti=0x%x has zero TBS", i, sched_result.pusch[i].dci.rnti); } - - // No more uses of ue_db beyond here } // Copy PHICH actions @@ -991,22 +1038,13 @@ int mac::get_ul_sched(uint32_t tti_tx_ul, ul_sched_list_t& ul_sched_res_list) return SRSRAN_SUCCESS; } -bool mac::process_pdus() -{ - srsran::rwlock_read_guard lock(rwlock); - bool ret = false; - for (auto& u : ue_db) { - ret |= u.second->process_pdus(); - } - return ret; -} - void mac::write_mcch(const srsran::sib2_mbms_t* sib2_, const srsran::sib13_t* sib13_, const srsran::mcch_msg_t* mcch_, const uint8_t* mcch_payload, const uint8_t mcch_payload_length) { + srsran::rwlock_write_guard lock(rwlock); mcch = *mcch_; mch.num_mtch_sched = this->mcch.pmch_info_list[0].nof_mbms_session_info; for (uint32_t i = 0; i < mch.num_mtch_sched; ++i) { @@ -1016,21 +1054,25 @@ void mac::write_mcch(const srsran::sib2_mbms_t* sib2_, sib13 = *sib13_; memcpy(mcch_payload_buffer, mcch_payload, mcch_payload_length * sizeof(uint8_t)); current_mcch_length = mcch_payload_length; - ue_db[SRSRAN_MRNTI] = std::unique_ptr{ - new ue(SRSRAN_MRNTI, args.nof_prb, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get())}; + unique_rnti_ptr ue_ptr = make_rnti_obj( + SRSRAN_MRNTI, SRSRAN_MRNTI, 0, &scheduler, rrc_h, rlc_h, phy_h, logger, cells.size(), softbuffer_pool.get()); + + auto ret = ue_db.insert(SRSRAN_MRNTI, std::move(ue_ptr)); + if (!ret) { + logger.info("Failed to allocate rnti=0x%x.for eMBMS", SRSRAN_MRNTI); + } rrc_h->add_user(SRSRAN_MRNTI, {}); } -bool mac::check_ue_exists(uint16_t rnti) +// Internal helper function, caller must hold UE DB rwlock +bool mac::check_ue_active(uint16_t rnti) { if (not ue_db.contains(rnti)) { - if (not ues_to_rem.count(rnti)) { - logger.error("User rnti=0x%x not found", rnti); - } + logger.error("User rnti=0x%x not found", rnti); return false; } - return true; + return ue_db[rnti]->is_active(); } } // namespace srsenb diff --git a/srsenb/src/stack/mac/mac_nr.cc b/srsenb/src/stack/mac/mac_nr.cc deleted file mode 100644 index f1791bd23..000000000 --- a/srsenb/src/stack/mac/mac_nr.cc +++ /dev/null @@ -1,280 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsenb/hdr/stack/mac/mac_nr.h" -#include "srsran/common/buffer_pool.h" -#include "srsran/common/log_helper.h" -#include -#include -#include -#include - -namespace srsenb { - -mac_nr::mac_nr() : logger(srslog::fetch_basic_logger("MAC-NR")) {} - -mac_nr::~mac_nr() -{ - stop(); -} - -int mac_nr::init(const mac_nr_args_t& args_, - phy_interface_stack_nr* phy_, - stack_interface_mac* stack_, - rlc_interface_mac_nr* rlc_, - rrc_interface_mac_nr* rrc_) -{ - args = args_; - - phy_h = phy_; - stack_h = stack_; - rlc_h = rlc_; - rrc_h = rrc_; - - logger.set_level(srslog::str_to_basic_level(args.log_level)); - logger.set_hex_dump_max_size(args.log_hex_limit); - - if (args.pcap.enable) { - pcap = std::unique_ptr(new srsran::mac_pcap()); - pcap->open(args.pcap.filename); - } - - bcch_bch_payload = srsran::make_byte_buffer(); - if (bcch_bch_payload == nullptr) { - return SRSRAN_ERROR; - } - - // allocate 8 tx buffers for UE (TODO: as we don't handle softbuffers why do we need so many buffers) - for (int i = 0; i < SRSRAN_FDD_NOF_HARQ; i++) { - srsran::unique_byte_buffer_t buffer = srsran::make_byte_buffer(); - if (buffer == nullptr) { - return SRSRAN_ERROR; - } - ue_tx_buffer.emplace_back(std::move(buffer)); - } - - ue_rlc_buffer = srsran::make_byte_buffer(); - if (ue_rlc_buffer == nullptr) { - return SRSRAN_ERROR; - } - - logger.info("Started"); - - started = true; - - return SRSRAN_SUCCESS; -} - -void mac_nr::stop() -{ - if (started) { - if (pcap != nullptr) { - pcap->close(); - } - - started = false; - } -} - -void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics) {} - -// Fills both, DL_CONFIG.request and TX.request structs -void mac_nr::get_dl_config(const uint32_t tti, - phy_interface_stack_nr::dl_config_request_t& config_request, - phy_interface_stack_nr::tx_request_t& tx_request) -{ - // send MIB over BCH every 80ms - if (tti % 80 == 0) { - // try to read BCH PDU from RRC - if (rrc_h->read_pdu_bcch_bch(tti, bcch_bch_payload) == SRSRAN_SUCCESS) { - logger.info("Adding BCH in TTI=%d", tti); - tx_request.pdus[tx_request.nof_pdus].pbch.mib_present = true; - tx_request.pdus[tx_request.nof_pdus].data[0] = bcch_bch_payload->msg; - tx_request.pdus[tx_request.nof_pdus].length = bcch_bch_payload->N_bytes; - tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus; - tx_request.nof_pdus++; - - if (pcap) { - pcap->write_dl_bch(bcch_bch_payload->msg, bcch_bch_payload->N_bytes, 0xffff, 0, tti); - } - } else { - logger.error("Couldn't read BCH payload from RRC"); - } - } - - // Schedule SIBs - for (auto& sib : bcch_dlsch_payload) { - if (sib.payload->N_bytes > 0) { - if (tti % (sib.periodicity * 10) == 0) { - logger.info("Adding SIB %d in TTI=%d", sib.index, tti); - - tx_request.pdus[tx_request.nof_pdus].data[0] = sib.payload->msg; - tx_request.pdus[tx_request.nof_pdus].length = sib.payload->N_bytes; - tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus; - - if (pcap) { - pcap->write_dl_si_rnti_nr(sib.payload->msg, sib.payload->N_bytes, 0xffff, 0, tti); - } - - tx_request.nof_pdus++; - } - } - } - - // Add MAC padding if TTI is empty - if (tx_request.nof_pdus == 0) { - uint32_t buffer_index = tti % SRSRAN_FDD_NOF_HARQ; - - ue_tx_buffer.at(buffer_index)->clear(); - ue_tx_pdu.init_tx(ue_tx_buffer.at(buffer_index).get(), args.tb_size); - - // read RLC PDU - ue_rlc_buffer->clear(); - int pdu_len = rlc_h->read_pdu(args.rnti, 4, ue_rlc_buffer->msg, args.tb_size - 2); - - // Only create PDU if RLC has something to tx - if (pdu_len > 0) { - logger.info("Adding MAC PDU for RNTI=%d", args.rnti); - ue_rlc_buffer->N_bytes = pdu_len; - logger.info(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes); - - // add to MAC PDU and pack - ue_tx_pdu.add_sdu(4, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes); - ue_tx_pdu.pack(); - - logger.debug(ue_tx_buffer.at(buffer_index)->msg, - ue_tx_buffer.at(buffer_index)->N_bytes, - "Generated MAC PDU (%d B)", - ue_tx_buffer.at(buffer_index)->N_bytes); - - tx_request.pdus[tx_request.nof_pdus].data[0] = ue_tx_buffer.at(buffer_index)->msg; - tx_request.pdus[tx_request.nof_pdus].length = ue_tx_buffer.at(buffer_index)->N_bytes; - tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus; - - if (pcap) { - pcap->write_dl_crnti_nr(tx_request.pdus[tx_request.nof_pdus].data[0], - tx_request.pdus[tx_request.nof_pdus].length, - args.rnti, - buffer_index, - tti); - } - - tx_request.nof_pdus++; - } - } - - config_request.tti = tti; - tx_request.tti = tti; -} - -int mac_nr::sf_indication(const uint32_t tti) -{ - phy_interface_stack_nr::dl_config_request_t config_request = {}; - phy_interface_stack_nr::tx_request_t tx_request = {}; - - // step MAC TTI - logger.set_context(tti); - - get_dl_config(tti, config_request, tx_request); - - // send DL_CONFIG.request - phy_h->dl_config_request(config_request); - - // send TX.request - phy_h->tx_request(tx_request); - - return SRSRAN_SUCCESS; -} - -int mac_nr::rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& rx_data) -{ - // push received PDU on queue - if (rx_data.tb != nullptr) { - if (pcap) { - pcap->write_ul_crnti_nr(rx_data.tb->msg, rx_data.tb->N_bytes, rx_data.rnti, true, rx_data.tti); - } - ue_rx_pdu_queue.push(std::move(rx_data.tb)); - } - - // inform stack that new PDUs may have been received - stack_h->process_pdus(); - - return SRSRAN_SUCCESS; -} - -/** - * Called from the main stack thread to process received PDUs - */ -void mac_nr::process_pdus() -{ - while (started and not ue_rx_pdu_queue.empty()) { - srsran::unique_byte_buffer_t pdu = ue_rx_pdu_queue.wait_pop(); - /// TODO; delegate to demux class - handle_pdu(std::move(pdu)); - } -} - -int mac_nr::handle_pdu(srsran::unique_byte_buffer_t pdu) -{ - logger.info(pdu->msg, pdu->N_bytes, "Handling MAC PDU (%d B)", pdu->N_bytes); - - ue_rx_pdu.init_rx(true); - if (ue_rx_pdu.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) { - return SRSRAN_ERROR; - } - - for (uint32_t i = 0; i < ue_rx_pdu.get_num_subpdus(); ++i) { - srsran::mac_sch_subpdu_nr subpdu = ue_rx_pdu.get_subpdu(i); - logger.info("Handling subPDU %d/%d: lcid=%d, sdu_len=%d", - i, - ue_rx_pdu.get_num_subpdus(), - subpdu.get_lcid(), - subpdu.get_sdu_length()); - - // rlc_h->write_pdu(args.rnti, subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length()); - } - return SRSRAN_SUCCESS; -} - -int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg) -{ - cfg = *cell_cfg; - - // read SIBs from RRC (SIB1 for now only) - for (int i = 0; i < srsenb::sched_interface::MAX_SIBS; i++) { - if (cell_cfg->sibs->len > 0) { - sib_info_t sib = {}; - sib.index = i; - sib.periodicity = cell_cfg->sibs->period_rf; - sib.payload = srsran::make_byte_buffer(); - if (rrc_h->read_pdu_bcch_dlsch(sib.index, sib.payload) != SRSRAN_SUCCESS) { - logger.error("Couldn't read SIB %d from RRC", sib.index); - } - - logger.info("Including SIB %d into SI scheduling", sib.index); - bcch_dlsch_payload.push_back(std::move(sib)); - } - } - - return SRSRAN_SUCCESS; -} - -} // namespace srsenb diff --git a/srsenb/src/stack/mac/sched.cc b/srsenb/src/stack/mac/sched.cc index f3616ff45..95cfdbbf5 100644 --- a/srsenb/src/stack/mac/sched.cc +++ b/srsenb/src/stack/mac/sched.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -116,14 +116,14 @@ int sched::ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t& ue_cfg) // Add new user case std::unique_ptr ue{new sched_ue(rnti, sched_cell_params, ue_cfg)}; std::lock_guard lock(sched_mutex); - ue_db.insert(std::make_pair(rnti, std::move(ue))); + ue_db.insert(rnti, std::move(ue)); return SRSRAN_SUCCESS; } int sched::ue_rem(uint16_t rnti) { std::lock_guard lock(sched_mutex); - if (ue_db.count(rnti) > 0) { + if (ue_db.contains(rnti)) { ue_db.erase(rnti); } else { Error("User rnti=0x%x not found", rnti); @@ -145,7 +145,7 @@ void sched::phy_config_enabled(uint16_t rnti, bool enabled) rnti, [this, enabled](sched_ue& ue) { ue.phy_config_enabled(last_tti, enabled); }, __PRETTY_FUNCTION__); } -int sched::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const sched_interface::ue_bearer_cfg_t& cfg_) +int sched::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, const mac_lc_ch_cfg_t& cfg_) { return ue_db_access_locked(rnti, [lc_id, cfg_](sched_ue& ue) { ue.set_bearer_cfg(lc_id, cfg_); }); } @@ -174,9 +174,9 @@ uint32_t sched::get_ul_buffer(uint16_t rnti) return ret; } -int sched::dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) +int sched::dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t prio_tx_queue) { - return ue_db_access_locked(rnti, [&](sched_ue& ue) { ue.dl_buffer_state(lc_id, tx_queue, retx_queue); }); + return ue_db_access_locked(rnti, [&](sched_ue& ue) { ue.dl_buffer_state(lc_id, tx_queue, prio_tx_queue); }); } int sched::dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code, uint32_t nof_cmds) @@ -218,6 +218,13 @@ int sched::dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_ rnti, [tti, enb_cc_idx, cqi_value](sched_ue& ue) { ue.set_dl_cqi(tti_point{tti}, enb_cc_idx, cqi_value); }); } +int sched::dl_sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t enb_cc_idx, uint32_t sb_idx, uint32_t cqi_value) +{ + return ue_db_access_locked(rnti, [tti, enb_cc_idx, cqi_value, sb_idx](sched_ue& ue) { + ue.set_dl_sb_cqi(tti_point{tti}, enb_cc_idx, sb_idx, cqi_value); + }); +} + int sched::dl_rach_info(uint32_t enb_cc_idx, dl_sched_rar_info_t rar_info) { std::lock_guard lock(sched_mutex); @@ -240,10 +247,10 @@ int sched::ul_buffer_add(uint16_t rnti, uint32_t lcid, uint32_t bytes) return ue_db_access_locked(rnti, [lcid, bytes](sched_ue& ue) { ue.ul_buffer_add(lcid, bytes); }); } -int sched::ul_phr(uint16_t rnti, int phr) +int sched::ul_phr(uint16_t rnti, int phr, uint32_t ul_nof_prb) { return ue_db_access_locked( - rnti, [phr](sched_ue& ue) { ue.ul_phr(phr); }, __PRETTY_FUNCTION__); + rnti, [phr, ul_nof_prb](sched_ue& ue) { ue.ul_phr(phr, ul_nof_prb); }, __PRETTY_FUNCTION__); } int sched::ul_sr_info(uint32_t tti, uint16_t rnti) @@ -276,18 +283,28 @@ std::array sched::get_enb_ue_cc_map(uint16_t rnti) return ret; } -std::array sched::get_scell_activation_mask(uint16_t rnti) +std::array sched::get_enb_ue_activ_cc_map(uint16_t rnti) { - std::array scell_mask = {}; - ue_db_access_locked(rnti, [this, &scell_mask](sched_ue& ue) { - for (size_t enb_cc_idx = 0; enb_cc_idx < carrier_schedulers.size(); ++enb_cc_idx) { - const sched_ue_cell* cc_ue = ue.find_ue_carrier(enb_cc_idx); - if (cc_ue != nullptr and (cc_ue->cc_state() == cc_st::active or cc_ue->cc_state() == cc_st::activating)) { - scell_mask[cc_ue->get_ue_cc_idx()] = true; - } - } - }); - return scell_mask; + std::array ret{}; + ret.fill(-1); // -1 for inactive & non-existent carriers + ue_db_access_locked( + rnti, + [this, &ret](sched_ue& ue) { + for (size_t enb_cc_idx = 0; enb_cc_idx < carrier_schedulers.size(); ++enb_cc_idx) { + const sched_ue_cell* cc_ue = ue.find_ue_carrier(enb_cc_idx); + if (cc_ue != nullptr and (cc_ue->cc_state() == cc_st::active or cc_ue->cc_state() == cc_st::activating)) { + ret[enb_cc_idx] = cc_ue->get_ue_cc_idx(); + } + } + }, + __PRETTY_FUNCTION__); + return ret; +} + +int sched::set_pdcch_order(uint32_t enb_cc_idx, dl_sched_po_info_t pdcch_order_info) +{ + std::lock_guard lock(sched_mutex); + return carrier_schedulers[enb_cc_idx]->pdcch_order_info(pdcch_order_info); } /******************************************************* @@ -359,6 +376,12 @@ bool sched::is_generated(srsran::tti_point tti_rx, uint32_t enb_cc_idx) const return sched_results.has_sf(tti_rx) and sched_results.get_sf(tti_rx)->is_generated(enb_cc_idx); } +int sched::metrics_read(uint16_t rnti, mac_ue_metrics_t& metrics) +{ + return ue_db_access_locked( + rnti, [&metrics](sched_ue& ue) { ue.metrics_read(metrics); }, "metrics_read"); +} + // Common way to access ue_db elements in a read locking way template int sched::ue_db_access_locked(uint16_t rnti, Func&& f, const char* func_name, bool log_fail) diff --git a/srsenb/src/stack/mac/sched_carrier.cc b/srsenb/src/stack/mac/sched_carrier.cc index e0eaf5368..39c5aec66 100644 --- a/srsenb/src/stack/mac/sched_carrier.cc +++ b/srsenb/src/stack/mac/sched_carrier.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,7 +25,7 @@ #include "srsenb/hdr/stack/mac/schedulers/sched_time_rr.h" #include "srsran/common/standard_streams.h" #include "srsran/common/string_helpers.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" namespace srsenb { @@ -133,8 +133,8 @@ void bc_sched::alloc_sibs(sf_sched* tti_sched) } } if (ret != alloc_result::success) { - logger.warning("SCHED: Could not allocate SIB=%d, len=%d. Cause: %s", - sib_idx + 1, + logger.warning("SCHED: Could not allocate SI message, idx=%d, len=%d. Cause: %s", + sib_idx, cc_cfg->cfg.sibs[sib_idx].len, to_string(ret)); } @@ -349,6 +349,7 @@ void sched::carrier_sched::reset() { ra_sched_ptr.reset(); bc_sched_ptr.reset(); + pending_pdcch_orders.clear(); } void sched::carrier_sched::carrier_cfg(const sched_cell_params_t& cell_params_) @@ -411,6 +412,9 @@ const cc_sched_result& sched::carrier_sched::generate_tti_result(tti_point tti_r /* Schedule Msg3 */ sf_sched* sf_msg3_sched = get_sf_sched(tti_rx + MSG3_DELAY_MS); ra_sched_ptr->ul_sched(tti_sched, sf_msg3_sched); + + /* Schedule PDCCH orders */ + pdcch_order_sched(tti_sched); } /* Prioritize PDCCH scheduling for DL and UL data in a RoundRobin fashion */ @@ -448,7 +452,7 @@ void sched::carrier_sched::alloc_dl_users(sf_sched* tti_result) // NOTE: In case of 6 PRBs, do not transmit if there is going to be a PRACH in the UL to avoid collisions if (cc_cfg->nof_prb() == 6) { tti_point tti_rx_ack = to_tx_dl_ack(tti_result->get_tti_rx()); - if (srsran_prach_tti_opportunity_config_fdd(cc_cfg->cfg.prach_config, tti_rx_ack.to_uint(), -1)) { + if (srsran_prach_in_window_config_fdd(cc_cfg->cfg.prach_config, tti_rx_ack.to_uint(), -1)) { tti_result->reserve_dl_rbgs(0, cc_cfg->nof_rbgs); } } @@ -490,4 +494,38 @@ int sched::carrier_sched::dl_rach_info(dl_sched_rar_info_t rar_info) return ra_sched_ptr->dl_rach_info(rar_info); } +int sched::carrier_sched::pdcch_order_info(dl_sched_po_info_t pdcch_order_info) +{ + logger.info("SCHED: New PDCCH order preamble=%d, prach_mask_idx=%d crnti=0x%x", + pdcch_order_info.preamble_idx, + pdcch_order_info.prach_mask_idx, + pdcch_order_info.crnti); + + // create new PDCCH order + pending_pdcch_orders.push_back(pdcch_order_info); + + return SRSRAN_SUCCESS; +} + +void sched::carrier_sched::pdcch_order_sched(sf_sched* tti_sched) +{ + for (auto it = pending_pdcch_orders.begin(); it != pending_pdcch_orders.end();) { + auto& pending_pdcch_order = *it; + + alloc_result ret = alloc_result::no_sch_space; + + rbg_interval rbg_interv = find_empty_rbg_interval(1, tti_sched->get_dl_mask()); + if (rbg_interv.length() == 1) { + ret = tti_sched->alloc_pdcch_order(pending_pdcch_order, po_aggr_level, rbg_interv); + } + + if (ret == alloc_result::success) { + it = pending_pdcch_orders.erase(it); + } else { + logger.warning("SCHED: Could not allocate PDCCH order, cause=%s", to_string(ret)); + ++it; + } + } +} + } // namespace srsenb diff --git a/srsenb/src/stack/mac/sched_grid.cc b/srsenb/src/stack/mac/sched_grid.cc index 94c9d200a..60b3b44b6 100644 --- a/srsenb/src/stack/mac/sched_grid.cc +++ b/srsenb/src/stack/mac/sched_grid.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,33 +25,6 @@ namespace srsenb { -const char* to_string(alloc_result result) -{ - switch (result) { - case alloc_result::success: - return "success"; - case alloc_result::sch_collision: - return "Collision with existing SCH allocations"; - case alloc_result::other_cause: - return "error"; - case alloc_result::no_cch_space: - return "No space available in PUCCH or PDCCH"; - case alloc_result::no_sch_space: - return "Requested number of PRBs not available"; - case alloc_result::no_rnti_opportunity: - return "rnti cannot be allocated (e.g. already allocated, no data, meas gap collision, carrier inactive, etc.)"; - case alloc_result::invalid_grant_params: - return "invalid grant arguments (e.g. invalid prb mask)"; - case alloc_result::invalid_coderate: - return "Effective coderate exceeds threshold"; - case alloc_result::no_grant_space: - return "Max number of allocations reached"; - default: - break; - } - return "unknown error"; -} - void sf_sched_result::new_tti(tti_point tti_rx_) { assert(tti_rx != tti_rx_); @@ -114,10 +87,17 @@ void sf_grid_t::init(const sched_cell_params_t& cell_params_) // Compute reserved PRBs for CQI, SR and HARQ-ACK, and store it in a bitmask pucch_mask.resize(cc_cfg->nof_prb()); - pucch_nrb = (cc_cfg->cfg.nrb_pucch > 0) ? (uint32_t)cc_cfg->cfg.nrb_pucch : 0; - srsran_pucch_cfg_t pucch_cfg = cell_params_.pucch_cfg_common; - pucch_cfg.n_pucch = cc_cfg->nof_cce_table[SRSRAN_NOF_CFI - 1] - 1 + cc_cfg->cfg.n1pucch_an; - pucch_nrb = std::max(pucch_nrb, srsran_pucch_m(&pucch_cfg, cc_cfg->cfg.cell.cp) / 2 + 1); + pucch_nrb = (cc_cfg->cfg.nrb_pucch > 0) ? (uint32_t)cc_cfg->cfg.nrb_pucch : 0; + srsran_pucch_cfg_t pucch_cfg = cell_params_.pucch_cfg_common; + uint32_t harq_pucch = 0; + if (cc_cfg->sched_cfg->pucch_harq_max_rb > 0) { + harq_pucch = cc_cfg->sched_cfg->pucch_harq_max_rb; + } else { + pucch_cfg.n_pucch = + cc_cfg->nof_cce_table[cell_params_.sched_cfg->max_nof_ctrl_symbols - 1] - 1 + cc_cfg->cfg.n1pucch_an; + harq_pucch = srsran_pucch_m(&pucch_cfg, cc_cfg->cfg.cell.cp) / 2 + 1; + } + pucch_nrb = std::max(pucch_nrb, harq_pucch); if (pucch_nrb > 0) { pucch_mask.fill(0, pucch_nrb); pucch_mask.fill(cc_cfg->nof_prb() - pucch_nrb, cc_cfg->nof_prb()); @@ -135,7 +115,7 @@ void sf_grid_t::new_tti(tti_point tti_rx_) ul_mask |= pucch_mask; // Reserve PRBs for PRACH - if (srsran_prach_tti_opportunity_config_fdd(cc_cfg->cfg.prach_config, to_tx_ul(tti_rx).to_uint(), -1)) { + if (srsran_prach_in_window_config_fdd(cc_cfg->cfg.prach_config, to_tx_ul(tti_rx).to_uint(), -1)) { prbmask_t prach_mask{cc_cfg->nof_prb()}; prach_mask.fill(cc_cfg->cfg.prach_freq_offset, cc_cfg->cfg.prach_freq_offset + 6); reserve_ul_prbs(prach_mask, false); // TODO: set to true once test sib.conf files are updated @@ -188,7 +168,7 @@ alloc_result sf_grid_t::alloc_dl(uint32_t aggr_idx, alloc_result sf_grid_t::alloc_dl_ctrl(uint32_t aggr_idx, rbg_interval rbg_range, alloc_type_t alloc_type) { if (alloc_type != alloc_type_t::DL_RAR and alloc_type != alloc_type_t::DL_BC and - alloc_type != alloc_type_t::DL_PCCH) { + alloc_type != alloc_type_t::DL_PCCH and alloc_type != alloc_type_t::DL_PDCCH_ORDER) { logger.error("SCHED: DL control allocations must be RAR/BC/PDCCH"); return alloc_result::other_cause; } @@ -345,6 +325,7 @@ void sf_sched::new_tti(tti_point tti_rx_, sf_sched_result* cc_results_) // reset internal state bc_allocs.clear(); rar_allocs.clear(); + po_allocs.clear(); data_allocs.clear(); ul_data_allocs.clear(); @@ -355,7 +336,7 @@ void sf_sched::new_tti(tti_point tti_rx_, sf_sched_result* cc_results_) // setup first prb to be used for msg3 alloc. Account for potential PRACH alloc last_msg3_prb = tti_alloc.get_pucch_width(); tti_point tti_msg3_alloc = to_tx_ul(tti_rx) + MSG3_DELAY_MS; - if (srsran_prach_tti_opportunity_config_fdd(cc_cfg->cfg.prach_config, tti_msg3_alloc.to_uint(), -1)) { + if (srsran_prach_in_window_config_fdd(cc_cfg->cfg.prach_config, tti_msg3_alloc.to_uint(), -1)) { last_msg3_prb = std::max(last_msg3_prb, cc_cfg->cfg.prach_freq_offset + 6); } } @@ -472,6 +453,40 @@ alloc_result sf_sched::alloc_rar(uint32_t aggr_lvl, const pending_rar_t& rar, rb return ret; } +alloc_result +sf_sched::alloc_pdcch_order(const sched_interface::dl_sched_po_info_t& po_cfg, uint32_t aggr_lvl, rbg_interval rbgs) +{ + if (po_allocs.full()) { + logger.warning("SCHED: Maximum number of PDCCH order allocations per TTI reached."); + return alloc_result::no_grant_space; + } + + uint32_t buf_pdcch_order = 7; // TODO get actual size + + // Allocate RBGs and PDCCH + alloc_result ret = tti_alloc.alloc_dl_ctrl(aggr_lvl, rbgs, alloc_type_t::DL_PDCCH_ORDER); + if (ret != alloc_result::success) { + return ret; + } + + po_alloc_t po_alloc; + po_alloc.po_grant.crnti = po_cfg.crnti; + po_alloc.po_grant.preamble_idx = po_cfg.preamble_idx; + po_alloc.po_grant.prach_mask_idx = po_cfg.prach_mask_idx; + po_alloc.po_grant.tbs = buf_pdcch_order; + + // Generate DCI for PDCCH order message + generate_pdcch_order_dci(po_alloc.po_grant, get_tti_tx_dl(), *cc_cfg, tti_alloc.get_cfi()); + + // Allocation Successful + po_alloc.dci_idx = tti_alloc.get_pdcch_grid().nof_allocs() - 1; + po_alloc.rbg_range = rbgs; + po_alloc.req_bytes = buf_pdcch_order; + po_allocs.push_back(po_alloc); + + return alloc_result::success; +} + bool is_periodic_cqi_expected(const sched_interface::ue_cfg_t& ue_cfg, tti_point tti_tx_ul) { for (const sched_interface::ue_cfg_t::cc_cfg_t& cc : ue_cfg.supported_cc_list) { @@ -504,23 +519,25 @@ alloc_result sf_sched::alloc_dl_user(sched_ue* user, const rbgmask_t& user_mask, return alloc_result::no_rnti_opportunity; } - // Check if allocation would cause segmentation - const dl_harq_proc& h = user->get_dl_harq(pid, cc_cfg->enb_cc_idx); - if (h.is_empty()) { - // It is newTx - rbg_interval r = user->get_required_dl_rbgs(cc_cfg->enb_cc_idx); - if (r.start() > user_mask.count()) { - logger.debug("SCHED: The number of RBGs allocated to rnti=0x%x will force segmentation", user->get_rnti()); - return alloc_result::invalid_grant_params; - } - } - srsran_dci_format_t dci_format = user->get_dci_format(); if (dci_format == SRSRAN_DCI_FORMAT1A and not is_contiguous(user_mask)) { logger.warning("SCHED: Can't use distributed RBGs for DCI format 1A"); return alloc_result::invalid_grant_params; } + // Check if allocation is too small to fit headers, BSR or would cause SRB0 segmentation + const dl_harq_proc& h = user->get_dl_harq(pid, cc_cfg->enb_cc_idx); + if (h.is_empty()) { + // It is newTx + srsran::interval req_bytes = user->get_requested_dl_bytes(get_enb_cc_idx()); + tbs_info tb = compute_mcs_and_tbs_lower_bound(*cc, get_tti_tx_dl(), user_mask, dci_format); + if ((int)req_bytes.start() > tb.tbs_bytes) { + logger.debug("SCHED: The number of RBGs allocated to rnti=0x%x is too small to fit essential control information", + user->get_rnti()); + return alloc_result::invalid_grant_params; + } + } + bool has_pusch_grant = is_ul_alloc(user->get_rnti()) or cc_results->is_ul_alloc(user->get_rnti()); // Check if there is space in the PUCCH for HARQ ACKs @@ -586,7 +603,7 @@ sf_sched::alloc_ul(sched_ue* user, prb_interval alloc, ul_alloc_t::type_t alloc_ // Check if there is no collision with measGap bool needs_pdcch = alloc_type == ul_alloc_t::ADAPT_RETX or (alloc_type == ul_alloc_t::NEWTX and not is_msg3); if (not user->pusch_enabled(get_tti_rx(), cc_cfg->enb_cc_idx, needs_pdcch)) { - logger.debug("SCHED: PDCCH would collide with rnti=0x%x Measurement Gap", user->get_rnti()); + logger.debug("SCHED: PDCCH/PUSCH would collide with rnti=0x%x Measurement Gap", user->get_rnti()); return alloc_result::no_rnti_opportunity; } @@ -630,12 +647,6 @@ alloc_result sf_sched::alloc_phich(sched_ue* user) { using phich_t = sched_interface::ul_sched_phich_t; - auto* ul_sf_result = &cc_results->get_cc(cc_cfg->enb_cc_idx)->ul_sched_result; - if (ul_sf_result->phich.full()) { - logger.warning("SCHED: Maximum number of PHICH allocations has been reached"); - return alloc_result::no_grant_space; - } - auto p = user->get_active_cell_index(cc_cfg->enb_cc_idx); if (not p.first) { // user does not support this carrier @@ -643,15 +654,45 @@ alloc_result sf_sched::alloc_phich(sched_ue* user) } ul_harq_proc* h = user->get_ul_harq(get_tti_tx_ul(), cc_cfg->enb_cc_idx); + if (not h->has_pending_phich()) { + // No PHICH pending + return alloc_result::no_rnti_opportunity; + } + + auto* ul_sf_result = &cc_results->get_cc(cc_cfg->enb_cc_idx)->ul_sched_result; + if (ul_sf_result->phich.full()) { + logger.warning( + "SCHED: UL skipped retx rnti=0x%x, pid=%d. Cause: No PHICH space left", user->get_rnti(), h->get_id()); + h->pop_pending_phich(); + return alloc_result::no_grant_space; + } + + if (not user->phich_enabled(get_tti_rx(), cc_cfg->enb_cc_idx)) { + // PHICH falls in measGap. PHICH hi=1 is assumed by UE. In case of NACK, the HARQ is going to be resumed later on. + bool ack = h->pop_pending_phich(); // empty pending PHICH + if (h->is_empty(0)) { + logger.debug("SCHED: PHICH hi=%d not sent for rnti=0x%x, cc=%d, pid=%d. Cause: PHICH-measGap collision", + (int)ack, + user->get_rnti(), + get_enb_cc_idx(), + h->get_id()); + } else { + // Note: Given that the UE assumes PHICH hi=1, it is not expecting PUSCH grants for tti_tx_ul. Requesting PDCCH + // for the UL Harq has the effect of forbidding PUSCH grants, since phich_tti == pdcch_tti. + h->request_pdcch(); + logger.info("SCHED: UL skipped retx rnti=0x%x, cc=%d, pid=%d. Cause: PHICH-measGap collision", + user->get_rnti(), + get_enb_cc_idx(), + h->get_id()); + } + return alloc_result::no_cch_space; + } /* Indicate PHICH acknowledgment if needed */ - if (h->has_pending_phich()) { - ul_sf_result->phich.emplace_back(); - ul_sf_result->phich.back().rnti = user->get_rnti(); - ul_sf_result->phich.back().phich = h->pop_pending_phich() ? phich_t::ACK : phich_t::NACK; - return alloc_result::success; - } - return alloc_result::no_rnti_opportunity; + ul_sf_result->phich.emplace_back(); + ul_sf_result->phich.back().rnti = user->get_rnti(); + ul_sf_result->phich.back().phich = h->pop_pending_phich() ? phich_t::ACK : phich_t::NACK; + return alloc_result::success; } void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& dci_result, @@ -695,8 +736,8 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& // Print Resulting DL Allocation fmt::memory_buffer str_buffer; fmt::format_to(str_buffer, - "SCHED: DL {} rnti=0x{:x}, cc={}, pid={}, mask=0x{:x}, dci=({}, {}), n_rtx={}, tbs={}, " - "buffer={}/{}, tti_tx_dl={}", + "SCHED: DL {} rnti=0x{:x}, cc={}, pid={}, mask=0x{:x}, dci=({}, {}), n_rtx={}, cfi={}, " + "tbs={}, buffer={}/{}, tti_tx_dl={}", is_newtx ? "tx" : "retx", user->get_rnti(), cc_cfg->enb_cc_idx, @@ -705,6 +746,7 @@ void sf_sched::set_dl_data_sched_result(const sf_cch_allocator::alloc_result_t& data->dci.location.L, data->dci.location.ncce, dl_harq.nof_retx(0) + dl_harq.nof_retx(1), + tti_alloc.get_cfi(), tbs, data_before, user->get_pending_dl_bytes(cc_cfg->enb_cc_idx), @@ -824,13 +866,13 @@ void sf_sched::set_ul_sched_result(const sf_cch_allocator::alloc_result_t& dci_r sched_interface::ul_sched_data_t& pusch = ul_result->pusch.back(); uint32_t total_data_before = user->get_pending_ul_data_total(get_tti_tx_ul(), cc_cfg->enb_cc_idx); int tbs = user->generate_format0(&pusch, - get_tti_tx_ul(), - cc_cfg->enb_cc_idx, - ul_alloc.alloc, - ul_alloc.needs_pdcch(), - cce_range, - ul_alloc.msg3_mcs, - uci_type); + get_tti_tx_ul(), + cc_cfg->enb_cc_idx, + ul_alloc.alloc, + ul_alloc.needs_pdcch(), + cce_range, + ul_alloc.msg3_mcs, + uci_type); ul_harq_proc* h = user->get_ul_harq(get_tti_tx_ul(), cc_cfg->enb_cc_idx); uint32_t new_pending_bytes = user->get_pending_ul_new_data(get_tti_tx_ul(), cc_cfg->enb_cc_idx); @@ -857,7 +899,8 @@ void sf_sched::set_ul_sched_result(const sf_cch_allocator::alloc_result_t& dci_r if (logger.info.enabled()) { fmt::memory_buffer str_buffer; fmt::format_to(str_buffer, - "SCHED: {} {} rnti=0x{:x}, cc={}, pid={}, dci=({},{}), prb={}, n_rtx={}, tbs={}, bsr={} ({}-{})", + "SCHED: {} {} rnti=0x{:x}, cc={}, pid={}, dci=({},{}), prb={}, n_rtx={}, cfi={}, tbs={}, bsr={} " + "({}-{}), tti_tx_ul={}", ul_alloc.is_msg3 ? "Msg3" : "UL", ul_alloc.is_retx() ? "retx" : "tx", user->get_rnti(), @@ -867,10 +910,12 @@ void sf_sched::set_ul_sched_result(const sf_cch_allocator::alloc_result_t& dci_r pusch.dci.location.ncce, ul_alloc.alloc, h->nof_retx(0), + tti_alloc.get_cfi(), tbs, new_pending_bytes, total_data_before, - old_pending_bytes); + old_pending_bytes, + get_tti_tx_ul().to_uint()); logger.info("%s", srsran::to_c_str(str_buffer)); } @@ -899,15 +944,23 @@ void sf_sched::generate_sched_results(sched_ue_list& ue_db) /* Resume UL HARQs with pending retxs that did not get allocated */ using phich_t = sched_interface::ul_sched_phich_t; auto& phich_list = cc_result->ul_sched_result.phich; - for (uint32_t i = 0; i < cc_result->ul_sched_result.phich.size(); ++i) { - auto& phich = phich_list[i]; - if (phich.phich == phich_t::NACK) { - auto& ue = *ue_db[phich.rnti]; - ul_harq_proc* h = ue.get_ul_harq(get_tti_tx_ul(), cc_cfg->enb_cc_idx); - if (not is_ul_alloc(ue.get_rnti()) and h != nullptr and not h->is_empty()) { - // There was a missed UL harq retx. Halt+Resume the HARQ - phich.phich = phich_t::ACK; - logger.debug("SCHED: rnti=0x%x UL harq pid=%d is being resumed", ue.get_rnti(), h->get_id()); + for (auto& ue_pair : ue_db) { + auto& ue = *ue_pair.second; + uint16_t rnti = ue.get_rnti(); + ul_harq_proc* h = ue.get_ul_harq(get_tti_tx_ul(), cc_cfg->enb_cc_idx); + if (h != nullptr and not h->is_empty() and not is_ul_alloc(rnti)) { + // There was a missed UL harq retx. Halt+Resume the HARQ + h->retx_skipped(); + auto same_rnti = [rnti](const phich_t& p) { return p.rnti == rnti; }; + phich_t* phich = std::find_if(phich_list.begin(), phich_list.end(), same_rnti); + if (phich != phich_list.end()) { + srsran_assert(phich->phich == phich_t::NACK, "Expected hi=0 in case of active UL HARQ that was not retx"); + logger.info("SCHED: UL skipped retx rnti=0x%x, pid=%d. Cause: %s", + ue.get_rnti(), + h->get_id(), + ue.pusch_enabled(get_tti_rx(), cc_cfg->enb_cc_idx, false) ? "lack of PHY resources" + : "PUSCH-measGap collision"); + phich->phich = phich_t::ACK; } } } @@ -933,6 +986,12 @@ void sf_sched::generate_sched_results(sched_ue_list& ue_db) log_rar_allocation(cc_result->dl_sched_result.rar.back(), rar_alloc.alloc_data.rbg_range); } + for (const auto& po_alloc : po_allocs) { + cc_result->dl_sched_result.po.emplace_back(po_alloc.po_grant); + cc_result->dl_sched_result.po.back().dci.location = dci_result[po_alloc.dci_idx]->dci_pos; + log_po_allocation(cc_result->dl_sched_result.po.back(), po_alloc.rbg_range, *cc_cfg); + } + set_dl_data_sched_result(dci_result, &cc_result->dl_sched_result, ue_db); set_ul_sched_result(dci_result, &cc_result->ul_sched_result, ue_db); diff --git a/srsenb/src/stack/mac/sched_helpers.cc b/srsenb/src/stack/mac/sched_helpers.cc index 8ef449454..3402e6f41 100644 --- a/srsenb/src/stack/mac/sched_helpers.cc +++ b/srsenb/src/stack/mac/sched_helpers.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -161,48 +161,6 @@ void log_phich_cc_results(srslog::basic_logger& logger, } } -prb_interval prb_interval::rbgs_to_prbs(const rbg_interval& rbgs, uint32_t cell_nof_prb) -{ - uint32_t P = srsran_ra_type0_P(cell_nof_prb); - return prb_interval{rbgs.start() * P, std::min(rbgs.stop() * P, cell_nof_prb)}; -} - -rbg_interval rbg_interval::rbgmask_to_rbgs(const rbgmask_t& mask) -{ - int rb_start = -1; - for (uint32_t i = 0; i < mask.size(); i++) { - if (rb_start == -1) { - if (mask.test(i)) { - rb_start = i; - } - } else { - if (!mask.test(i)) { - return rbg_interval(rb_start, i); - } - } - } - if (rb_start != -1) { - return rbg_interval(rb_start, mask.size()); - } else { - return rbg_interval(); - } -} - -prb_interval prb_interval::riv_to_prbs(uint32_t riv, uint32_t nof_prbs, int nof_vrbs) -{ - if (nof_vrbs < 0) { - nof_vrbs = nof_prbs; - } - uint32_t rb_start, l_crb; - srsran_ra_type2_from_riv(riv, &l_crb, &rb_start, nof_prbs, (uint32_t)nof_vrbs); - return {rb_start, rb_start + l_crb}; -} - -bool is_contiguous(const rbgmask_t& mask) -{ - return rbg_interval::rbgmask_to_rbgs(mask).length() == mask.count(); -} - /******************************************************* * Sched Params *******************************************************/ @@ -433,24 +391,28 @@ void generate_cce_location(srsran_regs_t* regs_, * DCI-specific helper functions *******************************************************/ -uint32_t -get_aggr_level(uint32_t nof_bits, uint32_t dl_cqi, uint32_t max_aggr_lvl, uint32_t cell_nof_prb, bool use_tbs_index_alt) +uint32_t get_aggr_level(uint32_t nof_bits, + uint32_t dl_cqi, + uint32_t min_aggr_lvl, + uint32_t max_aggr_lvl, + uint32_t cell_nof_prb, + bool use_tbs_index_alt) { - uint32_t l = 0; float max_coderate = srsran_cqi_to_coderate(dl_cqi, use_tbs_index_alt); - float coderate; - float factor = 1.5; - uint32_t l_max = 3; + float factor = 1.5; + uint32_t l_max = 3; if (cell_nof_prb == 6) { factor = 1.0; l_max = 2; } - l_max = SRSRAN_MIN(max_aggr_lvl, l_max); + l_max = std::min(max_aggr_lvl, l_max); - do { - coderate = srsran_pdcch_coderate(nof_bits, l); + uint32_t l = std::min(min_aggr_lvl, l_max); + float coderate = srsran_pdcch_coderate(nof_bits, l); + while (factor * coderate > max_coderate and l < l_max) { l++; - } while (l < l_max && factor * coderate > max_coderate); + coderate = srsran_pdcch_coderate(nof_bits, l); + } Debug("SCHED: CQI=%d, l=%d, nof_bits=%d, coderate=%.2f, max_coderate=%.2f", dl_cqi, @@ -497,20 +459,4 @@ int check_ue_cfg_correctness(const sched_interface::ue_cfg_t& ue_cfg) return ret; } -const char* to_string(sched_interface::ue_bearer_cfg_t::direction_t dir) -{ - switch (dir) { - case sched_interface::ue_bearer_cfg_t::IDLE: - return "idle"; - case sched_interface::ue_bearer_cfg_t::BOTH: - return "bi-dir"; - case sched_interface::ue_bearer_cfg_t::DL: - return "DL"; - case sched_interface::ue_bearer_cfg_t::UL: - return "UL"; - default: - return "unrecognized direction"; - } -} - } // namespace srsenb diff --git a/srsenb/src/stack/mac/sched_phy_ch/sched_dci.cc b/srsenb/src/stack/mac/sched_phy_ch/sched_dci.cc index d0687d702..06f750bfa 100644 --- a/srsenb/src/stack/mac/sched_phy_ch/sched_dci.cc +++ b/srsenb/src/stack/mac/sched_phy_ch/sched_dci.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,8 +20,8 @@ */ #include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h" -#include "srsenb/hdr/stack/mac/sched_common.h" #include "srsenb/hdr/stack/mac/sched_helpers.h" +#include "srsenb/hdr/stack/mac/sched_lte_common.h" #include "srsran/common/string_helpers.h" #include @@ -86,16 +86,26 @@ tbs_info compute_mcs_and_tbs(uint32_t nof_prb, bool is_ul, bool ulqam64_enabled, bool use_tbs_index_alt) +{ + float max_coderate = srsran_cqi_to_coderate(std::min(cqi + 1U, 15U), use_tbs_index_alt); + return compute_mcs_and_tbs(nof_prb, nof_re, max_coderate, max_mcs, is_ul, ulqam64_enabled, use_tbs_index_alt); +} + +tbs_info compute_mcs_and_tbs(uint32_t nof_prb, + uint32_t nof_re, + float max_coderate, + uint32_t max_mcs, + bool is_ul, + bool ulqam64_enabled, + bool use_tbs_index_alt) { assert((not is_ul or not use_tbs_index_alt) && "UL cannot use Alt CQI Table"); assert((is_ul or not ulqam64_enabled) && "DL cannot use UL-QAM64 enable flag"); - float max_coderate = srsran_cqi_to_coderate(std::min(cqi + 1U, 15U), use_tbs_index_alt); - uint32_t max_Qm = (is_ul) ? (ulqam64_enabled ? 6 : 4) : (use_tbs_index_alt ? 8 : 6); - max_coderate = std::min(max_coderate, 0.932F * max_Qm); + uint32_t max_Qm = (is_ul) ? (ulqam64_enabled ? 6 : 4) : (use_tbs_index_alt ? 8 : 6); + max_coderate = std::min(max_coderate, 0.930F * max_Qm); - int mcs = 0; - float prev_max_coderate = 0; + int mcs = 0; do { // update max TBS based on max coderate int max_tbs = coderate_to_tbs(max_coderate, nof_re); @@ -122,7 +132,7 @@ tbs_info compute_mcs_and_tbs(uint32_t nof_prb, // update max coderate based on mcs srsran_mod_t mod = (is_ul) ? srsran_ra_ul_mod_from_mcs(mcs) : srsran_ra_dl_mod_from_mcs(mcs, use_tbs_index_alt); uint32_t Qm = srsran_mod_bits_x_symbol(mod); - max_coderate = std::min(0.932F * Qm, max_coderate); + max_coderate = std::min(0.930F * Qm, max_coderate); if (coderate <= max_coderate) { // solution was found @@ -134,7 +144,7 @@ tbs_info compute_mcs_and_tbs(uint32_t nof_prb, // start with smaller max mcs in next iteration max_mcs = mcs - 1; - } while (mcs > 0 and max_coderate != prev_max_coderate); + } while (mcs > 0); return tbs_info{}; } @@ -150,7 +160,7 @@ tbs_info compute_min_mcs_and_tbs_from_required_bytes(uint32_t nof_prb, { // get max MCS/TBS that meets max coderate requirements tbs_info tb_max = compute_mcs_and_tbs(nof_prb, nof_re, cqi, max_mcs, is_ul, ulqam64_enabled, use_tbs_index_alt); - if (tb_max.tbs_bytes + 8 <= (int)req_bytes or tb_max.mcs == 0 or req_bytes <= 0) { + if (tb_max.tbs_bytes + 8 <= (int)req_bytes or tb_max.mcs == 0) { // if mcs cannot be lowered or a decrease in TBS index won't meet req_bytes requirement return tb_max; } @@ -188,8 +198,8 @@ int generate_ra_bc_dci_format1a_common(srsran_dci_dl_t& dci, const sched_cell_params_t& cell_params, uint32_t current_cfi) { - static const uint32_t Qm = 2, bc_rar_cqi = 4; - static const float max_ctrl_coderate = std::min(srsran_cqi_to_coderate(bc_rar_cqi + 1, false), 0.932F * Qm); + static const uint32_t Qm = 2; + static const float max_ctrl_coderate = std::min(cell_params.sched_cfg->max_sib_coderate, 0.932F * Qm); // Calculate I_tbs for this TBS int tbs = static_cast(req_bytes) * 8; @@ -321,6 +331,22 @@ bool generate_rar_dci(sched_interface::dl_sched_rar_t& rar, return true; } +void generate_pdcch_order_dci(sched_interface::dl_sched_po_t& pdcch_order, + tti_point tti_tx_dl, + const sched_cell_params_t& cell_params, + uint32_t current_cfi) +{ + // Generate DCI Format1A PDCCH order content + pdcch_order.dci.format = SRSRAN_DCI_FORMAT1A; + pdcch_order.dci.alloc_type = SRSRAN_RA_ALLOC_TYPE2; // TODO: is this correct? + pdcch_order.dci.rnti = pdcch_order.crnti; + pdcch_order.dci.is_pdcch_order = true; + pdcch_order.dci.preamble_idx = pdcch_order.preamble_idx; + pdcch_order.dci.prach_mask_idx = pdcch_order.prach_mask_idx; + + get_mac_logger().debug("PDCCH order: rnti=0x%x", pdcch_order.dci.rnti); +} + void log_broadcast_allocation(const sched_interface::dl_sched_bc_t& bc, rbg_interval rbg_range, const sched_cell_params_t& cell_params) @@ -333,17 +359,18 @@ void log_broadcast_allocation(const sched_interface::dl_sched_bc_t& bc, fmt::format_to(str_buffer, "{}", rbg_range); if (bc.type == sched_interface::dl_sched_bc_t::bc_type::BCCH) { - get_mac_logger().debug("SCHED: SIB%d, cc=%d, rbgs=(%d,%d), dci=(%d,%d), rv=%d, len=%d, period=%d, mcs=%d", - bc.index + 1, - cell_params.enb_cc_idx, - rbg_range.start(), - rbg_range.stop(), - bc.dci.location.L, - bc.dci.location.ncce, - bc.dci.tb[0].rv, - cell_params.cfg.sibs[bc.index].len, - cell_params.cfg.sibs[bc.index].period_rf, - bc.dci.tb[0].mcs_idx); + get_mac_logger().debug( + "SCHED: SI message, cc=%d, idx=%d, rbgs=(%d,%d), dci=(%d,%d), rv=%d, len=%d, period=%d, mcs=%d", + bc.index, + cell_params.enb_cc_idx, + rbg_range.start(), + rbg_range.stop(), + bc.dci.location.L, + bc.dci.location.ncce, + bc.dci.tb[0].rv, + cell_params.cfg.sibs[bc.index].len, + cell_params.cfg.sibs[bc.index].period_rf, + bc.dci.tb[0].mcs_idx); } else { get_mac_logger().info("SCHED: PCH, cc=%d, rbgs=%s, dci=(%d,%d), tbs=%d, mcs=%d", cell_params.enb_cc_idx, @@ -382,4 +409,24 @@ void log_rar_allocation(const sched_interface::dl_sched_rar_t& rar, rbg_interval srsran::to_c_str(str_buffer2)); } +void log_po_allocation(const sched_interface::dl_sched_po_t& pdcch_order, + rbg_interval rbg_range, + const sched_cell_params_t& cell_params) +{ + if (not get_mac_logger().info.enabled()) { + return; + } + + fmt::memory_buffer str_buffer; + fmt::format_to(str_buffer, "{}", rbg_range); + + get_mac_logger().info("SCHED: PDCCH order, cc=%d, rbgs=%s, dci=(%d,%d), tbs=%d, mcs=%d", + cell_params.enb_cc_idx, + srsran::to_c_str(str_buffer), + pdcch_order.dci.location.L, + pdcch_order.dci.location.ncce, + pdcch_order.tbs, + pdcch_order.dci.tb[0].mcs_idx); +} + } // namespace srsenb diff --git a/srsenb/src/stack/mac/sched_phy_ch/sched_phy_resource.cc b/srsenb/src/stack/mac/sched_phy_ch/sched_phy_resource.cc new file mode 100644 index 000000000..a942644fd --- /dev/null +++ b/srsenb/src/stack/mac/sched_phy_ch/sched_phy_resource.cc @@ -0,0 +1,138 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h" +extern "C" { +#include "lib/include/srsran/phy/dft/dft_precoding.h" +} + +namespace srsenb { + +rbg_interval rbg_interval::find_first_interval(const rbgmask_t& mask) +{ + int rb_start = mask.find_lowest(0, mask.size()); + if (rb_start != -1) { + int rb_end = mask.find_lowest(rb_start + 1, mask.size(), false); + return rbg_interval(rb_start, rb_end < 0 ? mask.size() : rb_end); + } + return rbg_interval(); +} + +prb_interval prb_interval::riv_to_prbs(uint32_t riv, uint32_t nof_prbs, int nof_vrbs) +{ + if (nof_vrbs < 0) { + nof_vrbs = nof_prbs; + } + uint32_t rb_start, l_crb; + srsran_ra_type2_from_riv(riv, &l_crb, &rb_start, nof_prbs, (uint32_t)nof_vrbs); + return {rb_start, rb_start + l_crb}; +} + +template ::value, prb_interval, rbg_interval>::type> +RBInterval find_contiguous_interval(const RBMask& in_mask, uint32_t max_size) +{ + RBInterval max_interv; + + for (size_t n = 0; n < in_mask.size();) { + int pos = in_mask.find_lowest(n, in_mask.size(), false); + if (pos < 0) { + break; + } + + size_t max_pos = std::min(in_mask.size(), (size_t)pos + max_size); + int pos2 = in_mask.find_lowest(pos + 1, max_pos, true); + RBInterval interv(pos, pos2 < 0 ? max_pos : pos2); + if (interv.length() >= max_size) { + return interv; + } + if (interv.length() > max_interv.length()) { + max_interv = interv; + } + n = interv.stop(); + } + return max_interv; +} + +rbgmask_t find_available_rbgmask(const rbgmask_t& in_mask, uint32_t max_size) +{ + // 1's for free RBs + rbgmask_t localmask = ~(in_mask); + + if (max_size >= localmask.size() or max_size >= localmask.count()) { + // shortcut in case rbg count < max_size + return localmask; + } + + uint32_t i = 0, nof_alloc = 0; + for (; i < localmask.size() and nof_alloc < max_size; ++i) { + if (localmask.test(i)) { + nof_alloc++; + } + } + localmask.fill(i, localmask.size(), false); + return localmask; +} + +rbg_interval find_empty_rbg_interval(uint32_t max_nof_rbgs, const rbgmask_t& current_mask) +{ + return find_contiguous_interval(current_mask, max_nof_rbgs); +} + +rbgmask_t find_available_rbgmask(uint32_t max_nof_rbgs, bool is_contiguous, const rbgmask_t& current_mask) +{ + // Allocate enough RBs that accommodate pending data + rbgmask_t newtx_mask(current_mask.size()); + if (is_contiguous) { + rbg_interval interv = find_contiguous_interval(current_mask, max_nof_rbgs); + newtx_mask.fill(interv.start(), interv.stop()); + } else { + newtx_mask = find_available_rbgmask(current_mask, max_nof_rbgs); + } + return newtx_mask; +} + +prb_interval find_contiguous_ul_prbs(uint32_t L, const prbmask_t& current_mask) +{ + prb_interval prb_interv = find_contiguous_interval(current_mask, L); + if (prb_interv.empty()) { + return prb_interv; + } + + // Make sure L is allowed by SC-FDMA modulation + prb_interval prb_interv2 = prb_interv; + while (not srsran_dft_precoding_valid_prb(prb_interv.length()) and prb_interv.stop() < current_mask.size() and + not current_mask.test(prb_interv.stop())) { + prb_interv.resize_by(1); + } + if (not srsran_dft_precoding_valid_prb(prb_interv.length())) { + // if length increase failed, try to decrease + prb_interv = prb_interv2; + prb_interv.resize_by(-1); + while (not srsran_dft_precoding_valid_prb(prb_interv.length()) and not prb_interv.empty()) { + prb_interv.resize_by(-1); + } + } + return prb_interv; +} + +} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/mac/sched_phy_ch/sf_cch_allocator.cc b/srsenb/src/stack/mac/sched_phy_ch/sf_cch_allocator.cc index 824d1fbc9..024725b5d 100644 --- a/srsenb/src/stack/mac/sched_phy_ch/sf_cch_allocator.cc +++ b/srsenb/src/stack/mac/sched_phy_ch/sf_cch_allocator.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -61,6 +61,8 @@ sf_cch_allocator::get_cce_loc_table(alloc_type_t alloc_type, sched_ue* user, uin return &cc_cfg->common_locations[cfix]; case alloc_type_t::DL_RAR: return &cc_cfg->rar_locations[to_tx_dl(tti_rx).sf_idx()][cfix]; + case alloc_type_t::DL_PDCCH_ORDER: + return &cc_cfg->common_locations[cfix]; case alloc_type_t::DL_DATA: case alloc_type_t::UL_DATA: return user->get_locations(cc_cfg->enb_cc_idx, cfix + 1, to_tx_dl(tti_rx).sf_idx()); @@ -81,7 +83,7 @@ bool sf_cch_allocator::alloc_dci(alloc_type_t alloc_type, uint32_t aggr_idx, sch record.alloc_type = alloc_type; record.pusch_uci = has_pusch_grant; - if (is_dl_ctrl_alloc(alloc_type) and nof_allocs() == 0 and cc_cfg->nof_prb() == 6 and + if (is_dl_ctrl_alloc(alloc_type) and nof_allocs() == 0 and cc_cfg->nof_prb() <= 25 and current_max_cfix > current_cfix) { // Given that CFI is not currently dynamic for ctrl allocs, in case of SIB/RAR alloc and a low number of PRBs, // start with an CFI that maximizes nof potential CCE locs @@ -190,6 +192,15 @@ bool sf_cch_allocator::alloc_dfs_node(const alloc_record& record, uint32_t start // PUCCH allocation would collide with other PUCCH/PUSCH grants. Try another CCE position continue; } + int low_rb = node.pucch_n_prb < (int)cc_cfg->cfg.cell.nof_prb / 2 + ? node.pucch_n_prb + : cc_cfg->cfg.cell.nof_prb - node.pucch_n_prb - 1; + if (cc_cfg->sched_cfg->pucch_harq_max_rb > 0 && low_rb >= cc_cfg->sched_cfg->pucch_harq_max_rb) { + // PUCCH allocation would fall outside the maximum allowed PUCCH HARQ region. Try another CCE position + logger.info("Skipping PDCCH allocation for CCE=%d due to PUCCH HARQ falling outside region\n", + node.dci_pos.ncce); + continue; + } } node.current_mask.reset(); diff --git a/srsenb/src/stack/mac/sched_ue.cc b/srsenb/src/stack/mac/sched_ue.cc index 193f75249..0fefaf9ce 100644 --- a/srsenb/src/stack/mac/sched_ue.cc +++ b/srsenb/src/stack/mac/sched_ue.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,8 +27,6 @@ #include "srsran/common/string_helpers.h" #include "srsran/srslog/bundled/fmt/ranges.h" -using srsran::tti_interval; - namespace srsenb { /****************************************************** @@ -44,9 +42,8 @@ namespace srsenb { *******************************************************/ sched_ue::sched_ue(uint16_t rnti_, const std::vector& cell_list_params_, const ue_cfg_t& cfg_) : - logger(srslog::fetch_basic_logger("MAC")) + logger(srslog::fetch_basic_logger("MAC")), rnti(rnti_), lch_handler(rnti_) { - rnti = rnti_; cells.reserve(cell_list_params_.size()); for (auto& c : cell_list_params_) { cells.emplace_back(rnti_, c, current_tti); @@ -105,15 +102,9 @@ void sched_ue::new_subframe(tti_point tti_rx, uint32_t enb_cc_idx) current_tti = tti_rx; lch_handler.new_tti(); for (auto& cc : cells) { - if (cc.configured()) { - cc.harq_ent.new_tti(tti_rx); - } + cc.new_tti(tti_rx); } } - - if (cells[enb_cc_idx].configured()) { - cells[enb_cc_idx].tpc_fsm.new_tti(); - } } /******************************************************* @@ -122,7 +113,7 @@ void sched_ue::new_subframe(tti_point tti_rx, uint32_t enb_cc_idx) * *******************************************************/ -void sched_ue::set_bearer_cfg(uint32_t lc_id, const bearer_cfg_t& cfg_) +void sched_ue::set_bearer_cfg(uint32_t lc_id, const mac_lc_ch_cfg_t& cfg_) { cfg.ue_bearers[lc_id] = cfg_; lch_handler.config_lcid(lc_id, cfg_); @@ -130,17 +121,12 @@ void sched_ue::set_bearer_cfg(uint32_t lc_id, const bearer_cfg_t& cfg_) void sched_ue::rem_bearer(uint32_t lc_id) { - cfg.ue_bearers[lc_id] = sched_interface::ue_bearer_cfg_t{}; - lch_handler.config_lcid(lc_id, sched_interface::ue_bearer_cfg_t{}); + cfg.ue_bearers[lc_id] = mac_lc_ch_cfg_t{}; + lch_handler.config_lcid(lc_id, mac_lc_ch_cfg_t{}); } void sched_ue::phy_config_enabled(tti_point tti_rx, bool enabled) { - for (sched_ue_cell& c : cells) { - if (c.configured()) { - c.dl_cqi_tti_rx = tti_rx; - } - } phy_config_dedicated_enabled = enabled; } @@ -154,9 +140,9 @@ void sched_ue::ul_buffer_add(uint8_t lcid, uint32_t bytes) lch_handler.ul_buffer_add(lcid, bytes); } -void sched_ue::ul_phr(int phr) +void sched_ue::ul_phr(int phr, uint32_t grant_nof_prb) { - cells[cfg.supported_cc_list[0].enb_cc_idx].tpc_fsm.set_phr(phr); + cells[cfg.supported_cc_list[0].enb_cc_idx].tpc_fsm.set_phr(phr, grant_nof_prb); } void sched_ue::dl_buffer_state(uint8_t lc_id, uint32_t tx_queue, uint32_t retx_queue) @@ -187,6 +173,13 @@ void sched_ue::unset_sr() sr = false; } +void sched_ue::metrics_read(mac_ue_metrics_t& metrics) +{ + sched_ue_cell& pcell = cells[cfg.supported_cc_list[0].enb_cc_idx]; + metrics.ul_snr_offset = pcell.get_ul_snr_offset(); + metrics.dl_cqi_offset = pcell.get_dl_cqi_offset(); +} + tti_point prev_meas_gap_start(tti_point tti, uint32_t period, uint32_t offset) { return tti_point{static_cast(floor(static_cast((tti - offset).to_uint()) / period)) * period + @@ -237,7 +230,7 @@ bool sched_ue::pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_p tti_interval meas_gap{mgap_tti, mgap_tti + 6}; // disable TTIs that leads to PUSCH tx or PHICH rx falling in measGap - if (meas_gap.contains(tti_tx_ul) or meas_gap.contains(to_tx_ul_ack(tti_rx))) { + if (meas_gap.contains(tti_tx_ul)) { return false; } // disable TTIs which respective PDCCH falls in measGap (in case PDCCH is needed) @@ -248,34 +241,32 @@ bool sched_ue::pusch_enabled(tti_point tti_rx, uint32_t enb_cc_idx, bool needs_p return true; } +bool sched_ue::phich_enabled(tti_point tti_rx, uint32_t enb_cc_idx) const +{ + if (cfg.supported_cc_list[0].enb_cc_idx != enb_cc_idx) { + return true; + } + + // Check measGap collision with PHICH + if (cfg.measgap_period > 0) { + tti_point tti_tx_dl = to_tx_dl(tti_rx); + tti_point mgap_tti = nearest_meas_gap(tti_tx_dl, cfg.measgap_period, cfg.measgap_offset); + tti_interval meas_gap{mgap_tti, mgap_tti + 6}; + if (meas_gap.contains(tti_tx_dl)) { + return false; + } + } + return true; +} + int sched_ue::set_ack_info(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t tb_idx, bool ack) { - int tbs_acked = -1; - if (cells[enb_cc_idx].cc_state() != cc_st::idle) { - std::pair p2 = cells[enb_cc_idx].harq_ent.set_ack_info(tti_rx, tb_idx, ack); - tbs_acked = p2.second; - if (tbs_acked > 0) { - logger.debug( - "SCHED: Set DL ACK=%d for rnti=0x%x, pid=%d, tb=%d, tti=%d", ack, rnti, p2.first, tb_idx, tti_rx.to_uint()); - } else { - logger.warning("SCHED: Received ACK info for unknown TTI=%d", tti_rx.to_uint()); - } - } else { - logger.warning("Received DL ACK for invalid cell index %d", enb_cc_idx); - } - return tbs_acked; + return cells[enb_cc_idx].set_ack_info(tti_rx, tb_idx, ack); } void sched_ue::set_ul_crc(tti_point tti_rx, uint32_t enb_cc_idx, bool crc_res) { - if (cells[enb_cc_idx].cc_state() != cc_st::idle) { - int ret = cells[enb_cc_idx].harq_ent.set_ul_crc(tti_rx, 0, crc_res); - if (ret < 0) { - logger.warning("Received UL CRC for invalid tti_rx=%d", (int)tti_rx.to_uint()); - } - } else { - logger.warning("Received UL CRC for invalid cell index %d", enb_cc_idx); - } + cells[enb_cc_idx].set_ul_crc(tti_rx, crc_res); } void sched_ue::set_dl_ri(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t ri) @@ -300,24 +291,17 @@ void sched_ue::set_dl_pmi(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t pmi) void sched_ue::set_dl_cqi(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t cqi) { - if (cells[enb_cc_idx].cc_state() != cc_st::idle) { - cells[enb_cc_idx].set_dl_cqi(tti_rx, cqi); - } else { - logger.warning("Received DL CQI for invalid enb cell index %d", enb_cc_idx); - } + cells[enb_cc_idx].set_dl_wb_cqi(tti_rx, cqi); +} + +void sched_ue::set_dl_sb_cqi(tti_point tti_rx, uint32_t enb_cc_idx, uint32_t sb_idx, uint32_t cqi) +{ + cells[enb_cc_idx].set_dl_sb_cqi(tti_rx, sb_idx, cqi); } void sched_ue::set_ul_snr(tti_point tti_rx, uint32_t enb_cc_idx, float snr, uint32_t ul_ch_code) { - if (cells[enb_cc_idx].cc_state() != cc_st::idle) { - cells[enb_cc_idx].tpc_fsm.set_snr(snr, ul_ch_code); - if (ul_ch_code == tpc::PUSCH_CODE) { - cells[enb_cc_idx].ul_cqi = srsran_cqi_from_snr(snr); - cells[enb_cc_idx].ul_cqi_tti_rx = tti_rx; - } - } else { - logger.warning("Received SNR info for invalid cell index %d", enb_cc_idx); - } + cells[enb_cc_idx].set_ul_snr(tti_rx, snr, ul_ch_code); } /******************************************************* @@ -339,8 +323,7 @@ tbs_info sched_ue::allocate_new_dl_mac_pdu(sched::dl_sched_data_t* data, uint32_t tb) { srsran_dci_dl_t* dci = &data->dci; - uint32_t nof_prb = count_prb_per_tb(user_mask); - tbs_info tb_info = compute_mcs_and_tbs(enb_cc_idx, tti_tx_dl, nof_prb, cfi, *dci); + tbs_info tb_info = compute_mcs_and_tbs(enb_cc_idx, tti_tx_dl, user_mask, cfi, *dci); // Allocate MAC PDU (subheaders, CEs, and SDUS) int rem_tbs = tb_info.tbs_bytes; @@ -426,7 +409,7 @@ int sched_ue::generate_format1a(uint32_t pid, dci->alloc_type = SRSRAN_RA_ALLOC_TYPE2; dci->type2_alloc.mode = srsran_ra_type2_t::SRSRAN_RA_TYPE2_LOC; - rbg_interval rbg_int = rbg_interval::rbgmask_to_rbgs(user_mask); + rbg_interval rbg_int = rbg_interval::find_first_interval(user_mask); prb_interval prb_int = prb_interval::rbgs_to_prbs(rbg_int, cell.nof_prb); uint32_t L_crb = prb_int.length(); uint32_t RB_start = prb_int.start(); @@ -487,25 +470,25 @@ int sched_ue::generate_format1(uint32_t pid, * Based on the amount of tx data, allocated PRBs, DCI params, etc. compute a valid MCS and resulting TBS * @param enb_cc_idx user carrier index * @param tti_tx_dl tti when the tx will occur - * @param nof_alloc_prbs number of PRBs that were allocated + * @param rbgs RBG mask * @param cfi Number of control symbols in Subframe * @param dci contains the RBG mask, and alloc type * @return pair with MCS and TBS (in bytes) */ tbs_info sched_ue::compute_mcs_and_tbs(uint32_t enb_cc_idx, tti_point tti_tx_dl, - uint32_t nof_alloc_prbs, + const rbgmask_t& rbg_mask, uint32_t cfi, const srsran_dci_dl_t& dci) { - assert(cells[enb_cc_idx].configured()); + srsran_assert(cells[enb_cc_idx].configured(), "computation of MCS/TBS called for non-configured CC"); srsran::interval req_bytes = get_requested_dl_bytes(enb_cc_idx); // Calculate exact number of RE for this PRB allocation uint32_t nof_re = cells[enb_cc_idx].cell_cfg->get_dl_nof_res(tti_tx_dl, dci, cfi); // Compute MCS+TBS - tbs_info tb = cqi_to_tbs_dl(cells[enb_cc_idx], nof_alloc_prbs, nof_re, dci.format, req_bytes.stop()); + tbs_info tb = cqi_to_tbs_dl(cells[enb_cc_idx], rbg_mask, nof_re, dci.format, req_bytes.stop()); if (tb.tbs_bytes > 0 and tb.tbs_bytes < (int)req_bytes.start()) { logger.info("SCHED: Could not get PRB allocation that avoids MAC CE or RLC SRB0 PDU segmentation"); @@ -733,8 +716,8 @@ bool sched_ue::needs_cqi(uint32_t tti, uint32_t enb_cc_idx, bool will_send) bool ret = false; if (phy_config_dedicated_enabled && cfg.supported_cc_list[0].aperiodic_cqi_period && lch_handler.has_pending_dl_txs()) { - uint32_t interval = srsran_tti_interval(tti, cells[enb_cc_idx].dl_cqi_tti_rx.to_uint()); - bool needscqi = interval >= cfg.supported_cc_list[0].aperiodic_cqi_period; + bool needscqi = tti_point(tti) >= + cells[enb_cc_idx].dl_cqi().last_cqi_info_tti() - cfg.supported_cc_list[0].aperiodic_cqi_period; if (needscqi) { uint32_t interval_sent = srsran_tti_interval(tti, cqi_request_tti); if (interval_sent >= 16) { @@ -767,7 +750,7 @@ rbg_interval sched_ue::get_required_dl_rbgs(uint32_t enb_cc_idx) // Cannot fit allocation in given PRBs logger.error("SCHED: DL CQI=%d does now allow fitting %d non-segmentable DL tx bytes into the cell bandwidth. " "Consider increasing initial CQI value.", - cells[enb_cc_idx].dl_cqi, + cells[enb_cc_idx].get_dl_cqi(), req_bytes.start()); return {cellparams->nof_prb(), cellparams->nof_prb()}; } @@ -855,7 +838,7 @@ uint32_t sched_ue::get_expected_dl_bitrate(uint32_t enb_cc_idx, int nof_rbgs) co auto& cc = cells[enb_cc_idx]; uint32_t nof_re = cc.cell_cfg->get_dl_lb_nof_re(to_tx_dl(current_tti), count_prb_per_tb_approx(nof_rbgs, cc.cell_cfg->nof_prb())); - float max_coderate = srsran_cqi_to_coderate(std::min(cc.dl_cqi + 1u, 15u), cfg.use_tbs_index_alt); + float max_coderate = srsran_cqi_to_coderate(std::min(cc.get_dl_cqi() + 1u, 15u), cfg.use_tbs_index_alt); // Inverse of srsran_coderate(tbs, nof_re) uint32_t tbs = max_coderate * nof_re - 24; @@ -869,7 +852,7 @@ uint32_t sched_ue::get_expected_ul_bitrate(uint32_t enb_cc_idx, int nof_prbs) co uint32_t N_srs = 0; uint32_t nof_symb = 2 * (SRSRAN_CP_NSYMB(cell.cp) - 1) - N_srs; uint32_t nof_re = nof_symb * nof_prbs_alloc * SRSRAN_NRE; - float max_coderate = srsran_cqi_to_coderate(std::min(cells[enb_cc_idx].ul_cqi + 1u, 15u), false); + float max_coderate = srsran_cqi_to_coderate(std::min(cells[enb_cc_idx].get_ul_cqi() + 1u, 15u), false); // Inverse of srsran_coderate(tbs, nof_re) uint32_t tbs = max_coderate * nof_re - 24; @@ -924,7 +907,7 @@ uint32_t sched_ue::get_pending_ul_data_total(tti_point tti_tx_ul, int this_enb_c uint32_t max_cqi = 0, max_cc_idx = 0; for (uint32_t cc = 0; cc < cells.size(); ++cc) { if (cells[cc].configured()) { - uint32_t sum_cqi = cells[cc].dl_cqi + cells[cc].ul_cqi; + uint32_t sum_cqi = cells[cc].get_dl_cqi() + cells[cc].get_ul_cqi(); if (cells[cc].cc_state() == cc_st::active and sum_cqi > max_cqi) { max_cqi = sum_cqi; max_cc_idx = cc; @@ -1016,8 +999,7 @@ std::pair sched_ue::get_active_cell_index(uint32_t enb_cc_idx) c uint32_t sched_ue::get_aggr_level(uint32_t enb_cc_idx, uint32_t nof_bits) { - const auto& cc = cells[enb_cc_idx]; - return srsenb::get_aggr_level(nof_bits, cc.dl_cqi, cc.max_aggr_level, cc.cell_cfg->nof_prb(), cfg.use_tbs_index_alt); + return cells[enb_cc_idx].get_aggr_level(nof_bits); } void sched_ue::finish_tti(tti_point tti_rx, uint32_t enb_cc_idx) diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_dl_cqi.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_dl_cqi.cc new file mode 100644 index 000000000..970423eab --- /dev/null +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_dl_cqi.cc @@ -0,0 +1,106 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h" +#include "srsenb/hdr/stack/mac/schedulers/sched_base.h" + +namespace srsenb { + +rbgmask_t sched_dl_cqi::get_optim_rbgmask(const rbgmask_t& dl_mask, uint32_t req_rbgs, bool max_flag) const +{ + req_rbgs = std::min(req_rbgs, cell_nof_rbg); + if (not subband_cqi_enabled()) { + // in case of wideband, just find any available RBGs + return find_available_rbgmask(req_rbgs, false, dl_mask); + } + + rbgmask_t emptymask = ~dl_mask; + if (emptymask.none() or req_rbgs >= emptymask.size() or emptymask.count() <= req_rbgs) { + return emptymask; + } + + srsran::bounded_vector sorted_cqi_pos; + srsran::bounded_vector sorted_cqis; + for (int pos = emptymask.find_lowest(0, emptymask.size(), true); pos >= 0; + pos = emptymask.find_lowest(pos + 1, emptymask.size(), true)) { + sorted_cqis.push_back(get_rbg_cqi(pos)); + sorted_cqi_pos.push_back(pos); + } + std::stable_sort(sorted_cqi_pos.begin(), sorted_cqi_pos.end(), [&sorted_cqis](uint32_t lhs, uint32_t rhs) { + return sorted_cqis[lhs] > sorted_cqis[rhs]; + }); + if (max_flag) { + for (size_t i = req_rbgs; i < sorted_cqi_pos.size(); ++i) { + emptymask.set(sorted_cqi_pos[i], false); + } + } else { + for (size_t i = 0; i < sorted_cqi_pos.size() - req_rbgs; ++i) { + emptymask.set(sorted_cqi_pos[i], false); + } + } + + return emptymask; +} + +rbgmask_t find_min_cqi_rbgs(const rbgmask_t& mask, const sched_dl_cqi& dl_cqi, int& min_cqi) +{ + if (mask.none()) { + min_cqi = -1; + return mask; + } + + if (not dl_cqi.subband_cqi_enabled()) { + min_cqi = dl_cqi.get_wb_cqi_info(); + return mask; + } + + rbgmask_t min_mask(mask.size()); + int rbg = mask.find_lowest(0, mask.size()); + min_cqi = std::numeric_limits::max(); + for (; rbg != -1; rbg = mask.find_lowest(rbg, mask.size())) { + uint32_t sb = dl_cqi.rbg_to_sb_index(rbg); + int cqi = dl_cqi.get_subband_cqi(sb); + if (cqi < min_cqi) { + min_cqi = cqi; + min_mask.reset(); + min_mask.set(rbg); + } else if (cqi == min_cqi) { + min_mask.set(rbg); + } + rbg++; + } + min_cqi = min_cqi == std::numeric_limits::max() ? -1 : min_cqi; + + return min_mask; +} + +rbgmask_t remove_min_cqi_rbgs(const rbgmask_t& rbgmask, const sched_dl_cqi& dl_cqi) +{ + int min_cqi; + rbgmask_t minmask = find_min_cqi_rbgs(rbgmask, dl_cqi, min_cqi); + if (min_cqi < 0) { + return minmask; + } + minmask = ~minmask & rbgmask; + return minmask; +} + +} // namespace srsenb \ No newline at end of file diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc index 89422716f..51e42fd5a 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -121,11 +121,8 @@ void harq_proc::new_retx_common(uint32_t tb_idx, tti_point tti_, int* mcs, int* void harq_proc::reset_pending_data_common() { - // reuse harqs with no retxs - if (max_retx == 0 and not is_empty()) { - for (bool& tb : active) { - tb = false; - } + for (bool& tb : active) { + tb = false; } } @@ -242,6 +239,9 @@ void ul_harq_proc::new_tti() logger->info( "SCHED: discarding UL pid=%d, tti=%d, maximum number of retx exceeded (%d)", get_id(), tti.to_uint(), max_retx); active[0] = false; + if (not pending_phich) { + reset_pending_data(); + } } } @@ -259,9 +259,10 @@ void ul_harq_proc::new_tx(tti_point tti_, int mcs, int tbs, prb_interval alloc, { allocation = alloc; new_tx_common(0, tti_point{tti_}, mcs, tbs, max_retx_); - pending_data = tbs; - pending_phich = true; - is_msg3_ = is_msg3; + pending_data = tbs; + pending_phich = true; + is_msg3_ = is_msg3; + pdcch_requested = false; } void ul_harq_proc::new_retx(tti_point tti_, int* mcs, int* tbs, prb_interval alloc) @@ -269,12 +270,15 @@ void ul_harq_proc::new_retx(tti_point tti_, int* mcs, int* tbs, prb_interval all // If PRBs changed, or there was no tx in last oportunity (e.g. HARQ is being resumed) allocation = alloc; new_retx_common(0, tti_point{tti_}, mcs, tbs); - pending_phich = true; + pending_phich = true; + pdcch_requested = false; } -bool ul_harq_proc::retx_requires_pdcch(srsran::tti_point tti_, prb_interval alloc) const +bool ul_harq_proc::retx_requires_pdcch(tti_point tti_, prb_interval alloc) const { - return alloc != allocation or tti_ != to_tx_ul(tti); + // Adaptive retx if: (1) PRBs changed, (2) HARQ resumed due to last PUSCH retx being skipped (3) HARQ resumed due to + // last PHICH alloc being skipped (e.g. due to measGaps) + return alloc != allocation or pdcch_requested; } bool ul_harq_proc::set_ack(uint32_t tb_idx, bool ack_) @@ -283,6 +287,9 @@ bool ul_harq_proc::set_ack(uint32_t tb_idx, bool ack_) return false; } set_ack_common(tb_idx, ack_); + if (is_empty(0) and not pending_phich) { + reset_pending_data(); + } return true; } @@ -291,15 +298,27 @@ bool ul_harq_proc::has_pending_phich() const return pending_phich; } +void ul_harq_proc::request_pdcch() +{ + pdcch_requested = true; +} + +void ul_harq_proc::retx_skipped() +{ + // Note: This function should be called in case of PHICH allocation is successful + // Flagging "PDCCH required" for next retx, as HARQ is being resumed + pdcch_requested = true; + n_rtx[0]++; +} + bool ul_harq_proc::pop_pending_phich() { - assert(pending_phich); + srsran_assert(pending_phich, "pop_pending_phich called for HARQ with no pending PHICH"); bool ret = ack_state[0]; pending_phich = false; if (is_empty(0)) { - // fully reset UL HARQ once PHICH is dispatched - is_msg3_ = false; - pending_data = 0; + // fully reset HARQ info once PHICH is dispatched for an acked / maxretx reached HARQ + reset_pending_data(); } return ret; } @@ -307,15 +326,14 @@ bool ul_harq_proc::pop_pending_phich() void ul_harq_proc::reset_pending_data() { reset_pending_data_common(); - if (is_empty(0)) { - pending_data = 0; - is_msg3_ = false; - } + pending_data = 0; + is_msg3_ = false; + pdcch_requested = false; } uint32_t ul_harq_proc::get_pending_data() const { - return (uint32_t)pending_data; + return is_empty() ? 0 : (uint32_t)pending_data; } /******************** @@ -342,6 +360,9 @@ void harq_entity::reset() for (auto& h : ul_harqs) { for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { h.reset(tb); + // The reset_pending_data() is called after reset(), when generating PHICH. However, in the case of full HARQ + // reset (e.g. during handover) no PHICH is going to be generated. + h.reset_pending_data(); } } } @@ -375,17 +396,17 @@ dl_harq_proc* harq_entity::get_pending_dl_harq(tti_point tti_tx_dl) return get_oldest_dl_harq(tti_tx_dl); } -std::pair harq_entity::set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack) +std::tuple harq_entity::set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack) { for (auto& h : dl_harqs) { if (h.get_tti() + FDD_HARQ_DELAY_DL_MS == tti_rx) { if (h.set_ack(tb_idx, ack) == SRSRAN_SUCCESS) { - return {h.get_id(), h.get_tbs(tb_idx)}; + return std::make_tuple(h.get_id(), h.get_tbs(tb_idx), h.get_mcs(tb_idx)); } - return {h.get_id(), -1}; + return std::make_tuple(h.get_id(), -1, -1); } } - return {dl_harqs.size(), -1}; + return std::make_tuple(dl_harqs.size(), -1, -1); } ul_harq_proc* harq_entity::get_ul_harq(tti_point tti_tx_ul) @@ -400,16 +421,20 @@ int harq_entity::set_ul_crc(tti_point tti_rx, uint32_t tb_idx, bool ack_) return h->set_ack(tb_idx, ack_) ? pid : -1; } -void harq_entity::reset_pending_data(tti_point tti_rx) +void harq_entity::finish_tti(tti_point tti_rx) { - tti_point tti_tx_ul = to_tx_ul(tti_rx); + // Reset UL HARQ if no retxs + auto* hul = get_ul_harq(to_tx_ul(tti_rx)); + if (not hul->is_empty() and hul->max_nof_retx() == 0) { + hul->reset_pending_data(); + } - // Reset ACK state of UL Harq - get_ul_harq(tti_tx_ul)->reset_pending_data(); - - // Reset any DL harq which has 0 retxs + // Reset DL harq which has 0 retxs for (auto& h : dl_harqs) { - h.reset_pending_data(); + if (not h.is_empty() and h.max_nof_retx() == 0) { + // reuse harqs with no retxs + h.reset_pending_data(); + } } } diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_lch.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_lch.cc index 71c19426e..7fdfe9339 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_lch.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_lch.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,9 +66,7 @@ uint32_t get_ul_mac_sdu_size_with_overhead(uint32_t rlc_pdu_bytes) void lch_ue_manager::set_cfg(const sched_interface::ue_cfg_t& cfg) { - for (uint32_t lcid = 0; lcid < sched_interface::MAX_LC; lcid++) { - config_lcid(lcid, cfg.ue_bearers[lcid]); - } + config_lcids(cfg.ue_bearers); } void lch_ue_manager::new_tti() @@ -76,95 +74,59 @@ void lch_ue_manager::new_tti() prio_idx++; for (uint32_t lcid = 0; lcid < sched_interface::MAX_LC; ++lcid) { if (is_bearer_active(lcid)) { - if (lch[lcid].cfg.pbr != pbr_infinity) { - lch[lcid].Bj = std::min(lch[lcid].Bj + (int)(lch[lcid].cfg.pbr * tti_duration_ms), lch[lcid].bucket_size); + if (channels[lcid].cfg.pbr != pbr_infinity) { + channels[lcid].Bj = + std::min(channels[lcid].Bj + (int)(channels[lcid].cfg.pbr * tti_duration_ms), channels[lcid].bucket_size); } } } } -void lch_ue_manager::config_lcid(uint32_t lc_id, const sched_interface::ue_bearer_cfg_t& bearer_cfg) +void lch_ue_manager::dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t prio_tx_queue) { - if (lc_id >= sched_interface::MAX_LC) { - logger.warning("Adding bearer with invalid logical channel id=%d", lc_id); - return; - } - if (bearer_cfg.group >= sched_interface::MAX_LC_GROUP) { - logger.warning("Adding bearer with invalid logical channel group id=%d", bearer_cfg.group); - return; - } - - // update bearer config - bool is_equal = memcmp(&bearer_cfg, &lch[lc_id].cfg, sizeof(bearer_cfg)) == 0; - - if (not is_equal) { - lch[lc_id].cfg = bearer_cfg; - if (lch[lc_id].cfg.pbr == pbr_infinity) { - lch[lc_id].bucket_size = std::numeric_limits::max(); - lch[lc_id].Bj = std::numeric_limits::max(); - } else { - lch[lc_id].bucket_size = lch[lc_id].cfg.bsd * lch[lc_id].cfg.pbr; - lch[lc_id].Bj = 0; - } - logger.info("SCHED: bearer configured: lc_id=%d, mode=%s, prio=%d", - lc_id, - to_string(lch[lc_id].cfg.direction), - lch[lc_id].cfg.priority); + if (base_type::dl_buffer_state(lcid, tx_queue, prio_tx_queue) == SRSRAN_SUCCESS) { + logger.debug("SCHED: rnti=0x%x DL lcid=%d buffer_state=%d,%d", rnti, lcid, tx_queue, prio_tx_queue); } } -void lch_ue_manager::ul_bsr(uint8_t lcg_id, uint32_t bsr) +void lch_ue_manager::ul_bsr(uint32_t lcg_id, uint32_t val) { - if (lcg_id >= sched_interface::MAX_LC_GROUP) { - logger.warning("The provided logical channel group id=%d is not valid", lcg_id); - return; - } - lcg_bsr[lcg_id] = bsr; - if (logger.debug.enabled()) { - fmt::memory_buffer str_buffer; - fmt::format_to(str_buffer, "{}", get_bsr_state()); - logger.debug("SCHED: bsr=%d, lcg_id=%d, bsr=%s", bsr, lcg_id, srsran::to_c_str(str_buffer)); + if (base_type::ul_bsr(lcg_id, val) == SRSRAN_SUCCESS) { + if (logger.debug.enabled()) { + fmt::memory_buffer str_buffer; + fmt::format_to(str_buffer, "{}", lcg_bsr); + logger.debug( + "SCHED: rnti=0x%x, lcg_id=%d, bsr=%d. Current state=%s", rnti, lcg_id, val, srsran::to_c_str(str_buffer)); + } } } void lch_ue_manager::ul_buffer_add(uint8_t lcid, uint32_t bytes) { if (lcid >= sched_interface::MAX_LC) { - logger.warning("The provided lcid=%d is not valid", lcid); + logger.warning("SCHED: The provided lcid=%d for rnti=0x%x is not valid", lcid, rnti); return; } - lcg_bsr[lch[lcid].cfg.group] += bytes; + lcg_bsr[channels[lcid].cfg.group] += bytes; if (logger.debug.enabled()) { fmt::memory_buffer str_buffer; fmt::format_to(str_buffer, "{}", get_bsr_state()); - logger.debug( - "SCHED: UL buffer update=%d, lcg_id=%d, bsr=%s", bytes, lch[lcid].cfg.group, srsran::to_c_str(str_buffer)); + logger.debug("SCHED: rnti=0x%x UL buffer update=%d, lcg_id=%d, bsr=%s", + rnti, + bytes, + channels[lcid].cfg.group, + srsran::to_c_str(str_buffer)); } } -void lch_ue_manager::dl_buffer_state(uint8_t lcid, uint32_t tx_queue, uint32_t retx_queue) -{ - if (lcid >= sched_interface::MAX_LC) { - logger.warning("The provided lcid=%d is not valid", lcid); - return; - } - if (lcid < 3 and (lch[lcid].buf_tx != (int)tx_queue or lch[lcid].buf_retx != (int)retx_queue)) { - logger.info("SCHED: DL lcid=%d buffer_state=%d,%d", lcid, tx_queue, retx_queue); - } else { - logger.debug("SCHED: DL lcid=%d buffer_state=%d,%d", lcid, tx_queue, retx_queue); - } - lch[lcid].buf_retx = retx_queue; - lch[lcid].buf_tx = tx_queue; -} - int lch_ue_manager::get_max_prio_lcid() const { int min_prio_val = std::numeric_limits::max(), prio_lcid = -1; - // Prioritize retxs - for (uint32_t lcid = 0; lcid < MAX_LC; ++lcid) { - if (get_dl_retx(lcid) > 0 and lch[lcid].cfg.priority < min_prio_val) { - min_prio_val = lch[lcid].cfg.priority; + // Prioritized Txs first (e.g. Retxs, status PDUs) + for (uint32_t lcid = 0; is_lcid_valid(lcid); ++lcid) { + if (get_dl_prio_tx(lcid) > 0 and channels[lcid].cfg.priority < min_prio_val) { + min_prio_val = channels[lcid].cfg.priority; prio_lcid = lcid; } } @@ -173,9 +135,9 @@ int lch_ue_manager::get_max_prio_lcid() const } // Select lcid with new txs using Bj - for (uint32_t lcid = 0; lcid < MAX_LC; ++lcid) { - if (get_dl_tx(lcid) > 0 and lch[lcid].Bj > 0 and lch[lcid].cfg.priority < min_prio_val) { - min_prio_val = lch[lcid].cfg.priority; + for (uint32_t lcid = 0; is_lcid_valid(lcid); ++lcid) { + if (get_dl_tx(lcid) > 0 and channels[lcid].Bj > 0 and channels[lcid].cfg.priority < min_prio_val) { + min_prio_val = channels[lcid].cfg.priority; prio_lcid = lcid; } } @@ -184,15 +146,15 @@ int lch_ue_manager::get_max_prio_lcid() const } // Disregard Bj - size_t nof_lcids = 0; - std::array chosen_lcids = {}; - for (uint32_t lcid = 0; lcid < MAX_LC; ++lcid) { + size_t nof_lcids = 0; + std::array chosen_lcids = {}; + for (uint32_t lcid = 0; is_lcid_valid(lcid); ++lcid) { if (get_dl_tx_total(lcid) > 0) { - if (lch[lcid].cfg.priority < min_prio_val) { - min_prio_val = lch[lcid].cfg.priority; + if (channels[lcid].cfg.priority < min_prio_val) { + min_prio_val = channels[lcid].cfg.priority; chosen_lcids[0] = lcid; nof_lcids = 1; - } else if (lch[lcid].cfg.priority == min_prio_val) { + } else if (channels[lcid].cfg.priority == min_prio_val) { chosen_lcids[nof_lcids++] = lcid; } } @@ -214,10 +176,10 @@ int lch_ue_manager::alloc_rlc_pdu(sched_interface::dl_sched_pdu_t* rlc_pdu, int return alloc_bytes; } - // try first to allocate retxs - alloc_bytes = alloc_retx_bytes(lcid, rem_bytes); + // try first to allocate high priority txs (e.g. retxs, status pdus) + alloc_bytes = alloc_prio_tx_bytes(lcid, rem_bytes); - // if no retx alloc, try newtx + // if no prio tx alloc, try newtx if (alloc_bytes == 0) { alloc_bytes = alloc_tx_bytes(lcid, rem_bytes); } @@ -235,15 +197,15 @@ int lch_ue_manager::alloc_rlc_pdu(sched_interface::dl_sched_pdu_t* rlc_pdu, int return alloc_bytes; } -int lch_ue_manager::alloc_retx_bytes(uint8_t lcid, int rem_bytes) +int lch_ue_manager::alloc_prio_tx_bytes(uint8_t lcid, int rem_bytes) { const int rlc_overhead = (lcid == 0) ? 0 : RLC_MAX_HEADER_SIZE_NO_LI; if (rem_bytes <= rlc_overhead) { return 0; } int rem_bytes_no_header = rem_bytes - rlc_overhead; - int alloc = std::min(rem_bytes_no_header, get_dl_retx(lcid)); - lch[lcid].buf_retx -= alloc; + int alloc = std::min(rem_bytes_no_header, get_dl_prio_tx(lcid)); + channels[lcid].buf_prio_tx -= alloc; return alloc + (alloc > 0 ? rlc_overhead : 0); } @@ -255,35 +217,20 @@ int lch_ue_manager::alloc_tx_bytes(uint8_t lcid, int rem_bytes) } int rem_bytes_no_header = rem_bytes - rlc_overhead; int alloc = std::min(rem_bytes_no_header, get_dl_tx(lcid)); - lch[lcid].buf_tx -= alloc; - if (alloc > 0 and lch[lcid].cfg.pbr != pbr_infinity) { + channels[lcid].buf_tx -= alloc; + if (alloc > 0 and channels[lcid].cfg.pbr != pbr_infinity) { // Update Bj - lch[lcid].Bj -= alloc; + channels[lcid].Bj -= alloc; } return alloc + (alloc > 0 ? rlc_overhead : 0); } -bool lch_ue_manager::is_bearer_active(uint32_t lcid) const -{ - return lch[lcid].cfg.direction != sched_interface::ue_bearer_cfg_t::IDLE; -} - -bool lch_ue_manager::is_bearer_ul(uint32_t lcid) const -{ - return is_bearer_active(lcid) and lch[lcid].cfg.direction != sched_interface::ue_bearer_cfg_t::DL; -} - -bool lch_ue_manager::is_bearer_dl(uint32_t lcid) const -{ - return is_bearer_active(lcid) and lch[lcid].cfg.direction != sched_interface::ue_bearer_cfg_t::UL; -} - bool lch_ue_manager::has_pending_dl_txs() const { if (not pending_ces.empty()) { return true; } - for (uint32_t lcid = 0; lcid < lch.size(); ++lcid) { + for (uint32_t lcid = 0; is_lcid_valid(lcid); ++lcid) { if (get_dl_tx_total(lcid) > 0) { return true; } @@ -291,81 +238,59 @@ bool lch_ue_manager::has_pending_dl_txs() const return false; } -int lch_ue_manager::get_dl_tx_total() const -{ - int sum = 0; - for (size_t lcid = 0; lcid < lch.size(); ++lcid) { - sum += get_dl_tx_total(lcid); - } - return sum; -} - int lch_ue_manager::get_dl_tx_total_with_overhead(uint32_t lcid) const { - return get_dl_retx_with_overhead(lcid) + get_dl_tx_with_overhead(lcid); + return get_dl_prio_tx_with_overhead(lcid) + get_dl_tx_with_overhead(lcid); } -int lch_ue_manager::get_dl_tx(uint32_t lcid) const -{ - return is_bearer_dl(lcid) ? lch[lcid].buf_tx : 0; -} int lch_ue_manager::get_dl_tx_with_overhead(uint32_t lcid) const { return get_dl_mac_sdu_size_with_overhead(lcid, get_dl_tx(lcid)); } -int lch_ue_manager::get_dl_retx(uint32_t lcid) const +int lch_ue_manager::get_dl_prio_tx_with_overhead(uint32_t lcid) const { - return is_bearer_dl(lcid) ? lch[lcid].buf_retx : 0; -} -int lch_ue_manager::get_dl_retx_with_overhead(uint32_t lcid) const -{ - return get_dl_mac_sdu_size_with_overhead(lcid, get_dl_retx(lcid)); + return get_dl_mac_sdu_size_with_overhead(lcid, get_dl_prio_tx(lcid)); } -bool lch_ue_manager::is_lcg_active(uint32_t lcg) const -{ - if (lcg == 0) { - return true; - } - for (uint32_t lcid = 0; lcid < MAX_LC; ++lcid) { - if (is_bearer_ul(lcid) and lch[lcid].cfg.group == (int)lcg) { - return true; - } - } - return false; -} -int lch_ue_manager::get_bsr(uint32_t lcg) const -{ - return is_lcg_active(lcg) ? lcg_bsr[lcg] : 0; -} int lch_ue_manager::get_bsr_with_overhead(uint32_t lcg) const { return get_ul_mac_sdu_size_with_overhead(get_bsr(lcg)); } -const std::array& lch_ue_manager::get_bsr_state() const -{ - return lcg_bsr; -} - uint32_t allocate_mac_sdus(sched_interface::dl_sched_data_t* data, lch_ue_manager& lch_handler, uint32_t total_tbs, uint32_t tbidx) { - uint32_t rem_tbs = total_tbs; + uint32_t rem_tbs = total_tbs; + auto& pdu = data->pdu[tbidx]; + uint32_t& nof_pdu_elems = data->nof_pdu_elems[tbidx]; // if we do not have enough bytes to fit MAC subheader, skip MAC SDU allocation // NOTE: we do not account RLC header because some LCIDs (e.g. CCCH) do not need them + uint32_t first_pdu_idx = nof_pdu_elems; while (rem_tbs > MAC_MAX_HEADER_SIZE and data->nof_pdu_elems[tbidx] < sched_interface::MAX_RLC_PDU_LIST) { uint32_t max_sdu_bytes = rem_tbs - get_mac_subheader_size(rem_tbs - MAC_MIN_HEADER_SIZE); - uint32_t alloc_sdu_bytes = lch_handler.alloc_rlc_pdu(&data->pdu[tbidx][data->nof_pdu_elems[tbidx]], max_sdu_bytes); + uint32_t alloc_sdu_bytes = lch_handler.alloc_rlc_pdu(&pdu[nof_pdu_elems], max_sdu_bytes); if (alloc_sdu_bytes == 0) { break; } rem_tbs -= get_mac_sdu_and_subheader_size(alloc_sdu_bytes); // account for MAC sub-header - data->nof_pdu_elems[tbidx]++; + + // In case the same LCID got reallocated (e.g. retx and newtx), merge with previous SDU. + // Otherwise, increment number of scheduled SDUs + uint32_t prev_same_lcid_idx = first_pdu_idx; + for (; prev_same_lcid_idx < nof_pdu_elems; ++prev_same_lcid_idx) { + if (pdu[prev_same_lcid_idx].lcid == pdu[nof_pdu_elems].lcid) { + pdu[prev_same_lcid_idx].nbytes += pdu[nof_pdu_elems].nbytes; + pdu[nof_pdu_elems].nbytes = 0; + break; + } + } + if (prev_same_lcid_idx == nof_pdu_elems) { + nof_pdu_elems++; + } } return total_tbs - rem_tbs; diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc index 0a0b1415a..89048960c 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,6 +24,14 @@ #include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h" #include +#define CHECK_VALID_CC(feedback_type) \ + do { \ + if (cc_state() == cc_st::idle) { \ + logger.warning("SCHED: rnti=0x%x received " feedback_type " for idle cc=%d", rnti, cell_cfg->enb_cc_idx); \ + return SRSRAN_ERROR; \ + } \ + } while (0) + namespace srsenb { /******************************************************* @@ -36,16 +44,28 @@ sched_ue_cell::sched_ue_cell(uint16_t rnti_, const sched_cell_params_t& cell_cfg cell_cfg(&cell_cfg_), dci_locations(generate_cce_location_table(rnti_, cell_cfg_)), harq_ent(SCHED_MAX_HARQ_PROC, SCHED_MAX_HARQ_PROC), - tpc_fsm(cell_cfg->nof_prb(), + tpc_fsm(rnti_, + cell_cfg->nof_prb(), cell_cfg->cfg.target_pucch_ul_sinr, cell_cfg->cfg.target_pusch_ul_sinr, - cell_cfg->cfg.enable_phr_handling), + cell_cfg->cfg.enable_phr_handling, + cell_cfg->cfg.min_phr_thres, + cell_cfg->sched_cfg->min_tpc_tti_interval, + cell_cfg->sched_cfg->ul_snr_avg_alpha, + cell_cfg->sched_cfg->init_ul_snr_value), fixed_mcs_dl(cell_cfg_.sched_cfg->pdsch_mcs), fixed_mcs_ul(cell_cfg_.sched_cfg->pusch_mcs), current_tti(current_tti_), - max_aggr_level(cell_cfg_.sched_cfg->max_aggr_level >= 0 ? cell_cfg_.sched_cfg->max_aggr_level : 3) + max_aggr_level(cell_cfg_.sched_cfg->max_aggr_level >= 0 ? cell_cfg_.sched_cfg->max_aggr_level : 3), + dl_cqi_ctxt(cell_cfg_.nof_prb(), 0, cell_cfg_.sched_cfg->init_dl_cqi) { - clear_feedback(); + float target_bler = cell_cfg->sched_cfg->target_bler; + dl_delta_inc = cell_cfg->sched_cfg->adaptive_dl_mcs_step_size; // delta_{down} of OLLA + dl_delta_dec = (1 - target_bler) * dl_delta_inc / target_bler; + ul_delta_inc = cell_cfg->sched_cfg->adaptive_ul_mcs_step_size; // delta_{down} of OLLA + ul_delta_dec = (1 - target_bler) * ul_delta_inc / target_bler; + max_cqi_coeff = cell_cfg->sched_cfg->max_delta_dl_cqi; + max_snr_coeff = cell_cfg->sched_cfg->max_delta_ul_snr; } void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) @@ -62,6 +82,7 @@ void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) } } if (ue_cc_idx < 0 and prev_ue_cc_idx < 0) { + // CC was inactive and remain inactive return; } @@ -75,10 +96,18 @@ void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) max_mcs_dl = std::min(max_mcs_dl, 27U); } + if (ue_cc_idx >= 0) { + const auto& cc = ue_cfg_.supported_cc_list[ue_cc_idx]; + if (cc.dl_cfg.cqi_report.periodic_configured) { + dl_cqi_ctxt.set_K(cc.dl_cfg.cqi_report.subband_wideband_ratio); + } + } + // If new cell configuration, clear Cell HARQs if (ue_cc_idx != prev_ue_cc_idx) { clear_feedback(); harq_ent.reset(); + logger.info("SCHED: Resetting rnti=0x%x, cc=%d HARQs and feedback state", rnti, cell_cfg->enb_cc_idx); } // Update carrier state @@ -92,15 +121,21 @@ void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) case cc_st::active: if (ue_cc_idx < 0 or not ue_cfg->supported_cc_list[ue_cc_idx].active) { cc_state_ = cc_st::deactivating; - logger.info("SCHED: Deactivating rnti=0x%x, SCellIndex=%d...", rnti, ue_cc_idx); + if (ue_cc_idx > 0) { + logger.info( + "SCHED: Deactivating SCell, rnti=0x%x, cc=%d, SCellIndex=%d...", rnti, cell_cfg->enb_cc_idx, ue_cc_idx); + } else { + logger.info("SCHED: Deactivating previous PCell, rnti=0x%x, cc=%d...", rnti, cell_cfg->enb_cc_idx); + } } break; case cc_st::deactivating: case cc_st::idle: if (ue_cc_idx > 0 and ue_cfg->supported_cc_list[ue_cc_idx].active) { cc_state_ = cc_st::activating; - dl_cqi = 0; - logger.info("SCHED: Activating rnti=0x%x, SCellIndex=%d...", rnti, ue_cc_idx); + dl_cqi_ctxt.reset_cqi(0); + logger.info( + "SCHED: Activating SCell, rnti=0x%x, cc=%d, SCellIndex=%d...", rnti, cell_cfg->enb_cc_idx, ue_cc_idx); } break; default: @@ -111,8 +146,14 @@ void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) void sched_ue_cell::new_tti(tti_point tti_rx) { + if (not configured()) { + return; + } current_tti = tti_rx; + harq_ent.new_tti(tti_rx); + tpc_fsm.new_tti(); + // Check if cell state needs to be updated if (ue_cc_idx > 0 and cc_state_ == cc_st::deactivating) { // wait for all ACKs to be received before completely deactivating SCell @@ -130,31 +171,155 @@ void sched_ue_cell::clear_feedback() dl_ri_tti_rx = tti_point{}; dl_pmi = 0; dl_pmi_tti_rx = tti_point{}; - dl_cqi = ue_cc_idx == 0 ? cell_cfg->cfg.initial_dl_cqi : 1; - dl_cqi_tti_rx = tti_point{}; - dl_cqi_rx = false; - ul_cqi = 1; + dl_cqi_ctxt.reset_cqi(ue_cc_idx == 0 ? cell_cfg->sched_cfg->init_dl_cqi : 1); ul_cqi_tti_rx = tti_point{}; } void sched_ue_cell::finish_tti(tti_point tti_rx) { // clear_feedback PIDs with pending data or blocked - harq_ent.reset_pending_data(tti_rx); + harq_ent.finish_tti(tti_rx); } -void sched_ue_cell::set_dl_cqi(tti_point tti_rx, uint32_t dl_cqi_) +void sched_ue_cell::check_cc_activation(uint32_t dl_cqi) { - dl_cqi = dl_cqi_; - dl_cqi_tti_rx = tti_rx; - dl_cqi_rx = dl_cqi_rx or dl_cqi > 0; - if (ue_cc_idx > 0 and cc_state_ == cc_st::activating and dl_cqi_rx) { + if (ue_cc_idx > 0 and cc_state_ == cc_st::activating and dl_cqi > 0) { // Wait for SCell to receive a positive CQI before activating it cc_state_ = cc_st::active; logger.info("SCHED: SCell index=%d is now active", ue_cc_idx); } } +int sched_ue_cell::set_dl_wb_cqi(tti_point tti_rx, uint32_t dl_cqi_) +{ + CHECK_VALID_CC("DL CQI"); + dl_cqi_ctxt.cqi_wb_info(tti_rx, dl_cqi_); + check_cc_activation(dl_cqi_); + logger.debug("SCHED: DL CQI cc=%d, cqi=%d", cell_cfg->enb_cc_idx, dl_cqi_); + return SRSRAN_SUCCESS; +} + +int sched_ue_cell::set_dl_sb_cqi(tti_point tti_rx, uint32_t sb_idx, uint32_t dl_cqi_) +{ + CHECK_VALID_CC("DL CQI"); + dl_cqi_ctxt.cqi_sb_info(tti_rx, sb_idx, dl_cqi_); + check_cc_activation(dl_cqi_); + logger.debug("SCHED: DL CQI cc=%d, sb_idx=%d, cqi=%d", cell_cfg->enb_cc_idx, sb_idx, dl_cqi_); + return SRSRAN_SUCCESS; +} + +int sched_ue_cell::set_ul_crc(tti_point tti_rx, bool crc_res) +{ + CHECK_VALID_CC("UL CRC"); + + // Adapt UL MCS based on BLER + if (cell_cfg->sched_cfg->target_bler > 0 and fixed_mcs_ul < 0) { + auto* ul_harq = harq_ent.get_ul_harq(tti_rx); + if (ul_harq != nullptr) { + int mcs = ul_harq->get_mcs(0); + // Note: Avoid keeping increasing the snr delta offset, if MCS is already is at its limit + float delta_dec_eff = mcs <= 0 ? 0 : ul_delta_dec; + float delta_inc_eff = mcs >= (int)max_mcs_ul ? 0 : ul_delta_inc; + ul_snr_coeff += crc_res ? delta_inc_eff : -delta_dec_eff; + ul_snr_coeff = std::min(std::max(-max_snr_coeff, ul_snr_coeff), max_snr_coeff); + logger.info("SCHED: UL adaptive link: rnti=0x%x, snr_estim=%.2f, last_mcs=%d, snr_offset=%f", + rnti, + tpc_fsm.get_ul_snr_estim(), + mcs, + ul_snr_coeff); + } + } + + // Update HARQ process + int pid = harq_ent.set_ul_crc(tti_rx, 0, crc_res); + if (pid < 0) { + logger.warning("SCHED: rnti=0x%x received UL CRC for invalid tti_rx=%d", rnti, (int)tti_rx.to_uint()); + return SRSRAN_ERROR; + } + + return pid; +} + +int sched_ue_cell::set_ack_info(tti_point tti_rx, uint32_t tb_idx, bool ack) +{ + CHECK_VALID_CC("DL ACK Info"); + + std::tuple p2 = harq_ent.set_ack_info(tti_rx, tb_idx, ack); + int tbs_acked = std::get<1>(p2); + if (tbs_acked <= 0) { + logger.warning("SCHED: Received ACK info for unknown TTI=%d", tti_rx.to_uint()); + return tbs_acked; + } + + // Adapt DL MCS based on BLER + if (cell_cfg->sched_cfg->target_bler > 0 and fixed_mcs_dl < 0) { + int mcs = std::get<2>(p2); + // Note: Avoid keeping increasing the snr delta offset, if MCS is already is at its limit + float delta_dec_eff = mcs <= 0 ? 0 : dl_delta_dec; + float delta_inc_eff = mcs >= (int)max_mcs_dl ? 0 : dl_delta_inc; + dl_cqi_coeff += ack ? delta_inc_eff : -delta_dec_eff; + dl_cqi_coeff = std::min(std::max(-max_cqi_coeff, dl_cqi_coeff), max_cqi_coeff); + logger.info("SCHED: DL adaptive link: rnti=0x%x, cqi=%d, last_mcs=%d, cqi_offset=%f", + rnti, + dl_cqi_ctxt.get_avg_cqi(), + mcs, + dl_cqi_coeff); + } + return tbs_acked; +} + +int sched_ue_cell::set_ul_snr(tti_point tti_rx, float ul_snr, uint32_t ul_ch_code) +{ + CHECK_VALID_CC("UL SNR estimate"); + if (ue_cfg->ue_bearers[1].direction == mac_lc_ch_cfg_t::IDLE) { + // Ignore Msg3 SNR samples as Msg3 uses a separate power control loop + return SRSRAN_SUCCESS; + } + tpc_fsm.set_snr(ul_snr, ul_ch_code); + if (ul_ch_code == tpc::PUSCH_CODE) { + ul_cqi_tti_rx = tti_rx; + } + return SRSRAN_SUCCESS; +} + +int sched_ue_cell::get_ul_cqi() const +{ + if (not ul_cqi_tti_rx.is_valid()) { + return 1; + } + float snr = tpc_fsm.get_ul_snr_estim(); + return srsran_cqi_from_snr(snr + ul_snr_coeff); +} + +int sched_ue_cell::get_dl_cqi(const rbgmask_t& rbgs) const +{ + int min_cqi; + find_min_cqi_rbgs(rbgs, dl_cqi_ctxt, min_cqi); + return std::max(0, (int)std::min(static_cast(min_cqi) + dl_cqi_coeff, 15.0f)); +} + +int sched_ue_cell::get_dl_cqi() const +{ + return std::max(0, (int)std::min(dl_cqi_ctxt.get_avg_cqi() + dl_cqi_coeff, 15.0f)); +} + +uint32_t sched_ue_cell::get_aggr_level(uint32_t nof_bits) const +{ + uint32_t dl_cqi = 0; + if (cell_cfg->sched_cfg->adaptive_aggr_level) { + dl_cqi = get_dl_cqi(); + } else { + dl_cqi = dl_cqi_ctxt.get_avg_cqi(); + } + dl_cqi = std::max(cell_cfg->sched_cfg->pdcch_cqi_offset + (int)dl_cqi, 0); + return srsenb::get_aggr_level(nof_bits, + dl_cqi, + cell_cfg->sched_cfg->min_aggr_level, + max_aggr_level, + cell_cfg->nof_prb(), + ue_cfg->use_tbs_index_alt); +} + /************************************************************* * TBS/MCS derivation ************************************************************/ @@ -233,29 +398,32 @@ std::tuple false_position_method(int x1, int x2, YType y } tbs_info cqi_to_tbs_dl(const sched_ue_cell& cell, - uint32_t nof_prb, + const rbgmask_t& rbgs, uint32_t nof_re, srsran_dci_format_t dci_format, - int req_bytes) + uint32_t req_bytes) { - bool use_tbs_index_alt = cell.get_ue_cfg()->use_tbs_index_alt and dci_format != SRSRAN_DCI_FORMAT1A; + bool use_tbs_index_alt = cell.get_ue_cfg()->use_tbs_index_alt and dci_format != SRSRAN_DCI_FORMAT1A; + uint32_t nof_prbs = count_prb_per_tb(rbgs); tbs_info ret; - if (cell.fixed_mcs_dl < 0 or not cell.dl_cqi_rx) { - // Dynamic MCS + if (cell.fixed_mcs_dl < 0 or not cell.dl_cqi().is_cqi_info_received()) { + // Dynamic MCS configured or first Tx + uint32_t dl_cqi = cell.get_dl_cqi(rbgs); + ret = compute_min_mcs_and_tbs_from_required_bytes( - nof_prb, nof_re, cell.dl_cqi, cell.max_mcs_dl, req_bytes, false, false, use_tbs_index_alt); + nof_prbs, nof_re, dl_cqi, cell.max_mcs_dl, req_bytes, false, false, use_tbs_index_alt); // If coderate > SRSRAN_MIN(max_coderate, 0.932 * Qm) we should set TBS=0. We don't because it's not correctly // handled by the scheduler, but we might be scheduling undecodable codewords at very low SNR if (ret.tbs_bytes < 0) { ret.mcs = 0; - ret.tbs_bytes = get_tbs_bytes((uint32_t)ret.mcs, nof_prb, use_tbs_index_alt, false); + ret.tbs_bytes = get_tbs_bytes((uint32_t)ret.mcs, nof_prbs, use_tbs_index_alt, false); } } else { - // Fixed MCS + // Fixed MCS configured ret.mcs = cell.fixed_mcs_dl; - ret.tbs_bytes = get_tbs_bytes((uint32_t)cell.fixed_mcs_dl, nof_prb, use_tbs_index_alt, false); + ret.tbs_bytes = get_tbs_bytes((uint32_t)cell.fixed_mcs_dl, nof_prbs, use_tbs_index_alt, false); } return ret; } @@ -270,7 +438,7 @@ tbs_info cqi_to_tbs_ul(const sched_ue_cell& cell, uint32_t nof_prb, uint32_t nof if (mcs < 0) { // Dynamic MCS ret = compute_min_mcs_and_tbs_from_required_bytes( - nof_prb, nof_re, cell.ul_cqi, cell.max_mcs_ul, req_bytes, true, ulqam64_enabled, false); + nof_prb, nof_re, cell.get_ul_cqi(), cell.max_mcs_ul, req_bytes, true, ulqam64_enabled, false); // If coderate > SRSRAN_MIN(max_coderate, 0.932 * Qm) we should set TBS=0. We don't because it's not correctly // handled by the scheduler, but we might be scheduling undecodable codewords at very low SNR @@ -293,8 +461,9 @@ int get_required_prb_dl(const sched_ue_cell& cell, uint32_t req_bytes) { auto compute_tbs_approx = [tti_tx_dl, &cell, dci_format](uint32_t nof_prb) { - uint32_t nof_re = cell.cell_cfg->get_dl_lb_nof_re(tti_tx_dl, nof_prb); - tbs_info tb = cqi_to_tbs_dl(cell, nof_prb, nof_re, dci_format, -1); + uint32_t nof_re = cell.cell_cfg->get_dl_lb_nof_re(tti_tx_dl, nof_prb); + rbgmask_t min_cqi_rbgs = cell.dl_cqi().get_optim_rbgmask(nof_prb, false); + tbs_info tb = cqi_to_tbs_dl(cell, min_cqi_rbgs, nof_re, dci_format); return tb.tbs_bytes; }; @@ -307,7 +476,7 @@ int get_required_prb_dl(const sched_ue_cell& cell, uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) { - const static int MIN_ALLOC_BYTES = 10; + const static int MIN_ALLOC_BYTES = 10; /// There should be enough space for RLC header + BSR + some payload if (req_bytes == 0) { return 0; } @@ -326,9 +495,7 @@ uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) uint32_t final_tbs = std::get<3>(ret); while (final_tbs < MIN_ALLOC_BYTES and req_prbs < cell.cell_cfg->nof_prb()) { // Note: If PHR<0 is limiting the max nof PRBs per UL grant, the UL grant may become too small to fit any - // data other than headers + BSR. Besides, forcing unnecessary segmentation, it may additionally - // forbid the UE from fitting small RRC messages (e.g. RRCReconfComplete) in the UL grants. - // To avoid TBS<10, we force an increase the nof required PRBs. + // data other than headers + BSR. In this edge-case, force an increase the nof required PRBs. req_prbs++; final_tbs = compute_tbs_approx(req_prbs); } @@ -338,4 +505,93 @@ uint32_t get_required_prb_ul(const sched_ue_cell& cell, uint32_t req_bytes) return req_prbs; } +/// Computes the minimum TBS/MCS achievable for provided UE cell configuration, RBG mask, TTI, DCI format +tbs_info compute_mcs_and_tbs_lower_bound(const sched_ue_cell& ue_cell, + tti_point tti_tx_dl, + const rbgmask_t& rbg_mask, + srsran_dci_format_t dci_format) +{ + uint32_t nof_prbs = count_prb_per_tb(rbg_mask); + if (nof_prbs == 0) { + return tbs_info{}; + } + uint32_t nof_re_lb = ue_cell.cell_cfg->get_dl_lb_nof_re(tti_tx_dl, nof_prbs); + return cqi_to_tbs_dl(ue_cell, rbg_mask, nof_re_lb, dci_format); +} + +bool find_optimal_rbgmask(const sched_ue_cell& ue_cell, + tti_point tti_tx_dl, + const rbgmask_t& dl_mask, + srsran_dci_format_t dci_format, + srsran::interval req_bytes, + tbs_info& tb, + rbgmask_t& newtxmask) +{ + // Find the largest set of available RBGs possible + newtxmask = find_available_rbgmask(dl_mask.size(), dci_format == SRSRAN_DCI_FORMAT1A, dl_mask); + + // Compute MCS/TBS if all available RBGs were allocated + tb = compute_mcs_and_tbs_lower_bound(ue_cell, tti_tx_dl, newtxmask, dci_format); + + if (not ue_cell.dl_cqi().subband_cqi_enabled()) { + // Wideband CQI case + // NOTE: for wideband CQI, the TBS is directly proportional to the nof_prbs, so we can use an iterative method + // to compute the best mask given "req_bytes" + + if (tb.tbs_bytes < (int)req_bytes.start()) { + // the grant is too small. it may lead to srb0 segmentation or not space for headers + return false; + } + if (tb.tbs_bytes <= (int)req_bytes.stop()) { + // the grant is not sufficiently large to fit max required bytes. Stop search at this point + return true; + } + // Reduce DL grant size to the minimum that can fit the pending DL bytes + srsran::bounded_vector tb_table(newtxmask.count()); + auto compute_tbs_approx = [tti_tx_dl, &ue_cell, dci_format, &tb_table](uint32_t nof_rbgs) { + rbgmask_t search_mask(ue_cell.cell_cfg->nof_rbgs); + search_mask.fill(0, nof_rbgs); + tb_table[nof_rbgs - 1] = compute_mcs_and_tbs_lower_bound(ue_cell, tti_tx_dl, search_mask, dci_format); + return tb_table[nof_rbgs - 1].tbs_bytes; + }; + std::tuple ret = false_position_method( + 1U, tb_table.size(), (int)req_bytes.stop(), compute_tbs_approx, [](int y) { return y == SRSRAN_ERROR; }); + uint32_t upper_nrbg = std::get<2>(ret); + int upper_tbs = std::get<3>(ret); + if (upper_tbs >= (int)req_bytes.stop()) { + tb = tb_table[upper_nrbg - 1]; + int pos = 0; + for (uint32_t n_rbgs = newtxmask.count(); n_rbgs > upper_nrbg; --n_rbgs) { + pos = newtxmask.find_lowest(pos + 1, newtxmask.size()); + } + newtxmask.from_uint64(~((1U << (uint64_t)pos) - 1U) & ((1U << newtxmask.size()) - 1U)); + } + return true; + } + + // Subband CQI case + // NOTE: There is no monotonically increasing guarantee between TBS and nof allocated prbs. + // One single subband CQI could be dropping the CQI of the whole TB. + // We start with largest RBG allocation and continue removing RBGs. However, there is no guarantee this is + // going to be the optimal solution + + // Subtract RBGs with lowest CQI until objective is not met + // TODO: can be optimized + rbgmask_t smaller_mask; + tbs_info tb2; + do { + smaller_mask = remove_min_cqi_rbgs(newtxmask, ue_cell.dl_cqi()); + tb2 = compute_mcs_and_tbs_lower_bound(ue_cell, tti_tx_dl, smaller_mask, dci_format); + if (tb2.tbs_bytes >= (int)req_bytes.stop() or tb.tbs_bytes <= tb2.tbs_bytes) { + tb = tb2; + newtxmask = smaller_mask; + } + } while (tb2.tbs_bytes > (int)req_bytes.stop()); + if (tb.tbs_bytes <= (int)req_bytes.stop()) { + return true; + } + + return true; +} + } // namespace srsenb diff --git a/srsenb/src/stack/mac/schedulers/CMakeLists.txt b/srsenb/src/stack/mac/schedulers/CMakeLists.txt index a3b488c13..da2f40821 100644 --- a/srsenb/src/stack/mac/schedulers/CMakeLists.txt +++ b/srsenb/src/stack/mac/schedulers/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsenb/src/stack/mac/schedulers/sched_base.cc b/srsenb/src/stack/mac/schedulers/sched_base.cc index 50cde80c5..d8f72adca 100644 --- a/srsenb/src/stack/mac/schedulers/sched_base.cc +++ b/srsenb/src/stack/mac/schedulers/sched_base.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,74 +23,6 @@ namespace srsenb { -/********************************* - * Common UL/DL Helper methods - ********************************/ - -template ::value, prb_interval, rbg_interval>::type> -RBInterval find_contiguous_interval(const RBMask& in_mask, uint32_t max_size) -{ - RBInterval max_interv; - - for (size_t n = 0; n < in_mask.size();) { - int pos = in_mask.find_lowest(n, in_mask.size(), false); - if (pos < 0) { - break; - } - - size_t max_pos = std::min(in_mask.size(), (size_t)pos + max_size); - int pos2 = in_mask.find_lowest(pos, max_pos, true); - RBInterval interv(pos, pos2 < 0 ? max_pos : pos2); - if (interv.length() >= max_size) { - return interv; - } - if (interv.length() > max_interv.length()) { - max_interv = interv; - } - n = interv.stop(); - } - return max_interv; -} - -/**************************** - * DL Helper methods - ***************************/ - -rbgmask_t find_available_rb_mask(const rbgmask_t& in_mask, uint32_t max_size) -{ - // 1's for free RBs - rbgmask_t localmask = ~(in_mask); - - uint32_t i = 0, nof_alloc = 0; - for (; i < localmask.size() and nof_alloc < max_size; ++i) { - if (localmask.test(i)) { - nof_alloc++; - } - } - localmask.fill(i, localmask.size(), false); - return localmask; -} - -rbg_interval find_empty_rbg_interval(uint32_t max_nof_rbgs, const rbgmask_t& current_mask) -{ - return find_contiguous_interval(current_mask, max_nof_rbgs); -} - -rbgmask_t compute_rbgmask_greedy(uint32_t max_nof_rbgs, bool is_contiguous, const rbgmask_t& current_mask) -{ - // Allocate enough RBs that accommodate pending data - rbgmask_t newtx_mask(current_mask.size()); - if (is_contiguous) { - rbg_interval interv = find_contiguous_interval(current_mask, max_nof_rbgs); - newtx_mask.fill(interv.start(), interv.stop()); - } else { - newtx_mask = find_available_rb_mask(current_mask, max_nof_rbgs); - } - return newtx_mask; -} - int get_ue_cc_idx_if_pdsch_enabled(const sched_ue& user, sf_sched* tti_sched) { // Do not allocate a user multiple times in the same tti @@ -137,7 +69,7 @@ alloc_result try_dl_retx_alloc(sf_sched& tti_sched, sched_ue& ue, const dl_harq_ // If previous mask does not fit, find another with exact same number of rbgs size_t nof_rbg = retx_mask.count(); bool is_contiguous_alloc = ue.get_dci_format() == SRSRAN_DCI_FORMAT1A; - retx_mask = compute_rbgmask_greedy(nof_rbg, is_contiguous_alloc, tti_sched.get_dl_mask()); + retx_mask = find_available_rbgmask(nof_rbg, is_contiguous_alloc, tti_sched.get_dl_mask()); if (retx_mask.count() == nof_rbg) { return tti_sched.alloc_dl_user(&ue, retx_mask, h.get_id()); } @@ -157,22 +89,25 @@ alloc_result try_dl_newtx_alloc_greedy(sf_sched& tti_sched, sched_ue& ue, const } // If there is no data to transmit, no need to allocate - rbg_interval req_rbgs = ue.get_required_dl_rbgs(tti_sched.get_enb_cc_idx()); - if (req_rbgs.stop() == 0) { + srsran::interval req_bytes = ue.get_requested_dl_bytes(tti_sched.get_enb_cc_idx()); + if (req_bytes.stop() == 0) { return alloc_result::no_rnti_opportunity; } - // Find RBG mask that accommodates pending data - bool is_contiguous_alloc = ue.get_dci_format() == SRSRAN_DCI_FORMAT1A; - rbgmask_t newtxmask = compute_rbgmask_greedy(req_rbgs.stop(), is_contiguous_alloc, current_mask); - if (newtxmask.none() or newtxmask.count() < req_rbgs.start()) { + sched_ue_cell* ue_cell = ue.find_ue_carrier(tti_sched.get_enb_cc_idx()); + srsran_assert(ue_cell != nullptr, "dl newtx alloc called for invalid cell"); + srsran_dci_format_t dci_format = ue.get_dci_format(); + tbs_info tb; + rbgmask_t opt_mask; + if (not find_optimal_rbgmask( + *ue_cell, tti_sched.get_tti_tx_dl(), current_mask, dci_format, req_bytes, tb, opt_mask)) { return alloc_result::no_sch_space; } // empty RBGs were found. Attempt allocation - alloc_result ret = tti_sched.alloc_dl_user(&ue, newtxmask, h.get_id()); + alloc_result ret = tti_sched.alloc_dl_user(&ue, opt_mask, h.get_id()); if (ret == alloc_result::success and result_mask != nullptr) { - *result_mask = newtxmask; + *result_mask = opt_mask; } return ret; } @@ -181,30 +116,6 @@ alloc_result try_dl_newtx_alloc_greedy(sf_sched& tti_sched, sched_ue& ue, const * UL Helpers ****************/ -prb_interval find_contiguous_ul_prbs(uint32_t L, const prbmask_t& current_mask) -{ - prb_interval prb_interv = find_contiguous_interval(current_mask, L); - if (prb_interv.empty()) { - return prb_interv; - } - - // Make sure L is allowed by SC-FDMA modulation - prb_interval prb_interv2 = prb_interv; - while (not srsran_dft_precoding_valid_prb(prb_interv.length()) and prb_interv.stop() < current_mask.size() and - not current_mask.test(prb_interv.stop())) { - prb_interv.resize_by(1); - } - if (not srsran_dft_precoding_valid_prb(prb_interv.length())) { - // if length increase failed, try to decrease - prb_interv = prb_interv2; - prb_interv.resize_by(-1); - while (not srsran_dft_precoding_valid_prb(prb_interv.length()) and not prb_interv.empty()) { - prb_interv.resize_by(-1); - } - } - return prb_interv; -} - int get_ue_cc_idx_if_pusch_enabled(const sched_ue& user, sf_sched* tti_sched, bool needs_pdcch) { // Do not allocate a user multiple times in the same tti diff --git a/srsenb/src/stack/mac/schedulers/sched_time_pf.cc b/srsenb/src/stack/mac/schedulers/sched_time_pf.cc index b3563db48..90c45cb8e 100644 --- a/srsenb/src/stack/mac/schedulers/sched_time_pf.cc +++ b/srsenb/src/stack/mac/schedulers/sched_time_pf.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,21 +33,27 @@ sched_time_pf::sched_time_pf(const sched_cell_params_t& cell_params_, const sche fairness_coeff = std::stof(sched_args.sched_policy_args); } - std::vector dl_storage; + std::vector dl_storage; dl_storage.reserve(SRSENB_MAX_UES); dl_queue = ue_dl_queue_t(ue_dl_prio_compare{}, std::move(dl_storage)); - std::vector ul_storage; + std::vector ul_storage; ul_storage.reserve(SRSENB_MAX_UES); ul_queue = ue_ul_queue_t(ue_ul_prio_compare{}, std::move(ul_storage)); } void sched_time_pf::new_tti(sched_ue_list& ue_db, sf_sched* tti_sched) { + while (not dl_queue.empty()) { + dl_queue.pop(); + } + while (not ul_queue.empty()) { + ul_queue.pop(); + } current_tti_rx = tti_point{tti_sched->get_tti_rx()}; // remove deleted users from history for (auto it = ue_history_db.begin(); it != ue_history_db.end();) { - if (not ue_db.count(it->first)) { + if (not ue_db.contains(it->first)) { it = ue_history_db.erase(it); } else { ++it; diff --git a/srsenb/src/stack/mac/schedulers/sched_time_rr.cc b/srsenb/src/stack/mac/schedulers/sched_time_rr.cc index 73e50a413..04b8c58a0 100644 --- a/srsenb/src/stack/mac/schedulers/sched_time_rr.cc +++ b/srsenb/src/stack/mac/schedulers/sched_time_rr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index d8d82b82e..758e988c3 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,7 +28,7 @@ #include "srsran/common/string_helpers.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" namespace srsenb { @@ -71,93 +71,67 @@ void ue_cc_softbuffers::clear() } } -cc_used_buffers_map::cc_used_buffers_map(srsran::pdu_queue& shared_pdu_queue_) : - shared_pdu_queue(&shared_pdu_queue_), logger(&srslog::fetch_basic_logger("MAC")) -{} +cc_used_buffers_map::cc_used_buffers_map() : logger(&srslog::fetch_basic_logger("MAC")) {} cc_used_buffers_map::~cc_used_buffers_map() { clear(); } -bool cc_used_buffers_map::push_pdu(tti_point tti, uint32_t len) +srsran::unique_byte_buffer_t cc_used_buffers_map::release_pdu(tti_point tti) { + std::unique_lock lock(mutex); if (not has_tti(tti)) { - return false; - } - uint8_t* buffer = pdu_map[tti.to_uint()]; - if (len > 0) { - shared_pdu_queue->push(buffer, len); - } else { - shared_pdu_queue->deallocate(buffer); - logger->error("Error pushing PDU: null length"); + return nullptr; } + + // Extract PDU from PDU map + srsran::unique_byte_buffer_t pdu = std::move(pdu_map[tti.to_uint()]); + // clear entry in map pdu_map.erase(tti.to_uint()); - return len > 0; + return pdu; } uint8_t* cc_used_buffers_map::request_pdu(tti_point tti, uint32_t len) { + std::unique_lock lock(mutex); if (not pdu_map.has_space(tti.to_uint())) { logger->error("UE buffers: could not allocate buffer for tti=%d", tti.to_uint()); return nullptr; } - uint8_t* pdu = shared_pdu_queue->request(len); + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); if (pdu == nullptr) { - logger->error("UE buffers: Requesting buffer from pool"); + logger->error("UE buffers: Requesting buffer from byte buffer pool"); return nullptr; } + srsran_assert(len < pdu->get_tailroom(), "Requested UL pdu doesn't fit in byte_buffer"); + pdu->N_bytes = len; - bool inserted = pdu_map.insert(tti.to_uint(), pdu); - srsran_assert(inserted, "Failure to allocate new buffer"); - return pdu; + auto inserted_elem = pdu_map.insert(tti.to_uint(), std::move(pdu)); + srsran_assert(inserted_elem.has_value(), "Failure to allocate new buffer in mac::ue"); + return inserted_elem.value()->second->data(); } void cc_used_buffers_map::clear_old_pdus(tti_point current_tti) { - static const uint32_t old_tti_threshold = SRSRAN_FDD_NOF_HARQ + 4; + std::unique_lock lock(mutex); + static const uint32_t old_tti_threshold = SRSRAN_FDD_NOF_HARQ + 4; tti_point max_tti{current_tti - old_tti_threshold}; for (auto& pdu_pair : pdu_map) { tti_point t(pdu_pair.first); if (t < max_tti) { logger->warning("UE buffers: Removing old buffer tti=%d, interval=%d", t.to_uint(), current_tti - t); - remove_pdu(t); + pdu_map.erase(t.to_uint()); } } } -void cc_used_buffers_map::remove_pdu(tti_point tti) -{ - uint8_t* buffer = pdu_map[tti.to_uint()]; - // return pdus back to the queue - shared_pdu_queue->deallocate(buffer); - // clear entry in map - pdu_map.erase(tti.to_uint()); -} - -bool cc_used_buffers_map::try_deallocate_pdu(tti_point tti) -{ - if (has_tti(tti)) { - remove_pdu(tti); - return true; - } - return false; -} - -void cc_used_buffers_map::clear() -{ - for (auto& buffer : pdu_map) { - shared_pdu_queue->deallocate(buffer.second); - } - pdu_map.clear(); -} - uint8_t*& cc_used_buffers_map::operator[](tti_point tti) { - return pdu_map[tti.to_uint()]; + return pdu_map[tti.to_uint()]->msg; } bool cc_used_buffers_map::has_tti(tti_point tti) const @@ -167,7 +141,7 @@ bool cc_used_buffers_map::has_tti(tti_point tti) const //////////////// -cc_buffer_handler::cc_buffer_handler(srsran::pdu_queue& shared_pdu_queue_) : rx_used_buffers(shared_pdu_queue_) +cc_buffer_handler::cc_buffer_handler() { for (auto& harq_buffers : tx_payload_buffer) { for (srsran::unique_byte_buffer_t& tb_buffer : harq_buffers) { @@ -211,7 +185,7 @@ void cc_buffer_handler::reset() } ue::ue(uint16_t rnti_, - uint32_t nof_prb_, + uint32_t enb_cc_idx, sched_interface* sched_, rrc_interface_mac* rrc_, rlc_interface_mac* rlc_, @@ -228,30 +202,22 @@ ue::ue(uint16_t rnti_, mac_msg_dl(20, logger_), mch_mac_msg_dl(10, logger_), mac_msg_ul(20, logger_), - pdus(logger_), ta_fsm(this), - softbuffer_pool(softbuffer_pool_) + softbuffer_pool(softbuffer_pool_), + cc_buffers(nof_cells_) { - for (size_t i = 0; i < nof_cells_; ++i) { - cc_buffers.emplace_back(pdus); - } - pdus.init(this); - // Allocate buffer for PCell - cc_buffers[0].allocate_cc(softbuffer_pool->make()); + cc_buffers[enb_cc_idx].allocate_cc(softbuffer_pool->make()); } -ue::~ue() -{ - std::unique_lock lock(rx_buffers_mutex); - for (auto& cc : cc_buffers) { - cc.get_rx_used_buffers().clear(); - } -} +ue::~ue() {} void ue::reset() { - ue_metrics = {}; + { + std::lock_guard lock(metrics_mutex); + ue_metrics = {}; + } nof_failures = 0; for (auto& cc : cc_buffers) { @@ -269,54 +235,50 @@ void ue::start_pcap(srsran::mac_pcap* pcap_) pcap = pcap_; } -srsran_softbuffer_rx_t* ue::get_rx_softbuffer(const uint32_t ue_cc_idx, const uint32_t tti) +void ue::ue_cfg(const sched_interface::ue_cfg_t& ue_cfg) { - if ((size_t)ue_cc_idx >= cc_buffers.size()) { - ERROR("UE CC Index (%d/%zd) out-of-range", ue_cc_idx, cc_buffers.size()); + for (const auto& ue_cc : ue_cfg.supported_cc_list) { + // Allocate and initialize Rx/Tx softbuffers for new carriers (exclude PCell) + if (ue_cc.active and cc_buffers[ue_cc.enb_cc_idx].empty()) { + cc_buffers[ue_cc.enb_cc_idx].allocate_cc(softbuffer_pool->make()); + } + } +} + +srsran_softbuffer_rx_t* ue::get_rx_softbuffer(uint32_t enb_cc_idx, uint32_t tti) +{ + if ((size_t)enb_cc_idx >= cc_buffers.size() or cc_buffers[enb_cc_idx].empty()) { + ERROR("eNB CC Index (%d/%zd) out-of-range", enb_cc_idx, cc_buffers.size()); return nullptr; } - return &cc_buffers[ue_cc_idx].get_rx_softbuffer(tti); + return &cc_buffers[enb_cc_idx].get_rx_softbuffer(tti); } -srsran_softbuffer_tx_t* -ue::get_tx_softbuffer(const uint32_t ue_cc_idx, const uint32_t harq_process, const uint32_t tb_idx) +srsran_softbuffer_tx_t* ue::get_tx_softbuffer(uint32_t enb_cc_idx, uint32_t harq_process, uint32_t tb_idx) { - if ((size_t)ue_cc_idx >= cc_buffers.size()) { - ERROR("UE CC Index (%d/%zd) out-of-range", ue_cc_idx, cc_buffers.size()); + if ((size_t)enb_cc_idx >= cc_buffers.size() or cc_buffers[enb_cc_idx].empty()) { + ERROR("eNB CC Index (%d/%zd) out-of-range", enb_cc_idx, cc_buffers.size()); return nullptr; } - return &cc_buffers[ue_cc_idx].get_tx_softbuffer(harq_process, tb_idx); + return &cc_buffers[enb_cc_idx].get_tx_softbuffer(harq_process, tb_idx); } -uint8_t* ue::request_buffer(uint32_t tti, uint32_t ue_cc_idx, const uint32_t len) +uint8_t* ue::request_buffer(uint32_t tti, uint32_t enb_cc_idx, uint32_t len) { - std::unique_lock lock(rx_buffers_mutex); - uint8_t* pdu = nullptr; - if (len > 0) { - pdu = cc_buffers[ue_cc_idx].get_rx_used_buffers().request_pdu(tti_point(tti), len); - } else { - logger.error("UE buffers: Requesting buffer for zero bytes"); - } - return pdu; + srsran_assert(len > 0, "UE buffers: Requesting buffer for zero bytes"); + return cc_buffers[enb_cc_idx].get_rx_used_buffers().request_pdu(tti_point(tti), len); } void ue::clear_old_buffers(uint32_t tti) { - std::unique_lock lock(rx_buffers_mutex); - // remove old buffers for (auto& cc : cc_buffers) { cc.get_rx_used_buffers().clear_old_pdus(tti_point{tti}); } } -bool ue::process_pdus() -{ - return pdus.process_pdus(); -} - void ue::set_tti(uint32_t tti) { last_tti = tti; @@ -338,11 +300,11 @@ uint32_t ue::set_ta(int ta_) return nof_cmd; } -void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel) +void ue::process_pdu(srsran::unique_byte_buffer_t pdu, uint32_t ue_cc_idx, uint32_t grant_nof_prbs) { // Unpack ULSCH MAC PDU - mac_msg_ul.init_rx(nof_bytes, true); - mac_msg_ul.parse_packet(pdu); + mac_msg_ul.init_rx(pdu->size(), true); + mac_msg_ul.parse_packet(pdu->data()); if (logger.info.enabled()) { fmt::memory_buffer str_buffer; @@ -350,16 +312,14 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe logger.info("0x%x %s", rnti, srsran::to_c_str(str_buffer)); } - if (pcap) { - pcap->write_ul_crnti(pdu, nof_bytes, rnti, true, last_tti, UL_CC_IDX); + if (pcap != nullptr) { + pcap->write_ul_crnti(pdu->data(), pdu->size(), rnti, true, last_tti, ue_cc_idx); } - if (pcap_net) { - pcap_net->write_ul_crnti(pdu, nof_bytes, rnti, true, last_tti, UL_CC_IDX); + if (pcap_net != nullptr) { + pcap_net->write_ul_crnti(pdu->data(), pdu->size(), rnti, true, last_tti, ue_cc_idx); } - pdus.deallocate(pdu); - uint32_t lcid_most_data = 0; int most_data = -99; @@ -395,7 +355,7 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe // Indicate DRB activity in UL to RRC if (mac_msg_ul.get()->get_sdu_lcid() > 2) { rrc->set_activity_user(rnti); - logger.debug("UL activity rnti=0x%x, n_bytes=%d", rnti, nof_bytes); + logger.debug("UL activity rnti=0x%x, n_bytes=%d", rnti, pdu->size()); } if ((int)mac_msg_ul.get()->get_payload_size() > most_data) { @@ -426,7 +386,7 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe assert(mac_msg_ul.get()); if (!mac_msg_ul.get()->is_sdu()) { // Process MAC Control Element - bsr_received |= process_ce(mac_msg_ul.get()); + bsr_received |= process_ce(mac_msg_ul.get(), grant_nof_prbs); } } @@ -440,24 +400,12 @@ void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channe logger.debug("MAC PDU processed"); } -void ue::deallocate_pdu(uint32_t tti, uint32_t ue_cc_idx) +srsran::unique_byte_buffer_t ue::release_pdu(uint32_t tti, uint32_t enb_cc_idx) { - std::unique_lock lock(rx_buffers_mutex); - if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().try_deallocate_pdu(tti_point(tti))) { - logger.warning( - "UE buffers: Null RX PDU pointer in deallocate_pdu for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx); - } + return cc_buffers[enb_cc_idx].get_rx_used_buffers().release_pdu(tti_point(tti)); } -void ue::push_pdu(uint32_t tti, uint32_t ue_cc_idx, uint32_t len) -{ - std::unique_lock lock(rx_buffers_mutex); - if (not cc_buffers[ue_cc_idx].get_rx_used_buffers().push_pdu(tti_point(tti), len)) { - logger.warning("UE buffers: Failed to push RX PDU for rnti=0x%x tti=%d cc_idx=%d", rnti, tti, ue_cc_idx); - } -} - -bool ue::process_ce(srsran::sch_subh* subh) +bool ue::process_ce(srsran::sch_subh* subh, uint32_t grant_nof_prbs) { uint32_t buff_size_idx[4] = {}; uint32_t buff_size_bytes[4] = {}; @@ -468,7 +416,8 @@ bool ue::process_ce(srsran::sch_subh* subh) switch (subh->ul_sch_ce_type()) { case srsran::ul_sch_lcid::PHR_REPORT: phr = subh->get_phr(); - sched->ul_phr(rnti, (int)phr); + srsran_assert(grant_nof_prbs > 0, "Invalid nof prbs=%d provided for PHR handling", grant_nof_prbs); + sched->ul_phr(rnti, (int)phr, grant_nof_prbs); metrics_phr(phr); break; case srsran::ul_sch_lcid::CRNTI: @@ -511,7 +460,7 @@ bool ue::process_ce(srsran::sch_subh* subh) return is_bsr; } -int ue::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) +uint32_t ue::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) { return rlc->read_pdu(rnti, lcid, payload, requested_bytes); } @@ -573,15 +522,15 @@ void ue::allocate_ce(srsran::sch_pdu* pdu, uint32_t lcid) break; case srsran::dl_sch_lcid::SCELL_ACTIVATION: if (pdu->new_subh()) { - std::array active_scell_list = sched->get_scell_activation_mask(rnti); + std::array active_ccs = sched->get_enb_ue_activ_cc_map(rnti); + std::array active_scell_list{}; + for (int ue_cc_idx : active_ccs) { + if (ue_cc_idx > 0) { + active_scell_list[ue_cc_idx] = true; + } + } if (pdu->get()->set_scell_activation_cmd(active_scell_list)) { phy->set_activation_deactivation_scell(rnti, active_scell_list); - // Allocate and initialize Rx/Tx softbuffers for new carriers (exclude PCell) - for (size_t i = 0; i < std::min(active_scell_list.size(), cc_buffers.size()); ++i) { - if (active_scell_list[i] and cc_buffers[i].empty()) { - cc_buffers[i].allocate_cc(softbuffer_pool->make()); - } - } } else { logger.error("CE: Setting SCell Activation CE"); } @@ -595,7 +544,7 @@ void ue::allocate_ce(srsran::sch_pdu* pdu, uint32_t lcid) } } -uint8_t* ue::generate_pdu(uint32_t ue_cc_idx, +uint8_t* ue::generate_pdu(uint32_t enb_cc_idx, uint32_t harq_pid, uint32_t tb_idx, const sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST], @@ -604,38 +553,34 @@ uint8_t* ue::generate_pdu(uint32_t ue_cc_idx, { std::lock_guard lock(mutex); uint8_t* ret = nullptr; - if (rlc) { - if (ue_cc_idx < SRSRAN_MAX_CARRIERS && harq_pid < SRSRAN_FDD_NOF_HARQ && tb_idx < SRSRAN_MAX_TB) { - srsran::byte_buffer_t* buffer = cc_buffers[ue_cc_idx].get_tx_payload_buffer(harq_pid, tb_idx); - buffer->clear(); - mac_msg_dl.init_tx(buffer, grant_size, false); - for (uint32_t i = 0; i < nof_pdu_elems; i++) { - if (pdu[i].lcid <= (uint32_t)srsran::ul_sch_lcid::PHR_REPORT) { - allocate_sdu(&mac_msg_dl, pdu[i].lcid, pdu[i].nbytes); - } else { - allocate_ce(&mac_msg_dl, pdu[i].lcid); - } + if (enb_cc_idx < SRSRAN_MAX_CARRIERS && harq_pid < SRSRAN_FDD_NOF_HARQ && tb_idx < SRSRAN_MAX_TB) { + srsran::byte_buffer_t* buffer = cc_buffers[enb_cc_idx].get_tx_payload_buffer(harq_pid, tb_idx); + buffer->clear(); + mac_msg_dl.init_tx(buffer, grant_size, false); + for (uint32_t i = 0; i < nof_pdu_elems; i++) { + if (pdu[i].lcid <= (uint32_t)srsran::ul_sch_lcid::PHR_REPORT) { + allocate_sdu(&mac_msg_dl, pdu[i].lcid, pdu[i].nbytes); + } else { + allocate_ce(&mac_msg_dl, pdu[i].lcid); } - ret = mac_msg_dl.write_packet(logger); - if (logger.info.enabled()) { - fmt::memory_buffer str_buffer; - mac_msg_dl.to_string(str_buffer); - logger.info("0x%x %s", rnti, srsran::to_c_str(str_buffer)); - } - } else { - logger.error( - "Invalid parameters calling generate_pdu: cc_idx=%d, harq_pid=%d, tb_idx=%d", ue_cc_idx, harq_pid, tb_idx); + } + ret = mac_msg_dl.write_packet(logger); + if (logger.info.enabled()) { + fmt::memory_buffer str_buffer; + mac_msg_dl.to_string(str_buffer); + logger.info("0x%x %s", rnti, srsran::to_c_str(str_buffer)); } } else { - std::cout << "Error ue not configured (must call config() first" << std::endl; + logger.error( + "Invalid parameters calling generate_pdu: cc_idx=%d, harq_pid=%d, tb_idx=%d", enb_cc_idx, harq_pid, tb_idx); } return ret; } -uint8_t* ue::generate_mch_pdu(uint32_t harq_pid, - sched_interface::dl_pdu_mch_t sched_, - uint32_t nof_pdu_elems, - uint32_t grant_size) +uint8_t* ue::generate_mch_pdu(uint32_t harq_pid, + const sched_interface::dl_pdu_mch_t& sched_, + uint32_t nof_pdu_elems, + uint32_t grant_size) { std::lock_guard lock(mutex); uint8_t* ret = nullptr; @@ -663,9 +608,13 @@ uint8_t* ue::generate_mch_pdu(uint32_t harq_pid, /******* METRICS interface ***************/ void ue::metrics_read(mac_ue_metrics_t* metrics_) { + uint32_t ul_buffer = sched->get_ul_buffer(rnti); + uint32_t dl_buffer = sched->get_dl_buffer(rnti); + + std::lock_guard lock(metrics_mutex); ue_metrics.rnti = rnti; - ue_metrics.ul_buffer = sched->get_ul_buffer(rnti); - ue_metrics.dl_buffer = sched->get_dl_buffer(rnti); + ue_metrics.ul_buffer = ul_buffer; + ue_metrics.dl_buffer = dl_buffer; // set PCell sector id std::array cc_list = sched->get_enb_ue_cc_map(rnti); @@ -681,12 +630,14 @@ void ue::metrics_read(mac_ue_metrics_t* metrics_) void ue::metrics_phr(float phr) { + std::lock_guard lock(metrics_mutex); ue_metrics.phr = SRSRAN_VEC_CMA(phr, ue_metrics.phr, phr_counter); phr_counter++; } void ue::metrics_dl_ri(uint32_t dl_ri) { + std::lock_guard lock(metrics_mutex); if (ue_metrics.dl_ri == 0.0f) { ue_metrics.dl_ri = (float)dl_ri + 1.0f; } else { @@ -697,18 +648,21 @@ void ue::metrics_dl_ri(uint32_t dl_ri) void ue::metrics_dl_pmi(uint32_t dl_ri) { + std::lock_guard lock(metrics_mutex); ue_metrics.dl_pmi = SRSRAN_VEC_CMA((float)dl_ri, ue_metrics.dl_pmi, dl_pmi_counter); dl_pmi_counter++; } void ue::metrics_dl_cqi(uint32_t dl_cqi) { + std::lock_guard lock(metrics_mutex); ue_metrics.dl_cqi = SRSRAN_VEC_CMA((float)dl_cqi, ue_metrics.dl_cqi, dl_cqi_counter); dl_cqi_counter++; } void ue::metrics_rx(bool crc, uint32_t tbs) { + std::lock_guard lock(metrics_mutex); if (crc) { ue_metrics.rx_brate += tbs * 8; } else { @@ -719,6 +673,7 @@ void ue::metrics_rx(bool crc, uint32_t tbs) void ue::metrics_tx(bool crc, uint32_t tbs) { + std::lock_guard lock(metrics_mutex); if (crc) { ue_metrics.tx_brate += tbs * 8; } else { @@ -729,6 +684,7 @@ void ue::metrics_tx(bool crc, uint32_t tbs) void ue::metrics_cnt() { + std::lock_guard lock(metrics_mutex); ue_metrics.nof_tti++; } @@ -741,4 +697,9 @@ void ue::tic() } } +void ue::trigger_padding(int lcid) +{ + sched->ul_bsr(rnti, lcid, 20e6); +} + } // namespace srsenb diff --git a/srsenb/src/stack/rrc/CMakeLists.txt b/srsenb/src/stack/rrc/CMakeLists.txt index e6555ea14..14525ee41 100644 --- a/srsenb/src/stack/rrc/CMakeLists.txt +++ b/srsenb/src/stack/rrc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,8 +18,6 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES rrc.cc rrc_ue.cc rrc_mobility.cc rrc_cell_cfg.cc rrc_bearer_cfg.cc mac_controller.cc ue_rr_cfg.cc ue_meas_cfg.cc) +set(SOURCES rrc.cc rrc_ue.cc rrc_mobility.cc rrc_cell_cfg.cc rrc_bearer_cfg.cc mac_controller.cc ue_rr_cfg.cc ue_meas_cfg.cc rrc_endc.cc) add_library(srsenb_rrc STATIC ${SOURCES}) - -set(SOURCES rrc_nr.cc) -add_library(srsgnb_rrc STATIC ${SOURCES}) + \ No newline at end of file diff --git a/srsenb/src/stack/rrc/mac_controller.cc b/srsenb/src/stack/rrc/mac_controller.cc index ea3f73fec..e744b2783 100644 --- a/srsenb/src/stack/rrc/mac_controller.cc +++ b/srsenb/src/stack/rrc/mac_controller.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,28 +32,28 @@ using ue_cfg_t = sched_interface::ue_cfg_t; /********* Helper Methods ********/ // TS 36.331 9.1.1.2 - CCCH configuration -sched_interface::ue_bearer_cfg_t get_bearer_default_ccch_config() +mac_lc_ch_cfg_t get_bearer_default_ccch_config() { - sched_interface::ue_bearer_cfg_t bearer = {}; - bearer.direction = sched_interface::ue_bearer_cfg_t::BOTH; - bearer.priority = 1; - bearer.pbr = -1; - bearer.bsd = -1; - bearer.group = 0; + mac_lc_ch_cfg_t bearer = {}; + bearer.direction = mac_lc_ch_cfg_t::BOTH; + bearer.priority = 1; + bearer.pbr = -1; + bearer.bsd = -1; + bearer.group = 0; return bearer; } // TS 36.331 9.2.1.1 - SRB1 -sched_interface::ue_bearer_cfg_t get_bearer_default_srb1_config() +mac_lc_ch_cfg_t get_bearer_default_srb1_config() { return get_bearer_default_ccch_config(); } // TS 36.331 9.2.1.2 - SRB2 -sched_interface::ue_bearer_cfg_t get_bearer_default_srb2_config() +mac_lc_ch_cfg_t get_bearer_default_srb2_config() { - sched_interface::ue_bearer_cfg_t bearer = get_bearer_default_srb1_config(); - bearer.priority = 3; + mac_lc_ch_cfg_t bearer = get_bearer_default_srb1_config(); + bearer.priority = 3; return bearer; } @@ -125,7 +125,7 @@ void mac_controller::handle_con_reject() if (not crnti_set) { crnti_set = true; // Need to schedule ConRes CE for UE to see the Reject message - mac->ue_set_crnti(rnti, rnti, ¤t_sched_ue_cfg); + mac->ue_set_crnti(rnti, rnti, current_sched_ue_cfg); } } @@ -146,7 +146,10 @@ int mac_controller::handle_crnti_ce(uint32_t temp_crnti) current_sched_ue_cfg.ue_bearers[i] = next_sched_ue_cfg.ue_bearers[i]; } - return mac->ue_set_crnti(temp_crnti, rnti, ¤t_sched_ue_cfg); + // keep SRB2 disabled until RRCReconfComplete is received + set_srb2_activation(false); + + return mac->ue_set_crnti(temp_crnti, rnti, current_sched_ue_cfg); } int mac_controller::apply_basic_conn_cfg(const asn1::rrc::rr_cfg_ded_s& rr_cfg) @@ -192,7 +195,7 @@ int mac_controller::apply_basic_conn_cfg(const asn1::rrc::rr_cfg_ded_s& rr_cfg) // In case of RRC Connection Setup/Reest message (Msg4), we need to resolve the contention by sending a ConRes CE mac->phy_config_enabled(rnti, false); crnti_set = true; - return mac->ue_set_crnti(rnti, rnti, ¤t_sched_ue_cfg); + return mac->ue_set_crnti(rnti, rnti, current_sched_ue_cfg); } void mac_controller::handle_con_setup_complete() @@ -221,18 +224,23 @@ void mac_controller::handle_con_reconf(const asn1::rrc::rrc_conn_recfg_r8_ies_s& set_drb_activation(false); // Apply changes to MAC scheduler - update_mac(proc_stage_t::config_tx); + update_mac(); + mac->phy_config_enabled(rnti, false); } void mac_controller::handle_con_reconf_complete() { current_sched_ue_cfg = next_sched_ue_cfg; - // Setup all bearers + // Setup SRB2 + set_srb2_activation(true); + + // Setup all data bearers apply_current_bearers_cfg(); // Apply SCell+Bearer changes to MAC - update_mac(proc_stage_t::config_complete); + update_mac(); + mac->phy_config_enabled(rnti, true); } void mac_controller::apply_current_bearers_cfg() @@ -242,7 +250,7 @@ void mac_controller::apply_current_bearers_cfg() for (const drb_to_add_mod_s& drb : drbs) { auto& bcfg = current_sched_ue_cfg.ue_bearers[drb.lc_ch_id]; bcfg = {}; - bcfg.direction = sched_interface::ue_bearer_cfg_t::BOTH; + bcfg.direction = mac_lc_ch_cfg_t::BOTH; bcfg.group = 1; bcfg.priority = 4; if (drb.lc_ch_cfg_present and drb.lc_ch_cfg.ul_specific_params_present) { @@ -263,7 +271,9 @@ void mac_controller::handle_target_enb_ho_cmd(const asn1::rrc::rrc_conn_recfg_r8 ue_cfg_apply_capabilities(next_sched_ue_cfg, *rrc_cfg, uecaps); ue_cfg_apply_reconf_complete_updates(next_sched_ue_cfg, conn_recfg, ue_cell_list); - // Temporarily freeze new allocations for DRBs (SRBs are needed to send RRC Reconf Message) + // Temporarily freeze SRB2 and DRBs. SRB1 is needed to send + // RRC Reconfiguration and receive RRC Reconfiguration Complete + set_srb2_activation(false); set_drb_activation(false); // Apply changes to MAC scheduler @@ -275,10 +285,11 @@ void mac_controller::handle_intraenb_ho_cmd(const asn1::rrc::rrc_conn_recfg_r8_i const srsran::rrc_ue_capabilities_t& uecaps) { next_sched_ue_cfg = current_sched_ue_cfg; - next_sched_ue_cfg.supported_cc_list.resize(1); - next_sched_ue_cfg.supported_cc_list[0].active = true; next_sched_ue_cfg.supported_cc_list[0].enb_cc_idx = cell_common_list.get_pci(conn_recfg.mob_ctrl_info.target_pci)->enb_cc_idx; + for (uint32_t i = 0; i < next_sched_ue_cfg.supported_cc_list.size(); ++i) { + next_sched_ue_cfg.supported_cc_list[0].active = true; + } ue_cfg_apply_conn_reconf(next_sched_ue_cfg, conn_recfg, *rrc_cfg); ue_cfg_apply_capabilities(next_sched_ue_cfg, *rrc_cfg, uecaps); ue_cfg_apply_reconf_complete_updates(next_sched_ue_cfg, conn_recfg, ue_cell_list); @@ -291,11 +302,12 @@ void mac_controller::handle_intraenb_ho_cmd(const asn1::rrc::rrc_conn_recfg_r8_i set_drb_activation(false); // Stop any SRB UL (including SRs) - for (uint32_t i = srb_to_lcid(lte_srb::srb1); i <= srb_to_lcid(lte_srb::srb2); ++i) { - next_sched_ue_cfg.ue_bearers[i].direction = sched_interface::ue_bearer_cfg_t::DL; + for (uint32_t i = srb_to_lcid(lte_srb::srb0); i <= srb_to_lcid(lte_srb::srb2); ++i) { + current_sched_ue_cfg.ue_bearers[i].direction = mac_lc_ch_cfg_t::DL; } - update_mac(mac_controller::config_tx); + update_mac(); + mac->phy_config_enabled(rnti, false); } void mac_controller::handle_ho_prep(const asn1::rrc::ho_prep_info_r8_ies_s& ho_prep) @@ -306,10 +318,16 @@ void mac_controller::handle_ho_prep(const asn1::rrc::ho_prep_info_r8_ies_s& ho_p } } -void mac_controller::handle_max_retx() +void mac_controller::set_radio_bearer_state(mac_lc_ch_cfg_t::direction_t dir) { - set_drb_activation(false); - update_mac(other); + for (uint32_t i = srb_to_lcid(lte_srb::srb0); i <= srb_to_lcid(lte_srb::srb2); ++i) { + current_sched_ue_cfg.ue_bearers[i].direction = dir; + } + for (auto& drb : bearer_list.get_established_drbs()) { + current_sched_ue_cfg.ue_bearers[drb.lc_ch_id].direction = dir; + } + + update_mac(); } void mac_controller::set_scell_activation(const std::bitset& scell_mask) @@ -319,22 +337,24 @@ void mac_controller::set_scell_activation(const std::bitset } } +void mac_controller::set_srb2_activation(bool active) +{ + current_sched_ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction = + active ? mac_lc_ch_cfg_t::BOTH : mac_lc_ch_cfg_t::IDLE; +} + void mac_controller::set_drb_activation(bool active) { for (const drb_to_add_mod_s& drb : bearer_list.get_established_drbs()) { current_sched_ue_cfg.ue_bearers[drb_to_lcid((lte_drb)drb.drb_id)].direction = - active ? sched_interface::ue_bearer_cfg_t::BOTH : sched_interface::ue_bearer_cfg_t::IDLE; + active ? mac_lc_ch_cfg_t::BOTH : mac_lc_ch_cfg_t::IDLE; } } -void mac_controller::update_mac(proc_stage_t stage) +void mac_controller::update_mac() { // Apply changes to MAC scheduler mac->ue_cfg(rnti, ¤t_sched_ue_cfg); - if (stage != proc_stage_t::other) { - // Acknowledge Dedicated Configuration - mac->phy_config_enabled(rnti, stage == proc_stage_t::config_complete); - } } void ue_cfg_apply_phy_cfg_ded(ue_cfg_t& ue_cfg, const asn1::rrc::phys_cfg_ded_s& phy_cfg, const rrc_cfg_t& rrc_cfg) @@ -350,9 +370,14 @@ void ue_cfg_apply_phy_cfg_ded(ue_cfg_t& ue_cfg, const asn1::rrc::phys_cfg_ded_s& auto& pcell_cfg = ue_cfg.supported_cc_list[0]; if (phy_cfg.cqi_report_cfg_present) { if (phy_cfg.cqi_report_cfg.cqi_report_periodic_present) { - auto& cqi_cfg = phy_cfg.cqi_report_cfg.cqi_report_periodic.setup(); - ue_cfg.pucch_cfg.n_pucch = cqi_cfg.cqi_pucch_res_idx; - pcell_cfg.dl_cfg.cqi_report.pmi_idx = cqi_cfg.cqi_pmi_cfg_idx; + const auto& cqi_cfg = phy_cfg.cqi_report_cfg.cqi_report_periodic.setup(); + ue_cfg.pucch_cfg.n_pucch = cqi_cfg.cqi_pucch_res_idx; + pcell_cfg.dl_cfg.cqi_report.pmi_idx = cqi_cfg.cqi_pmi_cfg_idx; + pcell_cfg.dl_cfg.cqi_report.subband_wideband_ratio = 0; + if (cqi_cfg.cqi_format_ind_periodic.type().value == + cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types_opts::subband_cqi) { + pcell_cfg.dl_cfg.cqi_report.subband_wideband_ratio = cqi_cfg.cqi_format_ind_periodic.subband_cqi().k; + } pcell_cfg.dl_cfg.cqi_report.periodic_configured = true; } else if (phy_cfg.cqi_report_cfg.cqi_report_mode_aperiodic_present) { pcell_cfg.aperiodic_cqi_period = rrc_cfg.cqi_cfg.period; @@ -387,7 +412,7 @@ void ue_cfg_apply_srb_updates(ue_cfg_t& ue_cfg, const srb_to_add_mod_list_l& srb srslog::fetch_basic_logger("RRC").warning("Invalid SRB ID %d", (int)srb.srb_id); bcfg = {}; } - bcfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + bcfg.direction = srsenb::mac_lc_ch_cfg_t::BOTH; if (srb.lc_ch_cfg_present and srb.lc_ch_cfg.type().value == srb_to_add_mod_s::lc_ch_cfg_c_::types_opts::explicit_value and srb.lc_ch_cfg.explicit_value().ul_specific_params_present) { @@ -454,9 +479,10 @@ void ue_cfg_apply_reconf_complete_updates(ue_cfg_t& ue_cfg, if (ul_cfg.cqi_report_cfg_scell_r10.cqi_report_periodic_scell_r10_present and ul_cfg.cqi_report_cfg_scell_r10.cqi_report_periodic_scell_r10.type().value == setup_opts::setup) { // periodic CQI - auto& periodic = ul_cfg.cqi_report_cfg_scell_r10.cqi_report_periodic_scell_r10.setup(); - mac_scell.dl_cfg.cqi_report.periodic_configured = true; - mac_scell.dl_cfg.cqi_report.pmi_idx = periodic.cqi_pmi_cfg_idx; + const auto& periodic = ul_cfg.cqi_report_cfg_scell_r10.cqi_report_periodic_scell_r10.setup(); + mac_scell.dl_cfg.cqi_report.periodic_configured = true; + mac_scell.dl_cfg.cqi_report.pmi_idx = periodic.cqi_pmi_cfg_idx; + mac_scell.dl_cfg.cqi_report.subband_wideband_ratio = 0; } else if (ul_cfg.cqi_report_cfg_scell_r10.cqi_report_mode_aperiodic_r10_present) { // aperiodic CQI mac_scell.dl_cfg.cqi_report.aperiodic_configured = diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index df552b0c2..bc4f0f60f 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,16 +20,21 @@ */ #include "srsenb/hdr/stack/rrc/rrc.h" +#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h" +#include "srsenb/hdr/stack/rrc/rrc_endc.h" #include "srsenb/hdr/stack/rrc/rrc_mobility.h" +#include "srsenb/hdr/stack/rrc/rrc_paging.h" +#include "srsenb/hdr/stack/s1ap/s1ap.h" #include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/bcd_helpers.h" +#include "srsran/common/enb_events.h" #include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" #include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/sched_interface.h" using srsran::byte_buffer_t; @@ -37,9 +42,10 @@ using namespace asn1::rrc; namespace srsenb { -rrc::rrc(srsran::task_sched_handle task_sched_) : - logger(srslog::fetch_basic_logger("RRC")), task_sched(task_sched_), rx_pdu_queue(64) -{} +rrc::rrc(srsran::task_sched_handle task_sched_, enb_bearer_manager& manager_) : + logger(srslog::fetch_basic_logger("RRC")), bearer_manager(manager_), task_sched(task_sched_), rx_pdu_queue(128) +{ +} rrc::~rrc() {} @@ -51,12 +57,25 @@ int32_t rrc::init(const rrc_cfg_t& cfg_, s1ap_interface_rrc* s1ap_, gtpu_interface_rrc* gtpu_) { - phy = phy_; - mac = mac_; - rlc = rlc_; - pdcp = pdcp_; - gtpu = gtpu_; - s1ap = s1ap_; + return init(cfg_, phy_, mac_, rlc_, pdcp_, s1ap_, gtpu_, nullptr); +} + +int32_t rrc::init(const rrc_cfg_t& cfg_, + phy_interface_rrc_lte* phy_, + mac_interface_rrc* mac_, + rlc_interface_rrc* rlc_, + pdcp_interface_rrc* pdcp_, + s1ap_interface_rrc* s1ap_, + gtpu_interface_rrc* gtpu_, + rrc_nr_interface_rrc* rrc_nr_) +{ + phy = phy_; + mac = mac_; + rlc = rlc_; + pdcp = pdcp_; + gtpu = gtpu_; + s1ap = s1ap_; + rrc_nr = rrc_nr_; cfg = cfg_; @@ -70,6 +89,14 @@ int32_t rrc::init(const rrc_cfg_t& cfg_, // Loads the PRACH root sequence cfg.sibs[1].sib2().rr_cfg_common.prach_cfg.root_seq_idx = cfg.cell_list[0].root_seq_idx; + if (cfg.num_nr_cells > 0) { + cfg.sibs[1].sib2().ext = true; + cfg.sibs[1].sib2().plmn_info_list_r15.set_present(); + cfg.sibs[1].sib2().plmn_info_list_r15.get()->resize(1); + auto& plmn = cfg.sibs[1].sib2().plmn_info_list_r15.get()->back(); + plmn.upper_layer_ind_r15_present = true; + } + if (generate_sibs() != SRSRAN_SUCCESS) { logger.error("Couldn't generate SIBs."); return false; @@ -90,8 +117,19 @@ int32_t rrc::init(const rrc_cfg_t& cfg_, logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms); logger.info("Max consecutive MAC KOs: %d", cfg.max_mac_dl_kos); + pending_paging.reset(new paging_manager(cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.default_paging_cycle.to_number(), + cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.nb.to_number())); + running = true; + if (logger.debug.enabled()) { + asn1::json_writer js{}; + cfg.srb1_cfg.rlc_cfg.to_json(js); + logger.debug("SRB1 configuration: %s", js.to_string().c_str()); + js = {}; + cfg.srb2_cfg.rlc_cfg.to_json(js); + logger.debug("SRB2 configuration: %s", js.to_string().c_str()); + } return SRSRAN_SUCCESS; } @@ -141,7 +179,7 @@ void rrc::set_radiolink_dl_state(uint16_t rnti, bool crc_res) rrc_pdu p = {rnti, LCID_RADLINK_DL, crc_res, nullptr}; if (not rx_pdu_queue.try_push(std::move(p))) { - logger.error("Failed to push UE activity command to RRC queue"); + logger.error("Failed to push radio link DL state"); } } @@ -151,7 +189,7 @@ void rrc::set_radiolink_ul_state(uint16_t rnti, bool crc_res) rrc_pdu p = {rnti, LCID_RADLINK_UL, crc_res, nullptr}; if (not rx_pdu_queue.try_push(std::move(p))) { - logger.error("Failed to push UE activity command to RRC queue"); + logger.error("Failed to push radio link UL state"); } } @@ -179,12 +217,20 @@ uint32_t rrc::get_nof_users() void rrc::max_retx_attempted(uint16_t rnti) { - rrc_pdu p = {rnti, LCID_RTX_USER, false, nullptr}; + rrc_pdu p = {rnti, LCID_RLC_RTX, false, nullptr}; if (not rx_pdu_queue.try_push(std::move(p))) { logger.error("Failed to push max Retx event to RRC queue"); } } +void rrc::protocol_failure(uint16_t rnti) +{ + rrc_pdu p = {rnti, LCID_PROT_FAIL, false, nullptr}; + if (not rx_pdu_queue.try_push(std::move(p))) { + logger.error("Failed to push protocol failure to RRC queue"); + } +} + // This function is called from PRACH worker (can wait) int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg) { @@ -209,12 +255,12 @@ int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg) if (rnti == SRSRAN_MRNTI) { for (auto& mbms_item : mcch.msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9[0].mbms_session_info_list_r9) { uint32_t lcid = mbms_item.lc_ch_id_r9; - + uint32_t addr_in; // adding UE object to MAC for MRNTI without scheduling configuration (broadcast not part of regular scheduling) - mac->ue_cfg(SRSRAN_MRNTI, NULL); rlc->add_bearer_mrb(SRSRAN_MRNTI, lcid); + bearer_manager.add_eps_bearer(SRSRAN_MRNTI, 1, srsran::srsran_rat_t::lte, lcid); pdcp->add_bearer(SRSRAN_MRNTI, lcid, srsran::make_drb_pdcp_config_t(1, false)); - gtpu->add_bearer(SRSRAN_MRNTI, lcid, 1, 1); + gtpu->add_bearer(SRSRAN_MRNTI, lcid, 1, 1, addr_in); } } return SRSRAN_SUCCESS; @@ -226,12 +272,16 @@ int rrc::add_user(uint16_t rnti, const sched_interface::ue_cfg_t& sched_ue_cfg) void rrc::upd_user(uint16_t new_rnti, uint16_t old_rnti) { // Remove new_rnti - rem_user_thread(new_rnti); + auto new_ue_it = users.find(new_rnti); + if (new_ue_it != users.end()) { + new_ue_it->second->deactivate_bearers(); + rem_user_thread(new_rnti); + } // Send Reconfiguration to old_rnti if is RRC_CONNECT or RRC Release if already released here auto old_it = users.find(old_rnti); if (old_it == users.end()) { - send_rrc_connection_reject(old_rnti); + logger.info("rnti=0x%x received MAC CRNTI CE: 0x%x, but old context is unavailable", new_rnti, old_rnti); return; } ue* ue_ptr = old_it->second.get(); @@ -245,6 +295,10 @@ void rrc::upd_user(uint16_t new_rnti, uint16_t old_rnti) old_it->second->send_connection_reconf(); } } + + // Log event. + event_logger::get().log_connection_resume( + ue_ptr->get_cell_list().get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, old_rnti, new_rnti); } // Note: this method is not part of UE methods, because the UE context may not exist anymore when reject is sent @@ -266,9 +320,8 @@ void rrc::send_rrc_connection_reject(uint16_t rnti) } pdu->N_bytes = bref.distance_bytes(); - char buf[32] = {}; - sprintf(buf, "SRB0 - rnti=0x%x", rnti); - log_rrc_message(buf, Tx, pdu.get(), dl_ccch_msg, dl_ccch_msg.msg.c1().type().to_string()); + log_rrc_message(Tx, rnti, srb_to_lcid(lte_srb::srb0), *pdu, dl_ccch_msg, dl_ccch_msg.msg.c1().type().to_string()); + rlc->write_sdu(rnti, srb_to_lcid(lte_srb::srb0), std::move(pdu)); } @@ -283,6 +336,12 @@ void rrc::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t p } } +void rrc::notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) +{ + logger.warning("Received integrity protection failure indication, rnti=0x%x, lcid=%u", rnti, lcid); + s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::unspecified); +} + /******************************************************************************* S1AP interface *******************************************************************************/ @@ -457,132 +516,39 @@ int rrc::modify_erab(uint16_t rnti, void rrc::add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) { - std::lock_guard lock(paging_mutex); - if (pending_paging.count(ueid) > 0) { - logger.warning("Received Paging for UEID=%d but not yet transmitted", ueid); - return; - } - - paging_record_s paging_elem; if (ue_paging_id.type().value == asn1::s1ap::ue_paging_id_c::types_opts::imsi) { - paging_elem.ue_id.set_imsi(); - paging_elem.ue_id.imsi().resize(ue_paging_id.imsi().size()); - memcpy(paging_elem.ue_id.imsi().data(), ue_paging_id.imsi().data(), ue_paging_id.imsi().size()); - srsran::console("Warning IMSI paging not tested\n"); + pending_paging->add_imsi_paging(ueid, ue_paging_id.imsi()); } else { - paging_elem.ue_id.set_s_tmsi(); - paging_elem.ue_id.s_tmsi().mmec.from_number(ue_paging_id.s_tmsi().mmec[0]); - uint32_t m_tmsi = 0; - uint32_t nof_octets = ue_paging_id.s_tmsi().m_tmsi.size(); - for (uint32_t i = 0; i < nof_octets; i++) { - m_tmsi |= ue_paging_id.s_tmsi().m_tmsi[i] << (8u * (nof_octets - i - 1u)); - } - paging_elem.ue_id.s_tmsi().m_tmsi.from_number(m_tmsi); + pending_paging->add_tmsi_paging(ueid, ue_paging_id.s_tmsi().mmec[0], ue_paging_id.s_tmsi().m_tmsi); } - paging_elem.cn_domain = paging_record_s::cn_domain_e_::ps; - - pending_paging.insert(std::make_pair(ueid, paging_elem)); } -// Described in Section 7 of 36.304 bool rrc::is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { - constexpr static int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}}; - - if (tti == paging_tti) { - *payload_len = byte_buf_paging.N_bytes; - logger.debug("Sending paging to extra carriers. Payload len=%d, TTI=%d", *payload_len, tti); - return true; - } else { - paging_tti = INVALID_TTI; - } - - if (pending_paging.empty()) { - return false; - } - - asn1::rrc::pcch_msg_s pcch_msg; - pcch_msg.msg.set_c1(); - paging_s* paging_rec = &pcch_msg.msg.c1().paging(); - - // Default paging cycle, should get DRX from user - uint32_t T = cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.default_paging_cycle.to_number(); - uint32_t Nb = T * cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.nb.to_number(); - - uint32_t N = T < Nb ? T : Nb; - uint32_t Ns = Nb / T > 1 ? Nb / T : 1; - uint32_t sfn = tti / 10; - - std::vector ue_to_remove; - - { - std::lock_guard lock(paging_mutex); - - int n = 0; - for (auto& item : pending_paging) { - if (n >= ASN1_RRC_MAX_PAGE_REC) { - break; - } - const asn1::rrc::paging_record_s& u = item.second; - uint32_t ueid = ((uint32_t)item.first) % 1024; - uint32_t i_s = (ueid / N) % Ns; - - if ((sfn % T) != (T / N) * (ueid % N)) { - continue; - } - - int sf_idx = sf_pattern[i_s % 4][(Ns - 1) % 4]; - if (sf_idx < 0) { - logger.error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d", Ns, i_s, ueid); - continue; - } - - if ((uint32_t)sf_idx == (tti % 10)) { - paging_rec->paging_record_list_present = true; - paging_rec->paging_record_list.push_back(u); - ue_to_remove.push_back(ueid); - n++; - logger.info("Assembled paging for ue_id=%d, tti=%d", ueid, tti); - } - } - - for (unsigned int i : ue_to_remove) { - pending_paging.erase(i); - } - } - - if (paging_rec->paging_record_list.size() > 0) { - byte_buf_paging.clear(); - asn1::bit_ref bref(byte_buf_paging.msg, byte_buf_paging.get_tailroom()); - if (pcch_msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { - logger.error("Failed to pack PCCH"); - return false; - } - byte_buf_paging.N_bytes = (uint32_t)bref.distance_bytes(); - uint32_t N_bits = (uint32_t)bref.distance(); - - if (payload_len) { - *payload_len = byte_buf_paging.N_bytes; - } - logger.info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes, nbits=%d", - paging_rec->paging_record_list.size(), - byte_buf_paging.N_bytes, - N_bits); - log_rrc_message("PCCH-Message", Tx, &byte_buf_paging, pcch_msg, pcch_msg.msg.c1().type().to_string()); - - paging_tti = tti; // Store paging tti for other carriers - return true; - } - - return false; + *payload_len = pending_paging->pending_pcch_bytes(tti_point(tti)); + return *payload_len > 0; } -void rrc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) +void rrc::read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size) { - std::lock_guard lock(paging_mutex); - if (byte_buf_paging.N_bytes <= buffer_size) { - memcpy(payload, byte_buf_paging.msg, byte_buf_paging.N_bytes); - } + auto read_func = [this, payload, buffer_size](srsran::const_byte_span pdu, const pcch_msg_s& msg, bool first_tx) { + // copy PCCH pdu to buffer + if (pdu.size() > buffer_size) { + logger.warning("byte buffer with size=%zd is too small to fit pcch msg with size=%zd", buffer_size, pdu.size()); + return false; + } + std::copy(pdu.begin(), pdu.end(), payload); + + if (first_tx) { + logger.info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes", + msg.msg.c1().paging().paging_record_list.size(), + pdu.size()); + log_broadcast_rrc_message(SRSRAN_PRNTI, pdu, msg, msg.msg.c1().type().to_string()); + } + return true; + }; + + pending_paging->read_pdu_pcch(tti_point(tti_tx_dl), read_func); } /******************************************************************************* @@ -607,61 +573,113 @@ void rrc::set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_st ue_it->second->mobility_handler->trigger(erabs); } +/******************************************************************************* + EN-DC/NSA helper functions +*******************************************************************************/ + +void rrc::sgnb_addition_ack(uint16_t eutra_rnti, sgnb_addition_ack_params_t params) +{ + logger.info("Received SgNB addition acknowledgement for rnti=0x%x", eutra_rnti); + auto ue_it = users.find(eutra_rnti); + if (ue_it == users.end()) { + logger.warning("rnti=0x%x does not exist", eutra_rnti); + return; + } + ue_it->second->endc_handler->trigger(ue::rrc_endc::sgnb_add_req_ack_ev{params}); + + // trigger RRC Reconfiguration to send NR config to UE + ue_it->second->send_connection_reconf(); +} + +void rrc::sgnb_addition_reject(uint16_t eutra_rnti) +{ + logger.error("Received SgNB addition reject for rnti=%d", eutra_rnti); + auto ue_it = users.find(eutra_rnti); + if (ue_it == users.end()) { + logger.warning("rnti=0x%x does not exist", eutra_rnti); + return; + } + ue_it->second->endc_handler->trigger(ue::rrc_endc::sgnb_add_req_reject_ev{}); +} + +void rrc::sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) +{ + logger.info("User rnti=0x%x successfully enabled EN-DC", eutra_rnti); + auto ue_it = users.find(eutra_rnti); + if (ue_it == users.end()) { + logger.warning("rnti=0x%x does not exist", eutra_rnti); + return; + } + ue_it->second->endc_handler->trigger(ue::rrc_endc::sgnb_add_complete_ev{nr_rnti}); +} + +void rrc::sgnb_inactivity_timeout(uint16_t eutra_rnti) +{ + logger.info("Received NR inactivity timeout for rnti=0x%x - releasing UE", eutra_rnti); + auto ue_it = users.find(eutra_rnti); + if (ue_it == users.end()) { + logger.warning("rnti=0x%x does not exist", eutra_rnti); + return; + } + s1ap->user_release(eutra_rnti, asn1::s1ap::cause_radio_network_opts::user_inactivity); +} + +void rrc::sgnb_release_ack(uint16_t eutra_rnti) +{ + auto ue_it = users.find(eutra_rnti); + if (ue_it != users.end()) { + logger.info("Received SgNB release acknowledgement for rnti=0x%x", eutra_rnti); + ue_it->second->endc_handler->trigger(ue::rrc_endc::sgnb_rel_req_ack_ev{}); + } else { + // The EUTRA does not need to wait for Release Ack in case it wants to destroy the EUTRA UE + logger.info("Received SgNB release acknowledgement for already released rnti=0x%x", eutra_rnti); + } +} + /******************************************************************************* Private functions All private functions are not mutexed and must be called from a mutexed environment from either a public function or the internal thread *******************************************************************************/ -void rrc::parse_ul_ccch(uint16_t rnti, srsran::unique_byte_buffer_t pdu) +void rrc::parse_ul_ccch(ue& ue, srsran::unique_byte_buffer_t pdu) { - if (pdu) { - ul_ccch_msg_s ul_ccch_msg; - asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); - if (ul_ccch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or - ul_ccch_msg.msg.type().value != ul_ccch_msg_type_c::types_opts::c1) { - logger.error("Failed to unpack UL-CCCH message"); - return; - } + srsran_assert(pdu != nullptr, "handle_ul_ccch called for empty message"); - log_rrc_message("SRB0", Rx, pdu.get(), ul_ccch_msg, ul_ccch_msg.msg.c1().type().to_string()); + ul_ccch_msg_s ul_ccch_msg; + asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); + if (ul_ccch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or + ul_ccch_msg.msg.type().value != ul_ccch_msg_type_c::types_opts::c1) { + log_rx_pdu_fail(ue.rnti, srb_to_lcid(lte_srb::srb0), *pdu, "Failed to unpack UL-CCCH message"); + return; + } - auto user_it = users.find(rnti); - switch (ul_ccch_msg.msg.c1().type().value) { - case ul_ccch_msg_type_c::c1_c_::types::rrc_conn_request: - if (user_it != users.end()) { - user_it->second->save_ul_message(std::move(pdu)); - user_it->second->handle_rrc_con_req(&ul_ccch_msg.msg.c1().rrc_conn_request()); - } else { - logger.error("Received ConnectionSetup for rnti=0x%x without context", rnti); - } - break; - case ul_ccch_msg_type_c::c1_c_::types::rrc_conn_reest_request: - if (user_it != users.end()) { - user_it->second->save_ul_message(std::move(pdu)); - user_it->second->handle_rrc_con_reest_req(&ul_ccch_msg.msg.c1().rrc_conn_reest_request()); - } else { - logger.error("Received ConnectionReestablishment for rnti=0x%x without context.", rnti); - } - break; - default: - logger.error("UL CCCH message not recognised"); - break; - } + // Log Rx message + log_rrc_message( + Rx, ue.rnti, srsran::srb_to_lcid(lte_srb::srb0), *pdu, ul_ccch_msg, ul_ccch_msg.msg.c1().type().to_string()); + + switch (ul_ccch_msg.msg.c1().type().value) { + case ul_ccch_msg_type_c::c1_c_::types::rrc_conn_request: + ue.save_ul_message(std::move(pdu)); + ue.handle_rrc_con_req(&ul_ccch_msg.msg.c1().rrc_conn_request()); + break; + case ul_ccch_msg_type_c::c1_c_::types::rrc_conn_reest_request: + ue.save_ul_message(std::move(pdu)); + ue.handle_rrc_con_reest_req(&ul_ccch_msg.msg.c1().rrc_conn_reest_request()); + break; + default: + logger.error("Processing UL-CCCH for rnti=0x%x - Unsupported message type %s", + ul_ccch_msg.msg.c1().type().to_string()); + break; } } ///< User mutex must be hold by caller -void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) +void rrc::parse_ul_dcch(ue& ue, uint32_t lcid, srsran::unique_byte_buffer_t pdu) { - if (pdu) { - auto user_it = users.find(rnti); - if (user_it != users.end()) { - user_it->second->parse_ul_dcch(lcid, std::move(pdu)); - } else { - logger.error("Processing %s: Unknown rnti=0x%x", get_rb_name(lcid), rnti); - } - } + srsran_assert(pdu != nullptr, "handle_ul_dcch called for empty message"); + + ue.parse_ul_dcch(lcid, std::move(pdu)); } ///< User mutex must be hold by caller @@ -689,19 +707,18 @@ void rrc::rem_user(uint16_t rnti) { auto user_it = users.find(rnti); if (user_it != users.end()) { - srsran::console("Disconnecting rnti=0x%x.\n", rnti); - logger.info("Disconnecting rnti=0x%x.", rnti); - - /* First remove MAC and GTPU to stop processing DL/UL traffic for this user - */ + // First remove MAC and GTPU to stop processing DL/UL traffic for this user mac->ue_rem(rnti); // MAC handles PHY gtpu->rem_user(rnti); // Now remove RLC and PDCP + bearer_manager.rem_user(rnti); rlc->rem_user(rnti); pdcp->rem_user(rnti); users.erase(rnti); + + srsran::console("Disconnecting rnti=0x%x.\n", rnti); logger.info("Removed user rnti=0x%x", rnti); } else { logger.error("Removing user rnti=0x%x (does not exist)", rnti); @@ -736,20 +753,21 @@ void rrc::config_mac() item.prach_freq_offset = cfg.sibs[1].sib2().rr_cfg_common.prach_cfg.prach_cfg_info.prach_freq_offset; item.maxharq_msg3tx = cfg.sibs[1].sib2().rr_cfg_common.rach_cfg_common.max_harq_msg3_tx; item.enable_64qam = cfg.sibs[1].sib2().rr_cfg_common.pusch_cfg_common.pusch_cfg_basic.enable64_qam; - item.initial_dl_cqi = cfg.cell_list[ccidx].initial_dl_cqi; item.target_pucch_ul_sinr = cfg.cell_list[ccidx].target_pucch_sinr_db; item.target_pusch_ul_sinr = cfg.cell_list[ccidx].target_pusch_sinr_db; item.enable_phr_handling = cfg.cell_list[ccidx].enable_phr_handling; + item.min_phr_thres = cfg.cell_list[ccidx].min_phr_thres; item.delta_pucch_shift = cfg.sibs[1].sib2().rr_cfg_common.pucch_cfg_common.delta_pucch_shift.to_number(); item.ncs_an = cfg.sibs[1].sib2().rr_cfg_common.pucch_cfg_common.ncs_an; item.n1pucch_an = cfg.sibs[1].sib2().rr_cfg_common.pucch_cfg_common.n1_pucch_an; item.nrb_cqi = cfg.sibs[1].sib2().rr_cfg_common.pucch_cfg_common.nrb_cqi; - item.nrb_pucch = SRSRAN_MAX(cfg.sr_cfg.nof_prb, cfg.cqi_cfg.nof_prb); + item.nrb_pucch = SRSRAN_MAX(cfg.sr_cfg.nof_prb, item.nrb_cqi); logger.info("Allocating %d PRBs for PUCCH", item.nrb_pucch); // Copy base cell configuration - item.cell = cfg.cell; + item.cell = cfg.cell; + item.cell.id = cfg.cell_list[ccidx].pci; // copy secondary cell list info sched_cfg[ccidx].scell_list.reserve(cfg.cell_list[ccidx].scell_list.size()); @@ -833,7 +851,7 @@ uint32_t rrc::generate_sibs() return SRSRAN_ERROR; } asn1::bit_ref bref(sib_buffer->msg, sib_buffer->get_tailroom()); - if (msg[msg_index].pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { + if (msg[msg_index].pack(bref) != asn1::SRSASN_SUCCESS) { logger.error("Failed to pack SIB message %d", msg_index); return SRSRAN_ERROR; } @@ -841,9 +859,13 @@ uint32_t rrc::generate_sibs() cell_ctxt->sib_buffer.push_back(std::move(sib_buffer)); // Log SIBs in JSON format - std::string log_msg("CC" + std::to_string(cc_idx) + " SIB payload"); - log_rrc_message( - log_msg, Tx, cell_ctxt->sib_buffer.back().get(), msg[msg_index], msg[msg_index].msg.c1().type().to_string()); + fmt::memory_buffer membuf; + const char* msg_str = msg[msg_index].msg.c1().type().to_string(); + if (msg[msg_index].msg.c1().type().value != asn1::rrc::bcch_dl_sch_msg_type_c::c1_c_::types_opts::sib_type1) { + msg_str = msg[msg_index].msg.c1().sys_info().crit_exts.type().to_string(); + } + fmt::format_to(membuf, "{}, cc={}, idx={}", msg_str, cc_idx, msg_index); + log_broadcast_rrc_message(SRSRAN_SIRNTI, *cell_ctxt->sib_buffer.back(), msg[msg_index], srsran::to_c_str(membuf)); } if (cfg.sibs[6].type() == asn1::rrc::sys_info_r8_ies_s::sib_type_and_info_item_c_::types::sib7) { @@ -933,8 +955,12 @@ void rrc::configure_mbsfn_sibs() pmch_item->data_mcs = mbms_mcs; pmch_item->mch_sched_period = srsran::pmch_info_t::mch_sched_period_t::rf64; pmch_item->sf_alloc_end = 64 * 6; - phy->configure_mbsfn(&sibs2, &sibs13, mcch_t); - mac->write_mcch(&sibs2, &sibs13, &mcch_t, mcch_payload_buffer, current_mcch_length); + + // Configure PHY when PHY is done being initialized + task_sched.defer_task([this, sibs2, sibs13, mcch_t]() mutable { + phy->configure_mbsfn(&sibs2, &sibs13, mcch_t); + mac->write_mcch(&sibs2, &sibs13, &mcch_t, mcch_payload_buffer, current_mcch_length); + }); } int rrc::pack_mcch() @@ -987,7 +1013,10 @@ int rrc::pack_mcch() const int rlc_header_len = 1; asn1::bit_ref bref(&mcch_payload_buffer[rlc_header_len], sizeof(mcch_payload_buffer) - rlc_header_len); - mcch.pack(bref); + if (mcch.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack MCCH message"); + } + current_mcch_length = bref.distance_bytes(&mcch_payload_buffer[1]); current_mcch_length = current_mcch_length + rlc_header_len; return current_mcch_length; @@ -1002,26 +1031,26 @@ void rrc::tti_clock() // pop cmds from queue rrc_pdu p; while (rx_pdu_queue.try_pop(p)) { - // print Rx PDU - if (p.pdu != nullptr) { - logger.info(p.pdu->msg, p.pdu->N_bytes, "Rx %s PDU", get_rb_name(p.lcid)); - } - // check if user exists auto user_it = users.find(p.rnti); if (user_it == users.end()) { - logger.warning("Discarding PDU for removed rnti=0x%x", p.rnti); + if (p.pdu != nullptr) { + log_rx_pdu_fail(p.rnti, p.lcid, *p.pdu, "unknown rnti"); + } else { + logger.warning("Ignoring rnti=0x%x command %d arg %d. Cause: unknown rnti", p.rnti, p.lcid, p.arg); + } continue; } + ue& ue = *user_it->second; // handle queue cmd switch (p.lcid) { case srb_to_lcid(lte_srb::srb0): - parse_ul_ccch(p.rnti, std::move(p.pdu)); + parse_ul_ccch(ue, std::move(p.pdu)); break; case srb_to_lcid(lte_srb::srb1): case srb_to_lcid(lte_srb::srb2): - parse_ul_dcch(p.rnti, p.lcid, std::move(p.pdu)); + parse_ul_dcch(ue, p.lcid, std::move(p.pdu)); break; case LCID_REM_USER: rem_user(p.rnti); @@ -1038,8 +1067,11 @@ void rrc::tti_clock() case LCID_RADLINK_UL: user_it->second->set_radiolink_ul_state(p.arg); break; - case LCID_RTX_USER: - user_it->second->max_retx_reached(); + case LCID_RLC_RTX: + user_it->second->max_rlc_retx_reached(); + break; + case LCID_PROT_FAIL: + user_it->second->protocol_failure(); break; case LCID_EXIT: logger.info("Exiting thread"); @@ -1051,4 +1083,32 @@ void rrc::tti_clock() } } +void rrc::log_rx_pdu_fail(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu, const char* cause_str) +{ + logger.error( + pdu.data(), pdu.size(), "Rx %s PDU, rnti=0x%x - Discarding. Cause: %s", get_rb_name(lcid), rnti, cause_str); +} + +void rrc::log_rxtx_pdu_impl(direction_t dir, + uint16_t rnti, + uint32_t lcid, + srsran::const_byte_span pdu, + const char* msg_type) +{ + static const char* dir_str[] = {"Rx", "Tx", "Tx S1AP", "Rx S1AP"}; + fmt::memory_buffer membuf; + fmt::format_to(membuf, "{} ", dir_str[dir]); + if (rnti != SRSRAN_PRNTI and rnti != SRSRAN_SIRNTI) { + if (dir == Tx or dir == Rx) { + fmt::format_to(membuf, "{} ", srsran::get_srb_name(srsran::lte_lcid_to_srb(lcid))); + } + fmt::format_to(membuf, "PDU, rnti=0x{:x} ", rnti); + } else { + fmt::format_to(membuf, "Broadcast PDU "); + } + fmt::format_to(membuf, "- {} ({} B)", msg_type, pdu.size()); + + logger.info(pdu.data(), pdu.size(), "%s", srsran::to_c_str(membuf)); +} + } // namespace srsenb diff --git a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc index ea711bae0..ae50967f5 100644 --- a/srsenb/src/stack/rrc/rrc_bearer_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_bearer_cfg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,8 +21,8 @@ #include "srsenb/hdr/stack/rrc/rrc_bearer_cfg.h" #include "srsenb/hdr/common/common_enb.h" +#include "srsran/asn1/obj_id_cmp_utils.h" #include "srsran/asn1/rrc_utils.h" -#include "srsran/rrc/rrc_cfg_utils.h" namespace srsenb { @@ -221,12 +221,12 @@ void bearer_cfg_handler::reestablish_bearers(bearer_cfg_handler&& old_rnti_beare old_rnti_bearers.current_drbs.clear(); } -int bearer_cfg_handler::add_erab(uint8_t erab_id, - const asn1::s1ap::erab_level_qos_params_s& qos, - const asn1::bounded_bitstring<1, 160, true, true>& addr, - uint32_t teid_out, - srsran::const_span nas_pdu, - asn1::s1ap::cause_c& cause) +int bearer_cfg_handler::addmod_erab(uint8_t erab_id, + const asn1::s1ap::erab_level_qos_params_s& qos, + const asn1::bounded_bitstring<1, 160, true, true>& addr, + uint32_t teid_out, + srsran::const_span nas_pdu, + asn1::s1ap::cause_c& cause) { if (erab_id < 5) { logger->error("ERAB id=%d is invalid", erab_id); @@ -267,19 +267,14 @@ int bearer_cfg_handler::add_erab(uint8_t } const rrc_cfg_qci_t& qci_cfg = qci_it->second; - erabs[erab_id].id = erab_id; - erabs[erab_id].lcid = lcid; - erabs[erab_id].qos_params = qos; - erabs[erab_id].address = addr; - erabs[erab_id].teid_out = teid_out; - + // perform checks on QCI config if (addr.length() > 32) { logger->error("Only addresses with length <= 32 are supported"); cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::invalid_qos_combination; return SRSRAN_ERROR; } if (qos.gbr_qos_info_present and not qci_cfg.configured) { - logger->warning("Provided E-RAB id=%d QoS not supported", erab_id); + logger->error("Provided E-RAB id=%d QoS not supported", erab_id); cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::invalid_qos_combination; return SRSRAN_ERROR; } @@ -289,21 +284,29 @@ int bearer_cfg_handler::add_erab(uint8_t int16_t pbr_kbps = qci_cfg.lc_cfg.prioritised_bit_rate.to_number(); uint64_t pbr = pbr_kbps < 0 ? std::numeric_limits::max() : pbr_kbps * 1000u; if (req_bitrate > pbr) { - logger->warning("Provided E-RAB id=%d QoS not supported (guaranteed bitrates)", erab_id); + logger->error("Provided E-RAB id=%d QoS not supported (guaranteed bitrates)", erab_id); cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::invalid_qos_combination; return SRSRAN_ERROR; } } - if (qos.alloc_retention_prio.pre_emption_cap.value == asn1::s1ap::pre_emption_cap_opts::may_trigger_pre_emption and - qos.alloc_retention_prio.prio_level < qci_cfg.lc_cfg.prio) { - logger->warning("Provided E-RAB id=%d QoS not supported (priority %d < %d)", - erab_id, - qos.alloc_retention_prio.prio_level, - qci_cfg.lc_cfg.prio); - cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::invalid_qos_combination; - return SRSRAN_ERROR; + + // If it is an E-RAB modification, remove previous DRB object + if (erabs.count(erab_id) > 0) { + for (auto& drb : current_drbs) { + if (drb.eps_bearer_id_present and drb.eps_bearer_id == erab_id) { + srsran::rem_rrc_obj_id(current_drbs, drb.drb_id); + break; + } + } } + // Consider ERAB as accepted + erabs[erab_id].id = erab_id; + erabs[erab_id].lcid = lcid; + erabs[erab_id].qos_params = qos; + erabs[erab_id].address = addr; + erabs[erab_id].teid_out = teid_out; + if (not nas_pdu.empty()) { erab_info_list[erab_id].assign(nas_pdu.begin(), nas_pdu.end()); logger->info( @@ -368,8 +371,7 @@ int bearer_cfg_handler::modify_erab(uint8_t e } auto address = erab_it->second.address; uint32_t teid_out = erab_it->second.teid_out; - release_erab(erab_id); - return add_erab(erab_id, qos, address, teid_out, nas_pdu, cause); + return addmod_erab(erab_id, qos, address, teid_out, nas_pdu, cause); } int bearer_cfg_handler::add_gtpu_bearer(uint32_t erab_id) @@ -401,9 +403,10 @@ srsran::expected bearer_cfg_handler::add_gtpu_bearer(uint32_t // Initialize ERAB tunnel in GTPU right-away. DRBs are only created during RRC setup/reconf erab_t& erab = it->second; erab_t::gtpu_tunnel bearer; + uint32_t addr_in; bearer.teid_out = teid_out; bearer.addr = addr; - srsran::expected teidin = gtpu->add_bearer(rnti, erab.lcid, addr, teid_out, props); + srsran::expected teidin = gtpu->add_bearer(rnti, erab.id, addr, teid_out, addr_in, props); if (teidin.is_error()) { logger->error("Adding erab_id=%d to GTPU", erab_id); return srsran::default_error_t(); @@ -415,12 +418,7 @@ srsran::expected bearer_cfg_handler::add_gtpu_bearer(uint32_t void bearer_cfg_handler::rem_gtpu_bearer(uint32_t erab_id) { - auto it = erabs.find(erab_id); - if (it == erabs.end()) { - logger->warning("Removing erab_id=%d from GTPU", erab_id); - return; - } - gtpu->rem_bearer(rnti, it->second.lcid); + gtpu->rem_bearer(rnti, erab_id); } void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_s* msg) @@ -449,7 +447,7 @@ void bearer_cfg_handler::fill_pending_nas_info(asn1::rrc::rrc_conn_recfg_r8_ies_ memcpy(msg->ded_info_nas_list[idx].data(), &erab_info[0], erab_info.size()); erab_info_list.erase(info_it); } else { - logger->info("Not adding NAS message to connection reconfiguration. E-RAB id %d", erab_id); + logger->debug("Not adding NAS message to connection reconfiguration. E-RAB id %d", erab_id); } idx++; } diff --git a/srsenb/src/stack/rrc/rrc_cell_cfg.cc b/srsenb/src/stack/rrc/rrc_cell_cfg.cc index 1f7d306c6..21746083e 100644 --- a/srsenb/src/stack/rrc/rrc_cell_cfg.cc +++ b/srsenb/src/stack/rrc/rrc_cell_cfg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -179,7 +179,7 @@ const ue_cell_ded* ue_cell_ded_list::find_cell(uint32_t earfcn, uint32_t pci) co return it == cell_list.end() ? nullptr : &(*it); } -ue_cell_ded* ue_cell_ded_list::add_cell(uint32_t enb_cc_idx) +ue_cell_ded* ue_cell_ded_list::add_cell(uint32_t enb_cc_idx, bool init_pucch) { const enb_cell_common* cell_common = common_list.get_cc_idx(enb_cc_idx); if (cell_common == nullptr) { @@ -202,9 +202,11 @@ ue_cell_ded* ue_cell_ded_list::add_cell(uint32_t enb_cc_idx) cell_list.emplace_back(cell_list.size(), *cell_common); // Allocate CQI, SR, and PUCCH CS resources. If failure, do not add new cell - if (not alloc_cell_resources(ue_cc_idx)) { - rem_last_cell(); - return nullptr; + if (init_pucch) { + if (not alloc_cell_resources(ue_cc_idx)) { + rem_last_cell(); + return nullptr; + } } return &cell_list.back(); @@ -225,8 +227,20 @@ bool ue_cell_ded_list::rem_last_cell() return true; } +bool ue_cell_ded_list::init_pucch_pcell() +{ + if (not alloc_cell_resources(UE_PCELL_CC_IDX)) { + dealloc_sr_resources(); + dealloc_pucch_cs_resources(); + dealloc_cqi_resources(UE_PCELL_CC_IDX); + return false; + } + return true; +} + bool ue_cell_ded_list::alloc_cell_resources(uint32_t ue_cc_idx) { + const uint32_t meas_gap_duration = 6; // Allocate CQI, SR, and PUCCH CS resources. If failure, do not add new cell if (ue_cc_idx == UE_PCELL_CC_IDX) { if (not alloc_sr_resources(cfg.sr_cfg.period)) { @@ -235,9 +249,31 @@ bool ue_cell_ded_list::alloc_cell_resources(uint32_t ue_cc_idx) } ue_cell_ded* cell = get_ue_cc_idx(UE_PCELL_CC_IDX); + cell->meas_gap_offset = 0; cell->meas_gap_period = cell->cell_common->cell_cfg.meas_cfg.meas_gap_period; - cell->meas_gap_offset = pucch_res->next_measgap_offset; - pucch_res->next_measgap_offset += 6; + if (cell->meas_gap_period > 0) { + if (not cell->cell_common->cell_cfg.meas_cfg.meas_gap_offset_subframe.empty()) { + // subframes specified + uint32_t min_users = std::numeric_limits::max(); + for (uint32_t i = 0; i < cell->cell_common->cell_cfg.meas_cfg.meas_gap_offset_subframe.size(); ++i) { + uint32_t idx_offset = cell->cell_common->cell_cfg.meas_cfg.meas_gap_offset_subframe[i] / meas_gap_duration; + if (pucch_res->meas_gap_alloc_map[idx_offset] < min_users) { + min_users = pucch_res->meas_gap_alloc_map[idx_offset]; + cell->meas_gap_offset = cell->cell_common->cell_cfg.meas_cfg.meas_gap_offset_subframe[i]; + } + } + } else { + uint32_t min_users = std::numeric_limits::max(); + for (uint32_t meas_offset = 0; meas_offset < cell->cell_common->cell_cfg.meas_cfg.meas_gap_period; + meas_offset += meas_gap_duration) { + if (pucch_res->meas_gap_alloc_map[meas_offset / meas_gap_duration] < min_users) { + min_users = pucch_res->meas_gap_alloc_map[meas_offset / meas_gap_duration]; + cell->meas_gap_offset = meas_offset; + } + } + } + logger.info("Setup measurement gap period=%d offset=%d", cell->meas_gap_period, cell->meas_gap_offset); + } } else { if (ue_cc_idx == 1 and not n_pucch_cs_present) { // Allocate resources for Format1b CS (will be optional PUCCH3/CS) @@ -251,7 +287,7 @@ bool ue_cell_ded_list::alloc_cell_resources(uint32_t ue_cc_idx) } } if (not alloc_cqi_resources(ue_cc_idx, cfg.cqi_cfg.period)) { - logger.error("Failed to allocate CQIresources for cell ue_cc_idx=%d", ue_cc_idx); + logger.error("Failed to allocate CQI resources for cell ue_cc_idx=%d", ue_cc_idx); return false; } @@ -341,17 +377,13 @@ bool ue_cell_ded_list::alloc_cqi_resources(uint32_t ue_cc_idx, uint32_t period) return false; } - const auto& pcell_pucch_cfg = get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->sib2.rr_cfg_common.pucch_cfg_common; - uint32_t c = SRSRAN_CP_ISNORM(cfg.cell.cp) ? 3 : 2; - uint32_t delta_pucch_shift = pcell_pucch_cfg.delta_pucch_shift.to_number(); - delta_pucch_shift = SRSRAN_MAX(1, delta_pucch_shift); - uint32_t max_users = 12 * c / delta_pucch_shift; + uint32_t max_users = 12; // Allocate all CQI resources for all carriers now // Find freq-time resources with least number of users - int i_min = 0, j_min = 0; + int i_min = -1, j_min = -1; uint32_t min_users = std::numeric_limits::max(); - for (uint32_t i = 0; i < cfg.cqi_cfg.nof_prb; i++) { + for (uint32_t i = 0; i < cfg.sibs[1].sib2().rr_cfg_common.pucch_cfg_common.nrb_cqi; i++) { for (uint32_t j = 0; j < cfg.cqi_cfg.nof_subframes; j++) { if (pucch_res->cqi_sched.nof_users[i][j] < min_users) { i_min = i; @@ -360,7 +392,7 @@ bool ue_cell_ded_list::alloc_cqi_resources(uint32_t ue_cc_idx, uint32_t period) } } } - if (pucch_res->cqi_sched.nof_users[i_min][j_min] > max_users) { + if (pucch_res->cqi_sched.nof_users[i_min][j_min] > max_users || i_min == -1 || j_min == -1) { logger.error("Not enough PUCCH resources to allocate Scheduling Request"); return false; } @@ -396,9 +428,6 @@ bool ue_cell_ded_list::alloc_cqi_resources(uint32_t ue_cc_idx, uint32_t period) // Compute n_pucch_2 uint16_t n_pucch = i_min * max_users + pucch_res->cqi_sched.nof_users[i_min][j_min]; - if (pcell_pucch_cfg.ncs_an) { - n_pucch += pcell_pucch_cfg.ncs_an; - } cell->cqi_res_present = true; cell->cqi_res.pmi_idx = pmi_idx; @@ -453,7 +482,7 @@ bool ue_cell_ded_list::alloc_sr_resources(uint32_t period) uint32_t max_users = 12 * c / delta_pucch_shift; // Find freq-time resources with least number of users - int i_min = 0, j_min = 0; + int i_min = -1, j_min = -1; uint32_t min_users = std::numeric_limits::max(); for (uint32_t i = 0; i < cfg.sr_cfg.nof_prb; i++) { for (uint32_t j = 0; j < cfg.sr_cfg.nof_subframes; j++) { @@ -465,7 +494,7 @@ bool ue_cell_ded_list::alloc_sr_resources(uint32_t period) } } - if (pucch_res->sr_sched.nof_users[i_min][j_min] > max_users) { + if (pucch_res->sr_sched.nof_users[i_min][j_min] > max_users || i_min == -1 || j_min == -1) { logger.error("Not enough PUCCH resources to allocate Scheduling Request"); return false; } diff --git a/srsenb/src/stack/rrc/rrc_endc.cc b/srsenb/src/stack/rrc/rrc_endc.cc new file mode 100644 index 000000000..9ae4bd2e3 --- /dev/null +++ b/srsenb/src/stack/rrc/rrc_endc.cc @@ -0,0 +1,418 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsenb/hdr/stack/rrc/rrc_endc.h" + +namespace srsenb { + +#define Info(fmt, ...) logger.info("ENDC: " fmt, ##__VA_ARGS__) +#define Error(fmt, ...) logger.error("ENDC: " fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) logger.warning("ENDC: " fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) logger.debug("ENDC: " fmt, ##__VA_ARGS__) + +#define procInfo(fmt, ...) parent->logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procWarning(fmt, ...) parent->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procError(fmt, ...) parent->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) + +using namespace asn1::rrc; + +/************************************************************************************************* + * rrc_endc class + ************************************************************************************************/ + +rrc::ue::rrc_endc::rrc_endc(rrc::ue* outer_ue, const rrc_endc_cfg_t& endc_cfg_) : + base_t(outer_ue->parent->logger), + rrc_ue(outer_ue), + rrc_enb(outer_ue->parent), + logger(outer_ue->parent->logger), + endc_cfg(endc_cfg_) +{ + // start SgNB activation if B1 events are disabled + if (endc_cfg.act_from_b1_event == false) { + start_sgnb_addition(); + } +} + +rrc::ue::rrc_endc::~rrc_endc() +{ + start_sgnb_release(); +} + +//! Method to add NR fields to a RRC Connection Reconfiguration Message +bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg) +{ + if (not is_endc_supported()) { + // skipping ENDC-related field + return false; + } + + if (not is_endc_activation_running() && endc_cfg.act_from_b1_event) { + // add measConfig + conn_recfg->meas_cfg_present = true; + meas_cfg_s& meas_cfg = conn_recfg->meas_cfg; + + meas_cfg.meas_obj_to_add_mod_list_present = true; + + // store id of nr meas object to remove it in second reconfiguration message + nr_meas_obj_id = meas_cfg.meas_obj_to_add_mod_list.size() + 1; + + meas_obj_to_add_mod_s meas_obj = {}; + meas_obj.meas_obj_id = nr_meas_obj_id; + meas_obj.meas_obj.set_meas_obj_nr_r15(); + auto& meas_obj_nr = meas_obj.meas_obj.meas_obj_nr_r15(); + meas_obj_nr.carrier_freq_r15 = endc_cfg.abs_frequency_ssb; + meas_obj_nr.rs_cfg_ssb_r15.meas_timing_cfg_r15.periodicity_and_offset_r15 = endc_cfg.ssb_period_offset; + meas_obj_nr.rs_cfg_ssb_r15.meas_timing_cfg_r15.ssb_dur_r15 = endc_cfg.ssb_duration; + meas_obj_nr.rs_cfg_ssb_r15.subcarrier_spacing_ssb_r15 = endc_cfg.ssb_ssc; + meas_obj_nr.ext = true; + meas_obj_nr.band_nr_r15.set_present(true); + meas_obj_nr.band_nr_r15.get()->set_setup() = endc_cfg.nr_band; + meas_cfg.meas_obj_to_add_mod_list.push_back(meas_obj); + + // report config + meas_cfg.report_cfg_to_add_mod_list_present = true; + + // store id of nr report config to remove it in second reconfiguration message + nr_report_cfg_id = meas_cfg.report_cfg_to_add_mod_list.size() + 1; + + report_cfg_to_add_mod_s report_cfg = {}; + report_cfg.report_cfg_id = nr_report_cfg_id; + report_cfg.report_cfg.set_report_cfg_inter_rat(); + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.set_event(); + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().event_id.set_event_b1_nr_r15(); + report_cfg.report_cfg.report_cfg_inter_rat() + .trigger_type.event() + .event_id.event_b1_nr_r15() + .b1_thres_nr_r15.set_nr_rsrp_r15(); + report_cfg.report_cfg.report_cfg_inter_rat() + .trigger_type.event() + .event_id.event_b1_nr_r15() + .b1_thres_nr_r15.nr_rsrp_r15() = 56; + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().event_id.event_b1_nr_r15().report_on_leave_r15 = + false; + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().hysteresis = 0; + report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().time_to_trigger = time_to_trigger_opts::ms100; + + report_cfg.report_cfg.report_cfg_inter_rat().max_report_cells = 8; + report_cfg.report_cfg.report_cfg_inter_rat().report_interv = report_interv_opts::ms120; + report_cfg.report_cfg.report_cfg_inter_rat().report_amount = report_cfg_inter_rat_s::report_amount_opts::r1; + report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.set_present(true); + report_cfg.report_cfg.report_cfg_inter_rat().ext = true; + report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_rsrp = true; + report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_rsrq = true; + report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.get()->ss_sinr = true; + meas_cfg.report_cfg_to_add_mod_list.push_back(report_cfg); + + // measIdToAddModList + meas_cfg.meas_id_to_add_mod_list_present = true; + + // store id of nr meas to remove it in second reconfiguration message + nr_meas_id = meas_cfg.meas_id_to_add_mod_list.size() + 1; + + meas_id_to_add_mod_s meas_id = {}; + meas_id.meas_id = nr_meas_id; + meas_id.meas_obj_id = meas_obj.meas_obj_id; + meas_id.report_cfg_id = report_cfg.report_cfg_id; + meas_cfg.meas_id_to_add_mod_list.push_back(meas_id); + + // quantityConfig + meas_cfg.quant_cfg_present = true; + meas_cfg.quant_cfg.quant_cfg_eutra_present = true; + meas_cfg.quant_cfg.quant_cfg_nr_list_r15.set_present(true); + meas_cfg.quant_cfg.quant_cfg_nr_list_r15.get()->resize(1); + meas_cfg.quant_cfg.ext = true; + auto& meas_quant = meas_cfg.quant_cfg.quant_cfg_nr_list_r15.get()[0]; + meas_quant[0].meas_quant_cell_nr_r15.filt_coeff_rsrp_r15_present = true; + meas_quant[0].meas_quant_cell_nr_r15.filt_coeff_rsrp_r15 = filt_coef_opts::fc3; + + // measGapConfig + meas_cfg.meas_gap_cfg_present = false; // No LTE measGaps allowed while in NSA mode + meas_cfg.meas_gap_cfg.set_setup(); + meas_cfg.meas_gap_cfg.setup().gap_offset.set_gp0() = 16; + } else if (is_in_state()) { + // Deactivate measurement reports, as we do not support measurements after NR activation + conn_recfg->meas_cfg_present = true; + meas_cfg_s& meas_cfg = conn_recfg->meas_cfg; + // Remove meas config + meas_cfg.meas_obj_to_rem_list_present = true; + meas_cfg.meas_obj_to_rem_list.resize(1); + meas_cfg.meas_obj_to_rem_list[0] = nr_meas_obj_id; + // remove report config + meas_cfg.report_cfg_to_rem_list_present = true; + meas_cfg.report_cfg_to_rem_list.resize(1); + meas_cfg.report_cfg_to_rem_list[0] = nr_report_cfg_id; + // remove meas id + meas_cfg.meas_id_to_rem_list_present = true; + meas_cfg.meas_id_to_rem_list.resize(1); + meas_cfg.meas_id_to_rem_list[0] = nr_meas_id; + + // TODO: use bearer manager to remove EUTRA DRB + conn_recfg->rr_cfg_ded.drb_to_release_list_present = true; + conn_recfg->rr_cfg_ded.drb_to_release_list.resize(1); + conn_recfg->rr_cfg_ded.drb_to_release_list[0] = 1; + + // don't send EUTRA dedicated config again + conn_recfg->rr_cfg_ded.phys_cfg_ded_present = false; + + // set MAC main config dedicated + conn_recfg->rr_cfg_ded.mac_main_cfg_present = true; + conn_recfg->rr_cfg_ded.mac_main_cfg.set_explicit_value(); + + auto& mac_main_cfg = conn_recfg->rr_cfg_ded.mac_main_cfg.explicit_value(); + + mac_main_cfg.time_align_timer_ded = time_align_timer_opts::infinity; + mac_main_cfg.phr_cfg_present = true; + mac_main_cfg.phr_cfg.set_setup(); + mac_main_cfg.phr_cfg.setup().dl_pathloss_change = + asn1::rrc::mac_main_cfg_s::phr_cfg_c_::setup_s_::dl_pathloss_change_opts::db3; + mac_main_cfg.phr_cfg.setup().periodic_phr_timer = + asn1::rrc::mac_main_cfg_s::phr_cfg_c_::setup_s_::periodic_phr_timer_opts::sf500; + mac_main_cfg.phr_cfg.setup().prohibit_phr_timer = + asn1::rrc::mac_main_cfg_s::phr_cfg_c_::setup_s_::prohibit_phr_timer_opts::sf200; + + // Disable DC-PHR reporting + mac_main_cfg.ext = false; + mac_main_cfg.mac_main_cfg_v1020.set_present(); + mac_main_cfg.dual_connect_phr.set_present(); + mac_main_cfg.dual_connect_phr.get()->set_setup(); + mac_main_cfg.dual_connect_phr.get()->setup().phr_mode_other_cg_r12 = + asn1::rrc::mac_main_cfg_s::dual_connect_phr_c_::setup_s_::phr_mode_other_cg_r12_opts::real; + + // only add reconfigure EN-DC extension/release 15.10 field if ENDC activation is active + conn_recfg->non_crit_ext_present = true; + conn_recfg->non_crit_ext.non_crit_ext_present = true; + conn_recfg->non_crit_ext.non_crit_ext.non_crit_ext_present = true; + conn_recfg->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true; + conn_recfg->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true; + conn_recfg->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true; + conn_recfg->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = + true; + conn_recfg->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext_present = true; + rrc_conn_recfg_v1510_ies_s& reconf_v1510 = conn_recfg->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + reconf_v1510.nr_cfg_r15_present = true; + reconf_v1510.nr_cfg_r15.set_setup(); + + reconf_v1510.nr_cfg_r15.setup().endc_release_and_add_r15 = false; + reconf_v1510.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present = true; + reconf_v1510.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15 = + get_state()->sgnb_config.nr_secondary_cell_group_cfg_r15; + + reconf_v1510.sk_counter_r15_present = true; + reconf_v1510.sk_counter_r15 = 0; + + reconf_v1510.nr_radio_bearer_cfg1_r15_present = true; + reconf_v1510.nr_radio_bearer_cfg1_r15 = get_state()->sgnb_config.nr_radio_bearer_cfg1_r15; + + // inform FSM + rrc_recfg_sent_ev recfg_sent{}; + trigger(recfg_sent); + } + + return true; +} + +//! Called when UE capabilities are received +void rrc::ue::rrc_endc::handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps) +{ + // skip any further checks if eNB runs without NR cells + if (rrc_enb->cfg.num_nr_cells == 0) { + Debug("Skipping UE capabilities. No NR cell configured."); + trigger(disable_endc_ev{}); + return; + } + + // Only enabled ENDC support if UE caps have been exchanged and UE signals support + if (eutra_caps.non_crit_ext_present) { + if (eutra_caps.non_crit_ext.non_crit_ext_present) { + if (eutra_caps.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present) { + if (eutra_caps.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present) { + auto& ue_cap_v1170 = + eutra_caps.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + if (ue_cap_v1170.non_crit_ext_present) { + if (ue_cap_v1170.non_crit_ext.non_crit_ext.non_crit_ext_present) { + if (ue_cap_v1170.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present) { + if (ue_cap_v1170.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext_present) { + if (ue_cap_v1170.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext.non_crit_ext_present) { + auto& ue_cap_v1330 = ue_cap_v1170.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + if (ue_cap_v1330.non_crit_ext_present) { + if (ue_cap_v1330.non_crit_ext.non_crit_ext.non_crit_ext_present) { + if (ue_cap_v1330.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present) { + if (ue_cap_v1330.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext_present) { + auto& ue_cap_v1510 = ue_cap_v1330.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + if (ue_cap_v1510.irat_params_nr_r15_present) { + if (ue_cap_v1510.irat_params_nr_r15.en_dc_r15_present) { + logger.info("Enabling ENDC support for rnti=%d", rrc_ue->rnti); + return; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + + trigger(disable_endc_ev{}); +} + +//! Method called whenever the eNB receives a MeasReport from the UE +void rrc::ue::rrc_endc::handle_ue_meas_report(const meas_report_s& msg) +{ + // Ignore event if ENDC isn't supported + if (not is_endc_supported()) { + return; + } + + if (not is_in_state()) { + Info("Received a MeasReport while already enabling ENDC support. Ignoring..."); + return; + } + // Check if meas_id is valid + const meas_results_s& meas_res = msg.crit_exts.c1().meas_report_r8().meas_results; + if (not meas_res.meas_result_neigh_cells_present) { + Info("Received a MeasReport, but the UE did not detect any cell."); + return; + } + + // only handle NR cells here, EUTRA is handled in mobility class + if (meas_res.meas_result_neigh_cells.type().value != + meas_results_s::meas_result_neigh_cells_c_::types::meas_result_neigh_cell_list_nr_r15) { + return; + } + + start_sgnb_addition(); +} + +void rrc::ue::rrc_endc::start_sgnb_addition() +{ + // Start EN-DC activation using EPS bearer of EUTRA DRB1 + rrc_nr_interface_rrc::sgnb_addition_req_params_t params = {}; + + const auto& drb_list = rrc_ue->bearer_list.get_established_drbs(); + if (drb_list.size() > 0) { + // move first establised DRB to NR cell + const auto& drb1 = drb_list[0]; + const auto& erab_list = rrc_ue->bearer_list.get_erabs(); + auto erab_it = erab_list.find(drb1.eps_bearer_id); + if (erab_it != erab_list.end()) { + params.eps_bearer_id = drb1.eps_bearer_id; + params.five_qi = erab_it->second.qos_params.qci; // use QCI as 5QI + } else { + logger.error("Couldn't find ERAB config for DRB%d. Aborting SgNB addition for E-UTRA rnti=0x%x", + drb1.drb_id, + rrc_ue->rnti); + return; + } + } else { + logger.error("No LTE DRB established. Aborting SgNB addition for E-UTRA rnti=0x%x", rrc_ue->rnti); + return; + } + + logger.info("Triggering SgNB addition for E-UTRA rnti=0x%x", rrc_ue->rnti); + rrc_enb->rrc_nr->sgnb_addition_request(rrc_ue->rnti, params); + + sgnb_add_req_sent_ev sgnb_add_req{}; + trigger(sgnb_add_req); +} + +void rrc::ue::rrc_endc::start_sgnb_release() +{ + sgnb_rel_req_ev sgnb_rel_req{nr_rnti}; + trigger(sgnb_rel_req); +} + +rrc::ue::rrc_endc::prepare_recfg_st::prepare_recfg_st(rrc_endc* parent_) : logger(parent_->logger) {} + +void rrc::ue::rrc_endc::prepare_recfg_st::enter(rrc_endc* f, const sgnb_add_req_ack_ev& ev) +{ + // store SgNB provided config + sgnb_config = ev.params; + + logger.debug(sgnb_config.nr_secondary_cell_group_cfg_r15.data(), + sgnb_config.nr_secondary_cell_group_cfg_r15.size(), + "nr-SecondaryCellGroupConfig-r15:"); + logger.debug(sgnb_config.nr_radio_bearer_cfg1_r15.data(), + sgnb_config.nr_radio_bearer_cfg1_r15.size(), + "nr-RadioBearerConfig1-r15:"); +} + +// The gNB has accepted the SgNB addition and has already allocated the user and established all bearers +void rrc::ue::rrc_endc::handle_sgnb_add_req_ack(wait_sgnb_add_req_resp_st& s, const sgnb_add_req_ack_ev& ev) +{ + // TODO: copy buffered PDCP data to SeNB + + // TODO: path update procedure with GTPU modify bearer request (for mode 3A and 3X) + + // re-register EPS bearer over NR PDCP + rrc_enb->bearer_manager.add_eps_bearer( + ev.params.nr_rnti, ev.params.eps_bearer_id, srsran::srsran_rat_t::nr, lcid_drb_nr); + + // change GTPU tunnel RNTI to match NR RNTI + rrc_enb->gtpu->mod_bearer_rnti(rrc_ue->rnti, ev.params.nr_rnti); + + // store RNTI for later + nr_rnti = ev.params.nr_rnti; +} + +void rrc::ue::rrc_endc::handle_sgnb_rel_req(const sgnb_rel_req_ev& ev) +{ + logger.info("Triggering SgNB release for E-UTRA rnti=0x%x", rrc_ue->rnti); + rrc_enb->bearer_manager.rem_user(nr_rnti); + rrc_enb->rrc_nr->sgnb_release_request(nr_rnti); +} + +bool rrc::ue::rrc_endc::is_endc_supported() +{ + return not is_in_state(); +} + +void rrc::ue::rrc_endc::handle_rrc_reest(endc_activated_st& s, const rrc_reest_rx_ev& ev) +{ + // Transition GTPU tunnel rnti back from NR RNTI to LTE RNTI, given that the reconfiguration failed + rrc_enb->gtpu->mod_bearer_rnti(nr_rnti, rrc_ue->rnti); +} + +void rrc::ue::rrc_endc::handle_endc_disabled(const disable_endc_ev& ev) +{ + logger.info("Disabling NR EN-DC support for rnti=0x%x", nr_rnti); +} + +bool rrc::ue::rrc_endc::requires_rel_req(const sgnb_rel_req_ev& ev) +{ + return not is_in_state() and not is_in_state() and + not is_in_state(); +} + +} // namespace srsenb diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index 8ac273127..68aeddc0e 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,6 +24,7 @@ #include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h" #include "srsenb/hdr/stack/rrc/ue_meas_cfg.h" #include "srsenb/hdr/stack/rrc/ue_rr_cfg.h" +#include "srsran/asn1/obj_id_cmp_utils.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/bcd_helpers.h" #include "srsran/common/common.h" @@ -34,7 +35,6 @@ #include "srsran/interfaces/enb_pdcp_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" #include "srsran/interfaces/enb_s1ap_interfaces.h" -#include "srsran/rrc/rrc_cfg_utils.h" #include #include @@ -159,7 +159,7 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& const enb_cell_common* target_cell = cell_common_list->get_cell_id(rrc_details::eci_to_cellid(target_eci)); if (target_cell == nullptr) { logger.error("The S1-handover target cell_id=0x%x does not exist", rrc_details::eci_to_cellid(target_eci)); - cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::ho_target_not_allowed; + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::cell_not_available; return SRSRAN_INVALID_RNTI; } @@ -170,21 +170,33 @@ uint16_t rrc::start_ho_ue_resource_alloc(const asn1::s1ap::ho_request_s& ue_cfg.supported_cc_list.resize(1); ue_cfg.supported_cc_list[0].active = true; ue_cfg.supported_cc_list[0].enb_cc_idx = target_cell->enb_cc_idx; - ue_cfg.ue_bearers[0].direction = sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH; ue_cfg.supported_cc_list[0].dl_cfg.tm = SRSRAN_TM1; uint16_t rnti = mac->reserve_new_crnti(ue_cfg); if (rnti == SRSRAN_INVALID_RNTI) { logger.error("Failed to allocate C-RNTI resources"); - cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::radio_res_not_available; + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell; return SRSRAN_INVALID_RNTI; } // Register new user in RRC - add_user(rnti, ue_cfg); + if (add_user(rnti, ue_cfg) != SRSRAN_SUCCESS) { + logger.error("Failed to create user"); + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell; + return SRSRAN_INVALID_RNTI; + } auto it = users.find(rnti); ue* ue_ptr = it->second.get(); + if (not ue_ptr->init_pucch()) { + rem_user(rnti); + logger.warning("Failed to allocate PUCCH resources for rnti=0x%x", rnti); + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell; + return SRSRAN_INVALID_RNTI; + } + // Reset activity timer (Response is not expected) ue_ptr->set_activity_timeout(ue::UE_INACTIVITY_TIMEOUT); + ue_ptr->set_activity(false); // /* Setup e-RABs & DRBs / establish an UL/DL S1 bearer to the S-GW */ // if (not setup_ue_erabs(rnti, msg)) { @@ -224,6 +236,14 @@ bool rrc::ue::rrc_mobility::fill_conn_recfg_no_ho_cmd(asn1::rrc::rrc_conn_recfg_ //! Method called whenever the eNB receives a MeasReport from the UE. In normal situations, an HO procedure is started void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg, srsran::unique_byte_buffer_t pdu) { + asn1::json_writer json_writer; + msg.to_json(json_writer); + event_logger::get().log_measurement_report( + rrc_ue->ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + asn1::octstring_to_string(pdu->msg, pdu->N_bytes), + json_writer.to_string(), + rrc_ue->rnti); + if (not is_in_state()) { Info("Received a MeasReport while UE is performing Handover. Ignoring..."); return; @@ -231,12 +251,12 @@ void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg, srsr // Check if meas_id is valid const meas_results_s& meas_res = msg.crit_exts.c1().meas_report_r8().meas_results; if (not meas_res.meas_result_neigh_cells_present) { - Info("Received a MeasReport, but the UE did not detect any cell."); + Debug("Received a MeasReport, but the UE did not detect any cell."); return; } if (meas_res.meas_result_neigh_cells.type().value != meas_results_s::meas_result_neigh_cells_c_::types::meas_result_list_eutra) { - Error("MeasReports regarding non-EUTRA are not supported!"); + Debug("Skipping non-EUTRA MeasReport."); return; } const meas_id_list& measid_list = rrc_ue->current_ue_cfg.meas_cfg.meas_id_to_add_mod_list; @@ -262,9 +282,11 @@ void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg, srsr const enb_cell_common* c = rrc_enb->cell_common_list->get_pci(e.pci); if (meas_it != meas_list_cfg.end()) { meas_ev.target_eci = meas_it->eci; + meas_ev.target_tac = meas_it->tac; meas_ev.direct_fwd_path = meas_it->direct_forward_path_available; } else if (c != nullptr) { meas_ev.target_eci = (rrc_enb->cfg.enb_id << 8u) + c->cell_cfg.cell_id; + meas_ev.target_tac = pcell->cell_common->cell_cfg.tac; } else { logger.warning("The PCI=%d inside the MeasReport is not recognized.", e.pci); continue; @@ -276,11 +298,6 @@ void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg, srsr break; } } - - event_logger::get().log_measurement_report( - rrc_ue->ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, - asn1::octstring_to_string(pdu->msg, pdu->N_bytes), - rrc_ue->rnti); } /** @@ -290,6 +307,7 @@ void rrc::ue::rrc_mobility::handle_ue_meas_report(const meas_report_s& msg, srsr * - This struct goes in a transparent container to the S1AP */ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, + uint16_t target_tac, uint8_t measobj_id, bool fwd_direct_path_available) { @@ -310,8 +328,7 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, capitem.ue_category = 4; capitem.pdcp_params.max_num_rohc_context_sessions_present = true; capitem.pdcp_params.max_num_rohc_context_sessions = asn1::rrc::pdcp_params_s::max_num_rohc_context_sessions_e_::cs2; - bzero(&capitem.pdcp_params.supported_rohc_profiles, - sizeof(asn1::rrc::rohc_profile_support_list_r15_s)); // TODO: why is it r15? + capitem.pdcp_params.supported_rohc_profiles = {}; capitem.phy_layer_params.ue_specific_ref_sigs_supported = false; capitem.phy_layer_params.ue_tx_ant_sel_supported = false; capitem.rf_params.supported_band_list_eutra.resize(1); @@ -400,7 +417,7 @@ bool rrc::ue::rrc_mobility::start_ho_preparation(uint32_t target_eci, } return rrc_enb->s1ap->send_ho_required( - rrc_ue->rnti, target_eci, target_plmn, fwd_erabs, std::move(buffer), fwd_direct_path_available); + rrc_ue->rnti, target_eci, target_tac, target_plmn, fwd_erabs, std::move(buffer), fwd_direct_path_available); } /** @@ -428,9 +445,9 @@ void rrc::ue::rrc_mobility::handle_ho_preparation_complete(rrc::ho_prep_result } // Check if any E-RAB that was not admitted. Cancel Handover, in such case. - if (msg.protocol_ies.erab_to_release_list_ho_cmd_present) { + if (msg->erab_to_release_list_ho_cmd_present) { get_logger().warning("E-RAB id=%d was not admitted in target eNB. Cancelling handover...", - msg.protocol_ies.erab_to_release_list_ho_cmd.value[0].value.erab_item().erab_id); + msg->erab_to_release_list_ho_cmd.value[0]->erab_item().erab_id); asn1::s1ap::cause_c cause; cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::no_radio_res_available_in_target_cell; trigger(ho_cancel_ev{cause}); @@ -500,14 +517,26 @@ void rrc::ue::rrc_mobility::fill_mobility_reconf_common(asn1::rrc::dl_dcch_msg_s recfg_r8.mob_ctrl_info_present = true; auto& mob_info = recfg_r8.mob_ctrl_info; mob_info.target_pci = target_cell.cell_cfg.pci; - mob_info.t304.value = mob_ctrl_info_s::t304_opts::ms2000; // TODO: make it reconfigurable + mob_info.t304 = target_cell.cell_cfg.t304; mob_info.new_ue_id.from_number(rrc_ue->rnti); - mob_info.rr_cfg_common.pusch_cfg_common = target_cell.sib2.rr_cfg_common.pusch_cfg_common; - mob_info.rr_cfg_common.prach_cfg.root_seq_idx = target_cell.sib2.rr_cfg_common.prach_cfg.root_seq_idx; - mob_info.rr_cfg_common.ul_cp_len = target_cell.sib2.rr_cfg_common.ul_cp_len; - mob_info.rr_cfg_common.p_max_present = true; - mob_info.rr_cfg_common.p_max = rrc_enb->cfg.sib1.p_max; - mob_info.carrier_freq_present = false; // same frequency handover for now + + mob_info.rr_cfg_common.rach_cfg_common_present = true; + mob_info.rr_cfg_common.rach_cfg_common = target_cell.sib2.rr_cfg_common.rach_cfg_common; + mob_info.rr_cfg_common.prach_cfg.root_seq_idx = target_cell.sib2.rr_cfg_common.prach_cfg.root_seq_idx; + mob_info.rr_cfg_common.pdsch_cfg_common_present = true; + mob_info.rr_cfg_common.pdsch_cfg_common = target_cell.sib2.rr_cfg_common.pdsch_cfg_common; + mob_info.rr_cfg_common.pusch_cfg_common = target_cell.sib2.rr_cfg_common.pusch_cfg_common; + mob_info.rr_cfg_common.pucch_cfg_common_present = true; + mob_info.rr_cfg_common.pucch_cfg_common = target_cell.sib2.rr_cfg_common.pucch_cfg_common; + mob_info.rr_cfg_common.srs_ul_cfg_common_present = true; + mob_info.rr_cfg_common.srs_ul_cfg_common = target_cell.sib2.rr_cfg_common.srs_ul_cfg_common; + mob_info.rr_cfg_common.ul_pwr_ctrl_common_present = true; + mob_info.rr_cfg_common.ul_pwr_ctrl_common = target_cell.sib2.rr_cfg_common.ul_pwr_ctrl_common; + mob_info.rr_cfg_common.p_max_present = true; + mob_info.rr_cfg_common.p_max = rrc_enb->cfg.sib1.p_max; + mob_info.rr_cfg_common.ul_cp_len = target_cell.sib2.rr_cfg_common.ul_cp_len; + + mob_info.carrier_freq_present = false; // same frequency handover for now asn1::number_to_enum(mob_info.carrier_bw.dl_bw, target_cell.mib.dl_bw.to_number()); if (target_cell.cell_cfg.dl_earfcn != src_dl_earfcn) { mob_info.carrier_freq_present = true; @@ -604,11 +633,11 @@ rrc::ue::rrc_mobility::s1_source_ho_st::start_enb_status_transfer(const asn1::s1 } // Setup GTPU forwarding tunnel - if (s1ap_ho_cmd.protocol_ies.erab_subjectto_data_forwarding_list_present) { - const auto& fwd_erab_list = s1ap_ho_cmd.protocol_ies.erab_subjectto_data_forwarding_list.value; + if (s1ap_ho_cmd->erab_subjectto_data_forwarding_list_present) { + const auto& fwd_erab_list = s1ap_ho_cmd->erab_subjectto_data_forwarding_list.value; const auto& erab_list = rrc_ue->bearer_list.get_erabs(); for (const auto& e : fwd_erab_list) { - const auto& fwd_erab = e.value.erab_data_forwarding_item(); + const auto& fwd_erab = e->erab_data_forwarding_item(); auto it = erab_list.find(fwd_erab.erab_id); if (it == erab_list.end()) { Warning("E-RAB id=%d subject to forwarding not found\n", fwd_erab.erab_id); @@ -636,14 +665,16 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::enter(rrc_mobility* f, const ho_mea logger.info("Starting S1 Handover of rnti=0x%x to cellid=0x%x.", rrc_ue->rnti, ev.target_eci); report = ev; - if (not parent_fsm()->start_ho_preparation(report.target_eci, report.meas_obj->meas_obj_id, ev.direct_fwd_path)) { + if (not parent_fsm()->start_ho_preparation( + report.target_eci, report.target_tac, report.meas_obj->meas_obj_id, ev.direct_fwd_path)) { trigger(srsran::failure_ev{}); } } /** * TS 36.413, Section 8.4.2 - Handover Resource Allocation - * @brief: Send "eNBStatusTransfer" message from source eNB to MME, and setup Forwarding GTPU tunnel + * @brief: Called in SeNB when "Handover Command" is received + * Send "eNBStatusTransfer" message from source eNB to MME, and setup Forwarding GTPU tunnel * - PDCP provides the bearers' DL/UL HFN and COUNT to be put inside a transparent container * - The eNB sends eNBStatusTransfer to MME * - A GTPU forwarding tunnel is opened to forward buffered PDCP PDUs and incoming GTPU PDUs @@ -680,20 +711,41 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cmd(wait_ho_cmd& s, const } /* Enter Handover Execution */ - // TODO: Do anything with MeasCfg info within the Msg (e.g. update ue_var_meas)? - // Disable DRBs in the MAC, while Reconfiguration is taking place. + // Disable DRBs in the MAC and PDCP, while Reconfiguration is taking place. + for (const drb_to_add_mod_s& drb : rrc_ue->bearer_list.get_established_drbs()) { + rrc_ue->parent->pdcp->set_enabled(rrc_ue->rnti, drb_to_lcid((lte_drb)drb.drb_id), false); + } rrc_ue->mac_ctrl.set_drb_activation(false); - rrc_ue->mac_ctrl.update_mac(mac_controller::proc_stage_t::other); + rrc_ue->mac_ctrl.update_mac(); // Send HO Command to UE - if (not rrc_ue->send_dl_dcch(&dl_dcch_msg)) { + std::string octet_str; + if (not rrc_ue->send_dl_dcch(&dl_dcch_msg, nullptr, &octet_str)) { asn1::s1ap::cause_c cause; cause.set_protocol().value = asn1::s1ap::cause_protocol_opts::unspecified; trigger(ho_cancel_ev{cause}); return; } + // Log rrc release event. + asn1::json_writer json_writer; + dl_dcch_msg.to_json(json_writer); + event_logger::get().log_rrc_event(rrc_ue->ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + octet_str, + json_writer.to_string(), + static_cast(rrc_event_type::con_reconf), + static_cast(procedure_result_code::none), + rrc_ue->rnti); + + // Log HO command. + event_logger::get().log_handover_command( + rrc_ue->ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + reconf.crit_exts.c1().rrc_conn_recfg_r8().mob_ctrl_info.target_pci, + reconf.crit_exts.c1().rrc_conn_recfg_r8().mob_ctrl_info.carrier_freq.dl_carrier_freq, + reconf.crit_exts.c1().rrc_conn_recfg_r8().mob_ctrl_info.new_ue_id.to_number(), + rrc_ue->rnti); + /* Start S1AP eNBStatusTransfer Procedure */ asn1::s1ap::cause_c cause = start_enb_status_transfer(*ho_cmd.s1ap_ho_cmd); if (cause.type().value != asn1::s1ap::cause_c::types_opts::nulltype) { @@ -741,7 +793,7 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& trigger(ho_failure_ev{cause}); return; } - rrc_enb->log_rrc_message("HandoverPreparation", direction_t::fromS1AP, rrc_container, hoprep, "HandoverPreparation"); + rrc_enb->log_rrc_message(direction_t::fromS1AP, rrc_ue->rnti, -1, rrc_container, hoprep, "HandoverPreparation"); /* Setup UE current state in TeNB based on HandoverPreparation message */ const ho_prep_info_r8_ies_s& hoprep_r8 = hoprep.crit_exts.c1().ho_prep_info_r8(); @@ -770,7 +822,7 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& rrc_ue->ue_security_cfg.get_security_algorithm_cfg(); recfg_r8.security_cfg_ho.handov_type.intra_lte().key_change_ind = false; recfg_r8.security_cfg_ho.handov_type.intra_lte().next_hop_chaining_count = - ho_req.ho_req_msg->protocol_ies.security_context.value.next_hop_chaining_count; + (*ho_req.ho_req_msg)->security_context.value.next_hop_chaining_count; /* Prepare Handover Command to be sent via S1AP */ srsran::unique_byte_buffer_t ho_cmd_pdu = srsran::make_byte_buffer(); @@ -788,7 +840,7 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& return; } ho_cmd_pdu->N_bytes = bref2.distance_bytes(); - rrc_enb->log_rrc_message("RRC container", direction_t::toS1AP, ho_cmd_pdu.get(), dl_dcch_msg, "HandoverCommand"); + rrc_enb->log_rrc_message(direction_t::toS1AP, rrc_ue->rnti, -1, *ho_cmd_pdu, dl_dcch_msg, "HandoverCommand"); asn1::rrc::ho_cmd_s ho_cmd; asn1::rrc::ho_cmd_r8_ies_s& ho_cmd_r8 = ho_cmd.crit_exts.set_c1().set_ho_cmd_r8(); @@ -823,16 +875,16 @@ void rrc::ue::rrc_mobility::handle_ho_requested(idle_st& s, const ho_req_rx_ev& // Establish GTPU Forwarding Paths if (ho_req.transparent_container->erab_info_list_present) { const auto& lst = ho_req.transparent_container->erab_info_list; - const auto* it = std::find_if( - lst.begin(), - lst.end(), - [&erab](const asn1::s1ap::protocol_ie_single_container_s& fwd_erab) { - return fwd_erab.value.erab_info_list_item().erab_id == erab.second.id; - }); + const auto* it = + std::find_if(lst.begin(), + lst.end(), + [&erab](const asn1::protocol_ie_single_container_s& fwd_erab) { + return fwd_erab->erab_info_list_item().erab_id == erab.second.id; + }); if (it == lst.end()) { continue; } - const auto& fwd_erab = it->value.erab_info_list_item(); + const auto& fwd_erab = (*it)->erab_info_list_item(); if (fwd_erab.dl_forwarding_present and fwd_erab.dl_forwarding.value == asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed) { @@ -895,8 +947,8 @@ bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& const cell_cfg_t& target_cell_cfg = target_cell->cell_common->cell_cfg; // Establish ERABs/DRBs - for (const auto& erab_item : ho_req_msg.protocol_ies.erab_to_be_setup_list_ho_req.value) { - const auto& erab = erab_item.value.erab_to_be_setup_item_ho_req(); + for (const auto& erab_item : ho_req_msg->erab_to_be_setup_list_ho_req.value) { + const auto& erab = erab_item->erab_to_be_setup_item_ho_req(); if (erab.ext) { get_logger().warning("Not handling E-RABToBeSetupList extensions"); } @@ -912,7 +964,7 @@ bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& uint32_t teid_out = 0; srsran::uint8_to_uint32(erab.gtp_teid.data(), &teid_out); asn1::s1ap::cause_c erab_cause; - if (rrc_ue->bearer_list.add_erab( + if (rrc_ue->bearer_list.addmod_erab( erab.erab_id, erab.erab_level_qos_params, erab.transport_layer_address, teid_out, {}, erab_cause) != SRSRAN_SUCCESS) { erabs_failed_to_setup.emplace_back(); @@ -932,13 +984,13 @@ bool rrc::ue::rrc_mobility::apply_ho_prep_cfg(const ho_prep_info_r8_ies_s& // Regenerate AS Keys // See TS 33.401, Sec. 7.2.8.4.3 - if (not rrc_ue->ue_security_cfg.set_security_capabilities(ho_req_msg.protocol_ies.ue_security_cap.value)) { + if (not rrc_ue->ue_security_cfg.set_security_capabilities(ho_req_msg->ue_security_cap.value)) { cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::encryption_and_or_integrity_protection_algorithms_not_supported; return false; } - rrc_ue->ue_security_cfg.set_security_key(ho_req_msg.protocol_ies.security_context.value.next_hop_param); - rrc_ue->ue_security_cfg.set_ncc(ho_req_msg.protocol_ies.security_context.value.next_hop_chaining_count); + rrc_ue->ue_security_cfg.set_security_key(ho_req_msg->security_context.value.next_hop_param); + rrc_ue->ue_security_cfg.set_ncc(ho_req_msg->security_context.value.next_hop_chaining_count); rrc_ue->ue_security_cfg.regenerate_keys_handover(target_cell_cfg.pci, target_cell_cfg.dl_earfcn); // Save UE Capabilities @@ -983,6 +1035,12 @@ void rrc::ue::rrc_mobility::handle_recfg_complete(wait_recfg_comp& s, const recf uint64_t target_eci = (rrc_enb->cfg.enb_id << 8u) + target_cell->cell_common->cell_cfg.cell_id; rrc_enb->s1ap->send_ho_notify(rrc_ue->rnti, target_eci); + + // Enable forwarding of GTPU SDUs coming from Source eNB Tunnel to PDCP + auto& fwd_tunnels = get_state()->pending_tunnels; + for (uint32_t teid : fwd_tunnels) { + rrc_enb->gtpu->set_tunnel_status(teid, true); + } } void rrc::ue::rrc_mobility::handle_status_transfer(s1_target_ho_st& s, const status_transfer_ev& erabs) @@ -992,7 +1050,7 @@ void rrc::ue::rrc_mobility::handle_status_transfer(s1_target_ho_st& s, const sta // Set DRBs SNs for (const auto& erab : erabs) { - const auto& erab_item = erab.value.bearers_subject_to_status_transfer_item(); + const auto& erab_item = erab->bearers_subject_to_status_transfer_item(); auto erab_it = rrc_ue->bearer_list.get_erabs().find(erab_item.erab_id); if (erab_it == rrc_ue->bearer_list.get_erabs().end()) { logger.warning("The E-RAB Id=%d is not recognized", erab_item.erab_id); @@ -1003,7 +1061,7 @@ void rrc::ue::rrc_mobility::handle_status_transfer(s1_target_ho_st& s, const sta auto drb_it = std::find_if( drbs.begin(), drbs.end(), [drbid](const drb_to_add_mod_s& drb) { return (lte_drb)drb.drb_id == drbid; }); if (drb_it == drbs.end()) { - logger.warning("The DRB id=%d does not exist", (int)drbid); + logger.warning("The DRB id=%d does not exist", drbid); } srsran::pdcp_lte_state_t drb_state{}; @@ -1021,11 +1079,6 @@ void rrc::ue::rrc_mobility::handle_status_transfer(s1_target_ho_st& s, const sta rrc_enb->pdcp->set_bearer_state(rrc_ue->rnti, drb_it->lc_ch_id, drb_state); } - // Enable forwarding of GTPU SDUs coming from Source eNB Tunnel to PDCP - for (uint32_t teid : s.pending_tunnels) { - rrc_enb->gtpu->set_tunnel_status(teid, true); - } - // Check if there is any pending Reconfiguration Complete. If there is, self-trigger if (pending_recfg_complete.crit_exts.type().value != rrc_conn_recfg_complete_s::crit_exts_c_::types_opts::nulltype) { trigger(pending_recfg_complete); @@ -1084,6 +1137,14 @@ void rrc::ue::rrc_mobility::intraenb_ho_st::enter(rrc_mobility* f, const ho_meas f->trigger(srsran::failure_ev{}); return; } + + // Log HO command. + event_logger::get().log_handover_command( + f->rrc_ue->get_cell_list().get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + reconf_r8.mob_ctrl_info.target_pci, + reconf_r8.mob_ctrl_info.carrier_freq.dl_carrier_freq, + reconf_r8.mob_ctrl_info.new_ue_id.to_number(), + f->rrc_ue->rnti); } void rrc::ue::rrc_mobility::handle_crnti_ce(intraenb_ho_st& s, const user_crnti_upd_ev& ev) @@ -1093,6 +1154,13 @@ void rrc::ue::rrc_mobility::handle_crnti_ce(intraenb_ho_st& s, const user_crnti_ s.last_temp_crnti = ev.temp_crnti; if (is_first_crnti_ce) { + // Stop all running RLF timers + // Note: The RLF timer can be triggered during Handover because the UE did not RLC-ACK the Handover Command + // Once the Handover is complete, to avoid releasing the UE, the RLF timer should stop. + rrc_ue->rlc_rlf_timer.stop(); + rrc_ue->phy_dl_rlf_timer.stop(); + rrc_ue->phy_ul_rlf_timer.stop(); + // Need to reset SNs of bearers. rrc_enb->rlc->reestablish(rrc_ue->rnti); rrc_enb->pdcp->reestablish(rrc_ue->rnti); diff --git a/srsenb/src/stack/rrc/rrc_nr.cc b/srsenb/src/stack/rrc/rrc_nr.cc deleted file mode 100644 index d66058b98..000000000 --- a/srsenb/src/stack/rrc/rrc_nr.cc +++ /dev/null @@ -1,409 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsenb/hdr/stack/rrc/rrc_nr.h" -#include "srsenb/hdr/common/common_enb.h" -#include "srsran/asn1/rrc_nr_utils.h" -#include "srsran/common/common_nr.h" - -using namespace asn1::rrc_nr; - -namespace srsenb { - -rrc_nr::rrc_nr(srsran::timer_handler* timers_) : logger(srslog::fetch_basic_logger("RRC-NR")), timers(timers_) {} - -int rrc_nr::init(const rrc_nr_cfg_t& cfg_, - phy_interface_stack_nr* phy_, - mac_interface_rrc_nr* mac_, - rlc_interface_rrc_nr* rlc_, - pdcp_interface_rrc_nr* pdcp_, - ngap_interface_rrc_nr* ngap_, - gtpu_interface_rrc_nr* gtpu_) -{ - phy = phy_; - mac = mac_; - rlc = rlc_; - pdcp = pdcp_; - gtpu = gtpu_; - ngap = ngap_; - - // TODO: overwriting because we are not passing config right now - cfg = update_default_cfg(cfg_); - - // config logging - logger.set_level(srslog::str_to_basic_level(cfg.log_level)); - logger.set_hex_dump_max_size(cfg.log_hex_limit); - - // derived - slot_dur_ms = 1; - - if (generate_sibs() != SRSRAN_SUCCESS) { - logger.error("Couldn't generate SIB messages."); - return SRSRAN_ERROR; - } - - config_mac(); - - // add dummy user - logger.info("Creating dummy DRB for RNTI=%d on LCID=%d", cfg.coreless.rnti, cfg.coreless.drb_lcid); - add_user(cfg.coreless.rnti); - srsran::rlc_config_t rlc_cnfg = srsran::rlc_config_t::default_rlc_um_nr_config(6); - rlc->add_bearer(cfg.coreless.rnti, cfg.coreless.drb_lcid, rlc_cnfg); - srsran::pdcp_config_t pdcp_cnfg{cfg.coreless.drb_lcid, - srsran::PDCP_RB_IS_DRB, - srsran::SECURITY_DIRECTION_DOWNLINK, - srsran::SECURITY_DIRECTION_UPLINK, - srsran::PDCP_SN_LEN_18, - srsran::pdcp_t_reordering_t::ms500, - srsran::pdcp_discard_timer_t::infinity, - false, - srsran::srsran_rat_t::nr}; - pdcp->add_bearer(cfg.coreless.rnti, cfg.coreless.drb_lcid, pdcp_cnfg); - - logger.info("Started"); - - running = true; - - return SRSRAN_SUCCESS; -} - -void rrc_nr::stop() -{ - if (running) { - running = false; - } - users.clear(); -} - -template -void rrc_nr::log_rrc_message(const std::string& source, - const direction_t dir, - const srsran::byte_buffer_t* pdu, - const T& msg) -{ - if (logger.debug.enabled()) { - asn1::json_writer json_writer; - msg.to_json(json_writer); - logger.debug(pdu->msg, - pdu->N_bytes, - "%s - %s %s (%d B)", - source.c_str(), - dir == Tx ? "Tx" : "Rx", - msg.msg.c1().type().to_string(), - pdu->N_bytes); - logger.debug("Content:\n%s", json_writer.to_string().c_str()); - } else if (logger.info.enabled()) { - logger.info( - "%s - %s %s (%d B)", source.c_str(), dir == Tx ? "Tx" : "Rx", msg.msg.c1().type().to_string(), pdu->N_bytes); - } -} - -rrc_nr_cfg_t rrc_nr::update_default_cfg(const rrc_nr_cfg_t& current) -{ - // NOTE: This function is temporary. - rrc_nr_cfg_t cfg_default = current; - - // Fill MIB - cfg_default.mib.sub_carrier_spacing_common.value = mib_s::sub_carrier_spacing_common_opts::scs15or60; - cfg_default.mib.ssb_subcarrier_offset = 0; - cfg_default.mib.intra_freq_resel.value = mib_s::intra_freq_resel_opts::allowed; - cfg_default.mib.cell_barred.value = mib_s::cell_barred_opts::not_barred; - cfg_default.mib.pdcch_cfg_sib1.search_space_zero = 0; - cfg_default.mib.pdcch_cfg_sib1.ctrl_res_set_zero = 0; - cfg_default.mib.dmrs_type_a_position.value = mib_s::dmrs_type_a_position_opts::pos2; - cfg_default.mib.sys_frame_num.from_number(0); - - cfg_default.cell.nof_prb = 25; - cfg_default.cell.nof_ports = 1; - cfg_default.cell.id = 0; - cfg_default.cell.cp = SRSRAN_CP_NORM; - cfg_default.cell.frame_type = SRSRAN_FDD; - cfg_default.cell.phich_length = SRSRAN_PHICH_NORM; - cfg_default.cell.phich_resources = SRSRAN_PHICH_R_1; - - // Fill SIB1 - cfg_default.sib1.cell_access_related_info.plmn_id_list.resize(1); - cfg_default.sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list.resize(1); - srsran::plmn_id_t plmn; - plmn.from_string("90170"); - srsran::to_asn1(&cfg_default.sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list[0], plmn); - cfg_default.sib1.cell_access_related_info.plmn_id_list[0].cell_id.from_number(1); - cfg_default.sib1.cell_access_related_info.plmn_id_list[0].cell_reserved_for_oper.value = - plmn_id_info_s::cell_reserved_for_oper_opts::not_reserved; - cfg_default.sib1.si_sched_info_present = true; - cfg_default.sib1.si_sched_info.si_request_cfg.rach_occasions_si_present = true; - cfg_default.sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.ra_resp_win.value = - rach_cfg_generic_s::ra_resp_win_opts::sl8; - cfg_default.sib1.si_sched_info.si_win_len.value = si_sched_info_s::si_win_len_opts::s20; - cfg_default.sib1.si_sched_info.sched_info_list.resize(1); - cfg_default.sib1.si_sched_info.sched_info_list[0].si_broadcast_status.value = - sched_info_s::si_broadcast_status_opts::broadcasting; - cfg_default.sib1.si_sched_info.sched_info_list[0].si_periodicity.value = sched_info_s::si_periodicity_opts::rf16; - cfg_default.sib1.si_sched_info.sched_info_list[0].sib_map_info.resize(1); - // scheduling of SI messages - cfg_default.sib1.si_sched_info.sched_info_list[0].sib_map_info[0].type.value = sib_type_info_s::type_opts::sib_type2; - cfg_default.sib1.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag_present = true; - cfg_default.sib1.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag = 0; - - // Fill SIB2+ - cfg_default.nof_sibs = 1; - sib2_s& sib2 = cfg_default.sibs[0].set_sib2(); - sib2.cell_resel_info_common.q_hyst.value = sib2_s::cell_resel_info_common_s_::q_hyst_opts::db5; - // TODO: Fill SIB2 values - - // set loglevel - cfg_default.log_level = "debug"; - cfg_default.log_hex_limit = 10000; - - return cfg_default; -} - -// This function is called from PRACH worker (can wait) -void rrc_nr::add_user(uint16_t rnti) -{ - if (users.count(rnti) == 0) { - users.insert(std::make_pair(rnti, std::unique_ptr(new ue(this, rnti)))); - rlc->add_user(rnti); - pdcp->add_user(rnti); - logger.info("Added new user rnti=0x%x", rnti); - } else { - logger.error("Adding user rnti=0x%x (already exists)", rnti); - } -} - -void rrc_nr::config_mac() -{ - // Fill MAC scheduler configuration for SIBs - srsenb::sched_interface::cell_cfg_t sched_cfg; - set_sched_cell_cfg_sib1(&sched_cfg, cfg.sib1); - - // set SIB length - for (uint32_t i = 0; i < nof_si_messages + 1; i++) { - sched_cfg.sibs[i].len = sib_buffer[i]->N_bytes; - } - - // PUCCH width - sched_cfg.nrb_pucch = SRSRAN_MAX(cfg.sr_cfg.nof_prb, cfg.cqi_cfg.nof_prb); - logger.info("Allocating %d PRBs for PUCCH", sched_cfg.nrb_pucch); - - // Copy Cell configuration - sched_cfg.cell = cfg.cell; - - // Configure MAC scheduler - mac->cell_cfg(&sched_cfg); -} - -int32_t rrc_nr::generate_sibs() -{ - // MIB packing - bcch_bch_msg_s mib_msg; - mib_s& mib = mib_msg.msg.set_mib(); - mib = cfg.mib; - { - srsran::unique_byte_buffer_t mib_buf = srsran::make_byte_buffer(); - if (mib_buf == nullptr) { - logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); - return SRSRAN_ERROR; - } - asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom()); - mib_msg.pack(bref); - mib_buf->N_bytes = bref.distance_bytes(); - logger.debug(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)", mib_buf->N_bytes); - mib_buffer = std::move(mib_buf); - } - - si_sched_info_s::sched_info_list_l_& sched_info = cfg.sib1.si_sched_info.sched_info_list; - uint32_t nof_messages = cfg.sib1.si_sched_info_present ? cfg.sib1.si_sched_info.sched_info_list.size() : 0; - - // msg is array of SI messages, each SI message msg[i] may contain multiple SIBs - // all SIBs in a SI message msg[i] share the same periodicity - sib_buffer.reserve(nof_messages + 1); - asn1::dyn_array msg(nof_messages + 1); - - // Copy SIB1 to first SI message - msg[0].msg.set_c1().set_sib_type1() = cfg.sib1; - - // Copy rest of SIBs - for (uint32_t sched_info_elem = 0; sched_info_elem < nof_messages; sched_info_elem++) { - uint32_t msg_index = sched_info_elem + 1; // first msg is SIB1, therefore start with second - - msg[msg_index].msg.set_c1().set_sys_info().crit_exts.set_sys_info(); - auto& sib_list = msg[msg_index].msg.c1().sys_info().crit_exts.sys_info().sib_type_and_info; - - for (uint32_t mapping = 0; mapping < sched_info[sched_info_elem].sib_map_info.size(); ++mapping) { - uint32_t sibidx = sched_info[sched_info_elem].sib_map_info[mapping].type; // SIB2 == 0 - sib_list.push_back(cfg.sibs[sibidx]); - } - } - - // Pack payload for all messages - for (uint32_t msg_index = 0; msg_index < nof_messages + 1; msg_index++) { - srsran::unique_byte_buffer_t sib = srsran::make_byte_buffer(); - if (sib == nullptr) { - logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); - return SRSRAN_ERROR; - } - asn1::bit_ref bref(sib->msg, sib->get_tailroom()); - msg[msg_index].pack(bref); - sib->N_bytes = bref.distance_bytes(); - sib_buffer.push_back(std::move(sib)); - - // Log SIBs in JSON format - log_rrc_message("SIB payload", Tx, sib_buffer.back().get(), msg[msg_index]); - } - - nof_si_messages = sib_buffer.size() - 1; - - return SRSRAN_SUCCESS; -} - -/******************************************************************************* - MAC interface -*******************************************************************************/ - -int rrc_nr::read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) -{ - if (mib_buffer == nullptr || buffer->get_tailroom() < mib_buffer->N_bytes) { - return SRSRAN_ERROR; - } - memcpy(buffer->msg, mib_buffer->msg, mib_buffer->N_bytes); - buffer->N_bytes = mib_buffer->N_bytes; - return SRSRAN_SUCCESS; -} - -int rrc_nr::read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) -{ - if (sib_index >= sib_buffer.size()) { - logger.error("SIB %d is not a configured SIB.", sib_index); - return SRSRAN_ERROR; - } - - if (buffer->get_tailroom() < sib_buffer[sib_index]->N_bytes) { - logger.error("Not enough space to fit SIB %d into buffer (%d < %d)", - sib_index, - buffer->get_tailroom(), - sib_buffer[sib_index]->N_bytes); - return SRSRAN_ERROR; - } - - memcpy(buffer->msg, sib_buffer[sib_index]->msg, sib_buffer[sib_index]->N_bytes); - buffer->N_bytes = sib_buffer[sib_index]->N_bytes; - - return SRSRAN_SUCCESS; -} - -void rrc_nr::get_metrics(srsenb::rrc_metrics_t& m) -{ - // return metrics -} - -void rrc_nr::handle_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) -{ - if (pdu) { - logger.info(pdu->msg, pdu->N_bytes, "Rx %s PDU", get_rb_name(lcid)); - } - - if (users.count(rnti) == 1) { - switch (static_cast(lcid)) { - case srsran::nr_srb::srb0: - // parse_ul_ccch(rnti, std::move(pdu)); - break; - case srsran::nr_srb::srb1: - case srsran::nr_srb::srb2: - // parse_ul_dcch(p.rnti, p.lcid, std::move(p.pdu)); - break; - default: - logger.error("Rx PDU with invalid bearer id: %d", lcid); - break; - } - } else { - logger.warning("Discarding PDU for removed rnti=0x%x", rnti); - } -} - -/******************************************************************************* - PDCP interface -*******************************************************************************/ -void rrc_nr::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) -{ - handle_pdu(rnti, lcid, std::move(pdu)); -} - -/******************************************************************************* - UE class - - Every function in UE class is called from a mutex environment thus does not - need extra protection. -*******************************************************************************/ -rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_) : parent(parent_), rnti(rnti_) -{ - // setup periodic RRCSetup send - rrc_setup_periodic_timer = parent->timers->get_unique_timer(); - rrc_setup_periodic_timer.set(5000, [this](uint32_t tid) { - send_connection_setup(); - rrc_setup_periodic_timer.run(); - }); - rrc_setup_periodic_timer.run(); -} - -void rrc_nr::ue::send_connection_setup() -{ - dl_ccch_msg_s dl_ccch_msg; - dl_ccch_msg.msg.set_c1().set_rrc_setup().rrc_transaction_id = ((transaction_id++) % 4u); - rrc_setup_ies_s& setup = dl_ccch_msg.msg.c1().rrc_setup().crit_exts.set_rrc_setup(); - radio_bearer_cfg_s& rr_cfg = setup.radio_bearer_cfg; - - // Add DRB1 to cfg - rr_cfg.drb_to_add_mod_list_present = true; - rr_cfg.drb_to_add_mod_list.resize(1); - auto& drb_item = rr_cfg.drb_to_add_mod_list[0]; - drb_item.drb_id = 1; - drb_item.pdcp_cfg_present = true; - drb_item.pdcp_cfg.ciphering_disabled_present = true; - // drb_item.cn_assoc_present = true; - // drb_item.cn_assoc.set_eps_bearer_id() = ; - drb_item.recover_pdcp_present = false; - - // TODO: send config to RLC/PDCP - - send_dl_ccch(&dl_ccch_msg); -} - -void rrc_nr::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg) -{ - // Allocate a new PDU buffer, pack the message and send to PDCP - srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); - if (pdu == nullptr) { - parent->logger.error("Allocating pdu"); - } - asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); - if (dl_ccch_msg->pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { - parent->logger.error("Failed to pack DL-CCCH message. Discarding msg."); - } - pdu->N_bytes = bref.distance_bytes(); - - char buf[32] = {}; - sprintf(buf, "SRB0 - rnti=0x%x", rnti); - parent->log_rrc_message(buf, Tx, pdu.get(), *dl_ccch_msg); - parent->rlc->write_sdu(rnti, (uint32_t)srsran::nr_srb::srb0, std::move(pdu)); -} - -} // namespace srsenb diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index 4b25573a6..3c9a86984 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,15 +22,16 @@ #include "srsenb/hdr/stack/rrc/rrc_ue.h" #include "srsenb/hdr/common/common_enb.h" #include "srsenb/hdr/stack/rrc/mac_controller.h" +#include "srsenb/hdr/stack/rrc/rrc_endc.h" #include "srsenb/hdr/stack/rrc/rrc_mobility.h" #include "srsenb/hdr/stack/rrc/ue_rr_cfg.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/enb_events.h" -#include "srsran/common/int_helpers.h" #include "srsran/common/standard_streams.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" #include "srsran/interfaces/enb_s1ap_interfaces.h" +#include "srsran/support/srsran_assert.h" using namespace asn1::rrc; @@ -55,22 +56,49 @@ rrc::ue::ue(rrc* outer_rrc, uint16_t rnti_, const sched_interface::ue_cfg_t& sch rrc::ue::~ue() {} +bool rrc::ue::init_pucch() +{ + // Allocate PUCCH resources for PCell + return ue_cell_list.init_pucch_pcell(); +} + int rrc::ue::init() { - // Allocate cell and PUCCH resources - if (ue_cell_list.add_cell(mac_ctrl.get_ue_sched_cfg().supported_cc_list[0].enb_cc_idx) == nullptr) { + // Allocate cell (PUCCH resources are not allocated here) + if (ue_cell_list.add_cell(mac_ctrl.get_ue_sched_cfg().supported_cc_list[0].enb_cc_idx, false) == nullptr) { return SRSRAN_ERROR; } // Configure apply_setup_phy_common(parent->cfg.sibs[1].sib2().rr_cfg_common, true); - rlf_timer = parent->task_sched.get_unique_timer(); - activity_timer = parent->task_sched.get_unique_timer(); + phy_dl_rlf_timer = parent->task_sched.get_unique_timer(); + phy_ul_rlf_timer = parent->task_sched.get_unique_timer(); + rlc_rlf_timer = parent->task_sched.get_unique_timer(); + activity_timer = parent->task_sched.get_unique_timer(); set_activity_timeout(MSG3_RX_TIMEOUT); // next UE response is Msg3 - set_rlf_timeout(); + + // Set timeout to release UE context after RLF detection + uint32_t deadline_ms = parent->cfg.rlf_release_timer_ms; + if (rnti != SRSRAN_MRNTI) { + auto timer_expire_func = [this](uint32_t tid) { rlf_timer_expired(tid); }; + phy_dl_rlf_timer.set(deadline_ms, timer_expire_func); + phy_ul_rlf_timer.set(deadline_ms, timer_expire_func); + rlc_rlf_timer.set(deadline_ms, timer_expire_func); + parent->logger.info("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline_ms); + } else { + // in case of M-RNTI do not handle rlf timer expiration + auto timer_expire_func = [](uint32_t tid) {}; + phy_dl_rlf_timer.set(deadline_ms, timer_expire_func); + phy_ul_rlf_timer.set(deadline_ms, timer_expire_func); + rlc_rlf_timer.set(deadline_ms, timer_expire_func); + } mobility_handler = make_rnti_obj(rnti, this); + if (parent->rrc_nr != nullptr) { + endc_handler = make_rnti_obj(rnti, this, parent->cfg.endc_cfg); + } + return SRSRAN_SUCCESS; } @@ -93,13 +121,22 @@ void rrc::ue::get_metrics(rrc_ue_metrics_t& ue_metrics) const } } -void rrc::ue::set_activity() +void rrc::ue::set_activity(bool enabled) { + if (rnti == SRSRAN_MRNTI) { + return; + } + if (not enabled) { + if (activity_timer.is_running()) { + parent->logger.debug("Inactivity timer interrupted for rnti=0x%x", rnti); + } + activity_timer.stop(); + return; + } + // re-start activity timer with current timeout value activity_timer.run(); - if (parent) { - parent->logger.debug("Activity registered for rnti=0x%x (timeout_value=%dms)", rnti, activity_timer.duration()); - } + parent->logger.debug("Activity registered for rnti=0x%x (timeout_value=%dms)", rnti, activity_timer.duration()); } void rrc::ue::set_radiolink_dl_state(bool crc_res) @@ -107,25 +144,30 @@ void rrc::ue::set_radiolink_dl_state(bool crc_res) parent->logger.debug( "Radio-Link downlink state for rnti=0x%x: crc_res=%d, consecutive_ko=%d", rnti, crc_res, consecutive_kos_dl); - // If received OK, restart counter and stop RLF timer + // If received OK, restart DL counter and stop RLF timer if (crc_res) { consecutive_kos_dl = 0; - consecutive_kos_ul = 0; - rlf_timer.stop(); + if (phy_dl_rlf_timer.is_running()) { + parent->logger.info( + "DL RLF timer stopped for rnti=0x%x (time elapsed=%dms)", rnti, phy_dl_rlf_timer.time_elapsed()); + phy_dl_rlf_timer.stop(); + mac_ctrl.set_radio_bearer_state(mac_lc_ch_cfg_t::BOTH); + } return; } // Count KOs in MAC and trigger release if it goes above a certain value. // This is done to detect out-of-coverage UEs - if (rlf_timer.is_running()) { + if (phy_dl_rlf_timer.is_running()) { // RLF timer already running, no need to count KOs return; } consecutive_kos_dl++; if (consecutive_kos_dl > parent->cfg.max_mac_dl_kos) { - parent->logger.info("Max KOs in DL reached, triggering release rnti=0x%x", rnti); - max_retx_reached(); + parent->logger.info("Max KOs in DL reached, starting RLF timer rnti=0x%x", rnti); + mac_ctrl.set_radio_bearer_state(mac_lc_ch_cfg_t::IDLE); + phy_dl_rlf_timer.run(); } } @@ -134,128 +176,144 @@ void rrc::ue::set_radiolink_ul_state(bool crc_res) parent->logger.debug( "Radio-Link uplink state for rnti=0x%x: crc_res=%d, consecutive_ko=%d", rnti, crc_res, consecutive_kos_ul); - // If received OK, restart counter and stop RLF timer + // If received OK, restart UL counter and stop RLF timer if (crc_res) { - consecutive_kos_dl = 0; consecutive_kos_ul = 0; - rlf_timer.stop(); + if (phy_ul_rlf_timer.is_running()) { + parent->logger.info( + "UL RLF timer stopped for rnti=0x%x (time elapsed=%dms)", rnti, phy_ul_rlf_timer.time_elapsed()); + phy_ul_rlf_timer.stop(); + mac_ctrl.set_radio_bearer_state(mac_lc_ch_cfg_t::BOTH); + } + return; + } + + if (mobility_handler->is_ho_running()) { + // Do not count UL KOs if handover is on-going. + // Source eNB should only rely in relocation timer + // Target eNB should either wait for UE to handover or explicit release by the MME return; } // Count KOs in MAC and trigger release if it goes above a certain value. // This is done to detect out-of-coverage UEs - if (rlf_timer.is_running()) { + if (phy_ul_rlf_timer.is_running()) { // RLF timer already running, no need to count KOs return; } consecutive_kos_ul++; if (consecutive_kos_ul > parent->cfg.max_mac_ul_kos) { - parent->logger.info("Max KOs in UL reached, triggering release rnti=0x%x", rnti); - max_retx_reached(); + parent->logger.info("Max KOs in UL reached, starting RLF timer rnti=0x%x", rnti); + mac_ctrl.set_radio_bearer_state(mac_lc_ch_cfg_t::IDLE); + phy_ul_rlf_timer.run(); } } void rrc::ue::activity_timer_expired(const activity_timeout_type_t type) { - rlf_timer.stop(); - if (parent) { - parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); + parent->logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); - if (parent->s1ap->user_exists(rnti)) { - if (type == UE_INACTIVITY_TIMEOUT) { + if (parent->s1ap->user_exists(rnti)) { + switch (type) { + case UE_INACTIVITY_TIMEOUT: parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::user_inactivity); con_release_result = procedure_result_code::activity_timeout; - } else if (type == MSG3_RX_TIMEOUT) { + break; + case MSG3_RX_TIMEOUT: + case MSG5_RX_TIMEOUT_T300: + case MSG5_RX_TIMEOUT_T301: // MSG3 timeout, no need to notify S1AP, just remove UE parent->rem_user_thread(rnti); con_release_result = procedure_result_code::msg3_timeout; - } else { + break; + default: // Unhandled activity timeout, just remove UE and log an error parent->rem_user_thread(rnti); con_release_result = procedure_result_code::activity_timeout; parent->logger.error( "Unhandled reason for activity timer expiration. rnti=0x%x, cause %d", rnti, static_cast(type)); - } - } else { - if (rnti != SRSRAN_MRNTI) { - parent->rem_user_thread(rnti); - } } + } else { + parent->rem_user_thread(rnti); } state = RRC_STATE_RELEASE_REQUEST; } -void rrc::ue::rlf_timer_expired() +void rrc::ue::rlf_timer_expired(uint32_t timeout_id) { activity_timer.stop(); - if (parent) { - parent->logger.info("RLF timer for rnti=0x%x expired after %d ms", rnti, rlf_timer.time_elapsed()); - if (parent->s1ap->user_exists(rnti)) { - parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost); - con_release_result = procedure_result_code::radio_conn_with_ue_lost; - } else { - if (rnti != SRSRAN_MRNTI) { - parent->rem_user(rnti); - } - } + std::string event_type = "Unknown"; + if (timeout_id == phy_dl_rlf_timer.id()) { + event_type = "dl_rlf"; + parent->logger.info("DL RLF timer for rnti=0x%x expired after %d ms", rnti, phy_dl_rlf_timer.time_elapsed()); + } else if (timeout_id == phy_ul_rlf_timer.id()) { + event_type = "ul_rlf"; + parent->logger.info("UL RLF timer for rnti=0x%x expired after %d ms", rnti, phy_ul_rlf_timer.time_elapsed()); + } else if (timeout_id == rlc_rlf_timer.id()) { + event_type = "rlc_rlf"; + parent->logger.info("RLC RLF timer for rnti=0x%x expired after %d ms", rnti, rlc_rlf_timer.time_elapsed()); } + phy_ul_rlf_timer.stop(); + phy_dl_rlf_timer.stop(); + rlc_rlf_timer.stop(); state = RRC_STATE_RELEASE_REQUEST; + + parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost); + con_release_result = procedure_result_code::radio_conn_with_ue_lost; + + // Log event. + event_logger::get().log_rlf_detected( + ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, event_type, rnti); } -void rrc::ue::max_retx_reached() +void rrc::ue::max_rlc_retx_reached() { - if (parent) { - parent->logger.info("Max retx reached for rnti=0x%x", rnti); + parent->logger.info("Max RLC retx reached for rnti=0x%x", rnti); - // Give UE time to start re-establishment - rlf_timer.run(); - - mac_ctrl.handle_max_retx(); - } + // Turn off scheduling but give UE chance to start re-establishment + mac_ctrl.set_radio_bearer_state(mac_lc_ch_cfg_t::IDLE); + rlc_rlf_timer.run(); } -void rrc::ue::set_rlf_timeout() +void rrc::ue::protocol_failure() { - uint32_t deadline_s = 0; - uint32_t deadline_ms = 0; + parent->logger.info("RLC protocol failure for rnti=0x%x", rnti); - deadline_ms = static_cast((get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t310.to_number()) + - (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t311.to_number()) + - (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.n310.to_number())); - deadline_s = deadline_ms / 1000; - deadline_ms = deadline_ms % 1000; + // Release UE immediately with appropiate cause + state = RRC_STATE_RELEASE_REQUEST; - uint32_t deadline = deadline_s * 1e3 + deadline_ms; - rlf_timer.set(deadline, [this](uint32_t tid) { rlf_timer_expired(); }); - parent->logger.info("Setting RLF timer for rnti=0x%x to %dms", rnti, deadline); + parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::fail_in_radio_interface_proc); + con_release_result = procedure_result_code::fail_in_radio_interface_proc; } -void rrc::ue::set_activity_timeout(const activity_timeout_type_t type) +void rrc::ue::set_activity_timeout(activity_timeout_type_t type) { - uint32_t deadline_s = 0; uint32_t deadline_ms = 0; switch (type) { case MSG3_RX_TIMEOUT: - deadline_s = 0; deadline_ms = static_cast( (get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.rr_cfg_common.rach_cfg_common.max_harq_msg3_tx + 1) * 16); break; case UE_INACTIVITY_TIMEOUT: - deadline_s = parent->cfg.inactivity_timeout_ms / 1000; - deadline_ms = parent->cfg.inactivity_timeout_ms % 1000; + deadline_ms = parent->cfg.inactivity_timeout_ms; + break; + case MSG5_RX_TIMEOUT_T300: + deadline_ms = get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t300.to_number(); + break; + case MSG5_RX_TIMEOUT_T301: + deadline_ms = get_ue_cc_cfg(UE_PCELL_CC_IDX)->sib2.ue_timers_and_consts.t301.to_number(); break; default: parent->logger.error("Unknown timeout type %d", type); } - uint32_t deadline = deadline_s * 1e3 + deadline_ms; - activity_timer.set(deadline, [this, type](uint32_t tid) { activity_timer_expired(type); }); - parent->logger.debug("Setting timer for %s for rnti=0x%x to %dms", to_string(type).c_str(), rnti, deadline); + activity_timer.set(deadline_ms, [this, type](uint32_t tid) { activity_timer_expired(type); }); + parent->logger.debug("Setting timer for %s for rnti=0x%x to %dms", to_string(type).c_str(), rnti, deadline_ms); set_activity(); } @@ -276,11 +334,12 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); if (ul_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or ul_dcch_msg.msg.type().value != ul_dcch_msg_type_c::types_opts::c1) { - parent->logger.error("Failed to unpack UL-DCCH message"); + parent->log_rx_pdu_fail(rnti, lcid, *pdu, "Failed to unpack UL-DCCH message"); return; } - parent->log_rrc_message(get_rb_name(lcid), Rx, pdu.get(), ul_dcch_msg, ul_dcch_msg.msg.c1().type().to_string()); + // Log Rx message + parent->log_rrc_message(Rx, rnti, lcid, *pdu, ul_dcch_msg, ul_dcch_msg.msg.c1().type().to_string()); srsran::unique_byte_buffer_t original_pdu = std::move(pdu); pdu = srsran::make_byte_buffer(); @@ -295,11 +354,13 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_setup_complete: save_ul_message(std::move(original_pdu)); handle_rrc_con_setup_complete(&ul_dcch_msg.msg.c1().rrc_conn_setup_complete(), std::move(pdu)); + set_activity_timeout(UE_INACTIVITY_TIMEOUT); set_activity(); break; case ul_dcch_msg_type_c::c1_c_::types::rrc_conn_reest_complete: save_ul_message(std::move(original_pdu)); handle_rrc_con_reest_complete(&ul_dcch_msg.msg.c1().rrc_conn_reest_complete(), std::move(pdu)); + set_activity_timeout(UE_INACTIVITY_TIMEOUT); set_activity(); break; case ul_dcch_msg_type_c::c1_c_::types::ul_info_transfer: @@ -328,27 +389,38 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) break; case ul_dcch_msg_type_c::c1_c_::types::security_mode_complete: handle_security_mode_complete(&ul_dcch_msg.msg.c1().security_mode_complete()); - send_ue_cap_enquiry(); + send_ue_cap_enquiry({asn1::rrc::rat_type_opts::options::eutra}); state = RRC_STATE_WAIT_FOR_UE_CAP_INFO; break; case ul_dcch_msg_type_c::c1_c_::types::security_mode_fail: handle_security_mode_failure(&ul_dcch_msg.msg.c1().security_mode_fail()); break; case ul_dcch_msg_type_c::c1_c_::types::ue_cap_info: - if (handle_ue_cap_info(&ul_dcch_msg.msg.c1().ue_cap_info())) { - parent->s1ap->ue_ctxt_setup_complete(rnti); - send_connection_reconf(std::move(pdu)); - state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; + if (handle_ue_cap_info(&ul_dcch_msg.msg.c1().ue_cap_info()) == SRSRAN_SUCCESS) { + if (endc_handler != nullptr && endc_handler->is_endc_supported() && state == RRC_STATE_WAIT_FOR_UE_CAP_INFO) { + // request EUTRA-NR and NR capabilities as well + send_ue_cap_enquiry({asn1::rrc::rat_type_opts::options::eutra_nr, asn1::rrc::rat_type_opts::options::nr}); + state = RRC_STATE_WAIT_FOR_UE_CAP_INFO_ENDC; // avoid endless loop + } else { + // send RRC reconfiguration to complete procedure + send_connection_reconf(std::move(pdu)); + } } else { send_connection_reject(procedure_result_code::none); state = RRC_STATE_IDLE; } break; case ul_dcch_msg_type_c::c1_c_::types::meas_report: - if (mobility_handler != nullptr) { - mobility_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report(), std::move(original_pdu)); + if (state == RRC_STATE_REGISTERED) { + if (mobility_handler != nullptr) { + mobility_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report(), std::move(original_pdu)); + } + if (endc_handler != nullptr) { + endc_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report()); + } } else { - parent->logger.warning("Received MeasReport but no mobility configuration is available"); + parent->logger.warning( + "measurementReport for rnti=0x%x ignored. Cause: RRC Reconfiguration is not yet complete", rnti); } break; case ul_dcch_msg_type_c::c1_c_::types::ue_info_resp_r9: @@ -362,7 +434,7 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) std::string rrc::ue::to_string(const activity_timeout_type_t& type) { - constexpr static const char* options[] = {"Msg3 reception", "UE inactivity", "UE reestablishment"}; + constexpr static const char* options[] = {"Msg3 reception", "UE inactivity", "UE establishment", "UE reestablishment"}; return srsran::enum_to_text(options, (uint32_t)activity_timeout_type_t::nulltype, (uint32_t)type); } @@ -372,8 +444,11 @@ std::string rrc::ue::to_string(const activity_timeout_type_t& type) void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg) { // Log event. + asn1::json_writer json_writer; + msg->to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + json_writer.to_string(), static_cast(rrc_event_type::con_request), static_cast(procedure_result_code::none), rnti); @@ -384,6 +459,13 @@ void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg) return; } + // Allocate PUCCH resources and reject if not available + if (not init_pucch()) { + parent->logger.warning("Could not allocate PUCCH resources for rnti=0x%x. Sending Connection Reject", rnti); + send_connection_reject(procedure_result_code::fail_in_radio_interface_proc); + return; + } + rrc_conn_request_r8_ies_s* msg_r8 = &msg->crit_exts.rrc_conn_request_r8(); if (msg_r8->ue_id.type() == init_ue_id_c::types::s_tmsi) { @@ -395,10 +477,8 @@ void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg) for (auto& user : parent->users) { if (user.first != rnti && user.second->has_tmsi && user.second->mmec == mmec && user.second->m_tmsi == m_tmsi) { parent->logger.info("RRC connection request: UE context already exists. M-TMSI=%d", m_tmsi); - if (parent->s1ap->user_release(rnti, asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost)) { - // Do not wait for MME response - parent->rem_user_thread(user.first); - } + user.second->state = RRC_STATE_IDLE; // Set old rnti to IDLE so that enb doesn't send RRC Connection Release + parent->s1ap->user_release(user.first, asn1::s1ap::cause_radio_network_opts::interaction_with_other_proc); break; } } @@ -408,7 +488,7 @@ void rrc::ue::handle_rrc_con_req(rrc_conn_request_s* msg) send_connection_setup(); state = RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE; - set_activity_timeout(UE_INACTIVITY_TIMEOUT); + set_activity_timeout(MSG5_RX_TIMEOUT_T300); } void rrc::ue::send_connection_setup() @@ -422,7 +502,10 @@ void rrc::ue::send_connection_setup() rr_cfg_ded_s& rr_cfg = setup_r8.rr_cfg_ded; // Fill RR config dedicated - fill_rr_cfg_ded_setup(rr_cfg, parent->cfg, ue_cell_list); + if (fill_rr_cfg_ded_setup(rr_cfg, parent->cfg, ue_cell_list)) { + parent->logger.error("Generating ConnectionSetup. Aborting"); + return; + } // Apply ConnectionSetup Configuration to MAC scheduler mac_ctrl.handle_con_setup(setup_r8); @@ -439,8 +522,11 @@ void rrc::ue::send_connection_setup() send_dl_ccch(&dl_ccch_msg, &octet_str); // Log event. + asn1::json_writer json_writer; + dl_ccch_msg.to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, octet_str, + json_writer.to_string(), static_cast(rrc_event_type::con_setup), static_cast(procedure_result_code::none), rnti); @@ -451,8 +537,11 @@ void rrc::ue::send_connection_setup() void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsran::unique_byte_buffer_t pdu) { // Log event. + asn1::json_writer json_writer; + msg->to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + json_writer.to_string(), static_cast(rrc_event_type::con_setup_complete), static_cast(procedure_result_code::none), rnti); @@ -481,7 +570,6 @@ void rrc::ue::handle_rrc_con_setup_complete(rrc_conn_setup_complete_s* msg, srsr } else { parent->s1ap->initial_ue(rnti, enb_cc_idx, s1ap_cause, std::move(pdu)); } - state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; // 2> if the UE has radio link failure or handover failure information available if (msg->crit_exts.type().value == c1_or_crit_ext_opts::c1 and @@ -505,8 +593,11 @@ void rrc::ue::send_connection_reject(procedure_result_code cause) send_dl_ccch(&dl_ccch_msg, &octet_str); // Log event. + asn1::json_writer json_writer; + dl_ccch_msg.to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, octet_str, + json_writer.to_string(), static_cast(rrc_event_type::con_reject), static_cast(cause), rnti); @@ -518,91 +609,142 @@ void rrc::ue::send_connection_reject(procedure_result_code cause) void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) { // Log event. + asn1::json_writer json_writer; + msg->to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + json_writer.to_string(), static_cast(rrc_event_type::con_reest_req), static_cast(procedure_result_code::none), rnti); const rrc_conn_reest_request_r8_ies_s& req_r8 = msg->crit_exts.rrc_conn_reest_request_r8(); uint16_t old_rnti = req_r8.ue_id.c_rnti.to_number(); - srsran::console( - "User 0x%x requesting RRC Reestablishment as 0x%x. Cause: %s\n", rnti, old_rnti, req_r8.reest_cause.to_string()); - if (not parent->s1ap->is_mme_connected()) { - parent->logger.error("MME isn't connected. Sending Connection Reject"); + parent->logger.error("RRCReestablishmentReject for rnti=0x%x. Cause: MME not connected", rnti); send_connection_reest_rej(procedure_result_code::error_mme_not_connected); - srsran::console("User 0x%x RRC Reestablishment Request rejected\n", rnti); + srsran::console("RRCReestablishmentReject for rnti=0x%x. Cause: MME not connected.\n", rnti); return; } + + // Allocate PUCCH resources and reject if not available + if (not init_pucch()) { + parent->logger.warning("Could not allocate PUCCH resources for rnti=0x%x. Sending RRCReestablishmentReject", rnti); + send_connection_reest_rej(procedure_result_code::fail_in_radio_interface_proc); + return; + } + parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s", (uint32_t)msg->crit_exts.rrc_conn_reest_request_r8().ue_id.c_rnti.to_number(), msg->crit_exts.rrc_conn_reest_request_r8().ue_id.pci, (uint32_t)msg->crit_exts.rrc_conn_reest_request_r8().ue_id.short_mac_i.to_number(), msg->crit_exts.rrc_conn_reest_request_r8().reest_cause.to_string()); - if (is_idle()) { - uint16_t old_pci = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.pci; - const enb_cell_common* old_cell = parent->cell_common_list->get_pci(old_pci); - auto ue_it = parent->users.find(old_rnti); - // Reject unrecognized rntis, and PCIs that do not belong to eNB - if (ue_it != parent->users.end() and old_cell != nullptr and - ue_it->second->ue_cell_list.get_enb_cc_idx(old_cell->enb_cc_idx) != nullptr) { - parent->logger.info("ConnectionReestablishmentRequest for rnti=0x%x. Sending Connection Reestablishment", - old_rnti); - // Cancel Handover in Target eNB if on-going - asn1::s1ap::cause_c cause; - cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::interaction_with_other_proc; - parent->users.at(old_rnti)->mobility_handler->trigger(rrc_mobility::ho_cancel_ev{cause}); - - // Recover security setup - const enb_cell_common* pcell_cfg = get_ue_cc_cfg(UE_PCELL_CC_IDX); - ue_security_cfg = parent->users.at(old_rnti)->ue_security_cfg; - ue_security_cfg.regenerate_keys_handover(pcell_cfg->cell_cfg.pci, pcell_cfg->cell_cfg.dl_earfcn); - - // send reestablishment and restore bearer configuration - send_connection_reest(parent->users.at(old_rnti)->ue_security_cfg.get_ncc()); - - // Get PDCP entity state (required when using RLC AM) - for (const auto& erab_pair : parent->users.at(old_rnti)->bearer_list.get_erabs()) { - uint16_t lcid = erab_pair.second.id - 2; - old_reest_pdcp_state[lcid] = {}; - parent->pdcp->get_bearer_state(old_rnti, lcid, &old_reest_pdcp_state[lcid]); - - parent->logger.debug("Getting PDCP state for E-RAB with LCID %d", lcid); - parent->logger.debug("Got PDCP state: TX HFN %d, NEXT_PDCP_TX_SN %d, RX_HFN %d, NEXT_PDCP_RX_SN %d, " - "LAST_SUBMITTED_PDCP_RX_SN %d", - old_reest_pdcp_state[lcid].tx_hfn, - old_reest_pdcp_state[lcid].next_pdcp_tx_sn, - old_reest_pdcp_state[lcid].rx_hfn, - old_reest_pdcp_state[lcid].next_pdcp_rx_sn, - old_reest_pdcp_state[lcid].last_submitted_pdcp_rx_sn); - } - - // Make sure UE capabilities are copied over to new RNTI - eutra_capabilities = parent->users.at(old_rnti)->eutra_capabilities; - eutra_capabilities_unpacked = parent->users.at(old_rnti)->eutra_capabilities_unpacked; - ue_capabilities = parent->users.at(old_rnti)->ue_capabilities; - if (parent->logger.debug.enabled()) { - asn1::json_writer js{}; - eutra_capabilities.to_json(js); - parent->logger.debug("rnti=0x%x EUTRA capabilities: %s", rnti, js.to_string().c_str()); - } - - old_reest_rnti = old_rnti; - state = RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE; - set_activity_timeout(UE_INACTIVITY_TIMEOUT); - } else { - parent->logger.error("Received ConnectionReestablishment for rnti=0x%x without context", old_rnti); - send_connection_reest_rej(procedure_result_code::error_unknown_rnti); - srsran::console( - "User 0x%x RRC Reestablishment Request rejected. Cause: no rnti=0x%x context available\n", rnti, old_rnti); - } - } else { - parent->logger.error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE", rnti); + if (not is_idle()) { + // The created RNTI has to receive ReestablishmentRequest as first message + parent->logger.error( + "RRCReestablishmentReject for rnti=0x%x. Cause: old rnti=0x%x is not in RRC_IDLE", rnti, old_rnti); send_connection_reest_rej(procedure_result_code::error_unknown_rnti); - srsran::console("ERROR: User 0x%x requesting Reestablishment is not in RRC_IDLE\n", rnti); + srsran::console("ERROR: RRCReestablishmentReject for rnti=0x%x not in RRC_IDLE\n", rnti); + return; } + + uint16_t old_pci = msg->crit_exts.rrc_conn_reest_request_r8().ue_id.pci; + const enb_cell_common* old_cell = parent->cell_common_list->get_pci(old_pci); + auto old_ue_it = parent->users.find(old_rnti); + + // Reject unrecognized rntis, and PCIs that do not belong to eNB + if (old_ue_it == parent->users.end() or old_cell == nullptr or + old_ue_it->second->ue_cell_list.get_enb_cc_idx(old_cell->enb_cc_idx) == nullptr) { + send_connection_reest_rej(procedure_result_code::error_unknown_rnti); + parent->logger.info( + "RRCReestablishmentReject for rnti=0x%x. Cause: no rnti=0x%x context available", rnti, old_rnti); + srsran::console("RRCReestablishmentReject for rnti=0x%x. Cause: no context available\n", rnti); + return; + } + ue* old_ue = old_ue_it->second.get(); + bool old_ue_supported_endc = old_ue->endc_handler and old_ue->endc_handler->is_endc_supported(); + if (not old_ue_supported_endc and req_r8.reest_cause.value == reest_cause_opts::recfg_fail) { + // Reestablishment Reject for ReconfigFailures of LTE-only mode + parent->logger.info( + "RRCReestablishmentReject for rnti=0x%x. Cause: Unhandled Reestablishment due to ReconfigFailure", rnti); + srsran::console("RRCReestablishmentReject for rnti=0x%x. Cause: Unhandled Reestablishment due to ReconfigFailure\n", + rnti); + return; + } + + // Reestablishment procedure going forward + parent->logger.info("ConnectionReestablishmentRequest for rnti=0x%x. Sending Connection Reestablishment", old_rnti); + srsran::console( + "User 0x%x requesting RRC Reestablishment as 0x%x. Cause: %s\n", rnti, old_rnti, req_r8.reest_cause.to_string()); + + if (endc_handler != nullptr) { + old_ue->endc_handler->trigger(rrc_endc::rrc_reest_rx_ev{}); + } + + // Cancel Handover in Target eNB if on-going + asn1::s1ap::cause_c cause; + cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::interaction_with_other_proc; + old_ue->mobility_handler->trigger(rrc_mobility::ho_cancel_ev{cause}); + + // Recover security setup + const enb_cell_common* pcell_cfg = get_ue_cc_cfg(UE_PCELL_CC_IDX); + ue_security_cfg = old_ue->ue_security_cfg; + ue_security_cfg.regenerate_keys_handover(pcell_cfg->cell_cfg.pci, pcell_cfg->cell_cfg.dl_earfcn); + + // send RRC Reestablishment message and restore bearer configuration + send_connection_reest(old_ue->ue_security_cfg.get_ncc()); + + // Get PDCP entity state (required when using RLC AM) + for (const auto& erab_pair : old_ue->bearer_list.get_erabs()) { + uint16_t lcid = erab_pair.second.lcid; + old_reest_pdcp_state[lcid] = {}; + parent->pdcp->get_bearer_state(old_rnti, lcid, &old_reest_pdcp_state[lcid]); + + parent->logger.debug("Getting PDCP state for E-RAB with LCID %d", lcid); + parent->logger.debug("Got PDCP state: TX HFN %d, NEXT_PDCP_TX_SN %d, RX_HFN %d, NEXT_PDCP_RX_SN %d, " + "LAST_SUBMITTED_PDCP_RX_SN %d", + old_reest_pdcp_state[lcid].tx_hfn, + old_reest_pdcp_state[lcid].next_pdcp_tx_sn, + old_reest_pdcp_state[lcid].rx_hfn, + old_reest_pdcp_state[lcid].next_pdcp_rx_sn, + old_reest_pdcp_state[lcid].last_submitted_pdcp_rx_sn); + } + + // Make sure UE capabilities are copied over to new RNTI + eutra_capabilities = old_ue->eutra_capabilities; + eutra_capabilities_unpacked = old_ue->eutra_capabilities_unpacked; + ue_capabilities = old_ue->ue_capabilities; + if (parent->logger.debug.enabled()) { + asn1::json_writer js{}; + eutra_capabilities.to_json(js); + parent->logger.debug("rnti=0x%x EUTRA capabilities: %s", rnti, js.to_string().c_str()); + } + if (endc_handler) { + if (req_r8.reest_cause.value == reest_cause_opts::recfg_fail) { + // In case of Reestablishment due to ReconfFailure, avoid re-enabling NR EN-DC, otherwise + // the eNB and UE may enter in a reconfiguration + reestablishment loop. + endc_handler->trigger(rrc_endc::disable_endc_ev{}); + } else { + // In case of Reestablishment with cause other than ReconfFailure, recompute whether + // the new RNTI supports NR EN-DC. + endc_handler->handle_eutra_capabilities(eutra_capabilities); + } + } + + // Recover GTP-U tunnels and S1AP context + parent->gtpu->mod_bearer_rnti(old_rnti, rnti); + parent->s1ap->user_mod(old_rnti, rnti); + + // Reestablish E-RABs of old rnti later, during ConnectionReconfiguration + bearer_list.reestablish_bearers(std::move(old_ue->bearer_list)); + + // remove old RNTI + old_ue->mac_ctrl.set_drb_activation(false); + parent->rem_user_thread(old_rnti); + + state = RRC_STATE_WAIT_FOR_CON_REEST_COMPLETE; + set_activity_timeout(MSG5_RX_TIMEOUT_T301); } void rrc::ue::send_connection_reest(uint8_t ncc) @@ -614,7 +756,10 @@ void rrc::ue::send_connection_reest(uint8_t ncc) rr_cfg_ded_s& rr_cfg = reest_r8.rr_cfg_ded; // Fill RR config dedicated - fill_rr_cfg_ded_setup(rr_cfg, parent->cfg, ue_cell_list); + if (fill_rr_cfg_ded_setup(rr_cfg, parent->cfg, ue_cell_list)) { + parent->logger.error("Generating ConnectionReestablishment. Aborting..."); + return; + } // Set NCC reest_r8.next_hop_chaining_count = ncc; @@ -636,8 +781,11 @@ void rrc::ue::send_connection_reest(uint8_t ncc) apply_rr_cfg_ded_diff(current_ue_cfg.rr_cfg, rr_cfg); // Log event. + asn1::json_writer json_writer; + dl_ccch_msg.to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, octet_str, + json_writer.to_string(), static_cast(rrc_event_type::con_reest), static_cast(procedure_result_code::none), rnti); @@ -646,8 +794,11 @@ void rrc::ue::send_connection_reest(uint8_t ncc) void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsran::unique_byte_buffer_t pdu) { // Log event. + asn1::json_writer json_writer; + msg->to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + json_writer.to_string(), static_cast(rrc_event_type::con_reest_complete), static_cast(procedure_result_code::none), rnti); @@ -660,24 +811,9 @@ void rrc::ue::handle_rrc_con_reest_complete(rrc_conn_reest_complete_s* msg, srsr // TODO: msg->selected_plmn_id - used to select PLMN from SIB1 list // TODO: if(msg->registered_mme_present) - the indicated MME should be used from a pool - // Modify GTP-U tunnel and S1AP context - parent->gtpu->mod_bearer_rnti(old_reest_rnti, rnti); - parent->s1ap->user_mod(old_reest_rnti, rnti); - - // Signal MAC scheduler that configuration was successful + // signal mac scheduler that configuration was successful mac_ctrl.handle_con_reest_complete(); - // Activate security for SRB1 - parent->pdcp->config_security(rnti, srb_to_lcid(lte_srb::srb1), ue_security_cfg.get_as_sec_cfg()); - parent->pdcp->enable_integrity(rnti, srb_to_lcid(lte_srb::srb1)); - parent->pdcp->enable_encryption(rnti, srb_to_lcid(lte_srb::srb1)); - - // Reestablish E-RABs of old rnti during ConnectionReconfiguration - bearer_list.reestablish_bearers(std::move(parent->users.at(old_reest_rnti)->bearer_list)); - - // remove old RNTI - parent->rem_user_thread(old_reest_rnti); - state = RRC_STATE_REESTABLISHMENT_COMPLETE; // 2> if the UE has radio link failure or handover failure information available @@ -702,8 +838,11 @@ void rrc::ue::send_connection_reest_rej(procedure_result_code cause) send_dl_ccch(&dl_ccch_msg, &octet_str); // Log event. + asn1::json_writer json_writer; + dl_ccch_msg.to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, octet_str, + json_writer.to_string(), static_cast(rrc_event_type::con_reest_reject), static_cast(cause), rnti); @@ -727,8 +866,11 @@ void rrc::ue::send_connection_reconf(srsran::unique_byte_buffer_t pdu, rrc_conn_recfg_r8_ies_s& recfg_r8 = rrc_conn_recfg.crit_exts.set_c1().set_rrc_conn_recfg_r8(); // Fill RR Config Ded and SCells - apply_reconf_updates( - recfg_r8, current_ue_cfg, parent->cfg, ue_cell_list, bearer_list, ue_capabilities, phy_cfg_updated); + if (apply_reconf_updates( + recfg_r8, current_ue_cfg, parent->cfg, ue_cell_list, bearer_list, ue_capabilities, phy_cfg_updated)) { + parent->logger.error("Generating ConnectionReconfiguration. Aborting..."); + return; + } // Add measConfig if (mobility_handler != nullptr) { @@ -741,6 +883,21 @@ void rrc::ue::send_connection_reconf(srsran::unique_byte_buffer_t pdu, return; } + // Fill in NAS PDU - Only for RRC Connection Reconfiguration during E-RAB Release Command + if (nas_pdu.size() > 0 and !recfg_r8.ded_info_nas_list_present) { + recfg_r8.ded_info_nas_list_present = true; + recfg_r8.ded_info_nas_list.resize(recfg_r8.rr_cfg_ded.drb_to_release_list.size()); + // Add NAS PDU + for (uint32_t idx = 0; idx < recfg_r8.rr_cfg_ded.drb_to_release_list.size(); idx++) { + recfg_r8.ded_info_nas_list[idx].resize(nas_pdu.size()); + memcpy(recfg_r8.ded_info_nas_list[idx].data(), nas_pdu.data(), nas_pdu.size()); + } + } + + if (endc_handler != nullptr) { + endc_handler->fill_conn_recfg(&recfg_r8); + } + /* Apply updates present in RRCConnectionReconfiguration to lower layers */ // apply PHY config apply_reconf_phy_config(recfg_r8, true); @@ -753,17 +910,6 @@ void rrc::ue::send_connection_reconf(srsran::unique_byte_buffer_t pdu, // UE MAC scheduler updates mac_ctrl.handle_con_reconf(recfg_r8, ue_capabilities); - // Fill in NAS PDU - Only for RRC Connection Reconfiguration during E-RAB Release Command - if (nas_pdu.size() > 0 and !recfg_r8.ded_info_nas_list_present) { - recfg_r8.ded_info_nas_list_present = true; - recfg_r8.ded_info_nas_list.resize(recfg_r8.rr_cfg_ded.drb_to_release_list.size()); - // Add NAS PDU - for (uint32_t idx = 0; idx < recfg_r8.rr_cfg_ded.drb_to_release_list.size(); idx++) { - recfg_r8.ded_info_nas_list[idx].resize(nas_pdu.size()); - memcpy(recfg_r8.ded_info_nas_list[idx].data(), nas_pdu.data(), nas_pdu.size()); - } - } - // Reuse same PDU if (pdu != nullptr) { pdu->clear(); @@ -774,8 +920,11 @@ void rrc::ue::send_connection_reconf(srsran::unique_byte_buffer_t pdu, send_dl_dcch(&dl_dcch_msg, std::move(pdu), &octet_str); // Log event. + asn1::json_writer json_writer; + dl_dcch_msg.to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, octet_str, + json_writer.to_string(), static_cast(rrc_event_type::con_reconf), static_cast(procedure_result_code::none), rnti); @@ -795,8 +944,11 @@ void rrc::ue::handle_rrc_reconf_complete(rrc_conn_recfg_complete_s* msg, srsran: } // Log event. + asn1::json_writer json_writer; + msg->to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, asn1::octstring_to_string(last_ul_msg->msg, last_ul_msg->N_bytes), + json_writer.to_string(), static_cast(rrc_event_type::con_reconf_complete), static_cast(procedure_result_code::none), rnti); @@ -813,6 +965,9 @@ void rrc::ue::handle_rrc_reconf_complete(rrc_conn_recfg_complete_s* msg, srsran: rlf_info_pending = false; send_ue_info_req(); } + + // Many S1AP procedures end with RRC Reconfiguration. Notify S1AP accordingly. + parent->s1ap->notify_rrc_reconf_complete(rnti); } void rrc::ue::send_ue_info_req() @@ -832,8 +987,12 @@ void rrc::ue::handle_ue_info_resp(const asn1::rrc::ue_info_resp_r9_s& msg, srsra { auto& resp_r9 = msg.crit_exts.c1().ue_info_resp_r9(); if (resp_r9.rlf_report_r9_present) { - std::string msg_str = asn1::octstring_to_string(pdu->msg, pdu->N_bytes); - event_logger::get().log_rlf(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, msg_str, rnti); + asn1::json_writer json_writer; + msg.to_json(json_writer); + event_logger::get().log_rlf_report(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, + asn1::octstring_to_string(pdu->msg, pdu->N_bytes), + json_writer.to_string(), + rnti); } if (resp_r9.rach_report_r9_present) { // TODO: Handle RACH-Report @@ -874,7 +1033,7 @@ void rrc::ue::handle_security_mode_failure(security_mode_fail_s* msg) /* * UE capabilities info */ -void rrc::ue::send_ue_cap_enquiry() +void rrc::ue::send_ue_cap_enquiry(const std::vector& rats) { dl_dcch_msg_s dl_dcch_msg; dl_dcch_msg.msg.set_c1().set_ue_cap_enquiry().crit_exts.set_c1().set_ue_cap_enquiry_r8(); @@ -882,28 +1041,34 @@ void rrc::ue::send_ue_cap_enquiry() ue_cap_enquiry_s* enq = &dl_dcch_msg.msg.c1().ue_cap_enquiry(); enq->rrc_transaction_id = (uint8_t)((transaction_id++) % 4); - enq->crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request.resize(1); - enq->crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[0].value = rat_type_e::eutra; + enq->crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request.resize(rats.size()); + for (uint32_t i = 0; i < rats.size(); ++i) { + enq->crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[i].value = rats.at(i); + } send_dl_dcch(&dl_dcch_msg); } -bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) +/** + * @brief Handle the reception of UE capability information message + * + * @return int SRSRAN_SUCCESS if unpacking was ok. SRSRAN_ERROR otherwise + */ +int rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) { parent->logger.info("UECapabilityInformation transaction ID: %d", msg->rrc_transaction_id); ue_cap_info_r8_ies_s* msg_r8 = &msg->crit_exts.c1().ue_cap_info_r8(); for (uint32_t i = 0; i < msg_r8->ue_cap_rat_container_list.size(); i++) { if (msg_r8->ue_cap_rat_container_list[i].rat_type != rat_type_e::eutra) { - parent->logger.warning("Not handling UE capability information for RAT type %s", - msg_r8->ue_cap_rat_container_list[i].rat_type.to_string()); + // Not handling UE capability information for RATs other than EUTRA continue; } asn1::cbit_ref bref(msg_r8->ue_cap_rat_container_list[i].ue_cap_rat_container.data(), msg_r8->ue_cap_rat_container_list[i].ue_cap_rat_container.size()); if (eutra_capabilities.unpack(bref) != asn1::SRSASN_SUCCESS) { parent->logger.error("Failed to unpack EUTRA capabilities message"); - return false; + return SRSRAN_ERROR; } if (parent->logger.debug.enabled()) { asn1::json_writer js{}; @@ -914,13 +1079,17 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) ue_capabilities = srsran::make_rrc_ue_capabilities(eutra_capabilities); parent->logger.info("UE rnti: 0x%x category: %d", rnti, eutra_capabilities.ue_category); + + if (endc_handler != nullptr) { + endc_handler->handle_eutra_capabilities(eutra_capabilities); + } } if (eutra_capabilities_unpacked) { srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); if (pdu == nullptr) { parent->logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); - return false; + return SRSRAN_ERROR; } asn1::bit_ref bref2{pdu->msg, pdu->get_tailroom()}; msg->pack(bref2); @@ -929,12 +1098,15 @@ bool rrc::ue::handle_ue_cap_info(ue_cap_info_s* msg) dest.resize(bref2.distance_bytes()); memcpy(dest.data(), pdu->msg, bref2.distance_bytes()); bref2 = asn1::bit_ref{pdu->msg, pdu->get_tailroom()}; - ue_rat_caps.pack(bref2); + if (ue_rat_caps.pack(bref2) != asn1::SRSASN_SUCCESS) { + parent->logger.error("Couldn't pack ue rat caps"); + return SRSRAN_ERROR; + } pdu->N_bytes = bref2.distance_bytes(); parent->s1ap->send_ue_cap_info_indication(rnti, std::move(pdu)); } - return true; + return SRSRAN_SUCCESS; } /* @@ -961,8 +1133,11 @@ void rrc::ue::send_connection_release() send_dl_dcch(&dl_dcch_msg, nullptr, &octet_str); // Log rrc release event. + asn1::json_writer json_writer; + dl_dcch_msg.to_json(json_writer); event_logger::get().log_rrc_event(ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX)->cell_common->enb_cc_idx, octet_str, + json_writer.to_string(), static_cast(rrc_event_type::con_release), static_cast(con_release_result), rnti); @@ -975,14 +1150,14 @@ void rrc::ue::send_connection_release() */ void rrc::ue::handle_ue_init_ctxt_setup_req(const asn1::s1ap::init_context_setup_request_s& msg) { - set_bitrates(msg.protocol_ies.ueaggregate_maximum_bitrate.value); - ue_security_cfg.set_security_capabilities(msg.protocol_ies.ue_security_cap.value); - ue_security_cfg.set_security_key(msg.protocol_ies.security_key.value); + set_bitrates(msg->ueaggregate_maximum_bitrate.value); + ue_security_cfg.set_security_capabilities(msg->ue_security_cap.value); + ue_security_cfg.set_security_key(msg->security_key.value); // CSFB - if (msg.protocol_ies.cs_fallback_ind_present) { - if (msg.protocol_ies.cs_fallback_ind.value.value == asn1::s1ap::cs_fallback_ind_opts::cs_fallback_required or - msg.protocol_ies.cs_fallback_ind.value.value == asn1::s1ap::cs_fallback_ind_opts::cs_fallback_high_prio) { + if (msg->cs_fallback_ind_present) { + if (msg->cs_fallback_ind.value.value == asn1::s1ap::cs_fallback_ind_opts::cs_fallback_required or + msg->cs_fallback_ind.value.value == asn1::s1ap::cs_fallback_ind_opts::cs_fallback_high_prio) { is_csfb = true; } } @@ -993,25 +1168,25 @@ void rrc::ue::handle_ue_init_ctxt_setup_req(const asn1::s1ap::init_context_setup bool rrc::ue::handle_ue_ctxt_mod_req(const asn1::s1ap::ue_context_mod_request_s& msg) { - if (msg.protocol_ies.cs_fallback_ind_present) { - if (msg.protocol_ies.cs_fallback_ind.value.value == asn1::s1ap::cs_fallback_ind_opts::cs_fallback_required || - msg.protocol_ies.cs_fallback_ind.value.value == asn1::s1ap::cs_fallback_ind_opts::cs_fallback_high_prio) { + if (msg->cs_fallback_ind_present) { + if (msg->cs_fallback_ind.value.value == asn1::s1ap::cs_fallback_ind_opts::cs_fallback_required || + msg->cs_fallback_ind.value.value == asn1::s1ap::cs_fallback_ind_opts::cs_fallback_high_prio) { /* Remember that we are in a CSFB right now */ is_csfb = true; } } // UEAggregateMaximumBitrate - if (msg.protocol_ies.ueaggregate_maximum_bitrate_present) { - set_bitrates(msg.protocol_ies.ueaggregate_maximum_bitrate.value); + if (msg->ueaggregate_maximum_bitrate_present) { + set_bitrates(msg->ueaggregate_maximum_bitrate.value); } - if (msg.protocol_ies.ue_security_cap_present) { - ue_security_cfg.set_security_capabilities(msg.protocol_ies.ue_security_cap.value); + if (msg->ue_security_cap_present) { + ue_security_cfg.set_security_capabilities(msg->ue_security_cap.value); } - if (msg.protocol_ies.security_key_present) { - ue_security_cfg.set_security_key(msg.protocol_ies.security_key.value); + if (msg->security_key_present) { + ue_security_cfg.set_security_key(msg->security_key.value); send_security_mode_command(); } @@ -1024,35 +1199,6 @@ void rrc::ue::set_bitrates(const asn1::s1ap::ue_aggregate_maximum_bitrate_s& rat bitrates = rates; } -bool rrc::ue::setup_erabs(const asn1::s1ap::erab_to_be_setup_list_ctxt_su_req_l& e) -{ - for (const auto& item : e) { - const auto& erab = item.value.erab_to_be_setup_item_ctxt_su_req(); - if (erab.ext) { - parent->logger.warning("Not handling E-RABToBeSetupListCtxtSURequest extensions"); - } - if (erab.ie_exts_present) { - parent->logger.warning("Not handling E-RABToBeSetupListCtxtSURequest extensions"); - } - if (erab.transport_layer_address.length() > 32) { - parent->logger.error("IPv6 addresses not currently supported"); - return false; - } - - uint32_t teid_out = 0; - srsran::uint8_to_uint32(erab.gtp_teid.data(), &teid_out); - srsran::const_span nas_pdu; - if (erab.nas_pdu_present) { - nas_pdu = erab.nas_pdu; - } - asn1::s1ap::cause_c cause; - bearer_list.add_erab( - erab.erab_id, erab.erab_level_qos_params, erab.transport_layer_address, teid_out, nas_pdu, cause); - bearer_list.add_gtpu_bearer(erab.erab_id); - } - return true; -} - bool rrc::ue::release_erabs() { bearer_list.release_erabs(); @@ -1087,12 +1233,14 @@ int rrc::ue::setup_erab(uint16_t erab_ cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::multiple_erab_id_instances; return SRSRAN_ERROR; } - if (bearer_list.add_erab(erab_id, qos_params, addr, gtpu_teid_out, nas_pdu, cause) != SRSRAN_SUCCESS) { + if (bearer_list.addmod_erab(erab_id, qos_params, addr, gtpu_teid_out, nas_pdu, cause) != SRSRAN_SUCCESS) { + parent->logger.error("Couldn't add E-RAB id=%d for rnti=0x%x", erab_id, rnti); return SRSRAN_ERROR; } if (bearer_list.add_gtpu_bearer(erab_id) != SRSRAN_SUCCESS) { cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::radio_res_not_available; bearer_list.release_erab(erab_id); + parent->logger.error("Couldn't add E-RAB id=%d for rnti=0x%x", erab_id, rnti); return SRSRAN_ERROR; } return SRSRAN_SUCCESS; @@ -1121,12 +1269,11 @@ void rrc::ue::update_scells() const ue_cell_ded* pcell = ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX); const enb_cell_common* pcell_cfg = pcell->cell_common; - if (ue_cell_list.nof_cells() == pcell_cfg->scells.size() + 1) { - // SCells already added + // Check whether UE supports CA + if (eutra_capabilities.access_stratum_release.to_number() < 10) { + parent->logger.info("UE doesn't support CA. Skipping SCell activation"); return; } - - // Check whether UE supports CA if (not eutra_capabilities.non_crit_ext_present or not eutra_capabilities.non_crit_ext.non_crit_ext_present or not eutra_capabilities.non_crit_ext.non_crit_ext.non_crit_ext_present or not eutra_capabilities.non_crit_ext.non_crit_ext.non_crit_ext.rf_params_v1020_present or @@ -1136,6 +1283,11 @@ void rrc::ue::update_scells() return; } + if (ue_cell_list.nof_cells() == pcell_cfg->scells.size() + 1) { + // SCells already added + return; + } + for (const enb_cell_common* scell : pcell_cfg->scells) { ue_cell_list.add_cell(scell->enb_cc_idx); } @@ -1157,9 +1309,9 @@ void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg, std::string* octet_str) } pdu->N_bytes = (uint32_t)bref.distance_bytes(); - char buf[32] = {}; - sprintf(buf, "SRB0 - rnti=0x%x", rnti); - parent->log_rrc_message(buf, Tx, pdu.get(), *dl_ccch_msg, dl_ccch_msg->msg.c1().type().to_string()); + // Log Tx message + parent->log_rrc_message( + Tx, rnti, srb_to_lcid(lte_srb::srb0), *pdu, *dl_ccch_msg, dl_ccch_msg->msg.c1().type().to_string()); // Encode the pdu as an octet string if the user passed a valid pointer. if (octet_str) { @@ -1174,38 +1326,37 @@ void rrc::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg, std::string* octet_str) bool rrc::ue::send_dl_dcch(const dl_dcch_msg_s* dl_dcch_msg, srsran::unique_byte_buffer_t pdu, std::string* octet_str) { - if (!pdu) { + if (pdu == nullptr) { pdu = srsran::make_byte_buffer(); - } - if (pdu) { - asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); - if (dl_dcch_msg->pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { - parent->logger.error("Failed to encode DL-DCCH-Msg"); + if (pdu == nullptr) { + parent->logger.error("Allocating pdu"); return false; } - pdu->N_bytes = (uint32_t)bref.distance_bytes(); + } - lte_srb rb = lte_srb::srb1; - if (dl_dcch_msg->msg.c1().type() == dl_dcch_msg_type_c::c1_c_::types_opts::dl_info_transfer) { - // send messages with NAS on SRB2 if user is fully registered (after RRC reconfig complete) - rb = (parent->rlc->has_bearer(rnti, srb_to_lcid(lte_srb::srb2)) && state == RRC_STATE_REGISTERED) ? lte_srb::srb2 - : lte_srb::srb1; - } - - char buf[32] = {}; - sprintf(buf, "%s - rnti=0x%x", srsran::get_srb_name(rb), rnti); - parent->log_rrc_message(buf, Tx, pdu.get(), *dl_dcch_msg, dl_dcch_msg->msg.c1().type().to_string()); - - // Encode the pdu as an octet string if the user passed a valid pointer. - if (octet_str) { - *octet_str = asn1::octstring_to_string(pdu->msg, pdu->N_bytes); - } - - parent->pdcp->write_sdu(rnti, srb_to_lcid(rb), std::move(pdu)); - } else { - parent->logger.error("Allocating pdu"); + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + if (dl_dcch_msg->pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { + parent->logger.error("Failed to encode DL-DCCH-Msg for rnti=0x%x", rnti); return false; } + pdu->N_bytes = (uint32_t)bref.distance_bytes(); + + lte_srb rb = lte_srb::srb1; + if (dl_dcch_msg->msg.c1().type() == dl_dcch_msg_type_c::c1_c_::types_opts::dl_info_transfer) { + // send messages with NAS on SRB2 if user is fully registered (after RRC reconfig complete) + rb = (parent->rlc->has_bearer(rnti, srb_to_lcid(lte_srb::srb2)) && state == RRC_STATE_REGISTERED) ? lte_srb::srb2 + : lte_srb::srb1; + } + + // Log Tx message + parent->log_rrc_message(Tx, rnti, srb_to_lcid(rb), *pdu, *dl_dcch_msg, dl_dcch_msg->msg.c1().type().to_string()); + + // Encode the pdu as an octet string if the user passed a valid pointer. + if (octet_str != nullptr) { + *octet_str = asn1::octstring_to_string(pdu->msg, pdu->N_bytes); + } + + parent->pdcp->write_sdu(rnti, srb_to_lcid(rb), std::move(pdu)); return true; } @@ -1386,18 +1537,53 @@ void rrc::ue::apply_pdcp_drb_updates(const rr_cfg_ded_s& pending_rr_cfg) void rrc::ue::apply_rlc_rb_updates(const rr_cfg_ded_s& pending_rr_cfg) { for (const srb_to_add_mod_s& srb : pending_rr_cfg.srb_to_add_mod_list) { - parent->rlc->add_bearer(rnti, srb.srb_id, srsran::rlc_config_t::srb_config(srb.srb_id)); + srb_cfg_t* srb_cfg; + if (srb.srb_id == 1) { + srb_cfg = &parent->cfg.srb1_cfg; + } else if (srb.srb_id == 2) { + srb_cfg = &parent->cfg.srb2_cfg; + } else { + srsran_assertion_failure("Invalid LTE SRB id=%d", srb.srb_id); + } + + if (srb_cfg->rlc_cfg.type() == srb_to_add_mod_s::rlc_cfg_c_::types_opts::explicit_value) { + srsran::rlc_config_t rlc_cfg = srsran::make_rlc_config_t(srb_cfg->rlc_cfg.explicit_value()); + if (rlc_cfg.rlc_mode == srsran::rlc_mode_t::am and srb_cfg->enb_dl_max_retx_thres > 0) { + rlc_cfg.am.max_retx_thresh = srb_cfg->enb_dl_max_retx_thres; + } + parent->rlc->add_bearer(rnti, srb.srb_id, rlc_cfg); + } else { + srsran::rlc_config_t rlc_cfg = srsran::rlc_config_t::srb_config(srb.srb_id); + if (rlc_cfg.rlc_mode == srsran::rlc_mode_t::am and srb_cfg->enb_dl_max_retx_thres > 0) { + rlc_cfg.am.max_retx_thresh = srb_cfg->enb_dl_max_retx_thres; + } + parent->rlc->add_bearer(rnti, srb.srb_id, rlc_cfg); + } } + if (pending_rr_cfg.drb_to_release_list.size() > 0) { for (uint8_t drb_id : pending_rr_cfg.drb_to_release_list) { parent->rlc->del_bearer(rnti, drb_to_lcid((lte_drb)drb_id)); + + // deregister EPS bearer + uint8_t eps_bearer_id = parent->bearer_manager.get_lcid_bearer(rnti, drb_to_lcid((lte_drb)drb_id)).eps_bearer_id; + parent->bearer_manager.remove_eps_bearer(rnti, eps_bearer_id); } } for (const drb_to_add_mod_s& drb : pending_rr_cfg.drb_to_add_mod_list) { if (not drb.rlc_cfg_present) { parent->logger.warning("Default RLC DRB config not supported"); } - parent->rlc->add_bearer(rnti, drb.lc_ch_id, srsran::make_rlc_config_t(drb.rlc_cfg)); + srsran::rlc_config_t rlc_cfg = srsran::make_rlc_config_t(drb.rlc_cfg); + const bearer_cfg_handler::erab_t& erab = bearer_list.get_erabs().at(drb.eps_bearer_id); + if (rlc_cfg.rlc_mode == srsran::rlc_mode_t::am and + parent->cfg.qci_cfg.at(erab.qos_params.qci).enb_dl_max_retx_thres > 0) { + rlc_cfg.am.max_retx_thresh = parent->cfg.qci_cfg.at(erab.qos_params.qci).enb_dl_max_retx_thres; + } + parent->rlc->add_bearer(rnti, drb.lc_ch_id, rlc_cfg); + + // register EPS bearer over LTE PDCP + parent->bearer_manager.add_eps_bearer(rnti, drb.eps_bearer_id, srsran::srsran_rat_t::lte, drb.lc_ch_id); } } diff --git a/srsenb/src/stack/rrc/ue_meas_cfg.cc b/srsenb/src/stack/rrc/ue_meas_cfg.cc index b5b2900de..17c39f604 100644 --- a/srsenb/src/stack/rrc/ue_meas_cfg.cc +++ b/srsenb/src/stack/rrc/ue_meas_cfg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,7 +21,8 @@ #include "srsenb/hdr/stack/rrc/ue_meas_cfg.h" #include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h" -#include "srsran/rrc/rrc_cfg_utils.h" +#include "srsran/asn1/obj_id_cmp_utils.h" +#include "srsran/asn1/rrc_utils.h" using namespace asn1::rrc; @@ -93,8 +94,8 @@ std::tuple add_cell_enb_cfg(meas_obj_lis bool inserted_flag = true; cells_to_add_mod_s new_cell; - asn1::number_to_enum(new_cell.cell_individual_offset, (uint8_t)cellcfg.q_offset); - new_cell.pci = cellcfg.pci; + new_cell.cell_individual_offset = cellcfg.cell_individual_offset; + new_cell.pci = cellcfg.pci; std::pair ret = find_cell(meas_obj_list, cellcfg.earfcn, cellcfg.pci); @@ -360,11 +361,12 @@ bool fill_meascfg_enb_cfg(meas_cfg_s& meascfg, const ue_cell_ded_list& ue_cell_l meascfg.quant_cfg.quant_cfg_eutra = pcell_meascfg.quant_cfg; // Insert all measIds - // TODO: add this to the parser + // TODO: add this to the parser. Now we combine all reports with all objects if (meascfg.report_cfg_to_add_mod_list.size() > 0) { for (const auto& measobj : meascfg.meas_obj_to_add_mod_list) { - add_measid_cfg( - meascfg.meas_id_to_add_mod_list, measobj.meas_obj_id, meascfg.report_cfg_to_add_mod_list[0].report_cfg_id); + for (const auto& measrep : meascfg.report_cfg_to_add_mod_list) { + add_measid_cfg(meascfg.meas_id_to_add_mod_list, measobj.meas_obj_id, measrep.report_cfg_id); + } } } @@ -432,8 +434,7 @@ bool apply_meascfg_updates(meas_cfg_s& meascfg, for (auto it = current_meascfg.meas_id_to_add_mod_list.begin(); it != current_meascfg.meas_id_to_add_mod_list.end();) { if (it->meas_obj_id == found_src_obj->meas_obj_id) { - auto rit = it++; - current_meascfg.meas_id_to_add_mod_list.erase(rit); + it = current_meascfg.meas_id_to_add_mod_list.erase(it); } else { ++it; } diff --git a/srsenb/src/stack/rrc/ue_rr_cfg.cc b/srsenb/src/stack/rrc/ue_rr_cfg.cc index 41b51e474..19e73620f 100644 --- a/srsenb/src/stack/rrc/ue_rr_cfg.cc +++ b/srsenb/src/stack/rrc/ue_rr_cfg.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,8 +23,8 @@ #include "srsenb/hdr/stack/rrc/rrc_bearer_cfg.h" #include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h" #include "srsenb/hdr/stack/rrc/rrc_config.h" +#include "srsran/asn1/obj_id_cmp_utils.h" #include "srsran/asn1/rrc_utils.h" -#include "srsran/rrc/rrc_cfg_utils.h" #define SET_OPT_FIELD(fieldname, out, in) \ if (in.fieldname##_present) { \ @@ -42,7 +42,8 @@ namespace srsenb { * SRBs / DRBs *****************************/ -srb_to_add_mod_s* add_srb(srb_to_add_mod_list_l& srbs, uint8_t srb_id) +srb_to_add_mod_s* +add_srb(srb_to_add_mod_list_l& srbs, uint8_t srb_id, const asn1::rrc::srb_to_add_mod_s::rlc_cfg_c_& srb_cfg) { if (srb_id > 2 or srb_id == 0) { srslog::fetch_basic_logger("RRC").error("Invalid SRB id=%d", srb_id); @@ -54,20 +55,19 @@ srb_to_add_mod_s* add_srb(srb_to_add_mod_list_l& srbs, uint8_t srb_id) srb_it->lc_ch_cfg_present = true; srb_it->lc_ch_cfg.set(srb_to_add_mod_s::lc_ch_cfg_c_::types_opts::default_value); srb_it->rlc_cfg_present = true; - srb_it->rlc_cfg.set(srb_to_add_mod_s::rlc_cfg_c_::types_opts::default_value); - + srb_it->rlc_cfg = srb_cfg; return srb_it; } -void fill_srbs_reconf(srb_to_add_mod_list_l& srbs, const srb_to_add_mod_list_l& current_srbs) +void fill_srbs_reconf(srb_to_add_mod_list_l& srbs, const srb_to_add_mod_list_l& current_srbs, const rrc_cfg_t& enb_cfg) { // NOTE: In case of Handover, the Reconf includes SRB1 if (srsran::find_rrc_obj_id(current_srbs, 1) == current_srbs.end()) { - add_srb(srbs, 1); + add_srb(srbs, 1, enb_cfg.srb1_cfg.rlc_cfg); } if (srsran::find_rrc_obj_id(current_srbs, 2) == current_srbs.end()) { - add_srb(srbs, 2); + add_srb(srbs, 2, enb_cfg.srb2_cfg.rlc_cfg); } } @@ -75,11 +75,17 @@ void fill_srbs_reconf(srb_to_add_mod_list_l& srbs, const srb_to_add_mod_list_l& * SR Config *****************************/ -void fill_sr_cfg_setup(sched_request_cfg_c& sr_cfg, const ue_cell_ded_list& ue_cell_list) +int fill_sr_cfg_setup(sched_request_cfg_c& sr_cfg, const ue_cell_ded_list& ue_cell_list) { - auto& setup = sr_cfg.setup(); - setup.sr_cfg_idx = ue_cell_list.get_sr_res()->sr_I; - setup.sr_pucch_res_idx = ue_cell_list.get_sr_res()->sr_N_pucch; + if (ue_cell_list.get_sr_res()) { + auto& setup = sr_cfg.setup(); + setup.sr_cfg_idx = ue_cell_list.get_sr_res()->sr_I; + setup.sr_pucch_res_idx = ue_cell_list.get_sr_res()->sr_N_pucch; + return SRSRAN_SUCCESS; + } else { + srslog::fetch_basic_logger("RRC").error("SR resource is not configured."); + return SRSRAN_ERROR; + } } /****************************** @@ -129,8 +135,14 @@ void fill_cqi_report_enb_cfg(cqi_report_cfg_s& cqi_report_cfg, const rrc_cfg_t& } else { cqi_report_cfg.cqi_report_periodic_present = true; auto& cqi_setup = cqi_report_cfg.cqi_report_periodic.set_setup(); - cqi_setup.cqi_format_ind_periodic.set( - cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::wideband_cqi); + if (enb_cfg.cqi_cfg.subband_k == 0) { + cqi_setup.cqi_format_ind_periodic.set( + cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::wideband_cqi); + } else { + cqi_setup.cqi_format_ind_periodic.set( + cqi_report_periodic_c::setup_s_::cqi_format_ind_periodic_c_::types::subband_cqi); + cqi_setup.cqi_format_ind_periodic.subband_cqi().k = enb_cfg.cqi_cfg.subband_k; + } cqi_setup.simul_ack_nack_and_cqi = enb_cfg.cqi_cfg.simultaneousAckCQI; } cqi_report_cfg.nom_pdsch_rs_epre_offset = 0; @@ -155,11 +167,11 @@ int fill_cqi_report_setup(cqi_report_cfg_s& cqi_rep, const rrc_cfg_t& enb_cfg, c return SRSRAN_SUCCESS; } -void fill_cqi_report_reconf(cqi_report_cfg_s& cqi_rep, const rrc_cfg_t& enb_cfg, const ue_cell_ded_list& ue_cell_list) +int fill_cqi_report_reconf(cqi_report_cfg_s& cqi_rep, const rrc_cfg_t& enb_cfg, const ue_cell_ded_list& ue_cell_list) { // Get RRC setup CQI config if (fill_cqi_report_setup(cqi_rep, enb_cfg, ue_cell_list) == SRSRAN_ERROR) { - return; + return SRSRAN_ERROR; } if (cqi_rep.cqi_report_mode_aperiodic_present) { @@ -183,9 +195,11 @@ void fill_cqi_report_reconf(cqi_report_cfg_s& cqi_rep, const rrc_cfg_t& enb_cfg, cqi_setup.ri_cfg_idx = ri_idx; } else { srslog::fetch_basic_logger("RRC").warning("Warning: Configured wrong M_ri parameter."); + return SRSRAN_ERROR; } } } + return SRSRAN_SUCCESS; } /****************************** @@ -234,26 +248,33 @@ void fill_phy_cfg_ded_enb_cfg(phys_cfg_ded_s& phy_cfg, const rrc_cfg_t& enb_cfg) fill_cqi_report_enb_cfg(phy_cfg.cqi_report_cfg, enb_cfg); } -void fill_phy_cfg_ded_setup(phys_cfg_ded_s& phy_cfg, const rrc_cfg_t& enb_cfg, const ue_cell_ded_list& ue_cell_list) +int fill_phy_cfg_ded_setup(phys_cfg_ded_s& phy_cfg, const rrc_cfg_t& enb_cfg, const ue_cell_ded_list& ue_cell_list) { // Set PHYConfigDedicated base fill_phy_cfg_ded_enb_cfg(phy_cfg, enb_cfg); // Setup SR PUCCH config - fill_sr_cfg_setup(phy_cfg.sched_request_cfg, ue_cell_list); + if (fill_sr_cfg_setup(phy_cfg.sched_request_cfg, ue_cell_list)) { + return SRSRAN_ERROR; + } // Setup CQI PUCCH config - fill_cqi_report_setup(phy_cfg.cqi_report_cfg, enb_cfg, ue_cell_list); + if (fill_cqi_report_setup(phy_cfg.cqi_report_cfg, enb_cfg, ue_cell_list)) { + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } /// Fills ASN1 PhysicalConfigurationDedicated struct with eNB config params at RRCReconf -void fill_phy_cfg_ded_reconf(phys_cfg_ded_s& phy_cfg, - const rrc_cfg_t& enb_cfg, - const ue_cell_ded_list& ue_cell_list, - const srsran::rrc_ue_capabilities_t& ue_caps) +int fill_phy_cfg_ded_reconf(phys_cfg_ded_s& phy_cfg, + const rrc_cfg_t& enb_cfg, + const ue_cell_ded_list& ue_cell_list, + const srsran::rrc_ue_capabilities_t& ue_caps) { // Use RRCSetup as starting point - fill_phy_cfg_ded_setup(phy_cfg, enb_cfg, ue_cell_list); + if (fill_phy_cfg_ded_setup(phy_cfg, enb_cfg, ue_cell_list)) { + return SRSRAN_ERROR; + } // Antenna Configuration ant_info_ded_s& ant_info = phy_cfg.ant_info.explicit_value(); @@ -270,6 +291,7 @@ void fill_phy_cfg_ded_reconf(phys_cfg_ded_s& phy_cfg, phy_cfg.cqi_report_cfg_pcell_v1250->alt_cqi_table_r12.value = cqi_report_cfg_v1250_s::alt_cqi_table_r12_opts::all_sfs; } + return SRSRAN_SUCCESS; } /*********************************** @@ -299,32 +321,32 @@ void fill_rr_cfg_ded_enb_cfg(asn1::rrc::rr_cfg_ded_s& rr_cfg, const rrc_cfg_t& e rr_cfg.sps_cfg_present = false; } -void fill_rr_cfg_ded_setup(asn1::rrc::rr_cfg_ded_s& rr_cfg, - const rrc_cfg_t& enb_cfg, - const ue_cell_ded_list& ue_cell_list) +int fill_rr_cfg_ded_setup(asn1::rrc::rr_cfg_ded_s& rr_cfg, + const rrc_cfg_t& enb_cfg, + const ue_cell_ded_list& ue_cell_list) { // Establish default enb config fill_rr_cfg_ded_enb_cfg(rr_cfg, enb_cfg); // (Re)establish SRB1 rr_cfg.srb_to_add_mod_list_present = true; - add_srb(rr_cfg.srb_to_add_mod_list, 1); + add_srb(rr_cfg.srb_to_add_mod_list, 1, enb_cfg.srb1_cfg.rlc_cfg); // Setup SR/CQI configs rr_cfg.phys_cfg_ded_present = true; - fill_phy_cfg_ded_setup(rr_cfg.phys_cfg_ded, enb_cfg, ue_cell_list); + return fill_phy_cfg_ded_setup(rr_cfg.phys_cfg_ded, enb_cfg, ue_cell_list); } -void fill_rr_cfg_ded_reconf(asn1::rrc::rr_cfg_ded_s& rr_cfg, - const rr_cfg_ded_s& current_rr_cfg, - const rrc_cfg_t& enb_cfg, - const ue_cell_ded_list& ue_cell_list, - const bearer_cfg_handler& bearers, - const srsran::rrc_ue_capabilities_t& ue_caps, - bool phy_cfg_updated) +int fill_rr_cfg_ded_reconf(asn1::rrc::rr_cfg_ded_s& rr_cfg, + const rr_cfg_ded_s& current_rr_cfg, + const rrc_cfg_t& enb_cfg, + const ue_cell_ded_list& ue_cell_list, + const bearer_cfg_handler& bearers, + const srsran::rrc_ue_capabilities_t& ue_caps, + bool phy_cfg_updated) { // (Re)establish SRBs - fill_srbs_reconf(rr_cfg.srb_to_add_mod_list, current_rr_cfg.srb_to_add_mod_list); + fill_srbs_reconf(rr_cfg.srb_to_add_mod_list, current_rr_cfg.srb_to_add_mod_list, enb_cfg); rr_cfg.srb_to_add_mod_list_present = rr_cfg.srb_to_add_mod_list.size() > 0; // Update DRBs if required @@ -338,8 +360,9 @@ void fill_rr_cfg_ded_reconf(asn1::rrc::rr_cfg_ded_s& rr_cfg, // PhysCfgDed update needed if (phy_cfg_updated) { rr_cfg.phys_cfg_ded_present = true; - fill_phy_cfg_ded_reconf(rr_cfg.phys_cfg_ded, enb_cfg, ue_cell_list, ue_caps); + return fill_phy_cfg_ded_reconf(rr_cfg.phys_cfg_ded, enb_cfg, ue_cell_list, ue_caps); } + return SRSRAN_SUCCESS; } /** @@ -563,18 +586,20 @@ void apply_scells_to_add_diff(asn1::rrc::scell_to_add_mod_list_r10_l& current_sc **********************************/ /// Apply Reconf updates and update current state -void apply_reconf_updates(asn1::rrc::rrc_conn_recfg_r8_ies_s& recfg_r8, - ue_var_cfg_t& current_ue_cfg, - const rrc_cfg_t& enb_cfg, - const ue_cell_ded_list& ue_cell_list, - bearer_cfg_handler& bearers, - const srsran::rrc_ue_capabilities_t& ue_caps, - bool phy_cfg_updated) +int apply_reconf_updates(asn1::rrc::rrc_conn_recfg_r8_ies_s& recfg_r8, + ue_var_cfg_t& current_ue_cfg, + const rrc_cfg_t& enb_cfg, + const ue_cell_ded_list& ue_cell_list, + bearer_cfg_handler& bearers, + const srsran::rrc_ue_capabilities_t& ue_caps, + bool phy_cfg_updated) { // Compute pending updates and fill reconf msg recfg_r8.rr_cfg_ded_present = true; - fill_rr_cfg_ded_reconf( - recfg_r8.rr_cfg_ded, current_ue_cfg.rr_cfg, enb_cfg, ue_cell_list, bearers, ue_caps, phy_cfg_updated); + if (fill_rr_cfg_ded_reconf( + recfg_r8.rr_cfg_ded, current_ue_cfg.rr_cfg, enb_cfg, ue_cell_list, bearers, ue_caps, phy_cfg_updated)) { + return SRSRAN_ERROR; + } fill_scells_reconf(recfg_r8, current_ue_cfg.scells, enb_cfg, ue_cell_list, ue_caps); recfg_r8.meas_cfg_present |= recfg_r8.meas_cfg.meas_gap_cfg_present; @@ -584,6 +609,7 @@ void apply_reconf_updates(asn1::rrc::rrc_conn_recfg_r8_ies_s& recfg_r8, // Update current rr_cfg_ded and scells state apply_rr_cfg_ded_diff(current_ue_cfg.rr_cfg, recfg_r8.rr_cfg_ded); apply_scells_to_add_diff(current_ue_cfg.scells, recfg_r8); + return SRSRAN_SUCCESS; } } // namespace srsenb diff --git a/srsenb/src/stack/s1ap/CMakeLists.txt b/srsenb/src/stack/s1ap/CMakeLists.txt new file mode 100644 index 000000000..f7abee651 --- /dev/null +++ b/srsenb/src/stack/s1ap/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES s1ap.cc) +add_library(srsenb_s1ap STATIC ${SOURCES}) diff --git a/srsenb/src/stack/upper/s1ap.cc b/srsenb/src/stack/s1ap/s1ap.cc similarity index 69% rename from srsenb/src/stack/upper/s1ap.cc rename to srsenb/src/stack/s1ap/s1ap.cc index cfbc933f8..a68e65c83 100644 --- a/srsenb/src/stack/upper/s1ap.cc +++ b/srsenb/src/stack/s1ap/s1ap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,13 +19,13 @@ * */ -#include "srsenb/hdr/stack/upper/s1ap.h" +#include "srsenb/hdr/stack/s1ap/s1ap.h" #include "srsran/adt/scope_exit.h" #include "srsran/common/bcd_helpers.h" #include "srsran/common/enb_events.h" #include "srsran/common/int_helpers.h" #include "srsran/common/standard_streams.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_s1ap.h" #include //for inet_ntop() #include @@ -41,6 +41,7 @@ using srsran::uint32_to_uint8; #define procError(fmt, ...) s1ap_ptr->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) #define procWarning(fmt, ...) s1ap_ptr->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) #define procInfo(fmt, ...) s1ap_ptr->logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procDebug(fmt, ...) s1ap_ptr->logger.debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) #define WarnUnsupportFeature(cond, featurename) \ do { \ @@ -61,7 +62,10 @@ asn1::bounded_bitstring<1, 160, true, true> addr_to_asn1(const char* addr_str) { asn1::bounded_bitstring<1, 160, true, true> transport_layer_addr(32); uint8_t addr[4]; - inet_pton(AF_INET, addr_str, addr); + if (inet_pton(AF_INET, addr_str, addr) != 1) { + srsran::console("Invalid addr_str: %s\n", addr_str); + perror("inet_pton"); + } for (uint32_t j = 0; j < 4; ++j) { transport_layer_addr.data()[j] = addr[3 - j]; } @@ -114,7 +118,7 @@ void fill_erab_failed_setup_list(OutList& output_list, const s1ap::erab_item_lis output_list.resize(input_list.size()); for (size_t i = 0; i < input_list.size(); ++i) { output_list[i].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM); - output_list[i].value.erab_item() = input_list[i]; + output_list[i]->erab_item() = input_list[i]; } } @@ -124,6 +128,7 @@ void fill_erab_failed_setup_list(OutList& output_list, const s1ap::erab_item_lis s1ap::ue::ho_prep_proc_t::ho_prep_proc_t(s1ap::ue* ue_) : ue_ptr(ue_), s1ap_ptr(ue_->s1ap_ptr) {} srsran::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t target_eci_, + uint16_t target_tac_, srsran::plmn_id_t target_plmn_, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container_, @@ -131,11 +136,12 @@ srsran::proc_outcome_t s1ap::ue::ho_prep_proc_t::init(uint32_t { ho_cmd_msg = nullptr; target_eci = target_eci_; + target_tac = target_tac_; target_plmn = target_plmn_; procInfo("Sending HandoverRequired to MME id=%d", ue_ptr->ctxt.mme_ue_s1ap_id.value()); if (not ue_ptr->send_ho_required( - target_eci, target_plmn, fwd_erabs, std::move(rrc_container_), has_direct_fwd_path)) { + target_eci, target_tac, target_plmn, fwd_erabs, std::move(rrc_container_), has_direct_fwd_path)) { procError("Failed to send HORequired to cell 0x%x", target_eci); return srsran::proc_outcome_t::error; } @@ -155,7 +161,7 @@ srsran::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(const ho_prep_fail_s& msg { ue_ptr->ts1_reloc_prep.stop(); - std::string cause = s1ap_ptr->get_cause(msg.protocol_ies.cause.value); + std::string cause = s1ap_ptr->get_cause(msg->cause.value); procError("HO preparation Failure. Cause: %s", cause.c_str()); srsran::console("HO preparation Failure. Cause: %s\n", cause.c_str()); @@ -173,16 +179,16 @@ srsran::proc_outcome_t s1ap::ue::ho_prep_proc_t::react(const asn1::s1ap::ho_cmd_ ue_ptr->ts1_reloc_overall.run(); // Check for unsupported S1AP fields - if (msg.ext or msg.protocol_ies.target_to_source_transparent_container_secondary_present or - msg.protocol_ies.handov_type.value.value != handov_type_opts::intralte or - msg.protocol_ies.crit_diagnostics_present or msg.protocol_ies.nas_security_paramsfrom_e_utran_present) { + if (msg.ext or msg->target_to_source_transparent_container_secondary_present or + msg->handov_type.value.value != handov_type_opts::intralte or msg->crit_diagnostics_present or + msg->nas_security_paramsfrom_e_utran_present) { procWarning("Not handling HandoverCommand extensions and non-intraLTE params"); } // In case of intra-system Handover, Target to Source Transparent Container IE shall be encoded as // Target eNB to Source eNB Transparent Container IE - asn1::cbit_ref bref(msg.protocol_ies.target_to_source_transparent_container.value.data(), - msg.protocol_ies.target_to_source_transparent_container.value.size()); + asn1::cbit_ref bref(msg->target_to_source_transparent_container.value.data(), + msg->target_to_source_transparent_container.value.size()); asn1::s1ap::targetenb_to_sourceenb_transparent_container_s container; if (container.unpack(bref) != asn1::SRSASN_SUCCESS) { procError("Failed to decode TargeteNBToSourceeNBTransparentContainer"); @@ -226,6 +232,7 @@ void s1ap::ue::ho_prep_proc_t::then(const srsran::proc_state_t& result) srsran::proc_outcome_t s1ap::s1_setup_proc_t::init() { procInfo("Starting new MME connection."); + connect_count++; return start_mme_connection(); } @@ -240,23 +247,41 @@ srsran::proc_outcome_t s1ap::s1_setup_proc_t::start_mme_connection() return srsran::proc_outcome_t::success; } - if (not s1ap_ptr->connect_mme()) { - procInfo("Could not connect to MME"); - return srsran::proc_outcome_t::error; - } + auto connect_callback = [this]() { + bool connected = s1ap_ptr->connect_mme(); - if (not s1ap_ptr->setup_s1()) { - procError("S1 setup failed. Exiting..."); - srsran::console("S1 setup failed\n"); - s1ap_ptr->running = false; - return srsran::proc_outcome_t::error; - } + auto notify_result = [this, connected]() { + s1_setup_proc_t::s1connectresult res; + res.success = connected; + s1ap_ptr->s1setup_proc.trigger(res); + }; + s1ap_ptr->task_sched.notify_background_task_result(notify_result); + }; + srsran::get_background_workers().push_task(connect_callback); + procDebug("Connection to MME requested."); - s1ap_ptr->s1setup_timeout.run(); - procInfo("S1SetupRequest sent. Waiting for response..."); return srsran::proc_outcome_t::yield; } +srsran::proc_outcome_t s1ap::s1_setup_proc_t::react(const srsenb::s1ap::s1_setup_proc_t::s1connectresult& event) +{ + if (event.success) { + procInfo("Connected to MME. Sending S1 setup request."); + s1ap_ptr->s1setup_timeout.run(); + if (not s1ap_ptr->setup_s1()) { + procError("S1 setup failed. Exiting..."); + srsran::console("S1 setup failed\n"); + s1ap_ptr->running = false; + return srsran::proc_outcome_t::error; + } + procInfo("S1 setup request sent. Waiting for response."); + return srsran::proc_outcome_t::yield; + } + + procInfo("Could not connected to MME. Aborting"); + return srsran::proc_outcome_t::error; +} + srsran::proc_outcome_t s1ap::s1_setup_proc_t::react(const srsenb::s1ap::s1_setup_proc_t::s1setupresult& event) { if (s1ap_ptr->s1setup_timeout.is_running()) { @@ -271,7 +296,7 @@ srsran::proc_outcome_t s1ap::s1_setup_proc_t::react(const srsenb::s1ap::s1_setup return srsran::proc_outcome_t::error; } -void s1ap::s1_setup_proc_t::then(const srsran::proc_state_t& result) const +void s1ap::s1_setup_proc_t::then(const srsran::proc_state_t& result) { if (result.is_error()) { procInfo("Failed to initiate S1 connection. Attempting reconnection in %d seconds", @@ -282,7 +307,13 @@ void s1ap::s1_setup_proc_t::then(const srsran::proc_state_t& result) const s1ap_ptr->mme_socket.close(); procInfo("S1AP socket closed."); s1ap_ptr->mme_connect_timer.run(); + if (s1ap_ptr->args.max_s1_setup_retries > 0 && connect_count > s1ap_ptr->args.max_s1_setup_retries) { + s1ap_ptr->alarms_channel("s1apError"); + srsran_terminate("Error connecting to MME"); + } // Try again with in 10 seconds + } else { + connect_count = 0; } } @@ -293,7 +324,11 @@ void s1ap::s1_setup_proc_t::then(const srsran::proc_state_t& result) const s1ap::s1ap(srsran::task_sched_handle task_sched_, srslog::basic_logger& logger, srsran::socket_manager_itf* rx_socket_handler_) : - s1setup_proc(this), logger(logger), task_sched(task_sched_), rx_socket_handler(rx_socket_handler_) + s1setup_proc(this), + logger(logger), + task_sched(task_sched_), + rx_socket_handler(rx_socket_handler_), + alarms_channel(srslog::fetch_log_channel("alarms")) { mme_task_queue = task_sched.make_task_queue(); } @@ -309,11 +344,11 @@ int s1ap::init(const s1ap_args_t& args_, rrc_interface_s1ap* rrc_) mme_connect_timer = task_sched.get_unique_timer(); auto mme_connect_run = [this](uint32_t tid) { if (s1setup_proc.is_busy()) { - logger.error("Failed to initiate S1Setup procedure."); + logger.error("Failed to initiate S1Setup procedure: procedure is busy."); } s1setup_proc.launch(); }; - mme_connect_timer.set(10000, mme_connect_run); + mme_connect_timer.set(args.s1_connect_timer * 1000, mme_connect_run); // Setup S1Setup timeout s1setup_timeout = task_sched.get_unique_timer(); uint32_t s1setup_timeout_val = 5000; @@ -327,7 +362,7 @@ int s1ap::init(const s1ap_args_t& args_, rrc_interface_s1ap* rrc_) running = true; // starting MME connection if (not s1setup_proc.launch()) { - logger.error("Failed to initiate S1Setup procedure."); + logger.error("Failed to initiate S1Setup procedure: error launching procedure."); } return SRSRAN_SUCCESS; @@ -420,16 +455,9 @@ void s1ap::write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) bool s1ap::user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_radio) { - logger.info("User inactivity - RNTI:0x%x", rnti); - ue* u = users.find_ue_rnti(rnti); if (u == nullptr) { - return false; - } - - if (u->was_uectxtrelease_requested() or not u->ctxt.mme_ue_s1ap_id.has_value()) { - logger.warning("UE context for RNTI:0x%x is in zombie state. Releasing...", rnti); - users.erase(u); + logger.warning("Released UE with rnti=0x%x not found", rnti); rrc->release_ue(rnti); return false; } @@ -468,6 +496,15 @@ void s1ap::ue_ctxt_setup_complete(uint16_t rnti) u->ue_ctxt_setup_complete(); } +void s1ap::notify_rrc_reconf_complete(uint16_t rnti) +{ + ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + return; + } + u->notify_rrc_reconf_complete(); +} + bool s1ap::is_mme_connected() { return mme_connected; @@ -482,8 +519,39 @@ bool s1ap::connect_mme() using namespace srsran::net_utils; logger.info("Connecting to MME %s:%d", args.mme_addr.c_str(), int(MME_PORT)); - // Init SCTP socket and bind it - if (not sctp_init_client(&mme_socket, socket_type::seqpacket, args.s1c_bind_addr.c_str())) { + // Open SCTP socket + if (not mme_socket.open_socket( + srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP)) { + return false; + } + + // Set SO_REUSE_ADDR if necessary + if (args.sctp_reuse_addr) { + if (not mme_socket.reuse_addr()) { + mme_socket.close(); + return false; + } + } + + // Subscribe to shutdown events + if (not mme_socket.sctp_subscribe_to_events()) { + mme_socket.close(); + return false; + } + + // Set SRTO_MAX + if (not mme_socket.sctp_set_rto_opts(args.sctp_rto_max)) { + return false; + } + + // Set SCTP init options + if (not mme_socket.sctp_set_init_msg_opts(args.sctp_init_max_attempts, args.sctp_max_init_timeo)) { + return false; + } + + // Bind socket + if (not mme_socket.bind_addr(args.s1c_bind_addr.c_str(), args.s1c_bind_port)) { + mme_socket.close(); return false; } logger.info("SCTP socket opened. fd=%d", mme_socket.fd()); @@ -520,25 +588,25 @@ bool s1ap::setup_s1() s1ap_pdu_c pdu; pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_S1_SETUP); - s1_setup_request_ies_container& container = pdu.init_msg().value.s1_setup_request().protocol_ies; - container.global_enb_id.value.plm_nid[0] = ((uint8_t*)&plmn)[1]; - container.global_enb_id.value.plm_nid[1] = ((uint8_t*)&plmn)[2]; - container.global_enb_id.value.plm_nid[2] = ((uint8_t*)&plmn)[3]; + s1_setup_request_s& container = pdu.init_msg().value.s1_setup_request(); + container->global_enb_id.value.plm_nid[0] = ((uint8_t*)&plmn)[1]; + container->global_enb_id.value.plm_nid[1] = ((uint8_t*)&plmn)[2]; + container->global_enb_id.value.plm_nid[2] = ((uint8_t*)&plmn)[3]; - container.global_enb_id.value.enb_id.set_macro_enb_id().from_number(args.enb_id); + container->global_enb_id.value.enb_id.set_macro_enb_id().from_number(args.enb_id); - container.enbname_present = true; - container.enbname.value.from_string(args.enb_name); + container->enbname_present = true; + container->enbname.value.from_string(args.enb_name); - container.supported_tas.value.resize(1); + container->supported_tas.value.resize(1); tmp16 = htons(args.tac); - memcpy(container.supported_tas.value[0].tac.data(), (uint8_t*)&tmp16, 2); - container.supported_tas.value[0].broadcast_plmns.resize(1); - container.supported_tas.value[0].broadcast_plmns[0][0] = ((uint8_t*)&plmn)[1]; - container.supported_tas.value[0].broadcast_plmns[0][1] = ((uint8_t*)&plmn)[2]; - container.supported_tas.value[0].broadcast_plmns[0][2] = ((uint8_t*)&plmn)[3]; + memcpy(container->supported_tas.value[0].tac.data(), (uint8_t*)&tmp16, 2); + container->supported_tas.value[0].broadcast_plmns.resize(1); + container->supported_tas.value[0].broadcast_plmns[0][0] = ((uint8_t*)&plmn)[1]; + container->supported_tas.value[0].broadcast_plmns[0][1] = ((uint8_t*)&plmn)[2]; + container->supported_tas.value[0].broadcast_plmns[0][2] = ((uint8_t*)&plmn)[3]; - container.default_paging_drx.value.value = asn1::s1ap::paging_drx_opts::v128; // Todo: add to args, config file + container->default_paging_drx.value.value = asn1::s1ap::paging_drx_opts::v128; // Todo: add to args, config file return sctp_send_s1ap_pdu(pdu, 0, "s1SetupRequest"); } @@ -556,18 +624,37 @@ bool s1ap::handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, if (flags & MSG_NOTIFICATION) { // Received notification union sctp_notification* notification = (union sctp_notification*)pdu->msg; - logger.debug("SCTP Notification %d", notification->sn_header.sn_type); + logger.info("SCTP Notification %04x", notification->sn_header.sn_type); + bool restart_s1 = false; if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { logger.info("SCTP Association Shutdown. Association: %d", sri.sinfo_assoc_id); srsran::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id); - rx_socket_handler->remove_socket(mme_socket.get_socket()); - mme_socket.close(); + restart_s1 = true; } else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE && notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) { logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id); srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id); + restart_s1 = true; + } else if (notification->sn_header.sn_type == SCTP_REMOTE_ERROR) { + logger.info("SCTP remote error. Association: %d", sri.sinfo_assoc_id); + srsran::console("SCTP remote error. Association: %d\n", sri.sinfo_assoc_id); + restart_s1 = true; + } else if (notification->sn_header.sn_type == SCTP_ASSOC_CHANGE) { + logger.info("SCTP association changed. Association: %d", sri.sinfo_assoc_id); + srsran::console("SCTP association changed. Association: %d\n", sri.sinfo_assoc_id); + } + if (restart_s1) { + logger.info("Restarting S1 connection"); + srsran::console("Restarting S1 connection\n"); rx_socket_handler->remove_socket(mme_socket.get_socket()); mme_socket.close(); + while (users.size() != 0) { + std::unordered_map >::iterator it = users.begin(); + uint16_t rnti = it->second->ctxt.rnti; + rrc->release_erabs(rnti); + rrc->release_ue(rnti); + users.erase(it->second.get()); + } } } else if (pdu->N_bytes == 0) { logger.error("SCTP return 0 bytes. Closing socket"); @@ -585,7 +672,10 @@ bool s1ap::handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, return false; } - handle_s1ap_rx_pdu(pdu.get()); + if ((flags & MSG_NOTIFICATION) == 0 && pdu->N_bytes != 0) { + handle_s1ap_rx_pdu(pdu.get()); + } + return true; } @@ -682,6 +772,13 @@ bool s1ap::handle_unsuccessfuloutcome(const unsuccessful_outcome_s& msg) bool s1ap::handle_s1setupresponse(const asn1::s1ap::s1_setup_resp_s& msg) { + if (s1setup_proc.is_idle()) { + asn1::s1ap::cause_c cause; + cause.set_protocol().value = cause_protocol_opts::msg_not_compatible_with_receiver_state; + send_error_indication(cause); + return false; + } + s1setupresponse = msg; mme_connected = true; s1_setup_proc_t::s1setupresult res; @@ -695,16 +792,15 @@ bool s1ap::handle_dlnastransport(const dl_nas_transport_s& msg) if (msg.ext) { logger.warning("Not handling S1AP message extension"); } - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } - if (msg.protocol_ies.ho_restrict_list_present) { + if (msg->ho_restrict_list_present) { logger.warning("Not handling HandoverRestrictionList"); } - if (msg.protocol_ies.subscriber_profile_idfor_rfp_present) { + if (msg->subscriber_profile_idfor_rfp_present) { logger.warning("Not handling SubscriberProfileIDforRFP"); } @@ -713,35 +809,41 @@ bool s1ap::handle_dlnastransport(const dl_nas_transport_s& msg) logger.error("Fatal Error: Couldn't allocate buffer in s1ap::run_thread()."); return false; } - memcpy(pdu->msg, msg.protocol_ies.nas_pdu.value.data(), msg.protocol_ies.nas_pdu.value.size()); - pdu->N_bytes = msg.protocol_ies.nas_pdu.value.size(); + memcpy(pdu->msg, msg->nas_pdu.value.data(), msg->nas_pdu.value.size()); + pdu->N_bytes = msg->nas_pdu.value.size(); rrc->write_dl_info(u->ctxt.rnti, std::move(pdu)); return true; } bool s1ap::handle_initialctxtsetuprequest(const init_context_setup_request_s& msg) { - const auto& prot_ies = msg.protocol_ies; WarnUnsupportFeature(msg.ext, "message extension"); - WarnUnsupportFeature(prot_ies.add_cs_fallback_ind_present, "AdditionalCSFallbackIndicator"); - WarnUnsupportFeature(prot_ies.csg_membership_status_present, "CSGMembershipStatus"); - WarnUnsupportFeature(prot_ies.gummei_id_present, "GUMMEI_ID"); - WarnUnsupportFeature(prot_ies.ho_restrict_list_present, "HandoverRestrictionList"); - WarnUnsupportFeature(prot_ies.management_based_mdt_allowed_present, "ManagementBasedMDTAllowed"); - WarnUnsupportFeature(prot_ies.management_based_mdtplmn_list_present, "ManagementBasedMDTPLMNList"); - WarnUnsupportFeature(prot_ies.mme_ue_s1ap_id_minus2_present, "MME_UE_S1AP_ID_2"); - WarnUnsupportFeature(prot_ies.registered_lai_present, "RegisteredLAI"); - WarnUnsupportFeature(prot_ies.srvcc_operation_possible_present, "SRVCCOperationPossible"); - WarnUnsupportFeature(prot_ies.subscriber_profile_idfor_rfp_present, "SubscriberProfileIDforRFP"); - WarnUnsupportFeature(prot_ies.trace_activation_present, "TraceActivation"); - WarnUnsupportFeature(prot_ies.ue_radio_cap_present, "UERadioCapability"); + WarnUnsupportFeature(msg->add_cs_fallback_ind_present, "AdditionalCSFallbackIndicator"); + WarnUnsupportFeature(msg->csg_membership_status_present, "CSGMembershipStatus"); + WarnUnsupportFeature(msg->gummei_id_present, "GUMMEI_ID"); + WarnUnsupportFeature(msg->ho_restrict_list_present, "HandoverRestrictionList"); + WarnUnsupportFeature(msg->management_based_mdt_allowed_present, "ManagementBasedMDTAllowed"); + WarnUnsupportFeature(msg->management_based_mdtplmn_list_present, "ManagementBasedMDTPLMNList"); + WarnUnsupportFeature(msg->mme_ue_s1ap_id_minus2_present, "MME_UE_S1AP_ID_2"); + WarnUnsupportFeature(msg->registered_lai_present, "RegisteredLAI"); + WarnUnsupportFeature(msg->srvcc_operation_possible_present, "SRVCCOperationPossible"); + WarnUnsupportFeature(msg->subscriber_profile_idfor_rfp_present, "SubscriberProfileIDforRFP"); + WarnUnsupportFeature(msg->trace_activation_present, "TraceActivation"); + WarnUnsupportFeature(msg->ue_radio_cap_present, "UERadioCapability"); - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } + if (u->get_state() == s1ap_proc_id_t::init_context_setup_request) { + logger.warning("Initial Context Setup Request already in progress. Ignoring ICS request."); + asn1::s1ap::cause_c cause; + cause.set_protocol().value = cause_protocol_opts::msg_not_compatible_with_receiver_state; + send_error_indication(cause, msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); + return false; + } + // Setup UE ctxt in RRC if (not rrc->setup_ue_ctxt(u->ctxt.rnti, msg)) { return false; @@ -750,10 +852,10 @@ bool s1ap::handle_initialctxtsetuprequest(const init_context_setup_request_s& ms // Update E-RABs erab_id_list updated_erabs; erab_item_list failed_cfg_erabs; - add_repeated_erab_ids(prot_ies.erab_to_be_setup_list_ctxt_su_req.value, failed_cfg_erabs); + add_repeated_erab_ids(msg->erab_to_be_setup_list_ctxt_su_req.value, failed_cfg_erabs); - for (const auto& item : msg.protocol_ies.erab_to_be_setup_list_ctxt_su_req.value) { - const auto& erab = item.value.erab_to_be_setup_item_ctxt_su_req(); + for (const auto& item : msg->erab_to_be_setup_list_ctxt_su_req.value) { + const auto& erab = item->erab_to_be_setup_item_ctxt_su_req(); if (contains_erab_id(failed_cfg_erabs, erab.erab_id)) { // E-RAB is duplicate continue; @@ -786,9 +888,9 @@ bool s1ap::handle_initialctxtsetuprequest(const init_context_setup_request_s& ms } /* Ideally the check below would be "if (users[rnti].is_csfb)" */ - if (msg.protocol_ies.cs_fallback_ind_present) { - if (msg.protocol_ies.cs_fallback_ind.value.value == cs_fallback_ind_opts::cs_fallback_required || - msg.protocol_ies.cs_fallback_ind.value.value == cs_fallback_ind_opts::cs_fallback_high_prio) { + if (msg->cs_fallback_ind_present) { + if (msg->cs_fallback_ind.value.value == cs_fallback_ind_opts::cs_fallback_required || + msg->cs_fallback_ind.value.value == cs_fallback_ind_opts::cs_fallback_high_prio) { // Send RRC Release (cs-fallback-triggered) to MME cause_c cause; cause.set_radio_network().value = cause_radio_network_opts::cs_fallback_triggered; @@ -808,8 +910,8 @@ bool s1ap::handle_paging(const asn1::s1ap::paging_s& msg) { WarnUnsupportFeature(msg.ext, "S1AP message extension"); - uint32_t ueid = msg.protocol_ies.ue_id_idx_value.value.to_number(); - rrc->add_paging_id(ueid, msg.protocol_ies.ue_paging_id.value); + uint32_t ueid = msg->ue_id_idx_value.value.to_number(); + rrc->add_paging_id(ueid, msg->ue_paging_id.value); return true; } @@ -817,22 +919,21 @@ bool s1ap::handle_erabsetuprequest(const erab_setup_request_s& msg) { WarnUnsupportFeature(msg.ext, "S1AP message extension"); - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } - if (msg.protocol_ies.ueaggregate_maximum_bitrate_present) { - rrc->set_aggregate_max_bitrate(u->ctxt.rnti, msg.protocol_ies.ueaggregate_maximum_bitrate.value); + if (msg->ueaggregate_maximum_bitrate_present) { + rrc->set_aggregate_max_bitrate(u->ctxt.rnti, msg->ueaggregate_maximum_bitrate.value); } erab_id_list updated_erabs; erab_item_list failed_cfg_erabs; - add_repeated_erab_ids(msg.protocol_ies.erab_to_be_setup_list_bearer_su_req.value, failed_cfg_erabs); + add_repeated_erab_ids(msg->erab_to_be_setup_list_bearer_su_req.value, failed_cfg_erabs); - for (const auto& item : msg.protocol_ies.erab_to_be_setup_list_bearer_su_req.value) { - const auto& erab = item.value.erab_to_be_setup_item_bearer_su_req(); + for (const auto& item : msg->erab_to_be_setup_list_bearer_su_req.value) { + const auto& erab = item->erab_to_be_setup_item_bearer_su_req(); if (contains_erab_id(failed_cfg_erabs, erab.erab_id)) { // E-RAB is duplicate continue; @@ -877,22 +978,21 @@ bool s1ap::handle_erabmodifyrequest(const erab_modify_request_s& msg) { WarnUnsupportFeature(msg.ext, "S1AP message extension"); - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } - if (msg.protocol_ies.ueaggregate_maximum_bitrate_present) { - rrc->set_aggregate_max_bitrate(u->ctxt.rnti, msg.protocol_ies.ueaggregate_maximum_bitrate.value); + if (msg->ueaggregate_maximum_bitrate_present) { + rrc->set_aggregate_max_bitrate(u->ctxt.rnti, msg->ueaggregate_maximum_bitrate.value); } erab_id_list updated_erabs; erab_item_list failed_cfg_erabs; - add_repeated_erab_ids(msg.protocol_ies.erab_to_be_modified_list_bearer_mod_req.value, failed_cfg_erabs); + add_repeated_erab_ids(msg->erab_to_be_modified_list_bearer_mod_req.value, failed_cfg_erabs); - for (const auto& item : msg.protocol_ies.erab_to_be_modified_list_bearer_mod_req.value) { - const auto& erab = item.value.erab_to_be_modified_item_bearer_mod_req(); + for (const auto& item : msg->erab_to_be_modified_list_bearer_mod_req.value) { + const auto& erab = item->erab_to_be_modified_item_bearer_mod_req(); if (contains_erab_id(failed_cfg_erabs, erab.erab_id)) { // E-RAB is duplicate continue; @@ -932,8 +1032,7 @@ bool s1ap::handle_erabreleasecommand(const erab_release_cmd_s& msg) { WarnUnsupportFeature(msg.ext, "S1AP message extension"); - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -947,8 +1046,8 @@ bool s1ap::handle_erabreleasecommand(const erab_release_cmd_s& msg) return e.erab_id == erab_id; })); }; - for (const auto& item : msg.protocol_ies.erab_to_be_released_list.value) { - const auto& erab = item.value.erab_item(); + for (const auto& item : msg->erab_to_be_released_list.value) { + const auto& erab = item->erab_item(); if (is_repeated_erab_id(erab.erab_id)) { // TS 36.413, 8.2.3.3 - ignore the duplication of E-RAB ID IEs @@ -966,7 +1065,7 @@ bool s1ap::handle_erabreleasecommand(const erab_release_cmd_s& msg) // Notify RRC of E-RAB update. (RRC reconf message is going to be sent. if (not updated_erabs.empty()) { - rrc->notify_ue_erab_updates(u->ctxt.rnti, msg.protocol_ies.nas_pdu.value); + rrc->notify_ue_erab_updates(u->ctxt.rnti, msg->nas_pdu.value); } // Send E-RAB release response back to the MME @@ -982,13 +1081,12 @@ bool s1ap::handle_erabreleasecommand(const erab_release_cmd_s& msg) bool s1ap::handle_uecontextmodifyrequest(const ue_context_mod_request_s& msg) { WarnUnsupportFeature(msg.ext, "S1AP message extension"); - WarnUnsupportFeature(msg.protocol_ies.add_cs_fallback_ind_present, "AdditionalCSFallbackIndicator"); - WarnUnsupportFeature(msg.protocol_ies.csg_membership_status_present, "CSGMembershipStatus"); - WarnUnsupportFeature(msg.protocol_ies.registered_lai_present, "RegisteredLAI"); - WarnUnsupportFeature(msg.protocol_ies.subscriber_profile_idfor_rfp_present, "SubscriberProfileIDforRFP"); + WarnUnsupportFeature(msg->add_cs_fallback_ind_present, "AdditionalCSFallbackIndicator"); + WarnUnsupportFeature(msg->csg_membership_status_present, "CSGMembershipStatus"); + WarnUnsupportFeature(msg->registered_lai_present, "RegisteredLAI"); + WarnUnsupportFeature(msg->subscriber_profile_idfor_rfp_present, "SubscriberProfileIDforRFP"); - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } @@ -1004,9 +1102,9 @@ bool s1ap::handle_uecontextmodifyrequest(const ue_context_mod_request_s& msg) u->send_uectxtmodifyresp(); /* Ideally the check below would be "if (users[rnti].is_csfb)" */ - if (msg.protocol_ies.cs_fallback_ind_present) { - if (msg.protocol_ies.cs_fallback_ind.value.value == cs_fallback_ind_opts::cs_fallback_required || - msg.protocol_ies.cs_fallback_ind.value.value == cs_fallback_ind_opts::cs_fallback_high_prio) { + if (msg->cs_fallback_ind_present) { + if (msg->cs_fallback_ind.value.value == cs_fallback_ind_opts::cs_fallback_required || + msg->cs_fallback_ind.value.value == cs_fallback_ind_opts::cs_fallback_high_prio) { // Send RRC Release (cs-fallback-triggered) to MME cause_c cause; cause.set_radio_network().value = cause_radio_network_opts::cs_fallback_triggered; @@ -1023,8 +1121,8 @@ bool s1ap::handle_uectxtreleasecommand(const ue_context_release_cmd_s& msg) WarnUnsupportFeature(msg.ext, "S1AP message extension"); ue* u = nullptr; - if (msg.protocol_ies.ue_s1ap_ids.value.type().value == ue_s1ap_ids_c::types_opts::ue_s1ap_id_pair) { - const auto& idpair = msg.protocol_ies.ue_s1ap_ids.value.ue_s1ap_id_pair(); + if (msg->ue_s1ap_ids.value.type().value == ue_s1ap_ids_c::types_opts::ue_s1ap_id_pair) { + const auto& idpair = msg->ue_s1ap_ids.value.ue_s1ap_id_pair(); if (idpair.ext) { logger.warning("Not handling S1AP message extension"); @@ -1037,7 +1135,7 @@ bool s1ap::handle_uectxtreleasecommand(const ue_context_release_cmd_s& msg) return false; } } else { - uint32_t mme_ue_id = msg.protocol_ies.ue_s1ap_ids.value.mme_ue_s1ap_id(); + uint32_t mme_ue_id = msg->ue_s1ap_ids.value.mme_ue_s1ap_id(); u = users.find_ue_mmeid(mme_ue_id); if (u == nullptr) { logger.warning("UE for mme_ue_s1ap_id:%d not found - discarding message", mme_ue_id); @@ -1056,7 +1154,18 @@ bool s1ap::handle_uectxtreleasecommand(const ue_context_release_cmd_s& msg) bool s1ap::handle_s1setupfailure(const asn1::s1ap::s1_setup_fail_s& msg) { - std::string cause = get_cause(msg.protocol_ies.cause.value); + if (s1setup_proc.is_idle()) { + asn1::s1ap::cause_c cause; + cause.set_protocol().value = cause_protocol_opts::msg_not_compatible_with_receiver_state; + send_error_indication(cause); + return false; + } + + s1_setup_proc_t::s1setupresult res; + res.success = false; + s1setup_proc.trigger(res); + + std::string cause = get_cause(msg->cause.value); logger.error("S1 Setup Failure. Cause: %s", cause.c_str()); srsran::console("S1 Setup Failure. Cause: %s\n", cause.c_str()); return true; @@ -1064,22 +1173,35 @@ bool s1ap::handle_s1setupfailure(const asn1::s1ap::s1_setup_fail_s& msg) bool s1ap::handle_handover_preparation_failure(const ho_prep_fail_s& msg) { - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } + + if (u->ho_prep_proc.is_idle()) { + asn1::s1ap::cause_c cause; + cause.set_protocol().value = cause_protocol_opts::msg_not_compatible_with_receiver_state; + send_error_indication(cause); + return false; + } + u->ho_prep_proc.trigger(msg); return true; } bool s1ap::handle_handover_command(const asn1::s1ap::ho_cmd_s& msg) { - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } + + if (u->ho_prep_proc.is_idle()) { + asn1::s1ap::cause_c cause; + cause.set_protocol().value = cause_protocol_opts::msg_not_compatible_with_receiver_state; + send_error_indication(cause); + return false; + } u->ho_prep_proc.trigger(msg); return true; } @@ -1091,15 +1213,15 @@ bool s1ap::handle_handover_command(const asn1::s1ap::ho_cmd_s& msg) bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg) { uint16_t rnti = SRSRAN_INVALID_RNTI; - uint32_t mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value; + uint32_t mme_ue_s1ap_id = msg->mme_ue_s1ap_id.value.value; asn1::s1ap::cause_c cause; cause.set_misc().value = cause_misc_opts::unspecified; - if (msg.ext or msg.protocol_ies.ho_restrict_list_present) { + if (msg.ext or msg->ho_restrict_list_present) { logger.warning("Not handling S1AP Handover Request extensions or Handover Restriction List"); } - if (msg.protocol_ies.handov_type.value.value != handov_type_opts::intralte) { + if (msg->handov_type.value.value != handov_type_opts::intralte) { logger.error("Not handling S1AP non-intra LTE handovers"); cause.set_radio_network().value = cause_radio_network_opts::interrat_redirection; send_ho_failure(mme_ue_s1ap_id, cause); @@ -1107,9 +1229,9 @@ bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg) } // Confirm the UE does not exist in TeNB - if (users.find_ue_mmeid(msg.protocol_ies.mme_ue_s1ap_id.value.value) != nullptr) { + if (users.find_ue_mmeid(msg->mme_ue_s1ap_id.value.value) != nullptr) { logger.error("The provided MME_UE_S1AP_ID=%" PRIu64 " is already connected to the cell", - msg.protocol_ies.mme_ue_s1ap_id.value.value); + msg->mme_ue_s1ap_id.value.value); cause.set_radio_network().value = cause_radio_network_opts::unknown_mme_ue_s1ap_id; send_ho_failure(mme_ue_s1ap_id, cause); return false; @@ -1117,13 +1239,13 @@ bool s1ap::handle_handover_request(const asn1::s1ap::ho_request_s& msg) // Create user ctxt object and associated MME context std::unique_ptr ue_ptr{new ue{this}}; - ue_ptr->ctxt.mme_ue_s1ap_id = msg.protocol_ies.mme_ue_s1ap_id.value.value; + ue_ptr->ctxt.mme_ue_s1ap_id = msg->mme_ue_s1ap_id.value.value; srsran_assert(users.add_user(std::move(ue_ptr)) != nullptr, "Unexpected failure to create S1AP UE"); // Unpack Transparent Container sourceenb_to_targetenb_transparent_container_s container; - asn1::cbit_ref bref{msg.protocol_ies.source_to_target_transparent_container.value.data(), - msg.protocol_ies.source_to_target_transparent_container.value.size()}; + asn1::cbit_ref bref{msg->source_to_target_transparent_container.value.data(), + msg->source_to_target_transparent_container.value.size()}; if (container.unpack(bref) != asn1::SRSASN_SUCCESS) { logger.warning("Failed to unpack SourceToTargetTransparentContainer"); cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error; @@ -1150,10 +1272,10 @@ void s1ap::send_ho_failure(uint32_t mme_ue_s1ap_id, const asn1::s1ap::cause_c& c s1ap_pdu_c tx_pdu; tx_pdu.set_unsuccessful_outcome().load_info_obj(ASN1_S1AP_ID_HO_RES_ALLOC); - ho_fail_ies_container& container = tx_pdu.unsuccessful_outcome().value.ho_fail().protocol_ies; + ho_fail_s& container = tx_pdu.unsuccessful_outcome().value.ho_fail(); - container.mme_ue_s1ap_id.value = mme_ue_s1ap_id; - container.cause.value = cause; + container->mme_ue_s1ap_id.value = mme_ue_s1ap_id; + container->cause.value = cause; sctp_send_s1ap_pdu(tx_pdu, SRSRAN_INVALID_RNTI, "HandoverFailure"); } @@ -1167,24 +1289,24 @@ bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, { s1ap_pdu_c tx_pdu; tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_HO_RES_ALLOC); - ho_request_ack_ies_container& container = tx_pdu.successful_outcome().value.ho_request_ack().protocol_ies; + ho_request_ack_s& container = tx_pdu.successful_outcome().value.ho_request_ack(); - ue* ue_ptr = users.find_ue_mmeid(msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* ue_ptr = users.find_ue_mmeid(msg->mme_ue_s1ap_id.value.value); if (ue_ptr == nullptr) { - logger.error("The MME-S1AP-UE-ID=%ld is not valid", msg.protocol_ies.mme_ue_s1ap_id.value.value); + logger.error("The MME-S1AP-UE-ID=%ld is not valid", msg->mme_ue_s1ap_id.value.value); return false; } ue_ptr->ctxt.rnti = rnti; ue_ptr->ctxt.enb_cc_idx = enb_cc_idx; - container.mme_ue_s1ap_id.value = msg.protocol_ies.mme_ue_s1ap_id.value.value; - container.enb_ue_s1ap_id.value = ue_ptr->ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = msg->mme_ue_s1ap_id.value.value; + container->enb_ue_s1ap_id.value = ue_ptr->ctxt.enb_ue_s1ap_id; // Add admitted E-RABs - container.erab_admitted_list.value.resize(admitted_bearers.size()); + container->erab_admitted_list.value.resize(admitted_bearers.size()); for (size_t i = 0; i < admitted_bearers.size(); ++i) { - container.erab_admitted_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ADMITTED_ITEM); - auto& c = container.erab_admitted_list.value[i].value.erab_admitted_item(); + container->erab_admitted_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ADMITTED_ITEM); + auto& c = container->erab_admitted_list.value[i]->erab_admitted_item(); c = admitted_bearers[i]; if (!args.gtp_advertise_addr.empty()) { c.transport_layer_address = addr_to_asn1(args.gtp_advertise_addr.c_str()); @@ -1205,12 +1327,12 @@ bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, // Add failed to Setup E-RABs if (not not_admitted_bearers.empty()) { - container.erab_failed_to_setup_list_ho_req_ack_present = true; - container.erab_failed_to_setup_list_ho_req_ack.value.resize(not_admitted_bearers.size()); + container->erab_failed_to_setup_list_ho_req_ack_present = true; + container->erab_failed_to_setup_list_ho_req_ack.value.resize(not_admitted_bearers.size()); for (size_t i = 0; i < not_admitted_bearers.size(); ++i) { - container.erab_failed_to_setup_list_ho_req_ack.value[i].load_info_obj( + container->erab_failed_to_setup_list_ho_req_ack.value[i].load_info_obj( ASN1_S1AP_ID_ERAB_FAILEDTO_SETUP_ITEM_HO_REQ_ACK); - auto& erab = container.erab_failed_to_setup_list_ho_req_ack.value[i].value.erab_failedto_setup_item_ho_req_ack(); + auto& erab = container->erab_failed_to_setup_list_ho_req_ack.value[i]->erab_failedto_setup_item_ho_req_ack(); erab.erab_id = not_admitted_bearers[i].erab_id; erab.cause = not_admitted_bearers[i].cause; } @@ -1227,23 +1349,21 @@ bool s1ap::send_ho_req_ack(const asn1::s1ap::ho_request_s& msg, logger.error("Failed to pack TargeteNBToSourceeNBTransparentContainer"); return false; } - container.target_to_source_transparent_container.value.resize(bref.distance_bytes()); - memcpy(container.target_to_source_transparent_container.value.data(), pdu->msg, bref.distance_bytes()); + container->target_to_source_transparent_container.value.resize(bref.distance_bytes()); + memcpy(container->target_to_source_transparent_container.value.data(), pdu->msg, bref.distance_bytes()); return sctp_send_s1ap_pdu(tx_pdu, rnti, "HandoverRequestAcknowledge"); } bool s1ap::handle_mme_status_transfer(const asn1::s1ap::mme_status_transfer_s& msg) { - ue* u = - handle_s1apmsg_ue_id(msg.protocol_ies.enb_ue_s1ap_id.value.value, msg.protocol_ies.mme_ue_s1ap_id.value.value); + ue* u = handle_s1apmsg_ue_id(msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); if (u == nullptr) { return false; } - rrc->set_erab_status( - u->ctxt.rnti, - msg.protocol_ies.enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list); + rrc->set_erab_status(u->ctxt.rnti, + msg->enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list); return true; } @@ -1257,14 +1377,14 @@ void s1ap::send_ho_notify(uint16_t rnti, uint64_t target_eci) s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_NOTIF); - ho_notify_ies_container& container = tx_pdu.init_msg().value.ho_notify().protocol_ies; + ho_notify_s& container = tx_pdu.init_msg().value.ho_notify(); - container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id.value(); - container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; - container.eutran_cgi.value = eutran_cgi; - container.eutran_cgi.value.cell_id.from_number(target_eci); - container.tai.value = tai; + container->eutran_cgi.value = eutran_cgi; + container->eutran_cgi.value.cell_id.from_number(target_eci); + container->tai.value = tai; sctp_send_s1ap_pdu(tx_pdu, rnti, "HandoverNotify"); } @@ -1277,16 +1397,7 @@ void s1ap::send_ho_cancel(uint16_t rnti, const asn1::s1ap::cause_c& cause) return; } - s1ap_pdu_c tx_pdu; - - tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_CANCEL); - ho_cancel_ies_container& container = tx_pdu.init_msg().value.ho_cancel().protocol_ies; - - container.mme_ue_s1ap_id.value = user_ptr->ctxt.mme_ue_s1ap_id.value(); - container.enb_ue_s1ap_id.value = user_ptr->ctxt.enb_ue_s1ap_id; - container.cause.value = cause; - - sctp_send_s1ap_pdu(tx_pdu, rnti, "HandoverCancel"); + user_ptr->send_ho_cancel(cause); } bool s1ap::release_erabs(uint16_t rnti, const std::vector& erabs_successfully_released) @@ -1317,24 +1428,24 @@ bool s1ap::send_error_indication(const asn1::s1ap::cause_c& cause, s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ERROR_IND); - auto& container = tx_pdu.init_msg().value.error_ind().protocol_ies; + auto& container = tx_pdu.init_msg().value.error_ind(); - uint16_t rnti = SRSRAN_INVALID_RNTI; - container.enb_ue_s1ap_id_present = enb_ue_s1ap_id.has_value(); + uint16_t rnti = SRSRAN_INVALID_RNTI; + container->enb_ue_s1ap_id_present = enb_ue_s1ap_id.has_value(); if (enb_ue_s1ap_id.has_value()) { - container.enb_ue_s1ap_id.value = enb_ue_s1ap_id.value(); - ue* user_ptr = users.find_ue_enbid(enb_ue_s1ap_id.value()); - rnti = user_ptr != nullptr ? user_ptr->ctxt.rnti : SRSRAN_INVALID_RNTI; + container->enb_ue_s1ap_id.value = enb_ue_s1ap_id.value(); + ue* user_ptr = users.find_ue_enbid(enb_ue_s1ap_id.value()); + rnti = user_ptr != nullptr ? user_ptr->ctxt.rnti : SRSRAN_INVALID_RNTI; } - container.mme_ue_s1ap_id_present = mme_ue_s1ap_id.has_value(); + container->mme_ue_s1ap_id_present = mme_ue_s1ap_id.has_value(); if (mme_ue_s1ap_id.has_value()) { - container.mme_ue_s1ap_id.value = mme_ue_s1ap_id.value(); + container->mme_ue_s1ap_id.value = mme_ue_s1ap_id.value(); } - container.s_tmsi_present = false; + container->s_tmsi_present = false; - container.cause_present = true; - container.cause.value = cause; + container->cause_present = true; + container->cause.value = cause; return sctp_send_s1ap_pdu(tx_pdu, rnti, "Error Indication"); } @@ -1355,30 +1466,30 @@ bool s1ap::ue::send_initialuemessage(asn1::s1ap::rrc_establishment_cause_e cause s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_INIT_UE_MSG); - init_ue_msg_ies_container& container = tx_pdu.init_msg().value.init_ue_msg().protocol_ies; + init_ue_msg_s& container = tx_pdu.init_msg().value.init_ue_msg(); // S_TMSI if (has_tmsi) { - container.s_tmsi_present = true; - uint32_to_uint8(m_tmsi, container.s_tmsi.value.m_tmsi.data()); - container.s_tmsi.value.mmec[0] = mmec; + container->s_tmsi_present = true; + uint32_to_uint8(m_tmsi, container->s_tmsi.value.m_tmsi.data()); + container->s_tmsi.value.mmec[0] = mmec; } // ENB_UE_S1AP_ID - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; // NAS_PDU - container.nas_pdu.value.resize(pdu->N_bytes); - memcpy(container.nas_pdu.value.data(), pdu->msg, pdu->N_bytes); + container->nas_pdu.value.resize(pdu->N_bytes); + memcpy(container->nas_pdu.value.data(), pdu->msg, pdu->N_bytes); // TAI - container.tai.value = s1ap_ptr->tai; + container->tai.value = s1ap_ptr->tai; // EUTRAN_CGI - container.eutran_cgi.value = s1ap_ptr->eutran_cgi; + container->eutran_cgi.value = s1ap_ptr->eutran_cgi; // RRC Establishment Cause - container.rrc_establishment_cause.value = cause; + container->rrc_establishment_cause.value = cause; return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialUEMessage"); } @@ -1392,19 +1503,19 @@ bool s1ap::ue::send_ulnastransport(srsran::unique_byte_buffer_t pdu) s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UL_NAS_TRANSPORT); - asn1::s1ap::ul_nas_transport_ies_container& container = tx_pdu.init_msg().value.ul_nas_transport().protocol_ies; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + ul_nas_transport_s& container = tx_pdu.init_msg().value.ul_nas_transport(); + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; // NAS PDU - container.nas_pdu.value.resize(pdu->N_bytes); - memcpy(container.nas_pdu.value.data(), pdu->msg, pdu->N_bytes); + container->nas_pdu.value.resize(pdu->N_bytes); + memcpy(container->nas_pdu.value.data(), pdu->msg, pdu->N_bytes); // EUTRAN CGI - container.eutran_cgi.value = s1ap_ptr->eutran_cgi; + container->eutran_cgi.value = s1ap_ptr->eutran_cgi; // TAI - container.tai.value = s1ap_ptr->tai; + container->tai.value = s1ap_ptr->tai; return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UplinkNASTransport"); } @@ -1413,30 +1524,59 @@ bool s1ap::ue::send_uectxtreleaserequest(const cause_c& cause) { if (not ctxt.mme_ue_s1ap_id.has_value()) { logger.error("Cannot send UE context release request without a MME-UE-S1AP-Id allocated."); + s1ap_ptr->rrc->release_ue(ctxt.rnti); + s1ap_ptr->users.erase(this); + return false; + } + + if (ts1_reloc_overall.is_running() and cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network and + (cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::user_inactivity or + cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::radio_conn_with_ue_lost)) { + logger.info("Ignoring UE context release request from lower layers for UE rnti=0x%x performing S1 Handover.", + ctxt.rnti); + // Leave the UE context alive during S1 Handover until ts1_reloc_overall expiry. Ignore releases due to + // UE inactivity or RLF + return false; + } + + if (was_uectxtrelease_requested()) { + // let timeout auto-remove user. return false; } - release_requested = true; s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE_REQUEST); - ue_context_release_request_ies_container& container = - tx_pdu.init_msg().value.ue_context_release_request().protocol_ies; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + ue_context_release_request_s& container = tx_pdu.init_msg().value.ue_context_release_request(); + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; // Cause - container.cause.value = cause; + container->cause.value = cause; - return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseRequest"); + release_requested = s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseRequest"); + if (not release_requested) { + s1ap_ptr->rrc->release_ue(ctxt.rnti); + s1ap_ptr->users.erase(this); + } else { + overall_procedure_timeout.set(10000, [this](uint32_t tid) { + logger.warning("UE context for RNTI:0x%x is in zombie state. Releasing...", ctxt.rnti); + s1ap_ptr->rrc->release_ue(ctxt.rnti); + s1ap_ptr->users.erase(this); + }); + overall_procedure_timeout.run(); + } + return release_requested; } bool s1ap::ue::send_uectxtreleasecomplete() { + overall_procedure_timeout.stop(); + s1ap_pdu_c tx_pdu; tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE); - auto& container = tx_pdu.successful_outcome().value.ue_context_release_complete().protocol_ies; - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + auto& container = tx_pdu.successful_outcome().value.ue_context_release_complete(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); // Stop TS1 Reloc Overall ts1_reloc_overall.stop(); @@ -1447,6 +1587,17 @@ bool s1ap::ue::send_uectxtreleasecomplete() return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete"); } +void s1ap::ue::notify_rrc_reconf_complete() +{ + if (current_state == s1ap_elem_procs_o::init_msg_c::types_opts::init_context_setup_request) { + logger.info("Procedure %s,rnti=0x%x - Received RRC reconf complete. Finishing UE context setup.", + s1ap_elem_procs_o::init_msg_c::types_opts{current_state}.to_string(), + ctxt.rnti); + ue_ctxt_setup_complete(); + return; + } +} + void s1ap::ue::ue_ctxt_setup_complete() { if (current_state != s1ap_elem_procs_o::init_msg_c::types_opts::init_context_setup_request) { @@ -1461,34 +1612,41 @@ void s1ap::ue::ue_ctxt_setup_complete() if (updated_erabs.empty()) { // It is ICS Failure tx_pdu.set_unsuccessful_outcome().load_info_obj(ASN1_S1AP_ID_INIT_CONTEXT_SETUP); - auto& container = tx_pdu.unsuccessful_outcome().value.init_context_setup_fail().protocol_ies; + auto& container = tx_pdu.unsuccessful_outcome().value.init_context_setup_fail(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - container.cause.value = failed_cfg_erabs.front().cause; + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + if (not failed_cfg_erabs.empty()) { + container->cause.value = failed_cfg_erabs.front().cause; + } else { + logger.warning("Procedure %s,rnti=0x%x - no specified cause for failed configuration", + s1ap_elem_procs_o::init_msg_c::types_opts{current_state}.to_string(), + ctxt.rnti); + container->cause.value.set_misc().value = cause_misc_opts::unspecified; + } s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationFailure"); return; } // It is ICS Response tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_INIT_CONTEXT_SETUP); - auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies; + auto& container = tx_pdu.successful_outcome().value.init_context_setup_resp(); // Fill in the MME and eNB IDs - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; // Add list of E-RABs that were not setup if (not failed_cfg_erabs.empty()) { - container.erab_failed_to_setup_list_ctxt_su_res_present = true; - fill_erab_failed_setup_list(container.erab_failed_to_setup_list_ctxt_su_res.value, failed_cfg_erabs); + container->erab_failed_to_setup_list_ctxt_su_res_present = true; + fill_erab_failed_setup_list(container->erab_failed_to_setup_list_ctxt_su_res.value, failed_cfg_erabs); } // Add setup E-RABs - container.erab_setup_list_ctxt_su_res.value.resize(updated_erabs.size()); + container->erab_setup_list_ctxt_su_res.value.resize(updated_erabs.size()); for (size_t i = 0; i < updated_erabs.size(); ++i) { - container.erab_setup_list_ctxt_su_res.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_SETUP_ITEM_CTXT_SU_RES); - auto& item = container.erab_setup_list_ctxt_su_res.value[i].value.erab_setup_item_ctxt_su_res(); + container->erab_setup_list_ctxt_su_res.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_SETUP_ITEM_CTXT_SU_RES); + auto& item = container->erab_setup_list_ctxt_su_res.value[i]->erab_setup_item_ctxt_su_res(); item.erab_id = updated_erabs[i]; get_erab_addr(item.erab_id, item.transport_layer_address, item.gtp_teid); } @@ -1496,7 +1654,7 @@ void s1ap::ue::ue_ctxt_setup_complete() // Log event. event_logger::get().log_s1_ctx_create(ctxt.enb_cc_idx, ctxt.mme_ue_s1ap_id.value(), ctxt.enb_ue_s1ap_id, ctxt.rnti); - s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABSetupResponse"); + s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupResponse"); } bool s1ap::ue::send_erab_setup_response(const erab_id_list& erabs_setup, const erab_item_list& erabs_failed) @@ -1506,21 +1664,21 @@ bool s1ap::ue::send_erab_setup_response(const erab_id_list& erabs_setup, const e erab_setup_resp_s& res = tx_pdu.successful_outcome().value.erab_setup_resp(); // Fill in the MME and eNB IDs - res.protocol_ies.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - res.protocol_ies.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + res->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + res->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; // Add list of E-RABs that were not setup if (not erabs_failed.empty()) { - res.protocol_ies.erab_failed_to_setup_list_bearer_su_res_present = true; - fill_erab_failed_setup_list(res.protocol_ies.erab_failed_to_setup_list_bearer_su_res.value, erabs_failed); + res->erab_failed_to_setup_list_bearer_su_res_present = true; + fill_erab_failed_setup_list(res->erab_failed_to_setup_list_bearer_su_res.value, erabs_failed); } if (not erabs_setup.empty()) { - res.protocol_ies.erab_setup_list_bearer_su_res_present = true; - res.protocol_ies.erab_setup_list_bearer_su_res.value.resize(erabs_setup.size()); + res->erab_setup_list_bearer_su_res_present = true; + res->erab_setup_list_bearer_su_res.value.resize(erabs_setup.size()); for (size_t i = 0; i < erabs_setup.size(); ++i) { - res.protocol_ies.erab_setup_list_bearer_su_res.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_SETUP_ITEM_BEARER_SU_RES); - auto& item = res.protocol_ies.erab_setup_list_bearer_su_res.value[i].value.erab_setup_item_bearer_su_res(); + res->erab_setup_list_bearer_su_res.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_SETUP_ITEM_BEARER_SU_RES); + auto& item = res->erab_setup_list_bearer_su_res.value[i]->erab_setup_item_bearer_su_res(); item.erab_id = erabs_setup[i]; get_erab_addr(item.erab_id, item.transport_layer_address, item.gtp_teid); } @@ -1537,10 +1695,10 @@ bool s1ap::ue::send_uectxtmodifyresp() s1ap_pdu_c tx_pdu; tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_MOD); - auto& container = tx_pdu.successful_outcome().value.ue_context_mod_resp().protocol_ies; + auto& container = tx_pdu.successful_outcome().value.ue_context_mod_resp(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationResponse"); } @@ -1553,11 +1711,11 @@ bool s1ap::ue::send_uectxtmodifyfailure(const cause_c& cause) s1ap_pdu_c tx_pdu; tx_pdu.set_unsuccessful_outcome().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_MOD); - auto& container = tx_pdu.unsuccessful_outcome().value.ue_context_mod_fail().protocol_ies; + auto& container = tx_pdu.unsuccessful_outcome().value.ue_context_mod_fail(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - container.cause.value = cause; + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->cause.value = cause; return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UEContextModificationFailure"); } @@ -1574,26 +1732,26 @@ bool s1ap::ue::send_erab_release_response(const erab_id_list& erabs_released, co asn1::s1ap::s1ap_pdu_c tx_pdu; tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_ERAB_RELEASE); - auto& container = tx_pdu.successful_outcome().value.erab_release_resp().protocol_ies; - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + auto& container = tx_pdu.successful_outcome().value.erab_release_resp(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); // Fill in which E-RABs were successfully released if (not erabs_released.empty()) { - container.erab_release_list_bearer_rel_comp_present = true; - container.erab_release_list_bearer_rel_comp.value.resize(erabs_released.size()); + container->erab_release_list_bearer_rel_comp_present = true; + container->erab_release_list_bearer_rel_comp.value.resize(erabs_released.size()); for (size_t i = 0; i < erabs_released.size(); ++i) { - container.erab_release_list_bearer_rel_comp.value[i].load_info_obj( + container->erab_release_list_bearer_rel_comp.value[i].load_info_obj( ASN1_S1AP_ID_ERAB_RELEASE_ITEM_BEARER_REL_COMP); - container.erab_release_list_bearer_rel_comp.value[i].value.erab_release_item_bearer_rel_comp().erab_id = + container->erab_release_list_bearer_rel_comp.value[i]->erab_release_item_bearer_rel_comp().erab_id = erabs_released[i]; } } // Fill in which E-RABs were *not* successfully released if (not erabs_failed.empty()) { - container.erab_failed_to_release_list_present = true; - fill_erab_failed_setup_list(container.erab_failed_to_release_list.value, erabs_failed); + container->erab_failed_to_release_list_present = true; + fill_erab_failed_setup_list(container->erab_failed_to_release_list.value, erabs_failed); } return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABReleaseResponse"); @@ -1604,25 +1762,25 @@ bool s1ap::ue::send_erab_modify_response(const erab_id_list& erabs_modified, con asn1::s1ap::s1ap_pdu_c tx_pdu; tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_ERAB_MODIFY); - auto& container = tx_pdu.successful_outcome().value.erab_modify_resp().protocol_ies; - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + auto& container = tx_pdu.successful_outcome().value.erab_modify_resp(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); // Fill in which E-RABs were successfully released if (not erabs_modified.empty()) { - container.erab_modify_list_bearer_mod_res_present = true; - container.erab_modify_list_bearer_mod_res.value.resize(erabs_modified.size()); - for (uint32_t i = 0; i < container.erab_modify_list_bearer_mod_res.value.size(); i++) { - container.erab_modify_list_bearer_mod_res.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_MODIFY_ITEM_BEARER_MOD_RES); - container.erab_modify_list_bearer_mod_res.value[i].value.erab_modify_item_bearer_mod_res().erab_id = + container->erab_modify_list_bearer_mod_res_present = true; + container->erab_modify_list_bearer_mod_res.value.resize(erabs_modified.size()); + for (uint32_t i = 0; i < container->erab_modify_list_bearer_mod_res.value.size(); i++) { + container->erab_modify_list_bearer_mod_res.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_MODIFY_ITEM_BEARER_MOD_RES); + container->erab_modify_list_bearer_mod_res.value[i]->erab_modify_item_bearer_mod_res().erab_id = erabs_modified[i]; } } // Fill in which E-RABs were *not* successfully released if (not erabs_failed.empty()) { - container.erab_failed_to_modify_list_present = true; - fill_erab_failed_setup_list(container.erab_failed_to_modify_list.value, erabs_failed); + container->erab_failed_to_modify_list_present = true; + fill_erab_failed_setup_list(container->erab_failed_to_modify_list.value, erabs_failed); } return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABModifyResponse"); @@ -1637,16 +1795,16 @@ bool s1ap::ue::send_erab_release_indication(const std::vector& erabs_s asn1::s1ap::s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ERAB_RELEASE_IND); - erab_release_ind_ies_container& container = tx_pdu.init_msg().value.erab_release_ind().protocol_ies; + erab_release_ind_s& container = tx_pdu.init_msg().value.erab_release_ind(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); // Fill in which E-RABs were successfully released - container.erab_released_list.value.resize(erabs_successfully_released.size()); - for (size_t i = 0; i < container.erab_released_list.value.size(); ++i) { - container.erab_released_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM); - container.erab_released_list.value[i].value.erab_item().erab_id = erabs_successfully_released[i]; + container->erab_released_list.value.resize(erabs_successfully_released.size()); + for (size_t i = 0; i < container->erab_released_list.value.size(); ++i) { + container->erab_released_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM); + container->erab_released_list.value[i]->erab_item().erab_id = erabs_successfully_released[i]; } return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "E-RABReleaseIndication"); @@ -1656,17 +1814,35 @@ bool s1ap::ue::send_ue_cap_info_indication(srsran::unique_byte_buffer_t ue_radio { asn1::s1ap::s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UE_CAP_INFO_IND); - ue_cap_info_ind_ies_container& container = tx_pdu.init_msg().value.ue_cap_info_ind().protocol_ies; + ue_cap_info_ind_s& container = tx_pdu.init_msg().value.ue_cap_info_ind(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - container.ue_radio_cap.value.resize(ue_radio_cap->N_bytes); - memcpy(container.ue_radio_cap.value.data(), ue_radio_cap->msg, ue_radio_cap->N_bytes); + container->ue_radio_cap.value.resize(ue_radio_cap->N_bytes); + memcpy(container->ue_radio_cap.value.data(), ue_radio_cap->msg, ue_radio_cap->N_bytes); return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "UECapabilityInfoIndication"); } +void s1ap::ue::send_ho_cancel(const asn1::s1ap::cause_c& cause) +{ + // Stop handover timers + ts1_reloc_prep.stop(); + ts1_reloc_overall.stop(); + + // Send S1AP Handover Cancel + s1ap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_CANCEL); + ho_cancel_s& container = tx_pdu.init_msg().value.ho_cancel(); + + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->cause.value = cause; + + s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "HandoverCancel"); +} + void s1ap::ue::set_state(s1ap_proc_id_t next_state, const erab_id_list& erabs_updated, const erab_item_list& erabs_failed_to_modify) @@ -1685,9 +1861,17 @@ void s1ap::ue::get_erab_addr(uint16_t erab_id, transp_addr_t& transp_addr, asn1: transp_addr.resize(32); uint8_t addr[4]; if (!s1ap_ptr->args.gtp_advertise_addr.empty()) { - inet_pton(AF_INET, s1ap_ptr->args.gtp_advertise_addr.c_str(), addr); + if (inet_pton(AF_INET, s1ap_ptr->args.gtp_advertise_addr.c_str(), addr) != 1) { + logger.error("Invalid gtp_advertise_addr: %s", s1ap_ptr->args.gtp_advertise_addr.c_str()); + srsran::console("Invalid gtp_advertise_addr: %s\n", s1ap_ptr->args.gtp_advertise_addr.c_str()); + perror("inet_pton"); + } } else { - inet_pton(AF_INET, s1ap_ptr->args.gtp_bind_addr.c_str(), addr); + if (inet_pton(AF_INET, s1ap_ptr->args.gtp_bind_addr.c_str(), addr) != 1) { + logger.error("Invalid gtp_bind_addr: %s", s1ap_ptr->args.gtp_bind_addr.c_str()); + srsran::console("Invalid gtp_bind_addr: %s\n", s1ap_ptr->args.gtp_bind_addr.c_str()); + perror("inet_pton"); + } } for (uint32_t j = 0; j < 4; ++j) { transp_addr.data()[j] = addr[3 - j]; @@ -1701,6 +1885,7 @@ void s1ap::ue::get_erab_addr(uint16_t erab_id, transp_addr_t& transp_addr, asn1: bool s1ap::send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -1715,7 +1900,8 @@ bool s1ap::send_ho_required(uint16_t rnti, } // launch procedure - if (not u->ho_prep_proc.launch(target_eci, target_plmn, fwd_erabs, std::move(rrc_container), has_direct_fwd_path)) { + if (not u->ho_prep_proc.launch( + target_eci, target_tac, target_plmn, fwd_erabs, std::move(rrc_container), has_direct_fwd_path)) { logger.error("Failed to initiate an HandoverPreparation procedure for user rnti=0x%x", u->ctxt.rnti); return false; } @@ -1802,7 +1988,6 @@ void s1ap::user_list::erase(ue* ue_ptr) /******************************************************************************* /* General helpers ********************************************************************************/ - bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name) { if (not mme_connected and rnti != SRSRAN_INVALID_RNTI) { @@ -1810,13 +1995,29 @@ bool s1ap::sctp_send_s1ap_pdu(const asn1::s1ap::s1ap_pdu_c& tx_pdu, uint32_t rnt return false; } + // Reset the state if it is a successful or unsuccessfull message + if (tx_pdu.type() == s1ap_pdu_c::types_opts::successful_outcome || + tx_pdu.type() == s1ap_pdu_c::types_opts::unsuccessful_outcome) { + if (rnti != SRSRAN_INVALID_RNTI) { + s1ap::ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + logger.warning("Could not find user for %s. RNTI=%x", procedure_name, rnti); + } else { + u->set_state(s1ap_proc_id_t::nulltype, {}, {}); + } + } + } + srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer(); if (buf == nullptr) { logger.error("Fatal Error: Couldn't allocate buffer for %s.", procedure_name); return false; } asn1::bit_ref bref(buf->msg, buf->get_tailroom()); - tx_pdu.pack(bref); + if (tx_pdu.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack TX PDU %s", procedure_name); + return false; + } buf->N_bytes = bref.distance_bytes(); // Save message to PCAP @@ -1863,6 +2064,9 @@ s1ap::ue* s1ap::handle_s1apmsg_ue_id(uint32_t enb_id, uint32_t mme_id) ue* user_ptr = users.find_ue_enbid(enb_id); ue* user_mme_ptr = nullptr; cause_c cause; + + logger.info("Checking UE S1 logical connection. eNB UE S1AP ID=%d, MME UE S1AP ID=%d", enb_id, mme_id); + if (user_ptr != nullptr) { if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) { // No ID inconsistency found @@ -1950,17 +2154,20 @@ s1ap::ue::ue(s1ap* s1ap_ptr_) : s1ap_ptr(s1ap_ptr_), ho_prep_proc(this), logger( // initialize timers ts1_reloc_prep = s1ap_ptr->task_sched.get_unique_timer(); - ts1_reloc_prep.set(ts1_reloc_prep_timeout_ms, + ts1_reloc_prep.set(s1ap_ptr->args.ts1_reloc_prep_timeout, [this](uint32_t tid) { ho_prep_proc.trigger(ho_prep_proc_t::ts1_reloc_prep_expired{}); }); ts1_reloc_overall = s1ap_ptr->task_sched.get_unique_timer(); - ts1_reloc_overall.set(ts1_reloc_overall_timeout_ms, [this](uint32_t tid) { + ts1_reloc_overall.set(s1ap_ptr->args.ts1_reloc_overall_timeout, [this](uint32_t tid) { //> If the UE Context Release procedure is not initiated towards the eNB before the expiry of the timer // TS1RELOCOverall, the eNB shall request the MME to release the UE context. s1ap_ptr->user_release(ctxt.rnti, asn1::s1ap::cause_radio_network_opts::ts1relocoverall_expiry); }); + overall_procedure_timeout = s1ap_ptr->task_sched.get_unique_timer(); + overall_procedure_timeout.set(10000); } bool s1ap::ue::send_ho_required(uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -1969,29 +2176,29 @@ bool s1ap::ue::send_ho_required(uint32_t target_eci, /*** Setup S1AP PDU as HandoverRequired ***/ s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_HO_PREP); - ho_required_ies_container& container = tx_pdu.init_msg().value.ho_required().protocol_ies; + ho_required_s& container = tx_pdu.init_msg().value.ho_required(); /*** fill HO Required message ***/ - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); - container.handov_type.value.value = handov_type_opts::intralte; // NOTE: only intra-LTE HO supported - container.cause.value.set_radio_network().value = cause_radio_network_opts::ho_desirable_for_radio_reason; + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->handov_type.value.value = handov_type_opts::intralte; // NOTE: only intra-LTE HO supported + container->cause.value.set_radio_network().value = cause_radio_network_opts::ho_desirable_for_radio_reason; - container.direct_forwarding_path_availability_present = has_direct_fwd_path; - if (container.direct_forwarding_path_availability_present) { - container.direct_forwarding_path_availability.value.value = + container->direct_forwarding_path_availability_present = has_direct_fwd_path; + if (container->direct_forwarding_path_availability_present) { + container->direct_forwarding_path_availability.value.value = asn1::s1ap::direct_forwarding_path_availability_opts::direct_path_available; } /*** set the target eNB ***/ - container.csg_id_present = false; // NOTE: CSG/hybrid target cell not supported - container.cell_access_mode_present = false; // only for hybrid cells + container->csg_id_present = false; // NOTE: CSG/hybrid target cell not supported + container->cell_access_mode_present = false; // only for hybrid cells // no GERAN/UTRAN/PS - auto& targetenb = container.target_id.value.set_targetenb_id(); + auto& targetenb = container->target_id.value.set_targetenb_id(); // set PLMN and TAI of target // NOTE: Only HO without TAU supported. uint16_t tmp16; - tmp16 = htons(s1ap_ptr->args.tac); + tmp16 = htons(target_tac); memcpy(targetenb.sel_tai.tac.data(), &tmp16, sizeof(uint16_t)); target_plmn.to_s1ap_plmn_bytes(targetenb.sel_tai.plm_nid.data()); // NOTE: Only HO to different Macro eNB is supported. @@ -2000,7 +2207,7 @@ bool s1ap::ue::send_ho_required(uint32_t target_eci, macroenb.from_number(target_eci >> 8U); /*** fill the transparent container ***/ - container.source_to_target_transparent_container_secondary_present = false; + container->source_to_target_transparent_container_secondary_present = false; sourceenb_to_targetenb_transparent_container_s transparent_cntr; transparent_cntr.subscriber_profile_idfor_rfp_present = false; // TODO: CHECK @@ -2008,9 +2215,9 @@ bool s1ap::ue::send_ho_required(uint32_t target_eci, transparent_cntr.erab_info_list.resize(fwd_erabs.size()); for (uint32_t i = 0; i < fwd_erabs.size(); ++i) { transparent_cntr.erab_info_list[i].load_info_obj(ASN1_S1AP_ID_ERAB_INFO_LIST_ITEM); - transparent_cntr.erab_info_list[i].value.erab_info_list_item().erab_id = fwd_erabs[i]; - transparent_cntr.erab_info_list[i].value.erab_info_list_item().dl_forwarding_present = true; - transparent_cntr.erab_info_list[i].value.erab_info_list_item().dl_forwarding.value = + transparent_cntr.erab_info_list[i]->erab_info_list_item().erab_id = fwd_erabs[i]; + transparent_cntr.erab_info_list[i]->erab_info_list_item().dl_forwarding_present = true; + transparent_cntr.erab_info_list[i]->erab_info_list_item().dl_forwarding.value = dl_forwarding_opts::dl_forwarding_proposed; } // - set target cell ID @@ -2046,8 +2253,8 @@ bool s1ap::ue::send_ho_required(uint32_t target_eci, logger.error("Failed to pack transparent container of HO Required message"); return false; } - container.source_to_target_transparent_container.value.resize(bref.distance_bytes()); - memcpy(container.source_to_target_transparent_container.value.data(), buffer->msg, bref.distance_bytes()); + container->source_to_target_transparent_container.value.resize(bref.distance_bytes()); + memcpy(container->source_to_target_transparent_container.value.data(), buffer->msg, bref.distance_bytes()); // Send to HandoverRequired message to MME return s1ap_ptr->sctp_send_s1ap_pdu(tx_pdu, ctxt.rnti, "Handover Required"); @@ -2061,17 +2268,17 @@ bool s1ap::ue::send_enb_status_transfer_proc(std::vector& be s1ap_pdu_c tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ENB_STATUS_TRANSFER); - enb_status_transfer_ies_container& container = tx_pdu.init_msg().value.enb_status_transfer().protocol_ies; + enb_status_transfer_s& container = tx_pdu.init_msg().value.enb_status_transfer(); - container.enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; - container.mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); + container->enb_ue_s1ap_id.value = ctxt.enb_ue_s1ap_id; + container->mme_ue_s1ap_id.value = ctxt.mme_ue_s1ap_id.value(); /* Create StatusTransfer transparent container with all the bearer ctxt to transfer */ - auto& list = container.enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list; + auto& list = container->enb_status_transfer_transparent_container.value.bearers_subject_to_status_transfer_list; list.resize(bearer_status_list.size()); for (uint32_t i = 0; i < list.size(); ++i) { list[i].load_info_obj(ASN1_S1AP_ID_BEARERS_SUBJECT_TO_STATUS_TRANSFER_ITEM); - auto& asn1bearer = list[i].value.bearers_subject_to_status_transfer_item(); + auto& asn1bearer = list[i]->bearers_subject_to_status_transfer_item(); bearer_status_info& item = bearer_status_list[i]; asn1bearer.erab_id = item.erab_id; @@ -2091,7 +2298,7 @@ bool s1ap::ue::send_enb_status_transfer_proc(std::vector& be void s1ap::log_s1ap_msg(const asn1::s1ap::s1ap_pdu_c& msg, srsran::const_span sdu, bool is_rx) { - std::string msg_type; + const char* msg_type; switch (msg.type().value) { case s1ap_pdu_c::types_opts::init_msg: @@ -2108,7 +2315,7 @@ void s1ap::log_s1ap_msg(const asn1::s1ap::s1ap_pdu_c& msg, srsran::const_span #include @@ -45,9 +45,10 @@ gtpu_tunnel_manager::gtpu_tunnel_manager(srsran::task_sched_handle task_sched_, logger(logger), task_sched(task_sched_), tunnels(1) {} -void gtpu_tunnel_manager::init(pdcp_interface_gtpu* pdcp_) +void gtpu_tunnel_manager::init(const gtpu_args_t& args, pdcp_interface_gtpu* pdcp_) { - pdcp = pdcp_; + gtpu_args = &args; + pdcp = pdcp_; } const gtpu_tunnel_manager::tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t teid) @@ -56,34 +57,34 @@ const gtpu_tunnel_manager::tunnel* gtpu_tunnel_manager::find_tunnel(uint32_t tei return it != tunnels.end() ? &it->second : nullptr; } -gtpu_tunnel_manager::ue_lcid_tunnel_list* gtpu_tunnel_manager::find_rnti_tunnels(uint16_t rnti) +gtpu_tunnel_manager::ue_bearer_tunnel_list* gtpu_tunnel_manager::find_rnti_tunnels(uint16_t rnti) { - if (not ue_teidin_db.contains(rnti)) { - return nullptr; - } - return &ue_teidin_db[rnti]; + auto it = ue_teidin_db.find(rnti); + return it != ue_teidin_db.end() ? &ue_teidin_db[rnti] : nullptr; } -srsran::span gtpu_tunnel_manager::find_rnti_lcid_tunnels(uint16_t rnti, uint32_t lcid) +srsran::span +gtpu_tunnel_manager::find_rnti_bearer_tunnels(uint16_t rnti, uint32_t eps_bearer_id) { - if (not is_lte_rb(lcid)) { - logger.warning("Searching for bearer with invalid lcid=%d", lcid); + if (not is_lte_rb(eps_bearer_id)) { + logger.warning("Searching for bearer with invalid eps-BearerID=%d", eps_bearer_id); return {}; } auto* ue_ptr = find_rnti_tunnels(rnti); if (ue_ptr == nullptr) { return {}; } - auto lcid_it_begin = std::lower_bound(ue_ptr->begin(), ue_ptr->end(), lcid_tunnel{lcid, 0}); - auto lcid_it_end = std::lower_bound(ue_ptr->begin(), ue_ptr->end(), lcid_tunnel{lcid + 1, 0}); + auto bearer_it_begin = std::lower_bound(ue_ptr->begin(), ue_ptr->end(), bearer_teid_pair{eps_bearer_id, 0}); + auto bearer_it_end = std::lower_bound(ue_ptr->begin(), ue_ptr->end(), bearer_teid_pair{eps_bearer_id + 1, 0}); - return srsran::span(&(*lcid_it_begin), &(*lcid_it_end)); + return srsran::span(&(*bearer_it_begin), &(*bearer_it_end)); } -const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, uint32_t teidout, uint32_t spgw_addr) +const gtpu_tunnel* +gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t eps_bearer_id, uint32_t teidout, uint32_t spgw_addr) { - if (not is_lte_rb(lcid)) { - logger.warning("Adding TEID with invalid lcid=%d", lcid); + if (not is_lte_rb(eps_bearer_id)) { + logger.warning("Adding TEID with invalid eps-BearerID=%d", eps_bearer_id); return nullptr; } auto ret_pair = tunnels.insert(tunnel()); @@ -91,16 +92,16 @@ const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, logger.warning("Unable to create new GTPU TEID In"); return nullptr; } - tunnel* tun = &tunnels[ret_pair.value()]; - tun->teid_in = ret_pair.value(); - tun->rnti = rnti; - tun->lcid = lcid; - tun->teid_out = teidout; - tun->spgw_addr = spgw_addr; + tunnel* tun = &tunnels[ret_pair.value()]; + tun->teid_in = ret_pair.value(); + tun->rnti = rnti; + tun->eps_bearer_id = eps_bearer_id; + tun->teid_out = teidout; + tun->spgw_addr = spgw_addr; - if (not ue_teidin_db.contains(rnti)) { - auto ret = ue_teidin_db.insert(rnti, ue_lcid_tunnel_list()); - if (ret.is_error()) { + if (ue_teidin_db.find(rnti) == ue_teidin_db.end()) { + auto ret = ue_teidin_db.emplace(rnti, ue_bearer_tunnel_list()); + if (!ret.second) { logger.error("Failed to allocate rnti=0x%x", rnti); return nullptr; } @@ -112,16 +113,16 @@ const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, tunnels.erase(tun->teid_in); return nullptr; } - ue_tunnels.push_back(lcid_tunnel{lcid, tun->teid_in}); + ue_tunnels.push_back(bearer_teid_pair{eps_bearer_id, tun->teid_in}); std::sort(ue_tunnels.begin(), ue_tunnels.end()); fmt::memory_buffer str_buffer; srsran::gtpu_ntoa(str_buffer, htonl(spgw_addr)); - logger.info("New tunnel created - " TEID_IN_FMT ", " TEID_OUT_FMT ", rnti=0x%x, lcid=%d, remote addr=%s", + logger.info("New tunnel created - " TEID_IN_FMT ", " TEID_OUT_FMT ", rnti=0x%x, eps-BearerID=%d, remote addr=%s", tun->teid_in, teidout, rnti, - lcid, + eps_bearer_id, srsran::to_c_str(str_buffer)); return tun; @@ -129,20 +130,40 @@ const gtpu_tunnel* gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t lcid, bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti) { - srsran_assert(find_rnti_tunnels(new_rnti) == nullptr, "New rnti=0x%x already exists", new_rnti); - auto* old_rnti_ptr = find_rnti_tunnels(old_rnti); + auto* new_rnti_ptr = find_rnti_tunnels(new_rnti); + if (old_rnti_ptr == nullptr or (new_rnti_ptr != nullptr and not new_rnti_ptr->empty())) { + // The old rnti must exist and the new rnti TEID list must be empty + logger.error("Modifying bearer rnti. Old rnti=0x%x, new rnti=0x%x", old_rnti, new_rnti); + return false; + } logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti); // create new RNTI and update TEIDs of old rnti to reflect new rnti - if (not ue_teidin_db.insert(new_rnti, ue_lcid_tunnel_list())) { + if (new_rnti_ptr == nullptr and not ue_teidin_db.insert({new_rnti, ue_bearer_tunnel_list()}).second) { logger.error("Failure to create new rnti=0x%x", new_rnti); return false; } std::swap(ue_teidin_db[new_rnti], *old_rnti_ptr); - auto& new_rnti_obj = ue_teidin_db[new_rnti]; - for (lcid_tunnel& bearer : new_rnti_obj) { + ue_bearer_tunnel_list& new_rnti_obj = ue_teidin_db[new_rnti]; + srsran::bounded_vector to_remove; + for (bearer_teid_pair& bearer : new_rnti_obj) { tunnels[bearer.teid].rnti = new_rnti; + // Remove forwarding path + if (tunnels[bearer.teid].state == tunnel_state::forward_to) { + tunnels[bearer.teid].state = tunnel_state::pdcp_active; + tunnels[bearer.teid].fwd_tunnel = nullptr; + logger.info("Taking down forwarding tunnel for rnti=0x%x, eps-BearerID=%d. New default " TEID_IN_FMT, + new_rnti, + bearer.eps_bearer_id, + bearer.teid); + } else if (tunnels[bearer.teid].state == tunnel_state::forwarded_from) { + to_remove.push_back(bearer.teid); + } + } + while (not to_remove.empty()) { + remove_tunnel(to_remove.back()); + to_remove.pop_back(); } // Leave old_rnti as zombie to be removed later @@ -155,39 +176,26 @@ bool gtpu_tunnel_manager::remove_tunnel(uint32_t teidin) { tunnel& tun = tunnels[teidin]; - // erase keeping the relative order - auto& ue = ue_teidin_db[tun.rnti]; - auto lcid_it = std::lower_bound(ue.begin(), ue.end(), lcid_tunnel{tun.lcid, tun.teid_in}); - srsran_assert(lcid_it->teid == tun.teid_in and lcid_it->lcid == tun.lcid, "TEID in undefined state"); - ue.erase(lcid_it); + // update forwarding paths if required + deactivate_tunnel(teidin); - logger.info("Removed rnti=0x%x,lcid=%d tunnel with " TEID_IN_FMT, tun.rnti, tun.lcid, teidin); + // erase keeping the relative order + auto& ue = ue_teidin_db[tun.rnti]; + auto bearer_it = std::lower_bound(ue.begin(), ue.end(), bearer_teid_pair{tun.eps_bearer_id, tun.teid_in}); + srsran_assert(bearer_it->teid == tun.teid_in and bearer_it->eps_bearer_id == tun.eps_bearer_id, + "TEID in undefined state"); + ue.erase(bearer_it); + + logger.info("Removed rnti=0x%x,eps-BearerID=%d tunnel with " TEID_IN_FMT, tun.rnti, tun.eps_bearer_id, teidin); tunnels.erase(teidin); return true; } -bool gtpu_tunnel_manager::remove_bearer(uint16_t rnti, uint32_t lcid) -{ - logger.info("Removing rnti=0x%x,lcid=%d", rnti, lcid); - bool removed = false; - for (srsran::span to_rem = find_rnti_lcid_tunnels(rnti, lcid); not to_rem.empty(); - to_rem = find_rnti_lcid_tunnels(rnti, lcid)) { - uint32_t teid = to_rem.front().teid; - bool ret = remove_tunnel(teid); - srsran_expect(ret, - "Inconsistency detected between internal data structures for rnti=0x%x,lcid=%d," TEID_IN_FMT, - rnti, - lcid, - teid); - removed |= ret; - } - return removed; -} - bool gtpu_tunnel_manager::remove_rnti(uint16_t rnti) { - if (not ue_teidin_db.contains(rnti)) { - logger.warning("removing rnti. rnti=0x%x not found.", rnti); + auto it = ue_teidin_db.find(rnti); + if (it == ue_teidin_db.end()) { + logger.warning("Removing rnti. rnti=0x%x not found.", rnti); return false; } logger.info("Removing rnti=0x%x", rnti); @@ -221,7 +229,8 @@ void gtpu_tunnel_manager::activate_tunnel(uint32_t teid) for (auto& sdu_pair : *tun.buffer) { uint32_t pdcp_sn = sdu_pair.first; - pdcp->write_sdu(tun.rnti, tun.lcid, std::move(sdu_pair.second), pdcp_sn == undefined_pdcp_sn ? -1 : pdcp_sn); + pdcp->write_sdu( + tun.rnti, tun.eps_bearer_id, std::move(sdu_pair.second), pdcp_sn == undefined_pdcp_sn ? -1 : pdcp_sn); } tun.buffer.reset(); tun.state = tunnel_state::pdcp_active; @@ -239,10 +248,28 @@ void gtpu_tunnel_manager::suspend_tunnel(uint32_t teid) tun.state = tunnel_state::buffering; } +void gtpu_tunnel_manager::deactivate_tunnel(uint32_t teid) +{ + tunnel& tun = tunnels[teid]; + + if (tun.state == tunnel_state::forwarded_from) { + // Deactivate respective MME->SeNB forwarding tunnel + for (auto bearer_tun : find_rnti_bearer_tunnels(tun.rnti, tun.eps_bearer_id)) { + if (bearer_tun.teid != tun.teid_in) { + const gtpu_tunnel_manager::tunnel* mmeenb_tun = find_tunnel(bearer_tun.teid); + if (mmeenb_tun->state == gtpu_tunnel_manager::tunnel_state::forward_to and mmeenb_tun->fwd_tunnel == &tun) { + deactivate_tunnel(mmeenb_tun->teid_in); + } + } + } + } + + tun.state = tunnel_state::inactive; +} + void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t after_teid) { tunnel& before_tun = tunnels[before_teid]; - tunnel& after_tun = tunnels[after_teid]; // GTPU should not forward SDUs from main tunnel until the SeNB-TeNB tunnel has been flushed suspend_tunnel(after_teid); @@ -258,13 +285,17 @@ void gtpu_tunnel_manager::set_tunnel_priority(uint32_t before_teid, uint32_t aft // TS 36.300 - On detection of the "end marker", the target eNB may also initiate the release of the data forwarding // resource. However, the release of the data forwarding resource is implementation dependent and could // also be based on other mechanisms (e.g. timer-based mechanism). - before_tun.rx_timer = task_sched.get_unique_timer(); - before_tun.rx_timer.set(2000, [this, before_teid](uint32_t tid) { - // Note: This will self-destruct the callback object - logger.info("Forwarding tunnel " TEID_IN_FMT "being closed after timeout=2000 msec", before_teid); - remove_tunnel(before_teid); - }); - before_tun.rx_timer.run(); + if (gtpu_args->indirect_tunnel_timeout_msec > 0) { + before_tun.rx_timer = task_sched.get_unique_timer(); + before_tun.rx_timer.set(gtpu_args->indirect_tunnel_timeout_msec, [this, before_teid](uint32_t tid) { + // Note: This will self-destruct the callback object + logger.info("Forwarding tunnel " TEID_IN_FMT "being closed after timeout=%d msec", + before_teid, + gtpu_args->indirect_tunnel_timeout_msec); + remove_tunnel(before_teid); + }); + before_tun.rx_timer.run(); + } } void gtpu_tunnel_manager::handle_rx_pdcp_sdu(uint32_t teid) @@ -317,8 +348,10 @@ void gtpu_tunnel_manager::setup_forwarding(uint32_t rx_teid, uint32_t tx_teid) fmt::format_to(addrbuf, ":0x{:x} > ", rx_tun.teid_out); srsran::gtpu_ntoa(addrbuf, htonl(tx_tun.spgw_addr)); fmt::format_to(addrbuf, ":0x{:x}", tx_tun.teid_out); - logger.info( - "Created forwarding tunnel for rnti=0x%x, lcid=%d, %s", rx_tun.rnti, rx_tun.lcid, srsran::to_c_str(addrbuf)); + logger.info("Created forwarding tunnel for rnti=0x%x, eps-BearerID=%d, %s", + rx_tun.rnti, + rx_tun.eps_bearer_id, + srsran::to_c_str(addrbuf)); } /******************** @@ -342,18 +375,14 @@ gtpu::~gtpu() stop(); } -int gtpu::init(std::string gtp_bind_addr_, - std::string mme_addr_, - std::string m1u_multiaddr_, - std::string m1u_if_addr_, - srsenb::pdcp_interface_gtpu* pdcp_, - bool enable_mbsfn_) +int gtpu::init(const gtpu_args_t& gtpu_args, pdcp_interface_gtpu* pdcp_) { + args = gtpu_args; pdcp = pdcp_; - gtp_bind_addr = gtp_bind_addr_; - mme_addr = mme_addr_; + gtp_bind_addr = gtpu_args.gtp_bind_addr; + mme_addr = gtpu_args.mme_addr; - tunnels.init(pdcp); + tunnels.init(args, pdcp); char errbuf[128] = {}; @@ -375,13 +404,9 @@ int gtpu::init(std::string gtp_bind_addr_, struct sockaddr_in bindaddr; bzero(&bindaddr, sizeof(struct sockaddr_in)); - bindaddr.sin_family = AF_INET; - bindaddr.sin_addr.s_addr = inet_addr(gtp_bind_addr.c_str()); - bindaddr.sin_port = htons(GTPU_PORT); - - if (bind(fd, (struct sockaddr*)&bindaddr, sizeof(struct sockaddr_in))) { + // Bind socket + if (not net_utils::bind_addr(fd, gtp_bind_addr.c_str(), GTPU_PORT, &bindaddr)) { snprintf(errbuf, sizeof(errbuf), "%s", strerror(errno)); - logger.error("Failed to bind on address %s, port %d: %s", gtp_bind_addr.c_str(), int(GTPU_PORT), errbuf); srsran::console("Failed to bind on address %s, port %d: %s\n", gtp_bind_addr.c_str(), int(GTPU_PORT), errbuf); return SRSRAN_ERROR; } @@ -393,9 +418,8 @@ int gtpu::init(std::string gtp_bind_addr_, rx_socket_handler->add_socket_handler(fd, srsran::make_sdu_handler(logger, gtpu_queue, rx_callback)); // Start MCH socket if enabled - enable_mbsfn = enable_mbsfn_; - if (enable_mbsfn) { - if (not m1u.init(m1u_multiaddr_, m1u_if_addr_)) { + if (args.embms_enable) { + if (not m1u.init(args.embms_m1u_multiaddr, args.embms_m1u_if_addr)) { return SRSRAN_ERROR; } } @@ -411,11 +435,11 @@ void gtpu::stop() } // gtpu_interface_pdcp -void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) +void gtpu::write_pdu(uint16_t rnti, uint32_t eps_bearer_id, srsran::unique_byte_buffer_t pdu) { - srsran::span teids = tunnels.find_rnti_lcid_tunnels(rnti, lcid); + srsran::span teids = tunnels.find_rnti_bearer_tunnels(rnti, eps_bearer_id); if (teids.empty()) { - logger.warning("The rnti=0x%x,lcid=%d does not have any pdcp_active tunnel", rnti, lcid); + logger.warning("The rnti=0x%x, eps-BearerID=%d does not have any pdcp_active tunnel", rnti, eps_bearer_id); return; } const gtpu_tunnel& tx_tun = *tunnels.find_tunnel(teids[0].teid); @@ -462,11 +486,15 @@ void gtpu::send_pdu_to_tunnel(const gtpu_tunnel& tx_tun, srsran::unique_byte_buf } } -srsran::expected -gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) +srsran::expected gtpu::add_bearer(uint16_t rnti, + uint32_t eps_bearer_id, + uint32_t addr_out, + uint32_t teid_out, + uint32_t& addr_in, + const bearer_props* props) { // Allocate a TEID for the incoming tunnel - const gtpu_tunnel* new_tun = tunnels.add_tunnel(rnti, lcid, teid_out, addr); + const gtpu_tunnel* new_tun = tunnels.add_tunnel(rnti, eps_bearer_id, teid_out, addr_out); if (new_tun == nullptr) { return default_error_t(); } @@ -490,6 +518,14 @@ gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, } } + // Return bind address for S1AP and NGAP setup + uint8_t addr_in_tmp[4]; + if ((inet_pton(AF_INET, gtp_bind_addr.c_str(), &addr_in_tmp)) < 1) { + logger.error("Invalid address or failure during conversion: %s\n", gtp_bind_addr.c_str()); + } + addr_in = 0; + addr_in = addr_in_tmp[3] | (addr_in_tmp[2] << 8) | (addr_in_tmp[1] << 16) | (addr_in_tmp[0] << 24); + return teid_in; } @@ -507,52 +543,68 @@ void gtpu::set_tunnel_status(uint32_t teidin, bool dl_active) } } -void gtpu::rem_bearer(uint16_t rnti, uint32_t lcid) +void gtpu::rem_bearer(uint16_t rnti, uint32_t eps_bearer_id) { - if (tunnels.find_rnti_lcid_tunnels(rnti, lcid).empty()) { - logger.error("Removing non-existent bearer rnti=0x%x,lcid=%d", rnti, lcid); + srsran::span bearer_tuns = + tunnels.find_rnti_bearer_tunnels(rnti, eps_bearer_id); + if (bearer_tuns.empty()) { + logger.info("Removing bearer rnti=0x%x,eps-BearerID=%d without any active tunnels", rnti, eps_bearer_id); return; } - tunnels.remove_bearer(rnti, lcid); + do { + rem_tunnel(bearer_tuns.front().teid); + bearer_tuns = tunnels.find_rnti_bearer_tunnels(rnti, eps_bearer_id); + } while (not bearer_tuns.empty()); } void gtpu::mod_bearer_rnti(uint16_t old_rnti, uint16_t new_rnti) { - auto* old_rnti_ptr = tunnels.find_rnti_tunnels(old_rnti); - if (old_rnti_ptr == nullptr or tunnels.find_rnti_tunnels(new_rnti) != nullptr) { - logger.error("Modifying bearer rnti. Old rnti=0x%x, new rnti=0x%x", old_rnti, new_rnti); - return; - } tunnels.update_rnti(old_rnti, new_rnti); } void gtpu::rem_tunnel(uint32_t teidin) { - if (not tunnels.has_teid(teidin)) { + const gtpu_tunnel_manager::tunnel* tun = tunnels.find_tunnel(teidin); + if (tun == nullptr) { logger.warning("Removing tunnel - " TEID_IN_FMT " does not exist", teidin); return; } + if (tun->state == gtpu_tunnel_manager::tunnel_state::forwarded_from) { + // TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover + // END MARKER should be forwarded to TeNB if forwarding is activated + send_end_marker(tun->teid_in); + } else if (tun->state == gtpu_tunnel_manager::tunnel_state::forward_to) { + // Delete respective forwarding SeNB-TeNB tunnel + send_end_marker(tun->fwd_tunnel->teid_in); + rem_tunnel(tun->fwd_tunnel->teid_in); + } tunnels.remove_tunnel(teidin); } void gtpu::rem_user(uint16_t rnti) { - if (tunnels.find_rnti_tunnels(rnti) == nullptr) { + const gtpu_tunnel_manager::ue_bearer_tunnel_list* tun_lst = tunnels.find_rnti_tunnels(rnti); + if (tun_lst == nullptr) { logger.info("Removing user - rnti=0x%x not found.", rnti); return; } + while (not tun_lst->empty()) { + // Note: May send End-Marker to active forwarding tunnels during their removal + rem_tunnel(tun_lst->front().teid); + } tunnels.remove_rnti(rnti); } void gtpu::handle_end_marker(const gtpu_tunnel& rx_tunnel) { uint16_t rnti = rx_tunnel.rnti; - logger.info("Received GTPU End Marker for " TEID_IN_FMT ", rnti=0x%x.", rx_tunnel.teid_in, rnti); + logger.info("Rx GTPU End Marker, " TEID_IN_FMT ", rnti=0x%x.", rx_tunnel.teid_in, rnti); if (rx_tunnel.state == gtpu_tunnel_state::forward_to) { // TS 36.300, Sec 10.1.2.2.1 - Path Switch upon handover // END MARKER should be forwarded to TeNB if forwarding is activated send_end_marker(rx_tunnel.fwd_tunnel->teid_in); + rem_tunnel(rx_tunnel.fwd_tunnel->teid_in); } // Remove tunnel that received End Marker @@ -623,8 +675,8 @@ void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, pdcp_sn = (header.ext_buffer[1] << 8U) + header.ext_buffer[2]; } - uint16_t rnti = rx_tunnel.rnti; - uint16_t lcid = rx_tunnel.lcid; + uint16_t rnti = rx_tunnel.rnti; + uint16_t eps_bearer_id = rx_tunnel.eps_bearer_id; log_message(rx_tunnel, true, srsran::make_span(pdu)); @@ -641,7 +693,7 @@ void gtpu::handle_msg_data_pdu(const gtpu_header_t& header, break; } case gtpu_tunnel_manager::tunnel_state::pdcp_active: { - pdcp->write_sdu(rnti, lcid, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : (int)pdcp_sn); + pdcp->write_sdu(rnti, eps_bearer_id, std::move(pdu), pdcp_sn == undefined_pdcp_sn ? -1 : (int)pdcp_sn); break; } case gtpu_tunnel_manager::tunnel_state::forwarded_from: @@ -669,7 +721,7 @@ int gtpu::create_dl_fwd_tunnel(uint32_t rx_teid_in, uint32_t tx_teid_in) tunnels.setup_forwarding(rx_teid_in, tx_teid_in); // Get all buffered PDCP PDUs, and forward them through tx tunnel - std::map pdus = pdcp->get_buffered_pdus(rx_tun->rnti, rx_tun->lcid); + std::map pdus = pdcp->get_buffered_pdus(rx_tun->rnti, rx_tun->eps_bearer_id); for (auto& pdu_pair : pdus) { uint32_t pdcp_sn = pdu_pair.first; log_message(*tx_tun, false, srsran::make_span(pdu_pair.second), pdcp_sn); @@ -751,12 +803,12 @@ void gtpu::echo_response(in_addr_t addr, in_port_t port, uint16_t seq) ***************************************************************************/ bool gtpu::send_end_marker(uint32_t teidin) { - logger.info("TX GTPU End Marker."); const gtpu_tunnel* tx_tun = tunnels.find_tunnel(teidin); if (tx_tun == nullptr) { logger.error("TEID=%d not found to send the end marker to", teidin); return false; } + logger.info("Tx GTPU End Marker, " TEID_IN_FMT ", rnti=0x%x", teidin, tx_tun->rnti); gtpu_header_t header = {}; unique_byte_buffer_t pdu = make_byte_buffer(); @@ -778,7 +830,12 @@ bool gtpu::send_end_marker(uint32_t teidin) servaddr.sin_addr.s_addr = htonl(tx_tun->spgw_addr); servaddr.sin_port = htons(GTPU_PORT); - return sendto(fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in)) > 0; + bool success = + sendto(fd, pdu->msg, pdu->N_bytes, MSG_EOR, (struct sockaddr*)&servaddr, sizeof(struct sockaddr_in)) > 0; + if (success) { + tunnels.deactivate_tunnel(tx_tun->teid_in); + } + return success; } /**************************************************************************** @@ -829,11 +886,11 @@ void gtpu::log_message(const gtpu_tunnel& tun, bool is_rx, srsran::span fmt::format_to(strbuf2, "> {}:0x{:0x}, ", srsran::to_c_str(addrbuf), tun.teid_in); } fmt::format_to(strbuf, - "{} S1-U SDU, {}rnti=0x{:0x}, lcid={}, n_bytes={}, IPv{}", + "{} S1-U SDU, {}rnti=0x{:0x}, eps-BearerID={}, n_bytes={}, IPv{}", dir, fmt::to_string(strbuf2), tun.rnti, - tun.lcid, + tun.eps_bearer_id, pdu.size(), (int)ip_pkt->version); if (ip_pkt->version == 4) { @@ -886,8 +943,20 @@ bool gtpu::m1u_handler::init(std::string m1u_multiaddr_, std::string m1u_if_addr /* Send an ADD MEMBERSHIP message via setsockopt */ struct ip_mreq mreq {}; - mreq.imr_multiaddr.s_addr = inet_addr(m1u_multiaddr.c_str()); // Multicast address of the service - mreq.imr_interface.s_addr = inet_addr(m1u_if_addr.c_str()); // Address of the IF the socket will listen to. + // Multicast address of the service + if (inet_pton(AF_INET, m1u_multiaddr.c_str(), &mreq.imr_multiaddr) != 1) { + logger.error("Invalid m1u_multiaddr: %s", m1u_multiaddr.c_str()); + srsran::console("Invalid m1u_multiaddr: %s\n", m1u_multiaddr.c_str()); + perror("inet_pton"); + return false; + } + // Address of the IF the socket will listen to. + if (inet_pton(AF_INET, m1u_if_addr.c_str(), &mreq.imr_interface) != 1) { + logger.error("Invalid m1u_if_addr: %s", m1u_if_addr.c_str()); + srsran::console("Invalid m1u_if_addr: %s\n", m1u_if_addr.c_str()); + perror("inet_pton"); + return false; + } if (setsockopt(m1u_sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { logger.error("Register musticast group for M1-U"); logger.error("M1-U infterface IP: %s, M1-U Multicast Address %s", m1u_if_addr.c_str(), m1u_multiaddr.c_str()); @@ -895,8 +964,8 @@ bool gtpu::m1u_handler::init(std::string m1u_multiaddr_, std::string m1u_if_addr } logger.info("M1-U initialized"); - initiated = true; - lcid_counter = 1; + initiated = true; + bearer_counter = 1; // Assign a handler to rx M1U packets auto rx_callback = [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from) { @@ -914,7 +983,7 @@ void gtpu::m1u_handler::handle_rx_packet(srsran::unique_byte_buffer_t pdu, const gtpu_header_t header; gtpu_read_header(pdu.get(), &header, logger); - pdcp->write_sdu(SRSRAN_MRNTI, lcid_counter, std::move(pdu)); + pdcp->write_sdu(SRSRAN_MRNTI, bearer_counter, std::move(pdu)); } } // namespace srsenb diff --git a/srsenb/src/stack/upper/pdcp.cc b/srsenb/src/stack/upper/pdcp.cc index 36cb2f1d4..3e5edf97e 100644 --- a/srsenb/src/stack/upper/pdcp.cc +++ b/srsenb/src/stack/upper/pdcp.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,7 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsran/interfaces/enb_gtpu_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_pdcp.h" namespace srsenb { @@ -77,7 +77,7 @@ void pdcp::rem_user(uint16_t rnti) } } -void pdcp::add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cfg) +void pdcp::add_bearer(uint16_t rnti, uint32_t lcid, const srsran::pdcp_config_t& cfg) { if (users.count(rnti)) { if (rnti != SRSRAN_MRNTI) { @@ -95,6 +95,13 @@ void pdcp::del_bearer(uint16_t rnti, uint32_t lcid) } } +void pdcp::set_enabled(uint16_t rnti, uint32_t lcid, bool enabled) +{ + if (users.count(rnti)) { + users[rnti].pdcp->set_enabled(lcid, enabled); + } +} + void pdcp::reset(uint16_t rnti) { if (users.count(rnti)) { @@ -102,7 +109,7 @@ void pdcp::reset(uint16_t rnti) } } -void pdcp::config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t sec_cfg) +void pdcp::config_security(uint16_t rnti, uint32_t lcid, const srsran::as_security_config_t& sec_cfg) { if (users.count(rnti)) { users[rnti].pdcp->config_security(lcid, sec_cfg); @@ -219,6 +226,11 @@ bool pdcp::user_interface_rlc::rb_is_um(uint32_t lcid) return rlc->rb_is_um(rnti, lcid); } +bool pdcp::user_interface_rlc::is_suspended(uint32_t lcid) +{ + return rlc->is_suspended(rnti, lcid); +} + bool pdcp::user_interface_rlc::sdu_queue_is_full(uint32_t lcid) { return rlc->sdu_queue_is_full(rnti, lcid); @@ -229,6 +241,11 @@ void pdcp::user_interface_rrc::write_pdu(uint32_t lcid, srsran::unique_byte_buff rrc->write_pdu(rnti, lcid, std::move(pdu)); } +void pdcp::user_interface_rrc::notify_pdcp_integrity_error(uint32_t lcid) +{ + rrc->notify_pdcp_integrity_error(rnti, lcid); +} + void pdcp::user_interface_rrc::write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) { ERROR("Error: Received BCCH from ue=%d", rnti); diff --git a/srsenb/src/stack/upper/pdcp_nr.cc b/srsenb/src/stack/upper/pdcp_nr.cc deleted file mode 100644 index a9fc86472..000000000 --- a/srsenb/src/stack/upper/pdcp_nr.cc +++ /dev/null @@ -1,189 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsenb/hdr/stack/upper/pdcp_nr.h" -#include "srsenb/hdr/common/common_enb.h" - -namespace srsenb { - -pdcp_nr::pdcp_nr(srsran::task_sched_handle task_sched_, const char* logname) : - task_sched(task_sched_), logger(srslog::fetch_basic_logger(logname)) -{} - -void pdcp_nr::init(const pdcp_nr_args_t& args_, - rlc_interface_pdcp_nr* rlc_, - rrc_interface_pdcp_nr* rrc_, - sdap_interface_pdcp_nr* sdap_) -{ - m_args = args_; - m_rlc = rlc_; - m_rrc = rrc_; - m_sdap = sdap_; - - logger.set_level(srslog::str_to_basic_level(m_args.log_level)); - logger.set_hex_dump_max_size(m_args.log_hex_limit); -} - -void pdcp_nr::stop() -{ - for (auto& user : users) { - user.second.pdcp->stop(); - } - users.clear(); -} - -void pdcp_nr::add_user(uint16_t rnti) -{ - if (users.count(rnti) == 0) { - users[rnti].pdcp.reset(new srsran::pdcp(task_sched, "PDCP")); - users[rnti].rlc_itf.rnti = rnti; - users[rnti].sdap_itf.rnti = rnti; - users[rnti].rrc_itf.rnti = rnti; - users[rnti].rlc_itf.rlc = m_rlc; - users[rnti].rrc_itf.rrc = m_rrc; - users[rnti].sdap_itf.sdap = m_sdap; - users[rnti].pdcp->init(&users[rnti].rlc_itf, &users[rnti].rrc_itf, &users[rnti].sdap_itf); - } -} - -void pdcp_nr::rem_user(uint16_t rnti) -{ - users.erase(rnti); -} - -void pdcp_nr::add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cfg) -{ - if (users.count(rnti)) { - users[rnti].pdcp->add_bearer(lcid, cfg); - } -} - -void pdcp_nr::reset(uint16_t rnti) -{ - if (users.count(rnti)) { - users[rnti].pdcp->reset(); - } -} - -void pdcp_nr::config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t sec_cfg) -{ - if (users.count(rnti)) { - users[rnti].pdcp->config_security(lcid, sec_cfg); - } -} - -void pdcp_nr::enable_integrity(uint16_t rnti, uint32_t lcid) -{ - users[rnti].pdcp->enable_integrity(lcid, srsran::DIRECTION_TXRX); -} - -void pdcp_nr::enable_encryption(uint16_t rnti, uint32_t lcid) -{ - users[rnti].pdcp->enable_encryption(lcid, srsran::DIRECTION_TXRX); -} - -void pdcp_nr::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) -{ - if (users.count(rnti)) { - users[rnti].pdcp->write_pdu(lcid, std::move(sdu)); - } else { - logger.error("Can't write PDU. RNTI=0x%X doesn't exist.", rnti); - } -} - -void pdcp_nr::notify_delivery(uint16_t rnti, uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) -{ - if (users.count(rnti)) { - users[rnti].pdcp->notify_delivery(lcid, pdcp_sns); - } else { - logger.error("Can't notify Ack of PDU. RNTI=0x%X doesn't exist.", rnti); - } -} - -void pdcp_nr::notify_failure(uint16_t rnti, uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) -{ - if (users.count(rnti)) { - users[rnti].pdcp->notify_failure(lcid, pdcp_sns); - } else { - logger.error("Can't notify Ack of PDU. RNTI=0x%X doesn't exist.", rnti); - } -} - -void pdcp_nr::write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) -{ - if (users.count(rnti)) { - users[rnti].pdcp->write_sdu(lcid, std::move(sdu)); - } else { - logger.error("Can't write SDU. RNTI=0x%X doesn't exist.", rnti); - } -} - -void pdcp_nr::user_interface_sdap::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) -{ - sdap->write_pdu(rnti, lcid, std::move(pdu)); -} - -void pdcp_nr::user_interface_rlc::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) -{ - rlc->write_sdu(rnti, lcid, std::move(sdu)); -} - -void pdcp_nr::user_interface_rlc::discard_sdu(uint32_t lcid, uint32_t discard_sn) -{ - fprintf(stderr, "discard_sdu method not implemented.\n"); -} - -bool pdcp_nr::user_interface_rlc::rb_is_um(uint32_t lcid) -{ - return rlc->rb_is_um(rnti, lcid); -} - -bool pdcp_nr::user_interface_rlc::sdu_queue_is_full(uint32_t lcid) -{ - return rlc->sdu_queue_is_full(rnti, lcid); -} - -void pdcp_nr::user_interface_rrc::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) -{ - rrc->write_pdu(rnti, lcid, std::move(pdu)); -} - -void pdcp_nr::user_interface_rrc::write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) -{ - ERROR("Error: Received BCCH from ue=%d", rnti); -} - -void pdcp_nr::user_interface_rrc::write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) -{ - ERROR("Error: Received BCCH from ue=%d", rnti); -} - -void pdcp_nr::user_interface_rrc::write_pdu_pcch(srsran::unique_byte_buffer_t pdu) -{ - ERROR("Error: Received PCCH from ue=%d", rnti); -} - -const char* pdcp_nr::user_interface_rrc::get_rb_name(uint32_t lcid) -{ - return srsenb::get_rb_name(lcid); -} - -} // namespace srsenb diff --git a/srsenb/src/stack/upper/rlc.cc b/srsenb/src/stack/upper/rlc.cc index 716a4b1c4..8611ec105 100644 --- a/srsenb/src/stack/upper/rlc.cc +++ b/srsenb/src/stack/upper/rlc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,7 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsran/interfaces/enb_mac_interfaces.h" #include "srsran/interfaces/enb_pdcp_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_rlc.h" namespace srsenb { @@ -63,7 +63,7 @@ void rlc::get_metrics(rlc_metrics_t& m, const uint32_t nof_tti) void rlc::add_user(uint16_t rnti) { - pthread_rwlock_rdlock(&rwlock); + pthread_rwlock_wrlock(&rwlock); if (users.count(rnti) == 0) { auto obj = make_rnti_obj(rnti, logger.id().c_str()); obj->init(&users[rnti], @@ -84,14 +84,17 @@ void rlc::add_user(uint16_t rnti) void rlc::rem_user(uint16_t rnti) { - pthread_rwlock_wrlock(&rwlock); + pthread_rwlock_rdlock(&rwlock); if (users.count(rnti)) { users[rnti].rlc->stop(); - users.erase(rnti); } else { logger.error("Removing rnti=0x%x. Already removed", rnti); } pthread_rwlock_unlock(&rwlock); + + pthread_rwlock_wrlock(&rwlock); + users.erase(rnti); + pthread_rwlock_unlock(&rwlock); } void rlc::clear_buffer(uint16_t rnti) @@ -109,7 +112,7 @@ void rlc::clear_buffer(uint16_t rnti) pthread_rwlock_unlock(&rwlock); } -void rlc::add_bearer(uint16_t rnti, uint32_t lcid, srsran::rlc_config_t cnfg) +void rlc::add_bearer(uint16_t rnti, uint32_t lcid, const srsran::rlc_config_t& cnfg) { pthread_rwlock_rdlock(&rwlock); if (users.count(rnti)) { @@ -159,6 +162,17 @@ bool rlc::suspend_bearer(uint16_t rnti, uint32_t lcid) return result; } +bool rlc::is_suspended(uint16_t rnti, uint32_t lcid) +{ + pthread_rwlock_rdlock(&rwlock); + bool result = false; + if (users.count(rnti)) { + result = users[rnti].rlc->is_suspended(lcid); + } + pthread_rwlock_unlock(&rwlock); + return result; +} + bool rlc::resume_bearer(uint16_t rnti, uint32_t lcid) { pthread_rwlock_rdlock(&rwlock); @@ -182,15 +196,10 @@ void rlc::reestablish(uint16_t rnti) // In the eNodeB, there is no polling for buffer state from the scheduler. // This function is called by UE RLC instance every time the tx/retx buffers are updated -void rlc::update_bsr(uint32_t rnti, uint32_t lcid, uint32_t tx_queue, uint32_t retx_queue) +void rlc::update_bsr(uint32_t rnti, uint32_t lcid, uint32_t tx_queue, uint32_t prio_tx_queue) { - logger.debug("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d", rnti, lcid, tx_queue); - mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); -} - -void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) -{ - rrc->read_pdu_pcch(payload, buffer_size); + logger.debug("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d, prio_tx_queue=%d", rnti, lcid, tx_queue, prio_tx_queue); + mac->rlc_buffer_state(rnti, lcid, tx_queue, prio_tx_queue); } int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) @@ -269,6 +278,11 @@ void rlc::user_interface::max_retx_attempted() rrc->max_retx_attempted(rnti); } +void rlc::user_interface::protocol_failure() +{ + rrc->protocol_failure(rnti); +} + void rlc::user_interface::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { if (lcid == srb_to_lcid(lte_srb::srb0)) { diff --git a/srsenb/src/stack/upper/rlc_nr.cc b/srsenb/src/stack/upper/rlc_nr.cc deleted file mode 100644 index bc9f5506f..000000000 --- a/srsenb/src/stack/upper/rlc_nr.cc +++ /dev/null @@ -1,228 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsenb/hdr/stack/upper/rlc_nr.h" -#include "srsran/common/common_nr.h" -namespace srsenb { - -rlc_nr::rlc_nr(const char* logname) : logger(srslog::fetch_basic_logger(logname)) {} - -void rlc_nr::init(pdcp_interface_rlc_nr* pdcp_, - rrc_interface_rlc_nr* rrc_, - mac_interface_rlc_nr* mac_, - srsran::timer_handler* timers_) -{ - m_pdcp = pdcp_; - m_rrc = rrc_; - m_mac = mac_; - timers = timers_; -} - -void rlc_nr::stop() -{ - for (auto& user : users) { - user.second.m_rlc->stop(); - } - users.clear(); -} - -void rlc_nr::add_user(uint16_t rnti) -{ - if (users.count(rnti) == 0) { - user_interface user_itf; - user_itf.rnti = rnti; - user_itf.m_pdcp = m_pdcp; - user_itf.m_rrc = m_rrc; - user_itf.parent = this; - user_itf.m_rlc.reset(new srsran::rlc(logger.id().c_str())); - users[rnti] = std::move(user_itf); - users[rnti].m_rlc->init(&users[rnti], &users[rnti], timers, (int)srsran::nr_srb::srb0); - } -} - -void rlc_nr::rem_user(uint16_t rnti) -{ - if (users.count(rnti)) { - users[rnti].m_rlc->stop(); - users.erase(rnti); - } else { - logger.error("Removing rnti=0x%x. Already removed", rnti); - } -} - -void rlc_nr::clear_buffer(uint16_t rnti) -{ - if (users.count(rnti)) { - users[rnti].m_rlc->empty_queue(); - for (int i = 0; i < SRSRAN_N_RADIO_BEARERS; i++) { - m_mac->rlc_buffer_state(rnti, i, 0, 0); - } - logger.info("Cleared buffer rnti=0x%x", rnti); - } -} - -void rlc_nr::add_bearer(uint16_t rnti, uint32_t lcid, srsran::rlc_config_t cnfg) -{ - if (users.count(rnti)) { - users[rnti].m_rlc->add_bearer(lcid, cnfg); - } -} - -void rlc_nr::add_bearer_mrb(uint16_t rnti, uint32_t lcid) -{ - if (users.count(rnti)) { - users[rnti].m_rlc->add_bearer_mrb(lcid); - } -} - -void rlc_nr::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) -{ - m_rrc->read_pdu_pcch(payload, buffer_size); -} - -int rlc_nr::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) -{ - int ret; - uint32_t tx_queue; - - if (users.count(rnti)) { - if (rnti != SRSRAN_MRNTI) { - ret = users[rnti].m_rlc->read_pdu(lcid, payload, nof_bytes); - tx_queue = users[rnti].m_rlc->get_buffer_state(lcid); - } else { - ret = users[rnti].m_rlc->read_pdu_mch(lcid, payload, nof_bytes); - tx_queue = users[rnti].m_rlc->get_total_mch_buffer_state(lcid); - } - // In the eNodeB, there is no polling for buffer state from the scheduler, thus - // communicate buffer state every time a PDU is read - - uint32_t retx_queue = 0; - logger.debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d", rnti, lcid, tx_queue); - m_mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); - } else { - ret = SRSRAN_ERROR; - } - return ret; -} - -void rlc_nr::write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) -{ - if (users.count(rnti)) { - users[rnti].m_rlc->write_pdu(lcid, payload, nof_bytes); - - // In the eNodeB, there is no polling for buffer state from the scheduler, thus - // communicate buffer state every time a new PDU is written - uint32_t tx_queue = users[rnti].m_rlc->get_buffer_state(lcid); - uint32_t retx_queue = 0; - logger.debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d", rnti, lcid, tx_queue); - m_mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); - } -} - -// void rlc::read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t* payload) -//{ -// // RLC is transparent for BCCH -// m_rrc->read_pdu_bcch_dlsch(sib_index, payload); -//} - -void rlc_nr::write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) -{ - uint32_t tx_queue; - - if (users.count(rnti)) { - if (rnti != SRSRAN_MRNTI) { - users[rnti].m_rlc->write_sdu(lcid, std::move(sdu)); - tx_queue = users[rnti].m_rlc->get_buffer_state(lcid); - } else { - users[rnti].m_rlc->write_sdu_mch(lcid, std::move(sdu)); - tx_queue = users[rnti].m_rlc->get_total_mch_buffer_state(lcid); - } - // In the eNodeB, there is no polling for buffer state from the scheduler, thus - // communicate buffer state every time a new SDU is written - - uint32_t retx_queue = 0; - m_mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); - logger.info("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d", rnti, lcid, tx_queue); - } -} - -bool rlc_nr::rb_is_um(uint16_t rnti, uint32_t lcid) -{ - bool ret = false; - if (users.count(rnti)) { - ret = users[rnti].m_rlc->rb_is_um(lcid); - } - return ret; -} - -bool rlc_nr::sdu_queue_is_full(uint16_t rnti, uint32_t lcid) -{ - bool ret = false; - if (users.count(rnti)) { - ret = users[rnti].m_rlc->sdu_queue_is_full(lcid); - } - return ret; -} -void rlc_nr::user_interface::max_retx_attempted() -{ - m_rrc->max_retx_attempted(rnti); -} - -void rlc_nr::user_interface::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) -{ - if (lcid == (int)srsran::nr_srb::srb0) { - m_rrc->write_pdu(rnti, lcid, std::move(sdu)); - } else { - m_pdcp->write_pdu(rnti, lcid, std::move(sdu)); - } -} - -void rlc_nr::user_interface::write_pdu_bcch_bch(srsran::unique_byte_buffer_t sdu) -{ - ERROR("Error: Received BCCH from ue=%d", rnti); -} - -void rlc_nr::user_interface::write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t sdu) -{ - ERROR("Error: Received BCCH from ue=%d", rnti); -} - -void rlc_nr::user_interface::write_pdu_pcch(srsran::unique_byte_buffer_t sdu) -{ - ERROR("Error: Received PCCH from ue=%d", rnti); -} - -const char* rlc_nr::user_interface::get_rb_name(uint32_t lcid) -{ - return m_rrc->get_rb_name(lcid); -} - -void rlc_nr::user_interface::notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) -{ - m_pdcp->notify_delivery(rnti, lcid, pdcp_sns); -} - -void rlc_nr::user_interface::notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) -{ - m_pdcp->notify_failure(rnti, lcid, pdcp_sns); -} - -} // namespace srsenb diff --git a/srsenb/test/CMakeLists.txt b/srsenb/test/CMakeLists.txt index 26ca5543f..dcf7ce5f8 100644 --- a/srsenb/test/CMakeLists.txt +++ b/srsenb/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -22,6 +22,7 @@ add_subdirectory(mac) add_subdirectory(phy) add_subdirectory(upper) add_subdirectory(rrc) +add_subdirectory(s1ap) add_executable(enb_metrics_test enb_metrics_test.cc ../src/metrics_stdout.cc ../src/metrics_csv.cc) target_link_libraries(enb_metrics_test srsran_phy srsran_common) diff --git a/srsenb/test/common/dummy_classes.h b/srsenb/test/common/dummy_classes.h index 023ad1a7a..37f6e2990 100644 --- a/srsenb/test/common/dummy_classes.h +++ b/srsenb/test/common/dummy_classes.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,10 +25,9 @@ #include "srsran/interfaces/enb_gtpu_interfaces.h" #include "srsran/interfaces/enb_interfaces.h" #include "srsran/interfaces/enb_mac_interfaces.h" -#include "srsran/interfaces/enb_pdcp_interfaces.h" #include "srsran/interfaces/enb_phy_interfaces.h" #include "srsran/interfaces/enb_rlc_interfaces.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_s1ap.h" #include "srsran/interfaces/enb_s1ap_interfaces.h" namespace srsenb { @@ -37,11 +36,10 @@ class mac_dummy : public mac_interface_rrc { public: int cell_cfg(const std::vector& cell_cfg) override { return 0; } - void reset() override {} - int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) override { return 0; } + int ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t* cfg) override { return 0; } int ue_rem(uint16_t rnti) override { return 0; } - int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, sched_interface::ue_cfg_t* cfg) override { return 0; } - int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) override { return 0; } + int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, const sched_interface::ue_cfg_t& cfg) override { return 0; } + int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, mac_lc_ch_cfg_t* cfg) override { return 0; } int bearer_ue_rem(uint16_t rnti, uint32_t lc_id) override { return 0; } void phy_config_enabled(uint16_t rnti, bool enabled) override {} void write_mcch(const srsran::sib2_mbms_t* sib2_, @@ -55,45 +53,6 @@ public: uint16_t last_rnti = 70; }; -class rlc_dummy : public rlc_interface_rrc -{ -public: - void clear_buffer(uint16_t rnti) override {} - void add_user(uint16_t rnti) override {} - void rem_user(uint16_t rnti) override {} - void add_bearer(uint16_t rnti, uint32_t lcid, srsran::rlc_config_t cnfg) override {} - void add_bearer_mrb(uint16_t rnti, uint32_t lcid) override {} - void del_bearer(uint16_t rnti, uint32_t lcid) override {} - void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) override {} - bool has_bearer(uint16_t rnti, uint32_t lcid) override { return false; } - bool suspend_bearer(uint16_t rnti, uint32_t lcid) override { return true; } - bool resume_bearer(uint16_t rnti, uint32_t lcid) override { return true; } - void reestablish(uint16_t rnti) override {} -}; - -class pdcp_dummy : public pdcp_interface_rrc, public pdcp_interface_gtpu -{ -public: - void reset(uint16_t rnti) override {} - void add_user(uint16_t rnti) override {} - void rem_user(uint16_t rnti) override {} - void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn) override {} - void add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cnfg) override {} - void del_bearer(uint16_t rnti, uint32_t lcid) override {} - void config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t sec_cfg_) override {} - void enable_integrity(uint16_t rnti, uint32_t lcid) override {} - void enable_encryption(uint16_t rnti, uint32_t lcid) override {} - bool get_bearer_state(uint16_t rnti, uint32_t lcid, srsran::pdcp_lte_state_t* state) override { return true; } - bool set_bearer_state(uint16_t rnti, uint32_t lcid, const srsran::pdcp_lte_state_t& state) override { return true; } - void reestablish(uint16_t rnti) override {} - void send_status_report(uint16_t rnti) override {} - void send_status_report(uint16_t rnti, uint32_t lcid) override {} - std::map get_buffered_pdus(uint16_t rnti, uint32_t lcid) override - { - return {}; - } -}; - class s1ap_dummy : public s1ap_interface_rrc { public: @@ -113,10 +72,11 @@ public: void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) override {} bool user_exists(uint16_t rnti) override { return true; } bool user_release(uint16_t rnti, asn1::s1ap::cause_radio_network_e cause_radio) override { return true; } - void ue_ctxt_setup_complete(uint16_t rnti) override {} + void notify_rrc_reconf_complete(uint16_t rnti) override {} bool is_mme_connected() override { return true; } bool send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -159,8 +119,12 @@ public: class gtpu_dummy : public srsenb::gtpu_interface_rrc { public: - srsran::expected - add_bearer(uint16_t rnti, uint32_t lcid, uint32_t addr, uint32_t teid_out, const bearer_props* props) override + srsran::expected add_bearer(uint16_t rnti, + uint32_t lcid, + uint32_t addr, + uint32_t teid_out, + uint32_t& addr_in, + const bearer_props* props) override { return 1; } diff --git a/srsenb/test/common/dummy_classes_common.h b/srsenb/test/common/dummy_classes_common.h new file mode 100644 index 000000000..b094cc114 --- /dev/null +++ b/srsenb/test/common/dummy_classes_common.h @@ -0,0 +1,75 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_DUMMY_CLASSES_COMMON_H +#define SRSENB_DUMMY_CLASSES_COMMON_H + +#include "srsran/interfaces/enb_pdcp_interfaces.h" +#include "srsran/interfaces/enb_rlc_interfaces.h" + +namespace srsenb { + +class rlc_dummy : public rlc_interface_rrc +{ +public: + void clear_buffer(uint16_t rnti) override {} + void add_user(uint16_t rnti) override {} + void rem_user(uint16_t rnti) override {} + void add_bearer(uint16_t rnti, uint32_t lcid, const srsran::rlc_config_t& cnfg) override {} + void add_bearer_mrb(uint16_t rnti, uint32_t lcid) override {} + void del_bearer(uint16_t rnti, uint32_t lcid) override {} + void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) override { last_sdu = std::move(sdu); } + bool has_bearer(uint16_t rnti, uint32_t lcid) override { return false; } + bool suspend_bearer(uint16_t rnti, uint32_t lcid) override { return true; } + bool is_suspended(uint16_t rnti, uint32_t lcid) override { return false; } + bool resume_bearer(uint16_t rnti, uint32_t lcid) override { return true; } + void reestablish(uint16_t rnti) override {} + + srsran::unique_byte_buffer_t last_sdu; +}; + +class pdcp_dummy : public pdcp_interface_rrc, public pdcp_interface_gtpu +{ +public: + void set_enabled(uint16_t rnti, uint32_t lcid, bool enabled) override {} + void reset(uint16_t rnti) override {} + void add_user(uint16_t rnti) override {} + void rem_user(uint16_t rnti) override {} + void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn) override {} + void add_bearer(uint16_t rnti, uint32_t lcid, const srsran::pdcp_config_t& cnfg) override {} + void del_bearer(uint16_t rnti, uint32_t lcid) override {} + void config_security(uint16_t rnti, uint32_t lcid, const srsran::as_security_config_t& sec_cfg_) override {} + void enable_integrity(uint16_t rnti, uint32_t lcid) override {} + void enable_encryption(uint16_t rnti, uint32_t lcid) override {} + bool get_bearer_state(uint16_t rnti, uint32_t lcid, srsran::pdcp_lte_state_t* state) override { return true; } + bool set_bearer_state(uint16_t rnti, uint32_t lcid, const srsran::pdcp_lte_state_t& state) override { return true; } + void reestablish(uint16_t rnti) override {} + void send_status_report(uint16_t rnti) override {} + void send_status_report(uint16_t rnti, uint32_t lcid) override {} + std::map get_buffered_pdus(uint16_t rnti, uint32_t lcid) override + { + return {}; + } +}; + +} // namespace srsenb + +#endif // SRSENB_DUMMY_CLASSES_COMMON_H diff --git a/srsenb/test/common/dummy_nr_classes.h b/srsenb/test/common/dummy_nr_classes.h deleted file mode 100644 index fee395dba..000000000 --- a/srsenb/test/common/dummy_nr_classes.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSRAN_DUMMY_NR_CLASSES_H -#define SRSRAN_DUMMY_NR_CLASSES_H - -#include "srsran/interfaces/gnb_interfaces.h" - -namespace srsenb { - -class mac_dummy : public mac_interface_rrc_nr -{ -public: - int cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg_) - { - cellcfgobj = *cell_cfg_; - return SRSRAN_SUCCESS; - } - - srsenb::sched_interface::cell_cfg_t cellcfgobj; -}; - -class rlc_dummy : public rlc_interface_rrc_nr -{ -public: - void clear_buffer(uint16_t rnti) override {} - void add_user(uint16_t rnti) override {} - void rem_user(uint16_t rnti) override {} - void add_bearer(uint16_t rnti, uint32_t lcid, srsran::rlc_config_t cnfg) override {} - void add_bearer_mrb(uint16_t rnti, uint32_t lcid) override {} - void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) override { last_sdu = std::move(sdu); } - - srsran::unique_byte_buffer_t last_sdu; -}; - -class pdcp_dummy : public pdcp_interface_rrc_nr -{ -public: - void reset(uint16_t rnti) override {} - void add_user(uint16_t rnti) override {} - void rem_user(uint16_t rnti) override {} - void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) override {} - void add_bearer(uint16_t rnti, uint32_t lcid, srsran::pdcp_config_t cnfg) override {} - void config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t sec_cfg) override {} - void enable_integrity(uint16_t rnti, uint32_t lcid) override {} - void enable_encryption(uint16_t rnti, uint32_t lcid) override {} -}; - -} // namespace srsenb - -#endif // SRSRAN_DUMMY_NR_CLASSES_H diff --git a/srsenb/test/common/rlc_test_dummy.h b/srsenb/test/common/rlc_test_dummy.h new file mode 100644 index 000000000..6cbd71cab --- /dev/null +++ b/srsenb/test/common/rlc_test_dummy.h @@ -0,0 +1,37 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RLC_TEST_DUMMY_H +#define SRSRAN_RLC_TEST_DUMMY_H + +#include "srsran/interfaces/enb_rlc_interfaces.h" + +namespace srsenb { + +class rlc_dummy : public rlc_interface_mac +{ + int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) { return SRSRAN_SUCCESS; } + void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) {} +}; + +} // namespace srsenb + +#endif // SRSRAN_RLC_TEST_DUMMY_H diff --git a/srsenb/test/enb_metrics_test.cc b/srsenb/test/enb_metrics_test.cc index 5c5d542ca..f608016b1 100644 --- a/srsenb/test/enb_metrics_test.cc +++ b/srsenb/test/enb_metrics_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -46,7 +46,7 @@ public: { // first entry metrics[0].rf.rf_o = 10; - metrics[0].stack.rrc.ues.resize(1); + metrics[0].stack.rrc.ues.resize(2); metrics[0].stack.mac.ues.resize(metrics[0].stack.rrc.ues.size()); metrics[0].stack.mac.ues[0].rnti = 0x46; metrics[0].stack.mac.ues[0].tx_pkts = 1000; @@ -61,12 +61,32 @@ public: metrics[0].stack.mac.ues[0].dl_ri = 1.5; metrics[0].stack.mac.ues[0].dl_pmi = 1.0; metrics[0].stack.mac.ues[0].phr = 12.0; - metrics[0].phy.resize(1); - metrics[0].phy[0].dl.mcs = 28.0; - metrics[0].phy[0].ul.mcs = 20.2; + metrics[0].phy.resize(2); + metrics[0].phy[0].dl.mcs = 28.0; + metrics[0].phy[0].ul.mcs = 20.2; metrics[0].phy[0].ul.pucch_sinr = 14.2; metrics[0].phy[0].ul.pusch_sinr = 14.2; + metrics[0].rf.rf_o = 10; + metrics[0].nr_stack.mac.ues.resize(1); + metrics[0].nr_stack.mac.ues[0].rnti = 0x4601; + metrics[0].nr_stack.mac.ues[0].tx_pkts = 2000; + metrics[0].nr_stack.mac.ues[0].tx_errors = 2000; + metrics[0].nr_stack.mac.ues[0].tx_brate = 0; + metrics[0].nr_stack.mac.ues[0].rx_pkts = 50; + metrics[0].nr_stack.mac.ues[0].rx_errors = 49; + metrics[0].nr_stack.mac.ues[0].rx_brate = 2; + metrics[0].nr_stack.mac.ues[0].ul_buffer = 100; + metrics[0].nr_stack.mac.ues[0].dl_buffer = 200; + metrics[0].nr_stack.mac.ues[0].dl_cqi = 15.9; + metrics[0].nr_stack.mac.ues[0].dl_ri = 1.5; + metrics[0].nr_stack.mac.ues[0].dl_pmi = 1.0; + metrics[0].nr_stack.mac.ues[0].phr = 12.0; + metrics[0].nr_stack.mac.ues[0].dl_mcs = 28; + metrics[0].nr_stack.mac.ues[0].ul_mcs = 22; + metrics[0].nr_stack.mac.ues[0].pusch_sinr = 14; + metrics[0].nr_stack.mac.ues[0].pucch_sinr = 14.7; + // second metrics[1].rf.rf_o = 10; metrics[1].stack.rrc.ues.resize(1); @@ -85,8 +105,8 @@ public: metrics[1].stack.mac.ues[0].dl_pmi = 1.0; metrics[1].stack.mac.ues[0].phr = 99.1; metrics[1].phy.resize(1); - metrics[1].phy[0].dl.mcs = 6.2; - metrics[1].phy[0].ul.mcs = 28.0; + metrics[1].phy[0].dl.mcs = 6.2; + metrics[1].phy[0].ul.mcs = 28.0; metrics[1].phy[0].ul.pucch_sinr = 22.2; metrics[1].phy[0].ul.pusch_sinr = 22.2; @@ -108,14 +128,14 @@ public: metrics[2].stack.mac.ues[0].dl_pmi = 1.0; metrics[2].stack.mac.ues[0].phr = 12.0; metrics[2].phy.resize(1); - metrics[2].phy[0].dl.mcs = 28.0; - metrics[2].phy[0].ul.mcs = 20.2; + metrics[2].phy[0].dl.mcs = 28.0; + metrics[2].phy[0].ul.mcs = 20.2; metrics[2].phy[0].ul.pusch_sinr = 14.2; metrics[2].phy[0].ul.pucch_sinr = 14.2; // fourth entry with incomple PHY and MAC stats metrics[3].rf.rf_o = 10; - metrics[3].stack.rrc.ues.resize(2); + metrics[3].stack.rrc.ues.resize(1); metrics[3].stack.mac.ues.resize(metrics[3].stack.rrc.ues.size()); metrics[3].stack.mac.ues[0].rnti = 0x1; metrics[3].stack.mac.ues[0].tx_pkts = 9999; @@ -190,8 +210,7 @@ int main(int argc, char** argv) metrics_screen.set_handle(&enb); // the CSV file writer - metrics_csv metrics_file(csv_file_name); - metrics_file.set_handle(&enb); + metrics_csv metrics_file(csv_file_name, &enb); // create metrics hub and register metrics for stdout srsran::metrics_hub metricshub; diff --git a/srsenb/test/mac/CMakeLists.txt b/srsenb/test/mac/CMakeLists.txt index 79e2c889d..1ed6c0717 100644 --- a/srsenb/test/mac/CMakeLists.txt +++ b/srsenb/test/mac/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -71,6 +71,18 @@ add_executable(sched_dci_test sched_dci_test.cc) target_link_libraries(sched_dci_test srsran_common srsenb_mac srsran_mac sched_test_common) add_test(sched_dci_test sched_dci_test) +add_executable(sched_ue_cell_test sched_ue_cell_test.cc) +target_link_libraries(sched_ue_cell_test srsran_common srsenb_mac srsran_mac sched_test_common) +add_test(sched_ue_cell_test sched_ue_cell_test) + add_executable(sched_benchmark_test sched_benchmark.cc) target_link_libraries(sched_benchmark_test srsran_common srsenb_mac srsran_mac sched_test_common) add_test(sched_benchmark_test sched_benchmark_test) + +add_executable(sched_cqi_test sched_cqi_test.cc) +target_link_libraries(sched_cqi_test srsran_common srsenb_mac srsran_mac sched_test_common) +add_test(sched_cqi_test sched_cqi_test) + +add_executable(sched_phy_resource_test sched_phy_resource_test.cc) +target_link_libraries(sched_phy_resource_test srsran_common srsenb_mac srsran_mac sched_test_common) +add_test(sched_phy_resource_test sched_phy_resource_test) \ No newline at end of file diff --git a/srsenb/test/mac/sched_benchmark.cc b/srsenb/test/mac/sched_benchmark.cc index 83db01cf4..032c1fd8d 100644 --- a/srsenb/test/mac/sched_benchmark.cc +++ b/srsenb/test/mac/sched_benchmark.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -37,7 +37,7 @@ struct run_params { struct run_params_range { std::vector nof_prbs{srsran::lte_cell_nof_prbs.begin(), srsran::lte_cell_nof_prbs.end()}; - std::vector nof_ues = {1, 2, 5}; + std::vector nof_ues = {1, 2, 5, 32}; uint32_t nof_ttis = 10000; std::vector cqi = {5, 10, 15}; std::vector sched_policy = {"time_rr", "time_pf"}; @@ -89,8 +89,9 @@ public: std::vector ul_result; struct throughput_stats { - srsran::rolling_average mean_dl_tbs, mean_ul_tbs, avg_dl_mcs, avg_ul_mcs; - srsran::rolling_average avg_latency; + srsran::rolling_average mean_dl_tbs, mean_ul_tbs, avg_dl_mcs, avg_ul_mcs; + srsran::rolling_average avg_latency; + std::vector latency_samples; }; throughput_stats total_stats; @@ -107,6 +108,7 @@ public: std::chrono::time_point tp2 = std::chrono::steady_clock::now(); std::chrono::nanoseconds tdur = std::chrono::duration_cast(tp2 - tp); total_stats.avg_latency.push(tdur.count()); + total_stats.latency_samples.push_back(tdur.count()); } sf_output_res_t sf_out{get_cell_params(), tti_rx, ul_result, dl_result}; @@ -164,6 +166,7 @@ struct run_data { float avg_dl_mcs; float avg_ul_mcs; std::chrono::microseconds avg_latency; + std::chrono::microseconds q0_9_latency; }; int run_benchmark_scenario(run_params params, std::vector& run_results) @@ -202,12 +205,14 @@ int run_benchmark_scenario(run_params params, std::vector& run_results tester.advance_tti(); ue_db_ctxt = tester.get_enb_ctxt().ue_db; } - tester.total_stats = {}; // Run benchmark + tester.total_stats = {}; + tester.total_stats.latency_samples.reserve(params.nof_ttis); for (uint32_t count = 0; count < params.nof_ttis; ++count) { tester.advance_tti(); } + std::sort(tester.total_stats.latency_samples.begin(), tester.total_stats.latency_samples.end()); run_data run_result = {}; run_result.params = params; @@ -215,7 +220,9 @@ int run_benchmark_scenario(run_params params, std::vector& run_results run_result.avg_ul_throughput = tester.total_stats.mean_ul_tbs.value() * 8.0F / 1e-3F; run_result.avg_dl_mcs = tester.total_stats.avg_dl_mcs.value(); run_result.avg_ul_mcs = tester.total_stats.avg_ul_mcs.value(); - run_result.avg_latency = std::chrono::microseconds(static_cast(tester.total_stats.avg_latency.value() / 1000)); + run_result.avg_latency = std::chrono::microseconds(static_cast(tester.total_stats.avg_latency.value() / 1000)); + run_result.q0_9_latency = std::chrono::microseconds( + tester.total_stats.latency_samples[static_cast(tester.total_stats.latency_samples.size() * 0.9)] / 1000); run_results.push_back(run_result); return SRSRAN_SUCCESS; @@ -243,12 +250,12 @@ run_data expected_run_result(run_params params) ret.avg_ul_throughput *= 0.75; break; case 15: - ret.avg_dl_throughput *= 0.95; + ret.avg_dl_throughput *= 0.94; ret.avg_ul_throughput *= 0.7; break; default: - ret.avg_dl_throughput *= 0.97; - ret.avg_ul_throughput *= 0.85; + ret.avg_dl_throughput *= 0.96; + ret.avg_ul_throughput *= 0.84; break; } return ret; @@ -257,9 +264,9 @@ run_data expected_run_result(run_params params) void print_benchmark_results(const std::vector& run_results) { srslog::flush(); - fmt::print("run | Nprb | cqi | sched pol | Nue | DL/UL [Mbps] | DL/UL mcs | DL/UL OH [%] | latency " + fmt::print("run | Nprb | cqi | sched pol | Nue | DL/UL [Mbps] | DL/UL mcs | DL/UL OH [%] | latency | latency q0.9 " "[usec]\n"); - fmt::print("---------------------------------------------------------------------------------------" + fmt::print("------------------------------------------------------------------------------------------------------" "------\n"); for (uint32_t i = 0; i < run_results.size(); ++i) { const run_data& r = run_results[i]; @@ -272,7 +279,7 @@ void print_benchmark_results(const std::vector& run_results) tbs = srsran_ra_tbs_from_idx(tbs_idx, nof_pusch_prbs); float ul_rate_overhead = 1.0F - r.avg_ul_throughput / (static_cast(tbs) * 1e3F); - fmt::print("{:>3d}{:>6d}{:>6d}{:>12}{:>6d}{:>9.2}/{:>4.2}{:>9.1f}/{:>4.1f}{:9.1f}/{:>4.1f}{:12d}\n", + fmt::print("{:>3d}{:>6d}{:>6d}{:>12}{:>6d}{:>9.2}/{:>4.2}{:>9.1f}/{:>4.1f}{:9.1f}/{:>4.1f}{:>9d}{:12d}\n", i, r.params.nof_prbs, r.params.cqi, @@ -284,7 +291,8 @@ void print_benchmark_results(const std::vector& run_results) r.avg_ul_mcs, dl_rate_overhead * 100, ul_rate_overhead * 100, - r.avg_latency.count()); + r.avg_latency.count(), + r.q0_9_latency.count()); } } diff --git a/srsenb/test/mac/sched_ca_test.cc b/srsenb/test/mac/sched_ca_test.cc index d9f85aa7e..476cc1a42 100644 --- a/srsenb/test/mac/sched_ca_test.cc +++ b/srsenb/test/mac/sched_ca_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/test/mac/sched_common_test_suite.cc b/srsenb/test/mac/sched_common_test_suite.cc index 9200b40a6..d8be1787d 100644 --- a/srsenb/test/mac/sched_common_test_suite.cc +++ b/srsenb/test/mac/sched_common_test_suite.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,7 +48,7 @@ int test_pusch_collisions(const sf_output_res_t& sf_out, uint32_t enb_cc_idx, co /* TEST: Check if there is space for PRACH */ bool is_prach_tti_tx_ul = - srsran_prach_tti_opportunity_config_fdd(cell_params.cfg.prach_config, to_tx_ul(sf_out.tti_rx).to_uint(), -1); + srsran_prach_in_window_config_fdd(cell_params.cfg.prach_config, to_tx_ul(sf_out.tti_rx).to_uint(), -1); if (is_prach_tti_tx_ul) { try_ul_fill({cell_params.cfg.prach_freq_offset, cell_params.cfg.prach_freq_offset + 6}, "PRACH"); } @@ -131,8 +131,7 @@ int test_pdsch_collisions(const sf_output_res_t& sf_out, uint32_t enb_cc_idx, co // forbid Data in DL if its ACKs conflict with PRACH for PRB==6 if (cell_params.nof_prb() == 6) { - if (srsran_prach_tti_opportunity_config_fdd( - cell_params.cfg.prach_config, to_tx_dl_ack(sf_out.tti_rx).to_uint(), -1)) { + if (srsran_prach_in_window_config_fdd(cell_params.cfg.prach_config, to_tx_dl_ack(sf_out.tti_rx).to_uint(), -1)) { dl_allocs.fill(0, dl_allocs.size()); } } @@ -226,7 +225,7 @@ int test_pdcch_collisions(const sf_output_res_t& sf_out, // Helper Function: checks if there is any collision. If not, fills the PDCCH mask auto try_cce_fill = [&](const srsran_dci_location_t& dci_loc, const char* ch) { uint32_t cce_start = dci_loc.ncce, cce_stop = dci_loc.ncce + (1u << dci_loc.L); - CONDERROR(dci_loc.L == 0, "The aggregation level %d is not valid", dci_loc.L); + CONDERROR(dci_loc.L > 3, "The aggregation level %d is not valid", dci_loc.L); CONDERROR( cce_start >= ncce or cce_stop > ncce, "The CCE positions (%u, %u) do not fit in PDCCH", cce_start, cce_stop); CONDERROR( @@ -275,7 +274,9 @@ int test_dci_content_common(const sf_output_res_t& sf_out, uint32_t enb_cc_idx) CONDERROR(pusch.tbs == 0, "Allocated PUSCH with invalid TBS=%d", pusch.tbs); CONDERROR(alloc_rntis.count(rnti) > 0, "The user rnti=0x%x got allocated multiple times in UL", rnti); alloc_rntis.insert(pusch.dci.rnti); - CONDERROR(not((pusch.current_tx_nb == 0) xor (pusch.dci.tb.rv != 0)), "Number of txs incorrectly set"); + CONDERROR(not(((pusch.current_tx_nb % 4) == 0) xor (pusch.dci.tb.rv != 0)), + "[rnti=0x%x] Number of txs incorrectly set", + rnti); if (not pusch.needs_pdcch) { // In case of non-adaptive retx or Msg3 continue; diff --git a/srsenb/test/mac/sched_common_test_suite.h b/srsenb/test/mac/sched_common_test_suite.h index 8594d20d2..f796c2bd4 100644 --- a/srsenb/test/mac/sched_common_test_suite.h +++ b/srsenb/test/mac/sched_common_test_suite.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,11 +22,11 @@ #ifndef SRSRAN_SCHED_COMMON_TEST_SUITE_H #define SRSRAN_SCHED_COMMON_TEST_SUITE_H -#include "srsenb/hdr/stack/mac/sched_common.h" -#include "srsran/adt/bounded_bitset.h" +#include "srsenb/hdr/stack/mac/sched_interface.h" +#include "srsenb/hdr/stack/mac/sched_lte_common.h" +#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h" #include "srsran/adt/span.h" #include "srsran/common/tti_point.h" -#include "srsran/interfaces/sched_interface.h" namespace srsenb { diff --git a/srsenb/test/mac/sched_cqi_test.cc b/srsenb/test/mac/sched_cqi_test.cc new file mode 100644 index 000000000..f5226fe22 --- /dev/null +++ b/srsenb/test/mac/sched_cqi_test.cc @@ -0,0 +1,102 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsenb/hdr/stack/mac/sched_ue_ctrl/sched_dl_cqi.h" +#include "srsran/common/test_common.h" + +namespace srsenb { + +void test_sched_cqi_one_subband_cqi() +{ + // 50 PRBs, K=4 + sched_dl_cqi ue_cqi(50, 4, 0); + + // J == 3, N == 9 + TESTASSERT(ue_cqi.nof_bandwidth_parts() == 3); + TESTASSERT(ue_cqi.nof_subbands() == 9); + + // Ni = 0 -> cqi=5 + ue_cqi.cqi_sb_info(tti_point(0), 0, 5); + + // TEST: updated part has positive cqi. Non-updated cqi didn't change + TESTASSERT(ue_cqi.get_grant_avg_cqi(rbg_interval(0, 1)) == 5); + for (uint32_t i = 1; i < 5; ++i) { + TESTASSERT(ue_cqi.get_grant_avg_cqi(rbg_interval(i, i + 1)) > 0); + } + TESTASSERT(ue_cqi.get_grant_avg_cqi(rbg_interval(6, cell_nof_prb_to_rbg(50))) == 0); + + // TEST: Check average cqi over a mask of RBGs + rbgmask_t mask(cell_nof_prb_to_rbg(50)); + mask.fill(10, mask.size()); + TESTASSERT(ue_cqi.get_grant_avg_cqi(mask) == 0); + mask.reset(); + mask.set(1); + TESTASSERT(ue_cqi.get_grant_avg_cqi(mask) == 5); + mask.fill(0, mask.size()); + TESTASSERT(ue_cqi.get_grant_avg_cqi(mask) > 0 and ue_cqi.get_grant_avg_cqi(mask) < 5); + + // TEST: Get optimal RBG mask in terms of CQI + mask = ue_cqi.get_optim_rbgmask(5); + TESTASSERT(mask.count() == 5); + for (uint32_t i = 0; i < 5; ++i) { + TESTASSERT(mask.test(i) > 0); + } +} + +void test_sched_cqi_wideband_cqi() +{ + uint32_t nof_prb = 50; + uint32_t nof_rbgs = cell_nof_prb_to_rbg(nof_prb); + + sched_dl_cqi ue_cqi(nof_prb, 0, 0); + + ue_cqi.cqi_wb_info(tti_point(0), 5); + + // TEST: all bandwidth has positive cqi. + for (uint32_t i = 0; i < nof_rbgs; ++i) { + TESTASSERT(ue_cqi.get_grant_avg_cqi(rbg_interval(i, i + 1)) == 5); + } + TESTASSERT(ue_cqi.get_grant_avg_cqi(rbg_interval(0, nof_rbgs)) == 5); + + // TEST: Check average cqi over a mask of RBGs + rbgmask_t mask(cell_nof_prb_to_rbg(50)); + mask.fill(10, mask.size()); + TESTASSERT(ue_cqi.get_grant_avg_cqi(mask) == 5); + + // TEST: Get optimal RBG mask in terms of CQI + mask = ue_cqi.get_optim_rbgmask(5); + TESTASSERT(mask.count() == 5); + for (uint32_t i = 0; i < 5; ++i) { + TESTASSERT(mask.test(i) > 0); + } +} + +} // namespace srsenb + +int main(int argc, char** argv) +{ + srsran::test_init(argc, argv); + + srsenb::test_sched_cqi_one_subband_cqi(); + srsenb::test_sched_cqi_wideband_cqi(); + + return SRSRAN_SUCCESS; +} diff --git a/srsenb/test/mac/sched_dci_test.cc b/srsenb/test/mac/sched_dci_test.cc index 107def96b..687d15a08 100644 --- a/srsenb/test/mac/sched_dci_test.cc +++ b/srsenb/test/mac/sched_dci_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,10 +20,10 @@ */ #include "sched_test_utils.h" -#include "srsenb/hdr/stack/mac/sched_common.h" +#include "srsenb/hdr/stack/mac/sched_lte_common.h" #include "srsenb/hdr/stack/mac/sched_phy_ch/sched_dci.h" #include "srsran/common/common_lte.h" -#include "srsran/common/test_common.h" +#include "srsran/support/srsran_test.h" namespace srsenb { @@ -260,7 +260,7 @@ int test_min_mcs_tbs_specific() CONDERROR(result.tbs_bytes * 8 != 120, "Invalid min TBS calculation"); args.req_bytes = 50; - TESTASSERT(test_min_mcs_tbs_dl_helper(cell_params, args, &result) == SRSRAN_SUCCESS); + TESTASSERT_SUCCESS(test_min_mcs_tbs_dl_helper(cell_params, args, &result)); CONDERROR(result.tbs_bytes < (int)args.req_bytes, "Invalid MCS calculation"); CONDERROR(result.tbs_bytes * 8 != 424, "Invalid min TBS calculation"); @@ -273,13 +273,50 @@ int test_min_mcs_tbs_specific() // Check equality case args.req_bytes = 109; - TESTASSERT(test_min_mcs_tbs_dl_helper(cell_params, args, &result) == SRSRAN_SUCCESS); + TESTASSERT_SUCCESS(test_min_mcs_tbs_dl_helper(cell_params, args, &result)); CONDERROR(result.tbs_bytes < (int)args.req_bytes, "Invalid MCS calculation"); CONDERROR(result.tbs_bytes * 8 != 872, "Invalid min TBS calculation"); return SRSRAN_SUCCESS; } +void test_ul_mcs_tbs_derivation() +{ + uint32_t cqi = 15; + uint32_t max_mcs = 28; + + sched_cell_params_t cell_params; + prbmask_t prbs; + + auto compute_tbs_mcs = [&prbs, &cell_params, &max_mcs, &cqi](uint32_t Nprb, uint32_t prb_grant_size) { + sched_interface::cell_cfg_t cell_cfg = generate_default_cell_cfg(Nprb); + sched_interface::sched_args_t sched_args = {}; + cell_params.set_cfg(0, cell_cfg, sched_args); + prbs.resize(Nprb); + prbs.fill(2, prb_grant_size); + uint32_t req_bytes = 1000000; + uint32_t N_srs = 0; + uint32_t nof_symb = 2 * (SRSRAN_CP_NSYMB(cell_params.cfg.cell.cp) - 1) - N_srs; + uint32_t nof_re = nof_symb * prbs.count() * SRSRAN_NRE; + return compute_min_mcs_and_tbs_from_required_bytes( + prbs.count(), nof_re, cqi, max_mcs, req_bytes, true, false, false); + }; + + cqi = 0; + TESTASSERT_EQ(0, compute_tbs_mcs(25, 25 - 4).mcs); + TESTASSERT_EQ(0, compute_tbs_mcs(50, 50 - 5).mcs); + + cqi = 5; + TESTASSERT_EQ(9, compute_tbs_mcs(25, 25 - 4).mcs); + TESTASSERT_EQ(9, compute_tbs_mcs(50, 50 - 5).mcs); + + cqi = 15; + TESTASSERT_EQ(23, compute_tbs_mcs(25, 25 - 4).mcs); + TESTASSERT_EQ(23, compute_tbs_mcs(50, 50 - 5).mcs); + TESTASSERT_EQ(24, compute_tbs_mcs(75, 75 - 5).mcs); + TESTASSERT_EQ(23, compute_tbs_mcs(100, 100 - 5).mcs); +} + } // namespace srsenb int main() @@ -295,7 +332,8 @@ int main() TESTASSERT(srsenb::test_mcs_lookup_specific() == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_mcs_tbs_consistency_all() == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_min_mcs_tbs_specific() == SRSRAN_SUCCESS); + srsenb::test_ul_mcs_tbs_derivation(); printf("Success\n"); return 0; -} \ No newline at end of file +} diff --git a/srsenb/test/mac/sched_grid_test.cc b/srsenb/test/mac/sched_grid_test.cc index e6ffb8f81..34f2ae16c 100644 --- a/srsenb/test/mac/sched_grid_test.cc +++ b/srsenb/test/mac/sched_grid_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/test/mac/sched_lc_ch_test.cc b/srsenb/test/mac/sched_lc_ch_test.cc index 38d077828..480a529a9 100644 --- a/srsenb/test/mac/sched_lc_ch_test.cc +++ b/srsenb/test/mac/sched_lc_ch_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -53,7 +53,7 @@ int test_pdu_alloc_successful(srsenb::lch_ue_manager& lch_handler, int test_retx_until_empty(srsenb::lch_ue_manager& lch_handler, int lcid, uint32_t rlc_payload_size) { - int start_rlc_bytes = lch_handler.get_dl_retx(lcid); + int start_rlc_bytes = lch_handler.get_dl_prio_tx(lcid); int nof_pdus = ceil(static_cast(start_rlc_bytes) / static_cast(rlc_payload_size)); int rem_rlc_bytes = start_rlc_bytes; @@ -62,7 +62,7 @@ int test_retx_until_empty(srsenb::lch_ue_manager& lch_handler, int lcid, uint32_ uint32_t expected_payload_size = std::min(rlc_payload_size, (uint32_t)rem_rlc_bytes); TESTASSERT(test_pdu_alloc_successful(lch_handler, pdu, lcid, expected_payload_size) == SRSRAN_SUCCESS); rem_rlc_bytes -= expected_payload_size; - TESTASSERT(lch_handler.get_dl_retx(lcid) == rem_rlc_bytes); + TESTASSERT(lch_handler.get_dl_prio_tx(lcid) == rem_rlc_bytes); } return start_rlc_bytes; } @@ -85,17 +85,17 @@ int test_newtx_until_empty(srsenb::lch_ue_manager& lch_handler, int lcid, uint32 int test_lc_ch_pbr_infinity() { - srsenb::lch_ue_manager lch_handler; + srsenb::lch_ue_manager lch_handler{0x46}; srsenb::sched_interface::ue_cfg_t ue_cfg = generate_default_ue_cfg(); ue_cfg = generate_setup_ue_cfg(ue_cfg); ue_cfg.ue_bearers[srb_to_lcid((lte_srb::srb1))] = {}; - ue_cfg.ue_bearers[srb_to_lcid((lte_srb::srb1))].direction = sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[srb_to_lcid((lte_srb::srb1))].direction = mac_lc_ch_cfg_t::BOTH; ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))] = {}; - ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))].direction = sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))].direction = mac_lc_ch_cfg_t::BOTH; ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))].priority = 5; ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))] = {}; - ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))].direction = sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))].direction = mac_lc_ch_cfg_t::BOTH; ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))].priority = 3; lch_handler.set_cfg(ue_cfg); @@ -106,15 +106,15 @@ int test_lc_ch_pbr_infinity() lch_handler.dl_buffer_state(drb_to_lcid(lte_drb::drb2), 5000, 10000); // TEST1 - retx of SRB1 is prioritized. Do not transmit other bearers until there are no SRB1 retxs - int nof_pending_bytes = lch_handler.get_dl_retx(srb_to_lcid(lte_srb::srb1)); + int nof_pending_bytes = lch_handler.get_dl_prio_tx(srb_to_lcid(lte_srb::srb1)); TESTASSERT(test_retx_until_empty(lch_handler, srb_to_lcid(lte_srb::srb1), 500) == nof_pending_bytes); // TEST2 - the DRB2 has lower prio level than SRB1, but has retxs - nof_pending_bytes = lch_handler.get_dl_retx(drb_to_lcid(lte_drb::drb2)); + nof_pending_bytes = lch_handler.get_dl_prio_tx(drb_to_lcid(lte_drb::drb2)); TESTASSERT(test_retx_until_empty(lch_handler, drb_to_lcid(lte_drb::drb2), 500) == nof_pending_bytes); // TEST3 - the DRB1 has lower prio level, but has retxs - nof_pending_bytes = lch_handler.get_dl_retx(drb_to_lcid(lte_drb::drb1)); + nof_pending_bytes = lch_handler.get_dl_prio_tx(drb_to_lcid(lte_drb::drb1)); TESTASSERT(test_retx_until_empty(lch_handler, drb_to_lcid(lte_drb::drb1), 500) == nof_pending_bytes); // TEST4 - The SRB1 newtx buffer is emptied before other bearers newtxs @@ -134,20 +134,20 @@ int test_lc_ch_pbr_infinity() int test_lc_ch_pbr_finite() { - srsenb::lch_ue_manager lch_handler; + srsenb::lch_ue_manager lch_handler{0x46}; sched_interface::dl_sched_pdu_t pdu; srsenb::sched_interface::ue_cfg_t ue_cfg = generate_default_ue_cfg(); ue_cfg = generate_setup_ue_cfg(ue_cfg); ue_cfg.ue_bearers[srb_to_lcid((lte_srb::srb1))] = {}; - ue_cfg.ue_bearers[srb_to_lcid((lte_srb::srb1))].direction = sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[srb_to_lcid((lte_srb::srb1))].direction = mac_lc_ch_cfg_t::BOTH; ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))] = {}; - ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))].direction = sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))].direction = mac_lc_ch_cfg_t::BOTH; ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))].pbr = 256; // kBps ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))].bsd = 50; // msec ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb1))].priority = 5; ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))] = {}; - ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))].direction = sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))].direction = mac_lc_ch_cfg_t::BOTH; ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))].pbr = 8; // kBps ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))].bsd = 50; // msec ue_cfg.ue_bearers[drb_to_lcid((lte_drb::drb2))].priority = 3; @@ -163,11 +163,11 @@ int test_lc_ch_pbr_finite() lch_handler.dl_buffer_state(drb_to_lcid(lte_drb::drb2), 50000, 0); // TEST1 - SRB1 retxs are emptied first - int nof_pending_bytes = lch_handler.get_dl_retx(srb_to_lcid(lte_srb::srb1)); + int nof_pending_bytes = lch_handler.get_dl_prio_tx(srb_to_lcid(lte_srb::srb1)); TESTASSERT(test_retx_until_empty(lch_handler, srb_to_lcid(lte_srb::srb1), 500) == nof_pending_bytes); // TEST2 - DRB1 retxs are emptied - nof_pending_bytes = lch_handler.get_dl_retx(drb_to_lcid(lte_drb::drb1)); + nof_pending_bytes = lch_handler.get_dl_prio_tx(drb_to_lcid(lte_drb::drb1)); TESTASSERT(test_retx_until_empty(lch_handler, drb_to_lcid(lte_drb::drb1), 500) == nof_pending_bytes); // TEST3 - SRB1 newtxs are emptied (PBR==infinity) diff --git a/srsenb/test/mac/sched_phy_resource_test.cc b/srsenb/test/mac/sched_phy_resource_test.cc new file mode 100644 index 000000000..e16883161 --- /dev/null +++ b/srsenb/test/mac/sched_phy_resource_test.cc @@ -0,0 +1,78 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsenb/hdr/stack/mac/sched_phy_ch/sched_phy_resource.h" +#include "srsran/common/common_lte.h" +#include "srsran/common/test_common.h" +#include + +namespace srsenb { + +std::random_device rd; +std::mt19937 rand_gen(rd()); + +uint32_t get_rand_Nrbg() +{ + return cell_nof_prb_to_rbg(srsran::lte_cell_nof_prbs[std::uniform_int_distribution{ + 0, srsran::lte_cell_nof_prbs.size() - 1}(rand_gen)]); +} + +void test_rbg_mask_helpers() +{ + rbgmask_t rbgs(MAX_NOF_RBGS); + + // TEST: Find contiguous range of zero RBGs in RBG mask + rbgs.set(0); + rbgs.set(2); + rbg_interval interv = find_empty_rbg_interval(1, rbgs); + TESTASSERT(not interv.empty() and interv.length() == 1 and interv.start() == 1); + interv = find_empty_rbg_interval(2, rbgs); + TESTASSERT(not interv.empty() and interv.length() == 2 and interv.start() == 3); + interv = find_empty_rbg_interval(rbgs.size(), rbgs); + TESTASSERT(interv.length() + 3 == rbgs.size() and interv.start() == 3); + + // TEST: find mask of zero RBGs in RBG mask + rbgmask_t empty_rbgs = find_available_rbgmask(1, false, rbgs); + TESTASSERT(empty_rbgs.count() == 1 and empty_rbgs.test(1)); + empty_rbgs = find_available_rbgmask(5, false, rbgs); + TESTASSERT(empty_rbgs.count() == 5 and empty_rbgs.test(1) and empty_rbgs.test(3) and not empty_rbgs.test(2)); + + // TEST: find mask of zero RBGs in random RBG mask + std::bernoulli_distribution dist{0.5}; + rbgs = rbgmask_t(get_rand_Nrbg()); + for (size_t i = 0; i < rbgs.size(); ++i) { + rbgs.set(i, dist(rand_gen)); + } + empty_rbgs = find_available_rbgmask(rbgs.size(), false, rbgs); + TESTASSERT(empty_rbgs == ~rbgs); + uint32_t L = std::uniform_int_distribution{1, (uint32_t)rbgs.size() - 1}(rand_gen); + empty_rbgs = find_available_rbgmask(L, false, rbgs); + TESTASSERT(empty_rbgs.count() <= L and (empty_rbgs & rbgs).none()); + uint32_t nprb = count_prb_per_tb(rbgs); + TESTASSERT(nprb <= MAX_NOF_PRBS); +} + +} // namespace srsenb + +int main() +{ + srsenb::test_rbg_mask_helpers(); +} diff --git a/srsenb/test/mac/sched_sim_ue.cc b/srsenb/test/mac/sched_sim_ue.cc index 193d6a6bd..3b9fa444a 100644 --- a/srsenb/test/mac/sched_sim_ue.cc +++ b/srsenb/test/mac/sched_sim_ue.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -70,7 +70,7 @@ void ue_sim::set_cfg(const sched_interface::ue_cfg_t& ue_cfg_) } } -void ue_sim::bearer_cfg(uint32_t lc_id, const sched_interface::ue_bearer_cfg_t& cfg) +void ue_sim::bearer_cfg(uint32_t lc_id, const mac_lc_ch_cfg_t& cfg) { ctxt.ue_cfg.ue_bearers.at(lc_id) = cfg; } @@ -115,35 +115,44 @@ void ue_sim::update_ul_harqs(const sf_output_res_t& sf_out) { uint32_t pid = to_tx_ul(sf_out.tti_rx).to_uint() % (FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS); for (uint32_t cc = 0; cc < sf_out.cc_params.size(); ++cc) { + const auto *cc_cfg = ctxt.get_cc_cfg(cc), *start = &ctxt.ue_cfg.supported_cc_list[0]; + if (cc_cfg == nullptr) { + continue; + } + uint32_t ue_cc_idx = std::distance(start, cc_cfg); + auto& ue_cc_ctxt = ctxt.cc_list[ue_cc_idx]; + auto& h = ue_cc_ctxt.ul_harqs[pid]; + // Update UL harqs with PHICH info + bool found_phich = false; + bool is_msg3 = h.nof_txs == h.nof_retxs + 1 and ctxt.msg3_tti_rx.is_valid() and h.first_tti_rx == ctxt.msg3_tti_rx; + uint32_t max_retxs = is_msg3 ? sf_out.cc_params[0].cfg.maxharq_msg3tx : ctxt.ue_cfg.maxharq_tx; + bool last_retx = h.nof_retxs + 1 >= max_retxs; for (uint32_t i = 0; i < sf_out.ul_cc_result[cc].phich.size(); ++i) { const auto& phich = sf_out.ul_cc_result[cc].phich[i]; if (phich.rnti != ctxt.rnti) { continue; } - - const auto *cc_cfg = ctxt.get_cc_cfg(cc), *start = &ctxt.ue_cfg.supported_cc_list[0]; - uint32_t ue_cc_idx = std::distance(start, cc_cfg); - auto& ue_cc_ctxt = ctxt.cc_list[ue_cc_idx]; - auto& h = ue_cc_ctxt.ul_harqs[pid]; + found_phich = true; bool is_ack = phich.phich == phich_t::ACK; - bool is_msg3 = - h.nof_txs == h.nof_retxs + 1 and ctxt.msg3_tti_rx.is_valid() and h.first_tti_rx == ctxt.msg3_tti_rx; - bool last_retx = h.nof_retxs + 1 >= (is_msg3 ? sf_out.cc_params[0].cfg.maxharq_msg3tx : ctxt.ue_cfg.maxharq_tx); if (is_ack or last_retx) { h.active = false; } } + if (not found_phich and h.active) { + // There can be missing PHICH due to measGap collisions. In such case, we deactivate the harq and assume hi=1 + h.active = false; + } // Update UL harqs with PUSCH grants + bool pusch_found = false; for (uint32_t i = 0; i < sf_out.ul_cc_result[cc].pusch.size(); ++i) { const auto& data = sf_out.ul_cc_result[cc].pusch[i]; if (data.dci.rnti != ctxt.rnti) { continue; } - auto& ue_cc_ctxt = ctxt.cc_list[data.dci.ue_cc_idx]; - auto& h = ue_cc_ctxt.ul_harqs[to_tx_ul(sf_out.tti_rx).to_uint() % ue_cc_ctxt.ul_harqs.size()]; + pusch_found = true; if (h.nof_txs == 0 or h.ndi != data.dci.tb.ndi) { // newtx @@ -159,6 +168,11 @@ void ue_sim::update_ul_harqs(const sf_output_res_t& sf_out) h.riv = data.dci.type2_alloc.riv; h.nof_txs++; } + if (not pusch_found and h.nof_retxs < max_retxs) { + // PUSCH *may* be skipped due to measGap. nof_retxs keeps getting incremented + h.nof_retxs++; + h.nof_txs++; + } } } @@ -264,7 +278,7 @@ int sched_sim_base::ue_recfg(uint16_t rnti, const sched_interface::ue_cfg_t& ue_ return SRSRAN_SUCCESS; } -int sched_sim_base::bearer_cfg(uint16_t rnti, uint32_t lc_id, const sched_interface::ue_bearer_cfg_t& cfg) +int sched_sim_base::bearer_cfg(uint16_t rnti, uint32_t lc_id, const mac_lc_ch_cfg_t& cfg) { ue_db.at(rnti).bearer_cfg(lc_id, cfg); return sched_ptr->bearer_ue_cfg(rnti, lc_id, cfg); diff --git a/srsenb/test/mac/sched_sim_ue.h b/srsenb/test/mac/sched_sim_ue.h index 9b7b2c82b..95de284ee 100644 --- a/srsenb/test/mac/sched_sim_ue.h +++ b/srsenb/test/mac/sched_sim_ue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,7 @@ #define SRSRAN_SCHED_SIM_UE_H #include "sched_common_test_suite.h" -#include "srsran/interfaces/sched_interface.h" +#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/srslog/srslog.h" #include #include @@ -35,7 +35,7 @@ struct ue_harq_ctxt_t { bool ndi = false; uint32_t pid = 0; uint32_t nof_txs = 0; - uint32_t nof_retxs = 0; + uint32_t nof_retxs = std::numeric_limits::max(); uint32_t riv = 0; srsran_dci_location_t dci_loc = {}; uint32_t tbs = 0; @@ -89,7 +89,7 @@ public: uint32_t preamble_idx); void set_cfg(const sched_interface::ue_cfg_t& ue_cfg_); - void bearer_cfg(uint32_t lc_id, const sched_interface::ue_bearer_cfg_t& cfg); + void bearer_cfg(uint32_t lc_id, const mac_lc_ch_cfg_t& cfg); int update(const sf_output_res_t& sf_out); @@ -115,7 +115,7 @@ public: int add_user(uint16_t rnti, const sched_interface::ue_cfg_t& ue_cfg_, uint32_t preamble_idx); int ue_recfg(uint16_t rnti, const sched_interface::ue_cfg_t& ue_cfg_); - int bearer_cfg(uint16_t rnti, uint32_t lc_id, const sched_interface::ue_bearer_cfg_t& cfg); + int bearer_cfg(uint16_t rnti, uint32_t lc_id, const mac_lc_ch_cfg_t& cfg); int rem_user(uint16_t rnti); void new_tti(srsran::tti_point tti_rx); diff --git a/srsenb/test/mac/sched_test_common.cc b/srsenb/test/mac/sched_test_common.cc index b8b22452c..97a17344f 100644 --- a/srsenb/test/mac/sched_test_common.cc +++ b/srsenb/test/mac/sched_test_common.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/test/mac/sched_test_common.h b/srsenb/test/mac/sched_test_common.h index 0b9cb95b1..ea39b5969 100644 --- a/srsenb/test/mac/sched_test_common.h +++ b/srsenb/test/mac/sched_test_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,7 +25,7 @@ #include "sched_sim_ue.h" #include "sched_test_utils.h" #include "srsenb/hdr/stack/mac/sched.h" -#include "srsran/interfaces/enb_rrc_interfaces.h" +#include "srsran/interfaces/enb_rrc_interface_mac.h" #include "srsran/srslog/srslog.h" #include @@ -48,6 +48,7 @@ public: void set_radiolink_dl_state(uint16_t rnti, bool crc_res) {} bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { return false; } uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) { return nullptr; } + void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t n_bytes) {} }; /************************** diff --git a/srsenb/test/mac/sched_test_rand.cc b/srsenb/test/mac/sched_test_rand.cc index 466354d8a..8ddcd9688 100644 --- a/srsenb/test/mac/sched_test_rand.cc +++ b/srsenb/test/mac/sched_test_rand.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,7 +28,7 @@ #include #include -#include "srsran/interfaces/sched_interface.h" +#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/phy/utils/debug.h" #include "sched_common_test_suite.h" @@ -137,7 +137,7 @@ void sched_tester::before_sched() for (auto& it : ue_db) { uint16_t rnti = it.first; srsenb::sched_ue* user = it.second.get(); - tester_user_results d; + tester_user_results d = {}; tti_data.ue_data.insert(std::make_pair(rnti, d)); // NOTE: ACK might have just cleared the harq for tti_info.tti_params.tti_tx_ul @@ -283,6 +283,7 @@ sched_sim_events rand_sim_params(uint32_t nof_ttis) sim_gen.sim_args.cell_cfg[0].target_pucch_ul_sinr = pick_random_uniform({10, 15, 20, -1}); sim_gen.sim_args.cell_cfg[0].target_pusch_ul_sinr = pick_random_uniform({10, 15, 20, -1}); sim_gen.sim_args.cell_cfg[0].enable_phr_handling = false; + sim_gen.sim_args.cell_cfg[0].min_phr_thres = 0; sim_gen.sim_args.default_ue_sim_cfg.ue_cfg = generate_default_ue_cfg(); sim_gen.sim_args.default_ue_sim_cfg.periodic_cqi = true; sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.maxharq_tx = std::uniform_int_distribution<>{1, 5}(srsenb::get_rand_gen()); @@ -294,7 +295,7 @@ sched_sim_events rand_sim_params(uint32_t nof_ttis) sim_gen.sim_args.default_ue_sim_cfg.prob_ul_ack_mask.back() = 1; sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.measgap_period = pick_random_uniform({0, 40, 80}); sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.measgap_offset = std::uniform_int_distribution{ - 0, sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.measgap_period}(srsenb::get_rand_gen()); + 0, std::max(sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.measgap_period, 1u) - 1}(srsenb::get_rand_gen()); sim_gen.sim_args.default_ue_sim_cfg.ue_cfg.pucch_cfg.n_pucch_sr = std::uniform_int_distribution{0, 2047}(srsenb::get_rand_gen()); @@ -303,6 +304,7 @@ sched_sim_events rand_sim_params(uint32_t nof_ttis) boolean_dist() ? -1 : std::uniform_int_distribution<>{0, 24}(srsenb::get_rand_gen()); sim_gen.sim_args.sched_args.pusch_mcs = boolean_dist() ? -1 : std::uniform_int_distribution<>{0, 24}(srsenb::get_rand_gen()); + sim_gen.sim_args.sched_args.min_aggr_level = std::uniform_int_distribution<>{0, 3}(srsenb::get_rand_gen()); generator.tti_events.resize(nof_ttis); @@ -354,7 +356,7 @@ int main() } auto& mac_log = srslog::fetch_basic_logger("MAC"); - mac_log.set_level(srslog::basic_levels::info); + mac_log.set_level(srslog::basic_levels::debug); auto& test_log = srslog::fetch_basic_logger("TEST", *spy, false); test_log.set_level(srslog::basic_levels::info); diff --git a/srsenb/test/mac/sched_test_utils.h b/srsenb/test/mac/sched_test_utils.h index 2b7a25e19..d80e3940f 100644 --- a/srsenb/test/mac/sched_test_utils.h +++ b/srsenb/test/mac/sched_test_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,8 +24,8 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsenb/hdr/stack/mac/sched.h" +#include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/common/test_common.h" -#include "srsran/interfaces/sched_interface.h" #include #include #include @@ -58,9 +58,10 @@ inline srsenb::sched_interface::cell_cfg_t generate_default_cell_cfg(uint32_t no cell_cfg.prach_freq_offset = (cell_cfg_phy.nof_prb == 6) ? 0 : 4; cell_cfg.prach_rar_window = 3; cell_cfg.maxharq_msg3tx = 3; - cell_cfg.initial_dl_cqi = 6; cell_cfg.target_pusch_ul_sinr = -1; cell_cfg.target_pucch_ul_sinr = -1; + cell_cfg.enable_phr_handling = false; + cell_cfg.min_phr_thres = 0; cell_cfg.nrb_cqi = 1; cell_cfg.n1pucch_an = 12; cell_cfg.delta_pucch_shift = 1; @@ -79,10 +80,10 @@ inline srsenb::sched_interface::ue_cfg_t generate_default_ue_cfg() ue_cfg.supported_cc_list[0].enb_cc_idx = 0; ue_cfg.supported_cc_list[0].active = true; ue_cfg.supported_cc_list[0].dl_cfg.tm = SRSRAN_TM1; - ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; + ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; + ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; + ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].group = 1; ue_cfg.pucch_cfg.sr_configured = true; @@ -96,9 +97,9 @@ inline srsenb::sched_interface::ue_cfg_t generate_default_ue_cfg2() { srsenb::sched_interface::ue_cfg_t ue_cfg = generate_default_ue_cfg(); - ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; + ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; + ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].group = 1; return ue_cfg; @@ -107,7 +108,7 @@ inline srsenb::sched_interface::ue_cfg_t generate_default_ue_cfg2() inline srsenb::sched_interface::ue_cfg_t generate_rach_ue_cfg(const srsenb::sched_interface::ue_cfg_t& final_cfg) { srsenb::sched_interface::ue_cfg_t cfg = {}; - cfg.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + cfg.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; cfg.supported_cc_list.resize(1); cfg.supported_cc_list[0].enb_cc_idx = final_cfg.supported_cc_list[0].enb_cc_idx; cfg.supported_cc_list[0].active = true; @@ -119,7 +120,7 @@ inline srsenb::sched_interface::ue_cfg_t generate_setup_ue_cfg(const srsenb::sch srsenb::sched_interface::ue_cfg_t cfg = generate_rach_ue_cfg(final_cfg); cfg.maxharq_tx = final_cfg.maxharq_tx; - cfg.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + cfg.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; cfg.supported_cc_list[0].dl_cfg.tm = SRSRAN_TM1; cfg.continuous_pusch = final_cfg.continuous_pusch; @@ -158,11 +159,11 @@ struct tti_ev { uint32_t dl_nof_retxs = 0; ///< update DL buffer retx }; struct user_cfg_ev { - uint16_t rnti; - std::unique_ptr ue_sim_cfg; ///< optional ue_cfg call - std::unique_ptr bearer_cfg; ///< optional bearer_cfg call - std::unique_ptr buffer_ev; ///< update of a user dl/ul buffer - bool rem_user = false; ///< whether to remove a ue + uint16_t rnti; + std::unique_ptr ue_sim_cfg; ///< optional ue_cfg call + std::unique_ptr bearer_cfg; ///< optional bearer_cfg call + std::unique_ptr buffer_ev; ///< update of a user dl/ul buffer + bool rem_user = false; ///< whether to remove a ue }; std::vector user_updates; }; @@ -267,11 +268,9 @@ struct sched_sim_event_generator { ue_sim_cfg.ue_cfg = generate_default_ue_cfg(); user->ue_sim_cfg.reset(new ue_ctxt_test_cfg{ue_sim_cfg}); // it should by now have a DRB1. Add other DRBs manually - user->ue_sim_cfg->ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction = - srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - user->ue_sim_cfg->ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction = - srsenb::sched_interface::ue_bearer_cfg_t::BOTH; - user->ue_sim_cfg->ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].group = 1; + user->ue_sim_cfg->ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; + user->ue_sim_cfg->ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction = srsenb::mac_lc_ch_cfg_t::BOTH; + user->ue_sim_cfg->ue_cfg.ue_bearers[drb_to_lcid(lte_drb::drb1)].group = 1; return user; } diff --git a/srsenb/test/mac/sched_tpc_test.cc b/srsenb/test/mac/sched_tpc_test.cc index 1ce863351..b61c83054 100644 --- a/srsenb/test/mac/sched_tpc_test.cc +++ b/srsenb/test/mac/sched_tpc_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,9 +35,14 @@ int test_finite_target_snr() const uint32_t nof_prbs = 50; const int target_snr = 15; - tpc tpcfsm(nof_prbs, 15, 15, true); + tpc tpcfsm(0x46, nof_prbs, 15, 15, true); - // TEST: While no SNR info is provided, no TPC commands are sent + // TEST: While UL SNR ~ target, no TPC commands are sent + for (uint32_t i = 0; i < 100 and tpcfsm.get_ul_snr_estim(0) < 14; ++i) { + tpcfsm.set_snr(15, 0); + tpcfsm.set_snr(15, 1); + tpcfsm.new_tti(); + } for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); TESTASSERT(decode_tpc(tpcfsm.encode_pucch_tpc()) == 0); @@ -75,6 +80,42 @@ int test_finite_target_snr() TESTASSERT(sum_pucch > 0 and sum_pucch <= -snr_diff); } + // TEST: PHR is negative. Checks: + // - TPCs sent should be negative or zero + // - The accumulation of TPCs should lead to next PHR being zero. + snr_diff = -10; + int next_phr = -2; + tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE); + tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE); + sum_pucch = 0; + for (uint32_t i = 0; i < 3; ++i) { + tpcfsm.set_phr(next_phr, 1); + for (uint32_t j = 0; j < 100; ++j) { + tpcfsm.new_tti(); + int tpc_pusch = decode_tpc(tpcfsm.encode_pusch_tpc()); + TESTASSERT(tpc_pusch <= 0); + next_phr -= tpc_pusch; + sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); + } + TESTASSERT(next_phr == 0); + } + TESTASSERT(sum_pucch == -snr_diff); // PUCCH doesnt get affected by neg PHR + + // TEST: PHR is positive and SINR < target SINR. Checks: + // - accumulation of TPCs should not make next PHR negative + // - TPCs should be positive or zero + next_phr = 5; + snr_diff = -10; + tpcfsm.set_phr(next_phr, 1); + tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE); + for (uint32_t j = 0; j < 100; ++j) { + tpcfsm.new_tti(); + int tpc_pusch = decode_tpc(tpcfsm.encode_pusch_tpc()); + next_phr -= tpc_pusch; + TESTASSERT(tpc_pusch >= 0); + } + TESTASSERT(next_phr == 0); + return SRSRAN_SUCCESS; } @@ -82,7 +123,7 @@ int test_undefined_target_snr() { const uint32_t nof_prbs = 50; - tpc tpcfsm(nof_prbs, -1, -1, true); + tpc tpcfsm(0x46, nof_prbs, -1, -1, true); TESTASSERT(tpcfsm.max_ul_prbs() == 50); // TEST: While the PHR is not updated, a limited number of TPC commands should be sent @@ -111,7 +152,7 @@ int test_undefined_target_snr() // TEST: Check that high PHR allows full utilization of available PRBs, TPC remains at zero (no target SINR) int phr = 30; - tpcfsm.set_phr(phr); + tpcfsm.set_phr(phr, 1); TESTASSERT(tpcfsm.max_ul_prbs() == 50); sum_pusch = 0; sum_pucch = 0; @@ -124,7 +165,7 @@ int test_undefined_target_snr() // TEST: PHR is too low to allow all PRBs to be allocated. This event should not affect TPC commands phr = 5; - tpcfsm.set_phr(phr); + tpcfsm.set_phr(phr, 1); TESTASSERT(tpcfsm.max_ul_prbs() < 50); for (uint32_t i = 0; i < 100; ++i) { tpcfsm.new_tti(); @@ -134,7 +175,7 @@ int test_undefined_target_snr() // TEST: PHR is negative. The TPC should slightly decrease Tx UL power until next PHR phr = -1; - tpcfsm.set_phr(phr); + tpcfsm.set_phr(phr, 1); TESTASSERT(tpcfsm.max_ul_prbs() == tpc::PHR_NEG_NOF_PRB); sum_pusch = 0; sum_pucch = 0; @@ -149,11 +190,49 @@ int test_undefined_target_snr() return SRSRAN_SUCCESS; } +void test_finite_target_snr_tpc_period_above_1() +{ + const uint32_t nof_prbs = 50; + const int target_snr = 15; + + tpc tpcfsm(0x46, nof_prbs, 15, 15, true, 0, 5); + + // TEST: While UL SNR ~ target, no TPC commands are sent + for (uint32_t i = 0; i < 100 and tpcfsm.get_ul_snr_estim(0) < 14; ++i) { + tpcfsm.set_snr(15, 0); + tpcfsm.set_snr(15, 1); + tpcfsm.new_tti(); + } + for (uint32_t i = 0; i < 100; ++i) { + tpcfsm.new_tti(); + TESTASSERT(decode_tpc(tpcfsm.encode_pucch_tpc()) == 0); + TESTASSERT(decode_tpc(tpcfsm.encode_pusch_tpc()) == 0); + } + + // TEST: current SNR above target SNR. Checks: + // - TPC commands should be sent to decrease power + // - The sum power of TPC commands should not exceed the difference between current and target SNRs + int snr_diff = 10; + tpcfsm.set_snr(target_snr + snr_diff, tpc::PUSCH_CODE); + tpcfsm.set_snr(target_snr + snr_diff, tpc::PUCCH_CODE); + int sum_pusch = 0, sum_pucch = 0; + for (uint32_t i = 0; i < 100; ++i) { + tpcfsm.new_tti(); + int tpc = decode_tpc(tpcfsm.encode_pusch_tpc()); + TESTASSERT(tpc <= 0); + sum_pusch += tpc; + sum_pucch += decode_tpc(tpcfsm.encode_pucch_tpc()); + TESTASSERT(sum_pucch < 0 and sum_pucch >= -snr_diff); + } + TESTASSERT(sum_pusch == -snr_diff); +} + } // namespace srsenb int main() { TESTASSERT(srsenb::test_finite_target_snr() == 0); TESTASSERT(srsenb::test_undefined_target_snr() == 0); + srsenb::test_finite_target_snr_tpc_period_above_1(); printf("Success\n"); } diff --git a/srsenb/test/mac/sched_ue_cell_test.cc b/srsenb/test/mac/sched_ue_cell_test.cc new file mode 100644 index 000000000..d73521d58 --- /dev/null +++ b/srsenb/test/mac/sched_ue_cell_test.cc @@ -0,0 +1,155 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_test_common.h" +#include "sched_test_utils.h" +#include "srsenb/hdr/stack/mac/sched_ue.h" +#include "srsran/common/test_common.h" + +using namespace srsenb; +const uint32_t seed = std::chrono::system_clock::now().time_since_epoch().count(); + +/** + * Test scenario where PHR < 0, and the UL grant size is limited. + * - The UL grant size should be the smallest possible that guarantees fitting both a BSR, RLC header, and some RLC + * payload. + */ +void test_neg_phr_scenario() +{ + sched_interface::cell_cfg_t cell_cfg = generate_default_cell_cfg(50); + cell_cfg.target_pucch_ul_sinr = 20; + cell_cfg.target_pusch_ul_sinr = 20; + cell_cfg.min_phr_thres = 0; + cell_cfg.enable_phr_handling = true; + sched_interface::sched_args_t sched_cfg = {}; + sched_cell_params_t cell_params; + cell_params.set_cfg(0, cell_cfg, sched_cfg); + sched_interface::ue_cfg_t ue_cfg = generate_default_ue_cfg(); + + sched_ue_cell ue_cc(0x46, cell_params, tti_point(0)); + ue_cc.set_ue_cfg(ue_cfg); + + float snr = 20; + tti_point tti{0}; + for (; ue_cc.tpc_fsm.get_ul_snr_estim(0) < snr - 2; ++tti) { + ue_cc.set_ul_snr(tti, snr, 0); + ue_cc.set_ul_snr(tti, snr, 1); + ue_cc.tpc_fsm.set_phr(-5, 1); + ue_cc.new_tti(tti); + } + + uint32_t req_bytes = 10000; + uint32_t pending_prbs = get_required_prb_ul(ue_cc, req_bytes); + TESTASSERT(pending_prbs == 1); // The PHR<0 is limiting the number of allocated PRBs + + uint32_t N_srs = 0; + uint32_t prb_grant_size = pending_prbs; + uint32_t nof_symb = 2 * (SRSRAN_CP_NSYMB(cell_cfg.cell.cp) - 1) - N_srs; + uint32_t nof_re = nof_symb * prb_grant_size * SRSRAN_NRE; + tbs_info tbinfo = cqi_to_tbs_ul(ue_cc, prb_grant_size, nof_re, req_bytes); + TESTASSERT(tbinfo.tbs_bytes >= 10); +} + +void test_interferer_subband_cqi_scenario() +{ + uint32_t Nprb = 50; + sched_interface::cell_cfg_t cell_cfg = generate_default_cell_cfg(Nprb); + sched_interface::sched_args_t sched_cfg = {}; + sched_cell_params_t cell_params; + cell_params.set_cfg(0, cell_cfg, sched_cfg); + sched_interface::ue_cfg_t ue_cfg = generate_default_ue_cfg(); + + sched_ue_cell ue_cc(0x46, cell_params, tti_point(0)); + ue_cfg.supported_cc_list[0].dl_cfg.cqi_report.subband_wideband_ratio = 4; + ue_cfg.supported_cc_list[0].dl_cfg.cqi_report.periodic_configured = true; + ue_cc.set_ue_cfg(ue_cfg); + + TESTASSERT(ue_cc.dl_cqi().subband_cqi_enabled()); + TESTASSERT(ue_cc.dl_cqi().nof_bandwidth_parts() == 3); + TESTASSERT(ue_cc.dl_cqi().nof_subbands() == 9); + + ue_cc.set_dl_wb_cqi(tti_point{0}, 10); + ue_cc.set_dl_sb_cqi(tti_point{40}, 1, 15); + ue_cc.set_dl_sb_cqi(tti_point{80}, 3, 15); + ue_cc.set_dl_sb_cqi(tti_point{160}, 8, 0); // interferer in last BP + + rbgmask_t test_mask(cell_params.nof_rbgs); + test_mask.fill(0, 12); + + rbgmask_t rbgs(cell_params.nof_rbgs); + tbs_info tb; + rbgmask_t grant_mask(cell_params.nof_rbgs); + TESTASSERT(find_optimal_rbgmask(ue_cc, + tti_point{160 + TX_ENB_DELAY}, + rbgs, + SRSRAN_DCI_FORMAT1, + srsran::interval{0, 10000}, + tb, + grant_mask)); + TESTASSERT(grant_mask == test_mask); + + ue_cc.set_dl_wb_cqi(tti_point{0}, 15); + ue_cc.set_dl_sb_cqi(tti_point{40}, 1, 15); + ue_cc.set_dl_sb_cqi(tti_point{80}, 3, 15); + ue_cc.set_dl_sb_cqi(tti_point{160}, 8, 10); // interferer in last BP + TESTASSERT(find_optimal_rbgmask(ue_cc, + tti_point{160 + TX_ENB_DELAY}, + rbgs, + SRSRAN_DCI_FORMAT1, + srsran::interval{0, 10000}, + tb, + grant_mask)); + TESTASSERT(grant_mask == test_mask); + + ue_cc.set_dl_wb_cqi(tti_point{0}, 15); + ue_cc.set_dl_sb_cqi(tti_point{40}, 1, 15); + ue_cc.set_dl_sb_cqi(tti_point{80}, 3, 15); + ue_cc.set_dl_sb_cqi(tti_point{160}, 8, 14); // interferer in last BP + TESTASSERT(find_optimal_rbgmask(ue_cc, + tti_point{160 + TX_ENB_DELAY}, + rbgs, + SRSRAN_DCI_FORMAT1, + srsran::interval{0, 10000}, + tb, + grant_mask)); + test_mask.reset(); + test_mask.fill(0, cell_params.nof_rbgs); + TESTASSERT(grant_mask == test_mask); +} + +int main() +{ + srsenb::set_randseed(seed); + srsran::console("This is the chosen seed: %u\n", seed); + + auto& test_log = srslog::fetch_basic_logger("TEST", false); + test_log.set_level(srslog::basic_levels::info); + + // Start the log backend. + srslog::init(); + + test_neg_phr_scenario(); + test_interferer_subband_cqi_scenario(); + + srslog::flush(); + + srsran::console("Success\n"); +} diff --git a/srsenb/test/mac/sched_ue_ded_test_suite.cc b/srsenb/test/mac/sched_ue_ded_test_suite.cc index 60f40e695..a99be1169 100644 --- a/srsenb/test/mac/sched_ue_ded_test_suite.cc +++ b/srsenb/test/mac/sched_ue_ded_test_suite.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -50,6 +50,13 @@ int sim_ue_ctxt_t::enb_to_ue_cc_idx(uint32_t enb_cc_idx) const return it == ue_cfg.supported_cc_list.end() ? -1 : std::distance(ue_cfg.supported_cc_list.begin(), it); } +const phich_t* find_phich_grant(uint16_t rnti, const sched_interface::ul_sched_res_t& ul_cc_res) +{ + const phich_t* phich_ptr = std::find_if( + ul_cc_res.phich.begin(), ul_cc_res.phich.end(), [rnti](const phich_t& phich) { return phich.rnti == rnti; }); + return phich_ptr == ul_cc_res.phich.end() ? nullptr : phich_ptr; +} + const pusch_t* find_pusch_grant(uint16_t rnti, const sched_interface::ul_sched_res_t& ul_cc_res) { const pusch_t* ptr = std::find_if( @@ -138,6 +145,22 @@ int test_dl_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& return SRSRAN_SUCCESS; } +bool is_in_measgap(srsran::tti_point tti, uint32_t period, uint32_t offset) +{ + if (period == 0) { + return false; + } + uint32_t T = period / 10; + for (uint32_t i = 0; i < 6; ++i) { + tti_point tti_gap_start = tti - i; + bool is_gap_start = (tti_gap_start.sfn() % T == offset / 10) and (tti_gap_start.sf_idx() == offset % 10); + if (is_gap_start) { + return true; + } + } + return false; +} + int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out) { uint32_t pid = to_tx_ul(sf_out.tti_rx).to_uint() % (FDD_HARQ_DELAY_UL_MS + FDD_HARQ_DELAY_DL_MS); @@ -165,9 +188,7 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& uint16_t rnti = ue.rnti; int ue_cc_idx = ue.enb_to_ue_cc_idx(cc); - const phich_t* phich_ptr = - std::find_if(phich_begin, phich_end, [rnti](const phich_t& phich) { return phich.rnti == rnti; }); - phich_ptr = phich_ptr == phich_end ? nullptr : phich_ptr; + const phich_t* phich_ptr = find_phich_grant(rnti, sf_out.ul_cc_result[cc]); const pusch_t* pusch_ptr = find_pusch_grant(rnti, sf_out.ul_cc_result[cc]); // TEST: Check that idle CCs do not receive PUSCH grants or PHICH @@ -177,22 +198,32 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& continue; } - const auto& h = ue.cc_list[ue_cc_idx].ul_harqs[pid]; - bool phich_ack = phich_ptr != nullptr and phich_ptr->phich == phich_t::ACK; - bool is_msg3 = h.first_tti_rx == ue.msg3_tti_rx and h.nof_txs == h.nof_retxs + 1; - bool last_retx = h.nof_retxs + 1 >= (is_msg3 ? sf_out.cc_params[0].cfg.maxharq_msg3tx : ue.ue_cfg.maxharq_tx); - bool h_inactive = (not h.active) or (phich_ack or last_retx); + const auto& h = ue.cc_list[ue_cc_idx].ul_harqs[pid]; + bool phich_ack = phich_ptr != nullptr and phich_ptr->phich == phich_t::ACK; + bool is_msg3 = h.first_tti_rx == ue.msg3_tti_rx and h.nof_txs == h.nof_retxs + 1; + uint32_t max_nof_retxs = is_msg3 ? sf_out.cc_params[0].cfg.maxharq_msg3tx : ue.ue_cfg.maxharq_tx; + bool last_retx = h.nof_retxs + 1 >= max_nof_retxs; + tti_point tti_tx_phich = to_tx_dl(sf_out.tti_rx); + bool phich_in_meas_gap = is_in_measgap(tti_tx_phich, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset); + bool pusch_in_meas_gap = + is_in_measgap(to_tx_ul(sf_out.tti_rx), ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset); + bool h_cleared = (not h.active) or (phich_ack or last_retx); - // TEST: Already active UL HARQs have to receive PHICH - CONDERROR(h.active and phich_ptr == nullptr, "PHICH not received for rnti=0x%x active UL HARQ pid=%d", rnti, pid); + // TEST: Already active UL HARQs have to receive PHICH (unless MeasGap collision) + CONDERROR(h.active and phich_ptr == nullptr and not phich_in_meas_gap, + "PHICH not received for rnti=0x%x active UL HARQ pid=%d", + rnti, + pid); CONDERROR(not h.active and phich_ptr != nullptr, "PHICH for rnti=0x%x corresponds to inactive UL HARQ pid=%d", rnti, pid); // TEST: absent PUSCH grants for active UL HARQs must be either ACKs, last retx, or interrupted HARQs - if ((phich_ptr != nullptr) and (pusch_ptr == nullptr)) { - CONDERROR(not h_inactive, "PHICH NACK received for rnti=0x%x but no PUSCH retx reallocated", rnti); + if (phich_ptr != nullptr) { + CONDERROR(not h_cleared and pusch_ptr == nullptr and not pusch_in_meas_gap, + "PHICH NACK received for rnti=0x%x but no PUSCH retx reallocated", + rnti); } if (pusch_ptr != nullptr) { @@ -205,11 +236,13 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& // newtx CONDERROR(nof_retx != 0, "Invalid rv index for new UL tx"); CONDERROR(pusch_ptr->current_tx_nb != 0, "UL HARQ retxs need to have been previously transmitted"); - CONDERROR(not h_inactive, "New tx for already active UL HARQ"); + CONDERROR(not h_cleared, "New tx for already active UL HARQ"); CONDERROR(not pusch_ptr->needs_pdcch and ue.msg3_tti_rx.is_valid() and sf_out.tti_rx > ue.msg3_tti_rx, "In case of newtx, PDCCH allocation is required, unless it is Msg3"); } else { CONDERROR(pusch_ptr->current_tx_nb == 0, "UL retx has to have nof tx > 0"); + CONDERROR(h.nof_retxs >= max_nof_retxs, "UL max nof retxs exceeded"); + CONDERROR(pusch_ptr->current_tx_nb != h.nof_retxs + 1, "UL HARQ nof_retx mismatch"); if (not h.active) { // the HARQ is being resumed. PDCCH must be active with the exception of Msg3 CONDERROR(ue.msg4_tti_rx.is_valid() and not pusch_ptr->needs_pdcch, @@ -222,7 +255,7 @@ int test_ul_sched_result(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& CONDERROR(pusch_ptr->dci.type2_alloc.riv != h.riv, "Non-adaptive retx must keep the same riv"); } } - CONDERROR(get_rvidx(h.nof_retxs + 1) != (uint32_t)pusch_ptr->dci.tb.rv, "Invalid rv index for retx"); + CONDERROR(get_rvidx(h.nof_retxs + 1) != (uint32_t)pusch_ptr->dci.tb.rv, "Invalid rv index for UL retx"); CONDERROR(h.tbs != pusch_ptr->tbs, "TBS changed during HARQ retx"); CONDERROR(to_tx_ul(h.last_tti_rx) > sf_out.tti_rx, "UL harq pid=%d was reused too soon", h.pid); } @@ -325,8 +358,19 @@ int test_ra(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out) // TEST: No UL allocs except for Msg3 before Msg4 for (uint32_t i = 0; i < ul_cc_res.pusch.size(); ++i) { if (ul_cc_res.pusch[i].dci.rnti == rnti) { - CONDERROR(not ue.rar_tti_rx.is_valid(), "No UL allocs before RAR allowed"); - srsran::tti_point expected_msg3_tti = ue.rar_tti_rx + MSG3_DELAY_MS; + tti_point rar_tti_rx = ue.rar_tti_rx; + if (not rar_tti_rx.is_valid()) { + for (uint32_t j = 0; j < dl_cc_res.rar.size(); ++j) { + for (const auto& grant : dl_cc_res.rar[i].msg3_grant) { + if (grant.data.temp_crnti == ue.rnti) { + rar_tti_rx = sf_out.tti_rx; + break; + } + } + } + } + CONDERROR(not rar_tti_rx.is_valid(), "No UL allocs before RAR allowed"); + srsran::tti_point expected_msg3_tti = rar_tti_rx + MSG3_DELAY_MS; CONDERROR(expected_msg3_tti > sf_out.tti_rx, "No UL allocs before Msg3 is scheduled"); if (expected_msg3_tti < sf_out.tti_rx) { bool msg3_retx = @@ -364,12 +408,6 @@ int test_ra(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out) return SRSRAN_SUCCESS; } -bool is_in_measgap(srsran::tti_point tti, uint32_t period, uint32_t offset) -{ - uint32_t T = period / 10; - return (tti.sfn() % T == offset / 10) and (tti.sf_idx() == offset % 10); -} - int test_meas_gaps(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out) { for (uint32_t cc = 0; cc < enb_ctxt.cell_params.size(); ++cc) { @@ -380,16 +418,19 @@ int test_meas_gaps(const sim_enb_ctxt_t& enb_ctxt, const sf_output_res_t& sf_out uint16_t rnti = ue.rnti; uint32_t ue_cc_idx = ue.enb_to_ue_cc_idx(cc); srsran::tti_point tti_tx_ul = to_tx_ul(sf_out.tti_rx), tti_tx_dl = to_tx_dl(sf_out.tti_rx), - tti_tx_dl_ack = to_tx_dl_ack(sf_out.tti_rx), tti_tx_phich = to_tx_ul_ack(sf_out.tti_rx); + tti_tx_dl_ack = to_tx_dl_ack(sf_out.tti_rx); if (ue_cc_idx != 0 or ue.ue_cfg.measgap_period == 0) { continue; } - if (is_in_measgap(tti_tx_ul, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset) or - is_in_measgap(tti_tx_phich, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset)) { + if (is_in_measgap(tti_tx_dl, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset)) { + const phich_t* phich_ptr = find_phich_grant(rnti, ul_cc_res); + CONDERROR(phich_ptr != nullptr, "PHICH grants cannot fall in UE measGap"); + } + if (is_in_measgap(tti_tx_ul, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset)) { const pusch_t* pusch_ptr = find_pusch_grant(rnti, ul_cc_res); - CONDERROR(pusch_ptr != nullptr, "PUSCH grants and PHICH cannot fall in UE measGap"); + CONDERROR(pusch_ptr != nullptr, "PUSCH grants cannot fall in UE measGap"); } if (is_in_measgap(tti_tx_dl, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset) or is_in_measgap(tti_tx_dl_ack, ue.ue_cfg.measgap_period, ue.ue_cfg.measgap_offset)) { diff --git a/srsenb/test/mac/sched_ue_ded_test_suite.h b/srsenb/test/mac/sched_ue_ded_test_suite.h index ad84501e2..f845d91f9 100644 --- a/srsenb/test/mac/sched_ue_ded_test_suite.h +++ b/srsenb/test/mac/sched_ue_ded_test_suite.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsenb/test/phy/CMakeLists.txt b/srsenb/test/phy/CMakeLists.txt index eae7d98df..044f85eb8 100644 --- a/srsenb/test/phy/CMakeLists.txt +++ b/srsenb/test/phy/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsenb/test/phy/enb_phy_test.cc b/srsenb/test/phy/enb_phy_test.cc index e9ae85bd8..039956c16 100644 --- a/srsenb/test/phy/enb_phy_test.cc +++ b/srsenb/test/phy/enb_phy_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,9 +20,9 @@ */ #include "srsran/common/threads.h" -#include "srsran/phy/common/phy_common.h" #include "srsran/phy/utils/random.h" #include "srsran/srslog/srslog.h" +#include "srsran/srsran.h" #include #include #include @@ -31,7 +31,6 @@ #include #include #include -#include static inline bool dl_ack_value(uint32_t ue_cc_idx, uint32_t tti) { @@ -83,7 +82,7 @@ private: std::vector ringbuffers_rx; srsran::rf_timestamp_t ts_rx = {}; double rx_srate = 0.0; - bool running = true; + std::atomic running = {true}; CALLBACK(tx); CALLBACK(tx_end); @@ -293,6 +292,7 @@ private: CALLBACK(ri_info); CALLBACK(pmi_info); CALLBACK(cqi_info); + CALLBACK(sb_cqi_info); CALLBACK(snr_info); CALLBACK(ta_info); CALLBACK(ack_info); @@ -327,6 +327,7 @@ private: uint32_t cqi; } tti_cqi_info_t; + std::mutex phy_mac_mutex; std::queue tti_dl_info_sched_queue; std::queue tti_dl_info_ack_queue; std::queue tti_ul_info_sched_queue; @@ -414,10 +415,16 @@ public: srsran_random_free(random_gen); } - void set_active_cell_list(std::vector& active_cell_list_) { active_cell_list = active_cell_list_; } + void set_active_cell_list(std::vector& active_cell_list_) + { + std::lock_guard lock(phy_mac_mutex); + active_cell_list = active_cell_list_; + } int sr_detected(uint32_t tti, uint16_t rnti) override { + std::lock_guard lock(phy_mac_mutex); + tti_sr_info_t tti_sr_info = {}; tti_sr_info.tti = tti; tti_sr_info_queue.push(tti_sr_info); @@ -450,6 +457,8 @@ public: } int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override { + std::lock_guard lock(phy_mac_mutex); + tti_cqi_info_t tti_cqi_info = {}; tti_cqi_info.tti = tti; tti_cqi_info.cc_idx = cc_idx; @@ -462,6 +471,13 @@ public: return SRSRAN_SUCCESS; } + int sb_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t sb_idx, uint32_t cqi_value) override + { + notify_sb_cqi_info(); + logger.info("Received CQI tti=%d; rnti=0x%x; cc_idx=%d; sb_idx=%d cqi=%d;", tti, rnti, cc_idx, sb_idx, cqi_value); + + return SRSRAN_SUCCESS; + } int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) override { notify_snr_info(); @@ -475,6 +491,8 @@ public: } int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) override { + std::lock_guard lock(phy_mac_mutex); + // Push grant info in queue tti_dl_info_t tti_dl_info = {}; tti_dl_info.tti = tti; @@ -489,6 +507,8 @@ public: } int crc_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) override { + std::lock_guard lock(phy_mac_mutex); + // Push grant info in queue tti_ul_info_t tti_ul_info = {}; tti_ul_info.tti = tti; @@ -501,7 +521,8 @@ public: return 0; } - int push_pdu(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) override + int push_pdu(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res, uint32_t grant_nof_prbs) + override { logger.info("Received push_pdu tti=%d; rnti=0x%x; ack=%d;", tti, rnti, crc_res); notify_push_pdu(); @@ -510,20 +531,21 @@ public: } int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override { + std::lock_guard lock(phy_mac_mutex); + // Notify test engine notify_get_dl_sched(); - /// Make sure it writes the first cell always - dl_sched_res[0].cfi = cfi; + // Make sure it writes the CFI in all cells + for (dl_sched_t& dl_sched : dl_sched_res) { + dl_sched.cfi = cfi; + } // Iterate for each carrier uint32_t ue_cc_idx = 0; for (uint32_t& cc_idx : active_cell_list) { auto& dl_sched = dl_sched_res[cc_idx]; - // Required - dl_sched.cfi = cfi; - // Default TB scheduling bool sched_tb[SRSRAN_MAX_TB] = {}; @@ -623,6 +645,8 @@ public: // Notify test engine notify_get_ul_sched(); + std::lock_guard lock(phy_mac_mutex); + // Iterate for each carrier following the eNb/Cell order for (uint32_t cc_idx = 0; cc_idx < ul_sched_res.size(); cc_idx++) { auto scell_idx = active_cell_list.size(); @@ -695,9 +719,11 @@ public: return SRSRAN_SUCCESS; } void set_sched_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) override { notify_set_sched_dl_tti_mask(); } - void tti_clock() override { notify_tti_clock(); } + void tti_clock() { notify_tti_clock(); } int run_tti(bool enable_assert) { + std::lock_guard lock(phy_mac_mutex); + // Check DL ACKs match with grants while (not tti_dl_info_ack_queue.empty()) { // Get both Info @@ -1133,7 +1159,7 @@ typedef std::unique_ptr unique_dummy_ue_phy_t; typedef std::unique_ptr unique_srsenb_phy_t; -class phy_test_bench +class phy_test_bench : public srsenb::enb_time_interface { public: struct args_t { @@ -1148,6 +1174,7 @@ public: uint32_t tm_u32 = 1; uint32_t period_pcell_rotate = 0; srsran_tm_t tm = SRSRAN_TM1; + bool extended_cp = false; args_t() { cell.nof_prb = 6; @@ -1191,8 +1218,8 @@ private: srslog::basic_logger& logger; args_t args = {}; ///< Test arguments - srsenb::phy_args_t phy_args; ///< PHY arguments - srsenb::phy_cfg_t phy_cfg; ///< eNb Cell/Carrier configuration + srsenb::phy_args_t phy_args = {}; ///< PHY arguments + srsenb::phy_cfg_t phy_cfg = {}; ///< eNb Cell/Carrier configuration srsenb::phy_interface_rrc_lte::phy_rrc_cfg_list_t phy_rrc_cfg; ///< UE PHY configuration uint64_t tti_counter = 0; @@ -1224,6 +1251,7 @@ public: q.cell = args.cell; q.cell.id = i; q.cell_id = i; + q.cell.cp = args.extended_cp ? SRSRAN_CP_EXT : SRSRAN_CP_NORM; q.dl_freq_hz = 0.0f; ///< Frequencies are irrelevant in this test q.ul_freq_hz = 0.0f; q.root_seq_idx = 25 + i; ///< Different PRACH root sequences @@ -1309,7 +1337,7 @@ public: stack->set_active_cell_list(args.ue_cell_list); /// Initiate eNb PHY with the given RNTI - if (enb_phy->init(phy_args, phy_cfg, radio.get(), stack.get()) < 0) { + if (enb_phy->init(phy_args, phy_cfg, radio.get(), stack.get(), this) < 0) { return SRSRAN_ERROR; } enb_phy->set_config(args.rnti, phy_rrc_cfg); @@ -1331,7 +1359,7 @@ public: enb_phy->stop(); } - ~phy_test_bench() = default; + virtual ~phy_test_bench() = default; int run_tti() { @@ -1354,7 +1382,7 @@ public: } break; case change_state_flush: - if (tti_counter >= 2 * FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS) { + if (tti_counter >= 2 * (FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS)) { logger.warning("******* Cell rotation: Reconfigure *******"); std::array activation = {}; ///< Activation/Deactivation vector @@ -1403,6 +1431,11 @@ public: return ret; } + + void tti_clock() final + { + // nothing to do + } }; typedef std::unique_ptr unique_phy_test_bench; @@ -1426,6 +1459,7 @@ int parse_args(int argc, char** argv, phy_test_bench::args_t& args) ("ack_mode", bpo::value(&args.ack_mode), "HARQ ACK/NACK mode: normal, pucch3, cs") ("cell.nof_prb", bpo::value(&args.cell.nof_prb)->default_value(args.cell.nof_prb), "eNb Cell/Carrier bandwidth") ("cell.nof_ports", bpo::value(&args.cell.nof_ports)->default_value(args.cell.nof_ports), "eNb Cell/Carrier number of ports") + ("cell.cp", bpo::value(&args.extended_cp)->default_value(false), "use extended CP") ("tm", bpo::value(&args.tm_u32)->default_value(args.tm_u32), "Transmission mode") ("rotation", bpo::value(&args.period_pcell_rotate), "Serving cells rotation period in ms, set to zero to disable") ; @@ -1460,6 +1494,10 @@ int parse_args(int argc, char** argv, phy_test_bench::args_t& args) int main(int argc, char** argv) { + // First of all, initialise crash handler - Crash files from ctest executions will be stored in + // srsenb/test/phy/srsRAN.backtrace.crash + srsran_debug_handle_crash(argc, argv); + phy_test_bench::args_t test_args; // Parse arguments @@ -1478,20 +1516,23 @@ int main(int argc, char** argv) if (not valid_cfg) { // Verify that phy returns with an error if provided an invalid configuration TESTASSERT(err_code != SRSRAN_SUCCESS); - return 0; + return SRSRAN_SUCCESS; } - TESTASSERT(err_code == SRSRAN_SUCCESS); // Run Simulation - for (uint32_t i = 0; i < test_args.duration; i++) { - TESTASSERT(test_bench->run_tti() >= SRSRAN_SUCCESS); + for (uint32_t i = 0; i < test_args.duration and err_code >= SRSRAN_SUCCESS; i++) { + err_code = test_bench->run_tti(); } test_bench->stop(); srslog::flush(); - std::cout << "Passed" << std::endl; + if (err_code >= SRSRAN_SUCCESS) { + std::cout << "Ok" << std::endl; + } else { + std::cout << "Error" << std::endl; + } - return SRSRAN_SUCCESS; + return err_code; } diff --git a/srsenb/test/rrc/CMakeLists.txt b/srsenb/test/rrc/CMakeLists.txt index ef63adc1c..9a387d199 100644 --- a/srsenb/test/rrc/CMakeLists.txt +++ b/srsenb/test/rrc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,7 +18,22 @@ # and at http://www.gnu.org/licenses/. # -add_executable(rrc_nr_test rrc_nr_test.cc) -target_link_libraries(rrc_nr_test srsgnb_rrc srsran_common rrc_nr_asn1) -add_test(rrc_nr_test rrc_nr_test) +add_library(test_helpers STATIC test_helpers.cc) +target_link_libraries(test_helpers srsenb_rrc srsenb_common rrc_asn1 rrc_nr_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) +add_executable(rrc_meascfg_test rrc_meascfg_test.cc) +target_link_libraries(rrc_meascfg_test test_helpers ${ATOMIC_LIBS}) + +add_executable(erab_setup_test erab_setup_test.cc) +target_link_libraries(erab_setup_test test_helpers ${LIBCONFIGPP_LIBRARIES} ${ATOMIC_LIBS}) + +add_executable(rrc_mobility_test rrc_mobility_test.cc) +target_link_libraries(rrc_mobility_test srsran_asn1 test_helpers ${ATOMIC_LIBS}) + +add_executable(rrc_paging_test rrc_paging_test.cc) +target_link_libraries(rrc_paging_test srsran_asn1 test_helpers) + +add_test(rrc_mobility_test rrc_mobility_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) +add_test(erab_setup_test erab_setup_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) +add_test(rrc_meascfg_test rrc_meascfg_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) +add_test(rrc_paging_test rrc_paging_test) diff --git a/srsenb/test/upper/erab_setup_test.cc b/srsenb/test/rrc/erab_setup_test.cc similarity index 91% rename from srsenb/test/upper/erab_setup_test.cc rename to srsenb/test/rrc/erab_setup_test.cc index 3d3a22df9..65a9f4324 100644 --- a/srsenb/test/upper/erab_setup_test.cc +++ b/srsenb/test/rrc/erab_setup_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,9 +21,9 @@ #include "srsenb/hdr/enb.h" #include "srsenb/src/enb_cfg_parser.h" +#include "srsenb/test/rrc/test_helpers.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/test_common.h" -#include "test_helpers.h" #include int test_erab_setup(srsran::log_sink_spy& spy, bool qci_exists) @@ -42,7 +42,8 @@ int test_erab_setup(srsran::log_sink_spy& spy, bool qci_exists) logger.set_hex_dump_max_size(1024); logger.set_level(srslog::basic_levels::info); - srsenb::rrc rrc{&task_sched}; + enb_bearer_manager bearers; + srsenb::rrc rrc{&task_sched, bearers}; mac_dummy mac; rlc_dummy rlc; test_dummies::pdcp_mobility_dummy pdcp; @@ -52,7 +53,7 @@ int test_erab_setup(srsran::log_sink_spy& spy, bool qci_exists) rrc.init(cfg, &phy, &mac, &rlc, &pdcp, &s1ap, >pu); uint16_t rnti = 0x46; - sched_interface::ue_cfg_t ue_cfg; + sched_interface::ue_cfg_t ue_cfg = {}; ue_cfg.supported_cc_list.resize(1); ue_cfg.supported_cc_list[0].active = true; ue_cfg.supported_cc_list[0].enb_cc_idx = 0; @@ -97,12 +98,12 @@ int test_erab_setup(srsran::log_sink_spy& spy, bool qci_exists) asn1::cbit_ref bref(byte_buf.msg, byte_buf.N_bytes); TESTASSERT(s1ap_pdu.unpack(bref) == asn1::SRSASN_SUCCESS); - const auto& setupmsg = s1ap_pdu.init_msg().value.erab_setup_request().protocol_ies; - if (setupmsg.ueaggregate_maximum_bitrate_present) { - rrc.set_aggregate_max_bitrate(rnti, setupmsg.ueaggregate_maximum_bitrate.value); + const auto& setupmsg = s1ap_pdu.init_msg().value.erab_setup_request(); + if (setupmsg->ueaggregate_maximum_bitrate_present) { + rrc.set_aggregate_max_bitrate(rnti, setupmsg->ueaggregate_maximum_bitrate.value); } - for (const auto& item : setupmsg.erab_to_be_setup_list_bearer_su_req.value) { - const auto& erab = item.value.erab_to_be_setup_item_bearer_su_req(); + for (const auto& item : setupmsg->erab_to_be_setup_list_bearer_su_req.value) { + const auto& erab = item->erab_to_be_setup_item_bearer_su_req(); asn1::s1ap::cause_c cause; int ret = rrc.setup_erab(rnti, erab.erab_id, diff --git a/srsenb/test/upper/rrc_meascfg_test.cc b/srsenb/test/rrc/rrc_meascfg_test.cc similarity index 94% rename from srsenb/test/upper/rrc_meascfg_test.cc rename to srsenb/test/rrc/rrc_meascfg_test.cc index 46df10064..f72b05d83 100644 --- a/srsenb/test/upper/rrc_meascfg_test.cc +++ b/srsenb/test/rrc/rrc_meascfg_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,10 +21,10 @@ #include "srsenb/hdr/enb.h" #include "srsenb/hdr/stack/rrc/ue_meas_cfg.h" +#include "srsran/asn1/obj_id_cmp_utils.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/test_common.h" #include "srsran/interfaces/enb_rrc_interface_types.h" -#include "srsran/rrc/rrc_cfg_utils.h" #include "test_helpers.h" using namespace asn1::rrc; @@ -40,14 +40,14 @@ namespace srsenb { */ int test_correct_meascfg_insertion() { - meas_cell_cfg_t cell1 = generate_cell1(), cell2{}, cell3{}, cell4{}; - cell2 = cell1; - cell2.pci = 2; - cell2.eci = 0x19C02; - cell3 = cell1; - cell3.earfcn = 2850; - cell4 = cell1; - cell4.q_offset = 1; + meas_cell_cfg_t cell1 = generate_cell1(), cell2{}, cell3{}, cell4{}; + cell2 = cell1; + cell2.pci = 2; + cell2.eci = 0x19C02; + cell3 = cell1; + cell3.earfcn = 2850; + cell4 = cell1; + cell4.cell_individual_offset = asn1::rrc::q_offset_range_opts::db1; report_cfg_eutra_s rep1 = generate_rep1(); @@ -117,13 +117,13 @@ int test_correct_meascfg_calculation() meas_cfg_s src_var{}, target_var{}; meas_cell_cfg_t cell1{}, cell2{}; - cell1.earfcn = 3400; - cell1.pci = 1; - cell1.q_offset = 0; - cell1.eci = 0x19C01; - cell2 = cell1; - cell2.pci = 2; - cell2.eci = 0x19C02; + cell1.earfcn = 3400; + cell1.pci = 1; + cell1.cell_individual_offset = asn1::rrc::q_offset_range_opts::db0; + cell1.eci = 0x19C01; + cell2 = cell1; + cell2.pci = 2; + cell2.eci = 0x19C02; report_cfg_eutra_s rep1 = generate_rep1(), rep2{}, rep3{}; rep2 = rep1; @@ -178,8 +178,8 @@ int test_correct_meascfg_calculation() TESTASSERT(result_meascfg.report_cfg_to_add_mod_list.size() == 0); // TEST 3: Cell is added to cellsToAddModList if just a field was updated - cell1.q_offset = 5; - src_var = target_var; + cell1.cell_individual_offset = asn1::rrc::q_offset_range_opts::db5; + src_var = target_var; add_cell_enb_cfg(target_var.meas_obj_to_add_mod_list, cell1); TESTASSERT(compute_diff_meascfg(src_var, target_var, result_meascfg)); TESTASSERT(result_meascfg.meas_obj_to_add_mod_list_present); diff --git a/srsenb/test/upper/rrc_mobility_test.cc b/srsenb/test/rrc/rrc_mobility_test.cc similarity index 81% rename from srsenb/test/upper/rrc_mobility_test.cc rename to srsenb/test/rrc/rrc_mobility_test.cc index cbb6ad6cf..7abb07cb6 100644 --- a/srsenb/test/upper/rrc_mobility_test.cc +++ b/srsenb/test/rrc/rrc_mobility_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,7 +66,7 @@ const char* to_string(test_event event) struct mobility_tester { explicit mobility_tester(const test_event& args_) : - args(args_), logger(srslog::fetch_basic_logger("RRC")), rrc(&task_sched) + args(args_), logger(srslog::fetch_basic_logger("RRC")), rrc(&task_sched, bearers) { logger.set_level(srslog::basic_levels::info); logger.set_hex_dump_max_size(1024); @@ -102,6 +102,7 @@ struct mobility_tester { test_dummies::pdcp_mobility_dummy pdcp; test_dummies::phy_mobility_dummy phy; test_dummies::s1ap_mobility_dummy s1ap; + enb_bearer_manager bearers; gtpu_dummy gtpu; void tic() @@ -192,10 +193,10 @@ int test_s1ap_mobility(srsran::log_sink_spy& spy, test_event test_params) /* Receive MeasReport from UE (correct if PCI=2) */ if (test_params == test_event::wrong_measreport) { uint8_t meas_report[] = {0x08, 0x10, 0x38, 0x74, 0x00, 0x0D, 0xBC, 0x80}; // PCI == 3 - test_helpers::copy_msg_to_buffer(pdu, meas_report); + copy_msg_to_buffer(pdu, meas_report); } else { uint8_t meas_report[] = {0x08, 0x10, 0x38, 0x74, 0x00, 0x09, 0xBC, 0x80}; // PCI == 2 - test_helpers::copy_msg_to_buffer(pdu, meas_report); + copy_msg_to_buffer(pdu, meas_report); } tester.rrc.write_pdu(tester.rnti, 1, std::move(pdu)); tester.tic(); @@ -212,7 +213,7 @@ int test_s1ap_mobility(srsran::log_sink_spy& spy, test_event test_params) if (test_params == test_event::concurrent_ho) { s1ap.last_ho_required = {}; uint8_t meas_report[] = {0x08, 0x10, 0x38, 0x74, 0x00, 0x09, 0xBC, 0x80}; // PCI == 2 - test_helpers::copy_msg_to_buffer(pdu, meas_report); + copy_msg_to_buffer(pdu, meas_report); tester.rrc.write_pdu(tester.rnti, 1, std::move(pdu)); tester.tic(); TESTASSERT(s1ap.last_ho_required.rrc_container == nullptr); @@ -250,7 +251,7 @@ int test_s1ap_mobility(srsran::log_sink_spy& spy, test_event test_params) 0x01, 0x48, 0x04, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0xa0, 0x07, 0xa0, 0x10, 0x00, 0x01, 0x00, 0x05, 0x00, 0xa7, 0xd0, 0xc1, 0xf6, 0xaf, 0x3e, 0x12, 0xcc, 0x86, 0x0d, 0x30, 0x00, 0x0b, 0x5a, 0x02, 0x17, 0x86, 0x00, 0x05, 0xa0, 0x20}; - test_helpers::copy_msg_to_buffer(pdu, ho_cmd_rrc_container); + copy_msg_to_buffer(pdu, ho_cmd_rrc_container); TESTASSERT(s1ap.last_enb_status.rnti != tester.rnti); tester.rrc.ho_preparation_complete(tester.rnti, rrc::ho_prep_result::success, asn1::s1ap::ho_cmd_s{}, std::move(pdu)); TESTASSERT(s1ap.last_enb_status.status_present); @@ -280,14 +281,14 @@ int test_s1ap_tenb_mobility(test_event test_params) /* TeNB receives S1AP Handover Request */ asn1::s1ap::ho_request_s ho_req; - ho_req.protocol_ies.erab_to_be_setup_list_ho_req.value.resize(1); - auto& erab = ho_req.protocol_ies.erab_to_be_setup_list_ho_req.value[0].value.erab_to_be_setup_item_ho_req(); - erab.erab_id = 5; + ho_req->erab_to_be_setup_list_ho_req.value.resize(1); + auto& erab = ho_req->erab_to_be_setup_list_ho_req.value[0]->erab_to_be_setup_item_ho_req(); + erab.erab_id = 5; erab.erab_level_qos_params.qci = 9; if (test_params == test_event::unknown_qci) { erab.erab_level_qos_params.qci = 10; } - ho_req.protocol_ies.ue_security_cap.value.integrity_protection_algorithms.set(14, true); + ho_req->ue_security_cap.value.integrity_protection_algorithms.set(14, true); asn1::s1ap::sourceenb_to_targetenb_transparent_container_s container; container.target_cell_id.cell_id.from_number(0x19C02); if (test_params == test_event::wrong_target_cell) { @@ -296,9 +297,9 @@ int test_s1ap_tenb_mobility(test_event test_params) container.erab_info_list_present = true; container.erab_info_list.resize(1); container.erab_info_list[0].load_info_obj(ASN1_S1AP_ID_ERAB_INFO_LIST_ITEM); - container.erab_info_list[0].value.erab_info_list_item().erab_id = 5; - container.erab_info_list[0].value.erab_info_list_item().dl_forwarding_present = true; - container.erab_info_list[0].value.erab_info_list_item().dl_forwarding.value = + container.erab_info_list[0]->erab_info_list_item().erab_id = 5; + container.erab_info_list[0]->erab_info_list_item().dl_forwarding_present = true; + container.erab_info_list[0]->erab_info_list_item().dl_forwarding.value = asn1::s1ap::dl_forwarding_opts::dl_forwarding_proposed; uint8_t ho_prep_container[] = { 0x0a, 0x10, 0x0b, 0x81, 0x80, 0x00, 0x01, 0x80, 0x00, 0xf3, 0x02, 0x08, 0x00, 0x00, 0x15, 0x80, 0x00, 0x14, @@ -313,9 +314,9 @@ int test_s1ap_tenb_mobility(test_event test_params) asn1::s1ap::cause_c cause; int rnti = tester.rrc.start_ho_ue_resource_alloc(ho_req, container, cause); if (test_params == test_event::wrong_target_cell) { - TESTASSERT(rnti == SRSRAN_INVALID_RNTI); + TESTASSERT_EQ(SRSRAN_INVALID_RNTI, rnti); TESTASSERT(cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network); - TESTASSERT(cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::ho_target_not_allowed); + TESTASSERT_EQ(asn1::s1ap::cause_radio_network_opts::cell_not_available, cause.radio_network().value); TESTASSERT(tester.rrc.get_nof_users() == 0); return SRSRAN_SUCCESS; } @@ -326,21 +327,32 @@ int test_s1ap_tenb_mobility(test_event test_params) TESTASSERT(tester.rrc.get_nof_users() == 0); return SRSRAN_SUCCESS; } - tester.tic(); - TESTASSERT(tester.rrc.get_nof_users() == 1); TESTASSERT(tester.mac.ue_db.count(0x46)); auto& mac_ue = tester.mac.ue_db[0x46]; TESTASSERT(mac_ue.supported_cc_list[0].active); TESTASSERT(mac_ue.supported_cc_list[0].enb_cc_idx == 0); - TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == sched_interface::ue_bearer_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); + + tester.tic(); + TESTASSERT(tester.rrc.get_nof_users() == 1); + TESTASSERT(tester.mac.ue_db.count(0x46)); + TESTASSERT(mac_ue.supported_cc_list[0].active); + TESTASSERT(mac_ue.supported_cc_list[0].enb_cc_idx == 0); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); // Check Security Configuration TESTASSERT(tester.pdcp.bearers.count(0x46)); TESTASSERT(tester.pdcp.bearers[0x46].count(srb_to_lcid(lte_srb::srb1)) and tester.pdcp.bearers[0x46].count(srb_to_lcid(lte_srb::srb2))); TESTASSERT(tester.pdcp.bearers[0x46][srb_to_lcid(lte_srb::srb1)].enable_encryption); TESTASSERT(tester.pdcp.bearers[0x46][srb_to_lcid(lte_srb::srb1)].enable_integrity); - sec_cfg.set_security_capabilities(ho_req.protocol_ies.ue_security_cap.value); - sec_cfg.set_security_key(ho_req.protocol_ies.security_context.value.next_hop_param); + sec_cfg.set_security_capabilities(ho_req->ue_security_cap.value); + sec_cfg.set_security_key(ho_req->security_context.value.next_hop_param); sec_cfg.regenerate_keys_handover(tester.cfg.cell_list[0].pci, tester.cfg.cell_list[0].dl_earfcn); srsran::as_security_config_t as_sec_cfg = sec_cfg.get_as_sec_cfg(); TESTASSERT(tester.pdcp.bearers[0x46][srb_to_lcid(lte_srb::srb1)].sec_cfg.k_rrc_int == as_sec_cfg.k_rrc_int); @@ -349,11 +361,14 @@ int test_s1ap_tenb_mobility(test_event test_params) TESTASSERT(tester.pdcp.bearers[0x46][srb_to_lcid(lte_srb::srb1)].sec_cfg.cipher_algo == as_sec_cfg.cipher_algo); TESTASSERT(tester.pdcp.bearers[0x46][srb_to_lcid(lte_srb::srb1)].sec_cfg.integ_algo == as_sec_cfg.integ_algo); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); + // Check if S1AP Handover Request ACK send is called TESTASSERT(tester.s1ap.last_ho_req_ack.rnti == 0x46); TESTASSERT(tester.s1ap.last_ho_req_ack.ho_cmd_pdu != nullptr); - TESTASSERT(tester.s1ap.last_ho_req_ack.admitted_bearers.size() == - ho_req.protocol_ies.erab_to_be_setup_list_ho_req.value.size()); + TESTASSERT(tester.s1ap.last_ho_req_ack.admitted_bearers.size() == ho_req->erab_to_be_setup_list_ho_req.value.size()); ho_cmd_s ho_cmd; asn1::cbit_ref bref{tester.s1ap.last_ho_req_ack.ho_cmd_pdu->msg, tester.s1ap.last_ho_req_ack.ho_cmd_pdu->N_bytes}; TESTASSERT(ho_cmd.unpack(bref) == asn1::SRSASN_SUCCESS); @@ -365,14 +380,18 @@ int test_s1ap_tenb_mobility(test_event test_params) TESTASSERT(dl_dcch_msg.msg.c1().type().value == dl_dcch_msg_type_c::c1_c_::types_opts::rrc_conn_recfg); auto& recfg_r8 = dl_dcch_msg.msg.c1().rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8(); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); + // Receives MMEStatusTransfer asn1::s1ap::bearers_subject_to_status_transfer_list_l bearers; bearers.resize(1); - bearers[0].value.bearers_subject_to_status_transfer_item().erab_id = 5; - bearers[0].value.bearers_subject_to_status_transfer_item().dl_coun_tvalue.pdcp_sn = 100; - bearers[0].value.bearers_subject_to_status_transfer_item().dl_coun_tvalue.hfn = 3; - bearers[0].value.bearers_subject_to_status_transfer_item().ul_coun_tvalue.pdcp_sn = 120; - bearers[0].value.bearers_subject_to_status_transfer_item().ul_coun_tvalue.hfn = 4; + bearers[0]->bearers_subject_to_status_transfer_item().erab_id = 5; + bearers[0]->bearers_subject_to_status_transfer_item().dl_coun_tvalue.pdcp_sn = 100; + bearers[0]->bearers_subject_to_status_transfer_item().dl_coun_tvalue.hfn = 3; + bearers[0]->bearers_subject_to_status_transfer_item().ul_coun_tvalue.pdcp_sn = 120; + bearers[0]->bearers_subject_to_status_transfer_item().ul_coun_tvalue.hfn = 4; tester.rrc.set_erab_status(0x46, bearers); TESTASSERT(tester.pdcp.bearers.count(0x46)); TESTASSERT(tester.pdcp.bearers[0x46].count(3)); @@ -381,6 +400,10 @@ int test_s1ap_tenb_mobility(test_event test_params) TESTASSERT(tester.pdcp.bearers[0x46][3].state.next_pdcp_rx_sn == 120); TESTASSERT(tester.pdcp.bearers[0x46][3].state.rx_hfn == 4); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); + // user PRACHs and sends C-RNTI CE sched_interface::ue_cfg_t ue_cfg{}; ue_cfg.supported_cc_list.resize(1); @@ -390,12 +413,12 @@ int test_s1ap_tenb_mobility(test_event test_params) tester.rrc.upd_user(0x47, 0x46); uint8_t recfg_complete[] = {0x10, 0x00}; - test_helpers::copy_msg_to_buffer(pdu, recfg_complete); + copy_msg_to_buffer(pdu, recfg_complete); tester.rrc.write_pdu(0x46, srb_to_lcid(lte_srb::srb1), std::move(pdu)); tester.tic(); - TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == sched_interface::ue_bearer_cfg_t::BOTH); - TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == sched_interface::ue_bearer_cfg_t::BOTH); - TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == sched_interface::ue_bearer_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::BOTH); TESTASSERT(mac_ue.pucch_cfg.I_sr == recfg_r8.rr_cfg_ded.phys_cfg_ded.sched_request_cfg.setup().sr_cfg_idx); TESTASSERT(mac_ue.pucch_cfg.n_pucch_sr == recfg_r8.rr_cfg_ded.phys_cfg_ded.sched_request_cfg.setup().sr_pucch_res_idx); @@ -430,10 +453,10 @@ int test_intraenb_mobility(srsran::log_sink_spy& spy, test_event test_params) /* Receive MeasReport from UE (correct if PCI=2) */ if (test_params == test_event::wrong_measreport) { uint8_t meas_report[] = {0x08, 0x10, 0x38, 0x74, 0x00, 0x0D, 0xBC, 0x80}; // PCI == 3 - test_helpers::copy_msg_to_buffer(pdu, meas_report); + copy_msg_to_buffer(pdu, meas_report); } else { uint8_t meas_report[] = {0x08, 0x10, 0x38, 0x74, 0x00, 0x09, 0xBC, 0x80}; // PCI == 2 - test_helpers::copy_msg_to_buffer(pdu, meas_report); + copy_msg_to_buffer(pdu, meas_report); } tester.rrc.write_pdu(tester.rnti, 1, std::move(pdu)); tester.tic(); @@ -453,7 +476,7 @@ int test_intraenb_mobility(srsran::log_sink_spy& spy, test_event test_params) if (test_params == test_event::concurrent_ho) { tester.pdcp.last_sdu = {}; uint8_t meas_report[] = {0x08, 0x10, 0x38, 0x74, 0x00, 0x09, 0xBC, 0x80}; // PCI == 2 - test_helpers::copy_msg_to_buffer(pdu, meas_report); + copy_msg_to_buffer(pdu, meas_report); tester.rrc.write_pdu(tester.rnti, 1, std::move(pdu)); tester.tic(); TESTASSERT(tester.pdcp.last_sdu.sdu == nullptr); @@ -482,6 +505,9 @@ int test_intraenb_mobility(srsran::log_sink_spy& spy, test_event test_params) TESTASSERT(recfg_r8.meas_cfg.meas_gap_cfg.type().value == setup_opts::setup); TESTASSERT((1 + recfg_r8.meas_cfg.meas_gap_cfg.setup().gap_offset.type().value) * 40u == tester.cfg.cell_list[1].meas_cfg.meas_gap_period); + auto* ue_cfg = &tester.mac.ue_db[tester.rnti]; + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == srsenb::mac_lc_ch_cfg_t::DL); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == srsenb::mac_lc_ch_cfg_t::DL); /* Test Case: The UE sends a C-RNTI CE. Bearers are reestablished, PHY is configured */ tester.pdcp.last_sdu.sdu = nullptr; @@ -489,8 +515,12 @@ int test_intraenb_mobility(srsran::log_sink_spy& spy, test_event test_params) TESTASSERT(tester.rlc.ue_db[tester.rnti].reest_sdu_counter == 0); TESTASSERT(tester.pdcp.last_sdu.sdu == nullptr); TESTASSERT(tester.phy.phy_cfg_set); - TESTASSERT(tester.phy.last_cfg.size() == 1 and tester.mac.ue_db[tester.rnti].supported_cc_list.size() == 1); - TESTASSERT(tester.phy.last_cfg[0].enb_cc_idx == tester.mac.ue_db[tester.rnti].supported_cc_list[0].enb_cc_idx); + TESTASSERT(tester.phy.last_cfg.size() == 1 and ue_cfg->supported_cc_list.size() == 1); + TESTASSERT(tester.phy.last_cfg[0].enb_cc_idx == ue_cfg->supported_cc_list[0].enb_cc_idx); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == srsenb::mac_lc_ch_cfg_t::IDLE); + TESTASSERT(ue_cfg->ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == srsenb::mac_lc_ch_cfg_t::IDLE); /* Test Case: The UE receives a duplicate C-RNTI CE. Nothing should happen */ if (test_params == test_event::duplicate_crnti_ce) { @@ -503,20 +533,25 @@ int test_intraenb_mobility(srsran::log_sink_spy& spy, test_event test_params) /* Test Case: Terminate first Handover. No extra messages should be sent DL. SR/CQI resources match recfg message */ uint8_t recfg_complete[] = {0x10, 0x00}; - test_helpers::copy_msg_to_buffer(pdu, recfg_complete); - tester.rrc.write_pdu(tester.rnti, srb_to_lcid(lte_srb::srb2), std::move(pdu)); + copy_msg_to_buffer(pdu, recfg_complete); + tester.rrc.write_pdu(tester.rnti, srb_to_lcid(lte_srb::srb1), std::move(pdu)); + tester.tic(); TESTASSERT(tester.pdcp.last_sdu.sdu == nullptr); - sched_interface::ue_cfg_t& ue_cfg = tester.mac.ue_db[tester.rnti]; - TESTASSERT(ue_cfg.pucch_cfg.sr_configured); - TESTASSERT(ue_cfg.pucch_cfg.n_pucch_sr == phy_cfg_ded.sched_request_cfg.setup().sr_pucch_res_idx); - TESTASSERT(ue_cfg.pucch_cfg.I_sr == phy_cfg_ded.sched_request_cfg.setup().sr_cfg_idx); - TESTASSERT(ue_cfg.supported_cc_list[0].dl_cfg.cqi_report.pmi_idx == + ue_cfg = &tester.mac.ue_db[tester.rnti]; + TESTASSERT(ue_cfg->pucch_cfg.sr_configured); + TESTASSERT(ue_cfg->pucch_cfg.n_pucch_sr == phy_cfg_ded.sched_request_cfg.setup().sr_pucch_res_idx); + TESTASSERT(ue_cfg->pucch_cfg.I_sr == phy_cfg_ded.sched_request_cfg.setup().sr_cfg_idx); + TESTASSERT(ue_cfg->supported_cc_list[0].dl_cfg.cqi_report.pmi_idx == phy_cfg_ded.cqi_report_cfg.cqi_report_periodic.setup().cqi_pmi_cfg_idx); - TESTASSERT(ue_cfg.pucch_cfg.n_pucch == phy_cfg_ded.cqi_report_cfg.cqi_report_periodic.setup().cqi_pucch_res_idx); + TESTASSERT(ue_cfg->pucch_cfg.n_pucch == phy_cfg_ded.cqi_report_cfg.cqi_report_periodic.setup().cqi_pucch_res_idx); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); /* Test Case: The RRC should be able to start a new handover */ uint8_t meas_report[] = {0x08, 0x10, 0x38, 0x74, 0x00, 0x05, 0xBC, 0x80}; // PCI == 1 - test_helpers::copy_msg_to_buffer(pdu, meas_report); + copy_msg_to_buffer(pdu, meas_report); tester.rrc.write_pdu(tester.rnti, 1, std::move(pdu)); tester.tic(); TESTASSERT(tester.s1ap.last_ho_required.rrc_container == nullptr); diff --git a/srsenb/test/rrc/rrc_nr_test.cc b/srsenb/test/rrc/rrc_nr_test.cc deleted file mode 100644 index 13564f704..000000000 --- a/srsenb/test/rrc/rrc_nr_test.cc +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsenb/hdr/stack/rrc/rrc_nr.h" -#include "srsenb/test/common/dummy_nr_classes.h" -#include "srsran/common/test_common.h" -#include - -using namespace asn1::rrc_nr; - -namespace srsenb { - -int test_cell_cfg(const srsenb::sched_interface::cell_cfg_t& cellcfg) -{ - // SIB1 must exist and have period 16rf - TESTASSERT(cellcfg.sibs[0].len > 0); - TESTASSERT(cellcfg.sibs[0].period_rf == 16); - - TESTASSERT(cellcfg.si_window_ms > 0); - return SRSRAN_SUCCESS; -} - -/* - * Test 1 - Test default SIB generation - * Description: Check whether the SIBs were set correctly - */ -int test_sib_generation() -{ - srsran::timer_handler timers_db(128); - - mac_dummy mac_obj; - rlc_dummy rlc_obj; - pdcp_dummy pdcp_obj; - rrc_nr rrc_obj(&timers_db); - - // set cfg - rrc_nr_cfg_t default_cfg = {}; - rrc_nr_cfg_t rrc_cfg = rrc_obj.update_default_cfg(default_cfg); - auto& sched_elem = rrc_cfg.sib1.si_sched_info.sched_info_list[0]; - - TESTASSERT(rrc_obj.init(rrc_cfg, nullptr, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr) == SRSRAN_SUCCESS); - - TESTASSERT(test_cell_cfg(mac_obj.cellcfgobj) == SRSRAN_SUCCESS); - // TEMP tests - TESTASSERT(mac_obj.cellcfgobj.sibs[1].len > 0); - TESTASSERT(mac_obj.cellcfgobj.sibs[1].period_rf == sched_elem.si_periodicity.to_number()); - for (int i = 2; i < 16; ++i) { - TESTASSERT(mac_obj.cellcfgobj.sibs[i].len == 0); - } - TESTASSERT(mac_obj.cellcfgobj.cell.nof_prb == 25); - - return SRSRAN_SUCCESS; -} - -int test_rrc_setup() -{ - srsran::timer_handler timers_db(128); - - mac_dummy mac_obj; - rlc_dummy rlc_obj; - pdcp_dummy pdcp_obj; - rrc_nr rrc_obj(&timers_db); - - // set cfg - rrc_nr_cfg_t default_cfg = {}; - rrc_nr_cfg_t rrc_cfg = rrc_obj.update_default_cfg(default_cfg); - TESTASSERT(rrc_obj.init(rrc_cfg, nullptr, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr) == SRSRAN_SUCCESS); - - for (uint32_t n = 0; n < 2; ++n) { - uint32_t timeout = 5500; - for (uint32_t i = 0; i < timeout and rlc_obj.last_sdu == nullptr; ++i) { - timers_db.step_all(); - } - TESTASSERT(rlc_obj.last_sdu != nullptr); - } - return SRSRAN_SUCCESS; -} - -} // namespace srsenb - -int main() -{ - TESTASSERT(srsenb::test_sib_generation() == SRSRAN_SUCCESS); - TESTASSERT(srsenb::test_rrc_setup() == SRSRAN_SUCCESS); - - return SRSRAN_SUCCESS; -} diff --git a/srsenb/test/rrc/rrc_paging_test.cc b/srsenb/test/rrc/rrc_paging_test.cc new file mode 100644 index 000000000..c4f574b2e --- /dev/null +++ b/srsenb/test/rrc/rrc_paging_test.cc @@ -0,0 +1,57 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsenb/hdr/stack/rrc/rrc_paging.h" +#include "srsran/common/test_common.h" + +using namespace srsenb; + +void test_paging() +{ + unsigned paging_cycle = 32; + float nb = 1; + paging_manager pcch_manager{paging_cycle, nb}; + + unsigned ue_id = 4780; + unsigned mmec = 10; + uint8_t m_tmsi[] = {0x64, 0x04, 0x00, 0x02}; + + pcch_manager.add_tmsi_paging(ue_id, mmec, m_tmsi); + + // \remark: See TS 36.304, section 7.1. + unsigned N = std::min(paging_cycle, (unsigned)std::round(nb * paging_cycle)); + unsigned Ns = std::max(1, (int)nb); + unsigned i_s = (ue_id / N) % Ns; + TESTASSERT_EQ(0, i_s); + tti_point t{0}; + for (unsigned count = 0; count < 1024 * 10; ++count, ++t) { + if (pcch_manager.pending_pcch_bytes(t) > 0) { + fmt::print("[{}]\n", t); + TESTASSERT_EQ((paging_cycle / N) * (ue_id % N), (t.sfn() % paging_cycle)); + TESTASSERT_EQ(9, t.sf_idx()); // PO when i_s == 0. + } + } +} + +int main() +{ + test_paging(); +} \ No newline at end of file diff --git a/srsenb/test/upper/test_helpers.cc b/srsenb/test/rrc/test_helpers.cc similarity index 76% rename from srsenb/test/upper/test_helpers.cc rename to srsenb/test/rrc/test_helpers.cc index 5b8fcd681..5d066e2a7 100644 --- a/srsenb/test/upper/test_helpers.cc +++ b/srsenb/test/rrc/test_helpers.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,6 +23,8 @@ #include "srsenb/hdr/enb.h" #include "srsran/common/test_common.h" +using namespace asn1::rrc; + namespace argparse { std::string repository_dir; @@ -37,7 +39,7 @@ int parse_default_cfg_phy(rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, srsenb::all_ar *rrc_cfg = {}; args.enb_files.sib_config = argparse::repository_dir + "/sib.conf.example"; args.enb_files.rr_config = argparse::repository_dir + "/rr.conf.example"; - args.enb_files.drb_config = argparse::repository_dir + "/drb.conf.example"; + args.enb_files.rb_config = argparse::repository_dir + "/rb.conf.example"; srslog::fetch_basic_logger("TEST").debug("sib file path=%s", args.enb_files.sib_config.c_str()); args.enb.enb_id = 0x19B; @@ -48,34 +50,51 @@ int parse_default_cfg_phy(rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, srsenb::all_ar args.general.rrc_inactivity_timer = 60000; - return enb_conf_sections::parse_cfg_files(&args, rrc_cfg, phy_cfg); + rrc_nr_cfg_t rrc_cfg_nr; + + return enb_conf_sections::parse_cfg_files(&args, rrc_cfg, &rrc_cfg_nr, phy_cfg); } int parse_default_cfg(rrc_cfg_t* rrc_cfg, srsenb::all_args_t& args) { - args = {}; - *rrc_cfg = {}; - args.enb_files.sib_config = argparse::repository_dir + "/sib.conf.example"; - args.enb_files.rr_config = argparse::repository_dir + "/rr.conf.example"; - args.enb_files.drb_config = argparse::repository_dir + "/drb.conf.example"; - srslog::fetch_basic_logger("TEST").debug("sib file path=%s", args.enb_files.sib_config.c_str()); + phy_cfg_t phy_cfg; + rrc_nr_cfg_t rrc_cfg_nr; + return parse_default_cfg(&args, rrc_cfg, &phy_cfg, &rrc_cfg_nr); +} - args.enb.enb_id = 0x19B; - args.enb.dl_earfcn = 3400; - args.enb.n_prb = 50; - TESTASSERT(srsran::string_to_mcc("001", &args.stack.s1ap.mcc)); - TESTASSERT(srsran::string_to_mnc("01", &args.stack.s1ap.mnc)); - args.enb.transmission_mode = 1; - args.enb.nof_ports = 1; - args.general.eia_pref_list = "EIA2, EIA1, EIA0"; - args.general.eea_pref_list = "EEA0, EEA2, EEA1"; - args.stack.mac.nof_prealloc_ues = 2; +int parse_default_cfg(rrc_nr_cfg_t* rrc_nr_cfg) +{ + srsenb::all_args_t args; + phy_cfg_t phy_cfg; + rrc_cfg_t rrc_cfg; + return parse_default_cfg(&args, &rrc_cfg, &phy_cfg, rrc_nr_cfg); +} - args.general.rrc_inactivity_timer = 60000; +int parse_default_cfg(srsenb::all_args_t* args, rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, rrc_nr_cfg_t* rrc_nr_cfg) +{ + *args = {}; + *rrc_cfg = {}; + *phy_cfg = {}; - phy_cfg_t phy_cfg; + args->enb_files.sib_config = argparse::repository_dir + "/sib.conf.example"; + args->enb_files.rr_config = argparse::repository_dir + "/rr.conf.example"; + args->enb_files.rb_config = argparse::repository_dir + "/rb.conf.example"; + srslog::fetch_basic_logger("TEST").debug("sib file path=%s", args->enb_files.sib_config.c_str()); - return enb_conf_sections::parse_cfg_files(&args, rrc_cfg, &phy_cfg); + args->enb.enb_id = 0x19B; + args->enb.dl_earfcn = 3400; + args->enb.n_prb = 50; + TESTASSERT(srsran::string_to_mcc("001", &args->stack.s1ap.mcc)); + TESTASSERT(srsran::string_to_mnc("01", &args->stack.s1ap.mnc)); + args->enb.transmission_mode = 1; + args->enb.nof_ports = 1; + args->general.eia_pref_list = "EIA2, EIA1, EIA0"; + args->general.eea_pref_list = "EEA0, EEA2, EEA1"; + args->stack.mac.nof_prealloc_ues = 2; + + args->general.rrc_inactivity_timer = 60000; + + return enb_conf_sections::parse_cfg_files(args, rrc_cfg, rrc_nr_cfg, phy_cfg); } int bring_rrc_to_reconf_state(srsenb::rrc& rrc, srsran::timer_handler& timers, uint16_t rnti) @@ -118,9 +137,8 @@ int bring_rrc_to_reconf_state(srsenb::rrc& rrc, srsran::timer_handler& timers, u asn1::cbit_ref bref(byte_buf.msg, byte_buf.N_bytes); TESTASSERT(s1ap_pdu.unpack(bref) == asn1::SRSASN_SUCCESS); rrc.setup_ue_ctxt(rnti, s1ap_pdu.init_msg().value.init_context_setup_request()); - for (auto& item : - s1ap_pdu.init_msg().value.init_context_setup_request().protocol_ies.erab_to_be_setup_list_ctxt_su_req.value) { - const auto& erab = item.value.erab_to_be_setup_item_ctxt_su_req(); + for (auto& item : s1ap_pdu.init_msg().value.init_context_setup_request()->erab_to_be_setup_list_ctxt_su_req.value) { + const auto& erab = item->erab_to_be_setup_item_ctxt_su_req(); asn1::s1ap::cause_c cause; TESTASSERT(rrc.setup_erab(rnti, erab.erab_id, @@ -165,10 +183,10 @@ namespace srsenb { meas_cell_cfg_t generate_cell1() { meas_cell_cfg_t cell1{}; - cell1.earfcn = 3400; - cell1.pci = 1; - cell1.q_offset = 0; - cell1.eci = 0x19C01; + cell1.earfcn = 3400; + cell1.pci = 1; + cell1.cell_individual_offset = asn1::rrc::q_offset_range_opts::db0; + cell1.eci = 0x19C01; return cell1; } @@ -190,7 +208,7 @@ report_cfg_eutra_s generate_rep1() bool is_cell_cfg_equal(const meas_cell_cfg_t& cfg, const cells_to_add_mod_s& cell) { - return cfg.pci == cell.pci and cell.cell_individual_offset.to_number() == (int8_t)round(cfg.q_offset); + return cfg.pci == cell.pci and cell.cell_individual_offset == cell.cell_individual_offset; } } // namespace srsenb diff --git a/srsenb/test/upper/test_helpers.h b/srsenb/test/rrc/test_helpers.h similarity index 90% rename from srsenb/test/upper/test_helpers.h rename to srsenb/test/rrc/test_helpers.h index e0f4c2e28..34727d472 100644 --- a/srsenb/test/upper/test_helpers.h +++ b/srsenb/test/rrc/test_helpers.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,10 +24,10 @@ #include "srsenb/src/enb_cfg_parser.h" #include "srsenb/test/common/dummy_classes.h" +#include "srsenb/test/common/dummy_classes_common.h" #include "srsran/adt/span.h" using namespace srsenb; -using namespace asn1::rrc; namespace argparse { @@ -90,6 +90,7 @@ public: bool send_ho_required(uint16_t rnti, uint32_t target_eci, + uint16_t target_tac, srsran::plmn_id_t target_plmn, srsran::span fwd_erabs, srsran::unique_byte_buffer_t rrc_container, @@ -148,7 +149,7 @@ public: } void enable_integrity(uint16_t rnti, uint32_t lcid) override { bearers[rnti][lcid].enable_integrity = true; } void enable_encryption(uint16_t rnti, uint32_t lcid) override { bearers[rnti][lcid].enable_encryption = true; } - void config_security(uint16_t rnti, uint32_t lcid, srsran::as_security_config_t sec_cfg_) override + void config_security(uint16_t rnti, uint32_t lcid, const srsran::as_security_config_t& sec_cfg_) override { bearers[rnti][lcid].sec_cfg = sec_cfg_; } @@ -184,14 +185,14 @@ public: class mac_mobility_dummy : public mac_dummy { public: - int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) override + int ue_cfg(uint16_t rnti, const sched_interface::ue_cfg_t* cfg) override { ue_db[rnti] = *cfg; return 0; } - int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, sched_interface::ue_cfg_t* cfg) override + int ue_set_crnti(uint16_t temp_crnti, uint16_t crnti, const sched_interface::ue_cfg_t& cfg) override { - ue_db[crnti] = *cfg; + ue_db[crnti] = cfg; return 0; } std::map ue_db; @@ -213,7 +214,9 @@ public: namespace test_helpers { +int parse_default_cfg(srsenb::all_args_t* args, rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, rrc_nr_cfg_t* rrc_nr_cfg); int parse_default_cfg(rrc_cfg_t* rrc_cfg, srsenb::all_args_t& args); +int parse_default_cfg(rrc_nr_cfg_t* rrc_nr_cfg); int parse_default_cfg_phy(rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, srsenb::all_args_t& args); template @@ -227,13 +230,6 @@ bool unpack_asn1(ASN1Type& asn1obj, srsran::const_byte_span pdu) return true; } -inline void copy_msg_to_buffer(srsran::unique_byte_buffer_t& pdu, srsran::const_byte_span msg) -{ - pdu = srsran::make_byte_buffer(); - memcpy(pdu->msg, msg.data(), msg.size()); - pdu->N_bytes = msg.size(); -} - int bring_rrc_to_reconf_state(srsenb::rrc& rrc, srsran::timer_handler& timers, uint16_t rnti); } // namespace test_helpers @@ -242,9 +238,9 @@ namespace srsenb { meas_cell_cfg_t generate_cell1(); -report_cfg_eutra_s generate_rep1(); +asn1::rrc::report_cfg_eutra_s generate_rep1(); -bool is_cell_cfg_equal(const meas_cell_cfg_t& cfg, const cells_to_add_mod_s& cell); +bool is_cell_cfg_equal(const meas_cell_cfg_t& cfg, const asn1::rrc::cells_to_add_mod_s& cell); } // namespace srsenb diff --git a/srsenb/test/s1ap/CMakeLists.txt b/srsenb/test/s1ap/CMakeLists.txt new file mode 100644 index 000000000..7bebcff4b --- /dev/null +++ b/srsenb/test/s1ap/CMakeLists.txt @@ -0,0 +1,23 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(s1ap_test s1ap_test.cc) +target_link_libraries(s1ap_test srsran_common s1ap_asn1 srsenb_s1ap srsenb_upper s1ap_asn1 ${SCTP_LIBRARIES} ${ATOMIC_LIBS}) +add_test(s1ap_test s1ap_test) \ No newline at end of file diff --git a/srsenb/test/upper/s1ap_test.cc b/srsenb/test/s1ap/s1ap_test.cc similarity index 74% rename from srsenb/test/upper/s1ap_test.cc rename to srsenb/test/s1ap/s1ap_test.cc index 629dce670..9088fb3c1 100644 --- a/srsenb/test/upper/s1ap_test.cc +++ b/srsenb/test/s1ap/s1ap_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsenb/hdr/stack/upper/s1ap.h" +#include "srsenb/hdr/stack/s1ap/s1ap.h" #include "srsenb/test/common/dummy_classes.h" #include "srsran/common/network_utils.h" #include "srsran/common/test_common.h" @@ -96,7 +96,7 @@ struct dummy_socket_manager : public srsran::socket_manager_itf { return true; } - int s1u_fd; + int s1u_fd = -1; recv_callback_t callback; }; @@ -141,8 +141,8 @@ void run_s1_setup(s1ap& s1ap_obj, mme_dummy& mme) sctp_sndrcvinfo rcvinfo = {}; int flags = 0; uint8_t s1_setup_resp[] = {0x20, 0x11, 0x00, 0x25, 0x00, 0x00, 0x03, 0x00, 0x3d, 0x40, 0x0a, 0x03, 0x80, 0x73, - 0x72, 0x73, 0x6d, 0x6d, 0x65, 0x30, 0x31, 0x00, 0x69, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0xf1, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00, 0x57, 0x40, 0x01, 0xff}; + 0x72, 0x73, 0x6d, 0x6d, 0x65, 0x30, 0x31, 0x00, 0x69, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0xf1, 0x10, 0x00, 0x00, 0x01, 0x00, 0x00, 0x1a, 0x00, 0x57, 0x40, 0x01, 0xff}; memcpy(sdu->msg, s1_setup_resp, sizeof(s1_setup_resp)); sdu->N_bytes = sizeof(s1_setup_resp); TESTASSERT(s1ap_obj.handle_mme_rx_msg(std::move(sdu), mme_addr, rcvinfo, flags)); @@ -173,16 +173,16 @@ void add_rnti(s1ap& s1ap_obj, mme_dummy& mme) sctp_sndrcvinfo rcvinfo = {}; int flags = 0; uint8_t icsr_msg[] = { - 0x00, 0x09, 0x00, 0x80, 0xac, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, - 0x00, 0x01, 0x00, 0x42, 0x00, 0x0a, 0x18, 0x3b, 0x9a, 0xca, 0x00, 0x60, 0x3b, 0x9a, 0xca, 0x00, 0x00, 0x18, - 0x00, 0x5e, 0x00, 0x00, 0x34, 0x00, 0x59, 0x45, 0x00, 0x09, 0x3c, 0x0f, 0x80, 0x7f, 0x00, 0x01, 0x64, 0x00, - 0x00, 0x00, 0x01, 0x4a, 0x27, 0x9b, 0x6d, 0xe9, 0x42, 0x01, 0x07, 0x42, 0x01, 0x3e, 0x06, 0x00, 0x00, 0xf1, - 0x10, 0x00, 0x07, 0x00, 0x1d, 0x52, 0x01, 0xc1, 0x01, 0x09, 0x07, 0x06, 0x73, 0x72, 0x73, 0x61, 0x70, 0x6e, - 0x05, 0x01, 0xc0, 0xa8, 0x0a, 0x02, 0x27, 0x08, 0x80, 0x00, 0x0d, 0x04, 0x08, 0x08, 0x08, 0x08, 0x50, 0x0b, - 0xf6, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x1a, 0x32, 0xdd, 0x59, 0x35, 0x13, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x23, - 0x05, 0xf4, 0x32, 0xdd, 0x59, 0x35, 0x00, 0x6b, 0x00, 0x05, 0x18, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x49, 0x00, - 0x20, 0x84, 0xa4, 0xea, 0x15, 0x55, 0xb3, 0xe0, 0xf4, 0x55, 0xbe, 0x1f, 0x41, 0x52, 0x92, 0xfc, 0x04, 0xd8, - 0x02, 0x38, 0x0d, 0xe0, 0x81, 0x29, 0xe1, 0xaa, 0xd7, 0xc4, 0x7b, 0x12, 0x95, 0x72, 0xbe}; + 0x00, 0x09, 0x00, 0x80, 0xac, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x08, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x42, 0x00, 0x0a, 0x18, 0x3b, 0x9a, 0xca, 0x00, 0x60, 0x3b, 0x9a, 0xca, 0x00, 0x00, 0x18, + 0x00, 0x5e, 0x00, 0x00, 0x34, 0x00, 0x59, 0x45, 0x00, 0x09, 0x3c, 0x0f, 0x80, 0x7f, 0x00, 0x01, 0x64, 0x00, + 0x00, 0x00, 0x01, 0x4a, 0x27, 0x9b, 0x6d, 0xe9, 0x42, 0x01, 0x07, 0x42, 0x01, 0x3e, 0x06, 0x00, 0x00, 0xf1, + 0x10, 0x00, 0x07, 0x00, 0x1d, 0x52, 0x01, 0xc1, 0x01, 0x09, 0x07, 0x06, 0x73, 0x72, 0x73, 0x61, 0x70, 0x6e, + 0x05, 0x01, 0xc0, 0xa8, 0x0a, 0x02, 0x27, 0x08, 0x80, 0x00, 0x0d, 0x04, 0x08, 0x08, 0x08, 0x08, 0x50, 0x0b, + 0xf6, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x1a, 0x32, 0xdd, 0x59, 0x35, 0x13, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x23, + 0x05, 0xf4, 0x32, 0xdd, 0x59, 0x35, 0x00, 0x6b, 0x00, 0x05, 0x18, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x49, 0x00, + 0x20, 0x84, 0xa4, 0xea, 0x15, 0x55, 0xb3, 0xe0, 0xf4, 0x55, 0xbe, 0x1f, 0x41, 0x52, 0x92, 0xfc, 0x04, 0xd8, + 0x02, 0x38, 0x0d, 0xe0, 0x81, 0x29, 0xe1, 0xaa, 0xd7, 0xc4, 0x7b, 0x12, 0x95, 0x72, 0xbe}; sdu = srsran::make_byte_buffer(); memcpy(sdu->msg, icsr_msg, sizeof(icsr_msg)); sdu->N_bytes = sizeof(icsr_msg); @@ -194,16 +194,16 @@ void add_rnti(s1ap& s1ap_obj, mme_dummy& mme) 0x40, 0x0a, 0x0a, 0x1f, 0x7f, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01}; cbref = asn1::cbit_ref(icsresp, sizeof(icsresp)); TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS); - s1ap_obj.ue_ctxt_setup_complete(0x46); + s1ap_obj.notify_rrc_reconf_complete(0x46); sdu = mme.read_msg(); TESTASSERT(sdu->N_bytes > 0); cbref = asn1::cbit_ref{sdu->msg, sdu->N_bytes}; TESTASSERT(s1ap_pdu.unpack(cbref) == SRSRAN_SUCCESS); TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::successful_outcome); TESTASSERT(s1ap_pdu.successful_outcome().proc_code == ASN1_S1AP_ID_INIT_CONTEXT_SETUP); - const auto& resp = s1ap_pdu.successful_outcome().value.init_context_setup_resp().protocol_ies; - TESTASSERT(resp.erab_setup_list_ctxt_su_res.value.size() > 0); - TESTASSERT(not resp.erab_failed_to_setup_list_ctxt_su_res_present); + const auto& resp = s1ap_pdu.successful_outcome().value.init_context_setup_resp(); + TESTASSERT(resp->erab_setup_list_ctxt_su_res.value.size() > 0); + TESTASSERT(not resp->erab_failed_to_setup_list_ctxt_su_res_present); } enum class test_event { success, wrong_erabid_mod, wrong_mme_s1ap_id, repeated_erabid_mod }; @@ -234,6 +234,9 @@ void test_s1ap_erab_setup(test_event event) args.enb_name = "srsenb01"; TESTASSERT(s1ap_obj.init(args, &rrc) == SRSRAN_SUCCESS); + // The S1 Setup Procedure will call `notify_background_task_result` + // which we need to manually trigger with `run_next_task()` + task_sched.run_next_task(); run_s1_setup(s1ap_obj, mme); add_rnti(s1ap_obj, mme); @@ -244,13 +247,13 @@ void test_s1ap_erab_setup(test_event event) int flags = 0; asn1::s1ap::s1ap_pdu_c mod_req_pdu; mod_req_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ERAB_MODIFY); - auto& protocols = mod_req_pdu.init_msg().value.erab_modify_request().protocol_ies; - protocols.enb_ue_s1ap_id.value = 1; - protocols.mme_ue_s1ap_id.value = event == test_event::wrong_mme_s1ap_id ? 2 : 1; - auto& erab_list = protocols.erab_to_be_modified_list_bearer_mod_req.value; + auto& protocols = mod_req_pdu.init_msg().value.erab_modify_request(); + protocols->enb_ue_s1ap_id.value = 1; + protocols->mme_ue_s1ap_id.value = event == test_event::wrong_mme_s1ap_id ? 2 : 1; + auto& erab_list = protocols->erab_to_be_modified_list_bearer_mod_req.value; erab_list.resize(2); erab_list[0].load_info_obj(ASN1_S1AP_ID_ERAB_TO_BE_MODIFIED_ITEM_BEARER_MOD_REQ); - auto* erab_ptr = &erab_list[0].value.erab_to_be_modified_item_bearer_mod_req(); + auto* erab_ptr = &erab_list[0]->erab_to_be_modified_item_bearer_mod_req(); erab_ptr->erab_id = 5; erab_ptr->erab_level_qos_params.qci = 9; erab_ptr->erab_level_qos_params.alloc_retention_prio.prio_level = 15; @@ -259,9 +262,10 @@ void test_s1ap_erab_setup(test_event event) erab_ptr->erab_level_qos_params.alloc_retention_prio.pre_emption_vulnerability.value = asn1::s1ap::pre_emption_vulnerability_opts::not_pre_emptable; erab_ptr->nas_pdu.resize(1); - erab_list[1] = erab_list[0]; - erab_ptr = &erab_list[1].value.erab_to_be_modified_item_bearer_mod_req(); - erab_ptr->erab_id = event == test_event::repeated_erabid_mod ? 5 : 6; + erab_ptr->nas_pdu[0] = 0; + erab_list[1] = erab_list[0]; + erab_ptr = &erab_list[1]->erab_to_be_modified_item_bearer_mod_req(); + erab_ptr->erab_id = event == test_event::repeated_erabid_mod ? 5 : 6; if (event == test_event::wrong_erabid_mod) { rrc.next_erabs_failed_to_modify.push_back(6); } @@ -281,34 +285,33 @@ void test_s1ap_erab_setup(test_event event) // See TS 36.413, Section 10.6 - Handling of AP ID TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::init_msg); TESTASSERT(s1ap_pdu.init_msg().proc_code == ASN1_S1AP_ID_ERROR_IND); - auto& protocol_ies = s1ap_pdu.init_msg().value.error_ind().protocol_ies; - TESTASSERT(protocol_ies.mme_ue_s1ap_id_present and protocol_ies.mme_ue_s1ap_id.value.value == 2); - TESTASSERT(protocol_ies.enb_ue_s1ap_id_present and protocol_ies.enb_ue_s1ap_id.value.value == 1); + auto& err_ind = s1ap_pdu.init_msg().value.error_ind(); + TESTASSERT(err_ind->mme_ue_s1ap_id_present and err_ind->mme_ue_s1ap_id.value.value == 2); + TESTASSERT(err_ind->enb_ue_s1ap_id_present and err_ind->enb_ue_s1ap_id.value.value == 1); TESTASSERT(rrc.last_released_rnti == 0x46); return; } TESTASSERT(s1ap_pdu.type().value == asn1::s1ap::s1ap_pdu_c::types_opts::successful_outcome); TESTASSERT(s1ap_pdu.successful_outcome().proc_code == ASN1_S1AP_ID_ERAB_MODIFY); - auto& protocol_ies = s1ap_pdu.successful_outcome().value.erab_modify_resp().protocol_ies; + auto& erab_mod = s1ap_pdu.successful_outcome().value.erab_modify_resp(); if (event == test_event::wrong_erabid_mod) { - TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res_present); - TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res.value.size() == 1); - TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res.value[0].value.erab_modify_item_bearer_mod_res().erab_id == - 5); - TESTASSERT(protocol_ies.erab_failed_to_modify_list_present); - TESTASSERT(protocol_ies.erab_failed_to_modify_list.value.size() == 1); - auto& erab_item = protocol_ies.erab_failed_to_modify_list.value[0].value.erab_item(); + TESTASSERT(erab_mod->erab_modify_list_bearer_mod_res_present); + TESTASSERT(erab_mod->erab_modify_list_bearer_mod_res.value.size() == 1); + TESTASSERT(erab_mod->erab_modify_list_bearer_mod_res.value[0]->erab_modify_item_bearer_mod_res().erab_id == 5); + TESTASSERT(erab_mod->erab_failed_to_modify_list_present); + TESTASSERT(erab_mod->erab_failed_to_modify_list.value.size() == 1); + auto& erab_item = erab_mod->erab_failed_to_modify_list.value[0]->erab_item(); TESTASSERT(erab_item.erab_id == 6); TESTASSERT(erab_item.cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network); TESTASSERT(erab_item.cause.radio_network().value == asn1::s1ap::cause_radio_network_opts::unknown_erab_id); return; } if (event == test_event::repeated_erabid_mod) { - TESTASSERT(not protocol_ies.erab_modify_list_bearer_mod_res_present); - TESTASSERT(protocol_ies.erab_failed_to_modify_list_present); - TESTASSERT(protocol_ies.erab_failed_to_modify_list.value.size() == 1); - auto& erab_item = protocol_ies.erab_failed_to_modify_list.value[0].value.erab_item(); + TESTASSERT(not erab_mod->erab_modify_list_bearer_mod_res_present); + TESTASSERT(erab_mod->erab_failed_to_modify_list_present); + TESTASSERT(erab_mod->erab_failed_to_modify_list.value.size() == 1); + auto& erab_item = erab_mod->erab_failed_to_modify_list.value[0]->erab_item(); TESTASSERT(erab_item.erab_id == 5); TESTASSERT(erab_item.cause.type().value == asn1::s1ap::cause_c::types_opts::radio_network); TESTASSERT(erab_item.cause.radio_network().value == @@ -316,10 +319,10 @@ void test_s1ap_erab_setup(test_event event) return; } - TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res_present); - TESTASSERT(not protocol_ies.erab_failed_to_modify_list_present); - TESTASSERT(protocol_ies.erab_modify_list_bearer_mod_res.value.size() == 2); - auto& erab_item = protocol_ies.erab_modify_list_bearer_mod_res.value[0].value.erab_modify_item_bearer_mod_res(); + TESTASSERT(erab_mod->erab_modify_list_bearer_mod_res_present); + TESTASSERT(not erab_mod->erab_failed_to_modify_list_present); + TESTASSERT(erab_mod->erab_modify_list_bearer_mod_res.value.size() == 2); + auto& erab_item = erab_mod->erab_modify_list_bearer_mod_res.value[0]->erab_modify_item_bearer_mod_res(); TESTASSERT(erab_item.erab_id == 5); } @@ -337,4 +340,4 @@ int main(int argc, char** argv) test_s1ap_erab_setup(test_event::wrong_erabid_mod); test_s1ap_erab_setup(test_event::wrong_mme_s1ap_id); test_s1ap_erab_setup(test_event::repeated_erabid_mod); -} \ No newline at end of file +} diff --git a/srsenb/test/upper/CMakeLists.txt b/srsenb/test/upper/CMakeLists.txt index 8ced20e9e..e054f1fcb 100644 --- a/srsenb/test/upper/CMakeLists.txt +++ b/srsenb/test/upper/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,31 +18,13 @@ # and at http://www.gnu.org/licenses/. # -add_library(test_helpers test_helpers.cc) -target_link_libraries(test_helpers srsenb_rrc srsenb_common rrc_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) - # Simple PLMN -> MCC/MNC test add_executable(plmn_test plmn_test.cc) target_link_libraries(plmn_test rrc_asn1) -add_executable(rrc_mobility_test rrc_mobility_test.cc) -target_link_libraries(rrc_mobility_test srsran_asn1 test_helpers) - -add_executable(erab_setup_test erab_setup_test.cc) -target_link_libraries(erab_setup_test test_helpers ${LIBCONFIGPP_LIBRARIES}) - -add_executable(rrc_meascfg_test rrc_meascfg_test.cc) -target_link_libraries(rrc_meascfg_test test_helpers) - add_executable(gtpu_test gtpu_test.cc) -target_link_libraries(gtpu_test srsran_common s1ap_asn1 srsenb_upper srsran_upper ${SCTP_LIBRARIES}) +target_link_libraries(gtpu_test srsran_common s1ap_asn1 srsenb_upper srsran_gtpu ${SCTP_LIBRARIES}) -add_executable(s1ap_test s1ap_test.cc) -target_link_libraries(s1ap_test srsran_common s1ap_asn1 srsenb_upper srsran_upper s1ap_asn1 ${SCTP_LIBRARIES}) - -add_test(rrc_mobility_test rrc_mobility_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) -add_test(erab_setup_test erab_setup_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) -add_test(rrc_meascfg_test rrc_meascfg_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_test(plmn_test plmn_test) add_test(gtpu_test gtpu_test) diff --git a/srsenb/test/upper/gtpu_test.cc b/srsenb/test/upper/gtpu_test.cc index e07d8295b..a25e275db 100644 --- a/srsenb/test/upper/gtpu_test.cc +++ b/srsenb/test/upper/gtpu_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,6 +26,7 @@ #include "srsenb/hdr/stack/upper/gtpu.h" #include "srsenb/test/common/dummy_classes.h" +#include "srsenb/test/common/dummy_classes_common.h" #include "srsran/common/network_utils.h" #include "srsran/common/test_common.h" #include "srsran/upper/gtpu.h" @@ -37,35 +38,35 @@ static const size_t PDU_HEADER_SIZE = 20; class pdcp_tester : public pdcp_dummy { public: - void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn) override + void write_sdu(uint16_t rnti, uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu, int pdcp_sn) override { - last_sdu = std::move(sdu); - last_pdcp_sn = pdcp_sn; - last_rnti = rnti; - last_lcid = lcid; + last_sdu = std::move(sdu); + last_pdcp_sn = pdcp_sn; + last_rnti = rnti; + last_eps_bearer_id = eps_bearer_id; } - std::map get_buffered_pdus(uint16_t rnti, uint32_t lcid) override + std::map get_buffered_pdus(uint16_t rnti, uint32_t eps_bearer_id) override { return std::move(buffered_pdus); } void send_status_report(uint16_t rnti) override {} - void send_status_report(uint16_t rnti, uint32_t lcid) override {} + void send_status_report(uint16_t rnti, uint32_t eps_bearer_id) override {} void push_buffered_pdu(uint32_t sn, srsran::unique_byte_buffer_t pdu) { buffered_pdus[sn] = std::move(pdu); } void clear() { - last_sdu = nullptr; - last_pdcp_sn = -1; - last_lcid = 0; - last_rnti = SRSRAN_INVALID_RNTI; + last_sdu = nullptr; + last_pdcp_sn = -1; + last_eps_bearer_id = 0; + last_rnti = SRSRAN_INVALID_RNTI; } std::map buffered_pdus; srsran::unique_byte_buffer_t last_sdu; - int last_pdcp_sn = -1; - uint16_t last_rnti = SRSRAN_INVALID_RNTI; - uint32_t last_lcid = 0; + int last_pdcp_sn = -1; + uint16_t last_rnti = SRSRAN_INVALID_RNTI; + uint32_t last_eps_bearer_id = 0; }; struct dummy_socket_manager : public srsran::socket_manager_itf { @@ -161,42 +162,44 @@ void test_gtpu_tunnel_manager() const char* sgw_addr_str = "127.0.0.1"; struct sockaddr_in sgw_sockaddr = {}; srsran::net_utils::set_sockaddr(&sgw_sockaddr, sgw_addr_str, GTPU_PORT); - uint32_t sgw_addr = ntohl(sgw_sockaddr.sin_addr.s_addr); - const uint32_t drb1_lcid = 3; + uint32_t sgw_addr = ntohl(sgw_sockaddr.sin_addr.s_addr); + const uint32_t drb1_eps_bearer_id = 5; srsran::task_scheduler task_sched; + gtpu_args_t gtpu_args = {}; gtpu_tunnel_manager tunnels(&task_sched, srslog::fetch_basic_logger("GTPU")); + tunnels.init(gtpu_args, nullptr); TESTASSERT(tunnels.find_tunnel(0) == nullptr); - TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty()); + TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x46, drb1_eps_bearer_id).empty()); TESTASSERT(tunnels.find_rnti_tunnels(0x46) == nullptr); - // Creation of tunnels for different users and lcids - const gtpu_tunnel* tun = tunnels.add_tunnel(0x46, drb1_lcid, 5, sgw_addr); + // Creation of tunnels for different users and bearers + const gtpu_tunnel* tun = tunnels.add_tunnel(0x46, drb1_eps_bearer_id, 5, sgw_addr); TESTASSERT(tun != nullptr); TESTASSERT(tunnels.find_tunnel(tun->teid_in) == tun); - const gtpu_tunnel* tun2 = tunnels.add_tunnel(0x47, drb1_lcid, 6, sgw_addr); + const gtpu_tunnel* tun2 = tunnels.add_tunnel(0x47, drb1_eps_bearer_id, 6, sgw_addr); TESTASSERT(tun2 != nullptr); TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2); - tun2 = tunnels.add_tunnel(0x47, drb1_lcid + 1, 7, sgw_addr); + tun2 = tunnels.add_tunnel(0x47, drb1_eps_bearer_id + 1, 7, sgw_addr); TESTASSERT(tun2 != nullptr); TESTASSERT(tunnels.find_tunnel(tun2->teid_in) == tun2); - TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1); - TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x47, drb1_lcid).size() == 1); - TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x47, drb1_lcid + 1).size() == 1); + TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x46, drb1_eps_bearer_id).size() == 1); + TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x47, drb1_eps_bearer_id).size() == 1); + TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x47, drb1_eps_bearer_id + 1).size() == 1); // TEST: Creation/Removal of indirect tunnel - const gtpu_tunnel* fwd_tun = tunnels.add_tunnel(0x46, drb1_lcid, 8, sgw_addr); + const gtpu_tunnel* fwd_tun = tunnels.add_tunnel(0x46, drb1_eps_bearer_id, 8, sgw_addr); TESTASSERT(fwd_tun != nullptr); TESTASSERT(tunnels.find_tunnel(fwd_tun->teid_in) == fwd_tun); tunnels.setup_forwarding(tun->teid_in, fwd_tun->teid_in); - TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2); + TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x46, drb1_eps_bearer_id).size() == 2); // Removing a tunnel also clears any associated forwarding tunnel TESTASSERT(tunnels.remove_tunnel(tun->teid_in)); - TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).empty()); + TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x46, drb1_eps_bearer_id).empty()); // TEST: Prioritization of one TEID over another - const gtpu_tunnel* before_tun = tunnels.add_tunnel(0x46, drb1_lcid, 7, sgw_addr); - const gtpu_tunnel* after_tun = tunnels.add_tunnel(0x46, drb1_lcid, 8, sgw_addr); + const gtpu_tunnel* before_tun = tunnels.add_tunnel(0x46, drb1_eps_bearer_id, 7, sgw_addr); + const gtpu_tunnel* after_tun = tunnels.add_tunnel(0x46, drb1_eps_bearer_id, 8, sgw_addr); TESTASSERT(before_tun != nullptr and after_tun != nullptr); tunnels.set_tunnel_priority(before_tun->teid_in, after_tun->teid_in); for (uint32_t i = 0; i < 1000; ++i) { @@ -206,20 +209,22 @@ void test_gtpu_tunnel_manager() tunnels.handle_rx_pdcp_sdu(before_tun->teid_in); } // Removing active TEID, will automatically switch TEID paths - TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 2); + TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x46, drb1_eps_bearer_id).size() == 2); tunnels.remove_tunnel(before_tun->teid_in); - TESTASSERT(tunnels.find_rnti_lcid_tunnels(0x46, drb1_lcid).size() == 1); + TESTASSERT(tunnels.find_rnti_bearer_tunnels(0x46, drb1_eps_bearer_id).size() == 1); TESTASSERT(after_tun->state == gtpu_tunnel_manager::tunnel_state::pdcp_active); } -enum class tunnel_test_event { success, wait_end_marker_timeout }; +enum class tunnel_test_event { success, wait_end_marker_timeout, ue_removal_no_marker, reest_senb }; int test_gtpu_direct_tunneling(tunnel_test_event event) { + std::random_device rd; + std::mt19937 g(rd()); srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST"); logger.info("\n\n**** Test GTPU Direct Tunneling ****\n"); uint16_t rnti = 0x46, rnti2 = 0x50; - uint32_t drb1 = 3; + uint32_t drb1_bearer_id = 5; uint32_t sgw_teidout1 = 1, sgw_teidout2 = 2; const char * sgw_addr_str = "127.0.0.1", *senb_addr_str = "127.0.1.1", *tenb_addr_str = "127.0.1.2"; struct sockaddr_in senb_sockaddr = {}, sgw_sockaddr = {}, tenb_sockaddr = {}; @@ -241,12 +246,18 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) dummy_socket_manager senb_rx_sockets, tenb_rx_sockets; srsenb::gtpu senb_gtpu(&task_sched, logger1, &senb_rx_sockets), tenb_gtpu(&task_sched, logger2, &tenb_rx_sockets); pdcp_tester senb_pdcp, tenb_pdcp; - senb_gtpu.init(senb_addr_str, sgw_addr_str, "", "", &senb_pdcp, false); - tenb_gtpu.init(tenb_addr_str, sgw_addr_str, "", "", &tenb_pdcp, false); - + gtpu_args_t gtpu_args; + gtpu_args.gtp_bind_addr = senb_addr_str; + gtpu_args.mme_addr = sgw_addr_str; + gtpu_args.indirect_tunnel_timeout_msec = std::uniform_int_distribution{500, 2000}(g); + senb_gtpu.init(gtpu_args, &senb_pdcp); + gtpu_args.gtp_bind_addr = tenb_addr_str; + tenb_gtpu.init(gtpu_args, &tenb_pdcp); + uint32_t addr_in1; + uint32_t addr_in2; // create tunnels MME-SeNB and MME-TeNB - uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1, sgw_addr, sgw_teidout1).value(); - uint32_t tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, sgw_addr, sgw_teidout2).value(); + uint32_t senb_teid_in = senb_gtpu.add_bearer(rnti, drb1_bearer_id, sgw_addr, sgw_teidout1, addr_in1).value(); + uint32_t tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1_bearer_id, sgw_addr, sgw_teidout2, addr_in2).value(); // Buffer PDUs in SeNB PDCP for (size_t sn = 6; sn < 10; ++sn) { @@ -259,14 +270,14 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) gtpu::bearer_props props; props.flush_before_teidin_present = true; props.flush_before_teidin = tenb_teid_in; - uint32_t dl_tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1, senb_addr, 0, &props).value(); + uint32_t addr_in3; + uint32_t dl_tenb_teid_in = tenb_gtpu.add_bearer(rnti2, drb1_bearer_id, senb_addr, 0, addr_in3, &props).value(); props = {}; props.forward_from_teidin_present = true; props.forward_from_teidin = senb_teid_in; - senb_gtpu.add_bearer(rnti, drb1, tenb_addr, dl_tenb_teid_in, &props); + uint32_t addr_in4; + senb_gtpu.add_bearer(rnti, drb1_bearer_id, tenb_addr, dl_tenb_teid_in, addr_in4, &props); - std::random_device rd; - std::mt19937 g(rd()); std::vector data_vec(10); std::iota(data_vec.begin(), data_vec.end(), 0); std::vector encoded_data; @@ -281,7 +292,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) pdu_view = srsran::make_span(tenb_pdcp.last_sdu); TESTASSERT(std::count(pdu_view.begin() + PDU_HEADER_SIZE, pdu_view.end(), 7) == 10); TESTASSERT(tenb_pdcp.last_rnti == rnti2); - TESTASSERT(tenb_pdcp.last_lcid == drb1); + TESTASSERT(tenb_pdcp.last_eps_bearer_id == drb1_bearer_id); TESTASSERT(tenb_pdcp.last_pdcp_sn == (int)7); // TEST: verify that PDCP buffered SNs have been forwarded through SeNB->TeNB tunnel @@ -290,7 +301,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) pdu_view = srsran::make_span(tenb_pdcp.last_sdu); TESTASSERT(std::count(pdu_view.begin() + PDU_HEADER_SIZE, pdu_view.end(), sn) == 10); TESTASSERT(tenb_pdcp.last_rnti == rnti2); - TESTASSERT(tenb_pdcp.last_lcid == drb1); + TESTASSERT(tenb_pdcp.last_eps_bearer_id == drb1_bearer_id); TESTASSERT(tenb_pdcp.last_pdcp_sn == (int)sn); } @@ -303,7 +314,7 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) pdu_view = srsran::make_span(tenb_pdcp.last_sdu); TESTASSERT(pdu_view.size() == encoded_data.size() and std::equal(pdu_view.begin(), pdu_view.end(), encoded_data.begin())); - TESTASSERT(tenb_pdcp.last_rnti == rnti2 and tenb_pdcp.last_lcid == drb1); + TESTASSERT(tenb_pdcp.last_rnti == rnti2 and tenb_pdcp.last_eps_bearer_id == drb1_bearer_id); // TEST: verify that MME->TeNB packets are buffered until SeNB->TeNB tunnel is closed tenb_pdcp.clear(); @@ -329,9 +340,28 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) TESTASSERT(tenb_pdcp.last_sdu == nullptr); if (event == tunnel_test_event::wait_end_marker_timeout) { // TEST: EndMarker does not reach TeNB, but there is a timeout that will resume the new GTPU tunnel - for (size_t i = 0; i < 2001; ++i) { + for (size_t i = 0; i < gtpu_args.indirect_tunnel_timeout_msec + 1; ++i) { task_sched.tic(); } + } else if (event == tunnel_test_event::ue_removal_no_marker) { + // TEST: EndMarker may even reach SeNB, but the SeNB receives in tandem the UEContextReleaseCommand and closes + // the user tunnels before the chance to send an EndMarker + senb_gtpu.rem_user(0x46); + tenb_gtpu.handle_gtpu_s1u_rx_packet(read_socket(tenb_rx_sockets.s1u_fd), senb_sockaddr); + } else if (event == tunnel_test_event::reest_senb) { + // TEST: UE may start a Reestablishment to the SeNB. In such case, the rnti will be updated, the forwarding tunnel + // taken down, and the previous main tunnel reestablished + senb_gtpu.mod_bearer_rnti(0x46, 0x47); + std::iota(data_vec.begin(), data_vec.end(), 0); + std::shuffle(data_vec.begin(), data_vec.end(), g); + pdu = encode_gtpu_packet(data_vec, senb_teid_in, sgw_sockaddr, senb_sockaddr); + encoded_data.assign(pdu->msg + 8u, pdu->msg + pdu->N_bytes); + senb_pdcp.last_sdu = nullptr; + senb_gtpu.handle_gtpu_s1u_rx_packet(std::move(pdu), sgw_sockaddr); + TESTASSERT(senb_pdcp.last_sdu != nullptr); + TESTASSERT(senb_pdcp.last_sdu->N_bytes == encoded_data.size() and + memcmp(senb_pdcp.last_sdu->msg, encoded_data.data(), encoded_data.size()) == 0); + return SRSRAN_SUCCESS; } else { // TEST: EndMarker is forwarded via MME->SeNB->TeNB, and TeNB buffered PDUs are flushed pdu = encode_end_marker(senb_teid_in); @@ -340,6 +370,13 @@ int test_gtpu_direct_tunneling(tunnel_test_event event) } srsran::span encoded_data2{tenb_pdcp.last_sdu->msg + 20u, tenb_pdcp.last_sdu->msg + 30u}; TESTASSERT(std::all_of(encoded_data2.begin(), encoded_data2.end(), [N_pdus](uint8_t b) { return b == N_pdus - 1; })); + if (event != tunnel_test_event::ue_removal_no_marker) { + // The User is removed in SeNB in case it hasn't and there was no reestablishment + if (std::uniform_int_distribution{0, 1}(g) > 0) { + senb_gtpu.rem_bearer(0x46, drb1_bearer_id); + } + senb_gtpu.rem_user(0x46); + } return SRSRAN_SUCCESS; } @@ -359,6 +396,8 @@ int main(int argc, char** argv) srsenb::test_gtpu_tunnel_manager(); TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::success) == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::wait_end_marker_timeout) == SRSRAN_SUCCESS); + TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::ue_removal_no_marker) == SRSRAN_SUCCESS); + TESTASSERT(srsenb::test_gtpu_direct_tunneling(srsenb::tunnel_test_event::reest_senb) == SRSRAN_SUCCESS); srslog::flush(); diff --git a/srsenb/test/upper/plmn_test.cc b/srsenb/test/upper/plmn_test.cc index 439257db0..c77944ede 100644 --- a/srsenb/test/upper/plmn_test.cc +++ b/srsenb/test/upper/plmn_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/CMakeLists.txt b/srsepc/CMakeLists.txt index 47df8d5b8..fb9c78800 100644 --- a/srsepc/CMakeLists.txt +++ b/srsepc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsepc/epc.conf.example b/srsepc/epc.conf.example index 5152c3cc9..e0aa15393 100644 --- a/srsepc/epc.conf.example +++ b/srsepc/epc.conf.example @@ -10,6 +10,8 @@ # tac: 16-bit Tracking Area Code. # mcc: Mobile Country Code # mnc: Mobile Network Code +# full_net_name Display Name of the Network +# short_net_name Short Display Name of the Network # apn: Set Access Point Name (APN) # mme_bind_addr: IP bind addr to listen for eNB S1-MME connnections # dns_addr: DNS server address for the UEs @@ -18,6 +20,8 @@ # integrity_algo: Preferred integrity protection algorithm for NAS # (supported: EIA0 (rejected by most UEs), EIA1 (default), EIA2, EIA3 # paging_timer: Value of paging timer in seconds (T3413) +# request_imeisv: Request UE's IMEI-SV in security mode command +# lac: 16-bit Location Area Code. # ##################################################################### [mme] @@ -32,6 +36,8 @@ dns_addr = 8.8.8.8 encryption_algo = EEA0 integrity_algo = EIA1 paging_timer = 2 +request_imeisv = false +lac = 0x0006 ##################################################################### # HSS configuration diff --git a/srsepc/hdr/hss/hss.h b/srsepc/hdr/hss/hss.h index da70f3409..5d18d6cfb 100644 --- a/srsepc/hdr/hss/hss.h +++ b/srsepc/hdr/hss/hss.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,7 +33,7 @@ #include "srsran/interfaces/epc_interfaces.h" #include "srsran/srslog/srslog.h" #include -#include + #include #define LTE_FDD_ENB_IND_HE_N_BITS 5 @@ -43,15 +43,15 @@ namespace srsepc { -typedef struct { +struct hss_args_t { std::string db_file; uint16_t mcc; uint16_t mnc; -} hss_args_t; +}; enum hss_auth_algo { HSS_ALGO_XOR, HSS_ALGO_MILENAGE }; -typedef struct { +struct hss_ue_ctx_t { // Members std::string name; uint64_t imsi; @@ -70,7 +70,7 @@ typedef struct { void set_sqn(const uint8_t* sqn_); void set_last_rand(const uint8_t* rand_); void get_last_rand(uint8_t* rand_); -} hss_ue_ctx_t; +}; class hss : public hss_interface_nas { @@ -103,7 +103,6 @@ private: void resync_sqn_milenage(hss_ue_ctx_t* ue_ctx, uint8_t* auts); void resync_sqn_xor(hss_ue_ctx_t* ue_ctx, uint8_t* auts); - std::vector split_string(const std::string& str, char delimiter); void get_uint_vec_from_hex_str(const std::string& key_str, uint8_t* key, uint len); void increment_ue_sqn(hss_ue_ctx_t* ue_ctx); diff --git a/srsepc/hdr/mbms-gw/mbms-gw.h b/srsepc/hdr/mbms-gw/mbms-gw.h index 6391a1317..c8883d4bb 100644 --- a/srsepc/hdr/mbms-gw/mbms-gw.h +++ b/srsepc/hdr/mbms-gw/mbms-gw.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/mme/mme.h b/srsepc/hdr/mme/mme.h index c8043d9fb..370d13c2b 100644 --- a/srsepc/hdr/mme/mme.h +++ b/srsepc/hdr/mme/mme.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/mme/mme_gtpc.h b/srsepc/hdr/mme/mme_gtpc.h index 53a5e4b1d..87fe681f8 100644 --- a/srsepc/hdr/mme/mme_gtpc.h +++ b/srsepc/hdr/mme/mme_gtpc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/mme/nas.h b/srsepc/hdr/mme/nas.h index 90c11e0ce..79f137bb8 100644 --- a/srsepc/hdr/mme/nas.h +++ b/srsepc/hdr/mme/nas.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -141,6 +141,8 @@ typedef struct { std::string short_net_name; srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + bool request_imeisv; + uint16_t lac; } nas_init_t; typedef struct { @@ -224,6 +226,7 @@ public: /* Uplink NAS messages handling */ bool handle_attach_request(srsran::byte_buffer_t* nas_rx); + bool handle_pdn_connectivity_request(srsran::byte_buffer_t* nas_rx); bool handle_authentication_response(srsran::byte_buffer_t* nas_rx); bool handle_security_mode_complete(srsran::byte_buffer_t* nas_rx); bool handle_attach_complete(srsran::byte_buffer_t* nas_rx); @@ -245,7 +248,7 @@ public: bool pack_attach_accept(srsran::byte_buffer_t* nas_buffer); /* Security functions */ - bool integrity_check(srsran::byte_buffer_t* pdu); + bool integrity_check(srsran::byte_buffer_t* pdu, bool warn_failure = true); bool short_integrity_check(srsran::byte_buffer_t* pdu); void integrity_generate(srsran::byte_buffer_t* pdu, uint8_t* mac); void cipher_decrypt(srsran::byte_buffer_t* pdu); @@ -277,6 +280,8 @@ private: std::string m_dns; std::string m_full_net_name; std::string m_short_net_name; + bool m_request_imeisv = false; + uint16_t m_lac = 0; // Timers timeout values uint16_t m_t3413 = 0; diff --git a/srsepc/hdr/mme/s1ap.h b/srsepc/hdr/mme/s1ap.h index 34d798baa..f2d8dd99e 100644 --- a/srsepc/hdr/mme/s1ap.h +++ b/srsepc/hdr/mme/s1ap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/mme/s1ap_common.h b/srsepc/hdr/mme/s1ap_common.h index 41813e3d3..9fa8c736f 100644 --- a/srsepc/hdr/mme/s1ap_common.h +++ b/srsepc/hdr/mme/s1ap_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -51,6 +51,8 @@ typedef struct { std::string pcap_filename; srsran::CIPHERING_ALGORITHM_ID_ENUM encryption_algo; srsran::INTEGRITY_ALGORITHM_ID_ENUM integrity_algo; + bool request_imeisv; + uint16_t lac; } s1ap_args_t; typedef struct { diff --git a/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h b/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h index f2243b930..9956051f6 100644 --- a/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h +++ b/srsepc/hdr/mme/s1ap_ctx_mngmt_proc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/mme/s1ap_erab_mngmt_proc.h b/srsepc/hdr/mme/s1ap_erab_mngmt_proc.h index e5b8958cf..299407116 100644 --- a/srsepc/hdr/mme/s1ap_erab_mngmt_proc.h +++ b/srsepc/hdr/mme/s1ap_erab_mngmt_proc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/mme/s1ap_mngmt_proc.h b/srsepc/hdr/mme/s1ap_mngmt_proc.h index a469b4848..88154068e 100644 --- a/srsepc/hdr/mme/s1ap_mngmt_proc.h +++ b/srsepc/hdr/mme/s1ap_mngmt_proc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/mme/s1ap_nas_transport.h b/srsepc/hdr/mme/s1ap_nas_transport.h index e7fd5fb0f..b9f22de0b 100644 --- a/srsepc/hdr/mme/s1ap_nas_transport.h +++ b/srsepc/hdr/mme/s1ap_nas_transport.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/mme/s1ap_paging.h b/srsepc/hdr/mme/s1ap_paging.h index 3831d5bd4..ea7e07530 100644 --- a/srsepc/hdr/mme/s1ap_paging.h +++ b/srsepc/hdr/mme/s1ap_paging.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,8 +45,8 @@ public: bool send_paging(uint64_t imsi, uint16_t erab_to_setup); private: - mme* m_mme; - s1ap* m_s1ap; + mme* m_mme = nullptr; + s1ap* m_s1ap = nullptr; srslog::basic_logger& m_logger = srslog::fetch_basic_logger("S1AP"); s1ap_args_t m_s1ap_args; diff --git a/srsepc/hdr/spgw/gtpc.h b/srsepc/hdr/spgw/gtpc.h index 93bf033b7..d04e8efd9 100644 --- a/srsepc/hdr/spgw/gtpc.h +++ b/srsepc/hdr/spgw/gtpc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/spgw/gtpu.h b/srsepc/hdr/spgw/gtpu.h index a605a627b..9b789625c 100644 --- a/srsepc/hdr/spgw/gtpu.h +++ b/srsepc/hdr/spgw/gtpu.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/hdr/spgw/spgw.h b/srsepc/hdr/spgw/spgw.h index 0a31c071e..9612b18a6 100644 --- a/srsepc/hdr/spgw/spgw.h +++ b/srsepc/hdr/spgw/spgw.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 979507085..b0a699ef4 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -39,9 +39,11 @@ target_link_libraries( srsepc srsepc_mme srsepc_hss srsepc_sgw s1ap_asn1 - srsran_upper + srsran_gtpu + srsran_asn1 srsran_common srslog + support ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${SEC_LIBRARIES} @@ -50,7 +52,8 @@ target_link_libraries( srsepc srsepc_mme add_executable(srsmbms mbms-gw/main.cc ) target_link_libraries(srsmbms srsepc_mbms_gw - srsran_upper + srsran_asn1 + srsran_gtpu srsran_common srslog ${CMAKE_THREAD_LIBS_INIT} @@ -73,5 +76,5 @@ else(NOT ${BUILDEPC_CMD} STREQUAL "") message(STATUS "No post-build-EPC command defined") endif (NOT ${BUILDEPC_CMD} STREQUAL "") -install(TARGETS srsepc DESTINATION ${RUNTIME_DIR}) -install(TARGETS srsmbms DESTINATION ${RUNTIME_DIR}) +install(TARGETS srsepc DESTINATION ${RUNTIME_DIR} OPTIONAL) +install(TARGETS srsmbms DESTINATION ${RUNTIME_DIR} OPTIONAL) diff --git a/srsepc/src/hss/CMakeLists.txt b/srsepc/src/hss/CMakeLists.txt index 7385b7a9a..1e79f7138 100644 --- a/srsepc/src/hss/CMakeLists.txt +++ b/srsepc/src/hss/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsepc/src/hss/hss.cc b/srsepc/src/hss/hss.cc index e26e0d28d..8f6271260 100644 --- a/srsepc/src/hss/hss.cc +++ b/srsepc/src/hss/hss.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,6 +20,7 @@ */ #include "srsepc/hdr/hss/hss.h" #include "srsran/common/security.h" +#include "srsran/common/string_helpers.h" #include #include // for printing uint64_t #include @@ -103,7 +104,7 @@ bool hss::read_db_file(std::string db_filename) while (std::getline(m_db_file, line)) { if (line[0] != '#' && line.length() > 0) { uint column_size = 10; - std::vector split = split_string(line, ','); + std::vector split = srsran::split_string(line, ','); if (split.size() != column_size) { m_logger.error("Error parsing UE database. Wrong number of columns in .csv"); m_logger.error("Columns: %zd, Expected %d.", split.size(), column_size); @@ -124,20 +125,20 @@ bool hss::read_db_file(std::string db_filename) return false; } ue_ctx->imsi = strtoull(split[2].c_str(), nullptr, 10); - get_uint_vec_from_hex_str(split[3], ue_ctx->key, 16); + srsran::get_uint_vec_from_hex_str(split[3], ue_ctx->key, 16); if (split[4] == std::string("op")) { ue_ctx->op_configured = true; - get_uint_vec_from_hex_str(split[5], ue_ctx->op, 16); + srsran::get_uint_vec_from_hex_str(split[5], ue_ctx->op, 16); srsran::compute_opc(ue_ctx->key, ue_ctx->op, ue_ctx->opc); } else if (split[4] == std::string("opc")) { ue_ctx->op_configured = false; - get_uint_vec_from_hex_str(split[5], ue_ctx->opc, 16); + srsran::get_uint_vec_from_hex_str(split[5], ue_ctx->opc, 16); } else { m_logger.error("Neither OP nor OPc configured."); return false; } - get_uint_vec_from_hex_str(split[6], ue_ctx->amf, 2); - get_uint_vec_from_hex_str(split[7], ue_ctx->sqn, 6); + srsran::get_uint_vec_from_hex_str(split[6], ue_ctx->amf, 2); + srsran::get_uint_vec_from_hex_str(split[7], ue_ctx->sqn, 6); m_logger.debug("Added user from DB, IMSI: %015" PRIu64 "", ue_ctx->imsi); m_logger.debug(ue_ctx->key, 16, "User Key : "); @@ -223,19 +224,19 @@ bool hss::write_db_file(std::string db_filename) m_db_file << ","; m_db_file << std::setfill('0') << std::setw(15) << it->second->imsi; m_db_file << ","; - m_db_file << hex_string(it->second->key, 16); + m_db_file << srsran::hex_string(it->second->key, 16); m_db_file << ","; if (it->second->op_configured) { m_db_file << "op,"; - m_db_file << hex_string(it->second->op, 16); + m_db_file << srsran::hex_string(it->second->op, 16); } else { m_db_file << "opc,"; - m_db_file << hex_string(it->second->opc, 16); + m_db_file << srsran::hex_string(it->second->opc, 16); } m_db_file << ","; - m_db_file << hex_string(it->second->amf, 2); + m_db_file << srsran::hex_string(it->second->amf, 2); m_db_file << ","; - m_db_file << hex_string(it->second->sqn, 6); + m_db_file << srsran::hex_string(it->second->sqn, 6); m_db_file << ","; m_db_file << it->second->qci; if (it->second->static_ip_addr != "0.0.0.0") { @@ -311,8 +312,12 @@ void hss::gen_auth_info_answer_milenage(hss_ue_ctx_t* ue_ctx, m_logger.debug(sqn, 6, "User SQN : "); m_logger.debug(mac, 8, "User MAC : "); + uint8_t ak_xor_sqn[6]; + for (int i = 0; i < 6; i++) { + ak_xor_sqn[i] = sqn[i] ^ ak[i]; + } // Generate K_asme - srsran::security_generate_k_asme(ck, ik, ak, sqn, mcc, mnc, k_asme); + srsran::security_generate_k_asme(ck, ik, ak_xor_sqn, mcc, mnc, k_asme); m_logger.debug("User MCC : %x MNC : %x ", mcc, mnc); m_logger.debug(k_asme, 32, "User k_asme : "); @@ -405,8 +410,12 @@ void hss::gen_auth_info_answer_xor(hss_ue_ctx_t* ue_ctx, uint8_t* k_asme, uint8_ autn[8 + i] = mac[i]; } + uint8_t ak_xor_sqn[6]; + for (int i = 0; i < 6; i++) { + ak_xor_sqn[i] = sqn[i] ^ ak[i]; + } // Generate K_asme - srsran::security_generate_k_asme(ck, ik, ak, sqn, mcc, mnc, k_asme); + srsran::security_generate_k_asme(ck, ik, ak_xor_sqn, mcc, mnc, k_asme); m_logger.debug("User MCC : %x MNC : %x ", mcc, mnc); m_logger.debug(k_asme, 32, "User k_asme : "); @@ -612,41 +621,6 @@ hss_ue_ctx_t* hss::get_ue_ctx(uint64_t imsi) return ue_ctx_it->second.get(); } -/* Helper functions*/ -std::vector hss::split_string(const std::string& str, char delimiter) -{ - std::vector tokens; - std::string token; - std::istringstream tokenStream(str); - - while (std::getline(tokenStream, token, delimiter)) { - tokens.push_back(token); - } - return tokens; -} - -void hss::get_uint_vec_from_hex_str(const std::string& key_str, uint8_t* key, uint len) -{ - const char* pos = key_str.c_str(); - - for (uint count = 0; count < len; count++) { - sscanf(pos, "%2hhx", &key[count]); - pos += 2; - } - return; -} - -std::string hss::hex_string(uint8_t* hex, int size) -{ - std::stringstream ss; - - ss << std::hex << std::setfill('0'); - for (int i = 0; i < size; i++) { - ss << std::setw(2) << static_cast(hex[i]); - } - return ss.str(); -} - std::map hss::get_ip_to_imsi(void) const { return m_ip_to_imsi; diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index d112298db..88bac0665 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,9 +27,10 @@ #include "srsran/common/common_helper.h" #include "srsran/common/config_file.h" #include "srsran/common/crash_handler.h" -#include "srsran/common/signal_handler.h" #include "srsran/srslog/srslog.h" #include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" +#include "srsran/support/signal_handler.h" #include #include @@ -64,6 +65,9 @@ typedef struct { log_args_t log_args; } all_args_t; +static srslog::sink* log_sink = nullptr; +static std::atomic running = {true}; + /********************************************************************** * Program arguments processing ***********************************************************************/ @@ -89,9 +93,11 @@ void parse_args(all_args_t* args, int argc, char* argv[]) string dns_addr; string full_net_name; string short_net_name; + bool request_imeisv; string hss_db_file; string hss_auth_algo; string log_filename; + string lac; // Command line only options bpo::options_description general("General options"); @@ -118,6 +124,8 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("mme.encryption_algo", bpo::value(&encryption_algo)->default_value("EEA0"), "Set preferred encryption algorithm for NAS layer ") ("mme.integrity_algo", bpo::value(&integrity_algo)->default_value("EIA1"), "Set preferred integrity protection algorithm for NAS") ("mme.paging_timer", bpo::value(&paging_timer)->default_value(2), "Set paging timer value in seconds (T3413)") + ("mme.request_imeisv", bpo::value(&request_imeisv)->default_value(false), "Enable IMEISV request in Security mode command") + ("mme.lac", bpo::value(&lac)->default_value("0x01"), "Location Area Code") ("hss.db_file", bpo::value(&hss_db_file)->default_value("ue_db.csv"), ".csv file that stores UE's keys") ("spgw.gtpu_bind_addr", bpo::value(&spgw_bind_addr)->default_value("127.0.0.1"), "IP address of SP-GW for the S1-U connection") ("spgw.sgi_if_addr", bpo::value(&sgi_if_addr)->default_value("176.16.0.1"), "IP address of TUN interface for the SGi connection") @@ -221,6 +229,11 @@ void parse_args(all_args_t* args, int argc, char* argv[]) sstr << std::hex << vm["mme.tac"].as(); sstr >> args->mme_args.s1ap_args.tac; } + { + std::stringstream sstr; + sstr << std::hex << vm["mme.lac"].as(); + sstr >> args->mme_args.s1ap_args.lac; + } // Convert MCC/MNC strings if (!srsran::string_to_mcc(mcc, &args->mme_args.s1ap_args.mcc)) { @@ -270,18 +283,19 @@ void parse_args(all_args_t* args, int argc, char* argv[]) cout << "Using default mme.integrity_algo: EIA1" << endl; } - args->mme_args.s1ap_args.mme_bind_addr = mme_bind_addr; - args->mme_args.s1ap_args.mme_name = mme_name; - args->mme_args.s1ap_args.dns_addr = dns_addr; + args->mme_args.s1ap_args.mme_bind_addr = mme_bind_addr; + args->mme_args.s1ap_args.mme_name = mme_name; + args->mme_args.s1ap_args.dns_addr = dns_addr; args->mme_args.s1ap_args.full_net_name = full_net_name; args->mme_args.s1ap_args.short_net_name = short_net_name; - args->mme_args.s1ap_args.mme_apn = mme_apn; - args->mme_args.s1ap_args.paging_timer = paging_timer; - args->spgw_args.gtpu_bind_addr = spgw_bind_addr; - args->spgw_args.sgi_if_addr = sgi_if_addr; - args->spgw_args.sgi_if_name = sgi_if_name; - args->spgw_args.max_paging_queue = max_paging_queue; - args->hss_args.db_file = hss_db_file; + args->mme_args.s1ap_args.mme_apn = mme_apn; + args->mme_args.s1ap_args.paging_timer = paging_timer; + args->mme_args.s1ap_args.request_imeisv = request_imeisv; + args->spgw_args.gtpu_bind_addr = spgw_bind_addr; + args->spgw_args.sgi_if_addr = sgi_if_addr; + args->spgw_args.sgi_if_name = sgi_if_name; + args->spgw_args.max_paging_queue = max_paging_queue; + args->hss_args.db_file = hss_db_file; // Apply all_level to any unset layers if (vm.count("log.all_level")) { @@ -362,9 +376,23 @@ std::string get_build_string() return ss.str(); } +static void emergency_cleanup_handler(void* data) +{ + srslog::flush(); + if (log_sink) { + log_sink->flush(); + } +} + +static void signal_handler() +{ + running = false; +} + int main(int argc, char* argv[]) { - srsran_register_signal_handler(); + srsran_register_signal_handler(signal_handler); + add_emergency_cleanup_handler(emergency_cleanup_handler, nullptr); // print build info cout << endl << get_build_string() << endl; @@ -372,7 +400,7 @@ int main(int argc, char* argv[]) cout << endl << "--- Software Radio Systems EPC ---" << endl << endl; srsran_debug_handle_crash(argc, argv); - all_args_t args; + all_args_t args = {}; parse_args(&args, argc, argv); // Setup logging. diff --git a/srsepc/src/mbms-gw/CMakeLists.txt b/srsepc/src/mbms-gw/CMakeLists.txt index 0ec89b729..36df424c0 100644 --- a/srsepc/src/mbms-gw/CMakeLists.txt +++ b/srsepc/src/mbms-gw/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsepc/src/mbms-gw/main.cc b/srsepc/src/mbms-gw/main.cc index d4dceced0..08ae3e96d 100644 --- a/srsepc/src/mbms-gw/main.cc +++ b/srsepc/src/mbms-gw/main.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/src/mbms-gw/mbms-gw.cc b/srsepc/src/mbms-gw/mbms-gw.cc index 95d7c56cc..184d5b633 100644 --- a/srsepc/src/mbms-gw/mbms-gw.cc +++ b/srsepc/src/mbms-gw/mbms-gw.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,6 +21,7 @@ #include "srsepc/hdr/mbms-gw/mbms-gw.h" #include "srsran/common/standard_streams.h" +#include "srsran/common/network_utils.h" #include "srsran/upper/gtpu.h" #include #include @@ -161,9 +162,14 @@ int mbms_gw::init_sgi_mb_if(mbms_gw_args_t* args) // Set IP of the interface struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr; - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = inet_addr(args->sgi_mb_if_addr.c_str()); - addr->sin_port = 0; + + if (not srsran::net_utils::set_sockaddr(addr, args->sgi_mb_if_addr.c_str(), 0)) { + m_logger.error("Invalid sgi_mb_if_addr: %s", args->sgi_mb_if_addr.c_str()); + srsran::console("Invalid sgi_mb_if_addr: %s\n", args->sgi_mb_if_addr.c_str()); + close(m_sgi_mb_if); + close(sgi_mb_sock); + return SRSRAN_ERROR_CANT_START; + } if (ioctl(sgi_mb_sock, SIOCSIFADDR, &ifr) < 0) { m_logger.error( @@ -174,7 +180,12 @@ int mbms_gw::init_sgi_mb_if(mbms_gw_args_t* args) } ifr.ifr_netmask.sa_family = AF_INET; - ((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr(args->sgi_mb_if_mask.c_str()); + if (inet_pton(ifr.ifr_netmask.sa_family, args->sgi_mb_if_mask.c_str(), &((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr ) != 1) { + m_logger.error("Invalid sgi_mb_if_mask: %s", args->sgi_mb_if_mask.c_str()); + srsran::console("Invalid sgi_mb_if_mask: %s\n", args->sgi_mb_if_mask.c_str()); + perror("inet_pton"); + return SRSRAN_ERROR_CANT_START; + } if (ioctl(sgi_mb_sock, SIOCSIFNETMASK, &ifr) < 0) { m_logger.error("Failed to set TUN interface Netmask. Error: %s", strerror(errno)); close(m_sgi_mb_if); @@ -210,7 +221,12 @@ int mbms_gw::init_m1_u(mbms_gw_args_t* args) /* Set local interface for outbound multicast packets*/ /* The IP must be associated with a local multicast capable interface */ struct in_addr local_if; - local_if.s_addr = inet_addr(args->m1u_multi_if.c_str()); + if (inet_pton(AF_INET, args->m1u_multi_if.c_str(), &local_if.s_addr) != 1) { + m_logger.error("Invalid m1u_multi_if: %s", args->m1u_multi_if.c_str()); + srsran::console("Invalid m1u_multi_if: %s\n", args->m1u_multi_if.c_str()); + perror("inet_pton"); + return SRSRAN_ERROR_CANT_START; + } if (setsockopt(m_m1u, IPPROTO_IP, IP_MULTICAST_IF, (char*)&local_if, sizeof(struct in_addr)) < 0) { m_logger.error("Error %s setting multicast interface %s.", strerror(errno), args->m1u_multi_if.c_str()); return SRSRAN_ERROR_CANT_START; @@ -227,7 +243,12 @@ int mbms_gw::init_m1_u(mbms_gw_args_t* args) bzero(&m_m1u_multi_addr, sizeof(m_m1u_multi_addr)); m_m1u_multi_addr.sin_family = AF_INET; m_m1u_multi_addr.sin_port = htons(GTPU_RX_PORT + 1); - m_m1u_multi_addr.sin_addr.s_addr = inet_addr(args->m1u_multi_addr.c_str()); + if (inet_pton(m_m1u_multi_addr.sin_family, args->m1u_multi_addr.c_str(), &m_m1u_multi_addr.sin_addr.s_addr) != 1) { + m_logger.error("Invalid m1u_multi_addr: %s", args->m1u_multi_addr.c_str()); + srsran::console("Invalid m1u_multi_addr: %s\n", args->m1u_multi_addr.c_str()); + perror("inet_pton"); + return SRSRAN_ERROR_CANT_START; + } m_logger.info("Initialized M1-U"); return SRSRAN_SUCCESS; diff --git a/srsepc/src/mme/CMakeLists.txt b/srsepc/src/mme/CMakeLists.txt index 530c67e86..df702d41d 100644 --- a/srsepc/src/mme/CMakeLists.txt +++ b/srsepc/src/mme/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsepc/src/mme/mme.cc b/srsepc/src/mme/mme.cc index 5d36810e4..07bbbe921 100644 --- a/srsepc/src/mme/mme.cc +++ b/srsepc/src/mme/mme.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/src/mme/mme_gtpc.cc b/srsepc/src/mme/mme_gtpc.cc index b52afca6a..7d61c623c 100644 --- a/srsepc/src/mme/mme_gtpc.cc +++ b/srsepc/src/mme/mme_gtpc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/src/mme/nas.cc b/srsepc/src/mme/nas.cc index 1633a2164..1b01060e2 100644 --- a/srsepc/src/mme/nas.cc +++ b/srsepc/src/mme/nas.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,7 +45,9 @@ nas::nas(const nas_init_t& args, const nas_if_t& itf) : m_dns(args.dns), m_full_net_name(args.full_net_name), m_short_net_name(args.short_net_name), - m_t3413(args.paging_timer) + m_t3413(args.paging_timer), + m_request_imeisv(args.request_imeisv), + m_lac(args.lac) { m_sec_ctx.integ_algo = args.integ_algo; m_sec_ctx.cipher_algo = args.cipher_algo; @@ -643,8 +645,6 @@ bool nas::handle_service_request(uint32_t m_tmsi, srsran::console("Service Request -- Short MAC valid\n"); nas_logger.info("Service Request -- Short MAC valid"); if (ecm_ctx->state == ECM_STATE_CONNECTED) { - nas_logger.error("Service Request -- User is ECM CONNECTED"); - // Release previous context nas_logger.info("Service Request -- Releasing previouse ECM context. eNB S1AP Id %d, MME UE S1AP Id %d", ecm_ctx->enb_ue_s1ap_id, @@ -696,8 +696,6 @@ bool nas::handle_service_request(uint32_t m_tmsi, srsran::console("Service Request -- Short MAC invalid\n"); nas_logger.info("Service Request -- Short MAC invalid"); if (ecm_ctx->state == ECM_STATE_CONNECTED) { - nas_logger.error("Service Request -- User is ECM CONNECTED"); - // Release previous context nas_logger.info("Service Request -- Releasing previouse ECM context. eNB S1AP Id %d, MME UE S1AP Id %d", ecm_ctx->enb_ue_s1ap_id, @@ -774,6 +772,31 @@ bool nas::handle_detach_request(uint32_t m_tmsi, ecm_ctx_t* ecm_ctx = &nas_ctx->m_ecm_ctx; sec_ctx_t* sec_ctx = &nas_ctx->m_sec_ctx; + // TS 24.301, Sec 5.5.2.2.1, UE initiated detach request + if (detach_req.detach_type.switch_off == 0) { + // UE expects detach accept + srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + nas_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } + + LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT detach_accept = {}; + err = liblte_mme_pack_detach_accept_msg(&detach_accept, + LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS, + sec_ctx->dl_nas_count, + (LIBLTE_BYTE_MSG_STRUCT*)nas_tx.get()); + if (err != LIBLTE_SUCCESS) { + nas_logger.error("Error packing Detach Accept\n"); + } + + nas_logger.info("Sending detach accept.\n"); + sec_ctx->dl_nas_count++; + s1ap->send_downlink_nas_transport(enb_ue_s1ap_id, s1ap->get_next_mme_ue_s1ap_id(), nas_tx.get(), *enb_sri); + } else { + nas_logger.info("UE is switched off\n"); + } + gtpc->send_delete_session_request(emm_ctx->imsi); emm_ctx->state = EMM_STATE_DEREGISTERED; sec_ctx->ul_nas_count++; @@ -939,8 +962,8 @@ bool nas::handle_attach_request(srsran::byte_buffer_t* nas_rx) m_s1ap->send_downlink_nas_transport( m_ecm_ctx.enb_ue_s1ap_id, m_ecm_ctx.mme_ue_s1ap_id, nas_tx.get(), m_ecm_ctx.enb_sri); - m_logger.info("Downlink NAS: Sending Authentication Request"); - srsran::console("Downlink NAS: Sending Authentication Request\n"); + m_logger.info("DL NAS: Sending Authentication Request"); + srsran::console("DL NAS: Sending Authentication Request\n"); return true; } else { m_logger.error("Attach request from known UE"); @@ -948,10 +971,51 @@ bool nas::handle_attach_request(srsran::byte_buffer_t* nas_rx) return true; } +bool nas::handle_pdn_connectivity_request(srsran::byte_buffer_t* nas_rx) +{ + LIBLTE_MME_PDN_CONNECTIVITY_REQUEST_MSG_STRUCT pdn_con_req = {}; + + // Get PDN connectivity request messages + LIBLTE_ERROR_ENUM err = + liblte_mme_unpack_pdn_connectivity_request_msg((LIBLTE_BYTE_MSG_STRUCT*)nas_rx->msg, &pdn_con_req); + if (err != LIBLTE_SUCCESS) { + m_logger.error("Error unpacking NAS PDN Connectivity Request. Error: %s", liblte_error_text[err]); + return false; + } + + // Send PDN connectivity reject + srsran::unique_byte_buffer_t nas_tx = srsran::make_byte_buffer(); + if (nas_tx == nullptr) { + m_logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } + + LIBLTE_MME_PDN_CONNECTIVITY_REJECT_MSG_STRUCT pdn_con_reject = {}; + pdn_con_reject.eps_bearer_id = pdn_con_req.eps_bearer_id; + pdn_con_reject.proc_transaction_id = pdn_con_req.proc_transaction_id; + pdn_con_reject.esm_cause = LIBLTE_MME_ESM_CAUSE_SERVICE_OPTION_NOT_SUPPORTED; + + err = liblte_mme_pack_pdn_connectivity_reject_msg(&pdn_con_reject, (LIBLTE_BYTE_MSG_STRUCT*)nas_tx.get()); + if (err != LIBLTE_SUCCESS) { + m_logger.error("Error packing PDN connectivity reject"); + srsran::console("Error packing PDN connectivity reject\n"); + return false; + } + + // Send reply to eNB + m_s1ap->send_downlink_nas_transport( + m_ecm_ctx.enb_ue_s1ap_id, m_ecm_ctx.mme_ue_s1ap_id, nas_tx.get(), m_ecm_ctx.enb_sri); + + m_logger.info("DL NAS: Sending PDN Connectivity Reject"); + srsran::console("DL NAS: Sending PDN Connectivity Reject\n"); + + return true; +} + bool nas::handle_authentication_response(srsran::byte_buffer_t* nas_rx) { LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_resp = {}; - bool ue_valid = true; + bool ue_valid = true; // Get NAS authentication response LIBLTE_ERROR_ENUM err = liblte_mme_unpack_authentication_response_msg((LIBLTE_BYTE_MSG_STRUCT*)nas_rx, &auth_resp); @@ -1364,9 +1428,13 @@ bool nas::pack_security_mode_command(srsran::byte_buffer_t* nas_buffer) sm_cmd.ue_security_cap.gea_present = m_sec_ctx.ms_network_cap_present; memcpy(sm_cmd.ue_security_cap.gea, m_sec_ctx.ms_network_cap.gea, 8 * sizeof(bool)); - sm_cmd.imeisv_req_present = false; - sm_cmd.nonce_ue_present = false; - sm_cmd.nonce_mme_present = false; + sm_cmd.imeisv_req_present = m_request_imeisv; + if (m_request_imeisv) { + sm_cmd.imeisv_req = LIBLTE_MME_IMEISV_REQUESTED; + } + + sm_cmd.nonce_ue_present = false; + sm_cmd.nonce_mme_present = false; uint8_t sec_hdr_type = 3; LIBLTE_ERROR_ENUM err = liblte_mme_pack_security_mode_command_msg( @@ -1483,7 +1551,7 @@ bool nas::pack_attach_accept(srsran::byte_buffer_t* nas_buffer) attach_accept.lai_present = true; attach_accept.lai.mcc = mcc; attach_accept.lai.mnc = mnc; - attach_accept.lai.lac = 001; + attach_accept.lai.lac = m_lac; attach_accept.ms_id_present = true; attach_accept.ms_id.type_of_id = LIBLTE_MME_MOBILE_ID_TYPE_TMSI; @@ -1522,7 +1590,12 @@ bool nas::pack_attach_accept(srsran::byte_buffer_t* nas_buffer) act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].len = 4; struct sockaddr_in dns_addr; - inet_pton(AF_INET, m_dns.c_str(), &(dns_addr.sin_addr)); + if (inet_pton(AF_INET, m_dns.c_str(), &(dns_addr.sin_addr)) != 1) { + m_logger.error("Invalid m_dns: %s", m_dns.c_str()); + srsran::console("Invalid m_dns: %s\n", m_dns.c_str()); + perror("inet_pton"); + return false; + } memcpy(act_def_eps_bearer_context_req.protocol_cnfg_opts.opt[0].contents, &dns_addr.sin_addr.s_addr, 4); // Make sure all unused options are set to false @@ -1680,12 +1753,14 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) return false; } + uint32_t estimated_count = (m_sec_ctx.ul_nas_count & 0xffffffe0) | (pdu->msg[1] & 0x1f); + switch (m_sec_ctx.integ_algo) { case srsran::INTEGRITY_ALGORITHM_ID_EIA0: break; case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1: srsran::security_128_eia1(&m_sec_ctx.k_nas_int[16], - m_sec_ctx.ul_nas_count, + estimated_count, 0, srsran::SECURITY_DIRECTION_UPLINK, &pdu->msg[0], @@ -1694,7 +1769,7 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) break; case srsran::INTEGRITY_ALGORITHM_ID_128_EIA2: srsran::security_128_eia2(&m_sec_ctx.k_nas_int[16], - m_sec_ctx.ul_nas_count, + estimated_count, 0, srsran::SECURITY_DIRECTION_UPLINK, &pdu->msg[0], @@ -1703,7 +1778,7 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) break; case srsran::INTEGRITY_ALGORITHM_ID_128_EIA3: srsran::security_128_eia3(&m_sec_ctx.k_nas_int[16], - m_sec_ctx.ul_nas_count, + estimated_count, 0, srsran::SECURITY_DIRECTION_UPLINK, &pdu->msg[0], @@ -1713,12 +1788,13 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) default: break; } + // Check if expected mac equals the sent mac for (i = 0; i < 2; i++) { if (exp_mac[i + 2] != mac[i]) { m_logger.warning("Short integrity check failure. Local: count=%d, [%02x %02x %02x %02x], " "Received: count=%d, [%02x %02x]", - m_sec_ctx.ul_nas_count, + estimated_count, exp_mac[0], exp_mac[1], exp_mac[2], @@ -1729,11 +1805,13 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) return false; } } + m_logger.info("Integrity check ok. Local: count=%d, Received: count=%d", m_sec_ctx.ul_nas_count, pdu->msg[1] & 0x1F); + m_sec_ctx.ul_nas_count = estimated_count; return true; } -bool nas::integrity_check(srsran::byte_buffer_t* pdu) +bool nas::integrity_check(srsran::byte_buffer_t* pdu, bool warn_failure) { uint8_t exp_mac[4] = {}; const uint8_t* mac = &pdu->msg[1]; @@ -1776,20 +1854,21 @@ bool nas::integrity_check(srsran::byte_buffer_t* pdu) // Check if expected mac equals the sent mac for (int i = 0; i < 4; i++) { if (exp_mac[i] != mac[i]) { - m_logger.warning("Integrity check failure. Algorithm=EIA%d", (int)m_sec_ctx.integ_algo); - m_logger.warning("UL Local: est_count=%d, old_count=%d, MAC=[%02x %02x %02x %02x], " - "Received: UL count=%d, MAC=[%02x %02x %02x %02x]", - estimated_count, - m_sec_ctx.ul_nas_count, - exp_mac[0], - exp_mac[1], - exp_mac[2], - exp_mac[3], - pdu->msg[5], - mac[0], - mac[1], - mac[2], - mac[3]); + srslog::log_channel& channel = warn_failure ? m_logger.warning : m_logger.info; + channel("Integrity check failure. Algorithm=EIA%d", (int)m_sec_ctx.integ_algo); + channel("UL Local: est_count=%d, old_count=%d, MAC=[%02x %02x %02x %02x], " + "Received: UL count=%d, MAC=[%02x %02x %02x %02x]", + estimated_count, + m_sec_ctx.ul_nas_count, + exp_mac[0], + exp_mac[1], + exp_mac[2], + exp_mac[3], + pdu->msg[5], + mac[0], + mac[1], + mac[2], + mac[3]); return false; } } diff --git a/srsepc/src/mme/s1ap.cc b/srsepc/src/mme/s1ap.cc index 1ed718374..ba1899d2c 100644 --- a/srsepc/src/mme/s1ap.cc +++ b/srsepc/src/mme/s1ap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,8 +23,10 @@ #include "srsran/asn1/gtpc.h" #include "srsran/common/bcd_helpers.h" #include "srsran/common/liblte_security.h" +#include "srsran/common/network_utils.h" #include #include // for printing uint64_t +#include namespace srsepc { @@ -62,7 +64,11 @@ int s1ap::init(const s1ap_args_t& s1ap_args) { m_s1ap_args = s1ap_args; srsran::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &m_plmn); - m_next_m_tmsi = rand(); + + std::random_device rd; + std::mt19937 generator(rd()); + std::uniform_int_distribution distr(0, std::numeric_limits::max()); + m_next_m_tmsi = distr(generator); // Get pointer to the HSS m_hss = hss::get_instance(); @@ -81,8 +87,12 @@ int s1ap::init(const s1ap_args_t& s1ap_args) // Get pointer to GTP-C class m_mme_gtpc = mme_gtpc::get_instance(); + // Initialize S1-MME m_s1mme = enb_listen(); + if (m_s1mme == SRSRAN_ERROR) { + return SRSRAN_ERROR; + } // Init PCAP m_pcap_enable = s1ap_args.pcap_enable; @@ -90,7 +100,7 @@ int s1ap::init(const s1ap_args_t& s1ap_args) m_pcap.open(s1ap_args.pcap_filename.c_str()); } m_logger.info("S1AP Initialized"); - return 0; + return SRSRAN_SUCCESS; } void s1ap::stop() @@ -147,7 +157,7 @@ int s1ap::enb_listen() sock_fd = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP); if (sock_fd == -1) { srsran::console("Could not create SCTP socket\n"); - return -1; + return SRSRAN_ERROR; } // Sets the data_io_event to be able to use sendrecv_info @@ -158,20 +168,23 @@ int s1ap::enb_listen() if (setsockopt(sock_fd, IPPROTO_SCTP, SCTP_EVENTS, &evnts, sizeof(evnts))) { close(sock_fd); srsran::console("Subscribing to sctp_data_io_events failed\n"); - return -1; + return SRSRAN_ERROR; } // S1-MME bind bzero(&s1mme_addr, sizeof(s1mme_addr)); - s1mme_addr.sin_family = AF_INET; - inet_pton(AF_INET, m_s1ap_args.mme_bind_addr.c_str(), &(s1mme_addr.sin_addr)); - s1mme_addr.sin_port = htons(S1MME_PORT); - err = bind(sock_fd, (struct sockaddr*)&s1mme_addr, sizeof(s1mme_addr)); - if (err != 0) { + if (not srsran::net_utils::set_sockaddr(&s1mme_addr, m_s1ap_args.mme_bind_addr.c_str(), S1MME_PORT)) { + close(sock_fd); + m_logger.error("Invalid mme_bind_addr: %s", m_s1ap_args.mme_bind_addr.c_str()); + srsran::console("Invalid mme_bind_addr: %s\n", m_s1ap_args.mme_bind_addr.c_str()); + return SRSRAN_ERROR; + } + + if (not srsran::net_utils::bind_addr(sock_fd, s1mme_addr)) { close(sock_fd); m_logger.error("Error binding SCTP socket"); srsran::console("Error binding SCTP socket\n"); - return -1; + return SRSRAN_ERROR; } // Listen for connections @@ -180,7 +193,7 @@ int s1ap::enb_listen() close(sock_fd); m_logger.error("Error in SCTP socket listen"); srsran::console("Error in SCTP socket listen\n"); - return -1; + return SRSRAN_ERROR; } return sock_fd; @@ -560,7 +573,7 @@ uint64_t s1ap::find_imsi_from_m_tmsi(uint32_t m_tmsi) return it->second; } else { m_logger.debug("Could not find IMSI from M-TMSI 0x%x", m_tmsi); - return 0; + return SRSRAN_SUCCESS; } } diff --git a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc index b36940408..6e35237cb 100644 --- a/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_ctx_mngmt_proc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,7 +25,6 @@ #include "srsran/common/buffer_pool.h" #include "srsran/common/int_helpers.h" #include "srsran/common/liblte_security.h" -#include namespace srsepc { @@ -83,24 +82,23 @@ bool s1ap_ctx_mngmt_proc::send_initial_context_setup_request(nas* nas_ctx, uint1 s1ap_pdu_t tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_INIT_CONTEXT_SETUP); - asn1::s1ap::init_context_setup_request_ies_container& in_ctx_req = - tx_pdu.init_msg().value.init_context_setup_request().protocol_ies; + asn1::s1ap::init_context_setup_request_s& in_ctx_req = tx_pdu.init_msg().value.init_context_setup_request(); // Add MME and eNB S1AP Ids - in_ctx_req.mme_ue_s1ap_id.value = ecm_ctx->mme_ue_s1ap_id; - in_ctx_req.enb_ue_s1ap_id.value = ecm_ctx->enb_ue_s1ap_id; + in_ctx_req->mme_ue_s1ap_id.value = ecm_ctx->mme_ue_s1ap_id; + in_ctx_req->enb_ue_s1ap_id.value = ecm_ctx->enb_ue_s1ap_id; // UE-AMBR - in_ctx_req.ueaggregate_maximum_bitrate.value.ueaggregate_maximum_bit_rate_dl = 1000000000; - in_ctx_req.ueaggregate_maximum_bitrate.value.ueaggregate_maximum_bit_rate_ul = 1000000000; + in_ctx_req->ueaggregate_maximum_bitrate.value.ueaggregate_maximum_bit_rate_dl = 1000000000; + in_ctx_req->ueaggregate_maximum_bitrate.value.ueaggregate_maximum_bit_rate_ul = 1000000000; // Number of E-RABs to be setup - in_ctx_req.erab_to_be_setup_list_ctxt_su_req.value.resize(1); - in_ctx_req.erab_to_be_setup_list_ctxt_su_req.value[0].load_info_obj(ASN1_S1AP_ID_ERAB_TO_BE_SETUP_ITEM_CTXT_SU_REQ); + in_ctx_req->erab_to_be_setup_list_ctxt_su_req.value.resize(1); + in_ctx_req->erab_to_be_setup_list_ctxt_su_req.value[0].load_info_obj(ASN1_S1AP_ID_ERAB_TO_BE_SETUP_ITEM_CTXT_SU_REQ); // Setup eRAB context asn1::s1ap::erab_to_be_setup_item_ctxt_su_req_s& erab_ctx_req = - in_ctx_req.erab_to_be_setup_list_ctxt_su_req.value[0].value.erab_to_be_setup_item_ctxt_su_req(); + in_ctx_req->erab_to_be_setup_list_ctxt_su_req.value[0]->erab_to_be_setup_item_ctxt_su_req(); erab_ctx_req.erab_id = esm_ctx->erab_id; // Setup E-RAB QoS parameters @@ -121,21 +119,21 @@ bool s1ap_ctx_mngmt_proc::send_initial_context_setup_request(nas* nas_ctx, uint1 // Set UE security capabilities and k_enb for (int i = 0; i < 3; i++) { if (sec_ctx->ue_network_cap.eea[i + 1] == true) { - in_ctx_req.ue_security_cap.value.encryption_algorithms.set(16 - i, true); // EEA supported + in_ctx_req->ue_security_cap.value.encryption_algorithms.set(16 - i, true); // EEA supported } else { - in_ctx_req.ue_security_cap.value.encryption_algorithms.set(16 - i, false); // EEA not supported + in_ctx_req->ue_security_cap.value.encryption_algorithms.set(16 - i, false); // EEA not supported } if (sec_ctx->ue_network_cap.eia[i + 1] == true) { - in_ctx_req.ue_security_cap.value.integrity_protection_algorithms.set(16 - i, true); // EIA supported + in_ctx_req->ue_security_cap.value.integrity_protection_algorithms.set(16 - i, true); // EIA supported } else { - in_ctx_req.ue_security_cap.value.integrity_protection_algorithms.set(16 - i, false); // EIA not supported + in_ctx_req->ue_security_cap.value.integrity_protection_algorithms.set(16 - i, false); // EIA not supported } } // Get K eNB - // memcpy(in_ctx_req.security_key.value.data(),sec_ctx->k_enb, 32); + // memcpy(in_ctx_req->security_key.value.data(),sec_ctx->k_enb, 32); for (uint8_t i = 0; i < 32; ++i) { - in_ctx_req.security_key.value.data()[31 - i] = sec_ctx->k_enb[i]; + in_ctx_req->security_key.value.data()[31 - i] = sec_ctx->k_enb[i]; } m_logger.info(sec_ctx->k_enb, 32, "Initial Context Setup Request -- Key eNB (k_enb)"); @@ -166,8 +164,8 @@ bool s1ap_ctx_mngmt_proc::send_initial_context_setup_request(nas* nas_ctx, uint1 m_logger.info( "Initial Context -- S1-U TEID 0x%" PRIx64 ". IP %s ", erab_ctx_req.gtp_teid.to_number(), inet_ntoa(addr)); m_logger.info("Initial Context Setup Request -- eNB UE S1AP Id %d, MME UE S1AP Id %" PRIu64 "", - in_ctx_req.enb_ue_s1ap_id.value.value, - in_ctx_req.mme_ue_s1ap_id.value.value); + in_ctx_req->enb_ue_s1ap_id.value.value, + in_ctx_req->mme_ue_s1ap_id.value.value); m_logger.info("Initial Context Setup Request -- E-RAB id %d", erab_ctx_req.erab_id); m_logger.info("Initial Context Setup Request -- S1-U TEID 0x%" PRIu64 ". IP %s ", erab_ctx_req.gtp_teid.to_number(), @@ -182,7 +180,7 @@ bool s1ap_ctx_mngmt_proc::send_initial_context_setup_request(nas* nas_ctx, uint1 bool s1ap_ctx_mngmt_proc::handle_initial_context_setup_response( const asn1::s1ap::init_context_setup_resp_s& in_ctxt_resp) { - uint32_t mme_ue_s1ap_id = in_ctxt_resp.protocol_ies.mme_ue_s1ap_id.value.value; + uint32_t mme_ue_s1ap_id = in_ctxt_resp->mme_ue_s1ap_id.value.value; nas* nas_ctx = m_s1ap->find_nas_ctx_from_mme_ue_s1ap_id(mme_ue_s1ap_id); if (nas_ctx == nullptr) { m_logger.error("Could not find UE's context in active UE's map"); @@ -195,12 +193,11 @@ bool s1ap_ctx_mngmt_proc::handle_initial_context_setup_response( srsran::console("Received Initial Context Setup Response\n"); // Setup E-RABs - for (const asn1::s1ap::protocol_ie_single_container_s& ie_container : - in_ctxt_resp.protocol_ies.erab_setup_list_ctxt_su_res.value) { + for (const asn1::protocol_ie_single_container_s& ie_container : + in_ctxt_resp->erab_setup_list_ctxt_su_res.value) { // Get E-RAB setup context item and E-RAB Id - const asn1::s1ap::erab_setup_item_ctxt_su_res_s& erab_setup_item_ctxt = - ie_container.value.erab_setup_item_ctxt_su_res(); - uint8_t erab_id = erab_setup_item_ctxt.erab_id; + const asn1::s1ap::erab_setup_item_ctxt_su_res_s& erab_setup_item_ctxt = ie_container->erab_setup_item_ctxt_su_res(); + uint8_t erab_id = erab_setup_item_ctxt.erab_id; // Make sure we requested the context setup esm_ctx_t* esm_ctx = &nas_ctx->m_esm_ctx[erab_id]; @@ -241,7 +238,7 @@ bool s1ap_ctx_mngmt_proc::handle_initial_context_setup_response( bool s1ap_ctx_mngmt_proc::handle_ue_context_release_request(const asn1::s1ap::ue_context_release_request_s& ue_rel, struct sctp_sndrcvinfo* enb_sri) { - uint32_t mme_ue_s1ap_id = ue_rel.protocol_ies.mme_ue_s1ap_id.value.value; + uint32_t mme_ue_s1ap_id = ue_rel->mme_ue_s1ap_id.value.value; m_logger.info("Received UE Context Release Request. MME-UE S1AP Id: %d", mme_ue_s1ap_id); srsran::console("Received UE Context Release Request. MME-UE S1AP Id %d\n", mme_ue_s1ap_id); @@ -313,14 +310,13 @@ bool s1ap_ctx_mngmt_proc::send_ue_context_release_command(nas* nas_ctx) s1ap_pdu_t tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_UE_CONTEXT_RELEASE); - asn1::s1ap::ue_context_release_cmd_ies_container& ctx_rel_cmd = - tx_pdu.init_msg().value.ue_context_release_cmd().protocol_ies; - ctx_rel_cmd.ue_s1ap_ids.value.set(asn1::s1ap::ue_s1ap_ids_c::types_opts::ue_s1ap_id_pair); - ctx_rel_cmd.ue_s1ap_ids.value.ue_s1ap_id_pair().mme_ue_s1ap_id = nas_ctx->m_ecm_ctx.mme_ue_s1ap_id; - ctx_rel_cmd.ue_s1ap_ids.value.ue_s1ap_id_pair().enb_ue_s1ap_id = nas_ctx->m_ecm_ctx.enb_ue_s1ap_id; + asn1::s1ap::ue_context_release_cmd_s& ctx_rel_cmd = tx_pdu.init_msg().value.ue_context_release_cmd(); + ctx_rel_cmd->ue_s1ap_ids.value.set(asn1::s1ap::ue_s1ap_ids_c::types_opts::ue_s1ap_id_pair); + ctx_rel_cmd->ue_s1ap_ids.value.ue_s1ap_id_pair().mme_ue_s1ap_id = nas_ctx->m_ecm_ctx.mme_ue_s1ap_id; + ctx_rel_cmd->ue_s1ap_ids.value.ue_s1ap_id_pair().enb_ue_s1ap_id = nas_ctx->m_ecm_ctx.enb_ue_s1ap_id; - ctx_rel_cmd.cause.value.set(asn1::s1ap::cause_c::types_opts::nas); - ctx_rel_cmd.cause.value.nas().value = asn1::s1ap::cause_nas_opts::options::normal_release; + ctx_rel_cmd->cause.value.set(asn1::s1ap::cause_c::types_opts::nas); + ctx_rel_cmd->cause.value.nas().value = asn1::s1ap::cause_nas_opts::options::normal_release; // Send Reply to eNB if (!m_s1ap->s1ap_tx_pdu(tx_pdu, &nas_ctx->m_ecm_ctx.enb_sri)) { @@ -333,7 +329,7 @@ bool s1ap_ctx_mngmt_proc::send_ue_context_release_command(nas* nas_ctx) bool s1ap_ctx_mngmt_proc::handle_ue_context_release_complete(const asn1::s1ap::ue_context_release_complete_s& rel_comp) { - uint32_t mme_ue_s1ap_id = rel_comp.protocol_ies.mme_ue_s1ap_id.value.value; + uint32_t mme_ue_s1ap_id = rel_comp->mme_ue_s1ap_id.value.value; m_logger.info("Received UE Context Release Complete. MME-UE S1AP Id: %d", mme_ue_s1ap_id); srsran::console("Received UE Context Release Complete. MME-UE S1AP Id %d\n", mme_ue_s1ap_id); diff --git a/srsepc/src/mme/s1ap_erab_mngmt_proc.cc b/srsepc/src/mme/s1ap_erab_mngmt_proc.cc index 7d678bcbf..4403c5739 100644 --- a/srsepc/src/mme/s1ap_erab_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_erab_mngmt_proc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -79,19 +79,19 @@ bool s1ap_erab_mngmt_proc::send_erab_release_command(uint32_t enb_ s1ap_pdu_t tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ERAB_RELEASE); - asn1::s1ap::erab_release_cmd_ies_container& erab_rel_cmd = tx_pdu.init_msg().value.erab_release_cmd().protocol_ies; + asn1::s1ap::erab_release_cmd_s& erab_rel_cmd = tx_pdu.init_msg().value.erab_release_cmd(); // Add MME and eNB S1AP Ids - erab_rel_cmd.mme_ue_s1ap_id.value = mme_ue_s1ap_id; - erab_rel_cmd.enb_ue_s1ap_id.value = enb_ue_s1ap_id; + erab_rel_cmd->mme_ue_s1ap_id.value = mme_ue_s1ap_id; + erab_rel_cmd->enb_ue_s1ap_id.value = enb_ue_s1ap_id; // Number of E-RABs to be setup - erab_rel_cmd.erab_to_be_released_list.value.resize(erabs_to_release.size()); - for (uint32_t i = 0; i < erab_rel_cmd.erab_to_be_released_list.value.size(); i++) { - erab_rel_cmd.erab_to_be_released_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM); - erab_rel_cmd.erab_to_be_released_list.value[i].value.erab_item().erab_id = erabs_to_release[i]; - erab_rel_cmd.erab_to_be_released_list.value[i].value.erab_item().cause.set(asn1::s1ap::cause_c::types::misc); - erab_rel_cmd.erab_to_be_released_list.value[i].value.erab_item().cause.misc() = + erab_rel_cmd->erab_to_be_released_list.value.resize(erabs_to_release.size()); + for (uint32_t i = 0; i < erab_rel_cmd->erab_to_be_released_list.value.size(); i++) { + erab_rel_cmd->erab_to_be_released_list.value[i].load_info_obj(ASN1_S1AP_ID_ERAB_ITEM); + erab_rel_cmd->erab_to_be_released_list.value[i]->erab_item().erab_id = erabs_to_release[i]; + erab_rel_cmd->erab_to_be_released_list.value[i]->erab_item().cause.set(asn1::s1ap::cause_c::types::misc); + erab_rel_cmd->erab_to_be_released_list.value[i]->erab_item().cause.misc() = asn1::s1ap::cause_misc_opts::unspecified; m_logger.info("Sending release comman to %d", erabs_to_release[i]); } @@ -115,21 +115,20 @@ bool s1ap_erab_mngmt_proc::send_erab_modify_request(uint32_t s1ap_pdu_t tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_ERAB_MODIFY); - asn1::s1ap::erab_modify_request_ies_container& erab_mod_req = - tx_pdu.init_msg().value.erab_modify_request().protocol_ies; + asn1::s1ap::erab_modify_request_s& erab_mod_req = tx_pdu.init_msg().value.erab_modify_request(); // Add MME and eNB S1AP Ids - erab_mod_req.enb_ue_s1ap_id.value = enb_ue_s1ap_id; - erab_mod_req.mme_ue_s1ap_id.value = mme_ue_s1ap_id; + erab_mod_req->enb_ue_s1ap_id.value = enb_ue_s1ap_id; + erab_mod_req->mme_ue_s1ap_id.value = mme_ue_s1ap_id; // Number of E-RABs to be setup - erab_mod_req.erab_to_be_modified_list_bearer_mod_req.value.resize(erabs_to_modify.size()); + erab_mod_req->erab_to_be_modified_list_bearer_mod_req.value.resize(erabs_to_modify.size()); uint32_t i = 0; for (auto erab_it = erabs_to_modify.begin(); erab_it != erabs_to_modify.end(); erab_it++) { - erab_mod_req.erab_to_be_modified_list_bearer_mod_req.value[i].load_info_obj( + erab_mod_req->erab_to_be_modified_list_bearer_mod_req.value[i].load_info_obj( ASN1_S1AP_ID_ERAB_TO_BE_MODIFIED_ITEM_BEARER_MOD_REQ); asn1::s1ap::erab_to_be_modified_item_bearer_mod_req_s& erab_to_mod = - erab_mod_req.erab_to_be_modified_list_bearer_mod_req.value[i].value.erab_to_be_modified_item_bearer_mod_req(); + erab_mod_req->erab_to_be_modified_list_bearer_mod_req.value[i]->erab_to_be_modified_item_bearer_mod_req(); erab_to_mod.erab_id = erab_it->first; erab_to_mod.erab_level_qos_params.qci = erab_it->second; erab_to_mod.erab_level_qos_params.alloc_retention_prio.prio_level = 15; // lowest diff --git a/srsepc/src/mme/s1ap_mngmt_proc.cc b/srsepc/src/mme/s1ap_mngmt_proc.cc index 3db567553..588a0a4a4 100644 --- a/srsepc/src/mme/s1ap_mngmt_proc.cc +++ b/srsepc/src/mme/s1ap_mngmt_proc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -126,36 +126,35 @@ bool s1ap_mngmt_proc::handle_s1_setup_request(const asn1::s1ap::s1_setup_request */ bool s1ap_mngmt_proc::unpack_s1_setup_request(const asn1::s1ap::s1_setup_request_s& msg, enb_ctx_t* enb_ctx) { - uint8_t enb_id_bits[32]; uint32_t plmn = 0; uint16_t tac, bplmn; uint32_t tmp32 = 0; - const asn1::s1ap::s1_setup_request_ies_container& s1_req = msg.protocol_ies; + const asn1::s1ap::s1_setup_request_s& s1_req = msg; // eNB Name - enb_ctx->enb_name_present = s1_req.enbname_present; - if (s1_req.enbname_present) { - enb_ctx->enb_name = s1_req.enbname.value.to_string(); + enb_ctx->enb_name_present = s1_req->enbname_present; + if (s1_req->enbname_present) { + enb_ctx->enb_name = s1_req->enbname.value.to_string(); } // eNB Id - enb_ctx->enb_id = s1_req.global_enb_id.value.enb_id.macro_enb_id().to_number(); + enb_ctx->enb_id = s1_req->global_enb_id.value.enb_id.macro_enb_id().to_number(); // PLMN Id - ((uint8_t*)&plmn)[1] = s1_req.global_enb_id.value.plm_nid[0]; - ((uint8_t*)&plmn)[2] = s1_req.global_enb_id.value.plm_nid[1]; - ((uint8_t*)&plmn)[3] = s1_req.global_enb_id.value.plm_nid[2]; + ((uint8_t*)&plmn)[1] = s1_req->global_enb_id.value.plm_nid[0]; + ((uint8_t*)&plmn)[2] = s1_req->global_enb_id.value.plm_nid[1]; + ((uint8_t*)&plmn)[3] = s1_req->global_enb_id.value.plm_nid[2]; enb_ctx->plmn = ntohl(plmn); srsran::s1ap_plmn_to_mccmnc(enb_ctx->plmn, &enb_ctx->mcc, &enb_ctx->mnc); // SupportedTAs - enb_ctx->nof_supported_ta = s1_req.supported_tas.value.size(); + enb_ctx->nof_supported_ta = s1_req->supported_tas.value.size(); for (uint16_t i = 0; i < enb_ctx->nof_supported_ta; i++) { - const asn1::s1ap::supported_tas_item_s& tas = s1_req.supported_tas.value[i]; + const asn1::s1ap::supported_tas_item_s& tas = s1_req->supported_tas.value[i]; // TAC ((uint8_t*)&enb_ctx->tacs[i])[0] = tas.tac[0]; ((uint8_t*)&enb_ctx->tacs[i])[1] = tas.tac[1]; @@ -172,7 +171,7 @@ bool s1ap_mngmt_proc::unpack_s1_setup_request(const asn1::s1ap::s1_setup_request } // Default Paging DRX - enb_ctx->drx.value = s1_req.default_paging_drx.value; + enb_ctx->drx.value = s1_req->default_paging_drx.value; return true; } @@ -182,10 +181,10 @@ bool s1ap_mngmt_proc::send_s1_setup_failure(asn1::s1ap::cause_misc_opts::options s1ap_pdu_t tx_pdu; tx_pdu.set_unsuccessful_outcome().load_info_obj(ASN1_S1AP_ID_S1_SETUP); - asn1::s1ap::s1_setup_fail_ies_container& s1_fail = tx_pdu.unsuccessful_outcome().value.s1_setup_fail().protocol_ies; + asn1::s1ap::s1_setup_fail_s& s1_fail = tx_pdu.unsuccessful_outcome().value.s1_setup_fail(); - s1_fail.cause.value.set(asn1::s1ap::cause_c::types_opts::misc); - s1_fail.cause.value.misc().value = cause; + s1_fail->cause.value.set(asn1::s1ap::cause_c::types_opts::misc); + s1_fail->cause.value.misc().value = cause; m_s1ap->s1ap_tx_pdu(tx_pdu, enb_sri); return true; @@ -198,20 +197,20 @@ bool s1ap_mngmt_proc::send_s1_setup_response(const s1ap_args_t& s1ap_args, struc s1ap_pdu_t tx_pdu; tx_pdu.set_successful_outcome().load_info_obj(ASN1_S1AP_ID_S1_SETUP); - asn1::s1ap::s1_setup_resp_ies_container& s1_resp = tx_pdu.successful_outcome().value.s1_setup_resp().protocol_ies; + asn1::s1ap::s1_setup_resp_s& s1_resp = tx_pdu.successful_outcome().value.s1_setup_resp(); // MME Name - s1_resp.mm_ename_present = true; - s1_resp.mm_ename.value.from_string(s1ap_args.mme_name); + s1_resp->mm_ename_present = true; + s1_resp->mm_ename.value.from_string(s1ap_args.mme_name); // Served GUMEIs - s1_resp.served_gummeis.value.resize(1); // TODO Only one served GUMMEI supported + s1_resp->served_gummeis.value.resize(1); // TODO Only one served GUMMEI supported uint32_t plmn = 0; srsran::s1ap_mccmnc_to_plmn(s1ap_args.mcc, s1ap_args.mnc, &plmn); plmn = htonl(plmn); - asn1::s1ap::served_gummeis_item_s& serv_gummei = s1_resp.served_gummeis.value[0]; + asn1::s1ap::served_gummeis_item_s& serv_gummei = s1_resp->served_gummeis.value[0]; serv_gummei.served_plmns.resize(1); serv_gummei.served_plmns[0][0] = ((uint8_t*)&plmn)[1]; @@ -224,7 +223,7 @@ bool s1ap_mngmt_proc::send_s1_setup_response(const s1ap_args_t& s1ap_args, struc serv_gummei.served_mmecs.resize(1); // Only one MMEC served serv_gummei.served_mmecs[0].from_number(s1ap_args.mme_code); - s1_resp.relative_mme_capacity.value = 255; + s1_resp->relative_mme_capacity.value = 255; if (!m_s1ap->s1ap_tx_pdu(tx_pdu, enb_sri)) { m_logger.error("Error sending S1 Setup Response."); diff --git a/srsepc/src/mme/s1ap_nas_transport.cc b/srsepc/src/mme/s1ap_nas_transport.cc index 453b5a6e3..63e1824f1 100644 --- a/srsepc/src/mme/s1ap_nas_transport.cc +++ b/srsepc/src/mme/s1ap_nas_transport.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -68,18 +68,20 @@ void s1ap_nas_transport::init() m_s1ap = s1ap::get_instance(); // Init NAS args - m_nas_init.mcc = m_s1ap->m_s1ap_args.mcc; - m_nas_init.mnc = m_s1ap->m_s1ap_args.mnc; - m_nas_init.mme_code = m_s1ap->m_s1ap_args.mme_code; - m_nas_init.mme_group = m_s1ap->m_s1ap_args.mme_group; - m_nas_init.tac = m_s1ap->m_s1ap_args.tac; - m_nas_init.apn = m_s1ap->m_s1ap_args.mme_apn; - m_nas_init.dns = m_s1ap->m_s1ap_args.dns_addr; + m_nas_init.mcc = m_s1ap->m_s1ap_args.mcc; + m_nas_init.mnc = m_s1ap->m_s1ap_args.mnc; + m_nas_init.mme_code = m_s1ap->m_s1ap_args.mme_code; + m_nas_init.mme_group = m_s1ap->m_s1ap_args.mme_group; + m_nas_init.tac = m_s1ap->m_s1ap_args.tac; + m_nas_init.apn = m_s1ap->m_s1ap_args.mme_apn; + m_nas_init.dns = m_s1ap->m_s1ap_args.dns_addr; m_nas_init.full_net_name = m_s1ap->m_s1ap_args.full_net_name; m_nas_init.short_net_name = m_s1ap->m_s1ap_args.short_net_name; - m_nas_init.paging_timer = m_s1ap->m_s1ap_args.paging_timer; - m_nas_init.integ_algo = m_s1ap->m_s1ap_args.integrity_algo; - m_nas_init.cipher_algo = m_s1ap->m_s1ap_args.encryption_algo; + m_nas_init.paging_timer = m_s1ap->m_s1ap_args.paging_timer; + m_nas_init.integ_algo = m_s1ap->m_s1ap_args.integrity_algo; + m_nas_init.cipher_algo = m_s1ap->m_s1ap_args.encryption_algo; + m_nas_init.request_imeisv = m_s1ap->m_s1ap_args.request_imeisv; + m_nas_init.lac = m_s1ap->m_s1ap_args.lac; // Init NAS interface m_nas_if.s1ap = s1ap::get_instance(); @@ -94,19 +96,19 @@ bool s1ap_nas_transport::handle_initial_ue_message(const asn1::s1ap::init_ue_msg bool err, mac_valid; uint8_t pd, msg_type, sec_hdr_type; srsran::unique_byte_buffer_t nas_msg = srsran::make_byte_buffer(); - memcpy(nas_msg->msg, init_ue.protocol_ies.nas_pdu.value.data(), init_ue.protocol_ies.nas_pdu.value.size()); - nas_msg->N_bytes = init_ue.protocol_ies.nas_pdu.value.size(); + memcpy(nas_msg->msg, init_ue->nas_pdu.value.data(), init_ue->nas_pdu.value.size()); + nas_msg->N_bytes = init_ue->nas_pdu.value.size(); uint64_t imsi = 0; uint32_t m_tmsi = 0; - uint32_t enb_ue_s1ap_id = init_ue.protocol_ies.enb_ue_s1ap_id.value.value; + uint32_t enb_ue_s1ap_id = init_ue->enb_ue_s1ap_id.value.value; liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT*)nas_msg.get(), &pd, &msg_type); srsran::console("Initial UE message: %s\n", liblte_nas_msg_type_to_string(msg_type)); m_logger.info("Initial UE message: %s", liblte_nas_msg_type_to_string(msg_type)); - if (init_ue.protocol_ies.s_tmsi_present) { - srsran::uint8_to_uint32(init_ue.protocol_ies.s_tmsi.value.m_tmsi.data(), &m_tmsi); + if (init_ue->s_tmsi_present) { + srsran::uint8_to_uint32(init_ue->s_tmsi.value.m_tmsi.data(), &m_tmsi); } switch (msg_type) { @@ -143,8 +145,8 @@ bool s1ap_nas_transport::handle_uplink_nas_transport(const asn1::s1ap::ul_nas_tr struct sctp_sndrcvinfo* enb_sri) { uint8_t pd, msg_type, sec_hdr_type; - uint32_t enb_ue_s1ap_id = ul_xport.protocol_ies.enb_ue_s1ap_id.value.value; - uint32_t mme_ue_s1ap_id = ul_xport.protocol_ies.mme_ue_s1ap_id.value.value; + uint32_t enb_ue_s1ap_id = ul_xport->enb_ue_s1ap_id.value.value; + uint32_t mme_ue_s1ap_id = ul_xport->mme_ue_s1ap_id.value.value; bool mac_valid = false; bool increase_ul_nas_cnt = true; @@ -162,8 +164,8 @@ bool s1ap_nas_transport::handle_uplink_nas_transport(const asn1::s1ap::ul_nas_tr // Parse NAS message header srsran::unique_byte_buffer_t nas_msg = srsran::make_byte_buffer(); - memcpy(nas_msg->msg, ul_xport.protocol_ies.nas_pdu.value.data(), ul_xport.protocol_ies.nas_pdu.value.size()); - nas_msg->N_bytes = ul_xport.protocol_ies.nas_pdu.value.size(); + memcpy(nas_msg->msg, ul_xport->nas_pdu.value.data(), ul_xport->nas_pdu.value.size()); + nas_msg->N_bytes = ul_xport->nas_pdu.value.size(); bool msg_encrypted = false; // Parse the message security header @@ -178,17 +180,28 @@ bool s1ap_nas_transport::handle_uplink_nas_transport(const asn1::s1ap::ul_nas_tr m_logger.error("Unhandled security header type in Uplink NAS Transport: %d", sec_hdr_type); return false; } - // Todo: Check on count mismatch of uplink count and do resync nas counter... + + // Some messages may have invalid MAC. Check wether we need to warn about MAC failures. + bool warn_integrity_fail = true; + if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY || + sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT) { + // Avoid unecessary warnings for identity response and authentication response. + liblte_mme_parse_msg_header((LIBLTE_BYTE_MSG_STRUCT*)nas_msg.get(), &pd, &msg_type); + if (msg_type == LIBLTE_MME_MSG_TYPE_IDENTITY_RESPONSE || msg_type == LIBLTE_MME_MSG_TYPE_AUTHENTICATION_RESPONSE) { + warn_integrity_fail = false; + } + } // Check MAC if message is integrity protected if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY || - sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED || sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT || + sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED || sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT) { - mac_valid = nas_ctx->integrity_check(nas_msg.get()); - if (mac_valid == false) { - m_logger.warning("Invalid MAC message. Even if security header indicates integrity protection (Maybe: " - "Identity Response or Authentication Response)"); + mac_valid = nas_ctx->integrity_check(nas_msg.get(), warn_integrity_fail); + if (not mac_valid) { + srslog::log_channel& channel = warn_integrity_fail ? m_logger.warning : m_logger.info; + channel("Invalid MAC message. Even if security header indicates integrity protection (Maybe: " + "Identity Response or Authentication Response)"); } } @@ -316,6 +329,11 @@ bool s1ap_nas_transport::handle_uplink_nas_transport(const asn1::s1ap::ul_nas_tr srsran::console("UL NAS: Tracking Area Update Request\n"); nas_ctx->handle_tracking_area_update_request(nas_msg.get()); break; + case LIBLTE_MME_MSG_TYPE_PDN_CONNECTIVITY_REQUEST: + m_logger.info("UL NAS: PDN Connectivity Request"); + srsran::console("UL NAS: PDN Connectivity Request\n"); + nas_ctx->handle_pdn_connectivity_request(nas_msg.get()); + break; default: m_logger.warning("Unhandled NAS integrity protected message %s", liblte_nas_msg_type_to_string(msg_type)); srsran::console("Unhandled NAS integrity protected message %s\n", liblte_nas_msg_type_to_string(msg_type)); @@ -345,13 +363,13 @@ bool s1ap_nas_transport::send_downlink_nas_transport(uint32_t enb_ tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_DL_NAS_TRANSPORT); // Setup Dw NAS structure - asn1::s1ap::dl_nas_transport_ies_container& dw_nas = tx_pdu.init_msg().value.dl_nas_transport().protocol_ies; - dw_nas.enb_ue_s1ap_id.value = enb_ue_s1ap_id; - dw_nas.mme_ue_s1ap_id.value = mme_ue_s1ap_id; + asn1::s1ap::dl_nas_transport_s& dw_nas = tx_pdu.init_msg().value.dl_nas_transport(); + dw_nas->enb_ue_s1ap_id.value = enb_ue_s1ap_id; + dw_nas->mme_ue_s1ap_id.value = mme_ue_s1ap_id; // Copy NAS PDU to Downlink NAS Trasport message buffer - dw_nas.nas_pdu.value.resize(nas_msg->N_bytes); - memcpy(dw_nas.nas_pdu.value.data(), nas_msg->msg, nas_msg->N_bytes); + dw_nas->nas_pdu.value.resize(nas_msg->N_bytes); + memcpy(dw_nas->nas_pdu.value.data(), nas_msg->msg, nas_msg->N_bytes); // Send Downlink NAS Transport Message m_s1ap->s1ap_tx_pdu(tx_pdu, &enb_sri); diff --git a/srsepc/src/mme/s1ap_paging.cc b/srsepc/src/mme/s1ap_paging.cc index bdf4728ea..bfa1b7e15 100644 --- a/srsepc/src/mme/s1ap_paging.cc +++ b/srsepc/src/mme/s1ap_paging.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -47,7 +47,7 @@ bool s1ap_paging::send_paging(uint64_t imsi, uint16_t erab_to_setup) // Prepare reply PDU s1ap_pdu_t tx_pdu; tx_pdu.set_init_msg().load_info_obj(ASN1_S1AP_ID_PAGING); - asn1::s1ap::paging_ies_container& paging = tx_pdu.init_msg().value.paging().protocol_ies; + asn1::s1ap::paging_s& paging = tx_pdu.init_msg().value.paging(); // Getting UE NAS Context nas* nas_ctx = m_s1ap->find_nas_ctx_from_imsi(imsi); @@ -58,25 +58,25 @@ bool s1ap_paging::send_paging(uint64_t imsi, uint16_t erab_to_setup) // UE Identity Index uint16_t ue_index = imsi % 1024; - paging.ue_id_idx_value.value.from_number(ue_index); + paging->ue_id_idx_value.value.from_number(ue_index); // UE Paging Id - paging.ue_paging_id.value.set_s_tmsi(); - paging.ue_paging_id.value.s_tmsi().mmec.from_number(m_s1ap->m_s1ap_args.mme_code); - paging.ue_paging_id.value.s_tmsi().m_tmsi.from_number(nas_ctx->m_sec_ctx.guti.m_tmsi); + paging->ue_paging_id.value.set_s_tmsi(); + paging->ue_paging_id.value.s_tmsi().mmec.from_number(m_s1ap->m_s1ap_args.mme_code); + paging->ue_paging_id.value.s_tmsi().m_tmsi.from_number(nas_ctx->m_sec_ctx.guti.m_tmsi); // CMDomain - paging.cn_domain.value = asn1::s1ap::cn_domain_opts::ps; + paging->cn_domain.value = asn1::s1ap::cn_domain_opts::ps; // TAI List - paging.tai_list.value.resize(1); - paging.tai_list.value[0].load_info_obj(ASN1_S1AP_ID_TAI_ITEM); + paging->tai_list.value.resize(1); + paging->tai_list.value[0].load_info_obj(ASN1_S1AP_ID_TAI_ITEM); uint32_t plmn = m_s1ap->get_plmn(); - paging.tai_list.value[0].value.tai_item().tai.plm_nid.from_number(plmn); + paging->tai_list.value[0]->tai_item().tai.plm_nid.from_number(plmn); uint16_t tac = m_s1ap->m_s1ap_args.tac; - paging.tai_list.value[0].value.tai_item().tai.tac.from_number(tac); + paging->tai_list.value[0]->tai_item().tai.tac.from_number(tac); // Start T3413 if (!nas_ctx->start_timer(T_3413)) { diff --git a/srsepc/src/spgw/CMakeLists.txt b/srsepc/src/spgw/CMakeLists.txt index df4a51dbb..747b3ad6e 100644 --- a/srsepc/src/spgw/CMakeLists.txt +++ b/srsepc/src/spgw/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsepc/src/spgw/gtpc.cc b/srsepc/src/spgw/gtpc.cc index 809b1ddaf..0c041cf37 100644 --- a/srsepc/src/spgw/gtpc.cc +++ b/srsepc/src/spgw/gtpc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -524,7 +524,7 @@ pkt_discard: bool spgw::gtpc::free_all_queued_packets(spgw_tunnel_ctx_t* tunnel_ctx) { if (!tunnel_ctx->paging_pending) { - m_logger.warning("Freeing queue with paging not pending."); + m_logger.info("Trying to free queued packets, but paging is not pending."); } while (!tunnel_ctx->paging_queue.empty()) { @@ -550,7 +550,10 @@ int spgw::gtpc::init_ue_ip(spgw_args_t* args, const std::map::const_iterator iter = ip_to_imsi.begin(); iter != ip_to_imsi.end(); ++iter) { struct in_addr in_addr; - in_addr.s_addr = inet_addr(iter->first.c_str()); + if (inet_pton(AF_INET, iter->first.c_str(), &in_addr.s_addr) != 1) { + perror("inet_pton"); + return SRSRAN_ERROR; + } if (!m_imsi_to_ip.insert(std::make_pair(iter->second, in_addr)).second) { m_logger.error( "SPGW: duplicate imsi %015" PRIu64 " for static ip address %s.", iter->second, iter->first.c_str()); @@ -562,7 +565,13 @@ int spgw::gtpc::init_ue_ip(spgw_args_t* args, const std::mapsgi_if_addr.c_str()) + htonl(n); + if (inet_pton(AF_INET, args->sgi_if_addr.c_str(), &ue_addr.s_addr) != 1) { + m_logger.error("Invalid sgi_if_addr: %s", args->sgi_if_addr.c_str()); + srsran::console("Invalid sgi_if_addr: %s\n", args->sgi_if_addr.c_str()); + perror("inet_pton"); + return SRSRAN_ERROR; + } + ue_addr.s_addr = ue_addr.s_addr + htonl(n); std::map::const_iterator iter = ip_to_imsi.find(inet_ntoa(ue_addr)); if (iter != ip_to_imsi.end()) { diff --git a/srsepc/src/spgw/gtpu.cc b/srsepc/src/spgw/gtpu.cc index 5bb074ed5..59bd7ca4d 100644 --- a/srsepc/src/spgw/gtpu.cc +++ b/srsepc/src/spgw/gtpu.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #include "srsepc/hdr/spgw/gtpu.h" #include "srsepc/hdr/mme/mme_gtpc.h" #include "srsran/common/string_helpers.h" +#include "srsran/common/network_utils.h" #include "srsran/upper/gtpu.h" #include #include @@ -140,9 +141,11 @@ int spgw::gtpu::init_sgi(spgw_args_t* args) // Set IP of the interface struct sockaddr_in* addr = (struct sockaddr_in*)&ifr.ifr_addr; - addr->sin_family = AF_INET; - addr->sin_addr.s_addr = inet_addr(args->sgi_if_addr.c_str()); - addr->sin_port = 0; + if (not srsran::net_utils::set_sockaddr(addr, args->sgi_if_addr.c_str(), 0)) { + m_logger.error("Invalid sgi_if_addr: %s", args->sgi_if_addr.c_str()); + srsran::console("Invalid sgi_if_addr: %s\n", args->sgi_if_addr.c_str()); + return SRSRAN_ERROR_CANT_START; + } if (ioctl(sgi_sock, SIOCSIFADDR, &ifr) < 0) { m_logger.error( @@ -152,8 +155,11 @@ int spgw::gtpu::init_sgi(spgw_args_t* args) return SRSRAN_ERROR_CANT_START; } - ifr.ifr_netmask.sa_family = AF_INET; - ((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); + ifr.ifr_netmask.sa_family = AF_INET; + if (inet_pton(ifr.ifr_netmask.sa_family , "255.255.255.0", &((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr) != 1) { + perror("inet_pton"); + return false; + } if (ioctl(sgi_sock, SIOCSIFNETMASK, &ifr) < 0) { m_logger.error("Failed to set TUN interface Netmask. Error: %s", strerror(errno)); close(m_sgi); @@ -179,7 +185,11 @@ int spgw::gtpu::init_s1u(spgw_args_t* args) // Bind the socket m_s1u_addr.sin_family = AF_INET; - m_s1u_addr.sin_addr.s_addr = inet_addr(args->gtpu_bind_addr.c_str()); + if (inet_pton(m_s1u_addr.sin_family, args->gtpu_bind_addr.c_str(), &m_s1u_addr.sin_addr.s_addr) != 1) { + m_logger.error("Invalid gtpu_bind_addr: %s", args->gtpu_bind_addr.c_str()); + srsran::console("Invalid gtpu_bind_addr: %s\n", args->gtpu_bind_addr.c_str()); + return SRSRAN_ERROR_CANT_START; + } m_s1u_addr.sin_port = htons(GTPU_RX_PORT); if (bind(m_s1u, (struct sockaddr*)&m_s1u_addr, sizeof(struct sockaddr_in))) { diff --git a/srsepc/src/spgw/spgw.cc b/srsepc/src/spgw/spgw.cc index aa97eb48d..f588eee2b 100644 --- a/srsepc/src/spgw/spgw.cc +++ b/srsepc/src/spgw/spgw.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsepc/srsepc_if_masq.sh b/srsepc/srsepc_if_masq.sh index e62a0c877..00c609d25 100755 --- a/srsepc/srsepc_if_masq.sh +++ b/srsepc/srsepc_if_masq.sh @@ -1,7 +1,7 @@ #!/bin/bash # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -26,7 +26,7 @@ sudo -v || exit #Check if outbound interface was specified if [ ! $# -eq 1 ] then - echo "Usage :'sudo ./if_masq.sh ' " + echo "Usage :'sudo ./srsepc_if_masq.sh ' " exit fi diff --git a/srsgnb/CMakeLists.txt b/srsgnb/CMakeLists.txt new file mode 100644 index 000000000..0d2803a95 --- /dev/null +++ b/srsgnb/CMakeLists.txt @@ -0,0 +1,21 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_subdirectory(src) \ No newline at end of file diff --git a/srsgnb/hdr/phy/phy_nr_interfaces.h b/srsgnb/hdr/phy/phy_nr_interfaces.h new file mode 100644 index 000000000..b350db138 --- /dev/null +++ b/srsgnb/hdr/phy/phy_nr_interfaces.h @@ -0,0 +1,42 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_PHY_NR_INTERFACES_H +#define SRSRAN_PHY_NR_INTERFACES_H + +#include "srsran/srsran.h" +#include + +namespace srsenb { + +struct phy_cell_cfg_nr_t { + srsran_carrier_nr_t carrier; + uint32_t rf_port; + uint32_t cell_id; + float gain_db; + bool dl_measure; +}; + +using phy_cell_cfg_list_nr_t = std::vector; + +} // namespace srsenb + +#endif // SRSRAN_PHY_NR_INTERFACES_H diff --git a/srsgnb/hdr/stack/common/test/dummy_nr_classes.h b/srsgnb/hdr/stack/common/test/dummy_nr_classes.h new file mode 100644 index 000000000..f35d51e20 --- /dev/null +++ b/srsgnb/hdr/stack/common/test/dummy_nr_classes.h @@ -0,0 +1,103 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_DUMMY_NR_CLASSES_H +#define SRSRAN_DUMMY_NR_CLASSES_H + +#include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/interfaces/gnb_mac_interfaces.h" +#include "srsran/interfaces/gnb_ngap_interfaces.h" + +namespace srsenb { + +class ngap_dummy : public ngap_interface_rrc_nr +{ + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu) + {} + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu, + uint32_t m_tmsi) + {} + + void write_pdu(uint16_t rnti, srsran::const_byte_span pdu) {} + bool user_exists(uint16_t rnti) { return true; } + void user_mod(uint16_t old_rnti, uint16_t new_rnti) {} + void user_release_request(uint16_t rnti, asn1::ngap::cause_radio_network_e cause_radio) {} + bool is_amf_connected() { return true; } + void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) {} +}; + +class rrc_nr_dummy : public rrc_interface_mac_nr +{ +public: + int read_pdu_bcch_bch(const uint32_t tti, srsran::byte_buffer_t& buffer) { return SRSRAN_SUCCESS; } + int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::byte_buffer_t& buffer) { return SRSRAN_SUCCESS; } + int add_user(uint16_t rnti, uint32_t pcell_cc_idx) { return SRSRAN_SUCCESS; } + int update_user(uint16_t new_rnti, uint16_t old_rnti) { return SRSRAN_SUCCESS; } + void set_activity_user(uint16_t rnti) {} +}; + +class rlc_nr_dummy : public rlc_interface_mac_nr +{ +public: + int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override { return SRSRAN_SUCCESS; } + void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) override {} + void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) override {} +}; + +class mac_nr_dummy : public mac_interface_rrc_nr +{ +public: + int cell_cfg(const std::vector& nr_cells_) override + { + nr_cells = nr_cells_; + return SRSRAN_SUCCESS; + } + uint16_t reserve_rnti(uint32_t enb_cc_idx, const sched_nr_ue_cfg_t& uecfg) override { return 0x4601; } + + int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) override + { + last_ue_cfg_rnti = rnti; + last_ue_cfg = ue_cfg; + return SRSRAN_SUCCESS; + } + + int remove_ue(uint16_t rnti) override { return SRSRAN_SUCCESS; } + + std::vector nr_cells; + uint16_t last_ue_cfg_rnti = SRSRAN_INVALID_RNTI; + sched_nr_interface::ue_cfg_t last_ue_cfg{}; +}; + +class phy_nr_dummy : public phy_interface_stack_nr +{ +public: + int set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg_) override { return SRSRAN_SUCCESS; } +}; + +} // namespace srsenb + +#endif // SRSRAN_DUMMY_NR_CLASSES_H diff --git a/srsgnb/hdr/stack/gnb_stack_nr.h b/srsgnb/hdr/stack/gnb_stack_nr.h new file mode 100644 index 000000000..7beae7bbc --- /dev/null +++ b/srsgnb/hdr/stack/gnb_stack_nr.h @@ -0,0 +1,176 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/****************************************************************************** + * File: gnb_stack_nr.h + * Description: L2/L3 gNB stack class. + *****************************************************************************/ + +#ifndef SRSRAN_GNB_STACK_NR_H +#define SRSRAN_GNB_STACK_NR_H + +#include "srsenb/hdr/stack/upper/pdcp.h" +#include "srsenb/hdr/stack/upper/rlc.h" +#include "srsgnb/hdr/stack/mac/mac_nr.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr.h" +#include "srsgnb/hdr/stack/sdap/sdap.h" + +#include "srsenb/hdr/stack/enb_stack_base.h" +#include "srsran/interfaces/gnb_interfaces.h" + +#include "srsran/common/ngap_pcap.h" + +namespace srsenb { + +class ngap; +class gtpu; +class enb_bearer_manager; +class gtpu_pdcp_adapter; + +struct gnb_stack_args_t { + stack_log_args_t log; + mac_nr_args_t mac; + ngap_args_t ngap; + pcap_args_t ngap_pcap; +}; + +class gnb_stack_nr final : public srsenb::enb_stack_base, + public stack_interface_phy_nr, + public stack_interface_mac, + public srsue::stack_interface_gw, + public rrc_nr_interface_rrc, + public pdcp_interface_gtpu, // for user-plane over X2 + public srsran::thread +{ +public: + explicit gnb_stack_nr(srslog::sink& log_sink); + ~gnb_stack_nr() final; + + int init(const gnb_stack_args_t& args_, + const rrc_nr_cfg_t& rrc_cfg_, + phy_interface_stack_nr* phy_, + x2_interface* x2_); + + // eNB stack base interface + void stop() final; + std::string get_type() final; + bool get_metrics(srsenb::stack_metrics_t* metrics) final; + + // GW srsue stack_interface_gw dummy interface + bool is_registered() override { return true; }; + bool start_service_request() override { return true; }; + + // Temporary GW interface + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) override; + bool has_active_radio_bearer(uint32_t eps_bearer_id) override; + bool switch_on(); + void tti_clock() override; + + // MAC interface to trigger processing of received PDUs + void process_pdus() final; + + void toggle_padding() override {} + + int slot_indication(const srsran_slot_cfg_t& slot_cfg) override; + dl_sched_t* get_dl_sched(const srsran_slot_cfg_t& slot_cfg) override; + ul_sched_t* get_ul_sched(const srsran_slot_cfg_t& slot_cfg) override; + int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override; + int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override; + void rach_detected(const rach_info_t& rach_info) override; + + // X2 interface + + // control plane, i.e. rrc_nr_interface_rrc + void sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) final + { + x2_task_queue.push([this, eutra_rnti, params]() { rrc.sgnb_addition_request(eutra_rnti, params); }); + }; + void sgnb_reconfiguration_complete(uint16_t eutra_rnti, const asn1::dyn_octstring& reconfig_response) final + { + x2_task_queue.push( + [this, eutra_rnti, reconfig_response]() { rrc.sgnb_reconfiguration_complete(eutra_rnti, reconfig_response); }); + }; + void sgnb_release_request(uint16_t nr_rnti) final + { + x2_task_queue.push([this, nr_rnti]() { return rrc.sgnb_release_request(nr_rnti); }); + } + // X2 data interface + void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) final + { + auto task = [this, rnti, lcid, pdcp_sn](srsran::unique_byte_buffer_t& sdu) { + pdcp.write_sdu(rnti, lcid, std::move(sdu), pdcp_sn); + }; + gtpu_task_queue.push(std::bind(task, std::move(sdu))); + } + std::map get_buffered_pdus(uint16_t rnti, uint32_t lcid) final + { + // TODO: make it thread-safe. For now, this function is unused + return pdcp.get_buffered_pdus(rnti, lcid); + } + +private: + void run_thread() final; + void tti_clock_impl(); + void stop_impl(); + + // args + gnb_stack_args_t args = {}; + phy_interface_stack_nr* phy = nullptr; + + srslog::basic_logger& rrc_logger; + srslog::basic_logger& mac_logger; + srslog::basic_logger& rlc_logger; + srslog::basic_logger& pdcp_logger; + srslog::basic_logger& ngap_logger; + srslog::basic_logger& gtpu_logger; + srslog::basic_logger& stack_logger; + + srsran::ngap_pcap ngap_pcap; + + // task scheduling + static const int STACK_MAIN_THREAD_PRIO = 4; + srsran::task_scheduler task_sched; + srsran::task_multiqueue::queue_handle sync_task_queue, gtpu_task_queue, metrics_task_queue, gnb_task_queue, + x2_task_queue; + + // metrics waiting condition + std::mutex metrics_mutex; + std::condition_variable metrics_cvar; + + // layers + srsenb::mac_nr mac; + srsenb::rlc rlc; + srsenb::pdcp pdcp; + srsenb::rrc_nr rrc; + std::unique_ptr ngap; + std::unique_ptr gtpu; + // std::unique_ptr m_sdap; + + std::unique_ptr bearer_manager; + std::unique_ptr gtpu_adapter; + + // state + std::atomic running = {false}; +}; + +} // namespace srsenb + +#endif // SRSRAN_GNB_STACK_NR_H diff --git a/srsgnb/hdr/stack/mac/harq_softbuffer.h b/srsgnb/hdr/stack/mac/harq_softbuffer.h new file mode 100644 index 000000000..9d6cc6db8 --- /dev/null +++ b/srsgnb/hdr/stack/mac/harq_softbuffer.h @@ -0,0 +1,151 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_HARQ_SOFTBUFFER_H +#define SRSRAN_HARQ_SOFTBUFFER_H + +#include "srsran/adt/pool/pool_interface.h" +#include "srsran/adt/span.h" +extern "C" { +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/fec/softbuffer.h" +#include "srsran/phy/phch/sch_nr.h" +#include "srsran/phy/utils/vector.h" +} + +namespace srsenb { + +class tx_harq_softbuffer +{ +public: + tx_harq_softbuffer() { bzero(&buffer, sizeof(buffer)); } + explicit tx_harq_softbuffer(uint32_t nof_prb_) + { + // Note: for now we use same size regardless of nof_prb_ + srsran_softbuffer_tx_init_guru(&buffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB); + } + tx_harq_softbuffer(const tx_harq_softbuffer&) = delete; + tx_harq_softbuffer(tx_harq_softbuffer&& other) noexcept + { + memcpy(&buffer, &other.buffer, sizeof(other.buffer)); + bzero(&other.buffer, sizeof(other.buffer)); + } + tx_harq_softbuffer& operator=(const tx_harq_softbuffer&) = delete; + tx_harq_softbuffer& operator =(tx_harq_softbuffer&& other) noexcept + { + if (this != &other) { + destroy(); + memcpy(&buffer, &other.buffer, sizeof(other.buffer)); + bzero(&other.buffer, sizeof(other.buffer)); + } + return *this; + } + ~tx_harq_softbuffer() { destroy(); } + + void reset() { srsran_softbuffer_tx_reset(&buffer); } + + srsran_softbuffer_tx_t& operator*() { return buffer; } + const srsran_softbuffer_tx_t& operator*() const { return buffer; } + srsran_softbuffer_tx_t* operator->() { return &buffer; } + const srsran_softbuffer_tx_t* operator->() const { return &buffer; } + srsran_softbuffer_tx_t* get() { return &buffer; } + const srsran_softbuffer_tx_t* get() const { return &buffer; } + +private: + void destroy() { srsran_softbuffer_tx_free(&buffer); } + + srsran_softbuffer_tx_t buffer; +}; + +class rx_harq_softbuffer +{ +public: + rx_harq_softbuffer() { bzero(&buffer, sizeof(buffer)); } + explicit rx_harq_softbuffer(uint32_t nof_prb_) + { + // Note: for now we use same size regardless of nof_prb_ + srsran_softbuffer_rx_init_guru(&buffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB); + } + rx_harq_softbuffer(const rx_harq_softbuffer&) = delete; + rx_harq_softbuffer(rx_harq_softbuffer&& other) noexcept + { + memcpy(&buffer, &other.buffer, sizeof(other.buffer)); + bzero(&other.buffer, sizeof(other.buffer)); + } + rx_harq_softbuffer& operator=(const rx_harq_softbuffer&) = delete; + rx_harq_softbuffer& operator =(rx_harq_softbuffer&& other) noexcept + { + if (this != &other) { + destroy(); + memcpy(&buffer, &other.buffer, sizeof(other.buffer)); + bzero(&other.buffer, sizeof(other.buffer)); + } + return *this; + } + ~rx_harq_softbuffer() { destroy(); } + + void reset() { srsran_softbuffer_rx_reset(&buffer); } + void reset(uint32_t tbs_bits) { srsran_softbuffer_rx_reset_tbs(&buffer, tbs_bits); } + + srsran_softbuffer_rx_t& operator*() { return buffer; } + const srsran_softbuffer_rx_t& operator*() const { return buffer; } + srsran_softbuffer_rx_t* operator->() { return &buffer; } + const srsran_softbuffer_rx_t* operator->() const { return &buffer; } + srsran_softbuffer_rx_t* get() { return &buffer; } + const srsran_softbuffer_rx_t* get() const { return &buffer; } + +private: + void destroy() { srsran_softbuffer_rx_free(&buffer); } + + srsran_softbuffer_rx_t buffer; +}; + +class harq_softbuffer_pool +{ +public: + harq_softbuffer_pool(const harq_softbuffer_pool&) = delete; + harq_softbuffer_pool(harq_softbuffer_pool&&) = delete; + harq_softbuffer_pool& operator=(const harq_softbuffer_pool&) = delete; + harq_softbuffer_pool& operator=(harq_softbuffer_pool&&) = delete; + + void init_pool(uint32_t nof_prb, uint32_t batch_size = MAX_HARQ * 4, uint32_t thres = 0, uint32_t init_size = 0); + + srsran::unique_pool_ptr get_tx(uint32_t nof_prb); + srsran::unique_pool_ptr get_rx(uint32_t nof_prb); + + static harq_softbuffer_pool& get_instance() + { + static harq_softbuffer_pool pool; + return pool; + } + +private: + const static uint32_t MAX_HARQ = 16; + + harq_softbuffer_pool() = default; + + std::array >, SRSRAN_MAX_PRB_NR> tx_pool; + std::array >, SRSRAN_MAX_PRB_NR> rx_pool; +}; + +} // namespace srsenb + +#endif // SRSRAN_HARQ_SOFTBUFFER_H diff --git a/srsgnb/hdr/stack/mac/mac_nr.h b/srsgnb/hdr/stack/mac/mac_nr.h new file mode 100644 index 000000000..f89d6da05 --- /dev/null +++ b/srsgnb/hdr/stack/mac/mac_nr.h @@ -0,0 +1,159 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_MAC_NR_H +#define SRSENB_MAC_NR_H + +#include "srsran/common/block_queue.h" +#include "srsran/common/mac_pcap.h" + +#include "srsenb/hdr/common/rnti_pool.h" +#include "srsenb/hdr/stack/enb_stack_base.h" +#include "srsgnb/hdr/stack/mac/ue_nr.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/interfaces/enb_metrics_interface.h" +#include "srsran/interfaces/enb_rlc_interfaces.h" +#include "srsran/interfaces/gnb_mac_interfaces.h" + +namespace srsenb { + +struct mac_nr_args_t { + srsran::phy_cfg_nr_t phy_base_cfg = {}; + int fixed_dl_mcs = -1; + int fixed_ul_mcs = -1; + sched_nr_interface::sched_args_t sched_cfg = {}; + srsenb::pcap_args_t pcap; +}; + +class sched_nr; +class mac_nr_rx; + +class mac_nr final : public mac_interface_phy_nr, + public mac_interface_rrc_nr, + public mac_interface_rlc_nr, + public mac_interface_pdu_demux_nr +{ +public: + explicit mac_nr(srsran::task_sched_handle task_sched_); + ~mac_nr(); + + int init(const mac_nr_args_t& args_, + phy_interface_stack_nr* phy, + stack_interface_mac* stack_, + rlc_interface_mac* rlc_, + rrc_interface_mac_nr* rrc_); + void stop(); + + /// Called from metrics thread. + void get_metrics(srsenb::mac_metrics_t& metrics); + + // MAC interface for RRC + int cell_cfg(const std::vector& nr_cells) override; + uint16_t reserve_rnti(uint32_t enb_cc_idx, const sched_nr_interface::ue_cfg_t& uecfg) override; + int read_pdu_bcch_bch(uint8_t* payload); + int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) override; + int remove_ue(uint16_t rnti) override; + + // MAC interface for RLC + int rlc_buffer_state(uint16_t rnti, uint32_t lcid, uint32_t tx_queue, uint32_t retx_queue) override; + + // Interface for PHY + int slot_indication(const srsran_slot_cfg_t& slot_cfg) override; + dl_sched_t* get_dl_sched(const srsran_slot_cfg_t& slot_cfg) override; + ul_sched_t* get_ul_sched(const srsran_slot_cfg_t& slot_cfg) override; + int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override; + int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override; + void rach_detected(const rach_info_t& rach_info) override; + + // MAC-internal interface + void store_msg3(uint16_t rnti, srsran::unique_byte_buffer_t pdu) override; + + // Test interface + void ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr); + +private: + uint16_t add_ue_(uint32_t enb_cc_idx); + uint16_t alloc_ue(uint32_t enb_cc_idx); + + // internal misc helpers + bool is_rnti_valid_nolock(uint16_t rnti); + bool is_rnti_active_nolock(uint16_t rnti); + + // handle UCI data from either PUCCH or PUSCH + bool handle_uci_data(uint16_t rnti, const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value); + + // Metrics processing + void get_metrics_nolock(srsenb::mac_metrics_t& metrics); + + // Encoding + srsran::byte_buffer_t* assemble_rar(srsran::const_span grants); + + srsran::unique_byte_buffer_t rar_pdu_buffer; + + // Interaction with other components + phy_interface_stack_nr* phy = nullptr; + stack_interface_mac* stack = nullptr; + rlc_interface_mac* rlc = nullptr; + rrc_interface_mac_nr* rrc = nullptr; + + // args + srsran::task_sched_handle task_sched; + srsran::task_queue_handle stack_task_queue; + mac_nr_args_t args = {}; + srslog::basic_logger& logger; + + // initial UE config, before RRC setup (without UE-dedicated) + srsran::phy_cfg_nr_t default_ue_phy_cfg; + + std::unique_ptr pcap = nullptr; + + std::atomic started = {false}; + + const static uint32_t NUMEROLOGY_IDX = 0; /// only 15kHz supported at this stage + std::unique_ptr sched; + std::vector cell_config; + + // Map of active UEs + pthread_rwlock_t rwmutex = {}; + static const uint16_t FIRST_RNTI = 0x4601; + srsran::static_circular_map, SRSENB_MAX_UES> ue_db; + + std::atomic ue_counter{0}; + + // BCH buffers + struct sib_info_t { + uint32_t index; + uint32_t periodicity; + srsran::unique_byte_buffer_t payload; + }; + std::vector bcch_dlsch_payload; + srsran::unique_byte_buffer_t bcch_bch_payload = nullptr; + + // Number of rach preambles detected for a CC + std::vector detected_rachs; + + // Decoding of UL PDUs + std::unique_ptr rx; +}; + +} // namespace srsenb + +#endif // SRSENB_MAC_NR_H diff --git a/srsgnb/hdr/stack/mac/sched_nr.h b/srsgnb/hdr/stack/mac/sched_nr.h new file mode 100644 index 000000000..9a0a2998c --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr.h @@ -0,0 +1,103 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_H +#define SRSRAN_SCHED_NR_H + +#include "sched_nr_cfg.h" +#include "sched_nr_interface.h" +#include "sched_nr_ue.h" +#include "srsran/adt/pool/cached_alloc.h" +#include "srsran/adt/pool/circular_stack_pool.h" +#include "srsran/common/slot_point.h" +#include +extern "C" { +#include "srsran/config.h" +} + +namespace srsenb { + +namespace sched_nr_impl { + +class cc_worker; + +} // namespace sched_nr_impl + +class sched_nr final : public sched_nr_interface +{ +public: + explicit sched_nr(); + ~sched_nr() override; + + void stop(); + int config(const sched_args_t& sched_cfg, srsran::const_span cell_list) override; + void ue_cfg(uint16_t rnti, const ue_cfg_t& cfg) override; + void ue_rem(uint16_t rnti) override; + + int dl_rach_info(const rar_info_t& rar_info); + + void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) override; + void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) override; + void ul_sr_info(uint16_t rnti) override; + void ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) override; + void dl_buffer_state(uint16_t rnti, uint32_t lcid, uint32_t newtx, uint32_t retx); + void dl_mac_ce(uint16_t rnti, uint32_t ce_lcid) override; + void dl_cqi_info(uint16_t rnti, uint32_t cc, uint32_t cqi_value); + + /// Called once per slot in a non-concurrent fashion + void slot_indication(slot_point slot_tx) override; + dl_res_t* get_dl_sched(slot_point pdsch_tti, uint32_t cc) override; + ul_res_t* get_ul_sched(slot_point pusch_tti, uint32_t cc) override; + + void get_metrics(mac_metrics_t& metrics); + +private: + int ue_cfg_impl(uint16_t rnti, const ue_cfg_t& cfg); + int add_ue_impl(uint16_t rnti, sched_nr_impl::unique_ue_ptr u); + + // args + sched_nr_impl::sched_params_t cfg; + srslog::basic_logger* logger = nullptr; + + // slot-specific + slot_point current_slot_tx; + std::atomic worker_count{0}; + + using slot_cc_worker = sched_nr_impl::cc_worker; + std::vector > cc_workers; + + // UE Database + std::unique_ptr > ue_pool; + using ue_map_t = sched_nr_impl::ue_map_t; + ue_map_t ue_db; + + // Feedback management + class event_manager; + std::unique_ptr pending_events; + + // metrics extraction + class ue_metrics_manager; + std::unique_ptr metrics_handler; +}; + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_bwp.h b/srsgnb/hdr/stack/mac/sched_nr_bwp.h new file mode 100644 index 000000000..adc5042eb --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_bwp.h @@ -0,0 +1,87 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_BWP_H +#define SRSRAN_SCHED_NR_BWP_H + +#include "sched_nr_cfg.h" +#include "sched_nr_grant_allocator.h" +#include "sched_nr_signalling.h" +#include "sched_nr_time_rr.h" +#include "srsran/adt/pool/cached_alloc.h" + +namespace srsenb { +namespace sched_nr_impl { + +using dl_sched_rar_info_t = sched_nr_interface::rar_info_t; + +/// RAR/Msg3 scheduler +class ra_sched +{ +public: + explicit ra_sched(const bwp_params_t& bwp_cfg_); + + /// Addition of detected PRACH into the queue + int dl_rach_info(const dl_sched_rar_info_t& rar_info); + + /// Allocate pending RARs + void run_slot(bwp_slot_allocator& slot_alloc); + + /// Check if there are pending RARs + bool empty() const { return pending_rars.empty(); } + +private: + struct pending_rar_t { + uint16_t ra_rnti = 0; + slot_point prach_slot; + slot_interval rar_win; + srsran::bounded_vector msg3_grant; + }; + + alloc_result + allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc); + + const bwp_params_t* bwp_cfg = nullptr; + srslog::basic_logger& logger; + + srsran::deque pending_rars; +}; + +class bwp_manager +{ +public: + explicit bwp_manager(const bwp_params_t& bwp_cfg); + + const bwp_params_t* cfg; + + // channel-specific schedulers + si_sched si; + ra_sched ra; + std::unique_ptr data_sched; + + // Stores pending allocations and PRB bitmaps + bwp_res_grid grid; +}; + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_BWP_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_cfg.h b/srsgnb/hdr/stack/mac/sched_nr_cfg.h new file mode 100644 index 000000000..a165fe402 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_cfg.h @@ -0,0 +1,158 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_CFG_H +#define SRSRAN_SCHED_NR_CFG_H + +#include "sched_nr_interface_utils.h" +#include "sched_nr_rb.h" +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/adt/optional_array.h" + +namespace srsenb { + +static const size_t MAX_NOF_AGGR_LEVELS = 5; + +namespace sched_nr_impl { + +constexpr static size_t MAX_GRANTS = sched_nr_interface::MAX_GRANTS; + +using pdcch_dl_t = mac_interface_phy_nr::pdcch_dl_t; +using pdcch_ul_t = mac_interface_phy_nr::pdcch_ul_t; +using pdsch_t = mac_interface_phy_nr::pdsch_t; +using pusch_t = mac_interface_phy_nr::pusch_t; +using pucch_t = mac_interface_phy_nr::pucch_t; +using pdcch_dl_list_t = srsran::bounded_vector; +using pdcch_ul_list_t = srsran::bounded_vector; +using pdsch_list_t = srsran::bounded_vector; +using pucch_list_t = srsran::bounded_vector; +using pusch_list_t = srsran::bounded_vector; +using nzp_csi_rs_list = srsran::bounded_vector; +using ssb_t = mac_interface_phy_nr::ssb_t; +using ssb_list = srsran::bounded_vector; +using sched_args_t = sched_nr_interface::sched_args_t; +using bwp_cfg_t = sched_nr_bwp_cfg_t; +using ue_cc_cfg_t = sched_nr_interface::ue_cc_cfg_t; +using pdcch_cce_pos_list = srsran::bounded_vector; +using bwp_cce_pos_list = std::array, SRSRAN_NOF_SF_X_FRAME>; +using dl_sched_t = sched_nr_interface::dl_sched_t; +using ul_sched_t = sched_nr_interface::ul_res_t; +using dl_sched_res_t = sched_nr_interface::dl_res_t; + +/// Generate list of CCE locations for UE based on coreset and search space configurations +void get_dci_locs(const srsran_coreset_t& coreset, + const srsran_search_space_t& search_space, + uint16_t rnti, + bwp_cce_pos_list& cce_locs); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +struct cell_config_manager; + +/// Structure that extends the sched_nr_interface::bwp_cfg_t passed by upper layers with other +/// derived BWP-specific params +struct bwp_params_t { + const uint32_t bwp_id; + const uint32_t cc; + const bwp_cfg_t cfg; + const cell_config_manager& cell_cfg; + const sched_args_t& sched_cfg; + sched_nr_bwp_cfg_t bwp_cfg; + + // derived params + srslog::basic_logger& logger; + uint32_t P; + uint32_t N_rbg; + uint32_t nof_prb; + + /// Table specifying if a slot has DL or UL enabled + struct slot_cfg { + bool is_dl; + bool is_ul; + }; + srsran::bounded_vector slots; + + struct pusch_ra_time_cfg { + uint32_t msg3_delay; ///< Includes K2 and delta. See TS 36.214 6.1.2.1.1-2/4/5 + uint32_t K; + uint32_t S; + uint32_t L; + }; + std::vector pusch_ra_list; + + bwp_cce_pos_list rar_cce_list; + + srsran::optional_vector common_cce_list; + + bwp_params_t(const cell_config_manager& cell, uint32_t bwp_id, const sched_nr_bwp_cfg_t& bwp_cfg); + + prb_interval coreset_prb_range(uint32_t cs_id) const { return coresets[cs_id].prb_limits; } + prb_interval dci_fmt_1_0_prb_lims(uint32_t cs_id) const { return coresets[cs_id].dci_1_0_prb_limits; } + bwp_rb_bitmap dci_fmt_1_0_excluded_prbs(uint32_t cs_id) const { return coresets[cs_id].usable_common_ss_prb_mask; } + + const srsran_search_space_t* get_ss(uint32_t ss_id) const + { + return cfg.pdcch.search_space_present[ss_id] ? &cfg.pdcch.search_space[ss_id] : nullptr; + } + +private: + bwp_rb_bitmap cached_empty_prb_mask; + struct coreset_cached_params { + prb_interval prb_limits; + prb_interval dci_1_0_prb_limits; /// See TS 38.214, section 5.1.2.2 + bwp_rb_bitmap usable_common_ss_prb_mask; + }; + srsran::optional_vector coresets; +}; + +/// Structure packing a single cell config params, and sched args +struct cell_config_manager { + const uint32_t cc; + srsran_carrier_nr_t carrier = {}; + srsran_mib_nr_t mib; + srsran::phy_cfg_nr_t::ssb_cfg_t ssb = {}; + std::vector bwps; // idx0 for BWP-common + std::vector sibs; + asn1::copy_ptr dl_cfg_common; + asn1::copy_ptr ul_cfg_common; + srsran_duplex_config_nr_t duplex = {}; + const sched_args_t& sched_args; + const srsran::phy_cfg_nr_t default_ue_phy_cfg; + + cell_config_manager(uint32_t cc_, const sched_nr_cell_cfg_t& cell, const sched_args_t& sched_args_); + + uint32_t nof_prb() const { return carrier.nof_prb; } +}; + +/// Structure packing both the sched args and all gNB NR cell configurations +struct sched_params_t { + sched_args_t sched_cfg; + std::vector cells; + + sched_params_t() = default; + explicit sched_params_t(const sched_args_t& sched_cfg_); +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_CFG_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h b/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h new file mode 100644 index 000000000..d97522860 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h @@ -0,0 +1,142 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_GRANT_ALLOCATOR_H +#define SRSRAN_SCHED_NR_GRANT_ALLOCATOR_H + +#include "lib/include/srsran/adt/circular_array.h" +#include "sched_nr_helpers.h" +#include "sched_nr_interface.h" +#include "sched_nr_pdcch.h" +#include "sched_nr_sch.h" +#include "sched_nr_ue.h" +#include "srsenb/hdr/stack/mac/sched_common.h" + +namespace srsenb { +namespace sched_nr_impl { + +// typedefs +using dl_sched_rar_info_t = sched_nr_interface::rar_info_t; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +using pdsch_list_t = srsran::bounded_vector; +using sched_rar_list_t = sched_nr_interface::sched_rar_list_t; +using pucch_list_t = srsran::bounded_vector; + +struct harq_ack_t { + const srsran::phy_cfg_nr_t* phy_cfg; + srsran_harq_ack_resource_t res; +}; +using harq_ack_list_t = srsran::bounded_vector; + +/// save data for scheduler to keep track of previous allocations +/// This only contains information about a given slot +struct bwp_slot_grid { + uint32_t slot_idx = 0; + const bwp_params_t* cfg = nullptr; + + dl_sched_res_t dl; + ul_sched_t ul; + harq_ack_list_t pending_acks; + bwp_pdcch_allocator pdcchs; /// slot PDCCH resource allocator + pdsch_allocator pdschs; /// slot PDSCH resource allocator + pusch_allocator puschs; /// slot PUSCH resource allocator + + srsran::unique_pool_ptr rar_softbuffer; + + explicit bwp_slot_grid(const bwp_params_t& bwp_params, uint32_t slot_idx_); + void reset(); + + void reserve_pdsch(const prb_grant& grant) { pdschs.reserve_prbs(grant); } + + bool is_dl() const { return cfg->slots[slot_idx].is_dl; } + bool is_ul() const { return cfg->slots[slot_idx].is_ul; } +}; + +struct bwp_res_grid { + explicit bwp_res_grid(const bwp_params_t& bwp_cfg_); + + bwp_slot_grid& operator[](slot_point tti) { return slots[tti.to_uint() % slots.capacity()]; }; + const bwp_slot_grid& operator[](slot_point tti) const { return slots[tti.to_uint() % slots.capacity()]; }; + uint32_t id() const { return cfg->bwp_id; } + uint32_t nof_prbs() const { return cfg->cfg.rb_width; } + + const bwp_params_t* cfg = nullptr; + +private: + // TTIMOD_SZ is the longest allocation in the future + srsran::bounded_vector slots; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Class responsible for jointly filling the DL/UL sched result fields and allocate RB/PDCCH resources in the RB grid + * to avoid potential RB/PDCCH collisions + */ +class bwp_slot_allocator +{ +public: + explicit bwp_slot_allocator(bwp_res_grid& bwp_grid_, slot_point pdcch_slot_, slot_ue_map_t& ues_); + + alloc_result alloc_si(uint32_t aggr_idx, + uint32_t si_idx, + uint32_t si_ntx, + const prb_interval& prbs, + tx_harq_softbuffer& softbuffer); + alloc_result alloc_rar_and_msg3(uint16_t ra_rnti, + uint32_t aggr_idx, + prb_interval interv, + srsran::const_span pending_rars); + alloc_result alloc_pdsch(slot_ue& ue, uint32_t ss_id, const prb_grant& dl_grant); + alloc_result alloc_pusch(slot_ue& ue, const prb_grant& grant); + + slot_point get_pdcch_tti() const { return pdcch_slot; } + slot_point get_tti_rx() const { return pdcch_slot - TX_ENB_DELAY; } + const bwp_res_grid& res_grid() const { return bwp_grid; } + const bwp_slot_grid& tx_slot_grid() const { return bwp_grid[pdcch_slot]; } + bwp_slot_grid& tx_slot_grid() { return bwp_grid[pdcch_slot]; } + + prb_bitmap occupied_dl_prbs(slot_point sl_tx, uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const + { + return bwp_grid[sl_tx].pdschs.occupied_prbs(ss_id, dci_fmt); + } + const prb_bitmap& occupied_ul_prbs(slot_point sl_tx) const { return bwp_grid[sl_tx].puschs.occupied_prbs(); } + + srslog::basic_logger& logger; + const bwp_params_t& cfg; + +private: + alloc_result verify_uci_space(const bwp_slot_grid& uci_grid) const; + + bwp_res_grid& bwp_grid; + + slot_point pdcch_slot; + slot_ue_map_t& slot_ues; +}; + +prb_grant find_optimal_dl_grant(bwp_slot_allocator& slot_alloc, const slot_ue& ue, uint32_t ss_id); + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_GRANT_ALLOCATOR_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_harq.h b/srsgnb/hdr/stack/mac/sched_nr_harq.h new file mode 100644 index 000000000..9c164c6ad --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_harq.h @@ -0,0 +1,193 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_HARQ_H +#define SRSRAN_SCHED_NR_HARQ_H + +#include "sched_nr_cfg.h" +#include "srsgnb/hdr/stack/mac/harq_softbuffer.h" +#include "srsran/common/slot_point.h" +#include + +namespace srsenb { +namespace sched_nr_impl { + +class harq_proc +{ +public: + explicit harq_proc(uint32_t id_) : pid(id_) {} + + bool empty() const + { + return std::all_of(tb.begin(), tb.end(), [](const tb_t& t) { return not t.active; }); + } + bool empty(uint32_t tb_idx) const { return not tb[tb_idx].active; } + bool has_pending_retx(slot_point slot_rx) const + { + return not empty() and not tb[0].ack_state and slot_ack <= slot_rx; + } + uint32_t nof_retx() const { return tb[0].n_rtx; } + uint32_t max_nof_retx() const { return max_retx; } + uint32_t tbs() const { return tb[0].tbs; } + uint32_t ndi() const { return tb[0].ndi; } + uint32_t mcs() const { return tb[0].mcs; } + const prb_grant& prbs() const { return prbs_; } + slot_point harq_slot_tx() const { return slot_tx; } + slot_point harq_slot_ack() const { return slot_ack; } + + int ack_info(uint32_t tb_idx, bool ack); + + bool clear_if_maxretx(slot_point slot_rx); + void reset(); + bool new_retx(slot_point slot_tx, slot_point slot_ack); + + // NOTE: Has to be used before first tx is dispatched + bool set_tbs(uint32_t tbs); + bool set_mcs(uint32_t mcs); + + const uint32_t pid; + +protected: + bool new_tx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, uint32_t mcs, uint32_t max_retx); + bool new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant); + + struct tb_t { + bool active = false; + bool ack_state = false; + bool ndi = false; + uint32_t n_rtx = 0; + uint32_t mcs = 0; + uint32_t tbs = 0; + }; + + uint32_t max_retx = 1; + slot_point slot_tx; + slot_point slot_ack; + prb_grant prbs_; + std::array tb; +}; + +class dl_harq_proc : public harq_proc +{ +public: + dl_harq_proc(uint32_t id_, uint32_t nprb); + + tx_harq_softbuffer& get_softbuffer() { return *softbuffer; } + srsran::unique_byte_buffer_t* get_tx_pdu() { return &pdu; } + + bool new_tx(slot_point slot_tx, + slot_point slot_ack, + const prb_grant& grant, + uint32_t mcs, + uint32_t max_retx, + srsran_dci_dl_nr_t& dci); + + bool new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, srsran_dci_dl_nr_t& dci); + +private: + void fill_dci(srsran_dci_dl_nr_t& dci); + + srsran::unique_pool_ptr softbuffer; + srsran::unique_byte_buffer_t pdu; +}; + +class ul_harq_proc : public harq_proc +{ +public: + ul_harq_proc(uint32_t id_, uint32_t nprb) : + harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_rx(nprb)) + {} + + bool new_tx(slot_point slot_tx, const prb_grant& grant, uint32_t mcs, uint32_t max_retx, srsran_dci_ul_nr_t& dci); + + bool new_retx(slot_point slot_tx, const prb_grant& grant, srsran_dci_ul_nr_t& dci); + + rx_harq_softbuffer& get_softbuffer() { return *softbuffer; } + + bool set_tbs(uint32_t tbs) + { + softbuffer->reset(tbs); + return harq_proc::set_tbs(tbs); + } + +private: + void fill_dci(srsran_dci_ul_nr_t& dci); + + srsran::unique_pool_ptr softbuffer; +}; + +class harq_entity +{ +public: + explicit harq_entity(uint16_t rnti, uint32_t nprb, uint32_t nof_harq_procs, srslog::basic_logger& logger); + void new_slot(slot_point slot_rx_); + + int dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack) { return dl_harqs[pid].ack_info(tb_idx, ack); } + int ul_crc_info(uint32_t pid, bool ack) { return ul_harqs[pid].ack_info(0, ack); } + + uint32_t nof_dl_harqs() const { return dl_harqs.size(); } + uint32_t nof_ul_harqs() const { return ul_harqs.size(); } + const dl_harq_proc& dl_harq(uint32_t pid) const { return dl_harqs[pid]; } + const ul_harq_proc& ul_harq(uint32_t pid) const { return ul_harqs[pid]; } + + dl_harq_proc* find_pending_dl_retx() + { + return find_dl([this](const dl_harq_proc& h) { return h.has_pending_retx(slot_rx); }); + } + ul_harq_proc* find_pending_ul_retx() + { + return find_ul([this](const ul_harq_proc& h) { return h.has_pending_retx(slot_rx); }); + } + dl_harq_proc* find_empty_dl_harq() + { + return find_dl([](const dl_harq_proc& h) { return h.empty(); }); + } + ul_harq_proc* find_empty_ul_harq() + { + return find_ul([](const ul_harq_proc& h) { return h.empty(); }); + } + +private: + template + dl_harq_proc* find_dl(Predicate p) + { + auto it = std::find_if(dl_harqs.begin(), dl_harqs.end(), p); + return (it == dl_harqs.end()) ? nullptr : &(*it); + } + template + ul_harq_proc* find_ul(Predicate p) + { + auto it = std::find_if(ul_harqs.begin(), ul_harqs.end(), p); + return (it == ul_harqs.end()) ? nullptr : &(*it); + } + + uint16_t rnti; + srslog::basic_logger& logger; + + slot_point slot_rx; + std::vector dl_harqs; + std::vector ul_harqs; +}; + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_HARQ_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_helpers.h b/srsgnb/hdr/stack/mac/sched_nr_helpers.h new file mode 100644 index 000000000..8829ee87e --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_helpers.h @@ -0,0 +1,75 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_HELPERS_H +#define SRSRAN_SCHED_NR_HELPERS_H + +#include "sched_nr_cfg.h" +#include "sched_nr_ue.h" +#include "srsran/adt/optional_array.h" + +namespace srsenb { +namespace sched_nr_impl { + +class slot_ue; +class ul_harq_proc; +struct bwp_res_grid; + +/// Helper function to verify if RNTI type can be placed in specified search space +/// Based on 38.213, Section 10.1 +inline bool is_rnti_type_valid_in_search_space(srsran_rnti_type_t rnti_type, srsran_search_space_type_t ss_type) +{ + switch (ss_type) { + case srsran_search_space_type_common_0: // fall-through + case srsran_search_space_type_common_0A: // Other SIBs + return rnti_type == srsran_rnti_type_si; + case srsran_search_space_type_common_1: + return rnti_type == srsran_rnti_type_ra or rnti_type == srsran_rnti_type_tc or + /* in case of Pcell -> */ rnti_type == srsran_rnti_type_c; + case srsran_search_space_type_common_2: + return rnti_type == srsran_rnti_type_p; + case srsran_search_space_type_common_3: + return rnti_type == srsran_rnti_type_c; // TODO: Fix + case srsran_search_space_type_ue: + return rnti_type == srsran_rnti_type_c or rnti_type == srsran_rnti_type_cs or + rnti_type == srsran_rnti_type_sp_csi; + default: + break; + } + return false; +} + +/// Log UE state for slot being scheduled +void log_sched_slot_ues(srslog::basic_logger& logger, + slot_point pdcch_slot, + uint32_t cc, + const slot_ue_map_t& slot_ues); + +/// Log Scheduling Result for a given BWP and slot +void log_sched_bwp_result(srslog::basic_logger& logger, + slot_point pdcch_slot, + const bwp_res_grid& res_grid, + const slot_ue_map_t& slot_ues); + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_HELPERS_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_interface.h b/srsgnb/hdr/stack/mac/sched_nr_interface.h new file mode 100644 index 000000000..932213c37 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_interface.h @@ -0,0 +1,198 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_INTERFACE_H +#define SRSRAN_SCHED_NR_INTERFACE_H + +#include "srsenb/hdr/stack/mac/common/sched_config.h" +#include "srsran/adt/bounded_vector.h" +#include "srsran/adt/optional.h" +#include "srsran/adt/span.h" +#include "srsran/asn1/rrc_nr.h" +#include "srsran/common/common_nr.h" +#include "srsran/common/phy_cfg_nr.h" +#include "srsran/common/slot_point.h" +#include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/phy/phch/dci_nr.h" + +namespace srsenb { + +const static size_t SCHED_NR_MAX_CARRIERS = 4; +const static uint16_t SCHED_NR_INVALID_RNTI = 0; +const static size_t SCHED_NR_MAX_NOF_RBGS = 18; +const static size_t SCHED_NR_MAX_TB = 1; +const static size_t SCHED_NR_MAX_HARQ = SRSRAN_DEFAULT_HARQ_PROC_DL_NR; +const static size_t SCHED_NR_MAX_BWP_PER_CELL = 2; +const static size_t SCHED_NR_MAX_LCID = srsran::MAX_NR_NOF_BEARERS; +const static size_t SCHED_NR_MAX_LC_GROUP = 7; + +struct sched_nr_ue_cc_cfg_t { + bool active = false; + uint32_t cc = 0; +}; + +struct sched_nr_ue_lc_ch_cfg_t { + uint32_t lcid; // 1..32 + mac_lc_ch_cfg_t cfg; +}; + +struct sched_nr_ue_cfg_t { + uint32_t maxharq_tx = 4; + srsran::bounded_vector carriers; + srsran::phy_cfg_nr_t phy_cfg = {}; + asn1::copy_ptr mac_cell_group_cfg; + asn1::copy_ptr phy_cell_group_cfg; + asn1::copy_ptr sp_cell_cfg; + std::vector lc_ch_to_add; + std::vector lc_ch_to_rem; +}; + +struct sched_nr_bwp_cfg_t { + uint32_t start_rb = 0; + uint32_t rb_width = 100; + srsran_pdcch_cfg_nr_t pdcch = {}; + srsran_sch_hl_cfg_nr_t pdsch = {}; + srsran_sch_hl_cfg_nr_t pusch = {}; + srsran_pucch_nr_hl_cfg_t pucch = {}; + srsran_harq_ack_cfg_hl_t harq_ack = {}; + uint32_t rar_window_size = 10; // See TS 38.331, ra-ResponseWindow: {1, 2, 4, 8, 10, 20, 40, 80} + uint32_t numerology_idx = 0; +}; + +struct sched_nr_cell_cfg_sib_t { + uint32_t len; + uint32_t period_rf; + uint32_t si_window_slots; +}; + +struct sched_nr_cell_cfg_t { + static const size_t MAX_SIBS = 2; + using ssb_positions_in_burst_t = asn1::rrc_nr::serving_cell_cfg_common_sib_s::ssb_positions_in_burst_s_; + + uint32_t nof_layers; + uint32_t pci; + uint32_t ssb_offset; + uint32_t dl_cell_nof_prb; + uint32_t ul_cell_nof_prb; + asn1::rrc_nr::dl_cfg_common_sib_s dl_cfg_common; + asn1::rrc_nr::ul_cfg_common_sib_s ul_cfg_common; + srsran::optional tdd_ul_dl_cfg_common; + ssb_positions_in_burst_t ssb_positions_in_burst; + uint32_t ssb_periodicity_ms = 0; + asn1::rrc_nr::mib_s::dmrs_type_a_position_e_ dmrs_type_a_position; + asn1::rrc_nr::subcarrier_spacing_e ssb_scs; + asn1::rrc_nr::pdcch_cfg_sib1_s pdcch_cfg_sib1; + int ss_pbch_block_power = 0; + // Extras + std::vector bwps{1}; // idx0 for BWP-common + std::vector sibs; + double dl_center_frequency_hz; + double ul_center_frequency_hz; + double ssb_center_freq_hz; +}; + +class sched_nr_interface +{ +public: + static const size_t MAX_GRANTS = mac_interface_phy_nr::MAX_GRANTS; + static const size_t MAX_SUBPDUS = 8; + + ///// Configuration ///// + struct sched_args_t { + bool pdsch_enabled = true; + bool pusch_enabled = true; + bool auto_refill_buffer = false; + int fixed_dl_mcs = 28; + int fixed_ul_mcs = 28; + std::string logger_name = "MAC-NR"; + }; + + using ue_cc_cfg_t = sched_nr_ue_cc_cfg_t; + using ue_cfg_t = sched_nr_ue_cfg_t; + + ////// RA signalling ////// + + struct rar_info_t { + uint32_t msg3_size = 7; + uint32_t cc; + uint16_t temp_crnti; + slot_point prach_slot; + uint32_t ofdm_symbol_idx; + uint32_t freq_idx; + uint32_t preamble_idx; + uint32_t ta_cmd; + }; + struct msg3_grant_t { + rar_info_t data; + srsran_dci_ul_nr_t msg3_dci = {}; + }; + struct rar_t { + srsran::bounded_vector grants; + }; + + ////// DL data signalling ////// + + struct dl_pdu_t { + srsran::bounded_vector subpdus; + }; + + ///// Sched Result ///// + + using dl_sched_t = mac_interface_phy_nr::dl_sched_t; + using ul_res_t = mac_interface_phy_nr::ul_sched_t; + + using sched_sib_list_t = srsran::bounded_vector; /// list of SI indexes + using sched_rar_list_t = srsran::bounded_vector; + using sched_dl_pdu_list_t = srsran::bounded_vector; + struct dl_res_t { + dl_sched_t phy; + sched_dl_pdu_list_t data; + sched_rar_list_t rar; + sched_sib_list_t sib_idxs; + }; + + virtual ~sched_nr_interface() = default; + virtual int config(const sched_args_t& sched_cfg, srsran::const_span ue_cfg) = 0; + virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0; + virtual void ue_rem(uint16_t rnti) = 0; + + virtual void slot_indication(slot_point slot_tx) = 0; + virtual dl_res_t* get_dl_sched(slot_point slot_rx, uint32_t cc) = 0; + virtual ul_res_t* get_ul_sched(slot_point slot_rx, uint32_t cc) = 0; + + virtual void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) = 0; + virtual void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) = 0; + virtual void ul_sr_info(uint16_t rnti) = 0; + virtual void ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) = 0; + + /** + * Enqueue MAC CEs for DL transmission + * + * @param rnti user rnti + * @param ce_lcid lcid of the MAC CE + * @return error code + */ + virtual void dl_mac_ce(uint16_t rnti, uint32_t ce_lcid) = 0; +}; + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_INTERFACE_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h b/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h new file mode 100644 index 000000000..39d8e0b41 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_interface_utils.h @@ -0,0 +1,79 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_INTERFACE_HELPERS_H +#define SRSRAN_SCHED_NR_INTERFACE_HELPERS_H + +#include "sched_nr_interface.h" +#include "srsran/adt/optional_array.h" + +namespace srsenb { + +//////////////////////////////////// Search Space Helpers //////////////////////////////////////////// + +/// Get a range of active search spaces in a PDCCH configuration +inline srsran::split_optional_span view_active_search_spaces(srsran_pdcch_cfg_nr_t& pdcch) +{ + return srsran::split_optional_span{pdcch.search_space, pdcch.search_space_present}; +} +inline srsran::split_optional_span +view_active_search_spaces(const srsran_pdcch_cfg_nr_t& pdcch) +{ + return srsran::split_optional_span{pdcch.search_space, pdcch.search_space_present}; +} + +inline bool contains_dci_format(const srsran_search_space_t& ss, srsran_dci_format_nr_t dci_fmt) +{ + auto is_dci_fmt = [dci_fmt](const srsran_dci_format_nr_t& f) { return f == dci_fmt; }; + return std::any_of(&ss.formats[0], &ss.formats[ss.nof_formats], is_dci_fmt); +} + +//////////////////////////////////// CORESET Helpers //////////////////////////////////////////// + +/// Get a range of active coresets in a PDCCH configuration +inline srsran::split_optional_span view_active_coresets(srsran_pdcch_cfg_nr_t& pdcch) +{ + return srsran::split_optional_span{pdcch.coreset, pdcch.coreset_present}; +} +inline srsran::split_optional_span view_active_coresets(const srsran_pdcch_cfg_nr_t& pdcch) +{ + return srsran::split_optional_span{pdcch.coreset, pdcch.coreset_present}; +} + +/// Get number of CCEs available in CORESET for PDCCH +uint32_t coreset_nof_cces(const srsran_coreset_t& coreset); + +//////////////////////////////////// Sched Output Helpers //////////////////////////////////////////// + +inline bool operator==(srsran_dci_location_t lhs, srsran_dci_location_t rhs) +{ + return lhs.ncce == rhs.ncce and lhs.L == rhs.L; +} + +//////////////////////////////////// UE configuration Helpers //////////////////////////////////////////// + +void make_mib_cfg(const sched_nr_cell_cfg_t& cfg, srsran_mib_nr_t* mib); +void make_ssb_cfg(const sched_nr_cell_cfg_t& cfg, srsran::phy_cfg_nr_t::ssb_cfg_t* ssb); +srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_cell_cfg_t& cfg); + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_INTERFACE_HELPERS_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_pdcch.h b/srsgnb/hdr/stack/mac/sched_nr_pdcch.h new file mode 100644 index 000000000..eee64d0c1 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_pdcch.h @@ -0,0 +1,216 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_PDCCH_H +#define SRSRAN_SCHED_NR_PDCCH_H + +#include "srsenb/hdr/stack/mac/sched_common.h" +#include "srsgnb/hdr/stack/mac/sched_nr_cfg.h" +#include "srsgnb/hdr/stack/mac/sched_ue/ue_cfg_manager.h" +#include "srsran/adt/bounded_bitset.h" +#include "srsran/adt/bounded_vector.h" +#include "srsran/phy/common/phy_common_nr.h" +#include "srsran/phy/phch/dci.h" + +namespace srsenb { + +namespace sched_nr_impl { + +/// Helper function to fill DCI with BWP params +void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci); +void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci); + +using coreset_bitmap = srsran::bounded_bitset; + +class coreset_region +{ +public: + coreset_region(const bwp_params_t& bwp_cfg_, uint32_t coreset_id_, uint32_t slot_idx); + void reset(); + + bool alloc_pdcch(srsran_rnti_type_t rnti_type, + bool is_dl, + uint32_t aggr_idx, + uint32_t search_space_id, + const ue_carrier_params_t* user, + srsran_dci_ctx_t& dci); + + void rem_last_pdcch(); + + uint32_t get_td_symbols() const { return coreset_cfg->duration; } + uint32_t get_freq_resources() const { return nof_freq_res; } + uint32_t nof_cces() const { return nof_freq_res * get_td_symbols(); } + size_t nof_allocs() const { return dfs_tree.size(); } + + void print_allocations(fmt::memory_buffer& fmtbuf) const; + +private: + const srsran_coreset_t* coreset_cfg; + uint32_t coreset_id; + uint32_t slot_idx; + uint32_t nof_freq_res = 0; + + const bwp_cce_pos_list& rar_cce_list; + const srsran::optional_vector& common_cce_list; + + // List of PDCCH grants + struct alloc_record { + uint32_t aggr_idx; + uint32_t ss_id; + srsran_dci_ctx_t* dci; + bool is_dl; + const ue_carrier_params_t* ue; + }; + srsran::bounded_vector dci_list; + + // DFS decision tree of PDCCH grants + struct tree_node { + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t record_idx = 0; + uint32_t dci_pos_idx = 0; + srsran_dci_location_t dci_pos = {0, 0}; + /// Accumulation of all PDCCH masks for the current solution (DFS path) + coreset_bitmap total_mask, current_mask; + }; + using alloc_tree_dfs_t = std::vector; + alloc_tree_dfs_t dfs_tree, saved_dfs_tree; + + srsran::span get_cce_loc_table(const alloc_record& record) const; + bool alloc_dfs_node(const alloc_record& record, uint32_t dci_idx); + bool get_next_dfs(); +}; + +using pdcch_dl_alloc_result = srsran::expected; +using pdcch_ul_alloc_result = srsran::expected; + +/** + * Class to handle the allocation of REs for a BWP PDCCH in a specific slot + */ +class bwp_pdcch_allocator +{ +public: + bwp_pdcch_allocator(const bwp_params_t& bwp_cfg_, + uint32_t slot_idx, + pdcch_dl_list_t& pdcch_dl_list, + pdcch_ul_list_t& pdcch_ul_list); + + /** + * Clear current slot allocations + */ + void reset(); + + /** + * Allocates RE space for RAR DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with RAR PDCCH allocation information + * @param ra_rnti RA-RNTI of RAR allocation + * @param aggr_idx Aggregation level index (0..4) + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_dl_alloc_result alloc_rar_pdcch(uint16_t ra_rnti, uint32_t aggr_idx); + + /** + * Allocates RE space for SI DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with SI PDCCH allocation information + * @param ss_id Search space ID + * @param aggr_idx Aggregation level index (0..4) + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_dl_alloc_result alloc_si_pdcch(uint32_t ss_id, uint32_t aggr_idx); + + /** + * Allocates RE space for UE DL DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with PDCCH allocation information + * @param rnti_type type of UE RNTI (e.g. C, TC) + * @param ss_id Search space ID + * @param aggr_idx Aggregation level index (0..4) + * @param user UE object parameters + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_dl_alloc_result + alloc_dl_pdcch(srsran_rnti_type_t rnti_type, uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user); + + /** + * @brief Allocates RE space for UL DCI in PDCCH, avoiding in the process collisions with other PDCCH allocations + * Fills DCI context with PDCCH allocation information + * @param ss_id Search space ID + * @param aggr_idx Aggregation level index (0..4) + * @param user UE object parameters + * @return PDCCH object with dci context filled if the allocation was successful. nullptr otherwise + */ + pdcch_ul_alloc_result alloc_ul_pdcch(uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user); + + /** + * Cancel and remove last PDCCH allocation. It should only be called once after each alloc_dl_pdcch/alloc_ul_pdcch + */ + void cancel_last_pdcch(); + + /// Returns the number of PDCCH allocations made in the slot + uint32_t nof_allocations() const; + + /// Number of CCEs in given coreset + uint32_t nof_cces(uint32_t coreset_id) const; + + void print_allocations(fmt::memory_buffer& fmtbuf) const; + std::string print_allocations() const; + +private: + using slot_coreset_list = srsran::optional_array; + + pdcch_dl_alloc_result alloc_dl_pdcch_common(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user = nullptr); + + /// Helper function to verify valid inputs + alloc_result check_args_valid(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user, + bool is_dl) const; + + /// Fill DCI context of allocated PDCCH + void fill_dci_ctx_common(srsran_dci_ctx_t& dci, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + const srsran_search_space_t& ss, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* ue); + + // args + const bwp_params_t& bwp_cfg; + srslog::basic_logger& logger; + const uint32_t slot_idx; + + pdcch_dl_list_t& pdcch_dl_list; + pdcch_ul_list_t& pdcch_ul_list; + slot_coreset_list coresets; + const srsran_dci_ctx_t* pending_dci = nullptr; /// Saves last PDCCH allocation, in case it needs to be aborted +}; + +} // namespace sched_nr_impl + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_PDCCH_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_rb.h b/srsgnb/hdr/stack/mac/sched_nr_rb.h new file mode 100644 index 000000000..47292e20b --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_rb.h @@ -0,0 +1,296 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_RB_H +#define SRSRAN_SCHED_NR_RB_H + +#include "srsgnb/hdr/stack/mac/sched_nr_interface.h" +#include "srsran/adt/bounded_bitset.h" +#include "srsran/phy/common/phy_common_nr.h" + +namespace srsenb { +namespace sched_nr_impl { + +using prb_bitmap = srsran::bounded_bitset; +using rbg_bitmap = srsran::bounded_bitset; + +/// TS 38.214, Table 6.1.2.2.1-1 - Nominal RBG size P +uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2); + +/// TS 38.214 - total number of RBGs for a uplink bandwidth part of size "bwp_nof_prb" PRBs +uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2); + +/// Struct to express a {min,...,max} range of PRBs +struct prb_interval : public srsran::interval { + using interval::interval; +}; + +struct prb_grant { + prb_grant() = default; + prb_grant(const prb_interval& other) noexcept : alloc_type_0(false), alloc(other) {} + prb_grant(const rbg_bitmap& other) noexcept : alloc_type_0(true), alloc(other) {} + prb_grant(const prb_grant& other) noexcept : alloc_type_0(other.alloc_type_0), alloc(other.alloc_type_0, other.alloc) + {} + prb_grant& operator=(const prb_grant& other) noexcept + { + if (this == &other) { + return *this; + } + if (other.alloc_type_0) { + *this = other.rbgs(); + } else { + *this = other.prbs(); + } + return *this; + } + prb_grant& operator=(const prb_interval& prbs) + { + if (alloc_type_0) { + alloc_type_0 = false; + alloc.rbgs.~rbg_bitmap(); + new (&alloc.interv) prb_interval(prbs); + } else { + alloc.interv = prbs; + } + return *this; + } + prb_grant& operator=(const rbg_bitmap& rbgs) + { + if (alloc_type_0) { + alloc.rbgs = rbgs; + } else { + alloc_type_0 = true; + alloc.interv.~prb_interval(); + new (&alloc.rbgs) rbg_bitmap(rbgs); + } + return *this; + } + ~prb_grant() + { + if (is_alloc_type0()) { + alloc.rbgs.~rbg_bitmap(); + } else { + alloc.interv.~prb_interval(); + } + } + + bool is_alloc_type0() const { return alloc_type_0; } + bool is_alloc_type1() const { return not is_alloc_type0(); } + const rbg_bitmap& rbgs() const + { + srsran_assert(is_alloc_type0(), "Invalid access to rbgs() field of grant with alloc type 1"); + return alloc.rbgs; + } + const prb_interval& prbs() const + { + srsran_assert(is_alloc_type1(), "Invalid access to prbs() field of grant with alloc type 0"); + return alloc.interv; + } + rbg_bitmap& rbgs() + { + srsran_assert(is_alloc_type0(), "Invalid access to rbgs() field of grant with alloc type 1"); + return alloc.rbgs; + } + prb_interval& prbs() + { + srsran_assert(is_alloc_type1(), "Invalid access to prbs() field of grant with alloc type 0"); + return alloc.interv; + } + + prb_grant& operator&=(const prb_interval interv) + { + if (is_alloc_type0()) { + alloc.rbgs &= rbg_bitmap{alloc.rbgs.size()}.fill(interv.start(), interv.stop()); + } else { + alloc.interv.intersect(interv); + } + return *this; + } + +private: + bool alloc_type_0 = false; + union alloc_t { + rbg_bitmap rbgs; + prb_interval interv; + + alloc_t() : interv(0, 0) {} + explicit alloc_t(const prb_interval& prbs) : interv(prbs) {} + explicit alloc_t(const rbg_bitmap& rbgs_) : rbgs(rbgs_) {} + alloc_t(bool type0, const alloc_t& other) + { + if (type0) { + new (&rbgs) rbg_bitmap(other.rbgs); + } else { + new (&interv) prb_interval(other.interv); + } + } + } alloc; +}; + +struct bwp_rb_bitmap { +public: + bwp_rb_bitmap() = default; + bwp_rb_bitmap(uint32_t bwp_nof_prbs, uint32_t bwp_prb_start_, bool config1_or_2); + + void reset() + { + prbs_.reset(); + rbgs_.reset(); + } + + template + void operator|=(const T& grant) + { + add(grant); + } + + void add(const prb_interval& prbs) + { + prbs_.fill(prbs.start(), prbs.stop()); + add_prbs_to_rbgs(prbs); + } + void add(const prb_bitmap& grant) + { + prbs_ |= grant; + add_prbs_to_rbgs(grant); + } + void add(const rbg_bitmap& grant) + { + rbgs_ |= grant; + add_rbgs_to_prbs(grant); + } + void add(const prb_grant& grant) + { + if (grant.is_alloc_type0()) { + add(grant.rbgs()); + } else { + add(grant.prbs()); + } + } + bool collides(const prb_grant& grant) const + { + if (grant.is_alloc_type0()) { + return (rbgs() & grant.rbgs()).any(); + } + return prbs().any(grant.prbs().start(), grant.prbs().stop()); + } + bool test(uint32_t prb_idx) { return prbs().test(prb_idx); } + void set(uint32_t prb_idx) + { + prbs_.set(prb_idx); + rbgs_.set(prb_to_rbg_idx(prb_idx)); + } + + const prb_bitmap& prbs() const { return prbs_; } + const rbg_bitmap& rbgs() const { return rbgs_; } + uint32_t P() const { return P_; } + uint32_t nof_prbs() const { return prbs_.size(); } + uint32_t nof_rbgs() const { return rbgs_.size(); } + + uint32_t prb_to_rbg_idx(uint32_t prb_idx) const; + + bwp_rb_bitmap& operator|=(const bwp_rb_bitmap& other) + { + prbs_ |= other.prbs_; + rbgs_ |= other.rbgs_; + return *this; + } + bwp_rb_bitmap& operator|=(const rbg_bitmap& other) + { + add(other); + return *this; + } + bwp_rb_bitmap& operator|=(const prb_bitmap& other) + { + add(other); + return *this; + } + +private: + prb_bitmap prbs_; + rbg_bitmap rbgs_; + uint32_t P_ = 0; + uint32_t Pnofbits = 0; + uint32_t first_rbg_size = 0; + + void add_prbs_to_rbgs(const prb_bitmap& grant); + void add_prbs_to_rbgs(const prb_interval& grant); + void add_rbgs_to_prbs(const rbg_bitmap& grant); +}; + +template +bwp_rb_bitmap operator|(const bwp_rb_bitmap& lhs, const Other& rhs) +{ + return bwp_rb_bitmap(lhs) |= rhs; +} + +inline prb_interval +find_next_empty_interval(const prb_bitmap& mask, size_t start_prb_idx = 0, size_t last_prb_idx = SRSRAN_MAX_PRB_NR) +{ + int rb_start = mask.find_lowest(start_prb_idx, std::min(mask.size(), last_prb_idx), false); + if (rb_start != -1) { + int rb_end = mask.find_lowest(rb_start + 1, std::min(mask.size(), last_prb_idx), true); + return {(uint32_t)rb_start, (uint32_t)(rb_end < 0 ? mask.size() : rb_end)}; + } + return {}; +} + +inline prb_interval find_empty_interval_of_length(const prb_bitmap& mask, size_t nof_prbs, uint32_t start_prb_idx = 0) +{ + prb_interval max_interv; + do { + prb_interval interv = find_next_empty_interval(mask, start_prb_idx, mask.size()); + if (interv.empty()) { + break; + } + if (interv.length() >= nof_prbs) { + max_interv.set(interv.start(), interv.start() + nof_prbs); + break; + } + if (interv.length() > max_interv.length()) { + max_interv = interv; + } + start_prb_idx = interv.stop() + 1; + } while (start_prb_idx < mask.size()); + return max_interv; +} + +} // namespace sched_nr_impl +} // namespace srsenb + +namespace fmt { + +template <> +struct formatter : public formatter { + template + auto format(const srsenb::sched_nr_impl::prb_grant& grant, FormatContext& ctx) + -> decltype(std::declval().out()) + { + if (grant.is_alloc_type1()) { + return formatter >{}.format(grant.prbs(), ctx); + } + return formatter::format(grant.rbgs(), ctx); + } +}; + +} // namespace fmt + +#endif // SRSRAN_SCHED_NR_RB_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_sch.h b/srsgnb/hdr/stack/mac/sched_nr_sch.h new file mode 100644 index 000000000..4a518d220 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_sch.h @@ -0,0 +1,193 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_SCH_H +#define SRSRAN_SCHED_NR_SCH_H + +#include "srsenb/hdr/stack/mac/sched_common.h" +#include "srsgnb/hdr/stack/mac/sched_nr_cfg.h" +#include "srsgnb/hdr/stack/mac/sched_ue/ue_cfg_manager.h" + +namespace srsenb { + +namespace sched_nr_impl { + +using pdsch_alloc_result = srsran::expected; + +class pdsch_allocator +{ +public: + pdsch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pdsch_list_t& pdsch_lst); + + /// Get available RBGs for allocation + rbg_bitmap occupied_rbgs() const + { + // Note: in case, RBGs are used, dci format is not 1_0 + return dl_prbs.rbgs(); + } + /// Get available PRBs for allocation + prb_bitmap occupied_prbs(uint32_t ss_id, srsran_dci_format_nr_t dci_fmt) const + { + if (dci_fmt == srsran_dci_format_nr_1_0) { + const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id); + if (ss != nullptr and SRSRAN_SEARCH_SPACE_IS_COMMON(ss->type)) { + return (dl_prbs | bwp_cfg.dci_fmt_1_0_excluded_prbs(ss->coreset_id)).prbs(); + } + } + return dl_prbs.prbs(); + } + + /// Marks a range of PRBS as occupied, preventing further allocations + void reserve_prbs(const prb_grant& grant) { dl_prbs |= grant; } + + /// Verifies if the input arguments are valid for an SI allocation and grant doesnt collide with other grants + alloc_result is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const; + + /// Verifies if the input arguments are valid for an RAR allocation and grant doesnt collide with other grants + alloc_result is_rar_grant_valid(const prb_grant& grant) const; + + /// Verifies if the input arguments are valid for an UE allocation and grant doesnt collide with other grants + alloc_result is_ue_grant_valid(const ue_carrier_params_t& ue, + uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant) const; + + /** + * @brief Tries to allocate UE PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations + * @param ss_id[in] SearchSpaceId used for allocation + * @param dci_fmt[in] Chosen DL DCI format + * @param grant[in] PRBs used for the grant + * @param ue[in] UE carrier parameters + * @param dci[out] DCI where frequency_assignment and time_assignment get stored. + * @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure + */ + pdsch_alloc_result alloc_ue_pdsch(uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + const ue_carrier_params_t& ue, + srsran_dci_dl_nr_t& dci); + + /// Similar to alloc_ue_pdsch, but it doesn't verify if input parameters are valid + pdsch_t& alloc_ue_pdsch_unchecked(uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + const ue_carrier_params_t& ue, + srsran_dci_dl_nr_t& dci); + + /** + * @brief Tries to allocate SI PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations + * @param ss_id[in] SearchSpaceId used for allocation + * @param grant[in] PRBs used for the grant + * @param dci[out] DCI where frequency_assignment and time_assignment get stored. + * @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure + */ + pdsch_alloc_result alloc_si_pdsch(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci); + /// Similar to alloc_si_pdsch, but it doesn't verify if input parameters are valid + pdsch_t& alloc_si_pdsch_unchecked(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci); + + /** + * @brief Tries to allocate RAR PDSCH grant. Ensures that there are no collisions with other previous PDSCH + * allocations + * @param grant[in] PRBs used for the grant + * @param dci[out] DCI where frequency_assignment and time_assignment get stored. + * @return pdsch_t* of allocated PDSCH in case of success. alloc_result error code in case of failure + */ + pdsch_alloc_result alloc_rar_pdsch(const prb_grant& grant, srsran_dci_dl_nr_t& dci); + /// Similar to alloc_rar_pdsch, but it doesn't verify if input parameters are valid + pdsch_t& alloc_rar_pdsch_unchecked(const prb_grant& grant, srsran_dci_dl_nr_t& dci); + + /// Cancel last PDSCH allocation + void cancel_last_pdsch(); + + /// Clear all PDSCHs + void reset(); + +private: + alloc_result is_grant_valid_common(srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t dci_fmt, + uint32_t coreset_id, + const prb_grant& grant) const; + pdsch_t& alloc_pdsch_unchecked(uint32_t coreset_id, + srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + srsran_dci_dl_nr_t& dci); + + const bwp_params_t& bwp_cfg; + uint32_t slot_idx = 0; + + pdsch_list_t& pdschs; + bwp_rb_bitmap dl_prbs; +}; + +using pusch_alloc_result = srsran::expected; + +class pusch_allocator +{ +public: + pusch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pusch_list_t& pusch_lst); + + /// Get available RBGs for allocation + const rbg_bitmap& occupied_rbgs() const { return ul_prbs.rbgs(); } + /// Get available PRBs for allocation + const prb_bitmap& occupied_prbs() const { return ul_prbs.prbs(); } + + alloc_result has_grant_space(uint32_t nof_grants = 1, bool verbose = true) const; + + /// Checks if provided PDSCH arguments produce a valid PDSCH that fits into cell PRBs and does not collide with other + /// allocations + alloc_result is_grant_valid(srsran_search_space_type_t ss_type, const prb_grant& grant, bool verbose = true) const; + + /** + * @brief Tries to allocate PDSCH grant. Ensures that there are no collisions with other previous PDSCH allocations + * @param ss_type[in] PDCCH chosen search space type + * @param grant[in] PRBs used for the grant + * @param pdcch[out] DCI where frequency_assignment and time_assignment get stored. + * @return pdsch_t object pointer in case of success. alloc_result error code in case of failure + */ + pusch_alloc_result + alloc_pusch(const srsran_search_space_type_t ss_type, const prb_grant& grant, srsran_dci_ul_nr_t& dci); + + /** + * @brief Allocates PDSCH grant without verifying for collisions. Useful to avoid redundant is_grant_valid(...) calls + * @param dci_ctx[in] PDCCH DL DCI context information + * @param grant[in] PRBs used for the grant + * @param pdcch[out] DCI where frequency and time assignment get stored. + */ + pusch_t& alloc_pusch_unchecked(const prb_grant& grant, srsran_dci_ul_nr_t& dci); + + void cancel_last_pusch(); + + void reset(); + +private: + const bwp_params_t& bwp_cfg; + uint32_t slot_idx = 0; + + pusch_list_t& puschs; + bwp_rb_bitmap ul_prbs; +}; + +} // namespace sched_nr_impl + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_SCH_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_signalling.h b/srsgnb/hdr/stack/mac/sched_nr_signalling.h new file mode 100644 index 000000000..5520bb280 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_signalling.h @@ -0,0 +1,98 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_SIGNALLING_H +#define SRSRAN_SCHED_NR_SIGNALLING_H + +#include "harq_softbuffer.h" +#include "sched_nr_cfg.h" +#include "sched_nr_interface.h" +#include "srsenb/hdr/stack/mac/sched_common.h" + +namespace srsenb { +namespace sched_nr_impl { + +class bwp_slot_allocator; + +/// Schedule NZP-CSI-RS resources for given slot +void sched_nzp_csi_rs(srsran::const_span nzp_csi_rs_sets, + const srsran_slot_cfg_t& slot_cfg, + nzp_csi_rs_list& csi_rs_list); + +/** + * @brief Schedule grant for SSB. + * + * The functions schedules the SSB according to a given periodicity. This function is a simplified version of an + * SSB scheduler and has several hard-coded parameters. + * + * @param[in] sl_point Slot point carrying information about current slot. + * @param[in] ssb_periodicity Periodicity of SSB in ms. + * @param[in] mib MIB message content + * @param[out] ssb_list List of SSB messages to be sent to PHY. + * + * @remark This function a is basic scheduling function that uses the following simplified assumption: + * 1) Subcarrier spacing: 15kHz + * 2) Frequency below 3GHz + * 3) Position in Burst is 1000, i.e., Only the first SSB of the 4 opportunities gets scheduled + */ +void sched_ssb_basic(const slot_point& sl_point, + uint32_t ssb_periodicity, + const srsran_mib_nr_t& mib, + ssb_list& ssb_list); + +/// Fill DCI fields with SIB info +bool fill_dci_sib(prb_interval interv, uint32_t sib_idx, const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci); + +/// For a given BWP and slot, schedule SSB, NZP CSI RS and SIBs +void sched_dl_signalling(bwp_slot_allocator& bwp_alloc); + +/// scheduler for SIBs +class si_sched +{ +public: + explicit si_sched(const bwp_params_t& bwp_cfg_); + + void run_slot(bwp_slot_allocator& slot_alloc); + +private: + const bwp_params_t* bwp_cfg = nullptr; + srslog::basic_logger& logger; + + struct si_msg_ctxt_t { + // args + uint32_t n = 0; /// 0 for SIB1, n/index in schedulingInfoList in si-SchedulingInfo in SIB1 + uint32_t len_bytes = 0; /// length in bytes of SIB1 / SI message + uint32_t win_len_slots = 0; /// window length in slots + uint32_t period_frames = 0; /// periodicity of SIB1/SI window in frames + + // state + uint32_t n_tx = 0; /// nof transmissions of the same SIB1 / SI message + alloc_result result = alloc_result::invalid_coderate; /// last attempt to schedule SI + slot_point win_start; /// start of SI window, invalid if outside + srsran::unique_pool_ptr si_softbuffer; + }; + srsran::bounded_vector pending_sis; /// configured SIB1 and SI messages +}; + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_SIGNALLING_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_time_rr.h b/srsgnb/hdr/stack/mac/sched_nr_time_rr.h new file mode 100644 index 000000000..787a4915d --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_time_rr.h @@ -0,0 +1,56 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_TIME_RR_H +#define SRSRAN_SCHED_NR_TIME_RR_H + +#include "sched_nr_grant_allocator.h" +#include "srsran/common/slot_point.h" + +namespace srsenb { +namespace sched_nr_impl { + +/** + * Base class for scheduler algorithms implementations + */ +class sched_nr_base +{ +public: + virtual ~sched_nr_base() = default; + + virtual void sched_dl_users(slot_ue_map_t& ue_db, bwp_slot_allocator& slot_alloc) = 0; + virtual void sched_ul_users(slot_ue_map_t& ue_db, bwp_slot_allocator& slot_alloc) = 0; + +protected: + srslog::basic_logger& logger = srslog::fetch_basic_logger("MAC"); +}; + +class sched_nr_time_rr : public sched_nr_base +{ +public: + void sched_dl_users(slot_ue_map_t& ue_db, bwp_slot_allocator& slot_alloc) override; + void sched_ul_users(slot_ue_map_t& ue_db, bwp_slot_allocator& slot_alloc) override; +}; + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_TIME_RR_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_ue.h b/srsgnb/hdr/stack/mac/sched_nr_ue.h new file mode 100644 index 000000000..44f670022 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_ue.h @@ -0,0 +1,229 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_UE_H +#define SRSRAN_SCHED_NR_UE_H + +#include "sched_nr_cfg.h" +#include "sched_nr_harq.h" +#include "sched_nr_interface.h" +#include "sched_ue/ue_cfg_manager.h" +#include "srsenb/hdr/stack/mac/common/base_ue_buffer_manager.h" +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" +#include "srsran/adt/circular_map.h" +#include "srsran/adt/move_callback.h" +#include "srsran/adt/pool/cached_alloc.h" +#include "srsran/adt/pool/pool_interface.h" + +namespace srsenb { + +namespace sched_nr_impl { + +class ue_buffer_manager : public base_ue_buffer_manager +{ + using base_type = base_ue_buffer_manager; + +public: + // Inherited methods from base_ue_buffer_manager base class + using base_type::base_type; + using base_type::config_lcid; + using base_type::dl_buffer_state; + using base_type::get_bsr; + using base_type::get_bsr_state; + using base_type::get_dl_prio_tx; + using base_type::get_dl_tx; + using base_type::get_dl_tx_total; + using base_type::is_bearer_active; + using base_type::is_bearer_dl; + using base_type::is_bearer_ul; + using base_type::is_lcg_active; + using base_type::ul_bsr; + + int get_dl_tx_total() const; + + // Control Element Command queue + struct ce_t { + uint32_t lcid; + uint32_t cc; + }; + srsran::deque pending_ces; + + /// Protected, thread-safe interface of "ue_buffer_manager" for "slot_ue" + struct pdu_builder { + pdu_builder() = default; + explicit pdu_builder(uint32_t cc_, ue_buffer_manager& parent_) : cc(cc_), parent(&parent_) {} + bool alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu); + uint32_t pending_bytes(uint32_t lcid) const { return parent->get_dl_tx(lcid); } + + private: + uint32_t cc = SRSRAN_MAX_CARRIERS; + ue_buffer_manager* parent = nullptr; + }; +}; + +/// Class containing context of UE that is common to all carriers +struct ue_context_common { + uint32_t pending_dl_bytes = 0; + uint32_t pending_ul_bytes = 0; +}; + +class slot_ue; + +class ue_carrier +{ +public: + ue_carrier(uint16_t rnti, + const ue_cfg_manager& cfg, + const cell_config_manager& cell_params_, + const ue_context_common& ctxt, + const ue_buffer_manager::pdu_builder& pdu_builder_); + + void set_cfg(const ue_cfg_manager& ue_cfg); + const ue_carrier_params_t& cfg() const { return bwp_cfg; } + + int dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack); + int ul_crc_info(uint32_t pid, bool crc); + + const uint16_t rnti; + const uint32_t cc; + const cell_config_manager& cell_params; + + // Channel state + uint32_t dl_cqi = 1; + uint32_t ul_cqi = 0; + + harq_entity harq_ent; + + ue_buffer_manager::pdu_builder pdu_builder; + + // metrics + mac_ue_metrics_t metrics = {}; + + // common context + const ue_context_common& common_ctxt; + +private: + friend class slot_ue; + + srslog::basic_logger& logger; + ue_carrier_params_t bwp_cfg; +}; + +class ue +{ +public: + ue(uint16_t rnti, uint32_t cc, const sched_params_t& sched_cfg_); + ue(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg, const sched_params_t& sched_cfg_); + + void new_slot(slot_point pdcch_slot); + + slot_ue make_slot_ue(slot_point pdcch_slot, uint32_t cc); + + /// Update UE CC configuration + void set_cfg(const sched_nr_ue_cfg_t& cfg); + const ue_cfg_manager& cfg() const { return ue_cfg; } + + void add_dl_mac_ce(uint32_t ce_lcid, uint32_t nof_cmds = 1); + void rlc_buffer_state(uint32_t lcid, uint32_t newtx, uint32_t retx); + + /// UE state feedback + void ul_bsr(uint32_t lcg, uint32_t bsr_val) { buffers.ul_bsr(lcg, bsr_val); } + void ul_sr_info() { last_sr_slot = last_tx_slot - TX_ENB_DELAY; } + + bool has_ca() const + { + return ue_cfg.carriers.size() > 1 and std::count_if(ue_cfg.carriers.begin() + 1, + ue_cfg.carriers.end(), + [](const ue_cc_cfg_t& cc) { return cc.active; }) > 0; + } + uint32_t pcell_cc() const { return ue_cfg.carriers[0].cc; } + + std::array, SCHED_NR_MAX_CARRIERS> carriers; + + const uint16_t rnti; + +private: + const sched_params_t& sched_cfg; + + ue_cfg_manager ue_cfg; + + slot_point last_tx_slot; + slot_point last_sr_slot; + ue_context_common common_ctxt; + + ue_buffer_manager buffers; +}; + +class slot_ue +{ +public: + slot_ue() = default; + explicit slot_ue(ue_carrier& ue, slot_point slot_tx_); + slot_ue(slot_ue&&) noexcept = default; + slot_ue& operator=(slot_ue&&) noexcept = default; + bool empty() const { return ue == nullptr; } + void release() { ue = nullptr; } + + const ue_carrier_params_t& cfg() const { return ue->bwp_cfg; } + const ue_carrier_params_t* operator->() const { return &ue->bwp_cfg; } + + /// Find available HARQs + dl_harq_proc* find_empty_dl_harq() { return ue->harq_ent.find_empty_dl_harq(); } + ul_harq_proc* find_empty_ul_harq() { return ue->harq_ent.find_empty_ul_harq(); } + + /// Build PDU with MAC CEs and MAC SDUs + bool build_pdu(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu, bool reset_buf_states = false) + { + return ue->pdu_builder.alloc_subpdus(rem_bytes, pdu); + } + + bool get_pending_bytes(uint32_t lcid) const { return ue->pdu_builder.pending_bytes(lcid); } + + /// Channel Information Getters + uint32_t dl_cqi() const { return ue->dl_cqi; } + uint32_t ul_cqi() const { return ue->ul_cqi; } + + // UE parameters common to all sectors + uint32_t dl_bytes = 0, ul_bytes = 0; + + // UE parameters that are sector specific + bool dl_active; + bool ul_active; + slot_point pdcch_slot; + slot_point pdsch_slot; + slot_point pusch_slot; + slot_point uci_slot; + dl_harq_proc* h_dl = nullptr; + ul_harq_proc* h_ul = nullptr; + +private: + ue_carrier* ue = nullptr; +}; + +using unique_ue_ptr = srsran::unique_pool_ptr; +using ue_map_t = rnti_map_t; +using slot_ue_map_t = rnti_map_t; + +} // namespace sched_nr_impl + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_UE_H diff --git a/srsgnb/hdr/stack/mac/sched_nr_worker.h b/srsgnb/hdr/stack/mac/sched_nr_worker.h new file mode 100644 index 000000000..05519a0d2 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_nr_worker.h @@ -0,0 +1,73 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_WORKER_H +#define SRSRAN_SCHED_NR_WORKER_H + +#include "sched_nr_bwp.h" +#include "sched_nr_cfg.h" +#include "sched_nr_grant_allocator.h" +#include "sched_nr_ue.h" +#include "srsran/adt/circular_array.h" +#include "srsran/adt/optional.h" +#include "srsran/adt/pool/cached_alloc.h" +#include "srsran/adt/span.h" +#include +#include + +namespace srsenb { + +struct mac_metrics_t; + +namespace sched_nr_impl { + +class cc_worker +{ +public: + explicit cc_worker(const cell_config_manager& params); + + void dl_rach_info(const sched_nr_interface::rar_info_t& rar_info); + + dl_sched_res_t* run_slot(slot_point pdcch_slot, ue_map_t& ue_db_); + ul_sched_t* get_ul_sched(slot_point sl); + + // const params + const cell_config_manager& cfg; + srslog::basic_logger& logger; + + // cc-specific resources + srsran::bounded_vector bwps; + +private: + void alloc_dl_ues(bwp_slot_allocator& bwp_alloc); + void alloc_ul_ues(bwp_slot_allocator& bwp_alloc); + void postprocess_decisions(bwp_slot_allocator& bwp_alloc); + + // {slot,cc} specific variables + slot_ue_map_t slot_ues; + + slot_point last_tx_sl; +}; + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_WORKER_H diff --git a/srsgnb/hdr/stack/mac/sched_ue/ue_cfg_manager.h b/srsgnb/hdr/stack/mac/sched_ue/ue_cfg_manager.h new file mode 100644 index 000000000..ff33ca4c5 --- /dev/null +++ b/srsgnb/hdr/stack/mac/sched_ue/ue_cfg_manager.h @@ -0,0 +1,107 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_UE_CFG_MANAGER_H +#define SRSRAN_UE_CFG_MANAGER_H + +#include "../sched_nr_cfg.h" + +namespace srsenb { +namespace sched_nr_impl { + +using ue_cc_cfg_list = srsran::bounded_vector; + +struct ue_cfg_manager { + uint32_t maxharq_tx = 4; + ue_cc_cfg_list carriers; + std::array ue_bearers = {}; + srsran::phy_cfg_nr_t phy_cfg = {}; + + explicit ue_cfg_manager(uint32_t enb_cc_idx = 0); + explicit ue_cfg_manager(const sched_nr_ue_cfg_t& cfg_req); + int apply_config_request(const sched_nr_ue_cfg_t& cfg_req); +}; + +/// Semi-static configuration of a UE for a given CC. +class ue_carrier_params_t +{ +public: + ue_carrier_params_t() = default; + explicit ue_carrier_params_t(uint16_t rnti, const bwp_params_t& active_bwp_cfg, const ue_cfg_manager& uecfg_); + + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t cc = SRSRAN_MAX_CARRIERS; + + const ue_cfg_manager& ue_cfg() const { return *cfg_; } + const srsran::phy_cfg_nr_t& phy() const { return cfg_->phy_cfg; } + const bwp_params_t& active_bwp() const { return *bwp_cfg; } + + /// Get SearchSpace based on SearchSpaceId + const srsran_search_space_t* get_ss(uint32_t ss_id) const + { + if (phy().pdcch.search_space_present[ss_id]) { + // UE-dedicated SearchSpace + return &bwp_cfg->cfg.pdcch.search_space[ss_id]; + } + return nullptr; + } + + srsran::const_span cce_pos_list(uint32_t search_id, uint32_t slot_idx, uint32_t aggr_idx) const + { + if (cce_positions_list.size() > ss_id_to_cce_idx[search_id]) { + auto& lst = cce_pos_list(search_id); + return lst[slot_idx][aggr_idx]; + } + return srsran::const_span{}; + } + const bwp_cce_pos_list& cce_pos_list(uint32_t search_id) const + { + return cce_positions_list[ss_id_to_cce_idx[search_id]]; + } + + uint32_t get_k1(slot_point pdsch_slot) const + { + if (phy().duplex.mode == SRSRAN_DUPLEX_MODE_TDD) { + return phy().harq_ack.dl_data_to_ul_ack[pdsch_slot.to_uint() % phy().duplex.tdd.pattern1.period_ms]; + } + return phy().harq_ack.dl_data_to_ul_ack[pdsch_slot.to_uint() % phy().harq_ack.nof_dl_data_to_ul_ack]; + } + int fixed_pdsch_mcs() const { return bwp_cfg->sched_cfg.fixed_dl_mcs; } + int fixed_pusch_mcs() const { return bwp_cfg->sched_cfg.fixed_ul_mcs; } + + const srsran_dci_cfg_nr_t& get_dci_cfg() const { return cached_dci_cfg; } + + int find_ss_id(srsran_dci_format_nr_t dci_fmt) const; + +private: + const ue_cfg_manager* cfg_ = nullptr; + const bwp_params_t* bwp_cfg = nullptr; + + // derived + std::vector cce_positions_list; + std::array ss_id_to_cce_idx; + srsran_dci_cfg_nr_t cached_dci_cfg; +}; + +} // namespace sched_nr_impl +} // namespace srsenb + +#endif // SRSRAN_UE_CFG_MANAGER_H diff --git a/srsgnb/hdr/stack/mac/ue_nr.h b/srsgnb/hdr/stack/mac/ue_nr.h new file mode 100644 index 000000000..a8662c818 --- /dev/null +++ b/srsgnb/hdr/stack/mac/ue_nr.h @@ -0,0 +1,122 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_UE_NR_H +#define SRSENB_UE_NR_H + +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" +#include "srsgnb/hdr/stack/mac/sched_nr_interface.h" +#include "srsran/common/block_queue.h" +#include "srsran/common/interfaces_common.h" +#include "srsran/interfaces/enb_rlc_interfaces.h" +#include "srsran/mac/bsr_nr.h" +#include "srsran/mac/mac_sch_pdu_nr.h" +#include +#include + +namespace srsenb { + +class rrc_interface_mac_nr; +class rlc_interface_mac_nr; +class phy_interface_stack_nr; + +class ue_nr : public srsran::read_pdu_interface +{ +public: + ue_nr(uint16_t rnti, + uint32_t enb_cc_idx, + sched_nr_interface* sched_, + rrc_interface_mac_nr* rrc_, + rlc_interface_mac* rlc, + phy_interface_stack_nr* phy_, + srslog::basic_logger& logger); + + virtual ~ue_nr(); + void reset(); + void ue_cfg(const sched_nr_interface::ue_cfg_t& ue_cfg); + + void set_tti(uint32_t tti); + uint16_t get_rnti() const { return rnti; } + void set_active(bool active) { active_state.store(active, std::memory_order_relaxed); } + bool is_active() const { return active_state.load(std::memory_order_relaxed); } + void store_msg3(srsran::unique_byte_buffer_t pdu); + + int generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size, srsran::const_span subpdu_lcids); + + std::mutex metrics_mutex = {}; + void metrics_read(mac_ue_metrics_t* metrics_); + void metrics_rx(bool crc, uint32_t tbs); + void metrics_tx(bool crc, uint32_t tbs); + void metrics_phr(float phr); + void metrics_dl_ri(uint32_t dl_cqi); + void metrics_dl_pmi(uint32_t dl_cqi); + void metrics_dl_cqi(const srsran_uci_cfg_nr_t& cfg_, uint32_t dl_cqi); + void metrics_dl_mcs(uint32_t mcs); + void metrics_ul_mcs(uint32_t mcs); + void metrics_pucch_sinr(float sinr); + void metrics_pusch_sinr(float sinr); + void metrics_cnt(); + + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) final; + +private: + rlc_interface_mac* rlc = nullptr; + rrc_interface_mac_nr* rrc = nullptr; + phy_interface_stack_nr* phy = nullptr; + srslog::basic_logger& logger; + sched_nr_interface* sched = nullptr; + + uint64_t conres_id = 0; + uint16_t rnti = 0; + uint32_t last_tti = 0; + uint32_t nof_failures = 0; + + std::atomic active_state{true}; + + // TODO: some counters are kept as members of class ue_nr, while some others (i.e., mcs) are kept in the ue_metrics + // We should make these counters more uniform + uint32_t phr_counter = 0; + uint32_t dl_cqi_valid_counter = 0; + uint32_t dl_ri_counter = 0; + uint32_t dl_pmi_counter = 0; + uint32_t pucch_sinr_counter = 0; + uint32_t pusch_sinr_counter = 0; + mac_ue_metrics_t ue_metrics = {}; + + // UE-specific buffer for MAC PDU packing, unpacking and handling + srsran::mac_sch_pdu_nr mac_pdu_dl, mac_pdu_ul; + std::vector ue_tx_buffer; + srsran::block_queue + ue_rx_pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc) + srsran::unique_byte_buffer_t ue_rlc_buffer; + + srsran::unique_byte_buffer_t last_msg3; ///< holds UE ID received in Msg3 for ConRes CE + + static constexpr int32_t MIN_RLC_PDU_LEN = + 5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU + + // Mutexes + std::mutex mutex; +}; + +} // namespace srsenb + +#endif // SRSENB_UE_NR_H diff --git a/srsgnb/hdr/stack/ngap/ngap.h b/srsgnb/hdr/stack/ngap/ngap.h new file mode 100644 index 000000000..8f74c51bb --- /dev/null +++ b/srsgnb/hdr/stack/ngap/ngap.h @@ -0,0 +1,211 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSENB_NGAP_H +#define SRSENB_NGAP_H + +#include "ngap_metrics.h" +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/adt/circular_map.h" +#include "srsran/adt/optional.h" +#include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/ngap.h" +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/network_utils.h" +#include "srsran/common/ngap_pcap.h" +#include "srsran/common/stack_procedure.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/common/threads.h" +#include "srsran/interfaces/enb_gtpu_interfaces.h" +#include "srsran/interfaces/gnb_ngap_interfaces.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" +#include "srsran/srslog/srslog.h" +#include +#include + +namespace srsenb { + +class ngap final : public ngap_interface_rrc_nr +{ +public: + class ue; + ngap(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler); + ~ngap(); + int init(const ngap_args_t& args_, rrc_interface_ngap_nr* rrc_, gtpu_interface_rrc* gtpu_); + void stop(); + + // RRC NR interface + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu) override; + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu, + uint32_t s_tmsi) override; + + void write_pdu(uint16_t rnti, srsran::const_byte_span pdu) override; + bool user_exists(uint16_t rnti) override { return true; }; + void user_mod(uint16_t old_rnti, uint16_t new_rnti) override {} + + // TS 38.413 - Section 8.3.2 - UE Context Release Request + void user_release_request(uint16_t rnti, asn1::ngap::cause_radio_network_e cause_radio) override; + + bool is_amf_connected() override; + bool send_error_indication(const asn1::ngap::cause_c& cause, + srsran::optional ran_ue_ngap_id = {}, + srsran::optional amf_ue_ngap_id = {}); + void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) override; + bool send_pdu_session_resource_setup_response(); + + // Stack interface + bool + handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); + void get_metrics(ngap_metrics_t& m); + void get_args(ngap_args_t& args_); + + // PCAP + void start_pcap(srsran::ngap_pcap* pcap_); + + // Logging + typedef enum { Rx = 0, Tx } direction_t; + void log_ngap_message(const asn1::ngap::ngap_pdu_c& msg, const direction_t dir, srsran::const_byte_span pdu); + +private: + static const int AMF_PORT = 38412; + static const int ADDR_FAMILY = AF_INET; + static const int SOCK_TYPE = SOCK_STREAM; + static const int PROTO = IPPROTO_SCTP; + static const int PPID = 60; + static const int NONUE_STREAM_ID = 0; + + // args + rrc_interface_ngap_nr* rrc = nullptr; + gtpu_interface_rrc* gtpu = nullptr; + ngap_args_t args = {}; + srslog::basic_logger& logger; + srsran::task_sched_handle task_sched; + srsran::task_queue_handle amf_task_queue; + srsran::socket_manager_itf* rx_socket_handler; + + srsran::unique_socket amf_socket; + struct sockaddr_in amf_addr = {}; // AMF address + bool amf_connected = false; + bool running = false; + uint32_t next_gnb_ue_ngap_id = 1; // Next GNB-side UE identifier + uint16_t next_ue_stream_id = 1; // Next UE SCTP stream identifier + srsran::unique_timer amf_connect_timer, ngsetup_timeout; + + // Protocol IEs sent with every UL NGAP message + asn1::ngap::tai_s tai; + asn1::ngap::nr_cgi_s nr_cgi; + + asn1::ngap::ng_setup_resp_s ngsetupresponse; + + int build_tai_cgi(); + bool connect_amf(); + bool setup_ng(); + bool sctp_send_ngap_pdu(const asn1::ngap::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name); + + bool handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu); + bool handle_successful_outcome(const asn1::ngap::successful_outcome_s& msg); + bool handle_unsuccessful_outcome(const asn1::ngap::unsuccessful_outcome_s& msg); + bool handle_initiating_message(const asn1::ngap::init_msg_s& msg); + + // TS 38.413 - Section 8.6.2 - Downlink NAS Transport + bool handle_dl_nas_transport(const asn1::ngap::dl_nas_transport_s& msg); + // TS 38.413 - Section 9.2.6.2 - NG Setup Response + bool handle_ng_setup_response(const asn1::ngap::ng_setup_resp_s& msg); + // TS 38.413 - Section 9.2.6.3 - NG Setup Failure + bool handle_ng_setup_failure(const asn1::ngap::ng_setup_fail_s& msg); + // TS 38.413 - Section 9.2.2.5 - UE Context Release Command + bool handle_ue_context_release_cmd(const asn1::ngap::ue_context_release_cmd_s& msg); + // TS 38.413 - Section 9.2.2.1 - Initial Context Setup Request + bool handle_initial_ctxt_setup_request(const asn1::ngap::init_context_setup_request_s& msg); + // TS 38.413 - Section 9.2.1.1 - PDU Session Resource Setup Request + bool handle_ue_pdu_session_res_setup_request(const asn1::ngap::pdu_session_res_setup_request_s& msg); + // TS 38.413 - Section 9.2.4.1 - Paging + bool handle_paging(const asn1::ngap::paging_s& msg); + + // PCAP + srsran::ngap_pcap* pcap = nullptr; + + class user_list + { + public: + using value_type = std::unique_ptr; + using iterator = std::unordered_map::iterator; + using const_iterator = std::unordered_map::const_iterator; + using pair_type = std::unordered_map::value_type; + + ue* find_ue_rnti(uint16_t rnti); + ue* find_ue_gnbid(uint32_t gnbid); + ue* find_ue_amfid(uint64_t amfid); + ue* add_user(value_type user); + void erase(ue* ue_ptr); + iterator begin() { return users.begin(); } + iterator end() { return users.end(); } + const_iterator cbegin() const { return users.begin(); } + const_iterator cend() const { return users.end(); } + size_t size() const { return users.size(); } + + private: + std::unordered_map > users; // maps ran_ue_ngap_id to user + }; + user_list users; + + // procedures + class ng_setup_proc_t + { + public: + struct ngsetupresult { + bool success = false; + enum class cause_t { timeout, failure } cause; + }; + + explicit ng_setup_proc_t(ngap* ngap_) : ngap_ptr(ngap_) {} + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + srsran::proc_outcome_t react(const ngsetupresult& event); + void then(const srsran::proc_state_t& result) const; + const char* name() const { return "AMF Connection"; } + + private: + srsran::proc_outcome_t start_amf_connection(); + + ngap* ngap_ptr = nullptr; + }; + + ue* handle_ngapmsg_ue_id(uint32_t gnb_id, uint64_t amf_id); + + srsran::proc_t ngsetup_proc; + + std::string get_cause(const asn1::ngap::cause_c& c); + void log_ngap_msg(const asn1::ngap::ngap_pdu_c& msg, srsran::const_span sdu, bool is_rx); +}; + +} // namespace srsenb +#endif diff --git a/srsgnb/hdr/stack/ngap/ngap_interfaces.h b/srsgnb/hdr/stack/ngap/ngap_interfaces.h new file mode 100644 index 000000000..b005eb75c --- /dev/null +++ b/srsgnb/hdr/stack/ngap/ngap_interfaces.h @@ -0,0 +1,40 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_NGAP_INTERFACES_H +#define SRSENB_NGAP_INTERFACES_H + +#include "srsran/asn1/ngap_utils.h" + +namespace srsenb { +class ngap_interface_ngap_proc +{ +public: + virtual bool send_initial_ctxt_setup_response() = 0; + virtual bool + send_pdu_session_resource_setup_response(uint16_t pdu_session_id, + uint32_t teid_out, + asn1::bounded_bitstring<1, 160, true, true> transport_layer_address) = 0; + virtual bool send_ue_ctxt_release_complete() = 0; +}; +} // namespace srsenb + +#endif \ No newline at end of file diff --git a/srsgnb/hdr/stack/ngap/ngap_metrics.h b/srsgnb/hdr/stack/ngap/ngap_metrics.h new file mode 100644 index 000000000..2a9185ca4 --- /dev/null +++ b/srsgnb/hdr/stack/ngap/ngap_metrics.h @@ -0,0 +1,39 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_NGAP_METRICS_H +#define SRSENB_NGAP_METRICS_H + +namespace srsenb { + +typedef enum { + ngap_attaching = 0, // Attempting to create NG connection + ngap_connected, // NG connected + ngap_error // Failure +} ngap_status_t; + +struct ngap_metrics_t { + ngap_status_t status; +}; + +} // namespace srsenb + +#endif // SRSENB_NGAP_METRICS_H diff --git a/srsgnb/hdr/stack/ngap/ngap_ue.h b/srsgnb/hdr/stack/ngap/ngap_ue.h new file mode 100644 index 000000000..a7499beb2 --- /dev/null +++ b/srsgnb/hdr/stack/ngap/ngap_ue.h @@ -0,0 +1,92 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSENB_NGAP_UE_H +#define SRSENB_NGAP_UE_H + +#include "ngap.h" +#include "ngap_ue_bearer_manager.h" +#include "ngap_ue_proc.h" +#include "ngap_ue_utils.h" +#include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/ngap.h" +#include "srsran/interfaces/enb_gtpu_interfaces.h" +namespace srsenb { + +class ngap::ue : public ngap_interface_ngap_proc +{ +public: + explicit ue(ngap* ngap_ptr_, + rrc_interface_ngap_nr* rrc_ptr_, + gtpu_interface_rrc* gtpu_ptr_, + srslog::basic_logger& logger_); + virtual ~ue(); + // TS 38.413 - Section 9.2.5.1 - Initial UE Message + bool send_initial_ue_message(asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu, + bool has_tmsi, + uint32_t s_tmsi = 0); + // TS 38.413 - Section 9.2.5.3 - Uplink NAS Transport + bool send_ul_nas_transport(srsran::const_byte_span pdu); + // TS 38.413 - Section 9.2.2.2 - Initial Context Setup Response + bool send_initial_ctxt_setup_response(); + // TS 38.413 - Section 9.2.2.3 - Initial Context Setup Failure + bool send_initial_ctxt_setup_failure(asn1::ngap::cause_c cause); + // TS 38.413 - Section 9.2.1.2 - PDU Session Resource Setup Response + bool send_pdu_session_resource_setup_response(uint16_t pdu_session_id, + uint32_t teid_in, + asn1::bounded_bitstring<1, 160, true, true> addr_in); + // TS 38.413 - Section 9.2.1.2 - UE Context Release Complete + bool send_ue_ctxt_release_complete(); + // TS 38.413 - Section 9.2.2.1 - Initial Context Setup Request + bool handle_initial_ctxt_setup_request(const asn1::ngap::init_context_setup_request_s& msg); + // TS 38.413 - Section 9.2.2.4 - UE Context Release Request + bool send_ue_context_release_request(asn1::ngap::cause_c cause); + // TS 38.413 - Section 9.2.2.5 - UE Context Release Command + bool handle_ue_context_release_cmd(const asn1::ngap::ue_context_release_cmd_s& msg); + // TS 38.413 - Section 9.2.1.1 - PDU Session Resource Setup Request + bool handle_pdu_session_res_setup_request(const asn1::ngap::pdu_session_res_setup_request_s& msg); + + /// Checks if a UE Context Release Request was already sent + bool was_ue_context_release_requested() const { return release_requested; } + void notify_rrc_reconf_complete(const bool reconf_complete_outcome); + + ngap_ue_ctxt_t ctxt = {}; + uint16_t stream_id = 1; + +private: + // args + ngap* ngap_ptr = nullptr; + + // state + bool release_requested = false; + ngap_ue_bearer_manager bearer_manager; + + // logger + srslog::basic_logger& logger; + + // procedures + srsran::proc_t initial_context_setup_proc; + srsran::proc_t ue_context_release_proc; + srsran::proc_t ue_pdu_session_res_setup_proc; +}; + +} // namespace srsenb +#endif diff --git a/srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h b/srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h new file mode 100644 index 000000000..be773b07d --- /dev/null +++ b/srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h @@ -0,0 +1,88 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_NGAP_UE_BEARER_MANAGER_H +#define SRSENB_NGAP_UE_BEARER_MANAGER_H + +#include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/ngap.h" +#include "srsran/config.h" +#include "srsran/interfaces/enb_gtpu_interfaces.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" + +namespace srsenb { + +/** + * @brief Manages the GTPU bearers as part of the NGAP session procedures + * */ + +class ngap_ue_bearer_manager +{ +public: + struct pdu_session_t { + struct gtpu_tunnel { + uint32_t teid_out = 0; + uint32_t teid_in = 0; + asn1::bounded_bitstring<1, 160, true, true> address_out; + asn1::bounded_bitstring<1, 160, true, true> address_in; + }; + uint8_t id = 0; + uint8_t lcid = 0; + asn1::ngap::qos_flow_level_qos_params_s qos_params; + std::vector tunnels; + }; + + ngap_ue_bearer_manager(gtpu_interface_rrc* gtpu_, srslog::basic_logger& logger_); + ~ngap_ue_bearer_manager(); + + int add_pdu_session(uint16_t rnti, + uint8_t pdu_session_id, + const asn1::ngap::qos_flow_level_qos_params_s& qos, + const asn1::bounded_bitstring<1, 160, true, true>& addr, + uint32_t teid_out, + uint16_t& lcid, + asn1::bounded_bitstring<1, 160, true, true>& addr_in, + uint32_t& teid_in, + asn1::ngap::cause_c& cause); + + int reset_pdu_sessions(uint16_t rnti); + + using pdu_session_list_t = std::map; + const pdu_session_list_t& pdu_sessions() const { return pdu_session_list; } + +private: + gtpu_interface_rrc* gtpu = nullptr; + pdu_session_list_t pdu_session_list; + srslog::basic_logger& logger; + + std::map next_lcid_list; // Map RNTI to next LCID to be allocated + + int add_gtpu_bearer(uint16_t rnti, + uint32_t pdu_session_id, + uint32_t teid_out, + asn1::bounded_bitstring<1, 160, true, true> address, + pdu_session_t::gtpu_tunnel& tunnel, // out parameter + const gtpu_interface_rrc::bearer_props* props = nullptr); + void rem_gtpu_bearer(uint16_t rnti, uint32_t pdu_session_id); + uint8_t allocate_lcid(uint32_t rnti); +}; +} // namespace srsenb +#endif // SRSENB_NGAP_UE_BEARER_MANAGER_H diff --git a/srsgnb/hdr/stack/ngap/ngap_ue_proc.h b/srsgnb/hdr/stack/ngap/ngap_ue_proc.h new file mode 100644 index 000000000..7d31275ec --- /dev/null +++ b/srsgnb/hdr/stack/ngap/ngap_ue_proc.h @@ -0,0 +1,110 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_NGAP_UE_PROC_H +#define SRSENB_NGAP_UE_PROC_H + +#include "ngap_interfaces.h" +#include "ngap_ue_utils.h" + +#include "srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h" +#include "srsran/asn1/asn1_utils.h" +#include "srsran/asn1/ngap.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/stack_procedure.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" + +#include +#include + +namespace srsenb { + +/* + * TS 38.413 - Section 8.2 - PDU Session Management Procedures + */ +// TS 38.413 - Section 8.2.1 PDU Session Resource Setup +class ngap_ue_pdu_session_res_setup_proc +{ +public: + explicit ngap_ue_pdu_session_res_setup_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt, + ngap_ue_bearer_manager* bearer_manager, + srslog::basic_logger& logger_); + srsran::proc_outcome_t init(const asn1::ngap::pdu_session_res_setup_request_s& msg); + srsran::proc_outcome_t step(); + static const char* name() { return "UE PDU Session Resource Setup"; } + +private: + ngap_ue_ctxt_t* ue_ctxt; + ngap_interface_ngap_proc* parent; + ngap_ue_bearer_manager* bearer_manager; + rrc_interface_ngap_nr* rrc = nullptr; + srslog::basic_logger& logger; +}; + +/* + * TS 38.413 - Section 8.3 - UE Context Management Procedures + */ +// TS 38.413 - Section 8.3.1 - Initial Context Setup +class ngap_ue_initial_context_setup_proc +{ +public: + explicit ngap_ue_initial_context_setup_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt, + srslog::basic_logger& logger_); + srsran::proc_outcome_t init(const asn1::ngap::init_context_setup_request_s& msg); + srsran::proc_outcome_t react(const bool rrc_reconf_outcome); + srsran::proc_outcome_t step(); + static const char* name() { return "Initial Context Setup"; } + +private: + ngap_ue_ctxt_t* ue_ctxt; + ngap_interface_ngap_proc* parent = nullptr; + rrc_interface_ngap_nr* rrc = nullptr; + srslog::basic_logger& logger; +}; + +// TS 38.413 - Section 8.3.2 - UE Context Release Request (NG-RAN node initiated) +class ngap_ue_ue_context_release_proc +{ +public: + explicit ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt, + ngap_ue_bearer_manager* bearer_manager, + srslog::basic_logger& logger_); + srsran::proc_outcome_t init(const asn1::ngap::ue_context_release_cmd_s& msg); + srsran::proc_outcome_t step(); + static const char* name() { return "UE Context Release"; } + +private: + ngap_ue_ctxt_t* ue_ctxt; + ngap_interface_ngap_proc* parent = nullptr; + rrc_interface_ngap_nr* rrc = nullptr; + ngap_ue_bearer_manager* bearer_manager = nullptr; + srslog::basic_logger& logger; +}; + +} // namespace srsenb + +#endif diff --git a/srsgnb/hdr/stack/ngap/ngap_ue_utils.h b/srsgnb/hdr/stack/ngap/ngap_ue_utils.h new file mode 100644 index 000000000..7152bed82 --- /dev/null +++ b/srsgnb/hdr/stack/ngap/ngap_ue_utils.h @@ -0,0 +1,47 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSENB_NGAP_UE_UTILS_H +#define SRSENB_NGAP_UE_UTILS_H + +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/adt/optional.h" +#include "srsran/common/common.h" +#include "srsran/phy/common/phy_common.h" + +namespace srsenb { + +struct ngap_ue_ctxt_t { + static const uint32_t invalid_gnb_id = std::numeric_limits::max(); + + uint16_t rnti = SRSRAN_INVALID_RNTI; + uint32_t ran_ue_ngap_id = invalid_gnb_id; + srsran::optional amf_ue_ngap_id; + uint32_t gnb_cc_idx = 0; + struct timeval init_timestamp = {}; + + // AMF identifier + uint16_t amf_set_id; + uint8_t amf_pointer; + uint8_t amf_region_id; +}; + +} // namespace srsenb +#endif \ No newline at end of file diff --git a/srsgnb/hdr/stack/rrc/cell_asn1_config.h b/srsgnb/hdr/stack/rrc/cell_asn1_config.h new file mode 100644 index 000000000..3f7e02fb8 --- /dev/null +++ b/srsgnb/hdr/stack/rrc/cell_asn1_config.h @@ -0,0 +1,64 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_CELL_ASN1_CONFIG_H +#define SRSRAN_CELL_ASN1_CONFIG_H + +#include "rrc_nr_config.h" +#include "srsran/asn1/rrc_nr.h" +#include "srsran/common/bearer_manager.h" +#include "srsran/common/common_nr.h" + +namespace srsenb { + +using rlc_bearer_list_t = asn1::rrc_nr::cell_group_cfg_s::rlc_bearer_to_add_mod_list_l_; + +// PHY helpers +void set_search_space_from_phy_cfg(const srsran_search_space_t& ss, asn1::rrc_nr::search_space_s& out); + +// NSA helpers +int fill_sp_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::sp_cell_cfg_s& sp_cell); + +// SA helpers +int fill_master_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::cell_group_cfg_s& out); + +int fill_mib_from_enb_cfg(const rrc_cell_cfg_nr_t& cell_cfg, asn1::rrc_nr::mib_s& mib); +int fill_sib1_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::sib1_s& sib1); + +/** + * Based on the previous and new radio bearer config, generate ASN1 diff + * @return if a change was detected + */ +bool compute_diff_radio_bearer_cfg(const rrc_nr_cfg_t& cfg, + const asn1::rrc_nr::radio_bearer_cfg_s& prev_bearers, + const asn1::rrc_nr::radio_bearer_cfg_s& next_bearers, + asn1::rrc_nr::radio_bearer_cfg_s& diff); + +/// Apply radioBearerConfig updates to CellGroupConfig +int fill_cellgroup_with_radio_bearer_cfg(const rrc_nr_cfg_t& cfg, + uint32_t rnti, + const enb_bearer_manager& bearer_mapper, + const asn1::rrc_nr::radio_bearer_cfg_s& bearers, + asn1::rrc_nr::cell_group_cfg_s& out); + +} // namespace srsenb + +#endif // SRSRAN_CELL_ASN1_CONFIG_H diff --git a/srsgnb/hdr/stack/rrc/rrc_nr.h b/srsgnb/hdr/stack/rrc/rrc_nr.h new file mode 100644 index 000000000..970b73fe3 --- /dev/null +++ b/srsgnb/hdr/stack/rrc/rrc_nr.h @@ -0,0 +1,208 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSENB_RRC_NR_H +#define SRSENB_RRC_NR_H + +#include "srsenb/hdr/stack/enb_stack_base.h" +#include "srsenb/hdr/stack/rrc/rrc_config_common.h" +#include "srsenb/hdr/stack/rrc/rrc_metrics.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config.h" +#include "srsran/asn1/rrc_nr.h" +#include "srsran/common/block_queue.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/common/threads.h" +#include "srsran/common/timeout.h" +#include "srsran/interfaces/enb_pdcp_interfaces.h" +#include "srsran/interfaces/enb_rlc_interfaces.h" +#include "srsran/interfaces/enb_x2_interfaces.h" +#include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/interfaces/gnb_mac_interfaces.h" +#include "srsran/interfaces/gnb_ngap_interfaces.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" +#include +#include + +namespace srsenb { + +class enb_bearer_manager; +class du_config_manager; + +enum class rrc_nr_state_t { RRC_IDLE, RRC_INACTIVE, RRC_CONNECTED }; + +class rrc_nr final : public rrc_interface_pdcp_nr, + public rrc_interface_mac_nr, + public rrc_interface_rlc_nr, + public rrc_interface_ngap_nr, + public rrc_nr_interface_rrc +{ +public: + explicit rrc_nr(srsran::task_sched_handle task_sched_); + ~rrc_nr(); + + int32_t init(const rrc_nr_cfg_t& cfg, + phy_interface_stack_nr* phy, + mac_interface_rrc_nr* mac, + rlc_interface_rrc* rlc, + pdcp_interface_rrc* pdcp, + ngap_interface_rrc_nr* ngap_, + gtpu_interface_rrc* gtpu_, + enb_bearer_manager& bearer_mapper_, + rrc_eutra_interface_rrc_nr* rrc_eutra_); + + void stop(); + + void get_metrics(srsenb::rrc_metrics_t& m); + + void config_phy(); + void config_mac(); + int32_t generate_sibs(); + int read_pdu_bcch_bch(const uint32_t tti, srsran::byte_buffer_t& buffer) final; + int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::byte_buffer_t& buffer) final; + + /// User management + int add_user(uint16_t rnti, uint32_t pcell_cc_idx) final; + void rem_user(uint16_t rnti); + int update_user(uint16_t new_rnti, uint16_t old_rnti) final; + void set_activity_user(uint16_t rnti) final; + int rrc_release(uint16_t rnti); + + // RLC interface + // TODO + void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) final {} + void max_retx_attempted(uint16_t rnti) final {} + void protocol_failure(uint16_t rnti) final {} + const char* get_rb_name(uint32_t lcid) final { return "invalid"; } + + // PDCP interface + void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; + void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) final; + + // Interface for EUTRA RRC + void sgnb_addition_request(uint16_t rnti, const sgnb_addition_req_params_t& params) final; + void sgnb_reconfiguration_complete(uint16_t rnti, const asn1::dyn_octstring& reconfig_response) final; + void sgnb_release_request(uint16_t nr_rnti) final; + + // Interfaces for NGAP + int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) final; + int ue_set_bitrates(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) final; + int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) final; + int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) final; + int establish_rrc_bearer(uint16_t rnti, + uint16_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) final; + int release_bearers(uint16_t rnti) final; + void release_user(uint16_t rnti) final; + void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) final; + int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) final; + int allocate_lcid(uint16_t rnti) final; + + // logging + typedef enum { Rx = 0, Tx } direction_t; + template + void log_rrc_message(const char* source, + const direction_t dir, + srsran::const_byte_span pdu, + const T& msg, + const char* msg_type); + + class ue; + +private: + static constexpr uint32_t UE_PSCELL_CC_IDX = 0; // first NR cell is always Primary Secondary Cell for UE + rrc_nr_cfg_t cfg = {}; + + // interfaces + phy_interface_stack_nr* phy = nullptr; + mac_interface_rrc_nr* mac = nullptr; + rlc_interface_rrc* rlc = nullptr; + pdcp_interface_rrc* pdcp = nullptr; + ngap_interface_rrc_nr* ngap = nullptr; + gtpu_interface_rrc* gtpu = nullptr; + rrc_eutra_interface_rrc_nr* rrc_eutra = nullptr; + enb_bearer_manager* bearer_mapper = nullptr; + + // args + srsran::task_sched_handle task_sched; + + // derived + uint32_t slot_dur_ms = 0; + srslog::basic_logger& logger; + + // vars + std::unique_ptr du_cfg; + struct cell_ctxt_t { + asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_l_ sibs; + std::vector sib_buffer; + std::unique_ptr master_cell_group; + srsran::phy_cfg_nr_t default_phy_ue_cfg_nr; + }; + std::unique_ptr cell_ctxt; + rnti_map_t > users; + bool running = false; + + /// Private Methods + void handle_pdu(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu); + void handle_ul_ccch(uint16_t rnti, srsran::const_byte_span pdu); + void handle_ul_dcch(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu); + + // TS 38.331, 5.3.3 - RRC connection establishment + void handle_rrc_setup_request(uint16_t rnti, const asn1::rrc_nr::rrc_setup_request_s& msg); + + // TS 38.331, 5.3.7 RRC connection reestablishment + void handle_rrc_reest_request(uint16_t rnti, const asn1::rrc_nr::rrc_reest_request_s& msg); + + /// This gets called by rrc_nr::sgnb_addition_request and WILL NOT TRIGGER the RX MSG3 activity timer + int add_user(uint16_t rnti, uint32_t pcell_cc_idx, bool start_msg3_timer); + + // Helper to create PDU from RRC message + template + srsran::unique_byte_buffer_t pack_into_pdu(const T& msg, const char* context_name = nullptr) + { + context_name = context_name == nullptr ? __FUNCTION__ : context_name; + // Allocate a new PDU buffer and pack the + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s.", context_name); + return nullptr; + } + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + if (msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { + logger.error("Failed to pack message in %s. Discarding it.", context_name); + return nullptr; + } + pdu->N_bytes = bref.distance_bytes(); + return pdu; + } + void log_rx_pdu_fail(uint16_t rnti, + uint32_t lcid, + srsran::const_byte_span pdu, + const char* cause_str, + bool log_hex = true); +}; + +} // namespace srsenb + +#endif // SRSENB_RRC_NR_H diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_config.h b/srsgnb/hdr/stack/rrc/rrc_nr_config.h new file mode 100644 index 000000000..30b808561 --- /dev/null +++ b/srsgnb/hdr/stack/rrc/rrc_nr_config.h @@ -0,0 +1,92 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_NR_CONFIG_H +#define SRSRAN_RRC_NR_CONFIG_H + +#include "srsenb/hdr/stack/rrc/rrc_config_common.h" +#include "srsgnb/hdr/phy/phy_nr_interfaces.h" +#include "srsran/asn1/rrc_nr.h" +#include "srsran/common/security.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" + +namespace srsenb { + +// Cell/Sector configuration for NR cells +struct rrc_cell_cfg_nr_t { + phy_cell_cfg_nr_t phy_cell; // already contains all PHY-related parameters (i.e. RF port, PCI, etc.) + uint32_t tac; // Tracking area code + uint32_t dl_arfcn; // DL freq already included in carrier + uint32_t ul_arfcn; // UL freq also in carrier + uint32_t dl_absolute_freq_point_a; // derived from DL ARFCN + uint32_t ul_absolute_freq_point_a; // derived from UL ARFCN + uint32_t band; + uint32_t prach_root_seq_idx; + uint32_t num_ra_preambles; + uint32_t coreset0_idx; // Table 13-{1,...15} row index + srsran_duplex_mode_t duplex_mode; + double ssb_freq_hz; + uint32_t ssb_absolute_freq_point; // derived from DL ARFCN (SSB arfcn) + uint32_t ssb_offset; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_pattern_t ssb_pattern; + asn1::rrc_nr::pdcch_cfg_common_s pdcch_cfg_common; + asn1::rrc_nr::pdcch_cfg_s pdcch_cfg_ded; + int8_t pdsch_rs_power; +}; + +typedef std::vector rrc_cell_list_nr_t; + +struct srb_5g_cfg_t { + bool present = false; + asn1::rrc_nr::rlc_cfg_c rlc_cfg; +}; + +struct rrc_nr_cfg_five_qi_t { + bool configured = false; + asn1::rrc_nr::pdcp_cfg_s pdcp_cfg; + asn1::rrc_nr::rlc_cfg_c rlc_cfg; +}; + +struct rrc_nr_cfg_t { + rrc_cell_list_nr_t cell_list; + uint32_t inactivity_timeout_ms = 100000; + uint32_t enb_id; + uint16_t mcc; + uint16_t mnc; + bool is_standalone; + + srb_5g_cfg_t srb1_cfg; + srb_5g_cfg_t srb2_cfg; + + std::map five_qi_cfg; + + std::array nea_preference_list; + std::array nia_preference_list; + + std::string log_name = "RRC-NR"; + std::string log_level; + uint32_t log_hex_limit; +}; + +} // namespace srsenb + +#endif // SRSRAN_RRC_NR_CONFIG_H diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h b/srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h new file mode 100644 index 000000000..2f9f43e9c --- /dev/null +++ b/srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h @@ -0,0 +1,44 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_NR_CONFIG_DEFAULT_H +#define SRSRAN_RRC_NR_CONFIG_DEFAULT_H + +#include "rrc_nr_config.h" + +namespace srsenb { + +// Helper methods +uint32_t coreset_get_bw(const asn1::rrc_nr::ctrl_res_set_s& coreset); +int coreset_get_pdcch_nr_max_candidates(const asn1::rrc_nr::ctrl_res_set_s& coreset, uint32_t aggregation_level); + +void generate_default_nr_cell(rrc_cell_cfg_nr_t& cell); + +int set_derived_nr_cell_params(bool is_sa, rrc_cell_cfg_nr_t& cell); +int set_derived_nr_rrc_params(rrc_nr_cfg_t& rrc_cfg); + +// Tests to ensure validity of config + +int check_nr_pdcch_cfg_valid(const srsran_pdcch_cfg_nr_t& pdcch); + +} // namespace srsenb + +#endif // SRSRAN_RRC_NR_CONFIG_DEFAULT_H diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h b/srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h new file mode 100644 index 000000000..30fce1009 --- /dev/null +++ b/srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h @@ -0,0 +1,85 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_NR_DU_MANAGER_H +#define SRSRAN_RRC_NR_DU_MANAGER_H + +#include "rrc_nr_config.h" +#include "srsgnb/hdr/stack/mac/sched_nr_interface.h" +#include "srsran/asn1/rrc_nr.h" + +namespace srsenb { + +class du_cell_config +{ +public: + uint32_t cc; + uint32_t pci; + + asn1::rrc_nr::mib_s mib; + srsran::unique_byte_buffer_t packed_mib; + + asn1::rrc_nr::sib1_s sib1; + srsran::unique_byte_buffer_t packed_sib1; + + asn1::rrc_nr::subcarrier_spacing_e ssb_scs; + srsran_ssb_pattern_t ssb_pattern; + double ssb_center_freq_hz; + double dl_freq_hz; + bool is_standalone; + + const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg_common() const + { + return sib1.serving_cell_cfg_common; + } + + /// SI messages (index=0 for SIB1) + srsran::const_byte_span packed_si_msg(uint32_t idx) { return srsran::make_span(packed_sib1); } + size_t nof_si_msgs() const { return 1; } +}; + +class du_config_manager +{ +public: + explicit du_config_manager(const rrc_nr_cfg_t& cfg); + ~du_config_manager(); + + const rrc_nr_cfg_t& cfg; + + int add_cell(); + + const du_cell_config& cell(uint32_t cc) const + { + srsran_assert(cc < cells.size(), "Unknown DU Cell Index=%d", cc); + return *cells[cc]; + } + +private: + srslog::basic_logger& logger; + + std::vector > cells; +}; + +void fill_phy_pdcch_cfg_common(const du_cell_config& cell, srsran_pdcch_cfg_nr_t* pdcch); + +} // namespace srsenb + +#endif // SRSRAN_RRC_NR_DU_MANAGER_H diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_security_context.h b/srsgnb/hdr/stack/rrc/rrc_nr_security_context.h new file mode 100644 index 000000000..147f2be18 --- /dev/null +++ b/srsgnb/hdr/stack/rrc/rrc_nr_security_context.h @@ -0,0 +1,81 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_NR_SECURITY_CONTEXT_H +#define SRSRAN_RRC_NR_SECURITY_CONTEXT_H + +#include "srsgnb/hdr/stack/rrc/rrc_nr_config.h" +#include "srsran/asn1/ngap.h" +#include "srsran/interfaces/gnb_interfaces.h" +#include "srsran/srslog/srslog.h" + +namespace srsgnb { + +class nr_security_context +{ +public: + explicit nr_security_context(const srsenb::rrc_nr_cfg_t& cfg_) : + cfg(cfg_), logger(srslog::fetch_basic_logger("RRC-NR")) + {} + + nr_security_context(const nr_security_context& other) : cfg(other.cfg), logger(srslog::fetch_basic_logger("RRC-NR")) + { + k_gnb_present = other.k_gnb_present; + security_capabilities = other.security_capabilities; + std::copy(other.k_gnb, other.k_gnb + 32, k_gnb); + sec_cfg = other.sec_cfg; + ncc = other.ncc; + } + + nr_security_context& operator=(const nr_security_context& other) + { + k_gnb_present = other.k_gnb_present; + security_capabilities = other.security_capabilities; + std::copy(other.k_gnb, other.k_gnb + 32, k_gnb); + sec_cfg = other.sec_cfg; + ncc = other.ncc; + return *this; + } + + bool set_security_capabilities(const asn1::ngap::ue_security_cap_s& caps); + void set_security_key(const asn1::fixed_bitstring<256, false, true>& key); + void set_ncc(uint8_t ncc_) { ncc = ncc_; } + + asn1::rrc_nr::security_algorithm_cfg_s get_security_algorithm_cfg() const; + const srsran::nr_as_security_config_t& get_as_sec_cfg() const { return sec_cfg; } + uint8_t get_ncc() const { return ncc; } + bool is_as_sec_cfg_valid() const { return k_gnb_present; } + + void regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_earfcn); + +private: + void generate_as_keys(); + + srslog::basic_logger& logger; + const srsenb::rrc_nr_cfg_t& cfg; + bool k_gnb_present = false; + asn1::ngap::ue_security_cap_s security_capabilities = {}; + uint8_t k_gnb[32] = {}; // Provided by MME + srsran::nr_as_security_config_t sec_cfg = {}; + uint8_t ncc = 0; +}; +} // namespace srsgnb +#endif diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h new file mode 100644 index 000000000..d41d18b02 --- /dev/null +++ b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h @@ -0,0 +1,217 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_NR_UE_H +#define SRSRAN_RRC_NR_UE_H + +#include "rrc_nr.h" +#include "rrc_nr_security_context.h" + +namespace srsenb { + +class rrc_nr::ue +{ +public: + enum activity_timeout_type_t { + MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs + UE_INACTIVITY_TIMEOUT, ///< (currently unused) UE inactivity timeout (usually bigger than reestablishment timeout) + MSG5_RX_TIMEOUT, ///< (currently unused) for receiving RRCConnectionSetupComplete/RRCReestablishmentComplete + nulltype + }; + + /// @param [in] start_msg3_timer: indicates whether the UE is created as part of a RACH process + ue(rrc_nr* parent_, uint16_t rnti_, uint32_t pcell_cc_idx, bool start_msg3_timer = true); + ~ue(); + + int handle_sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params); + void crnti_ce_received(); + + // getters + bool is_connected() { return state == rrc_nr_state_t::RRC_CONNECTED; } + bool is_idle() { return state == rrc_nr_state_t::RRC_IDLE; } + bool is_inactive() { return state == rrc_nr_state_t::RRC_INACTIVE; } + bool is_endc() { return endc; } + uint16_t get_eutra_rnti() { return eutra_rnti; } + void get_metrics(rrc_ue_metrics_t& ue_metrics) { ue_metrics = {}; /*TODO fill RRC metrics*/ }; + + // setters + void set_security_key(const asn1::fixed_bitstring<256, false, true>& key) { sec_ctx.set_security_key(key); } + void set_security_capabilities(const asn1::ngap::ue_security_cap_s& caps) { sec_ctx.set_security_capabilities(caps); } + + void deactivate_bearers(); + + /// methods to handle activity timer + std::string to_string(const activity_timeout_type_t& type); + void set_activity_timeout(activity_timeout_type_t type); + void set_activity(bool enabled = true); + void activity_timer_expired(const activity_timeout_type_t type); + + /** TS 38.331 - 5.3.3 RRC connection establishment */ + void handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request_s& msg); + void handle_rrc_setup_complete(const asn1::rrc_nr::rrc_setup_complete_s& msg); + + /** TS 38.331 - 5.3.4 Initial AS security activation */ + void handle_security_mode_complete(const asn1::rrc_nr::security_mode_complete_s& msg); + + /** TS 38.331 - 5.3.5 RRC reconfiguration */ + void handle_rrc_reconfiguration_complete(const asn1::rrc_nr::rrc_recfg_complete_s& msg); + + /** TS 38.331 - 5.3.7 RRC connection reestablishment */ + void handle_rrc_reestablishment_request(const asn1::rrc_nr::rrc_reest_request_s& msg); + void handle_rrc_reestablishment_complete(const asn1::rrc_nr::rrc_reest_complete_s& msg); + + /** TS 38.331 - 5.3.8 Connection Release */ + void send_rrc_release(); + + /* TS 38.331 - 5.6.1 UE capability transfer */ + void handle_ue_capability_information(const asn1::rrc_nr::ue_cap_info_s& msg); + + /** TS 38.331 - 5.7.1 DL information transfer */ + void send_dl_information_transfer(srsran::unique_byte_buffer_t sdu); + + /** TS 38.331 - 5.7.2 UL information transfer */ + void handle_ul_information_transfer(const asn1::rrc_nr::ul_info_transfer_s& msg); + + // NGAP interface + void establish_eps_bearer(uint32_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid, uint32_t five_qi); + + /* TS 38.331 - 5.3.4 Initial AS security activation */ + void send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu); + + /* TS 38.331 - 5.3.5 RRC reconfiguration */ + void send_rrc_reconfiguration(); + + /* TS 38.331 - 5.6.1 UE capability transfer */ + int send_ue_capability_enquiry(); + +private: + int send_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_ccch_msg); + int send_dl_dcch(srsran::nr_srb srb, const asn1::rrc_nr::dl_dcch_msg_s& dl_dcch_msg); + + /** TS 38.331 - 5.3.3 RRC connection establishment */ + void send_rrc_setup(); + void send_rrc_reject(uint8_t reject_wait_time_secs); + + /** TS 38.331 - 5.3.7 RRC connection reestablishment */ + void send_connection_reest(uint8_t ncc); + + /// Update PDCP bearers based on ASN1 structs passed to the UE + int update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_diff, + const asn1::rrc_nr::cell_group_cfg_s& cell_group_diff); + + /// Update RLC bearers based on ASN1 structs passed to the UE + int update_rlc_bearers(const asn1::rrc_nr::cell_group_cfg_s& cell_group_diff); + + /// Update MAC based on ASN1 message + int update_mac(const asn1::rrc_nr::cell_group_cfg_s& cell_group_config, bool is_config_complete); + + /// Update AS security config on active RB + int update_as_security(uint32_t lcid, bool enable_integrity, bool enable_ciphering); + + int pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig); + int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config); + + int pack_secondary_cell_group_mac_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_secondary_cell_group_sp_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_sp_cell_cfg_ded(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_sp_cell_cfg_ded_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_sp_cell_cfg_ded_init_dl_bwp_radio_link_monitoring(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_sp_cell_cfg_ded_ul_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pucch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pusch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_recfg_with_sync(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_recfg_with_sync_sp_cell_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common_phy_cell_group_cfg( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp_pusch_cfg_common( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + + int pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_bearer_config); + + int add_drb(uint32_t five_qi); + + bool init_pucch(); + + // logging helpers + template + void log_rrc_message(srsran::nr_srb srb, + const direction_t dir, + srsran::const_byte_span pdu, + const M& msg, + const char* msg_type); + template + void log_rrc_container(const direction_t dir, srsran::const_byte_span pdu, const M& msg, const char* msg_type); + + // args + rrc_nr* parent = nullptr; + srslog::basic_logger& logger; + uint16_t rnti = SRSRAN_INVALID_RNTI; + + // state + rrc_nr_state_t state = rrc_nr_state_t::RRC_IDLE; + uint8_t transaction_id = 0; + srsran::unique_timer activity_timer; /// for basic DL/UL activity timeout + + // RRC configs for UEs + asn1::rrc_nr::cell_group_cfg_s cell_group_cfg, next_cell_group_cfg; + asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_cfg, next_radio_bearer_cfg; + std::vector nas_pdu_queue; + + // MAC controller + sched_nr_interface::ue_cfg_t uecfg{}; + + const uint32_t drb1_lcid = 4; + uint32_t drb1_five_qi = 0; /// selected by 5GC + + // Security helper + srsgnb::nr_security_context sec_ctx; + + // SA specific variables + struct ctxt_t { + uint64_t setup_ue_id = -1; + asn1::rrc_nr::establishment_cause_opts connection_cause; + } ctxt; + + // NSA specific variables + bool endc = false; + uint16_t eutra_rnti = SRSRAN_INVALID_RNTI; +}; + +} // namespace srsenb + +#endif // SRSRAN_RRC_NR_UE_H diff --git a/srsenb/hdr/stack/upper/sdap.h b/srsgnb/hdr/stack/sdap/sdap.h similarity index 96% rename from srsenb/hdr/stack/upper/sdap.h rename to srsgnb/hdr/stack/sdap/sdap.h index cee33237e..f0ed40708 100644 --- a/srsenb/hdr/stack/upper/sdap.h +++ b/srsgnb/hdr/stack/sdap/sdap.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsgnb/src/CMakeLists.txt b/srsgnb/src/CMakeLists.txt new file mode 100644 index 000000000..172c61bf3 --- /dev/null +++ b/srsgnb/src/CMakeLists.txt @@ -0,0 +1,21 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_subdirectory(stack) \ No newline at end of file diff --git a/srsgnb/src/stack/CMakeLists.txt b/srsgnb/src/stack/CMakeLists.txt new file mode 100644 index 000000000..4d4b736b7 --- /dev/null +++ b/srsgnb/src/stack/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +include_directories(${PROJECT_SOURCE_DIR}) + +add_subdirectory(mac) +add_subdirectory(ngap) +add_subdirectory(rrc) +add_subdirectory(sdap) + +set(SOURCES gnb_stack_nr.cc) + +add_library(srsgnb_stack STATIC ${SOURCES}) \ No newline at end of file diff --git a/srsgnb/src/stack/gnb_stack_nr.cc b/srsgnb/src/stack/gnb_stack_nr.cc new file mode 100644 index 000000000..622f030b4 --- /dev/null +++ b/srsgnb/src/stack/gnb_stack_nr.cc @@ -0,0 +1,255 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/gnb_stack_nr.h" +#include "srsenb/hdr/stack/upper/gtpu.h" +#include "srsenb/hdr/stack/upper/gtpu_pdcp_adapter.h" +#include "srsgnb/hdr/stack/ngap/ngap.h" +#include "srsran/common/network_utils.h" +#include "srsran/srsran.h" +#include + +namespace srsenb { + +gnb_stack_nr::gnb_stack_nr(srslog::sink& log_sink) : + task_sched{512, 128}, + thread("gNB"), + mac_logger(srslog::fetch_basic_logger("MAC-NR", log_sink)), + rlc_logger(srslog::fetch_basic_logger("RLC-NR", log_sink, false)), + pdcp_logger(srslog::fetch_basic_logger("PDCP-NR", log_sink, false)), + rrc_logger(srslog::fetch_basic_logger("RRC-NR", log_sink, false)), + stack_logger(srslog::fetch_basic_logger("STCK-NR", log_sink, false)), + gtpu_logger(srslog::fetch_basic_logger("GTPU", log_sink, false)), + ngap_logger(srslog::fetch_basic_logger("NGAP", log_sink, false)), + mac(&task_sched), + rrc(&task_sched), + pdcp(&task_sched, pdcp_logger), + bearer_manager(new srsenb::enb_bearer_manager()), + rlc(rlc_logger) +{ + sync_task_queue = task_sched.make_task_queue(); + gtpu_task_queue = task_sched.make_task_queue(); + metrics_task_queue = task_sched.make_task_queue(); + gnb_task_queue = task_sched.make_task_queue(); + x2_task_queue = task_sched.make_task_queue(); +} + +gnb_stack_nr::~gnb_stack_nr() +{ + stop(); +} + +std::string gnb_stack_nr::get_type() +{ + return "nr"; +} + +int gnb_stack_nr::init(const gnb_stack_args_t& args_, + const rrc_nr_cfg_t& rrc_cfg_, + phy_interface_stack_nr* phy_, + x2_interface* x2_) +{ + args = args_; + phy = phy_; + + // setup logging + mac_logger.set_level(srslog::str_to_basic_level(args.log.mac_level)); + rlc_logger.set_level(srslog::str_to_basic_level(args.log.rlc_level)); + pdcp_logger.set_level(srslog::str_to_basic_level(args.log.pdcp_level)); + rrc_logger.set_level(srslog::str_to_basic_level(args.log.rrc_level)); + stack_logger.set_level(srslog::str_to_basic_level(args.log.stack_level)); + ngap_logger.set_level(srslog::str_to_basic_level(args.log.s1ap_level)); + gtpu_logger.set_level(srslog::str_to_basic_level(args.log.gtpu_level)); + srslog::fetch_basic_logger("COMN", false).set_level(srslog::str_to_basic_level(args.log.stack_level)); + + mac_logger.set_hex_dump_max_size(args.log.mac_hex_limit); + rlc_logger.set_hex_dump_max_size(args.log.rlc_hex_limit); + pdcp_logger.set_hex_dump_max_size(args.log.pdcp_hex_limit); + rrc_logger.set_hex_dump_max_size(args.log.rrc_hex_limit); + stack_logger.set_hex_dump_max_size(args.log.stack_hex_limit); + ngap_logger.set_hex_dump_max_size(args.log.s1ap_hex_limit); + gtpu_logger.set_hex_dump_max_size(args.log.gtpu_hex_limit); + srslog::fetch_basic_logger("COMN", false).set_hex_dump_max_size(args.log.stack_hex_limit); + + if (x2_ == nullptr) { + // SA mode + ngap.reset(new srsenb::ngap(&task_sched, ngap_logger, &srsran::get_rx_io_manager())); + gtpu.reset(new srsenb::gtpu(&task_sched, gtpu_logger, &srsran::get_rx_io_manager())); + gtpu_adapter.reset(new gtpu_pdcp_adapter(gtpu_logger, nullptr, &pdcp, gtpu.get(), *bearer_manager)); + } + + // Init all layers + if (mac.init(args.mac, phy, nullptr, &rlc, &rrc) != SRSRAN_SUCCESS) { + stack_logger.error("Couldn't initialize MAC-NR"); + return SRSRAN_ERROR; + } + + rlc.init(&pdcp, &rrc, &mac, task_sched.get_timer_handler()); + + if (rrc.init(rrc_cfg_, phy, &mac, &rlc, &pdcp, ngap.get(), gtpu.get(), *bearer_manager, x2_) != SRSRAN_SUCCESS) { + stack_logger.error("Couldn't initialize RRC"); + return SRSRAN_ERROR; + } + + if (ngap != nullptr) { + pdcp.init(&rlc, &rrc, gtpu_adapter.get()); + + if (args.ngap_pcap.enable) { + ngap_pcap.open(args.ngap_pcap.filename.c_str()); + ngap->start_pcap(&ngap_pcap); + } + + ngap->init(args.ngap, &rrc, gtpu.get()); + gtpu_args_t gtpu_args; + gtpu_args.embms_enable = false; + gtpu_args.mme_addr = args.ngap.amf_addr; + gtpu_args.gtp_bind_addr = args.ngap.gtp_bind_addr; + gtpu->init(gtpu_args, gtpu_adapter.get()); + } else { + pdcp.init(&rlc, &rrc, x2_); + } + + // TODO: add SDAP + + running = true; + + start(STACK_MAIN_THREAD_PRIO); + + return SRSRAN_SUCCESS; +} + +void gnb_stack_nr::stop() +{ + if (running) { + gnb_task_queue.push([this]() { stop_impl(); }); + wait_thread_finish(); + } +} + +void gnb_stack_nr::stop_impl() +{ + srsran::get_rx_io_manager().stop(); + + rrc.stop(); + pdcp.stop(); + mac.stop(); + + task_sched.stop(); + srsran::get_background_workers().stop(); + + running = false; +} + +bool gnb_stack_nr::switch_on() +{ + // Nothing to be done here + return true; +} + +void gnb_stack_nr::run_thread() +{ + while (running) { + task_sched.run_next_task(); + } +} + +void gnb_stack_nr::tti_clock() +{ + sync_task_queue.push([this]() { tti_clock_impl(); }); +} + +void gnb_stack_nr::tti_clock_impl() +{ + // m_ngap->run_tti(); + task_sched.tic(); +} + +void gnb_stack_nr::process_pdus() {} + +/******************************************************** + * + * Interface for upper layer timers + * + *******************************************************/ + +bool gnb_stack_nr::get_metrics(srsenb::stack_metrics_t* metrics) +{ + bool metrics_ready = false; + + // use stack thread to query RRC metrics + auto ret = metrics_task_queue.try_push([this, metrics, &metrics_ready]() { + rrc.get_metrics(metrics->rrc); + { + std::lock_guard lock(metrics_mutex); + metrics_ready = true; + } + metrics_cvar.notify_one(); + }); + if (not ret.has_value()) { + return false; + } + + // obtain MAC metrics (do not use stack thread) + mac.get_metrics(metrics->mac); + + // wait for RRC result + std::unique_lock lock(metrics_mutex); + metrics_cvar.wait(lock, [&metrics_ready]() { return metrics_ready; }); + return true; +} + +// Temporary GW interface +void gnb_stack_nr::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) +{ + // not implemented +} + +bool gnb_stack_nr::has_active_radio_bearer(uint32_t eps_bearer_id) +{ + return false; +} +int gnb_stack_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg) +{ + return mac.slot_indication(slot_cfg); +} +gnb_stack_nr::dl_sched_t* gnb_stack_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg) +{ + return mac.get_dl_sched(slot_cfg); +} +gnb_stack_nr::ul_sched_t* gnb_stack_nr::get_ul_sched(const srsran_slot_cfg_t& slot_cfg) +{ + return mac.get_ul_sched(slot_cfg); +} +int gnb_stack_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_phy_nr::pucch_info_t& pucch_info) +{ + return mac.pucch_info(slot_cfg, pucch_info); +} +int gnb_stack_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::pusch_info_t& pusch_info) +{ + return mac.pusch_info(slot_cfg, pusch_info); +} + +void gnb_stack_nr::rach_detected(const rach_info_t& rach_info) +{ + mac.rach_detected(rach_info); +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/CMakeLists.txt b/srsgnb/src/stack/mac/CMakeLists.txt new file mode 100644 index 000000000..1772c82d8 --- /dev/null +++ b/srsgnb/src/stack/mac/CMakeLists.txt @@ -0,0 +1,44 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES mac_nr.cc + ue_nr.cc + sched_nr.cc + sched_nr_ue.cc + sched_ue/ue_cfg_manager.cc + sched_nr_worker.cc + sched_nr_grant_allocator.cc + sched_nr_harq.cc + sched_nr_pdcch.cc + sched_nr_sch.cc + sched_nr_cfg.cc + sched_nr_helpers.cc + sched_nr_bwp.cc + sched_nr_rb.cc + sched_nr_time_rr.cc + harq_softbuffer.cc + sched_nr_signalling.cc + sched_nr_interface_utils.cc) + +add_library(srsgnb_mac STATIC ${SOURCES}) +target_link_libraries(srsgnb_mac srsenb_mac_common srsran_mac rrc_nr_asn1) +include_directories(${PROJECT_SOURCE_DIR}) + +add_subdirectory(test) diff --git a/srsgnb/src/stack/mac/harq_softbuffer.cc b/srsgnb/src/stack/mac/harq_softbuffer.cc new file mode 100644 index 000000000..4e9157797 --- /dev/null +++ b/srsgnb/src/stack/mac/harq_softbuffer.cc @@ -0,0 +1,71 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/harq_softbuffer.h" +#include "srsran/adt/pool/obj_pool.h" + +namespace srsenb { + +void harq_softbuffer_pool::init_pool(uint32_t nof_prb, uint32_t batch_size, uint32_t thres, uint32_t init_size) +{ + srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid nof prb=%d", nof_prb); + size_t idx = nof_prb - 1; + if (tx_pool[idx] != nullptr) { + return; + } + if (thres == 0) { + thres = batch_size; + } + if (init_size == 0) { + init_size = batch_size; + } + auto init_tx_softbuffers = [nof_prb](void* ptr) { new (ptr) tx_harq_softbuffer(nof_prb); }; + auto recycle_tx_softbuffers = [](tx_harq_softbuffer& softbuffer) { softbuffer.reset(); }; + tx_pool[idx].reset(new srsran::background_obj_pool( + batch_size, thres, init_size, init_tx_softbuffers, recycle_tx_softbuffers)); + + auto init_rx_softbuffers = [nof_prb](void* ptr) { new (ptr) rx_harq_softbuffer(nof_prb); }; + auto recycle_rx_softbuffers = [](rx_harq_softbuffer& softbuffer) { softbuffer.reset(); }; + rx_pool[idx].reset(new srsran::background_obj_pool( + batch_size, thres, init_size, init_rx_softbuffers, recycle_rx_softbuffers)); +} + +srsran::unique_pool_ptr harq_softbuffer_pool::get_tx(uint32_t nof_prb) +{ + srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid Nprb=%d", nof_prb); + size_t idx = nof_prb - 1; + if (tx_pool[idx] == nullptr) { + init_pool(nof_prb); + } + return tx_pool[idx]->make(); +} + +srsran::unique_pool_ptr harq_softbuffer_pool::get_rx(uint32_t nof_prb) +{ + srsran_assert(nof_prb <= SRSRAN_MAX_PRB_NR, "Invalid Nprb=%d", nof_prb); + size_t idx = nof_prb - 1; + if (rx_pool[idx] == nullptr) { + init_pool(nof_prb); + } + return rx_pool[idx]->make(); +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/mac_nr.cc b/srsgnb/src/stack/mac/mac_nr.cc new file mode 100644 index 000000000..a60c5226b --- /dev/null +++ b/srsgnb/src/stack/mac/mac_nr.cc @@ -0,0 +1,698 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/mac_nr.h" +#include "srsgnb/hdr/stack/mac/sched_nr.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/rwlock_guard.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" +#include "srsran/common/time_prof.h" +#include "srsran/mac/mac_rar_pdu_nr.h" + +//#define WRITE_SIB_PCAP + +namespace srsenb { + +/** + * @brief Handles UL PDU processing + * + * This class implements the demuxing of UL PDUs received at the MAC layer. + * When the PHY decodes a valid PUSCH it passes the PDU to the MAC which + * in turn puts them in a thread-safe task queue to return to the calling + * thread as quick as possible. + * + * The demuxing of the PDUs for all users takes place on the Stack thread + * which calls RLC and RRC for SDUs, or the MAC/scheduler for control elements. + * + */ +class mac_nr_rx +{ +public: + explicit mac_nr_rx(rlc_interface_mac* rlc_, + rrc_interface_mac_nr* rrc_, + srsran::task_queue_handle& stack_task_queue_, + sched_nr_interface* sched_, + mac_interface_pdu_demux_nr& mac_, + srslog::basic_logger& logger_) : + task_queue(stack_task_queue_), rlc(rlc_), rrc(rrc_), sched(sched_), mac(mac_), logger(logger_) + {} + + void handle_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) + { + task_queue.push(std::bind( + [this, rnti](srsran::unique_byte_buffer_t& pdu) { handle_pdu_impl(rnti, std::move(pdu)); }, std::move(pdu))); + } + +private: + int handle_pdu_impl(uint16_t rnti, srsran::unique_byte_buffer_t pdu) + { + pdu_ul.init_rx(true); + if (pdu_ul.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + if (logger.info.enabled()) { + fmt::memory_buffer str_buffer; + pdu_ul.to_string(str_buffer); + logger.info("Rx PDU: rnti=0x%x, %s", rnti, srsran::to_c_str(str_buffer)); + } + + // Process MAC CRNTI CE first, if it exists + uint32_t crnti_ce_pos = pdu_ul.get_num_subpdus(); + for (uint32_t n = pdu_ul.get_num_subpdus(); n > 0; --n) { + srsran::mac_sch_subpdu_nr& subpdu = pdu_ul.get_subpdu(n - 1); + if (subpdu.get_lcid() == srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CRNTI) { + if (process_ce_subpdu(rnti, subpdu) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + crnti_ce_pos = n - 1; + } + } + + // Process SDUs and remaining MAC CEs + for (uint32_t n = 0; n < pdu_ul.get_num_subpdus(); ++n) { + srsran::mac_sch_subpdu_nr& subpdu = pdu_ul.get_subpdu(n); + if (subpdu.is_sdu()) { + rrc->set_activity_user(rnti); + rlc->write_pdu(rnti, subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length()); + } else if (n != crnti_ce_pos) { + if (process_ce_subpdu(rnti, subpdu) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } + } + + return SRSRAN_SUCCESS; + } + + int process_ce_subpdu(uint16_t& rnti, const srsran::mac_sch_subpdu_nr& subpdu) + { + // Handle MAC CEs + switch (subpdu.get_lcid()) { + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH_SIZE_48: + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH_SIZE_64: { + srsran::mac_sch_subpdu_nr& ccch_subpdu = const_cast(subpdu); + rlc->write_pdu(rnti, 0, ccch_subpdu.get_sdu(), ccch_subpdu.get_sdu_length()); + // store content for ConRes CE and schedule CE accordingly + mac.store_msg3(rnti, + srsran::make_byte_buffer(ccch_subpdu.get_sdu(), ccch_subpdu.get_sdu_length(), __FUNCTION__)); + sched->dl_mac_ce(rnti, srsran::mac_sch_subpdu_nr::CON_RES_ID); + } break; + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CRNTI: { + uint16_t ce_crnti = subpdu.get_c_rnti(); + if (ce_crnti == SRSRAN_INVALID_RNTI) { + logger.error("Malformed C-RNTI CE detected. C-RNTI can't be 0x0.", subpdu.get_lcid()); + return SRSRAN_ERROR; + } + uint16_t prev_rnti = rnti; + rnti = ce_crnti; + rrc->update_user(prev_rnti, rnti); + sched->ul_sr_info(rnti); // provide UL grant regardless of other BSR content for UE to complete RA + } break; + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_BSR: + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_TRUNC_BSR: { + srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = subpdu.get_sbsr(); + uint32_t buffer_size_bytes = buff_size_field_to_bytes(sbsr.buffer_size, srsran::SHORT_BSR); + // Assume all LCGs are 0 if reported SBSR is 0 + if (buffer_size_bytes == 0) { + for (uint32_t j = 0; j <= SCHED_NR_MAX_LC_GROUP; j++) { + sched->ul_bsr(rnti, j, 0); + } + } else { + sched->ul_bsr(rnti, sbsr.lcg_id, buffer_size_bytes); + } + } break; + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR: + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_TRUNC_BSR: { + srsran::mac_sch_subpdu_nr::lbsr_t lbsr = subpdu.get_lbsr(); + for (auto& lb : lbsr.list) { + sched->ul_bsr(rnti, lb.lcg_id, buff_size_field_to_bytes(lb.buffer_size, srsran::LONG_BSR)); + } + } break; + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SE_PHR: + // SE_PHR not implemented + break; + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::PADDING: + break; + default: + logger.warning("Unhandled subPDU with LCID=%d", subpdu.get_lcid()); + } + + return SRSRAN_SUCCESS; + } + + /** Converts the buffer size field of a BSR (5 or 8-bit Buffer Size field) into Bytes + * @param buff_size_field The buffer size field contained in the MAC PDU + * @param format The BSR format that determines the buffer size field length + * @return uint32_t The actual buffer size level in Bytes + */ + static uint32_t buff_size_field_to_bytes(uint32_t buff_size_index, const srsran::bsr_format_nr_t& format) + { + using namespace srsran; + + // early exit + if (buff_size_index == 0) { + return 0; + } + + const uint32_t max_offset = 1; // make the reported value bigger than the 2nd biggest + + switch (format) { + case SHORT_BSR: + case SHORT_TRUNC_BSR: + if (buff_size_index >= buffer_size_levels_5bit_max_idx) { + return buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx] + max_offset; + } else { + return buffer_size_levels_5bit[buff_size_index]; + } + break; + case LONG_BSR: + case LONG_TRUNC_BSR: + if (buff_size_index > buffer_size_levels_8bit_max_idx) { + return buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx] + max_offset; + } else { + return buffer_size_levels_8bit[buff_size_index]; + } + break; + default: + break; + } + return 0; + } + + rlc_interface_mac* rlc; + rrc_interface_mac_nr* rrc; + sched_nr_interface* sched; + mac_interface_pdu_demux_nr& mac; + srslog::basic_logger& logger; + srsran::task_queue_handle& task_queue; + + srsran::mac_sch_pdu_nr pdu_ul; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +mac_nr::mac_nr(srsran::task_sched_handle task_sched_) : + logger(srslog::fetch_basic_logger("MAC-NR")), + task_sched(task_sched_), + bcch_bch_payload(srsran::make_byte_buffer()), + rar_pdu_buffer(srsran::make_byte_buffer()), + sched(new sched_nr{}) +{ + stack_task_queue = task_sched.make_task_queue(); +} + +mac_nr::~mac_nr() +{ + stop(); +} + +int mac_nr::init(const mac_nr_args_t& args_, + phy_interface_stack_nr* phy_, + stack_interface_mac* stack_, + rlc_interface_mac* rlc_, + rrc_interface_mac_nr* rrc_) +{ + args = args_; + + phy = phy_; + stack = stack_; + rlc = rlc_; + rrc = rrc_; + + if (args.pcap.enable) { + pcap = std::unique_ptr(new srsran::mac_pcap()); + pcap->open(args.pcap.filename); + } + + logger.info("Started"); + + started = true; + + return SRSRAN_SUCCESS; +} + +void mac_nr::stop() +{ + bool started_prev = started.exchange(false); + if (started_prev) { + sched->stop(); + if (pcap != nullptr) { + pcap->close(); + } + } +} + +/// Called from metrics thread. +/// Note: This can contend for the same mutexes as the ones used by L1/L2 workers. +/// However, get_metrics is called infrequently enough to cause major halts in the L1/L2 +void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics) +{ + // TODO: We should comment on the logic we follow to get the metrics. Some of them are retrieved from MAC, some + // others from the scheduler. + get_metrics_nolock(metrics); + sched->get_metrics(metrics); +} + +void mac_nr::get_metrics_nolock(srsenb::mac_metrics_t& metrics) +{ + srsran::rwlock_read_guard lock(rwmutex); + metrics.ues.reserve(ue_db.size()); + for (auto& u : ue_db) { + metrics.ues.emplace_back(); + u.second->metrics_read(&metrics.ues.back()); + } + metrics.cc_info.resize(detected_rachs.size()); + for (unsigned cc = 0, e = detected_rachs.size(); cc != e; ++cc) { + metrics.cc_info[cc].cc_rach_counter = detected_rachs[cc]; + metrics.cc_info[cc].pci = (cc < cell_config.size()) ? cell_config[cc].pci : 0; + } +} + +int mac_nr::cell_cfg(const std::vector& nr_cells) +{ + cell_config = nr_cells; + sched->config(args.sched_cfg, nr_cells); + detected_rachs.resize(nr_cells.size()); + + // read SIBs from RRC (SIB1 for now only) + for (uint32_t i = 0; i < nr_cells[0].sibs.size(); i++) { + sib_info_t sib = {}; + sib.index = i; + sib.periodicity = 160; // TODO: read period_rf from config + sib.payload = srsran::make_byte_buffer(); + if (sib.payload == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + if (rrc->read_pdu_bcch_dlsch(sib.index, *sib.payload) != SRSRAN_SUCCESS) { + logger.error("Couldn't read SIB %d from RRC", sib.index); + } + + logger.info("Including SIB %d into SI scheduling", sib.index + 1); + bcch_dlsch_payload.push_back(std::move(sib)); + } + + rx.reset(new mac_nr_rx{rlc, rrc, stack_task_queue, sched.get(), *this, logger}); + + default_ue_phy_cfg = get_common_ue_phy_cfg(cell_config[0]); + + return SRSRAN_SUCCESS; +} + +int mac_nr::ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) +{ + sched->ue_cfg(rnti, ue_cfg); + return SRSRAN_SUCCESS; +} + +uint16_t mac_nr::reserve_rnti(uint32_t enb_cc_idx, const sched_nr_ue_cfg_t& uecfg) +{ + uint16_t rnti = alloc_ue(enb_cc_idx); + if (rnti == SRSRAN_INVALID_RNTI) { + return rnti; + } + + sched->ue_cfg(rnti, uecfg); + + return rnti; +} + +void mac_nr::rach_detected(const rach_info_t& rach_info) +{ + static srsran::mutexed_tprof rach_tprof("rach_tprof", "MAC-NR", 1); + logger.set_context(rach_info.slot_index); + auto rach_tprof_meas = rach_tprof.start(); + + uint32_t enb_cc_idx = 0; + stack_task_queue.push([this, rach_info, enb_cc_idx, rach_tprof_meas]() mutable { + rach_tprof_meas.defer_stop(); + + uint16_t rnti = alloc_ue(enb_cc_idx); + + // Log this event. + { + srsran::rwlock_write_guard lock(rwmutex); + ++detected_rachs[enb_cc_idx]; + } + + // Trigger scheduler RACH + srsenb::sched_nr_interface::rar_info_t rar_info = {}; + rar_info.cc = enb_cc_idx; + rar_info.preamble_idx = rach_info.preamble; + rar_info.temp_crnti = rnti; + rar_info.ta_cmd = rach_info.time_adv; + rar_info.prach_slot = slot_point{NUMEROLOGY_IDX, rach_info.slot_index}; + sched->dl_rach_info(rar_info); + rrc->add_user(rnti, enb_cc_idx); + + logger.info("RACH: slot=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x", + rach_info.slot_index, + enb_cc_idx, + rach_info.preamble, + rach_info.time_adv, + rnti); + srsran::console("RACH: slot=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", + rach_info.slot_index, + enb_cc_idx, + rach_info.preamble, + rach_info.time_adv, + rnti); + }); +} + +uint16_t mac_nr::alloc_ue(uint32_t enb_cc_idx) +{ + ue_nr* inserted_ue = nullptr; + uint16_t rnti = SRSRAN_INVALID_RNTI; + + do { + // Assign new RNTI + rnti = FIRST_RNTI + (ue_counter.fetch_add(1, std::memory_order_relaxed) % 60000); + + // Pre-check if rnti is valid + { + srsran::rwlock_read_guard read_lock(rwmutex); + if (not is_rnti_valid_nolock(rnti)) { + continue; + } + } + + // Allocate and initialize UE object + std::unique_ptr ue_ptr(new ue_nr(rnti, enb_cc_idx, sched.get(), rrc, rlc, phy, logger)); + + // Add UE to rnti map + srsran::rwlock_write_guard rw_lock(rwmutex); + if (not is_rnti_valid_nolock(rnti)) { + continue; + } + auto ret = ue_db.insert(rnti, std::move(ue_ptr)); + if (ret.has_value()) { + inserted_ue = ret.value()->second.get(); + } else { + logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti); + } + } while (inserted_ue == nullptr); + + return rnti; +} + +// Remove UE from the perspective of L2/L3 +int mac_nr::remove_ue(uint16_t rnti) +{ + srsran::rwlock_write_guard lock(rwmutex); + if (is_rnti_active_nolock(rnti)) { + sched->ue_rem(rnti); + ue_db.erase(rnti); + } else { + logger.error("User rnti=0x%x not found", rnti); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +bool mac_nr::is_rnti_valid_nolock(uint16_t rnti) +{ + if (not started) { + logger.info("RACH ignored as eNB is being shutdown"); + return false; + } + if (ue_db.full()) { + logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", SRSENB_MAX_UES); + return false; + } + if (not ue_db.has_space(rnti)) { + logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti); + return false; + } + return true; +} + +bool mac_nr::is_rnti_active_nolock(uint16_t rnti) +{ + if (not ue_db.contains(rnti)) { + logger.error("User rnti=0x%x not found", rnti); + return false; + } + return ue_db[rnti]->is_active(); +} + +int mac_nr::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) +{ + sched->dl_buffer_state(rnti, lc_id, tx_queue, retx_queue); + return SRSRAN_SUCCESS; +} + +void mac_nr::ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr) +{ + sched->ul_bsr(rnti, lcid, bsr); +} + +int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg) +{ + return 0; +} + +void mac_nr::store_msg3(uint16_t rnti, srsran::unique_byte_buffer_t pdu) +{ + srsran::rwlock_read_guard rw_lock(rwmutex); + if (is_rnti_active_nolock(rnti)) { + ue_db[rnti]->store_msg3(std::move(pdu)); + } else { + logger.error("User rnti=0x%x not found. Can't store Msg3.", rnti); + } +} + +mac_nr::dl_sched_t* mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg) +{ + slot_point pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx}; + + logger.set_context((pdsch_slot - TX_ENB_DELAY).to_uint()); + + // Initiate new slot and sync UE internal states + sched->slot_indication(pdsch_slot); + + // Run DL Scheduler for CC + sched_nr::dl_res_t* dl_res = sched->get_dl_sched(pdsch_slot, 0); + if (dl_res == nullptr) { + return nullptr; + } + + // Generate MAC DL PDUs + uint32_t rar_count = 0, si_count = 0, data_count = 0; + srsran::rwlock_read_guard rw_lock(rwmutex); + for (pdsch_t& pdsch : dl_res->phy.pdsch) { + if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c) { + uint16_t rnti = pdsch.sch.grant.rnti; + if (not is_rnti_active_nolock(rnti)) { + continue; + } + for (auto& tb_data : pdsch.data) { + if (tb_data != nullptr and tb_data->N_bytes == 0) { + // TODO: exclude retx from packing + const sched_nr_interface::dl_pdu_t& pdu = dl_res->data[data_count++]; + ue_db[rnti]->generate_pdu(tb_data, pdsch.sch.grant.tb->tbs / 8, pdu.subpdus); + + if (pcap != nullptr) { + uint32_t pid = 0; // TODO: get PID from PDCCH struct? + pcap->write_dl_crnti_nr(tb_data->msg, tb_data->N_bytes, rnti, pid, slot_cfg.idx); + } + ue_db[rnti]->metrics_dl_mcs(pdsch.sch.grant.tb->mcs); + } + } + } else if (pdsch.sch.grant.rnti_type == srsran_rnti_type_ra) { + sched_nr_interface::rar_t& rar = dl_res->rar[rar_count++]; + // for RARs we could actually move the byte_buffer to the PHY, as there are no retx + pdsch.data[0] = assemble_rar(rar.grants); + } else if (pdsch.sch.grant.rnti_type == srsran_rnti_type_si) { + uint32_t sib_idx = dl_res->sib_idxs[si_count++]; + pdsch.data[0] = bcch_dlsch_payload[sib_idx].payload.get(); +#ifdef WRITE_SIB_PCAP + if (pcap != nullptr) { + pcap->write_dl_si_rnti_nr(bcch_dlsch_payload[sib_idx].payload->msg, + bcch_dlsch_payload[sib_idx].payload->N_bytes, + SI_RNTI, + 0, + slot_cfg.idx); + } +#endif + } + } + for (auto& u : ue_db) { + u.second->metrics_cnt(); + } + + return &dl_res->phy; +} + +mac_nr::ul_sched_t* mac_nr::get_ul_sched(const srsran_slot_cfg_t& slot_cfg) +{ + slot_point pusch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx}; + ul_sched_t* ul_sched = sched->get_ul_sched(pusch_slot, 0); + + srsran::rwlock_read_guard rw_lock(rwmutex); + for (auto& pusch : ul_sched->pusch) { + if (ue_db.contains(pusch.sch.grant.rnti)) { + ue_db[pusch.sch.grant.rnti]->metrics_ul_mcs(pusch.sch.grant.tb->mcs); + } + } + return ul_sched; +} + +int mac_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_phy_nr::pucch_info_t& pucch_info) +{ + if (not handle_uci_data(pucch_info.uci_data.cfg.pucch.rnti, pucch_info.uci_data.cfg, pucch_info.uci_data.value)) { + logger.error("Error handling UCI data from PUCCH reception"); + return SRSRAN_ERROR; + } + + // process PUCCH SNR + uint16_t rnti = pucch_info.uci_data.cfg.pucch.rnti; + srsran::rwlock_read_guard rw_lock(rwmutex); + if (ue_db.contains(rnti)) { + ue_db[rnti]->metrics_pucch_sinr(pucch_info.csi.snr_dB); + } + + return SRSRAN_SUCCESS; +} + +bool mac_nr::handle_uci_data(uint16_t rnti, const srsran_uci_cfg_nr_t& cfg_, const srsran_uci_value_nr_t& value) +{ + // Process HARQ-ACK + for (uint32_t i = 0; i < cfg_.ack.count; i++) { + const srsran_harq_ack_bit_t* ack_bit = &cfg_.ack.bits[i]; + bool is_ok = (value.ack[i] == 1) and value.valid; + sched->dl_ack_info(rnti, 0, ack_bit->pid, 0, is_ok); + srsran::rwlock_read_guard rw_lock(rwmutex); + if (ue_db.contains(rnti)) { + ue_db[rnti]->metrics_tx(is_ok, 0 /*TODO get size of packet from scheduler somehow*/); + } + } + + // Process SR + if (value.valid and value.sr > 0) { + sched->ul_sr_info(cfg_.pucch.rnti); + } + + // Process CQI + for (uint32_t i = 0; i < cfg_.nof_csi; i++) { + // Skip if invalid or not supported CSI report + if (not value.valid or cfg_.csi[i].cfg.quantity != SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI or + cfg_.csi[i].cfg.freq_cfg != SRSRAN_CSI_REPORT_FREQ_WIDEBAND or value.csi[i].wideband_cri_ri_pmi_cqi.cqi == 0) { + continue; + } + + // 1. Pass CQI report to scheduler + sched->dl_cqi_info(rnti, 0, value.csi->wideband_cri_ri_pmi_cqi.cqi); + + // 2. Save CQI report for metrics stats + srsran::rwlock_read_guard rw_lock(rwmutex); + if (ue_db.contains(rnti) && value.valid) { + ue_db[rnti]->metrics_dl_cqi(cfg_, value.csi->wideband_cri_ri_pmi_cqi.cqi); + } + } + + return true; +} + +int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::pusch_info_t& pusch_info) +{ + uint16_t rnti = pusch_info.rnti; + uint32_t nof_bytes = pusch_info.pdu->N_bytes; + + // Handle UCI data + if (not handle_uci_data(rnti, pusch_info.uci_cfg, pusch_info.pusch_data.uci)) { + logger.error("Error handling UCI data from PUCCH reception"); + return SRSRAN_ERROR; + } + + sched->ul_crc_info(rnti, 0, pusch_info.pid, pusch_info.pusch_data.tb[0].crc); + + // process only PDUs with CRC=OK + if (pusch_info.pusch_data.tb[0].crc) { + if (pcap) { + pcap->write_ul_crnti_nr( + pusch_info.pdu->msg, pusch_info.pdu->N_bytes, pusch_info.rnti, pusch_info.pid, slot_cfg.idx); + } + + // Decode and send PDU to upper layers + rx->handle_pdu(rnti, std::move(pusch_info.pdu)); + } + srsran::rwlock_read_guard rw_lock(rwmutex); + if (ue_db.contains(rnti)) { + ue_db[rnti]->metrics_rx(pusch_info.pusch_data.tb[0].crc, nof_bytes); + ue_db[rnti]->metrics_pusch_sinr(pusch_info.csi.snr_dB); + } + return SRSRAN_SUCCESS; +} + +srsran::byte_buffer_t* mac_nr::assemble_rar(srsran::const_span grants) +{ + srsran::mac_rar_pdu_nr rar_pdu; + + uint32_t pdsch_tbs = 10; // TODO: how big is the PDSCH? + rar_pdu.init_tx(rar_pdu_buffer.get(), pdsch_tbs); + + for (auto& rar_grant : grants) { + srsran::mac_rar_subpdu_nr& rar_subpdu = rar_pdu.add_subpdu(); + + // set values directly coming from scheduler + rar_subpdu.set_ta(rar_grant.data.ta_cmd); + rar_subpdu.set_rapid(rar_grant.data.preamble_idx); + rar_subpdu.set_temp_crnti(rar_grant.data.temp_crnti); + + // convert Msg3 grant to raw UL grant + srsran_dci_nr_t dci = {}; + srsran_dci_msg_nr_t dci_msg = {}; + if (srsran_dci_nr_ul_pack(&dci, &rar_grant.msg3_dci, &dci_msg) != SRSRAN_SUCCESS) { + logger.error("Couldn't pack Msg3 UL grant"); + return nullptr; + } + + if (logger.info.enabled()) { + std::array str; + srsran_dci_ul_nr_to_str(&dci, &rar_grant.msg3_dci, str.data(), str.size()); + logger.info("Setting RAR Grant %s", str.data()); + } + + // copy only the required bits + std::array packed_ul_grant = {}; + std::copy( + std::begin(dci_msg.payload), std::begin(dci_msg.payload) + SRSRAN_RAR_UL_GRANT_NBITS, packed_ul_grant.begin()); + rar_subpdu.set_ul_grant(packed_ul_grant); + } + + if (rar_pdu.pack() != SRSRAN_SUCCESS) { + logger.error("Couldn't assemble RAR PDU"); + return nullptr; + } + + fmt::memory_buffer buff; + rar_pdu.to_string(buff); + logger.info("%s", srsran::to_c_str(buff)); + + return rar_pdu_buffer.get(); +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr.cc b/srsgnb/src/stack/mac/sched_nr.cc new file mode 100644 index 000000000..eb2e48bcd --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr.cc @@ -0,0 +1,538 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr.h" +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" +#include "srsgnb/hdr/stack/mac/harq_softbuffer.h" +#include "srsgnb/hdr/stack/mac/sched_nr_bwp.h" +#include "srsgnb/hdr/stack/mac/sched_nr_worker.h" +#include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/string_helpers.h" +#include "srsran/common/thread_pool.h" + +namespace srsenb { + +using namespace sched_nr_impl; + +static int assert_ue_cfg_valid(uint16_t rnti, const sched_nr_interface::ue_cfg_t& uecfg); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Class that stores events that are not specific to a CC (e.g. SRs, removal of UEs, buffer state updates) +class sched_nr::event_manager +{ +public: + /// class used to accummulate all processed event messages of a single {slot,cc} and print them in a single log line + struct logger { + explicit logger(int cc_, srslog::basic_logger& logger_) : + log_enabled(logger_.debug.enabled()), cc(cc_), sched_logger(logger_) + { + } + logger(const logger&) = delete; + logger(logger&&) = delete; + logger& operator=(const logger&) = delete; + logger& operator=(logger&&) = delete; + ~logger() + { + if (log_enabled and event_fmtbuf.size() > 0) { + if (cc < 0) { + sched_logger.debug("SCHED: slot events: [%s]", srsran::to_c_str(event_fmtbuf)); + } else { + sched_logger.debug("SCHED: slot events, cc=%d: [%s]", cc, srsran::to_c_str(event_fmtbuf)); + } + } + } + + template + void push(const char* fmt, Args&&... args) + { + if (log_enabled) { + if (event_fmtbuf.size() > 0) { + fmt::format_to(event_fmtbuf, ", "); + } + fmt::format_to(event_fmtbuf, fmt, std::forward(args)...); + } + } + + private: + bool log_enabled; + int cc; + srslog::basic_logger& sched_logger; + fmt::memory_buffer event_fmtbuf; + }; + + explicit event_manager(sched_params_t& params) : + sched_logger(srslog::fetch_basic_logger(params.sched_cfg.logger_name)), carriers(params.cells.size()) + { + } + + /// Enqueue an event that does not map into a ue method (e.g. rem_user, add_user) + void enqueue_event(const char* event_name, srsran::move_callback ev) + { + std::lock_guard lock(event_mutex); + next_slot_events.emplace_back(event_name, std::move(ev)); + } + + /// Enqueue an event that directly maps into a ue method (e.g. ul_sr_info, ul_bsr, etc.) + /// Note: these events can be processed sequentially or in parallel, depending on whether the UE supports CA + void enqueue_ue_event(const char* event_name, uint16_t rnti, srsran::move_callback callback) + { + srsran_assert(rnti != SRSRAN_INVALID_RNTI, "Invalid rnti=0x%x passed to common event manager", rnti); + std::lock_guard lock(event_mutex); + next_slot_ue_events.emplace_back(rnti, event_name, std::move(callback)); + } + + /// Enqueue feedback directed at a given UE in a given cell (e.g. ACKs, CQI) + void enqueue_ue_cc_feedback(const char* event_name, + uint16_t rnti, + uint32_t cc, + srsran::move_callback callback) + { + srsran_assert(rnti != SRSRAN_INVALID_RNTI, "Invalid rnti=0x%x passed to event manager", rnti); + srsran_assert(cc < carriers.size(), "Invalid cc=%d passed to event manager", cc); + std::lock_guard lock(carriers[cc].event_cc_mutex); + carriers[cc].next_slot_ue_events.emplace_back(rnti, cc, event_name, std::move(callback)); + } + + /// Process all events that are not specific to a carrier or that are directed at CA-enabled UEs + /// Note: non-CA UEs are updated later in get_dl_sched, to leverage parallelism + void process_common(ue_map_t& ues) + { + // Extract pending feedback events + current_slot_ue_events.clear(); + current_slot_events.clear(); + { + std::lock_guard ev_lock(event_mutex); + next_slot_ue_events.swap(current_slot_ue_events); + next_slot_events.swap(current_slot_events); + } + + logger evlogger(-1, sched_logger); + + // non-UE specific events + for (event_t& ev : current_slot_events) { + ev.callback(evlogger); + } + + for (ue_event_t& ev : current_slot_ue_events) { + auto ue_it = ues.find(ev.rnti); + if (ue_it == ues.end()) { + sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x.", ev.event_name, ev.rnti); + ev.rnti = SRSRAN_INVALID_RNTI; + } else if (ue_it->second->has_ca()) { + // events specific to existing UEs with CA + ev.callback(*ue_it->second, evlogger); + ev.rnti = SRSRAN_INVALID_RNTI; + } + } + } + + /// Process events synchronized during slot_indication() that are directed at non CA-enabled UEs + void process_cc_events(ue_map_t& ues, uint32_t cc) + { + logger evlogger(cc, sched_logger); + + { + carriers[cc].current_slot_ue_events.clear(); + std::lock_guard lock(carriers[cc].event_cc_mutex); + carriers[cc].current_slot_ue_events.swap(carriers[cc].next_slot_ue_events); + } + + for (ue_event_t& ev : current_slot_ue_events) { + if (ev.rnti == SRSRAN_INVALID_RNTI) { + // events already processed + continue; + } + auto ue_it = ues.find(ev.rnti); + if (ue_it == ues.end()) { + sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x.", ev.event_name, ev.rnti); + ev.rnti = SRSRAN_INVALID_RNTI; + } else if (not ue_it->second->has_ca() and ue_it->second->carriers[cc] != nullptr) { + ev.callback(*ue_it->second, evlogger); + ev.rnti = SRSRAN_INVALID_RNTI; + } + } + + for (ue_cc_event_t& ev : carriers[cc].current_slot_ue_events) { + auto ue_it = ues.find(ev.rnti); + if (ue_it != ues.end() and ue_it->second->carriers[cc] != nullptr) { + ev.callback(*ue_it->second->carriers[cc], evlogger); + } else { + sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x,cc=%d.", ev.event_name, ev.rnti, ev.cc); + } + } + } + +private: + struct event_t { + const char* event_name; + srsran::move_callback callback; + event_t(const char* event_name_, srsran::move_callback c) : + event_name(event_name_), callback(std::move(c)) + { + } + }; + struct ue_event_t { + uint16_t rnti; + const char* event_name; + srsran::move_callback callback; + ue_event_t(uint16_t rnti_, const char* event_name_, srsran::move_callback c) : + rnti(rnti_), event_name(event_name_), callback(std::move(c)) + { + } + }; + struct ue_cc_event_t { + uint16_t rnti; + uint32_t cc; + const char* event_name; + srsran::move_callback callback; + ue_cc_event_t(uint16_t rnti_, + uint32_t cc_, + const char* event_name_, + srsran::move_callback c) : + rnti(rnti_), cc(cc_), event_name(event_name_), callback(std::move(c)) + { + } + }; + + srslog::basic_logger& sched_logger; + + std::mutex event_mutex; + std::deque next_slot_events, current_slot_events; + std::deque next_slot_ue_events, current_slot_ue_events; + struct cc_events { + std::mutex event_cc_mutex; + srsran::deque next_slot_ue_events, current_slot_ue_events; + }; + std::vector carriers; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +class sched_nr::ue_metrics_manager +{ +public: + explicit ue_metrics_manager(ue_map_t& ues_) : ues(ues_) {} + + void stop() + { + std::unique_lock lock(mutex); + if (not stopped) { + stopped = true; + // requests during sched::stop may not be fulfilled by sched main thread + save_metrics_nolock(); + } + } + + /// Blocking call that waits for the metrics to be filled + void get_metrics(mac_metrics_t& requested_metrics) + { + std::unique_lock lock(mutex); + pending_metrics = &requested_metrics; + if (not stopped) { + cvar.wait(lock, [this]() { return pending_metrics == nullptr; }); + } else { + save_metrics_nolock(); + } + } + + /// called from within the scheduler main thread to save metrics + void save_metrics() + { + { + std::unique_lock lock(mutex); + save_metrics_nolock(); + } + cvar.notify_one(); + } + +private: + void save_metrics_nolock() + { + if (pending_metrics == nullptr) { + return; + } + for (mac_ue_metrics_t& ue_metric : pending_metrics->ues) { + if (ues.contains(ue_metric.rnti) and ues[ue_metric.rnti]->carriers[0] != nullptr) { + auto& ue_cc = *ues[ue_metric.rnti]->carriers[0]; + ue_metric.tx_brate = ue_cc.metrics.tx_brate; + ue_metric.tx_errors = ue_cc.metrics.tx_errors; + ue_metric.tx_pkts = ue_cc.metrics.tx_pkts; + ue_cc.metrics = {}; + } + } + pending_metrics = nullptr; + } + + ue_map_t& ues; + + std::mutex mutex; + std::condition_variable cvar; + mac_metrics_t* pending_metrics = nullptr; + bool stopped = false; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +sched_nr::sched_nr() : logger(&srslog::fetch_basic_logger("MAC-NR")), metrics_handler(new ue_metrics_manager{ue_db}) {} + +sched_nr::~sched_nr() +{ + stop(); +} + +void sched_nr::stop() +{ + metrics_handler->stop(); +} + +int sched_nr::config(const sched_args_t& sched_cfg, srsran::const_span cell_list) +{ + cfg = sched_params_t{sched_cfg}; + logger = &srslog::fetch_basic_logger(sched_cfg.logger_name); + + // Initiate UE memory pool + ue_pool.reset(new srsran::circular_stack_pool(8, sizeof(ue), 4)); + + // Initiate Common Sched Configuration + cfg.cells.reserve(cell_list.size()); + for (uint32_t cc = 0; cc < cell_list.size(); ++cc) { + cfg.cells.emplace_back(cc, cell_list[cc], cfg.sched_cfg); + } + + pending_events.reset(new event_manager{cfg}); + + // Initiate cell-specific schedulers + cc_workers.resize(cfg.cells.size()); + for (uint32_t cc = 0; cc < cfg.cells.size(); ++cc) { + cc_workers[cc].reset(new slot_cc_worker{cfg.cells[cc]}); + } + + return SRSRAN_SUCCESS; +} + +void sched_nr::ue_cfg(uint16_t rnti, const ue_cfg_t& uecfg) +{ + srsran_assert(assert_ue_cfg_valid(rnti, uecfg) == SRSRAN_SUCCESS, "Invalid UE configuration"); + pending_events->enqueue_event("ue_cfg", [this, rnti, uecfg](event_manager::logger& ev_logger) { + if (ue_cfg_impl(rnti, uecfg) == SRSRAN_SUCCESS) { + ev_logger.push("ue_cfg(0x{:x})", rnti); + } else { + logger->warning("Failed to create UE object for rnti=0x{:x}", rnti); + } + }); +} + +void sched_nr::ue_rem(uint16_t rnti) +{ + pending_events->enqueue_event("ue_rem", [this, rnti](event_manager::logger& ev_logger) { + ue_db.erase(rnti); + logger->info("SCHED: Removed user rnti=0x%x", rnti); + ev_logger.push("ue_rem(0x{:x})", rnti); + }); +} + +int sched_nr::add_ue_impl(uint16_t rnti, sched_nr_impl::unique_ue_ptr u) +{ + logger->info("SCHED: New user rnti=0x%x, cc=%d", rnti, cfg.cells[0].cc); + return ue_db.insert(rnti, std::move(u)).has_value() ? SRSRAN_SUCCESS : SRSRAN_ERROR; +} + +int sched_nr::ue_cfg_impl(uint16_t rnti, const ue_cfg_t& uecfg) +{ + if (not ue_db.contains(rnti)) { + // create user object + unique_ue_ptr u = srsran::make_pool_obj_with_fallback(*ue_pool, rnti, rnti, uecfg, cfg); + return add_ue_impl(rnti, std::make_unique(rnti, uecfg, cfg)); + } + ue_db[rnti]->set_cfg(uecfg); + return SRSRAN_SUCCESS; +} + +// NOTE: there is no parallelism in these operations +void sched_nr::slot_indication(slot_point slot_tx) +{ + srsran_assert(worker_count.load(std::memory_order_relaxed) == 0, + "Call of sched slot_indication when previous TTI has not been completed"); + // mark the start of slot. + current_slot_tx = slot_tx; + worker_count.store(static_cast(cfg.cells.size()), std::memory_order_relaxed); + + // process non-cc specific feedback if pending (e.g. SRs, buffer state updates, UE config) for CA-enabled UEs + // Note: non-CA UEs are updated later in get_dl_sched, to leverage parallelism + pending_events->process_common(ue_db); + + // prepare CA-enabled UEs internal state for new slot + // Note: non-CA UEs are updated later in get_dl_sched, to leverage parallelism + for (auto& u : ue_db) { + if (u.second->has_ca()) { + u.second->new_slot(slot_tx); + } + } + + // If UE metrics were externally requested, store the current UE state + metrics_handler->save_metrics(); +} + +/// Generate {pdcch_slot,cc} scheduling decision +sched_nr::dl_res_t* sched_nr::get_dl_sched(slot_point pdsch_tti, uint32_t cc) +{ + srsran_assert(pdsch_tti == current_slot_tx, "Unexpected pdsch_tti slot received"); + + // process non-cc specific feedback if pending (e.g. SRs, buffer state updates, UE config) for non-CA UEs + pending_events->process_cc_events(ue_db, cc); + + // prepare non-CA UEs internal state for new slot + for (auto& u : ue_db) { + if (not u.second->has_ca() and u.second->carriers[cc] != nullptr) { + u.second->new_slot(current_slot_tx); + } + } + + // Process pending CC-specific feedback, generate {slot_idx,cc} scheduling decision + sched_nr::dl_res_t* ret = cc_workers[cc]->run_slot(pdsch_tti, ue_db); + + // decrement the number of active workers + int rem_workers = worker_count.fetch_sub(1, std::memory_order_release) - 1; + srsran_assert(rem_workers >= 0, "invalid number of calls to get_dl_sched(slot, cc)"); + if (rem_workers == 0) { + // Last Worker to finish slot + // TODO: Sync sched results with ue_db state + } + + return ret; +} + +/// Fetch {ul_slot,cc} UL scheduling decision +sched_nr::ul_res_t* sched_nr::get_ul_sched(slot_point slot_ul, uint32_t cc) +{ + return cc_workers[cc]->get_ul_sched(slot_ul); +} + +void sched_nr::get_metrics(mac_metrics_t& metrics) +{ + metrics_handler->get_metrics(metrics); +} + +int sched_nr::dl_rach_info(const rar_info_t& rar_info) +{ + // create user object outside of sched main thread + unique_ue_ptr u = + srsran::make_pool_obj_with_fallback(*ue_pool, rar_info.temp_crnti, rar_info.temp_crnti, rar_info.cc, cfg); + + // enqueue UE creation event + RACH handling + auto add_ue = [this, rar_info, u = std::move(u)](event_manager::logger& ev_logger) mutable { + uint16_t rnti = rar_info.temp_crnti; + if (add_ue_impl(rnti, std::move(u)) == SRSRAN_SUCCESS) { + ev_logger.push("dl_rach_info(temp c-rnti=0x{:x})", rar_info.temp_crnti); + // RACH is handled only once the UE object is created and inserted in the ue_db + uint32_t cc = rar_info.cc; + cc_workers[cc]->dl_rach_info(rar_info); + } else { + logger->warning("Failed to create UE object with rnti=0x%x", rar_info.temp_crnti); + } + }; + pending_events->enqueue_event("dl_rach_info", std::move(add_ue)); + return SRSRAN_SUCCESS; +} + +void sched_nr::dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) +{ + auto callback = [pid, tb_idx, ack](ue_carrier& ue_cc, event_manager::logger& ev_logger) { + if (ue_cc.dl_ack_info(pid, tb_idx, ack) >= 0) { + ev_logger.push("0x{:x}: dl_ack_info(pid={}, ack={})", ue_cc.rnti, pid, ack ? "OK" : "KO"); + } + }; + pending_events->enqueue_ue_cc_feedback("dl_ack_info", rnti, cc, callback); +} + +void sched_nr::ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) +{ + auto callback = [pid, crc](ue_carrier& ue_cc, event_manager::logger& ev_logger) { + if (ue_cc.ul_crc_info(pid, crc) >= 0) { + ev_logger.push("0x{:x}: ul_crc_info(pid={}, crc={})", ue_cc.rnti, pid, crc ? "OK" : "KO"); + } + }; + pending_events->enqueue_ue_cc_feedback("ul_crc_info", rnti, cc, callback); +} + +void sched_nr::ul_sr_info(uint16_t rnti) +{ + pending_events->enqueue_ue_event("ul_sr_info", rnti, [](ue& u, event_manager::logger& evlogger) { + u.ul_sr_info(); + evlogger.push("0x{:x}: ul_sr_info()", u.rnti); + }); +} + +void sched_nr::ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) +{ + pending_events->enqueue_ue_event("ul_bsr", rnti, [lcg_id, bsr](ue& u, event_manager::logger& evlogger) { + u.ul_bsr(lcg_id, bsr); + evlogger.push("0x{:x}: ul_bsr(lcg={}, bsr={})", u.rnti, lcg_id, bsr); + }); +} + +void sched_nr::dl_mac_ce(uint16_t rnti, uint32_t ce_lcid) +{ + pending_events->enqueue_ue_event("dl_mac_ce", rnti, [ce_lcid](ue& u, event_manager::logger& event_logger) { + // CE is added to list of pending CE + u.add_dl_mac_ce(ce_lcid, 1); + event_logger.push("0x{:x}: dl_mac_ce(lcid={})", u.rnti, ce_lcid); + }); +} + +void sched_nr::dl_buffer_state(uint16_t rnti, uint32_t lcid, uint32_t newtx, uint32_t retx) +{ + pending_events->enqueue_ue_event( + "dl_buffer_state", rnti, [lcid, newtx, retx](ue& u, event_manager::logger& event_logger) { + u.rlc_buffer_state(lcid, newtx, retx); + event_logger.push("0x{:x}: dl_buffer_state(lcid={}, bsr={},{})", u.rnti, lcid, newtx, retx); + }); +} + +void sched_nr::dl_cqi_info(uint16_t rnti, uint32_t cc, uint32_t cqi_value) +{ + auto callback = [cqi_value](ue_carrier& ue_cc, event_manager::logger& ev_logger) { + ue_cc.dl_cqi = cqi_value; + ev_logger.push("0x{:x}: dl_cqi_info(cqi={})", ue_cc.rnti, ue_cc.dl_cqi); + }; + pending_events->enqueue_ue_cc_feedback("dl_cqi_info", rnti, cc, callback); +} + +#define VERIFY_INPUT(cond, msg, ...) \ + do { \ + if (not(cond)) { \ + srslog::fetch_basic_logger("MAC").warning(msg, ##__VA_ARGS__); \ + return SRSRAN_ERROR; \ + } \ + } while (0) + +int assert_ue_cfg_valid(uint16_t rnti, const sched_nr_interface::ue_cfg_t& uecfg) +{ + VERIFY_INPUT(std::count(&uecfg.phy_cfg.pdcch.coreset_present[0], + &uecfg.phy_cfg.pdcch.coreset_present[SRSRAN_UE_DL_NR_MAX_NOF_CORESET], + true) > 0, + "Provided rnti=0x%x configuration does not contain any coreset", + rnti); + return SRSRAN_SUCCESS; +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_bwp.cc b/srsgnb/src/stack/mac/sched_nr_bwp.cc new file mode 100644 index 000000000..9ba2f2300 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_bwp.cc @@ -0,0 +1,178 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_bwp.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" + +namespace srsenb { +namespace sched_nr_impl { + +ra_sched::ra_sched(const bwp_params_t& bwp_cfg_) : + bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger(bwp_cfg_.sched_cfg.logger_name)) +{} + +alloc_result +ra_sched::allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc) +{ + const uint32_t rar_aggr_level = 2; + prb_bitmap prbs = slot_grid.res_grid()[slot_grid.get_pdcch_tti()].pdschs.occupied_prbs( + bwp_cfg->cfg.pdcch.ra_search_space.id, srsran_dci_format_nr_1_0); + + alloc_result ret = alloc_result::other_cause; + srsran::const_span msg3_grants{rar.msg3_grant}; + for (nof_grants_alloc = rar.msg3_grant.size(); nof_grants_alloc > 0; nof_grants_alloc--) { + ret = alloc_result::invalid_coderate; + uint32_t start_prb_idx = 0; + for (uint32_t nprb = 4; nprb < bwp_cfg->cfg.rb_width and ret == alloc_result::invalid_coderate; ++nprb) { + prb_interval interv = find_empty_interval_of_length(prbs, nprb, start_prb_idx); + start_prb_idx = interv.start(); + if (interv.length() == nprb) { + ret = + slot_grid.alloc_rar_and_msg3(rar.ra_rnti, rar_aggr_level, interv, msg3_grants.subspan(0, nof_grants_alloc)); + } else { + ret = alloc_result::no_sch_space; + } + } + + // If allocation was not successful because there were not enough RBGs, try allocating fewer Msg3 grants + if (ret != alloc_result::invalid_coderate and ret != alloc_result::no_sch_space) { + break; + } + } + if (ret != alloc_result::success) { + logger.info("SCHED: RAR allocation for L=%d was postponed. Cause=%s", rar_aggr_level, to_string(ret)); + } + return ret; +} + +void ra_sched::run_slot(bwp_slot_allocator& slot_alloc) +{ + slot_point pdcch_slot = slot_alloc.get_pdcch_tti(); + slot_point msg3_slot = pdcch_slot + bwp_cfg->pusch_ra_list[0].msg3_delay; + if (not bwp_cfg->slots[pdcch_slot.slot_idx()].is_dl or not bwp_cfg->slots[msg3_slot.slot_idx()].is_ul) { + // RAR only allowed if PDCCH is available and respective Msg3 slot is available for UL + return; + } + + for (auto it = pending_rars.begin(); it != pending_rars.end();) { + pending_rar_t& rar = *it; + + // In case of RAR outside RAR window: + // - if window has passed, discard RAR + // - if window hasn't started, stop loop, as RARs are ordered by TTI + if (not rar.rar_win.contains(pdcch_slot)) { + if (pdcch_slot >= rar.rar_win.stop()) { + fmt::memory_buffer str_buffer; + fmt::format_to(str_buffer, + "SCHED: Could not transmit RAR within the window={}, PRACH={}, RAR={}", + rar.rar_win, + rar.prach_slot, + pdcch_slot); + srsran::console("%s\n", srsran::to_c_str(str_buffer)); + logger.warning("%s", srsran::to_c_str(str_buffer)); + it = pending_rars.erase(it); + continue; + } + return; + } + + // Try to schedule DCIs + RBGs for RAR Grants + uint32_t nof_rar_allocs = 0; + alloc_result ret = allocate_pending_rar(slot_alloc, rar, nof_rar_allocs); + + if (ret == alloc_result::success) { + // If RAR allocation was successful: + // - in case all Msg3 grants were allocated, remove pending RAR, and continue with following RAR + // - otherwise, erase only Msg3 grants that were allocated, and stop iteration + + if (nof_rar_allocs == rar.msg3_grant.size()) { + it = pending_rars.erase(it); + } else { + std::copy(rar.msg3_grant.begin() + nof_rar_allocs, rar.msg3_grant.end(), rar.msg3_grant.begin()); + rar.msg3_grant.resize(rar.msg3_grant.size() - nof_rar_allocs); + break; + } + } else { + // If RAR allocation was not successful: + // - in case of unavailable PDCCH space, try next pending RAR allocation + // - otherwise, stop iteration + if (ret != alloc_result::no_cch_space) { + break; + } + ++it; + } + } +} + +/// See TS 38.321, 5.1.3 - RAP transmission +int ra_sched::dl_rach_info(const dl_sched_rar_info_t& rar_info) +{ + // RA-RNTI = 1 + s_id + 14 × t_id + 14 × 80 × f_id + 14 × 80 × 8 × ul_carrier_id + // s_id = index of the first OFDM symbol (0 <= s_id < 14) + // t_id = index of first slot of the PRACH (0 <= t_id < 80) + // f_id = index of the PRACH in the freq domain (0 <= f_id < 8) (for FDD, f_id=0) + // ul_carrier_id = 0 for NUL and 1 for SUL carrier + uint16_t ra_rnti = 1 + rar_info.ofdm_symbol_idx + 14 * rar_info.prach_slot.slot_idx() + 14 * 80 * rar_info.freq_idx; + + logger.info("SCHED: New PRACH slot=%d, preamble=%d, ra-rnti=0x%x, temp_crnti=0x%x, ta_cmd=%d, msg3_size=%d", + rar_info.prach_slot.to_uint(), + rar_info.preamble_idx, + ra_rnti, + rar_info.temp_crnti, + rar_info.ta_cmd, + rar_info.msg3_size); + + // find pending rar with same RA-RNTI + for (pending_rar_t& r : pending_rars) { + if (r.prach_slot == rar_info.prach_slot and ra_rnti == r.ra_rnti) { + if (r.msg3_grant.full()) { + logger.warning("PRACH ignored, as the the maximum number of RAR grants per tti has been reached"); + return SRSRAN_ERROR; + } + r.msg3_grant.push_back(rar_info); + return SRSRAN_SUCCESS; + } + } + + // create new RAR + pending_rar_t p; + p.ra_rnti = ra_rnti; + p.prach_slot = rar_info.prach_slot; + const static uint32_t prach_duration = 1; + for (slot_point t = rar_info.prach_slot + prach_duration; t < rar_info.prach_slot + bwp_cfg->slots.size(); ++t) { + if (bwp_cfg->slots[t.slot_idx()].is_dl) { + p.rar_win = {t, t + bwp_cfg->cfg.rar_window_size}; + break; + } + } + p.msg3_grant.push_back(rar_info); + pending_rars.push_back(p); + + return SRSRAN_SUCCESS; +} + +bwp_manager::bwp_manager(const bwp_params_t& bwp_cfg) : + cfg(&bwp_cfg), ra(bwp_cfg), si(bwp_cfg), grid(bwp_cfg), data_sched(new sched_nr_time_rr()) +{} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_cfg.cc b/srsgnb/src/stack/mac/sched_nr_cfg.cc new file mode 100644 index 000000000..bd0f23718 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_cfg.cc @@ -0,0 +1,186 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_cfg.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsran/adt/optional_array.h" +#include "srsran/asn1/rrc_nr_utils.h" +extern "C" { +#include "srsran/phy/phch/ra_ul_nr.h" +} + +namespace srsenb { +namespace sched_nr_impl { + +void get_dci_locs(const srsran_coreset_t& coreset, + const srsran_search_space_t& search_space, + uint16_t rnti, + bwp_cce_pos_list& cce_locs) +{ + for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { + for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { + pdcch_cce_pos_list pdcch_locs; + cce_locs[sl][agg_idx].resize(pdcch_locs.capacity()); + uint32_t n = + srsran_pdcch_nr_locations_coreset(&coreset, &search_space, rnti, agg_idx, sl, cce_locs[sl][agg_idx].data()); + cce_locs[sl][agg_idx].resize(n); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bwp_params_t::bwp_params_t(const cell_config_manager& cell, uint32_t bwp_id_, const sched_nr_bwp_cfg_t& bwp_cfg) : + cell_cfg(cell), + sched_cfg(cell.sched_args), + cc(cell.cc), + bwp_id(bwp_id_), + cfg(bwp_cfg), + nof_prb(cell_cfg.carrier.nof_prb), + logger(srslog::fetch_basic_logger(sched_cfg.logger_name)), + cached_empty_prb_mask(bwp_cfg.rb_width, bwp_cfg.start_rb, bwp_cfg.pdsch.rbg_size_cfg_1) +{ + srsran_assert(cfg.pdcch.ra_search_space_present, "BWPs without RA search space not supported"); + const uint32_t ra_coreset_id = cfg.pdcch.ra_search_space.coreset_id; + + P = get_P(cfg.rb_width, cfg.pdsch.rbg_size_cfg_1); + N_rbg = get_nof_rbgs(cfg.rb_width, cfg.start_rb, cfg.pdsch.rbg_size_cfg_1); + + for (const srsran_coreset_t& cs : view_active_coresets(cfg.pdcch)) { + coresets.emplace(cs.id); + uint32_t rb_start = srsran_coreset_start_rb(&cs); + coresets[cs.id].prb_limits = prb_interval{rb_start, rb_start + srsran_coreset_get_bw(&cs)}; + coresets[cs.id].usable_common_ss_prb_mask = cached_empty_prb_mask; + + // TS 38.214, 5.1.2.2 - For DCI format 1_0 and common search space, lowest RB of the CORESET is the RB index = 0 + coresets[cs.id].usable_common_ss_prb_mask |= prb_interval(0, rb_start); + coresets[cs.id].dci_1_0_prb_limits = prb_interval{rb_start, cfg.rb_width}; + + // TS 38.214, 5.1.2.2.2 - when DCI format 1_0, common search space and CORESET#0 is configured for the cell, + // RA type 1 allocs shall be within the CORESET#0 region + if (cfg.pdcch.coreset_present[0]) { + coresets[cs.id].dci_1_0_prb_limits = coresets[cs.id].prb_limits; + coresets[cs.id].usable_common_ss_prb_mask |= prb_interval(coresets[cs.id].prb_limits.stop(), cfg.rb_width); + } + } + + // Derive params of individual slots + uint32_t nof_slots = SRSRAN_NSLOTS_PER_FRAME_NR(cfg.numerology_idx); + for (size_t sl = 0; sl < nof_slots; ++sl) { + slot_cfg sl_cfg{}; + sl_cfg.is_dl = srsran_duplex_nr_is_dl(&cell_cfg.duplex, cfg.numerology_idx, sl); + sl_cfg.is_ul = srsran_duplex_nr_is_ul(&cell_cfg.duplex, cfg.numerology_idx, sl); + slots.push_back(sl_cfg); + } + + pusch_ra_list.resize(cfg.pusch.nof_common_time_ra); + srsran_sch_grant_nr_t grant; + for (uint32_t m = 0; m < cfg.pusch.nof_common_time_ra; ++m) { + int ret = + srsran_ra_ul_nr_time(&cfg.pusch, srsran_rnti_type_ra, srsran_search_space_type_rar, ra_coreset_id, m, &grant); + srsran_assert(ret == SRSRAN_SUCCESS, "Failed to obtain RA config"); + pusch_ra_list[m].msg3_delay = grant.k; + ret = srsran_ra_ul_nr_time(&cfg.pusch, srsran_rnti_type_c, srsran_search_space_type_ue, ra_coreset_id, m, &grant); + pusch_ra_list[m].K = grant.k; + pusch_ra_list[m].S = grant.S; + pusch_ra_list[m].L = grant.L; + srsran_assert(ret == SRSRAN_SUCCESS, "Failed to obtain RA config"); + } + srsran_assert(not pusch_ra_list.empty(), "Time-Domain Resource Allocation not valid"); + + for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { + for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { + rar_cce_list[sl][agg_idx].resize(SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR); + int n = srsran_pdcch_nr_locations_coreset(&cell_cfg.bwps[0].cfg.pdcch.coreset[ra_coreset_id], + &cell_cfg.bwps[0].cfg.pdcch.ra_search_space, + 0, + agg_idx, + sl, + rar_cce_list[sl][agg_idx].data()); + srsran_assert(n >= 0, "Failed to configure RAR DCI locations"); + rar_cce_list[sl][agg_idx].resize(n); + } + } + + for (uint32_t ss_id = 0; ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++ss_id) { + if (not cell_cfg.bwps[0].cfg.pdcch.search_space_present[ss_id]) { + continue; + } + auto& ss = cell_cfg.bwps[0].cfg.pdcch.search_space[ss_id]; + auto& coreset = cell_cfg.bwps[0].cfg.pdcch.coreset[ss.coreset_id]; + common_cce_list.emplace(ss_id); + bwp_cce_pos_list& ss_cce_list = common_cce_list[ss_id]; + for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { + for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { + ss_cce_list[sl][agg_idx].resize(SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR); + int n = srsran_pdcch_nr_locations_coreset( + &coreset, &ss, SRSRAN_SIRNTI, agg_idx, sl, ss_cce_list[sl][agg_idx].data()); + srsran_assert(n >= 0, "Failed to configure DCI locations of search space id=%d", ss_id); + ss_cce_list[sl][agg_idx].resize(n); + } + } + } +} + +cell_config_manager::cell_config_manager(uint32_t cc_, + const sched_nr_cell_cfg_t& cell, + const sched_args_t& sched_args_) : + cc(cc_), sched_args(sched_args_), default_ue_phy_cfg(get_common_ue_phy_cfg(cell)), sibs(cell.sibs) +{ + carrier.pci = cell.pci; + carrier.dl_center_frequency_hz = cell.dl_center_frequency_hz; + carrier.ul_center_frequency_hz = cell.ul_center_frequency_hz; + carrier.ssb_center_freq_hz = cell.ssb_center_freq_hz; + carrier.nof_prb = cell.dl_cell_nof_prb; + carrier.start = 0; // TODO: Check + carrier.max_mimo_layers = cell.nof_layers; + carrier.offset_to_carrier = cell.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; + carrier.scs = (srsran_subcarrier_spacing_t)cell.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value; + + // TDD-UL-DL-ConfigCommon + duplex.mode = SRSRAN_DUPLEX_MODE_FDD; + if (cell.tdd_ul_dl_cfg_common.has_value()) { + bool success = srsran::make_phy_tdd_cfg(*cell.tdd_ul_dl_cfg_common, &duplex); + srsran_assert(success, "Failed to generate Cell TDD config"); + } + + // Set SSB params + make_ssb_cfg(cell, &ssb); + + // MIB + make_mib_cfg(cell, &mib); + + bwps.reserve(cell.bwps.size()); + for (uint32_t i = 0; i < cell.bwps.size(); ++i) { + bwps.emplace_back(*this, i, cell.bwps[i]); + } + srsran_assert(not bwps.empty(), "No BWPs were configured"); +} + +sched_params_t::sched_params_t(const sched_args_t& sched_cfg_) : sched_cfg(sched_cfg_) +{ + srsran_assert(sched_cfg.fixed_ul_mcs >= 0, "Dynamic DL MCS not supported"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +} // namespace sched_nr_impl +} // namespace srsenb \ No newline at end of file diff --git a/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc new file mode 100644 index 000000000..fc90718f4 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_grant_allocator.cc @@ -0,0 +1,509 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h" +#include "srsgnb/hdr/stack/mac/sched_nr_bwp.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsran/mac/mac_sch_pdu_nr.h" + +namespace srsenb { +namespace sched_nr_impl { + +using candidate_ss_list_t = + srsran::bounded_vector; + +candidate_ss_list_t find_ss(const srsran_pdcch_cfg_nr_t& pdcch, + uint32_t aggr_idx, + srsran_rnti_type_t rnti_type, + srsran::const_span prio_dcis) +{ + candidate_ss_list_t ret; + auto active_ss_lst = view_active_search_spaces(pdcch); + + auto contains_dci_fmt = [prio_dcis, aggr_idx](const srsran_search_space_t& ss) { + if (ss.nof_candidates[aggr_idx] > 0 and ss.nof_formats > 0) { + for (uint32_t i = 0; i < prio_dcis.size(); ++i) { + for (uint32_t j = 0; j < ss.nof_formats; ++j) { + if (ss.formats[j] == prio_dcis[i]) { + return true; + } + } + } + } + return false; + }; + auto is_common_ss_allowed = [rnti_type](srsran_search_space_type_t ss_type) { + switch (rnti_type) { + case srsran_rnti_type_c: + return ss_type == srsran_search_space_type_common_1 or ss_type == srsran_search_space_type_common_3; + case srsran_rnti_type_tc: + case srsran_rnti_type_ra: + // TODO: Fix UE config to not use common3 + return ss_type == srsran_search_space_type_common_1 or ss_type == srsran_search_space_type_common_3; + case srsran_rnti_type_si: + return ss_type == srsran_search_space_type_common_0; + default: + // TODO: Remaining cases + break; + } + return false; + }; + + if (rnti_type == srsran_rnti_type_c) { + // First search UE-specific + for (const srsran_search_space_t& ss : active_ss_lst) { + if (ss.type == srsran_search_space_type_ue and contains_dci_fmt(ss)) { + ret.push_back(&ss); + } + } + } + for (const srsran_search_space_t& ss : active_ss_lst) { + if (is_common_ss_allowed(ss.type) and contains_dci_fmt(ss)) { + ret.push_back(&ss); + } + } + return ret; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bwp_slot_grid::bwp_slot_grid(const bwp_params_t& bwp_cfg_, uint32_t slot_idx_) : + slot_idx(slot_idx_), + cfg(&bwp_cfg_), + pdcchs(bwp_cfg_, slot_idx_, dl.phy.pdcch_dl, dl.phy.pdcch_ul), + pdschs(bwp_cfg_, slot_idx_, dl.phy.pdsch), + puschs(bwp_cfg_, slot_idx_, ul.pusch), + rar_softbuffer(harq_softbuffer_pool::get_instance().get_tx(bwp_cfg_.cfg.rb_width)) +{} + +void bwp_slot_grid::reset() +{ + pdcchs.reset(); + pdschs.reset(); + puschs.reset(); + dl.phy.ssb.clear(); + dl.phy.nzp_csi_rs.clear(); + dl.data.clear(); + dl.rar.clear(); + dl.sib_idxs.clear(); + ul.pucch.clear(); + pending_acks.clear(); +} + +bwp_res_grid::bwp_res_grid(const bwp_params_t& bwp_cfg_) : cfg(&bwp_cfg_) +{ + for (uint32_t sl = 0; sl < slots.capacity(); ++sl) { + slots.emplace_back(*cfg, sl % static_cast(SRSRAN_NSLOTS_PER_FRAME_NR(bwp_cfg_.cell_cfg.carrier.scs))); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bwp_slot_allocator::bwp_slot_allocator(bwp_res_grid& bwp_grid_, slot_point pdcch_slot_, slot_ue_map_t& ues_) : + logger(bwp_grid_.cfg->logger), cfg(*bwp_grid_.cfg), bwp_grid(bwp_grid_), pdcch_slot(pdcch_slot_), slot_ues(ues_) +{} + +alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, + uint32_t si_idx, + uint32_t si_ntx, + const prb_interval& prbs, + tx_harq_softbuffer& softbuffer) +{ + static const uint32_t ss_id = 0; + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; + + bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_slot]; + + // Verify there is space in PDSCH + alloc_result ret = bwp_pdcch_slot.pdschs.is_si_grant_valid(ss_id, prbs); + if (ret != alloc_result::success) { + return ret; + } + + // Allocate PDCCH + auto pdcch_result = bwp_pdcch_slot.pdcchs.alloc_si_pdcch(ss_id, aggr_idx); + if (pdcch_result.is_error()) { + logger.warning("SCHED: Cannot allocate SIB due to lack of PDCCH space."); + return pdcch_result.error(); + } + pdcch_dl_t& pdcch = *pdcch_result.value(); + + // Allocate PDSCH (no need to verify again if there is space in PDSCH) + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_si_pdsch_unchecked(ss_id, prbs, pdcch.dci); + + // Generate DCI for SIB + pdcch.dci_cfg.coreset0_bw = srsran_coreset_get_bw(&cfg.cfg.pdcch.coreset[0]); + pdcch.dci.mcs = 5; + pdcch.dci.rv = 0; + pdcch.dci.sii = si_idx == 0 ? 0 : 1; + + // Generate PDSCH + srsran_slot_cfg_t slot_cfg; + slot_cfg.idx = pdcch_slot.to_uint(); + int code = srsran_ra_dl_dci_to_grant_nr( + &cfg.cell_cfg.carrier, &slot_cfg, &cfg.cfg.pdsch, &pdcch.dci, &pdsch.sch, &pdsch.sch.grant); + if (code != SRSRAN_SUCCESS) { + logger.warning("Error generating SIB PDSCH grant."); + bwp_pdcch_slot.pdcchs.cancel_last_pdcch(); + bwp_pdcch_slot.dl.phy.pdsch.pop_back(); + return alloc_result::other_cause; + } + pdsch.sch.grant.tb[0].softbuffer.tx = softbuffer.get(); + + // Store SI msg index + bwp_pdcch_slot.dl.sib_idxs.push_back(si_idx); + + return alloc_result::success; +} + +alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t ra_rnti, + uint32_t aggr_idx, + prb_interval interv, + srsran::const_span pending_rachs) +{ + static const uint32_t msg3_nof_prbs = 3, m = 0; + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; + + bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_slot]; + slot_point msg3_slot = pdcch_slot + cfg.pusch_ra_list[m].msg3_delay; + bwp_slot_grid& bwp_msg3_slot = bwp_grid[msg3_slot]; + + // Verify there is space in PDSCH for RAR + alloc_result ret = bwp_pdcch_slot.pdschs.is_rar_grant_valid(interv); + if (ret != alloc_result::success) { + return ret; + } + for (auto& rach : pending_rachs) { + auto ue_it = slot_ues.find(rach.temp_crnti); + if (ue_it == slot_ues.end()) { + logger.info("SCHED: Postponing rnti=0x%x RAR allocation. Cause: The ue object not yet fully created", + rach.temp_crnti); + return alloc_result::no_rnti_opportunity; + } + } + srsran_sanity_check(not bwp_pdcch_slot.dl.rar.full(), "The #RARs should be below #PDSCHs"); + if (not bwp_pdcch_slot.dl.phy.ssb.empty()) { + // TODO: support concurrent PDSCH and SSB + logger.debug("SCHED: skipping RAR allocation. Cause: concurrent PDSCH and SSB not yet supported"); + return alloc_result::no_sch_space; + } + + // Verify there is space in PUSCH for Msg3s + ret = bwp_msg3_slot.puschs.has_grant_space(pending_rachs.size()); + if (ret != alloc_result::success) { + return ret; + } + // Check Msg3 RB collision + uint32_t total_msg3_nof_prbs = msg3_nof_prbs * pending_rachs.size(); + prb_interval all_msg3_rbs = + find_empty_interval_of_length(bwp_msg3_slot.puschs.occupied_prbs(), total_msg3_nof_prbs, 0); + if (all_msg3_rbs.length() < total_msg3_nof_prbs) { + logger.debug("SCHED: No space in PUSCH for Msg3."); + return alloc_result::sch_collision; + } + + // Allocate PDCCH position for RAR + auto pdcch_result = bwp_pdcch_slot.pdcchs.alloc_rar_pdcch(ra_rnti, aggr_idx); + if (pdcch_result.is_error()) { + // Could not find space in PDCCH + return pdcch_result.error(); + } + pdcch_dl_t& pdcch = *pdcch_result.value(); + pdcch.dci_cfg = slot_ues[pending_rachs[0].temp_crnti]->get_dci_cfg(); + pdcch.dci.mcs = 5; + + // Allocate RAR PDSCH + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_rar_pdsch_unchecked(interv, pdcch.dci); + + // Fill RAR PDSCH content + // TODO: Properly fill Msg3 grants + srsran_slot_cfg_t slot_cfg; + slot_cfg.idx = pdcch_slot.to_uint(); + int code = srsran_ra_dl_dci_to_grant_nr( + &cfg.cell_cfg.carrier, &slot_cfg, &cfg.cfg.pdsch, &pdcch.dci, &pdsch.sch, &pdsch.sch.grant); + srsran_assert(code == SRSRAN_SUCCESS, "Error converting DCI to grant"); + pdsch.sch.grant.tb[0].softbuffer.tx = bwp_pdcch_slot.rar_softbuffer->get(); + + // Generate Msg3 grants in PUSCH + uint32_t last_msg3 = all_msg3_rbs.start(); + const int mcs = 0, max_harq_msg3_retx = 4; + slot_cfg.idx = msg3_slot.to_uint(); + bwp_pdcch_slot.dl.rar.emplace_back(); + sched_nr_interface::rar_t& rar_out = bwp_pdcch_slot.dl.rar.back(); + for (const dl_sched_rar_info_t& grant : pending_rachs) { + slot_ue& ue = slot_ues[grant.temp_crnti]; + + // Generate RAR grant + rar_out.grants.emplace_back(); + auto& rar_grant = rar_out.grants.back(); + rar_grant.data = grant; + fill_dci_from_cfg(cfg, rar_grant.msg3_dci); + // Fill Msg3 DCI context + rar_grant.msg3_dci.ctx.coreset_id = pdcch.dci.ctx.coreset_id; + rar_grant.msg3_dci.ctx.rnti_type = srsran_rnti_type_tc; + rar_grant.msg3_dci.ctx.rnti = ue->rnti; + rar_grant.msg3_dci.ctx.ss_type = srsran_search_space_type_rar; + rar_grant.msg3_dci.ctx.format = srsran_dci_format_nr_rar; + + // Allocate Msg3 PUSCH allocation + prb_interval msg3_interv{last_msg3, last_msg3 + msg3_nof_prbs}; + last_msg3 += msg3_nof_prbs; + pusch_t& pusch = bwp_msg3_slot.puschs.alloc_pusch_unchecked(msg3_interv, rar_grant.msg3_dci); + + // Allocate UL HARQ + ue.h_ul = ue.find_empty_ul_harq(); + srsran_sanity_check(ue.h_ul != nullptr, "Failed to allocate Msg3"); + bool success = ue.h_ul->new_tx(msg3_slot, msg3_interv, mcs, max_harq_msg3_retx, rar_grant.msg3_dci); + srsran_sanity_check(success, "Failed to allocate Msg3"); + + // Generate PUSCH content + success = ue->phy().get_pusch_cfg(slot_cfg, rar_grant.msg3_dci, pusch.sch); + srsran_assert(success, "Error converting DCI to PUSCH grant"); + pusch.sch.grant.tb[0].softbuffer.rx = ue.h_ul->get_softbuffer().get(); + ue.h_ul->set_tbs(pusch.sch.grant.tb[0].tbs); + } + + return alloc_result::success; +} + +// ue is the UE (1 only) that will be allocated +// func computes the grant allocation for this UE +alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, uint32_t ss_id, const prb_grant& dl_grant) +{ + static const uint32_t aggr_idx = 2; + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; + static const srsran_rnti_type_t rnti_type = srsran_rnti_type_c; + + bwp_slot_grid& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot]; + bwp_slot_grid& bwp_pdsch_slot = bwp_grid[ue.pdsch_slot]; + bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot]; // UCI : UL control info + + // Verify there is space in PDSCH + alloc_result ret = bwp_pdcch_slot.pdschs.is_ue_grant_valid(ue.cfg(), ss_id, dci_fmt, dl_grant); + if (ret != alloc_result::success) { + return ret; + } + + alloc_result result = verify_uci_space(bwp_uci_slot); + if (result != alloc_result::success) { + return result; + } + if (ue.h_dl == nullptr) { + logger.warning("SCHED: Trying to allocate rnti=0x%x with no available DL HARQs", ue->rnti); + return result; + } + if (not bwp_pdsch_slot.dl.phy.ssb.empty()) { + // TODO: support concurrent PDSCH and SSB + logger.debug("SCHED: skipping PDSCH allocation. Cause: concurrent PDSCH and SSB not yet supported"); + return alloc_result::no_sch_space; + } + + // Check space in PUCCH/PUSCH for UCI + // TODO + + // Find space and allocate PDCCH + auto pdcch_result = bwp_pdcch_slot.pdcchs.alloc_dl_pdcch(rnti_type, ss_id, aggr_idx, ue.cfg()); + if (pdcch_result.is_error()) { + // Could not find space in PDCCH + return pdcch_result.error(); + } + pdcch_dl_t& pdcch = *pdcch_result.value(); + pdcch.dci_cfg = ue->get_dci_cfg(); + pdcch.dci.pucch_resource = 0; + pdcch.dci.dai = std::count_if(bwp_uci_slot.pending_acks.begin(), + bwp_uci_slot.pending_acks.end(), + [&ue](const harq_ack_t& p) { return p.res.rnti == ue->rnti; }); + pdcch.dci.dai %= 4; + + // Allocate PDSCH + pdsch_t& pdsch = bwp_pdcch_slot.pdschs.alloc_ue_pdsch_unchecked(ss_id, dci_fmt, dl_grant, ue.cfg(), pdcch.dci); + + // Select MCS and Allocate HARQ + int mcs = ue->fixed_pdsch_mcs(); + const static int min_MCS_ccch = 4; + if (ue.h_dl->empty()) { + if (mcs < 0) { + mcs = srsran_ra_nr_cqi_to_mcs(/* cqi */ ue.dl_cqi(), + /* cqi_table_idx */ ue.cfg().phy().csi.reports->cqi_table, + /* mcs_table */ pdsch.sch.sch_cfg.mcs_table, + /* dci_format */ pdcch.dci.ctx.format, + /* search_space_type*/ pdcch.dci.ctx.ss_type, + /* rnti_type */ rnti_type); + if (mcs < 0) { + logger.warning("SCHED: UE rnti=0x%x reported CQI=0 - Using lowest MCS=0", ue->rnti); + mcs = 0; + } + } + // Overwrite MCS if there are pending bytes for LCID. The optimal way would be to verify that there are pending + // bytes and that the MAC SDU for CCCH gets segmented. But since the event of segmentation happens at most a couple + // of times (e.g., to send msg4/RRCSetup), we opt for the less optimal but simpler approach. + if (ue.get_pending_bytes(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH) and mcs < min_MCS_ccch) { + mcs = min_MCS_ccch; + logger.info("SCHED: MCS increased to min value %d to allocate SRB0/CCCH for rnti=0x%x", min_MCS_ccch, ue->rnti); + } + bool success = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, 4, pdcch.dci); + srsran_assert(success, "Failed to allocate DL HARQ"); + } else { + bool success = ue.h_dl->new_retx(ue.pdsch_slot, ue.uci_slot, dl_grant, pdcch.dci); + mcs = ue.h_dl->mcs(); + srsran_assert(success, "Failed to allocate DL HARQ retx"); + } + + srsran_slot_cfg_t slot_cfg; + slot_cfg.idx = ue.pdsch_slot.to_uint(); + // Value 0.95 is from TS 38.214 v15.14.00, Section 5.1.3, page 17 + const static float max_R = 0.95; + double R_prime; + // The purpose of the internal loop is to decrease the MCS if the effective coderate is too high. This loop + // only affects the high MCS values + while (true) { + // Generate PDSCH + bool success = ue->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); + srsran_assert(success, "Error converting DCI to grant"); + if (ue.h_dl->nof_retx() != 0) { + srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); + } + R_prime = pdsch.sch.grant.tb[0].R_prime; + if (ue.h_dl->nof_retx() > 0 or R_prime < max_R or mcs <= 0 or + (ue.get_pending_bytes(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH) and mcs <= min_MCS_ccch)) { + break; + } + // Decrease MCS if first tx and rate is too high + mcs--; + pdcch.dci.mcs = mcs; + } + if (R_prime >= max_R and mcs == 0) { + logger.warning("Couldn't find mcs that leads to R<0.95"); + } + + ue.h_dl->set_mcs(mcs); + ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // set HARQ TBS + pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); + pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); + + // Select scheduled LCIDs and update UE buffer state + bwp_pdsch_slot.dl.data.emplace_back(); + // NOTE: ue.h_dl->tbs() has to be converted from bits to bytes + bool segmented_ccch_pdu = not ue.build_pdu(ue.h_dl->tbs() / 8, bwp_pdsch_slot.dl.data.back()); + if (segmented_ccch_pdu) { + logger.error("SCHED: Insufficient resources to allocate SRB0/CCCH for rnti=0x%x", min_MCS_ccch, ue->rnti); + } + + // Generate PUCCH + bwp_uci_slot.pending_acks.emplace_back(); + bwp_uci_slot.pending_acks.back().phy_cfg = &ue->phy(); + bool success = ue->phy().get_pdsch_ack_resource(pdcch.dci, bwp_uci_slot.pending_acks.back().res); + srsran_assert(success, "Error getting ack resource"); + + return alloc_result::success; +} + +alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_grant) +{ + static const uint32_t aggr_idx = 2; + static const std::array dci_fmt_list{srsran_dci_format_nr_0_1, srsran_dci_format_nr_0_0}; + static const srsran_rnti_type_t rnti_type = srsran_rnti_type_c; + + auto& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot]; + auto& bwp_pusch_slot = bwp_grid[ue.pusch_slot]; + + if (ue.h_ul == nullptr) { + logger.warning("SCHED: Trying to allocate rnti=0x%x with no available UL HARQs", ue->rnti); + return alloc_result::no_rnti_opportunity; + } + + // Choose SearchSpace + DCI format + candidate_ss_list_t ss_candidates = find_ss(ue->phy().pdcch, aggr_idx, rnti_type, dci_fmt_list); + if (ss_candidates.empty()) { + // Could not find space in PDCCH + logger.warning("SCHED: No PDCCH candidates for any of the rnti=0x%x search spaces", ue->rnti); + return alloc_result::no_cch_space; + } + const srsran_search_space_t& ss = *ss_candidates[0]; + + // Verify if PUSCH allocation is valid + alloc_result ret = bwp_pusch_slot.puschs.is_grant_valid(ss.type, ul_grant); + if (ret != alloc_result::success) { + return ret; + } + + auto pdcch_result = bwp_pdcch_slot.pdcchs.alloc_ul_pdcch(ss.id, aggr_idx, ue.cfg()); + if (pdcch_result.is_error()) { + // Could not find space in PDCCH + return pdcch_result.error(); + } + + // Allocation Successful + pdcch_ul_t& pdcch = *pdcch_result.value(); + pdcch.dci_cfg = ue->get_dci_cfg(); + + // Allocate PUSCH + pusch_t& pusch = bwp_pusch_slot.puschs.alloc_pusch_unchecked(ul_grant, pdcch.dci); + + if (ue.h_ul->empty()) { + int mcs = ue->fixed_pusch_mcs(); + bool success = ue.h_ul->new_tx(ue.pusch_slot, ul_grant, mcs, ue->ue_cfg().maxharq_tx, pdcch.dci); + srsran_assert(success, "Failed to allocate UL HARQ"); + } else { + bool success = ue.h_ul->new_retx(ue.pusch_slot, ul_grant, pdcch.dci); + srsran_assert(success, "Failed to allocate UL HARQ retx"); + } + + // Generate PUSCH content + srsran_slot_cfg_t slot_cfg; + slot_cfg.idx = ue.pusch_slot.to_uint(); + pusch.pid = ue.h_ul->pid; + bool success = ue->phy().get_pusch_cfg(slot_cfg, pdcch.dci, pusch.sch); + srsran_assert(success, "Error converting DCI to PUSCH grant"); + pusch.sch.grant.tb[0].softbuffer.rx = ue.h_ul->get_softbuffer().get(); + if (ue.h_ul->nof_retx() == 0) { + ue.h_ul->set_tbs(pusch.sch.grant.tb[0].tbs); // update HARQ with correct TBS + } else { + srsran_assert(pusch.sch.grant.tb[0].tbs == (int)ue.h_ul->tbs(), "The TBS did not remain constant in retx"); + } + + return alloc_result::success; +} + +alloc_result bwp_slot_allocator::verify_uci_space(const bwp_slot_grid& uci_grid) const +{ + if (uci_grid.pending_acks.full()) { + logger.warning("SCHED: No space for ACK."); + return alloc_result::no_grant_space; + } + return alloc_result::success; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +prb_grant find_optimal_dl_grant(bwp_slot_allocator& slot_alloc, const slot_ue& ue, uint32_t ss_id) +{ + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; // TODO: Support more DCI formats + + prb_bitmap used_prb_mask = slot_alloc.occupied_dl_prbs(ue.pdsch_slot, ss_id, dci_fmt); + + prb_interval prb_interv = find_empty_interval_of_length(used_prb_mask, used_prb_mask.size(), 0); + + return prb_interv; +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_harq.cc b/srsgnb/src/stack/mac/sched_nr_harq.cc new file mode 100644 index 000000000..b941d6711 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_harq.cc @@ -0,0 +1,236 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_harq.h" +#include "srsran/common/buffer_pool.h" + +namespace srsenb { +namespace sched_nr_impl { + +int harq_proc::ack_info(uint32_t tb_idx, bool ack) +{ + if (empty(tb_idx)) { + return SRSRAN_ERROR; + } + tb[tb_idx].ack_state = ack; + if (ack) { + tb[tb_idx].active = false; + } + return ack ? tb[tb_idx].tbs : 0; +} + +bool harq_proc::clear_if_maxretx(slot_point slot_rx) +{ + if (has_pending_retx(slot_rx) and nof_retx() + 1 > max_nof_retx()) { + tb[0].active = false; + return true; + } + return false; +} + +void harq_proc::reset() +{ + tb[0].ack_state = false; + tb[0].active = false; + tb[0].n_rtx = 0; + tb[0].mcs = std::numeric_limits::max(); + tb[0].tbs = std::numeric_limits::max(); +} + +bool harq_proc::new_tx(slot_point slot_tx_, + slot_point slot_ack_, + const prb_grant& grant, + uint32_t mcs, + uint32_t max_retx_) +{ + if (not empty()) { + return false; + } + reset(); + max_retx = max_retx_; + slot_tx = slot_tx_; + slot_ack = slot_ack_; + prbs_ = grant; + tb[0].ndi = !tb[0].ndi; + tb[0].mcs = mcs; + tb[0].tbs = 0; + tb[0].active = true; + return true; +} + +bool harq_proc::set_tbs(uint32_t tbs) +{ + if (empty() or nof_retx() > 0) { + return false; + } + tb[0].tbs = tbs; + return true; +} + +bool harq_proc::set_mcs(uint32_t mcs) +{ + if (empty() or nof_retx() > 0) { + return false; + } + tb[0].mcs = mcs; + return true; +} + +bool harq_proc::new_retx(slot_point slot_tx_, slot_point slot_ack_, const prb_grant& grant) +{ + if (grant.is_alloc_type0() != prbs_.is_alloc_type0() or + (grant.is_alloc_type0() and grant.rbgs().count() != prbs_.rbgs().count()) or + (grant.is_alloc_type1() and grant.prbs().length() != prbs_.prbs().length())) { + return false; + } + if (new_retx(slot_tx_, slot_ack_)) { + prbs_ = grant; + return true; + } + return false; +} + +bool harq_proc::new_retx(slot_point slot_tx_, slot_point slot_ack_) +{ + if (empty()) { + return false; + } + slot_tx = slot_tx_; + slot_ack = slot_ack_; + tb[0].ack_state = false; + tb[0].n_rtx++; + return true; +} + +dl_harq_proc::dl_harq_proc(uint32_t id_, uint32_t nprb) : + harq_proc(id_), softbuffer(harq_softbuffer_pool::get_instance().get_tx(nprb)), pdu(srsran::make_byte_buffer()) +{} + +void dl_harq_proc::fill_dci(srsran_dci_dl_nr_t& dci) +{ + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + dci.pid = pid; + dci.ndi = ndi(); + dci.mcs = mcs(); + dci.rv = rv_idx[nof_retx() % 4]; + if (dci.ctx.format == srsran_dci_format_nr_1_0) { + dci.harq_feedback = (slot_ack - slot_tx) - 1; + } else { + dci.harq_feedback = slot_tx.to_uint(); + } +} + +bool dl_harq_proc::new_tx(slot_point slot_tx, + slot_point slot_ack, + const prb_grant& grant, + uint32_t mcs_, + uint32_t max_retx, + srsran_dci_dl_nr_t& dci) +{ + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + if (harq_proc::new_tx(slot_tx, slot_ack, grant, mcs_, max_retx)) { + pdu->clear(); + fill_dci(dci); + return true; + } + return false; +} + +bool dl_harq_proc::new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + if (harq_proc::new_retx(slot_tx, slot_ack, grant)) { + fill_dci(dci); + return true; + } + return false; +} + +void ul_harq_proc::fill_dci(srsran_dci_ul_nr_t& dci) +{ + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + dci.pid = pid; + dci.ndi = ndi(); + dci.mcs = mcs(); + dci.rv = rv_idx[nof_retx() % 4]; +} + +bool ul_harq_proc::new_tx(slot_point slot_tx, + const prb_grant& grant, + uint32_t mcs_, + uint32_t max_retx, + srsran_dci_ul_nr_t& dci) +{ + const static uint32_t rv_idx[4] = {0, 2, 3, 1}; + + if (harq_proc::new_tx(slot_tx, slot_tx, grant, mcs_, max_retx)) { + fill_dci(dci); + return true; + } + return false; +} + +bool ul_harq_proc::new_retx(slot_point slot_tx, const prb_grant& grant, srsran_dci_ul_nr_t& dci) +{ + if (harq_proc::new_retx(slot_tx, slot_tx, grant)) { + fill_dci(dci); + return true; + } + return false; +} + +harq_entity::harq_entity(uint16_t rnti_, uint32_t nprb, uint32_t nof_harq_procs, srslog::basic_logger& logger_) : + rnti(rnti_), logger(logger_) +{ + // Create HARQs + dl_harqs.reserve(nof_harq_procs); + ul_harqs.reserve(nof_harq_procs); + for (uint32_t pid = 0; pid < nof_harq_procs; ++pid) { + dl_harqs.emplace_back(pid, nprb); + ul_harqs.emplace_back(pid, nprb); + } +} + +void harq_entity::new_slot(slot_point slot_rx_) +{ + slot_rx = slot_rx_; + for (harq_proc& dl_h : dl_harqs) { + if (dl_h.clear_if_maxretx(slot_rx)) { + logger.info("SCHED: discarding rnti=0x%x, DL TB pid=%d. Cause: Maximum number of retx exceeded (%d)", + rnti, + dl_h.pid, + dl_h.max_nof_retx()); + } + } + for (harq_proc& ul_h : ul_harqs) { + if (ul_h.clear_if_maxretx(slot_rx)) { + logger.info("SCHED: discarding rnti=0x%x, UL TB pid=%d. Cause: Maximum number of retx exceeded (%d)", + rnti, + ul_h.pid, + ul_h.max_nof_retx()); + } + } +} + +} // namespace sched_nr_impl +} // namespace srsenb \ No newline at end of file diff --git a/srsgnb/src/stack/mac/sched_nr_helpers.cc b/srsgnb/src/stack/mac/sched_nr_helpers.cc new file mode 100644 index 000000000..38c30c4b9 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_helpers.cc @@ -0,0 +1,162 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h" +#include "srsgnb/hdr/stack/mac/sched_nr_harq.h" +#include "srsgnb/hdr/stack/mac/sched_nr_ue.h" +#include "srsran/common/string_helpers.h" + +namespace srsenb { +namespace sched_nr_impl { + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void log_sched_slot_ues(srslog::basic_logger& logger, slot_point pdcch_slot, uint32_t cc, const slot_ue_map_t& slot_ues) +{ + if (not logger.debug.enabled() or slot_ues.empty()) { + return; + } + + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "SCHED: UE candidates, pdcch_tti={}, cc={}: [", pdcch_slot, cc); + + const char* use_comma = ""; + for (const auto& ue_pair : slot_ues) { + auto& ue = ue_pair->second; + + fmt::format_to(fmtbuf, "{}{{rnti=0x{:x}", use_comma, ue->rnti); + if (ue.dl_active) { + fmt::format_to(fmtbuf, ", dl_bs={}", ue.dl_bytes); + } + if (ue.ul_active) { + fmt::format_to(fmtbuf, ", ul_bs={}", ue.ul_bytes); + } + fmt::format_to(fmtbuf, "}}"); + use_comma = ", "; + } + + logger.debug("%s]", srsran::to_c_str(fmtbuf)); +} + +void log_sched_bwp_result(srslog::basic_logger& logger, + slot_point pdcch_slot, + const bwp_res_grid& res_grid, + const slot_ue_map_t& slot_ues) +{ + const bwp_slot_grid& bwp_slot = res_grid[pdcch_slot]; + size_t rar_count = 0, si_count = 0, data_count = 0; + for (const pdcch_dl_t& pdcch : bwp_slot.dl.phy.pdcch_dl) { + fmt::memory_buffer fmtbuf; + if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) { + const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti]; + fmt::format_to(fmtbuf, + "SCHED: DL {}, cc={}, rnti=0x{:x}, pid={}, cs={}, f={}, prbs={}, nrtx={}, dai={}, " + "lcids=[{}], tbs={}, bs={}, pdsch_slot={}, ack_slot={}", + ue.h_dl->nof_retx() == 0 ? "tx" : "retx", + res_grid.cfg->cc, + ue->rnti, + pdcch.dci.pid, + pdcch.dci.ctx.coreset_id, + srsran_dci_format_nr_string(pdcch.dci.ctx.format), + ue.h_dl->prbs(), + ue.h_dl->nof_retx(), + pdcch.dci.dai, + fmt::join(bwp_slot.dl.data[data_count].subpdus, ", "), + ue.h_dl->tbs() / 8u, + ue.dl_bytes, + ue.pdsch_slot, + ue.uci_slot); + data_count++; + } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) { + const pdsch_t& pdsch = bwp_slot.dl.phy.pdsch[std::distance(bwp_slot.dl.phy.pdcch_dl.data(), &pdcch)]; + srsran::const_span prbs{pdsch.sch.grant.prb_idx, pdsch.sch.grant.prb_idx + SRSRAN_MAX_PRB_NR}; + uint32_t start_idx = std::distance(prbs.begin(), std::find(prbs.begin(), prbs.end(), true)); + uint32_t end_idx = start_idx + pdsch.sch.grant.nof_prb; + fmt::format_to(fmtbuf, + "SCHED: RAR, cc={}, ra-rnti=0x{:x}, prbs={}, pdsch_slot={}, msg3_slot={}, nof_grants={}", + res_grid.cfg->cc, + pdcch.dci.ctx.rnti, + srsran::interval{start_idx, end_idx}, + pdcch_slot, + pdcch_slot + res_grid.cfg->pusch_ra_list[0].msg3_delay, + bwp_slot.dl.rar[rar_count].grants.size()); + rar_count++; + } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_si) { + if (logger.debug.enabled()) { + const pdsch_t& pdsch = bwp_slot.dl.phy.pdsch[std::distance(bwp_slot.dl.phy.pdcch_dl.data(), &pdcch)]; + srsran::const_span prbs{pdsch.sch.grant.prb_idx, pdsch.sch.grant.prb_idx + SRSRAN_MAX_PRB_NR}; + uint32_t start_idx = std::distance(prbs.begin(), std::find(prbs.begin(), prbs.end(), true)); + uint32_t end_idx = start_idx + pdsch.sch.grant.nof_prb; + fmt::format_to(fmtbuf, + "SCHED: SI{}, cc={}, prbs={}, pdsch_slot={}", + pdcch.dci.sii == 0 ? "B" : " message", + res_grid.cfg->cc, + srsran::interval{start_idx, end_idx}, + pdcch_slot); + si_count++; + } + } + + if (fmtbuf.size() > 0) { + if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_si) { + logger.debug("%s", srsran::to_c_str(fmtbuf)); + } else { + logger.info("%s", srsran::to_c_str(fmtbuf)); + } + } + } + for (const pdcch_ul_t& pdcch : bwp_slot.dl.phy.pdcch_ul) { + fmt::memory_buffer fmtbuf; + if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) { + const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti]; + fmt::format_to(fmtbuf, + "SCHED: UL {}, cc={}, rnti=0x{:x}, pid={}, cs={}, f={}, nrtx={}, tbs={}, bs={}, pusch_slot={}", + ue.h_ul->nof_retx() == 0 ? "tx" : "retx", + res_grid.cfg->cc, + ue->rnti, + pdcch.dci.pid, + pdcch.dci.ctx.coreset_id, + srsran_dci_format_nr_string(pdcch.dci.ctx.format), + ue.h_ul->nof_retx(), + ue.h_ul->tbs() / 8u, + ue.ul_bytes, + ue.pusch_slot); + } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_tc) { + const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti]; + fmt::format_to(fmtbuf, + "SCHED: UL Msg3, cc={}, tc-rnti=0x{:x}, pid={}, nrtx={}, f={}, tti_pusch={}", + res_grid.cfg->cc, + ue->rnti, + pdcch.dci.pid, + ue.h_ul->nof_retx(), + srsran_dci_format_nr_string(pdcch.dci.ctx.format), + ue.pusch_slot); + } else { + fmt::format_to(fmtbuf, "SCHED: unknown rnti format"); + } + + logger.info("%s", srsran::to_c_str(fmtbuf)); + } +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc new file mode 100644 index 000000000..d6530eaf1 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc @@ -0,0 +1,115 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h" +#include "srsran/asn1/rrc_nr_utils.h" +#include "srsran/common/band_helper.h" + +namespace srsenb { + +uint32_t coreset_nof_cces(const srsran_coreset_t& coreset) +{ + const bool* res_active = &coreset.freq_resources[0]; + uint32_t nof_freq_res = std::count(res_active, res_active + SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, true); + return nof_freq_res * coreset.duration; +} + +void make_mib_cfg(const sched_nr_cell_cfg_t& cfg, srsran_mib_nr_t* mib) +{ + *mib = {}; + mib->scs_common = (srsran_subcarrier_spacing_t)cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value; + mib->ssb_offset = cfg.ssb_offset; + mib->dmrs_typeA_pos = (srsran_dmrs_sch_typeA_pos_t)cfg.dmrs_type_a_position.value; + mib->coreset0_idx = cfg.pdcch_cfg_sib1.ctrl_res_set_zero; + mib->ss0_idx = cfg.pdcch_cfg_sib1.search_space_zero; + mib->cell_barred = false; + mib->intra_freq_reselection = true; +} + +void make_ssb_cfg(const sched_nr_cell_cfg_t& cfg, srsran::phy_cfg_nr_t::ssb_cfg_t* ssb) +{ + ssb->periodicity_ms = cfg.ssb_periodicity_ms; + ssb->position_in_burst = {}; + uint32_t N = cfg.ssb_positions_in_burst.in_one_group.length(); + for (uint32_t i = 0; i < N; ++i) { + ssb->position_in_burst[i] = cfg.ssb_positions_in_burst.in_one_group.get(i); + } + if (cfg.ssb_positions_in_burst.group_presence_present) { + for (uint32_t i = 1; i < cfg.ssb_positions_in_burst.group_presence.length(); ++i) { + if (cfg.ssb_positions_in_burst.group_presence.get(i)) { + std::copy( + ssb->position_in_burst.begin(), ssb->position_in_burst.begin() + N, ssb->position_in_burst.begin() + i * N); + } + } + } + ssb->scs = (srsran_subcarrier_spacing_t)cfg.ssb_scs.value; + ssb->pattern = SRSRAN_SSB_PATTERN_A; + if (cfg.dl_cfg_common.freq_info_dl.freq_band_list.size() > 0 and + cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present) { + uint32_t band = cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr; + ssb->pattern = srsran::srsran_band_helper::get_ssb_pattern(band, ssb->scs); + } +} + +srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_cell_cfg_t& cfg) +{ + srsran::phy_cfg_nr_t ue_phy_cfg; + + // TDD UL-DL config + ue_phy_cfg.duplex.mode = SRSRAN_DUPLEX_MODE_FDD; + if (cfg.tdd_ul_dl_cfg_common.has_value()) { + bool success = srsran::make_phy_tdd_cfg(*cfg.tdd_ul_dl_cfg_common, &ue_phy_cfg.duplex); + srsran_sanity_check(success, "Failed to convert Cell TDDConfig to UEPHYConfig"); + } + + ue_phy_cfg.pdcch = cfg.bwps[0].pdcch; + ue_phy_cfg.pdsch = cfg.bwps[0].pdsch; + ue_phy_cfg.pusch = cfg.bwps[0].pusch; + ue_phy_cfg.pucch = cfg.bwps[0].pucch; + srsran::make_phy_rach_cfg(cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), + cfg.tdd_ul_dl_cfg_common.has_value() ? SRSRAN_DUPLEX_MODE_TDD : SRSRAN_DUPLEX_MODE_FDD, + &ue_phy_cfg.prach); + ue_phy_cfg.harq_ack = cfg.bwps[0].harq_ack; + ue_phy_cfg.csi = {}; // disable CSI until RA is complete + ue_phy_cfg.carrier.pci = cfg.pci; + ue_phy_cfg.carrier.dl_center_frequency_hz = cfg.dl_center_frequency_hz; + ue_phy_cfg.carrier.ul_center_frequency_hz = cfg.ul_center_frequency_hz; + ue_phy_cfg.carrier.ssb_center_freq_hz = cfg.ssb_center_freq_hz; + ue_phy_cfg.carrier.offset_to_carrier = cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; + ue_phy_cfg.carrier.scs = + (srsran_subcarrier_spacing_t)cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value; + ue_phy_cfg.carrier.nof_prb = cfg.dl_cell_nof_prb; + ue_phy_cfg.carrier.max_mimo_layers = cfg.nof_layers; + make_ssb_cfg(cfg, &ue_phy_cfg.ssb); + + // remove UE-specific SearchSpaces (they will be added later via RRC) + for (uint32_t i = 0; i < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++i) { + if (ue_phy_cfg.pdcch.search_space_present[i] and + ue_phy_cfg.pdcch.search_space[i].type == srsran_search_space_type_ue) { + ue_phy_cfg.pdcch.search_space_present[i] = false; + ue_phy_cfg.pdcch.search_space[i] = {}; + } + } + + return ue_phy_cfg; +} + +} // namespace srsenb \ No newline at end of file diff --git a/srsgnb/src/stack/mac/sched_nr_pdcch.cc b/srsgnb/src/stack/mac/sched_nr_pdcch.cc new file mode 100644 index 000000000..5ccf50b64 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_pdcch.cc @@ -0,0 +1,521 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsran/common/string_helpers.h" + +namespace srsenb { +namespace sched_nr_impl { + +template +void log_pdcch_alloc_failure(srslog::log_channel& log_ch, + srsran_rnti_type_t rnti_type, + uint32_t ss_id, + uint16_t rnti, + const char* cause_fmt, + Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + + // Log PDCCH allocation failure + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, + "SCHED: Failure to allocate PDCCH for {}-rnti=0x{:x}, SS#{}. Cause: ", + srsran_rnti_type_str_short(rnti_type), + rnti, + ss_id); + fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); + log_ch("%s", srsran::to_c_str(fmtbuf)); +} + +void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci) +{ + dci.bwp_id = bwp_cfg.bwp_id; + dci.cc_id = bwp_cfg.cc; + dci.tpc = 1; + dci.coreset0_bw = bwp_cfg.cfg.pdcch.coreset_present[0] ? bwp_cfg.coreset_prb_range(0).length() : 0; +} + +void fill_dci_from_cfg(const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci) +{ + dci.bwp_id = bwp_cfg.bwp_id; + dci.cc_id = bwp_cfg.cc; + dci.tpc = 1; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +coreset_region::coreset_region(const bwp_params_t& bwp_cfg_, uint32_t coreset_id_, uint32_t slot_idx_) : + coreset_cfg(&bwp_cfg_.cfg.pdcch.coreset[coreset_id_]), + coreset_id(coreset_id_), + slot_idx(slot_idx_), + rar_cce_list(bwp_cfg_.rar_cce_list), + common_cce_list(bwp_cfg_.common_cce_list) +{ + const bool* res_active = &coreset_cfg->freq_resources[0]; + nof_freq_res = std::count(res_active, res_active + SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE, true); + srsran_assert(get_td_symbols() <= SRSRAN_CORESET_DURATION_MAX, + "Possible number of time-domain OFDM symbols in CORESET must be within {1,2,3}"); + srsran_assert(nof_freq_res <= bwp_cfg_.cell_cfg.nof_prb(), + "The number of frequency resources=%d of CORESET#%d exceeds BWP bandwidth=%d", + nof_freq_res, + coreset_id, + bwp_cfg_.cell_cfg.nof_prb()); +} + +void coreset_region::reset() +{ + dfs_tree.clear(); + saved_dfs_tree.clear(); + dci_list.clear(); +} + +bool coreset_region::alloc_pdcch(srsran_rnti_type_t rnti_type, + bool is_dl, + uint32_t aggr_idx, + uint32_t search_space_id, + const ue_carrier_params_t* user, + srsran_dci_ctx_t& dci) +{ + saved_dfs_tree.clear(); + + alloc_record record; + record.dci = &dci; + record.ue = user; + record.aggr_idx = aggr_idx; + record.ss_id = search_space_id; + record.is_dl = is_dl; + record.dci->rnti_type = rnti_type; + + // Try to allocate grant. If it fails, attempt the same grant, but using a different permutation of past grant DCI + // positions + do { + bool success = alloc_dfs_node(record, 0); + if (success) { + // DCI record allocation successful + dci_list.push_back(record); + return true; + } + if (saved_dfs_tree.empty()) { + saved_dfs_tree = dfs_tree; + } + } while (get_next_dfs()); + + // Revert steps to initial state, before dci record allocation was attempted + dfs_tree.swap(saved_dfs_tree); + return false; +} + +void coreset_region::rem_last_pdcch() +{ + srsran_assert(not dci_list.empty(), "%s called when no PDCCH have yet been allocated", __FUNCTION__); + + // Remove DCI record + dfs_tree.pop_back(); + dci_list.pop_back(); +} + +bool coreset_region::get_next_dfs() +{ + do { + if (dfs_tree.empty()) { + // If we reach root, the allocation failed + return false; + } + // Attempt to re-add last tree node, but with a higher node child index + uint32_t start_child_idx = dfs_tree.back().dci_pos_idx + 1; + dfs_tree.pop_back(); + while (dfs_tree.size() < dci_list.size() and alloc_dfs_node(dci_list[dfs_tree.size()], start_child_idx)) { + start_child_idx = 0; + } + } while (dfs_tree.size() < dci_list.size()); + + // Finished computation of next DFS node + return true; +} + +bool coreset_region::alloc_dfs_node(const alloc_record& record, uint32_t start_dci_idx) +{ + alloc_tree_dfs_t& alloc_dfs = dfs_tree; + // Get DCI Location Table + auto cce_locs = get_cce_loc_table(record); + if (start_dci_idx >= cce_locs.size()) { + return false; + } + + tree_node node; + node.dci_pos_idx = start_dci_idx; + node.dci_pos.L = record.aggr_idx; + node.rnti = record.ue != nullptr ? record.ue->rnti : SRSRAN_INVALID_RNTI; + node.current_mask.resize(nof_cces()); + // get cumulative pdcch bitmap + if (not alloc_dfs.empty()) { + node.total_mask = alloc_dfs.back().total_mask; + } else { + node.total_mask.resize(nof_cces()); + } + + for (; node.dci_pos_idx < cce_locs.size(); ++node.dci_pos_idx) { + node.dci_pos.ncce = cce_locs[node.dci_pos_idx]; + + node.current_mask.reset(); + node.current_mask.fill(node.dci_pos.ncce, node.dci_pos.ncce + (1U << record.aggr_idx)); + if ((node.total_mask & node.current_mask).any()) { + // there is a PDCCH collision. Try another CCE position + continue; + } + + // Allocation successful + node.total_mask |= node.current_mask; + alloc_dfs.push_back(node); + record.dci->location = node.dci_pos; + return true; + } + + return false; +} + +srsran::span coreset_region::get_cce_loc_table(const alloc_record& record) const +{ + switch (record.dci->rnti_type) { + case srsran_rnti_type_ra: + return rar_cce_list[slot_idx][record.aggr_idx]; + case srsran_rnti_type_si: + return common_cce_list[record.ss_id][slot_idx][record.aggr_idx]; + case srsran_rnti_type_c: + case srsran_rnti_type_tc: + case srsran_rnti_type_mcs_c: + case srsran_rnti_type_sp_csi: + return record.ue->cce_pos_list(record.ss_id, slot_idx, record.aggr_idx); + default: + srsran_terminate("Invalid RNTI type=%s", srsran_rnti_type_str(record.dci->rnti_type)); + break; + } + return {}; +} + +void coreset_region::print_allocations(fmt::memory_buffer& fmtbuf) const +{ + if (not dci_list.empty()) { + fmt::format_to(fmtbuf, "CORESET#{} (#CCEs={}):\n", coreset_id, nof_cces()); + } + for (const alloc_record& dci : dci_list) { + fmt::format_to(fmtbuf, + " {}-rnti=0x{:x}: ({}, {})\n", + srsran_rnti_type_str_short(dci.dci->rnti_type), + dci.dci->rnti, + dci.dci->location.ncce, + dci.dci->location.L); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bwp_pdcch_allocator::bwp_pdcch_allocator(const bwp_params_t& bwp_cfg_, + uint32_t slot_idx_, + pdcch_dl_list_t& dl_pdcchs, + pdcch_ul_list_t& ul_pdcchs) : + bwp_cfg(bwp_cfg_), pdcch_dl_list(dl_pdcchs), pdcch_ul_list(ul_pdcchs), slot_idx(slot_idx_), logger(bwp_cfg_.logger) +{ + for (uint32_t cs_idx = 0; cs_idx < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++cs_idx) { + if (bwp_cfg.cfg.pdcch.coreset_present[cs_idx]) { + uint32_t cs_id = bwp_cfg.cfg.pdcch.coreset[cs_idx].id; + coresets.emplace(cs_id, bwp_cfg, cs_id, slot_idx); + } + } +} + +void bwp_pdcch_allocator::fill_dci_ctx_common(srsran_dci_ctx_t& dci, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + const srsran_search_space_t& ss, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* ue) +{ + // Note: Location is filled by coreset_region class. + dci.ss_type = ss.type; + dci.coreset_id = ss.coreset_id; + const srsran_coreset_t* coreset = + ue == nullptr ? &bwp_cfg.cfg.pdcch.coreset[ss.coreset_id] : &ue->phy().pdcch.coreset[ss.coreset_id]; + dci.coreset_start_rb = srsran_coreset_start_rb(coreset); + dci.rnti_type = rnti_type; + dci.rnti = rnti; + dci.format = dci_fmt; +} + +pdcch_dl_alloc_result bwp_pdcch_allocator::alloc_rar_pdcch(uint16_t ra_rnti, uint32_t aggr_idx) +{ + srsran_assert(bwp_cfg.cfg.pdcch.ra_search_space_present, "Allocating RAR PDCCH in BWP without RA SearchSpace"); + return alloc_dl_pdcch_common( + srsran_rnti_type_ra, ra_rnti, bwp_cfg.cfg.pdcch.ra_search_space.id, aggr_idx, srsran_dci_format_nr_1_0, nullptr); +} + +pdcch_dl_alloc_result bwp_pdcch_allocator::alloc_si_pdcch(uint32_t ss_id, uint32_t aggr_idx) +{ + return alloc_dl_pdcch_common(srsran_rnti_type_si, SRSRAN_SIRNTI, ss_id, aggr_idx, srsran_dci_format_nr_1_0, nullptr); +} + +pdcch_dl_alloc_result bwp_pdcch_allocator::alloc_dl_pdcch(srsran_rnti_type_t rnti_type, + uint32_t ss_id, + uint32_t aggr_idx, + const ue_carrier_params_t& user) +{ + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; // TODO: make it configurable + srsran_assert(rnti_type == srsran_rnti_type_c or rnti_type == srsran_rnti_type_tc, + "Invalid RNTI type=%s for UE-specific PDCCH", + srsran_rnti_type_str_short(rnti_type)); + return alloc_dl_pdcch_common(rnti_type, user.rnti, ss_id, aggr_idx, dci_fmt, &user); +} + +pdcch_dl_alloc_result bwp_pdcch_allocator::alloc_dl_pdcch_common(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user) +{ + alloc_result r = check_args_valid(rnti_type, rnti, ss_id, aggr_idx, dci_fmt, user, true); + if (r != alloc_result::success) { + return {r}; + } + const srsran_search_space_t& ss = + (user == nullptr) + ? (rnti_type == srsran_rnti_type_ra ? bwp_cfg.cfg.pdcch.ra_search_space : *bwp_cfg.get_ss(ss_id)) + : *user->get_ss(ss_id); + + // Add new DL PDCCH to sched result + pdcch_dl_list.emplace_back(); + + bool success = + coresets[ss.coreset_id].alloc_pdcch(rnti_type, true, aggr_idx, ss_id, user, pdcch_dl_list.back().dci.ctx); + + if (not success) { + // Remove failed PDCCH allocation + pdcch_dl_list.pop_back(); + + // Log PDCCH allocation failure + srslog::log_channel& ch = user == nullptr ? logger.warning : logger.debug; + log_pdcch_alloc_failure(ch, rnti_type, ss_id, rnti, "No available PDCCH position"); + + return {alloc_result::no_cch_space}; + } + + // PDCCH allocation was successful + pdcch_dl_t& pdcch = pdcch_dl_list.back(); + + // Fill DCI with semi-static config + fill_dci_from_cfg(bwp_cfg, pdcch.dci); + + // Fill DCI context information + fill_dci_ctx_common(pdcch.dci.ctx, rnti_type, rnti, ss, dci_fmt, user); + + // register last PDCCH coreset, in case it needs to be aborted + pending_dci = &pdcch.dci.ctx; + + return {&pdcch}; +} + +pdcch_ul_alloc_result +bwp_pdcch_allocator::alloc_ul_pdcch(uint32_t ss_id, uint32_t aggr_idx, const ue_carrier_params_t& user) +{ + static const srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_0_0; // TODO: make it configurable + alloc_result r = check_args_valid(srsran_rnti_type_c, user.rnti, ss_id, aggr_idx, dci_fmt, &user, false); + if (r != alloc_result::success) { + return {r}; + } + const srsran_search_space_t& ss = *user.get_ss(ss_id); + + // Add new UL PDCCH to sched result + pdcch_ul_list.emplace_back(); + + bool success = coresets[ss.coreset_id].alloc_pdcch( + srsran_rnti_type_c, false, aggr_idx, ss_id, &user, pdcch_ul_list.back().dci.ctx); + + if (not success) { + // Remove failed PDCCH allocation + pdcch_ul_list.pop_back(); + + // Log PDCCH allocation failure + log_pdcch_alloc_failure(logger.debug, srsran_rnti_type_c, ss_id, user.rnti, "No available PDCCH position"); + + return {alloc_result::no_cch_space}; + } + + // PDCCH allocation was successful + pdcch_ul_t& pdcch = pdcch_ul_list.back(); + + // Fill DCI with semi-static config + fill_dci_from_cfg(bwp_cfg, pdcch.dci); + + // Fill DCI context information + fill_dci_ctx_common(pdcch.dci.ctx, srsran_rnti_type_c, user.rnti, ss, dci_fmt, &user); + + // register last PDCCH coreset, in case it needs to be aborted + pending_dci = &pdcch.dci.ctx; + + return {&pdcch}; +} + +void bwp_pdcch_allocator::cancel_last_pdcch() +{ + srsran_assert(pending_dci != nullptr, "Trying to abort PDCCH allocation that does not exist"); + uint32_t cs_id = pending_dci->coreset_id; + + if (&pdcch_dl_list.back().dci.ctx == pending_dci) { + pdcch_dl_list.pop_back(); + } else if (&pdcch_ul_list.back().dci.ctx == pending_dci) { + pdcch_ul_list.pop_back(); + } else { + logger.error("Invalid DCI context provided to be removed"); + return; + } + coresets[cs_id].rem_last_pdcch(); + pending_dci = nullptr; +} + +void bwp_pdcch_allocator::reset() +{ + pending_dci = nullptr; + pdcch_dl_list.clear(); + pdcch_ul_list.clear(); + for (coreset_region& coreset : coresets) { + coreset.reset(); + } +} + +uint32_t bwp_pdcch_allocator::nof_allocations() const +{ + uint32_t count = 0; + for (const coreset_region& coreset : coresets) { + count += coreset.nof_allocs(); + } + return count; +} + +uint32_t bwp_pdcch_allocator::nof_cces(uint32_t coreset_id) const +{ + return coresets[coreset_id].nof_cces(); +} + +alloc_result bwp_pdcch_allocator::check_args_valid(srsran_rnti_type_t rnti_type, + uint16_t rnti, + uint32_t ss_id, + uint32_t aggr_idx, + srsran_dci_format_nr_t dci_fmt, + const ue_carrier_params_t* user, + bool is_dl) const +{ + srsran_assert(ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE, "Invalid SearchSpace#%d", ss_id); + srsran_assert( + aggr_idx < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR, "Invalid aggregation level index=%d", aggr_idx); + + // DL must be active in given slot + if (not bwp_cfg.slots[slot_idx].is_dl) { + log_pdcch_alloc_failure(logger.error, rnti_type, ss_id, rnti, "DL is disabled for slot={}", slot_idx); + return alloc_result::no_cch_space; + } + + // Verify SearchSpace validity + const srsran_search_space_t* ss = + (user == nullptr) + ? (rnti_type == srsran_rnti_type_ra ? &bwp_cfg.cfg.pdcch.ra_search_space : bwp_cfg.get_ss(ss_id)) + : user->get_ss(ss_id); + if (ss == nullptr) { + // Couldn't find SearchSpace + log_pdcch_alloc_failure(logger.error, rnti_type, ss_id, rnti, "SearchSpace has not been configured"); + return alloc_result::invalid_grant_params; + } + if (ss->nof_candidates[aggr_idx] == 0) { + // No valid DCI position candidates given aggregation level + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Chosen SearchSpace doesn't have CCE candidates for L={}", aggr_idx); + return alloc_result::invalid_grant_params; + } + if (not is_rnti_type_valid_in_search_space(rnti_type, ss->type)) { + // RNTI type doesnt match SearchSpace type + log_pdcch_alloc_failure(logger.warning, + rnti_type, + ss_id, + rnti, + "Chosen SearchSpace type \"{}\" does not match rnti_type.", + srsran_ss_type_str(ss->type)); + return alloc_result::invalid_grant_params; + } + auto dci_fmt_equal = [dci_fmt](srsran_dci_format_nr_t f) { return f == dci_fmt; }; + if (std::none_of(&ss->formats[0], &ss->formats[ss->nof_formats], dci_fmt_equal)) { + log_pdcch_alloc_failure(logger.warning, + rnti_type, + ss_id, + rnti, + "Chosen SearchSpace does not support chosen dci format={}", + srsran_dci_format_nr_string(dci_fmt)); + return alloc_result::invalid_grant_params; + } + + if (is_dl) { + if (pdcch_dl_list.full()) { + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Maximum number of allocations={} reached", pdcch_dl_list.size()); + return alloc_result::no_cch_space; + } + } else if (pdcch_ul_list.full()) { + log_pdcch_alloc_failure( + logger.warning, rnti_type, ss_id, rnti, "Maximum number of UL allocations={} reached", pdcch_ul_list.size()); + return alloc_result::no_cch_space; + } + + if (user != nullptr) { + if (user->active_bwp().bwp_id != bwp_cfg.bwp_id) { + log_pdcch_alloc_failure(logger.warning, + rnti_type, + ss_id, + rnti, + "Trying to allocate BWP#{} which is inactive for the UE.", + user->active_bwp().bwp_id); + return alloc_result::no_rnti_opportunity; + } + } + + srsran_sanity_check(pdcch_dl_list.size() + pdcch_ul_list.size() == nof_allocations(), "Invalid PDCCH state"); + return alloc_result::success; +} + +void bwp_pdcch_allocator::print_allocations(fmt::memory_buffer& fmtbuf) const +{ + fmt::format_to( + fmtbuf, "PDCCH allocations: ({} active coresets):{}\n", coresets.size(), nof_allocations() == 0 ? " None" : ""); + for (const coreset_region& cs : coresets) { + cs.print_allocations(fmtbuf); + } +} + +std::string bwp_pdcch_allocator::print_allocations() const +{ + fmt::memory_buffer fmtbuf; + print_allocations(fmtbuf); + return fmt::to_string(fmtbuf); +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_rb.cc b/srsgnb/src/stack/mac/sched_nr_rb.cc new file mode 100644 index 000000000..abc59708e --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_rb.cc @@ -0,0 +1,114 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_rb.h" + +namespace srsenb { +namespace sched_nr_impl { + +/// TS 38.214, Table 6.1.2.2.1-1 - Nominal RBG size P +uint32_t get_P(uint32_t bwp_nof_prb, bool config_1_or_2) +{ + srsran_assert(bwp_nof_prb > 0 and bwp_nof_prb <= 275, "Invalid BWP size"); + if (bwp_nof_prb <= 36) { + return config_1_or_2 ? 2 : 4; + } + if (bwp_nof_prb <= 72) { + return config_1_or_2 ? 4 : 8; + } + if (bwp_nof_prb <= 144) { + return config_1_or_2 ? 8 : 16; + } + return 16; +} + +/// TS 38.214 - total number of RBGs for a uplink bandwidth part of size "bwp_nof_prb" PRBs +uint32_t get_nof_rbgs(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2) +{ + uint32_t P = get_P(bwp_nof_prb, config1_or_2); + return srsran::ceil_div(bwp_nof_prb + (bwp_start % P), P); +} + +uint32_t get_rbg_size(uint32_t bwp_nof_prb, uint32_t bwp_start, bool config1_or_2, uint32_t rbg_idx) +{ + uint32_t P = get_P(bwp_nof_prb, config1_or_2); + uint32_t nof_rbgs = get_nof_rbgs(bwp_nof_prb, bwp_start, config1_or_2); + if (rbg_idx == 0) { + return P - (bwp_start % P); + } + if (rbg_idx == nof_rbgs - 1) { + uint32_t ret = (bwp_start + bwp_nof_prb) % P; + return ret > 0 ? ret : P; + } + return P; +} + +bwp_rb_bitmap::bwp_rb_bitmap(uint32_t bwp_nof_prbs, uint32_t bwp_prb_start_, bool config1_or_2) : + prbs_(bwp_nof_prbs), + rbgs_(get_nof_rbgs(bwp_nof_prbs, bwp_prb_start_, config1_or_2)), + P_(get_P(bwp_nof_prbs, config1_or_2)), + Pnofbits(log2(P_)), + first_rbg_size(get_rbg_size(bwp_nof_prbs, bwp_prb_start_, config1_or_2, 0)) +{} + +uint32_t bwp_rb_bitmap::prb_to_rbg_idx(uint32_t prb_idx) const +{ + return ((prb_idx + P_ - first_rbg_size) >> Pnofbits); +} + +void bwp_rb_bitmap::add_prbs_to_rbgs(const prb_bitmap& grant) +{ + int idx = 0; + do { + idx = grant.find_lowest(idx, grant.size(), true); + if (idx < 0) { + return; + } + uint32_t rbg_idx = prb_to_rbg_idx(idx); + rbgs_.set(rbg_idx, true); + idx++; + } while (idx != (int)prbs_.size()); +} + +void bwp_rb_bitmap::add_prbs_to_rbgs(const prb_interval& grant) +{ + uint32_t rbg_start = prb_to_rbg_idx(grant.start()); + uint32_t rbg_stop = std::min(prb_to_rbg_idx(grant.stop() - 1) + 1u, (uint32_t)rbgs_.size()); + rbgs_.fill(rbg_start, rbg_stop); +} + +void bwp_rb_bitmap::add_rbgs_to_prbs(const rbg_bitmap& grant) +{ + int idx = 0; + do { + idx = grant.find_lowest(idx, grant.size(), true); + if (idx < 0) { + return; + } + uint32_t prb_idx = (idx - 1) * P_ + first_rbg_size; + uint32_t prb_end = std::min(prb_idx + ((idx == 0) ? first_rbg_size : P_), (uint32_t)prbs_.size()); + prbs_.fill(prb_idx, prb_end); + idx++; + } while (idx != (int)prbs_.size()); +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_sch.cc b/srsgnb/src/stack/mac/sched_nr_sch.cc new file mode 100644 index 000000000..9eaf21f92 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_sch.cc @@ -0,0 +1,371 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_sch.h" +#include "srsran/common/string_helpers.h" + +namespace srsenb { +namespace sched_nr_impl { + +template +void log_alloc_failure(srslog::log_channel& log_ch, const char* cause_fmt, Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + + // Log allocation failure + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "SCHED: Failure to allocate PDSCH. Cause: "); + fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); + log_ch("%s", srsran::to_c_str(fmtbuf)); +} + +pdsch_allocator::pdsch_allocator(const bwp_params_t& cfg_, uint32_t slot_index, pdsch_list_t& pdsch_lst) : + bwp_cfg(cfg_), + slot_idx(slot_index), + pdschs(pdsch_lst), + dl_prbs(bwp_cfg.cfg.rb_width, bwp_cfg.cfg.start_rb, bwp_cfg.cfg.pdsch.rbg_size_cfg_1) +{} + +void pdsch_allocator::reset() +{ + pdschs.clear(); + dl_prbs.reset(); +} + +alloc_result pdsch_allocator::is_grant_valid_common(srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t dci_fmt, + uint32_t coreset_id, + const prb_grant& grant) const +{ + // DL must be active in given slot + if (not bwp_cfg.slots[slot_idx].is_dl) { + log_alloc_failure(bwp_cfg.logger.error, "DL is disabled for slot={}", slot_idx); + return alloc_result::no_sch_space; + } + + // No space in Scheduler PDSCH output list + if (pdschs.full()) { + log_alloc_failure(bwp_cfg.logger.warning, "Maximum number of PDSCHs={} reached.", pdschs.size()); + return alloc_result::no_sch_space; + } + + // TS 38.214, 5.1.2.2 - "The UE shall assume that when the scheduling grant is received with DCI format 1_0, then + // downlink resource allocation type 1 is used." + if (dci_fmt == srsran_dci_format_nr_1_0 and not grant.is_alloc_type1()) { + log_alloc_failure(bwp_cfg.logger.warning, "DL Resource Allocation type 1 must be used in case of DCI format 1_0."); + return alloc_result::invalid_grant_params; + } + + // TS 38.214 - 5.1.2.2 - For DCI format 1_0 and Common Search Space, the list of available PRBs is limited by the + // rb_start and bandwidth of the coreset + if (dci_fmt == srsran_dci_format_nr_1_0 and SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) { + // Grant PRBs do not collide with CORESET PRB limits (in case of common SearchSpace) + if (bwp_cfg.dci_fmt_1_0_excluded_prbs(coreset_id).collides(grant)) { + log_alloc_failure( + bwp_cfg.logger.debug, "Provided PRB grant={:x} falls outside common CORESET PRB boundaries.", grant); + return alloc_result::sch_collision; + } + } + + // Grant PRBs do not collide with previous PDSCH allocations + if (dl_prbs.collides(grant)) { + log_alloc_failure( + bwp_cfg.logger.debug, "Provided PRB grant={:x} collides with allocations previously made.", grant); + return alloc_result::sch_collision; + } + + return alloc_result::success; +} + +alloc_result pdsch_allocator::is_si_grant_valid(uint32_t ss_id, const prb_grant& grant) const +{ + // Verify SearchSpace validity + const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id); + if (ss == nullptr) { + // Couldn't find SearchSpace + log_alloc_failure(bwp_cfg.logger.error, "SearchSpace has not been configured."); + return alloc_result::invalid_grant_params; + } + return is_grant_valid_common(ss->type, srsran_dci_format_nr_1_0, ss->coreset_id, grant); +} + +alloc_result pdsch_allocator::is_rar_grant_valid(const prb_grant& grant) const +{ + srsran_sanity_check(bwp_cfg.cfg.pdcch.ra_search_space_present, + "Attempting RAR allocation in BWP with no raSearchSpace"); + return is_grant_valid_common(bwp_cfg.cfg.pdcch.ra_search_space.type, + srsran_dci_format_nr_1_0, + bwp_cfg.cfg.pdcch.ra_search_space.coreset_id, + grant); +} + +alloc_result pdsch_allocator::is_ue_grant_valid(const ue_carrier_params_t& ue, + uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant) const +{ + const srsran_search_space_t* ss = ue.get_ss(ss_id); + if (ss == nullptr) { + // Couldn't find SearchSpace + log_alloc_failure(bwp_cfg.logger.error, "rnti=0x%x,SearchSpaceId={} has not been configured.", ue.rnti, ss_id); + return alloc_result::invalid_grant_params; + } + alloc_result ret = is_grant_valid_common(ss->type, dci_fmt, ss->coreset_id, grant); + if (ret != alloc_result::success) { + return ret; + } + + // TS 38.214, 5.1.2.2 - "the UE shall use the downlink frequency resource allocation type as defined by the higher + // layer parameter resourceAllocation" + if (ue.phy().pdsch.alloc != srsran_resource_alloc_dynamic) { + if ((ue.phy().pdsch.alloc == srsran_resource_alloc_type0) != grant.is_alloc_type0()) { + log_alloc_failure(bwp_cfg.logger.warning, + "UE rnti=0x{:x} PDSCH RA configuration type {} doesn't match grant type", + ue.rnti, + grant.is_alloc_type0() ? 0 : 1); + return alloc_result::invalid_grant_params; + } + } + + return alloc_result::success; +} + +pdsch_alloc_result pdsch_allocator::alloc_si_pdsch(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + alloc_result code = is_si_grant_valid(ss_id, grant); + if (code != alloc_result::success) { + return code; + } + return {&alloc_si_pdsch_unchecked(ss_id, grant, dci)}; +} + +pdsch_t& pdsch_allocator::alloc_si_pdsch_unchecked(uint32_t ss_id, const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + // Verify SearchSpace validity + const srsran_search_space_t* ss = bwp_cfg.get_ss(ss_id); + srsran_sanity_check(ss != nullptr, "SearchSpace has not been configured"); + return alloc_pdsch_unchecked(ss->coreset_id, ss->type, srsran_dci_format_nr_1_0, grant, dci); +} + +pdsch_alloc_result pdsch_allocator::alloc_rar_pdsch(const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + alloc_result code = is_rar_grant_valid(grant); + if (code != alloc_result::success) { + return code; + } + return {&alloc_rar_pdsch_unchecked(grant, dci)}; +} + +pdsch_t& pdsch_allocator::alloc_rar_pdsch_unchecked(const prb_grant& grant, srsran_dci_dl_nr_t& dci) +{ + // TS 38.213, 8.2 - "In response to a PRACH transmission, a UE attempts to detect a DCI format 1_0" + const static srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0; + + return alloc_pdsch_unchecked( + bwp_cfg.cfg.pdcch.ra_search_space.coreset_id, bwp_cfg.cfg.pdcch.ra_search_space.type, dci_fmt, grant, dci); +} + +pdsch_alloc_result pdsch_allocator::alloc_ue_pdsch(uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + const ue_carrier_params_t& ue, + srsran_dci_dl_nr_t& dci) +{ + alloc_result code = is_ue_grant_valid(ue, ss_id, dci_fmt, grant); + if (code != alloc_result::success) { + return code; + } + return {&alloc_ue_pdsch_unchecked(ss_id, dci_fmt, grant, ue, dci)}; +} + +pdsch_t& pdsch_allocator::alloc_ue_pdsch_unchecked(uint32_t ss_id, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + const ue_carrier_params_t& ue, + srsran_dci_dl_nr_t& dci) +{ + const srsran_search_space_t* ss = ue.get_ss(ss_id); + srsran_sanity_check(ss != nullptr, "SearchSpace has not been configured"); + return alloc_pdsch_unchecked(ss->coreset_id, ss->type, dci_fmt, grant, dci); +} + +pdsch_t& pdsch_allocator::alloc_pdsch_unchecked(uint32_t coreset_id, + srsran_search_space_type_t ss_type, + srsran_dci_format_nr_t dci_fmt, + const prb_grant& grant, + srsran_dci_dl_nr_t& out_dci) +{ + // Create new PDSCH entry in output PDSCH list + pdschs.emplace_back(); + pdsch_t& pdsch = pdschs.back(); + + // Register allocated PRBs in accumulated bitmap + dl_prbs |= grant; + + // Fill DCI with PDSCH freq/time allocation information + out_dci.time_domain_assigment = 0; + if (grant.is_alloc_type0()) { + out_dci.freq_domain_assigment = grant.rbgs().to_uint64(); + } else { + uint32_t rb_start = grant.prbs().start(), nof_prb = bwp_cfg.nof_prb; + if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) { + prb_interval lims = bwp_cfg.coreset_prb_range(coreset_id); + if (dci_fmt == srsran_dci_format_nr_1_0) { + srsran_sanity_check(rb_start >= lims.start(), "Invalid PRB grant"); + rb_start -= lims.start(); + } + if (coreset_id == 0) { + nof_prb = lims.length(); + } + } + srsran_sanity_check(rb_start + grant.prbs().length() <= nof_prb, "Invalid PRB grant"); + out_dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, rb_start, grant.prbs().length()); + } + + return pdsch; +} + +void pdsch_allocator::cancel_last_pdsch() +{ + srsran_assert(not pdschs.empty(), "Trying to abort PDSCH allocation that does not exist"); + pdschs.pop_back(); + // TODO: clear bitmap allocated RBs +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +template +void log_pusch_alloc_failure(srslog::log_channel& log_ch, const char* cause_fmt, Args&&... args) +{ + if (not log_ch.enabled()) { + return; + } + + // Log allocation failure + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "SCHED: Failure to allocate PUSCH. Cause: "); + fmt::format_to(fmtbuf, cause_fmt, std::forward(args)...); + log_ch("%s", srsran::to_c_str(fmtbuf)); +} + +pusch_allocator::pusch_allocator(const bwp_params_t& cfg_, uint32_t sl_index, pusch_list_t& pusch_lst) : + bwp_cfg(cfg_), + slot_idx(sl_index), + puschs(pusch_lst), + ul_prbs(bwp_cfg.cfg.rb_width, bwp_cfg.cfg.start_rb, bwp_cfg.cfg.pdsch.rbg_size_cfg_1) +{} + +void pusch_allocator::reset() +{ + puschs.clear(); + ul_prbs.reset(); +} + +alloc_result pusch_allocator::has_grant_space(uint32_t nof_grants, bool verbose) const +{ + // UL must be active in given slot + if (not bwp_cfg.slots[slot_idx].is_ul) { + if (verbose) { + log_pusch_alloc_failure(bwp_cfg.logger.error, "UL is disabled for slot={}", slot_idx); + } + return alloc_result::no_sch_space; + } + + // No space in Scheduler PDSCH output list + if (puschs.size() + nof_grants > puschs.capacity()) { + if (verbose) { + log_pusch_alloc_failure(bwp_cfg.logger.warning, "Maximum number of PUSCHs={} reached.", puschs.capacity()); + } + return alloc_result::no_sch_space; + } + + return alloc_result::success; +} + +alloc_result +pusch_allocator::is_grant_valid(srsran_search_space_type_t ss_type, const prb_grant& grant, bool verbose) const +{ + alloc_result ret = has_grant_space(1, verbose); + if (ret != alloc_result::success) { + return ret; + } + + if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss_type)) { + // In case of common SearchSpaces, the PRBs must be contiguous + if (grant.is_alloc_type0()) { + log_pusch_alloc_failure(bwp_cfg.logger.warning, "AllocType0 not allowed in common SearchSpace."); + return alloc_result::invalid_grant_params; + } + } + + // Grant PRBs do not collide with previous PDSCH allocations + if (ul_prbs.collides(grant)) { + if (verbose) { + log_pusch_alloc_failure(bwp_cfg.logger.debug, "SCHED: Provided UL PRB mask collides with previous allocations."); + } + return alloc_result::sch_collision; + } + + return alloc_result::success; +} + +pusch_alloc_result +pusch_allocator::alloc_pusch(const srsran_search_space_type_t ss_type, const prb_grant& grant, srsran_dci_ul_nr_t& dci) +{ + alloc_result code = is_grant_valid(ss_type, grant); + if (code != alloc_result::success) { + return code; + } + + return {&alloc_pusch_unchecked(grant, dci)}; +} + +pusch_t& pusch_allocator::alloc_pusch_unchecked(const prb_grant& grant, srsran_dci_ul_nr_t& out_dci) +{ + // Create new PUSCH entry in output PUSCH list + puschs.emplace_back(); + pusch_t& pusch = puschs.back(); + + // Register allocated PRBs in accumulated bitmap + ul_prbs |= grant; + + // Fill DCI with PUSCH freq/time allocation information + out_dci.time_domain_assigment = 0; + if (grant.is_alloc_type0()) { + out_dci.freq_domain_assigment = grant.rbgs().to_uint64(); + } else { + uint32_t nof_prb = bwp_cfg.nof_prb; + out_dci.freq_domain_assigment = srsran_ra_nr_type1_riv(nof_prb, grant.prbs().start(), grant.prbs().length()); + } + + return pusch; +} + +void pusch_allocator::cancel_last_pusch() +{ + srsran_assert(not puschs.empty(), "Trying to abort PUSCH allocation that does not exist"); + puschs.pop_back(); +} + +} // namespace sched_nr_impl +} // namespace srsenb \ No newline at end of file diff --git a/srsgnb/src/stack/mac/sched_nr_signalling.cc b/srsgnb/src/stack/mac/sched_nr_signalling.cc new file mode 100644 index 000000000..9beb4f509 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_signalling.cc @@ -0,0 +1,216 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_signalling.h" +#include "srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h" + +#define POS_IN_BURST_FIRST_BIT_IDX 0 +#define POS_IN_BURST_SECOND_BIT_IDX 1 +#define POS_IN_BURST_THIRD_BIT_IDX 2 +#define POS_IN_BURST_FOURTH_BIT_IDX 3 + +#define DEFAULT_SSB_PERIODICITY 5 +#define MAX_SIB_TX 8 + +namespace srsenb { +namespace sched_nr_impl { + +void sched_nzp_csi_rs(srsran::const_span nzp_csi_rs_sets_cfg, + const srsran_slot_cfg_t& slot_cfg, + nzp_csi_rs_list& csi_rs_list) +{ + for (const srsran_csi_rs_nzp_set_t& set : nzp_csi_rs_sets_cfg) { + // For each NZP-CSI-RS resource available in the set + for (uint32_t i = 0; i < set.count; ++i) { + // Select resource + const srsran_csi_rs_nzp_resource_t& nzp_csi_resource = set.data[i]; + + // Check if the resource is scheduled for this slot + if (srsran_csi_rs_send(&nzp_csi_resource.periodicity, &slot_cfg)) { + if (csi_rs_list.full()) { + srslog::fetch_basic_logger("MAC-NR").error("SCHED: Failed to allocate NZP-CSI RS"); + return; + } + csi_rs_list.push_back(nzp_csi_resource); + } + } + } +} + +void sched_ssb_basic(const slot_point& sl_point, + uint32_t ssb_periodicity, + const srsran_mib_nr_t& mib, + ssb_list& ssb_list) +{ + if (ssb_list.full()) { + srslog::fetch_basic_logger("MAC-NR").error("SCHED: Failed to allocate SSB"); + return; + } + // If the periodicity is 0, it means that the parameter was not passed by the upper layers. + // In that case, we use default value of 5ms (see Clause 4.1, TS 38.213) + if (ssb_periodicity == 0) { + ssb_periodicity = DEFAULT_SSB_PERIODICITY; + } + + uint32_t sl_cnt = sl_point.to_uint(); + // Perform mod operation of slot index by ssb_periodicity; + // "ssb_periodicity * nof_slots_per_subframe" gives the number of slots in 1 ssb_periodicity time interval + uint32_t sl_point_mod = sl_cnt % (ssb_periodicity * (uint32_t)sl_point.nof_slots_per_subframe()); + + // code below is simplified, it assumes 15kHz subcarrier spacing and sub 3GHz carrier + if (sl_point_mod == 0) { + ssb_t ssb_msg = {}; + srsran_mib_nr_t mib_msg = mib; + mib_msg.sfn = sl_point.sfn(); + mib_msg.hrf = (sl_point.slot_idx() % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) >= + SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) / 2); + // This corresponds to "Position in Burst" = 1000 + mib_msg.ssb_idx = 0; + // Remaining MIB parameters remain constant + + // Pack mib message to be sent to PHY + int packing_ret_code = srsran_pbch_msg_nr_mib_pack(&mib_msg, &ssb_msg.pbch_msg); + srsran_assert(packing_ret_code == SRSRAN_SUCCESS, "SSB packing returned en error"); + ssb_list.push_back(ssb_msg); + } +} + +void sched_dl_signalling(bwp_slot_allocator& bwp_alloc) +{ + const bwp_params_t& bwp_params = bwp_alloc.cfg; + slot_point sl_pdcch = bwp_alloc.get_pdcch_tti(); + bwp_slot_grid& sl_grid = bwp_alloc.tx_slot_grid(); + + srsran_slot_cfg_t cfg; + cfg.idx = sl_pdcch.to_uint(); + + // Schedule SSB + sched_ssb_basic(sl_pdcch, bwp_params.cell_cfg.ssb.periodicity_ms, bwp_params.cell_cfg.mib, sl_grid.dl.phy.ssb); + + // Mark SSB region as occupied + if (!sl_grid.dl.phy.ssb.empty()) { + float ssb_offset_hz = + bwp_params.cell_cfg.carrier.ssb_center_freq_hz - bwp_params.cell_cfg.carrier.dl_center_frequency_hz; + int ssb_offset_rb = ceil(ssb_offset_hz / (15000.0f * 12)); + int ssb_start_rb = bwp_params.cell_cfg.carrier.nof_prb / 2 + ssb_offset_rb - 10; + uint32_t ssb_len_rb = 20; + assert(ssb_start_rb >= 0 && ssb_start_rb + ssb_len_rb < bwp_params.cell_cfg.carrier.nof_prb); + sl_grid.reserve_pdsch(prb_grant({(uint32_t)ssb_start_rb, ssb_start_rb + ssb_len_rb})); + } + + // Schedule NZP-CSI-RS + sched_nzp_csi_rs(bwp_params.cfg.pdsch.nzp_csi_rs_sets, cfg, sl_grid.dl.phy.nzp_csi_rs); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +si_sched::si_sched(const bwp_params_t& bwp_cfg_) : + bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger(bwp_cfg_.sched_cfg.logger_name)) +{ + for (uint32_t i = 0; i < bwp_cfg->cell_cfg.sibs.size(); ++i) { + pending_sis.emplace_back(); + si_msg_ctxt_t& si = pending_sis.back(); + si.n = i; + si.len_bytes = bwp_cfg->cell_cfg.sibs[i].len; + si.period_frames = bwp_cfg->cell_cfg.sibs[i].period_rf; + si.win_len_slots = bwp_cfg->cell_cfg.sibs[i].si_window_slots; + si.si_softbuffer = harq_softbuffer_pool::get_instance().get_tx(bwp_cfg->nof_prb); + } +} + +void si_sched::run_slot(bwp_slot_allocator& bwp_alloc) +{ + if (not bwp_alloc.cfg.cfg.pdcch.coreset_present[0]) { + // CORESET#0 must be present, otherwise SIs are not allocated + // TODO: provide proper config + return; + } + const uint32_t si_aggr_level = 2; + const uint32_t ss_id = 0; + slot_point sl_pdcch = bwp_alloc.get_pdcch_tti(); + prb_bitmap prbs = bwp_alloc.res_grid()[sl_pdcch].pdschs.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + + // Update SI windows + uint32_t N = bwp_cfg->slots.size(); + for (si_msg_ctxt_t& si : pending_sis) { + uint32_t x = (si.n - 1) * si.win_len_slots; + + if (not si.win_start.valid()) { + bool start_window; + if (si.n == 0) { + // SIB1 (slot index zero of even frames) + start_window = sl_pdcch.slot_idx() == 0 and sl_pdcch.sfn() % 2 == 0; + } else { + // 5.2.2.3.2 - Acquisition of SI message + start_window = + (sl_pdcch.sfn() % si.period_frames == x / N) and sl_pdcch.slot_idx() == x % bwp_cfg->slots.size(); + } + if (start_window) { + // If start of SI message window + si.win_start = sl_pdcch; + si.n_tx = 0; + } + } else if (si.win_start + si.win_len_slots >= sl_pdcch) { + // If end of SI message window + srsran_always_assert( + si.n == 0, "SCHED: Could not allocate SIB1, len=%d. Cause: %s", si.len_bytes, to_string(si.result)); + logger.warning( + "SCHED: Could not allocate SI message idx=%d, len=%d. Cause: %s", si.n, si.len_bytes, to_string(si.result)); + si.win_start.clear(); + } + } + + // Schedule pending SIBs + if (not bwp_cfg->slots[sl_pdcch.slot_idx()].is_dl) { + return; + } + for (si_msg_ctxt_t& si : pending_sis) { + if (not si.win_start.valid() or si.n_tx >= MAX_SIB_TX) { + continue; + } + + // Attempt grants with increasing number of PRBs (if the number of PRBs is too low, the coderate is invalid) + si.result = alloc_result::invalid_coderate; + uint32_t nprbs = 8; + prb_interval grant = find_empty_interval_of_length(prbs, nprbs, 0); + if (grant.length() >= nprbs) { + si.result = bwp_alloc.alloc_si(si_aggr_level, si.n, si.n_tx, grant, *si.si_softbuffer.get()); + if (si.result == alloc_result::success) { + // SIB scheduled successfully + si.win_start.clear(); + si.n_tx++; + if (si.n == 0) { + logger.debug("SCHED: Allocated SIB1, len=%d.", si.len_bytes); + } else { + logger.debug("SCHED: Allocated SI message idx=%d, len=%d.", si.n, si.len_bytes); + } + } + } + if (si.result != alloc_result::success) { + logger.warning("SCHED: Failed to allocate SI%s%d ntx=%d", si.n == 0 ? "B" : " message idx=", si.n + 1, si.n_tx); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_time_rr.cc b/srsgnb/src/stack/mac/sched_nr_time_rr.cc new file mode 100644 index 000000000..e178af892 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_time_rr.cc @@ -0,0 +1,116 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_time_rr.h" + +namespace srsenb { +namespace sched_nr_impl { + +/** + * @brief Algorithm to select next UE to allocate in a time-domain RR fashion + * @param ue_db map of "slot_ue" + * @param rr_count starting index to select next UE + * @param p callable with signature "bool(slot_ue&)" that returns true if UE allocation was successful + * @return true if a UE was allocated + */ +template +bool round_robin_apply(slot_ue_map_t& ue_db, uint32_t rr_count, Predicate p) +{ + if (ue_db.empty()) { + return false; + } + auto it = ue_db.begin(); + std::advance(it, (rr_count % ue_db.size())); + for (uint32_t count = 0; count < ue_db.size(); ++count, ++it) { + if (it == ue_db.end()) { + // wrap-around + it = ue_db.begin(); + } + if (p(it->second)) { + return true; + } + } + return false; +} + +void sched_nr_time_rr::sched_dl_users(slot_ue_map_t& ue_db, bwp_slot_allocator& slot_alloc) +{ + // Start with retxs + auto retx_ue_function = [&slot_alloc](slot_ue& ue) { + if (ue.h_dl != nullptr and ue.h_dl->has_pending_retx(slot_alloc.get_tti_rx())) { + alloc_result res = slot_alloc.alloc_pdsch(ue, ue->find_ss_id(srsran_dci_format_nr_1_0), ue.h_dl->prbs()); + if (res == alloc_result::success) { + return true; + } + } + return false; + }; + if (round_robin_apply(ue_db, slot_alloc.get_pdcch_tti().to_uint(), retx_ue_function)) { + return; + } + + // Move on to new txs + auto newtx_ue_function = [&slot_alloc](slot_ue& ue) { + if (ue.dl_bytes > 0 and ue.h_dl != nullptr and ue.h_dl->empty()) { + int ss_id = ue->find_ss_id(srsran_dci_format_nr_1_0); + if (ss_id < 0) { + return false; + } + prb_grant prbs = find_optimal_dl_grant(slot_alloc, ue, ss_id); + alloc_result res = slot_alloc.alloc_pdsch(ue, ss_id, prbs); + if (res == alloc_result::success) { + return true; + } + } + return false; + }; + round_robin_apply(ue_db, slot_alloc.get_pdcch_tti().to_uint(), newtx_ue_function); +} + +void sched_nr_time_rr::sched_ul_users(slot_ue_map_t& ue_db, bwp_slot_allocator& slot_alloc) +{ + // Start with retxs + if (round_robin_apply(ue_db, slot_alloc.get_pdcch_tti().to_uint(), [&slot_alloc](slot_ue& ue) { + if (ue.h_ul != nullptr and ue.h_ul->has_pending_retx(slot_alloc.get_tti_rx())) { + alloc_result res = slot_alloc.alloc_pusch(ue, ue.h_ul->prbs()); + if (res == alloc_result::success) { + return true; + } + } + return false; + })) { + return; + } + + // Move on to new txs + round_robin_apply(ue_db, slot_alloc.get_pdcch_tti().to_uint(), [&slot_alloc](slot_ue& ue) { + if (ue.ul_bytes > 0 and ue.h_ul != nullptr and ue.h_ul->empty()) { + alloc_result res = slot_alloc.alloc_pusch(ue, prb_interval{0, slot_alloc.cfg.cfg.rb_width}); + if (res == alloc_result::success) { + return true; + } + } + return false; + }); +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_ue.cc b/srsgnb/src/stack/mac/sched_nr_ue.cc new file mode 100644 index 000000000..d79a46adf --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_ue.cc @@ -0,0 +1,262 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_ue.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsran/common/string_helpers.h" +#include "srsran/mac/mac_sch_pdu_nr.h" + +namespace srsenb { +namespace sched_nr_impl { + +int ue_buffer_manager::get_dl_tx_total() const +{ + int total_bytes = base_type::get_dl_tx_total(); + for (ue_buffer_manager::ce_t ce : pending_ces) { + total_bytes += srsran::mac_sch_subpdu_nr::sizeof_ce(ce.lcid, false); + } + return total_bytes; +} + +/** + * @brief Allocates LCIDs and update US buffer states depending on available resources and checks if there is SRB0/CCCH + MAC PDU segmentation + + * @param rem_bytes TBS to be filled with MAC CEs and MAC SDUs [in bytes] + * @param reset_buf_states If true, when there is SRB0/CCCH MAC PDU segmentation, restore the UE buffers and scheduled + LCIDs as before running this function + * @return true if there is no SRB0/CCCH MAC PDU segmentation, false otherwise + */ +bool ue_buffer_manager::pdu_builder::alloc_subpdus(uint32_t rem_bytes, sched_nr_interface::dl_pdu_t& pdu) +{ + // First step: allocate MAC CEs until resources allow + srsran::deque restore_ces; + for (ce_t ce : parent->pending_ces) { + if (ce.cc == cc) { + // Note: This check also avoids thread collisions across UE carriers + uint32_t size_ce = srsran::mac_sch_subpdu_nr::sizeof_ce(ce.lcid, false); + if (size_ce > rem_bytes) { + break; + } + rem_bytes -= size_ce; + pdu.subpdus.push_back(ce.lcid); + parent->pending_ces.pop_front(); + } + } + + // Second step: allocate the remaining LCIDs (LCIDs for MAC CEs are addressed above) + for (uint32_t lcid = 0; rem_bytes > 0 and is_lcid_valid(lcid); ++lcid) { + uint32_t pending_lcid_bytes = parent->get_dl_tx_total(lcid); + // Return false if the TBS is too small to store the entire CCCH buffer without segmentation + if (lcid == srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH and pending_lcid_bytes > rem_bytes) { + pdu.subpdus.push_back(lcid); + return false; + } + if (pending_lcid_bytes > 0) { + rem_bytes -= std::min(rem_bytes, pending_lcid_bytes); + pdu.subpdus.push_back(lcid); + } + } + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +slot_ue::slot_ue(ue_carrier& ue_, slot_point slot_tx_) : ue(&ue_), pdcch_slot(slot_tx_) +{ + const uint32_t k0 = 0; + pdsch_slot = pdcch_slot + k0; + uint32_t k1 = ue->bwp_cfg.get_k1(pdsch_slot); + uci_slot = pdsch_slot + k1; + uint32_t k2 = ue->bwp_cfg.active_bwp().pusch_ra_list[0].K; + pusch_slot = pdcch_slot + k2; + + dl_active = ue->cell_params.bwps[0].slots[pdsch_slot.slot_idx()].is_dl; + if (dl_active) { + dl_bytes = ue->common_ctxt.pending_dl_bytes; + h_dl = ue->harq_ent.find_pending_dl_retx(); + if (h_dl == nullptr) { + h_dl = ue->harq_ent.find_empty_dl_harq(); + } + } + ul_active = ue->cell_params.bwps[0].slots[pusch_slot.slot_idx()].is_ul; + if (ul_active) { + ul_bytes = ue->common_ctxt.pending_ul_bytes; + h_ul = ue->harq_ent.find_pending_ul_retx(); + if (h_ul == nullptr) { + h_ul = ue->harq_ent.find_empty_ul_harq(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ue_carrier::ue_carrier(uint16_t rnti_, + const ue_cfg_manager& uecfg_, + const cell_config_manager& cell_params_, + const ue_context_common& ctxt, + const ue_buffer_manager::pdu_builder& pdu_builder_) : + rnti(rnti_), + cc(cell_params_.cc), + logger(srslog::fetch_basic_logger(cell_params_.sched_args.logger_name)), + bwp_cfg(rnti_, cell_params_.bwps[0], uecfg_), + cell_params(cell_params_), + pdu_builder(pdu_builder_), + common_ctxt(ctxt), + harq_ent(rnti_, cell_params_.nof_prb(), SCHED_NR_MAX_HARQ, cell_params_.bwps[0].logger) +{} + +void ue_carrier::set_cfg(const ue_cfg_manager& ue_cfg) +{ + bwp_cfg = ue_carrier_params_t(rnti, cell_params.bwps[0], ue_cfg); +} + +int ue_carrier::dl_ack_info(uint32_t pid, uint32_t tb_idx, bool ack) +{ + int tbs = harq_ent.dl_ack_info(pid, tb_idx, ack); + if (tbs < 0) { + logger.warning("SCHED: rnti=0x%x received DL HARQ-ACK for empty pid=%d", rnti, pid); + return tbs; + } + if (ack) { + metrics.tx_brate += tbs; + } else { + metrics.tx_errors++; + } + metrics.tx_pkts++; + return tbs; +} + +int ue_carrier::ul_crc_info(uint32_t pid, bool crc) +{ + int ret = harq_ent.ul_crc_info(pid, crc); + if (ret < 0) { + logger.warning("SCHED: rnti=0x%x,cc=%d received CRC for empty pid=%d", rnti, cc, pid); + } + return ret; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Generate basic UE config at the point of RACH +sched_nr_ue_cfg_t get_rach_ue_cfg_helper(uint32_t cc, const sched_params_t& sched_params) +{ + sched_nr_ue_cfg_t uecfg = {}; + uecfg.carriers.resize(1); + uecfg.carriers[0].active = true; + uecfg.carriers[0].cc = cc; + uecfg.phy_cfg = sched_params.cells[cc].default_ue_phy_cfg; + return uecfg; +} + +ue::ue(uint16_t rnti_, uint32_t cc, const sched_params_t& sched_cfg_) : + ue(rnti_, get_rach_ue_cfg_helper(cc, sched_cfg_), sched_cfg_) +{} + +ue::ue(uint16_t rnti_, const sched_nr_ue_cfg_t& uecfg, const sched_params_t& sched_cfg_) : + rnti(rnti_), + sched_cfg(sched_cfg_), + buffers(rnti_, srslog::fetch_basic_logger(sched_cfg_.sched_cfg.logger_name)), + ue_cfg(uecfg.carriers[0].cc) +{ + set_cfg(uecfg); +} + +void ue::set_cfg(const sched_nr_ue_cfg_t& cfg) +{ + ue_cfg.apply_config_request(cfg); + for (auto& ue_cc_cfg : cfg.carriers) { + if (ue_cc_cfg.active) { + if (carriers[ue_cc_cfg.cc] == nullptr) { + carriers[ue_cc_cfg.cc] = std::make_unique(rnti, + ue_cfg, + sched_cfg.cells[ue_cc_cfg.cc], + common_ctxt, + ue_buffer_manager::pdu_builder{ue_cc_cfg.cc, buffers}); + } else { + carriers[ue_cc_cfg.cc]->set_cfg(ue_cfg); + } + } + } + + buffers.config_lcids(ue_cfg.ue_bearers); +} + +void ue::add_dl_mac_ce(uint32_t ce_lcid, uint32_t nof_cmds) +{ + for (uint32_t i = 0; i < nof_cmds; ++i) { + // If not specified otherwise, the CE is transmitted in PCell + buffers.pending_ces.push_back(ue_buffer_manager::ce_t{ce_lcid, cfg().carriers[0].cc}); + } +} + +void ue::rlc_buffer_state(uint32_t lcid, uint32_t newtx, uint32_t priotx) +{ + buffers.dl_buffer_state(lcid, newtx, priotx); +} + +void ue::new_slot(slot_point pdcch_slot) +{ + last_tx_slot = pdcch_slot; + + for (std::unique_ptr& cc : carriers) { + if (cc != nullptr) { + cc->harq_ent.new_slot(pdcch_slot - TX_ENB_DELAY); + } + } + + // Compute pending DL/UL bytes for {rnti, pdcch_slot} + if (sched_cfg.sched_cfg.auto_refill_buffer) { + common_ctxt.pending_dl_bytes = 1000000; + common_ctxt.pending_ul_bytes = 1000000; + } else { + common_ctxt.pending_dl_bytes = buffers.get_dl_tx_total(); + common_ctxt.pending_ul_bytes = buffers.get_bsr(); + for (auto& ue_cc_cfg : ue_cfg.carriers) { + auto& cc = carriers[ue_cc_cfg.cc]; + if (cc != nullptr) { + // Discount UL HARQ pending bytes to BSR + for (uint32_t pid = 0; pid < cc->harq_ent.nof_ul_harqs(); ++pid) { + if (not cc->harq_ent.ul_harq(pid).empty()) { + common_ctxt.pending_ul_bytes -= std::min(cc->harq_ent.ul_harq(pid).tbs() / 8, common_ctxt.pending_ul_bytes); + if (last_sr_slot.valid() and cc->harq_ent.ul_harq(pid).harq_slot_tx() > last_sr_slot) { + last_sr_slot.clear(); + } + } + } + } + } + if (common_ctxt.pending_ul_bytes == 0 and last_sr_slot.valid()) { + // If unanswered SR is pending + common_ctxt.pending_ul_bytes = 512; + } + } +} + +slot_ue ue::make_slot_ue(slot_point pdcch_slot, uint32_t cc) +{ + srsran_assert(carriers[cc] != nullptr, "make_slot_ue() called for unknown rnti=0x%x,cc=%d", rnti, cc); + return slot_ue(*carriers[cc], pdcch_slot); +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_nr_worker.cc b/srsgnb/src/stack/mac/sched_nr_worker.cc new file mode 100644 index 000000000..7e3890ab5 --- /dev/null +++ b/srsgnb/src/stack/mac/sched_nr_worker.cc @@ -0,0 +1,224 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_worker.h" +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" +#include "srsgnb/hdr/stack/mac/sched_nr_signalling.h" +#include "srsran/common/string_helpers.h" + +namespace srsenb { +namespace sched_nr_impl { + +cc_worker::cc_worker(const cell_config_manager& params) : + cfg(params), logger(srslog::fetch_basic_logger(params.sched_args.logger_name)) +{ + for (uint32_t bwp_id = 0; bwp_id < cfg.bwps.size(); ++bwp_id) { + bwps.emplace_back(cfg.bwps[bwp_id]); + } + + // Pre-allocate HARQs in common pool of softbuffers + harq_softbuffer_pool::get_instance().init_pool(cfg.nof_prb()); +} + +void cc_worker::dl_rach_info(const sched_nr_interface::rar_info_t& rar_info) +{ + bwps[0].ra.dl_rach_info(rar_info); +} + +/// Called within a locked context, to generate {slot, cc} scheduling decision + +dl_sched_res_t* cc_worker::run_slot(slot_point tx_sl, ue_map_t& ue_db) +{ + // Reset old sched outputs + if (not last_tx_sl.valid()) { + last_tx_sl = tx_sl; + } + while (last_tx_sl != tx_sl) { + last_tx_sl++; + slot_point old_slot = last_tx_sl - TX_ENB_DELAY - 1; + for (bwp_manager& bwp : bwps) { + bwp.grid[old_slot].reset(); + } + } + + // Reserve UEs for this worker slot (select candidate UEs) + for (auto& ue_pair : ue_db) { + uint16_t rnti = ue_pair.first; + ue& u = *ue_pair.second; + if (u.carriers[cfg.cc] == nullptr) { + continue; + } + + // info for a given UE on a slot to be process + slot_ues.insert(rnti, u.make_slot_ue(tx_sl, cfg.cc)); + if (slot_ues[rnti].empty()) { + // Failed to generate slot UE because UE has no conditions for DL/UL tx + slot_ues.erase(rnti); + continue; + } + // UE acquired successfully for scheduling in this {slot, cc} + } + + // Create an BWP allocator object that will passed along to RA, SI, Data schedulers + bwp_slot_allocator bwp_alloc{bwps[0].grid, tx_sl, slot_ues}; + + // Log UEs state for slot + log_sched_slot_ues(logger, tx_sl, cfg.cc, slot_ues); + + const uint32_t ss_id = 0; + slot_point sl_pdcch = bwp_alloc.get_pdcch_tti(); + + prb_bitmap prbs_before = bwp_alloc.res_grid()[sl_pdcch].pdschs.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + // Allocate cell DL signalling + sched_dl_signalling(bwp_alloc); + + prb_bitmap prbs_after = bwp_alloc.res_grid()[sl_pdcch].pdschs.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + + // Allocate pending SIBs + bwps[0].si.run_slot(bwp_alloc); + + // Allocate pending RARs + bwps[0].ra.run_slot(bwp_alloc); + + // TODO: Prioritize PDCCH scheduling for DL and UL data in a Round-Robin fashion + alloc_dl_ues(bwp_alloc); + alloc_ul_ues(bwp_alloc); + + // Post-processing of scheduling decisions + postprocess_decisions(bwp_alloc); + + // Log CC scheduler result + log_sched_bwp_result(logger, bwp_alloc.get_pdcch_tti(), bwps[0].grid, slot_ues); + + // releases UE resources + slot_ues.clear(); + + return &bwp_alloc.tx_slot_grid().dl; +} + +ul_sched_t* cc_worker::get_ul_sched(slot_point sl) +{ + return &bwps[0].grid[sl].ul; +} + +void cc_worker::alloc_dl_ues(bwp_slot_allocator& bwp_alloc) +{ + if (not cfg.sched_args.pdsch_enabled) { + return; + } + bwps[0].data_sched->sched_dl_users(slot_ues, bwp_alloc); +} + +void cc_worker::alloc_ul_ues(bwp_slot_allocator& bwp_alloc) +{ + if (not cfg.sched_args.pusch_enabled) { + return; + } + bwps[0].data_sched->sched_ul_users(slot_ues, bwp_alloc); +} + +void cc_worker::postprocess_decisions(bwp_slot_allocator& bwp_alloc) +{ + auto& bwp_slot = bwps[0].grid[bwp_alloc.get_pdcch_tti()]; + srsran_slot_cfg_t slot_cfg{}; + slot_cfg.idx = bwp_alloc.get_pdcch_tti().to_uint(); + + for (auto& ue_pair : slot_ues) { + auto& ue = ue_pair.second; + // Group pending HARQ ACKs + srsran_pdsch_ack_nr_t ack = {}; + + for (auto& h_ack : bwp_slot.pending_acks) { + if (h_ack.res.rnti == ue->rnti) { + ack.nof_cc = 1; + + srsran_harq_ack_m_t ack_m = {}; + ack_m.resource = h_ack.res; + ack_m.present = true; + srsran_harq_ack_insert_m(&ack, &ack_m); + } + } + + srsran_uci_cfg_nr_t uci_cfg = {}; + if (not ue->phy().get_uci_cfg(slot_cfg, ack, uci_cfg)) { + logger.error("Error getting UCI configuration"); + continue; + } + + if (uci_cfg.ack.count == 0 and uci_cfg.nof_csi == 0 and uci_cfg.o_sr == 0) { + continue; + } + + bool has_pusch = false; + for (auto& pusch : bwp_slot.ul.pusch) { + if (pusch.sch.grant.rnti == ue->rnti) { + // Put UCI configuration in PUSCH config + has_pusch = true; + + // If has PUSCH, no SR shall be received + uci_cfg.o_sr = 0; + + if (not ue->phy().get_pusch_uci_cfg(slot_cfg, uci_cfg, pusch.sch)) { + logger.error("Error setting UCI configuration in PUSCH"); + continue; + } + break; + } + } + if (not has_pusch) { + // If any UCI information is triggered, schedule PUCCH + if (bwp_slot.ul.pucch.full()) { + logger.warning("SCHED: Cannot fit pending UCI into PUCCH"); + continue; + } + bwp_slot.ul.pucch.emplace_back(); + mac_interface_phy_nr::pucch_t& pucch = bwp_slot.ul.pucch.back(); + + uci_cfg.pucch.rnti = ue->rnti; + pucch.candidates.emplace_back(); + pucch.candidates.back().uci_cfg = uci_cfg; + if (not ue->phy().get_pucch_uci_cfg(slot_cfg, uci_cfg, pucch.pucch_cfg, pucch.candidates.back().resource)) { + logger.error("Error getting UCI CFG"); + continue; + } + + // If this slot has a SR opportunity and the selected PUCCH format is 1, consider positive SR. + if (uci_cfg.o_sr > 0 and uci_cfg.ack.count > 0 and + pucch.candidates.back().resource.format == SRSRAN_PUCCH_NR_FORMAT_1) { + // Set SR negative + if (uci_cfg.o_sr > 0) { + uci_cfg.sr_positive_present = false; + } + + // Append new resource + pucch.candidates.emplace_back(); + pucch.candidates.back().uci_cfg = uci_cfg; + if (not ue->phy().get_pucch_uci_cfg(slot_cfg, uci_cfg, pucch.pucch_cfg, pucch.candidates.back().resource)) { + logger.error("Error getting UCI CFG"); + continue; + } + } + } + } +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/sched_ue/ue_cfg_manager.cc b/srsgnb/src/stack/mac/sched_ue/ue_cfg_manager.cc new file mode 100644 index 000000000..4541b583e --- /dev/null +++ b/srsgnb/src/stack/mac/sched_ue/ue_cfg_manager.cc @@ -0,0 +1,108 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_ue/ue_cfg_manager.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsran/asn1/rrc_nr_utils.h" + +namespace srsenb { +namespace sched_nr_impl { + +ue_cfg_manager::ue_cfg_manager(uint32_t enb_cc_idx) : carriers(1) +{ + carriers[enb_cc_idx].active = true; + carriers[enb_cc_idx].cc = enb_cc_idx; + ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH; +} + +ue_cfg_manager::ue_cfg_manager(const sched_nr_ue_cfg_t& cfg_req) : ue_cfg_manager() +{ + apply_config_request(cfg_req); +} + +int ue_cfg_manager::apply_config_request(const sched_nr_ue_cfg_t& cfg_req) +{ + maxharq_tx = cfg_req.maxharq_tx; + carriers = cfg_req.carriers; + phy_cfg = cfg_req.phy_cfg; + + if (cfg_req.sp_cell_cfg.is_present()) { + srsran::make_pdsch_cfg_from_serv_cell(cfg_req.sp_cell_cfg->sp_cell_cfg_ded, &phy_cfg.pdsch); + srsran::make_csi_cfg_from_serv_cell(cfg_req.sp_cell_cfg->sp_cell_cfg_ded, &phy_cfg.csi); + } + for (uint32_t lcid : cfg_req.lc_ch_to_rem) { + assert(lcid > 0 && "LCID=0 cannot be removed"); + ue_bearers[lcid] = {}; + } + for (const sched_nr_ue_lc_ch_cfg_t& lc_ch : cfg_req.lc_ch_to_add) { + assert(lc_ch.lcid > 0 && "LCID=0 cannot be configured"); + ue_bearers[lc_ch.lcid] = lc_ch.cfg; + } + + return SRSRAN_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +ue_carrier_params_t::ue_carrier_params_t(uint16_t rnti_, const bwp_params_t& bwp_cfg_, const ue_cfg_manager& uecfg_) : + rnti(rnti_), cc(bwp_cfg_.cc), cfg_(&uecfg_), bwp_cfg(&bwp_cfg_), cached_dci_cfg(uecfg_.phy_cfg.get_dci_cfg()) +{ + std::fill(ss_id_to_cce_idx.begin(), ss_id_to_cce_idx.end(), SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE); + const auto& pdcch = phy().pdcch; + auto ss_view = srsran::make_optional_span(pdcch.search_space, pdcch.search_space_present); + auto coreset_view = srsran::make_optional_span(pdcch.coreset, pdcch.coreset_present); + for (const auto& ss : ss_view) { + srsran_assert(coreset_view.contains(ss.coreset_id), + "Invalid mapping search space id=%d to coreset id=%d", + ss.id, + ss.coreset_id); + cce_positions_list.emplace_back(); + get_dci_locs(coreset_view[ss.coreset_id], ss, rnti, cce_positions_list.back()); + ss_id_to_cce_idx[ss.id] = cce_positions_list.size() - 1; + } +} + +int ue_carrier_params_t::find_ss_id(srsran_dci_format_nr_t dci_fmt) const +{ + static const uint32_t aggr_idx = 2; // TODO: Make it dynamic + static const srsran_rnti_type_t rnti_type = srsran_rnti_type_c; // TODO: Use TC-RNTI for Msg4 + + auto active_ss_lst = view_active_search_spaces(phy().pdcch); + + for (const srsran_search_space_t& ss : active_ss_lst) { + // Prioritize UE-dedicated SearchSpaces + if (ss.type == srsran_search_space_type_ue and ss.nof_candidates[aggr_idx] > 0 and + contains_dci_format(ss, dci_fmt) and is_rnti_type_valid_in_search_space(rnti_type, ss.type)) { + return ss.id; + } + } + // Search Common SearchSpaces + for (const srsran_search_space_t& ss : active_ss_lst) { + if (SRSRAN_SEARCH_SPACE_IS_COMMON(ss.type) and ss.nof_candidates[aggr_idx] > 0 and + contains_dci_format(ss, dci_fmt) and is_rnti_type_valid_in_search_space(rnti_type, ss.type)) { + return ss.id; + } + } + return -1; +} + +} // namespace sched_nr_impl +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/test/CMakeLists.txt b/srsgnb/src/stack/mac/test/CMakeLists.txt new file mode 100644 index 000000000..4f38b08f0 --- /dev/null +++ b/srsgnb/src/stack/mac/test/CMakeLists.txt @@ -0,0 +1,68 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set_directory_properties(PROPERTIES LABELS "sched;nr") + +add_library(sched_nr_test_suite STATIC sched_nr_common_test.cc sched_nr_ue_ded_test_suite.cc sched_nr_sim_ue.cc) +target_link_libraries(sched_nr_test_suite srsgnb_mac srsran_common rrc_nr_asn1) + +add_executable(sched_nr_parallel_test sched_nr_parallel_test.cc) +target_link_libraries(sched_nr_parallel_test + srsgnb_mac + sched_nr_test_suite + srsran_common + rrc_nr_asn1 + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) +add_nr_test(sched_nr_parallel_test sched_nr_parallel_test) + +add_executable(sched_nr_prb_test sched_nr_prb_test.cc) +target_link_libraries(sched_nr_prb_test + srsgnb_mac + srsran_common + rrc_nr_asn1 + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) +add_nr_test(sched_nr_prb_test sched_nr_prb_test) + +add_executable(sched_nr_pdcch_test sched_nr_pdcch_test.cc) +target_link_libraries(sched_nr_pdcch_test srsgnb_mac sched_nr_test_suite srsran_common rrc_nr_asn1) +add_nr_test(sched_nr_pdcch_test sched_nr_pdcch_test) + +add_executable(sched_nr_sch_test sched_nr_sch_test.cc) +target_link_libraries(sched_nr_sch_test srsgnb_mac sched_nr_test_suite srsran_common rrc_nr_asn1) +add_nr_test(sched_nr_sch_test sched_nr_sch_test) + +add_executable(sched_nr_rar_test sched_nr_rar_test.cc) +target_link_libraries(sched_nr_rar_test srsgnb_mac sched_nr_test_suite srsran_common rrc_nr_asn1) +add_nr_test(sched_nr_rar_test sched_nr_rar_test) + +add_executable(sched_nr_dci_utilities_tests sched_nr_dci_utilities_tests.cc) +target_link_libraries(sched_nr_dci_utilities_tests srsgnb_mac srsran_common rrc_nr_asn1) +add_nr_test(sched_nr_dci_utilities_tests sched_nr_dci_utilities_tests) + +add_executable(sched_nr_test sched_nr_test.cc) +target_link_libraries(sched_nr_test + srsgnb_mac + sched_nr_test_suite + rrc_nr_asn1 + srsran_common ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) +add_nr_test(sched_nr_test sched_nr_test) diff --git a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h new file mode 100644 index 000000000..6d1559c9c --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h @@ -0,0 +1,215 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_CFG_GENERATORS_H +#define SRSRAN_SCHED_NR_CFG_GENERATORS_H + +#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h" +#include "srsran/asn1/rrc_nr_utils.h" +#include "srsran/common/phy_cfg_nr_default.h" + +namespace srsenb { + +inline srsran_coreset_t get_default_coreset0(uint32_t nof_prb) +{ + srsran_coreset_t coreset{}; + coreset.id = 0; + coreset.duration = 1; + coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; ++i) { + coreset.freq_resources[i] = i < (nof_prb / 6); + } + return coreset; +} + +inline srsran_search_space_t get_default_search_space0() +{ + srsran_search_space_t ss{}; + ss.coreset_id = 0; + ss.nof_formats = 1; + ss.formats[0] = srsran_dci_format_nr_1_0; + ss.type = srsran_search_space_type_common_0; + ss.id = 0; + ss.nof_candidates[2] = 1; + ss.duration = 1; + return ss; +} + +inline sched_nr_cell_cfg_t get_default_cell_cfg(const srsran::phy_cfg_nr_t& phy_cfg = srsran::phy_cfg_nr_default_t{ + srsran::phy_cfg_nr_default_t::reference_cfg_t{}}) +{ + sched_nr_cell_cfg_t cell_cfg{}; + + cell_cfg.pci = phy_cfg.carrier.pci; + cell_cfg.dl_center_frequency_hz = phy_cfg.carrier.dl_center_frequency_hz; + cell_cfg.ul_center_frequency_hz = phy_cfg.carrier.ul_center_frequency_hz; + cell_cfg.ssb_center_freq_hz = phy_cfg.carrier.ssb_center_freq_hz; + cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list.resize(1); + cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing = + (asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.carrier.scs; + cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = + phy_cfg.carrier.offset_to_carrier; + cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing = + (asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.carrier.scs; + cell_cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common_present = true; + srsran::fill_rach_cfg_common(phy_cfg.prach, cell_cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common.set_setup()); + cell_cfg.dl_cell_nof_prb = phy_cfg.carrier.nof_prb; + cell_cfg.nof_layers = phy_cfg.carrier.max_mimo_layers; + cell_cfg.ssb_periodicity_ms = phy_cfg.ssb.periodicity_ms; + for (uint32_t i = 0; i < cell_cfg.ssb_positions_in_burst.in_one_group.length(); ++i) { + cell_cfg.ssb_positions_in_burst.in_one_group.set(i, phy_cfg.ssb.position_in_burst[i]); + } + // TODO: phy_cfg.ssb_positions_in_burst.group_presence_present + cell_cfg.dmrs_type_a_position.value = asn1::rrc_nr::mib_s::dmrs_type_a_position_opts::pos2; + cell_cfg.ssb_scs.value = (asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.ssb.scs; + cell_cfg.pdcch_cfg_sib1.ctrl_res_set_zero = 0; + cell_cfg.pdcch_cfg_sib1.search_space_zero = 0; + + cell_cfg.bwps.resize(1); + cell_cfg.bwps[0].pdcch = phy_cfg.pdcch; + cell_cfg.bwps[0].pdsch = phy_cfg.pdsch; + cell_cfg.bwps[0].pusch = phy_cfg.pusch; + cell_cfg.bwps[0].pucch = phy_cfg.pucch; + cell_cfg.bwps[0].harq_ack = phy_cfg.harq_ack; + cell_cfg.bwps[0].rb_width = phy_cfg.carrier.nof_prb; + + if (phy_cfg.duplex.mode == SRSRAN_DUPLEX_MODE_TDD) { + cell_cfg.tdd_ul_dl_cfg_common.emplace(); + srsran_assert(srsran::make_phy_tdd_cfg( + phy_cfg.duplex, srsran_subcarrier_spacing_15kHz, &cell_cfg.tdd_ul_dl_cfg_common.value()), + "Failed to generate TDD config"); + } + + return cell_cfg; +} + +inline std::vector get_default_cells_cfg( + uint32_t nof_sectors, + const srsran::phy_cfg_nr_t& phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}) +{ + std::vector cells; + cells.reserve(nof_sectors); + for (uint32_t i = 0; i < nof_sectors; ++i) { + cells.push_back(get_default_cell_cfg(phy_cfg)); + } + return cells; +} + +inline sched_nr_interface::ue_cfg_t get_rach_ue_cfg(uint32_t cc, + const srsran::phy_cfg_nr_t& phy_cfg = srsran::phy_cfg_nr_default_t{ + srsran::phy_cfg_nr_default_t::reference_cfg_t{}}) +{ + sched_nr_interface::ue_cfg_t uecfg{}; + + // set PCell + uecfg.carriers.resize(1); + uecfg.carriers[0].active = true; + uecfg.carriers[0].cc = cc; + + // set basic PHY config + uecfg.phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}; + uecfg.phy_cfg.csi = {}; + for (srsran_search_space_t& ss : view_active_search_spaces(uecfg.phy_cfg.pdcch)) { + // disable UE-specific search spaces + if (ss.type == srsran_search_space_type_ue) { + uecfg.phy_cfg.pdcch.search_space_present[ss.id] = false; + } + } + + return uecfg; +} + +inline sched_nr_interface::ue_cfg_t get_default_ue_cfg( + uint32_t nof_cc, + const srsran::phy_cfg_nr_t& phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}) +{ + sched_nr_interface::ue_cfg_t uecfg{}; + uecfg.carriers.resize(nof_cc); + for (uint32_t cc = 0; cc < nof_cc; ++cc) { + uecfg.carriers[cc].cc = cc; + uecfg.carriers[cc].active = true; + } + uecfg.phy_cfg = phy_cfg; + + return uecfg; +} + +inline sched_nr_cell_cfg_t get_default_sa_cell_cfg_common() +{ + srsran::phy_cfg_nr_default_t::reference_cfg_t ref; + ref.duplex = srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD; + sched_nr_cell_cfg_t cell_cfg = get_default_cell_cfg(srsran::phy_cfg_nr_default_t{ref}); + cell_cfg.bwps[0].pdcch.coreset_present[0] = true; + cell_cfg.bwps[0].pdcch.coreset[0] = get_default_coreset0(52); + cell_cfg.bwps[0].pdcch.coreset[0].offset_rb = 1; + cell_cfg.bwps[0].pdcch.search_space_present[0] = true; + cell_cfg.bwps[0].pdcch.search_space[0] = get_default_search_space0(); + cell_cfg.bwps[0].pdcch.coreset_present[1] = false; + cell_cfg.bwps[0].pdcch.search_space[1].coreset_id = 0; + cell_cfg.bwps[0].pdcch.search_space[1].type = srsran_search_space_type_common_1; + cell_cfg.bwps[0].pdcch.search_space[1].nof_candidates[2] = 1; + cell_cfg.bwps[0].pdcch.search_space[1].nof_formats = 2; + cell_cfg.bwps[0].pdcch.search_space[1].formats[0] = srsran_dci_format_nr_1_0; + cell_cfg.bwps[0].pdcch.search_space[1].formats[1] = srsran_dci_format_nr_0_0; + cell_cfg.bwps[0].pdcch.ra_search_space = cell_cfg.bwps[0].pdcch.search_space[1]; + return cell_cfg; +} + +// Generate default UE-dedicated CORESET config +inline srsran_coreset_t get_default_ue_specific_coreset(uint32_t id, uint32_t pci) +{ + srsran_coreset_t coreset = {}; + coreset.id = id; + coreset.mapping_type = srsran_coreset_mapping_type_non_interleaved; + coreset.duration = 1; + for (uint32_t i = 0; i < 8; ++i) { + coreset.freq_resources[i] = true; + } + coreset.dmrs_scrambling_id_present = false; + coreset.precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle; + coreset.interleaver_size = srsran_coreset_bundle_size_n2; + coreset.reg_bundle_size = srsran_coreset_bundle_size_n6; + coreset.shift_index = pci; + coreset.offset_rb = 0; + return coreset; +} + +inline srsran_search_space_t get_default_ue_specific_search_space(uint32_t id, uint32_t coreset_id) +{ + srsran_search_space_t ss = {}; + ss.id = id; + ss.coreset_id = coreset_id; + ss.duration = 1; + ss.type = srsran_search_space_type_ue; + ss.nof_formats = 2; + ss.formats[0] = srsran_dci_format_nr_1_0; + ss.formats[1] = srsran_dci_format_nr_0_0; + ss.nof_candidates[0] = 2; + ss.nof_candidates[1] = 2; + ss.nof_candidates[2] = 2; + ss.nof_candidates[3] = 1; + ss.nof_candidates[4] = 0; + return ss; +} + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_CFG_GENERATORS_H diff --git a/srsgnb/src/stack/mac/test/sched_nr_common_test.cc b/srsgnb/src/stack/mac/test/sched_nr_common_test.cc new file mode 100644 index 000000000..b501121e3 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_common_test.cc @@ -0,0 +1,156 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_nr_common_test.h" +#include "srsgnb/hdr/stack/mac/sched_nr_cfg.h" +#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h" +#include "srsran/support/srsran_test.h" + +namespace srsenb { + +using namespace sched_nr_impl; + +void test_dci_ctx_consistency(const srsran_pdcch_cfg_nr_t& pdcch_cfg, const srsran_dci_ctx_t& dci_ctx) +{ + TESTASSERT(dci_ctx.coreset_id < SRSRAN_UE_DL_NR_MAX_NOF_CORESET); + TESTASSERT(pdcch_cfg.coreset_present[dci_ctx.coreset_id]); + const srsran_coreset_t& coreset = pdcch_cfg.coreset[dci_ctx.coreset_id]; + + // DCI location + TESTASSERT(dci_ctx.location.L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR); + TESTASSERT(dci_ctx.location.ncce + (1U << dci_ctx.location.L) <= coreset_nof_cces(coreset)); + // RNTI type, SearchSpace type, DCI format + TESTASSERT(sched_nr_impl::is_rnti_type_valid_in_search_space(dci_ctx.rnti_type, dci_ctx.ss_type)); + switch (dci_ctx.rnti_type) { + case srsran_rnti_type_si: + TESTASSERT_EQ(srsran_dci_format_nr_1_0, dci_ctx.format); + TESTASSERT_EQ(srsran_search_space_type_common_0, dci_ctx.ss_type); + TESTASSERT_EQ(SRSRAN_SIRNTI, dci_ctx.rnti); + break; + case srsran_rnti_type_ra: + TESTASSERT_EQ(dci_ctx.format, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(dci_ctx.ss_type, srsran_search_space_type_common_1); + TESTASSERT(pdcch_cfg.ra_search_space_present); + TESTASSERT_EQ(pdcch_cfg.ra_search_space.coreset_id, dci_ctx.coreset_id); + TESTASSERT(pdcch_cfg.ra_search_space.nof_candidates[dci_ctx.location.L] > 0); + break; + case srsran_rnti_type_tc: + TESTASSERT_EQ(srsran_dci_format_nr_1_0, dci_ctx.format); + TESTASSERT_EQ(srsran_search_space_type_common_1, dci_ctx.ss_type); + break; + case srsran_rnti_type_c: + TESTASSERT(dci_ctx.format == srsran_dci_format_nr_1_0 or dci_ctx.format == srsran_dci_format_nr_1_1 or + dci_ctx.format == srsran_dci_format_nr_0_0 or dci_ctx.format == srsran_dci_format_nr_0_1); + break; + default: + srsran_terminate("rnti type=%d not supported", dci_ctx.rnti_type); + } + // CORESET position + TESTASSERT_EQ(srsran_coreset_start_rb(&coreset), dci_ctx.coreset_start_rb); +} + +void test_pdcch_collisions(const srsran_pdcch_cfg_nr_t& pdcch_cfg, + srsran::const_span dl_pdcchs, + srsran::const_span ul_pdcchs) +{ + srsran::optional_vector coreset_bitmaps; + for (const srsran_coreset_t& coreset : view_active_coresets(pdcch_cfg)) { + coreset_bitmaps.emplace(coreset.id, coreset_bitmap(coreset_nof_cces(coreset))); + } + + for (const pdcch_dl_t& pdcch : dl_pdcchs) { + coreset_bitmap& total_bitmap = coreset_bitmaps[pdcch.dci.ctx.coreset_id]; + coreset_bitmap alloc(total_bitmap.size()); + alloc.fill(pdcch.dci.ctx.location.ncce, pdcch.dci.ctx.location.ncce + (1U << pdcch.dci.ctx.location.L)); + TESTASSERT((alloc & total_bitmap).none()); + total_bitmap |= alloc; + } + for (const pdcch_ul_t& pdcch : ul_pdcchs) { + coreset_bitmap& total_bitmap = coreset_bitmaps[pdcch.dci.ctx.coreset_id]; + coreset_bitmap alloc(total_bitmap.size()); + alloc.fill(pdcch.dci.ctx.location.ncce, pdcch.dci.ctx.location.ncce + (1U << pdcch.dci.ctx.location.L)); + TESTASSERT((alloc & total_bitmap).none()); + total_bitmap |= alloc; + } +} + +void test_dl_pdcch_consistency(const cell_config_manager& cell_cfg, + srsran::const_span dl_pdcchs) +{ + for (const auto& pdcch : dl_pdcchs) { + TESTASSERT(pdcch.dci.bwp_id < cell_cfg.bwps.size()); + const srsran_pdcch_cfg_nr_t& pdcch_cfg = cell_cfg.bwps[pdcch.dci.bwp_id].cfg.pdcch; + test_dci_ctx_consistency(pdcch_cfg, pdcch.dci.ctx); + + const srsran_coreset_t& coreset = pdcch_cfg.coreset[pdcch.dci.ctx.coreset_id]; + if (pdcch.dci.ctx.coreset_id == 0) { + TESTASSERT_EQ(srsran_coreset_get_bw(&pdcch_cfg.coreset[0]), pdcch.dci.coreset0_bw); + } + + switch (pdcch.dci.ctx.rnti_type) { + case srsran_rnti_type_si: + TESTASSERT(pdcch.dci.sii != 0 or pdcch.dci.ctx.coreset_id == 0); // sii=0 must go in CORESET#0 + break; + default: + break; + } + } +} + +void test_pdsch_consistency(srsran::const_span pdschs) +{ + for (const mac_interface_phy_nr::pdsch_t& pdsch : pdschs) { + TESTASSERT(pdsch.sch.grant.nof_layers > 0); + if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c) { + TESTASSERT(pdsch.sch.grant.tb[0].softbuffer.tx != nullptr); + TESTASSERT(pdsch.sch.grant.tb[0].softbuffer.tx->buffer_b != nullptr); + TESTASSERT(pdsch.sch.grant.tb[0].softbuffer.tx->max_cb > 0); + } + } +} + +void test_ssb_scheduled_grant( + const srsran::slot_point& sl_point, + const sched_nr_impl::cell_config_manager& cell_cfg, + const srsran::bounded_vector& ssb_list) +{ + /* + * Verify that, with correct SSB periodicity, dl_res has: + * 1) SSB grant + * 2) 4 LSBs of SFN in packed MIB message are correct + * 3) SSB index is 0 + */ + if (sl_point.to_uint() % (cell_cfg.ssb.periodicity_ms * (uint32_t)sl_point.nof_slots_per_subframe()) == 0) { + TESTASSERT(ssb_list.size() == 1); + auto& ssb_item = ssb_list.back(); + TESTASSERT(ssb_item.pbch_msg.sfn_4lsb == ((uint8_t)sl_point.sfn() & 0b1111)); + bool expected_hrf = sl_point.slot_idx() % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) >= + SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) / 2; + TESTASSERT(ssb_item.pbch_msg.hrf == expected_hrf); + TESTASSERT(ssb_item.pbch_msg.ssb_idx == 0); + } + // Verify that, outside SSB periodicity, there is NO SSB grant + else { + TESTASSERT(ssb_list.size() == 0); + } +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/test/sched_nr_common_test.h b/srsgnb/src/stack/mac/test/sched_nr_common_test.h new file mode 100644 index 000000000..58ad6181e --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_common_test.h @@ -0,0 +1,49 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_COMMON_TEST_H +#define SRSRAN_SCHED_NR_COMMON_TEST_H + +#include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h" +#include "srsran/adt/span.h" + +namespace srsenb { + +/// Test DCI context consistency +void test_dci_ctx_consistency(const srsran_pdcch_cfg_nr_t& pdcch_cfg, const srsran_dci_ctx_t& dci); + +/// Test PDCCH collisions +void test_pdcch_collisions(const srsran_pdcch_cfg_nr_t& pdcch_cfg, + srsran::const_span dl_pdcchs, + srsran::const_span ul_pddchs); + +void test_dl_pdcch_consistency(const sched_nr_impl::cell_config_manager& cell_cfg, + srsran::const_span dl_pdcch); +void test_pdsch_consistency(srsran::const_span dl_pdcch); +/// @brief Test whether the SSB grant gets scheduled with the correct periodicity. +void test_ssb_scheduled_grant( + const srsran::slot_point& sl_point, + const sched_nr_impl::cell_config_manager& cell_cfg, + const srsran::bounded_vector& ssb_list); + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_COMMON_TEST_H diff --git a/srsgnb/src/stack/mac/test/sched_nr_dci_utilities_tests.cc b/srsgnb/src/stack/mac/test/sched_nr_dci_utilities_tests.cc new file mode 100644 index 000000000..16f63692b --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_dci_utilities_tests.cc @@ -0,0 +1,478 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +extern "C" { +#include "srsran/phy/phch/ra_nr.h" +} + +void test_cqi_to_mcs() +{ + // Sample random CQI values and test the returned MCS, for different combinations of CQI and MCS tables + // Set the parameters so that to select a given MCS table (1, 2, or 3) + + // TEST CQI Table 1 - MCS table 1 + int mcs = srsran_ra_nr_cqi_to_mcs(/* cqi */ 3, + /* cqi_table_idx */ SRSRAN_CSI_CQI_TABLE_1, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(2, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(5, + SRSRAN_CSI_CQI_TABLE_1, + srsran_mcs_table_64qam, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(6, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(9, + SRSRAN_CSI_CQI_TABLE_1, + srsran_mcs_table_64qam, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(15, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(12, + SRSRAN_CSI_CQI_TABLE_1, + srsran_mcs_table_64qam, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(22, mcs); + + // TEST CQI Table 2 - MCS table 2 + mcs = srsran_ra_nr_cqi_to_mcs(1, + SRSRAN_CSI_CQI_TABLE_2, + srsran_mcs_table_256qam, + srsran_dci_format_nr_1_1, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(4, + SRSRAN_CSI_CQI_TABLE_2, + srsran_mcs_table_256qam, + srsran_dci_format_nr_1_1, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(5, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(7, + SRSRAN_CSI_CQI_TABLE_2, + srsran_mcs_table_256qam, + srsran_dci_format_nr_1_1, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(11, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(11, + SRSRAN_CSI_CQI_TABLE_2, + srsran_mcs_table_256qam, + srsran_dci_format_nr_1_1, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(19, mcs); + + // TEST CQI Table 3 - MCS table 3 + mcs = srsran_ra_nr_cqi_to_mcs(2, + SRSRAN_CSI_CQI_TABLE_3, + srsran_mcs_table_qam64LowSE, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(2, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(8, + SRSRAN_CSI_CQI_TABLE_3, + srsran_mcs_table_qam64LowSE, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(14, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(13, + SRSRAN_CSI_CQI_TABLE_3, + srsran_mcs_table_qam64LowSE, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(24, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(15, + SRSRAN_CSI_CQI_TABLE_3, + srsran_mcs_table_qam64LowSE, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(28, mcs); + + // TEST CQI Table 1 - MCS table 2 + mcs = srsran_ra_nr_cqi_to_mcs(6, + SRSRAN_CSI_CQI_TABLE_1, + srsran_mcs_table_256qam, + srsran_dci_format_nr_1_1, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(4, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(14, + SRSRAN_CSI_CQI_TABLE_1, + srsran_mcs_table_256qam, + srsran_dci_format_nr_1_1, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(19, mcs); + + // TEST CQI Table 1 - MCS table 3 + mcs = srsran_ra_nr_cqi_to_mcs(7, + SRSRAN_CSI_CQI_TABLE_1, + srsran_mcs_table_qam64LowSE, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(16, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(10, + SRSRAN_CSI_CQI_TABLE_1, + srsran_mcs_table_qam64LowSE, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(22, mcs); + + // TEST CQI Table 2 - MCS table 1 + mcs = srsran_ra_nr_cqi_to_mcs(3, + SRSRAN_CSI_CQI_TABLE_2, + srsran_mcs_table_64qam, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(6, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(11, + SRSRAN_CSI_CQI_TABLE_2, + srsran_mcs_table_64qam, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(26, mcs); + + // TEST CQI Table 2 - MCS table 3 + mcs = srsran_ra_nr_cqi_to_mcs(7, + SRSRAN_CSI_CQI_TABLE_2, + srsran_mcs_table_qam64LowSE, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(22, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(10, + SRSRAN_CSI_CQI_TABLE_2, + srsran_mcs_table_qam64LowSE, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(28, mcs); + + // TEST CQI Table 3 - MCS table 1 + mcs = srsran_ra_nr_cqi_to_mcs(2, + SRSRAN_CSI_CQI_TABLE_3, + srsran_mcs_table_64qam, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(13, + SRSRAN_CSI_CQI_TABLE_3, + srsran_mcs_table_64qam, + srsran_dci_format_nr_1_0, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(20, mcs); + + // TEST CQI Table 3 - MCS table 2 + mcs = srsran_ra_nr_cqi_to_mcs(5, + SRSRAN_CSI_CQI_TABLE_3, + srsran_mcs_table_256qam, + srsran_dci_format_nr_1_1, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(1, mcs); + + mcs = srsran_ra_nr_cqi_to_mcs(14, + SRSRAN_CSI_CQI_TABLE_3, + srsran_mcs_table_256qam, + srsran_dci_format_nr_1_1, + srsran_search_space_type_ue, + srsran_rnti_type_c); + TESTASSERT_EQ(15, mcs); +} + +void test_cqi_to_se() +{ + // TEST CQI Table 1 + double se = srsran_ra_nr_cqi_to_se(2, SRSRAN_CSI_CQI_TABLE_1); + TESTASSERT_EQ(0.2344, se); + + se = srsran_ra_nr_cqi_to_se(5, SRSRAN_CSI_CQI_TABLE_1); + TESTASSERT_EQ(0.8770, se); + + se = srsran_ra_nr_cqi_to_se(8, SRSRAN_CSI_CQI_TABLE_1); + TESTASSERT_EQ(1.9141, se); + + se = srsran_ra_nr_cqi_to_se(11, SRSRAN_CSI_CQI_TABLE_1); + TESTASSERT_EQ(3.3223, se); + + se = srsran_ra_nr_cqi_to_se(13, SRSRAN_CSI_CQI_TABLE_1); + TESTASSERT_EQ(4.5234, se); + + se = srsran_ra_nr_cqi_to_se(15, SRSRAN_CSI_CQI_TABLE_1); + TESTASSERT_EQ(5.5547, se); + + // TEST CQI Table 2 + se = srsran_ra_nr_cqi_to_se(1, SRSRAN_CSI_CQI_TABLE_2); + TESTASSERT_EQ(0.1523, se); + + se = srsran_ra_nr_cqi_to_se(4, SRSRAN_CSI_CQI_TABLE_2); + TESTASSERT_EQ(1.4766, se); + + se = srsran_ra_nr_cqi_to_se(7, SRSRAN_CSI_CQI_TABLE_2); + TESTASSERT_EQ(2.7305, se); + + se = srsran_ra_nr_cqi_to_se(10, SRSRAN_CSI_CQI_TABLE_2); + TESTASSERT_EQ(4.5234, se); + + se = srsran_ra_nr_cqi_to_se(12, SRSRAN_CSI_CQI_TABLE_2); + TESTASSERT_EQ(5.5547, se); + + se = srsran_ra_nr_cqi_to_se(14, SRSRAN_CSI_CQI_TABLE_2); + TESTASSERT_EQ(6.9141, se); + + // TEST CQI Table 3 + se = srsran_ra_nr_cqi_to_se(1, SRSRAN_CSI_CQI_TABLE_3); + TESTASSERT_EQ(0.0586, se); + + se = srsran_ra_nr_cqi_to_se(3, SRSRAN_CSI_CQI_TABLE_3); + TESTASSERT_EQ(0.1523, se); + + se = srsran_ra_nr_cqi_to_se(7, SRSRAN_CSI_CQI_TABLE_3); + TESTASSERT_EQ(0.8770, se); + + se = srsran_ra_nr_cqi_to_se(9, SRSRAN_CSI_CQI_TABLE_3); + TESTASSERT_EQ(1.4766, se); + + se = srsran_ra_nr_cqi_to_se(12, SRSRAN_CSI_CQI_TABLE_3); + TESTASSERT_EQ(2.7305, se); + + se = srsran_ra_nr_cqi_to_se(15, SRSRAN_CSI_CQI_TABLE_3); + TESTASSERT_EQ(4.5234, se); +} + +void test_se_to_mcs() +{ + // MCS table 1 + int mcs = srsran_ra_nr_se_to_mcs(/* se */ .1, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ .2344, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 1, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(6, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 1.0273, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(7, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 2.5, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(15, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 2.5703, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(16, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 2.57, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(16, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 2.5664, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(17, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 5.5, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(27, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 5.5574, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(28, mcs); + + mcs = srsran_ra_nr_se_to_mcs(/* se */ 6, + /* mcs_table */ srsran_mcs_table_64qam, + /* dci_format */ srsran_dci_format_nr_1_0, + /* search_space_type*/ srsran_search_space_type_ue, + /* rnti_type */ srsran_rnti_type_c); + TESTASSERT_EQ(28, mcs); + + // MCS table 2 + mcs = srsran_ra_nr_se_to_mcs( + 0.1, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 0.2344, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 0.24, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 1.47, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(4, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 1.4766, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(5, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 3.5, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(13, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 3.6094, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(14, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 5.2, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(19, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 5.3320, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(20, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 5.4, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(20, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 7.4063, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(27, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 7.5, srsran_mcs_table_256qam, srsran_dci_format_nr_1_1, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(27, mcs); + + // MCS table 3 + mcs = srsran_ra_nr_se_to_mcs( + 0.05, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 0.0586, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 0.056, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(0, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 0.15, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(3, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 0.1523, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(4, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 0.49, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(8, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 0.4902, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(9, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 1.69, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(16, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 1.6953, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(17, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 4.52, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(27, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 4.5234, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(28, mcs); + + mcs = srsran_ra_nr_se_to_mcs( + 4.6, srsran_mcs_table_qam64LowSE, srsran_dci_format_nr_1_0, srsran_search_space_type_ue, srsran_rnti_type_c); + TESTASSERT_EQ(28, mcs); +}; + +int main() +{ + test_cqi_to_mcs(); + test_cqi_to_se(); + test_se_to_mcs(); +} \ No newline at end of file diff --git a/srsgnb/src/stack/mac/test/sched_nr_parallel_test.cc b/srsgnb/src/stack/mac/test/sched_nr_parallel_test.cc new file mode 100644 index 000000000..6439b3e59 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_parallel_test.cc @@ -0,0 +1,133 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_nr_cfg_generators.h" +#include "sched_nr_sim_ue.h" +#include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/test_common.h" +#include + +namespace srsenb { + +using dl_sched_t = sched_nr_interface::dl_sched_t; + +static const srsran::phy_cfg_nr_t default_phy_cfg = + srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}; + +class sched_nr_tester : public sched_nr_base_test_bench +{ +public: + using sched_nr_base_test_bench::sched_nr_base_test_bench; + + void process_slot_result(const sim_nr_enb_ctxt_t& slot_ctxt, srsran::const_span cc_list) override + { + tot_latency_sched_ns += + std::max_element(cc_list.begin(), cc_list.end(), [](const cc_result_t& lhs, const cc_result_t& rhs) { + return lhs.cc_latency_ns < rhs.cc_latency_ns; + })->cc_latency_ns.count(); + + for (auto& cc_out : cc_list) { + pdsch_count += cc_out.res.dl->phy.pdcch_dl.size(); + cc_res_count++; + + bool is_dl_slot = srsran_duplex_nr_is_dl(&cell_params[cc_out.res.cc].duplex, 0, current_slot_tx.slot_idx()); + + if (is_dl_slot) { + if (cc_out.res.dl->phy.ssb.empty() and not slot_ctxt.ue_db.empty()) { + TESTASSERT(slot_ctxt.ue_db.empty() or cc_out.res.dl->phy.pdcch_dl.size() >= 1); + } else { + TESTASSERT(cc_out.res.dl->phy.pdcch_dl.size() == 0); + } + } + } + } + + void print_results() const + { + test_logger.info("TESTER: %f PDSCH/{slot,cc} were allocated", pdsch_count / (double)cc_res_count); + srslog::flush(); + } + + srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); + + uint64_t tot_latency_sched_ns = 0; + uint32_t cc_res_count = 0; + uint32_t pdsch_count = 0; +}; + +void run_sched_nr_test(uint32_t nof_workers) +{ + srsran_assert(nof_workers > 0, "There must be at least one worker"); + uint32_t max_nof_ttis = 1000, nof_sectors = 4; + uint16_t rnti = 0x4601; + + sched_nr_interface::sched_args_t cfg; + cfg.auto_refill_buffer = true; + + std::vector cells_cfg = get_default_cells_cfg(nof_sectors); + + std::string test_name = "Serialized Test"; + if (nof_workers > 1) { + test_name = fmt::format("Parallel Test with {} workers", nof_workers); + } + sched_nr_tester tester(cfg, cells_cfg, test_name, nof_workers); + + for (uint32_t nof_slots = 0; nof_slots < max_nof_ttis; ++nof_slots) { + slot_point slot_rx(0, nof_slots % 10240); + slot_point slot_tx = slot_rx + TX_ENB_DELAY; + if (nof_slots == 9) { + sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(nof_sectors); + uecfg.lc_ch_to_add.emplace_back(); + uecfg.lc_ch_to_add.back().lcid = 1; + uecfg.lc_ch_to_add.back().cfg.direction = mac_lc_ch_cfg_t::BOTH; + tester.user_cfg(rnti, uecfg); + } + tester.run_slot(slot_tx); + } + + tester.stop(); + tester.print_results(); + TESTASSERT(tester.pdsch_count > 0); + // TESTASSERT(tester.pdsch_count == (int)(max_nof_ttis * nof_sectors * 0.6)); + + double final_avg_usec = tester.tot_latency_sched_ns; + final_avg_usec = final_avg_usec / 1000.0 / max_nof_ttis; + printf("Total time taken per slot: %f usec\n", final_avg_usec); +} + +} // namespace srsenb + +int main() +{ + auto& test_logger = srslog::fetch_basic_logger("TEST"); + test_logger.set_level(srslog::basic_levels::warning); + auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR"); + mac_nr_logger.set_level(srslog::basic_levels::warning); + auto& pool_logger = srslog::fetch_basic_logger("POOL"); + pool_logger.set_level(srslog::basic_levels::debug); + + // Start the log backend. + srslog::init(); + + srsenb::run_sched_nr_test(1); + srsenb::run_sched_nr_test(2); + srsenb::run_sched_nr_test(4); +} diff --git a/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc b/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc new file mode 100644 index 000000000..14299f1bf --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_pdcch_test.cc @@ -0,0 +1,320 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_nr_cfg_generators.h" +#include "sched_nr_common_test.h" +#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h" +#include "srsgnb/hdr/stack/mac/sched_nr_pdcch.h" +#include "srsran/common/test_common.h" + +namespace srsenb { + +using namespace sched_nr_impl; + +/** + * Test for the case CORESET#0 is active. + * Given only one PDCCH candidate position is supported, only one PDCCH allocation should take place per slot + * The test additionally verifies that the DCI context content is correct for each PDCCH allocation + */ +void test_coreset0_cfg() +{ + const uint32_t aggr_idx = 2; + + srsran::test_delimit_logger delimiter{"Test PDCCH Allocation in CORESET#0"}; + + sched_nr_interface::sched_args_t sched_args; + sched_nr_impl::cell_config_manager cell_cfg{0, get_default_sa_cell_cfg_common(), sched_args}; + bwp_params_t& bwp_params = cell_cfg.bwps[0]; + + // UE config + ue_cfg_manager uecfg{get_rach_ue_cfg(0)}; + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].cfg.pdcch; // Starts without UE-specific PDCCH + ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg}; + + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdcch_dl_alloc_result dl_pdcch_result; + pdcch_ul_alloc_result ul_pdcch_result; + pdcch_dl_t* dl_pdcch = nullptr; + pdcch_ul_t* ul_pdcch = nullptr; + + bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs); + for (const srsran_coreset_t& cs : view_active_coresets(cell_cfg.bwps[0].cfg.pdcch)) { + // Verify nof CCEs is correctly computed + TESTASSERT_EQ(coreset_nof_cces(cs), pdcch_sched.nof_cces(cs.id)); + } + + // Slot with SIB1 + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + + // SIB1 allocation should be successful + dl_pdcch_result = pdcch_sched.alloc_si_pdcch(0, aggr_idx); + TESTASSERT(dl_pdcch_result.has_value()); + dl_pdcch = dl_pdcch_result.value(); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_si, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR, UE PDSCH/PUSCH + TESTASSERT(pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); + + // Slot with RAR + pdcch_sched.reset(); + + // RAR allocation should be successful + dl_pdcch_result = pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx); + TESTASSERT(dl_pdcch_result.has_value()); + dl_pdcch = dl_pdcch_result.value(); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_ra, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, dl_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR, UE PDSCH/PUSCH + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); + + // Slot with DL PDSCH + pdcch_sched.reset(); + + // 1st PDCCH allocation for DL should be successful + dl_pdcch_result = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc); + TESTASSERT(dl_pdcch_result.has_value()); + dl_pdcch = dl_pdcch_result.value(); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0u, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, dl_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for 2nd PDCCH allocation + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); + + // Slot with UL PDSCH + pdcch_sched.reset(); + + // 1st PDCCH allocation for UL should be successful + ul_pdcch_result = pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc); + TESTASSERT(ul_pdcch_result.has_value()); + ul_pdcch = ul_pdcch_result.value(); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, ul_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0u, ul_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_common_1, ul_pdcch->dci.ctx.ss_type); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, ul_pdcch->dci.ctx); + + // No space for 2nd PDCCH allocation + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); +} + +/** + * Test for the case CORESET#2 is active. + * The PDCCH allocator should find enough space to fit SIB1/RAR (in CORESET#0) and UE-dedicated PDCCHs in (CORESET#2) + * The test additionally verifies that the DCI context content is correct for each PDCCH allocation and there are no + * collisions between PDCCH CCE allocations + */ +void test_coreset2_cfg() +{ + const uint32_t aggr_idx = 2; + + srsran::test_delimit_logger delimiter{"Test PDCCH Allocation in CORESET#0 and CORESET#2"}; + + sched_nr_interface::sched_args_t sched_args; + sched_nr_cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + cell_cfg.bwps[0].pdcch.search_space_present[2] = true; + cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2); + cell_cfg.bwps[0].pdcch.coreset_present[2] = true; + cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.pci); + sched_nr_impl::cell_config_manager cellparams{0, cell_cfg, sched_args}; + bwp_params_t& bwp_params = cellparams.bwps[0]; + + // UE config + ue_cfg_manager uecfg{get_rach_ue_cfg(0)}; + uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH + ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg}; + + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdcch_dl_t* dl_pdcch = nullptr; + pdcch_ul_t* ul_pdcch = nullptr; + + bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs); + for (const srsran_coreset_t& cs : view_active_coresets(cell_cfg.bwps[0].pdcch)) { + // Verify nof CCEs is correctly computed + TESTASSERT_EQ(coreset_nof_cces(cs), pdcch_sched.nof_cces(cs.id)); + } + + // Slot with SIB1 + DL PDCCH and UL PDCCH + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + + // SIB1 allocation should be successful + dl_pdcch = pdcch_sched.alloc_si_pdcch(0, aggr_idx).value(); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(1, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_si, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(0, dl_pdcch->dci.ctx.coreset_id); + srsran_dci_location_t expected_loc{aggr_idx, 0}; + TESTASSERT(dl_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // No space for RAR or PDSCH in SS#1 + TESTASSERT(pdcch_sched.alloc_rar_pdcch(0x2, aggr_idx).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + TESTASSERT(pdcch_sched.alloc_ul_pdcch(1, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + // there is space for UE DL PDCCH in SS#2 + dl_pdcch = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 2, aggr_idx, ue_cc).value(); + TESTASSERT(dl_pdcch != nullptr); + TESTASSERT_EQ(2, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, dl_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(2u, dl_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_ue, dl_pdcch->dci.ctx.ss_type); + expected_loc = srsran_dci_location_t{aggr_idx, 0}; + TESTASSERT(dl_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, dl_pdcch->dci.ctx); + + // there is space for UE UL PDCCH in SS#2 + ul_pdcch = pdcch_sched.alloc_ul_pdcch(2, aggr_idx, ue_cc).value(); + TESTASSERT(ul_pdcch != nullptr); + TESTASSERT_EQ(3, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(srsran_rnti_type_c, ul_pdcch->dci.ctx.rnti_type); + TESTASSERT_EQ(srsran_dci_format_nr_0_0, ul_pdcch->dci.ctx.format); + TESTASSERT_EQ(2u, ul_pdcch->dci.ctx.coreset_id); + TESTASSERT_EQ(srsran_search_space_type_ue, ul_pdcch->dci.ctx.ss_type); + expected_loc = srsran_dci_location_t{aggr_idx, 4}; + TESTASSERT(ul_pdcch->dci.ctx.location == expected_loc); + test_dci_ctx_consistency(bwp_params.cfg.pdcch, ul_pdcch->dci.ctx); + + // No space for 3rd PDCCH allocation in SS#2 + TESTASSERT(pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 2, aggr_idx, ue_cc).error() == alloc_result::no_cch_space); + + // Verify there are no PDCCH collisions + TESTASSERT_EQ(3, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(2, dl_pdcchs.size()); + TESTASSERT_EQ(1, ul_pdcchs.size()); + test_pdcch_collisions(bwp_params.cfg.pdcch, dl_pdcchs, ul_pdcchs); + + srslog::fetch_basic_logger("TEST").info("%s", pdcch_sched.print_allocations()); + + // Verify all coresets are correctly cleaned up + pdcch_sched.reset(); + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + TESTASSERT_EQ(0, dl_pdcchs.size()); + TESTASSERT_EQ(0, ul_pdcchs.size()); +} + +void test_invalid_params() +{ + const uint32_t aggr_idx = 2; + + srsran::test_delimit_logger delimiter{"Test PDCCH Allocation with Invalid Arguments"}; + + sched_nr_cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + cell_cfg.bwps[0].pdcch.search_space_present[2] = true; + cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2); + cell_cfg.bwps[0].pdcch.coreset_present[2] = true; + cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.pci); + cell_cfg.bwps[0].pdcch.search_space_present[3] = true; + cell_cfg.bwps[0].pdcch.search_space[3] = get_default_ue_specific_search_space(3, 2); + cell_cfg.bwps[0].pdcch.search_space[3].nof_formats = 1; // only DL + cell_cfg.bwps[0].pdcch.search_space[3].formats[0] = srsran_dci_format_nr_1_0; + sched_nr_interface::sched_args_t sched_args; + sched_nr_impl::cell_config_manager cellparams{0, cell_cfg, sched_args}; + bwp_params_t& bwp_params = cellparams.bwps[0]; + + // UE config + ue_cfg_manager uecfg{get_rach_ue_cfg(0)}; + uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH + ue_carrier_params_t ue_cc{0x46, bwp_params, uecfg}; + + pdcch_dl_list_t dl_pdcchs; + pdcch_ul_list_t ul_pdcchs; + pdcch_dl_alloc_result dl_res; + pdcch_ul_alloc_result ul_res; + + bwp_pdcch_allocator pdcch_sched(bwp_params, 0, dl_pdcchs, ul_pdcchs); + + // Slot with SIB1 + DL PDCCH and UL PDCCH + TESTASSERT_EQ(0, pdcch_sched.nof_allocations()); + + // Pass UE search space for SI alloc + dl_res = pdcch_sched.alloc_si_pdcch(2, aggr_idx); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // Pass aggregation index for which there are no candidates + dl_res = pdcch_sched.alloc_si_pdcch(2, 4); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // SearchSpace must exist + dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 4, aggr_idx, ue_cc); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // TC-RNTI cannot be allocated in Common SearchSpace Type1 + dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_tc, 2, aggr_idx, ue_cc); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // C-RNTI cannot be allocated in Common SearchSpace Type0 + dl_res = pdcch_sched.alloc_dl_pdcch(srsran_rnti_type_c, 0, aggr_idx, ue_cc); + TESTASSERT(dl_res.is_error() and dl_res.error() == alloc_result::invalid_grant_params); + + // UL allocation cannot be made in SearchSpace without DCI format 0_0 + ul_res = pdcch_sched.alloc_ul_pdcch(3, aggr_idx, ue_cc); + TESTASSERT(ul_res.is_error() and ul_res.error() == alloc_result::invalid_grant_params); + + // Success case + TESTASSERT(pdcch_sched.nof_allocations() == 0); + ul_res = pdcch_sched.alloc_ul_pdcch(2, aggr_idx, ue_cc); + TESTASSERT(ul_res.has_value() and ul_res.value()->dci.ctx.format == srsran_dci_format_nr_0_0); + TESTASSERT(pdcch_sched.nof_allocations() == 1); +} + +} // namespace srsenb + +int main() +{ + auto& test_logger = srslog::fetch_basic_logger("TEST"); + test_logger.set_level(srslog::basic_levels::info); + auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR"); + mac_nr_logger.set_level(srslog::basic_levels::debug); + auto& pool_logger = srslog::fetch_basic_logger("POOL"); + pool_logger.set_level(srslog::basic_levels::debug); + + // Start the log backend. + srslog::init(); + + srsenb::test_coreset0_cfg(); + srsenb::test_coreset2_cfg(); + srsenb::test_invalid_params(); +} \ No newline at end of file diff --git a/srsgnb/src/stack/mac/test/sched_nr_prb_test.cc b/srsgnb/src/stack/mac/test/sched_nr_prb_test.cc new file mode 100644 index 000000000..4ed42fc97 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_prb_test.cc @@ -0,0 +1,143 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/mac/sched_nr_rb.h" +#include "srsran/common/test_common.h" + +using namespace srsenb; +using namespace srsenb::sched_nr_impl; + +void test_bwp_prb_grant() +{ + // TEST: default ctor + prb_grant grant; + TESTASSERT(grant.is_alloc_type1()); + TESTASSERT(grant.prbs().length() == 0); + + // TEST: assignment of RBGs + rbg_bitmap rbgs(18); + rbgs.set(1); + grant = rbgs; + TESTASSERT(grant.is_alloc_type0() and grant.rbgs().count() == 1); + + // TEST: assignment of PRBs + prb_interval prb_interv(2, 5); + grant = prb_interv; + TESTASSERT(grant.is_alloc_type1() and grant.prbs().length() == 3); + + // TEST: non-default ctor + prb_grant grant2(prb_interv), grant3(rbgs); + TESTASSERT(grant2.is_alloc_type1() and grant2.prbs().length() == 3); + TESTASSERT(grant3.is_alloc_type0() and grant3.rbgs().count() == 1); + + // TEST: copy ctor + prb_grant grant4(grant2), grant5(grant3); + TESTASSERT(grant4.is_alloc_type1() and grant4.prbs().length() == 3); + TESTASSERT(grant5.is_alloc_type0() and grant5.rbgs().count() == 1); + + // TEST: copy assignment + grant = grant3; + TESTASSERT(grant.is_alloc_type0() and grant.rbgs().count() == 1); + grant = grant2; + TESTASSERT(grant.is_alloc_type1() and grant.prbs().length() == 3); + + // TEST: formatting + TESTASSERT_EQ("[2, 5)", fmt::format("{}", grant2)); + TESTASSERT_EQ("0x10000", fmt::format("0x{:x}", grant3)); +} + +void test_bwp_rb_bitmap() +{ + bwp_rb_bitmap rb_bitmap(275, 0, true); + + TESTASSERT(rb_bitmap.P() == 16); + TESTASSERT(rb_bitmap.rbgs().none()); + TESTASSERT(rb_bitmap.prbs().none()); + TESTASSERT(rb_bitmap.prbs().size() == 275 and rb_bitmap.nof_prbs() == 275); + TESTASSERT(rb_bitmap.rbgs().size() == 18 and rb_bitmap.nof_rbgs() == 18); + + rb_bitmap.add(prb_interval{0, 1}); + TESTASSERT(rb_bitmap.prbs().count() == 1 and rb_bitmap.prbs().test(0)); + TESTASSERT(rb_bitmap.rbgs().count() == 1 and rb_bitmap.rbgs().test(0)); + rb_bitmap.add(prb_interval{2, 4}); + TESTASSERT(rb_bitmap.prbs().count() == 3 and rb_bitmap.prbs().test(2) and not rb_bitmap.prbs().test(1)); + TESTASSERT(rb_bitmap.rbgs().count() == 1 and rb_bitmap.rbgs().test(0)); + + prb_bitmap prbs(rb_bitmap.nof_prbs()); + prbs.set(1); + prbs.set(2); + prbs.set(15); + rb_bitmap.add(prbs); + TESTASSERT(rb_bitmap.prbs().count() == 5 and rb_bitmap.prbs().test(1) and rb_bitmap.prbs().test(15)); + TESTASSERT(rb_bitmap.rbgs().count() == 1 and rb_bitmap.rbgs().test(0)); + prbs.set(16); + rb_bitmap |= prbs; + TESTASSERT(rb_bitmap.prbs().count() == 6 and rb_bitmap.prbs().test(16)); + TESTASSERT(rb_bitmap.rbgs().count() == 2 and rb_bitmap.rbgs().test(1)); + + rbg_bitmap rbgs(rb_bitmap.nof_rbgs()); + rbgs.set(3); + rbgs.set(17); + rb_bitmap |= rbgs; + TESTASSERT(rb_bitmap.prbs().count() == (6 + 16 + 3) and rb_bitmap.prbs().test(rb_bitmap.nof_prbs() - 1)); + TESTASSERT(rb_bitmap.rbgs().count() == 4 and rb_bitmap.rbgs().test(3) and rb_bitmap.rbgs().test(17)); + rbgs.set(0); + rb_bitmap |= rbgs; + TESTASSERT(rb_bitmap.prbs().count() == (16 + 1 + 16 + 3) and rb_bitmap.prbs().test(rb_bitmap.nof_prbs() - 1)); + TESTASSERT(rb_bitmap.rbgs().count() == 4 and rb_bitmap.rbgs().test(3) and rb_bitmap.rbgs().test(17)); + + // TEST: collides operator + TESTASSERT(rb_bitmap.collides(rbgs)); + TESTASSERT(rb_bitmap.collides(prb_interval{0, 2})); +} + +void test_bwp_rb_bitmap_search() +{ + bwp_rb_bitmap rb_bitmap(275, 0, true); + + prb_interval prbs = find_empty_interval_of_length(rb_bitmap.prbs(), 5); + TESTASSERT(prbs == prb_interval(0, 5)); + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), rb_bitmap.prbs().size()); + TESTASSERT(prbs == prb_interval(0, rb_bitmap.prbs().size())); + + rb_bitmap |= prb_interval{1, 5}; + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), rb_bitmap.prbs().size()); + TESTASSERT(prbs == prb_interval(5, rb_bitmap.prbs().size())); + + rb_bitmap |= prb_interval{16, 32}; + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), rb_bitmap.prbs().size()); + TESTASSERT(prbs == prb_interval(32, rb_bitmap.prbs().size())); + + rb_bitmap |= prb_interval{270, 275}; + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), rb_bitmap.prbs().size()); + TESTASSERT(prbs == prb_interval(32, 270)); + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), 1); + TESTASSERT(prbs == prb_interval(0, 1)); + prbs = find_empty_interval_of_length(rb_bitmap.prbs(), 5); + TESTASSERT(prbs == prb_interval(5, 10)); +} + +int main() +{ + test_bwp_prb_grant(); + test_bwp_rb_bitmap(); + test_bwp_rb_bitmap_search(); +} diff --git a/srsgnb/src/stack/mac/test/sched_nr_rar_test.cc b/srsgnb/src/stack/mac/test/sched_nr_rar_test.cc new file mode 100644 index 000000000..0bcf7b496 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_rar_test.cc @@ -0,0 +1,149 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_nr_cfg_generators.h" +#include "sched_nr_common_test.h" +#include "srsgnb/hdr/stack/mac/sched_nr_bwp.h" +#include "srsran/common/test_common.h" +#include "srsran/support/srsran_test.h" +#include + +uint32_t seed = std::chrono::system_clock::now().time_since_epoch().count(); + +namespace srsenb { + +void test_single_prach() +{ + using namespace sched_nr_impl; + const uint16_t rnti = 0x1234; + static srslog::basic_logger& mac_logger = srslog::fetch_basic_logger("MAC"); + std::default_random_engine rand_gen(seed); + std::default_random_engine rgen(rand_gen()); + + // Set scheduler configuration + sched_nr_interface::sched_args_t sched_cfg{}; + sched_cfg.auto_refill_buffer = std::uniform_int_distribution{0, 1}(rgen) > 0; + + // Set cells configuration + std::vector cells_cfg = get_default_cells_cfg(1); + sched_params_t schedparams{sched_cfg}; + schedparams.cells.emplace_back(0, cells_cfg[0], sched_cfg); + const bwp_params_t& bwpparams = schedparams.cells[0].bwps[0]; + slot_ue_map_t slot_ues; + + ra_sched rasched(bwpparams); + TESTASSERT(rasched.empty()); + + std::unique_ptr res_grid(new bwp_res_grid{bwpparams}); + + // Create UE + sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(1); + ue u(rnti, uecfg, schedparams); + + slot_point pdcch_slot{0, TX_ENB_DELAY}; + slot_point prach_slot{0, std::uniform_int_distribution{TX_ENB_DELAY, 20}(rgen)}; + + const bwp_slot_grid* result = nullptr; + auto run_slot = [&res_grid, &rasched, &pdcch_slot, &slot_ues, &u]() -> const bwp_slot_grid* { + mac_logger.set_context(pdcch_slot.to_uint()); + + // delete old outputs + (*res_grid)[pdcch_slot - TX_ENB_DELAY - 1].reset(); + + // setup UE state for slot + u.new_slot(pdcch_slot); + + // pre-calculate UE slot vars + slot_ues.clear(); + slot_ue sfu = u.make_slot_ue(pdcch_slot, 0); + if (not sfu.empty()) { + slot_ues.insert(rnti, std::move(sfu)); + } + bwp_slot_allocator alloc(*res_grid, pdcch_slot, slot_ues); + + rasched.run_slot(alloc); + + log_sched_bwp_result(mac_logger, alloc.get_pdcch_tti(), alloc.res_grid(), slot_ues); + const bwp_slot_grid* result = &alloc.res_grid()[alloc.get_pdcch_tti()]; + test_dl_pdcch_consistency(res_grid->cfg->cell_cfg, result->dl.phy.pdcch_dl); + ++pdcch_slot; + return result; + }; + + // Start Run + + for (; pdcch_slot - TX_ENB_DELAY < prach_slot;) { + result = run_slot(); + TESTASSERT(result->dl.phy.pdcch_dl.empty()); + } + + // A PRACH arrives... + sched_nr_interface::rar_info_t rainfo{}; + rainfo.ofdm_symbol_idx = 0; + rainfo.preamble_idx = 10; + rainfo.temp_crnti = rnti; + rainfo.prach_slot = prach_slot; + rainfo.msg3_size = 7; + TESTASSERT_SUCCESS(rasched.dl_rach_info(rainfo)); + uint16_t ra_rnti = 1 + rainfo.ofdm_symbol_idx + 14 * rainfo.prach_slot.slot_idx() + 14 * 80 * rainfo.freq_idx; + + // RAR is scheduled + const uint32_t prach_duration = 1; + slot_point rar_slot; + while (true) { + slot_point current_slot = pdcch_slot; + result = run_slot(); + if (bwpparams.slots[current_slot.slot_idx()].is_dl and + bwpparams.slots[(current_slot + bwpparams.pusch_ra_list[0].msg3_delay).slot_idx()].is_ul) { + TESTASSERT_EQ(1, result->dl.phy.pdcch_dl.size()); + const auto& pdcch = result->dl.phy.pdcch_dl[0]; + TESTASSERT_EQ(pdcch.dci.ctx.rnti, ra_rnti); + TESTASSERT_EQ(pdcch.dci.ctx.rnti_type, srsran_rnti_type_ra); + TESTASSERT(current_slot < prach_slot + prach_duration + bwpparams.cfg.rar_window_size); + rar_slot = current_slot; + break; + } else { + TESTASSERT(result->dl.phy.pdcch_dl.empty()); + } + } + + slot_point msg3_slot = rar_slot + bwpparams.pusch_ra_list[0].msg3_delay; + while (pdcch_slot <= msg3_slot) { + result = run_slot(); + } + TESTASSERT(result->ul.pusch.size() == 1); +} + +} // namespace srsenb + +int main(int argc, char** argv) +{ + auto& test_logger = srslog::fetch_basic_logger("TEST"); + test_logger.set_level(srslog::basic_levels::info); + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + mac_logger.set_level(srslog::basic_levels::info); + + srsran::test_init(argc, argv); + + printf("Test random seed=%u\n\n", seed); + + srsenb::test_single_prach(); +} diff --git a/srsgnb/src/stack/mac/test/sched_nr_sch_test.cc b/srsgnb/src/stack/mac/test/sched_nr_sch_test.cc new file mode 100644 index 000000000..5fe688ca0 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_sch_test.cc @@ -0,0 +1,496 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_nr_cfg_generators.h" +#include "srsgnb/hdr/stack/mac/sched_nr_interface_utils.h" +#include "srsgnb/hdr/stack/mac/sched_nr_sch.h" +#include "srsran/common/test_common.h" +extern "C" { +#include "srsran/phy/common/sliv.h" +} + +namespace srsenb { + +using namespace sched_nr_impl; + +sched_nr_cell_cfg_t get_cell_cfg() +{ + sched_nr_cell_cfg_t cell_cfg = get_default_sa_cell_cfg_common(); + cell_cfg.bwps[0].pdcch.search_space_present[2] = true; + cell_cfg.bwps[0].pdcch.search_space[2] = get_default_ue_specific_search_space(2, 2); + cell_cfg.bwps[0].pdcch.coreset_present[2] = true; + cell_cfg.bwps[0].pdcch.coreset[2] = get_default_ue_specific_coreset(2, cell_cfg.pci); + return cell_cfg; +} + +sched_nr_interface::ue_cfg_t get_ue_cfg(const sched_nr_cell_cfg_t& cell_cfg) +{ + sched_nr_ue_cfg_t uecfg = get_rach_ue_cfg(0); + uecfg.phy_cfg = get_common_ue_phy_cfg(cell_cfg); + uecfg.phy_cfg.pdcch = cell_cfg.bwps[0].pdcch; // Starts with UE-specific PDCCH + return uecfg; +} + +srsran_dci_ctx_t generate_dci_ctx(const srsran_pdcch_cfg_nr_t& pdcch, + uint32_t ss_id, + srsran_rnti_type_t rnti_type, + uint16_t rnti, + srsran_dci_format_nr_t dci_fmt = srsran_dci_format_nr_1_0) +{ + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + const srsran_coreset_t& cs = pdcch.coreset[ss.coreset_id]; + + srsran_dci_ctx_t ctx; + ctx.location = {2, 4}; + ctx.ss_type = ss.type; + ctx.coreset_id = ss.coreset_id; + ctx.coreset_start_rb = srsran_coreset_start_rb(&cs); + ctx.rnti_type = rnti_type; + ctx.format = dci_fmt; + ctx.rnti = rnti; + return ctx; +} + +void test_dci_freq_assignment(const bwp_params_t& bwp_params, prb_interval grant, const pdcch_dl_t& pdcch) +{ + // Compute BWP PRB limits + prb_interval lims{0, bwp_params.nof_prb}; + if (SRSRAN_SEARCH_SPACE_IS_COMMON(pdcch.dci.ctx.ss_type) and pdcch.dci.ctx.format == srsran_dci_format_nr_1_0) { + lims = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id); + } + + // RB indexing should start from the first PRB of CORESET + uint32_t expected_freq_assignment = + srsran_ra_nr_type1_riv(lims.length(), grant.start() - lims.start(), grant.length()); + TESTASSERT_EQ(expected_freq_assignment, pdcch.dci.freq_domain_assigment); + + uint32_t st, len; + srsran_sliv_to_s_and_l(lims.length(), pdcch.dci.freq_domain_assigment, &st, &len); + prb_interval allocated_prbs{st + lims.start(), st + lims.start() + len}; + TESTASSERT(allocated_prbs == grant); +} + +void test_si() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH SI Allocation"}; + + static const uint32_t ss_id = 0; + + // Create Cell and UE configs + sched_nr_interface::sched_args_t sched_args; + sched_nr_cell_cfg_t cellcfg = get_cell_cfg(); + sched_nr_impl::cell_config_manager cell_params{0, cellcfg, sched_args}; + const bwp_params_t& bwp_params = cell_params.bwps[0]; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch; + pdcch.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, ss_id, srsran_rnti_type_si, SRSRAN_SIRNTI); + + uint32_t min_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).start(); + uint32_t max_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).stop(); + + std::array grant_list = { + prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb}}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + prb_interval grant = grant_list[i]; + + bool success_expected = grant.start() >= min_prb and grant.stop() <= max_prb; + + alloc_result check_ret = pdsch_sched.is_si_grant_valid(ss_id, grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ((int)min_prb, avail_prbs.find_lowest(0, avail_prbs.size(), false)); + TESTASSERT_EQ((int)max_prb, avail_prbs.find_lowest(min_prb, avail_prbs.size(), true)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_si_pdsch(ss_id, grant, pdcch.dci); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT(alloc_res.has_value()); + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(alloc_res.is_error()); + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_rar() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH RAR Allocation"}; + static const uint32_t ss_id = 1; + + // Create Cell and UE configs + sched_nr_interface::sched_args_t sched_args; + sched_nr_impl::cell_config_manager cell_cfg{0, get_cell_cfg(), sched_args}; + const bwp_params_t& bwp_params = cell_cfg.bwps[0]; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch; + pdcch.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, ss_id, srsran_rnti_type_ra, 0x2); + + uint32_t min_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).start(); + uint32_t max_prb = bwp_params.dci_fmt_1_0_prb_lims(pdcch.dci.ctx.coreset_id).stop(); + + std::array grant_list = { + prb_interval{2, 4}, prb_interval{min_prb, max_prb}, prb_interval{0, bwp_params.nof_prb}}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + prb_interval grant = grant_list[i]; + + bool success_expected = grant.start() >= min_prb and grant.stop() <= max_prb; + + alloc_result check_ret = pdsch_sched.is_rar_grant_valid(grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ((int)min_prb, avail_prbs.find_lowest(0, avail_prbs.size(), false)); + TESTASSERT_EQ((int)max_prb, avail_prbs.find_lowest(min_prb, avail_prbs.size(), true)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_rar_pdsch(grant, pdcch.dci); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT(alloc_res.has_value()); + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(alloc_res.is_error()); + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_ue_pdsch() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH UE Allocation"}; + + // Create Cell and UE configs + sched_nr_interface::sched_args_t sched_args; + sched_nr_cell_cfg_t cellcfg = get_cell_cfg(); + sched_nr_impl::cell_config_manager cell_params{0, get_cell_cfg(), sched_args}; + sched_nr_impl::ue_cfg_manager uecfg{get_ue_cfg(cellcfg)}; + const bwp_params_t& bwp_params = cell_params.bwps[0]; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch_common, pdcch_ue; + pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4601); + + prb_interval lims_common = bwp_params.dci_fmt_1_0_prb_lims(pdcch_common.dci.ctx.coreset_id); + prb_interval lims_ue{0, bwp_params.nof_prb}; + + std::array, 4> grant_list = {std::make_pair(1, prb_interval{2, 4}), + std::make_pair(1, lims_common), + std::make_pair(1, lims_ue), + std::make_pair(2, lims_common)}; + + for (uint32_t i = 0; i < grant_list.size(); ++i) { + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + + auto g = grant_list[i]; + uint32_t ss_id = g.first; + prb_interval grant = g.second; + prb_interval lims = ss_id == 1 ? lims_common : lims_ue; + pdcch_dl_t& pdcch = ss_id == 1 ? pdcch_common : pdcch_ue; + + bool success_expected = grant.start() >= lims.start() and grant.stop() <= lims.stop(); + + alloc_result check_ret = pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, grant); + prb_bitmap avail_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + int pos = avail_prbs.find_lowest(0, avail_prbs.size(), false); + TESTASSERT_EQ((int)lims.start(), pos); + pos = avail_prbs.find_lowest(lims.start(), avail_prbs.size(), true); + TESTASSERT_EQ((int)lims.stop(), (pos < 0 ? (int)avail_prbs.size() : pos)); + + printf("Attempt %d should be %ssuccessful\n", i, success_expected ? "" : "un"); + alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, grant, ue_cc, pdcch.dci); + TESTASSERT(success_expected == alloc_res.has_value()); + if (success_expected) { + // SIB1 allocation doesnt go outside CORESET#0 BW + TESTASSERT_EQ(1, pdschs.size()); + TESTASSERT(&pdschs.back() == alloc_res.value()); + TESTASSERT_EQ(0, pdcch.dci.time_domain_assigment); + + TESTASSERT(not avail_prbs.any(grant.start(), grant.stop())); + + test_dci_freq_assignment(bwp_params, grant, pdcch); + } else { + TESTASSERT(check_ret == alloc_res.error()); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT(avail_prbs.any(grant.start(), grant.stop())); + } + } +} + +void test_pdsch_fail() +{ + srsran::test_delimit_logger delimiter{"Test PDSCH Allocation Failure"}; + + // Create Cell and UE configs + sched_nr_interface::sched_args_t sched_args; + sched_nr_cell_cfg_t cellcfg = get_cell_cfg(); + sched_nr_impl::cell_config_manager cell_params{0, cellcfg, sched_args}; + sched_nr_impl::ue_cfg_manager uecfg{get_ue_cfg(cellcfg)}; + const bwp_params_t& bwp_params = cell_params.bwps[0]; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch_common, pdcch_ue, pdcch_rar, pdcch_si, pdcch; + pdcch_si.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 0, srsran_rnti_type_si, SRSRAN_SIRNTI); + pdcch_rar.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_ra, 0x2); + pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4601); + + // Allocations of type 0 are not compatible with DCI format 1_0 + rbg_bitmap rbgs(bwp_params.N_rbg); + rbgs.set(1); + rbgs.set(3); + prb_grant grant_type0 = rbgs; + TESTASSERT_EQ(alloc_result::invalid_grant_params, pdsch_sched.alloc_si_pdsch(0, grant_type0, pdcch_si.dci).error()); + TESTASSERT_EQ(alloc_result::invalid_grant_params, pdsch_sched.alloc_rar_pdsch(grant_type0, pdcch_rar.dci).error()); + TESTASSERT_EQ(alloc_result::invalid_grant_params, + pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type0, ue_cc, pdcch.dci).error()); + TESTASSERT_EQ(alloc_result::invalid_grant_params, + pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, grant_type0, ue_cc, pdcch.dci).error()); + + // Resource Allocation type must be compatible with UE PDSCH configuration + TESTASSERT_EQ(alloc_result::invalid_grant_params, + pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_1, grant_type0, ue_cc, pdcch.dci).error()); + + // Allocations of DCI format 1_0 should start from CORESET first RB and their BW should be limited by CORESET#0 BW + prb_grant grant_type1 = prb_interval{0, bwp_params.coreset_prb_range(0).stop()}; + TESTASSERT(pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).is_error()); + grant_type1 = prb_interval{bwp_params.coreset_prb_range(0).start(), bwp_params.nof_prb}; + TESTASSERT(pdsch_sched.alloc_ue_pdsch(1, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).is_error()); + TESTASSERT(pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, grant_type1, ue_cc, pdcch.dci).has_value()); + + // PRB collisions are detected + TESTASSERT(pdsch_sched.alloc_ue_pdsch(2, srsran_dci_format_nr_1_0, prb_interval{5, 6}, ue_cc, pdcch.dci).is_error()); +} + +void test_multi_pdsch() +{ + srsran::test_delimit_logger delimiter{"Test Multiple PDSCH Allocations"}; + + // Create Cell and UE configs + sched_nr_interface::sched_args_t sched_args; + sched_nr_cell_cfg_t cellcfg = get_cell_cfg(); + sched_nr_impl::cell_config_manager cell_params{0, cellcfg, sched_args}; + sched_nr_impl::ue_cfg_manager uecfg{get_ue_cfg(cellcfg)}; + const bwp_params_t& bwp_params = cell_params.bwps[0]; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + ue_carrier_params_t ue_cc2{0x4602, bwp_params, uecfg}; + + pdsch_list_t pdschs; + pdsch_alloc_result alloc_res; + + pdsch_allocator pdsch_sched(bwp_params, 0, pdschs); + + pdcch_dl_t pdcch_sib, pdcch_common, pdcch_ue; + pdcch_sib.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 0, srsran_rnti_type_si, SRSRAN_SIRNTI); + pdcch_common.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4602); + + // Allocate SIB1 + uint32_t ss_id = 0; + pdcch_dl_t* pdcch = &pdcch_sib; + prb_bitmap used_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + fmt::print("No allocations yet. Occupied PRBs for common SearchSpace: {:b}\n", used_prbs); + uint32_t sib1_grant_size = 4; + prb_bitmap sib_prbs = ~used_prbs; + int first_prb = sib_prbs.find_lowest(0, sib_prbs.size(), true); + prb_interval sib_grant{(uint32_t)first_prb, sib1_grant_size}; + TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_si_grant_valid(ss_id, sib_grant)); + alloc_res = pdsch_sched.alloc_si_pdsch(ss_id, sib_grant, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, sib_grant, *pdcch); + prb_bitmap used_prbs_sib1 = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_sib1.count(), used_prbs.count() + sib_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, pdsch_sched.is_si_grant_valid(ss_id, sib_grant)); + + prb_bitmap last_prb_bitmap(used_prbs.size()); + last_prb_bitmap.fill(sib_grant.start(), sib_grant.stop()); + fmt::print("SIB1 allocated. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_sib1); + + // Allocate UE in common SearchSpace + ss_id = 1; + pdcch = &pdcch_common; + prb_bitmap ue_prbs = ~used_prbs_sib1; + first_prb = ue_prbs.find_lowest(0, ue_prbs.size(), true); + uint32_t ue_grant_size = 10; + prb_interval ue_grant{(uint32_t)first_prb, ue_grant_size}; + TESTASSERT_EQ(alloc_result::success, pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant)); + alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, ue_grant, ue_cc, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, ue_grant, *pdcch); + prb_bitmap used_prbs_ue = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_ue.count(), used_prbs_sib1.count() + ue_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, + pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant)); + + last_prb_bitmap.reset(); + last_prb_bitmap.fill(ue_grant.start(), ue_grant.stop()); + fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue); + + // Allocate UE in UE SearchSpace + ss_id = 2; + pdcch = &pdcch_ue; + used_prbs = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + prb_interval ue_grant2 = find_empty_interval_of_length(used_prbs, used_prbs_ue.size(), 0); + TESTASSERT_EQ(bwp_params.nof_prb, ue_grant2.stop()); + TESTASSERT_EQ(alloc_result::success, + pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant2)); + alloc_res = pdsch_sched.alloc_ue_pdsch(ss_id, srsran_dci_format_nr_1_0, ue_grant2, ue_cc, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + test_dci_freq_assignment(bwp_params, ue_grant2, *pdcch); + prb_bitmap used_prbs_ue2 = pdsch_sched.occupied_prbs(ss_id, srsran_dci_format_nr_1_0); + TESTASSERT_EQ(used_prbs_ue2.count(), used_prbs.count() + ue_grant2.length()); + TESTASSERT_EQ(alloc_result::sch_collision, + pdsch_sched.is_ue_grant_valid(ue_cc, ss_id, srsran_dci_format_nr_1_0, ue_grant2)); + + last_prb_bitmap.reset(); + last_prb_bitmap.fill(ue_grant2.start(), ue_grant2.stop()); + fmt::print("C-RNTI allocated in UE-dedicated common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", + last_prb_bitmap, + used_prbs_ue2); + + TESTASSERT_EQ(3, pdschs.size()); + pdsch_sched.reset(); + TESTASSERT_EQ(0, pdschs.size()); + TESTASSERT_EQ(0, pdsch_sched.occupied_prbs(2, srsran_dci_format_nr_1_0).count()); +} + +void test_multi_pusch() +{ + srsran::test_delimit_logger delimiter{"Test Multiple PUSCH Allocations"}; + + // Create Cell and UE configs + sched_nr_interface::sched_args_t sched_args; + sched_nr_cell_cfg_t cellcfg = get_cell_cfg(); + sched_nr_impl::cell_config_manager cell_params{0, cellcfg, sched_args}; + sched_nr_impl::ue_cfg_manager uecfg{get_ue_cfg(cellcfg)}; + const bwp_params_t& bwp_params = cell_params.bwps[0]; + ue_carrier_params_t ue_cc{0x4601, bwp_params, uecfg}; + ue_carrier_params_t ue_cc2{0x4602, bwp_params, uecfg}; + + pusch_list_t puschs; + pusch_alloc_result alloc_res; + + pusch_allocator pusch_sched(bwp_params, 0, puschs); + + pdcch_ul_t pdcch_ue1, pdcch_ue2; + pdcch_ue1.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 1, srsran_rnti_type_c, 0x4601); + pdcch_ue2.dci.ctx = generate_dci_ctx(bwp_params.cfg.pdcch, 2, srsran_rnti_type_c, 0x4602); + + // Allocate UE in common SearchSpace + uint32_t ss_id = 1; + pdcch_ul_t* pdcch = &pdcch_ue1; + prb_bitmap used_prbs = pusch_sched.occupied_prbs(); + uint32_t ue_grant_size = 10; + prb_interval ue_grant = find_empty_interval_of_length(used_prbs, ue_grant_size); + TESTASSERT_EQ(alloc_result::success, pusch_sched.is_grant_valid(srsran_search_space_type_common_1, ue_grant)); + alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx.ss_type, ue_grant, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + prb_bitmap used_prbs_ue1 = pusch_sched.occupied_prbs(); + TESTASSERT_EQ(used_prbs_ue1.count(), used_prbs.count() + ue_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, + pusch_sched.is_grant_valid(srsran_search_space_type_common_1, ue_grant, false)); + + prb_bitmap last_prb_bitmap(used_prbs.size()); + last_prb_bitmap.fill(ue_grant.start(), ue_grant.stop()); + fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue1); + + // Allocate UE in dedicated SearchSpace + ss_id = 2; + pdcch = &pdcch_ue2; + used_prbs = pusch_sched.occupied_prbs(); + prb_interval ue2_grant = find_empty_interval_of_length(used_prbs, used_prbs.size()); + TESTASSERT_EQ(alloc_result::success, pusch_sched.is_grant_valid(srsran_search_space_type_ue, ue2_grant)); + alloc_res = pusch_sched.alloc_pusch(pdcch->dci.ctx.ss_type, ue2_grant, pdcch->dci); + TESTASSERT(alloc_res.has_value()); + prb_bitmap used_prbs_ue2 = pusch_sched.occupied_prbs(); + TESTASSERT_EQ(used_prbs_ue2.count(), used_prbs.count() + ue2_grant.length()); + TESTASSERT_EQ(alloc_result::sch_collision, pusch_sched.is_grant_valid(srsran_search_space_type_ue, ue2_grant, false)); + + last_prb_bitmap.reset(); + last_prb_bitmap.fill(ue2_grant.start(), ue2_grant.stop()); + fmt::print("C-RNTI allocated in Common SearchSpace. Occupied PRBs:\n{:b} -> {:b}\n", last_prb_bitmap, used_prbs_ue2); +} + +} // namespace srsenb + +int main() +{ + auto& test_logger = srslog::fetch_basic_logger("TEST"); + test_logger.set_level(srslog::basic_levels::info); + auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR"); + mac_nr_logger.set_level(srslog::basic_levels::debug); + auto& pool_logger = srslog::fetch_basic_logger("POOL"); + pool_logger.set_level(srslog::basic_levels::debug); + + // Start the log backend. + srslog::init(); + + srsenb::test_si(); + srsenb::test_rar(); + srsenb::test_ue_pdsch(); + srsenb::test_pdsch_fail(); + srsenb::test_multi_pdsch(); + srsenb::test_multi_pusch(); +} \ No newline at end of file diff --git a/srsgnb/src/stack/mac/test/sched_nr_sim_ue.cc b/srsgnb/src/stack/mac/test/sched_nr_sim_ue.cc new file mode 100644 index 000000000..35c8e1234 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_sim_ue.cc @@ -0,0 +1,548 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_nr_sim_ue.h" +#include "sched_nr_common_test.h" +#include "sched_nr_ue_ded_test_suite.h" +#include "srsran/common/test_common.h" +#include "srsran/common/thread_pool.h" + +namespace srsenb { + +sched_nr_ue_sim::sched_nr_ue_sim(uint16_t rnti_, const sched_nr_ue_cfg_t& ue_cfg_) : + logger(srslog::fetch_basic_logger("MAC")) +{ + ctxt.rnti = rnti_; + ctxt.ue_cfg.apply_config_request(ue_cfg_); + ctxt.preamble_idx = -1; + + ctxt.cc_list.resize(ue_cfg_.carriers.size()); + for (auto& cc : ctxt.cc_list) { + for (size_t pid = 0; pid < SCHED_NR_MAX_HARQ; ++pid) { + cc.ul_harqs[pid].pid = pid; + cc.dl_harqs[pid].pid = pid; + } + } +} + +sched_nr_ue_sim::sched_nr_ue_sim(uint16_t rnti_, + const sched_nr_ue_cfg_t& ue_cfg_, + slot_point prach_slot_rx, + uint32_t preamble_idx) : + sched_nr_ue_sim(rnti_, ue_cfg_) +{ + ctxt.prach_slot_rx = prach_slot_rx; + ctxt.preamble_idx = preamble_idx; +} + +int sched_nr_ue_sim::update(const sched_nr_cc_result_view& cc_out) +{ + update_dl_harqs(cc_out); + + for (uint32_t i = 0; i < cc_out.dl->phy.pdcch_dl.size(); ++i) { + const auto& data = cc_out.dl->phy.pdcch_dl[i]; + if (data.dci.ctx.rnti != ctxt.rnti) { + continue; + } + slot_point pdcch_slot = cc_out.slot; + uint32_t k1 = ctxt.ue_cfg.phy_cfg.harq_ack + .dl_data_to_ul_ack[pdcch_slot.slot_idx() % ctxt.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + slot_point uci_slot = pdcch_slot + k1; + + ctxt.cc_list[cc_out.cc].pending_acks[uci_slot.to_uint()]++; + } + + // clear up old slots + ctxt.cc_list[cc_out.cc].pending_acks[(cc_out.slot - 1).to_uint()] = 0; + + return SRSRAN_SUCCESS; +} + +void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_result_view& cc_out) +{ + uint32_t cc = cc_out.cc; + + // Update DL Harqs + for (uint32_t i = 0; i < cc_out.dl->phy.pdcch_dl.size(); ++i) { + const auto& data = cc_out.dl->phy.pdcch_dl[i]; + if (data.dci.ctx.rnti != ctxt.rnti) { + continue; + } + auto& h = ctxt.cc_list[cc].dl_harqs[data.dci.pid]; + if (h.nof_txs == 0 or h.ndi != data.dci.ndi) { + // It is newtx + h.nof_retxs = 0; + h.ndi = data.dci.ndi; + h.first_slot_tx = cc_out.slot; + h.dci_loc = data.dci.ctx.location; + for (const sched_nr_impl::pdsch_t& pdsch : cc_out.dl->phy.pdsch) { + if (pdsch.sch.grant.rnti == data.dci.ctx.rnti) { + h.tbs = pdsch.sch.grant.tb[0].tbs / 8u; + } + } + } else { + // it is retx + h.nof_retxs++; + } + h.active = true; + h.last_slot_tx = cc_out.slot; + h.last_slot_ack = + h.last_slot_tx + + ctxt.ue_cfg.phy_cfg.harq_ack + .dl_data_to_ul_ack[h.last_slot_tx.slot_idx() % ctxt.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + h.nof_txs++; + } + + // Update UL harqs + for (uint32_t i = 0; i < cc_out.dl->phy.pdcch_ul.size(); ++i) { + const auto& data = cc_out.dl->phy.pdcch_ul[i]; + if (data.dci.ctx.rnti != ctxt.rnti) { + continue; + } + auto& h = ctxt.cc_list[cc].ul_harqs[data.dci.pid]; + if (h.nof_txs == 0 or h.ndi != data.dci.ndi) { + // It is newtx + h.is_msg3 = false; + h.nof_retxs = 0; + h.ndi = data.dci.ndi; + h.first_slot_tx = cc_out.slot + 4; // TODO + h.dci_loc = data.dci.ctx.location; + h.tbs = 100; // TODO + } else { + // it is retx + h.nof_retxs++; + } + h.active = true; + h.last_slot_tx = cc_out.slot + 4; // TODO + h.last_slot_ack = h.last_slot_tx; + h.nof_txs++; + } + + uint32_t rar_count = 0; + for (uint32_t i = 0; i < cc_out.dl->phy.pdcch_dl.size(); ++i) { + const auto& rar_pdcch = cc_out.dl->phy.pdcch_dl[i]; + if (rar_pdcch.dci.ctx.rnti_type != srsran_rnti_type_ra) { + continue; + } + const auto& rar_data = cc_out.dl->rar[rar_count++]; + for (uint32_t j = 0; j < rar_data.grants.size(); ++j) { + auto& msg3_grant = rar_data.grants[j]; + if (msg3_grant.msg3_dci.ctx.rnti != ctxt.rnti) { + continue; + } + auto& h = ctxt.cc_list[cc].ul_harqs[msg3_grant.msg3_dci.pid]; + if (h.nof_txs == 0) { + // It is newtx + h.is_msg3 = true; + h.nof_retxs = 0; + h.ndi = msg3_grant.msg3_dci.ndi; + h.first_slot_tx = cc_out.slot + 4 + MSG3_DELAY_MS; // TODO + h.dci_loc = msg3_grant.msg3_dci.ctx.location; + h.tbs = 100; // TODO + } else { + // it is retx + h.nof_retxs++; + } + h.active = true; + h.last_slot_tx = cc_out.slot + 4 + MSG3_DELAY_MS; // TODO + h.last_slot_ack = h.last_slot_tx; + h.nof_txs++; + } + } +} + +sched_nr_base_test_bench::sched_nr_base_test_bench(const sched_nr_interface::sched_args_t& sched_args, + const std::vector& cell_cfg_list, + std::string test_name_, + uint32_t nof_workers) : + logger(srslog::fetch_basic_logger("TEST")), + mac_logger(srslog::fetch_basic_logger("MAC-NR")), + sched_ptr(new sched_nr()), + test_delimiter(new srsran::test_delimit_logger{test_name_.c_str()}) +{ + sem_init(&slot_sem, 0, 1); + + cell_params.reserve(cell_cfg_list.size()); + for (uint32_t cc = 0; cc < cell_cfg_list.size(); ++cc) { + cell_params.emplace_back(cc, cell_cfg_list[cc], sched_args); + } + sched_ptr->config(sched_args, cell_cfg_list); // call parent cfg + + cc_workers.resize(nof_workers - 1); + for (uint32_t i = 0; i < cc_workers.size(); ++i) { + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "worker{}", i + 1); + cc_workers[i].reset(new srsran::task_worker{to_string(fmtbuf), 10}); + } + + cc_results.resize(cell_params.size()); + + TESTASSERT(cell_params.size() > 0); +} + +sched_nr_base_test_bench::~sched_nr_base_test_bench() +{ + stop(); +} + +void sched_nr_base_test_bench::stop() +{ + bool stopping = not stopped.exchange(true); + if (stopping) { + sem_wait(&slot_sem); + sem_post(&slot_sem); + for (auto& worker : cc_workers) { + worker->stop(); + } + sem_destroy(&slot_sem); + test_delimiter.reset(); + } +} + +std::vector sched_nr_base_test_bench::get_slot_results() const +{ + sem_wait(&slot_sem); + auto ret = cc_results; + sem_post(&slot_sem); + return ret; +} + +int sched_nr_base_test_bench::rach_ind(uint16_t rnti, uint32_t cc, slot_point tti_rx, uint32_t preamble_idx) +{ + sem_wait(&slot_sem); + + TESTASSERT(ue_db.count(rnti) == 0); + + sched_nr_interface::rar_info_t rach_info{}; + rach_info.cc = cc; + rach_info.temp_crnti = rnti; + rach_info.prach_slot = tti_rx; + rach_info.preamble_idx = preamble_idx; + rach_info.msg3_size = 7; + sched_ptr->dl_rach_info(rach_info); + + sched_nr_ue_cfg_t uecfg; + uecfg.carriers.resize(1); + uecfg.carriers[0].active = true; + uecfg.carriers[0].cc = cc; + uecfg.phy_cfg = cell_params[cc].default_ue_phy_cfg; + ue_db.insert(std::make_pair(rnti, sched_nr_ue_sim(rnti, uecfg, current_slot_tx, preamble_idx))); + + sem_post(&slot_sem); + return SRSRAN_SUCCESS; +} + +void sched_nr_base_test_bench::user_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg_) +{ + sem_wait(&slot_sem); + + if (ue_db.count(rnti) == 0) { + ue_db.insert(std::make_pair(rnti, sched_nr_ue_sim(rnti, ue_cfg_))); + } else { + ue_db.at(rnti).get_ctxt().ue_cfg.apply_config_request(ue_cfg_); + } + sched_ptr->ue_cfg(rnti, ue_cfg_); + + sem_post(&slot_sem); +} + +void sched_nr_base_test_bench::add_rlc_dl_bytes(uint16_t rnti, uint32_t lcid, uint32_t pdu_size_bytes) +{ + TESTASSERT(ue_db.count(rnti) > 0); + dl_buffer_state_diff(rnti, lcid, pdu_size_bytes); +} + +void sched_nr_base_test_bench::run_slot(slot_point slot_tx) +{ + srsran_assert(not stopped.load(std::memory_order_relaxed), "Running scheduler when it has already been stopped"); + // Block concurrent or out-of-order calls to the scheduler + sem_wait(&slot_sem); + current_slot_tx = slot_tx; + nof_cc_remaining = cell_params.size(); + + logger.set_context(slot_tx.to_uint()); + mac_logger.set_context(slot_tx.to_uint()); + + // Clear previous slot results + for (uint32_t cc = 0; cc < cc_results.size(); ++cc) { + cc_results[cc] = {}; + } + logger.info("---------------- TTI=%d ---------------", slot_tx.to_uint()); + + // Process pending feedback + for (auto& ue : ue_db) { + ue_nr_slot_events events; + set_default_slot_events(ue.second.get_ctxt(), events); + set_external_slot_events(ue.second.get_ctxt(), events); + apply_slot_events(ue.second.get_ctxt(), events); + } + + slot_ctxt = get_enb_ctxt(); + slot_start_tp = std::chrono::steady_clock::now(); + + sched_ptr->slot_indication(current_slot_tx); + + // Generate CC result (parallel or serialized) + uint32_t worker_idx = 0; + for (uint32_t cc = 0; cc < cell_params.size(); ++cc) { + if (worker_idx == cc_workers.size()) { + generate_cc_result(cc); + } else { + cc_workers[worker_idx]->push_task([this, cc]() { generate_cc_result(cc); }); + } + worker_idx = (worker_idx + 1) % (cc_workers.size() + 1); + } +} + +void sched_nr_base_test_bench::generate_cc_result(uint32_t cc) +{ + // Run scheduler + cc_results[cc].res.slot = current_slot_tx; + cc_results[cc].res.cc = cc; + cc_results[cc].res.dl = sched_ptr->get_dl_sched(current_slot_tx, cc); + cc_results[cc].res.ul = sched_ptr->get_ul_sched(current_slot_tx, cc); + auto tp2 = std::chrono::steady_clock::now(); + cc_results[cc].cc_latency_ns = std::chrono::duration_cast(tp2 - slot_start_tp); + + if (--nof_cc_remaining > 0) { + // there are still missing CC results + return; + } + + // Run tests and update UE state + process_results(); + + // Notify awaiting new slot worker + sem_post(&slot_sem); +} + +void sched_nr_base_test_bench::process_results() +{ + // Derived class-defined tests + process_slot_result(slot_ctxt, cc_results); + + for (uint32_t cc = 0; cc < cell_params.size(); ++cc) { + sched_nr_cc_result_view cc_out = cc_results[cc].res; + + // Run common tests + test_dl_pdcch_consistency(cell_params[cc], cc_out.dl->phy.pdcch_dl); + test_pdsch_consistency(cc_out.dl->phy.pdsch); + test_ssb_scheduled_grant(cc_out.slot, cell_params[cc_out.cc], cc_out.dl->phy.ssb); + + // Run UE-dedicated tests + test_dl_sched_result(slot_ctxt, cc_out); + + // Update UE state + for (auto& u : ue_db) { + u.second.update(cc_out); + } + + // Update scheduler buffers + update_sched_buffer_state(cc_out); + } +} + +void sched_nr_base_test_bench::dl_buffer_state_diff(uint16_t rnti, uint32_t lcid, int newtx) +{ + auto& lch = gnb_ue_db[rnti].logical_channels[lcid]; + lch.rlc_unacked = std::max(0, (int)lch.rlc_unacked + newtx); + update_sched_buffer_state(rnti); + logger.debug("STATUS: rnti=0x%x, lcid=%d DL buffer state is (unacked=%d, newtx=%d)", + rnti, + lcid, + lch.rlc_unacked, + lch.rlc_newtx); +} + +void sched_nr_base_test_bench::dl_buffer_state_diff(uint16_t rnti, int diff_bs) +{ + if (diff_bs == 0) { + return; + } + if (diff_bs > 0) { + const auto& ue_bearers = ue_db.at(rnti).get_ctxt().ue_cfg.ue_bearers; + for (int i = ue_bearers.size() - 1; i >= 0; --i) { + if (ue_bearers[i].is_dl()) { + dl_buffer_state_diff(rnti, i, diff_bs); + return; + } + } + srsran_terminate("Updating UE RLC buffer state but no bearers are active"); + } + for (auto& lch : gnb_ue_db[rnti].logical_channels) { + if (not ue_db.at(rnti).get_ctxt().ue_cfg.ue_bearers[lch.first].is_dl()) { + continue; + } + int max_diff = -std::min((int)lch.second.rlc_unacked, -diff_bs); + if (max_diff != 0) { + dl_buffer_state_diff(rnti, lch.first, max_diff); + diff_bs -= max_diff; + } + } +} + +void sched_nr_base_test_bench::update_sched_buffer_state(uint16_t rnti) +{ + auto& u = ue_db.at(rnti); + for (auto& lch : gnb_ue_db[rnti].logical_channels) { + int newtx = lch.second.rlc_unacked; + for (auto& cc : u.get_ctxt().cc_list) { + if (newtx <= 0) { + break; + } + for (auto& dl_h : cc.dl_harqs) { + int tbs = dl_h.active ? dl_h.tbs : 0; + newtx = std::max(0, newtx - tbs); + } + } + if (newtx != (int)lch.second.rlc_newtx) { + sched_ptr->dl_buffer_state(rnti, lch.first, newtx, 0); + lch.second.rlc_newtx = newtx; + logger.debug("STATUS: rnti=0x%x, lcid=%d DL buffer state is (unacked=%d, newtx=%d)", + rnti, + lch.first, + lch.second.rlc_unacked, + lch.second.rlc_newtx); + } + } +} + +void sched_nr_base_test_bench::update_sched_buffer_state(const sched_nr_cc_result_view& cc_out) +{ + for (auto& dl : cc_out.dl->phy.pdcch_dl) { + if (dl.dci.ctx.rnti_type == srsran_rnti_type_c or dl.dci.ctx.rnti_type == srsran_rnti_type_tc) { + update_sched_buffer_state(dl.dci.ctx.rnti); + } + } +} + +int sched_nr_base_test_bench::set_default_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, + ue_nr_slot_events& pending_events) +{ + pending_events.cc_list.clear(); + pending_events.cc_list.resize(cell_params.size()); + pending_events.slot_rx = current_slot_tx; + + for (uint32_t enb_cc_idx = 0; enb_cc_idx < pending_events.cc_list.size(); ++enb_cc_idx) { + auto& cc_feedback = pending_events.cc_list[enb_cc_idx]; + + cc_feedback.configured = true; + for (uint32_t pid = 0; pid < SCHED_NR_MAX_HARQ; ++pid) { + auto& dl_h = ue_ctxt.cc_list[enb_cc_idx].dl_harqs[pid]; + auto& ul_h = ue_ctxt.cc_list[enb_cc_idx].ul_harqs[pid]; + + // Set default DL ACK + if (dl_h.active and dl_h.last_slot_ack == current_slot_tx) { + cc_feedback.dl_acks.push_back(ue_nr_slot_events::ack_t{pid, true}); + } + + // Set default UL ACK + if (ul_h.active and ul_h.last_slot_ack == current_slot_tx) { + cc_feedback.ul_acks.emplace_back(ue_nr_slot_events::ack_t{pid, true}); + } + + // Set default CQI + if (ue_ctxt.ue_cfg.phy_cfg.csi.reports[0].type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { + auto& p = ue_ctxt.ue_cfg.phy_cfg.csi.reports[0].periodic; + if (p.offset == pending_events.slot_rx.to_uint() % p.period) { + cc_feedback.cqi = 15; + } + } + + // TODO: other CSI + } + } + + return SRSRAN_SUCCESS; +} + +int sched_nr_base_test_bench::apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_slot_events& events) +{ + for (uint32_t enb_cc_idx = 0; enb_cc_idx < events.cc_list.size(); ++enb_cc_idx) { + const auto& cc_feedback = events.cc_list[enb_cc_idx]; + if (not cc_feedback.configured) { + continue; + } + + for (auto& ack : cc_feedback.dl_acks) { + auto& h = ue_ctxt.cc_list[enb_cc_idx].dl_harqs[ack.pid]; + + if (ack.ack) { + logger.info("EVENT: DL ACK rnti=0x%x slot_dl_tx=%u cc=%d pid=%d, tbs=%d", + ue_ctxt.rnti, + h.last_slot_tx.to_uint(), + enb_cc_idx, + ack.pid, + h.tbs); + dl_buffer_state_diff(ue_ctxt.rnti, -(int)h.tbs); + } + + // update scheduler + sched_ptr->dl_ack_info(ue_ctxt.rnti, enb_cc_idx, h.pid, 0, ack.ack); + + // update UE sim context + if (ack.ack or ue_ctxt.is_last_dl_retx(enb_cc_idx, h.pid)) { + h.active = false; + } + } + + for (auto& ack : cc_feedback.ul_acks) { + auto& h = ue_ctxt.cc_list[enb_cc_idx].ul_harqs[ack.pid]; + + if (ack.ack) { + logger.info("EVENT: UL ACK rnti=0x%x, slot_ul_tx=%u, cc=%d pid=%d", + ue_ctxt.rnti, + h.last_slot_tx.to_uint(), + enb_cc_idx, + h.pid); + } + + // update scheduler + sched_ptr->ul_crc_info(ue_ctxt.rnti, enb_cc_idx, ack.pid, ack.ack); + + if (h.is_msg3) { + logger.info("STATUS: rnti=0x%x received Msg3", ue_ctxt.rnti); + dl_buffer_state_diff(ue_ctxt.rnti, 0, 150); // Schedule RRC setup + } + } + + if (cc_feedback.cqi >= 0) { + logger.info("EVENT: DL CQI rnti=0x%x, cqi=%d", ue_ctxt.rnti, cc_feedback.cqi); + sched_ptr->dl_cqi_info(ue_ctxt.rnti, enb_cc_idx, cc_feedback.cqi); + } + } + + return SRSRAN_SUCCESS; +} + +sim_nr_enb_ctxt_t sched_nr_base_test_bench::get_enb_ctxt() const +{ + sim_nr_enb_ctxt_t ctxt; + ctxt.cell_params = cell_params; + + for (auto& ue_pair : ue_db) { + ctxt.ue_db.insert(std::make_pair(ue_pair.first, &ue_pair.second.get_ctxt())); + } + + return ctxt; +} + +} // namespace srsenb \ No newline at end of file diff --git a/srsgnb/src/stack/mac/test/sched_nr_sim_ue.h b/srsgnb/src/stack/mac/test/sched_nr_sim_ue.h new file mode 100644 index 000000000..fb2062bbc --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_sim_ue.h @@ -0,0 +1,213 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_SIM_UE_H +#define SRSRAN_SCHED_NR_SIM_UE_H + +#include "srsenb/test/mac/sched_sim_ue.h" +#include "srsgnb/hdr/stack/mac/sched_nr.h" +#include "srsran/adt/circular_array.h" +#include "srsran/common/test_common.h" +#include +#include + +namespace srsran { +class task_worker; +} + +namespace srsenb { + +const static uint32_t MAX_GRANTS = mac_interface_phy_nr::MAX_GRANTS; + +struct ue_nr_harq_ctxt_t { + bool active = false; + bool ndi = false; + bool is_msg3 = false; + uint32_t pid = 0; + uint32_t nof_txs = 0; + uint32_t nof_retxs = std::numeric_limits::max(); + uint32_t riv = 0; + srsran_dci_location_t dci_loc = {}; + uint32_t tbs = 0; + slot_point last_slot_tx, first_slot_tx, last_slot_ack; +}; +struct sched_nr_cc_result_view { + slot_point slot; + uint32_t cc = 0; + const sched_nr_interface::dl_res_t* dl = nullptr; + const sched_nr_interface::ul_res_t* ul = nullptr; +}; + +struct ue_nr_cc_ctxt_t { + std::array dl_harqs; + std::array ul_harqs; + srsran::circular_array pending_acks; +}; + +struct ue_nr_slot_events { + struct ack_t { + uint32_t pid; + bool ack; + }; + struct cc_data { + bool configured = false; + srsran::bounded_vector dl_acks; + srsran::bounded_vector ul_acks; + int cqi = -1; + }; + slot_point slot_rx; + std::vector cc_list; +}; + +struct sim_nr_ue_ctxt_t { + uint16_t rnti; + uint32_t preamble_idx; + slot_point prach_slot_rx; + sched_nr_impl::ue_cfg_manager ue_cfg; + std::vector cc_list; + + bool is_last_dl_retx(uint32_t ue_cc_idx, uint32_t pid) const + { + auto& h = cc_list.at(ue_cc_idx).dl_harqs[pid]; + return h.nof_retxs + 1 >= ue_cfg.maxharq_tx; + } +}; +struct sim_nr_enb_ctxt_t { + srsran::span cell_params; + std::map ue_db; +}; + +class sched_nr_ue_sim +{ +public: + sched_nr_ue_sim(uint16_t rnti_, const sched_nr_ue_cfg_t& ue_cfg_); + sched_nr_ue_sim(uint16_t rnti_, const sched_nr_ue_cfg_t& ue_cfg_, slot_point prach_slot_rx, uint32_t preamble_idx); + + int update(const sched_nr_cc_result_view& cc_out); + + const sim_nr_ue_ctxt_t& get_ctxt() const { return ctxt; } + sim_nr_ue_ctxt_t& get_ctxt() { return ctxt; } + +private: + void update_dl_harqs(const sched_nr_cc_result_view& sf_out); + + srslog::basic_logger& logger; + sim_nr_ue_ctxt_t ctxt; +}; + +/// Implementation of features common to parallel and sequential sched nr testers +class sched_nr_base_test_bench +{ +public: + struct cc_result_t { + sched_nr_cc_result_view res; + std::chrono::nanoseconds cc_latency_ns; + }; + + sched_nr_base_test_bench(const sched_nr_interface::sched_args_t& sched_args, + const std::vector& cell_params_, + std::string test_name, + uint32_t nof_workers = 1); + virtual ~sched_nr_base_test_bench(); + + void run_slot(slot_point slot_tx); + void stop(); + + slot_point get_slot_tx() const { return current_slot_tx; } + + /// may block waiting for scheduler to finish generating slot result + std::vector get_slot_results() const; + + int rach_ind(uint16_t rnti, uint32_t cc, slot_point tti_rx, uint32_t preamble_idx); + + void user_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg_); + + void add_rlc_dl_bytes(uint16_t rnti, uint32_t lcid, uint32_t pdu_size_bytes); + + srsran::const_span get_cell_params() const { return cell_params; } + + /** + * @brief Specify external events that will be forwarded to the scheduler (CQI, ACKs, etc.) in the given slot + * This method can be overridden by the derived class to simulate the environment of interest. + * @param[in] ue_ctxt simulated UE context object + * @param[in/out] pending_events events to be sent to the scheduler. The passed arg is initialized with the + * "default events", sufficient to ensure a stable connection without retxs. + * The derived class can decide to erase/modify/add new events + */ + virtual void set_external_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events) {} + + /** + * @brief Called every slot to process the scheduler output for a given CC. + * @param enb_ctxt simulated eNB context object + * @param cc_out scheduler result for a given CC + */ + virtual void process_slot_result(const sim_nr_enb_ctxt_t& enb_ctxt, srsran::const_span cc_out) {} + +protected: + void generate_cc_result(uint32_t cc); + sim_nr_enb_ctxt_t get_enb_ctxt() const; + + void dl_buffer_state_diff(uint16_t rnti, uint32_t lcid, int newtx); + void dl_buffer_state_diff(uint16_t rnti, int newtx); + void update_sched_buffer_state(uint16_t rnti); + void update_sched_buffer_state(const sched_nr_cc_result_view& cc_out); + + int set_default_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events); + int apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_slot_events& events); + + /// Runs general tests to verify result consistency, and updates UE state + void process_results(); + + std::unique_ptr test_delimiter; + srslog::basic_logger& logger; + srslog::basic_logger& mac_logger; + std::unique_ptr sched_ptr; + std::vector cell_params; + + std::vector > cc_workers; + + // UE context from the UE's point-of-view + std::map ue_db; + + // gNB point-of-view of UE state + struct gnb_ue_ctxt { + struct channel_ctxt { + uint32_t rlc_unacked = 0; + uint32_t rlc_newtx = 0; + }; + std::map logical_channels; + }; + std::map gnb_ue_db; + + // slot-specific + slot_point current_slot_tx; + std::chrono::steady_clock::time_point slot_start_tp; + sim_nr_enb_ctxt_t slot_ctxt; + std::vector cc_results; + + std::atomic stopped{false}; + mutable sem_t slot_sem; + std::atomic nof_cc_remaining{0}; +}; + +} // namespace srsenb + +#endif // SRSRAN_SCHED_NR_SIM_UE_H diff --git a/srsgnb/src/stack/mac/test/sched_nr_test.cc b/srsgnb/src/stack/mac/test/sched_nr_test.cc new file mode 100644 index 000000000..dce3c8bb9 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_test.cc @@ -0,0 +1,334 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_nr_cfg_generators.h" +#include "sched_nr_sim_ue.h" +#include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/test_common.h" +#include "srsran/support/emergency_handlers.h" +#include +#include +#include + +// shorten boost program options namespace +namespace bpo = boost::program_options; + +namespace srsenb { + +std::default_random_engine rand_gen; + +struct sim_args_t { + uint32_t rand_seed; + uint32_t fixed_cqi; + std::string mac_log_level; + std::string test_log_level; +}; + +class sched_tester : public sched_nr_base_test_bench +{ + sim_args_t args; + +public: + explicit sched_tester(sim_args_t args_, + const sched_nr_interface::sched_args_t& sched_args, + const std::vector& cell_params_, + std::string test_name) : + sched_nr_base_test_bench(sched_args, cell_params_, test_name), args(args_) + {} + + void process_slot_result(const sim_nr_enb_ctxt_t& enb_ctxt, srsran::const_span cc_out) override + { + for (auto& cc : cc_out) { + for (auto& pdsch : cc.res.dl->phy.pdsch) { + if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c or pdsch.sch.grant.rnti_type == srsran_rnti_type_tc) { + ue_metrics[pdsch.sch.grant.rnti].nof_dl_txs++; + ue_metrics[pdsch.sch.grant.rnti].nof_dl_bytes += pdsch.sch.grant.tb[0].tbs / 8u; + } + } + for (auto& pusch : cc.res.ul->pusch) { + if (pusch.sch.grant.rnti_type == srsran_rnti_type_c or pusch.sch.grant.rnti_type == srsran_rnti_type_tc) { + ue_metrics[pusch.sch.grant.rnti].nof_ul_txs++; + ue_metrics[pusch.sch.grant.rnti].nof_ul_bytes += pusch.sch.grant.tb[0].tbs / 8u; + } + } + } + } + + void set_external_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events) override + { + for (uint32_t cc = 0; cc < pending_events.cc_list.size(); ++cc) { + auto& cc_events = pending_events.cc_list[cc]; + // if CQI is expected, set it to fixed value + if (cc_events.cqi >= 0) { + cc_events.cqi = args.fixed_cqi; + } + } + } + + void print_results() + { + srslog::flush(); + fmt::print("SCHED UE metrics:\n"); + for (auto& u : ue_metrics) { + fmt::print(" 0x{:x}: nof_txs=({}, {}), nof_bytes=({}, {})\n", + u.first, + u.second.nof_dl_txs, + u.second.nof_ul_txs, + u.second.nof_dl_bytes, + u.second.nof_ul_bytes); + } + } + + struct sched_ue_metrics { + uint32_t nof_dl_txs = 0, nof_ul_txs = 0; + uint64_t nof_dl_bytes = 0, nof_ul_bytes = 0; + }; + std::map ue_metrics; +}; + +struct sched_event_t { + uint32_t slot_count; + std::function run; +}; + +sched_event_t add_user(uint32_t slot_count, uint16_t rnti, uint32_t preamble_idx) +{ + auto task = [rnti, preamble_idx](sched_nr_base_test_bench& tester) { + tester.rach_ind(rnti, 0, tester.get_slot_tx() - TX_ENB_DELAY, preamble_idx); + }; + return sched_event_t{slot_count, task}; +} + +sched_event_t ue_cfg(uint32_t slot_count, uint16_t rnti, const sched_nr_ue_cfg_t& ue_cfg) +{ + auto task = [rnti, ue_cfg](sched_nr_base_test_bench& tester) { tester.user_cfg(rnti, ue_cfg); }; + return sched_event_t{slot_count, task}; +} + +sched_event_t add_rlc_dl_bytes(uint32_t slot_count, uint16_t rnti, uint32_t lcid, uint32_t pdu_size) +{ + auto task = [rnti, pdu_size, lcid](sched_nr_base_test_bench& tester) { + tester.add_rlc_dl_bytes(rnti, lcid, pdu_size); + }; + return sched_event_t{slot_count, task}; +} + +void test_sched_nr_no_data(sim_args_t args) +{ + uint32_t max_nof_ttis = 1000, nof_sectors = 1; + uint16_t rnti = 0x4601; + + sched_nr_interface::sched_args_t cfg; + cfg.auto_refill_buffer = false; + std::vector cells_cfg = get_default_cells_cfg(nof_sectors); + + std::string test_name = "Test with no data"; + sched_tester tester(args, cfg, cells_cfg, test_name); + + /* Set events */ + std::deque events; + events.push_back(add_user(9, rnti, 0)); + sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(1); + events.push_back(ue_cfg(20, rnti, uecfg)); + + /* Run Test */ + for (uint32_t nof_slots = 0; nof_slots < max_nof_ttis; ++nof_slots) { + slot_point slot_rx(0, nof_slots % 10240); + slot_point slot_tx = slot_rx + TX_ENB_DELAY; + + // run events + while (not events.empty() and events.front().slot_count <= nof_slots) { + events.front().run(tester); + events.pop_front(); + } + + // call sched + tester.run_slot(slot_tx); + } + + tester.print_results(); + + // Since DL buffers were not externally updated, we should only see Msg4 as DL tx + TESTASSERT_EQ(1, tester.ue_metrics[rnti].nof_dl_txs); + // Since UL buffers were not externally updated, we should only see Msg3 as UL tx + TESTASSERT_EQ(1, tester.ue_metrics[rnti].nof_ul_txs); +} + +void test_sched_nr_data(sim_args_t args) +{ + uint32_t nof_sectors = 1; + uint16_t rnti = 0x4601; + uint32_t nof_dl_bytes_to_tx = + std::uniform_int_distribution{1, 9}(rand_gen)*pow(10, std::uniform_int_distribution{1, 7}(rand_gen)); + + sched_nr_interface::sched_args_t cfg; + cfg.auto_refill_buffer = false; + std::vector cells_cfg = get_default_cells_cfg(nof_sectors); + + std::string test_name = "Test with data"; + sched_tester tester(args, cfg, cells_cfg, test_name); + + /* Set events */ + std::deque events; + events.push_back(add_user(9, rnti, 0)); + sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(1); + events.push_back(ue_cfg(20, rnti, uecfg)); + events.push_back(add_rlc_dl_bytes(50, rnti, 0, nof_dl_bytes_to_tx)); + + /* Run Test */ + uint32_t stop_tti = std::numeric_limits::max(); + auto finish_condition = [&stop_tti, rnti, nof_dl_bytes_to_tx, &tester](uint32_t nof_slots) mutable { + if (stop_tti == std::numeric_limits::max() and + tester.ue_metrics[rnti].nof_dl_bytes >= nof_dl_bytes_to_tx) { + stop_tti = nof_slots + 10; + } + return nof_slots >= std::min(stop_tti, 100000u); + }; + for (uint32_t nof_slots = 0; not finish_condition(nof_slots); ++nof_slots) { + slot_point slot_rx(0, nof_slots % 10240); + slot_point slot_tx = slot_rx + TX_ENB_DELAY; + + // run events + while (not events.empty() and events.front().slot_count <= nof_slots) { + events.front().run(tester); + events.pop_front(); + } + + // call sched + tester.run_slot(slot_tx); + } + // Run a few extra slots to ensure the scheduler does not allocate more DL bytes than necessary + for (uint32_t nof_slots_extra = 0; nof_slots_extra < 40; ++nof_slots_extra) { + tester.run_slot(tester.get_slot_tx() + 1); + auto results = tester.get_slot_results(); + TESTASSERT(results[0].res.dl->phy.pdcch_dl.empty()); + TESTASSERT(results[0].res.dl->phy.pdcch_ul.empty()); + } + + srslog::flush(); + fmt::print("== Results ==\n"); + fmt::print("Enqueued RLC DL bytes: {}\n", nof_dl_bytes_to_tx); + tester.print_results(); + + TESTASSERT(tester.ue_metrics[rnti].nof_dl_txs > 0); + TESTASSERT(tester.ue_metrics[rnti].nof_dl_bytes >= nof_dl_bytes_to_tx); + TESTASSERT(tester.ue_metrics[rnti].nof_dl_bytes < nof_dl_bytes_to_tx + 20000); + // Since UL buffers were not externally updated, we should only see Msg3 as UL tx + TESTASSERT_EQ(1, tester.ue_metrics[rnti].nof_ul_txs); +} + +sim_args_t handle_args(int argc, char** argv) +{ + sim_args_t args; + + std::string config_file; + bpo::options_description options; + bpo::options_description options_sim("Test Sim options"); + bpo::options_description options_conf_file("Configuration file"); + + // clang-format off + options_sim.add_options() + ("seed", bpo::value(&args.rand_seed)->default_value(std::chrono::system_clock::now().time_since_epoch().count()), "Simulation Random Seed") + ("cqi", bpo::value(&args.fixed_cqi)->default_value(15), "UE DL CQI") + ("log.mac_level", bpo::value(&args.mac_log_level)->default_value("info"), "MAC log level") + ("log.test_level", bpo::value(&args.test_log_level)->default_value("info"), "TEST log level") + ; + options_conf_file.add_options() + ("config_file", bpo::value(&config_file), "Configuration file") + ; + + bpo::positional_options_description p; + p.add("config_file", -1); + + options.add(options_sim).add(options_conf_file).add_options() + ("help", "Show this message") + ; + // clang-format on + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).positional(p).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + srsran_terminate("%s", e.what()); + } + + // help option was given or error - print usage and exit + if (vm.count("help")) { + fmt::print("Usage: {} [OPTIONS] config_file\n\n", argv[0]); + fmt::print("{}\n", options); + exit(0); + } + + // if config file given + if (vm.count("config_file") != 0U) { + fmt::print("Reading configuration file {}...\n", config_file); + std::ifstream conf(config_file.c_str(), std::ios::in); + if (conf.fail()) { + fmt::print("Failed to read configuration file {} - exiting\n", config_file); + exit(1); + } + + // parse config file and handle errors gracefully + try { + bpo::store(bpo::parse_config_file(conf, options), vm); + bpo::notify(vm); + } catch (const boost::program_options::error& e) { + srsran_terminate("%s\n", e.what()); + } + } + + return args; +} + +} // namespace srsenb + +int main(int argc, char** argv) +{ + // Start the log backend. + srslog::init(); + + // Parse args + srsenb::sim_args_t args = srsenb::handle_args(argc, argv); + + // Initialize Loggers + auto& test_logger = srslog::fetch_basic_logger("TEST"); + test_logger.set_level(srslog::str_to_basic_level(args.test_log_level)); + auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR"); + mac_nr_logger.set_level(srslog::str_to_basic_level(args.mac_log_level)); + auto& pool_logger = srslog::fetch_basic_logger("POOL"); + pool_logger.set_level(srslog::basic_levels::debug); + + // Setup Random Generator + srsenb::rand_gen = std::default_random_engine(args.rand_seed); + fmt::print("TEST: Random Seed is {}", args.rand_seed); + add_emergency_cleanup_handler( + [](void* args_void) { + auto* args = (srsenb::sim_args_t*)args_void; + fmt::print("TEST: Random Seed was {}", args->rand_seed); + }, + (void*)&args); + + srsenb::test_sched_nr_no_data(args); + srsenb::test_sched_nr_data(args); + + fmt::print("TEST: Random Seed was {}", args.rand_seed); +} diff --git a/srsgnb/src/stack/mac/test/sched_nr_ue_ded_test_suite.cc b/srsgnb/src/stack/mac/test/sched_nr_ue_ded_test_suite.cc new file mode 100644 index 000000000..ac0b82c47 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_ue_ded_test_suite.cc @@ -0,0 +1,62 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "sched_nr_ue_ded_test_suite.h" +#include "srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h" +#include "srsran/common/test_common.h" + +namespace srsenb { + +using namespace srsenb::sched_nr_impl; + +void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_result_view& cc_out) +{ + slot_point pdcch_slot = cc_out.slot; + const pdcch_dl_list_t& pdcchs = cc_out.dl->phy.pdcch_dl; + + // Iterate over UE PDCCH allocations + for (const pdcch_dl_t& pdcch : pdcchs) { + if (pdcch.dci.ctx.rnti_type != srsran_rnti_type_c) { + continue; + } + const sim_nr_ue_ctxt_t& ue = *enb_ctxt.ue_db.at(pdcch.dci.ctx.rnti); + uint32_t k1 = ue.ue_cfg.phy_cfg.harq_ack + .dl_data_to_ul_ack[pdcch_slot.slot_idx() % ue.ue_cfg.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + + // CHECK: Carrier activation + TESTASSERT(ue.ue_cfg.carriers[cc_out.cc].active); + + // CHECK: Coreset chosen/DCI content + TESTASSERT(ue.ue_cfg.phy_cfg.pdcch.coreset_present[pdcch.dci.ctx.coreset_id]); + const auto& coreset = ue.ue_cfg.phy_cfg.pdcch.coreset[pdcch.dci.ctx.coreset_id]; + TESTASSERT(coreset.id == pdcch.dci.ctx.coreset_id); + + // CHECK: UCI + if (pdcch.dci.ctx.format == srsran_dci_format_nr_1_0) { + TESTASSERT_EQ(k1 - 1, pdcch.dci.harq_feedback); + } else { + TESTASSERT(pdcch.dci.harq_feedback == pdcch_slot.slot_idx()); + } + TESTASSERT(ue.cc_list[cc_out.cc].pending_acks[(pdcch_slot + k1).to_uint()] % 4 == pdcch.dci.dai); + } +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/mac/test/sched_nr_ue_ded_test_suite.h b/srsgnb/src/stack/mac/test/sched_nr_ue_ded_test_suite.h new file mode 100644 index 000000000..2ae4b9773 --- /dev/null +++ b/srsgnb/src/stack/mac/test/sched_nr_ue_ded_test_suite.h @@ -0,0 +1,33 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_SCHED_NR_UE_DED_TEST_SUITE_H +#define SRSRAN_SCHED_NR_UE_DED_TEST_SUITE_H + +#include "sched_nr_sim_ue.h" + +namespace srsenb { + +void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_result_view& cc_out); + +} + +#endif // SRSRAN_SCHED_NR_UE_DED_TEST_SUITE_H diff --git a/srsgnb/src/stack/mac/ue_nr.cc b/srsgnb/src/stack/mac/ue_nr.cc new file mode 100644 index 000000000..3fb68651c --- /dev/null +++ b/srsgnb/src/stack/mac/ue_nr.cc @@ -0,0 +1,273 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include +#include +#include +#include + +#include "srsgnb/hdr/stack/mac/ue_nr.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/string_helpers.h" +#include "srsran/interfaces/gnb_interfaces.h" + +namespace srsenb { + +ue_nr::ue_nr(uint16_t rnti_, + uint32_t enb_cc_idx, + sched_nr_interface* sched_, + rrc_interface_mac_nr* rrc_, + rlc_interface_mac* rlc_, + phy_interface_stack_nr* phy_, + srslog::basic_logger& logger_) : + rnti(rnti_), + sched(sched_), + rrc(rrc_), + rlc(rlc_), + phy(phy_), + logger(logger_), + ue_rlc_buffer(srsran::make_byte_buffer()) +{} + +ue_nr::~ue_nr() {} + +void ue_nr::reset() +{ + { + std::lock_guard lock(metrics_mutex); + ue_metrics = {}; + } + nof_failures = 0; +} + +void ue_nr::ue_cfg(const sched_nr_interface::ue_cfg_t& ue_cfg) +{ + // nop +} + +void ue_nr::set_tti(uint32_t tti) +{ + last_tti = tti; +} + +uint32_t ue_nr::read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) +{ + return rlc->read_pdu(rnti, lcid, payload, requested_bytes); +} + +int ue_nr::generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size, srsran::const_span subpdu_lcids) +{ + std::lock_guard lock(mutex); + + if (mac_pdu_dl.init_tx(pdu, grant_size) != SRSRAN_SUCCESS) { + logger.error("Couldn't initialize MAC PDU buffer"); + return SRSRAN_ERROR; + } + + bool drb_activity = false; // inform RRC about user activity if true + + int32_t remaining_len = mac_pdu_dl.get_remaing_len(); + + logger.debug("0x%x Generating MAC PDU (%d B)", rnti, remaining_len); + + // First, add CEs as indicated by scheduler + for (const auto& lcid : subpdu_lcids) { + logger.debug("adding lcid=%d", lcid); + if (lcid == srsran::mac_sch_subpdu_nr::CON_RES_ID) { + if (last_msg3 != nullptr) { + srsran::mac_sch_subpdu_nr::ue_con_res_id_t id; + memcpy(id.data(), last_msg3->msg, id.size()); + if (mac_pdu_dl.add_ue_con_res_id_ce(id) != SRSRAN_SUCCESS) { + logger.error("0x%x Failed to add ConRes CE.", rnti); + } + last_msg3 = nullptr; // don't use this Msg3 again + } else { + logger.warning("0x%x Can't add ConRes CE. No Msg3 stored.", rnti); + } + } else { + // add SDUs for given LCID + while (remaining_len >= MIN_RLC_PDU_LEN) { + // clear read buffer + ue_rlc_buffer->clear(); + + // Determine space for RLC + remaining_len -= remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2; + + // read RLC PDU + int pdu_len = rlc->read_pdu(rnti, lcid, ue_rlc_buffer->msg, remaining_len); + + if (pdu_len > remaining_len) { + logger.error("Can't add SDU of %d B. Available space %d B", pdu_len, remaining_len); + break; + } else { + // Add SDU if RLC has something to tx + if (pdu_len > 0) { + ue_rlc_buffer->N_bytes = pdu_len; + logger.debug(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes); + + // add to MAC PDU and pack + if (mac_pdu_dl.add_sdu(lcid, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes) != SRSRAN_SUCCESS) { + logger.error("Error packing MAC PDU"); + break; + } + + // set DRB activity flag but only notify RRC once + if (lcid > 3) { + drb_activity = true; + } + } else { + break; + } + + remaining_len -= pdu_len; + logger.debug("%d B remaining PDU", remaining_len); + } + } + } + } + + mac_pdu_dl.pack(); + + if (drb_activity) { + // Indicate DRB activity in DL to RRC + rrc->set_activity_user(rnti); + logger.debug("DL activity rnti=0x%x", rnti); + } + + if (logger.info.enabled()) { + fmt::memory_buffer str_buffer; + mac_pdu_dl.to_string(str_buffer); + logger.info("0x%x %s", rnti, srsran::to_c_str(str_buffer)); + } + return SRSRAN_SUCCESS; +} + +/******* METRICS interface ***************/ +void ue_nr::metrics_read(mac_ue_metrics_t* metrics_) +{ + uint32_t ul_buffer = 0; // sched->get_ul_buffer(rnti); + uint32_t dl_buffer = 0; // sched->get_dl_buffer(rnti); + + std::lock_guard lock(metrics_mutex); + ue_metrics.rnti = rnti; + ue_metrics.ul_buffer = ul_buffer; + ue_metrics.dl_buffer = dl_buffer; + + // set PCell sector id + // TODO: use ue_cfg when multiple NR carriers are supported + ue_metrics.cc_idx = 0; + + *metrics_ = ue_metrics; + phr_counter = 0; + dl_cqi_valid_counter = 0; + pucch_sinr_counter = 0; + pusch_sinr_counter = 0; + ue_metrics = {}; +} + +void ue_nr::metrics_dl_cqi(const srsran_uci_cfg_nr_t& cfg_, uint32_t dl_cqi) +{ + std::lock_guard lock(metrics_mutex); + + // Process CQI + for (uint32_t i = 0; i < cfg_.nof_csi; i++) { + // Skip if invalid or not supported CSI report + if (cfg_.csi[i].cfg.quantity != SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI or + cfg_.csi[i].cfg.freq_cfg != SRSRAN_CSI_REPORT_FREQ_WIDEBAND) { + continue; + } + + // Add statistics + ue_metrics.dl_cqi = SRSRAN_VEC_SAFE_CMA(dl_cqi, ue_metrics.dl_cqi, dl_cqi_valid_counter); + dl_cqi_valid_counter++; + } +} + +void ue_nr::metrics_rx(bool crc, uint32_t tbs) +{ + std::lock_guard lock(metrics_mutex); + if (crc) { + ue_metrics.rx_brate += tbs * 8; + } else { + ue_metrics.rx_errors++; + } + ue_metrics.rx_pkts++; +} + +void ue_nr::metrics_tx(bool crc, uint32_t tbs) +{ + std::lock_guard lock(metrics_mutex); + if (crc) { + ue_metrics.tx_brate += tbs * 8; + } else { + ue_metrics.tx_errors++; + } + ue_metrics.tx_pkts++; +} + +void ue_nr::metrics_dl_mcs(uint32_t mcs) +{ + std::lock_guard lock(metrics_mutex); + ue_metrics.dl_mcs = SRSRAN_VEC_CMA((float)mcs, ue_metrics.dl_mcs, ue_metrics.dl_mcs_samples); + ue_metrics.dl_mcs_samples++; +} + +void ue_nr::metrics_ul_mcs(uint32_t mcs) +{ + std::lock_guard lock(metrics_mutex); + ue_metrics.ul_mcs = SRSRAN_VEC_CMA((float)mcs, ue_metrics.ul_mcs, ue_metrics.ul_mcs_samples); + ue_metrics.ul_mcs_samples++; +} + +void ue_nr::metrics_cnt() +{ + std::lock_guard lock(metrics_mutex); + ue_metrics.nof_tti++; +} + +void ue_nr::metrics_pucch_sinr(float sinr) +{ + std::lock_guard lock(metrics_mutex); + // discard nan or inf values for average SINR + if (!std::isinf(sinr) && !std::isnan(sinr)) { + ue_metrics.pucch_sinr = SRSRAN_VEC_SAFE_CMA((float)sinr, ue_metrics.pucch_sinr, pucch_sinr_counter); + pucch_sinr_counter++; + } +} + +void ue_nr::metrics_pusch_sinr(float sinr) +{ + std::lock_guard lock(metrics_mutex); + // discard nan or inf values for average SINR + if (!std::isinf(sinr) && !std::isnan(sinr)) { + ue_metrics.pusch_sinr = SRSRAN_VEC_SAFE_CMA((float)sinr, ue_metrics.pusch_sinr, pusch_sinr_counter); + pusch_sinr_counter++; + } +} + +// Called from Stack thread when demuxing UL PDUs +void ue_nr::store_msg3(srsran::unique_byte_buffer_t pdu) +{ + std::lock_guard lock(mutex); + last_msg3 = std::move(pdu); +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/ngap/CMakeLists.txt b/srsgnb/src/stack/ngap/CMakeLists.txt new file mode 100644 index 000000000..548e462af --- /dev/null +++ b/srsgnb/src/stack/ngap/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES ngap.cc ngap_ue.cc ngap_ue_proc.cc ngap_ue_bearer_manager.cc) +add_library(srsgnb_ngap STATIC ${SOURCES}) + +add_subdirectory(test) + diff --git a/srsgnb/src/stack/ngap/ngap.cc b/srsgnb/src/stack/ngap/ngap.cc new file mode 100644 index 000000000..881e8ada2 --- /dev/null +++ b/srsgnb/src/stack/ngap/ngap.cc @@ -0,0 +1,888 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/ngap/ngap.h" +#include "srsgnb/hdr/stack/ngap/ngap_ue.h" +#include "srsran/common/int_helpers.h" + +using srsran::s1ap_mccmnc_to_plmn; +using srsran::uint32_to_uint8; + +#define procError(fmt, ...) ngap_ptr->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procWarning(fmt, ...) ngap_ptr->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define procInfo(fmt, ...) ngap_ptr->logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) + +#define WarnUnsupportFeature(cond, featurename) \ + do { \ + if (cond) { \ + logger.warning("Not handling feature - %s", featurename); \ + } \ + } while (0) + +using namespace asn1::ngap; + +namespace srsenb { +/********************************************************* + * AMF Connection + *********************************************************/ + +srsran::proc_outcome_t ngap::ng_setup_proc_t::init() +{ + procInfo("Starting new AMF connection."); + return start_amf_connection(); +} + +srsran::proc_outcome_t ngap::ng_setup_proc_t::start_amf_connection() +{ + if (not ngap_ptr->running) { + procInfo("NGAP is not running anymore."); + return srsran::proc_outcome_t::error; + } + if (ngap_ptr->amf_connected) { + procInfo("gNB NGAP is already connected to AMF"); + return srsran::proc_outcome_t::success; + } + + if (not ngap_ptr->connect_amf()) { + procInfo("Could not connect to AMF"); + return srsran::proc_outcome_t::error; + } + + if (not ngap_ptr->setup_ng()) { + procError("NG setup failed. Exiting..."); + srsran::console("NG setup failed\n"); + ngap_ptr->running = false; + return srsran::proc_outcome_t::error; + } + + ngap_ptr->ngsetup_timeout.run(); + procInfo("NGSetupRequest sent. Waiting for response..."); + return srsran::proc_outcome_t::yield; +} + +srsran::proc_outcome_t ngap::ng_setup_proc_t::react(const srsenb::ngap::ng_setup_proc_t::ngsetupresult& event) +{ + if (ngap_ptr->ngsetup_timeout.is_running()) { + ngap_ptr->ngsetup_timeout.stop(); + } + if (event.success) { + procInfo("NGSetup procedure completed successfully"); + srsran::console("NG connection successful\n"); + return srsran::proc_outcome_t::success; + } + procError("NGSetup failed."); + srsran::console("NGsetup failed\n"); + return srsran::proc_outcome_t::error; +} + +void ngap::ng_setup_proc_t::then(const srsran::proc_state_t& result) const +{ + if (result.is_error()) { + procInfo("Failed to initiate NG connection. Attempting reconnection in %d seconds", + ngap_ptr->amf_connect_timer.duration() / 1000); + srsran::console("Failed to initiate NG connection. Attempting reconnection in %d seconds\n", + ngap_ptr->amf_connect_timer.duration() / 1000); + ngap_ptr->rx_socket_handler->remove_socket(ngap_ptr->amf_socket.get_socket()); + ngap_ptr->amf_socket.close(); + procInfo("NGAP socket closed."); + ngap_ptr->amf_connect_timer.run(); + // Try again with in 10 seconds + } +} + +/********************************************************* + * NGAP class + *********************************************************/ + +ngap::ngap(srsran::task_sched_handle task_sched_, + srslog::basic_logger& logger, + srsran::socket_manager_itf* rx_socket_handler_) : + ngsetup_proc(this), logger(logger), task_sched(task_sched_), rx_socket_handler(rx_socket_handler_) +{ + amf_task_queue = task_sched.make_task_queue(); +} + +ngap::~ngap() {} + +int ngap::init(const ngap_args_t& args_, rrc_interface_ngap_nr* rrc_, gtpu_interface_rrc* gtpu_) +{ + rrc = rrc_; + args = args_; + gtpu = gtpu_; + + build_tai_cgi(); + + // Setup AMF reconnection timer + amf_connect_timer = task_sched.get_unique_timer(); + auto amf_connect_run = [this](uint32_t tid) { + if (ngsetup_proc.is_busy()) { + logger.error("Failed to initiate NGSetup procedure."); + } + ngsetup_proc.launch(); + }; + amf_connect_timer.set(10000, amf_connect_run); + // Setup NGSetup timeout + ngsetup_timeout = task_sched.get_unique_timer(); + uint32_t ngsetup_timeout_val = 5000; + ngsetup_timeout.set(ngsetup_timeout_val, [this](uint32_t tid) { + ng_setup_proc_t::ngsetupresult res; + res.success = false; + res.cause = ng_setup_proc_t::ngsetupresult::cause_t::timeout; + ngsetup_proc.trigger(res); + }); + + running = true; + // starting AMF connection + if (not ngsetup_proc.launch()) { + logger.error("Failed to initiate NGSetup procedure."); + } + + return SRSRAN_SUCCESS; +} + +void ngap::stop() +{ + running = false; + amf_socket.close(); +} + +void ngap::get_metrics(ngap_metrics_t& m) +{ + if (!running) { + m.status = ngap_error; + return; + } + if (amf_connected) { + m.status = ngap_connected; + } else { + m.status = ngap_attaching; + } +} + +void ngap::get_args(ngap_args_t& args_) +{ + args_ = args; +} + +bool ngap::is_amf_connected() +{ + return amf_connected; +} + +// Generate common NGAP protocol IEs from config args +int ngap::build_tai_cgi() +{ + uint32_t plmn; + uint8_t shift; + + // TAI + srsran::s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tai.plmn_id.from_number(plmn); + tai.tac.from_number(args.tac); + + // NR CGI + nr_cgi.plmn_id.from_number(plmn); + + // NR CELL ID (36 bits) = gnb_id (22...32 bits) + cell_id (4...14 bits) + if (((uint8_t)log2(args.gnb_id) + (uint8_t)log2(args.cell_id) + 2) > 36) { + logger.error("gNB ID and Cell ID combination greater than 36 bits"); + return SRSRAN_ERROR; + } + // Consider moving sanity checks into the parsing function of the configs. + if (((uint8_t)log2(args.gnb_id) + 1) < 22) { + shift = 14; + } else { + shift = 36 - ((uint8_t)log2(args.gnb_id) + 1); + } + + nr_cgi.nrcell_id.from_number((uint64_t)args.gnb_id << shift | args.cell_id); + return SRSRAN_SUCCESS; +} + +/******************************************************************************* +/* RRC interface +********************************************************************************/ +void ngap::initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu) +{ + std::unique_ptr ue_ptr{new ue{this, rrc, gtpu, logger}}; + ue_ptr->ctxt.rnti = rnti; + ue_ptr->ctxt.gnb_cc_idx = gnb_cc_idx; + ue* u = users.add_user(std::move(ue_ptr)); + if (u == nullptr) { + logger.error("Failed to add rnti=0x%x", rnti); + return; + } + u->send_initial_ue_message(cause, pdu, false); +} + +void ngap::initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu, + uint32_t s_tmsi) +{ + std::unique_ptr ue_ptr{new ue{this, rrc, gtpu, logger}}; + ue_ptr->ctxt.rnti = rnti; + ue_ptr->ctxt.gnb_cc_idx = gnb_cc_idx; + ue* u = users.add_user(std::move(ue_ptr)); + if (u == nullptr) { + logger.error("Failed to add rnti=0x%x", rnti); + return; + } + u->send_initial_ue_message(cause, pdu, true, s_tmsi); +} + +void ngap::ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) +{ + ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + return; + } + u->notify_rrc_reconf_complete(outcome); +} + +void ngap::write_pdu(uint16_t rnti, srsran::const_byte_span pdu) +{ + logger.info(pdu.data(), pdu.size(), "Received RRC SDU"); + + ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + logger.info("The rnti=0x%x does not exist", rnti); + return; + } + u->send_ul_nas_transport(pdu); +} + +void ngap::user_release_request(uint16_t rnti, asn1::ngap::cause_radio_network_e cause_radio) +{ + ue* u = users.find_ue_rnti(rnti); + if (u == nullptr) { + logger.warning("Released UE rnti=0x%x not found.", rnti); + return; + } + + cause_c cause; + cause.set_radio_network().value = cause_radio; + if (not u->send_ue_context_release_request(cause)) { + logger.error("Failed to initiate RRC Release for rnti=0x%x. Removing user", rnti); + rrc->release_user(rnti); + users.erase(u); + } +} + +/********************************************************* + * ngap::user_list class + *********************************************************/ + +ngap::ue* ngap::user_list::find_ue_rnti(uint16_t rnti) +{ + if (rnti == SRSRAN_INVALID_RNTI) { + return nullptr; + } + auto it = std::find_if( + users.begin(), users.end(), [rnti](const user_list::pair_type& v) { return v.second->ctxt.rnti == rnti; }); + return it != users.end() ? it->second.get() : nullptr; +} + +ngap::ue* ngap::user_list::find_ue_gnbid(uint32_t gnbid) +{ + auto it = users.find(gnbid); + return (it != users.end()) ? it->second.get() : nullptr; +} + +ngap::ue* ngap::user_list::find_ue_amfid(uint64_t amfid) +{ + auto it = std::find_if(users.begin(), users.end(), [amfid](const user_list::pair_type& v) { + return v.second->ctxt.amf_ue_ngap_id == amfid; + }); + return it != users.end() ? it->second.get() : nullptr; +} + +ngap::ue* ngap::user_list::add_user(std::unique_ptr user) +{ + static srslog::basic_logger& logger = srslog::fetch_basic_logger("NGAP"); + // Check for ID repetitions + if (find_ue_rnti(user->ctxt.rnti) != nullptr) { + logger.error("The user to be added with rnti=0x%x already exists", user->ctxt.rnti); + return nullptr; + } + if (find_ue_gnbid(user->ctxt.ran_ue_ngap_id) != nullptr) { + logger.error("The user to be added with ran ue ngap id=%d already exists", user->ctxt.ran_ue_ngap_id); + return nullptr; + } + if (user->ctxt.amf_ue_ngap_id.has_value() and find_ue_amfid(user->ctxt.amf_ue_ngap_id.value()) != nullptr) { + logger.error("The user to be added with amf id=%d already exists", user->ctxt.amf_ue_ngap_id.value()); + return nullptr; + } + auto p = users.insert(std::make_pair(user->ctxt.ran_ue_ngap_id, std::move(user))); + return p.second ? p.first->second.get() : nullptr; +} + +void ngap::user_list::erase(ue* ue_ptr) +{ + static srslog::basic_logger& logger = srslog::fetch_basic_logger("NGAP"); + auto it = users.find(ue_ptr->ctxt.ran_ue_ngap_id); + if (it == users.end()) { + logger.error("User to be erased does not exist"); + return; + } + users.erase(it); +} + +/******************************************************************************* +/* NGAP message handlers +********************************************************************************/ +bool ngap::handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, + const sockaddr_in& from, + const sctp_sndrcvinfo& sri, + int flags) +{ + // Handle Notification Case + if (flags & MSG_NOTIFICATION) { + // Received notification + union sctp_notification* notification = (union sctp_notification*)pdu->msg; + logger.debug("SCTP Notification %d", notification->sn_header.sn_type); + if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { + logger.info("SCTP Association Shutdown. Association: %d", sri.sinfo_assoc_id); + srsran::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id); + rx_socket_handler->remove_socket(amf_socket.get_socket()); + amf_socket.close(); + } else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE && + notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) { + logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id); + srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id); + rx_socket_handler->remove_socket(amf_socket.get_socket()); + amf_socket.close(); + } + } else if (pdu->N_bytes == 0) { + logger.error("SCTP return 0 bytes. Closing socket"); + amf_socket.close(); + } + + // Restart AMF connection procedure if we lost connection + if (not amf_socket.is_open()) { + amf_connected = false; + if (ngsetup_proc.is_busy()) { + logger.error("Failed to initiate AMF connection procedure, as it is already running."); + return false; + } + ngsetup_proc.launch(); + return false; + } + + handle_ngap_rx_pdu(pdu.get()); + return true; +} + +bool ngap::handle_ngap_rx_pdu(srsran::byte_buffer_t* pdu) +{ + // Save message to PCAP + if (pcap != nullptr) { + pcap->write_ngap(pdu->msg, pdu->N_bytes); + } + + // Unpack + ngap_pdu_c rx_pdu; + asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); + + if (rx_pdu.unpack(bref) != asn1::SRSASN_SUCCESS) { + logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack received PDU"); + cause_c cause; + cause.set_protocol().value = cause_protocol_opts::transfer_syntax_error; + send_error_indication(cause); + return false; + } + + // Logging + log_ngap_message(rx_pdu, Rx, srsran::make_span(*pdu)); + + // Handle the NGAP message + switch (rx_pdu.type().value) { + case ngap_pdu_c::types_opts::init_msg: + return handle_initiating_message(rx_pdu.init_msg()); + case ngap_pdu_c::types_opts::successful_outcome: + return handle_successful_outcome(rx_pdu.successful_outcome()); + case ngap_pdu_c::types_opts::unsuccessful_outcome: + return handle_unsuccessful_outcome(rx_pdu.unsuccessful_outcome()); + default: + logger.warning("Unhandled PDU type %d", rx_pdu.type().value); + return false; + } + + return true; +} + +bool ngap::handle_initiating_message(const asn1::ngap::init_msg_s& msg) +{ + switch (msg.value.type().value) { + case ngap_elem_procs_o::init_msg_c::types_opts::dl_nas_transport: + return handle_dl_nas_transport(msg.value.dl_nas_transport()); + case ngap_elem_procs_o::init_msg_c::types_opts::init_context_setup_request: + return handle_initial_ctxt_setup_request(msg.value.init_context_setup_request()); + case ngap_elem_procs_o::init_msg_c::types_opts::ue_context_release_cmd: + return handle_ue_context_release_cmd(msg.value.ue_context_release_cmd()); + case ngap_elem_procs_o::init_msg_c::types_opts::pdu_session_res_setup_request: + return handle_ue_pdu_session_res_setup_request(msg.value.pdu_session_res_setup_request()); + case ngap_elem_procs_o::init_msg_c::types_opts::paging: + return handle_paging(msg.value.paging()); + default: + logger.warning("Unhandled initiating message: %s", msg.value.type().to_string()); + } + return true; +} + +bool ngap::handle_successful_outcome(const successful_outcome_s& msg) +{ + switch (msg.value.type().value) { + case ngap_elem_procs_o::successful_outcome_c::types_opts::ng_setup_resp: + return handle_ng_setup_response(msg.value.ng_setup_resp()); + default: + logger.error("Unhandled successful outcome message: %s", msg.value.type().to_string()); + } + return true; +} + +bool ngap::handle_unsuccessful_outcome(const asn1::ngap::unsuccessful_outcome_s& msg) +{ + switch (msg.value.type().value) { + case ngap_elem_procs_o::unsuccessful_outcome_c::types_opts::ng_setup_fail: + return handle_ng_setup_failure(msg.value.ng_setup_fail()); + default: + logger.error("Unhandled unsuccessful outcome message: %s", msg.value.type().to_string()); + } + return true; +} + +bool ngap::handle_ng_setup_response(const asn1::ngap::ng_setup_resp_s& msg) +{ + ngsetupresponse = msg; + amf_connected = true; + ng_setup_proc_t::ngsetupresult res; + res.success = true; + logger.info("AMF name: %s", ngsetupresponse->amf_name.value.to_string()); + ngsetup_proc.trigger(res); + + return true; +} + +bool ngap::handle_ng_setup_failure(const asn1::ngap::ng_setup_fail_s& msg) +{ + std::string cause = get_cause(msg->cause.value); + logger.error("NG Setup Failure. Cause: %s", cause.c_str()); + srsran::console("NG Setup Failure. Cause: %s\n", cause.c_str()); + return true; +} + +bool ngap::handle_dl_nas_transport(const asn1::ngap::dl_nas_transport_s& msg) +{ + if (msg.ext) { + logger.warning("Not handling NGAP message extension"); + } + ue* u = handle_ngapmsg_ue_id(msg->ran_ue_ngap_id.value.value, msg->amf_ue_ngap_id.value.value); + + if (u == nullptr) { + logger.warning("Couldn't find user with ran_ue_ngap_id %d and %d", + msg->ran_ue_ngap_id.value.value, + msg->amf_ue_ngap_id.value.value); + return false; + } + + if (msg->old_amf_present) { + logger.warning("Not handling OldAMF"); + } + + if (msg->ran_paging_prio_present) { + logger.warning("Not handling RANPagingPriority"); + } + + if (msg->mob_restrict_list_present) { + logger.warning("Not handling MobilityRestrictionList"); + } + + if (msg->idx_to_rfsp_present) { + logger.warning("Not handling IndexToRFSP"); + } + + if (msg->ue_aggregate_maximum_bit_rate_present) { + logger.warning("Not handling UEAggregateMaximumBitRate"); + } + + if (msg->allowed_nssai_present) { + logger.warning("Not handling AllowedNSSAI"); + } + + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Fatal Error: Couldn't allocate buffer in ngap::run_thread()."); + return false; + } + memcpy(pdu->msg, msg->nas_pdu.value.data(), msg->nas_pdu.value.size()); + pdu->N_bytes = msg->nas_pdu.value.size(); + rrc->write_dl_info(u->ctxt.rnti, std::move(pdu)); + return true; +} + +bool ngap::handle_initial_ctxt_setup_request(const asn1::ngap::init_context_setup_request_s& msg) +{ + ue* u = handle_ngapmsg_ue_id(msg->ran_ue_ngap_id.value.value, msg->amf_ue_ngap_id.value.value); + if (u == nullptr) { + logger.warning("Can not find UE"); + return false; + } + + u->handle_initial_ctxt_setup_request(msg); + + return true; +} + +bool ngap::handle_ue_context_release_cmd(const asn1::ngap::ue_context_release_cmd_s& msg) +{ + const asn1::ngap::ue_ngap_id_pair_s& ue_ngap_id_pair = msg->ue_ngap_ids.value.ue_ngap_id_pair(); + + ue* u = handle_ngapmsg_ue_id(ue_ngap_id_pair.ran_ue_ngap_id, ue_ngap_id_pair.amf_ue_ngap_id); + if (u == nullptr) { + logger.warning("Can not find UE"); + return false; + } + + return u->handle_ue_context_release_cmd(msg); +} + +bool ngap::handle_ue_pdu_session_res_setup_request(const asn1::ngap::pdu_session_res_setup_request_s& msg) +{ + ue* u = handle_ngapmsg_ue_id(msg->ran_ue_ngap_id.value.value, msg->amf_ue_ngap_id.value.value); + if (u == nullptr) { + logger.warning("Can not find UE"); + return false; + } + + if (msg->ue_aggregate_maximum_bit_rate_present) { + rrc->set_aggregate_max_bitrate(u->ctxt.rnti, msg->ue_aggregate_maximum_bit_rate.value); + } + u->handle_pdu_session_res_setup_request(msg); + + return true; +} + +bool ngap::handle_paging(const asn1::ngap::paging_s& msg) +{ + logger.info("Paging is not supported yet."); + + // TODO: Handle Paging after RRC Paging is implemented + + // uint32_t ue_paging_id = msg->ue_paging_id.id; + // Note: IMSI Paging is not supported in NR + // uint64_t tmsi = msg->ue_paging_id.value.five_g_s_tmsi().five_g_tmsi.to_number(); + // rrc->add_paging(ue_paging_id, tmsi); + + return true; +} + +/******************************************************************************* +/* NGAP message senders +********************************************************************************/ + +bool ngap::send_error_indication(const asn1::ngap::cause_c& cause, + srsran::optional ran_ue_ngap_id, + srsran::optional amf_ue_ngap_id) +{ + if (amf_connected == false) { + logger.warning("AMF not connected."); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_ID_ERROR_IND); + auto& container = tx_pdu.init_msg().value.error_ind(); + + uint16_t rnti = SRSRAN_INVALID_RNTI; + container->ran_ue_ngap_id_present = ran_ue_ngap_id.has_value(); + if (ran_ue_ngap_id.has_value()) { + container->ran_ue_ngap_id.value = ran_ue_ngap_id.value(); + ue* user_ptr = users.find_ue_gnbid(ran_ue_ngap_id.value()); + rnti = user_ptr != nullptr ? user_ptr->ctxt.rnti : SRSRAN_INVALID_RNTI; + } + container->amf_ue_ngap_id_present = amf_ue_ngap_id.has_value(); + if (amf_ue_ngap_id.has_value()) { + container->amf_ue_ngap_id.value = amf_ue_ngap_id.value(); + } + + container->cause_present = true; + container->cause.value = cause; + + return sctp_send_ngap_pdu(tx_pdu, rnti, "Error Indication"); +} +/******************************************************************************* +/* NGAP connection helpers +********************************************************************************/ + +bool ngap::connect_amf() +{ + using namespace srsran::net_utils; + logger.info("Connecting to AMF %s:%d", args.amf_addr.c_str(), int(AMF_PORT)); + + // Open SCTP socket + if (not amf_socket.open_socket( + srsran::net_utils::addr_family::ipv4, socket_type::seqpacket, srsran::net_utils::protocol_type::SCTP)) { + return false; + } + logger.info("SCTP socket opened. fd=%d", amf_socket.fd()); + + if (not amf_socket.sctp_subscribe_to_events()) { + amf_socket.close(); + return false; + } + + if (not amf_socket.sctp_set_rto_opts(5000)) { + amf_socket.close(); + return false; + } + + if (not amf_socket.sctp_set_init_msg_opts(3, 6000)) { + amf_socket.close(); + return false; + } + + // Bind socket + if (not amf_socket.bind_addr(args.ngc_bind_addr.c_str(), 0)) { + amf_socket.close(); + return false; + } + + // Connect to the AMF address + if (not amf_socket.connect_to(args.amf_addr.c_str(), AMF_PORT, &amf_addr)) { + return false; + } + logger.info("SCTP socket connected with AMF. fd=%d", amf_socket.fd()); + + // Assign a handler to rx AMF packets + auto rx_callback = + [this](srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags) { + // Defer the handling of AMF packet to eNB stack main thread + handle_amf_rx_msg(std::move(pdu), from, sri, flags); + }; + rx_socket_handler->add_socket_handler(amf_socket.fd(), + srsran::make_sctp_sdu_handler(logger, amf_task_queue, rx_callback)); + + logger.info("SCTP socket established with AMF"); + return true; +} + +bool ngap::setup_ng() +{ + uint32_t tmp32; + uint16_t tmp16; + + uint32_t plmn; + srsran::s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + plmn = htonl(plmn); + + ngap_pdu_c pdu; + pdu.set_init_msg().load_info_obj(ASN1_NGAP_ID_NG_SETUP); + ng_setup_request_s& container = pdu.init_msg().value.ng_setup_request(); + global_gnb_id_s& global_gnb_id = container->global_ran_node_id.value.set_global_gnb_id(); + global_gnb_id.plmn_id = tai.plmn_id; + + global_gnb_id.gnb_id.set_gnb_id().from_number(args.gnb_id); + + container->ran_node_name_present = true; + if (args.gnb_name.length() >= 150) { + args.gnb_name.resize(150); + } + + container->ran_node_name.value.resize(args.gnb_name.size()); + memcpy(&container->ran_node_name.value[0], &args.gnb_name[0], args.gnb_name.size()); + + container->supported_ta_list.value.resize(1); + container->supported_ta_list.value[0].tac = tai.tac; + container->supported_ta_list.value[0].broadcast_plmn_list.resize(1); + container->supported_ta_list.value[0].broadcast_plmn_list[0].plmn_id = tai.plmn_id; + container->supported_ta_list.value[0].broadcast_plmn_list[0].tai_slice_support_list.resize(1); + container->supported_ta_list.value[0].broadcast_plmn_list[0].tai_slice_support_list[0].s_nssai.sst.from_number(1); + + container->default_paging_drx.value.value = asn1::ngap::paging_drx_opts::v256; // Todo: add to args, config file + + return sctp_send_ngap_pdu(pdu, 0, "ngSetupRequest"); +} + +/******************************************************************************* +/* General helpers +********************************************************************************/ + +bool ngap::sctp_send_ngap_pdu(const asn1::ngap::ngap_pdu_c& tx_pdu, uint32_t rnti, const char* procedure_name) +{ + if (not amf_connected and rnti != SRSRAN_INVALID_RNTI) { + logger.error("Aborting %s for rnti=0x%x. Cause: AMF is not connected.", procedure_name, rnti); + return false; + } + + srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer(); + if (buf == nullptr) { + logger.error("Fatal Error: Couldn't allocate buffer for %s.", procedure_name); + return false; + } + asn1::bit_ref bref(buf->msg, buf->get_tailroom()); + if (tx_pdu.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack TX PDU %s", procedure_name); + return false; + } + buf->N_bytes = bref.distance_bytes(); + + // Save message to PCAP + if (pcap != nullptr) { + pcap->write_ngap(buf->msg, buf->N_bytes); + } + + if (rnti != SRSRAN_INVALID_RNTI) { + logger.info(buf->msg, buf->N_bytes, "Tx NGAP SDU, %s, rnti=0x%x", procedure_name, rnti); + } else { + logger.info(buf->msg, buf->N_bytes, "Tx NGAP SDU, %s", procedure_name); + } + + uint16_t streamid = rnti == SRSRAN_INVALID_RNTI ? NONUE_STREAM_ID : users.find_ue_rnti(rnti)->stream_id; + + ssize_t n_sent = sctp_sendmsg(amf_socket.fd(), + buf->msg, + buf->N_bytes, + (struct sockaddr*)&amf_addr, + sizeof(struct sockaddr_in), + htonl(PPID), + 0, + streamid, + 0, + 0); + if (n_sent == -1) { + if (rnti != SRSRAN_INVALID_RNTI) { + logger.error("Error: Failure at Tx NGAP SDU, %s, rnti=0x%x", procedure_name, rnti); + } else { + logger.error("Error: Failure at Tx NGAP SDU, %s", procedure_name); + } + return false; + } + return true; +} + +/** + * Helper method to find user based on the ran_ue_ngap_id stored in an S1AP Msg, and update amf_ue_ngap_id + * @param gnb_id ran_ue_ngap_id value stored in NGAP message + * @param amf_id amf_ue_ngap_id value stored in NGAP message + * @return pointer to user if it has been found + */ +ngap::ue* ngap::handle_ngapmsg_ue_id(uint32_t gnb_id, uint64_t amf_id) +{ + ue* user_ptr = users.find_ue_gnbid(gnb_id); + ue* user_amf_ptr = nullptr; + cause_c cause; + // TODO: Introduce proper error handling for faulty ids + if (user_ptr != nullptr) { + if (user_ptr->ctxt.amf_ue_ngap_id == amf_id) { + return user_ptr; + } + + user_amf_ptr = users.find_ue_amfid(amf_id); + if (not user_ptr->ctxt.amf_ue_ngap_id.has_value() and user_amf_ptr == nullptr) { + user_ptr->ctxt.amf_ue_ngap_id = amf_id; + return user_ptr; + } + + logger.warning("AMF UE NGAP ID=%d not found - discarding message", amf_id); + if (user_amf_ptr != nullptr) { + cause.set_radio_network().value = cause_radio_network_opts::unknown_target_id; + } + + } else { + logger.warning("User associated with gNB ID %d not found", gnb_id); + user_amf_ptr = users.find_ue_amfid(amf_id); + logger.warning("RAN UE NGAP ID=%d not found - discarding message", gnb_id); + if (user_amf_ptr != nullptr) { + cause.set_radio_network().value = cause_radio_network_opts::unknown_local_ue_ngap_id; + } + } + + send_error_indication(cause, gnb_id, amf_id); + + return nullptr; +} + +std::string ngap::get_cause(const cause_c& c) +{ + std::string cause = c.type().to_string(); + cause += " - "; + switch (c.type().value) { + case cause_c::types_opts::radio_network: + cause += c.radio_network().to_string(); + break; + case cause_c::types_opts::transport: + cause += c.transport().to_string(); + break; + case cause_c::types_opts::nas: + cause += c.nas().to_string(); + break; + case cause_c::types_opts::protocol: + cause += c.protocol().to_string(); + break; + case cause_c::types_opts::misc: + cause += c.misc().to_string(); + break; + default: + cause += "unknown"; + break; + } + return cause; +} + +/* + * PCAP + */ +void ngap::start_pcap(srsran::ngap_pcap* pcap_) +{ + pcap = pcap_; +} + +void ngap::log_ngap_message(const ngap_pdu_c& msg, const direction_t dir, srsran::const_byte_span pdu) +{ + std::string msg_type = {}; + switch (msg.type().value) { + case ngap_pdu_c::types_opts::init_msg: + msg_type = msg.init_msg().value.type().to_string(); + break; + case ngap_pdu_c::types_opts::successful_outcome: + msg_type = msg.successful_outcome().value.type().to_string(); + break; + case ngap_pdu_c::types_opts::unsuccessful_outcome: + msg_type = msg.unsuccessful_outcome().value.type().to_string(); + break; + default: + return; + } + if (logger.debug.enabled()) { + asn1::json_writer json_writer; + msg.to_json(json_writer); + logger.debug(pdu.data(), pdu.size(), "%s - %s (%d B)", (dir == Rx) ? "Rx" : "Tx", msg_type, pdu.size()); + logger.debug("Content:%s", json_writer.to_string().c_str()); + } else if (logger.info.enabled()) { + logger.info(pdu.data(), pdu.size(), "%s - %s (%d B)", (dir == Rx) ? "Rx" : "Tx", msg_type, pdu.size()); + } +} +} // namespace srsenb diff --git a/srsgnb/src/stack/ngap/ngap_ue.cc b/srsgnb/src/stack/ngap/ngap_ue.cc new file mode 100644 index 000000000..49166eed4 --- /dev/null +++ b/srsgnb/src/stack/ngap/ngap_ue.cc @@ -0,0 +1,330 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/ngap/ngap_ue.h" +#include "srsgnb/hdr/stack/ngap/ngap.h" +#include "srsgnb/hdr/stack/ngap/ngap_ue_proc.h" +#include "srsran/common/int_helpers.h" + +using namespace asn1::ngap; + +namespace srsenb { + +/******************************************************************************* +/* ngap_ptr::ue Class +********************************************************************************/ + +ngap::ue::ue(ngap* ngap_ptr_, + rrc_interface_ngap_nr* rrc_ptr_, + gtpu_interface_rrc* gtpu_ptr_, + srslog::basic_logger& logger_) : + logger(logger_), + ngap_ptr(ngap_ptr_), + bearer_manager(gtpu_ptr_, logger_), + initial_context_setup_proc(this, rrc_ptr_, &ctxt, logger_), + ue_context_release_proc(this, rrc_ptr_, &ctxt, &bearer_manager, logger_), + ue_pdu_session_res_setup_proc(this, rrc_ptr_, &ctxt, &bearer_manager, logger_) +{ + ctxt.ran_ue_ngap_id = ngap_ptr->next_gnb_ue_ngap_id++; + gettimeofday(&ctxt.init_timestamp, nullptr); + stream_id = ngap_ptr->next_ue_stream_id; +} + +ngap::ue::~ue() {} + +/******************************************************************************* +/* NGAP message senders +********************************************************************************/ + +bool ngap::ue::send_initial_ue_message(asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu, + bool has_tmsi, + uint32_t s_tmsi) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_ID_INIT_UE_MSG); + init_ue_msg_s& container = tx_pdu.init_msg().value.init_ue_msg(); + + // 5G-S-TMSI + if (has_tmsi) { + container->five_g_s_tmsi_present = true; + srsran::uint32_to_uint8(s_tmsi, container->five_g_s_tmsi.value.five_g_tmsi.data()); + container->five_g_s_tmsi.value.amf_set_id.from_number(ctxt.amf_set_id); + container->five_g_s_tmsi.value.amf_pointer.from_number(ctxt.amf_pointer); + } + + // RAN_UE_NGAP_ID + container->ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + + // NAS_PDU + container->nas_pdu.value.resize(pdu.size()); + memcpy(container->nas_pdu.value.data(), pdu.data(), pdu.size()); + + // RRC Establishment Cause + container->rrcestablishment_cause.value = cause; + + // User Location Info + + // userLocationInformationNR + container->user_location_info.value.set_user_location_info_nr(); + container->user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id; + container->user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id; + container->user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id; + container->user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac; + + // UE context request for setup in the NAS registration request + container->ue_context_request_present = true; + container->ue_context_request.value = asn1::ngap::ue_context_request_opts::options::requested; + + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "InitialUEMessage"); +} + +bool ngap::ue::send_ul_nas_transport(srsran::const_byte_span pdu) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_ID_UL_NAS_TRANSPORT); + ul_nas_transport_s& container = tx_pdu.init_msg().value.ul_nas_transport(); + + // AMF UE NGAP ID + if (ctxt.amf_ue_ngap_id.has_value()) { + container->amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + } else { + logger.error("Attempting to send UL NAS Transport without AMF context"); + return false; + } + + // RAN UE NGAP ID + container->ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + + // NAS PDU + container->nas_pdu.value.resize(pdu.size()); + memcpy(container->nas_pdu.value.data(), pdu.data(), pdu.size()); + + // User Location Info + // userLocationInformationNR + container->user_location_info.value.set_user_location_info_nr(); + container->user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id; + container->user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id; + container->user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id; + container->user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac; + + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UplinkNASTransport"); +} + +void ngap::ue::notify_rrc_reconf_complete(const bool outcome) +{ + initial_context_setup_proc.trigger(outcome); +} + +bool ngap::ue::send_initial_ctxt_setup_response() +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_successful_outcome().load_info_obj(ASN1_NGAP_ID_INIT_CONTEXT_SETUP); + init_context_setup_resp_s& container = tx_pdu.successful_outcome().value.init_context_setup_resp(); + + // AMF UE NGAP ID + container->amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + // RAN UE NGAP ID + container->ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "InitialContextSetupResponse"); +} + +bool ngap::ue::send_initial_ctxt_setup_failure(cause_c cause) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_ID_INIT_CONTEXT_SETUP); + init_context_setup_fail_s& container = tx_pdu.unsuccessful_outcome().value.init_context_setup_fail(); + + // AMF UE NGAP ID + container->amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + // RAN UE NGAP ID + container->ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + + /* // TODO: PDU Session Resource Setup Response List - Integrate PDU Session and Bearer management into NGAP + container->pdu_session_res_setup_list_cxt_res_present = true; + + // Case PDU Session Resource Failed to Setup List + container->pdu_session_res_failed_to_setup_list_cxt_res_present = true; */ + + return true; +} + +bool ngap::ue::send_pdu_session_resource_setup_response(uint16_t pdu_session_id, + uint32_t teid_in, + asn1::bounded_bitstring<1, 160, true, true> addr_in) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + // TODO: QOS Params + ngap_pdu_c tx_pdu; + tx_pdu.set_successful_outcome().load_info_obj(ASN1_NGAP_ID_PDU_SESSION_RES_SETUP); + pdu_session_res_setup_resp_s& container = tx_pdu.successful_outcome().value.pdu_session_res_setup_resp(); + container->amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + container->ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + container->pdu_session_res_setup_list_su_res_present = true; + pdu_session_res_setup_item_su_res_s su_res; + su_res.pdu_session_res_setup_resp_transfer.resize(512); + su_res.pdu_session_id = pdu_session_id; + pdu_session_res_setup_resp_transfer_s resp_transfer; + + gtp_tunnel_s& gtp_tunnel = resp_transfer.dlqos_flow_per_tnl_info.uptransport_layer_info.set_gtp_tunnel(); + + gtp_tunnel.gtp_teid.from_number(teid_in); + gtp_tunnel.transport_layer_address = addr_in; + asn1::ngap::associated_qos_flow_list_l qos_flow_list; + asn1::ngap::associated_qos_flow_item_s qos_flow_item; + qos_flow_item.qos_flow_id = 1; + qos_flow_list.push_back(qos_flow_item); + resp_transfer.dlqos_flow_per_tnl_info.associated_qos_flow_list = qos_flow_list; + + asn1::bit_ref bref(su_res.pdu_session_res_setup_resp_transfer.data(), + su_res.pdu_session_res_setup_resp_transfer.size()); + resp_transfer.pack(bref); + su_res.pdu_session_res_setup_resp_transfer.resize(bref.distance_bytes()); + + container->pdu_session_res_setup_list_su_res.value.push_back(su_res); + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "PDUSessionResourceSetupResponse"); +} + +bool ngap::ue::send_ue_ctxt_release_complete() +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_successful_outcome().load_info_obj(ASN1_NGAP_ID_UE_CONTEXT_RELEASE); + ue_context_release_complete_s& container = tx_pdu.successful_outcome().value.ue_context_release_complete(); + + // userLocationInformationNR + container->user_location_info.value.set_user_location_info_nr(); + container->user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id; + container->user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id; + container->user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id; + container->user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac; + + container->ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + container->amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete"); +} + +bool ngap::ue::send_ue_context_release_request(asn1::ngap::cause_c cause) +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + if (not ctxt.amf_ue_ngap_id.has_value()) { + logger.warning("Can't send release request. User 0x%x has no AMF UE Id.", ctxt.rnti); + return false; + } + + if (was_ue_context_release_requested()) { + // let timeout auto-remove user. + return false; + } + release_requested = true; + + ngap_pdu_c tx_pdu; + tx_pdu.set_init_msg().load_info_obj(ASN1_NGAP_ID_UE_CONTEXT_RELEASE_REQUEST); + ue_context_release_request_s& container = tx_pdu.init_msg().value.ue_context_release_request(); + + container->cause.value = cause; + + // PDU Session Resource List + auto& session_lst = container->pdu_session_res_list_cxt_rel_req.value; + for (const auto& pdu_pair : bearer_manager.pdu_sessions()) { + const ngap_ue_bearer_manager::pdu_session_t& session = pdu_pair.second; + + pdu_session_res_item_cxt_rel_req_s obj; + obj.pdu_session_id = session.id; + session_lst.push_back(obj); + } + container->pdu_session_res_list_cxt_rel_req_present = session_lst.size() > 0; + + container->ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + container->amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + // TODO: Implement timeout + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseRequest"); +} + +/******************************************************************************* +/* NGAP message handler +********************************************************************************/ + +bool ngap::ue::handle_initial_ctxt_setup_request(const asn1::ngap::init_context_setup_request_s& msg) +{ + if (not initial_context_setup_proc.launch(msg)) { + logger.error("Failed to start Initial Context Setup Procedure"); + return false; + } + return true; +} + +bool ngap::ue::handle_ue_context_release_cmd(const asn1::ngap::ue_context_release_cmd_s& msg) +{ + // TODO: Release UE context + if (not ue_context_release_proc.launch(msg)) { + logger.error("Failed to start UE Context Release Procedure"); + return false; + } + return true; +} + +bool ngap::ue::handle_pdu_session_res_setup_request(const asn1::ngap::pdu_session_res_setup_request_s& msg) +{ + if (not ue_pdu_session_res_setup_proc.launch(msg)) { + logger.error("Failed to start UE PDU Session Resource Setup Procedure"); + return false; + } + return true; +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/ngap/ngap_ue_bearer_manager.cc b/srsgnb/src/stack/ngap/ngap_ue_bearer_manager.cc new file mode 100644 index 000000000..822992e07 --- /dev/null +++ b/srsgnb/src/stack/ngap/ngap_ue_bearer_manager.cc @@ -0,0 +1,133 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h" + +namespace srsenb { +ngap_ue_bearer_manager::ngap_ue_bearer_manager(gtpu_interface_rrc* gtpu_, srslog::basic_logger& logger_) : + gtpu(gtpu_), logger(logger_) +{} +ngap_ue_bearer_manager::~ngap_ue_bearer_manager(){}; + +int ngap_ue_bearer_manager::add_pdu_session(uint16_t rnti, + uint8_t pdu_session_id, + const asn1::ngap::qos_flow_level_qos_params_s& qos, + const asn1::bounded_bitstring<1, 160, true, true>& addr_out, + uint32_t teid_out, + uint16_t& lcid, + asn1::bounded_bitstring<1, 160, true, true>& addr_in, + uint32_t& teid_in, + asn1::ngap::cause_c& cause) +{ + // Only add session if gtpu was successful + pdu_session_t::gtpu_tunnel tunnel; + + if (addr_out.length() > 32) { + logger.error("Only addresses with length <= 32 (IPv4) are supported"); + cause.set_radio_network().value = asn1::ngap::cause_radio_network_opts::invalid_qos_combination; + return SRSRAN_ERROR; + } + + lcid = allocate_lcid(rnti); + + // TODO: remove lcid and just use pdu_session_id and rnti as id for GTP tunnel + int rtn = add_gtpu_bearer(rnti, pdu_session_id, teid_out, addr_out, tunnel); + if (rtn != SRSRAN_SUCCESS) { + logger.error("Adding PDU Session ID=%d to GTPU", pdu_session_id); + return SRSRAN_ERROR; + } + + pdu_session_list[pdu_session_id].id = pdu_session_id; + pdu_session_list[pdu_session_id].lcid = lcid; + pdu_session_list[pdu_session_id].qos_params = qos; + pdu_session_list[pdu_session_id].tunnels.push_back(tunnel); + + // return values + teid_in = tunnel.teid_in; + addr_in = tunnel.address_in; + + return SRSRAN_SUCCESS; +} + +int ngap_ue_bearer_manager::reset_pdu_sessions(uint16_t rnti) +{ + for (auto iter = pdu_session_list.begin(); iter != pdu_session_list.end(); iter++) { + auto pdu_session_id = iter->first; + rem_gtpu_bearer(pdu_session_id, rnti); + } + next_lcid_list.erase(rnti); + return true; +} + +int ngap_ue_bearer_manager::add_gtpu_bearer(uint16_t rnti, + uint32_t pdu_session_id, + uint32_t teid_out, + asn1::bounded_bitstring<1, 160, true, true> address_out, + pdu_session_t::gtpu_tunnel& tunnel, + const gtpu_interface_rrc::bearer_props* props) +{ + // Initialize ERAB tunnel in GTPU right-away. DRBs are only created during RRC setup/reconf + uint32_t addr_in; + srsran::expected rtn = + gtpu->add_bearer(rnti, pdu_session_id, address_out.to_number(), teid_out, addr_in, props); + if (rtn.is_error()) { + logger.error("Failed adding pdu_session_id=%d to GTPU", pdu_session_id); + return SRSRAN_ERROR; + } + + tunnel.teid_out = teid_out; + tunnel.address_out = address_out; + + logger.info("Addr in %x", addr_in); + + tunnel.address_in.from_number(addr_in, 32); + tunnel.teid_in = rtn.value(); + + logger.info("Added GTPU tunnel rnti 0x%04x, pdu_session_id=%d, teid_out %d, teid_in %d, address out 0x%x, " + "address in 0x%x", + rnti, + pdu_session_id, + tunnel.teid_out, + tunnel.teid_in, + tunnel.address_out.to_number(), + tunnel.address_in.to_number()); + return SRSRAN_SUCCESS; +} + +void ngap_ue_bearer_manager::rem_gtpu_bearer(uint16_t rnti, uint32_t pdu_session_id) +{ + auto it = pdu_session_list.find(pdu_session_id); + if (it == pdu_session_list.end()) { + logger.warning("Removing pdu_session=%d from GTPU", pdu_session_id); + return; + } + gtpu->rem_bearer(rnti, it->second.lcid); +} + +uint8_t ngap_ue_bearer_manager::allocate_lcid(uint32_t rnti) +{ + if (next_lcid_list.find(rnti) == next_lcid_list.end()) { + next_lcid_list[rnti] = 4; + } + return next_lcid_list[rnti]++; +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/ngap/ngap_ue_proc.cc b/srsgnb/src/stack/ngap/ngap_ue_proc.cc new file mode 100644 index 000000000..2384ced0a --- /dev/null +++ b/srsgnb/src/stack/ngap/ngap_ue_proc.cc @@ -0,0 +1,207 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/ngap/ngap_ue_proc.h" + +using namespace srsran; + +namespace srsenb { + +/* + * TS 38.413 - Section 8.2.1 PDU Session Resource Setup + */ +ngap_ue_pdu_session_res_setup_proc::ngap_ue_pdu_session_res_setup_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt_, + ngap_ue_bearer_manager* bearer_manager_, + srslog::basic_logger& logger_) : + parent(parent_), rrc(rrc_), ue_ctxt(ue_ctxt_), bearer_manager(bearer_manager_), logger(logger_) +{} + +proc_outcome_t ngap_ue_pdu_session_res_setup_proc::init(const asn1::ngap::pdu_session_res_setup_request_s& msg) +{ + if (msg->pdu_session_res_setup_list_su_req.value.size() != 1) { + logger.error("Not handling zero or multiple su requests"); + return proc_outcome_t::error; + } + + asn1::ngap::pdu_session_res_setup_item_su_req_s su_req = msg->pdu_session_res_setup_list_su_req.value[0]; + + asn1::cbit_ref pdu_session_bref(su_req.pdu_session_res_setup_request_transfer.data(), + su_req.pdu_session_res_setup_request_transfer.size()); + + asn1::ngap::pdu_session_res_setup_request_transfer_s pdu_ses_res_setup_req_trans; + + if (pdu_ses_res_setup_req_trans.unpack(pdu_session_bref) != SRSRAN_SUCCESS) { + logger.error("Unable to unpack PDU session response setup request"); + return proc_outcome_t::error; + } + + if (pdu_ses_res_setup_req_trans->qos_flow_setup_request_list.value.size() != 1) { + logger.error("Expected one item in QoS flow setup request list"); + return proc_outcome_t::error; + } + + if (pdu_ses_res_setup_req_trans->ul_ngu_up_tnl_info.value.type() != + asn1::ngap::up_transport_layer_info_c::types::gtp_tunnel) { + logger.error("Expected GTP Tunnel"); + return proc_outcome_t::error; + } + asn1::ngap::qos_flow_setup_request_item_s qos_flow_setup = + pdu_ses_res_setup_req_trans->qos_flow_setup_request_list.value[0]; + srsran::const_span nas_pdu_dummy; + uint32_t teid_out = 0; + + teid_out |= pdu_ses_res_setup_req_trans->ul_ngu_up_tnl_info.value.gtp_tunnel().gtp_teid[0] << 24u; + teid_out |= pdu_ses_res_setup_req_trans->ul_ngu_up_tnl_info.value.gtp_tunnel().gtp_teid[1] << 16u; + teid_out |= pdu_ses_res_setup_req_trans->ul_ngu_up_tnl_info.value.gtp_tunnel().gtp_teid[2] << 8u; + teid_out |= pdu_ses_res_setup_req_trans->ul_ngu_up_tnl_info.value.gtp_tunnel().gtp_teid[3]; + + // TODO: Check cause + asn1::ngap::cause_c cause; + uint32_t teid_in = {}; + uint16_t lcid = {}; + asn1::bounded_bitstring<1, 160, true, true> addr_in; + + if (bearer_manager->add_pdu_session( + ue_ctxt->rnti, + su_req.pdu_session_id, + qos_flow_setup.qos_flow_level_qos_params, + pdu_ses_res_setup_req_trans->ul_ngu_up_tnl_info.value.gtp_tunnel().transport_layer_address, + teid_out, + lcid, + addr_in, + teid_in, + cause) != SRSRAN_SUCCESS) { + logger.warning("Failed to add pdu session\n"); + return proc_outcome_t::error; + } + + uint16_t five_qi = pdu_ses_res_setup_req_trans->qos_flow_setup_request_list.value[0] + .qos_flow_level_qos_params.qos_characteristics.non_dynamic5_qi() + .five_qi; + + logger.info("Added PDU Session with LCID %d, 5QI %d, teid_out %d, teid_in %d, addr_in %s", + lcid, + five_qi, + teid_out, + teid_in, + addr_in.to_string()); + // QoS parameter mapping in config in LTE enb + if (su_req.pdu_session_nas_pdu.size() > 0) { + if (rrc->establish_rrc_bearer(ue_ctxt->rnti, su_req.pdu_session_id, su_req.pdu_session_nas_pdu, lcid, five_qi) == + SRSRAN_SUCCESS) { + parent->send_pdu_session_resource_setup_response(su_req.pdu_session_id, teid_in, addr_in); + return proc_outcome_t::success; + } + } + + return proc_outcome_t::yield; +} + +proc_outcome_t ngap_ue_pdu_session_res_setup_proc::step() +{ + return proc_outcome_t::success; +} + +/* + * TS 38.413 - Section 8.3.1 - Initial Context Setup + */ +ngap_ue_initial_context_setup_proc::ngap_ue_initial_context_setup_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt_, + srslog::basic_logger& logger_) : + logger(logger_), parent(parent_), rrc(rrc_), ue_ctxt(ue_ctxt_){}; + +proc_outcome_t ngap_ue_initial_context_setup_proc::init(const asn1::ngap::init_context_setup_request_s& msg) +{ + ue_ctxt->amf_pointer = msg->guami.value.amf_pointer.to_number(); + ue_ctxt->amf_set_id = msg->guami.value.amf_set_id.to_number(); + ue_ctxt->amf_region_id = msg->guami.value.amf_region_id.to_number(); + + if (msg->ue_aggregate_maximum_bit_rate_present == true) { + rrc->ue_set_bitrates(ue_ctxt->rnti, msg->ue_aggregate_maximum_bit_rate.value); + } + rrc->ue_set_security_cfg_capabilities(ue_ctxt->rnti, msg->ue_security_cap.value); + rrc->ue_set_security_cfg_key(ue_ctxt->rnti, msg->security_key.value); + + if (msg->nas_pdu_present) { + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Fatal Error: Couldn't allocate buffer in %s.", __FUNCTION__); + return proc_outcome_t::error; + } + memcpy(pdu->msg, msg->nas_pdu.value.data(), msg->nas_pdu.value.size()); + pdu->N_bytes = msg->nas_pdu.value.size(); + rrc->start_security_mode_procedure(ue_ctxt->rnti, std::move(pdu)); + } else { + rrc->start_security_mode_procedure(ue_ctxt->rnti, nullptr); + } + + return proc_outcome_t::yield; +}; + +proc_outcome_t ngap_ue_initial_context_setup_proc::react(bool rrc_reconf_outcome) +{ + if (rrc_reconf_outcome == true) { + parent->send_initial_ctxt_setup_response(); + return proc_outcome_t::success; + } + + return proc_outcome_t::error; +} + +proc_outcome_t ngap_ue_initial_context_setup_proc::step() +{ + return proc_outcome_t::yield; +} + +/* + * TS 38.413 - Section 8.3.2 - UE Context Release Request (NG-RAN node initiated) + */ +ngap_ue_ue_context_release_proc::ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_, + rrc_interface_ngap_nr* rrc_, + ngap_ue_ctxt_t* ue_ctxt_, + ngap_ue_bearer_manager* bearer_manager_, + srslog::basic_logger& logger_) : + logger(logger_) +{ + parent = parent_; + rrc = rrc_; + ue_ctxt = ue_ctxt_; + bearer_manager = bearer_manager_; +}; + +proc_outcome_t ngap_ue_ue_context_release_proc::init(const asn1::ngap::ue_context_release_cmd_s& msg) +{ + logger.info("Started %s", name()); + bearer_manager->reset_pdu_sessions(ue_ctxt->rnti); + rrc->release_user(ue_ctxt->rnti); + parent->send_ue_ctxt_release_complete(); + return proc_outcome_t::success; +} + +proc_outcome_t ngap_ue_ue_context_release_proc::step() +{ + return proc_outcome_t::success; +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/ngap/test/CMakeLists.txt b/srsgnb/src/stack/ngap/test/CMakeLists.txt new file mode 100644 index 000000000..d6c391ed1 --- /dev/null +++ b/srsgnb/src/stack/ngap/test/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(ngap_test ngap_test.cc) +target_link_libraries(ngap_test srsran_common ngap_nr_asn1 srsenb_upper srsran_gtpu ngap_nr_asn1 srsgnb_stack srsgnb_ngap ${SCTP_LIBRARIES}) + +add_test(ngap_test ngap_test) + diff --git a/srsgnb/src/stack/ngap/test/ngap_test.cc b/srsgnb/src/stack/ngap/test/ngap_test.cc new file mode 100644 index 000000000..6917ce961 --- /dev/null +++ b/srsgnb/src/stack/ngap/test/ngap_test.cc @@ -0,0 +1,322 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/ngap/ngap.h" +#include "srsran/common/network_utils.h" +#include "srsran/common/test_common.h" + +using namespace srsenb; + +struct amf_dummy { + amf_dummy(const char* addr_str_, int port_) : addr_str(addr_str_), port(port_) + { + srsran::net_utils::set_sockaddr(&amf_sockaddr, addr_str, port); + { + using namespace srsran::net_utils; + fd = open_socket(addr_family::ipv4, socket_type::seqpacket, protocol_type::SCTP); + TESTASSERT(fd > 0); + TESTASSERT(bind_addr(fd, amf_sockaddr)); + } + + int success = listen(fd, SOMAXCONN); + srsran_assert(success == 0, "Failed to listen to incoming SCTP connections"); + } + + ~amf_dummy() + { + if (fd > 0) { + close(fd); + } + } + + srsran::unique_byte_buffer_t read_msg(sockaddr_in* sockfrom = nullptr) + { + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + sockaddr_in from = {}; + socklen_t fromlen = sizeof(from); + sctp_sndrcvinfo sri = {}; + int flags = 0; + ssize_t n_recv = sctp_recvmsg(fd, pdu->msg, pdu->get_tailroom(), (struct sockaddr*)&from, &fromlen, &sri, &flags); + if (n_recv > 0) { + if (sockfrom != nullptr) { + *sockfrom = from; + } + pdu->N_bytes = n_recv; + } + return pdu; + } + + const char* addr_str; + int port; + struct sockaddr_in amf_sockaddr = {}; + int fd; + srsran::unique_byte_buffer_t last_sdu; +}; + +class rrc_nr_dummy : public rrc_interface_ngap_nr +{ +public: + rrc_nr_dummy() : rrc_logger(srslog::fetch_basic_logger("RRC_NR")) { rrc_logger.set_hex_dump_max_size(32); } + int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) + { + for (uint32_t i = 0; i < key.nof_octets(); ++i) { + sec_key[i] = key.data()[key.nof_octets() - 1 - i]; + } + rrc_logger.info(sec_key, 32, "Security key"); + return SRSRAN_SUCCESS; + } + int ue_set_bitrates(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) + { + rrc_logger.info("Setting aggregate max bitrate. RNTI 0x%x", rnti); + return SRSRAN_SUCCESS; + } + int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) + { + rrc_logger.info("Setting aggregate max bitrate"); + return SRSRAN_SUCCESS; + } + int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) + { + rrc_logger.info("Setting security capabilities"); + for (uint8_t i = 0; i < 8; i++) { + rrc_logger.info("NIA%d = %s", i, caps.nrintegrity_protection_algorithms.get(i) ? "true" : "false"); + } + for (uint8_t i = 0; i < 8; i++) { + rrc_logger.info("NEA%d = %s", i, caps.nrencryption_algorithms.get(i) ? "true" : "false"); + } + for (uint8_t i = 0; i < 8; i++) { + rrc_logger.info("EEA%d = %s", i, caps.eutr_aencryption_algorithms.get(i) ? "true" : "false"); + } + for (uint8_t i = 0; i < 8; i++) { + rrc_logger.info("EIA%d = %s", i, caps.eutr_aintegrity_protection_algorithms.get(i) ? "true" : "false"); + } + sec_caps = caps; + return SRSRAN_SUCCESS; + } + int start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) + { + rrc_logger.info("Starting securtity mode procedure"); + sec_mod_proc_started = true; + return SRSRAN_SUCCESS; + } + int establish_rrc_bearer(uint16_t rnti, + uint16_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) + { + rrc_logger.info("Establish RRC bearer"); + return SRSRAN_SUCCESS; + } + + int release_bearers(uint16_t rnti) { return SRSRAN_SUCCESS; } + void release_user(uint16_t rnti) {} + int allocate_lcid(uint16_t rnti) { return SRSRAN_SUCCESS; } + void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) {} + + bool sec_mod_proc_started = false; + uint8_t sec_key[32] = {}; + asn1::ngap::ue_security_cap_s sec_caps = {}; + srslog::basic_logger& rrc_logger; +}; +struct dummy_socket_manager : public srsran::socket_manager_itf { + dummy_socket_manager() : srsran::socket_manager_itf(srslog::fetch_basic_logger("TEST")) {} + + /// Register (fd, callback). callback is called within socket thread when fd has data. + bool add_socket_handler(int fd, recv_callback_t handler) final + { + if (s1u_fd > 0) { + return false; + } + s1u_fd = fd; + callback = std::move(handler); + return true; + } + + /// remove registered socket fd + bool remove_socket(int fd) final + { + if (s1u_fd < 0) { + return false; + } + s1u_fd = -1; + return true; + } + + int s1u_fd = 0; + recv_callback_t callback; +}; + +void run_ng_setup(ngap& ngap_obj, amf_dummy& amf) +{ + asn1::ngap::ngap_pdu_c ngap_pdu; + + // gNB -> AMF: NG Setup Request + srsran::unique_byte_buffer_t sdu = amf.read_msg(); + TESTASSERT(sdu->N_bytes > 0); + asn1::cbit_ref cbref(sdu->msg, sdu->N_bytes); + TESTASSERT(ngap_pdu.unpack(cbref) == asn1::SRSASN_SUCCESS); + TESTASSERT(ngap_pdu.type().value == asn1::ngap::ngap_pdu_c::types_opts::init_msg); + TESTASSERT(ngap_pdu.init_msg().proc_code == ASN1_NGAP_ID_NG_SETUP); + + // AMF -> gNB: ng Setup Response + sockaddr_in amf_addr = {}; + sctp_sndrcvinfo rcvinfo = {}; + int flags = 0; + + uint8_t ng_setup_resp[] = {0x20, 0x15, 0x00, 0x55, 0x00, 0x00, 0x04, 0x00, 0x01, 0x00, 0x31, 0x17, 0x00, 0x61, 0x6d, + 0x61, 0x72, 0x69, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x61, 0x6d, 0x66, 0x2e, 0x35, 0x67, 0x63, + 0x2e, 0x6d, 0x6e, 0x63, 0x30, 0x30, 0x31, 0x2e, 0x6d, 0x63, 0x63, 0x30, 0x30, 0x31, 0x2e, + 0x33, 0x67, 0x70, 0x70, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2e, 0x6f, 0x72, 0x67, + 0x00, 0x60, 0x00, 0x08, 0x00, 0x00, 0x00, 0xf1, 0x10, 0x80, 0x01, 0x01, 0x00, 0x56, 0x40, + 0x01, 0x32, 0x00, 0x50, 0x00, 0x08, 0x00, 0x00, 0xf1, 0x10, 0x00, 0x00, 0x00, 0x08}; + memcpy(sdu->msg, ng_setup_resp, sizeof(ng_setup_resp)); + sdu->N_bytes = sizeof(ng_setup_resp); + TESTASSERT(ngap_obj.handle_amf_rx_msg(std::move(sdu), amf_addr, rcvinfo, flags)); +} + +void run_ng_initial_ue(ngap& ngap_obj, amf_dummy& amf, rrc_nr_dummy& rrc) +{ + // RRC will call the initial UE request with the NAS PDU + uint8_t nas_reg_req[] = {0x7e, 0x00, 0x41, 0x79, 0x00, 0x0d, 0x01, 0x00, 0xf1, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x32, 0x54, 0x76, 0x98, 0x2e, 0x02, 0xf0, 0xf0}; + + srsran::unique_byte_buffer_t nas_pdu = srsran::make_byte_buffer(); + memcpy(nas_pdu->msg, nas_reg_req, sizeof(nas_reg_req)); + nas_pdu->N_bytes = sizeof(nas_reg_req); + + uint32_t rnti = 0xf0f0; + ngap_obj.initial_ue(rnti, 0, asn1::ngap::rrcestablishment_cause_opts::mo_sig, srsran::make_span(nas_pdu)); + + // gNB -> AMF: Inital UE message + asn1::ngap::ngap_pdu_c ngap_initial_ue_pdu; + srsran::unique_byte_buffer_t sdu = amf.read_msg(); + TESTASSERT(sdu->N_bytes > 0); + asn1::cbit_ref cbref(sdu->msg, sdu->N_bytes); + TESTASSERT_EQ(ngap_initial_ue_pdu.unpack(cbref), asn1::SRSASN_SUCCESS); + TESTASSERT_EQ(ngap_initial_ue_pdu.type().value, asn1::ngap::ngap_pdu_c::types_opts::init_msg); + TESTASSERT_EQ(ngap_initial_ue_pdu.init_msg().proc_code, ASN1_NGAP_ID_INIT_UE_MSG); + + // AMF -> gNB: Initial Context Setup Request + sockaddr_in amf_addr = {}; + sctp_sndrcvinfo rcvinfo = {}; + int flags = 0; + + asn1::ngap::ngap_pdu_c ngap_initial_ctx_req_pdu; + ngap_initial_ctx_req_pdu.set_init_msg().load_info_obj(ASN1_NGAP_ID_INIT_CONTEXT_SETUP); + auto& container = ngap_initial_ctx_req_pdu.init_msg().value.init_context_setup_request(); + + container->amf_ue_ngap_id.value = 0x1; + container->ran_ue_ngap_id.value = 0x1; + container->nas_pdu_present = true; + + // Set allowed NSSAI + container->allowed_nssai.value.resize(1); + container->allowed_nssai.value[0].s_nssai.sst.from_number(1); + + // Set security key + uint8_t sec_key[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}; + for (uint8_t i = 0; i < 32; ++i) { + container->security_key.value.data()[31 - i] = sec_key[i]; + } + + // Set security capabilities + container->ue_security_cap.value.nrencryption_algorithms.set(0, true); + container->ue_security_cap.value.nrencryption_algorithms.set(1, true); + container->ue_security_cap.value.nrintegrity_protection_algorithms.set(0, true); + container->ue_security_cap.value.nrintegrity_protection_algorithms.set(1, true); + container->ue_security_cap.value.eutr_aencryption_algorithms.set(0, true); + container->ue_security_cap.value.eutr_aencryption_algorithms.set(1, true); + container->ue_security_cap.value.eutr_aintegrity_protection_algorithms.set(0, true); + container->ue_security_cap.value.eutr_aintegrity_protection_algorithms.set(1, true); + + // Set PDU Session Response Setup Item + // TODO + + srsran::unique_byte_buffer_t buf = srsran::make_byte_buffer(); + TESTASSERT_NEQ(buf, nullptr); + asn1::bit_ref bref(buf->msg, buf->get_tailroom()); + TESTASSERT_EQ(ngap_initial_ctx_req_pdu.pack(bref), asn1::SRSASN_SUCCESS); + buf->N_bytes = bref.distance_bytes(); + + // Feed Initial Context Setup Request to NGAP + TESTASSERT(ngap_obj.handle_amf_rx_msg(std::move(buf), amf_addr, rcvinfo, flags)); + + // Check RRC security key + for (uint8_t i = 0; i < 32; ++i) { + TESTASSERT_EQ(sec_key[i], rrc.sec_key[i]); + } + + // Check RRC security capabilities + for (uint8_t i = 0; i < 8; ++i) { + TESTASSERT_EQ(container->ue_security_cap.value.nrencryption_algorithms.get(i), + rrc.sec_caps.nrencryption_algorithms.get(i)); + TESTASSERT_EQ(container->ue_security_cap.value.nrintegrity_protection_algorithms.get(i), + rrc.sec_caps.nrintegrity_protection_algorithms.get(i)); + TESTASSERT_EQ(container->ue_security_cap.value.eutr_aencryption_algorithms.get(i), + rrc.sec_caps.eutr_aencryption_algorithms.get(i)); + TESTASSERT_EQ(container->ue_security_cap.value.eutr_aintegrity_protection_algorithms.get(i), + rrc.sec_caps.eutr_aintegrity_protection_algorithms.get(i)); + } + + // Check PDU Session Response Setup Item + // TODO + + // Check RRC security mode command was started + TESTASSERT(rrc.sec_mod_proc_started); +} + +int main(int argc, char** argv) +{ + // Setup logging. + srslog::basic_logger& logger = srslog::fetch_basic_logger("NGAP"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + + srsran::task_scheduler task_sched; + dummy_socket_manager rx_sockets; + ngap ngap_obj(&task_sched, logger, &rx_sockets); + + const char* amf_addr_str = "127.0.0.1"; + const uint32_t AMF_PORT = 38412; + amf_dummy amf(amf_addr_str, AMF_PORT); + + ngap_args_t args = {}; + args.cell_id = 0x01; + args.gnb_id = 0x19B; + args.mcc = 907; + args.mnc = 70; + args.ngc_bind_addr = "127.0.0.100"; + args.tac = 7; + args.gtp_bind_addr = "127.0.0.100"; + args.amf_addr = "127.0.0.1"; + args.gnb_name = "srsgnb01"; + + rrc_nr_dummy rrc; + gtpu_interface_rrc* gtpu = nullptr; + ngap_obj.init(args, &rrc, gtpu); + + // Start the log backend. + srsran::test_init(argc, argv); + run_ng_setup(ngap_obj, amf); + run_ng_initial_ue(ngap_obj, amf, rrc); +} diff --git a/srsgnb/src/stack/rrc/CMakeLists.txt b/srsgnb/src/stack/rrc/CMakeLists.txt new file mode 100644 index 000000000..f0f0e7b83 --- /dev/null +++ b/srsgnb/src/stack/rrc/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES rrc_nr_config_utils.cc) +add_library(srsgnb_rrc_config_utils STATIC ${SOURCES}) +target_link_libraries(srsgnb_rrc_config_utils srsran_phy) + +set(SOURCES rrc_nr.cc rrc_nr_ue.cc rrc_nr_security_context.cc cell_asn1_config.cc rrc_nr_du_manager.cc) +add_library(srsgnb_rrc STATIC ${SOURCES}) +target_link_libraries(srsgnb_rrc srsgnb_rrc_config_utils) + +include_directories(${PROJECT_SOURCE_DIR}) + +add_subdirectory(test) diff --git a/srsgnb/src/stack/rrc/cell_asn1_config.cc b/srsgnb/src/stack/rrc/cell_asn1_config.cc new file mode 100644 index 000000000..d4fd165eb --- /dev/null +++ b/srsgnb/src/stack/rrc/cell_asn1_config.cc @@ -0,0 +1,1377 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/rrc/cell_asn1_config.h" +#include "srsenb/hdr/common/common_enb.h" +#include "srsran/asn1/obj_id_cmp_utils.h" +#include "srsran/asn1/rrc_nr_utils.h" +#include "srsran/common/band_helper.h" +#include + +using namespace asn1::rrc_nr; + +#define HANDLE_ERROR(ret) \ + if ((ret) != SRSRAN_SUCCESS) { \ + return SRSRAN_ERROR; \ + } + +namespace srsenb { + +srslog::basic_logger& get_logger(const rrc_nr_cfg_t& cfg) +{ + static srslog::basic_logger& log_obj = srslog::fetch_basic_logger(cfg.log_name); + return log_obj; +} + +void set_search_space_from_phy_cfg(const srsran_search_space_t& cfg, asn1::rrc_nr::search_space_s& out) +{ + out.search_space_id = cfg.id; + out.ctrl_res_set_id_present = true; + out.ctrl_res_set_id = cfg.coreset_id; + out.monitoring_slot_periodicity_and_offset_present = true; + out.monitoring_slot_periodicity_and_offset.set_sl1(); + out.dur_present = false; // false for duration=1 + out.monitoring_symbols_within_slot_present = true; + out.monitoring_symbols_within_slot.from_number(0b10000000000000); + + out.nrof_candidates_present = true; + bool ret = asn1::number_to_enum(out.nrof_candidates.aggregation_level1, cfg.nof_candidates[0]); + srsran_assert(ret, "Failed to convert nof candidates=%d", cfg.nof_candidates[0]); + ret = asn1::number_to_enum(out.nrof_candidates.aggregation_level2, cfg.nof_candidates[1]); + srsran_assert(ret, "Failed to convert nof candidates=%d", cfg.nof_candidates[1]); + ret = asn1::number_to_enum(out.nrof_candidates.aggregation_level4, cfg.nof_candidates[2]); + srsran_assert(ret, "Failed to convert nof candidates=%d", cfg.nof_candidates[2]); + ret = asn1::number_to_enum(out.nrof_candidates.aggregation_level8, cfg.nof_candidates[3]); + srsran_assert(ret, "Failed to convert nof candidates=%d", cfg.nof_candidates[3]); + ret = asn1::number_to_enum(out.nrof_candidates.aggregation_level16, cfg.nof_candidates[4]); + srsran_assert(ret, "Failed to convert nof candidates=%d", cfg.nof_candidates[4]); + + out.search_space_type_present = true; + if ((cfg.type == srsran_search_space_type_common_0) or (cfg.type == srsran_search_space_type_common_0A) or + (cfg.type == srsran_search_space_type_common_1) or (cfg.type == srsran_search_space_type_common_2) or + (cfg.type == srsran_search_space_type_common_3)) { + out.search_space_type.set_common(); + + out.search_space_type.common().dci_format0_minus0_and_format1_minus0_present = true; + } else if (cfg.type == srsran_search_space_type_ue) { + out.search_space_type.set_ue_specific().dci_formats.value = + search_space_s::search_space_type_c_::ue_specific_s_::dci_formats_opts::formats0_minus0_and_minus1_minus0; + } else { + srsran_terminate("Config Error: Unsupported search space type."); + } +} + +void set_coreset_from_phy_cfg(const srsran_coreset_t& coreset_cfg, asn1::rrc_nr::ctrl_res_set_s& out) +{ + out.ctrl_res_set_id = coreset_cfg.id; + + std::bitset freq_domain_res; + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { + freq_domain_res[SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE - 1 - i] = coreset_cfg.freq_resources[i]; + } + out.freq_domain_res.from_number(freq_domain_res.to_ulong()); + + out.dur = coreset_cfg.duration; + + if (coreset_cfg.mapping_type == srsran_coreset_mapping_type_non_interleaved) { + out.cce_reg_map_type.set_non_interleaved(); + } else { + auto& interleaved = out.cce_reg_map_type.set_interleaved(); + interleaved.reg_bundle_size.value = (decltype(interleaved.reg_bundle_size.value))coreset_cfg.reg_bundle_size; + interleaved.interleaver_size.value = (decltype(interleaved.interleaver_size.value))coreset_cfg.interleaver_size; + interleaved.shift_idx_present = true; + interleaved.shift_idx = coreset_cfg.shift_index; + } + + if (coreset_cfg.precoder_granularity == srsran_coreset_precoder_granularity_reg_bundle) { + out.precoder_granularity = asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; + } else { + out.precoder_granularity = asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::all_contiguous_rbs; + } + + // TODO: Remaining fields +} + +void set_rach_cfg_common(const srsran_prach_cfg_t& prach_cfg, asn1::rrc_nr::rach_cfg_common_s& out) +{ + // rach-ConfigGeneric + out.rach_cfg_generic.prach_cfg_idx = prach_cfg.config_idx; + out.rach_cfg_generic.msg1_fdm.value = rach_cfg_generic_s::msg1_fdm_opts::one; + out.rach_cfg_generic.msg1_freq_start = prach_cfg.freq_offset; + out.rach_cfg_generic.zero_correlation_zone_cfg = prach_cfg.zero_corr_zone; + out.rach_cfg_generic.preamb_rx_target_pwr = -110; + out.rach_cfg_generic.preamb_trans_max.value = rach_cfg_generic_s::preamb_trans_max_opts::n7; + out.rach_cfg_generic.pwr_ramp_step.value = rach_cfg_generic_s::pwr_ramp_step_opts::db4; + out.rach_cfg_generic.ra_resp_win.value = rach_cfg_generic_s::ra_resp_win_opts::sl10; + + out.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present = true; + asn1::number_to_enum(out.ssb_per_rach_occasion_and_cb_preambs_per_ssb.set_one(), prach_cfg.num_ra_preambles); + out.ra_contention_resolution_timer.value = rach_cfg_common_s::ra_contention_resolution_timer_opts::sf64; + out.prach_root_seq_idx.set_l839() = prach_cfg.root_seq_idx; + out.restricted_set_cfg.value = rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void fill_tdd_ul_dl_config_common(const rrc_cell_cfg_nr_t& cfg, asn1::rrc_nr::tdd_ul_dl_cfg_common_s& tdd) +{ + srsran_assert(cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD, "This function should only be called for TDD configs"); + // TDD UL-DL config + tdd.ref_subcarrier_spacing.value = (asn1::rrc_nr::subcarrier_spacing_opts::options)cfg.phy_cell.carrier.scs; + tdd.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10; + tdd.pattern1.nrof_dl_slots = 6; + tdd.pattern1.nrof_dl_symbols = 0; + tdd.pattern1.nrof_ul_slots = 4; + tdd.pattern1.nrof_ul_symbols = 0; +} + +/// Fill list of CSI-ReportConfig with gNB config +int fill_csi_report_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +{ + if (cfg.is_standalone) { + csi_meas_cfg.csi_report_cfg_to_add_mod_list.resize(1); + + auto& csi_report = csi_meas_cfg.csi_report_cfg_to_add_mod_list[0]; + csi_report.report_cfg_id = 0; + csi_report.res_for_ch_meas = 0; + csi_report.csi_im_res_for_interference_present = true; + csi_report.csi_im_res_for_interference = 1; + csi_report.report_cfg_type.set_periodic(); + csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80(); + csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1); + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0; + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = + 17; // was 17 in orig PCAP, but code for NSA it was set to 1 + csi_report.report_quant.set_cri_ri_pmi_cqi(); + // Report freq config (optional) + csi_report.report_freq_cfg_present = true; + csi_report.report_freq_cfg.cqi_format_ind_present = true; + csi_report.report_freq_cfg.cqi_format_ind.value = + csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi; + csi_report.report_freq_cfg.pmi_format_ind_present = true; + csi_report.report_freq_cfg.pmi_format_ind.value = + csi_report_cfg_s::report_freq_cfg_s_::pmi_format_ind_opts::wideband_pmi; + csi_report.time_restrict_for_ch_meass.value = csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured; + csi_report.time_restrict_for_interference_meass.value = + asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured; + csi_report.codebook_cfg_present = true; + auto& type1 = csi_report.codebook_cfg.codebook_type.set_type1(); + type1.sub_type.set_type_i_single_panel(); + type1.sub_type.type_i_single_panel().nr_of_ant_ports.set_two(); + type1.sub_type.type_i_single_panel().nr_of_ant_ports.two().two_tx_codebook_subset_restrict.from_number(0b111111); + type1.sub_type.type_i_single_panel().type_i_single_panel_ri_restrict.from_number(0x03); + type1.codebook_mode = 1; + csi_report.group_based_beam_report.set_disabled(); + // Skip CQI table (optional) + csi_report.cqi_table_present = true; + csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table1; + csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; + + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 1; + } else { + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 7; + } + } else { + csi_meas_cfg.csi_report_cfg_to_add_mod_list.resize(1); + + auto& csi_report = csi_meas_cfg.csi_report_cfg_to_add_mod_list[0]; + csi_report.report_cfg_id = 0; + csi_report.res_for_ch_meas = 0; + csi_report.csi_im_res_for_interference_present = true; + csi_report.csi_im_res_for_interference = 1; + csi_report.report_cfg_type.set_periodic(); + csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80(); + csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1); + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0; + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = 1; // was 17 in orig PCAP + csi_report.report_quant.set_cri_ri_pmi_cqi(); + // Report freq config (optional) + csi_report.report_freq_cfg_present = true; + csi_report.report_freq_cfg.cqi_format_ind_present = true; + csi_report.report_freq_cfg.cqi_format_ind = csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi; + csi_report.time_restrict_for_ch_meass = csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured; + csi_report.time_restrict_for_interference_meass = + asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured; + csi_report.group_based_beam_report.set_disabled(); + // Skip CQI table (optional) + csi_report.cqi_table_present = true; + csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2; + csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; + + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 5; + } else { + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 7; + } + } + + return SRSRAN_SUCCESS; +} + +/// Fill lists of NZP-CSI-RS-Resource and NZP-CSI-RS-ResourceSet with gNB config +void fill_nzp_csi_rs_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +{ + if (cfg.is_standalone) { + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.resize(5); + auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list; + // item 0 + nzp_csi_res[0].nzp_csi_rs_res_id = 0; + nzp_csi_res[0].res_map.freq_domain_alloc.set_row2(); + nzp_csi_res[0].res_map.freq_domain_alloc.row2().from_number(0x800); + nzp_csi_res[0].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res[0].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res[0].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res[0].res_map.density.set_one(); + nzp_csi_res[0].res_map.freq_band.start_rb = 0; + nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res[0].pwr_ctrl_offset = 0; + nzp_csi_res[0].pwr_ctrl_offset_ss_present = true; + nzp_csi_res[0].pwr_ctrl_offset_ss.value = asn1::rrc_nr::nzp_csi_rs_res_s::pwr_ctrl_offset_ss_opts::db0; + nzp_csi_res[0].scrambling_id = cfg.cell_list[0].phy_cell.cell_id; + nzp_csi_res[0].periodicity_and_offset_present = true; + nzp_csi_res[0].periodicity_and_offset.set_slots80(); + nzp_csi_res[0].periodicity_and_offset.slots80() = 1; + // optional + nzp_csi_res[0].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res[0].qcl_info_periodic_csi_rs = 0; + // item 1 + nzp_csi_res[1] = nzp_csi_res[0]; + nzp_csi_res[1].nzp_csi_rs_res_id = 1; + nzp_csi_res[1].res_map.freq_domain_alloc.set_row1(); + nzp_csi_res[1].res_map.freq_domain_alloc.row1().from_number(0x1); + nzp_csi_res[1].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res[1].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res[1].res_map.density.set_three(); + nzp_csi_res[1].periodicity_and_offset.set_slots40(); + nzp_csi_res[1].periodicity_and_offset.slots40() = 11; + // item 2 + nzp_csi_res[2] = nzp_csi_res[1]; + nzp_csi_res[2].nzp_csi_rs_res_id = 2; + nzp_csi_res[2].res_map.first_ofdm_symbol_in_time_domain = 8; + // item 3 + nzp_csi_res[3] = nzp_csi_res[1]; + nzp_csi_res[3].nzp_csi_rs_res_id = 3; + nzp_csi_res[3].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res[3].periodicity_and_offset.set_slots40(); + nzp_csi_res[3].periodicity_and_offset.slots40() = 12; + // item 4 + nzp_csi_res[4] = nzp_csi_res[1]; + nzp_csi_res[4].nzp_csi_rs_res_id = 4; + nzp_csi_res[4].res_map.first_ofdm_symbol_in_time_domain = 8; + nzp_csi_res[4].periodicity_and_offset.set_slots40(); + nzp_csi_res[4].periodicity_and_offset.slots40() = 12; + } else { + csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.resize(1); + auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list; + nzp_csi_res[0].nzp_csi_rs_res_id = 0; + nzp_csi_res[0].res_map.freq_domain_alloc.set_row2(); + nzp_csi_res[0].res_map.freq_domain_alloc.row2().from_number(0b100000000000); + nzp_csi_res[0].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res[0].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res[0].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res[0].res_map.density.set_one(); + nzp_csi_res[0].res_map.freq_band.start_rb = 0; + nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res[0].pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res[0].periodicity_and_offset_present = true; + nzp_csi_res[0].periodicity_and_offset.set_slots80() = 0; + // optional + nzp_csi_res[0].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res[0].qcl_info_periodic_csi_rs = 0; + } + + // Fill NZP-CSI Resource Sets + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.resize(2); + auto& nzp_csi_res_set = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list; + // item 0 + nzp_csi_res_set[0].nzp_csi_res_set_id = 0; + nzp_csi_res_set[0].nzp_csi_rs_res.resize(1); + nzp_csi_res_set[0].nzp_csi_rs_res[0] = 0; + // item 1 + nzp_csi_res_set[1].nzp_csi_res_set_id = 1; + nzp_csi_res_set[1].nzp_csi_rs_res.resize(4); + nzp_csi_res_set[1].nzp_csi_rs_res[0] = 1; + nzp_csi_res_set[1].nzp_csi_rs_res[1] = 2; + nzp_csi_res_set[1].nzp_csi_rs_res[2] = 3; + nzp_csi_res_set[1].nzp_csi_rs_res[3] = 4; + nzp_csi_res_set[1].trs_info_present = true; + // // Skip TRS info + } else { + csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.resize(1); + auto& nzp_csi_res_set = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list; + nzp_csi_res_set[0].nzp_csi_res_set_id = 0; + nzp_csi_res_set[0].nzp_csi_rs_res.resize(1); + nzp_csi_res_set[0].nzp_csi_rs_res[0] = 0; + // Skip TRS info + } + } else { + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.resize(5); + auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list; + // item 0 + nzp_csi_res[0].nzp_csi_rs_res_id = 0; + nzp_csi_res[0].res_map.freq_domain_alloc.set_row2(); + nzp_csi_res[0].res_map.freq_domain_alloc.row2().from_number(0b100000000000); + nzp_csi_res[0].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res[0].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res[0].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res[0].res_map.density.set_one(); + nzp_csi_res[0].res_map.freq_band.start_rb = 0; + nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res[0].pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res[0].scrambling_id = cfg.cell_list[0].phy_cell.cell_id; + nzp_csi_res[0].periodicity_and_offset_present = true; + nzp_csi_res[0].periodicity_and_offset.set_slots80(); + nzp_csi_res[0].periodicity_and_offset.slots80() = 1; + // optional + nzp_csi_res[0].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res[0].qcl_info_periodic_csi_rs = 0; + // item 1 + nzp_csi_res[1] = nzp_csi_res[0]; + nzp_csi_res[1].nzp_csi_rs_res_id = 1; + nzp_csi_res[1].res_map.freq_domain_alloc.set_row1(); + nzp_csi_res[1].res_map.freq_domain_alloc.row1().from_number(0b0001); + nzp_csi_res[1].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res[1].res_map.density.set_three(); + nzp_csi_res[1].periodicity_and_offset.set_slots40(); + nzp_csi_res[1].periodicity_and_offset.slots40() = 11; + // item 2 + nzp_csi_res[2] = nzp_csi_res[1]; + nzp_csi_res[2].nzp_csi_rs_res_id = 2; + nzp_csi_res[2].res_map.first_ofdm_symbol_in_time_domain = 8; + nzp_csi_res[2].periodicity_and_offset.set_slots40(); + nzp_csi_res[2].periodicity_and_offset.slots40() = 11; + // item 3 + nzp_csi_res[3] = nzp_csi_res[1]; + nzp_csi_res[3].nzp_csi_rs_res_id = 3; + nzp_csi_res[3].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res[3].periodicity_and_offset.set_slots40(); + nzp_csi_res[3].periodicity_and_offset.slots40() = 12; + // item 4 + nzp_csi_res[4] = nzp_csi_res[1]; + nzp_csi_res[4].nzp_csi_rs_res_id = 4; + nzp_csi_res[4].res_map.first_ofdm_symbol_in_time_domain = 8; + nzp_csi_res[4].periodicity_and_offset.set_slots40(); + nzp_csi_res[4].periodicity_and_offset.slots40() = 12; + } else { + csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.resize(1); + auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list; + nzp_csi_res[0].nzp_csi_rs_res_id = 0; + nzp_csi_res[0].res_map.freq_domain_alloc.set_row2(); + nzp_csi_res[0].res_map.freq_domain_alloc.row2().from_number(0b100000000000); + nzp_csi_res[0].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res[0].res_map.first_ofdm_symbol_in_time_domain = 4; + nzp_csi_res[0].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; + nzp_csi_res[0].res_map.density.set_one(); + nzp_csi_res[0].res_map.freq_band.start_rb = 0; + nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52; + nzp_csi_res[0].pwr_ctrl_offset = 0; + // Skip pwr_ctrl_offset_ss_present + nzp_csi_res[0].periodicity_and_offset_present = true; + nzp_csi_res[0].periodicity_and_offset.set_slots80() = 0; + // optional + nzp_csi_res[0].qcl_info_periodic_csi_rs_present = true; + nzp_csi_res[0].qcl_info_periodic_csi_rs = 0; + } + + // Fill NZP-CSI Resource Sets + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.resize(2); + auto& nzp_csi_res_set = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list; + // item 0 + nzp_csi_res_set[0].nzp_csi_res_set_id = 0; + nzp_csi_res_set[0].nzp_csi_rs_res.resize(1); + nzp_csi_res_set[0].nzp_csi_rs_res[0] = 0; + // item 1 + nzp_csi_res_set[1].nzp_csi_res_set_id = 1; + nzp_csi_res_set[1].nzp_csi_rs_res.resize(4); + nzp_csi_res_set[1].nzp_csi_rs_res[0] = 1; + nzp_csi_res_set[1].nzp_csi_rs_res[1] = 2; + nzp_csi_res_set[1].nzp_csi_rs_res[2] = 3; + nzp_csi_res_set[1].nzp_csi_rs_res[3] = 4; + // // Skip TRS info + } else { + csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.resize(1); + auto& nzp_csi_res_set = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list; + nzp_csi_res_set[0].nzp_csi_res_set_id = 0; + nzp_csi_res_set[0].nzp_csi_rs_res.resize(1); + nzp_csi_res_set[0].nzp_csi_rs_res[0] = 0; + // Skip TRS info + } + } +} + +/// Fill csi-ResoureConfigToAddModList +void fill_csi_resource_cfg_to_add(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +{ + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_meas_cfg.csi_res_cfg_to_add_mod_list.resize(3); + + csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].csi_res_cfg_id = 0; + auto& nzp = csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].csi_rs_res_set_list.set_nzp_csi_rs_ssb(); + nzp.nzp_csi_rs_res_set_list.push_back(0); + csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].bwp_id = 0; + csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].res_type.value = csi_res_cfg_s::res_type_opts::periodic; + + csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_res_cfg_id = 1; + auto& im_res = csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_rs_res_set_list.set_csi_im_res_set_list(); + im_res.push_back(0); + csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].bwp_id = 0; + csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].res_type.value = csi_res_cfg_s::res_type_opts::periodic; + + csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].csi_res_cfg_id = 2; + auto& nzp2 = csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].csi_rs_res_set_list.set_nzp_csi_rs_ssb(); + nzp2.nzp_csi_rs_res_set_list.push_back(1); + csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].bwp_id = 0; + csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].res_type.value = csi_res_cfg_s::res_type_opts::periodic; + } +} + +void fill_csi_im_resource_cfg_to_add(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +{ + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + // csi-IM-ResourceToAddModList + csi_meas_cfg.csi_im_res_to_add_mod_list.resize(1); + + auto& csi_im_res = csi_meas_cfg.csi_im_res_to_add_mod_list[0]; + csi_im_res.csi_im_res_id = 0; + csi_im_res.csi_im_res_elem_pattern_present = true; + // csi-im-resource pattern1 + auto& csi_res_pattern1 = csi_im_res.csi_im_res_elem_pattern.set_pattern1(); + csi_res_pattern1.subcarrier_location_p1.value = + csi_im_res_s::csi_im_res_elem_pattern_c_::pattern1_s_::subcarrier_location_p1_opts::s8; + csi_res_pattern1.symbol_location_p1 = 8; + // csi-im-resource freqBand + csi_im_res.freq_band_present = true; + csi_im_res.freq_band.start_rb = 0; + csi_im_res.freq_band.nrof_rbs = 52; + // csi-im-resource periodicity_and_offset + csi_im_res.periodicity_and_offset_present = true; + csi_im_res.periodicity_and_offset.set_slots80(); + csi_im_res.periodicity_and_offset.slots80() = 1; + + // csi-IM-ResourceSetToAddModList + csi_meas_cfg.csi_im_res_set_to_add_mod_list.resize(1); + + auto& csi_im_res_set = csi_meas_cfg.csi_im_res_set_to_add_mod_list[0]; + csi_im_res_set.csi_im_res_set_id = 0; + csi_im_res_set.csi_im_res.push_back(0); + } +} + +/// Fill CSI-MeasConfig with gNB config +int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +{ + // // Fill CSI Report + // if (fill_csi_report_from_enb_cfg(cfg, csi_meas_cfg) != SRSRAN_SUCCESS) { + // get_logger(cfg).error("Failed to configure eNB CSI Report"); + // return SRSRAN_ERROR; + // } + + // Fill CSI resource config + fill_csi_resource_cfg_to_add(cfg, csi_meas_cfg); + + // Fill NZP-CSI Resources + fill_nzp_csi_rs_from_enb_cfg(cfg, csi_meas_cfg); + + if (cfg.is_standalone) { + // CSI IM config + fill_csi_im_resource_cfg_to_add(cfg, csi_meas_cfg); + + // CSI report config + fill_csi_report_from_enb_cfg(cfg, csi_meas_cfg); + } + + return SRSRAN_SUCCESS; +} + +void fill_pdsch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pdsch_cfg_s& out) +{ + out.dmrs_dl_for_pdsch_map_type_a_present = true; + out.dmrs_dl_for_pdsch_map_type_a.set_setup(); + out.dmrs_dl_for_pdsch_map_type_a.setup().dmrs_add_position_present = true; + out.dmrs_dl_for_pdsch_map_type_a.setup().dmrs_add_position = dmrs_dl_cfg_s::dmrs_add_position_opts::pos1; + + out.tci_states_to_add_mod_list.resize(1); + out.tci_states_to_add_mod_list[0].tci_state_id = 0; + out.tci_states_to_add_mod_list[0].qcl_type1.ref_sig.set_ssb(); + out.tci_states_to_add_mod_list[0].qcl_type1.ref_sig.ssb() = 0; + out.tci_states_to_add_mod_list[0].qcl_type1.qcl_type = asn1::rrc_nr::qcl_info_s::qcl_type_opts::type_d; + + out.res_alloc = pdsch_cfg_s::res_alloc_opts::res_alloc_type1; + out.rbg_size = pdsch_cfg_s::rbg_size_opts::cfg1; + out.prb_bundling_type.set_static_bundling(); + out.prb_bundling_type.static_bundling().bundle_size_present = true; + out.prb_bundling_type.static_bundling().bundle_size = + pdsch_cfg_s::prb_bundling_type_c_::static_bundling_s_::bundle_size_opts::wideband; + + // MCS Table + // NOTE: For Table 1 or QAM64, set false and comment value + // out.mcs_table_present = true; + // out.mcs_table.value = pdsch_cfg_s::mcs_table_opts::qam256; + + // ZP-CSI + out.zp_csi_rs_res_to_add_mod_list.resize(1); + out.zp_csi_rs_res_to_add_mod_list[0].zp_csi_rs_res_id = 0; + out.zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.set_row4(); + out.zp_csi_rs_res_to_add_mod_list[0].res_map.freq_domain_alloc.row4().from_number(0b100); + out.zp_csi_rs_res_to_add_mod_list[0].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p4; + + out.zp_csi_rs_res_to_add_mod_list[0].res_map.first_ofdm_symbol_in_time_domain = 8; + out.zp_csi_rs_res_to_add_mod_list[0].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::fd_cdm2; + out.zp_csi_rs_res_to_add_mod_list[0].res_map.density.set_one(); + + out.zp_csi_rs_res_to_add_mod_list[0].res_map.freq_band.start_rb = 0; + out.zp_csi_rs_res_to_add_mod_list[0].res_map.freq_band.nrof_rbs = cfg.cell_list[cc].phy_cell.carrier.nof_prb; + out.zp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset_present = true; + out.zp_csi_rs_res_to_add_mod_list[0].periodicity_and_offset.set_slots80() = 1; + + out.p_zp_csi_rs_res_set_present = false; // TEMP + out.p_zp_csi_rs_res_set.set_setup(); + out.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_set_id = 0; + out.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list.resize(1); + out.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list[0] = 0; +} + +/// Fill InitDlBwp with gNB config +int fill_init_dl_bwp_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_dl_ded_s& init_dl_bwp) +{ + init_dl_bwp.pdcch_cfg_present = true; + init_dl_bwp.pdcch_cfg.set_setup() = cfg.cell_list[cc].pdcch_cfg_ded; + + init_dl_bwp.pdsch_cfg_present = true; + fill_pdsch_cfg_from_enb_cfg(cfg, cc, init_dl_bwp.pdsch_cfg.set_setup()); + + // TODO: ADD missing fields + + return SRSRAN_SUCCESS; +} + +void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg_s& out) +{ + // Make 2 PUCCH resource sets + out.res_set_to_add_mod_list.resize(2); + + // Make PUCCH resource set for 1-2 bit + for (uint32_t set_id = 0; set_id < out.res_set_to_add_mod_list.size(); ++set_id) { + auto& res_set = out.res_set_to_add_mod_list[set_id]; + res_set.pucch_res_set_id = set_id; + res_set.res_list.resize(8); + for (uint32_t i = 0; i < res_set.res_list.size(); ++i) { + if (cfg.is_standalone) { + res_set.res_list[i] = i + set_id * 8; + } else { + res_set.res_list[i] = set_id; + } + } + } + + // Make 3 possible resources + out.res_to_add_mod_list.resize(18); + uint32_t j = 0, j2 = 0; + for (uint32_t i = 0; i < out.res_to_add_mod_list.size(); ++i) { + out.res_to_add_mod_list[i].pucch_res_id = i; + out.res_to_add_mod_list[i].intra_slot_freq_hop_present = false; + if (i < 8 or i == 16) { + out.res_to_add_mod_list[i].start_prb = 51; + out.res_to_add_mod_list[i].second_hop_prb_present = true; + out.res_to_add_mod_list[i].second_hop_prb = 0; + out.res_to_add_mod_list[i].format.set_format1().init_cyclic_shift = (4 * (j % 3)); + out.res_to_add_mod_list[i].format.format1().nrof_symbols = 14; + out.res_to_add_mod_list[i].format.format1().start_symbol_idx = 0; + out.res_to_add_mod_list[i].format.format1().time_domain_occ = j / 3; + j++; + } else if (i < 15) { + out.res_to_add_mod_list[i].start_prb = 1; + out.res_to_add_mod_list[i].second_hop_prb_present = true; + out.res_to_add_mod_list[i].second_hop_prb = 50; + out.res_to_add_mod_list[i].format.set_format2().nrof_prbs = 1; + out.res_to_add_mod_list[i].format.format2().nrof_symbols = 2; + out.res_to_add_mod_list[i].format.format2().start_symbol_idx = 2 * (j2 % 7); + j2++; + } else { + out.res_to_add_mod_list[i].start_prb = 50; + out.res_to_add_mod_list[i].second_hop_prb_present = true; + out.res_to_add_mod_list[i].second_hop_prb = 1; + out.res_to_add_mod_list[i].format.set_format2().nrof_prbs = 1; + out.res_to_add_mod_list[i].format.format2().nrof_symbols = 2; + out.res_to_add_mod_list[i].format.format2().start_symbol_idx = 2 * (j2 % 7); + j2++; + } + } + + out.format1_present = true; + out.format1.set_setup(); + + out.format2_present = true; + out.format2.set_setup(); + out.format2.setup().max_code_rate_present = true; + out.format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25; + // NOTE: IMPORTANT!! The gNB expects the CSI to be reported along with HARQ-ACK + // If simul_harq_ack_csi_present = false, PUCCH might not be decoded properly when CSI is reported + out.format2.setup().simul_harq_ack_csi_present = true; + + // SR resources + out.sched_request_res_to_add_mod_list.resize(1); + auto& sr_res1 = out.sched_request_res_to_add_mod_list[0]; + sr_res1.sched_request_res_id = 1; + sr_res1.sched_request_id = 0; + sr_res1.periodicity_and_offset_present = true; + sr_res1.periodicity_and_offset.set_sl40() = 8; + sr_res1.res_present = true; + sr_res1.res = 2; + + // DL data + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + out.dl_data_to_ul_ack.resize(1); + out.dl_data_to_ul_ack[0] = 4; + } else { + out.dl_data_to_ul_ack.resize(6); + out.dl_data_to_ul_ack[0] = 6; + out.dl_data_to_ul_ack[1] = 5; + out.dl_data_to_ul_ack[2] = 4; + out.dl_data_to_ul_ack[3] = 4; + out.dl_data_to_ul_ack[4] = 4; + out.dl_data_to_ul_ack[5] = 4; + } +} + +void fill_pusch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pusch_cfg_s& out) +{ + out.dmrs_ul_for_pusch_map_type_a_present = true; + out.dmrs_ul_for_pusch_map_type_a.set_setup(); + out.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position_present = true; + out.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position = dmrs_ul_cfg_s::dmrs_add_position_opts::pos1; + // PUSH power control skipped + out.res_alloc = pusch_cfg_s::res_alloc_opts::res_alloc_type1; + + // UCI + out.uci_on_pusch_present = true; + out.uci_on_pusch.set_setup(); + out.uci_on_pusch.setup().beta_offsets_present = true; + out.uci_on_pusch.setup().beta_offsets.set_semi_static(); + auto& beta_offset_semi_static = out.uci_on_pusch.setup().beta_offsets.semi_static(); + beta_offset_semi_static.beta_offset_ack_idx1_present = true; + beta_offset_semi_static.beta_offset_ack_idx1 = 9; + beta_offset_semi_static.beta_offset_ack_idx2_present = true; + beta_offset_semi_static.beta_offset_ack_idx2 = 9; + beta_offset_semi_static.beta_offset_ack_idx3_present = true; + beta_offset_semi_static.beta_offset_ack_idx3 = 9; + beta_offset_semi_static.beta_offset_csi_part1_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part1_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx2 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx2 = 6; + + out.uci_on_pusch.setup().scaling = uci_on_pusch_s::scaling_opts::f1; +} + +void fill_init_ul_bwp_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_ul_ded_s& out) +{ + if (cfg.is_standalone) { + out.pucch_cfg_present = true; + fill_pucch_cfg_from_enb_cfg(cfg, cc, out.pucch_cfg.set_setup()); + + out.pusch_cfg_present = true; + fill_pusch_cfg_from_enb_cfg(cfg, cc, out.pusch_cfg.set_setup()); + } +} + +/// Fill InitUlBwp with gNB config +void fill_ul_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, ul_cfg_s& out) +{ + out.init_ul_bwp_present = true; + fill_init_ul_bwp_from_enb_cfg(cfg, cc, out.init_ul_bwp); +} + +/// Fill ServingCellConfig with gNB config +int fill_serv_cell_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, serving_cell_cfg_s& serv_cell) +{ + serv_cell.csi_meas_cfg_present = true; + HANDLE_ERROR(fill_csi_meas_from_enb_cfg(cfg, serv_cell.csi_meas_cfg.set_setup())); + + serv_cell.init_dl_bwp_present = true; + fill_init_dl_bwp_from_enb_cfg(cfg, cc, serv_cell.init_dl_bwp); + + serv_cell.first_active_dl_bwp_id_present = true; + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + serv_cell.first_active_dl_bwp_id = 0; + } else { + serv_cell.first_active_dl_bwp_id = 1; + } + + serv_cell.ul_cfg_present = true; + fill_ul_cfg_from_enb_cfg(cfg, cc, serv_cell.ul_cfg); + + // TODO: remaining fields + + return SRSRAN_SUCCESS; +} + +void fill_pdcch_cfg_common(const rrc_nr_cfg_t& cfg, uint32_t cc, pdcch_cfg_common_s& out); + +/// Fill FrequencyInfoDL with gNB config +int fill_freq_info_dl_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, freq_info_dl_s& freq_info_dl) +{ + auto& cell_cfg = cfg.cell_list.at(cc); + + freq_info_dl.freq_band_list.push_back(cell_cfg.band); + freq_info_dl.absolute_freq_point_a = cell_cfg.dl_absolute_freq_point_a; + freq_info_dl.absolute_freq_ssb_present = true; + freq_info_dl.absolute_freq_ssb = cell_cfg.ssb_absolute_freq_point; + + freq_info_dl.scs_specific_carrier_list.resize(1); + auto& dl_carrier = freq_info_dl.scs_specific_carrier_list[0]; + dl_carrier.offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier; + + if (!asn1::number_to_enum(dl_carrier.subcarrier_spacing, + SRSRAN_SUBC_SPACING_NR(cell_cfg.phy_cell.carrier.scs) / 1000)) { + get_logger(cfg).error("Config Error: Invalid subcarrier spacing (%d).\n", + SRSRAN_SUBC_SPACING_NR(cell_cfg.phy_cell.carrier.scs)); + return SRSRAN_ERROR; + } + + dl_carrier.carrier_bw = cell_cfg.phy_cell.carrier.nof_prb; + + return SRSRAN_SUCCESS; +} + +/// Fill InitDlBwp with gNB config +int fill_init_dl_bwp_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_dl_common_s& init_dl_bwp) +{ + init_dl_bwp.pdcch_cfg_common_present = true; + fill_pdcch_cfg_common(cfg, cc, init_dl_bwp.pdcch_cfg_common.set_setup()); + // TODO: ADD missing fields + return SRSRAN_SUCCESS; +} + +/// Fill DLCellConfigCommon with gNB config +int fill_dl_cfg_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, dl_cfg_common_s& dl_cfg_common) +{ + dl_cfg_common.freq_info_dl_present = true; + HANDLE_ERROR(fill_freq_info_dl_from_enb_cfg(cfg, cc, dl_cfg_common.freq_info_dl)); + + dl_cfg_common.init_dl_bwp_present = true; + HANDLE_ERROR(fill_init_dl_bwp_common_from_enb_cfg(cfg, cc, dl_cfg_common.init_dl_bwp)); + + // TODO: ADD missing fields + + return SRSRAN_SUCCESS; +} + +/// Fill FrequencyInfoUL with gNB config +int fill_freq_info_ul_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, freq_info_ul_s& freq_info_ul) +{ + auto& cell_cfg = cfg.cell_list.at(cc); + + freq_info_ul.freq_band_list.push_back(cell_cfg.band); + freq_info_ul.absolute_freq_point_a_present = true; + freq_info_ul.absolute_freq_point_a = cell_cfg.ul_absolute_freq_point_a; + freq_info_ul.scs_specific_carrier_list.resize(1); + + auto& ul_carrier = freq_info_ul.scs_specific_carrier_list[0]; + ul_carrier.offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier; + + if (!asn1::number_to_enum(ul_carrier.subcarrier_spacing, + SRSRAN_SUBC_SPACING_NR(cell_cfg.phy_cell.carrier.scs) / 1000)) { + get_logger(cfg).error("Config Error: Invalid subcarrier spacing (%d).\n", + SRSRAN_SUBC_SPACING_NR(cell_cfg.phy_cell.carrier.scs)); + return SRSRAN_ERROR; + } + + ul_carrier.carrier_bw = cell_cfg.phy_cell.carrier.nof_prb; + + return SRSRAN_SUCCESS; +} + +int fill_rach_cfg_common(const rrc_nr_cfg_t& cfg, uint32_t cc, rach_cfg_common_s& rach) +{ + // rach-ConfigGeneric + rach.rach_cfg_generic.prach_cfg_idx = 0; + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + // Note: Give more time margin to fit RAR + rach.rach_cfg_generic.prach_cfg_idx = 8; + } + rach.rach_cfg_generic.msg1_fdm.value = rach_cfg_generic_s::msg1_fdm_opts::one; + rach.rach_cfg_generic.msg1_freq_start = 1; // zero not supported with current PRACH implementation + rach.rach_cfg_generic.zero_correlation_zone_cfg = 0; + rach.rach_cfg_generic.preamb_rx_target_pwr = -110; + rach.rach_cfg_generic.preamb_trans_max.value = rach_cfg_generic_s::preamb_trans_max_opts::n7; + rach.rach_cfg_generic.pwr_ramp_step.value = rach_cfg_generic_s::pwr_ramp_step_opts::db4; + rach.rach_cfg_generic.ra_resp_win.value = rach_cfg_generic_s::ra_resp_win_opts::sl10; + + // totalNumberOfRA-Preambles + if (cfg.cell_list[cc].num_ra_preambles != 64) { + rach.total_nof_ra_preambs_present = true; + rach.total_nof_ra_preambs = cfg.cell_list[cc].num_ra_preambles; + } + + // ssb-perRACH-OccasionAndCB-PreamblesPerSSB + rach.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present = true; + if (not asn1::number_to_enum(rach.ssb_per_rach_occasion_and_cb_preambs_per_ssb.set_one(), + cfg.cell_list[cc].num_ra_preambles)) { + get_logger(cfg).error("Invalid number of RA preambles=%d", cfg.cell_list[cc].num_ra_preambles); + return -1; + } + + rach.ra_contention_resolution_timer.value = rach_cfg_common_s::ra_contention_resolution_timer_opts::sf64; + rach.prach_root_seq_idx.set_l839() = cfg.cell_list[cc].prach_root_seq_idx; + rach.restricted_set_cfg.value = rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set; + + return SRSRAN_SUCCESS; +} + +/// Fill InitUlBwp with gNB config +int fill_init_ul_bwp_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_ul_common_s& init_ul_bwp) +{ + init_ul_bwp.rach_cfg_common_present = true; + HANDLE_ERROR(fill_rach_cfg_common(cfg, cc, init_ul_bwp.rach_cfg_common.set_setup())); + + // TODO: Add missing fields + + return SRSRAN_SUCCESS; +} + +/// Fill ULCellConfigCommon with gNB config +int fill_ul_cfg_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, ul_cfg_common_s& ul_cfg_common) +{ + ul_cfg_common.dummy = time_align_timer_opts::ms500; + + ul_cfg_common.freq_info_ul_present = true; + HANDLE_ERROR(fill_freq_info_ul_from_enb_cfg(cfg, cc, ul_cfg_common.freq_info_ul)); + + ul_cfg_common.init_ul_bwp_present = true; + fill_init_ul_bwp_from_enb_cfg(cfg, cc, ul_cfg_common.init_ul_bwp); + + // TODO: Add missing fields + + return SRSRAN_SUCCESS; +} + +/// Fill ServingCellConfigCommon with gNB config +int fill_serv_cell_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, serving_cell_cfg_common_s& serv_common) +{ + auto& cell_cfg = cfg.cell_list.at(cc); + + serv_common.ss_pbch_block_pwr = cell_cfg.pdsch_rs_power; + serv_common.n_timing_advance_offset_present = true; + serv_common.n_timing_advance_offset = serving_cell_cfg_common_s::n_timing_advance_offset_opts::n0; + serv_common.n_timing_advance_offset_present = true; + serv_common.dmrs_type_a_position = serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2; + + serv_common.pci_present = true; + serv_common.pci = cell_cfg.phy_cell.carrier.pci; + + serv_common.ssb_periodicity_serving_cell_present = true; + serv_common.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_s::ssb_periodicity_serving_cell_opts::ms10; + + // Fill SSB config + serv_common.ssb_positions_in_burst_present = true; + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + serv_common.ssb_positions_in_burst.set_short_bitmap().from_number(0b1000); + } else { + serv_common.ssb_positions_in_burst.set_medium_bitmap().from_number(0b10000000); + } + + // Set SSB SCS + serv_common.ssb_subcarrier_spacing_present = true; + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + serv_common.ssb_subcarrier_spacing = subcarrier_spacing_opts::khz15; + } else { + serv_common.ssb_subcarrier_spacing = subcarrier_spacing_opts::khz30; + } + + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + // TDD UL-DL config + serv_common.tdd_ul_dl_cfg_common_present = true; + fill_tdd_ul_dl_config_common(cfg.cell_list[cc], serv_common.tdd_ul_dl_cfg_common); + } + + serv_common.ul_cfg_common_present = true; + fill_ul_cfg_common_from_enb_cfg(cfg, cc, serv_common.ul_cfg_common); + + serv_common.dl_cfg_common_present = true; + fill_dl_cfg_common_from_enb_cfg(cfg, cc, serv_common.dl_cfg_common); + + return SRSRAN_SUCCESS; +} + +/// Fill reconfigurationWithSync with gNB config +int fill_recfg_with_sync_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, recfg_with_sync_s& sync) +{ + sync.sp_cell_cfg_common_present = true; + HANDLE_ERROR(fill_serv_cell_common_from_enb_cfg(cfg, cc, sync.sp_cell_cfg_common)); + + return SRSRAN_SUCCESS; +} + +/// Fill spCellConfig with gNB config +int fill_sp_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, sp_cell_cfg_s& sp_cell) +{ + if (not cfg.is_standalone) { + sp_cell.recfg_with_sync_present = true; + } + HANDLE_ERROR(fill_recfg_with_sync_from_enb_cfg(cfg, cc, sp_cell.recfg_with_sync)); + + sp_cell.sp_cell_cfg_ded_present = true; + HANDLE_ERROR(fill_serv_cell_from_enb_cfg(cfg, cc, sp_cell.sp_cell_cfg_ded)); + + return SRSRAN_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Fill SRB with parameters derived from cfg +void fill_srb(const rrc_nr_cfg_t& cfg, srsran::nr_srb srb_id, asn1::rrc_nr::rlc_bearer_cfg_s& out) +{ + srsran_assert(srb_id > srsran::nr_srb::srb0 and srb_id < srsran::nr_srb::count, "Invalid srb_id argument"); + + out.lc_ch_id = srsran::srb_to_lcid(srb_id); + out.served_radio_bearer_present = true; + out.served_radio_bearer.set_srb_id() = (uint8_t)srb_id; + + if (srb_id == srsran::nr_srb::srb1) { + if (cfg.srb1_cfg.present) { + out.rlc_cfg_present = true; + out.rlc_cfg = cfg.srb1_cfg.rlc_cfg; + } else { + out.rlc_cfg_present = false; + } + } else if (srb_id == srsran::nr_srb::srb2) { + if (cfg.srb2_cfg.present) { + out.rlc_cfg_present = true; + out.rlc_cfg = cfg.srb2_cfg.rlc_cfg; + } else { + out.rlc_cfg_present = false; + } + } else { + out.rlc_cfg_present = true; + auto& ul_am = out.rlc_cfg.set_am().ul_am_rlc; + ul_am.sn_field_len_present = true; + ul_am.sn_field_len.value = asn1::rrc_nr::sn_field_len_am_opts::size12; + ul_am.t_poll_retx.value = asn1::rrc_nr::t_poll_retx_opts::ms45; + ul_am.poll_pdu.value = asn1::rrc_nr::poll_pdu_opts::infinity; + ul_am.poll_byte.value = asn1::rrc_nr::poll_byte_opts::infinity; + ul_am.max_retx_thres.value = asn1::rrc_nr::ul_am_rlc_s::max_retx_thres_opts::t8; + auto& dl_am = out.rlc_cfg.am().dl_am_rlc; + dl_am.sn_field_len_present = true; + dl_am.sn_field_len.value = asn1::rrc_nr::sn_field_len_am_opts::size12; + dl_am.t_reassembly.value = t_reassembly_opts::ms35; + dl_am.t_status_prohibit.value = asn1::rrc_nr::t_status_prohibit_opts::ms0; + } + + // mac-LogicalChannelConfig -- Cond LCH-Setup + out.mac_lc_ch_cfg_present = true; + out.mac_lc_ch_cfg.ul_specific_params_present = true; + out.mac_lc_ch_cfg.ul_specific_params.prio = srb_id == srsran::nr_srb::srb1 ? 1 : 3; + out.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate.value = + lc_ch_cfg_s::ul_specific_params_s_::prioritised_bit_rate_opts::infinity; + out.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur.value = + lc_ch_cfg_s::ul_specific_params_s_::bucket_size_dur_opts::ms5; + out.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; + out.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 0; + out.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; + out.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; + out.mac_lc_ch_cfg.ul_specific_params.lc_ch_sr_mask = false; + out.mac_lc_ch_cfg.ul_specific_params.lc_ch_sr_delay_timer_applied = false; +} + +/// Fill DRB with parameters derived from cfg +void fill_drb(const rrc_nr_cfg_t& cfg, + const enb_bearer_manager::radio_bearer_t& rb, + srsran::nr_drb drb_id, + asn1::rrc_nr::rlc_bearer_cfg_s& out) +{ + out.lc_ch_id = rb.lcid; + out.served_radio_bearer_present = true; + out.served_radio_bearer.set_drb_id() = (uint8_t)drb_id; + + out.rlc_cfg_present = true; + out.rlc_cfg = cfg.five_qi_cfg.at(rb.five_qi).rlc_cfg; + + // MAC logical channel config + out.mac_lc_ch_cfg_present = true; + out.mac_lc_ch_cfg.ul_specific_params_present = true; + out.mac_lc_ch_cfg.ul_specific_params.prio = 11; // TODO + out.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = + lc_ch_cfg_s::ul_specific_params_s_::prioritised_bit_rate_opts::kbps0; + out.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = + asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::bucket_size_dur_opts::ms100; + out.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; + out.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 3; // TODO + out.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; + out.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; // TODO + out.mac_lc_ch_cfg.ul_specific_params.lc_ch_sr_mask = false; + out.mac_lc_ch_cfg.ul_specific_params.lc_ch_sr_delay_timer_applied = false; + // TODO: add LC config to MAC +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/// Fill MasterCellConfig with gNB config +int fill_master_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::cell_group_cfg_s& out) +{ + out.cell_group_id = 0; + out.rlc_bearer_to_add_mod_list.resize(1); + fill_srb(cfg, srsran::nr_srb::srb1, out.rlc_bearer_to_add_mod_list[0]); + + // mac-CellGroupConfig -- Need M + out.mac_cell_group_cfg_present = true; + out.mac_cell_group_cfg.sched_request_cfg_present = true; + out.mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list.resize(1); + out.mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list[0].sched_request_id = 0; + out.mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list[0].sr_trans_max.value = + sched_request_to_add_mod_s::sr_trans_max_opts::n64; + out.mac_cell_group_cfg.bsr_cfg_present = true; + out.mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.value = bsr_cfg_s::periodic_bsr_timer_opts::sf20; + out.mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.value = bsr_cfg_s::retx_bsr_timer_opts::sf320; + out.mac_cell_group_cfg.tag_cfg_present = true; + out.mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list.resize(1); + out.mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list[0].tag_id = 0; + out.mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list[0].time_align_timer.value = time_align_timer_opts::infinity; + out.mac_cell_group_cfg.phr_cfg_present = true; + phr_cfg_s& phr = out.mac_cell_group_cfg.phr_cfg.set_setup(); + phr.phr_periodic_timer.value = asn1::rrc_nr::phr_cfg_s::phr_periodic_timer_opts::sf500; + phr.phr_prohibit_timer.value = asn1::rrc_nr::phr_cfg_s::phr_prohibit_timer_opts::sf200; + phr.phr_tx_pwr_factor_change.value = asn1::rrc_nr::phr_cfg_s::phr_tx_pwr_factor_change_opts::db3; + phr.multiple_phr = false; + phr.dummy = false; + phr.phr_type2_other_cell = false; + phr.phr_mode_other_cg.value = asn1::rrc_nr::phr_cfg_s::phr_mode_other_cg_opts::real; + out.mac_cell_group_cfg.skip_ul_tx_dynamic = false; + out.mac_cell_group_cfg.phr_cfg_present = false; // Note: not supported + + // physicalCellGroupConfig -- Need M + out.phys_cell_group_cfg_present = true; + out.phys_cell_group_cfg.p_nr_fr1_present = true; + out.phys_cell_group_cfg.p_nr_fr1 = 10; + out.phys_cell_group_cfg.pdsch_harq_ack_codebook.value = + phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::dynamic_value; + + // spCellConfig -- Need M + out.sp_cell_cfg_present = true; + fill_sp_cell_cfg_from_enb_cfg(cfg, cc, out.sp_cell_cfg); + + return SRSRAN_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +int fill_mib_from_enb_cfg(const rrc_cell_cfg_nr_t& cell_cfg, asn1::rrc_nr::mib_s& mib) +{ + mib.sys_frame_num.from_number(0); + switch (cell_cfg.phy_cell.carrier.scs) { + case srsran_subcarrier_spacing_15kHz: + case srsran_subcarrier_spacing_60kHz: + mib.sub_carrier_spacing_common.value = mib_s::sub_carrier_spacing_common_opts::scs15or60; + break; + case srsran_subcarrier_spacing_30kHz: + case srsran_subcarrier_spacing_120kHz: + mib.sub_carrier_spacing_common.value = mib_s::sub_carrier_spacing_common_opts::scs30or120; + break; + default: + srsran_terminate("Invalid carrier SCS=%d Hz", SRSRAN_SUBC_SPACING_NR(cell_cfg.phy_cell.carrier.scs)); + } + mib.ssb_subcarrier_offset = cell_cfg.ssb_offset; + mib.dmrs_type_a_position.value = mib_s::dmrs_type_a_position_opts::pos2; + mib.pdcch_cfg_sib1.search_space_zero = 0; + mib.pdcch_cfg_sib1.ctrl_res_set_zero = cell_cfg.coreset0_idx; + mib.cell_barred.value = mib_s::cell_barred_opts::not_barred; + mib.intra_freq_resel.value = mib_s::intra_freq_resel_opts::allowed; + mib.spare.from_number(0); + return SRSRAN_SUCCESS; +} + +// Called for SA and NSA +void fill_pdcch_cfg_common(const rrc_nr_cfg_t& cfg, uint32_t cc, pdcch_cfg_common_s& out) +{ + auto& cell_cfg = cfg.cell_list[cc]; + + out.ctrl_res_set_zero_present = false; + out.search_space_zero_present = false; + + out.common_ctrl_res_set_present = cell_cfg.pdcch_cfg_common.common_ctrl_res_set_present; + out.common_ctrl_res_set = cell_cfg.pdcch_cfg_common.common_ctrl_res_set; + out.common_search_space_list = cell_cfg.pdcch_cfg_common.common_search_space_list; + + out.search_space_sib1_present = true; + out.search_space_sib1 = 0; + out.search_space_other_sys_info_present = true; + out.search_space_other_sys_info = 1; + out.paging_search_space_present = true; + out.paging_search_space = 1; + out.ra_search_space_present = true; + out.ra_search_space = 1; +} + +void fill_pdsch_cfg_common(const rrc_cell_cfg_nr_t& cell_cfg, pdsch_cfg_common_s& cfg) +{ + cfg.pdsch_time_domain_alloc_list.resize(1); + cfg.pdsch_time_domain_alloc_list[0].map_type.value = pdsch_time_domain_res_alloc_s::map_type_opts::type_a; + cfg.pdsch_time_domain_alloc_list[0].start_symbol_and_len = 40; +} + +// Called for SA +void fill_init_dl_bwp(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_dl_common_s& out) +{ + auto& cell_cfg = cfg.cell_list[cc]; + + out.generic_params.location_and_bw = 14025; + out.generic_params.subcarrier_spacing = (subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs; + + out.pdcch_cfg_common_present = true; + fill_pdcch_cfg_common(cfg, cc, out.pdcch_cfg_common.set_setup()); + out.pdsch_cfg_common_present = true; + fill_pdsch_cfg_common(cell_cfg, out.pdsch_cfg_common.set_setup()); +} + +void fill_dl_cfg_common_sib(const rrc_nr_cfg_t& cfg, uint32_t cc, dl_cfg_common_sib_s& out) +{ + auto& cell_cfg = cfg.cell_list[cc]; + + uint32_t scs_hz = SRSRAN_SUBC_SPACING_NR(cell_cfg.phy_cell.carrier.scs); + uint32_t prb_bw = scs_hz * SRSRAN_NRE; + + srsran::srsran_band_helper band_helper; + out.freq_info_dl.freq_band_list.resize(1); + out.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present = true; + out.freq_info_dl.freq_band_list[0].freq_band_ind_nr = cell_cfg.band; + double ssb_freq_start = cell_cfg.ssb_freq_hz - SRSRAN_SSB_BW_SUBC * scs_hz / 2; + double offset_point_a_hz = ssb_freq_start - band_helper.nr_arfcn_to_freq(cell_cfg.dl_absolute_freq_point_a); + uint32_t offset_point_a_prbs = offset_point_a_hz / prb_bw; + out.freq_info_dl.offset_to_point_a = offset_point_a_prbs; + out.freq_info_dl.scs_specific_carrier_list.resize(1); + out.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier; + out.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing = + (subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs; + out.freq_info_dl.scs_specific_carrier_list[0].carrier_bw = cell_cfg.phy_cell.carrier.nof_prb; + + fill_init_dl_bwp(cfg, cc, out.init_dl_bwp); + // disable InitialBWP-Only fields + out.init_dl_bwp.pdcch_cfg_common.setup().ctrl_res_set_zero_present = false; + out.init_dl_bwp.pdcch_cfg_common.setup().search_space_zero_present = false; + + out.bcch_cfg.mod_period_coeff.value = bcch_cfg_s::mod_period_coeff_opts::n4; + + out.pcch_cfg.default_paging_cycle.value = paging_cycle_opts::rf128; + out.pcch_cfg.nand_paging_frame_offset.set_one_t(); + out.pcch_cfg.ns.value = pcch_cfg_s::ns_opts::one; +} + +void fill_ul_cfg_common_sib(const rrc_nr_cfg_t& cfg, uint32_t cc, ul_cfg_common_sib_s& out) +{ + auto& cell_cfg = cfg.cell_list[cc]; + srsran::srsran_band_helper band_helper; + + out.freq_info_ul.freq_band_list.resize(1); + out.freq_info_ul.freq_band_list[0].freq_band_ind_nr_present = true; + out.freq_info_ul.freq_band_list[0].freq_band_ind_nr = cell_cfg.band; + + out.freq_info_ul.absolute_freq_point_a_present = true; + out.freq_info_ul.absolute_freq_point_a = + band_helper.get_abs_freq_point_a_arfcn(cell_cfg.phy_cell.carrier.nof_prb, cell_cfg.ul_arfcn); + + out.freq_info_ul.scs_specific_carrier_list.resize(1); + out.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier; + out.freq_info_ul.scs_specific_carrier_list[0].subcarrier_spacing = + (subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs; + out.freq_info_ul.scs_specific_carrier_list[0].carrier_bw = cell_cfg.phy_cell.carrier.nof_prb; + + out.freq_info_ul.p_max_present = true; + out.freq_info_ul.p_max = 10; + + out.init_ul_bwp.generic_params.location_and_bw = 14025; + out.init_ul_bwp.generic_params.subcarrier_spacing.value = + (subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs; + + out.init_ul_bwp.rach_cfg_common_present = true; + fill_rach_cfg_common(cfg, cc, out.init_ul_bwp.rach_cfg_common.set_setup()); + + out.init_ul_bwp.pusch_cfg_common_present = true; + pusch_cfg_common_s& pusch = out.init_ul_bwp.pusch_cfg_common.set_setup(); + pusch.pusch_time_domain_alloc_list.resize(1); + pusch.pusch_time_domain_alloc_list[0].k2_present = true; + pusch.pusch_time_domain_alloc_list[0].k2 = 4; + pusch.pusch_time_domain_alloc_list[0].map_type.value = pusch_time_domain_res_alloc_s::map_type_opts::type_a; + pusch.pusch_time_domain_alloc_list[0].start_symbol_and_len = 27; + pusch.p0_nominal_with_grant_present = true; + pusch.p0_nominal_with_grant = -76; + + out.init_ul_bwp.pucch_cfg_common_present = true; + pucch_cfg_common_s& pucch = out.init_ul_bwp.pucch_cfg_common.set_setup(); + pucch.pucch_res_common_present = true; + pucch.pucch_res_common = 11; + pucch.pucch_group_hop.value = pucch_cfg_common_s::pucch_group_hop_opts::neither; + pucch.p0_nominal_present = true; + pucch.p0_nominal = -90; + + out.time_align_timer_common.value = time_align_timer_opts::infinity; +} + +int fill_serv_cell_cfg_common_sib(const rrc_nr_cfg_t& cfg, uint32_t cc, serving_cell_cfg_common_sib_s& out) +{ + auto& cell_cfg = cfg.cell_list[cc]; + + fill_dl_cfg_common_sib(cfg, cc, out.dl_cfg_common); + + out.ul_cfg_common_present = true; + fill_ul_cfg_common_sib(cfg, cc, out.ul_cfg_common); + + out.ssb_positions_in_burst.in_one_group.from_number(0x80); + + out.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::ms10; + + // The time advance offset is not supported by the current PHY + out.n_timing_advance_offset_present = true; + out.n_timing_advance_offset = serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n0; + + // TDD UL-DL config + if (cell_cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + out.tdd_ul_dl_cfg_common_present = true; + fill_tdd_ul_dl_config_common(cell_cfg, out.tdd_ul_dl_cfg_common); + } + + out.ss_pbch_block_pwr = cell_cfg.pdsch_rs_power; + + return SRSRAN_SUCCESS; +} + +int fill_sib1_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::sib1_s& sib1) +{ + const rrc_cell_cfg_nr_t& cell_cfg = cfg.cell_list[cc]; + + sib1.cell_sel_info_present = true; + sib1.cell_sel_info.q_rx_lev_min = -70; + sib1.cell_sel_info.q_qual_min_present = true; + sib1.cell_sel_info.q_qual_min = -20; + + sib1.cell_access_related_info.plmn_id_list.resize(1); + sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list.resize(1); + srsran::plmn_id_t plmn; + plmn.from_number(cfg.mcc, cfg.mnc); + srsran::to_asn1(&sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list[0], plmn); + sib1.cell_access_related_info.plmn_id_list[0].tac_present = true; + sib1.cell_access_related_info.plmn_id_list[0].tac.from_number(cell_cfg.tac); + sib1.cell_access_related_info.plmn_id_list[0].cell_id.from_number((cfg.enb_id << 8U) + cell_cfg.phy_cell.cell_id); + sib1.cell_access_related_info.plmn_id_list[0].cell_reserved_for_oper.value = + plmn_id_info_s::cell_reserved_for_oper_opts::not_reserved; + + sib1.conn_est_fail_ctrl_present = true; + sib1.conn_est_fail_ctrl.conn_est_fail_count.value = conn_est_fail_ctrl_s::conn_est_fail_count_opts::n1; + sib1.conn_est_fail_ctrl.conn_est_fail_offset_validity.value = + conn_est_fail_ctrl_s::conn_est_fail_offset_validity_opts::s30; + sib1.conn_est_fail_ctrl.conn_est_fail_offset_present = true; + sib1.conn_est_fail_ctrl.conn_est_fail_offset = 1; + + // sib1.si_sched_info_present = true; + // sib1.si_sched_info.si_request_cfg.rach_occasions_si_present = true; + // sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.ra_resp_win.value = + // rach_cfg_generic_s::ra_resp_win_opts::sl8; + // sib1.si_sched_info.si_win_len.value = si_sched_info_s::si_win_len_opts::s20; + // sib1.si_sched_info.sched_info_list.resize(1); + // sib1.si_sched_info.sched_info_list[0].si_broadcast_status.value = + // sched_info_s::si_broadcast_status_opts::broadcasting; sib1.si_sched_info.sched_info_list[0].si_periodicity.value = + // sched_info_s::si_periodicity_opts::rf16; sib1.si_sched_info.sched_info_list[0].sib_map_info.resize(1); + // // scheduling of SI messages + // sib1.si_sched_info.sched_info_list[0].sib_map_info[0].type.value = sib_type_info_s::type_opts::sib_type2; + // sib1.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag_present = true; + // sib1.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag = 0; + + sib1.serving_cell_cfg_common_present = true; + HANDLE_ERROR(fill_serv_cell_cfg_common_sib(cfg, cc, sib1.serving_cell_cfg_common)); + + sib1.ue_timers_and_consts_present = true; + sib1.ue_timers_and_consts.t300.value = ue_timers_and_consts_s::t300_opts::ms1000; + sib1.ue_timers_and_consts.t301.value = ue_timers_and_consts_s::t301_opts::ms1000; + sib1.ue_timers_and_consts.t310.value = ue_timers_and_consts_s::t310_opts::ms1000; + sib1.ue_timers_and_consts.n310.value = ue_timers_and_consts_s::n310_opts::n1; + sib1.ue_timers_and_consts.t311.value = ue_timers_and_consts_s::t311_opts::ms30000; + sib1.ue_timers_and_consts.n311.value = ue_timers_and_consts_s::n311_opts::n1; + sib1.ue_timers_and_consts.t319.value = ue_timers_and_consts_s::t319_opts::ms1000; + + return SRSRAN_SUCCESS; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool compute_diff_radio_bearer_cfg(const rrc_nr_cfg_t& cfg, + const radio_bearer_cfg_s& prev_bearers, + const radio_bearer_cfg_s& next_bearers, + radio_bearer_cfg_s& diff) +{ + // Compute SRB differences + std::vector srbs_to_rem; + srsran::compute_cfg_diff( + prev_bearers.srb_to_add_mod_list, next_bearers.srb_to_add_mod_list, diff.srb_to_add_mod_list, srbs_to_rem); + + // Compute DRB differences + srsran::compute_cfg_diff(prev_bearers.drb_to_add_mod_list, + next_bearers.drb_to_add_mod_list, + diff.drb_to_add_mod_list, + diff.drb_to_release_list); + + return diff.srb_to_add_mod_list.size() > 0 or diff.drb_to_release_list.size() > 0 or + diff.drb_to_add_mod_list.size() > 0; +} + +int fill_cellgroup_with_radio_bearer_cfg(const rrc_nr_cfg_t& cfg, + const uint32_t rnti, + const enb_bearer_manager& bearer_mapper, + const asn1::rrc_nr::radio_bearer_cfg_s& bearers, + asn1::rrc_nr::cell_group_cfg_s& out) +{ + out.rlc_bearer_to_add_mod_list.clear(); + out.rlc_bearer_to_release_list.clear(); + + // Add SRBs + for (const srb_to_add_mod_s& srb : bearers.srb_to_add_mod_list) { + out.rlc_bearer_to_add_mod_list.push_back({}); + fill_srb(cfg, (srsran::nr_srb)srb.srb_id, out.rlc_bearer_to_add_mod_list.back()); + } + // Add DRBs + for (const drb_to_add_mod_s& drb : bearers.drb_to_add_mod_list) { + out.rlc_bearer_to_add_mod_list.push_back({}); + uint32_t lcid = drb.drb_id + (int)srsran::nr_srb::count - 1; + enb_bearer_manager::radio_bearer_t rb = bearer_mapper.get_lcid_bearer(rnti, lcid); + if (rb.is_valid() and cfg.five_qi_cfg.find(rb.five_qi) != cfg.five_qi_cfg.end()) { + fill_drb(cfg, rb, (srsran::nr_drb)drb.drb_id, out.rlc_bearer_to_add_mod_list.back()); + } else { + return SRSRAN_ERROR; + } + } + + // Release DRBs + for (uint8_t drb_id : bearers.drb_to_release_list) { + out.rlc_bearer_to_release_list.push_back(drb_id); + } + + return SRSRAN_SUCCESS; +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/rrc/rrc_nr.cc b/srsgnb/src/stack/rrc/rrc_nr.cc new file mode 100644 index 000000000..827347e30 --- /dev/null +++ b/srsgnb/src/stack/rrc/rrc_nr.cc @@ -0,0 +1,767 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/rrc/rrc_nr.h" +#include "srsenb/hdr/common/common_enb.h" +#include "srsgnb/hdr/stack/rrc/cell_asn1_config.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_ue.h" +#include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h" +#include "srsran/asn1/rrc_nr_utils.h" +#include "srsran/common/bearer_manager.h" +#include "srsran/common/common_nr.h" +#include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" + +using namespace asn1::rrc_nr; + +namespace srsenb { + +rrc_nr::rrc_nr(srsran::task_sched_handle task_sched_) : + logger(srslog::fetch_basic_logger("RRC-NR")), task_sched(task_sched_) +{} + +rrc_nr::~rrc_nr() {} + +int rrc_nr::init(const rrc_nr_cfg_t& cfg_, + phy_interface_stack_nr* phy_, + mac_interface_rrc_nr* mac_, + rlc_interface_rrc* rlc_, + pdcp_interface_rrc* pdcp_, + ngap_interface_rrc_nr* ngap_, + gtpu_interface_rrc* gtpu_, + enb_bearer_manager& bearer_mapper_, + rrc_eutra_interface_rrc_nr* rrc_eutra_) +{ + phy = phy_; + mac = mac_; + rlc = rlc_; + pdcp = pdcp_; + ngap = ngap_; + gtpu = gtpu_; + bearer_mapper = &bearer_mapper_; + rrc_eutra = rrc_eutra_; + + cfg = cfg_; + + // log cell configs + for (uint32_t i = 0; i < cfg.cell_list.size(); ++i) { + const auto& cell = cfg.cell_list.at(i); + logger.info("Cell idx=%d, pci=%d, nr_dl_arfcn=%d, nr_ul_arfcn=%d, band=%d, duplex=%s, n_rb_dl=%d, ssb_arfcn=%d", + i, + cell.phy_cell.carrier.pci, + cell.dl_arfcn, + cell.ul_arfcn, + cell.band, + cell.duplex_mode == SRSRAN_DUPLEX_MODE_FDD ? "FDD" : "TDD", + cell.phy_cell.carrier.nof_prb, + cell.ssb_absolute_freq_point); + } + + du_cfg = std::make_unique(cfg); + for (uint32_t i = 0; i < cfg.cell_list.size(); ++i) { + int ret = du_cfg->add_cell(); + srsran_assert(ret == SRSRAN_SUCCESS, "Failed to configure NR cell %d", i); + } + + // Generate cell config structs + cell_ctxt = std::make_unique(); + std::unique_ptr master_cell_group = std::make_unique(); + int ret = fill_master_cell_cfg_from_enb_cfg(cfg, 0, *master_cell_group); + srsran_assert(ret == SRSRAN_SUCCESS, "Failed to configure MasterCellGroup"); + cell_ctxt->master_cell_group = std::move(master_cell_group); + + // derived + slot_dur_ms = 1; + + if (generate_sibs() != SRSRAN_SUCCESS) { + logger.error("Couldn't generate SIB messages."); + return SRSRAN_ERROR; + } + + config_phy(); // if PHY is not yet initialized, config will be stored and applied on initialization + config_mac(); + + logger.info("Number of 5QI %d", cfg.five_qi_cfg.size()); + for (const std::pair& five_qi_cfg : cfg.five_qi_cfg) { + logger.info("5QI configuration. 5QI=%d", five_qi_cfg.first); + if (logger.info.enabled()) { + asn1::json_writer js{}; + five_qi_cfg.second.pdcp_cfg.to_json(js); + logger.info("PDCP NR configuration: %s", js.to_string().c_str()); + js = {}; + five_qi_cfg.second.rlc_cfg.to_json(js); + logger.info("RLC NR configuration: %s", js.to_string().c_str()); + } + } + logger.info("NIA preference list: NIA%d, NIA%d, NIA%d", + cfg.nia_preference_list[0], + cfg.nia_preference_list[1], + cfg.nia_preference_list[2]); + logger.info("NEA preference list: NEA%d, NEA%d, NEA%d", + cfg.nea_preference_list[0], + cfg.nea_preference_list[1], + cfg.nea_preference_list[2]); + running = true; + + return SRSRAN_SUCCESS; +} + +void rrc_nr::stop() +{ + if (running) { + running = false; + } + users.clear(); +} + +template +void rrc_nr::log_rrc_message(const char* source, + const direction_t dir, + srsran::const_byte_span pdu, + const T& msg, + const char* msg_type) +{ + if (logger.debug.enabled()) { + asn1::json_writer json_writer; + msg.to_json(json_writer); + logger.debug(pdu.data(), pdu.size(), "%s - %s %s (%d B)", source, (dir == Rx) ? "Rx" : "Tx", msg_type, pdu.size()); + logger.debug("Content:%s", json_writer.to_string().c_str()); + } else if (logger.info.enabled()) { + logger.info(pdu.data(), pdu.size(), "%s - %s %s (%d B)", source, (dir == Rx) ? "Rx" : "Tx", msg_type, pdu.size()); + } +} +template void rrc_nr::log_rrc_message(const char* source, + const direction_t dir, + srsran::const_byte_span pdu, + const dl_ccch_msg_s& msg, + const char* msg_type); +template void rrc_nr::log_rrc_message(const char* source, + const direction_t dir, + srsran::const_byte_span pdu, + const dl_dcch_msg_s& msg, + const char* msg_type); +template void rrc_nr::log_rrc_message(const char* source, + const direction_t dir, + srsran::const_byte_span pdu, + const cell_group_cfg_s& msg, + const char* msg_type); +template void rrc_nr::log_rrc_message(const char* source, + const direction_t dir, + srsran::const_byte_span pdu, + const radio_bearer_cfg_s& msg, + const char* msg_type); + +void rrc_nr::log_rx_pdu_fail(uint16_t rnti, + uint32_t lcid, + srsran::const_byte_span pdu, + const char* cause_str, + bool log_hex) +{ + if (log_hex) { + logger.error( + pdu.data(), pdu.size(), "Rx %s PDU, rnti=0x%x - Discarding. Cause: %s", get_rb_name(lcid), rnti, cause_str); + } else { + logger.error("Rx %s PDU, rnti=0x%x - Discarding. Cause: %s", get_rb_name(lcid), rnti, cause_str); + } +} + +/* @brief PRIVATE function, gets called by sgnb_addition_request + * + * This function WILL NOT TRIGGER the RX MSG3 activity timer + */ +int rrc_nr::add_user(uint16_t rnti, uint32_t pcell_cc_idx, bool start_msg3_timer) +{ + if (users.contains(rnti) == 0) { + // If in the ue ctor, "start_msg3_timer" is set to true, this will start the MSG3 RX TIMEOUT at ue creation + users.insert(rnti, std::make_unique(this, rnti, pcell_cc_idx, start_msg3_timer)); + rlc->add_user(rnti); + pdcp->add_user(rnti); + logger.info("Added new user rnti=0x%x", rnti); + return SRSRAN_SUCCESS; + } + logger.error("Adding user rnti=0x%x (already exists)", rnti); + return SRSRAN_ERROR; +} + +/* @brief PUBLIC function, gets called by mac_nr::rach_detected + * + * This function is called from PRACH worker (can wait) and WILL TRIGGER the RX MSG3 activity timer + */ +int rrc_nr::add_user(uint16_t rnti, uint32_t pcell_cc_idx) +{ + // Set "triggered_by_rach" to true to start the MSG3 RX TIMEOUT + return add_user(rnti, pcell_cc_idx, true); +} + +void rrc_nr::rem_user(uint16_t rnti) +{ + auto user_it = users.find(rnti); + if (user_it != users.end()) { + // First remove MAC and GTPU to stop processing DL/UL traffic for this user + mac->remove_ue(rnti); // MAC handles PHY + rlc->rem_user(rnti); + pdcp->rem_user(rnti); + users.erase(rnti); + + srsran::console("Disconnecting rnti=0x%x.\n", rnti); + logger.info("Removed user rnti=0x%x", rnti); + } else { + logger.error("Removing user rnti=0x%x (does not exist)", rnti); + } +} + +/// This function is called when the INACTIVITY TIMER FOR +int rrc_nr::rrc_release(uint16_t rnti) +{ + // TODO: we do not have yet a defined procedure to handle this + return SRSRAN_SUCCESS; +} + +/* Function called by MAC after the reception of a C-RNTI CE indicating that the UE still has a + * valid RNTI. + */ +int rrc_nr::update_user(uint16_t new_rnti, uint16_t old_rnti) +{ + if (new_rnti == old_rnti) { + logger.warning("rnti=0x%x received MAC CRNTI CE with same rnti", new_rnti); + return SRSRAN_ERROR; + } + + // Remove new_rnti + auto new_ue_it = users.find(new_rnti); + if (new_ue_it != users.end()) { + new_ue_it->second->deactivate_bearers(); + task_sched.defer_task([this, new_rnti]() { rem_user(new_rnti); }); + } + + // Send Reconfiguration to old_rnti if is RRC_CONNECT or RRC Release if already released here + auto old_it = users.find(old_rnti); + if (old_it == users.end()) { + logger.info("rnti=0x%x received MAC CRNTI CE: 0x%x, but old context is unavailable", new_rnti, old_rnti); + return SRSRAN_ERROR; + } + ue* ue_ptr = old_it->second.get(); + + logger.info("Resuming rnti=0x%x RRC connection due to received C-RNTI CE from rnti=0x%x.", old_rnti, new_rnti); + ue_ptr->crnti_ce_received(); + + return SRSRAN_SUCCESS; +} + +void rrc_nr::set_activity_user(uint16_t rnti) +{ + auto it = users.find(rnti); + if (it == users.end()) { + logger.info("rnti=0x%x not found. Can't set activity", rnti); + return; + } + ue* ue_ptr = it->second.get(); + + // Restart inactivity timer for RRC-NR + ue_ptr->set_activity(); + + // inform EUTRA RRC about user activity + if (ue_ptr->is_endc()) { + // inform EUTRA RRC about user activity + rrc_eutra->set_activity_user(ue_ptr->get_eutra_rnti()); + } +} + +void rrc_nr::config_phy() +{ + srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {}; + common_cfg.carrier = cfg.cell_list[0].phy_cell.carrier; + fill_phy_pdcch_cfg_common(du_cfg->cell(0), &common_cfg.pdcch); + bool ret = srsran::fill_phy_pdcch_cfg( + cell_ctxt->master_cell_group->sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(), &common_cfg.pdcch); + srsran_assert(ret, "Failed to generate Dedicated PDCCH config"); + srsran::make_phy_rach_cfg(du_cfg->cell(0).serv_cell_cfg_common().ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), + cfg.cell_list[0].duplex_mode, + &common_cfg.prach); + common_cfg.duplex_mode = cfg.cell_list[0].duplex_mode; + ret = srsran::fill_phy_ssb_cfg( + cfg.cell_list[0].phy_cell.carrier, du_cfg->cell(0).serv_cell_cfg_common(), &common_cfg.ssb); + srsran_assert(ret, "Failed to generate PHY config"); + if (phy->set_common_cfg(common_cfg) < SRSRAN_SUCCESS) { + logger.error("Couldn't set common PHY config"); + return; + } +} + +void rrc_nr::config_mac() +{ + uint32_t cc = 0; + // Fill MAC scheduler configuration for SIBs + // TODO: use parsed cell NR cfg configuration + srsran::phy_cfg_nr_default_t::reference_cfg_t ref_args{}; + ref_args.duplex = cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD + ? srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_TDD_CUSTOM_6_4 + : srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD; + std::vector sched_cells_cfg(1, get_default_cell_cfg(srsran::phy_cfg_nr_default_t{ref_args})); + sched_nr_cell_cfg_t& cell = sched_cells_cfg[cc]; + + // Derive cell config from rrc_nr_cfg_t + fill_phy_pdcch_cfg_common(du_cfg->cell(cc), &cell.bwps[0].pdcch); + bool ret = srsran::fill_phy_pdcch_cfg( + cell_ctxt->master_cell_group->sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(), &cell.bwps[0].pdcch); + srsran_assert(ret, "Failed to generate Dedicated PDCCH config"); + cell.pci = cfg.cell_list[cc].phy_cell.carrier.pci; + cell.nof_layers = cfg.cell_list[cc].phy_cell.carrier.max_mimo_layers; + cell.dl_cell_nof_prb = cfg.cell_list[cc].phy_cell.carrier.nof_prb; + cell.ul_cell_nof_prb = cfg.cell_list[cc].phy_cell.carrier.nof_prb; + cell.dl_center_frequency_hz = cfg.cell_list[cc].phy_cell.carrier.dl_center_frequency_hz; + cell.ul_center_frequency_hz = cfg.cell_list[cc].phy_cell.carrier.ul_center_frequency_hz; + cell.ssb_center_freq_hz = cfg.cell_list[cc].phy_cell.carrier.ssb_center_freq_hz; + cell.dmrs_type_a_position = du_cfg->cell(cc).mib.dmrs_type_a_position; + cell.pdcch_cfg_sib1 = du_cfg->cell(cc).mib.pdcch_cfg_sib1; + if (du_cfg->cell(cc).serv_cell_cfg_common().tdd_ul_dl_cfg_common_present) { + cell.tdd_ul_dl_cfg_common.emplace(du_cfg->cell(cc).serv_cell_cfg_common().tdd_ul_dl_cfg_common); + } + cell.dl_cfg_common = du_cfg->cell(cc).serv_cell_cfg_common().dl_cfg_common; + cell.ul_cfg_common = du_cfg->cell(cc).serv_cell_cfg_common().ul_cfg_common; + cell.ss_pbch_block_power = du_cfg->cell(cc).serv_cell_cfg_common().ss_pbch_block_pwr; + bool valid_cfg = srsran::make_pdsch_cfg_from_serv_cell(cell_ctxt->master_cell_group->sp_cell_cfg.sp_cell_cfg_ded, + &cell.bwps[0].pdsch); + srsran_assert(valid_cfg, "Invalid NR cell configuration."); + cell.ssb_positions_in_burst = du_cfg->cell(cc).serv_cell_cfg_common().ssb_positions_in_burst; + cell.ssb_periodicity_ms = du_cfg->cell(cc).serv_cell_cfg_common().ssb_periodicity_serving_cell.to_number(); + cell.ssb_scs.value = (subcarrier_spacing_e::options)cfg.cell_list[0].phy_cell.carrier.scs; + cell.ssb_offset = du_cfg->cell(cc).mib.ssb_subcarrier_offset; + if (not cfg.is_standalone) { + const serving_cell_cfg_common_s& serv_cell = + cell_ctxt->master_cell_group->sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common; + // Derive cell config from ASN1 + cell.ssb_scs = serv_cell.ssb_subcarrier_spacing; + } + + // Set SIB1 and SI messages + cell.sibs.resize(cell_ctxt->sib_buffer.size()); + for (uint32_t i = 0; i < cell_ctxt->sib_buffer.size(); i++) { + cell.sibs[i].len = cell_ctxt->sib_buffer[i]->N_bytes; + if (i == 0) { + cell.sibs[i].period_rf = 16; // SIB1 is always 16 rf + cell.sibs[i].si_window_slots = 160; + } else { + cell.sibs[i].period_rf = du_cfg->cell(0).sib1.si_sched_info.sched_info_list[i - 1].si_periodicity.to_number(); + cell.sibs[i].si_window_slots = du_cfg->cell(0).sib1.si_sched_info.si_win_len.to_number(); + } + } + + // Configure MAC/scheduler + mac->cell_cfg(sched_cells_cfg); + + // Make default UE PHY config object + cell_ctxt->default_phy_ue_cfg_nr = get_common_ue_phy_cfg(cell); +} + +int32_t rrc_nr::generate_sibs() +{ + if (not cfg.is_standalone) { + return SRSRAN_SUCCESS; + } + + // SIB1 packing + const si_sched_info_s::sched_info_list_l_& sched_info = du_cfg->cell(0).sib1.si_sched_info.sched_info_list; + + // SI messages packing + cell_ctxt->sibs.resize(1); + sib2_s& sib2 = cell_ctxt->sibs[0].set_sib2(); + sib2.cell_resel_info_common.q_hyst.value = sib2_s::cell_resel_info_common_s_::q_hyst_opts::db5; + + // msg is array of SI messages, each SI message msg[i] may contain multiple SIBs + // all SIBs in a SI message msg[i] share the same periodicity + const uint32_t nof_messages = + du_cfg->cell(0).sib1.si_sched_info_present ? du_cfg->cell(0).sib1.si_sched_info.sched_info_list.size() : 0; + cell_ctxt->sib_buffer.reserve(nof_messages + 1); + asn1::dyn_array msg(nof_messages + 1); + + // Copy SIB1 to first SI message + msg[0].msg.set_c1().set_sib_type1() = du_cfg->cell(0).sib1; + + // Copy rest of SIBs + for (uint32_t sched_info_elem = 0; sched_info_elem < nof_messages; sched_info_elem++) { + uint32_t msg_index = sched_info_elem + 1; // first msg is SIB1, therefore start with second + + msg[msg_index].msg.set_c1().set_sys_info().crit_exts.set_sys_info(); + auto& sib_list = msg[msg_index].msg.c1().sys_info().crit_exts.sys_info().sib_type_and_info; + + for (uint32_t mapping = 0; mapping < sched_info[sched_info_elem].sib_map_info.size(); ++mapping) { + uint32_t sibidx = sched_info[sched_info_elem].sib_map_info[mapping].type; // SIB2 == 0 + sib_list.push_back(cell_ctxt->sibs[sibidx]); + } + } + + // Pack payload for all messages + for (uint32_t msg_index = 0; msg_index < nof_messages + 1; msg_index++) { + srsran::unique_byte_buffer_t sib_pdu = pack_into_pdu(msg[msg_index]); + if (sib_pdu == nullptr) { + logger.error("Failed to pack SIB"); + return SRSRAN_ERROR; + } + cell_ctxt->sib_buffer.push_back(std::move(sib_pdu)); + + // Log SIBs in JSON format + fmt::memory_buffer strbuf; + if (msg_index == 0) { + fmt::format_to(strbuf, "SIB1 payload"); + } else { + fmt::format_to(strbuf, "SI message={} payload", msg_index + 1); + } + log_rrc_message("BCCH", Tx, *cell_ctxt->sib_buffer.back(), msg[msg_index], srsran::to_c_str(strbuf)); + } + + return SRSRAN_SUCCESS; +} + +/******************************************************************************* + MAC interface +*******************************************************************************/ + +int rrc_nr::read_pdu_bcch_bch(const uint32_t tti, srsran::byte_buffer_t& buffer) +{ + if (du_cfg->cell(0).packed_mib == nullptr || buffer.get_tailroom() < du_cfg->cell(0).packed_mib->N_bytes) { + return SRSRAN_ERROR; + } + buffer = *du_cfg->cell(0).packed_mib; + return SRSRAN_SUCCESS; +} + +int rrc_nr::read_pdu_bcch_dlsch(uint32_t sib_index, srsran::byte_buffer_t& buffer) +{ + if (sib_index >= cell_ctxt->sib_buffer.size()) { + logger.error("SI%s%d is not a configured SIB.", sib_index == 0 ? "B" : "", sib_index + 1); + return SRSRAN_ERROR; + } + + buffer = *cell_ctxt->sib_buffer[sib_index]; + + return SRSRAN_SUCCESS; +} + +void rrc_nr::get_metrics(srsenb::rrc_metrics_t& m) +{ + if (running) { + for (auto& ue : users) { + rrc_ue_metrics_t ue_metrics; + ue.second->get_metrics(ue_metrics); + m.ues.push_back(ue_metrics); + } + } +} + +void rrc_nr::handle_pdu(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu) +{ + switch (static_cast(lcid)) { + case srsran::nr_srb::srb0: + handle_ul_ccch(rnti, pdu); + break; + case srsran::nr_srb::srb1: + case srsran::nr_srb::srb2: + case srsran::nr_srb::srb3: + handle_ul_dcch(rnti, lcid, std::move(pdu)); + break; + default: + std::string errcause = fmt::format("Invalid LCID=%d", lcid); + log_rx_pdu_fail(rnti, lcid, pdu, errcause.c_str()); + break; + } +} + +void rrc_nr::handle_ul_ccch(uint16_t rnti, srsran::const_byte_span pdu) +{ + // Parse UL-CCCH + ul_ccch_msg_s ul_ccch_msg; + { + asn1::cbit_ref bref(pdu.data(), pdu.size()); + if (ul_ccch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or + ul_ccch_msg.msg.type().value != ul_ccch_msg_type_c::types_opts::c1) { + log_rx_pdu_fail(rnti, srb_to_lcid(lte_srb::srb0), pdu, "Failed to unpack UL-CCCH message", true); + return; + } + } + + // Log Rx message + fmt::memory_buffer fmtbuf, fmtbuf2; + fmt::format_to(fmtbuf, "rnti=0x{:x}, SRB0", rnti); + fmt::format_to(fmtbuf2, "UL-CCCH.{}", ul_ccch_msg.msg.c1().type().to_string()); + log_rrc_message(srsran::to_c_str(fmtbuf), Rx, pdu, ul_ccch_msg, srsran::to_c_str(fmtbuf2)); + + // Handle message + switch (ul_ccch_msg.msg.c1().type().value) { + case ul_ccch_msg_type_c::c1_c_::types_opts::rrc_setup_request: + handle_rrc_setup_request(rnti, ul_ccch_msg.msg.c1().rrc_setup_request()); + break; + case ul_ccch_msg_type_c::c1_c_::types_opts::rrc_reest_request: + handle_rrc_reest_request(rnti, ul_ccch_msg.msg.c1().rrc_reest_request()); + break; + default: + log_rx_pdu_fail(rnti, srb_to_lcid(lte_srb::srb0), pdu, "Unsupported UL-CCCH message type"); + // TODO Remove user + } +} + +void rrc_nr::handle_ul_dcch(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu) +{ + // Parse UL-DCCH + ul_dcch_msg_s ul_dcch_msg; + { + asn1::cbit_ref bref(pdu.data(), pdu.size()); + if (ul_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or + ul_dcch_msg.msg.type().value != ul_dcch_msg_type_c::types_opts::c1) { + log_rx_pdu_fail(rnti, lcid, pdu, "Failed to unpack UL-DCCH message"); + return; + } + } + + // Verify UE exists + auto ue_it = users.find(rnti); + if (ue_it == users.end()) { + log_rx_pdu_fail(rnti, lcid, pdu, "Inexistent rnti"); + } + ue& u = *ue_it->second; + + // Log Rx message + fmt::memory_buffer fmtbuf, fmtbuf2; + fmt::format_to(fmtbuf, "rnti=0x{:x}, {}", rnti, srsran::get_srb_name(srsran::nr_lcid_to_srb(lcid))); + fmt::format_to(fmtbuf2, "UL-DCCH.{}", ul_dcch_msg.msg.c1().type().to_string()); + log_rrc_message(srsran::to_c_str(fmtbuf), Rx, pdu, ul_dcch_msg, srsran::to_c_str(fmtbuf2)); + + // Handle message + switch (ul_dcch_msg.msg.c1().type().value) { + case ul_dcch_msg_type_c::c1_c_::types_opts::rrc_setup_complete: + u.handle_rrc_setup_complete(ul_dcch_msg.msg.c1().rrc_setup_complete()); + break; + case ul_dcch_msg_type_c::c1_c_::types_opts::security_mode_complete: + u.handle_security_mode_complete(ul_dcch_msg.msg.c1().security_mode_complete()); + break; + case ul_dcch_msg_type_c::c1_c_::types_opts::rrc_recfg_complete: + u.handle_rrc_reconfiguration_complete(ul_dcch_msg.msg.c1().rrc_recfg_complete()); + break; + case ul_dcch_msg_type_c::c1_c_::types_opts::ul_info_transfer: + u.handle_ul_information_transfer(ul_dcch_msg.msg.c1().ul_info_transfer()); + break; + case ul_dcch_msg_type_c::c1_c_::types_opts::rrc_reest_complete: + u.handle_rrc_reestablishment_complete(ul_dcch_msg.msg.c1().rrc_reest_complete()); + break; + case ul_dcch_msg_type_c::c1_c_::types_opts::ue_cap_info: + u.handle_ue_capability_information(ul_dcch_msg.msg.c1().ue_cap_info()); + break; + default: + log_rx_pdu_fail(rnti, srb_to_lcid(lte_srb::srb0), pdu, "Unsupported UL-CCCH message type", false); + // TODO Remove user + } +} + +void rrc_nr::handle_rrc_setup_request(uint16_t rnti, const asn1::rrc_nr::rrc_setup_request_s& msg) +{ + auto ue_it = users.find(rnti); + + // TODO: Defer creation of ue to this point + if (ue_it == users.end()) { + logger.error("%s received for inexistent rnti=0x%x", "UL-CCCH", rnti); + return; + } + ue& u = *ue_it->second; + u.handle_rrc_setup_request(msg); +} + +void rrc_nr::handle_rrc_reest_request(uint16_t rnti, const asn1::rrc_nr::rrc_reest_request_s& msg) +{ + auto ue_it = users.find(rnti); + + // TODO: Defer creation of ue to this point + if (ue_it == users.end()) { + logger.error("%s received for inexistent rnti=0x%x", "UL-CCCH", rnti); + return; + } + ue& u = *ue_it->second; + u.handle_rrc_reestablishment_request(msg); +} + +/******************************************************************************* + PDCP interface +*******************************************************************************/ +void rrc_nr::write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) +{ + if (pdu == nullptr or pdu->N_bytes == 0) { + logger.error("Rx %s PDU, rnti=0x%x - Discarding. Cause: PDU is empty", srsenb::get_rb_name(lcid), rnti); + return; + } + handle_pdu(rnti, lcid, *pdu); +} + +void rrc_nr::notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) {} + +/******************************************************************************* + NGAP interface +*******************************************************************************/ + +int rrc_nr::ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) +{ + logger.debug("Setting securtiy key for rnti=0x%x", rnti); + auto ue_it = users.find(rnti); + + if (ue_it == users.end()) { + logger.error("Trying to set key for non-existing rnti=0x%x", rnti); + return SRSRAN_ERROR; + } + ue& u = *ue_it->second; + u.set_security_key(key); + return SRSRAN_SUCCESS; +} +int rrc_nr::ue_set_bitrates(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) +{ + return SRSRAN_SUCCESS; +} +int rrc_nr::set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap::ue_aggregate_maximum_bit_rate_s& rates) +{ + return SRSRAN_SUCCESS; +} +int rrc_nr::ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap::ue_security_cap_s& caps) +{ + logger.debug("Setting securtiy capabilites for rnti=0x%x", rnti); + auto ue_it = users.find(rnti); + + if (ue_it == users.end()) { + logger.error("Trying to set security capabilities for non-existing rnti=0x%x", rnti); + return SRSRAN_ERROR; + } + ue& u = *ue_it->second; + u.set_security_capabilities(caps); + return SRSRAN_SUCCESS; +} +int rrc_nr::start_security_mode_procedure(uint16_t rnti, srsran::unique_byte_buffer_t nas_pdu) +{ + auto user_it = users.find(rnti); + if (user_it == users.end()) { + logger.error("Starting SecurityModeCommand procedure failed - rnti=0x%x not found", rnti); + return SRSRAN_ERROR; + } + user_it->second->send_security_mode_command(std::move(nas_pdu)); + return SRSRAN_SUCCESS; +} +int rrc_nr::establish_rrc_bearer(uint16_t rnti, + uint16_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) +{ + if (not users.contains(rnti)) { + logger.error("Establishing RRC bearers for inexistent rnti=0x%x", rnti); + return SRSRAN_ERROR; + } + + users[rnti]->establish_eps_bearer(pdu_session_id, nas_pdu, lcid, five_qi); + + // TODO: verify whether this is the best place where to call the RRCReconfig + users[rnti]->send_rrc_reconfiguration(); + return SRSRAN_SUCCESS; +} + +int rrc_nr::release_bearers(uint16_t rnti) +{ + return SRSRAN_SUCCESS; +} + +void rrc_nr::release_user(uint16_t rnti) +{ + if (not users.contains(rnti)) { + logger.warning("User rnti=0x%x has already been released", rnti); + return; + } + + users[rnti]->send_rrc_release(); +} + +int rrc_nr::allocate_lcid(uint16_t rnti) +{ + return SRSRAN_SUCCESS; +} + +void rrc_nr::write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) +{ + if (not users.contains(rnti)) { + logger.error("Received DL information transfer for inexistent rnti=0x%x", rnti); + return; + } + if (sdu == nullptr or sdu->size() == 0) { + logger.error("Received empty DL information transfer for rnti=0x%x", rnti); + return; + } + users[rnti]->send_dl_information_transfer(std::move(sdu)); +} + +/******************************************************************************* + Interface for EUTRA RRC +*******************************************************************************/ + +void rrc_nr::sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) +{ + // try to allocate new user + sched_nr_ue_cfg_t uecfg{}; + uecfg.carriers.resize(1); + uecfg.carriers[0].active = true; + uecfg.carriers[0].cc = 0; + uecfg.phy_cfg = cell_ctxt->default_phy_ue_cfg_nr; + + uint16_t nr_rnti = mac->reserve_rnti(0, uecfg); + if (nr_rnti == SRSRAN_INVALID_RNTI) { + logger.error("Failed to allocate RNTI at MAC"); + rrc_eutra->sgnb_addition_reject(eutra_rnti); + return; + } + + if (add_user(nr_rnti, 0, false) != SRSRAN_SUCCESS) { + logger.error("Failed to allocate RNTI at RRC"); + rrc_eutra->sgnb_addition_reject(eutra_rnti); + return; + } + + // new RNTI is now registered at MAC and RRC + auto user_it = users.find(nr_rnti); + if (user_it == users.end()) { + logger.warning("Unrecognised rnti: 0x%x", nr_rnti); + return; + } + user_it->second->handle_sgnb_addition_request(eutra_rnti, params); +} + +void rrc_nr::sgnb_reconfiguration_complete(uint16_t eutra_rnti, const asn1::dyn_octstring& reconfig_response) +{ + // user has completeted the reconfiguration and has acked on 4G side, wait until RA on NR + logger.info("Received Reconfiguration complete for RNTI=0x%x", eutra_rnti); +} + +void rrc_nr::sgnb_release_request(uint16_t nr_rnti) +{ + // remove user + auto it = users.find(nr_rnti); + uint16_t eutra_rnti = it != users.end() ? it->second->get_eutra_rnti() : SRSRAN_INVALID_RNTI; + rem_user(nr_rnti); + if (eutra_rnti != SRSRAN_INVALID_RNTI) { + rrc_eutra->sgnb_release_ack(eutra_rnti); + } +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc b/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc new file mode 100644 index 000000000..29536cff2 --- /dev/null +++ b/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc @@ -0,0 +1,363 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h" +#include "srsran/common/band_helper.h" + +#define HANDLE_ERROR(x) \ + do { \ + if (x != SRSRAN_SUCCESS) { \ + return SRSRAN_ERROR; \ + } \ + } while (0) + +#define ERROR_IF_NOT(x, fmt, ...) \ + do { \ + if (not(x)) { \ + fprintf(stderr, "ERROR: " fmt "\n", ##__VA_ARGS__); \ + return SRSRAN_ERROR; \ + } \ + } while (0) + +using namespace asn1::rrc_nr; + +namespace srsenb { + +uint32_t coreset_get_bw(const asn1::rrc_nr::ctrl_res_set_s& coreset) +{ + uint32_t prb_count = 0; + + // Iterate all the frequency domain resources bit-map... + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { + // ... and count 6 PRB for every frequency domain resource that it is enabled + if (coreset.freq_domain_res.get(i)) { + prb_count += 6; + } + } + + // Return the total count of physical resource blocks + return prb_count; +} + +int coreset_get_pdcch_nr_max_candidates(const asn1::rrc_nr::ctrl_res_set_s& coreset, uint32_t aggregation_level) +{ + uint32_t coreset_bw = coreset_get_bw(coreset); + uint32_t nof_cce = (coreset_bw * coreset.dur) / 6; + + uint32_t L = 1U << aggregation_level; + uint32_t nof_candidates = nof_cce / L; + + return SRSRAN_MIN(nof_candidates, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR); +} + +ctrl_res_set_s make_default_coreset(uint8_t coreset_id, uint32_t nof_prb) +{ + ctrl_res_set_s coreset; + coreset.ctrl_res_set_id = coreset_id; + // Generate frequency resources for the full BW + for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) { + coreset.freq_domain_res.set(coreset.freq_domain_res.length() - i - 1, i < SRSRAN_FLOOR(nof_prb, 6)); + } + coreset.dur = 1; + coreset.cce_reg_map_type.set_non_interleaved(); + coreset.precoder_granularity.value = ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; + return coreset; +} + +search_space_s make_default_common_search_space(uint8_t ss_id, const ctrl_res_set_s& cs) +{ + search_space_s ss; + ss.search_space_id = ss_id; + ss.ctrl_res_set_id_present = true; + ss.ctrl_res_set_id = cs.ctrl_res_set_id; + ss.dur_present = false; // false for duration=1 + ss.monitoring_slot_periodicity_and_offset_present = true; + ss.monitoring_slot_periodicity_and_offset.set_sl1(); + ss.monitoring_symbols_within_slot_present = true; + ss.monitoring_symbols_within_slot.from_number(0b10000000000000); + ss.search_space_type_present = true; + ss.search_space_type.set_common().dci_format0_minus0_and_format1_minus0_present = true; + ss.nrof_candidates_present = true; + uint32_t nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 0), 2); + asn1::number_to_enum(ss.nrof_candidates.aggregation_level1, nof_cand); + nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 1), 2); + asn1::number_to_enum(ss.nrof_candidates.aggregation_level2, nof_cand); + nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 2), 2); + asn1::number_to_enum(ss.nrof_candidates.aggregation_level4, nof_cand); + nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 3), 2); + asn1::number_to_enum(ss.nrof_candidates.aggregation_level8, nof_cand); + nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 4), 2); + asn1::number_to_enum(ss.nrof_candidates.aggregation_level16, nof_cand); + + return ss; +} + +/// Generate default phy cell configuration +void generate_default_nr_phy_cell(phy_cell_cfg_nr_t& phy_cell) +{ + phy_cell = {}; + + phy_cell.carrier.scs = srsran_subcarrier_spacing_15kHz; + phy_cell.carrier.nof_prb = 52; + phy_cell.carrier.max_mimo_layers = 1; +} + +/// Generate default rrc nr cell configuration +void generate_default_nr_cell(rrc_cell_cfg_nr_t& cell) +{ + cell = {}; + cell.coreset0_idx = 7; + cell.ssb_absolute_freq_point = 0; // auto derived + cell.num_ra_preambles = 8; + generate_default_nr_phy_cell(cell.phy_cell); + + // PDCCH + // - Add CORESET#2 as UE-specific + cell.pdcch_cfg_ded.ctrl_res_set_to_add_mod_list.resize(1); + auto& coreset2 = cell.pdcch_cfg_ded.ctrl_res_set_to_add_mod_list[0]; + coreset2 = make_default_coreset(2, cell.phy_cell.carrier.nof_prb); + + // - Add SearchSpace#2 as UE-specific -> CORESET#2 + cell.pdcch_cfg_ded.search_spaces_to_add_mod_list.resize(1); + auto& ss2 = cell.pdcch_cfg_ded.search_spaces_to_add_mod_list[0]; + ss2 = make_default_common_search_space(2, coreset2); + ss2.search_space_type.set_ue_specific().dci_formats.value = asn1::rrc_nr::search_space_s::search_space_type_c_:: + ue_specific_s_::dci_formats_opts::formats0_minus0_and_minus1_minus0; +} + +int derive_ssb_params(bool is_sa, + uint32_t dl_arfcn, + uint32_t band, + srsran_subcarrier_spacing_t pdcch_scs, + uint32_t coreset0_idx, + uint32_t nof_prb, + rrc_cell_cfg_nr_t& cell) +{ + // Verify essential parameters are specified and valid + ERROR_IF_NOT(dl_arfcn > 0, "Invalid DL ARFCN=%d", dl_arfcn); + ERROR_IF_NOT(band > 0, "Band is a mandatory parameter"); + ERROR_IF_NOT(pdcch_scs < srsran_subcarrier_spacing_invalid, "Invalid carrier SCS"); + ERROR_IF_NOT(coreset0_idx < 15, "Invalid controlResourceSetZero"); + ERROR_IF_NOT(nof_prb > 0, "Invalid DL number of PRBS=%d", nof_prb); + + srsran::srsran_band_helper band_helper; + + double dl_freq_hz = band_helper.nr_arfcn_to_freq(dl_arfcn); + uint32_t dl_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(nof_prb, dl_arfcn); + + // derive SSB pattern and scs + cell.ssb_pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz); + if (cell.ssb_pattern == SRSRAN_SSB_PATTERN_A) { + // 15kHz SSB SCS + cell.ssb_scs = srsran_subcarrier_spacing_15kHz; + } else { + // try to optain SSB pattern for same band with 30kHz SCS + cell.ssb_pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_30kHz); + if (cell.ssb_pattern == SRSRAN_SSB_PATTERN_B || cell.ssb_pattern == SRSRAN_SSB_PATTERN_C) { + // SSB SCS is 30 kHz + cell.ssb_scs = srsran_subcarrier_spacing_30kHz; + } else { + srsran_terminate("Can't derive SSB pattern from band %d", band); + } + } + + // derive SSB position + int coreset0_rb_offset = 0; + if (is_sa) { + // Get offset in RBs between CORESET#0 and SSB + coreset0_rb_offset = srsran_coreset0_ssb_offset(coreset0_idx, cell.ssb_scs, pdcch_scs); + ERROR_IF_NOT(coreset0_rb_offset >= 0, "Failed to compute RB offset between CORESET#0 and SSB"); + } else { + // TODO: Verify if specified SSB frequency is valid + } + uint32_t ssb_abs_freq_point = + band_helper.get_abs_freq_ssb_arfcn(band, cell.ssb_scs, dl_absolute_freq_point_a, coreset0_rb_offset); + ERROR_IF_NOT(ssb_abs_freq_point > 0, + "Can't derive SSB freq point for dl_arfcn=%d and band %d", + band_helper.freq_to_nr_arfcn(dl_freq_hz), + band); + + // Convert to frequency for PHY + cell.ssb_absolute_freq_point = ssb_abs_freq_point; + cell.ssb_freq_hz = band_helper.nr_arfcn_to_freq(ssb_abs_freq_point); + + double pointA_abs_freq_Hz = dl_freq_hz - nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(pdcch_scs) / 2; + uint32_t ssb_pointA_freq_offset_Hz = + (cell.ssb_freq_hz > pointA_abs_freq_Hz) ? (uint32_t)(cell.ssb_freq_hz - pointA_abs_freq_Hz) : 0; + + cell.ssb_offset = (uint32_t)(ssb_pointA_freq_offset_Hz / SRSRAN_SUBC_SPACING_NR(pdcch_scs)) % SRSRAN_NRE; + + // Validate Coreset0 has space + srsran_coreset_t coreset0 = {}; + ERROR_IF_NOT( + srsran_coreset_zero( + cell.phy_cell.cell_id, ssb_pointA_freq_offset_Hz, cell.ssb_scs, pdcch_scs, coreset0_idx, &coreset0) == 0, + "Deriving parameters for coreset0: index=%d, ssb_pointA_offset=%d kHz\n", + coreset0_idx, + ssb_pointA_freq_offset_Hz / 1000); + + ERROR_IF_NOT(srsran_coreset_start_rb(&coreset0) + srsran_coreset_get_bw(&coreset0) <= cell.phy_cell.carrier.nof_prb, + "Coreset0 index=%d is not compatible with DL ARFCN %d in band %d\n", + coreset0_idx, + cell.dl_arfcn, + cell.band); + + // Validate Coreset0 has less than 3 symbols + ERROR_IF_NOT(coreset0.duration < 3, + "Coreset0 index=%d is not supported due to overlap with SSB. Select a coreset0 index from 38.213 Table " + "13-1 such that N_symb_coreset < 3\n", + coreset0_idx); + + // Validate Coreset0 has more than 24 RB + ERROR_IF_NOT(srsran_coreset_get_bw(&coreset0) > 24, + "Coreset0 configuration index=%d has only %d RB. A coreset0 index >= 6 is required such as N_rb >= 48\n", + srsran_coreset_get_bw(&coreset0), + coreset0_idx); + + return SRSRAN_SUCCESS; +} + +int derive_phy_cell_freq_params(uint32_t dl_arfcn, uint32_t ul_arfcn, phy_cell_cfg_nr_t& phy_cell) +{ + // Verify essential parameters are specified and valid + ERROR_IF_NOT(dl_arfcn > 0, "DL ARFCN is a mandatory parameter"); + + // Use helper class to derive NR carrier parameters + srsran::srsran_band_helper band_helper; + + // derive DL freq from ARFCN + if (phy_cell.carrier.dl_center_frequency_hz == 0) { + phy_cell.carrier.dl_center_frequency_hz = band_helper.nr_arfcn_to_freq(dl_arfcn); + } + + // derive UL freq from ARFCN + if (phy_cell.carrier.ul_center_frequency_hz == 0) { + // auto-detect UL frequency + if (ul_arfcn == 0) { + // derive UL ARFCN from given DL ARFCN + ul_arfcn = band_helper.get_ul_arfcn_from_dl_arfcn(dl_arfcn); + ERROR_IF_NOT(ul_arfcn > 0, "Can't derive UL ARFCN from DL ARFCN %d", dl_arfcn); + } + phy_cell.carrier.ul_center_frequency_hz = band_helper.nr_arfcn_to_freq(ul_arfcn); + } + + return SRSRAN_SUCCESS; +} + +int set_derived_nr_cell_params(bool is_sa, rrc_cell_cfg_nr_t& cell) +{ + // Verify essential parameters are specified and valid + ERROR_IF_NOT(cell.dl_arfcn > 0, "DL ARFCN is a mandatory parameter"); + ERROR_IF_NOT(cell.band > 0, "Band is a mandatory parameter"); + ERROR_IF_NOT(cell.phy_cell.carrier.nof_prb > 0, "Number of PRBs is a mandatory parameter"); + + // Use helper class to derive NR carrier parameters + srsran::srsran_band_helper band_helper; + + if (cell.ul_arfcn == 0) { + // derive UL ARFCN from given DL ARFCN + cell.ul_arfcn = band_helper.get_ul_arfcn_from_dl_arfcn(cell.dl_arfcn); + ERROR_IF_NOT(cell.ul_arfcn > 0, "Can't derive UL ARFCN from DL ARFCN %d", cell.dl_arfcn); + } + + // duplex mode + cell.duplex_mode = band_helper.get_duplex_mode(cell.band); + + // PointA + cell.dl_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(cell.phy_cell.carrier.nof_prb, cell.dl_arfcn); + cell.ul_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(cell.phy_cell.carrier.nof_prb, cell.ul_arfcn); + + // Derive phy_cell parameters that depend on ARFCNs + derive_phy_cell_freq_params(cell.dl_arfcn, cell.ul_arfcn, cell.phy_cell); + + // Derive SSB params + ERROR_IF_NOT(derive_ssb_params(is_sa, + cell.dl_arfcn, + cell.band, + cell.phy_cell.carrier.scs, + cell.coreset0_idx, + cell.phy_cell.carrier.nof_prb, + cell) == 0, + "Deriving SSB parameters\n"); + + cell.phy_cell.carrier.ssb_center_freq_hz = cell.ssb_freq_hz; + + // Derive remaining config params + if (not is_sa) { + // Configure CORESET#1 + cell.pdcch_cfg_common.common_ctrl_res_set_present = true; + cell.pdcch_cfg_common.common_ctrl_res_set = make_default_coreset(1, cell.phy_cell.carrier.nof_prb); + } + + // Configure SearchSpace#1 + cell.pdcch_cfg_common.common_search_space_list.resize(1); + auto& ss1 = cell.pdcch_cfg_common.common_search_space_list[0]; + if (is_sa) { + // Configure SearchSpace#1 -> CORESET#0 + ctrl_res_set_s dummy_coreset = make_default_coreset(0, cell.phy_cell.carrier.nof_prb); + ss1 = make_default_common_search_space(1, dummy_coreset); + ss1.nrof_candidates.aggregation_level1.value = search_space_s::nrof_candidates_s_::aggregation_level1_opts::n0; + ss1.nrof_candidates.aggregation_level2.value = search_space_s::nrof_candidates_s_::aggregation_level2_opts::n0; + ss1.nrof_candidates.aggregation_level4.value = search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1; + ss1.nrof_candidates.aggregation_level8.value = search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; + ss1.nrof_candidates.aggregation_level16.value = search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; + } else { + // Configure SearchSpace#1 -> CORESET#1 + ss1 = make_default_common_search_space(1, cell.pdcch_cfg_common.common_ctrl_res_set); + // cell.phy_cell.pdcch.search_space[1].type = srsran_search_space_type_common_3; + } + cell.pdcch_cfg_common.ra_search_space_present = true; + cell.pdcch_cfg_common.ra_search_space = ss1.search_space_id; + + return SRSRAN_SUCCESS; +} + +int set_derived_nr_rrc_params(rrc_nr_cfg_t& rrc_cfg) +{ + for (rrc_cell_cfg_nr_t& cell : rrc_cfg.cell_list) { + HANDLE_ERROR(set_derived_nr_cell_params(rrc_cfg.is_standalone, cell)); + } + return SRSRAN_SUCCESS; +} + +int check_nr_pdcch_cfg_valid(const srsran_pdcch_cfg_nr_t& pdcch) +{ + // Verify Search Spaces + std::array used_coresets{}; + for (uint32_t ss_id = 0; ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++ss_id) { + if (pdcch.search_space_present[ss_id]) { + const srsran_search_space_t& ss = pdcch.search_space[ss_id]; + ERROR_IF_NOT(ss.id == ss_id, "SearchSpace#%d should match list index", ss_id); + uint32_t cs_id = ss.coreset_id; + ERROR_IF_NOT(pdcch.coreset_present[cs_id], "SearchSpace#%d points to absent CORESET#%d", ss_id, cs_id); + used_coresets[cs_id] = true; + } + } + + // Verify CORESET id + for (uint32_t cs_id = 0; cs_id < SRSRAN_UE_DL_NR_MAX_NOF_CORESET; ++cs_id) { + ERROR_IF_NOT(pdcch.coreset_present[cs_id] == used_coresets[cs_id], "CORESET#%d is configured but not used", cs_id); + } + + return SRSRAN_SUCCESS; +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/rrc/rrc_nr_du_manager.cc b/srsgnb/src/stack/rrc/rrc_nr_du_manager.cc new file mode 100644 index 000000000..543d65c7b --- /dev/null +++ b/srsgnb/src/stack/rrc/rrc_nr_du_manager.cc @@ -0,0 +1,161 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h" +#include "srsgnb/hdr/stack/rrc/cell_asn1_config.h" +#include "srsran/asn1/rrc_nr_utils.h" +#include "srsran/common/string_helpers.h" + +using namespace asn1::rrc_nr; + +namespace srsenb { + +du_config_manager::du_config_manager(const rrc_nr_cfg_t& cfg_) : cfg(cfg_), logger(srslog::fetch_basic_logger("RRC-NR")) +{} + +du_config_manager::~du_config_manager() {} + +int du_config_manager::add_cell() +{ + // add cell + std::unique_ptr obj = std::make_unique(); + du_cell_config& cell = *obj; + cell.cc = cells.size(); + + // Fill general cell params + cell.pci = cfg.cell_list[cell.cc].phy_cell.carrier.pci; + + // fill MIB ASN.1 + if (fill_mib_from_enb_cfg(cfg.cell_list[cell.cc], cell.mib) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Pack MIB + cell.packed_mib = srsran::make_byte_buffer(); + if (cell.packed_mib == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + { + asn1::bit_ref bref(cell.packed_mib->msg, cell.packed_mib->get_tailroom()); + bcch_bch_msg_s bch_msg; + bch_msg.msg.set_mib() = cell.mib; + if (bch_msg.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Couldn't pack mib msg"); + return SRSRAN_ERROR; + } + cell.packed_mib->N_bytes = bref.distance_bytes(); + } + logger.info( + cell.packed_mib->data(), cell.packed_mib->size(), "BCCH-BCH Message (with MIB) (%d B)", cell.packed_mib->size()); + asn1::json_writer js; + cell.mib.to_json(js); + logger.info("MIB content: %s", js.to_string().c_str()); + + // fill SIB1 ASN.1 + if (fill_sib1_from_enb_cfg(cfg, cell.cc, cell.sib1) != SRSRAN_SUCCESS) { + logger.error("Couldn't generate SIB1"); + return SRSRAN_ERROR; + } + + // Pack SIB1 + cell.packed_sib1 = srsran::make_byte_buffer(); + if (cell.packed_sib1 == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + { + asn1::bit_ref bref(cell.packed_sib1->msg, cell.packed_sib1->get_tailroom()); + bcch_dl_sch_msg_s bcch_msg; + bcch_msg.msg.set_c1().set_sib_type1() = cell.sib1; + if (bcch_msg.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Couldn't pack SIB1 msg"); + return SRSRAN_ERROR; + } + cell.packed_sib1->N_bytes = bref.distance_bytes(); + } + if (cfg.is_standalone) { + logger.info(cell.packed_sib1->data(), + cell.packed_sib1->size(), + "BCCH-DL-SCH-Message (with SIB1) (%d B)", + cell.packed_sib1->size()); + cell.sib1.to_json(js); + logger.info("SIB1 content: %s", js.to_string().c_str()); + } + + // Generate SSB SCS + srsran_subcarrier_spacing_t ssb_scs; + if (not srsran::fill_ssb_pattern_scs(cfg.cell_list[cell.cc].phy_cell.carrier, &cell.ssb_pattern, &ssb_scs)) { + return SRSRAN_ERROR; + } + cell.ssb_scs.value = (subcarrier_spacing_e::options)ssb_scs; + cell.ssb_center_freq_hz = cfg.cell_list[cell.cc].ssb_freq_hz; + cell.dl_freq_hz = cfg.cell_list[cell.cc].phy_cell.carrier.dl_center_frequency_hz; + cell.is_standalone = cfg.is_standalone; + + cells.push_back(std::move(obj)); + return SRSRAN_SUCCESS; +} + +void fill_phy_pdcch_cfg_common(const du_cell_config& cell, srsran_pdcch_cfg_nr_t* pdcch) +{ + const serving_cell_cfg_common_sib_s& serv_cell = cell.serv_cell_cfg_common(); + const pdcch_cfg_common_s& pdcch_common = serv_cell.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(); + + bool is_sa = cell.is_standalone; + uint8_t coreset0_idx = cell.mib.pdcch_cfg_sib1.ctrl_res_set_zero; + auto scs = (srsran_subcarrier_spacing_t)serv_cell.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value; + auto ssb_scs = (srsran_subcarrier_spacing_t)cell.ssb_scs.value; + uint32_t nof_prb = serv_cell.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].carrier_bw; + + if (is_sa) { + // Generate CORESET#0 + pdcch->coreset_present[0] = true; + // Get pointA and SSB absolute frequencies + double pointA_abs_freq_Hz = cell.dl_freq_hz - nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(scs) / 2; + double ssb_abs_freq_Hz = cell.ssb_center_freq_hz; + // Calculate integer SSB to pointA frequency offset in Hz + uint32_t ssb_pointA_freq_offset_Hz = + (ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0; + int ret = srsran_coreset_zero(cell.pci, ssb_pointA_freq_offset_Hz, ssb_scs, scs, coreset0_idx, &pdcch->coreset[0]); + srsran_assert(ret == SRSRAN_SUCCESS, "Failed to generate CORESET#0"); + + // Generate SearchSpace#0 + pdcch->search_space_present[0] = true; + pdcch->search_space[0].id = 0; + pdcch->search_space[0].coreset_id = 0; + pdcch->search_space[0].type = srsran_search_space_type_common_0; + pdcch->search_space[0].nof_candidates[0] = 1; + pdcch->search_space[0].nof_candidates[1] = 1; + pdcch->search_space[0].nof_candidates[2] = 1; + pdcch->search_space[0].nof_candidates[3] = 0; + pdcch->search_space[0].nof_candidates[4] = 0; + pdcch->search_space[0].nof_formats = 1; + pdcch->search_space[0].formats[0] = srsran_dci_format_nr_1_0; + pdcch->search_space[0].duration = 1; + } + + // Generate Common CORESETs and Search Spaces + bool ret = srsran::fill_phy_pdcch_cfg_common(pdcch_common, pdcch); + srsran_assert(ret, "PDCCH Config Common"); +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/rrc/rrc_nr_security_context.cc b/srsgnb/src/stack/rrc/rrc_nr_security_context.cc new file mode 100644 index 000000000..e7042301f --- /dev/null +++ b/srsgnb/src/stack/rrc/rrc_nr_security_context.cc @@ -0,0 +1,212 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/rrc/rrc_nr_security_context.h" +#include "srsran/asn1/obj_id_cmp_utils.h" +#include "srsran/asn1/rrc_utils.h" + +namespace srsgnb { + +asn1::rrc_nr::security_algorithm_cfg_s nr_security_context::get_security_algorithm_cfg() const +{ + asn1::rrc_nr::security_algorithm_cfg_s ret; + // TODO: select these based on UE capabilities and preference order + ret.integrity_prot_algorithm_present = true; + ret.integrity_prot_algorithm = (asn1::rrc_nr::integrity_prot_algorithm_e::options)sec_cfg.integ_algo; + ret.ciphering_algorithm = (asn1::rrc_nr::ciphering_algorithm_e::options)sec_cfg.cipher_algo; + return ret; +} + +bool nr_security_context::set_security_capabilities(const asn1::ngap::ue_security_cap_s& caps) +{ + security_capabilities = caps; + + // Selects security algorithms (cipher_algo and integ_algo) based on capabilities and config preferences + // Each position in the bitmap represents an encryption algorithm: + // “all bits equal to 0” – UE supports no other algorithm than NEA0, + // “first bit” – 128-NEA1, + // “second bit” – 128-NEA2, + // “third bit” – 128-NEA3, + // other bits reserved for future use. Value ‘1’ indicates support and value + // ‘0’ indicates no support of the algorithm. + // Algorithms are defined in TS 33.401 [15]. + // Note: information missing + + bool enc_algo_found = false; + bool integ_algo_found = false; + + for (const auto& cipher_item : cfg.nea_preference_list) { + auto& v = security_capabilities.nrencryption_algorithms; + switch (cipher_item) { + case srsran::CIPHERING_ALGORITHM_ID_NR_NEA0: + // “all bits equal to 0” – UE supports no other algorithm than EEA0, + // specification does not cover the case in which EEA0 is supported with other algorithms + // just assume that EEA0 is always supported even this can not be explicity signaled by S1AP + sec_cfg.cipher_algo = srsran::CIPHERING_ALGORITHM_ID_NR_NEA0; + enc_algo_found = true; + logger.info("Selected NEA0 as RRC encryption algorithm"); + break; + case srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA1: + // “first bit” – 128-EEA1, + if (v.get(v.length() - srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA1)) { + sec_cfg.cipher_algo = srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA1; + enc_algo_found = true; + logger.info("Selected NEA1 as RRC encryption algorithm"); + break; + } else { + logger.info("Failed to selected NEA1 as RRC encryption algorithm, due to unsupported algorithm"); + } + break; + case srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA2: + // “second bit” – 128-EEA2, + if (v.get(v.length() - srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA2)) { + sec_cfg.cipher_algo = srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA2; + enc_algo_found = true; + logger.info("Selected NEA2 as RRC encryption algorithm"); + break; + } else { + logger.info("Failed to selected NEA2 as RRC encryption algorithm, due to unsupported algorithm"); + } + break; + case srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA3: + // “third bit” – 128-EEA3, + if (v.get(v.length() - srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA3)) { + sec_cfg.cipher_algo = srsran::CIPHERING_ALGORITHM_ID_NR_128_NEA3; + enc_algo_found = true; + logger.info("Selected NEA3 as RRC encryption algorithm"); + break; + } else { + logger.info("Failed to selected NEA2 as RRC encryption algorithm, due to unsupported algorithm"); + } + break; + default: + enc_algo_found = false; + break; + } + if (enc_algo_found) { + break; + } + } + + for (const auto& eia_enum : cfg.nia_preference_list) { + auto& v = security_capabilities.nrintegrity_protection_algorithms; + switch (eia_enum) { + case srsran::INTEGRITY_ALGORITHM_ID_NR_NIA0: + // Null integrity is not supported + logger.info("Skipping NIA0 as RRC integrity algorithm. Null integrity is not supported."); + sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_NR_NIA0; + integ_algo_found = true; + break; + case srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA1: + // “first bit” – 128-EIA1, + if (v.get(v.length() - srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA1)) { + sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA1; + integ_algo_found = true; + logger.info("Selected NIA1 as RRC integrity algorithm."); + } else { + logger.info("Failed to selected NIA1 as RRC encryption algorithm, due to unsupported algorithm"); + } + break; + case srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA2: + // “second bit” – 128-EIA2, + if (v.get(v.length() - srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA2)) { + sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA2; + integ_algo_found = true; + logger.info("Selected NIA2 as RRC integrity algorithm."); + } else { + logger.info("Failed to selected NIA2 as RRC encryption algorithm, due to unsupported algorithm"); + } + break; + case srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA3: + // “third bit” – 128-EIA3, + if (v.get(v.length() - srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA3)) { + sec_cfg.integ_algo = srsran::INTEGRITY_ALGORITHM_ID_NR_128_NIA3; + integ_algo_found = true; + logger.info("Selected NIA3 as RRC integrity algorithm."); + } else { + logger.info("Failed to selected NIA3 as RRC encryption algorithm, due to unsupported algorithm"); + } + break; + default: + integ_algo_found = false; + break; + } + + if (integ_algo_found) { + break; + } + } + + if (not integ_algo_found || not enc_algo_found) { + logger.error("Did not find a matching integrity or encryption algorithm with the UE"); + return false; + } + return true; +} + +void nr_security_context::set_security_key(const asn1::fixed_bitstring<256, false, true>& key) +{ + k_gnb_present = true; + for (uint32_t i = 0; i < key.nof_octets(); ++i) { + k_gnb[i] = key.data()[key.nof_octets() - 1 - i]; + } + + generate_as_keys(); +} + +void nr_security_context::generate_as_keys() +{ + // Generate K_rrc_enc and K_rrc_int + srsran::security_generate_k_nr_rrc(k_gnb, + (srsran::CIPHERING_ALGORITHM_ID_ENUM)sec_cfg.cipher_algo, + (srsran::INTEGRITY_ALGORITHM_ID_ENUM)sec_cfg.integ_algo, + sec_cfg.k_nr_rrc_enc.data(), + sec_cfg.k_nr_rrc_int.data()); + + // Generate K_up_enc and K_up_int + security_generate_k_nr_up(k_gnb, + (srsran::CIPHERING_ALGORITHM_ID_ENUM)sec_cfg.cipher_algo, + (srsran::INTEGRITY_ALGORITHM_ID_ENUM)sec_cfg.integ_algo, + sec_cfg.k_nr_up_enc.data(), + sec_cfg.k_nr_up_int.data()); + + logger.info(k_gnb, 32, "K_gNB (k_gnb)"); + logger.info(sec_cfg.k_nr_rrc_enc.data(), 32, "NR RRC Encryption Key (k_nr_rrc_enc)"); + logger.info(sec_cfg.k_nr_rrc_int.data(), 32, "NR RRC Integrity Key (k_nr_rrc_int)"); + logger.info(sec_cfg.k_nr_up_enc.data(), 32, "NR UP Encryption Key (k_nr_up_enc)"); + logger.info(sec_cfg.k_nr_up_int.data(), 32, "NR UP Integrity Key (k_nr_up_int)"); +} + +void nr_security_context::regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_arfcn) +{ + logger.info("Regenerating KgNB with PCI=0x%02x, SSB-ARFCN=%d", new_pci, new_dl_arfcn); + logger.info(k_gnb, 32, "Old K_gNB (k_gnb)"); + // Generate K_enb* + uint8_t k_gnb_star[32]; + srsran::security_generate_k_gnb_star(k_gnb, new_pci, new_dl_arfcn, k_gnb_star); + + // K_enb becomes K_enb* + memcpy(k_gnb, k_gnb_star, 32); + + generate_as_keys(); +} + +} // namespace srsgnb diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc new file mode 100644 index 000000000..39d68f72e --- /dev/null +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -0,0 +1,1575 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsgnb/hdr/stack/rrc/rrc_nr_ue.h" +#include "srsgnb/hdr/stack/rrc/cell_asn1_config.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h" +#include "srsran/asn1/rrc_nr_utils.h" +#include "srsran/common/bearer_manager.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" + +using namespace asn1::rrc_nr; + +namespace srsenb { + +/******************************************************************************* + UE class + +Every function in UE class is called from a mutex environment thus does not + need extra protection. + *******************************************************************************/ +rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_, uint32_t pcell_cc_idx, bool start_msg3_timer) : + parent(parent_), logger(parent_->logger), rnti(rnti_), uecfg(), sec_ctx(parent->cfg) +{ + // Set default MAC UE config + uecfg.carriers.resize(1); + uecfg.carriers[0].active = true; + uecfg.carriers[0].cc = pcell_cc_idx; + uecfg.phy_cfg = parent->cell_ctxt->default_phy_ue_cfg_nr; + + if (not parent->cfg.is_standalone) { + // Add the final PDCCH config in case of NSA + srsran::fill_phy_pdcch_cfg( + parent->cell_ctxt->master_cell_group->sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(), + &uecfg.phy_cfg.pdcch); + } else { + cell_group_cfg = *parent->cell_ctxt->master_cell_group; + next_cell_group_cfg = cell_group_cfg; + } + + // Set timer for MSG3_RX_TIMEOUT or UE_INACTIVITY_TIMEOUT + activity_timer = parent->task_sched.get_unique_timer(); + start_msg3_timer ? set_activity_timeout(MSG3_RX_TIMEOUT) : set_activity_timeout(MSG5_RX_TIMEOUT); +} + +rrc_nr::ue::~ue() {} + +void rrc_nr::ue::set_activity_timeout(activity_timeout_type_t type) +{ + uint32_t deadline_ms = 0; + + switch (type) { + case MSG3_RX_TIMEOUT: + // TODO: Retrieve the parameters from somewhere(RRC?) - Currently hardcoded to 100ms + deadline_ms = 100; + break; + case MSG5_RX_TIMEOUT: + // TODO: Retrieve the parameters from somewhere(RRC?) - Currently hardcoded to 1s + deadline_ms = 5000; + break; + case UE_INACTIVITY_TIMEOUT: + deadline_ms = parent->cfg.inactivity_timeout_ms; + break; + default: + logger.error("Unknown timeout type %d", type); + return; + } + + activity_timer.set(deadline_ms, [this, type](uint32_t tid) { activity_timer_expired(type); }); + logger.debug("Setting timer for %s for rnti=0x%x to %dms", to_string(type).c_str(), rnti, deadline_ms); + + set_activity(); +} + +void rrc_nr::ue::set_activity(bool enabled) +{ + if (not enabled) { + if (activity_timer.is_running()) { + logger.debug("Inactivity timer interrupted for rnti=0x%x", rnti); + } + activity_timer.stop(); + return; + } + + // re-start activity timer with current timeout value + activity_timer.run(); + logger.debug("Activity registered for rnti=0x%x (timeout_value=%dms)", rnti, activity_timer.duration()); +} + +void rrc_nr::ue::activity_timer_expired(const activity_timeout_type_t type) +{ + logger.info("Activity timer for rnti=0x%x expired after %d ms", rnti, activity_timer.time_elapsed()); + + switch (type) { + case MSG5_RX_TIMEOUT: + case UE_INACTIVITY_TIMEOUT: { + state = rrc_nr_state_t::RRC_INACTIVE; + if (parent->cfg.is_standalone) { + // Start NGAP Release UE context + parent->ngap->user_release_request(rnti, asn1::ngap::cause_radio_network_opts::user_inactivity); + } else { + parent->rrc_eutra->sgnb_inactivity_timeout(eutra_rnti); + } + break; + } + case MSG3_RX_TIMEOUT: { + // MSG3 timeout, no need to notify NGAP or LTE stack. Just remove UE + state = rrc_nr_state_t::RRC_IDLE; + uint32_t rnti_to_rem = rnti; + parent->task_sched.defer_task([this, rnti_to_rem]() { parent->rem_user(rnti_to_rem); }); + break; + } + default: + // Unhandled activity timeout, just remove UE and log an error + parent->rem_user(rnti); + logger.error( + "Unhandled reason for activity timer expiration. rnti=0x%x, cause %d", rnti, static_cast(type)); + } +} + +std::string rrc_nr::ue::to_string(const activity_timeout_type_t& type) +{ + constexpr static const char* options[] = {"Msg3 reception", "UE inactivity", "Msg5 reception"}; + return srsran::enum_to_text(options, (uint32_t)activity_timeout_type_t::nulltype, (uint32_t)type); +} + +int rrc_nr::ue::send_dl_ccch(const dl_ccch_msg_s& dl_ccch_msg) +{ + // Allocate a new PDU buffer, pack the message and send to PDCP + srsran::unique_byte_buffer_t pdu = parent->pack_into_pdu(dl_ccch_msg, __FUNCTION__); + if (pdu == nullptr) { + logger.error("Failed to send DL-CCCH"); + return SRSRAN_ERROR; + } + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "DL-CCCH.{}", dl_ccch_msg.msg.c1().type().to_string()); + log_rrc_message(srsran::nr_srb::srb0, Tx, *pdu.get(), dl_ccch_msg, srsran::to_c_str(fmtbuf)); + parent->rlc->write_sdu(rnti, srsran::srb_to_lcid(srsran::nr_srb::srb0), std::move(pdu)); + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::send_dl_dcch(srsran::nr_srb srb, const asn1::rrc_nr::dl_dcch_msg_s& dl_dcch_msg) +{ + // Allocate a new PDU buffer, pack the message and send to PDCP + srsran::unique_byte_buffer_t pdu = parent->pack_into_pdu(dl_dcch_msg, __FUNCTION__); + if (pdu == nullptr) { + return SRSRAN_ERROR; + } + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "DL-DCCH.{}", dl_dcch_msg.msg.c1().type().to_string()); + log_rrc_message(srb, Tx, *pdu.get(), dl_dcch_msg, srsran::to_c_str(fmtbuf)); + parent->pdcp->write_sdu(rnti, srsran::srb_to_lcid(srb), std::move(pdu)); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_secondary_cell_group_mac_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // mac-CellGroup-Config for BSR and SR + cell_group_cfg_pack.mac_cell_group_cfg_present = true; + auto& mac_cell_group = cell_group_cfg_pack.mac_cell_group_cfg; + mac_cell_group.sched_request_cfg_present = true; + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list.resize(1); + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list[0].sched_request_id = 0; + mac_cell_group.sched_request_cfg.sched_request_to_add_mod_list[0].sr_trans_max = + asn1::rrc_nr::sched_request_to_add_mod_s::sr_trans_max_opts::n64; + mac_cell_group.bsr_cfg_present = true; + mac_cell_group.bsr_cfg.periodic_bsr_timer = asn1::rrc_nr::bsr_cfg_s::periodic_bsr_timer_opts::sf20; + mac_cell_group.bsr_cfg.retx_bsr_timer = asn1::rrc_nr::bsr_cfg_s::retx_bsr_timer_opts::sf320; + + // Skip TAG and PHR config + mac_cell_group.tag_cfg_present = false; + mac_cell_group.tag_cfg.tag_to_add_mod_list.resize(1); + mac_cell_group.tag_cfg.tag_to_add_mod_list[0].tag_id = 0; + mac_cell_group.tag_cfg.tag_to_add_mod_list[0].time_align_timer = time_align_timer_opts::infinity; + + mac_cell_group.phr_cfg_present = false; + mac_cell_group.phr_cfg.set_setup(); + mac_cell_group.phr_cfg.setup().phr_periodic_timer = asn1::rrc_nr::phr_cfg_s::phr_periodic_timer_opts::sf500; + mac_cell_group.phr_cfg.setup().phr_prohibit_timer = asn1::rrc_nr::phr_cfg_s::phr_prohibit_timer_opts::sf200; + mac_cell_group.phr_cfg.setup().phr_tx_pwr_factor_change = asn1::rrc_nr::phr_cfg_s::phr_tx_pwr_factor_change_opts::db3; + mac_cell_group.phr_cfg.setup().multiple_phr = true; + mac_cell_group.phr_cfg.setup().dummy = false; + mac_cell_group.phr_cfg.setup().phr_type2_other_cell = false; + mac_cell_group.phr_cfg.setup().phr_mode_other_cg = asn1::rrc_nr::phr_cfg_s::phr_mode_other_cg_opts::real; + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_sp_cell_cfg_ded_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present = true; + + pack_sp_cell_cfg_ded_init_dl_bwp_radio_link_monitoring(cell_group_cfg_pack); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_sp_cell_cfg_ded_init_dl_bwp_radio_link_monitoring( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.radio_link_monitoring_cfg_present = true; + auto& radio_link_monitoring = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.radio_link_monitoring_cfg; + + // add resource to detect RLF + radio_link_monitoring.set_setup().fail_detection_res_to_add_mod_list.resize(1); + auto& fail_detec_res_elem = radio_link_monitoring.set_setup().fail_detection_res_to_add_mod_list[0]; + fail_detec_res_elem.radio_link_monitoring_rs_id = 0; + fail_detec_res_elem.purpose = asn1::rrc_nr::radio_link_monitoring_rs_s::purpose_opts::rlf; + fail_detec_res_elem.detection_res.set_ssb_idx() = 0; + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pucch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // PUCCH + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg_present = true; + auto& pucch_cfg = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg; + + pucch_cfg.set_setup(); + pucch_cfg.setup().format2_present = true; + pucch_cfg.setup().format2.set_setup(); + pucch_cfg.setup().format2.setup().max_code_rate_present = true; + pucch_cfg.setup().format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25; + + // SR resources + pucch_cfg.setup().sched_request_res_to_add_mod_list.resize(1); + auto& sr_res1 = pucch_cfg.setup().sched_request_res_to_add_mod_list[0]; + sr_res1.sched_request_res_id = 1; + sr_res1.sched_request_id = 0; + sr_res1.periodicity_and_offset_present = true; + sr_res1.periodicity_and_offset.set_sl40() = 8; + sr_res1.res_present = true; + sr_res1.res = 2; // PUCCH resource for SR + + // DL data + if (parent->cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + pucch_cfg.setup().dl_data_to_ul_ack.resize(1); + pucch_cfg.setup().dl_data_to_ul_ack[0] = 4; + } else { + pucch_cfg.setup().dl_data_to_ul_ack.resize(6); + pucch_cfg.setup().dl_data_to_ul_ack[0] = 6; + pucch_cfg.setup().dl_data_to_ul_ack[1] = 5; + pucch_cfg.setup().dl_data_to_ul_ack[2] = 4; + pucch_cfg.setup().dl_data_to_ul_ack[3] = 4; + pucch_cfg.setup().dl_data_to_ul_ack[4] = 4; + pucch_cfg.setup().dl_data_to_ul_ack[5] = 4; + } + + // PUCCH Resource for format 1 + srsran_pucch_nr_resource_t resource_small = {}; + resource_small.starting_prb = 0; + resource_small.format = SRSRAN_PUCCH_NR_FORMAT_1; + resource_small.initial_cyclic_shift = 0; + resource_small.nof_symbols = 14; + resource_small.start_symbol_idx = 0; + resource_small.time_domain_occ = 0; + + // PUCCH Resource for format 2 + srsran_pucch_nr_resource_t resource_big = {}; + resource_big.starting_prb = 51; + resource_big.format = SRSRAN_PUCCH_NR_FORMAT_2; + resource_big.nof_prb = 1; + resource_big.nof_symbols = 2; + resource_big.start_symbol_idx = 12; + + // Resource for SR + srsran_pucch_nr_resource_t resource_sr = {}; + resource_sr.starting_prb = 51; + resource_sr.format = SRSRAN_PUCCH_NR_FORMAT_1; + resource_sr.initial_cyclic_shift = 0; + resource_sr.nof_symbols = 14; + resource_sr.start_symbol_idx = 0; + resource_sr.time_domain_occ = 0; + + // Make 3 possible resources + pucch_cfg.setup().res_to_add_mod_list.resize(3); + if (not srsran::make_phy_res_config(resource_small, pucch_cfg.setup().res_to_add_mod_list[0], 0)) { + logger.warning("Failed to create 1-2 bit NR PUCCH resource"); + } + if (not srsran::make_phy_res_config(resource_big, pucch_cfg.setup().res_to_add_mod_list[1], 1)) { + logger.warning("Failed to create >2 bit NR PUCCH resource"); + } + if (not srsran::make_phy_res_config(resource_sr, pucch_cfg.setup().res_to_add_mod_list[2], 2)) { + logger.warning("Failed to create SR NR PUCCH resource"); + } + + // Make 2 PUCCH resource sets + pucch_cfg.setup().res_set_to_add_mod_list.resize(2); + + // Make PUCCH resource set for 1-2 bit + pucch_cfg.setup().res_set_to_add_mod_list[0].pucch_res_set_id = 0; + pucch_cfg.setup().res_set_to_add_mod_list[0].res_list.resize(8); + for (auto& e : pucch_cfg.setup().res_set_to_add_mod_list[0].res_list) { + e = 0; + } + + // Make PUCCH resource set for >2 bit + pucch_cfg.setup().res_set_to_add_mod_list[1].pucch_res_set_id = 1; + pucch_cfg.setup().res_set_to_add_mod_list[1].res_list.resize(8); + for (auto& e : pucch_cfg.setup().res_set_to_add_mod_list[1].res_list) { + e = 1; + } + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pusch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // PUSCH config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg.set_setup(); + auto& pusch_cfg_ded = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg.setup(); + + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.set_setup(); + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position_present = true; + pusch_cfg_ded.dmrs_ul_for_pusch_map_type_a.setup().dmrs_add_position = dmrs_ul_cfg_s::dmrs_add_position_opts::pos1; + // PUSH power control skipped + pusch_cfg_ded.res_alloc = pusch_cfg_s::res_alloc_opts::res_alloc_type1; + + // UCI + pusch_cfg_ded.uci_on_pusch_present = true; + pusch_cfg_ded.uci_on_pusch.set_setup(); + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets_present = true; + pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.set_semi_static(); + auto& beta_offset_semi_static = pusch_cfg_ded.uci_on_pusch.setup().beta_offsets.semi_static(); + beta_offset_semi_static.beta_offset_ack_idx1_present = true; + beta_offset_semi_static.beta_offset_ack_idx1 = 9; + beta_offset_semi_static.beta_offset_ack_idx2_present = true; + beta_offset_semi_static.beta_offset_ack_idx2 = 9; + beta_offset_semi_static.beta_offset_ack_idx3_present = true; + beta_offset_semi_static.beta_offset_ack_idx3 = 9; + beta_offset_semi_static.beta_offset_csi_part1_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part1_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part1_idx2 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx1_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx1 = 6; + beta_offset_semi_static.beta_offset_csi_part2_idx2_present = true; + beta_offset_semi_static.beta_offset_csi_part2_idx2 = 6; + pusch_cfg_ded.uci_on_pusch.setup().scaling = uci_on_pusch_s::scaling_opts::f1; + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp_present = true; + + pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pucch_cfg(cell_group_cfg_pack); + pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pusch_cfg(cell_group_cfg_pack); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_sp_cell_cfg_ded_ul_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // UL config dedicated + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present = true; + + pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp(cell_group_cfg_pack); + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.first_active_ul_bwp_id_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.first_active_ul_bwp_id = 0; + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdcch_serving_cell_cfg.set_setup(); + + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.set_setup(); + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch_present = + true; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup().nrof_harq_processes_for_pdsch = + pdsch_serving_cell_cfg_s::nrof_harq_processes_for_pdsch_opts::n16; + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_sp_cell_cfg_ded(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // SP Cell Dedicated config + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded_present = true; + + pack_sp_cell_cfg_ded_ul_cfg(cell_group_cfg_pack); + pack_sp_cell_cfg_ded_init_dl_bwp(cell_group_cfg_pack); + + // Serving cell config (only to setup) + pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(cell_group_cfg_pack); + + // spCellConfig + if (fill_sp_cell_cfg_from_enb_cfg(parent->cfg, UE_PSCELL_CC_IDX, cell_group_cfg_pack.sp_cell_cfg) != SRSRAN_SUCCESS) { + logger.error("Failed to pack spCellConfig for rnti=0x%x", rnti); + } + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common_phy_cell_group_cfg( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + cell_group_cfg_pack.phys_cell_group_cfg_present = true; + cell_group_cfg_pack.phys_cell_group_cfg.pdsch_harq_ack_codebook = + phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::dynamic_value; + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // PDSCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp + .pdsch_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_cfg_common + .set_setup(); + + auto& pdsch_cfg_common = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp + .pdsch_cfg_common.setup(); + pdsch_cfg_common.pdsch_time_domain_alloc_list.resize(1); + pdsch_cfg_common.pdsch_time_domain_alloc_list[0].map_type = pdsch_time_domain_res_alloc_s::map_type_opts::type_a; + pdsch_cfg_common.pdsch_time_domain_alloc_list[0].start_symbol_and_len = 40; + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp_present = true; + auto& init_dl_bwp = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp; + + init_dl_bwp.generic_params.location_and_bw = 14025; + init_dl_bwp.generic_params.subcarrier_spacing = subcarrier_spacing_opts::khz15; + + pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common(cell_group_cfg_pack); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // DL config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present = true; + + pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common_phy_cell_group_cfg(cell_group_cfg_pack); + pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp(cell_group_cfg_pack); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp_pusch_cfg_common( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // PUSCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp + .pusch_cfg_common_present = true; + auto& pusch_cfg_common_pack = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.pusch_cfg_common; + pusch_cfg_common_pack.set_setup(); + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list.resize(2); + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].k2_present = true; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].k2 = 4; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].map_type = + asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::type_a; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[0].start_symbol_and_len = 27; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].k2_present = true; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].k2 = 3; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].map_type = + asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::type_a; + pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].start_symbol_and_len = 27; + pusch_cfg_common_pack.setup().p0_nominal_with_grant_present = true; + pusch_cfg_common_pack.setup().p0_nominal_with_grant = -60; + + // PUCCH config common + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp + .pucch_cfg_common_present = true; + auto& pucch_cfg_common_pack = + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.pucch_cfg_common; + pucch_cfg_common_pack.set_setup(); + pucch_cfg_common_pack.setup().pucch_group_hop = asn1::rrc_nr::pucch_cfg_common_s::pucch_group_hop_opts::neither; + pucch_cfg_common_pack.setup().p0_nominal_present = true; + pucch_cfg_common_pack.setup().p0_nominal = -60; + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params + .location_and_bw = 14025; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params + .subcarrier_spacing = subcarrier_spacing_opts::khz15; + + pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp_pusch_cfg_common(cell_group_cfg_pack); + + return SRSRAN_ERROR; +} + +int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // UL config + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.dummy = time_align_timer_opts::ms500; + + pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp(cell_group_cfg_pack); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + auto& pscell_cfg = parent->cfg.cell_list.at(UE_PSCELL_CC_IDX); + + if (pscell_cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.smtc.release(); + } + + // DL config + pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common(cell_group_cfg_pack); + + // UL config + pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common(cell_group_cfg_pack); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_recfg_with_sync(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + // Reconfig with Sync + cell_group_cfg_pack.cell_group_id = 1; // 0 identifies the MCG. Other values identify SCGs. + + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = rnti; + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000; + + pack_recfg_with_sync_sp_cell_cfg_common(cell_group_cfg_pack); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::pack_secondary_cell_group_sp_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +{ + cell_group_cfg_pack.sp_cell_cfg_present = true; + cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx_present = true; + cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx = 1; // Serving cell ID of a PSCell. The PCell of the MCG uses ID 0. + + pack_sp_cell_cfg_ded(cell_group_cfg_pack); + pack_recfg_with_sync(cell_group_cfg_pack); + + return SRSRAN_SUCCESS; +} + +// Helper for the RRC Reconfiguration sender to pack hard-coded config +int rrc_nr::ue::pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config) +{ + auto& cell_group_cfg_pack = cell_group_cfg; + + pack_secondary_cell_group_mac_cfg(cell_group_cfg_pack); + pack_secondary_cell_group_sp_cell_cfg(cell_group_cfg_pack); + + // make sufficiant space + packed_secondary_cell_config.resize(256); + asn1::bit_ref bref_pack(packed_secondary_cell_config.data(), packed_secondary_cell_config.size()); + if (cell_group_cfg_pack.pack(bref_pack) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack NR secondary cell config"); + return SRSRAN_ERROR; + } + packed_secondary_cell_config.resize(bref_pack.distance_bytes()); + + log_rrc_container(Tx, packed_secondary_cell_config, cell_group_cfg_pack, "nr-SecondaryCellGroupConfig-r15"); + + return SRSRAN_SUCCESS; +} + +// Packs a hard-coded RRC Reconfiguration with fixed params for all layers (for now) +int rrc_nr::ue::pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig) +{ + rrc_recfg_s reconfig; + reconfig.rrc_transaction_id = ((transaction_id++) % 4u); + rrc_recfg_ies_s& recfg_ies = reconfig.crit_exts.set_rrc_recfg(); + + // add secondary cell group config + if (pack_secondary_cell_group_cfg(recfg_ies.secondary_cell_group) == SRSRAN_ERROR) { + logger.error("Failed to pack secondary cell group"); + return SRSRAN_ERROR; + } + + // now pack .. + packed_rrc_reconfig.resize(512); + asn1::bit_ref bref_pack(packed_rrc_reconfig.data(), packed_rrc_reconfig.size()); + if (reconfig.pack(bref_pack) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack RRC Reconfiguration"); + return SRSRAN_ERROR; + } + packed_rrc_reconfig.resize(bref_pack.distance_bytes()); + + return SRSRAN_SUCCESS; +} + +// Packs a hard-coded NR radio bearer config with fixed params for RLC/PDCP (for now) +int rrc_nr::ue::pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_bearer_config) +{ + // set security config + auto& radio_bearer_cfg_pack = radio_bearer_cfg; + radio_bearer_cfg_pack.security_cfg_present = true; + auto& sec_cfg = radio_bearer_cfg_pack.security_cfg; + sec_cfg.key_to_use_present = true; + sec_cfg.key_to_use = asn1::rrc_nr::security_cfg_s::key_to_use_opts::secondary; + sec_cfg.security_algorithm_cfg_present = true; + sec_cfg.security_algorithm_cfg.ciphering_algorithm = ciphering_algorithm_opts::nea0; + sec_cfg.security_algorithm_cfg.integrity_prot_algorithm_present = true; + sec_cfg.security_algorithm_cfg.integrity_prot_algorithm = integrity_prot_algorithm_opts::nia0; + + // pack it + packed_nr_bearer_config.resize(128); + asn1::bit_ref bref_pack(packed_nr_bearer_config.data(), packed_nr_bearer_config.size()); + if (radio_bearer_cfg_pack.pack(bref_pack) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack NR radio bearer config"); + return SRSRAN_ERROR; + } + + // resize to packed length + packed_nr_bearer_config.resize(bref_pack.distance_bytes()); + + log_rrc_container(Tx, packed_nr_bearer_config, radio_bearer_cfg_pack, "nr-RadioBearerConfig1-r15"); + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::handle_sgnb_addition_request(uint16_t eutra_rnti_, const sgnb_addition_req_params_t& req_params) +{ + // Add DRB1 to RLC and PDCP + if (add_drb(req_params.five_qi) != SRSRAN_SUCCESS) { + parent->logger.error("Failed to configure DRB"); + parent->rrc_eutra->sgnb_addition_reject(eutra_rnti_); + return SRSRAN_ERROR; + } + + // provide hard-coded NR configs + rrc_eutra_interface_rrc_nr::sgnb_addition_ack_params_t ack_params = {}; + if (pack_rrc_reconfiguration(ack_params.nr_secondary_cell_group_cfg_r15) == SRSRAN_ERROR) { + parent->logger.error("Failed to pack RRC Reconfiguration. Sending SgNB addition reject."); + parent->rrc_eutra->sgnb_addition_reject(eutra_rnti_); + return SRSRAN_ERROR; + } + + if (pack_nr_radio_bearer_config(ack_params.nr_radio_bearer_cfg1_r15) == SRSRAN_ERROR) { + parent->logger.error("Failed to pack NR radio bearer config. Sending SgNB addition reject."); + parent->rrc_eutra->sgnb_addition_reject(eutra_rnti_); + return SRSRAN_ERROR; + } + + // send response to EUTRA + ack_params.nr_rnti = rnti; + ack_params.eps_bearer_id = req_params.eps_bearer_id; + parent->rrc_eutra->sgnb_addition_ack(eutra_rnti_, ack_params); + + // recognize RNTI as ENDC user + endc = true; + eutra_rnti = eutra_rnti_; + + return SRSRAN_SUCCESS; +} + +void rrc_nr::ue::crnti_ce_received() +{ + // Assume NSA mode active + if (endc) { + // send SgNB addition complete for ENDC users + parent->rrc_eutra->sgnb_addition_complete(eutra_rnti, rnti); + + // stop RX MSG3/MSG5 activity timer on MAC CE RNTI reception + set_activity_timeout(UE_INACTIVITY_TIMEOUT); + parent->logger.debug("Received MAC CE-RNTI for 0x%x - stopping MSG3/MSG5 timer, starting inactivity timer", rnti); + + // Add DRB1 to MAC + for (auto& drb : cell_group_cfg.rlc_bearer_to_add_mod_list) { + uecfg.lc_ch_to_add.emplace_back(); + uecfg.lc_ch_to_add.back().lcid = drb.lc_ch_id; + uecfg.lc_ch_to_add.back().cfg.direction = mac_lc_ch_cfg_t::BOTH; + uecfg.lc_ch_to_add.back().cfg.group = drb.mac_lc_ch_cfg.ul_specific_params.lc_ch_group; + } + + // Update UE phy params + srsran::make_phy_ssb_cfg(parent->cfg.cell_list[0].phy_cell.carrier, + cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common, + &uecfg.phy_cfg.ssb); + srsran::make_duplex_cfg_from_serv_cell(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common, + &uecfg.phy_cfg.duplex); + + srsran_assert(check_nr_pdcch_cfg_valid(uecfg.phy_cfg.pdcch) == SRSRAN_SUCCESS, "Invalid PhyCell Config"); + + uecfg.sp_cell_cfg.reset(new sp_cell_cfg_s{cell_group_cfg.sp_cell_cfg}); + uecfg.mac_cell_group_cfg.reset(new mac_cell_group_cfg_s{cell_group_cfg.mac_cell_group_cfg}); + uecfg.phy_cell_group_cfg.reset(new phys_cell_group_cfg_s{cell_group_cfg.phys_cell_group_cfg}); + parent->mac->ue_cfg(rnti, uecfg); + } +} + +/** + * @brief Set DRB configuration + * + * The function sets and configures all relavant fields for the DRB configuration (MAC, RLC, PDCP) in the + * cellGroupConfig and also adds the bearer to the local RLC and PDCP entities. + * + * @param int 5QI of the DRB to be added + * @return int SRSRAN_SUCCESS on success + */ +int rrc_nr::ue::add_drb(uint32_t five_qi) +{ + if (parent->cfg.five_qi_cfg.find(five_qi) == parent->cfg.five_qi_cfg.end()) { + parent->logger.error("No bearer config for 5QI %d present. Aborting DRB addition.", five_qi); + return SRSRAN_ERROR; + } + + // RLC for DRB1 (with fixed LCID) inside cell_group_cfg + auto& cell_group_cfg_pack = cell_group_cfg; + + cell_group_cfg_pack.rlc_bearer_to_add_mod_list.resize(1); + auto& rlc_bearer = cell_group_cfg_pack.rlc_bearer_to_add_mod_list[0]; + rlc_bearer.lc_ch_id = drb1_lcid; + rlc_bearer.served_radio_bearer_present = true; + rlc_bearer.served_radio_bearer.set_drb_id(); + rlc_bearer.served_radio_bearer.drb_id() = 1; + rlc_bearer.rlc_cfg_present = true; + rlc_bearer.rlc_cfg = parent->cfg.five_qi_cfg[five_qi].rlc_cfg; + + // add RLC bearer + srsran::rlc_config_t rlc_cfg; + /// NOTE, we need to pass the radio-bearer to the rlc_config + if (srsran::make_rlc_config_t(cell_group_cfg.rlc_bearer_to_add_mod_list[0].rlc_cfg, + rlc_bearer.served_radio_bearer.drb_id(), + &rlc_cfg) != SRSRAN_SUCCESS) { + parent->logger.error("Failed to build RLC config"); + return SRSRAN_ERROR; + } + parent->rlc->add_bearer(rnti, drb1_lcid, rlc_cfg); + + // MAC logical channel config + rlc_bearer.mac_lc_ch_cfg_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.prio = 11; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = + asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::prioritised_bit_rate_opts::kbps0; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = + asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::bucket_size_dur_opts::ms100; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 3; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; + // TODO: add LC config to MAC + + // PDCP config goes into radio_bearer_cfg + auto& radio_bearer_cfg_pack = radio_bearer_cfg; + radio_bearer_cfg_pack.drb_to_add_mod_list.resize(1); + + // configure fixed DRB1 + auto& drb_item = radio_bearer_cfg_pack.drb_to_add_mod_list[0]; + drb_item.drb_id = 1; + drb_item.cn_assoc_present = true; + drb_item.cn_assoc.set_eps_bearer_id() = 5; + drb_item.pdcp_cfg_present = true; + drb_item.pdcp_cfg = parent->cfg.five_qi_cfg[five_qi].pdcp_cfg; + + // Add DRB1 to PDCP + srsran::pdcp_config_t pdcp_cnfg = srsran::make_drb_pdcp_config_t(drb_item.drb_id, false, drb_item.pdcp_cfg); + parent->pdcp->add_bearer(rnti, rlc_bearer.lc_ch_id, pdcp_cnfg); + + // Note: DRB1 is only activated in the MAC when the C-RNTI CE is received + + return SRSRAN_SUCCESS; +} + +void rrc_nr::ue::handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request_s& msg) +{ + const uint8_t max_wait_time_secs = 16; + if (not parent->ngap->is_amf_connected()) { + logger.error("MME isn't connected. Sending Connection Reject"); + send_rrc_reject(max_wait_time_secs); + return; + } + + // Allocate PUCCH resources and reject if not available + if (not init_pucch()) { + logger.warning("Could not allocate PUCCH resources for rnti=0x%x. Sending Connection Reject", rnti); + send_rrc_reject(max_wait_time_secs); + return; + } + + const rrc_setup_request_ies_s& ies = msg.rrc_setup_request; + + switch (ies.ue_id.type().value) { + case init_ue_id_c::types_opts::ng_minus5_g_s_tmsi_part1: + ctxt.setup_ue_id = ies.ue_id.ng_minus5_g_s_tmsi_part1().to_number(); + break; + case asn1::rrc_nr::init_ue_id_c::types_opts::random_value: + ctxt.setup_ue_id = ies.ue_id.random_value().to_number(); + // TODO: communicate with NGAP + break; + default: + logger.error("Unsupported RRCSetupRequest"); + send_rrc_reject(max_wait_time_secs); + return; + } + ctxt.connection_cause.value = ies.establishment_cause.value; + + send_rrc_setup(); + set_activity_timeout(UE_INACTIVITY_TIMEOUT); +} + +void rrc_nr::ue::handle_rrc_reestablishment_request(const asn1::rrc_nr::rrc_reest_request_s& msg) +{ + uint32_t old_rnti = msg.rrc_reest_request.ue_id.c_rnti; + uint16_t pci = msg.rrc_reest_request.ue_id.pci; + + // Log event + parent->logger.debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s", + old_rnti, + pci, + (uint32_t)msg.rrc_reest_request.ue_id.short_mac_i.to_number(), + msg.rrc_reest_request.reest_cause.to_string()); + + // Check AMF connection + const uint8_t max_wait_time_secs = 16; + if (not parent->ngap->is_amf_connected()) { + logger.error("AMF not connected. Sending Connection Reject."); + send_rrc_reject(max_wait_time_secs); + return; + } + + // Allocate PUCCH resources and reject if not available + if (not init_pucch()) { + logger.warning("Could not allocate PUCCH resources for rnti=0x%x. Sending RRC Reject.", rnti); + send_rrc_reject(max_wait_time_secs); + return; + } + + if (not is_idle()) { + // The created RNTI has to receive ReestablishmentRequest as first message + parent->logger.error( + "Could not reestablish connection for rnti=0x%x. Cause: old rnti=0x%x is not in RRC_IDLE.", rnti, old_rnti); + send_rrc_reject(max_wait_time_secs); + return; + } + + auto old_ue_it = parent->users.find(old_rnti); + + // Fallback to connection establishment for unrecognized rntis, and PCIs that do not belong to eNB + if (old_ue_it == parent->users.end()) { + parent->logger.info( + "Fallback to connection establishment for rnti=0x%x. Cause: no rnti=0x%x context available", rnti, old_rnti); + srsran::console("Fallback to connection establishment for rnti=0x%x. Cause: no context available\n", rnti); + + // send RRC Setup + send_rrc_setup(); + set_activity_timeout(UE_INACTIVITY_TIMEOUT); + + return; + } + + // Reestablishment procedure going forward + parent->logger.info("ConnectionReestablishmentRequest for rnti=0x%x. Sending Connection Reestablishment.", old_rnti); + srsran::console("User 0x%x requesting RRC Reestablishment as 0x%x. Cause: %s\n", + rnti, + old_rnti, + msg.rrc_reest_request.reest_cause.to_string()); + + ue* old_ue = old_ue_it->second.get(); + + // Recover security setup + sec_ctx = old_ue->sec_ctx; + auto& pscell_cfg = parent->cfg.cell_list.at(UE_PSCELL_CC_IDX); + sec_ctx.regenerate_keys_handover(pscell_cfg.phy_cell.carrier.pci, pscell_cfg.ssb_absolute_freq_point); + + // For the reestablishment, only add SRB1 to new UE context + next_radio_bearer_cfg.srb_to_add_mod_list.resize(1); + srb_to_add_mod_s& srb1 = next_radio_bearer_cfg.srb_to_add_mod_list[0]; + srb1.srb_id = 1; + + // compute config and create SRB1 for new user + asn1::rrc_nr::radio_bearer_cfg_s dummy_radio_bearer_cfg; // just to compute difference, it's never sent to UE + compute_diff_radio_bearer_cfg(parent->cfg, radio_bearer_cfg, next_radio_bearer_cfg, dummy_radio_bearer_cfg); + if (fill_cellgroup_with_radio_bearer_cfg( + parent->cfg, old_rnti, *parent->bearer_mapper, dummy_radio_bearer_cfg, next_cell_group_cfg) != + SRSRAN_SUCCESS) { + logger.error("Couldn't fill cellGroupCfg during RRC Reestablishment"); + send_rrc_reject(max_wait_time_secs); + return; + } + + // send RRC Reestablishment message and restore bearer configuration + send_connection_reest(old_ue->sec_ctx.get_ncc()); + + // store current bearer/cell config with configured SRB1 + radio_bearer_cfg = next_radio_bearer_cfg; + cell_group_cfg = next_cell_group_cfg; + + // recover all previously created bearers from old UE object for (later) reconfiguration + next_radio_bearer_cfg = old_ue->radio_bearer_cfg; + next_cell_group_cfg = old_ue->cell_group_cfg; + + // Recover GTP-U tunnels and NGAP context + parent->gtpu->mod_bearer_rnti(old_rnti, rnti); + parent->ngap->user_mod(old_rnti, rnti); + + // Reestablish E-RABs of old rnti later, during ConnectionReconfiguration + // bearer_list.reestablish_bearers(std::move(old_ue->bearer_list)); + drb1_five_qi = old_ue->drb1_five_qi; + + // remove old RNTI + old_ue->deactivate_bearers(); + parent->bearer_mapper->rem_user(old_rnti); + parent->task_sched.defer_task([this, old_rnti]() { parent->rem_user(old_rnti); }); + + set_activity_timeout(MSG5_RX_TIMEOUT); +} + +void rrc_nr::ue::send_connection_reest(uint8_t ncc) +{ + dl_dcch_msg_s msg; + rrc_reest_ies_s& reest = msg.msg.set_c1().set_rrc_reest().crit_exts.set_rrc_reest(); + + msg.msg.c1().rrc_reest().rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + + // set NCC + reest.next_hop_chaining_count = ncc; + + // add RLC bearers + update_rlc_bearers(next_cell_group_cfg); + + // add PDCP bearers + // this is done after updating the RLC bearers, + // so the PDCP can query the RLC mode + update_pdcp_bearers(next_radio_bearer_cfg, next_cell_group_cfg); + + // add MAC bearers + update_mac(next_cell_group_cfg, false); + + if (send_dl_dcch(srsran::nr_srb::srb1, msg) != SRSRAN_SUCCESS) { + // TODO: Handle + } +} + +/// TS 38.331, RRCReject message +void rrc_nr::ue::send_rrc_reject(uint8_t reject_wait_time_secs) +{ + dl_ccch_msg_s msg; + rrc_reject_ies_s& reject = msg.msg.set_c1().set_rrc_reject().crit_exts.set_rrc_reject(); + + // See TS 38.331, RejectWaitTime + if (reject_wait_time_secs > 0) { + reject.wait_time_present = true; + reject.wait_time = reject_wait_time_secs; + } + if (send_dl_ccch(msg) != SRSRAN_SUCCESS) { + // TODO: Handle + } + + // TODO: remove user +} + +/// TS 38.331, RRCSetup +void rrc_nr::ue::send_rrc_setup() +{ + const uint8_t max_wait_time_secs = 16; + + // Add SRB1 to UE context + // Note: See 5.3.5.6.3 - SRB addition/modification + next_radio_bearer_cfg.srb_to_add_mod_list.resize(1); + srb_to_add_mod_s& srb1 = next_radio_bearer_cfg.srb_to_add_mod_list[0]; + srb1.srb_id = 1; + + // Generate RRC setup message + dl_ccch_msg_s msg; + rrc_setup_s& setup = msg.msg.set_c1().set_rrc_setup(); + setup.rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + rrc_setup_ies_s& setup_ies = setup.crit_exts.set_rrc_setup(); + + // Fill RRC Setup + // - Setup SRB1 + compute_diff_radio_bearer_cfg(parent->cfg, radio_bearer_cfg, next_radio_bearer_cfg, setup_ies.radio_bearer_cfg); + + // - Setup masterCellGroup + // - Derive master cell group config bearers + if (fill_cellgroup_with_radio_bearer_cfg( + parent->cfg, rnti, *parent->bearer_mapper, setup_ies.radio_bearer_cfg, next_cell_group_cfg) != + SRSRAN_SUCCESS) { + logger.error("Couldn't fill cellGroupCfg during RRC Setup"); + send_rrc_reject(max_wait_time_secs); + return; + } + + // - Pack masterCellGroup into container + srsran::unique_byte_buffer_t pdu = parent->pack_into_pdu(next_cell_group_cfg, __FUNCTION__); + if (pdu == nullptr) { + send_rrc_reject(max_wait_time_secs); + return; + } + setup_ies.master_cell_group.resize(pdu->N_bytes); + memcpy(setup_ies.master_cell_group.data(), pdu->data(), pdu->N_bytes); + if (logger.debug.enabled()) { + asn1::json_writer js; + next_cell_group_cfg.to_json(js); + logger.debug("Containerized MasterCellGroup: %s", js.to_string().c_str()); + } + + // add RLC bearers + update_rlc_bearers(next_cell_group_cfg); + + // add PDCP bearers + // this is done after updating the RLC bearers, + // so the PDCP can query the RLC mode + update_pdcp_bearers(next_radio_bearer_cfg, next_cell_group_cfg); + + // add MAC bearers + update_mac(next_cell_group_cfg, false); + + // Send RRC Setup message to UE + if (send_dl_ccch(msg) != SRSRAN_SUCCESS) { + send_rrc_reject(max_wait_time_secs); + } +} + +/// TS 38.331, RRCSetupComplete +void rrc_nr::ue::handle_rrc_setup_complete(const asn1::rrc_nr::rrc_setup_complete_s& msg) +{ + update_mac(next_cell_group_cfg, true); + + // Update current radio bearer cfg + radio_bearer_cfg = next_radio_bearer_cfg; + cell_group_cfg = next_cell_group_cfg; + + // Create UE context in NGAP + using ngap_cause_t = asn1::ngap::rrcestablishment_cause_opts::options; + auto ngap_cause = (ngap_cause_t)ctxt.connection_cause.value; // NGAP and RRC causes seem to have a 1-1 mapping + parent->ngap->initial_ue( + rnti, uecfg.carriers[0].cc, ngap_cause, msg.crit_exts.rrc_setup_complete().ded_nas_msg, ctxt.setup_ue_id); +} + +/// TS 38.331, SecurityModeCommand +void rrc_nr::ue::send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu) +{ + // apply selected security config and enable integrity on SRB1 before generating security mode command + update_as_security(srb_to_lcid(srsran::nr_srb::srb1), true, false); + + if (nas_pdu != nullptr) { + nas_pdu_queue.push_back(std::move(nas_pdu)); + } + + asn1::rrc_nr::dl_dcch_msg_s dl_dcch_msg; + dl_dcch_msg.msg.set_c1().set_security_mode_cmd().rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + security_mode_cmd_ies_s& ies = dl_dcch_msg.msg.c1().security_mode_cmd().crit_exts.set_security_mode_cmd(); + + ies.security_cfg_smc.security_algorithm_cfg.integrity_prot_algorithm_present = true; + ies.security_cfg_smc.security_algorithm_cfg = sec_ctx.get_security_algorithm_cfg(); + + if (send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg) != SRSRAN_SUCCESS) { + parent->ngap->user_release_request(rnti, asn1::ngap::cause_radio_network_opts::radio_res_not_available); + } +} + +/** + * @brief Internal helper to update the security configuration of a PDCP bearer + * + * If no valid AS security config is present (yet) the method doesn't modify the + * PDCP config and returns SRSRAN_ERROR. In some cases, however, + * for example during RRC Setup, this is in fact the expected behaviour as + * AS security isn't established yet. + * + * @param lcid Logical channel ID of the bearer + * @param enable_integrity Whether to enable integrity protection for the bearer + * @param enable_ciphering Whether to enable ciphering for the bearer + * @return int SRSRAN_SUCCESS if a valid AS security config was found and the security was configured + */ +int rrc_nr::ue::update_as_security(uint32_t lcid, bool enable_integrity = true, bool enable_ciphering = true) +{ + if (not sec_ctx.is_as_sec_cfg_valid()) { + parent->logger.error("Invalid AS security configuration. Skipping configuration for lcid=%d", lcid); + return SRSRAN_ERROR; + } + + // TODO: Currently we are using the PDCP-LTE, so we need to convert from nr_as_security_cfg to as_security_config. + // When we start using PDCP-NR we can avoid this step. + srsran::nr_as_security_config_t tmp_cnfg = sec_ctx.get_as_sec_cfg(); + srsran::as_security_config_t pdcp_cnfg = {}; + pdcp_cnfg.k_rrc_int = tmp_cnfg.k_nr_rrc_int; + pdcp_cnfg.k_rrc_enc = tmp_cnfg.k_nr_rrc_enc; + pdcp_cnfg.k_up_int = tmp_cnfg.k_nr_up_int; + pdcp_cnfg.k_up_enc = tmp_cnfg.k_nr_up_enc; + pdcp_cnfg.integ_algo = (srsran::INTEGRITY_ALGORITHM_ID_ENUM)tmp_cnfg.integ_algo; + pdcp_cnfg.cipher_algo = (srsran::CIPHERING_ALGORITHM_ID_ENUM)tmp_cnfg.cipher_algo; + + // configure algorithm and keys + parent->pdcp->config_security(rnti, lcid, pdcp_cnfg); + + if (enable_integrity) { + parent->pdcp->enable_integrity(rnti, lcid); + } + + if (enable_ciphering) { + parent->pdcp->enable_encryption(rnti, lcid); + } + + return SRSRAN_SUCCESS; +} + +/// TS 38.331, SecurityModeComplete +void rrc_nr::ue::handle_security_mode_complete(const asn1::rrc_nr::security_mode_complete_s& msg) +{ + parent->logger.info("SecurityModeComplete transaction ID: %d", msg.rrc_transaction_id); + + // finally, also enable ciphering on SRB1 + update_as_security(srb_to_lcid(srsran::nr_srb::srb1), false, true); + + send_ue_capability_enquiry(); +} + +/// TS 38.331, RRCReconfiguration +void rrc_nr::ue::send_rrc_reconfiguration() +{ + dl_dcch_msg_s dl_dcch_msg; + dl_dcch_msg.msg.set_c1().set_rrc_recfg().rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + rrc_recfg_ies_s& ies = dl_dcch_msg.msg.c1().rrc_recfg().crit_exts.set_rrc_recfg(); + + // Add new SRBs/DRBs + ies.radio_bearer_cfg_present = + compute_diff_radio_bearer_cfg(parent->cfg, radio_bearer_cfg, next_radio_bearer_cfg, ies.radio_bearer_cfg); + + // If no bearer to add/mod/remove, do not include master_cell_group + // Set ies.non_crit_ext_present (a few lines below) only if + // master_cell_group_present == true or ies.non_crit_ext.ded_nas_msg_list_present == true + if (ies.radio_bearer_cfg_present) { + // Fill masterCellGroup + cell_group_cfg_s master_cell_group; + master_cell_group.cell_group_id = 0; + if (fill_cellgroup_with_radio_bearer_cfg( + parent->cfg, rnti, *parent->bearer_mapper, ies.radio_bearer_cfg, master_cell_group) != SRSRAN_SUCCESS) { + logger.error("Couldn't fill cellGroupCfg during RRC Reconfiguration"); + parent->ngap->user_release_request(rnti, asn1::ngap::cause_radio_network_opts::radio_res_not_available); + return; + } + + // Pack masterCellGroup into container + srsran::unique_byte_buffer_t pdu = parent->pack_into_pdu(master_cell_group, __FUNCTION__); + if (pdu == nullptr) { + parent->ngap->user_release_request(rnti, asn1::ngap::cause_radio_network_opts::radio_res_not_available); + return; + } + ies.non_crit_ext.master_cell_group.resize(pdu->N_bytes); + memcpy(ies.non_crit_ext.master_cell_group.data(), pdu->data(), pdu->N_bytes); + if (logger.debug.enabled()) { + asn1::json_writer js; + master_cell_group.to_json(js); + logger.debug("Containerized MasterCellGroup: %s", js.to_string().c_str()); + } + + // Update lower layers + // add MAC bearers + update_mac(master_cell_group, false); + + // add RLC bearers + update_rlc_bearers(master_cell_group); + + // add PDCP bearers + // this is done after updating the RLC bearers, + // so the PDCP can query the RLC mode + update_pdcp_bearers(ies.radio_bearer_cfg, master_cell_group); + } + + if (nas_pdu_queue.size() > 0) { + // Pass stored NAS PDUs + ies.non_crit_ext.ded_nas_msg_list.resize(nas_pdu_queue.size()); + for (uint32_t i = 0; i < nas_pdu_queue.size(); ++i) { + ies.non_crit_ext.ded_nas_msg_list[i].resize(nas_pdu_queue[i]->size()); + memcpy(ies.non_crit_ext.ded_nas_msg_list[i].data(), nas_pdu_queue[i]->data(), nas_pdu_queue[i]->size()); + } + nas_pdu_queue.clear(); + } + + ies.non_crit_ext_present = + ies.non_crit_ext.master_cell_group.size() > 0 or ies.non_crit_ext.ded_nas_msg_list.size() > 0; + + if (send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg) != SRSRAN_SUCCESS) { + parent->ngap->user_release_request(rnti, asn1::ngap::cause_radio_network_opts::radio_res_not_available); + } +} + +int rrc_nr::ue::send_ue_capability_enquiry() +{ + dl_dcch_msg_s dl_dcch_msg; + dl_dcch_msg.msg.set_c1().set_ue_cap_enquiry().rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + ue_cap_enquiry_ies_s& ies = dl_dcch_msg.msg.c1().ue_cap_enquiry().crit_exts.set_ue_cap_enquiry(); + + // ue-CapabilityRAT-RequestList + ue_cap_rat_request_s cap_rat_request; + cap_rat_request.rat_type.value = rat_type_opts::nr; + + // capabilityRequestFilter + ue_cap_request_filt_nr_s request_filter; + + // frequencyBandListFilter + freq_band_info_c freq_band_info; + freq_band_info_nr_s& freq_band_info_nr = freq_band_info.set_band_info_nr(); + + // Iterate through cell list and assign bandInformationNR items + for (auto& iter : parent->cfg.cell_list) { + freq_band_info_nr.band_nr = iter.band; + request_filter.freq_band_list_filt.push_back(freq_band_info); + } + + // Pack capabilityRequestFilter + cap_rat_request.cap_request_filt.resize(128); + asn1::bit_ref bref_pack(cap_rat_request.cap_request_filt.data(), cap_rat_request.cap_request_filt.size()); + if (request_filter.pack(bref_pack) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack capabilityRequestFilter in UE Capability Enquiry"); + return SRSRAN_ERROR; + } + cap_rat_request.cap_request_filt.resize(bref_pack.distance_bytes()); + + ies.ue_cap_rat_request_list.push_back(cap_rat_request); + + send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg); + + return SRSRAN_SUCCESS; +} + +void rrc_nr::ue::handle_ue_capability_information(const asn1::rrc_nr::ue_cap_info_s& msg) +{ + logger.info("UECapabilityInformation transaction ID: %d", msg.rrc_transaction_id); + + send_rrc_reconfiguration(); + + // Send RRCReconfiguration if necessary + if (not nas_pdu_queue.empty()) { + send_rrc_reconfiguration(); + } +} + +void rrc_nr::ue::handle_rrc_reconfiguration_complete(const asn1::rrc_nr::rrc_recfg_complete_s& msg) +{ + update_mac(next_cell_group_cfg, true); + + radio_bearer_cfg = next_radio_bearer_cfg; + cell_group_cfg = next_cell_group_cfg; + parent->ngap->ue_notify_rrc_reconf_complete(rnti, true); +} + +void rrc_nr::ue::handle_rrc_reestablishment_complete(const asn1::rrc_nr::rrc_reest_complete_s& msg) +{ + // Register DRB again, TODO: combine/move to establish_eps_bearer() + for (const auto& drb : next_radio_bearer_cfg.drb_to_add_mod_list) { + uint16_t lcid = drb1_lcid; + parent->bearer_mapper->add_eps_bearer(rnti, lcid - 3, srsran::srsran_rat_t::nr, lcid); + parent->bearer_mapper->set_five_qi(rnti, lcid - 3, drb1_five_qi); + + logger.info("Established EPS bearer for LCID %u and RNTI 0x%x", lcid, rnti); + } + + // send reconfiguration to reestablish SRB2 and all previously established DRBs + send_rrc_reconfiguration(); +} + +void rrc_nr::ue::send_rrc_release() +{ + static const uint32_t release_delay = 60; // Taken from TS 38.331, 5.3.8.3 + + dl_dcch_msg_s dl_dcch_msg; + rrc_release_s& release = dl_dcch_msg.msg.set_c1().set_rrc_release(); + + release.rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + rrc_release_ies_s& ies = release.crit_exts.set_rrc_release(); + + ies.suspend_cfg_present = false; // goes to RRC_IDLE + + send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg); + state = rrc_nr_state_t::RRC_IDLE; + + // TODO: Obtain acknowledgment from lower layers that RRC Release was received + parent->task_sched.defer_callback(release_delay, [this]() { parent->rem_user(rnti); }); +} + +void rrc_nr::ue::send_dl_information_transfer(srsran::unique_byte_buffer_t sdu) +{ + dl_dcch_msg_s dl_dcch_msg; + dl_dcch_msg.msg.set_c1().set_dl_info_transfer().rrc_transaction_id = (uint8_t)((transaction_id++) % 4); + dl_info_transfer_ies_s& ies = dl_dcch_msg.msg.c1().dl_info_transfer().crit_exts.set_dl_info_transfer(); + + ies.ded_nas_msg.resize(sdu->N_bytes); + memcpy(ies.ded_nas_msg.data(), sdu->data(), ies.ded_nas_msg.size()); + + if (send_dl_dcch(srsran::nr_srb::srb1, dl_dcch_msg) != SRSRAN_SUCCESS) { + parent->ngap->user_release_request(rnti, asn1::ngap::cause_radio_network_opts::radio_res_not_available); + } +} + +void rrc_nr::ue::handle_ul_information_transfer(const asn1::rrc_nr::ul_info_transfer_s& msg) +{ + // Forward dedicatedNAS-Message to NGAP + parent->ngap->write_pdu(rnti, msg.crit_exts.ul_info_transfer().ded_nas_msg); +} + +void rrc_nr::ue::establish_eps_bearer(uint32_t pdu_session_id, + srsran::const_byte_span nas_pdu, + uint32_t lcid, + uint32_t five_qi) +{ + if (parent->cfg.five_qi_cfg.find(five_qi) == parent->cfg.five_qi_cfg.end()) { + parent->logger.error("No bearer config for 5QI %d present. Aborting DRB addition.", five_qi); + return; + } + + // Enqueue NAS PDU + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Couldn't allocate NAS PDU in %s().", __FUNCTION__); + return; + } + pdu->resize(nas_pdu.size()); + memcpy(pdu->data(), nas_pdu.data(), nas_pdu.size()); + nas_pdu_queue.push_back(std::move(pdu)); + + // Add SRB2, if not yet added + if (radio_bearer_cfg.srb_to_add_mod_list.size() <= 1) { + next_radio_bearer_cfg.srb_to_add_mod_list.push_back(srb_to_add_mod_s{}); + next_radio_bearer_cfg.srb_to_add_mod_list.back().srb_id = 2; + } + + drb_to_add_mod_s drb; + drb.cn_assoc_present = true; + drb.cn_assoc.set_sdap_cfg().pdu_session = 1; + drb.cn_assoc.sdap_cfg().sdap_hdr_dl.value = asn1::rrc_nr::sdap_cfg_s::sdap_hdr_dl_opts::absent; + drb.cn_assoc.sdap_cfg().sdap_hdr_ul.value = asn1::rrc_nr::sdap_cfg_s::sdap_hdr_ul_opts::absent; + drb.cn_assoc.sdap_cfg().default_drb = true; + drb.cn_assoc.sdap_cfg().mapped_qos_flows_to_add.resize(1); + drb.cn_assoc.sdap_cfg().mapped_qos_flows_to_add[0] = 1; + + drb.drb_id = 1; + drb.pdcp_cfg_present = true; + drb.pdcp_cfg = parent->cfg.five_qi_cfg[five_qi].pdcp_cfg; + + next_radio_bearer_cfg.drb_to_add_mod_list.push_back(drb); + + parent->bearer_mapper->add_eps_bearer( + rnti, lcid - 3, srsran::srsran_rat_t::nr, lcid); // TODO: configurable bearer id <-> lcid mapping + parent->bearer_mapper->set_five_qi(rnti, lcid - 3, five_qi); + + // store 5QI for possible reestablishment of DRB + drb1_five_qi = five_qi; + + logger.info("Established EPS bearer for LCID %u and RNTI 0x%x", lcid, rnti); +} + +bool rrc_nr::ue::init_pucch() +{ + // TODO: Allocate PUCCH resources + + return true; +} + +int rrc_nr::ue::update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_diff, + const asn1::rrc_nr::cell_group_cfg_s& cell_group_diff) +{ + // release DRBs + // TODO + + // add SRBs + for (const srb_to_add_mod_s& srb : radio_bearer_diff.srb_to_add_mod_list) { + srsran::pdcp_config_t pdcp_cnfg = srsran::make_nr_srb_pdcp_config_t(srb.srb_id, false); + const rlc_bearer_cfg_s* rlc_bearer = nullptr; + for (const rlc_bearer_cfg_s& item : cell_group_diff.rlc_bearer_to_add_mod_list) { + if (item.served_radio_bearer.type().value == rlc_bearer_cfg_s::served_radio_bearer_c_::types_opts::srb_id and + item.served_radio_bearer.srb_id() == srb.srb_id) { + rlc_bearer = &item; + break; + } + } + if (rlc_bearer == nullptr) { + logger.error("Inconsistency between cellGroupConfig and radioBearerConfig in ASN1 message"); + return SRSRAN_ERROR; + } + parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg); + + if (sec_ctx.is_as_sec_cfg_valid()) { + update_as_security(rlc_bearer->lc_ch_id); + } + } + + // Add DRBs + for (const drb_to_add_mod_s& drb : radio_bearer_diff.drb_to_add_mod_list) { + srsran::pdcp_config_t pdcp_cnfg = srsran::make_drb_pdcp_config_t(drb.drb_id, false, drb.pdcp_cfg); + const rlc_bearer_cfg_s* rlc_bearer = nullptr; + for (const rlc_bearer_cfg_s& item : cell_group_diff.rlc_bearer_to_add_mod_list) { + if (item.served_radio_bearer.type().value == rlc_bearer_cfg_s::served_radio_bearer_c_::types_opts::drb_id and + item.served_radio_bearer.drb_id() == drb.drb_id) { + rlc_bearer = &item; + break; + } + } + if (rlc_bearer == nullptr) { + logger.error("Inconsistency between cellGroupConfig and radioBearerConfig in ASN1 message"); + return SRSRAN_ERROR; + } + parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg); + + if (sec_ctx.is_as_sec_cfg_valid()) { + update_as_security(rlc_bearer->lc_ch_id, drb.pdcp_cfg.drb.integrity_protection_present, true); + } + } + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::update_rlc_bearers(const asn1::rrc_nr::cell_group_cfg_s& cell_group_diff) +{ + // Release RLC radio bearers + for (uint8_t lcid : cell_group_diff.rlc_bearer_to_release_list) { + parent->rlc->del_bearer(rnti, lcid); + } + + // Add/Mod RLC radio bearers + for (const rlc_bearer_cfg_s& rb : cell_group_diff.rlc_bearer_to_add_mod_list) { + srsran::rlc_config_t rlc_cfg; + uint8_t rb_id = 0; + if (rb.served_radio_bearer.type().value == rlc_bearer_cfg_s::served_radio_bearer_c_::types_opts::srb_id) { + rb_id = rb.served_radio_bearer.srb_id(); + if (not rb.rlc_cfg_present) { + rlc_cfg = srsran::rlc_config_t::default_rlc_am_nr_config(); + } else { + if (srsran::make_rlc_config_t(rb.rlc_cfg, rb_id, &rlc_cfg) != SRSRAN_SUCCESS) { + logger.error("Failed to build RLC config"); + // TODO: HANDLE + return SRSRAN_ERROR; + } + } + } else { + rb_id = rb.served_radio_bearer.drb_id(); + if (not rb.rlc_cfg_present) { + logger.error("No RLC config for DRB"); + // TODO: HANDLE + return SRSRAN_ERROR; + } + if (srsran::make_rlc_config_t(rb.rlc_cfg, rb_id, &rlc_cfg) != SRSRAN_SUCCESS) { + logger.error("Failed to build RLC config"); + // TODO: HANDLE + return SRSRAN_ERROR; + } + } + parent->rlc->add_bearer(rnti, rb.lc_ch_id, rlc_cfg); + } + + return SRSRAN_SUCCESS; +} + +int rrc_nr::ue::update_mac(const cell_group_cfg_s& cell_group_config, bool is_config_complete) +{ + if (not is_config_complete) { + // Release bearers + for (uint8_t lcid : cell_group_config.rlc_bearer_to_release_list) { + uecfg.lc_ch_to_rem.push_back(lcid); + } + + for (const rlc_bearer_cfg_s& bearer : cell_group_config.rlc_bearer_to_add_mod_list) { + uecfg.lc_ch_to_add.emplace_back(); + uecfg.lc_ch_to_add.back().lcid = bearer.lc_ch_id; + auto& lch = uecfg.lc_ch_to_add.back().cfg; + lch.direction = mac_lc_ch_cfg_t::BOTH; + if (bearer.mac_lc_ch_cfg.ul_specific_params_present) { + lch.priority = bearer.mac_lc_ch_cfg.ul_specific_params.prio; + lch.pbr = bearer.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate.to_number(); + lch.bsd = bearer.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur.to_number(); + lch.group = bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group; + // TODO: remaining fields + } + } + + if (cell_group_config.sp_cell_cfg_present and cell_group_config.sp_cell_cfg.sp_cell_cfg_ded_present and + cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present and + cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp_present and + cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg_present) { + auto& pucch_cfg = cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg.setup(); + srsran::fill_phy_pucch_cfg(pucch_cfg, &uecfg.phy_cfg.pucch); + } + } else { + auto& pdcch = cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(); + for (auto& ss : pdcch.search_spaces_to_add_mod_list) { + uecfg.phy_cfg.pdcch.search_space_present[ss.search_space_id] = true; + srsran::make_phy_search_space_cfg(ss, &uecfg.phy_cfg.pdcch.search_space[ss.search_space_id]); + } + for (auto& cs : pdcch.ctrl_res_set_to_add_mod_list) { + uecfg.phy_cfg.pdcch.coreset_present[cs.ctrl_res_set_id] = true; + srsran::make_phy_coreset_cfg(cs, &uecfg.phy_cfg.pdcch.coreset[cs.ctrl_res_set_id]); + } + } + + uecfg.sp_cell_cfg.reset(new sp_cell_cfg_s{cell_group_cfg.sp_cell_cfg}); + uecfg.mac_cell_group_cfg.reset(new mac_cell_group_cfg_s{cell_group_cfg.mac_cell_group_cfg}); + uecfg.phy_cell_group_cfg.reset(new phys_cell_group_cfg_s{cell_group_cfg.phys_cell_group_cfg}); + srsran::make_csi_cfg_from_serv_cell(cell_group_config.sp_cell_cfg.sp_cell_cfg_ded, &uecfg.phy_cfg.csi); + parent->mac->ue_cfg(rnti, uecfg); + + return SRSRAN_SUCCESS; +} + +/** + * @brief Deactivate all Bearers (MAC logical channel) for this specific RNTI + * + * The function iterates over the bearers or MAC logical channels and deactivates them by setting each one to IDLE + */ +void rrc_nr::ue::deactivate_bearers() +{ + // Iterate over the bearers (MAC LC CH) and set each of them to IDLE + for (uint32_t lcid = 1; lcid < SCHED_NR_MAX_LCID; ++lcid) { + uecfg.lc_ch_to_rem.push_back(lcid); + } + + // No need to check the returned value, as the function ue_cfg will return SRSRAN_SUCCESS (it asserts if it fails) + uecfg.phy_cell_group_cfg = {}; + uecfg.mac_cell_group_cfg = {}; + uecfg.sp_cell_cfg = {}; + parent->mac->ue_cfg(rnti, uecfg); +} + +template +void rrc_nr::ue::log_rrc_message(srsran::nr_srb srb, + const direction_t dir, + srsran::const_byte_span pdu, + const M& msg, + const char* msg_type) +{ + fmt::memory_buffer strbuf; + fmt::format_to(strbuf, "rnti=0x{:x}, {}", rnti, srsran::get_srb_name(srb)); + parent->log_rrc_message(srsran::to_c_str(strbuf), Tx, pdu, msg, msg_type); +} + +template +void rrc_nr::ue::log_rrc_container(const direction_t dir, + srsran::const_byte_span pdu, + const M& msg, + const char* msg_type) +{ + fmt::memory_buffer strbuf; + fmt::format_to(strbuf, "rnti=0x{:x}, container", rnti); + parent->log_rrc_message(srsran::to_c_str(strbuf), Tx, pdu, msg, msg_type); +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/rrc/test/CMakeLists.txt b/srsgnb/src/stack/rrc/test/CMakeLists.txt new file mode 100644 index 000000000..072d1b31c --- /dev/null +++ b/srsgnb/src/stack/rrc/test/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_library(rrc_nr_test_helpers STATIC rrc_nr_test_helpers.cc) + +add_executable(rrc_nr_test rrc_nr_test.cc) +target_link_libraries(rrc_nr_test srsgnb_rrc srsgnb_rrc_config_utils srsran_common rrc_nr_asn1 rrc_nr_test_helpers srsgnb_mac ${ATOMIC_LIBS}) +add_test(rrc_nr_test rrc_nr_test) + +add_executable(rrc_nr_core_test rrc_nr_core_test.cc) +target_link_libraries(rrc_nr_core_test srsgnb_rrc srsgnb_rrc_config_utils srsran_common rrc_nr_asn1 test_helpers + rrc_nr_test_helpers srsgnb_mac srsgnb_ngap ngap_nr_asn1 srsran_gtpu + srsenb_upper ${SCTP_LIBRARIES} ${ATOMIC_LIBS} ${Boost_LIBRARIES}) + diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_core_test.cc b/srsgnb/src/stack/rrc/test/rrc_nr_core_test.cc new file mode 100644 index 000000000..dc8181f69 --- /dev/null +++ b/srsgnb/src/stack/rrc/test/rrc_nr_core_test.cc @@ -0,0 +1,228 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rrc_nr_test_helpers.h" +#include "srsenb/hdr/enb.h" +#include "srsenb/hdr/stack/upper/gtpu.h" +#include "srsenb/test/rrc/test_helpers.h" +#include "srsgnb/hdr/stack/ngap/ngap.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h" +#include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h" +#include "srsran/common/network_utils.h" +#include "srsran/common/test_common.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" +#include "srsran/upper/gtpu.h" +#include +#include + +#include + +using namespace asn1::rrc_nr; +namespace bpo = boost::program_options; + +namespace srsenb { // namespace srsenb + +void parse_args(ngap_args_t* ngap_args, int argc, char* argv[]) +{ + // temporary helpers for conversion + std::string config_file; + std::string gnb_id{}; + std::string gnb_cell_id{}; + std::string gnb_tac{}; + std::string gnb_mcc{}; + std::string gnb_mnc{}; + + // Command line only options + bpo::options_description general("General options"); + + general.add_options()("help,h", "Produce help message"); + + // Command line or config file options + bpo::options_description common("Configuration options"); + + // clang-format off + common.add_options() + + ("gnb_id", bpo::value(&gnb_id)->default_value("0x0"), "gnb ID") + ("name", bpo::value(&ngap_args->gnb_name)->default_value("srsgnb01"), "gnb Name") + ("cell_id", bpo::value(&gnb_cell_id)->default_value("0x0"), "Cell ID") + ("tac", bpo::value(&gnb_tac)->default_value("0x0"), "Tracking Area Code") + ("mcc", bpo::value(&gnb_mcc)->default_value("001"), "Mobile Country Code") + ("mnc", bpo::value(&gnb_mnc)->default_value("01"), "Mobile Network Code") + ("amf_addr", bpo::value(&ngap_args->amf_addr)->default_value("127.0.0.1"), "IP address of AMF for NG connection") + ("n1c_bind_addr", bpo::value(&ngap_args->ngc_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for NGAP connection") + ("gtp_bind_addr", bpo::value(&ngap_args->gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection"); + + bpo::options_description position("Positional options"); + + // clang-format on + bpo::positional_options_description p{}; + p.add("config_file", -1); + + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).add(general); + + bpo::variables_map vm{}; + + try { + bpo::store(bpo::command_line_parser(argc, argv).options(cmdline_options).positional(p).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + exit(1); + } + + if (vm.count("help")) { + std::cout << common << std::endl << general << std::endl; + exit(0); + } + + // Convert hex strings + { + std::stringstream sstr{}; + sstr << std::hex << vm["gnb_id"].as(); + sstr >> ngap_args->gnb_id; + } + { + std::stringstream sstr{}; + sstr << std::hex << vm["cell_id"].as(); + uint16_t tmp; // Need intermediate uint16_t as uint8_t is treated as char + sstr >> tmp; + ngap_args->cell_id = tmp; + } + { + std::stringstream sstr{}; + sstr << std::hex << vm["tac"].as(); + sstr >> ngap_args->tac; + } + + // Convert MCC/MNC strings + if (!srsran::string_to_mcc(gnb_mcc, &ngap_args->mcc)) { + std::cout << "Error parsing mcc:" << gnb_mcc << " - must be a 3-digit string." << std::endl; + } + if (!srsran::string_to_mnc(gnb_mnc, &ngap_args->mnc)) { + std::cout << "Error parsing mnc:" << gnb_mnc << " - must be a 2 or 3-digit string." << std::endl; + } +} + +void test_rrc_sa_ngap_integration(ngap_args_t ngap_args) +{ + // This takes the existing RRC-NR tests and exercises the NGAP integration with a real core network. + // The test currently runs down untill the RRCReconfiguration. + srsran::task_scheduler task_sched; + + phy_nr_dummy phy_obj; + mac_nr_dummy mac_obj; + rlc_nr_rrc_tester rlc_obj; + pdcp_nr_rrc_tester pdcp_obj; + rrc_nr rrc_obj(&task_sched); + enb_bearer_manager bearer_mapper; + + // NGAP Setup + auto& ngap_logger = srslog::fetch_basic_logger("NGAP"); + ngap_logger.set_level(srslog::basic_levels::debug); + ngap_logger.set_hex_dump_max_size(-1); + auto& gtpu_logger = srslog::fetch_basic_logger("GTPU"); + gtpu_logger.set_level(srslog::basic_levels::debug); + gtpu_logger.set_hex_dump_max_size(-1); + + srsran::socket_manager rx_sockets; + srsenb::ngap ngap_obj(&task_sched, ngap_logger, &rx_sockets); + srsenb::gtpu gtpu_obj(&task_sched, gtpu_logger, &rx_sockets); + + gtpu_args_t gtpu_args; + gtpu_args.embms_enable = false; + gtpu_args.mme_addr = ngap_args.amf_addr; + gtpu_args.gtp_bind_addr = ngap_args.gtp_bind_addr; + TESTASSERT(gtpu_obj.init(gtpu_args, &pdcp_obj) == SRSRAN_SUCCESS); + TESTASSERT(ngap_obj.init(ngap_args, &rrc_obj, >pu_obj) == SRSRAN_SUCCESS); + task_sched.run_next_task(); + + // set cfg + rrc_nr_cfg_t rrc_cfg_nr = rrc_nr_cfg_t{}; + rrc_cfg_nr.cell_list.emplace_back(); + generate_default_nr_cell(rrc_cfg_nr.cell_list[0]); + rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500; + rrc_cfg_nr.cell_list[0].dl_arfcn = 368500; + rrc_cfg_nr.cell_list[0].band = 3; + rrc_cfg_nr.cell_list[0].phy_cell.carrier.nof_prb = 52; + rrc_cfg_nr.cell_list[0].duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + rrc_cfg_nr.is_standalone = true; + set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]); + + TESTASSERT( + rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, &ngap_obj, >pu_obj, bearer_mapper, nullptr) == + SRSRAN_SUCCESS); + + TESTASSERT_SUCCESS(rrc_obj.add_user(0x4601, 0)); + + // RRCSetupComplete triggers NGAP Initial UE Message with NAS-PDU: Registration Request + ngap_rrc_tester ngap_dummy; + test_rrc_nr_connection_establishment(task_sched, rrc_obj, rlc_obj, mac_obj, ngap_dummy, 0x4601); + task_sched.run_next_task(); + + // ULInformationTransfer -> UplinkNASTransport(NAS Authentication Response) + srsran::unique_byte_buffer_t auth_resp_pdu; + auth_resp_pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref_ar{auth_resp_pdu->data(), auth_resp_pdu->get_tailroom()}; + ul_dcch_msg_s ul_dcch_msg_auth_resp; + + ul_dcch_msg_auth_resp.msg.set_c1().set_ul_info_transfer().crit_exts.set_ul_info_transfer(); + ul_dcch_msg_auth_resp.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg.from_string( + "7e00572d10db165fffdb7b74c326e3fc3f154117fe"); + + TESTASSERT_SUCCESS(ul_dcch_msg_auth_resp.pack(bref_ar)); + auth_resp_pdu->N_bytes = bref_ar.distance_bytes(); + rrc_obj.write_pdu(0x4601, 1, std::move(auth_resp_pdu)); + task_sched.run_next_task(); + + // ULInformationTransfer -> UplinkNASTransport(NAS Security Mode Complete) + srsran::unique_byte_buffer_t sec_complete_pdu; + sec_complete_pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref_smc{sec_complete_pdu->data(), sec_complete_pdu->get_tailroom()}; + ul_dcch_msg_s ul_dcch_msg_smc; + ul_dcch_msg_smc.msg.set_c1().set_ul_info_transfer().crit_exts.set_ul_info_transfer(); + ul_dcch_msg_smc.msg.c1().ul_info_transfer().crit_exts.ul_info_transfer().ded_nas_msg.from_string( + "7e046b3737af017e005e7700093535940096783351f37100237e004179000d0100f11000000000103254760810030000002e02e0602f0201" + "01530100"); + TESTASSERT_SUCCESS(ul_dcch_msg_smc.pack(bref_smc)); + sec_complete_pdu->N_bytes = bref_smc.distance_bytes(); + rrc_obj.write_pdu(0x4601, 1, std::move(sec_complete_pdu)); + task_sched.run_next_task(); + + test_rrc_nr_security_mode_cmd(task_sched, rrc_obj, pdcp_obj, 0x4601); +} +} // namespace srsenb + +int main(int argc, char** argv) +{ + auto& logger = srslog::fetch_basic_logger("ASN1"); + logger.set_level(srslog::basic_levels::info); + auto& rrc_logger = srslog::fetch_basic_logger("RRC-NR"); + rrc_logger.set_level(srslog::basic_levels::debug); + + srslog::init(); + ngap_args_t ngap_args = {}; + srsenb::parse_args(&ngap_args, argc, argv); + srsenb::test_rrc_sa_ngap_integration(ngap_args); + + return SRSRAN_SUCCESS; +} \ No newline at end of file diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc new file mode 100644 index 000000000..da102146b --- /dev/null +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc @@ -0,0 +1,228 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rrc_nr_test_helpers.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h" +#include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h" +#include "srsran/common/bearer_manager.h" +#include "srsran/common/test_common.h" +#include "srsran/interfaces/gnb_rrc_nr_interfaces.h" +#include + +using namespace asn1::rrc_nr; + +namespace srsenb { + +int test_cell_cfg(const srsenb::sched_interface::cell_cfg_t& cellcfg) +{ + // SIB1 must exist and have period 16rf + TESTASSERT(cellcfg.sibs[0].len > 0); + TESTASSERT(cellcfg.sibs[0].period_rf == 16); + + TESTASSERT(cellcfg.si_window_ms > 0); + return SRSRAN_SUCCESS; +} + +/* + * Test 1 - Test default SIB generation + * Description: Check whether the SIBs were set correctly + */ +void test_sib_generation() +{ + srsran::test_delimit_logger test_logger{"SIB generation"}; + + srsran::task_scheduler task_sched; + phy_nr_dummy phy_obj; + mac_nr_dummy mac_obj; + rlc_dummy rlc_obj; + pdcp_dummy pdcp_obj; + rrc_nr rrc_obj(&task_sched); + enb_bearer_manager bearer_mapper; + + // set cfg + rrc_nr_cfg_t rrc_cfg_nr = {}; + rrc_cfg_nr.cell_list.emplace_back(); + generate_default_nr_cell(rrc_cfg_nr.cell_list[0]); + rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500; + rrc_cfg_nr.cell_list[0].dl_arfcn = 368500; + rrc_cfg_nr.cell_list[0].band = 3; + rrc_cfg_nr.cell_list[0].phy_cell.carrier.nof_prb = 52; + rrc_cfg_nr.cell_list[0].duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + rrc_cfg_nr.is_standalone = true; + rrc_cfg_nr.enb_id = 0x19B; + srsran::string_to_mcc("001", &rrc_cfg_nr.mcc); + srsran::string_to_mnc("01", &rrc_cfg_nr.mnc); + set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]); + + TESTASSERT( + rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr, bearer_mapper, nullptr) == + SRSRAN_SUCCESS); + + const sched_nr_cell_cfg_t& nrcell = mac_obj.nr_cells.at(0); + + TESTASSERT(nrcell.sibs.size() > 0); + + // TEST SIB1 + TESTASSERT(nrcell.sibs[0].len > 0); + TESTASSERT_EQ(16, nrcell.sibs[0].period_rf); + + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + TESTASSERT_EQ(SRSRAN_SUCCESS, rrc_obj.read_pdu_bcch_dlsch(0, *pdu)); + TESTASSERT(pdu->size() > 0); + asn1::rrc_nr::bcch_dl_sch_msg_s msg; + { + asn1::cbit_ref bref{pdu->data(), pdu->size()}; + TESTASSERT_EQ(SRSRAN_SUCCESS, msg.unpack(bref)); + } + TESTASSERT_EQ(bcch_dl_sch_msg_type_c::types_opts::c1, msg.msg.type().value); + TESTASSERT_EQ(bcch_dl_sch_msg_type_c::c1_c_::types_opts::sib_type1, msg.msg.c1().type().value); + asn1::rrc_nr::sib1_s& sib1 = msg.msg.c1().sib_type1(); + TESTASSERT(sib1.serving_cell_cfg_common_present); + + pdcch_cfg_common_s& pdcch = sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(); + TESTASSERT(not pdcch.ctrl_res_set_zero_present); // CORESET#0 id is passed in MIB + TESTASSERT(not pdcch.search_space_zero_present); // SS#0 id is passed in MIB +} + +int test_rrc_setup() +{ + srsran::test_delimit_logger test_logger{"NSA RRC"}; + + srsran::task_scheduler task_sched; + phy_nr_dummy phy_obj; + mac_nr_dummy mac_obj; + rlc_dummy rlc_obj; + pdcp_dummy pdcp_obj; + enb_bearer_manager bearer_mapper; + rrc_nr rrc_obj(&task_sched); + + // set cfg + rrc_nr_cfg_t rrc_cfg_nr = {}; + rrc_cfg_nr.cell_list.emplace_back(); + generate_default_nr_cell(rrc_cfg_nr.cell_list[0]); + rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500; + rrc_cfg_nr.cell_list[0].dl_arfcn = 634240; + rrc_cfg_nr.cell_list[0].coreset0_idx = 3; + rrc_cfg_nr.cell_list[0].band = 78; + rrc_cfg_nr.cell_list[0].phy_cell.carrier.nof_prb = 52; + rrc_cfg_nr.is_standalone = false; + rrc_cfg_nr.enb_id = 0x19B; + srsran::string_to_mcc("001", &rrc_cfg_nr.mcc); + srsran::string_to_mnc("01", &rrc_cfg_nr.mnc); + set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]); + TESTASSERT( + rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr, bearer_mapper, nullptr) == + SRSRAN_SUCCESS); + + for (uint32_t n = 0; n < 2; ++n) { + uint32_t timeout = 5500; + for (uint32_t i = 0; i < timeout and rlc_obj.last_sdu == nullptr; ++i) { + task_sched.tic(); + } + // TODO: trigger proper RRC Setup procedure (not timer based) + // TESTASSERT(rlc_obj.last_sdu != nullptr); + } + return SRSRAN_SUCCESS; +} + +void test_rrc_sa_connection() +{ + srsran::test_delimit_logger test_logger{"SA RRCConnectionEstablishment"}; + + srsran::task_scheduler task_sched; + phy_nr_dummy phy_obj; + mac_nr_dummy mac_obj; + rlc_nr_rrc_tester rlc_obj; + pdcp_nr_rrc_tester pdcp_obj; + ngap_rrc_tester ngap_obj; + enb_bearer_manager bearer_mapper; + + rrc_nr rrc_obj(&task_sched); + + // Dummy RLC/PDCP configs + asn1::rrc_nr::rlc_cfg_c rlc_cfg; + rlc_cfg.set_um_bi_dir(); + rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_e::ms50; + + // set cfg + rrc_nr_cfg_t rrc_cfg_nr = {}; + rrc_cfg_nr.cell_list.emplace_back(); + generate_default_nr_cell(rrc_cfg_nr.cell_list[0]); + rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500; + rrc_cfg_nr.cell_list[0].dl_arfcn = 368500; + rrc_cfg_nr.cell_list[0].band = 3; + rrc_cfg_nr.cell_list[0].phy_cell.carrier.nof_prb = 52; + rrc_cfg_nr.cell_list[0].duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + rrc_cfg_nr.is_standalone = true; + rrc_cfg_nr.enb_id = 0x19B; + rrc_cfg_nr.five_qi_cfg[9].configured = true; + rrc_cfg_nr.five_qi_cfg[9].rlc_cfg = rlc_cfg; + rrc_cfg_nr.five_qi_cfg[9].pdcp_cfg = {}; + srsran::string_to_mcc("001", &rrc_cfg_nr.mcc); + srsran::string_to_mnc("01", &rrc_cfg_nr.mnc); + set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]); + + TESTASSERT( + rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, &ngap_obj, nullptr, bearer_mapper, nullptr) == + SRSRAN_SUCCESS); + + TESTASSERT_SUCCESS(rrc_obj.add_user(0x4601, 0)); + TESTASSERT_SUCCESS(rrc_obj.ue_set_security_cfg_key(0x4601, {})); + + test_rrc_nr_connection_establishment(task_sched, rrc_obj, rlc_obj, mac_obj, ngap_obj, 0x4601); + test_rrc_nr_info_transfer(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601); + test_rrc_nr_security_mode_cmd(task_sched, rrc_obj, pdcp_obj, 0x4601); + test_rrc_nr_ue_capability_enquiry(task_sched, rrc_obj, pdcp_obj, 0x4601); + test_rrc_nr_reconfiguration(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601); + test_rrc_nr_2nd_reconfiguration(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601); +} + +} // namespace srsenb + +int main(int argc, char** argv) +{ + // Setup the log spy to intercept error and warning log entries. + if (!srslog::install_custom_sink( + srsran::log_sink_spy::name(), + std::unique_ptr(new srsran::log_sink_spy(srslog::get_default_log_formatter())))) { + return SRSRAN_ERROR; + } + + auto* spy = static_cast(srslog::find_sink(srsran::log_sink_spy::name())); + if (!spy) { + return SRSRAN_ERROR; + } + + auto& logger = srslog::fetch_basic_logger("ASN1", *spy, true); + logger.set_level(srslog::basic_levels::info); + auto& test_log = srslog::fetch_basic_logger("RRC-NR", *spy, true); + test_log.set_level(srslog::basic_levels::debug); + + srslog::init(); + + srsenb::test_sib_generation(); + TESTASSERT(srsenb::test_rrc_setup() == SRSRAN_SUCCESS); + srsenb::test_rrc_sa_connection(); + TESTASSERT_EQ(0, spy->get_warning_counter()); + TESTASSERT_EQ(0, spy->get_error_counter()); + + return SRSRAN_SUCCESS; +} diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc new file mode 100644 index 000000000..c30242460 --- /dev/null +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.cc @@ -0,0 +1,432 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "rrc_nr_test_helpers.h" +#include "srsran/common/test_common.h" +#include + +#define NAS_SEC_CMD_STR "d9119b97d7bb59fc842d5b9cc12f00c27e9d5e4c80ee4cceb99a0dbbc6e0b54daa21a5d9e36d2e3b" + +using namespace asn1::rrc_nr; + +namespace srsenb { + +void test_rrc_nr_connection_establishment(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + rlc_nr_rrc_tester& rlc, + mac_nr_dummy& mac, + ngap_rrc_tester& ngap, + uint16_t rnti) +{ + srsran::unique_byte_buffer_t pdu; + + // Step 1 - Send RRCSetupRequest (UE -> gNB) + ul_ccch_msg_s setup_msg; + rrc_setup_request_ies_s& setup = setup_msg.msg.set_c1().set_rrc_setup_request().rrc_setup_request; + setup.establishment_cause.value = establishment_cause_opts::mo_data; + setup.ue_id.set_random_value().from_number(0); + { + pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref{pdu->data(), pdu->get_tailroom()}; + TESTASSERT_SUCCESS(setup_msg.pack(bref)); + pdu->N_bytes = bref.distance_bytes(); + } + + // Pass message to RRC + rrc_obj.write_pdu(rnti, 0, std::move(pdu)); + task_sched.tic(); + + // Step 2 - RRCSetup (gNB -> UE) + // The response to RRCSetupRequest has been sent by RRC - check if this message (RRCSetup) is correct + TESTASSERT_EQ(rnti, rlc.last_sdu_rnti); + TESTASSERT_EQ(srsran::srb_to_lcid(srsran::nr_srb::srb0), rlc.last_sdu_lcid); + TESTASSERT(rlc.last_sdu->size() > 0); + + // dl_ccch_msg will store the unpacked RRCSetup msg sent by the RRC + dl_ccch_msg_s dl_ccch_msg; + { + asn1::cbit_ref bref{rlc.last_sdu->data(), rlc.last_sdu->size()}; + TESTASSERT_SUCCESS(dl_ccch_msg.unpack(bref)); + } + // Test if the RRC sent the correct RRCSetup msg + TESTASSERT_EQ(dl_ccch_msg_type_c::types_opts::c1, dl_ccch_msg.msg.type().value); + TESTASSERT_EQ(dl_ccch_msg_type_c::c1_c_::types_opts::rrc_setup, dl_ccch_msg.msg.c1().type().value); + TESTASSERT_EQ(rrc_setup_s::crit_exts_c_::types_opts::rrc_setup, + dl_ccch_msg.msg.c1().rrc_setup().crit_exts.type().value); + + const rrc_setup_ies_s& setup_ies = dl_ccch_msg.msg.c1().rrc_setup().crit_exts.rrc_setup(); + TESTASSERT(setup_ies.radio_bearer_cfg.srb_to_add_mod_list.size() > 0); + TESTASSERT_EQ(1, setup_ies.radio_bearer_cfg.srb_to_add_mod_list.size()); + + const srb_to_add_mod_s& srb1 = setup_ies.radio_bearer_cfg.srb_to_add_mod_list[0]; + TESTASSERT_EQ(srsran::srb_to_lcid(srsran::nr_srb::srb1), srb1.srb_id); + // Test UE context in MAC + TESTASSERT_EQ(rnti, mac.last_ue_cfg_rnti); + // Only LCID=1 is added + TESTASSERT_EQ(1, mac.last_ue_cfg.lc_ch_to_add.size()); + TESTASSERT_EQ(1, mac.last_ue_cfg.lc_ch_to_add.front().lcid); + TESTASSERT_EQ(mac_lc_ch_cfg_t::BOTH, mac.last_ue_cfg.lc_ch_to_add.front().cfg.direction); + bool found_common_ul_dci_format = false; + for (uint32_t ss_id = 0; ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++ss_id) { + if (mac.last_ue_cfg.phy_cfg.pdcch.search_space_present[ss_id]) { + const srsran_search_space_t& ss = mac.last_ue_cfg.phy_cfg.pdcch.search_space[ss_id]; + // Ensure MAC ue_cfg does not have yet any UE-specific SS + TESTASSERT_NEQ(srsran_search_space_type_ue, ss.type); + for (uint32_t f = 0; f < ss.nof_formats; ++f) { + found_common_ul_dci_format |= ss.formats[f] == srsran_dci_format_nr_0_0; + } + } + } + TESTASSERT(found_common_ul_dci_format); + + // Step 3 - RRCSetupComplete (UE -> gNB) - Configure the msg and send it to RRC + ul_dcch_msg_s ul_dcch_msg; + rrc_setup_complete_s& complete = ul_dcch_msg.msg.set_c1().set_rrc_setup_complete(); + complete.rrc_transaction_id = dl_ccch_msg.msg.c1().rrc_setup().rrc_transaction_id; + rrc_setup_complete_ies_s& complete_ies = complete.crit_exts.set_rrc_setup_complete(); + complete_ies.sel_plmn_id = 1; // First PLMN in list + complete_ies.registered_amf_present = true; + complete_ies.registered_amf.amf_id.from_number(0x800101); + complete_ies.guami_type_present = true; + complete_ies.guami_type.value = rrc_setup_complete_ies_s::guami_type_opts::native; + std::string NAS_msg_str = "7E01280E534C337E004109000BF200F110800101347B80802E02F07071002D7E004109000BF200F11080010134" + "7B80801001002E02F0702F0201015200F11000006418010174000090530101"; + auto& ded_nas_msg = complete_ies.ded_nas_msg.from_string(NAS_msg_str); + + { + pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref{pdu->data(), pdu->get_tailroom()}; + TESTASSERT_SUCCESS(ul_dcch_msg.pack(bref)); + pdu->N_bytes = bref.distance_bytes(); + } + rrc_obj.write_pdu(rnti, 1, std::move(pdu)); + + // Test new UE context in MAC + bool ss_ue_found = false; + for (uint32_t ss_id = 0; ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++ss_id) { + if (mac.last_ue_cfg.phy_cfg.pdcch.search_space_present[ss_id]) { + const srsran_search_space_t& ss = mac.last_ue_cfg.phy_cfg.pdcch.search_space[ss_id]; + if (ss.type == srsran_search_space_type_ue) { + ss_ue_found = true; + } + } + } + TESTASSERT(ss_ue_found); /// Ensure UE-specific SearchSpace was added + + // Check here if the MSG sent to NGAP is correct + // Create a unbounded_octstring for the expected MSG + asn1::unbounded_octstring expected; + expected.from_string(NAS_msg_str); + TESTASSERT(expected == ngap.last_pdu); +} + +void test_rrc_nr_info_transfer(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + ngap_rrc_tester& ngap, + uint16_t rnti) +{ + // STEP 1 : Send DLInformationTransfer (gNB -> UE) + // generate sdu to pass as NAS message in DLInformationTransfer + srsran::unique_byte_buffer_t nsa_sdu; + nsa_sdu = srsran::make_byte_buffer(); + + // create an unbounded_octstring object that contains a random NAS message (we simulate a NAS message) + asn1::unbounded_octstring NAS_DL_msg; + NAS_DL_msg.from_string("21d9dfe07800371095c79a751be8352fb44aba7d69b836f5aad594ede9e72b8e34105ca8d7669d5c"); + nsa_sdu->append_bytes(NAS_DL_msg.data(), NAS_DL_msg.size()); + + // trigger the RRC to send the DLInformationTransfer + rrc_obj.write_dl_info(rnti, std::move(nsa_sdu)); + + // Test whether there exists the SRB1 initiated in the Connection Establishment + // We test this as the SRB1 was setup in a different function + TESTASSERT_EQ(rnti, pdcp.last_sdu_rnti); + TESTASSERT_EQ(srsran::srb_to_lcid(srsran::nr_srb::srb1), pdcp.last_sdu_lcid); + + // Send SecurityModeCommand (gNB -> UE) + dl_dcch_msg_s dl_dcch_msg; + { + asn1::cbit_ref bref{pdcp.last_sdu->data(), pdcp.last_sdu->size()}; + TESTASSERT_SUCCESS(dl_dcch_msg.unpack(bref)); + } + + // Test if the unpacked message retrived from PCDP is correct + TESTASSERT_EQ(dl_dcch_msg_type_c::types_opts::c1, dl_dcch_msg.msg.type().value); + TESTASSERT_EQ(dl_dcch_msg_type_c::c1_c_::types_opts::dl_info_transfer, dl_dcch_msg.msg.c1().type().value); + TESTASSERT_EQ(dl_info_transfer_s::crit_exts_c_::types_opts::dl_info_transfer, + dl_dcch_msg.msg.c1().dl_info_transfer().crit_exts.type().value); + + dl_info_transfer_ies_s& ies_DL = dl_dcch_msg.msg.c1().dl_info_transfer().crit_exts.dl_info_transfer(); + TESTASSERT(ies_DL.ded_nas_msg.size() > 0); + TESTASSERT(NAS_DL_msg == ies_DL.ded_nas_msg); + + // STEP 2: Send ULInformationTransfer (UE -> gNB) + ul_dcch_msg_s ul_dcch_msg; + auto& ies_UL = ul_dcch_msg.msg.set_c1().set_ul_info_transfer().crit_exts.set_ul_info_transfer(); + + // Create an unbounded_octstring object that contains a random NAS message (we simulate a NAS message) + // We reuse ies_UL below to compare the string with the message sent to and unpacked by the gNB + ies_UL.ded_nas_msg.from_string("6671f8bc80b1860f29b3a8b3b8563ce6c36a591bb1a3dc6612674448fb958d274426d326356aa9aa"); + + srsran::unique_byte_buffer_t pdu; + { + pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref{pdu->data(), pdu->get_tailroom()}; + TESTASSERT_SUCCESS(ul_dcch_msg.pack(bref)); + pdu->N_bytes = bref.distance_bytes(); + } + + // send message to RRC + rrc_obj.write_pdu(rnti, 1, std::move(pdu)); + + // compare if the actual transmitted matches with the MSG created from the original string + TESTASSERT(ies_UL.ded_nas_msg == ngap.last_pdu); +} + +void test_rrc_nr_security_mode_cmd(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + uint16_t rnti) +{ + // create an unbounded_octstring object that contains a random NAS message (we simulate a NAS message) + asn1::unbounded_octstring NAS_msg; + NAS_msg.from_string(NAS_SEC_CMD_STR); + srsran::unique_byte_buffer_t nas_pdu; + nas_pdu = srsran::make_byte_buffer(); + nas_pdu->append_bytes(NAS_msg.data(), NAS_msg.size()); + + // Trigger Send SecurityCommand (simulate request from NGAP) + rrc_obj.start_security_mode_procedure(rnti, std::move(nas_pdu)); + + // Test whether there exists the SRB1 initiated in the Connection Establishment + // We test this as the SRB1 was setup in a different function + TESTASSERT_EQ(rnti, pdcp.last_sdu_rnti); + TESTASSERT_EQ(srsran::srb_to_lcid(srsran::nr_srb::srb1), pdcp.last_sdu_lcid); + + // STEP 1 - Send SecurityModeCommand (gNB -> UE) + dl_dcch_msg_s dl_dcch_msg; + { + asn1::cbit_ref bref{pdcp.last_sdu->data(), pdcp.last_sdu->size()}; + TESTASSERT_SUCCESS(dl_dcch_msg.unpack(bref)); + } + TESTASSERT_EQ(dl_dcch_msg_type_c::types_opts::c1, dl_dcch_msg.msg.type().value); + TESTASSERT_EQ(dl_dcch_msg_type_c::c1_c_::types_opts::security_mode_cmd, dl_dcch_msg.msg.c1().type().value); + TESTASSERT_EQ(security_mode_cmd_s::crit_exts_c_::types_opts::security_mode_cmd, + dl_dcch_msg.msg.c1().security_mode_cmd().crit_exts.type().value); + + security_mode_cmd_ies_s& ies = dl_dcch_msg.msg.c1().security_mode_cmd().crit_exts.security_mode_cmd(); + TESTASSERT_EQ(true, ies.security_cfg_smc.security_algorithm_cfg.integrity_prot_algorithm_present); + TESTASSERT_EQ(integrity_prot_algorithm_opts::nia0, + ies.security_cfg_smc.security_algorithm_cfg.integrity_prot_algorithm.value); + TESTASSERT_EQ(ciphering_algorithm_opts::nea0, ies.security_cfg_smc.security_algorithm_cfg.ciphering_algorithm.value); + + // STEP 2 - Send SecurityModeComplete (UE -> gNB) + ul_dcch_msg_s ul_dcch_msg; + auto& sec_cmd_complete_msg = ul_dcch_msg.msg.set_c1().set_security_mode_complete(); + sec_cmd_complete_msg.rrc_transaction_id = dl_dcch_msg.msg.c1().security_mode_cmd().rrc_transaction_id; + auto& ies_complete = sec_cmd_complete_msg.crit_exts.set_security_mode_complete(); + + srsran::unique_byte_buffer_t pdu; + { + pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref{pdu->data(), pdu->get_tailroom()}; + TESTASSERT_SUCCESS(ul_dcch_msg.pack(bref)); + pdu->N_bytes = bref.distance_bytes(); + } + + // send message to RRC + rrc_obj.write_pdu(rnti, 1, std::move(pdu)); +} + +void test_rrc_nr_ue_capability_enquiry(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + uint16_t rnti) +{ + dl_dcch_msg_s dl_dcch_msg; + { + asn1::cbit_ref bref{pdcp.last_sdu->data(), pdcp.last_sdu->size()}; + TESTASSERT_SUCCESS(dl_dcch_msg.unpack(bref)); + } + + // Check if unpacked message is correct (ueCapabilityEnquiry | gNB -> UE) + TESTASSERT_EQ(dl_dcch_msg_type_c::types_opts::c1, dl_dcch_msg.msg.type().value); + TESTASSERT_EQ(dl_dcch_msg_type_c::c1_c_::types_opts::ue_cap_enquiry, dl_dcch_msg.msg.c1().type().value); + TESTASSERT_EQ(ue_cap_enquiry_s::crit_exts_c_::types_opts::ue_cap_enquiry, + dl_dcch_msg.msg.c1().ue_cap_enquiry().crit_exts.type().value); + + // Send response (ueCapabilityInformation | UE -> gNB) + ul_dcch_msg_s ul_dcch_msg; + auto& ue_capability_information = ul_dcch_msg.msg.set_c1().set_ue_cap_info(); + ue_capability_information.rrc_transaction_id = dl_dcch_msg.msg.c1().ue_cap_enquiry().rrc_transaction_id; + ue_capability_information.crit_exts.set_ue_cap_info(); + + srsran::unique_byte_buffer_t pdu; + { + pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref{pdu->data(), pdu->get_tailroom()}; + TESTASSERT_SUCCESS(ul_dcch_msg.pack(bref)); + pdu->N_bytes = bref.distance_bytes(); + } + + // send message to RRC + rrc_obj.write_pdu(rnti, 1, std::move(pdu)); +} + +void test_rrc_nr_reconfiguration(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + ngap_rrc_tester& ngap, + uint16_t rnti) +{ + // Test whether there exists the SRB1 initiated in the Connection Establishment + // We test this as the SRB1 was set up in a different function + TESTASSERT_EQ(rnti, pdcp.last_sdu_rnti); + TESTASSERT_EQ(srsran::srb_to_lcid(srsran::nr_srb::srb1), pdcp.last_sdu_lcid); + + dl_dcch_msg_s dl_dcch_msg; + { + asn1::cbit_ref bref{pdcp.last_sdu->data(), pdcp.last_sdu->size()}; + TESTASSERT_SUCCESS(dl_dcch_msg.unpack(bref)); + } + + // Test whether the unpacked message is correct + TESTASSERT_EQ(dl_dcch_msg_type_c::types_opts::c1, dl_dcch_msg.msg.type().value); + TESTASSERT_EQ(dl_dcch_msg_type_c::c1_c_::types_opts::rrc_recfg, dl_dcch_msg.msg.c1().type().value); + TESTASSERT_EQ(rrc_recfg_s::crit_exts_c_::types_opts::rrc_recfg, + dl_dcch_msg.msg.c1().rrc_recfg().crit_exts.type().value); + const rrc_recfg_ies_s& reconf_ies = dl_dcch_msg.msg.c1().rrc_recfg().crit_exts.rrc_recfg(); + + // create an unbounded_octstring object that contains the same NAS message as in SecurityModeCommand + // The RRCreconfiguration reads the SecurityModeCommand NAS msg previously saved in the queue + asn1::unbounded_octstring NAS_msg; + NAS_msg.from_string(NAS_SEC_CMD_STR); + TESTASSERT(reconf_ies.non_crit_ext.ded_nas_msg_list.size() > 0); + // Test if NAS_msg is the same as the one sent in SecurityModeCommand + TESTASSERT(NAS_msg == reconf_ies.non_crit_ext.ded_nas_msg_list[0]); + + // STEP 2 - Send RRCReconfiguration (UE -> gNB) + ul_dcch_msg_s ul_dcch_msg; + auto& RRC_recfg_complete = ul_dcch_msg.msg.set_c1().set_rrc_recfg_complete(); + RRC_recfg_complete.rrc_transaction_id = dl_dcch_msg.msg.c1().rrc_recfg().rrc_transaction_id; + RRC_recfg_complete.crit_exts.set_rrc_recfg_complete(); + + srsran::unique_byte_buffer_t pdu; + { + pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref{pdu->data(), pdu->get_tailroom()}; + TESTASSERT_SUCCESS(ul_dcch_msg.pack(bref)); + pdu->N_bytes = bref.distance_bytes(); + } + + // send message to RRC + rrc_obj.write_pdu(rnti, 1, std::move(pdu)); + + // Verify the NGAP gets notified for the RRCReconfigurationComplete + TESTASSERT_EQ(true, ngap.last_rrc_recnf_complete); +} + +void test_rrc_nr_2nd_reconfiguration(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + ngap_rrc_tester& ngap, + uint16_t rnti) +{ + // Make sure the NGAP RRCReconfigurationComplete bool is reset to false + ngap.last_rrc_recnf_complete = false; + + // create an unbounded_octstring object that contains a NAS message (we simulate a random NAS nas) + asn1::unbounded_octstring NAS_msg; + NAS_msg.from_string("c574defc80ba722bffb8eacb6f8a163e3222cf1542ac529f6980bb15e0bf12d9f2b29f11fb458ec9"); + + // Test whether there exists the SRB1 initiated in the Connection Establishment + // We test this as the SRB1 was set up in a different function + TESTASSERT_EQ(rnti, pdcp.last_sdu_rnti); + TESTASSERT_EQ(srsran::srb_to_lcid(srsran::nr_srb::srb1), pdcp.last_sdu_lcid); + + // STEP 2 - Trigger and send RRCReconfiguration command (gNB -> UE) + rrc_obj.establish_rrc_bearer(rnti, 1, NAS_msg, 4, 9); + + dl_dcch_msg_s dl_dcch_msg; + { + asn1::cbit_ref bref{pdcp.last_sdu->data(), pdcp.last_sdu->size()}; + TESTASSERT_SUCCESS(dl_dcch_msg.unpack(bref)); + } + + // Test whether the unpacked message is correct + TESTASSERT_EQ(dl_dcch_msg_type_c::types_opts::c1, dl_dcch_msg.msg.type().value); + TESTASSERT_EQ(dl_dcch_msg_type_c::c1_c_::types_opts::rrc_recfg, dl_dcch_msg.msg.c1().type().value); + TESTASSERT_EQ(rrc_recfg_s::crit_exts_c_::types_opts::rrc_recfg, + dl_dcch_msg.msg.c1().rrc_recfg().crit_exts.type().value); + const rrc_recfg_ies_s& reconf_ies = dl_dcch_msg.msg.c1().rrc_recfg().crit_exts.rrc_recfg(); + TESTASSERT_EQ(true, reconf_ies.radio_bearer_cfg_present); + TESTASSERT(reconf_ies.radio_bearer_cfg.srb_to_add_mod_list.size() > 0); + TESTASSERT_EQ(1, reconf_ies.radio_bearer_cfg.srb_to_add_mod_list.size()); + TESTASSERT_EQ(2, reconf_ies.radio_bearer_cfg.srb_to_add_mod_list[0].srb_id); + TESTASSERT_EQ(1, reconf_ies.radio_bearer_cfg.drb_to_add_mod_list.size()); + auto& drb = reconf_ies.radio_bearer_cfg.drb_to_add_mod_list[0]; + TESTASSERT_EQ(1, drb.drb_id); + + TESTASSERT_EQ(true, reconf_ies.non_crit_ext_present); + TESTASSERT(reconf_ies.non_crit_ext.master_cell_group.size() > 0); + auto& master_group_msg = reconf_ies.non_crit_ext.master_cell_group; + + cell_group_cfg_s master_cell_group; + { + asn1::cbit_ref bref{master_group_msg.data(), master_group_msg.size()}; + TESTASSERT_SUCCESS(master_cell_group.unpack(bref)); + } + + // Test if the master_cell_group SRB and DRB IDs match those in the RadioBearerConfig + TESTASSERT_EQ(0, master_cell_group.cell_group_id); + TESTASSERT(master_cell_group.rlc_bearer_to_add_mod_list.size() > 0); + auto& rlc_srb = master_cell_group.rlc_bearer_to_add_mod_list[0]; + TESTASSERT_EQ(reconf_ies.radio_bearer_cfg.srb_to_add_mod_list[0].srb_id, rlc_srb.served_radio_bearer.srb_id()); + auto& rlc_drb = master_cell_group.rlc_bearer_to_add_mod_list[1]; + TESTASSERT_EQ(reconf_ies.radio_bearer_cfg.drb_to_add_mod_list[0].drb_id, rlc_drb.served_radio_bearer.drb_id()); + + // Test if NAS_msg is the same as the one sent in DLInformationTransfer + TESTASSERT(reconf_ies.non_crit_ext.ded_nas_msg_list.size() > 0); + TESTASSERT(NAS_msg == reconf_ies.non_crit_ext.ded_nas_msg_list[0]); + + // STEP 2 - Send RRCReconfiguration (UE -> gNB) + ul_dcch_msg_s ul_dcch_msg; + auto& RRC_recfg_complete = ul_dcch_msg.msg.set_c1().set_rrc_recfg_complete(); + RRC_recfg_complete.rrc_transaction_id = dl_dcch_msg.msg.c1().rrc_recfg().rrc_transaction_id; + RRC_recfg_complete.crit_exts.set_rrc_recfg_complete(); + + srsran::unique_byte_buffer_t pdu; + { + pdu = srsran::make_byte_buffer(); + asn1::bit_ref bref{pdu->data(), pdu->get_tailroom()}; + TESTASSERT_SUCCESS(ul_dcch_msg.pack(bref)); + pdu->N_bytes = bref.distance_bytes(); + } + + // send message to RRC + rrc_obj.write_pdu(rnti, 1, std::move(pdu)); + + // Verify the NGAP gets notified for the RRCReconfigurationComplete + TESTASSERT_EQ(true, ngap.last_rrc_recnf_complete); +} + +} // namespace srsenb diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.h b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.h new file mode 100644 index 000000000..34004f212 --- /dev/null +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test_helpers.h @@ -0,0 +1,137 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_NR_TEST_HELPERS_H +#define SRSRAN_RRC_NR_TEST_HELPERS_H + +#include "srsenb/test/common/dummy_classes_common.h" +#include "srsgnb/hdr/stack/common/test/dummy_nr_classes.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr.h" + +namespace srsenb { + +class pdcp_nr_rrc_tester : public pdcp_dummy +{ +public: + void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) override + { + last_sdu_rnti = rnti; + last_sdu_lcid = lcid; + last_sdu = std::move(sdu); + } + + uint16_t last_sdu_rnti = SRSRAN_INVALID_RNTI; + uint32_t last_sdu_lcid = srsran::MAX_NR_NOF_BEARERS; + srsran::unique_byte_buffer_t last_sdu; +}; + +class rlc_nr_rrc_tester : public rlc_dummy +{ +public: + void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) + { + last_sdu_rnti = rnti; + last_sdu_lcid = lcid; + last_sdu = std::move(sdu); + } + + uint16_t last_sdu_rnti; + uint32_t last_sdu_lcid; + srsran::unique_byte_buffer_t last_sdu; +}; + +class ngap_rrc_tester : public ngap_dummy +{ +public: + void initial_ue(uint16_t rnti, + uint32_t gnb_cc_idx, + asn1::ngap::rrcestablishment_cause_e cause, + srsran::const_byte_span pdu, + uint32_t s_tmsi) + { + last_sdu_rnti = rnti; + last_pdu.resize(pdu.size()); + memcpy(last_pdu.data(), pdu.data(), pdu.size()); + } + + void write_pdu(uint16_t rnti, srsran::const_byte_span pdu) + { + last_sdu_rnti = rnti; + last_pdu.resize(pdu.size()); + memcpy(last_pdu.data(), pdu.data(), pdu.size()); + } + + void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) { last_rrc_recnf_complete = outcome; } + + uint16_t last_sdu_rnti; + asn1::dyn_octstring last_pdu; + bool last_rrc_recnf_complete = false; +}; + +/** + * Run TS 38.331, 5.3.3 "RRC connection establishment" to completion + * RRC actions: + * - Rx RRCSetupRequest + * - Tx RRCSetup to lower layers + * - Tx RRCSetupComplete + * Checks: + * - the RRC sends RRCSetup as reply to RRCSetupRequest + * - verify that RRCSetup rnti, lcid are correct + * - verify that RRCSetup adds an SRB1 + */ +void test_rrc_nr_connection_establishment(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + rlc_nr_rrc_tester& rlc, + mac_nr_dummy& mac, + ngap_rrc_tester& ngap, + uint16_t rnti); + +void test_rrc_nr_info_transfer(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + ngap_rrc_tester& ngap, + uint16_t rnti); + +void test_rrc_nr_security_mode_cmd(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + uint16_t rnti); + +void test_rrc_nr_ue_capability_enquiry(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + uint16_t rnti); + +void test_rrc_nr_reconfiguration(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + ngap_rrc_tester& ngap, + uint16_t rnti); + +void test_rrc_nr_2nd_reconfiguration(srsran::task_scheduler& task_sched, + rrc_nr& rrc_obj, + pdcp_nr_rrc_tester& pdcp, + ngap_rrc_tester& ngap, + uint16_t rnti); + +} // namespace srsenb + +#endif // SRSRAN_RRC_NR_TEST_HELPERS_H diff --git a/srsgnb/src/stack/sdap/CMakeLists.txt b/srsgnb/src/stack/sdap/CMakeLists.txt new file mode 100644 index 000000000..ed07f1891 --- /dev/null +++ b/srsgnb/src/stack/sdap/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(SOURCES sdap.cc) +add_library(srsgnb_sdap STATIC ${SOURCES}) \ No newline at end of file diff --git a/srsenb/src/stack/upper/sdap.cc b/srsgnb/src/stack/sdap/sdap.cc similarity index 93% rename from srsenb/src/stack/upper/sdap.cc rename to srsgnb/src/stack/sdap/sdap.cc index d00e001c8..dbf10a5ae 100644 --- a/srsenb/src/stack/upper/sdap.cc +++ b/srsgnb/src/stack/sdap/sdap.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsenb/hdr/stack/upper/sdap.h" +#include "srsgnb/hdr/stack/sdap/sdap.h" namespace srsenb { diff --git a/srsue/CMakeLists.txt b/srsue/CMakeLists.txt index a5e7fa5a4..35aba61d7 100644 --- a/srsue/CMakeLists.txt +++ b/srsue/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -43,7 +43,6 @@ link_directories( # Add subdirectories ######################################################################## add_subdirectory(src) -add_subdirectory(test) ######################################################################## # Default configuration files diff --git a/srsue/hdr/metrics_csv.h b/srsue/hdr/metrics_csv.h index 80a45c317..312f913fe 100644 --- a/srsue/hdr/metrics_csv.h +++ b/srsue/hdr/metrics_csv.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/metrics_json.h b/srsue/hdr/metrics_json.h new file mode 100644 index 000000000..7d04bab43 --- /dev/null +++ b/srsue/hdr/metrics_json.h @@ -0,0 +1,53 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +/****************************************************************************** + * File: metrics_json.h + * Description: Metrics class printing to a json file. + *****************************************************************************/ + +#ifndef SRSUE_METRICS_JSON_H +#define SRSUE_METRICS_JSON_H + +#include "srsran/srslog/log_channel.h" +#include "ue_metrics_interface.h" + +namespace srsue { + +class metrics_json : public srsran::metrics_listener +{ +public: + explicit metrics_json(srslog::log_channel& c) : log_c(c) {} + + void set_metrics(const ue_metrics_t& m, const uint32_t period_usec) override; + void set_ue_handle(ue_metrics_interface* ue_); + void stop() override {} + +private: + srslog::log_channel& log_c; + ue_metrics_interface* ue = nullptr; + + std::mutex mutex = {}; +}; + +} // namespace srsue + +#endif // SRSUE_METRICS_JSON_H diff --git a/srsue/hdr/metrics_stdout.h b/srsue/hdr/metrics_stdout.h index f2370e09f..bb285b78a 100644 --- a/srsue/hdr/metrics_stdout.h +++ b/srsue/hdr/metrics_stdout.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -59,10 +59,11 @@ private: std::string float_to_eng_string(float f, int digits); void print_table(const bool display_neighbours, const bool is_nr); - bool do_print = false; + std::atomic do_print = {false}; bool table_has_neighbours = false; ///< state of last table head uint8_t n_reports = 10; ue_metrics_interface* ue = nullptr; + std::mutex mutex; }; } // namespace srsue diff --git a/srsue/hdr/phy/dummy_phy.h b/srsue/hdr/phy/dummy_phy.h new file mode 100644 index 000000000..71c7dd88a --- /dev/null +++ b/srsue/hdr/phy/dummy_phy.h @@ -0,0 +1,85 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_DUMMY_PHY_H +#define SRSRAN_DUMMY_PHY_H + +#include "srsran/interfaces/phy_interface_types.h" +#include "srsue/hdr/phy/ue_phy_base.h" + +namespace srsue { + +class dummy_phy final : public ue_phy_base, public phy_interface_stack_lte +{ +public: + // ue_phy_base + std::string get_type() final { return "dummy_phy"; } + void stop() final {} + void wait_initialize() final {} + bool is_initialized() final { return false; } + void start_plot() final {} + void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final {} + + // phy_interface_stack_lte + void prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec = 0.0f) final + {} + prach_info_t prach_get_info() final { return {}; } + + /* Indicates the transmission of a SR signal in the next opportunity */ + void sr_send() final {} + int sr_last_tx_tti() final { return 0; } + + void set_mch_period_stop(uint32_t stop) final {} + bool set_config(const srsran::phy_cfg_t& config, uint32_t cc_idx = 0) final { return false; } + bool set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) final { return false; } + void set_config_tdd(srsran_tdd_config_t& tdd_config) final {} + void set_config_mbsfn_sib2(srsran::mbsfn_sf_cfg_t* cfg_list, uint32_t nof_cfgs) final {} + void set_config_mbsfn_sib13(const srsran::sib13_t& sib13) final {} + void set_config_mbsfn_mcch(const srsran::mcch_msg_t& mcch) final {} + + void deactivate_scells() final {} + + /* Measurements interface */ + void set_cells_to_meas(uint32_t earfcn, const std::set& pci) final {} + void meas_stop() final {} + + /* Cell search and selection procedures */ + bool cell_search(int earfcn) final { return false; } + bool cell_select(phy_cell_t cell) final { return false; } + bool cell_is_camping() final { return false; } + + /* Time advance commands */ + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final {} + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final {} + + /* Activate / Disactivate SCell*/ + void set_activation_deactivation_scell(uint32_t cmd, uint32_t tti) final {} + + /* Sets RAR dci payload */ + void set_rar_grant(uint8_t grant_payload[SRSRAN_RAR_GRANT_LEN], uint16_t rnti) final {} + + uint32_t get_current_tti() final { return 0; } + + float get_phr() final { return 0.0; } + float get_pathloss_db() final { return 0.0; } +}; +} // namespace srsue +#endif // SRSRAN_DUMMY_PHY_H diff --git a/srsue/hdr/phy/lte/cc_worker.h b/srsue/hdr/phy/lte/cc_worker.h index 482f50be5..8bf46921a 100644 --- a/srsue/hdr/phy/lte/cc_worker.h +++ b/srsue/hdr/phy/lte/cc_worker.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -41,17 +41,16 @@ public: uint32_t get_buffer_len(); void set_tti(uint32_t tti); - void set_cfo_unlocked(float cfo); + void set_cfo_nolock(float cfo); float get_ref_cfo() const; // Functions to set configuration. // Warning: all these functions are unlocked and must be called while the worker is not processing data - void reset_cell_unlocked(); - bool set_cell_unlocked(srsran_cell_t cell_); - void set_tdd_config_unlocked(srsran_tdd_config_t config); - void set_config_unlocked(srsran::phy_cfg_t& phy_cfg); - void upd_config_dci_unlocked(srsran_dci_cfg_t& dci_cfg); - void enable_pregen_signals_unlocked(bool enabled); + void reset_cell_nolock(); + bool set_cell_nolock(srsran_cell_t cell_); + void set_tdd_config_nolock(srsran_tdd_config_t config); + void set_config_nolock(const srsran::phy_cfg_t& phy_cfg); + void upd_config_dci_nolock(const srsran_dci_cfg_t& dci_cfg); void set_uci_periodic_cqi(srsran_uci_data_t* uci_data); @@ -85,7 +84,7 @@ private: mac_interface_phy_lte::tb_action_dl_t* action, bool acks[SRSRAN_MAX_CODEWORDS]); int decode_pmch(mac_interface_phy_lte::tb_action_dl_t* action, srsran_mbsfn_cfg_t* mbsfn_cfg); - + void new_mch_dl(mac_interface_phy_lte::tb_action_dl_t*); /* Methods for UL */ bool encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srsran_uci_data_t* uci_data); void set_uci_sr(srsran_uci_data_t* uci_data); @@ -101,12 +100,14 @@ private: srsran_dl_sf_cfg_t sf_cfg_dl = {}; srsran_ul_sf_cfg_t sf_cfg_ul = {}; - uint32_t cc_idx = 0; - bool pregen_enabled = false; - bool cell_initiated = false; - cf_t* signal_buffer_rx[SRSRAN_MAX_PORTS] = {}; - cf_t* signal_buffer_tx[SRSRAN_MAX_PORTS] = {}; - uint32_t signal_buffer_max_samples = 0; + uint32_t cc_idx = 0; + bool cell_initiated = false; + cf_t* signal_buffer_rx[SRSRAN_MAX_PORTS] = {}; + cf_t* signal_buffer_tx[SRSRAN_MAX_PORTS] = {}; + uint32_t signal_buffer_max_samples = 0; + const static uint32_t mch_payload_buffer_sz = SRSRAN_MAX_BUFFER_SIZE_BYTES; + uint8_t mch_payload_buffer[mch_payload_buffer_sz]; + srsran_softbuffer_rx_t mch_softbuffer = {}; /* Objects for DL */ srsran_ue_dl_t ue_dl = {}; diff --git a/srsue/hdr/phy/lte/sf_worker.h b/srsue/hdr/phy/lte/sf_worker.h index 232fc99ec..83141bb4c 100644 --- a/srsue/hdr/phy/lte/sf_worker.h +++ b/srsue/hdr/phy/lte/sf_worker.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,21 +45,18 @@ public: sf_worker(uint32_t max_prb, phy_common* phy_, srslog::basic_logger& logger); virtual ~sf_worker(); - void reset_cell_unlocked(uint32_t cc_idx); - bool set_cell_unlocked(uint32_t cc_idx, srsran_cell_t cell_); + void reset_cell_nolock(uint32_t cc_idx); + bool set_cell_nolock(uint32_t cc_idx, srsran_cell_t cell_); /* Functions used by main PHY thread */ cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx); uint32_t get_buffer_len(); - void set_tti(uint32_t tti); - void set_tx_time(const srsran::rf_timestamp_t& tx_time); + void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx); void set_prach(cf_t* prach_ptr, float prach_power); - void set_cfo_unlocked(const uint32_t& cc_idx, float cfo); + void set_cfo_nolock(const uint32_t& cc_idx, float cfo); - void set_tdd_config_unlocked(srsran_tdd_config_t config); - void set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg); - void set_crnti_unlocked(uint16_t rnti); - void enable_pregen_signals_unlocked(bool enabled); + void set_tdd_config_nolock(srsran_tdd_config_t config); + void set_config_nolock(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg); ///< Methods for plotting called from GUI thread int read_ce_abs(float* ce_abs, uint32_t tx_antenna, uint32_t rx_antenna); @@ -100,8 +97,7 @@ private: cf_t* prach_ptr = nullptr; float prach_power = 0; - uint32_t tti = 0; - srsran::rf_timestamp_t tx_time = {}; + srsran::phy_common_interface::worker_context_t context = {}; }; } // namespace lte diff --git a/srsue/hdr/phy/lte/worker_pool.h b/srsue/hdr/phy/lte/worker_pool.h index 7a35d3110..f1bda3fbb 100644 --- a/srsue/hdr/phy/lte/worker_pool.h +++ b/srsue/hdr/phy/lte/worker_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -30,9 +30,25 @@ namespace lte { class worker_pool { +private: srsran::thread_pool pool; std::vector > workers; + class phy_cfg_stash_t + { + private: + std::vector pending; ///< Indicates for each SF worker if it has pending configuration + srsran::phy_cfg_t cfg; ///< Actual CC configuration + + public: + phy_cfg_stash_t(uint32_t max_workers) : pending(max_workers) {} + void set_cfg(const srsran::phy_cfg_t& c); + bool is_pending(uint32_t sf_idx); + const srsran::phy_cfg_t& get_cfg(uint32_t sf_idx); + }; + std::mutex phy_cfg_mutex; ///< Protects configuration stash + std::array phy_cfg_stash; ///< Stores the latest worker configuration + public: sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } @@ -42,6 +58,14 @@ public: sf_worker* wait_worker_id(uint32_t id); void start_worker(sf_worker* w); void stop(); + + /** + * @brief Sets a new configuration for a given CC, it copies the new configuration into the stash and it will be + * applied to the sf_worker at the time it is reserved. + * @param cc_idx CC index + * @param phy_cfg Actual PHY configuration + */ + void set_config(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg); }; } // namespace lte diff --git a/srsue/hdr/phy/nr/cc_worker.h b/srsue/hdr/phy/nr/cc_worker.h index da74b3bca..00eef8b45 100644 --- a/srsue/hdr/phy/nr/cc_worker.h +++ b/srsue/hdr/phy/nr/cc_worker.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,10 +31,10 @@ namespace nr { class cc_worker { public: - cc_worker(uint32_t cc_idx, srslog::basic_logger& log, state* phy_state_); + cc_worker(uint32_t cc_idx, srslog::basic_logger& log, state& phy_state_, const srsran::phy_cfg_nr_t& cfg); ~cc_worker(); - bool update_cfg(); + void update_cfg(const srsran::phy_cfg_nr_t& new_config); void set_tti(uint32_t tti); cf_t* get_rx_buffer(uint32_t antenna_idx); @@ -58,14 +58,28 @@ private: std::array rx_buffer = {}; std::array tx_buffer = {}; uint32_t buffer_sz = 0; - state* phy = nullptr; - srsran_ue_dl_nr_t ue_dl = {}; - srsran_ue_ul_nr_t ue_ul = {}; + state& phy; + srsran::phy_cfg_nr_t cfg; + srsran_ssb_t ssb = {}; + srsran_ue_dl_nr_t ue_dl = {}; + srsran_ue_ul_nr_t ue_ul = {}; srslog::basic_logger& logger; // Methods for DCI blind search void decode_pdcch_ul(); void decode_pdcch_dl(); + + /** + * @brief Decodes PDSCH in the current processing slot + * @return true if current configuration is valid and no error occur, false otherwise + */ + bool decode_pdsch_dl(); + + /** + * @brief Performs Channel State Information (CSI) measurements + * @return true if current configuration is valid and no error occur, false otherwise + */ + bool measure_csi(); }; } // namespace nr diff --git a/srsue/hdr/phy/nr/cell_search.h b/srsue/hdr/phy/nr/cell_search.h new file mode 100644 index 000000000..c47202e0b --- /dev/null +++ b/srsue/hdr/phy/nr/cell_search.h @@ -0,0 +1,68 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_CELL_SEARCH_H +#define SRSUE_CELL_SEARCH_H + +#include "srsran/interfaces/radio_interfaces.h" +#include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsran/srsran.h" + +namespace srsue { +namespace nr { +class cell_search +{ +public: + struct args_t { + double max_srate_hz; + srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz; + }; + + struct cfg_t { + double srate_hz; + double center_freq_hz; + double ssb_freq_hz; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_pattern_t ssb_pattern; + srsran_duplex_mode_t duplex_mode; + }; + + struct ret_t { + enum { CELL_FOUND = 1, CELL_NOT_FOUND = 0, ERROR = -1 } result; + srsran_ssb_search_res_t ssb_res; + }; + + cell_search(srslog::basic_logger& logger); + ~cell_search(); + + bool init(const args_t& args); + + bool start(const cfg_t& cfg); + ret_t run_slot(const cf_t* buffer, uint32_t slot_sz); + +private: + srslog::basic_logger& logger; + srsran_ssb_t ssb = {}; +}; +} // namespace nr +} // namespace srsue + +#endif // SRSUE_CELL_SEARCH_H diff --git a/srsue/hdr/phy/nr/sf_worker.h b/srsue/hdr/phy/nr/sf_worker.h index b4c8534b0..ff053db87 100644 --- a/srsue/hdr/phy/nr/sf_worker.h +++ b/srsue/hdr/phy/nr/sf_worker.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,9 +22,9 @@ #ifndef SRSUE_NR_PHCH_WORKER_H #define SRSUE_NR_PHCH_WORKER_H -#include "../phy_common.h" #include "cc_worker.h" #include "srsran/common/thread_pool.h" +#include "srsran/interfaces/phy_common_interface.h" namespace srsue { namespace nr { @@ -40,17 +40,25 @@ namespace nr { class sf_worker final : public srsran::thread_pool::worker { public: - sf_worker(phy_common* phy, state* phy_state_, srslog::basic_logger& logger); + sf_worker(srsran::phy_common_interface& common_, + state& phy_state_, + const srsran::phy_cfg_nr_t& cfg, + srslog::basic_logger& logger); ~sf_worker() = default; - bool update_cfg(uint32_t cc_idx); - /* Functions used by main PHY thread */ cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx); uint32_t get_buffer_len(); - void set_tti(uint32_t tti); + void set_context(const srsran::phy_common_interface::worker_context_t& w_ctx); int read_pdsch_d(cf_t* pdsch_d); void start_plot(); + void set_cfg(const srsran::phy_cfg_nr_t& new_cfg) + { + for (unsigned i = 0, e = cc_workers.size(); i != e; ++i) { + update_cfg(i, new_cfg); + } + sf_len = SRSRAN_SF_LEN_PRB_NR(new_cfg.carrier.nof_prb); + } void set_prach(cf_t* prach_ptr, float prach_power); @@ -58,15 +66,19 @@ private: /* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */ void work_imp() override; + void update_cfg(uint32_t cc_idx, const srsran::phy_cfg_nr_t& new_cfg); + +private: std::vector > cc_workers; - phy_common* phy = nullptr; - state* phy_state = nullptr; - srslog::basic_logger& logger; - - uint32_t tti_rx = 0; - cf_t* prach_ptr = nullptr; - float prach_power = 0; + srsran::phy_common_interface& common; + state& phy_state; + srslog::basic_logger& logger; + srsran::rf_timestamp_t tx_time = {}; + cf_t* prach_ptr = nullptr; + float prach_power = 0; + srsran::phy_common_interface::worker_context_t context = {}; + uint32_t sf_len = 0; }; } // namespace nr diff --git a/srsue/hdr/phy/nr/slot_sync.h b/srsue/hdr/phy/nr/slot_sync.h new file mode 100644 index 000000000..d747712a7 --- /dev/null +++ b/srsue/hdr/phy/nr/slot_sync.h @@ -0,0 +1,77 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_SLOT_SYNC_H +#define SRSUE_SLOT_SYNC_H + +#include "srsran/interfaces/radio_interfaces.h" +#include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsran/radio/rf_buffer.h" +#include "srsran/radio/rf_timestamp.h" +#include "srsran/srsran.h" + +namespace srsue { +namespace nr { +class slot_sync +{ +public: + struct args_t { + double max_srate_hz = 1.92e6; + uint32_t nof_rx_channels = 1; + srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz; + bool disable_cfo = false; + float pbch_dmrs_thr = 0.0f; ///< PBCH DMRS correlation detection threshold (0 means auto) + float cfo_alpha = 0.0f; ///< CFO averaging alpha (0 means auto) + int thread_priority = -1; + }; + + slot_sync(srslog::basic_logger& logger); + ~slot_sync(); + + bool init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); + + int set_sync_cfg(const srsran_ue_sync_nr_cfg_t& cfg); + + int recv_callback(srsran::rf_buffer_t& rf_buffer, srsran_timestamp_t* timestamp); + bool run_sfn_sync(); + bool run_camping(srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& timestamp); + void run_stack_tti(); + + srsran_slot_cfg_t get_slot_cfg(); + +private: + const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe + const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata + srslog::basic_logger& logger; + stack_interface_phy_nr* stack = nullptr; + srsran::radio_interface_phy* radio = nullptr; + srsran::rf_timestamp_t last_rx_time; + srsran_ue_sync_nr_t ue_sync_nr = {}; + srsran_timestamp_t stack_tti_ts_new = {}; + srsran_timestamp_t stack_tti_ts = {}; + bool forced_rx_time_init = true; // Rx time sync after first receive from radio + srsran::rf_buffer_t sfn_sync_buff = {}; + srsran_slot_cfg_t slot_cfg = {}; +}; +} // namespace nr +} // namespace srsue + +#endif // SRSUE_SLOT_SYNC_H diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index 6e8c36852..d876ca25b 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -46,10 +46,10 @@ private: mutable std::mutex pending_ul_grant_mutex; struct pending_dl_grant_t { - bool enable; - uint32_t pid; - srsran_sch_cfg_nr_t sch_cfg; - srsran_pdsch_ack_resource_nr_t ack_resource; + bool enable; + uint32_t pid; + srsran_sch_cfg_nr_t sch_cfg; + srsran_harq_ack_resource_t ack_resource; }; srsran::circular_array pending_dl_grant = {}; mutable std::mutex pending_dl_grant_mutex; @@ -66,7 +66,15 @@ private: mutable std::mutex metrics_mutex; /// CSI-RS measurements - std::array csi_measurements = {}; + std::mutex csi_measurements_mutex; + std::array csi_measurements = {}; + + /// TRS measurements + mutable std::mutex trs_measurements_mutex; + srsran_csi_trs_measurements_t trs_measurements = {}; + + /// Other measurements + std::atomic ul_ext_cfo_hz = {0.0f}; /** * @brief Resets all metrics (unprotected) @@ -85,33 +93,31 @@ public: /// Physical layer user configuration phy_args_nr_t args = {}; - /// Physical layer higher layer configuration, provided by higher layers through configuration messages - srsran::phy_cfg_nr_t cfg = {}; - /// Semaphore for aligning UL work srsran::tti_semaphore dl_ul_semaphore; - uint32_t rar_grant_tti = 0; - state() { // Hard-coded values, this should be set when the measurements take place csi_measurements[0].K_csi_rs = 1; csi_measurements[0].nof_ports = 1; csi_measurements[1].K_csi_rs = 4; - csi_measurements[0].nof_ports = 1; + csi_measurements[1].nof_ports = 1; } /** * @brief Stores a received UL DCI into the pending UL grant list - * @param tti_rx The TTI in which the grant was received + * @param cfg Physical layer configuration object + * @param slot_rx The TTI in which the grant was received * @param dci_ul The UL DCI message to store */ - void set_ul_pending_grant(uint32_t tti_rx, const srsran_dci_ul_nr_t& dci_ul) + void set_ul_pending_grant(const srsran::phy_cfg_nr_t& cfg, + const srsran_slot_cfg_t& slot_rx, + const srsran_dci_ul_nr_t& dci_ul) { // Convert UL DCI to grant srsran_sch_cfg_nr_t pusch_cfg = {}; - if (srsran_ra_ul_dci_to_grant_nr(&cfg.carrier, &cfg.pusch, &dci_ul, &pusch_cfg, &pusch_cfg.grant)) { + if (not cfg.get_pusch_cfg(slot_rx, dci_ul, pusch_cfg)) { std::array str; srsran_dci_ul_nr_to_str(NULL, &dci_ul, str.data(), str.size()); ERROR("Computing UL grant %s", str.data()); @@ -119,7 +125,7 @@ public: } // Calculate Transmit TTI - uint32_t tti_tx = TTI_ADD(tti_rx, pusch_cfg.grant.k); + uint32_t tti_tx = TTI_ADD(slot_rx.idx, pusch_cfg.grant.k); // Scope mutex to protect read/write the list std::lock_guard lock(pending_ul_grant_mutex); @@ -163,21 +169,26 @@ public: /** * @brief Stores a received DL DCI into the pending DL grant list + * @param cfg Physical layer configuration object * @param tti_rx The TTI in which the grant was received * @param dci_dl The DL DCI message to store */ - void set_dl_pending_grant(const srsran_slot_cfg_t& slot, const srsran_dci_dl_nr_t& dci_dl) + void + set_dl_pending_grant(const srsran::phy_cfg_nr_t& cfg, const srsran_slot_cfg_t& slot, const srsran_dci_dl_nr_t& dci_dl) { // Convert DL DCI to grant srsran_sch_cfg_nr_t pdsch_cfg = {}; - if (srsran_ra_dl_dci_to_grant_nr(&cfg.carrier, &slot, &cfg.pdsch, &dci_dl, &pdsch_cfg, &pdsch_cfg.grant)) { - ERROR("Computing UL grant"); + + if (not cfg.get_pdsch_cfg(slot, dci_dl, pdsch_cfg)) { + std::array str; + srsran_dci_dl_nr_to_str(NULL, &dci_dl, str.data(), str.size()); + ERROR("Computing DL grant %s", str.data()); return; } // Calculate DL DCI to PDSCH ACK resource - srsran_pdsch_ack_resource_nr_t ack_resource = {}; - if (srsran_ue_dl_nr_pdsch_ack_resource(&cfg.harq_ack, &dci_dl, &ack_resource) < SRSRAN_SUCCESS) { + srsran_harq_ack_resource_t ack_resource = {}; + if (not cfg.get_pdsch_ack_resource(dci_dl, ack_resource)) { ERROR("Computing UL ACK resource"); return; } @@ -204,10 +215,10 @@ public: * @param pid Provides the HARQ process identifier * @return true if there is a pending grant for the given TX tti, false otherwise */ - bool get_dl_pending_grant(uint32_t tti_rx, - srsran_sch_cfg_nr_t& pdsch_cfg, - srsran_pdsch_ack_resource_nr_t& ack_resource, - uint32_t& pid) + bool get_dl_pending_grant(uint32_t tti_rx, + srsran_sch_cfg_nr_t& pdsch_cfg, + srsran_harq_ack_resource_t& ack_resource, + uint32_t& pid) { // Scope mutex to protect read/write the list std::lock_guard lock(pending_dl_grant_mutex); @@ -236,16 +247,16 @@ public: * @param tti_rx The TTI in which the PDSCH transmission was received * @param dci_dl The DL DCI message to store */ - void set_pending_ack(const uint32_t& tti_rx, const srsran_pdsch_ack_resource_nr_t& ack_resource, const bool& crc_ok) + void set_pending_ack(const uint32_t& tti_rx, const srsran_harq_ack_resource_t& ack_resource, const bool& crc_ok) { // Calculate Receive TTI uint32_t tti_tx = TTI_ADD(tti_rx, ack_resource.k1); // Prepare ACK information - srsran_pdsch_ack_m_nr_t ack_m = {}; - ack_m.resource = ack_resource; - ack_m.value[0] = crc_ok ? 1 : 0; - ack_m.present = true; + srsran_harq_ack_m_t ack_m = {}; + ack_m.resource = ack_resource; + ack_m.value[0] = crc_ok ? 1 : 0; + ack_m.present = true; // Scope mutex to protect read/write the list std::lock_guard lock(pending_ack_mutex); @@ -255,8 +266,8 @@ public: ack.nof_cc = 1; // Insert PDSCH transmission information - if (srsran_ue_dl_nr_ack_insert_m(&ack, &ack_m) < SRSRAN_SUCCESS) { - ERROR("Error inserting ACK m value"); + if (srsran_harq_ack_insert_m(&ack, &ack_m) < SRSRAN_SUCCESS) { + ERROR("Error inserting ACK m value for Tx slot %d", tti_tx); } } @@ -286,9 +297,10 @@ public: { clear_pending_grants(); reset_metrics(); + reset_measurements(); } - bool has_valid_sr_resource(uint32_t sr_id) + bool has_valid_sr_resource(const srsran::phy_cfg_nr_t& cfg, uint32_t sr_id) { for (const srsran_pucch_nr_sr_resource_t& r : cfg.pucch.sr_resources) { if (r.configured && r.sr_id == sr_id) { @@ -300,16 +312,23 @@ public: void clear_pending_grants() { - // Scope mutex to protect read/write the list - std::lock_guard lock(pending_ul_grant_mutex); - // Clear all PDSCH assignments and PUSCH grants - pending_dl_grant = {}; - pending_ul_grant = {}; - pending_ack = {}; + // Scope mutex to protect read/write each list + { + std::lock_guard lock(pending_ul_grant_mutex); + pending_ul_grant = {}; + } + { + std::lock_guard lock(pending_dl_grant_mutex); + pending_dl_grant = {}; + } + { + std::lock_guard lock(pending_ack_mutex); + pending_ack = {}; + } } - void get_pending_sr(const uint32_t& tti, srsran_uci_data_nr_t& uci_data) + void get_pending_sr(const srsran::phy_cfg_nr_t& cfg, const uint32_t& tti, srsran_uci_data_nr_t& uci_data) { // Calculate all SR opportunities in the given TTI uint32_t sr_resource_id[SRSRAN_PUCCH_MAX_NOF_SR_RESOURCES] = {}; @@ -320,7 +339,7 @@ public: } // Initialise counters - uint32_t sr_count_all = (uint32_t)n; + uint32_t sr_count_all = (uint32_t)n; // Number of opportunities in this TTI uint32_t sr_count_positive = 0; // Iterate all opportunities and check if there is a pending SR @@ -337,20 +356,38 @@ public: } // Configure SR fields in UCI data - uci_data.cfg.pucch.sr_resource_id = sr_resource_id[0]; - uci_data.cfg.o_sr = srsran_ra_ul_nr_nof_sr_bits(sr_count_all); - uci_data.cfg.pucch.sr_positive_present = sr_count_positive > 0; - uci_data.value.sr = sr_count_positive; + uci_data.cfg.pucch.sr_resource_id = sr_resource_id[0]; + uci_data.cfg.o_sr = srsran_ra_ul_nr_nof_sr_bits(sr_count_all); + uci_data.cfg.sr_positive_present = sr_count_positive > 0; + uci_data.value.sr = sr_count_positive; } - void get_periodic_csi(const uint32_t& tti, srsran_uci_data_nr_t& uci_data) + void + get_periodic_csi(const srsran::phy_cfg_nr_t& cfg, const srsran_slot_cfg_t& slot_cfg, srsran_uci_data_nr_t& uci_data) { - int n = srsran_csi_generate_reports(&cfg.csi, tti, csi_measurements.data(), uci_data.cfg.csi, uci_data.value.csi); + // Generate report configurations + int n = srsran_csi_reports_generate(&cfg.csi, &slot_cfg, uci_data.cfg.csi); if (n > SRSRAN_SUCCESS) { uci_data.cfg.nof_csi = n; } - uci_data.cfg.pucch.rnti = stack->get_ul_sched_rnti_nr(tti).id; + // Quantify reports from measurements + n = srsran_csi_reports_quantify(uci_data.cfg.csi, csi_measurements.data(), uci_data.value.csi); + if (n > SRSRAN_SUCCESS) { + uci_data.cfg.nof_csi = n; + } + + // Set fix wideband CQI if it is not zero nor greater than 15 + if (args.fix_wideband_cqi != 0 && args.fix_wideband_cqi < 15) { + for (uint32_t i = 0; i < uci_data.cfg.nof_csi; i++) { + if (uci_data.cfg.csi[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI && + uci_data.cfg.csi[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND) { + uci_data.value.csi[i].wideband_cri_ri_pmi_cqi.cqi = args.fix_wideband_cqi; + } + } + } + + uci_data.cfg.pucch.rnti = stack->get_ul_sched_rnti_nr(slot_cfg.idx).id; } /** @@ -431,7 +468,101 @@ public: // Reset all metrics reset_metrics_(); } + + /** + * @brief Resets all PHY measurements (protected) + */ + void reset_measurements() + { + std::lock_guard lock(csi_measurements_mutex); + csi_measurements = {}; + } + + /** + * @brief Processes a new NZP-CSI-RS channel measurement + * @param cfg Physical layer configuration object + * @param new_measure New measurement + * @param resource_set_id NZP-CSI-RS resource set identifier used for the channel measurement + */ + void new_nzp_csi_rs_channel_measurement(const srsran::phy_cfg_nr_t& cfg, + const srsran_csi_channel_measurements_t& new_measure, + uint32_t resource_set_id) + { + std::lock_guard lock(csi_measurements_mutex); + + if (srsran_csi_new_nzp_csi_rs_measurement( + cfg.csi.csi_resources, csi_measurements.data(), &new_measure, resource_set_id) < SRSRAN_SUCCESS) { + ERROR("Error processing new NZP-CSI-RS"); + return; + } + } + + /** + * @brief Processes a new Tracking Reference Signal (TRS) measurement + * @param new_measure New measurement + * @param cfg Physical layer configuration object + * @param resource_set_id NZP-CSI-RS resource set identifier used for the channel measurement if it is configured from + * a NZP-CSI-RS + * @param K_csi_rs Number of NZP-CSI-RS resources used for the measurement, set to 0 if another type of signal is + * measured (i.e. SSB) + */ + void new_csi_trs_measurement(const srsran_csi_trs_measurements_t& new_meas, + const srsran::phy_cfg_nr_t& cfg, + uint32_t resource_set_id = 0, + uint32_t K_csi_rs = 0) + { + // Compute channel metrics and push it + ch_metrics_t new_ch_metrics = {}; + new_ch_metrics.sinr = new_meas.snr_dB; + new_ch_metrics.rsrp = new_meas.rsrp_dB; + new_ch_metrics.rsrq = 0.0f; // Not supported + new_ch_metrics.rssi = 0.0f; // Not supported + new_ch_metrics.sync_err = new_meas.delay_us; + set_channel_metrics(new_ch_metrics); + + // Compute synch metrics and report it to the PHY state + sync_metrics_t new_sync_metrics = {}; + new_sync_metrics.cfo = new_meas.cfo_hz + ul_ext_cfo_hz; + set_sync_metrics(new_sync_metrics); + + // Convert to CSI channel measurement and report new NZP-CSI-RS measurement to the PHY state + srsran_csi_channel_measurements_t measurements = {}; + measurements.cri = 0; + measurements.wideband_rsrp_dBm = new_meas.rsrp_dB; + measurements.wideband_epre_dBm = new_meas.epre_dB; + measurements.wideband_snr_db = new_meas.snr_dB; + measurements.nof_ports = 1; // Other values are not supported + measurements.K_csi_rs = K_csi_rs; + new_nzp_csi_rs_channel_measurement(cfg, measurements, resource_set_id); + + // Update tracking information + trs_measurements_mutex.lock(); + trs_measurements.rsrp_dB = SRSRAN_VEC_SAFE_EMA(new_meas.rsrp_dB, trs_measurements.rsrp_dB, args.trs_epre_ema_alpha); + trs_measurements.epre_dB = SRSRAN_VEC_SAFE_EMA(new_meas.epre_dB, trs_measurements.epre_dB, args.trs_rsrp_ema_alpha); + trs_measurements.snr_dB = SRSRAN_VEC_SAFE_EMA(new_meas.snr_dB, trs_measurements.snr_dB, args.trs_sinr_ema_alpha); + // Consider CFO measurement invalid if the SNR is negative. In this case, set CFO to 0. + if (new_meas.snr_dB > 0.0f) { + trs_measurements.cfo_hz = SRSRAN_VEC_SAFE_EMA(new_meas.cfo_hz, trs_measurements.cfo_hz, args.trs_cfo_ema_alpha); + } + trs_measurements.nof_re++; + trs_measurements_mutex.unlock(); + } + + float get_dl_cfo() + { + std::lock_guard lock(trs_measurements_mutex); + return trs_measurements.cfo_hz; + } + + float get_ul_cfo() const + { + std::lock_guard lock(trs_measurements_mutex); + return trs_measurements.cfo_hz + ul_ext_cfo_hz; + } + + void set_ul_ext_cfo(float ext_cfo_hz) { ul_ext_cfo_hz = ext_cfo_hz; } }; + } // namespace nr } // namespace srsue diff --git a/srsue/hdr/phy/nr/sync_sa.h b/srsue/hdr/phy/nr/sync_sa.h new file mode 100644 index 000000000..d0372dea1 --- /dev/null +++ b/srsue/hdr/phy/nr/sync_sa.h @@ -0,0 +1,141 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_SYNC_NR_SA_H +#define SRSUE_SYNC_NR_SA_H + +#include "cell_search.h" +#include "slot_sync.h" +#include "srsran/common/threads.h" +#include "srsran/interfaces/radio_interfaces.h" +#include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsran/radio/rf_buffer.h" +#include "srsran/radio/rf_timestamp.h" +#include "srsran/srslog/logger.h" +#include "srsran/srsran.h" +#include "srsue/hdr/phy/sync_state.h" +#include "worker_pool.h" +#include +#include +#include + +namespace srsue { +namespace nr { + +/** + * @brief NR Standalone synchronization class + */ +class sync_sa : public srsran::thread, public srsran::phy_common_interface +{ +public: + struct args_t { + double srate_hz = 61.44e6; + srsran_subcarrier_spacing_t ssb_min_scs = srsran_subcarrier_spacing_15kHz; + uint32_t nof_rx_channels = 1; + bool disable_cfo = false; + float pbch_dmrs_thr = 0.0f; ///< PBCH DMRS correlation detection threshold (0 means auto) + float cfo_alpha = 0.0f; ///< CFO averaging alpha (0 means auto) + int thread_priority = 1; + + cell_search::args_t get_cell_search() const + { + cell_search::args_t ret = {}; + ret.max_srate_hz = srate_hz; + return ret; + } + + slot_sync::args_t get_slot_sync() const + { + slot_sync::args_t ret = {}; + ret.max_srate_hz = srate_hz; + ret.nof_rx_channels = nof_rx_channels; + ret.ssb_min_scs = ssb_min_scs; + + return ret; + } + }; + + sync_sa(srslog::basic_logger& logger, worker_pool& workers_); + ~sync_sa(); + + bool init(const args_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); + bool reset(); + void stop(); + sync_state::state_t get_state(); + + // The following methods control the SYNC state machine + void cell_go_idle(); + cell_search::ret_t cell_search_run(const cell_search::cfg_t& cfg); + rrc_interface_phy_nr::cell_select_result_t cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req); + + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; + + void add_ta_cmd_rar(uint32_t tti, uint32_t ta_cmd); + void add_ta_cmd_new(uint32_t tti, uint32_t ta_cmd); + void add_ta_offset(uint32_t ta_offset); + +private: + stack_interface_phy_nr* stack = nullptr; ///< Stand-Alone RRC interface + srsran::radio_interface_phy* radio = nullptr; ///< Radio object + srslog::basic_logger& logger; ///< General PHY logger + worker_pool& workers; + + // FSM that manages RRC commands for cell search/select/sync procedures + std::mutex rrc_mutex; + enum { PROC_IDLE = 0, PROC_SELECT_RUNNING, PROC_SEARCH_RUNNING } rrc_proc_state = PROC_IDLE; + sync_state phy_state; + + std::atomic running = {false}; + cf_t* rx_buffer = nullptr; + double srate_hz = 0; ///< Sampling rate in Hz + uint32_t slot_sz = 0; ///< Subframe size (1-ms) + uint32_t tti = 0; + srsran::tti_semaphore tti_semaphore; + srsran::rf_timestamp_t last_rx_time; + std::atomic is_pending_tx_end = {false}; + uint32_t cell_search_nof_trials = 0; + const static uint32_t cell_search_max_trials = 100; + uint32_t sfn_sync_nof_trials = 0; + const static uint32_t sfn_sync_max_trials = 100; + + cell_search::ret_t cs_ret; + cell_search searcher; + slot_sync slot_synchronizer; + + // Time Aligment Controller, internal thread safe + ta_control ta; + + // FSM States + bool wait_idle(); + void run_state_idle(); + void run_state_cell_search(); + void run_state_sfn_sync(); + void run_state_cell_camping(); + + int radio_recv_fnc(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_time); + void run_stack_tti(); + void run_thread() override; +}; + +} // namespace nr +} // namespace srsue + +#endif // SRSUE_SYNC_NR_SA_H diff --git a/srsue/hdr/phy/nr/worker_pool.h b/srsue/hdr/phy/nr/worker_pool.h index c4daf6f8c..083656e84 100644 --- a/srsue/hdr/phy/nr/worker_pool.h +++ b/srsue/hdr/phy/nr/worker_pool.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -36,22 +36,42 @@ private: srsran::thread_pool pool; std::vector > workers; state phy_state; - std::unique_ptr prach_buffer = nullptr; + std::unique_ptr prach_buffer = nullptr; + uint32_t prach_nof_sf = 0; + uint32_t prach_sf_count = 0; + cf_t* prach_ptr = nullptr; + float prach_target_power = 0.0f; + uint32_t sf_sz = 0; + srsran::phy_cfg_nr_t cfg{}; + std::vector pending_cfgs; + std::mutex cfg_mutex; public: sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } - worker_pool(uint32_t max_workers); - bool init(const phy_args_nr_t& args_, phy_common* common, stack_interface_phy_nr* stack_, int prio); + worker_pool(srslog::basic_logger& logger_, uint32_t max_workers); + bool init(const phy_args_nr_t& args_, srsran::phy_common_interface& common, stack_interface_phy_nr* stack_); sf_worker* wait_worker(uint32_t tti); void start_worker(sf_worker* w); void stop(); - void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power); - int set_ul_grant(std::array array, uint16_t rnti, srsran_rnti_type_t rnti_type); - bool set_config(const srsran::phy_cfg_nr_t& cfg); + void send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec = 0.0f); + int set_rar_grant(uint32_t rx_tti, + std::array array, + uint16_t rnti, + srsran_rnti_type_t rnti_type); + bool set_config(const srsran::phy_cfg_nr_t& cfg); bool has_valid_sr_resource(uint32_t sr_id); void clear_pending_grants(); void get_metrics(phy_metrics_t& m); + + /** + * @brief Sets external CFO to compensate UL signal frequency offset + * @param ext_cfo_hz External CFO in Hz + */ + void set_ul_ext_cfo(float ext_cfo_hz) { phy_state.set_ul_ext_cfo(ext_cfo_hz); } }; } // namespace nr diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index a1f5bf1fc..928ad8729 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,53 +34,25 @@ #include "srsran/srsran.h" #include "srsue/hdr/phy/lte/worker_pool.h" #include "srsue/hdr/phy/nr/worker_pool.h" -#include "srsue/hdr/phy/ue_lte_phy_base.h" -#include "srsue/hdr/phy/ue_nr_phy_base.h" +#include "srsue/hdr/phy/ue_phy_base.h" #include "sync.h" namespace srsue { typedef _Complex float cf_t; -class phy_cmd_proc : public srsran::thread -{ -public: - phy_cmd_proc() : thread("PHY_CMD") { start(); } - - ~phy_cmd_proc() { stop(); } - - void add_cmd(std::function cmd) { cmd_queue.push(cmd); } - - void stop() - { - if (running) { - add_cmd([this]() { running = false; }); - wait_thread_finish(); - } - } - -private: - void run_thread() - { - std::function cmd; - while (running) { - cmd = cmd_queue.wait_pop(); - cmd(); - } - } - bool running = true; - // Queue for commands - srsran::block_queue > cmd_queue; -}; - -class phy final : public ue_lte_phy_base, public ue_nr_phy_base, public srsran::thread +class phy final : public ue_phy_base, + public phy_interface_stack_lte, + public phy_interface_stack_nr, + public srsran::phy_interface_radio, + public srsran::thread { public: explicit phy() : logger_phy(srslog::fetch_basic_logger("PHY")), logger_phy_lib(srslog::fetch_basic_logger("PHY_LIB")), lte_workers(MAX_WORKERS), - nr_workers(MAX_WORKERS), + nr_workers(logger_phy, MAX_WORKERS), common(logger_phy), sfsync(logger_phy, logger_phy_lib), prach_buffer(logger_phy), @@ -89,28 +61,23 @@ public: ~phy() final { stop(); } - // Init defined in base class - int init(const phy_args_t& args_) final; - // Init for LTE PHYs - int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) final; + int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_); void stop() final; void wait_initialize() final; - bool is_initiated(); + bool is_initialized() final; void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final; void srsran_phy_logger(phy_logger_level_t log_level, char* str); - void enable_pregen_signals(bool enable) final; - void radio_overflow() final; void radio_failure() final; /********** RRC INTERFACE ********************/ - bool cell_search() final; + bool cell_search(int earfcn) final; bool cell_select(phy_cell_t cell) final; // Sets the new PHY configuration for the given CC. The configuration is applied in the background. The notify() @@ -174,28 +141,35 @@ public: std::string get_type() final { return "lte_soft"; } - int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) final; - bool set_config(const srsran::phy_cfg_nr_t& cfg) final; - int set_ul_grant(std::array packed_ul_grant, - uint16_t rnti, - srsran_rnti_type_t rnti_type) final; - void send_prach(const uint32_t prach_occasion, - const int preamble_index, - const float preamble_received_target_power, - const float ta_base_sec = 0.0f) final; - int tx_request(const tx_request_t& request) final; - void set_earfcn(std::vector earfcns) final; - bool has_valid_sr_resource(uint32_t sr_id) final; - void clear_pending_grants() final; + /********** NR INTERFACE ********************/ + int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); + bool set_config(const srsran::phy_cfg_nr_t& cfg) final; + void send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec = 0.0f) final; + void set_earfcn(std::vector earfcns); + bool has_valid_sr_resource(uint32_t sr_id) final; + void clear_pending_grants() final; + int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) final; + phy_nr_state_t get_state() override { return PHY_NR_STATE_IDLE; }; + void reset_nr() override{}; + bool start_cell_search(const cell_search_args_t& req) override { return false; }; + bool start_cell_select(const cell_select_args_t& req) override { return false; }; private: void run_thread() final; void configure_prach_params(); void reset(); + bool set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn, bool run_in_background); + void set_scell_cmd(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn, bool earfcn_is_different); std::mutex config_mutex; std::condition_variable config_cond; - bool is_configured = false; + std::atomic is_configured = {false}; const static int SF_RECV_THREAD_PRIO = 0; const static int WORKERS_THREAD_PRIO = 2; @@ -204,7 +178,8 @@ private: srslog::basic_logger& logger_phy; srslog::basic_logger& logger_phy_lib; - srsue::stack_interface_phy_lte* stack = nullptr; + srsue::stack_interface_phy_lte* stack = nullptr; + srsue::stack_interface_phy_nr* stack_nr = nullptr; lte::worker_pool lte_workers; nr::worker_pool nr_workers; @@ -215,8 +190,9 @@ private: srsran_prach_cfg_t prach_cfg = {}; srsran_tdd_config_t tdd_config = {}; - srsran::phy_cfg_t config = {}; - phy_args_t args = {}; + srsran::phy_cfg_t config = {}; + srsran::phy_cfg_nr_t config_nr = {}; + phy_args_t args = {}; // Since cell_search/cell_select operations take a lot of time, we use another queue to process the other commands // in parallel and avoid accumulating in the queue @@ -225,6 +201,9 @@ private: // Tracks the current selected cell (last call to cell_select) srsran_cell_t selected_cell = {}; + // Tracks the current selected EARFCN (last call to cell_select) + uint32_t selected_earfcn = 0; + static void set_default_args(phy_args_t& args); bool check_args(const phy_args_t& args); }; diff --git a/srsue/hdr/phy/phy_common.h b/srsue/hdr/phy/phy_common.h index 7e7bbc9df..d936f76a5 100644 --- a/srsue/hdr/phy/phy_common.h +++ b/srsue/hdr/phy/phy_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,8 +24,11 @@ #include "phy_metrics.h" #include "srsran/adt/circular_array.h" +#include "srsran/common/block_queue.h" #include "srsran/common/gen_mch_tables.h" +#include "srsran/common/threads.h" #include "srsran/common/tti_sempahore.h" +#include "srsran/interfaces/phy_common_interface.h" #include "srsran/interfaces/phy_interface_types.h" #include "srsran/interfaces/radio_interfaces.h" #include "srsran/interfaces/rrc_interface_types.h" @@ -54,8 +57,39 @@ public: virtual void set_cfo(float cfo) = 0; }; +class phy_cmd_proc : public srsran::thread +{ +public: + phy_cmd_proc() : thread("PHY_CMD") { start(); } + + ~phy_cmd_proc() { stop(); } + + void add_cmd(std::function cmd) { cmd_queue.push(cmd); } + + void stop() + { + if (running) { + add_cmd([this]() { running = false; }); + wait_thread_finish(); + } + } + +private: + void run_thread() + { + std::function cmd; + while (running) { + cmd = cmd_queue.wait_pop(); + cmd(); + } + } + bool running = true; + // Queue for commands + srsran::block_queue > cmd_queue; +}; + /* Subclass that manages variables common to all workers */ -class phy_common +class phy_common : public srsran::phy_common_interface { public: /* Common variables used by all phy workers */ @@ -64,6 +98,8 @@ public: srsran::phy_cfg_mbsfn_t mbsfn_config = {}; + std::atomic cell_is_selecting = {false}; + // Secondary serving cell states scell::state cell_state; @@ -78,6 +114,9 @@ public: // Time Aligment Controller, internal thread safe ta_control ta; + // Last reported RI + std::atomic last_ri = {0}; + phy_common(srslog::basic_logger& logger); ~phy_common(); @@ -135,12 +174,52 @@ public: srsran_pdsch_ack_resource_t resource); bool get_dl_pending_ack(srsran_ul_sf_cfg_t* sf, uint32_t cc_idx, srsran_pdsch_ack_cc_t* ack); - void worker_end(void* h, bool tx_enable, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr); + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; void set_cell(const srsran_cell_t& c); - bool sr_enabled = false; - int sr_last_tx_tti = -1; + class sr_signal + { + public: + void reset() + { + std::lock_guard lock(mutex); + enabled = false; + last_tx_tti = -1; + } + bool is_triggered() + { + std::lock_guard lock(mutex); + return enabled; + } + void trigger() + { + std::lock_guard lock(mutex); + enabled = true; + last_tx_tti = -1; + } + int get_last_tx_tti() + { + std::lock_guard lock(mutex); + return last_tx_tti; + } + bool set_last_tx_tti(int last_tx_tti_) + { + std::lock_guard lock(mutex); + if (enabled) { + enabled = false; + last_tx_tti = last_tx_tti_; + return true; + } + return false; + } + + private: + std::mutex mutex; + bool enabled = false; + int last_tx_tti = -1; + }; + sr_signal sr; srsran::radio_interface_phy* get_radio(); @@ -174,6 +253,12 @@ public: */ uint32_t get_ul_earfcn(uint32_t dl_earfcn); + /** + * @brief Resets measurements from a given CC + * @param cc_idx CC index + */ + void reset_measurements(uint32_t cc_idx); + void update_measurements(uint32_t cc_idx, const srsran_chest_dl_res_t& chest_res, srsran_dl_sf_cfg_t sf_cfg_dl, @@ -207,7 +292,11 @@ public: return rx_gain_offset; } - void neighbour_cells_reset(uint32_t cc_idx) { avg_rsrp_neigh[cc_idx] = NAN; } + void neighbour_cells_reset(uint32_t cc_idx) + { + std::unique_lock lock(meas_mutex); + avg_rsrp_neigh[cc_idx] = NAN; + } void set_neighbour_cells(uint32_t cc_idx, const std::vector& meas) { @@ -217,6 +306,7 @@ public: total_rsrp += srsran_convert_dB_to_power(m.rsrp); } if (std::isnormal(total_rsrp)) { + std::unique_lock lock(meas_mutex); if (std::isnormal(avg_rsrp_neigh[cc_idx])) { avg_rsrp_neigh[cc_idx] = SRSRAN_VEC_EMA(total_rsrp, avg_rsrp_neigh[cc_idx], 0.9); } else { @@ -224,32 +314,32 @@ public: } } } - void reset_neighbour_cells() - { - for (uint32_t i = 0; i < SRSRAN_MAX_CARRIERS; i++) { - avg_rsrp_neigh[i] = NAN; - } - } + + srsran_cfr_cfg_t get_cfr_config() { return cfr_config; } private: std::mutex meas_mutex; - float pathloss[SRSRAN_MAX_CARRIERS] = {}; - float cur_pathloss = 0.0f; - float cur_pusch_power = 0.0f; - float avg_rsrp[SRSRAN_MAX_CARRIERS] = {}; - float avg_rsrp_dbm[SRSRAN_MAX_CARRIERS] = {}; - float avg_rsrq_db[SRSRAN_MAX_CARRIERS] = {}; - float avg_rssi_dbm[SRSRAN_MAX_CARRIERS] = {}; - float avg_cfo_hz[SRSRAN_MAX_CARRIERS] = {}; - float rx_gain_offset = 0.0f; - float avg_sinr_db[SRSRAN_MAX_CARRIERS] = {}; - float avg_snr_db[SRSRAN_MAX_CARRIERS] = {}; - float avg_noise[SRSRAN_MAX_CARRIERS] = {}; - float avg_rsrp_neigh[SRSRAN_MAX_CARRIERS] = {}; + float cur_pathloss = 0.0f; + float cur_pusch_power = 0.0f; + float rx_gain_offset = 0.0f; + std::array pathloss = {}; + std::array avg_rsrp = {}; + std::array avg_rsrp_dbm = {}; + std::array avg_rsrq_db = {}; + std::array avg_rssi_dbm = {}; + std::array avg_cfo_hz = {}; + std::array avg_sinr_db = {}; + std::array avg_snr_db = {}; + std::array avg_noise = {}; + std::array avg_rsrp_neigh = {}; - uint32_t pcell_report_period = 0; - uint32_t rssi_read_cnt = 0; + srsran_cfr_cfg_t cfr_config = {}; + + static constexpr uint32_t pcell_report_period = 20; + + static constexpr uint32_t update_rxgain_period = 10; + uint32_t update_rxgain_cnt = 0; rsrp_insync_itf* insync_itf = nullptr; @@ -257,7 +347,7 @@ private: std::mutex mtch_mutex; std::condition_variable mtch_cvar; - bool is_pending_tx_end = false; + std::atomic is_pending_tx_end{false}; srsran::radio_interface_phy* radio_h = nullptr; srslog::basic_logger& logger; @@ -328,8 +418,8 @@ private: bool is_mcch_subframe(srsran_mbsfn_cfg_t* cfg, uint32_t phy_tti); // NR carriers buffering synchronization, LTE workers are in charge of transmitting - srsran::rf_buffer_t nr_tx_buffer; - bool nr_tx_buffer_ready = false; + bool tx_enabled = false; + srsran::rf_buffer_t tx_buffer = {}; }; } // namespace srsue diff --git a/srsue/hdr/phy/phy_metrics.h b/srsue/hdr/phy/phy_metrics.h index 54b49c7d4..e4abc1699 100644 --- a/srsue/hdr/phy/phy_metrics.h +++ b/srsue/hdr/phy/phy_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -36,26 +36,26 @@ struct info_metrics_t { #define PHY_METRICS_SET(PARAM) \ do { \ - PARAM = PARAM + (other.PARAM - PARAM) / count; \ + PARAM = SRSRAN_VEC_SAFE_CMA(other.PARAM, PARAM, count); \ } while (false) struct sync_metrics_t { typedef std::array array_t; - float ta_us; - float distance_km; - float speed_kmph; - float cfo; - float sfo; + float ta_us = 0.0; + float distance_km = 0.0; + float speed_kmph = 0.0; + float cfo = 0.0; + float sfo = 0.0; void set(const sync_metrics_t& other) { - count++; ta_us = other.ta_us; distance_km = other.distance_km; speed_kmph = other.speed_kmph; PHY_METRICS_SET(cfo); PHY_METRICS_SET(sfo); + count++; } void reset() @@ -75,20 +75,23 @@ private: struct ch_metrics_t { typedef std::array array_t; - float n; - float sinr; - float rsrp; - float rsrq; - float rssi; - float ri; - float pathloss; - float sync_err; + float n = 0.0; + float sinr = 0.0; + float rsrp = 0.0; + float rsrq = 0.0; + float rssi = 0.0; + float ri = 0.0; + float pathloss = 0.0; + float sync_err = 0.0; void set(const ch_metrics_t& other) { count++; PHY_METRICS_SET(n); - PHY_METRICS_SET(sinr); + // We exclude inf and nan from the average SINR + if (!std::isnan(other.sinr) || !std::isinf(other.sinr)) { + PHY_METRICS_SET(sinr); + } PHY_METRICS_SET(rsrp); PHY_METRICS_SET(rsrq); PHY_METRICS_SET(rssi); @@ -117,9 +120,9 @@ private: struct dl_metrics_t { typedef std::array array_t; - float fec_iters; - float mcs; - float evm; + float fec_iters = 0.0; + float mcs = 0.0; + float evm = 0.0; void set(const dl_metrics_t& other) { diff --git a/srsue/hdr/phy/phy_nr_sa.h b/srsue/hdr/phy/phy_nr_sa.h new file mode 100644 index 000000000..c06e06e4d --- /dev/null +++ b/srsue/hdr/phy/phy_nr_sa.h @@ -0,0 +1,114 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_PHY_NR_SA_H +#define SRSUE_PHY_NR_SA_H + +#include "phy_common.h" +#include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsue/hdr/phy/nr/sync_sa.h" +#include "srsue/hdr/phy/ue_phy_base.h" + +namespace srsue { + +/** + * @brief NR Standalone PHY + */ +class phy_nr_sa final : public ue_phy_base, public phy_interface_stack_nr, public srsran::phy_interface_radio +{ +public: + phy_nr_sa(const char* logname); + ~phy_nr_sa() final { stop(); } + + int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); + void wait_initialize() final; + bool is_initialized() final; + void stop() final; + void reset_nr() final; + + void start_plot() final {} + + void radio_overflow() final {} + void radio_failure() final {} + + std::string get_type() final { return "nr_soft"; } + + int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) final; + void send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec) final; + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final; + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final; + + void set_earfcn(std::vector earfcns); + bool has_valid_sr_resource(uint32_t sr_id) final; + void clear_pending_grants() final; + bool set_config(const srsran::phy_cfg_nr_t& cfg) final; + + phy_nr_state_t get_state() final; + bool start_cell_search(const cell_search_args_t& req) final; + bool start_cell_select(const cell_select_args_t& req) final; + + void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final + { + if (rat == srsran::srsran_rat_t::nr) { + return workers.get_metrics(*m); + } + }; + void srsran_phy_logger(phy_logger_level_t log_level, char* str); + +private: + srslog::basic_logger& logger; + srslog::basic_logger& logger_phy_lib; + + nr::worker_pool workers; + nr::sync_sa sync; + + srsran::phy_cfg_nr_t config_nr = {}; + phy_args_nr_t args = {}; + srsran_carrier_nr_t selected_cell = {}; + + srsran::radio_interface_phy* radio = nullptr; + stack_interface_phy_nr* stack = nullptr; + + std::atomic is_configured = {false}; + + // Since cell_search/cell_select operations take a lot of time, we use another queue to process the other commands + // in parallel and avoid accumulating in the queue + phy_cmd_proc cmd_worker_cell, cmd_worker; + + // Run initialization functions in the background + void init_background(); + std::thread init_thread; + + const static int MAX_WORKERS = 4; + const static int DEFAULT_WORKERS = 4; + + static void set_default_args(phy_args_nr_t& args); + bool check_args(const phy_args_nr_t& args); +}; + +} // namespace srsue +#endif // SRSUE_PHY_NR_SA_H diff --git a/srsue/hdr/phy/prach.h b/srsue/hdr/phy/prach.h index 084ee6ecd..d68bbb53d 100644 --- a/srsue/hdr/phy/prach.h +++ b/srsue/hdr/phy/prach.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,6 +27,7 @@ #include "srsran/srslog/srslog.h" #include "srsran/srsran.h" #include +#include namespace srsue { @@ -49,36 +50,25 @@ public: private: bool generate_buffer(uint32_t f_idx); - bool is_buffer_generated(uint32_t f_idx, uint32_t preamble_index) const - { - return buffer_bitmask.test(f_idx * 64 + preamble_index); - } - - void set_buffer_as_generated(uint32_t f_idx, uint32_t preamble_index) - { - buffer_bitmask.set(f_idx * 64 + preamble_index); - } - private: static constexpr unsigned MAX_LEN_SF = 3; static constexpr unsigned max_fs = 12; static constexpr unsigned max_preambles = 64; - srslog::basic_logger& logger; - srsran_prach_t prach_obj = {}; - srsran_cell_t cell = {}; - srsran_cfo_t cfo_h = {}; - srsran_prach_cfg_t cfg = {}; - std::array, max_fs> buffer = {}; - cf_t* signal_buffer = nullptr; - int preamble_idx = -1; - uint32_t len = 0; - int allowed_subframe = 0; - int transmitted_tti = 0; - float target_power_dbm = 0; - bool mem_initiated = false; - bool cell_initiated = false; - std::bitset buffer_bitmask; + srslog::basic_logger& logger; + srsran_prach_t prach_obj = {}; + srsran_cell_t cell = {}; + srsran_cfo_t cfo_h = {}; + srsran_prach_cfg_t cfg = {}; + cf_t* signal_buffer = nullptr; + int preamble_idx = -1; + uint32_t len = 0; + int allowed_subframe = 0; + int transmitted_tti = 0; + float target_power_dbm = 0; + bool mem_initiated = false; + bool cell_initiated = false; + mutable std::mutex mutex; }; } // namespace srsue diff --git a/srsue/hdr/phy/scell/intra_measure.h b/srsue/hdr/phy/scell/intra_measure.h deleted file mode 100644 index af03a3a62..000000000 --- a/srsue/hdr/phy/scell/intra_measure.h +++ /dev/null @@ -1,227 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ -#ifndef SRSUE_INTRA_MEASURE_H -#define SRSUE_INTRA_MEASURE_H - -#include -#include -#include - -#include "scell_recv.h" - -namespace srsue { -namespace scell { - -// Class to perform intra-frequency measurements -class intra_measure : public srsran::thread -{ - /* - * The intra-cell measurment has 5 different states: - * - idle: it has been initiated and it is waiting to get configured to start capturing samples. From any state - * except quit can transition to idle. - * - wait: waits for at least intra_freq_meas_period_ms since last receive start and goes to receive. - * - receive: captures base-band samples for intra_freq_meas_len_ms and goes to measure. - * - measure: enables the inner thread to start the measuring function. The asynchronous buffer will transition to - * wait as soon as it has read the data from the buffer. - * - quit: stops the inner thread and quits. Transition from any state measure state. - * - * FSM abstraction: - * - * +------+ set_cells_to_meas +------+ intra_freq_meas_period_ms +---------+ - * | Idle | --------------------->| Wait |------------------------------>| Receive | - * +------+ +------+ +---------+ - * ^ ^ | stop +------+ - * | Read buffer | | ----->| Quit | - * init +---------+ intra_freq_meas_len_ms | +------+ - * meas_stop | Measure |<----------------------------------+ - * +---------+ - */ -public: - // Interface for reporting new cell measurements - class meas_itf - { - public: - virtual void cell_meas_reset(uint32_t cc_idx) = 0; - virtual void new_cell_meas(uint32_t cc_idx, const std::vector& meas) = 0; - }; - - /** - * Constructor - */ - intra_measure(srslog::basic_logger& logger); - - /** - * Destructor - */ - ~intra_measure(); - - /** - * Initiation function, necessary to configure main parameters - * @param common SRSUE phy_common instance pointer for providing intra_freq_meas_len_ms and intra_freq_meas_period_ms - * @param rrc SRSUE PHY->RRC interface for supplying the RRC with the measurements - */ - void init(uint32_t cc_idx, phy_common* common, meas_itf* new_cell_itf); - - /** - * Stops the operation of this component - */ - void stop(); - - /** - * Sets the primary cell, configures the cell bandwidth and sampling rate - * @param earfcn Frequency the component is receiving base-band from. Used only for reporting the EARFCN to the RRC - * @param cell Actual cell configuration - */ - void set_primary_cell(uint32_t earfcn, srsran_cell_t cell); - - /** - * Sets receiver gain offset to convert estimated dBFs to dBm in RSRP - * @param rx_gain_offset Gain offset in dB - */ - void set_rx_gain_offset(float rx_gain_offset_db); - - /** - * Sets the PCI list of the cells this components needs to measure and starts the FSM for measuring - * @param pci is the list of PCIs to measure - */ - void set_cells_to_meas(const std::set& pci); - - /** - * Stops the measurment FSM, setting the inner state to idle. - */ - void meas_stop(); - - /** - * Inputs the baseband IQ samples into the component, internal state dictates whether it will be written or not. - * @param tti The current physical layer TTI, used for calculating the buffer write - * @param data buffer with baseband IQ samples - * @param nsamples number of samples to write - */ - void write(uint32_t tti, cf_t* data, uint32_t nsamples); - - /** - * Get EARFCN of this component - * @return EARFCN - */ - uint32_t get_earfcn() { return current_earfcn; }; - - /** - * Synchronous wait mechanism, blocks the writer thread while it is in measure state. If the asynchonous thread is too - * slow, use this method for stalling the writing thread and wait the asynchronous thread to clear the buffer. - */ - void wait_meas() - { // Only used by scell_search_test - state.wait_change(internal_state::measure); - } - -private: - class internal_state ///< Internal state class, provides thread safe state management - { - public: - typedef enum { - idle = 0, ///< Initial state, internal thread runs, it does not capture data - wait, ///< Wait for the period time to pass - receive, ///< Accumulate samples in ring buffer - measure, ///< Module is busy measuring - quit ///< Quit thread, no transitions are allowed - } state_t; - - private: - state_t state = idle; - std::mutex mutex; - std::condition_variable cvar; - - public: - /** - * Get the internal state - * @return protected state - */ - state_t get_state() { return state; } - - /** - * Transitions to a different state, all transitions are allowed except from quit - * @param new_state - */ - void set_state(state_t new_state) - { - std::unique_lock lock(mutex); - // Do not allow transition from quit - if (state != quit) { - state = new_state; - } - - // Notifies to the inner thread about the change of state - cvar.notify_all(); - } - - /** - * Waits for a state transition to a state different than the provided, used for blocking the inner thread - */ - void wait_change(state_t s) - { - std::unique_lock lock(mutex); - while (state == s) { - cvar.wait(lock); - } - } - }; - - internal_state state; - - /** - * Measurement process helper method. Encapusulates the neighbour cell measurement functionality - */ - void measure_proc(); - - /** - * Internal asynchronous low priority thread, waits for measure internal state to execute the measurement process. It - * stops when the internal state transitions to quit. - */ - void run_thread() override; - - ///< Internal Thread priority, low by default - const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5; - - scell_recv scell; - meas_itf* new_cell_itf = nullptr; - srslog::basic_logger& logger; - uint32_t cc_idx = 0; - uint32_t current_earfcn = 0; - uint32_t current_sflen = 0; - srsran_cell_t serving_cell = {}; - std::set active_pci = {}; - std::mutex active_pci_mutex = {}; - uint32_t last_measure_tti = 0; - uint32_t intra_freq_meas_len_ms = 20; - uint32_t intra_freq_meas_period_ms = 200; - uint32_t rx_gain_offset_db = 0; - - cf_t* search_buffer = nullptr; - - srsran_ringbuffer_t ring_buffer = {}; - - srsran_refsignal_dl_sync_t refsignal_dl_sync = {}; -}; - -} // namespace scell -} // namespace srsue - -#endif // SRSUE_INTRA_MEASURE_H diff --git a/srsue/hdr/phy/scell/intra_measure_base.h b/srsue/hdr/phy/scell/intra_measure_base.h new file mode 100644 index 000000000..21f493bb4 --- /dev/null +++ b/srsue/hdr/phy/scell/intra_measure_base.h @@ -0,0 +1,319 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSUE_INTRA_MEASURE_BASE_H +#define SRSUE_INTRA_MEASURE_BASE_H + +#include "srsran/interfaces/ue_phy_interfaces.h" +#include +#include +#include +#include +#include +#include + +namespace srsue { +namespace scell { + +/** + * @brief Describes a generic base class to perform intra-frequency measurements + */ +class intra_measure_base : public srsran::thread +{ + /* + * The intra-cell measurement has 5 different states: + * - idle: it has been initiated and it is waiting to get configured to start capturing samples. From any state + * except quit can transition to idle. + * - wait: waits for the TTI trigger to transition to receive + * - receive: captures base-band samples for intra_freq_meas_len_ms and goes to measure. + * - measure: enables the inner thread to start the measuring function. The asynchronous buffer will transition to + * wait as soon as it has read the data from the buffer. + * - quit: stops the inner thread and quits. Transition from any state measure state. + * + * FSM abstraction: + * + * +------+ set_cells_to_meas +------+ receive_tti_trigger +---------+ + * | Idle | --------------------->| Wait |------------------------------>| Receive | + * +------+ +------+ +---------+ + * ^ ^ | stop +------+ + * | Read buffer | | ----->| Quit | + * init +---------+ intra_freq_meas_len_ms | +------+ + * meas_stop | Measure |<----------------------------------+ + * +---------+ + * + * This class has been designed to be thread safe. Any method can be called from different threads as long as + * init_generic is called when the FSM is in idle. + */ +public: + /** + * @brief Describes an interface for reporting new cell measurements + */ + class meas_itf + { + public: + virtual void cell_meas_reset(uint32_t cc_idx) = 0; + virtual void new_cell_meas(uint32_t cc_idx, const std::vector& meas) = 0; + }; + + /** + * @brief Describes the default generic configuration arguments + */ + struct args_t { + double srate_hz = 0.0; ///< Sampling rate in Hz, optional for LTE, compulsory for NR + uint32_t len_ms = 20; ///< Amount of time to accumulate + uint32_t period_ms = 200; ///< Minimum time interval between measurements, set to 0 for free-run + uint32_t tti_period = 0; ///< Measurement TTI trigger period, set to 0 to trigger at any TTI + uint32_t tti_offset = 0; ///< Measurement TTI trigger offset + float rx_gain_offset_db = 0.0f; ///< Gain offset, for calibrated measurements + }; + + /** + * @brief Stops the operation of this component and it cannot be started again + * @note use meas_stop() method to stop measurements temporally + */ + void stop(); + + /** + * @brief Updates the receiver gain offset to convert estimated dBFs to dBm in RSRP + * @param rx_gain_offset Gain offset in dB + */ + void set_rx_gain_offset(float rx_gain_offset_db); + + /** + * @brief Sets the PCI list of the cells this components needs to measure and starts the FSM for measuring + * @param pci is the list of PCIs to measure + */ + void set_cells_to_meas(const std::set& pci); + + /** + * @brief Stops the measurement FSM, setting the inner state to idle. + */ + void meas_stop(); + + /** + * @brief Inputs the baseband IQ samples into the component, internal state dictates whether it will be written or + * not. + * @param tti The current physical layer TTI, used for calculating the buffer write + * @param data buffer with baseband IQ samples + * @param nsamples number of samples to write + */ + void run_tti(uint32_t tti, cf_t* data, uint32_t nsamples); + + /** + * @brief Get EARFCN of this component + * @return EARFCN + */ + virtual uint32_t get_earfcn() const = 0; + + /** + * @brief Synchronous wait mechanism, blocks the writer thread while it is in measure state. If the asynchronous + * thread is too slow, use this method for stalling the writing thread and wait the asynchronous thread to clear the + * buffer. + */ + void wait_meas() + { // Only used by scell_search_test + state.wait_change(internal_state::measure); + } + +protected: + struct measure_context_t { + uint32_t cc_idx = 0; ///< Component carrier index + std::set active_pci = {}; ///< Set with the active PCIs + uint32_t sf_len = 0; ///< Subframe length in samples + uint32_t meas_len_ms = 20; ///< Measure length in milliseconds/sub-frames + uint32_t meas_period_ms = 200; ///< Minimum time between measurements + uint32_t trigger_tti_period = 0; ///< Measurement TTI trigger period + uint32_t trigger_tti_offset = 0; ///< Measurement TTI trigger offset + meas_itf& new_cell_itf; + + explicit measure_context_t(meas_itf& new_cell_itf_) : new_cell_itf(new_cell_itf_) {} + }; + + std::atomic rx_gain_offset_db = {0.0f}; ///< Current gain offset + + /** + * @brief Generic initialization method, necessary to configure main parameters + * @param cc_idx_ Indicates the component carrier index linked to the intra frequency measurement instance + * @param args Generic configuration arguments + */ + void init_generic(uint32_t cc_idx_, const args_t& args); + + /** + * @brief Constructor is only accessible through inherited classes + */ + intra_measure_base(srslog::basic_logger& logger, meas_itf& new_cell_itf_); + + /** + * @brief Destructor is only accessible through inherited classes + */ + ~intra_measure_base() override; + + /** + * @brief Subframe length setter, the inherited class shall set the subframe length + * @param new_sf_len New subframe length + */ + void set_current_sf_len(uint32_t new_sf_len) + { + std::lock_guard lock(mutex); + context.sf_len = new_sf_len; + } + +private: + /** + * @brief Describes the internal state class, provides thread safe state management + */ + class internal_state + { + public: + typedef enum { + initial = 0, /// Initial state, it transitions to idle once the internal thread has started + idle, ///< Internal thread runs, it does not capture data + wait_first, ///< Wait for the TTI trigger (if configured) + wait, ///< Wait for the period time to pass + receive, ///< Accumulate samples in ring buffer + measure, ///< Module is busy measuring + quit ///< Quit thread, no transitions are allowed + } state_t; + + private: + state_t state = initial; + std::mutex mutex; + std::condition_variable cvar; + + public: + /** + * @brief Get the internal state + * @return protected state + */ + state_t get_state() + { + std::lock_guard lock(mutex); + return state; + } + + /** + * @brief Transitions to a different state, all transitions are allowed except from quit + * @param new_state + */ + void set_state(state_t new_state) + { + std::unique_lock lock(mutex); + // Do not allow transition from quit + if (state != quit) { + state = new_state; + } + + // Notifies to the inner thread about the change of state + cvar.notify_all(); + } + + /** + * @brief Waits for a state transition to a state different than the provided, used for blocking the inner thread + */ + void wait_change(state_t s) + { + std::unique_lock lock(mutex); + while (state == s) { + cvar.wait(lock); + } + } + }; + + /** + * @brief Computes the measurement trigger based on TTI and the last TTI trigger + */ + bool receive_tti_trigger(uint32_t tti) + { + std::lock_guard lock(mutex); + + // If the elapsed time does not satisfy with the minimum time, do not trigger + uint32_t elapsed_tti = TTI_SUB(tti, last_measure_tti); + if (elapsed_tti < context.meas_period_ms and state.get_state() != internal_state::wait_first) { + return false; + } + + // If the TTI period is not configured, it will be always true + if (context.trigger_tti_period == 0) { + return true; + } + + // Check if trigger condition is satisfied + return tti % context.trigger_tti_period == context.trigger_tti_offset; + } + + /** + * @brief Writes baseband data in the internal soft-buffer + * @param data Provides baseband data + * @param nsamples Number of samples to write + */ + void write(cf_t* data, uint32_t nsamples); + + /** + * @brief Get the Radio Access Technology (RAT) that is being measured + * @return The measured RAT + */ + virtual srsran::srsran_rat_t get_rat() const = 0; + + /** + * @brief Pure virtual function to perform measurements + * @note The context is pass-by-value to protect it from concurrency. However, the buffer is pass-by-reference + * as it is protected by the state. + * @param context Provides current measurement context + * @param buffer Provides current measurement context + * @param rx_gain_offset Provides last received rx_gain_offset + * @return True if the measurement functions are executed without errors, otherwise false + */ + virtual bool measure_rat(const measure_context_t& context, std::vector& buffer, float rx_gain_offset) = 0; + + /** + * @brief Measurement process helper method. Encapsulates the neighbour cell measurement functionality + */ + void measure_proc(); + + /** + * @brief Internal asynchronous low priority thread, waits for measure internal state to execute the measurement + * process. It stops when the internal state transitions to quit. + */ + void run_thread() override; + + ///< Internal Thread priority, low by default + const static int INTRA_FREQ_MEAS_PRIO = DEFAULT_PRIORITY + 5; + + /// Returns a copy of the current used context. + measure_context_t get_context() const + { + std::lock_guard lock(mutex); + return context; + } + + internal_state state; + srslog::basic_logger& logger; + mutable std::mutex mutex; + uint32_t last_measure_tti = 0; + measure_context_t context; + + std::vector search_buffer; + srsran_ringbuffer_t ring_buffer = {}; +}; + +} // namespace scell +} // namespace srsue + +#endif // SRSUE_INTRA_MEASURE_BASE_H diff --git a/srsue/hdr/phy/scell/intra_measure_lte.h b/srsue/hdr/phy/scell/intra_measure_lte.h new file mode 100644 index 000000000..cfdcc5ef0 --- /dev/null +++ b/srsue/hdr/phy/scell/intra_measure_lte.h @@ -0,0 +1,97 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSRAN_INTRA_MEASURE_LTE_H +#define SRSRAN_INTRA_MEASURE_LTE_H + +#include "intra_measure_base.h" +#include "scell_recv.h" +#include + +namespace srsue { +namespace scell { + +/** + * @brief Describes a class for performing LTE intra-frequency cell search and measurement + */ +class intra_measure_lte : public intra_measure_base +{ +public: + /** + * @brief Constructor + * @param logger Logging object + * @param new_meas_itf_ Interface to report measurement to higher layers + */ + intra_measure_lte(srslog::basic_logger& logger, meas_itf& new_meas_itf_); + + /** + * @brief Destructor + */ + ~intra_measure_lte() override; + + /** + * @brief Initialises LTE specific measurement objects + * @param args Configuration arguments + */ + void init(uint32_t cc_idx, const args_t& args); + + /** + * @brief Sets the primary cell and selects LTE operation mode, configures the cell bandwidth and sampling rate + * @param earfcn Frequency the component is receiving base-band from. Used only for reporting the EARFCN to the RRC + * @param cell Actual cell configuration + */ + void set_primary_cell(uint32_t earfcn, srsran_cell_t cell); + + /** + * @brief Get EARFCN of this component + * @return EARFCN + */ + uint32_t get_earfcn() const override { return current_earfcn; }; + +private: + /** + * @brief Provides with the RAT to the base class + * @return The RAT measured by this class which is LTE + */ + srsran::srsran_rat_t get_rat() const override { return srsran::srsran_rat_t::lte; } + + /** + * @brief LTE specific measurement process + * @param context Measurement context + * @param buffer Provides the baseband buffer to perform the measurements + * @param rx_gain_offset Provides last received rx_gain_offset + * @return True if no error happens, otherwise false + */ + bool measure_rat(const measure_context_t& context, std::vector& buffer, float rx_gain_offset) override; + + srslog::basic_logger& logger; + srsran_cell_t serving_cell = {}; ///< Current serving cell in the EARFCN, to avoid reporting it + std::atomic current_earfcn = {0}; ///< Current EARFCN + std::mutex mutex; + + /// LTE-based measuring objects + scell_recv scell_rx; ///< Secondary cell searcher + srsran_refsignal_dl_sync_t refsignal_dl_sync = {}; ///< Reference signal based measurement +}; + +} // namespace scell +} // namespace srsue + +#endif // SRSRAN_INTRA_MEASURE_LTE_H diff --git a/srsue/hdr/phy/scell/intra_measure_nr.h b/srsue/hdr/phy/scell/intra_measure_nr.h new file mode 100644 index 000000000..6c94bb760 --- /dev/null +++ b/srsue/hdr/phy/scell/intra_measure_nr.h @@ -0,0 +1,140 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSRAN_INTRA_MEASURE_NR_H +#define SRSRAN_INTRA_MEASURE_NR_H + +#include "intra_measure_base.h" +#include + +namespace srsue { +namespace scell { + +/** + * @brief Describes a class for performing LTE intra-frequency cell search and measurement + */ +class intra_measure_nr : public intra_measure_base +{ +public: + /** + * @brief Describes initialization arguments. It is used to preallocate all memory and avoiding performing memory + * allocation when the configuration is set + */ + struct args_t { + float rx_gain_offset_dB = 0.0f; + uint32_t max_len_ms = 1; + double max_srate_hz = 61.44e6; + srsran_subcarrier_spacing_t min_scs = srsran_subcarrier_spacing_15kHz; + float thr_snr_db = 5.0f; ///< minimum SNR threshold + }; + + /** + * @brief Describes the required configuration arguments to start measurements + */ + struct config_t : public intra_measure_base::args_t { + /// Additional fields to the base arguments + uint32_t arfcn; ///< Carrier frequency in ARFCN + double center_freq_hz = 0.0; ///< Base-band center frequency in Hz + double ssb_freq_hz = 0.0; ///< SSB center frequency + srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_30kHz; ///< SSB configured Subcarrier spacing + int serving_cell_pci = -1; ///< Current serving cell PCI, set to -1 if no + ///< serving cell has been configured for this + ///< carrier + }; + + /** + * @brief Constructor + * @param logger Logging object + * @param new_meas_itf_ Interface to report measurement to higher layers + */ + intra_measure_nr(srslog::basic_logger& logger, meas_itf& new_meas_itf_); + + /** + * @brief Destructor + */ + ~intra_measure_nr() override; + + /** + * @brief Initialises LTE specific measurement objects + * @param args Configuration arguments + * @return True if initialization is successful, false otherwise + */ + bool init(uint32_t cc_idx, const args_t& args); + + /** + * @brief Sets the primary cell and selects NR operation mode, configures the cell bandwidth and sampling rate + * @param cfg Actual configuration + * @return True if configuration is successful, false otherwise + */ + bool set_config(const config_t& cfg); + + /** + * @brief Get current frequency number + * @return the current ARFCN + */ + uint32_t get_earfcn() const override { return current_arfcn; }; + + /** + * @brief Computes the average measurement performance since last configuration + * @return The performance in Millions of samples per second + */ + uint32_t get_perf() const + { + if (perf_count_us == 0) { + return 0; + } + return (uint32_t)(perf_count_samples / perf_count_us); + }; + +private: + /** + * @brief Provides with the RAT to the base class + * @return The RAT measured by this class which is NR + */ + srsran::srsran_rat_t get_rat() const override { return srsran::srsran_rat_t::nr; } + + /** + * @brief NR specific measurement process + * @attention It searches and measures the SSB with best SNR + * @param context Measurement context + * @param buffer Provides the baseband buffer to perform the measurements + * @param rx_gain_offset Provides last received rx_gain_offset + * @return True if no error happen, otherwise false + */ + bool measure_rat(const measure_context_t& context, std::vector& buffer, float rx_gain_offset) override; + + srslog::basic_logger& logger; + uint32_t cc_idx = 0; + uint32_t current_arfcn = 0; + float thr_snr_db = 5.0f; + int serving_cell_pci = -1; + + /// Performance + uint64_t perf_count_us = 0; ///< Counts execution time in microseconds + uint64_t perf_count_samples = 0; ///< Counts the number samples + + /// NR-based measuring objects + srsran_ssb_t ssb = {}; ///< SS/PBCH Block +}; + +} // namespace scell +} // namespace srsue + +#endif // SRSRAN_INTRA_MEASURE_NR_H diff --git a/srsue/hdr/phy/scell/scell_recv.h b/srsue/hdr/phy/scell/scell_recv.h index 499c9240b..10ff83877 100644 --- a/srsue/hdr/phy/scell/scell_recv.h +++ b/srsue/hdr/phy/scell/scell_recv.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,18 +35,26 @@ class scell_recv public: explicit scell_recv(srslog::basic_logger& logger) : logger(logger) {} - void init(uint32_t max_sf_window); - void deinit(); - void reset(); - std::set find_cells(const cf_t* input_buffer, const srsran_cell_t serving_cell, const uint32_t nof_sf); + void init(uint32_t max_sf_window); + void deinit(); + void reset(); + + /** + * @brief Find neighbour cells in a given buffer + * @param input_buffer Provides the baseband samples + * @param serving_cell Current serving cell + * @param nof_sf Number of subframes contained in the baseband buffer + * @param found_cell_ids Provides a set where to insert the found cell identifiers (PCIs) + */ + void find_cells(const cf_t* input_buffer, + const srsran_cell_t& serving_cell, + const uint32_t& nof_sf, + std::set& found_cell_ids); private: - // 36.133 9.1.2.1 for band 7 - constexpr static float ABSOLUTE_RSRP_THRESHOLD_DBM = -125; - - cf_t* sf_buffer[SRSRAN_MAX_PORTS]; + cf_t* sf_buffer[SRSRAN_MAX_PORTS] = {}; srslog::basic_logger& logger; - srsran_sync_t sync_find; + srsran_sync_t sync_find = {}; uint32_t current_fft_sz; }; diff --git a/srsue/hdr/phy/scell/scell_state.h b/srsue/hdr/phy/scell/scell_state.h index a31650b86..0d89fc6b6 100644 --- a/srsue/hdr/phy/scell/scell_state.h +++ b/srsue/hdr/phy/scell/scell_state.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -109,7 +109,6 @@ public: std::unique_lock lock(mutex); switch (activation_state) { - case idle: // waiting for receiving a command, do nothing break; @@ -123,7 +122,6 @@ public: case transition: // Detect when the TTI has increased enough to make sure there arent workers, set the configuration if (TTI_SUB(tti, activation_tti) >= activation_margin_tti) { - // Reload cell states for (uint32_t i = 1; i < SRSRAN_MAX_CARRIERS; i++) { // Get Activation command value @@ -158,7 +156,6 @@ public: bool is_active(uint32_t cc_idx, uint32_t tti) const { - if (cc_idx == 0) { return true; } @@ -180,7 +177,6 @@ public: bool is_configured(uint32_t cc_idx) const { - if (cc_idx == 0) { return true; } @@ -194,6 +190,22 @@ public: return scell_cfg[cc_idx].status != cfg::none; } + void reset(uint32_t cc_idx) + { + if (cc_idx == 0 or cc_idx >= SRSRAN_MAX_CARRIERS) { + return; + } + + std::unique_lock lock(mutex); + + activation_state = idle; + + cfg& e = scell_cfg[cc_idx]; + e.status = cfg::none; + e.earfcn = 0; + e.pci = UINT32_MAX; + } + void reset() { std::unique_lock lock(mutex); diff --git a/srsue/hdr/phy/scell/scell_sync.h b/srsue/hdr/phy/scell/scell_sync.h index 416d92ff0..e3c1756c7 100644 --- a/srsue/hdr/phy/scell/scell_sync.h +++ b/srsue/hdr/phy/scell/scell_sync.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -208,7 +208,11 @@ public: /** * Resets the class, goes back into IDLE mode */ - void stop() { state = STATE_IDLE; } + void stop() + { + std::unique_lock lock(mutex); + state = STATE_IDLE; + } /** * Runs internal FSM, performing Synchronization operations on the provided buffer. It expects data per subframe diff --git a/srsue/hdr/phy/search.h b/srsue/hdr/phy/search.h index 9d8f18f58..345b857b0 100644 --- a/srsue/hdr/phy/search.h +++ b/srsue/hdr/phy/search.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,11 +45,12 @@ public: explicit search(srslog::basic_logger& logger) : logger(logger) {} ~search(); - void init(srsran::rf_buffer_t& buffer_, uint32_t nof_rx_channels, search_callback* parent, int force_N_id_2_); + void init(srsran::rf_buffer_t& buffer_, uint32_t nof_rx_channels, search_callback* parent, int force_N_id_2_, int force_N_id_1_); void reset(); float get_last_cfo(); void set_agc_enable(bool enable); ret_code run(srsran_cell_t* cell, std::array& bch_payload); + void set_cp_en(bool enable); private: search_callback* p = nullptr; @@ -58,6 +59,7 @@ private: srsran_ue_cellsearch_t cs = {}; srsran_ue_mib_sync_t ue_mib_sync = {}; int force_N_id_2 = 0; + int force_N_id_1 = 0; }; }; // namespace srsue diff --git a/srsue/hdr/phy/sfn_sync.h b/srsue/hdr/phy/sfn_sync.h index ff312b611..3d20f93bf 100644 --- a/srsue/hdr/phy/sfn_sync.h +++ b/srsue/hdr/phy/sfn_sync.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index 871ec1907..992217a64 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,7 +29,7 @@ #include "phy_common.h" #include "prach.h" -#include "scell/intra_measure.h" +#include "scell/intra_measure_lte.h" #include "scell/scell_sync.h" #include "search.h" #include "sfn_sync.h" @@ -51,7 +51,7 @@ class sync : public srsran::thread, public rsrp_insync_itf, public search_callback, public scell::sync_callback, - public scell::intra_measure::meas_itf + public scell::intra_measure_base::meas_itf { public: sync(srslog::basic_logger& phy_logger, srslog::basic_logger& phy_lib_logger) : @@ -77,11 +77,18 @@ public: // RRC interface for controling the SYNC state bool cell_search_init(); - rrc_interface_phy_lte::cell_search_ret_t cell_search_start(phy_cell_t* cell); + rrc_interface_phy_lte::cell_search_ret_t cell_search_start(phy_cell_t* cell, int earfcn); bool cell_select_init(phy_cell_t cell); bool cell_select_start(phy_cell_t cell); bool cell_is_camping(); + /** + * @brief Interface for monitoring UE's synchronization transition to IDLE. In addition to IDLE transitioning, this + * method waits for workers to finish processing and ends the current RF transmission burst. + * @return true if SYNC transitioned to IDLE, false otherwise + */ + bool wait_idle(); + // RRC interface for controlling the neighbour cell measurement void set_cells_to_meas(uint32_t earfcn, const std::set& pci); void set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srsran_cell_t cell_); @@ -198,16 +205,17 @@ private: bool set_frequency(); bool set_cell(float cfo); - bool running = false; - bool is_overflow = false; + std::atomic running = {false}; + bool is_overflow = false; srsran::rf_timestamp_t last_rx_time; bool forced_rx_time_init = true; // Rx time sync after first receive from radio // Objects for internal use - search search_p; - sfn_sync sfn_p; - std::vector > intra_freq_meas; + search search_p; + sfn_sync sfn_p; + std::vector > intra_freq_meas; + std::mutex intra_freq_cfg_mutex; // Pointers to other classes stack_interface_phy_lte* stack = nullptr; @@ -238,11 +246,14 @@ private: srsran::rf_buffer_t dummy_buffer; // Sync metrics - sync_metrics_t metrics = {}; + std::atomic sfo = {}; // SFO estimate updated after each sync-cycle + std::atomic cfo = {}; // CFO estimate updated after each sync-cycle + std::atomic ref_cfo = {}; // provided adjustment value applied before sync + sync_metrics_t metrics = {}; // in-sync / out-of-sync counters - uint32_t out_of_sync_cnt = 0; - uint32_t in_sync_cnt = 0; + std::atomic out_of_sync_cnt = {0}; + std::atomic in_sync_cnt = {0}; std::mutex rrc_mutex; enum { @@ -257,12 +268,94 @@ private: search::ret_code cell_search_ret = search::CELL_NOT_FOUND; - // Sampling rate mode (find is 1.96 MHz, camp is the full cell BW) - enum { SRATE_NONE = 0, SRATE_FIND, SRATE_CAMP } srate_mode = SRATE_NONE; - float current_srate = 0; + // Sampling rate mode (find is 1.92 MHz, camp is the full cell BW) + class srate_safe + { + public: + void reset() + { + std::lock_guard lock(mutex); + srate_mode = SRATE_NONE; + current_srate = 0; + } + float get_srate() + { + std::lock_guard lock(mutex); + return current_srate; + } + bool is_normal() + { + std::lock_guard lock(mutex); + return std::isnormal(current_srate) and current_srate > 0.0f; + } + void set_camp(float new_srate) + { + std::lock_guard lock(mutex); + current_srate = new_srate; + srate_mode = SRATE_CAMP; + } + bool set_find() + { + std::lock_guard lock(mutex); + if (srate_mode != SRATE_FIND) { + srate_mode = SRATE_FIND; + current_srate = 1.92e6; + return true; + } + return false; + } + + private: + enum { SRATE_NONE = 0, SRATE_FIND, SRATE_CAMP } srate_mode = SRATE_NONE; + float current_srate = 0; + std::mutex mutex; + }; + // Protect sampling rate changes since accessed by multiple threads + srate_safe srate; // This is the primary cell - srsran_cell_t cell = {}; + class cell_safe + { + public: + void set_pci(uint32_t id) + { + std::lock_guard lock(mutex); + cell.id = id; + } + void reset() + { + std::lock_guard lock(mutex); + cell = {}; + } + bool is_valid() + { + std::lock_guard lock(mutex); + return srsran_cell_isvalid(&cell); + } + void set(srsran_cell_t& x) + { + std::lock_guard lock(mutex); + cell = x; + } + srsran_cell_t get() + { + std::lock_guard lock(mutex); + return cell; + } + bool equals(srsran_cell_t& x) + { + std::lock_guard lock(mutex); + return memcmp(&cell, &x, sizeof(srsran_cell_t)) == 0; + } + + private: + srsran_cell_t cell = {}; + std::mutex mutex; + }; + + // Protect access to cell configuration since it's accessed by multiple threads + cell_safe cell; + bool force_camping_sfn_sync = false; uint32_t tti = 0; srsran_timestamp_t stack_tti_ts_new = {}; @@ -277,10 +370,10 @@ private: float dl_freq = -1; float ul_freq = -1; - const static int MIN_TTI_JUMP = 1; // Time gap reported to stack after receiving subframe - const static int MAX_TTI_JUMP = 1000; // Maximum time gap tolerance in RF stream metadata - - const uint8_t SYNC_CC_IDX = 0; ///< From the sync POV, the CC idx is always the first + const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe + const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata + const uint8_t SYNC_CC_IDX = 0; ///< From the sync POV, the CC idx is always the first + const uint32_t TIMEOUT_TO_IDLE_MS = 2000; ///< Timeout in milliseconds for transitioning to IDLE }; } // namespace srsue diff --git a/srsue/hdr/phy/sync_state.h b/srsue/hdr/phy/sync_state.h index ee3b9e4fe..b0979fae2 100644 --- a/srsue/hdr/phy/sync_state.h +++ b/srsue/hdr/phy/sync_state.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,9 @@ #ifndef SRSUE_SYNC_STATE_H #define SRSUE_SYNC_STATE_H +#include +#include + namespace srsue { class sync_state @@ -39,7 +42,7 @@ public: */ state_t run_state() { - std::lock_guard lock(inside); + std::lock_guard lock(mutex); cur_state = next_state; if (state_setting) { state_setting = false; @@ -52,7 +55,7 @@ public: // Called by the main thread at the end of each state to indicate it has finished. void state_exit(bool exit_ok = true) { - std::lock_guard lock(inside); + std::lock_guard lock(mutex); if (cur_state == SFN_SYNC && exit_ok == true) { next_state = CAMPING; } else { @@ -63,10 +66,16 @@ public: } void force_sfn_sync() { - std::lock_guard lock(inside); + std::lock_guard lock(mutex); next_state = SFN_SYNC; } + state_t get_state() + { + std::lock_guard lock(mutex); + return cur_state; + } + /* Functions to be called from outside the STM thread to instruct the STM to switch state. * The functions change the state and wait until it has changed it. * @@ -74,31 +83,63 @@ public: */ void go_idle() { - std::lock_guard lock(outside); // Do not wait when transitioning to IDLE to avoid blocking - go_state_nowait(IDLE); + next_state = IDLE; } void run_cell_search() { - std::lock_guard lock(outside); go_state(CELL_SEARCH); wait_state_run(); wait_state_next(); } void run_sfn_sync() { - std::lock_guard lock(outside); go_state(SFN_SYNC); wait_state_run(); wait_state_next(); } /* Helpers below this */ - bool is_idle() { return cur_state == IDLE; } - bool is_camping() { return cur_state == CAMPING; } + bool is_idle() + { + std::lock_guard lock(mutex); + return cur_state == IDLE; + } + bool is_camping() + { + std::lock_guard lock(mutex); + return cur_state == CAMPING; + } + bool wait_idle(uint32_t timeout_ms) + { + std::unique_lock lock(mutex); + + // Avoid wasting time if the next state will not be IDLE + if (next_state != IDLE) { + return cur_state == IDLE; + } + + // Calculate timeout + std::chrono::system_clock::time_point expire_time = std::chrono::system_clock::now(); + expire_time += std::chrono::milliseconds(timeout_ms); + + // Wait until the state is IDLE + while (cur_state != IDLE) { + std::cv_status cv_status = cvar.wait_until(lock, expire_time); + + // Return if it timeouts + if (cv_status != std::cv_status::timeout) { + return cur_state == IDLE; + } + } + + // Return true if the state is IDLE + return true; + } const char* to_string() { + std::lock_guard lock(mutex); switch (cur_state) { case IDLE: return "IDLE"; @@ -118,7 +159,7 @@ public: private: void go_state(state_t s) { - std::unique_lock ul(inside); + std::unique_lock ul(mutex); next_state = s; state_setting = true; while (state_setting) { @@ -128,7 +169,7 @@ private: void go_state_nowait(state_t s) { - std::unique_lock ul(inside); + std::unique_lock ul(mutex); next_state = s; state_setting = true; } @@ -136,14 +177,14 @@ private: /* Waits until there is a call to set_state() and then run_state(). Returns when run_state() returns */ void wait_state_run() { - std::unique_lock ul(inside); + std::unique_lock ul(mutex); while (state_running) { cvar.wait(ul); } } void wait_state_next() { - std::unique_lock ul(inside); + std::unique_lock ul(mutex); while (cur_state != next_state) { cvar.wait(ul); } @@ -151,10 +192,9 @@ private: bool state_running = false; bool state_setting = false; - state_t cur_state = IDLE; - state_t next_state = IDLE; - std::mutex inside; - std::mutex outside; + std::atomic next_state = {IDLE}; // can be updated from outside (i.e. other thread) + state_t cur_state = IDLE; // will only be accessed when holding the mutex + std::mutex mutex; std::condition_variable cvar; }; diff --git a/srsue/hdr/phy/ta_control.h b/srsue/hdr/phy/ta_control.h index 9d97f736b..a76e26277 100644 --- a/srsue/hdr/phy/ta_control.h +++ b/srsue/hdr/phy/ta_control.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -104,6 +104,19 @@ public: next_base_sec * 1e6f); } + void add_ta_offset(uint32_t ta_offset) + { + std::lock_guard lock(mutex); + + // Assuming numerology 0 + next_base_nta = ta_offset / 64; + + // Update base in seconds + next_base_sec = static_cast(next_base_nta) * SRSRAN_LTE_TS; + + logger.info("PHY: Set TA offset: n_ta_offset: %d, ta_usec: %.1f", next_base_nta, next_base_sec * 1e6f); + } + /** * Increments (delta) the next base time according to time alignment command from a Random Access Response (RAR). * diff --git a/srsue/hdr/phy/ue_lte_phy_base.h b/srsue/hdr/phy/ue_lte_phy_base.h deleted file mode 100644 index 2a3134676..000000000 --- a/srsue/hdr/phy/ue_lte_phy_base.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -/****************************************************************************** - * File: ue_lte_phy_base.h - * Description: Base class for UE LTE PHYs. - *****************************************************************************/ - -#ifndef SRSUE_UE_LTE_PHY_BASE_H -#define SRSUE_UE_LTE_PHY_BASE_H - -#include "srsran/interfaces/radio_interfaces.h" -#include "srsue/hdr/phy/ue_phy_base.h" - -namespace srsue { - -class stack_interface_phy_lte; - -class ue_lte_phy_base : public ue_phy_base, public phy_interface_stack_lte, public srsran::phy_interface_radio -{ -public: - ue_lte_phy_base(){}; - virtual ~ue_lte_phy_base(){}; - - virtual std::string get_type() = 0; - - virtual int init(const phy_args_t& args_) = 0; - virtual int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) = 0; - virtual void stop() = 0; - - virtual void wait_initialize() = 0; - virtual void start_plot() = 0; - - virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0; -}; - -} // namespace srsue - -#endif // SRSUE_UE_LTE_PHY_BASE_H diff --git a/srsue/hdr/phy/ue_nr_phy_base.h b/srsue/hdr/phy/ue_nr_phy_base.h deleted file mode 100644 index 04bde236f..000000000 --- a/srsue/hdr/phy/ue_nr_phy_base.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -/****************************************************************************** - * File: ue_nr_phy_base.h - * Description: Base class for UE NR PHYs. - *****************************************************************************/ - -#ifndef SRSUE_UE_NR_PHY_BASE_H -#define SRSUE_UE_NR_PHY_BASE_H - -#include "srsran/interfaces/radio_interfaces.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include "srsue/hdr/phy/ue_phy_base.h" - -namespace srsue { - -class ue_nr_phy_base : public phy_interface_stack_nr -{ -public: - ue_nr_phy_base(){}; - virtual ~ue_nr_phy_base() {} - - virtual std::string get_type() = 0; - - virtual int init(const phy_args_t& args_) = 0; - virtual int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) = 0; - virtual void stop() = 0; - - virtual void set_earfcn(std::vector earfcns) = 0; - - virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0; -}; - -} // namespace srsue - -#endif // SRSUE_UE_NR_PHY_BASE_H \ No newline at end of file diff --git a/srsue/hdr/phy/ue_phy_base.h b/srsue/hdr/phy/ue_phy_base.h index 23f6fb605..6eab6424c 100644 --- a/srsue/hdr/phy/ue_phy_base.h +++ b/srsue/hdr/phy/ue_phy_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -40,11 +40,10 @@ public: virtual std::string get_type() = 0; - virtual int init(const phy_args_t& args_) = 0; - virtual void stop() = 0; virtual void wait_initialize() = 0; + virtual bool is_initialized() = 0; virtual void start_plot() = 0; virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0; diff --git a/srsue/hdr/phy/vnf_phy_nr.h b/srsue/hdr/phy/vnf_phy_nr.h deleted file mode 100644 index 8f4512ef4..000000000 --- a/srsue/hdr/phy/vnf_phy_nr.h +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#ifndef SRSUE_VNF_PHY_NR_H -#define SRSUE_VNF_PHY_NR_H - -#include "srsenb/hdr/phy/phy_common.h" -#include "srsran/common/basic_vnf.h" -#include "srsran/interfaces/enb_metrics_interface.h" -#include "srsran/interfaces/radio_interfaces.h" -#include "srsran/interfaces/ue_interfaces.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include "srsue/hdr/phy/ue_nr_phy_base.h" - -namespace srsue { - -class vnf_phy_nr : public srsue::ue_phy_base, public srsue::phy_interface_stack_nr -{ -public: - vnf_phy_nr() = default; - ~vnf_phy_nr(); - - int init(const srsue::phy_args_t& args, srsue::stack_interface_phy_nr* stack_); - - int init(const srsue::phy_args_t& args_) override; - - void set_earfcn(std::vector earfcns); - - void stop() override; - - void wait_initialize() override; - void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) override; - - std::string get_type() override { return "vnf_nr"; }; - - void start_plot() override; - - // RRC interface - bool set_config(const srsran::phy_cfg_nr_t& cfg) override; - - // MAC interface - int tx_request(const tx_request_t& request) override; - int set_ul_grant(std::array, uint16_t rnti, srsran_rnti_type_t rnti_type) override - { - return SRSRAN_SUCCESS; - }; - void send_prach(const uint32_t preamble_idx, - const int prach_occasion, - const float target_power_dbm, - const float ta_base_sec = 0.0f) override{}; - bool has_valid_sr_resource(uint32_t sr_id) override; - void clear_pending_grants() override; - -private: - std::unique_ptr vnf; - - srsue::stack_interface_phy_nr* stack = nullptr; - - bool initialized = false; -}; - -} // namespace srsue - -#endif // SRSUE_VNF_PHY_NR_H diff --git a/srsue/hdr/stack/mac/demux.h b/srsue/hdr/stack/mac/demux.h index 3f3ba93c6..ad49fb302 100644 --- a/srsue/hdr/stack/mac/demux.h +++ b/srsue/hdr/stack/mac/demux.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -65,7 +65,7 @@ public: bool get_uecrid_successful(); - void process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel); + void process_pdu(uint8_t* pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel, int ul_nof_prbs); void mch_start_rx(uint32_t lcid); private: diff --git a/srsue/hdr/stack/mac/dl_harq.h b/srsue/hdr/stack/mac/dl_harq.h index 1611863be..849396a2e 100644 --- a/srsue/hdr/stack/mac/dl_harq.h +++ b/srsue/hdr/stack/mac/dl_harq.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,6 +26,7 @@ #include "dl_sps.h" #include "srsran/common/mac_pcap.h" #include "srsran/common/timers.h" +#include "srsue/hdr/stack/mac_common/mac_common.h" /* Downlink HARQ entity as defined in 5.3.2 of 36.321 */ @@ -36,7 +37,7 @@ class dl_harq_entity public: dl_harq_entity(uint8_t cc_idx_); - bool init(mac_interface_rrc::ue_rnti_t* rntis, demux* demux_unit); + bool init(ue_rnti* rntis, demux* demux_unit); void reset(); void start_pcap(srsran::mac_pcap* pcap_); @@ -47,6 +48,7 @@ public: void set_si_window_start(int si_window_start); float get_average_retx(); + void set_average_retx(uint32_t n_retx); private: class dl_harq_process @@ -72,7 +74,7 @@ private: ~dl_tb_process(); bool init(int pid, dl_harq_entity* parent, uint32_t tb_idx); - void reset(bool lock = true); + void reset(); void reset_ndi(); void new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, mac_interface_phy_lte::tb_action_dl_t* action); @@ -82,6 +84,9 @@ private: // Determine if it's a new transmission 5.3.2.2 bool calc_is_new_transmission(mac_interface_phy_lte::mac_grant_dl_t grant); + // Internal function to reset process, caller must hold the mutex + void reset_nolock(); + std::mutex mutex; bool is_initiated; @@ -113,14 +118,16 @@ private: dl_sps dl_sps_assig; - std::vector proc; - dl_harq_process bcch_proc; - demux* demux_unit = nullptr; - srslog::basic_logger& logger; - srsran::mac_pcap* pcap = nullptr; - mac_interface_rrc::ue_rnti_t* rntis = nullptr; - uint16_t last_temporal_crnti = 0; - int si_window_start = 0; + std::vector proc; + dl_harq_process bcch_proc; + demux* demux_unit = nullptr; + srslog::basic_logger& logger; + srsran::mac_pcap* pcap = nullptr; + ue_rnti* rntis = nullptr; + uint16_t last_temporal_crnti = 0; + int si_window_start = 0; + + std::mutex retx_cnt_mutex = {}; float average_retx = 0.0; uint64_t nof_pkts = 0; diff --git a/srsue/hdr/stack/mac/dl_sps.h b/srsue/hdr/stack/mac/dl_sps.h index f5361c1fa..1e210d3a8 100644 --- a/srsue/hdr/stack/mac/dl_sps.h +++ b/srsue/hdr/stack/mac/dl_sps.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/mac/mac.h b/srsue/hdr/stack/mac/mac.h index 1a6ca17eb..00a2af861 100644 --- a/srsue/hdr/stack/mac/mac.h +++ b/srsue/hdr/stack/mac/mac.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -59,13 +59,12 @@ public: /* see mac_interface.h for comments */ void new_grant_ul(uint32_t cc_idx, mac_grant_ul_t grant, tb_action_ul_t* action); void new_grant_dl(uint32_t cc_idx, mac_grant_dl_t grant, tb_action_dl_t* action); - void new_mch_dl(const srsran_pdsch_grant_t& phy_grant, tb_action_dl_t* action); void tb_decoded(uint32_t cc_idx, mac_grant_dl_t grant, bool ack[SRSRAN_MAX_CODEWORDS]); void bch_decoded_ok(uint32_t cc_idx, uint8_t* payload, uint32_t len); uint16_t get_dl_sched_rnti(uint32_t tti); uint16_t get_ul_sched_rnti(uint32_t tti); - void mch_decoded(uint32_t len, bool crc); + void mch_decoded(uint32_t len, bool crc, uint8_t* payload); void process_mch_pdu(uint32_t len); void set_mbsfn_config(uint32_t nof_mbsfn_services); @@ -93,8 +92,8 @@ public: void set_rach_ded_cfg(uint32_t preamble_index, uint32_t prach_mask); - void get_rntis(ue_rnti_t* rntis); - void set_ho_rnti(uint16_t crnti, uint16_t target_pci); + uint16_t get_crnti(); + void set_ho_rnti(uint16_t crnti, uint16_t target_pci); /*********** interface for stack ******************/ void process_pdus(); @@ -109,8 +108,6 @@ public: private: void clear_rntis(); - bool is_in_window(uint32_t tti, int* start, int* len); - // Interaction with PHY phy_interface_mac_lte* phy_h = nullptr; rlc_interface_mac* rlc_h = nullptr; @@ -119,13 +116,11 @@ private: srslog::basic_logger& logger; mac_interface_phy_lte::mac_phy_cfg_mbsfn_t phy_mbsfn_cfg = {}; - // RNTI search window scheduling - int si_window_length = -1, si_window_start = -1; - int ra_window_length = -1, ra_window_start = -1; - int p_window_start = -1; + // Control scheduling for SI/RA/P RNTIs + rnti_window_safe si_window, ra_window, p_window; // UE-specific RNTIs - ue_rnti_t uernti; + ue_rnti uernti; /* Multiplexing/Demultiplexing Units */ mux mux_unit; @@ -144,12 +139,12 @@ private: /* Buffers for PCH reception (not included in DL HARQ) */ const static uint32_t pch_payload_buffer_sz = 8 * 1024; - srsran_softbuffer_rx_t pch_softbuffer; + srsran_softbuffer_rx_t pch_softbuffer = {}; uint8_t pch_payload_buffer[pch_payload_buffer_sz]; /* Buffers for MCH reception (not included in DL HARQ) */ const static uint32_t mch_payload_buffer_sz = SRSRAN_MAX_BUFFER_SIZE_BYTES; - srsran_softbuffer_rx_t mch_softbuffer; + srsran_softbuffer_rx_t mch_softbuffer = {}; uint8_t mch_payload_buffer[mch_payload_buffer_sz]; srsran::mch_pdu mch_msg; @@ -162,12 +157,13 @@ private: srsran::task_multiqueue::queue_handle stack_task_dispatch_queue; // pointer to MAC PCAP object - srsran::mac_pcap* pcap = nullptr; - bool is_first_ul_grant = false; + srsran::mac_pcap* pcap = nullptr; + std::atomic is_first_ul_grant{false}; + std::mutex metrics_mutex = {}; mac_metrics_t metrics[SRSRAN_MAX_CARRIERS] = {}; - bool initialized = false; + std::atomic initialized = {false}; const uint8_t PCELL_CC_IDX = 0; }; diff --git a/srsue/hdr/stack/mac/mac_metrics.h b/srsue/hdr/stack/mac/mac_metrics.h index c82b6f018..1716e7ffa 100644 --- a/srsue/hdr/stack/mac/mac_metrics.h +++ b/srsue/hdr/stack/mac/mac_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/mac/mux.h b/srsue/hdr/stack/mac/mux.h index 203d76949..e24181e1b 100644 --- a/srsue/hdr/stack/mac/mux.h +++ b/srsue/hdr/stack/mac/mux.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,6 +66,7 @@ public: void print_logical_channel_state(const std::string& info); private: + uint8_t* pdu_get_nolock(srsran::byte_buffer_t* payload, uint32_t pdu_sz); bool pdu_move_to_msg3(uint32_t pdu_sz); uint32_t allocate_sdu(uint32_t lcid, srsran::sch_pdu* pdu, int max_sdu_sz); bool sched_sdu(srsran::logical_channel_config_t* ch, int* sdu_space, int max_sdu_sz); diff --git a/srsue/hdr/stack/mac/proc.h b/srsue/hdr/stack/mac/proc.h index d46665ec7..540909b06 100644 --- a/srsue/hdr/stack/mac/proc.h +++ b/srsue/hdr/stack/mac/proc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/mac/proc_bsr.h b/srsue/hdr/stack/mac/proc_bsr.h index 94c6960b6..c7ac19ea6 100644 --- a/srsue/hdr/stack/mac/proc_bsr.h +++ b/srsue/hdr/stack/mac/proc_bsr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/mac/proc_phr.h b/srsue/hdr/stack/mac/proc_phr.h index bc499c3b0..4e0b0f3b1 100644 --- a/srsue/hdr/stack/mac/proc_phr.h +++ b/srsue/hdr/stack/mac/proc_phr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -61,6 +61,8 @@ private: srsran::timer_handler::unique_timer timer_periodic; srsran::timer_handler::unique_timer timer_prohibit; + + std::mutex mutex; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac/proc_ra.h b/srsue/hdr/stack/mac/proc_ra.h index 1a44ebad6..26c1c359e 100644 --- a/srsue/hdr/stack/mac/proc_ra.h +++ b/srsue/hdr/stack/mac/proc_ra.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -31,6 +31,7 @@ #include "srsran/common/mac_pcap.h" #include "srsran/common/timers.h" #include "srsran/mac/pdu.h" +#include "srsue/hdr/stack/mac_common/mac_common.h" /* Random access procedure as specified in Section 5.1 of 36.321 */ @@ -45,7 +46,7 @@ public: void init(phy_interface_mac_lte* phy_h, rrc_interface_mac* rrc_, - mac_interface_rrc::ue_rnti_t* rntis, + ue_rnti* rntis, srsran::timer_handler::unique_timer* time_alignment_timer_, mux* mux_unit, srsran::ext_task_sched_handle* task_sched_); @@ -59,7 +60,7 @@ public: void start_mac_order(uint32_t msg_len_bits = 56); void step(uint32_t tti); - void update_rar_window(int& rar_window_start, int& rar_window_length); + void update_rar_window(rnti_window_safe& ra_window); bool is_contention_resolution(); void harq_retx(); void harq_max_retx(); @@ -86,7 +87,7 @@ private: void response_error(); void complete(); - bool contention_resolution_id_received_unsafe(uint64_t rx_contention_id); + bool contention_resolution_id_received_nolock(uint64_t rx_contention_id); // Buffer to receive RAR PDU static const uint32_t MAX_RAR_PDU_LEN = 2048; @@ -149,14 +150,14 @@ private: srsran::timer_handler::unique_timer* time_alignment_timer = nullptr; srsran::timer_handler::unique_timer contention_resolution_timer; - mac_interface_rrc::ue_rnti_t* rntis = nullptr; + ue_rnti* rntis = nullptr; std::atomic transmitted_contention_id = {0}; std::atomic transmitted_crnti = {0}; - bool started_by_pdcch = false; - uint32_t rar_grant_nbytes = 0; - bool rar_received = false; + bool started_by_pdcch = false; + uint32_t rar_grant_nbytes = 0; + std::atomic rar_received = {false}; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac/proc_sr.h b/srsue/hdr/stack/mac/proc_sr.h index 7d179aaa9..a396ac728 100644 --- a/srsue/hdr/stack/mac/proc_sr.h +++ b/srsue/hdr/stack/mac/proc_sr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,6 +24,7 @@ #include "srsran/interfaces/ue_mac_interfaces.h" #include "srsran/srslog/srslog.h" +#include #include /* Scheduling Request procedure as defined in 5.4.4 of 36.321 */ @@ -57,6 +58,9 @@ private: phy_interface_mac_lte* phy_h; srslog::basic_logger& logger; + // Protects access to is_pending_sr, which can be accessed by PHY worker + std::mutex mutex = {}; + bool initiated; }; diff --git a/srsue/hdr/stack/mac/ul_harq.h b/srsue/hdr/stack/mac/ul_harq.h index c21151d52..5de11f294 100644 --- a/srsue/hdr/stack/mac/ul_harq.h +++ b/srsue/hdr/stack/mac/ul_harq.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,7 +38,7 @@ class ul_harq_entity public: ul_harq_entity(const uint8_t cc_idx_); - bool init(mac_interface_rrc_common::ue_rnti_t* rntis_, ra_proc* ra_proc_h_, mux* mux_unit_); + bool init(ue_rnti* rntis_, ra_proc* ra_proc_h_, mux* mux_unit_); void reset(); void reset_ndi(); @@ -75,14 +75,63 @@ private: void new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant, mac_interface_phy_lte::tb_action_ul_t* action); private: - mac_interface_phy_lte::mac_grant_ul_t cur_grant; + /// Thread safe wrapper for a mac_grant_ul_t object. + class lockable_grant + { + mac_interface_phy_lte::mac_grant_ul_t grant = {}; + mutable std::mutex mutex; - uint32_t pid; - uint32_t current_tx_nb; - uint32_t current_irv; - bool harq_feedback; - bool is_grant_configured; - bool is_initiated; + public: + void set(const mac_interface_phy_lte::mac_grant_ul_t& other) + { + std::lock_guard lock(mutex); + grant = other; + } + + void reset() + { + std::lock_guard lock(mutex); + grant = {}; + } + + void set_ndi(bool ndi) + { + std::lock_guard lock(mutex); + grant.tb.ndi = ndi; + } + + bool get_ndi() const + { + std::lock_guard lock(mutex); + return grant.tb.ndi; + } + + uint32_t get_tbs() const + { + std::lock_guard lock(mutex); + return grant.tb.tbs; + } + + int get_rv() const + { + std::lock_guard lock(mutex); + return grant.tb.rv; + } + + void set_rv(int rv) + { + std::lock_guard lock(mutex); + grant.tb.rv = rv; + } + }; + lockable_grant cur_grant; + + uint32_t pid; + std::atomic current_tx_nb = {0}; + std::atomic current_irv = {0}; + std::atomic harq_feedback = {false}; + std::atomic is_grant_configured = {false}; + bool is_initiated; srslog::basic_logger& logger; ul_harq_entity* harq_entity; @@ -105,12 +154,14 @@ private: srsran::mac_pcap* pcap = nullptr; srslog::basic_logger& logger; - mac_interface_rrc_common::ue_rnti_t* rntis = nullptr; - srsran::ul_harq_cfg_t harq_cfg = {}; + ue_rnti* rntis = nullptr; - float average_retx = 0.0; - uint64_t nof_pkts = 0; - ra_proc* ra_procedure = nullptr; + srsran::ul_harq_cfg_t harq_cfg = {}; + std::mutex config_mutex; + + std::atomic average_retx{0}; + std::atomic nof_pkts{0}; + ra_proc* ra_procedure = nullptr; uint8_t cc_idx = 0; }; diff --git a/srsue/hdr/stack/mac/ul_sps.h b/srsue/hdr/stack/mac/ul_sps.h index 643a369f3..d2717b479 100644 --- a/srsue/hdr/stack/mac/ul_sps.h +++ b/srsue/hdr/stack/mac/ul_sps.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/mac_common/mac_common.h b/srsue/hdr/stack/mac_common/mac_common.h index 25544689f..f1956ad6b 100644 --- a/srsue/hdr/stack/mac_common/mac_common.h +++ b/srsue/hdr/stack/mac_common/mac_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,8 +23,10 @@ #define SRSUE_MAC_COMMON_H #include "srsran/common/string_helpers.h" +#include "srsran/phy/common/phy_common.h" #include "srsran/srslog/srslog.h" #include +#include /** * @brief Common definitions/interfaces between LTE/NR MAC components @@ -34,6 +36,161 @@ */ namespace srsue { +// Helper class to protect access to RNTIs +class ue_rnti +{ +public: + void reset() + { + std::lock_guard lock(mutex); + crnti = 0; + rar_rnti = 0; + temp_rnti = 0; + tpc_rnti = 0; + sps_rnti = 0; + contention_id = 0; + } + uint16_t get_crnti() + { + std::lock_guard lock(mutex); + return crnti; + } + uint16_t get_rar_rnti() + { + std::lock_guard lock(mutex); + return rar_rnti; + } + uint16_t get_temp_rnti() + { + std::lock_guard lock(mutex); + return temp_rnti; + } + uint16_t get_sps_rnti() + { + std::lock_guard lock(mutex); + return sps_rnti; + } + uint64_t get_contention_id() + { + std::lock_guard lock(mutex); + return contention_id; + } + void set_crnti(uint16_t rnti) + { + std::lock_guard lock(mutex); + crnti = rnti; + } + void set_rar_rnti(uint16_t rnti) + { + std::lock_guard lock(mutex); + rar_rnti = rnti; + } + void set_temp_rnti(uint16_t rnti) + { + std::lock_guard lock(mutex); + temp_rnti = rnti; + } + void set_contention_id(uint64_t id) + { + std::lock_guard lock(mutex); + contention_id = id; + } + void set_crnti_to_temp() + { + std::lock_guard lock(mutex); + crnti = temp_rnti; + } + void clear_temp_rnti() + { + std::lock_guard lock(mutex); + temp_rnti = SRSRAN_INVALID_RNTI; + } + void clear_rar_rnti() + { + std::lock_guard lock(mutex); + rar_rnti = SRSRAN_INVALID_RNTI; + } + void clear_crnti() + { + std::lock_guard lock(mutex); + crnti = SRSRAN_INVALID_RNTI; + } + +private: + std::mutex mutex; + uint16_t crnti = 0; + uint16_t rar_rnti = 0; + uint16_t temp_rnti = 0; + uint16_t tpc_rnti = 0; + uint16_t sps_rnti = 0; + uint64_t contention_id = 0; +}; + +// Helper class to control RNTI search windows in a protected manner +class rnti_window_safe +{ +public: + void reset() + { + std::lock_guard lock(mutex); + length = -1; + start = -1; + } + void set(int length_, int start_) + { + std::lock_guard lock(mutex); + length = length_; + start = start_; + } + int get_length() + { + std::lock_guard lock(mutex); + return length; + } + int get_start() + { + std::lock_guard lock(mutex); + return start; + } + bool is_set() + { + std::lock_guard lock(mutex); + return start > 0; + } + bool is_in_window(int tti) + { + std::lock_guard lock(mutex); + if (start == 0 || length == 0) { + return false; + } + if ((int)srsran_tti_interval(tti, start) < length + 5) { + if (tti > start) { + if (tti <= start + length) { + return true; + } else { + start = 0; + length = 0; + return false; + } + } else { + if (tti <= (start + length) % 10240) { + return true; + } else { + start = 0; + length = 0; + return false; + } + } + } + return false; + } + +private: + int length = -1; + int start = -1; + std::mutex mutex; +}; + // BSR trigger are common between LTE and NR typedef enum { NONE, REGULAR, PADDING, PERIODIC } bsr_trigger_type_t; char* bsr_trigger_type_tostring(bsr_trigger_type_t type); diff --git a/srsue/hdr/stack/mac_common/mux_base.h b/srsue/hdr/stack/mac_common/mux_base.h index f740c808d..fb6161a8e 100644 --- a/srsue/hdr/stack/mac_common/mux_base.h +++ b/srsue/hdr/stack/mac_common/mux_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/mac_nr/demux_nr.h b/srsue/hdr/stack/mac_nr/demux_nr.h index f77ca1d8a..793dacfb0 100644 --- a/srsue/hdr/stack/mac_nr/demux_nr.h +++ b/srsue/hdr/stack/mac_nr/demux_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -24,6 +24,7 @@ #include "mac_nr_interfaces.h" #include "srsran/common/block_queue.h" +#include "srsran/interfaces/ue_nr_interfaces.h" #include "srsran/interfaces/ue_rlc_interfaces.h" namespace srsue { @@ -44,24 +45,32 @@ public: demux_nr(srslog::basic_logger& logger_); ~demux_nr(); - int32_t init(rlc_interface_mac* rlc_); + int32_t init(rlc_interface_mac* rlc_, phy_interface_mac_nr* phy_); void process_pdus(); /// Called by MAC to process received PDUs // HARQ interface - void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti); + void push_bcch(srsran::unique_byte_buffer_t pdu); + void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti); + void push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tti); + uint64_t get_received_crueid(); private: // internal helpers - void handle_pdu(srsran::unique_byte_buffer_t pdu); + void handle_pdu(srsran::mac_sch_pdu_nr& pdu_buffer, srsran::unique_byte_buffer_t pdu); srslog::basic_logger& logger; rlc_interface_mac* rlc = nullptr; + phy_interface_mac_nr* phy = nullptr; - ///< currently only DCH PDUs supported (add BCH, PCH, etc) + uint64_t received_crueid = 0; + + ///< currently only DCH & BCH PDUs supported (add PCH, etc) srsran::block_queue pdu_queue; + srsran::block_queue bcch_queue; srsran::mac_sch_pdu_nr rx_pdu; + srsran::mac_sch_pdu_nr rx_pdu_tcrnti; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/dl_harq_nr.h b/srsue/hdr/stack/mac_nr/dl_harq_nr.h index ecbbf48d2..1f2025053 100644 --- a/srsue/hdr/stack/mac_nr/dl_harq_nr.h +++ b/srsue/hdr/stack/mac_nr/dl_harq_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -51,7 +51,7 @@ public: ~dl_harq_entity_nr(); int32_t set_config(const srsran::dl_harq_cfg_nr_t& cfg_); - void reset(); + void reset(); /// PHY->MAC interface for DL processes void new_grant_dl(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_t* action); @@ -78,7 +78,7 @@ private: uint8_t get_ndi(); void - new_grant_dl(const mac_nr_grant_dl_t& grant, const bool& ndi_toggled, mac_interface_phy_nr::tb_action_dl_t* action); + new_grant_dl(const mac_nr_grant_dl_t& grant, const bool& ndi_toggled, mac_interface_phy_nr::tb_action_dl_t* action); void tb_decoded(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_result_t result); private: @@ -105,7 +105,8 @@ private: srslog::basic_logger& logger; uint16_t last_temporal_crnti = SRSRAN_INVALID_RNTI; dl_harq_metrics_t metrics = {}; - uint8_t cc_idx = 0; + std::mutex metrics_mutex; + uint8_t cc_idx = 0; pthread_rwlock_t rwlock; }; diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index 395911740..16cb7bbab 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -57,7 +57,6 @@ public: int init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy_, rlc_interface_mac* rlc_, rrc_interface_mac* rrc_); void stop(); - void reset(); void run_tti(const uint32_t tti); void start_pcap(srsran::mac_pcap* pcap_); @@ -84,10 +83,11 @@ public: void get_metrics(mac_metrics_t* metrics); /// Interface for RRC (RRC -> MAC) + void reset(); int setup_lcid(const srsran::logical_channel_config_t& config); int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg); int set_config(const srsran::sr_cfg_nr_t& sr_cfg); - void set_config(const srsran::rach_nr_cfg_t& rach_cfg); + void set_config(const srsran::rach_cfg_nr_t& rach_cfg_nr); int set_config(const srsran::dl_harq_cfg_nr_t& dl_hrq_cfg); void set_contention_id(const uint64_t ue_identity); bool set_crnti(const uint16_t crnti); @@ -97,16 +97,19 @@ public: void start_ra_procedure(); /// Interface for internal procedures (RA, MUX, HARQ) - uint64_t get_contention_id(); + bool received_contention_id(uint64_t rx_contention_id); uint16_t get_crnti(); uint16_t get_temp_crnti(); uint16_t get_csrnti() { return SRSRAN_INVALID_RNTI; }; // SPS not supported + void set_temp_crnti(uint16_t temp_crnti); + void set_crnti_to_temp(); /// procedure sr nr interface void start_ra() { proc_ra.start_by_mac(); } /// Interface for MUX srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr(); + void set_padding_bytes(uint32_t nof_bytes); void msg3_flush() { mux.msg3_flush(); } bool msg3_is_transmitted() { return mux.msg3_is_transmitted(); } @@ -115,7 +118,9 @@ public: bool msg3_is_empty() { return mux.msg3_is_empty(); } /// RRC - void rrc_ra_problem() { rrc->ra_problem(); } + void rrc_ra_problem(); + void rrc_ra_completed(); + void bcch_search(bool enabled); /// stack interface void process_pdus(); @@ -136,6 +141,7 @@ private: bool is_paging_opportunity(); bool has_crnti(); + bool has_temp_crnti(); bool is_valid_crnti(const uint16_t crnti); std::vector logical_channels; // stores the raw configs provide by upper layers @@ -156,8 +162,11 @@ private: std::atomic started = {false}; - uint16_t c_rnti = SRSRAN_INVALID_RNTI; - uint64_t contention_id = 0; + // Boolean to determine if need to decode SI-RNTI + std::atomic search_bcch = {false}; + + ue_rnti rntis; // thread-safe helper to store RNTIs, contention ID, etc + bool contention_res_successful; std::array metrics = {}; diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index b7df32290..52cc39dce 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,9 +33,10 @@ class mac_interface_proc_ra_nr { public: // Functions for identity handling, e.g., contention id and c-rnti - virtual uint64_t get_contention_id() = 0; - virtual uint16_t get_crnti() = 0; - virtual bool set_crnti(uint16_t c_rnti) = 0; + virtual uint16_t get_crnti() = 0; + virtual bool set_crnti(uint16_t c_rnti) = 0; + virtual void set_temp_crnti(uint16_t c_rnti) = 0; + virtual void set_crnti_to_temp() = 0; // Functions for msg3 manipulation which shall be transparent to the procedure virtual bool msg3_is_transmitted() = 0; @@ -44,7 +45,8 @@ public: virtual bool msg3_is_empty() = 0; // RRC functions - virtual void rrc_ra_problem() = 0; + virtual void rrc_ra_problem() = 0; + virtual void rrc_ra_completed() = 0; }; /** @@ -63,11 +65,17 @@ public: class mac_interface_mux_nr { public: + // MUX can ask MAC if a C-RNTI is present + virtual bool has_crnti() = 0; + // MUX can query MAC for current C-RNTI for Msg3 transmission virtual uint16_t get_crnti() = 0; // MUX queries MAC to return LCG state for SBSR virtual srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr() = 0; + + // MUX informs MAC about padding bytes so BSR proc can decide whether to create BSR or not + virtual void set_padding_bytes(uint32_t nof_bytes) = 0; }; /** @@ -82,6 +90,9 @@ public: // MAC also provides Temp C-RNTI (through RA proc) virtual uint16_t get_temp_crnti() = 0; + // HARQ can query MAC for current C-RNTI + virtual bool received_contention_id(uint64_t rx_contention_id) = 0; + // MAC provides the Currently Scheduled RNTI (for SPS) virtual uint16_t get_csrnti() = 0; }; @@ -93,7 +104,10 @@ class demux_interface_harq_nr { public: /// Inform demux unit about a newly decoded TB. - virtual void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti) = 0; + virtual void push_bcch(srsran::unique_byte_buffer_t pdu) = 0; + virtual void push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti) = 0; + virtual void push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tti) = 0; + virtual uint64_t get_received_crueid() = 0; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/mux_nr.h b/srsue/hdr/stack/mac_nr/mux_nr.h index 7f68bd617..1fc15ea42 100644 --- a/srsue/hdr/stack/mac_nr/mux_nr.h +++ b/srsue/hdr/stack/mac_nr/mux_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,7 +39,7 @@ class mux_nr final : mux_base, public mux_interface_bsr_nr public: explicit mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger); ~mux_nr(){}; - void reset(); + void reset(); int32_t init(rlc_interface_mac* rlc_); void msg3_flush(); @@ -56,15 +56,15 @@ public: srsran::unique_byte_buffer_t get_pdu(uint32_t max_pdu_len); // Interface for BSR procedure - void generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format); + void generate_bsr_mac_ce(const srsran::bsr_format_nr_t& format); private: // internal helper methods // ctor configured members - mac_interface_mux_nr& mac; - rlc_interface_mac* rlc = nullptr; - srslog::basic_logger& logger; + mac_interface_mux_nr& mac; + rlc_interface_mac* rlc = nullptr; + srslog::basic_logger& logger; // Msg3 related srsran::unique_byte_buffer_t msg3_buff = nullptr; @@ -78,7 +78,8 @@ private: srsran::mac_sch_pdu_nr tx_pdu; /// single MAC PDU for packing - enum { no_bsr, sbsr_ce, lbsr_ce } add_bsr_ce = no_bsr; /// BSR procedure requests MUX to add a BSR CE + enum bsr_req_t { no_bsr, sbsr_ce, lbsr_ce }; + std::atomic add_bsr_ce = {bsr_req_t::no_bsr}; /// BSR procedure requests MUX to add a BSR CE // Mutex for exclusive access std::mutex mutex; diff --git a/srsue/hdr/stack/mac_nr/proc_bsr_nr.h b/srsue/hdr/stack/mac_nr/proc_bsr_nr.h index 02eb438e3..2265eca82 100644 --- a/srsue/hdr/stack/mac_nr/proc_bsr_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_bsr_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,6 +27,7 @@ #include "proc_sr_nr.h" #include "srsran/common/task_scheduler.h" +#include "srsran/mac/bsr_nr.h" #include "srsran/mac/mac_sch_pdu_nr.h" #include "srsran/srslog/srslog.h" #include "srsue/hdr/stack/mac_common/mac_common.h" @@ -41,9 +42,6 @@ class rlc_interface_mac; class bsr_interface_mux_nr { public: - // TS 38.321 Sec 6.1.3.1 - typedef enum { SHORT_BSR, LONG_BSR, SHORT_TRUNC_BSR, LONG_TRUNC_BSR } bsr_format_nr_t; - /// MUX calls BSR to receive the buffer state of a single LCG. virtual srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr() = 0; }; @@ -52,7 +50,7 @@ class mux_interface_bsr_nr { public: /// Inform MUX unit to that a BSR needs to be generated in the next UL transmission. - virtual void generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format) = 0; + virtual void generate_bsr_mac_ce(const srsran::bsr_format_nr_t& format) = 0; }; /** @@ -80,7 +78,7 @@ public: /// MUX interface for BSR generation srsran::mac_sch_subpdu_nr::lcg_bsr_t generate_sbsr(); - bool generate_padding_bsr(uint32_t nof_padding_bytes); + void set_padding_bytes(uint32_t nof_bytes); private: const static int QUEUE_STATUS_PERIOD_MS = 1000; @@ -110,7 +108,7 @@ private: bool check_new_data(const mac_buffer_states_t& new_buffer_state); bool check_any_channel(); - uint8_t buff_size_bytes_to_field(uint32_t buffer_size, bsr_format_nr_t format); + uint8_t buff_size_bytes_to_field(uint32_t buffer_size, srsran::bsr_format_nr_t format); uint32_t find_max_priority_lcg_with_data(); diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h index 65451c381..55a43adae 100644 --- a/srsue/hdr/stack/mac_nr/proc_ra_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_ra_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -41,14 +41,16 @@ public: ~proc_ra_nr(){}; void init(phy_interface_mac_nr* phy_h_, srsran::ext_task_sched_handle* task_sched_); - void set_config(const srsran::rach_nr_cfg_t& rach_cfg); + void set_config(const srsran::rach_cfg_nr_t& rach_cfg_nr); bool is_contention_resolution(); - bool is_rar_opportunity(uint32_t tti); - bool has_rar_rnti(); + bool is_rar_opportunity(uint32_t tti); + bool has_rar_rnti(); uint16_t get_rar_rnti(); bool has_temp_crnti(); uint16_t get_temp_crnti(); + void set_crnti_to_temp(); + void received_contention_resolution(bool is_successful); // PHY interfaces void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id); @@ -70,11 +72,11 @@ private: srsran::ext_task_sched_handle* task_sched = nullptr; srsran::task_multiqueue::queue_handle task_queue; - int ra_window_length = -1, ra_window_start = -1; - uint16_t rar_rnti = SRSRAN_INVALID_RNTI; - uint16_t temp_crnti = SRSRAN_INVALID_RNTI; + int ra_window_length = -1, ra_window_start = -1; + uint16_t rar_rnti = SRSRAN_INVALID_RNTI; + std::mutex mutex; - srsran::rach_nr_cfg_t rach_cfg = {}; + srsran::rach_cfg_nr_t rach_cfg = {}; bool configured = false; enum ra_state_t { @@ -98,18 +100,18 @@ private: srsran::timer_handler::unique_timer backoff_timer; // 38.321 5.1.1 Variables - uint32_t preamble_index = 0; - uint32_t preamble_transmission_counter = 0; + uint32_t preamble_index = 0; + uint32_t preamble_transmission_counter = 0; uint32_t preamble_backoff = 0; // in ms - uint32_t preamble_power_ramping_step = 0; - int preamble_received_target_power = 0; - uint32_t scaling_factor_bi = 0; + uint32_t preamble_power_ramping_step = 0; + int preamble_received_target_power = 0; + uint32_t scaling_factor_bi = 0; // uint32_t temporary_c_rnti; uint32_t power_offset_2step_ra = 0; // not explicty mentioned uint32_t preambleTransMax = 0; - uint32_t prach_occasion = 0; + uint32_t prach_occasion = 0; uint32_t current_ta = 0; void timer_expired(uint32_t timer_id); @@ -118,9 +120,8 @@ private: void ra_resource_selection(); void ra_preamble_transmission(); void ra_response_reception(const mac_interface_phy_nr::tb_action_dl_result_t& tb); - void ra_contention_resolution(); - void ra_contention_resolution(uint64_t rx_contention_id); - void ra_completion(); + void ra_contention_resolution(bool received_con_res_matches_ue_id); + void ra_completion(); void ra_error(); }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/proc_sr_nr.h b/srsue/hdr/stack/mac_nr/proc_sr_nr.h index 14456b8e8..e2f4ba6a3 100644 --- a/srsue/hdr/stack/mac_nr/proc_sr_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_sr_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,10 +22,11 @@ #ifndef SRSUE_PROC_SR_NR_H #define SRSUE_PROC_SR_NR_H -#include "srsue/hdr/stack/mac_nr/mac_nr_interfaces.h" #include "srsran/interfaces/ue_mac_interfaces.h" #include "srsran/interfaces/ue_nr_interfaces.h" #include "srsran/srslog/srslog.h" +#include "srsue/hdr/stack/mac_nr/mac_nr_interfaces.h" +#include #include /// Scheduling Request procedure as defined in 5.4.4 of 38.321 @@ -48,6 +49,7 @@ public: bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx); private: + void reset_nolock(); int sr_counter = 0; bool is_pending_sr = false; @@ -58,7 +60,8 @@ private: phy_interface_mac_nr* phy = nullptr; srslog::basic_logger& logger; - bool initiated = false; + bool initiated = false; + std::mutex mutex; }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/ul_harq_nr.h b/srsue/hdr/stack/mac_nr/ul_harq_nr.h index 58631029a..d049c00b2 100644 --- a/srsue/hdr/stack/mac_nr/ul_harq_nr.h +++ b/srsue/hdr/stack/mac_nr/ul_harq_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -63,9 +63,9 @@ private: ul_harq_process_nr(); ~ul_harq_process_nr(); - bool init(uint32_t pid_, ul_harq_entity_nr* entity_); - void reset(); - void reset_ndi(); + bool init(uint32_t pid_, ul_harq_entity_nr* entity_); + void reset(); + void reset_ndi(); uint8_t get_ndi(); bool has_grant(); @@ -84,7 +84,7 @@ private: mac_interface_phy_nr::tb_action_ul_t* action); private: - mac_interface_phy_nr::mac_nr_grant_ul_t current_grant = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t current_grant = {}; bool grant_configured = false; uint32_t pid = 0; @@ -97,6 +97,8 @@ private: std::unique_ptr harq_buffer = nullptr; + void save_grant(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant); + void generate_tx(mac_interface_phy_nr::tb_action_ul_t* action); void generate_new_tx(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, mac_interface_phy_nr::tb_action_ul_t* action); @@ -112,6 +114,9 @@ private: srsran::ul_harq_cfg_t harq_cfg = {}; ul_harq_metrics_t metrics = {}; + std::mutex metrics_mutex; + + const static uint8_t NDI_NOT_SET = 100; }; typedef std::unique_ptr ul_harq_entity_nr_ptr; diff --git a/srsue/hdr/stack/rrc/phy_controller.h b/srsue/hdr/stack/rrc/phy_controller.h index 456df9cdf..9cbec2c47 100644 --- a/srsue/hdr/stack/rrc/phy_controller.h +++ b/srsue/hdr/stack/rrc/phy_controller.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -49,7 +49,9 @@ public: struct cell_sel_cmd { phy_cell_t phy_cell; }; - struct cell_search_cmd {}; + struct cell_search_cmd { + int earfcn; + }; struct in_sync_ev { static const bool log_verbose = false; }; @@ -61,7 +63,7 @@ public: // PHY procedures interfaces bool start_cell_select(const phy_cell_t& phy_cell, srsran::event_observer observer = {}); - bool start_cell_search(srsran::event_observer observer); + bool start_cell_search(srsran::event_observer observer, int earfcn); void cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t found_cell); void cell_selection_completed(bool outcome); void in_sync(); @@ -126,7 +128,7 @@ public: // clang-format on }; struct searching_cell { - void enter(phy_controller* f); + void enter(phy_controller* f, const cell_search_cmd& ev); }; private: diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index 594967d1e..329a65a78 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,8 +23,9 @@ #define SRSUE_RRC_H #include "rrc_cell.h" -#include "rrc_common.h" +#include "rrc_config.h" #include "rrc_metrics.h" +#include "rrc_rlf_report.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/bcd_helpers.h" #include "srsran/common/block_queue.h" @@ -34,34 +35,13 @@ #include "srsran/common/security.h" #include "srsran/common/stack_procedure.h" #include "srsran/interfaces/ue_interfaces.h" +#include "srsran/rrc/rrc_common.h" #include "srsran/srslog/srslog.h" #include #include #include -#define SRSRAN_RRC_N_BANDS 43 -typedef struct { - std::string ue_category_str; - uint32_t ue_category; - int ue_category_ul; - int ue_category_dl; - uint32_t release; - uint32_t feature_group; - std::array supported_bands; - uint32_t nof_supported_bands; - bool support_ca; - int mbms_service_id; - uint32_t mbms_service_port; -} rrc_args_t; - -#define SRSRAN_UE_CATEGORY_DEFAULT "4" -#define SRSRAN_UE_CATEGORY_MIN 1 -#define SRSRAN_UE_CATEGORY_MAX 21 -#define SRSRAN_RELEASE_MIN 8 -#define SRSRAN_RELEASE_MAX 15 -#define SRSRAN_RELEASE_DEFAULT (SRSRAN_RELEASE_MAX) - using srsran::byte_buffer_t; namespace srsue { @@ -156,6 +136,7 @@ public: void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu); void write_pdu_pcch(srsran::unique_byte_buffer_t pdu); void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); + void notify_pdcp_integrity_error(uint32_t lcid); bool srbs_flushed(); //< Check if data on SRBs still needs to be sent @@ -201,8 +182,6 @@ private: srsran::s_tmsi_t ue_identity; bool ue_identity_configured = false; - bool drb_up = false; - // PHY controller state machine std::unique_ptr phy_ctrl; @@ -228,6 +207,9 @@ private: const char* get_rb_name(uint32_t lcid) { return srsran::is_lte_rb(lcid) ? rb_id_str[lcid].c_str() : "invalid RB"; } + // Var-RLF-Report class + rrc_rlf_report var_rlf_report; + // Measurements private subclass class rrc_meas; std::unique_ptr measurements; @@ -238,6 +220,9 @@ private: meas_cell_list meas_cells_nr; + // if this is set to a valid earfcn, this earfcn will be used for cell search + int cell_search_earfcn = -1; + bool initiated = false; asn1::rrc::reest_cause_e m_reest_cause = asn1::rrc::reest_cause_e::nulltype; uint16_t m_reest_rnti = 0; @@ -291,6 +276,7 @@ private: class si_acquire_proc; class serving_cell_config_proc; class cell_selection_proc; + class connection_setup_proc; class connection_request_proc; class connection_reconf_no_ho_proc; class plmn_search_proc; @@ -306,6 +292,7 @@ private: srsran::proc_t idle_setter; srsran::proc_t pcch_processor; srsran::proc_t conn_req_proc; + srsran::proc_t conn_setup_proc; srsran::proc_t plmn_searcher; srsran::proc_t cell_reselector; srsran::proc_t connection_reest; @@ -325,6 +312,7 @@ private: // RLC interface void max_retx_attempted(); + void protocol_failure(); // RRC NR interface void nr_scg_failure_information(const srsran::scg_failure_cause_t cause); @@ -352,7 +340,8 @@ private: bool con_reconfig_ho(const asn1::rrc::rrc_conn_recfg_s& reconfig); void ho_failed(); void start_go_idle(); - void rrc_connection_release(const std::string& cause); + void handle_rrc_connection_release(const asn1::rrc::rrc_conn_release_s& release); + void start_rrc_redirect(uint32_t new_dl_earfcn); void radio_link_failure_push_cmd(); void radio_link_failure_process(); void leave_connected(); @@ -382,11 +371,14 @@ private: void handle_con_reest(const asn1::rrc::rrc_conn_reest_s& setup); void handle_rrc_con_reconfig(uint32_t lcid, const asn1::rrc::rrc_conn_recfg_s& reconfig); void handle_ue_capability_enquiry(const asn1::rrc::ue_cap_enquiry_s& enquiry); + void handle_ue_info_request(const ue_info_request_r9_s& request); void add_srb(const asn1::rrc::srb_to_add_mod_s& srb_cnfg); void add_drb(const asn1::rrc::drb_to_add_mod_s& drb_cnfg); void release_drb(uint32_t drb_id); + uint32_t get_lcid_for_drb_id(const uint32_t& drb_id); uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id); uint32_t get_drb_id_for_eps_bearer(const uint32_t& eps_bearer_id); + uint32_t get_eps_bearer_id_for_drb_id(const uint32_t& drb_id); void add_mrb(uint32_t lcid, uint32_t port); // Helpers for setting default values diff --git a/srsue/hdr/stack/rrc/rrc_cell.h b/srsue/hdr/stack/rrc/rrc_cell.h index d01a6b00f..9de51c782 100644 --- a/srsue/hdr/stack/rrc/rrc_cell.h +++ b/srsue/hdr/stack/rrc/rrc_cell.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -130,8 +130,6 @@ public: void set_sib3(const asn1::rrc_nr::sib3_s& sib3_); const asn1::rrc_nr::sib1_s* sib1ptr() const { return has_sib1() ? &sib1 : nullptr; } - const asn1::rrc_nr::sib2_s* sib2ptr() const { return has_sib2() ? &sib2 : nullptr; } - const asn1::rrc_nr::sib3_s* sib3ptr() const { return has_sib3() ? &sib3 : nullptr; } uint32_t get_cell_id() const { return (uint32_t)0xFFFF; } // TODO find the correct sib @@ -140,11 +138,7 @@ public: std::string to_string() const; - bool has_mcch = false; asn1::rrc_nr::sib1_s sib1 = {}; - asn1::rrc_nr::sib2_s sib2 = {}; - asn1::rrc_nr::sib3_s sib3 = {}; - asn1::rrc::mcch_msg_s mcch = {}; }; class meas_cell_eutra : public meas_cell @@ -156,6 +150,7 @@ public: bool has_plmn_id(asn1::rrc::plmn_id_s plmn_id) const; uint32_t nof_plmns() const { return has_sib1() ? sib1.cell_access_related_info.plmn_id_list.size() : 0; } srsran::plmn_id_t get_plmn(uint32_t idx) const; + asn1::rrc::plmn_id_s get_plmn_asn1(uint32_t idx) const; uint16_t get_tac() const { return has_sib1() ? (uint16_t)sib1.cell_access_related_info.tac.to_number() : 0; } @@ -170,6 +165,7 @@ public: const asn1::rrc::sib_type13_r9_s* sib13ptr() const { return has_sib13() ? &sib13 : nullptr; } uint32_t get_cell_id() const { return (uint32_t)sib1.cell_access_related_info.cell_id.to_number(); } + asn1::fixed_bitstring<28> get_cell_id_bit() const { return sib1.cell_access_related_info.cell_id; } bool has_sib13() const { return has_valid_sib13; } @@ -260,6 +256,10 @@ public: // serving cell handling int set_serving_cell(phy_cell_t phy_cell, bool discard_serving); + // Set serving cell and earfcn for each cc_idx + void set_scell_cc_idx(uint32_t cc_idx, uint32_t earfcn, uint32_t pci); + bool get_scell_cc_idx(uint32_t earfcn, uint32_t& pci); + T& serving_cell() { return *serv_cell; } const T& serving_cell() const { return *serv_cell; } @@ -276,6 +276,9 @@ private: unique_meas_cell serv_cell; std::vector neighbour_cells; + + // store serving pci and earfcn for each carrier + std::array, SRSRAN_MAX_CARRIERS> current_cell_pci_earfcn = {}; }; } // namespace srsue diff --git a/srsue/hdr/stack/rrc/rrc_config.h b/srsue/hdr/stack/rrc/rrc_config.h new file mode 100644 index 000000000..7e91dcb4d --- /dev/null +++ b/srsue/hdr/stack/rrc/rrc_config.h @@ -0,0 +1,53 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_CONFIG_H +#define SRSRAN_RRC_CONFIG_H + +namespace srsue { + +#define SRSRAN_RRC_N_BANDS 43 + +struct rrc_args_t { + std::string ue_category_str; + uint32_t ue_category; + int ue_category_ul; + int ue_category_dl; + uint32_t release; + uint32_t feature_group; + std::array supported_bands; + std::vector supported_bands_nr; + uint32_t nof_supported_bands; + bool support_ca; + int mbms_service_id; + uint32_t mbms_service_port; +}; + +#define SRSRAN_UE_CATEGORY_DEFAULT "4" +#define SRSRAN_UE_CATEGORY_MIN 1 +#define SRSRAN_UE_CATEGORY_MAX 21 +#define SRSRAN_RELEASE_MIN 8 +#define SRSRAN_RELEASE_MAX 15 +#define SRSRAN_RELEASE_DEFAULT (SRSRAN_RELEASE_MIN) + +} // namespace srsue + +#endif // SRSRAN_RRC_CONFIG_H diff --git a/srsue/hdr/stack/rrc/rrc_meas.h b/srsue/hdr/stack/rrc/rrc_meas.h index 71e0d1144..0fb8efa8f 100644 --- a/srsue/hdr/stack/rrc/rrc_meas.h +++ b/srsue/hdr/stack/rrc/rrc_meas.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -201,8 +201,6 @@ private: rrc* rrc_ptr = nullptr; // Static functions - static uint8_t value_to_range(const report_cfg_eutra_s::trigger_quant_opts::options q, float value); - static float range_to_value(const report_cfg_eutra_s::trigger_quant_opts::options q, const uint8_t range); static uint8_t value_to_range_nr(const asn1::rrc::thres_nr_r15_c::types_opts::options type, const float value); static float range_to_value_nr(const asn1::rrc::thres_nr_r15_c::types_opts::options type, const uint8_t range); static uint8_t offset_val(const meas_obj_eutra_s& meas_obj); diff --git a/srsue/hdr/stack/rrc/rrc_metrics.h b/srsue/hdr/stack/rrc/rrc_metrics.h index bfc0018fc..0e85b4fa3 100644 --- a/srsue/hdr/stack/rrc/rrc_metrics.h +++ b/srsue/hdr/stack/rrc/rrc_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,11 +22,18 @@ #ifndef SRSUE_RRC_METRICS_H #define SRSUE_RRC_METRICS_H -#include "rrc_common.h" #include "srsran/interfaces/phy_interface_types.h" namespace srsue { +// RRC states (3GPP 36.331 v10.0.0) +typedef enum { + RRC_STATE_IDLE = 0, + RRC_STATE_CONNECTED, + RRC_STATE_N_ITEMS, +} rrc_state_t; +static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", "CONNECTED"}; + struct rrc_metrics_t { rrc_state_t state; std::vector neighbour_cells; diff --git a/srsue/hdr/stack/rrc/rrc_procedures.h b/srsue/hdr/stack/rrc/rrc_procedures.h index ad8c9cd2d..5d27a1419 100644 --- a/srsue/hdr/stack/rrc/rrc_procedures.h +++ b/srsue/hdr/stack/rrc/rrc_procedures.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -202,6 +202,24 @@ private: srsran::proc_future_t serv_cfg_fut; }; +class rrc::connection_setup_proc +{ +public: + explicit connection_setup_proc(rrc* parent_); + srsran::proc_outcome_t init(const asn1::rrc::rr_cfg_ded_s* cnfg_, srsran::unique_byte_buffer_t dedicated_info_nas_); + srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + void then(const srsran::proc_state_t& result); + srsran::proc_outcome_t react(const bool& config_complete); + static const char* name() { return "Connection Setup"; } + +private: + // const + rrc* rrc_ptr; + srslog::basic_logger& logger; + // args + srsran::unique_byte_buffer_t dedicated_info_nas; +}; + class rrc::connection_reconf_no_ho_proc { public: diff --git a/srsue/hdr/stack/rrc/rrc_rlf_report.h b/srsue/hdr/stack/rrc/rrc_rlf_report.h new file mode 100644 index 000000000..e3d69202b --- /dev/null +++ b/srsue/hdr/stack/rrc/rrc_rlf_report.h @@ -0,0 +1,67 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#ifndef SRSRAN_RRC_RLF_REPORT_H_ +#define SRSRAN_RRC_RLF_REPORT_H_ + +#include "rrc_cell.h" +#include "srsran/asn1/rrc.h" +#include "srsran/common/common.h" + +namespace srsue { + +using namespace asn1::rrc; + +// RRC RLF-Report class +class rrc_rlf_report +{ +public: + enum failure_type_t { rlf, hof }; + + void init(srsran::task_sched_handle task_sched); + + // Returns true if VarRLF-Report structure has info available + bool has_info(); + + // Called upon T304 expiry (type == hof) or Detection of radio link failure (type == rlf) + void set_failure(meas_cell_list& meas_cells, failure_type_t type); + + // Called upon transmission of ReestablishmentRequest message + void set_reest_gci(const asn1::fixed_bitstring<28>& gci, const asn1::rrc::plmn_id_s& plmn_id); + + // Called upon initiation of RadioReconfiguration message including MobilityInfo IE + void received_ho_command(const asn1::fixed_bitstring<28>& current_gci); + + // Returns a copy of the rlf_report_r9 ASN1 struct + rlf_report_r9_s get_report(); + + // Clears VarRLF-Report contents + void clear(); + +private: + asn1::fixed_bitstring<28> ho_gci; + + bool has_event = false; + rlf_report_r9_s rlf_report = {}; + srsran::timer_handler::unique_timer timer_conn_failure = {}; +}; +} // namespace srsue + +#endif // SRSRAN_RRC_RLF_REPORT_H_ diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h similarity index 55% rename from srsue/hdr/stack/rrc/rrc_nr.h rename to srsue/hdr/stack/rrc_nr/rrc_nr.h index 61dd31d08..cfdf31cf3 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,15 +22,22 @@ #ifndef SRSUE_RRC_NR_H #define SRSUE_RRC_NR_H +#include "../rrc/rrc_cell.h" +#include "rrc_nr_config.h" +#include "rrc_nr_metrics.h" +#include "srsran/adt/circular_map.h" #include "srsran/asn1/rrc_nr.h" #include "srsran/asn1/rrc_nr_utils.h" #include "srsran/common/block_queue.h" -#include "srsran/common/common_nr.h" #include "srsran/common/buffer_pool.h" +#include "srsran/common/common_nr.h" #include "srsran/common/stack_procedure.h" #include "srsran/common/task_scheduler.h" -#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/interfaces/ue_interfaces.h" +#include "srsran/interfaces/ue_nas_interfaces.h" #include "srsran/interfaces/ue_nr_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/interfaces/ue_sdap_interfaces.h" #include "srsue/hdr/stack/upper/gw.h" namespace srsue { @@ -38,52 +45,33 @@ namespace srsue { class usim_interface_rrc_nr; class pdcp_interface_rrc; class rlc_interface_rrc; -class stack_interface_rrc; - -// Expert arguments to create GW without proper RRC -struct core_less_args_t { - std::string ip_addr; - srsue::gw_args_t gw_args; - uint8_t drb_lcid; -}; - -struct rrc_nr_args_t { - core_less_args_t coreless; - uint32_t sim_nr_meas_pci; - bool pdcp_short_sn_support; - std::string supported_bands_nr_str; - std::vector supported_bands_nr; - std::vector supported_bands_eutra; - std::string log_level; - uint32_t log_hex_limit; -}; - -struct rrc_nr_metrics_t {}; class rrc_nr final : public rrc_interface_phy_nr, public rrc_interface_pdcp, public rrc_interface_rlc, public rrc_interface_mac, public rrc_nr_interface_rrc, + public rrc_nr_interface_nas_5g, public srsran::timer_callback { public: rrc_nr(srsran::task_sched_handle task_sched_); ~rrc_nr(); - void init(phy_interface_rrc_nr* phy_, - mac_interface_rrc_nr* mac_, - rlc_interface_rrc* rlc_, - pdcp_interface_rrc* pdcp_, - gw_interface_rrc* gw_, - rrc_eutra_interface_rrc_nr* rrc_eutra_, - usim_interface_rrc_nr* usim_, - srsran::timer_handler* timers_, - stack_interface_rrc* stack_, - const rrc_nr_args_t& args_); + int init(phy_interface_rrc_nr* phy_, + mac_interface_rrc_nr* mac_, + rlc_interface_rrc* rlc_, + pdcp_interface_rrc* pdcp_, + sdap_interface_rrc* sdap_, + gw_interface_rrc* gw_, + nas_5g_interface_rrc_nr* nas_, + rrc_eutra_interface_rrc_nr* rrc_eutra_, + usim_interface_rrc_nr* usim_, + srsran::timer_handler* timers_, + stack_interface_rrc* stack_, + const rrc_nr_args_t& args_); void stop(); - void init_core_less(); void get_metrics(rrc_nr_metrics_t& m); @@ -104,15 +92,18 @@ public: asn1::dyn_octstring oct, const T& msg, const std::string& msg_type); + + void run_tti(uint32_t tti); + // PHY interface void in_sync() final; void out_of_sync() final; // RLC interface void max_retx_attempted() final; + void protocol_failure() final; // MAC interface - void run_tti(uint32_t tti) final; void ra_completed() final; void ra_problem() final; void release_pucch_srs() final; @@ -123,25 +114,56 @@ public: void write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) final; void write_pdu_pcch(srsran::unique_byte_buffer_t pdu) final; void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; + void notify_pdcp_integrity_error(uint32_t lcid) final; + + // NAS interface + int write_sdu(srsran::unique_byte_buffer_t sdu); + bool is_connected(); + int connection_request(srsran::nr_establishment_cause_t cause, srsran::unique_byte_buffer_t sdu); + uint16_t get_mcc(); + uint16_t get_mnc(); // RRC (LTE) interface - void get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps); - void get_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps); + int get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps); + int get_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps); void phy_meas_stop(); void phy_set_cells_to_meas(uint32_t carrier_freq_r15); - bool rrc_reconfiguration(bool endc_release_and_add_r15, - bool nr_secondary_cell_group_cfg_r15_present, - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - bool sk_counter_r15_present, - uint32_t sk_counter_r15, - bool nr_radio_bearer_cfg1_r15_present, - asn1::dyn_octstring nr_radio_bearer_cfg1_r15); + bool rrc_reconfiguration(bool endc_release_and_add_r15, const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf); + void rrc_release(); bool configure_sk_counter(uint16_t sk_counter); bool is_config_pending(); + // STACK interface - void cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell); + void cell_search_found_cell(const rrc_interface_phy_nr::cell_search_result_t& result) final; + void cell_select_completed(const cell_select_result_t& result) final; + void set_phy_config_complete(bool status) final; private: + // parsers + void decode_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu); + void decode_dl_ccch(srsran::unique_byte_buffer_t pdu); + void decode_dl_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); + // senders + void send_setup_request(srsran::nr_establishment_cause_t cause); + void send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg); + void send_rrc_reconfig_complete(); + int send_ue_capability_info(const asn1::rrc_nr::ue_cap_enquiry_s& msg); + void send_ul_info_transfer(srsran::unique_byte_buffer_t nas_msg); + void send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg); + void send_ul_dcch_msg(uint32_t lcid, const asn1::rrc_nr::ul_dcch_msg_s& msg); + void send_security_mode_complete(); + + // helpers + void set_phy_default_config(); + void handle_sib1(const asn1::rrc_nr::sib1_s& sib1); + bool handle_rrc_setup(const asn1::rrc_nr::rrc_setup_s& setup); + void handle_rrc_reconfig(const asn1::rrc_nr::rrc_recfg_s& reconfig); + void handle_ue_capability_enquiry(const asn1::rrc_nr::ue_cap_enquiry_s& ue_cap_enquiry); + void handle_dl_info_transfer(const asn1::rrc_nr::dl_info_transfer_s& dl_info_transfer); + void handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_s& smc); + void handle_rrc_release(const asn1::rrc_nr::rrc_release_s& rrc_release); + void generate_as_keys(); + srsran::task_sched_handle task_sched; struct cmd_msg_t { enum { PDU, PCCH, PDU_MCH, RLF, PDU_BCCH_DLSCH, STOP } command; @@ -160,30 +182,43 @@ private: mac_interface_rrc_nr* mac = nullptr; rlc_interface_rrc* rlc = nullptr; pdcp_interface_rrc* pdcp = nullptr; + sdap_interface_rrc* sdap = nullptr; gw_interface_rrc* gw = nullptr; + nas_5g_interface_rrc_nr* nas = nullptr; rrc_eutra_interface_rrc_nr* rrc_eutra = nullptr; usim_interface_rrc_nr* usim = nullptr; stack_interface_rrc* stack = nullptr; + meas_cell_list meas_cells; + + // PLMN + bool plmn_is_selected = false; + srsran::unique_byte_buffer_t dedicated_info_nas; + const uint32_t sim_measurement_timer_duration_ms = 250; uint32_t sim_measurement_carrier_freq_r15; srsran::timer_handler::unique_timer sim_measurement_timer; - /// RRC states (3GPP 38.331 v15.5.1 Sec 4.2.1) - enum rrc_nr_state_t { - RRC_NR_STATE_IDLE = 0, - RRC_NR_STATE_CONNECTED, - RRC_NR_STATE_CONNECTED_INACTIVE, - RRC_NR_STATE_N_ITEMS, - }; - const static char* rrc_nr_state_text[RRC_NR_STATE_N_ITEMS]; + rrc_nr_state_t state = RRC_NR_STATE_IDLE; - // rrc_nr_state_t state = RRC_NR_STATE_IDLE; - - rrc_nr_args_t args = {}; + uint8_t transaction_id = 0; // RRC constants and timers - srsran::timer_handler* timers = nullptr; + uint32_t n310_cnt = 0, N310 = 0; + uint32_t n311_cnt = 0, N311 = 0; + srsran::timer_handler::unique_timer t300, t301, t302, t310, t311, t304; + + // Stores the state of the PHY configuration setting + enum { + PHY_CFG_STATE_NONE = 0, + PHY_CFG_STATE_SA_MIB_CFG, + PHY_CFG_STATE_SA_SIB_CFG, + PHY_CFG_STATE_SA_FULL_CFG, + PHY_CFG_STATE_NSA_APPLY_SP_CELL, + PHY_CFG_STATE_NSA_RA_COMPLETED, + } phy_cfg_state; + + rrc_nr_args_t args = {}; const char* get_rb_name(uint32_t lcid) final; @@ -195,16 +230,17 @@ private: std::map drb_eps_bearer_id; // Map of drb id to eps_bearer_id // temporary maps for building the pucch nr resources - std::map res_list; - std::map res_list_present; - std::map csi_rs_zp_res; - std::map csi_rs_nzp_res; + srsran::static_circular_map pucch_res_list; + std::map csi_rs_zp_res; + std::map csi_rs_nzp_res; bool apply_cell_group_cfg(const asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg); + bool update_cell_group_cfg(const asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg); bool apply_radio_bearer_cfg(const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_cfg); bool apply_rlc_add_mod(const asn1::rrc_nr::rlc_bearer_cfg_s& rlc_bearer_cfg); bool apply_mac_cell_group(const asn1::rrc_nr::mac_cell_group_cfg_s& mac_cell_group_cfg); bool apply_sp_cell_cfg(const asn1::rrc_nr::sp_cell_cfg_s& sp_cell_cfg); + bool update_sp_cell_cfg(const asn1::rrc_nr::sp_cell_cfg_s& sp_cell_cfg); bool apply_phy_cell_group_cfg(const asn1::rrc_nr::phys_cell_group_cfg_s& phys_cell_group_cfg); bool apply_dl_common_cfg(const asn1::rrc_nr::dl_cfg_common_s& dl_cfg_common); bool apply_ul_common_cfg(const asn1::rrc_nr::ul_cfg_common_s& ul_cfg_common); @@ -214,44 +250,32 @@ private: bool apply_sp_cell_ded_ul_pusch(const asn1::rrc_nr::pusch_cfg_s& pusch_cfg); bool apply_csi_meas_cfg(const asn1::rrc_nr::csi_meas_cfg_s& csi_meas_cfg); bool apply_res_csi_report_cfg(const asn1::rrc_nr::csi_report_cfg_s& csi_report_cfg); + bool apply_srb_add_mod(const asn1::rrc_nr::srb_to_add_mod_s& srb_cfg); bool apply_drb_add_mod(const asn1::rrc_nr::drb_to_add_mod_s& drb_cfg); bool apply_drb_release(const uint8_t drb); bool apply_security_cfg(const asn1::rrc_nr::security_cfg_s& security_cfg); + // Security configuration + bool security_is_activated = false; srsran::as_security_config_t sec_cfg; typedef enum { mcg_srb1, en_dc_srb3, nr } reconf_initiator_t; - class connection_reconf_no_ho_proc - { - public: - explicit connection_reconf_no_ho_proc(rrc_nr* parent_); - srsran::proc_outcome_t init(const reconf_initiator_t initiator_, - const bool endc_release_and_add_r15, - const bool nr_secondary_cell_group_cfg_r15_present, - const asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - const bool sk_counter_r15_present, - const uint32_t sk_counter_r15, - const bool nr_radio_bearer_cfg1_r15_present, - const asn1::dyn_octstring nr_radio_bearer_cfg1_r15); - srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } - static const char* name() { return "NR Connection Reconfiguration"; } - srsran::proc_outcome_t react(const bool& config_complete); - void then(const srsran::proc_state_t& result); + // RRC procedures + enum class rrc_cell_search_result_t { changed_cell, same_cell, no_cell }; + class cell_selection_proc; + class connection_setup_proc; + class connection_reconf_no_ho_proc; + class setup_request_proc; - private: - // const - rrc_nr* rrc_ptr; - reconf_initiator_t initiator; - asn1::rrc_nr::rrc_recfg_s rrc_recfg; - asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; - }; - - srsran::proc_t conn_recfg_proc; + srsran::proc_t cell_selector; + srsran::proc_t conn_setup_proc; + srsran::proc_t conn_recfg_proc; + srsran::proc_t setup_req_proc; srsran::proc_manager_list_t callback_list; }; } // namespace srsue -#endif // SRSUE_RRC_NR_H \ No newline at end of file +#endif // SRSUE_RRC_NR_H diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr_config.h b/srsue/hdr/stack/rrc_nr/rrc_nr_config.h new file mode 100644 index 000000000..84fdf5c2e --- /dev/null +++ b/srsue/hdr/stack/rrc_nr/rrc_nr_config.h @@ -0,0 +1,47 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_RRC_NR_CONFIG_H +#define SRSRAN_RRC_NR_CONFIG_H + +#include +#include + +namespace srsue { + +struct rrc_nr_args_t { + uint32_t sim_nr_meas_pci; + bool pdcp_short_sn_support; + std::string supported_bands_nr_str; + std::vector supported_bands_nr; + std::vector supported_bands_eutra; + uint32_t dl_nr_arfcn; + uint32_t ssb_nr_arfcn; + uint32_t nof_prb; + srsran_subcarrier_spacing_t scs; + srsran_subcarrier_spacing_t ssb_scs; + std::string log_level; + uint32_t log_hex_limit; +}; + +} // namespace srsue + +#endif // SRSRAN_RRC_NR_CONFIG_H diff --git a/lib/include/srsran/upper/rlc_am_base.h b/srsue/hdr/stack/rrc_nr/rrc_nr_metrics.h similarity index 57% rename from lib/include/srsran/upper/rlc_am_base.h rename to srsue/hdr/stack/rrc_nr/rrc_nr_metrics.h index 123e5a82d..e8811d067 100644 --- a/lib/include/srsran/upper/rlc_am_base.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,32 +19,23 @@ * */ -#ifndef SRSRAN_RLC_AM_BASE_H -#define SRSRAN_RLC_AM_BASE_H - -#include "srsran/common/buffer_pool.h" -#include "srsran/common/common.h" -#include "srsran/upper/byte_buffer_queue.h" -#include "srsran/upper/rlc_common.h" -#include -#include -#include -#include - -namespace srsran { - -///< Add rlc_am_base here - -bool rlc_am_is_control_pdu(uint8_t* payload); -bool rlc_am_is_control_pdu(byte_buffer_t* pdu); - -} // namespace srsran +#ifndef SRSUE_RRC_NR_METRICS_H +#define SRSUE_RRC_NR_METRICS_H namespace srsue { -class pdcp_interface_rlc; -class rrc_interface_rlc; +/// RRC states (3GPP 38.331 v15.5.1 Sec 4.2.1) +enum rrc_nr_state_t { + RRC_NR_STATE_IDLE = 0, + RRC_NR_STATE_CONNECTED, + RRC_NR_STATE_CONNECTED_INACTIVE, + RRC_NR_STATE_N_ITEMS, +}; + +struct rrc_nr_metrics_t { + rrc_nr_state_t state; +}; } // namespace srsue -#endif // SRSRAN_RLC_AM_BASE_H +#endif // SRSUE_RRC_NR_METRICS_H diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h b/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h new file mode 100644 index 000000000..d03529d7e --- /dev/null +++ b/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h @@ -0,0 +1,131 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/srslog/srslog.h" +#include "srsue/hdr/stack/rrc_nr/rrc_nr.h" + +#ifndef SRSRAN_RRC_NR_PROCEDURES_H +#define SRSRAN_RRC_NR_PROCEDURES_H + +namespace srsue { + +/******************************** + * Procedures + *******************************/ + +class rrc_nr::cell_selection_proc +{ +public: + enum class state_t { phy_cell_search, phy_cell_select, sib_acquire }; + + using cell_selection_complete_ev = srsran::proc_result_t; + explicit cell_selection_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step(); + srsran::proc_outcome_t react(const rrc_interface_phy_nr::cell_search_result_t& event); + srsran::proc_outcome_t react(const rrc_interface_phy_nr::cell_select_result_t& event); + srsran::proc_outcome_t react(const bool sib1_found); + + void then(const cell_selection_complete_ev& proc_result) const; + + rrc_cell_search_result_t get_result() const { return rrc_search_result; } + static const char* name() { return "Cell Selection"; } + +private: + srsran::proc_outcome_t handle_cell_search_result(const rrc_interface_phy_nr::cell_search_result_t& result); + + // conts + rrc_nr& rrc_handle; + + // state vars + rrc_interface_phy_nr::cell_search_result_t phy_search_result = {}; + rrc_cell_search_result_t rrc_search_result = {}; + state_t state; +}; + +class rrc_nr::setup_request_proc +{ +public: + explicit setup_request_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(srsran::nr_establishment_cause_t cause_, + srsran::unique_byte_buffer_t dedicated_info_nas_); + srsran::proc_outcome_t step(); + void then(const srsran::proc_state_t& result); + srsran::proc_outcome_t react(const cell_selection_proc::cell_selection_complete_ev& e); + static const char* name() { return "Setup Request"; } + +private: + // const + rrc_nr& rrc_handle; + srslog::basic_logger& logger; + // args + srsran::nr_establishment_cause_t cause; + srsran::unique_byte_buffer_t dedicated_info_nas; + + // state variables + enum class state_t { cell_selection, config_serving_cell, wait_t300 } state; + rrc_cell_search_result_t cell_search_ret; + srsran::proc_future_t serv_cfg_fut; +}; + +class rrc_nr::connection_setup_proc +{ +public: + explicit connection_setup_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_cfg_, + const asn1::rrc_nr::cell_group_cfg_s& cell_group_, + srsran::unique_byte_buffer_t dedicated_info_nas_); + srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + static const char* name() { return "Connection Setup"; } + srsran::proc_outcome_t react(const bool& config_complete); + void then(const srsran::proc_state_t& result); + +private: + // const + rrc_nr& rrc_handle; + srslog::basic_logger& logger; + // args + srsran::unique_byte_buffer_t dedicated_info_nas; +}; + +class rrc_nr::connection_reconf_no_ho_proc +{ +public: + explicit connection_reconf_no_ho_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(const reconf_initiator_t initiator_, + const bool endc_release_and_add_r15, + const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf); + srsran::proc_outcome_t step() { return srsran::proc_outcome_t::yield; } + static const char* name() { return "NR Connection Reconfiguration"; } + srsran::proc_outcome_t react(const bool& config_complete); + void then(const srsran::proc_state_t& result); + +private: + // const + rrc_nr& rrc_handle; + reconf_initiator_t initiator; + asn1::rrc_nr::rrc_recfg_s rrc_recfg; + asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; +}; + +} // namespace srsue + +#endif // SRSRAN_RRC_NR_PROCEDURES_H diff --git a/srsue/hdr/stack/ue_stack_base.h b/srsue/hdr/stack/ue_stack_base.h index 8dc220561..f72c3340f 100644 --- a/srsue/hdr/stack/ue_stack_base.h +++ b/srsue/hdr/stack/ue_stack_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,11 +22,10 @@ #ifndef SRSUE_UE_STACK_BASE_H #define SRSUE_UE_STACK_BASE_H +#include "rrc/rrc_config.h" +#include "rrc_nr/rrc_nr_config.h" #include "srsue/hdr/stack/upper/nas_config.h" #include "srsue/hdr/ue_metrics_interface.h" - -#include "rrc/rrc.h" -#include "rrc/rrc_nr.h" #include "upper/gw.h" #include "upper/usim.h" @@ -65,7 +64,6 @@ typedef struct { } stack_log_args_t; typedef struct { - std::string type; pkt_trace_args_t pkt_trace; stack_log_args_t log; usim_args_t usim; @@ -73,9 +71,11 @@ typedef struct { rrc_nr_args_t rrc_nr; std::string ue_category_str; nas_args_t nas; + nas_5g_args_t nas_5g; gw_args_t gw; uint32_t sync_queue_size; // Max allowed difference between PHY and Stack clocks (in TTI) bool have_tti_time_stats; + bool sa_mode; } stack_args_t; class ue_stack_base diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index de55dfe18..0c64f3195 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,20 +26,11 @@ #ifndef SRSUE_UE_STACK_LTE_H #define SRSUE_UE_STACK_LTE_H -#include -#include -#include -#include - #include "mac/mac.h" #include "mac_nr/mac_nr.h" #include "rrc/rrc.h" -#include "srsran/radio/radio.h" -#include "srsran/upper/pdcp.h" -#include "srsran/upper/rlc.h" -#include "upper/nas.h" -#include "upper/usim.h" - +#include "rrc_nr/rrc_nr.h" +#include "srsran/common/bearer_manager.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/multiqueue.h" #include "srsran/common/string_helpers.h" @@ -47,13 +38,46 @@ #include "srsran/common/thread_pool.h" #include "srsran/common/time_prof.h" #include "srsran/interfaces/ue_interfaces.h" +#include "srsran/radio/radio.h" +#include "srsran/rlc/rlc.h" +#include "srsran/upper/pdcp.h" #include "srsue/hdr/ue_metrics_interface.h" #include "ue_stack_base.h" +#include "upper/nas.h" +#include "upper/nas_5g.h" +#include "upper/sdap.h" +#include "upper/usim.h" +#include +#include +#include +#include namespace srsue { class phy_interface_stack_lte; +class sdap_pdcp_adapter : public pdcp_interface_sdap_nr, public gw_interface_pdcp +{ +public: + sdap_pdcp_adapter(pdcp* parent_pdcp_, sdap* parent_sdap_) : parent_pdcp(parent_pdcp_), parent_sdap(parent_sdap_) {} + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final + { + parent_pdcp->write_sdu(lcid, std::move(pdu)); + } + void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final + { + parent_sdap->write_pdu(lcid, std::move(pdu)); + } + void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final + { + // not implemented + } + +private: + pdcp* parent_pdcp = nullptr; + sdap* parent_sdap = nullptr; +}; + class ue_stack_lte final : public ue_stack_base, public stack_interface_phy_lte, public stack_interface_phy_nr, @@ -92,6 +116,7 @@ public: void cell_select_complete(bool status) final; void set_config_complete(bool status) final; void set_scell_complete(bool status) final; + void set_phy_config_complete(bool status) final; // MAC Interface for EUTRA PHY uint16_t get_dl_sched_rnti(uint32_t tti) final { return mac.get_dl_sched_rnti(tti); } @@ -120,19 +145,17 @@ public: mac.bch_decoded_ok(cc_idx, payload, len); } - void mch_decoded(uint32_t len, bool crc) final { mac.mch_decoded(len, crc); } - - void new_mch_dl(const srsran_pdsch_grant_t& phy_grant, mac_interface_phy_lte::tb_action_dl_t* action) final - { - mac.new_mch_dl(phy_grant, action); - } + void mch_decoded(uint32_t len, bool crc, uint8_t* payload) final { mac.mch_decoded(len, crc, payload); } void set_mbsfn_config(uint32_t nof_mbsfn_services) final { mac.set_mbsfn_config(nof_mbsfn_services); } void run_tti(uint32_t tti, uint32_t tti_jump) final; + // RRC interface for NR PHY + void cell_search_found_cell(const cell_search_result_t& result) final; + void cell_select_completed(const cell_select_result_t& result) final; + // MAC Interface for NR PHY - int sf_indication(const uint32_t tti) final { return SRSRAN_SUCCESS; } void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_result_t result) final @@ -152,11 +175,6 @@ public: mac_nr.new_grant_ul(cc_idx, grant, action); } - void run_tti(const uint32_t tti) final - { - // ignored, timing will be handled by EUTRA - } - void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) final { mac_nr.prach_sent(tti, s_id, t_id, f_id, ul_carrier_id); @@ -168,12 +186,14 @@ public: } // Interface for GW - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final; - - bool is_lcid_enabled(uint32_t lcid) final { return pdcp.is_lcid_enabled(lcid); } + void write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu) final; + bool has_active_radio_bearer(uint32_t eps_bearer_id) final; // Interface for RRC tti_point get_current_tti() final { return current_tti; } + void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final; + void remove_eps_bearer(uint8_t eps_bearer_id) final; + void reset_eps_bearers() final; srsran::ext_task_sched_handle get_task_sched() { return {&task_sched}; } @@ -186,7 +206,7 @@ private: const std::chrono::milliseconds TTI_WARN_THRESHOLD_MS{5}; const uint32_t SYNC_QUEUE_WARN_THRESHOLD = 5; - bool running; + std::atomic running{false}; srsue::stack_args_t args; srsran::tti_point current_tti; @@ -199,10 +219,13 @@ private: srslog::basic_logger& rrc_logger; srslog::basic_logger& usim_logger; srslog::basic_logger& nas_logger; + srslog::basic_logger& nas5g_logger; - // UE nr stack logging + // UE NR stack logging srslog::basic_logger& mac_nr_logger; srslog::basic_logger& rrc_nr_logger; + srslog::basic_logger& rlc_nr_logger; + srslog::basic_logger& pdcp_nr_logger; // tracing srsran::mac_pcap mac_pcap; @@ -229,12 +252,21 @@ private: srsran::pdcp pdcp; srsue::rrc rrc; srsue::mac_nr mac_nr; + srsran::rlc rlc_nr; + srsran::pdcp pdcp_nr; srsue::rrc_nr rrc_nr; srsue::nas nas; + srsue::nas_5g nas_5g; std::unique_ptr usim; + // SDAP only applies to NR + srsue::sdap sdap; + sdap_pdcp_adapter sdap_pdcp; + + ue_bearer_manager bearers; // helper to manage mapping between EPS and radio bearers + // Metrics helper - uint32_t ul_dropped_sdus = 0; + std::atomic ul_dropped_sdus{0}; }; } // namespace srsue diff --git a/srsue/hdr/stack/ue_stack_nr.h b/srsue/hdr/stack/ue_stack_nr.h index 5be3d1f2a..36d6b2bbb 100644 --- a/srsue/hdr/stack/ue_stack_nr.h +++ b/srsue/hdr/stack/ue_stack_nr.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -28,17 +28,18 @@ #include #include "mac_nr/mac_nr.h" -#include "rrc/rrc_nr.h" #include "srsran/radio/radio.h" +#include "srsran/rlc/rlc.h" #include "srsran/upper/pdcp.h" -#include "srsran/upper/rlc.h" #include "upper/nas.h" +#include "upper/sdap.h" #include "upper/usim.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/mac_pcap.h" #include "srsran/common/multiqueue.h" #include "srsran/common/thread_pool.h" +#include "srsran/interfaces/ue_interfaces.h" #include "srsran/interfaces/ue_nr_interfaces.h" #include "srsue/hdr/ue_metrics_interface.h" @@ -46,6 +47,8 @@ namespace srsue { +class rrc_nr; + /** \brief L2/L3 stack class for 5G/NR UEs. * * This class wraps all L2/L3 blocks and provides a single interface towards the PHY. @@ -67,28 +70,27 @@ public: int init(const stack_args_t& args_, phy_interface_stack_nr* phy_, gw_interface_stack* gw_); bool switch_on() final; bool switch_off() final; - void stop(); + void stop() final; // GW srsue stack_interface_gw dummy interface - bool is_registered() { return true; }; - bool start_service_request() { return true; }; + bool is_registered() final { return true; }; + bool start_service_request() final { return true; }; - bool get_metrics(stack_metrics_t* metrics); + bool get_metrics(stack_metrics_t* metrics) final; bool is_rrc_connected(); // RRC interface for PHY void in_sync() final; void out_of_sync() final; - void run_tti(const uint32_t tti) final; + void set_phy_config_complete(bool status) final; + void cell_search_found_cell(const cell_search_result_t& result) final; + + void run_tti(uint32_t tti, uint32_t tti_jump) final; // MAC interface for PHY sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) final { return mac->get_dl_sched_rnti_nr(tti); } sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) final { return mac->get_ul_sched_rnti_nr(tti); } - int sf_indication(const uint32_t tti) - { - run_tti(tti); - return SRSRAN_SUCCESS; - } + void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) final { mac->tb_decoded(cc_idx, grant, std::move(result)); @@ -101,21 +103,25 @@ public: { mac->new_grant_ul(cc_idx, grant, action); } - void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) + void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) final { mac->prach_sent(tti, s_id, t_id, f_id, ul_carrier_id); } - bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) final { return mac->sr_opportunity(tti, sr_id, meas_gap, ul_sch_tx); } // Interface for GW - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final; - bool is_lcid_enabled(uint32_t lcid) final { return pdcp->is_lcid_enabled(lcid); } + void write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu) final; + bool has_active_radio_bearer(uint32_t eps_bearer_id) final { return true; /* TODO: add EPS to LCID mapping */ } // Interface for RRC - srsran::tti_point get_current_tti() { return srsran::tti_point{0}; }; + srsran::tti_point get_current_tti() final { return srsran::tti_point{0}; } + void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final {} + void remove_eps_bearer(uint8_t eps_bearer_id) final {} + void reset_eps_bearers() final {} + void cell_select_completed(const cell_select_result_t& result) override; private: void run_thread() final; @@ -139,6 +145,7 @@ private: std::unique_ptr rrc; std::unique_ptr rlc; std::unique_ptr pdcp; + std::unique_ptr sdap; // RAT-specific interfaces phy_interface_stack_nr* phy = nullptr; diff --git a/srsue/hdr/stack/upper/gw.h b/srsue/hdr/stack/upper/gw.h index a5f2dbbe2..e78de9aa5 100644 --- a/srsue/hdr/stack/upper/gw.h +++ b/srsue/hdr/stack/upper/gw.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -30,6 +30,8 @@ #include "srsran/interfaces/ue_gw_interfaces.h" #include "srsran/srslog/srslog.h" #include "tft_packet_filter.h" +#include +#include #include #include @@ -50,7 +52,8 @@ struct gw_args_t { class gw : public gw_interface_stack, public srsran::thread { public: - gw(); + gw(srslog::basic_logger& logger_); + ~gw(); int init(const gw_args_t& args_, stack_interface_gw* stack); void stop(); @@ -61,20 +64,13 @@ public: void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); // NAS interface - int setup_if_addr(uint32_t eps_bearer_id, - uint32_t lcid, - uint8_t pdn_type, - uint32_t ip_addr, - uint8_t* ipv6_if_addr, - char* err_str); - int apply_traffic_flow_template(const uint8_t& eps_bearer_id, - const uint8_t& lcid, - const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft); + int setup_if_addr(uint32_t eps_bearer_id, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_addr, char* err_str); + int deactivate_eps_bearer(const uint32_t eps_bearer_id); + int apply_traffic_flow_template(const uint8_t& eps_bearer_id, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft); void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms); // RRC interface void add_mch_port(uint32_t lcid, uint32_t port); - int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid); bool is_running(); private: @@ -84,19 +80,20 @@ private: gw_args_t args = {}; - bool running = false; - bool run_enable = false; - int32_t netns_fd = 0; - int32_t tun_fd = 0; - struct ifreq ifr = {}; - int32_t sock = 0; - bool if_up = false; - uint32_t default_lcid = 0; + std::atomic running = {false}; + std::atomic run_enable = {false}; + int32_t netns_fd = 0; + int32_t tun_fd = 0; + struct ifreq ifr = {}; + int32_t sock = 0; + std::atomic if_up = {false}; + + static const int NOT_ASSIGNED = -1; + int32_t default_eps_bearer_id = NOT_ASSIGNED; + std::mutex gw_mutex; srslog::basic_logger& logger; - std::map eps_lcid; // Mapping between eps bearer ID and LCID - uint32_t current_ip_addr = 0; uint8_t current_if_id[8]; diff --git a/srsue/hdr/stack/upper/gw_metrics.h b/srsue/hdr/stack/upper/gw_metrics.h index 8befc535b..3373f1150 100644 --- a/srsue/hdr/stack/upper/gw_metrics.h +++ b/srsue/hdr/stack/upper/gw_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/upper/nas.h b/srsue/hdr/stack/upper/nas.h index 4e52b2309..da6d3ed5b 100644 --- a/srsue/hdr/stack/upper/nas.h +++ b/srsue/hdr/stack/upper/nas.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #ifndef SRSUE_NAS_H #define SRSUE_NAS_H +#include "nas_base.h" #include "srsran/asn1/liblte_mme.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/common.h" @@ -43,12 +44,19 @@ class usim_interface_nas; class gw_interface_nas; class rrc_interface_nas; -class nas : public nas_interface_rrc, public srsran::timer_callback +/** + * @brief This class implements the NAS layer of a EUTRA UE. + * + * The class is *NOT* thread-safe. + * + */ + +class nas : public nas_interface_rrc, public srsran::timer_callback, public nas_base { public: - explicit nas(srsran::task_sched_handle task_sched_); + explicit nas(srslog::basic_logger& logger_, srsran::task_sched_handle task_sched_); virtual ~nas(); - void init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& args_); + int init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& args_); void stop(); void run_tti(); @@ -80,14 +88,10 @@ public: // timer callback void timer_expired(uint32_t timeout_id) override; - // PCAP - void start_pcap(srsran::nas_pcap* pcap_) { pcap = pcap_; } - private: - srslog::basic_logger& logger; - rrc_interface_nas* rrc = nullptr; - usim_interface_nas* usim = nullptr; - gw_interface_nas* gw = nullptr; + rrc_interface_nas* rrc = nullptr; + usim_interface_nas* usim = nullptr; + gw_interface_nas* gw = nullptr; bool running = false; @@ -101,18 +105,6 @@ private: std::vector known_plmns; - // Security context - struct nas_sec_ctxt { - uint8_t ksi; - uint8_t k_asme[32]; - uint32_t tx_count; - uint32_t rx_count; - uint32_t k_enb_count; - srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; - srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; - LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; - }; - typedef enum { DEFAULT_EPS_BEARER = 0, DEDICATED_EPS_BEARER } eps_bearer_type_t; typedef struct { @@ -125,11 +117,10 @@ private: typedef std::pair eps_bearer_map_pair_t; eps_bearer_map_t eps_bearer; - bool have_guti = false; - bool have_ctxt = false; - nas_sec_ctxt ctxt = {}; - bool auth_request = false; - uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS; + bool have_guti = false; + bool have_ctxt = false; + bool auth_request = false; + uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS; const uint32_t max_attach_attempts = 5; // Sec. 5.5.1.2.6 uint32_t attach_attempt_counter = 0; @@ -162,25 +153,15 @@ private: const uint8_t ue_svn_oct2 = 0x3; // Security - bool eia_caps[8] = {}; - bool eea_caps[8] = {}; - uint8_t k_nas_enc[32] = {}; - uint8_t k_nas_int[32] = {}; + bool eia_caps[8] = {}; + bool eea_caps[8] = {}; // Airplane mode simulation typedef enum { DISABLED = 0, ENABLED } airplane_mode_state_t; airplane_mode_state_t airplane_mode_state = {}; srsran::timer_handler::unique_timer airplane_mode_sim_timer; - // PCAP - srsran::nas_pcap* pcap = nullptr; - // Security - void - integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac); - bool integrity_check(srsran::byte_buffer_t* pdu); - void cipher_encrypt(srsran::byte_buffer_t* pdu); - void cipher_decrypt(srsran::byte_buffer_t* pdu); int apply_security_config(srsran::unique_byte_buffer_t& pdu, uint8_t sec_hdr_type); void reset_security_context(); void set_k_enb_count(uint32_t count); @@ -196,12 +177,12 @@ private: // Parsers void parse_attach_accept(uint32_t lcid, srsran::unique_byte_buffer_t pdu); - void parse_attach_reject(uint32_t lcid, srsran::unique_byte_buffer_t pdu); + void parse_attach_reject(uint32_t lcid, srsran::unique_byte_buffer_t pdu, const uint8_t sec_hdr_type); void parse_authentication_request(uint32_t lcid, srsran::unique_byte_buffer_t pdu, const uint8_t sec_hdr_type); void parse_authentication_reject(uint32_t lcid, srsran::unique_byte_buffer_t pdu); void parse_identity_request(srsran::unique_byte_buffer_t pdu, const uint8_t sec_hdr_type); void parse_security_mode_command(uint32_t lcid, srsran::unique_byte_buffer_t pdu); - void parse_service_reject(uint32_t lcid, srsran::unique_byte_buffer_t pdu); + void parse_service_reject(uint32_t lcid, srsran::unique_byte_buffer_t pdu, const uint8_t sec_hdr_type); void parse_esm_information_request(uint32_t lcid, srsran::unique_byte_buffer_t pdu); void parse_emm_information(uint32_t lcid, srsran::unique_byte_buffer_t pdu); void parse_detach_request(uint32_t lcid, srsran::unique_byte_buffer_t pdu); @@ -240,6 +221,9 @@ private: void airplane_mode_sim_switch_off(); void airplane_mode_sim_switch_on(); + // Misc helpers + void clear_eps_bearer(); + // FSM Helpers void enter_state(emm_state_t state_); void enter_emm_null(); @@ -247,8 +231,8 @@ private: void enter_emm_deregistered_initiated(); // security context persistence file - bool read_ctxt_file(nas_sec_ctxt* ctxt); - bool write_ctxt_file(nas_sec_ctxt ctxt_); + bool read_ctxt_file(nas_sec_ctxt* ctxt_, nas_sec_base_ctxt* ctxt_base_); + bool write_ctxt_file(nas_sec_ctxt ctxt_, nas_sec_base_ctxt ctxt_base_); // ctxt file helpers std::string hex_to_string(uint8_t* hex, int size); @@ -283,27 +267,11 @@ private: return true; } - std::vector split_string(const std::string input) - { - std::vector list; - std::stringstream ss(input); - while (ss.good()) { - std::string substr; - getline(ss, substr, ','); - if (not substr.empty()) { - list.push_back(strtol(substr.c_str(), nullptr, 10)); - } - } - return list; - } - // NAS Idle procedures class plmn_search_proc; // PLMN selection proc (fwd declared) srsran::proc_manager_list_t callbacks; srsran::proc_t plmn_searcher; - - const std::string gw_setup_failure_str = "Failed to setup/configure GW interface"; }; } // namespace srsue diff --git a/srsue/hdr/stack/upper/nas_5g.h b/srsue/hdr/stack/upper/nas_5g.h new file mode 100644 index 000000000..122ab15e2 --- /dev/null +++ b/srsue/hdr/stack/upper/nas_5g.h @@ -0,0 +1,211 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_NAS_5G_H +#define SRSUE_NAS_5G_H + +#include "nas_base.h" +#include "srsran/asn1/nas_5g_ies.h" +#include "srsran/asn1/nas_5g_msg.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/nas_pcap.h" +#include "srsran/common/security.h" +#include "srsran/common/stack_procedure.h" +#include "srsran/common/task_scheduler.h" +#include "srsran/interfaces/ue_gw_interfaces.h" +#include "srsran/interfaces/ue_nas_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/interfaces/ue_usim_interfaces.h" +#include "srsran/srslog/srslog.h" +#include "srsue/hdr/stack/upper/nas_5g_metrics.h" +#include "srsue/hdr/stack/upper/nas_5gmm_state.h" +#include "srsue/hdr/stack/upper/nas_config.h" + +using srsran::byte_buffer_t; + +#define MAX_PDU_SESSIONS 15 +#define MAX_TRANS_ID 255 + +namespace srsue { + +/** + * @brief This class implements the NAS layer of a 5G UE. + * + * The class is *NOT* thread-safe. + * + */ +class nas_5g : public nas_base, public nas_5g_interface_rrc_nr, public nas_5g_interface_procedures +{ +public: + explicit nas_5g(srslog::basic_logger& logger_, srsran::task_sched_handle task_sched_); + virtual ~nas_5g(); + int init(usim_interface_nas* usim_, + rrc_nr_interface_nas_5g* rrc_nr_, + gw_interface_nas* gw_, + const nas_5g_args_t& cfg_); + void stop(); + void run_tti(); + + // Stack+RRC interface + bool is_registered(); + int get_k_amf(srsran::as_key_t& k_amf); + uint32_t get_ul_nas_count(); + + int write_pdu(srsran::unique_byte_buffer_t pdu); + + // timer callback + void timer_expired(uint32_t timeout_id); + + // Stack interface + int switch_on(); + int switch_off(); + int enable_data(); + int disable_data(); + int start_service_request(); + + // Metrics getter + void get_metrics(nas_5g_metrics_t& metrics); + +private: + rrc_nr_interface_nas_5g* rrc_nr = nullptr; + usim_interface_nas* usim = nullptr; + gw_interface_nas* gw = nullptr; + + bool running = false; + bool has_sec_ctxt = false; + bool initial_sec_command = false; + srsran::nas_5g::mobile_identity_5gs_t::guti_5g_s guti_5g; + + srsran::nas_5g::nas_5gs_msg initial_registration_request_stored; + + nas_5g_args_t cfg = {}; + mm5g_state_t state; + + // Security + bool ia5g_caps[8] = {}; + bool ea5g_caps[8] = {}; + + void set_k_gnb_count(uint32_t count); + + // TS 23.003 Sec. 6.2.2 IMEISV's last two octets are Software Version Number (SVN) + // which identifies the software version number of the mobile equipment + const uint8_t ue_svn_oct1 = 0x5; + const uint8_t ue_svn_oct2 = 0x3; + + // timers + srsran::task_sched_handle task_sched; + srsran::timer_handler::unique_timer t3502; // started when registration failure and the attempt counter is equal to 5 + srsran::timer_handler::unique_timer t3510; // started when transmission of REGISTRATION REQUEST message. ON EXPIRY: + // start T3511 or T3502 as specified in subclause 5.5.1.2.7 + srsran::timer_handler::unique_timer t3511; // started when registration failure due to lower layer failure + srsran::timer_handler::unique_timer t3521; // started when detach request is sent + srsran::timer_handler::unique_timer reregistration_timer; // started to trigger delayed re-attach + + // Values according to TS 24.501 Sec 10.2 + const uint32_t t3502_duration_ms = 12 * 60 * 1000; // 12m + const uint32_t t3510_duration_ms = 15 * 1000; // 15s + const uint32_t t3511_duration_ms = 10 * 1000; // 10s + const uint32_t t3521_duration_ms = 15 * 1000; // 15s + const uint32_t reregistration_timer_duration_ms = 2 * 1000; // 2s (arbitrarily chosen to delay re-attach) + + srsran::proc_manager_list_t callbacks; + + // Procedures + // Forward declartion + class registration_procedure; + class pdu_session_establishment_procedure; + + srsran::proc_t registration_proc; + srsran::proc_t pdu_session_establishment_proc; + + // Network information + srsran::nas_5g::network_name_t full_network_name; + + // Message sender + int send_registration_request(); + int send_authentication_response(const uint8_t res[16]); + int send_security_mode_reject(const srsran::nas_5g::cause_5gmm_t::cause_5gmm_type_::options cause); + int send_authentication_failure(const srsran::nas_5g::cause_5gmm_t::cause_5gmm_type_::options cause, + const uint8_t res_star[16]); + int send_security_mode_complete(const srsran::nas_5g::security_mode_command_t& security_mode_command); + int send_registration_complete(); + int send_pdu_session_establishment_request(uint32_t transaction_identity, + uint16_t pdu_session_id, + const pdu_session_cfg_t& pdu_session); + int send_deregistration_request_ue_originating(bool switch_off); + int send_identity_response(srsran::nas_5g::identity_type_5gs_t::identity_types_::options requested_identity_type); + int send_configuration_update_complete(); + + // Helper functions + void fill_security_caps(srsran::nas_5g::ue_security_capability_t& sec_caps); + int apply_security_config(srsran::unique_byte_buffer_t& pdu, uint8_t sec_hdr_type); + bool check_replayed_ue_security_capabilities(srsran::nas_5g::ue_security_capability_t& caps); + + // message handler + int handle_registration_accept(srsran::nas_5g::registration_accept_t& registration_accept); + int handle_registration_reject(srsran::nas_5g::registration_reject_t& registration_reject); + int handle_authentication_request(srsran::nas_5g::authentication_request_t& authentication_request); + int handle_authentication_reject(srsran::nas_5g::authentication_reject_t& authentication_reject); + int handle_identity_request(srsran::nas_5g::identity_request_t& identity_request); + int handle_service_accept(srsran::nas_5g::service_accept_t& service_accept); + int handle_service_reject(srsran::nas_5g::service_reject_t& service_reject); + int handle_security_mode_command(srsran::nas_5g::security_mode_command_t& security_mode_command, + srsran::unique_byte_buffer_t pdu); + int handle_deregistration_accept_ue_terminated( + srsran::nas_5g::deregistration_accept_ue_terminated_t& deregistration_accept_ue_terminated); + int handle_deregistration_accept_ue_originating( + srsran::nas_5g::deregistration_accept_ue_originating_t& deregistration_accept_ue_originating); + int handle_deregistration_request_ue_terminated( + srsran::nas_5g::deregistration_request_ue_terminated_t& deregistration_request_ue_terminated); + int handle_configuration_update_command(srsran::nas_5g::configuration_update_command_t& configuration_update_command); + int handle_dl_nas_transport(srsran::nas_5g::dl_nas_transport_t& dl_nas_transport); + + // message handler container + int handle_n1_sm_information(std::vector payload_container_contents); + + // Transaction ID management + std::array pdu_trans_ids; + uint32_t allocate_next_proc_trans_id(); + void release_proc_trans_id(uint32_t proc_id); + + int trigger_pdu_session_est(); + + // PDU Session Management + int add_pdu_session(uint16_t pdu_session_id, uint16_t pdu_session_type, srsran::nas_5g::pdu_address_t pdu_address); + int init_pdu_sessions(std::vector pdu_session_cfgs); + int configure_pdu_session(uint16_t pdu_session_id); + bool unestablished_pdu_sessions(); + int get_unestablished_pdu_session(uint16_t& pdu_session_id, pdu_session_cfg_t& pdu_session_cfg); + int reset_pdu_sessions(); + uint32_t num_of_est_pdu_sessions(); + + struct pdu_session_t { + bool configured; + bool established; + uint16_t pdu_session_id; + pdu_session_cfg_t pdu_session_cfg; + }; + + std::array pdu_sessions; +}; +} // namespace srsue +#endif \ No newline at end of file diff --git a/srsue/hdr/stack/upper/nas_5g_metrics.h b/srsue/hdr/stack/upper/nas_5g_metrics.h new file mode 100644 index 000000000..be325a9ed --- /dev/null +++ b/srsue/hdr/stack/upper/nas_5g_metrics.h @@ -0,0 +1,36 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_NAS_5G_METRICS_H +#define SRSUE_NAS_5G_METRICS_H + +#include "nas_5gmm_state.h" + +namespace srsue { + +struct nas_5g_metrics_t { + uint32_t nof_active_pdu_sessions; + mm5g_state_t::state_t state; +}; + +} // namespace srsue + +#endif // SRSUE_NAS_5G_METRICS_H diff --git a/srsue/hdr/stack/upper/nas_5g_procedures.h b/srsue/hdr/stack/upper/nas_5g_procedures.h new file mode 100644 index 000000000..60716f75f --- /dev/null +++ b/srsue/hdr/stack/upper/nas_5g_procedures.h @@ -0,0 +1,76 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_NAS_5G_PROCEDURES_H_ +#define SRSUE_NAS_5G_PROCEDURES_H_ + +#include "srsue/hdr/stack/upper/nas_5g.h" + +namespace srsue { + +/** + * @brief 5G NAS registration procedure + * + * Specified in 24 501 V16.7.0 + * 5GMM specific procedures + * 5.5.1 Registration procedure + */ +class nas_5g::registration_procedure +{ +public: + explicit registration_procedure(nas_5g_interface_procedures* parent_nas_); + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step(); + srsran::proc_outcome_t then(); + static const char* name() { return "Registration Procedure"; } + +private: + nas_5g_interface_procedures* parent_nas; +}; + +/** + * @brief 5G NAS (5GSM) UE-requested PDU session establishment procedure + * + * Specified in 24 501 V16.7.0 + * UE-requested 5GSM procedures + * 6.4.1 UE-requested PDU session establishment procedure + */ +class nas_5g::pdu_session_establishment_procedure +{ +public: + explicit pdu_session_establishment_procedure(nas_5g_interface_procedures* parent_nas_, srslog::basic_logger& logger_); + srsran::proc_outcome_t init(const uint16_t pdu_session_id, const pdu_session_cfg_t& pdu_session); + srsran::proc_outcome_t react(const srsran::nas_5g::pdu_session_establishment_accept_t& pdu_session_est_accept); + srsran::proc_outcome_t react(const srsran::nas_5g::pdu_session_establishment_reject_t& pdu_session_est_reject); + srsran::proc_outcome_t step(); + srsran::proc_outcome_t then(); + static const char* name() { return "PDU Session Establishment Procedure"; } + +private: + srslog::basic_logger& logger; + nas_5g_interface_procedures* parent_nas; + uint32_t transaction_identity = 0; + uint16_t pdu_session_id = 0; +}; + +} // namespace srsue + +#endif // SRSUE_NAS_5G_PROCEDURES_H_ \ No newline at end of file diff --git a/srsue/hdr/stack/upper/nas_5gmm_state.h b/srsue/hdr/stack/upper/nas_5gmm_state.h new file mode 100644 index 000000000..9308313d0 --- /dev/null +++ b/srsue/hdr/stack/upper/nas_5gmm_state.h @@ -0,0 +1,99 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_NR_NAS_MM5G_STATE_H +#define SRSUE_NR_NAS_MM5G_STATE_H + +#include "srsran/srslog/srslog.h" +#include +#include + +namespace srsue { + +// 5GMM states (3GPP 24.501 v16.07.0) +class mm5g_state_t +{ +public: + enum class state_t { + null = 0, + deregistered, + registered_initiated, + registered, + deregistered_initiated, + service_request_initiated, + }; + + // 5GMM-DEREGISTERED sub-states (3GPP 24.501 v16.07.0) + enum class deregistered_substate_t { + null = 0, // This should be used when not in mm5g-DEREGISTERED + normal_service, + limited_service, + attempting_to_registration, + plmn_search, + no_supi, + no_cell_available, + e_call_inactive, + initial_registration_needed, + }; + + // 5GMM-DEREGISTERED sub-states (3GPP 24.501 v16.07.0) + enum class registered_substate_t { + null = 0, // This should be used when not in mm5g-REGISTERED + normal_service, + non_allowed_service, + attempting_registration_update, + limited_service, + plmn_search, + no_cell_available, + update_needed, + }; + + mm5g_state_t(srslog::basic_logger& logger_) : logger(logger_) {} + // FSM setters + void set_null(); + void set_deregistered(deregistered_substate_t substate); + void set_deregistered_initiated(); + void set_registered(registered_substate_t substate); + void set_registered_initiated(); + void set_service_request_initiated(); + + // FSM getters + state_t get_state() { return state; } + deregistered_substate_t get_deregistered_substate() { return deregistered_substate; } + registered_substate_t get_registered_substate() { return registered_substate; } + + // Text Helpers + const std::string get_full_state_text(); + +private: + state_t state = state_t::null; + deregistered_substate_t deregistered_substate = deregistered_substate_t::null; + registered_substate_t registered_substate = registered_substate_t::null; + srslog::basic_logger& logger; +}; + +const char* mm5g_state_text(mm5g_state_t::state_t type); +const char* mm5g_deregistered_substate_text(mm5g_state_t::deregistered_substate_t type); +const char* mm5g_registered_substate_text(mm5g_state_t::registered_substate_t type); + +} // namespace srsue + +#endif diff --git a/srsue/hdr/stack/upper/nas_base.h b/srsue/hdr/stack/upper/nas_base.h new file mode 100644 index 000000000..59a5eb3d9 --- /dev/null +++ b/srsue/hdr/stack/upper/nas_base.h @@ -0,0 +1,96 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_NAS_BASE_H +#define SRSUE_NAS_BASE_H + +#include "srsran/asn1/liblte_mme.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/nas_pcap.h" +#include "srsran/common/security.h" +#include "srsran/common/string_helpers.h" +#include "srsran/config.h" + +using srsran::byte_buffer_t; + +namespace srsue { + +class nas_base +{ +public: + nas_base(srslog::basic_logger& logger_, uint32_t mac_offset, uint32_t seq_offset_, uint32_t bearer_id_); + // PCAP + void start_pcap(srsran::nas_pcap* pcap_) { pcap = pcap_; } + +protected: + srslog::basic_logger& logger; + // PCAP + srsran::nas_pcap* pcap = nullptr; + + // Security context + // Base context applies for LTE and 5G + struct nas_sec_base_ctxt { + uint8_t k_nas_enc[32] = {}; + uint8_t k_nas_int[32] = {}; + srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + uint32_t tx_count; + uint32_t rx_count; + }; + + // Only applies for LTE + struct nas_sec_ctxt { + uint8_t ksi; + uint8_t k_asme[32]; + uint32_t k_enb_count; + LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; + }; + + // Only applies for 5G + struct nas_5g_sec_ctxt { + uint8_t ksi; + uint8_t k_amf[32]; + uint32_t k_gnb_count; + }; + + nas_sec_base_ctxt ctxt_base = {}; + nas_sec_ctxt ctxt = {}; + nas_5g_sec_ctxt ctxt_5g = {}; + + int parse_security_algorithm_list(std::string algorithm_string, bool* algorithm_caps); + + // Security + void + integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac); + bool integrity_check(srsran::byte_buffer_t* pdu); + void cipher_encrypt(srsran::byte_buffer_t* pdu); + void cipher_decrypt(srsran::byte_buffer_t* pdu); + + uint32_t mac_offset = 0; + uint32_t seq_offset = 0; + uint32_t bearer_id = 0; + + const std::string gw_setup_failure_str = "Failed to setup/configure GW interface"; +}; + +} // namespace srsue +#endif diff --git a/srsue/hdr/stack/upper/nas_config.h b/srsue/hdr/stack/upper/nas_config.h index 6c41d1971..0d7184e2f 100644 --- a/srsue/hdr/stack/upper/nas_config.h +++ b/srsue/hdr/stack/upper/nas_config.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,20 +22,21 @@ #ifndef SRSUE_NAS_CONFIG_H #define SRSUE_NAS_CONFIG_H +#include "srsran/interfaces/ue_nas_interfaces.h" #include namespace srsue { -typedef struct { +struct nas_sim_args_t { int airplane_t_on_ms = -1; int airplane_t_off_ms = -1; -} nas_sim_args_t; +}; class nas_args_t { public: nas_args_t() : force_imsi_attach(false) {} - + ~nas_args_t() = default; std::string apn_name; std::string apn_protocol; std::string apn_user; @@ -44,6 +45,28 @@ public: std::string eia; std::string eea; nas_sim_args_t sim; + + // 5G args + std::string ia5g; + std::string ea5g; + std::vector pdu_session_cfgs; +}; + +class nas_5g_args_t +{ +public: + nas_5g_args_t() : force_imsi_attach(false) {} + ~nas_5g_args_t() = default; + bool force_imsi_attach; + + // Need EPS sec capabilities in 5G + std::string eia; + std::string eea; + + // 5G Security capabilities + std::string ia5g; + std::string ea5g; + std::vector pdu_session_cfgs; }; } // namespace srsue diff --git a/srsue/hdr/stack/upper/nas_emm_state.h b/srsue/hdr/stack/upper/nas_emm_state.h index 2ae63f6d6..e1d78acfd 100644 --- a/srsue/hdr/stack/upper/nas_emm_state.h +++ b/srsue/hdr/stack/upper/nas_emm_state.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,6 @@ #define SRSUE_NAS_EMM_STATE_H #include "srsran/srslog/srslog.h" -#include #include namespace srsue { @@ -86,7 +85,7 @@ public: const std::string get_full_state_text(); private: - std::atomic state{state_t::null}; // The GW might require to know the NAS state from another thread + state_t state = state_t::null; deregistered_substate_t deregistered_substate = deregistered_substate_t::null; registered_substate_t registered_substate = registered_substate_t::null; srslog::basic_logger& logger = srslog::fetch_basic_logger("NAS"); @@ -97,4 +96,5 @@ const char* emm_deregistered_substate_text(emm_state_t::deregistered_substate_t const char* emm_registered_substate_text(emm_state_t::registered_substate_t type); } // namespace srsue + #endif diff --git a/srsue/hdr/stack/upper/nas_idle_procedures.h b/srsue/hdr/stack/upper/nas_idle_procedures.h index 5c556ce92..067cafa8f 100644 --- a/srsue/hdr/stack/upper/nas_idle_procedures.h +++ b/srsue/hdr/stack/upper/nas_idle_procedures.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/upper/nas_metrics.h b/srsue/hdr/stack/upper/nas_metrics.h index 3508a5eda..763bfa91b 100644 --- a/srsue/hdr/stack/upper/nas_metrics.h +++ b/srsue/hdr/stack/upper/nas_metrics.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/hdr/stack/upper/pcsc_usim.h b/srsue/hdr/stack/upper/pcsc_usim.h index 99a66aa69..b618e202c 100644 --- a/srsue/hdr/stack/upper/pcsc_usim.h +++ b/srsue/hdr/stack/upper/pcsc_usim.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -53,7 +53,14 @@ public: uint8_t* res, int* res_len, uint8_t* k_asme); - + // NAS interface + auth_result_t generate_authentication_response_5g(uint8_t* rand, + uint8_t* autn_enb, + const char* serving_network_name, + uint8_t* abba, + uint32_t abba_len, + uint8_t* res_star, + uint8_t* k_amf); // Helpers virtual std::string get_mnc_str(const uint8_t* imsi_vec, std::string mcc_str) final; diff --git a/srsue/hdr/stack/upper/sdap.h b/srsue/hdr/stack/upper/sdap.h new file mode 100644 index 000000000..0b533609a --- /dev/null +++ b/srsue/hdr/stack/upper/sdap.h @@ -0,0 +1,65 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_SDAP_H +#define SRSUE_SDAP_H + +#include "srsran/common/buffer_pool.h" +#include "srsran/common/common.h" +#include "srsran/common/common_nr.h" +#include "srsran/interfaces/ue_gw_interfaces.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_sdap_interfaces.h" + +namespace srsue { + +class sdap final : public sdap_interface_pdcp_nr, public sdap_interface_gw_nr, public sdap_interface_rrc +{ +public: + explicit sdap(const char* logname); + bool init(pdcp_interface_sdap_nr* pdcp_, srsue::gw_interface_pdcp* gw_); + void stop(); + + // Interface for GW + void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; + + // Interface for PDCP + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final; + + // Interface for RRC + bool set_bearer_cfg(uint32_t lcid, const sdap_interface_rrc::bearer_cfg_t& cfg) final; + +private: + pdcp_interface_sdap_nr* m_pdcp = nullptr; + gw_interface_pdcp* m_gw = nullptr; + + // state + bool running = false; + + // configuration + std::array bearers = {}; + + srslog::basic_logger& logger; +}; + +} // namespace srsue + +#endif // SRSUE_SDAP_H diff --git a/srsue/hdr/stack/upper/test/nas_test_common.h b/srsue/hdr/stack/upper/test/nas_test_common.h new file mode 100644 index 000000000..8db5c7094 --- /dev/null +++ b/srsue/hdr/stack/upper/test/nas_test_common.h @@ -0,0 +1,225 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSUE_NAS_TEST_COMMON +#define SRSUE_NAS_TEST_COMMON + +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/test_common.h" +#include "srsran/common/tsan_options.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/srslog/srslog.h" +#include "srsran/test/ue_test_interfaces.h" +#include "srsue/hdr/stack/upper/gw.h" +#include "srsue/hdr/stack/upper/nas.h" +#include "srsue/hdr/stack/upper/nas_5g.h" +#include "srsue/hdr/stack/upper/usim.h" +#include "srsue/hdr/stack/upper/usim_base.h" + +#define LCID 1 + +uint8_t auth_request_pdu[] = {0x07, 0x52, 0x01, 0x0c, 0x63, 0xa8, 0x54, 0x13, 0xe6, 0xa4, 0xce, 0xd9, + 0x86, 0xfb, 0xe5, 0xce, 0x9b, 0x62, 0x5e, 0x10, 0x67, 0x57, 0xb3, 0xc2, + 0xb9, 0x70, 0x90, 0x01, 0x0c, 0x72, 0x8a, 0x67, 0x57, 0x92, 0x52, 0xb8}; + +uint8_t sec_mode_command_pdu[] = {0x37, 0x4e, 0xfd, 0x57, 0x11, 0x00, 0x07, 0x5d, 0x02, 0x01, 0x02, 0xf0, 0x70, 0xc1}; + +uint8_t attach_accept_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x01, 0x3e, 0x06, 0x00, 0x00, + 0xf1, 0x10, 0x00, 0x01, 0x00, 0x2a, 0x52, 0x01, 0xc1, 0x01, 0x04, 0x1b, 0x07, + 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x06, 0x6d, 0x6e, 0x63, 0x30, 0x30, + 0x31, 0x06, 0x6d, 0x63, 0x63, 0x30, 0x30, 0x31, 0x04, 0x67, 0x70, 0x72, 0x73, + 0x05, 0x01, 0xc0, 0xa8, 0x05, 0x02, 0x27, 0x01, 0x80, 0x50, 0x0b, 0xf6, 0x00, + 0xf1, 0x10, 0x80, 0x01, 0x01, 0x35, 0x16, 0x6d, 0xbc, 0x64, 0x01, 0x00}; + +uint8_t esm_info_req_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5a, 0xd9}; + +uint8_t activate_dedicated_eps_bearer_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0xc5, 0x05, + 0x01, 0x01, 0x07, 0x21, 0x31, 0x00, 0x03, 0x40, 0x08, 0xae, + 0x5d, 0x02, 0x00, 0xc2, 0x81, 0x34, 0x01, 0x4d}; + +uint8_t deactivate_eps_bearer_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0xcd, 0x24}; + +uint16 mcc = 61441; +uint16 mnc = 65281; + +using namespace srsue; + +namespace srsran { + +// fake classes +class pdcp_dummy : public rrc_interface_pdcp, public pdcp_interface_stack +{ +public: + void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) override {} + void write_pdu_bcch_bch(unique_byte_buffer_t pdu) override {} + void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) override {} + void write_pdu_pcch(unique_byte_buffer_t pdu) override {} + void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) override {} + const char* get_rb_name(uint32_t lcid) override { return "lcid"; } + void write_sdu(uint32_t lcid, unique_byte_buffer_t sdu, int sn = -1) override {} + bool is_eps_bearer_id_enabled(uint32_t eps_bearer_id) { return false; } + void notify_pdcp_integrity_error(uint32_t lcid) override {} + bool is_lcid_enabled(uint32_t lcid) override { return false; } +}; + +class rrc_dummy : public rrc_interface_nas +{ +public: + rrc_dummy() : last_sdu_len(0) + { + plmns[0].plmn_id.from_number(mcc, mnc); + plmns[0].tac = 0xffff; + } + void init(nas* nas_) { nas_ptr = nas_; } + void write_sdu(unique_byte_buffer_t sdu) + { + last_sdu_len = sdu->N_bytes; + // printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); + // srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); + } + const char* get_rb_name(uint32_t lcid) { return "lcid"; } + uint32_t get_last_sdu_len() { return last_sdu_len; } + void reset() { last_sdu_len = 0; } + + bool plmn_search() + { + nas_ptr->plmn_search_completed(plmns, 1); + return true; + } + void plmn_select(srsran::plmn_id_t plmn_id){}; + void set_ue_identity(srsran::s_tmsi_t s_tmsi) {} + bool connection_request(srsran::establishment_cause_t cause, srsran::unique_byte_buffer_t sdu) + { + printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); + last_sdu_len = sdu->N_bytes; + srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); + is_connected_flag = true; + nas_ptr->connection_request_completed(true); + return true; + } + bool is_connected() { return is_connected_flag; } + + uint16_t get_mcc() { return mcc; } + uint16_t get_mnc() { return mnc; } + void enable_capabilities() {} + uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) { return 0; } + void paging_completed(bool outcome) {} + bool has_nr_dc() { return false; } + +private: + nas* nas_ptr; + uint32_t last_sdu_len; + nas_interface_rrc::found_plmn_t plmns[nas_interface_rrc::MAX_FOUND_PLMNS]; + bool is_connected_flag = false; +}; + +class rrc_nr_dummy : public rrc_nr_interface_nas_5g +{ +public: + rrc_nr_dummy() : last_sdu_len(0) + { + plmns[0].plmn_id.from_number(mcc, mnc); + plmns[0].tac = 0xffff; + } + void init(srsue::nas_5g* nas_5g_) { nas_5g_ptr = nas_5g_; } + int write_sdu(unique_byte_buffer_t sdu) + { + last_sdu_len = sdu->N_bytes; + // printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); + return SRSRAN_SUCCESS; + } + virtual bool is_connected() { return true; } + virtual int connection_request(srsran::nr_establishment_cause_t cause, srsran::unique_byte_buffer_t sdu) + { + return SRSRAN_SUCCESS; + } + uint16_t get_mcc() { return 0x0000; } + uint16_t get_mnc() { return 0x0000; } + +private: + srsue::nas_5g* nas_5g_ptr; + uint32_t last_sdu_len; + nas_interface_rrc::found_plmn_t plmns[nas_interface_rrc::MAX_FOUND_PLMNS]; +}; +template +class test_stack_dummy : public srsue::stack_test_dummy, public stack_interface_gw, public thread +{ +public: + test_stack_dummy(pdcp_interface_stack* pdcp_) : pdcp(pdcp_), thread("DUMMY STACK") {} + void init(T* nas_) + { + nas = nas_; + start(-1); + } + bool switch_on() + { + task_sched.defer_task([this]() { nas->switch_on(); }); + return true; + } + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { pdcp->write_sdu(lcid, std::move(sdu)); } + bool has_active_radio_bearer(uint32_t eps_bearer_id) { return true; } + + bool is_registered() { return true; } + + bool start_service_request() { return true; } + + void run_thread() + { + running = true; + while (running) { + task_sched.tic(); + task_sched.run_pending_tasks(); + nas->run_tti(); + } + } + void stop() + { + while (not running) { + usleep(1000); + } + running = false; + wait_thread_finish(); + } + pdcp_interface_stack* pdcp = nullptr; + T* nas = nullptr; + std::atomic running = {false}; +}; + +class gw_dummy : public gw_interface_nas, public gw_interface_pdcp +{ + int setup_if_addr(uint32_t eps_bearer_id, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) + { + return SRSRAN_SUCCESS; + } + int deactivate_eps_bearer(const uint32_t eps_bearer_id) { return SRSRAN_SUCCESS; } + int apply_traffic_flow_template(const uint8_t& eps_bearer_id, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) + { + return SRSRAN_SUCCESS; + } + void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) {} + void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} + void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms = 0) {} +}; + +} // namespace srsran + +#endif // SRSUE_NAS_TEST_COMMON \ No newline at end of file diff --git a/srsue/hdr/stack/upper/tft_packet_filter.h b/srsue/hdr/stack/upper/tft_packet_filter.h index 5f669a081..9b472f14e 100644 --- a/srsue/hdr/stack/upper/tft_packet_filter.h +++ b/srsue/hdr/stack/upper/tft_packet_filter.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -68,14 +68,12 @@ class tft_packet_filter_t { public: tft_packet_filter_t(uint8_t eps_bearer_id_, - uint8_t lcid_, const LIBLTE_MME_PACKET_FILTER_STRUCT& tft_, srslog::basic_logger& logger); bool match(const srsran::unique_byte_buffer_t& pdu); bool filter_contains(uint16_t filtertype); - uint8_t eps_bearer_id{}; - uint8_t lcid = {}; + uint8_t eps_bearer_id = {}; uint8_t id = {}; uint8_t eval_precedence = {}; uint32_t active_filters = {}; @@ -117,15 +115,15 @@ public: explicit tft_pdu_matcher(srslog::basic_logger& logger) : logger(logger) {} ~tft_pdu_matcher(){}; - void set_default_lcid(const uint8_t lcid); - uint8_t check_tft_filter_match(const srsran::unique_byte_buffer_t& pdu); + void reset(); + + int check_tft_filter_match(const srsran::unique_byte_buffer_t& pdu, uint8_t& eps_bearer_id); int apply_traffic_flow_template(const uint8_t& erab_id, - const uint8_t& lcid, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft); + void delete_tft_for_eps_bearer(const uint8_t eps_bearer_id); private: srslog::basic_logger& logger; - uint8_t default_lcid = 0; std::mutex tft_mutex; typedef std::map tft_filter_map_t; tft_filter_map_t tft_filter_map; diff --git a/srsue/hdr/stack/upper/usim.h b/srsue/hdr/stack/upper/usim.h index 8985b7d2e..b703d41a3 100644 --- a/srsue/hdr/stack/upper/usim.h +++ b/srsue/hdr/stack/upper/usim.h @@ -1,5 +1,5 @@ īģŋ/** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -45,21 +45,18 @@ public: int* res_len, uint8_t* k_asme); + auth_result_t generate_authentication_response_5g(uint8_t* rand, + uint8_t* autn_enb, + const char* serving_network_name, + uint8_t* abba, + uint32_t abba_len, + uint8_t* res_star, + uint8_t* k_amf); + private: - auth_result_t gen_auth_res_milenage(uint8_t* rand, - uint8_t* autn_enb, - uint16_t mcc, - uint16_t mnc, - uint8_t* res, - int* res_len, - uint8_t* k_asme); - auth_result_t gen_auth_res_xor(uint8_t* rand, - uint8_t* autn_enb, - uint16_t mcc, - uint16_t mnc, - uint8_t* res, - int* res_len, - uint8_t* k_asme); + auth_result_t + gen_auth_res_milenage(uint8_t* rand, uint8_t* autn_enb, uint8_t* res, int* res_len, uint8_t* ak_xor_sqn); + auth_result_t gen_auth_res_xor(uint8_t* rand, uint8_t* autn_enb, uint8_t* res, int* res_len, uint8_t* ak_xor_sqn); // Helpers virtual std::string get_mnc_str(const uint8_t* imsi_vec, std::string mcc_str) final; diff --git a/srsue/hdr/stack/upper/usim_base.h b/srsue/hdr/stack/upper/usim_base.h index 1ed536e39..f8286b872 100644 --- a/srsue/hdr/stack/upper/usim_base.h +++ b/srsue/hdr/stack/upper/usim_base.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -51,6 +51,7 @@ class usim_args_t { public: usim_args_t() : using_op(false) {} + ~usim_args_t() = default; std::string mode; std::string algo; bool using_op; @@ -78,6 +79,9 @@ public: std::string get_imei_str() final; bool get_imsi_vec(uint8_t* imsi_, uint32_t n) final; + bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) final; + bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) final; + bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) final; bool get_imei_vec(uint8_t* imei_, uint32_t n) final; bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) final; @@ -102,9 +106,24 @@ public: void restore_keys_from_failed_ho(srsran::as_security_config_t* as_ctx) final; // NR RRC interface + void generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) final; bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) final; bool update_nr_context(srsran::as_security_config_t* sec_cfg) final; + // 5G NAS interface + virtual auth_result_t generate_authentication_response_5g(uint8_t* rand, + uint8_t* autn_enb, + const char* serving_network_name, + uint8_t* abba, + uint32_t abba_len, + uint8_t* res_star, + uint8_t* k_amf) = 0; + + bool generate_nas_keys_5g(uint8_t* k_amf, + uint8_t* k_nas_enc, + uint8_t* k_nas_int, + srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); // Helpers std::string get_mcc_str(const uint8_t* imsi_vec); virtual std::string get_mnc_str(const uint8_t* imsi_vec, std::string mcc_str) = 0; @@ -131,6 +150,8 @@ protected: uint8_t k_enb_initial[KEY_LEN] = {}; uint8_t auts[AKA_AUTS_LEN] = {}; + srsran::as_key_t k_gnb_initial = {}; + // Current K_eNB context (K_eNB, NH and NCC) srsran::k_enb_context_t k_enb_ctx = {}; srsran::k_gnb_context_t k_gnb_ctx = {}; diff --git a/srsue/hdr/ue.h b/srsue/hdr/ue.h index 3c495380f..9047731fc 100644 --- a/srsue/hdr/ue.h +++ b/srsue/hdr/ue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -70,6 +70,8 @@ typedef struct { bool metrics_csv_append; int metrics_csv_flush_period_sec; std::string metrics_csv_filename; + bool metrics_json_enable; + std::string metrics_json_filename; bool tracing_enable; std::string tracing_filename; std::size_t tracing_buffcapacity; @@ -112,6 +114,7 @@ public: private: // UE consists of a radio, a PHY and a stack element std::unique_ptr phy; + std::unique_ptr dummy_phy; std::unique_ptr radio; std::unique_ptr stack; std::unique_ptr gw_inst; diff --git a/srsue/hdr/ue_metrics_interface.h b/srsue/hdr/ue_metrics_interface.h index 7645412a1..ca2518d9a 100644 --- a/srsue/hdr/ue_metrics_interface.h +++ b/srsue/hdr/ue_metrics_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -27,10 +27,11 @@ #include "phy/phy_metrics.h" #include "srsran/common/metrics_hub.h" #include "srsran/radio/radio_metrics.h" +#include "srsran/rlc/rlc_metrics.h" #include "srsran/system/sys_metrics.h" -#include "srsran/upper/rlc_metrics.h" #include "stack/mac/mac_metrics.h" #include "stack/rrc/rrc_metrics.h" +#include "stack/rrc_nr/rrc_nr_metrics.h" #include "stack/upper/gw_metrics.h" #include "stack/upper/nas_metrics.h" @@ -43,7 +44,7 @@ typedef struct { srsran::rlc_metrics_t rlc; nas_metrics_t nas; rrc_metrics_t rrc; - rrc_metrics_t rrc_nr; + rrc_nr_metrics_t rrc_nr; } stack_metrics_t; typedef struct { diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 37a3a40a3..12f82d049 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -20,6 +20,7 @@ add_subdirectory(phy) add_subdirectory(stack) +add_subdirectory(test) # Link libstdc++ and libgcc if(BUILD_STATIC) @@ -30,10 +31,10 @@ if (RPATH) set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) endif (RPATH) -add_executable(srsue main.cc ue.cc metrics_stdout.cc metrics_csv.cc) +add_executable(srsue main.cc ue.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) set(SRSUE_SOURCES srsue_phy srsue_stack srsue_upper srsue_mac srsue_rrc srslog system) -set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_radio srsran_upper rrc_asn1 srslog system) +set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_radio srsran_gtpu srsran_rlc srsran_pdcp rrc_asn1 srslog support system) set(SRSUE_SOURCES ${SRSUE_SOURCES} srsue_nr_stack srsue_rrc_nr srsue_mac_nr) set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) @@ -41,7 +42,8 @@ set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) target_link_libraries(srsue ${SRSUE_SOURCES} ${SRSRAN_SOURCES} ${CMAKE_THREAD_LIBS_INIT} - ${Boost_LIBRARIES}) + ${Boost_LIBRARIES} + ${ATOMIC_LIBS}) if (RPATH) set_target_properties(srsue PROPERTIES INSTALL_RPATH ".") @@ -74,4 +76,4 @@ else(NOT ${BUILDUE_CMD} STREQUAL "") message(STATUS "No post-build-UE command defined") endif (NOT ${BUILDUE_CMD} STREQUAL "") -install(TARGETS srsue DESTINATION ${RUNTIME_DIR}) +install(TARGETS srsue DESTINATION ${RUNTIME_DIR} OPTIONAL) diff --git a/srsue/src/main.cc b/srsue/src/main.cc index f915e1e30..ac51d2d98 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,24 +23,30 @@ #include "srsran/common/config_file.h" #include "srsran/common/crash_handler.h" #include "srsran/common/metrics_hub.h" -#include "srsran/common/signal_handler.h" +#include "srsran/common/multiqueue.h" +#include "srsran/common/tsan_options.h" #include "srsran/srslog/event_trace.h" #include "srsran/srslog/srslog.h" #include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" +#include "srsran/support/signal_handler.h" #include "srsran/version.h" #include "srsue/hdr/metrics_csv.h" +#include "srsue/hdr/metrics_json.h" #include "srsue/hdr/metrics_stdout.h" #include "srsue/hdr/ue.h" #include #include +#include #include #include #include #include #include +#include #include -extern bool simulate_rlf; +extern std::atomic simulate_rlf; using namespace std; using namespace srsue; @@ -50,8 +56,10 @@ namespace bpo = boost::program_options; * Local static variables ***********************************************************************/ -static bool do_metrics = false; -static metrics_stdout* metrics_screen = nullptr; +static bool do_metrics = false; +static metrics_stdout* metrics_screen = nullptr; +static srslog::sink* log_sink = nullptr; +static std::atomic running = {true}; /********************************************************************** * Program arguments processing @@ -60,7 +68,9 @@ string config_file; static int parse_args(all_args_t* args, int argc, char* argv[]) { - bool use_standard_lte_rates = false; + bool use_standard_lte_rates = false; + std::string scs_khz, ssb_scs_khz; // temporary value to store integer + std::string cfr_mode; // Command line only options bpo::options_description general("General options"); @@ -73,7 +83,6 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) common.add_options() ("ue.radio", bpo::value(&args->rf.type)->default_value("multi"), "Type of the radio [multi]") ("ue.phy", bpo::value(&args->phy.type)->default_value("lte"), "Type of the PHY [lte]") - ("ue.stack", bpo::value(&args->stack.type)->default_value("lte"), "Type of the upper stack [lte, nr]") ("rf.srate", bpo::value(&args->rf.srate_hz)->default_value(0.0), "Force Tx and Rx sampling rate in Hz") ("rf.freq_offset", bpo::value(&args->rf.freq_offset)->default_value(0), "(optional) Frequency offset") @@ -124,9 +133,14 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) ("rat.eutra.ul_freq", bpo::value(&args->phy.ul_freq)->default_value(-1), "Uplink Frequency (if positive overrides EARFCN)") ("rat.eutra.nof_carriers", bpo::value(&args->phy.nof_lte_carriers)->default_value(1), "Number of carriers") - ("rat.nr.bands", bpo::value(&args->stack.rrc_nr.supported_bands_nr_str)->default_value("78"), "Supported NR bands") + ("rat.nr.bands", bpo::value(&args->stack.rrc_nr.supported_bands_nr_str)->default_value("3"), "Supported NR bands") ("rat.nr.nof_carriers", bpo::value(&args->phy.nof_nr_carriers)->default_value(0), "Number of NR carriers") - ("rat.nr.max_nof_prb", bpo::value(&args->phy.nr_max_nof_prb)->default_value(106), "Maximum NR carrier bandwidth in PRB") + ("rat.nr.max_nof_prb", bpo::value(&args->phy.nr_max_nof_prb)->default_value(52), "Maximum NR carrier bandwidth in PRB") + ("rat.nr.dl_nr_arfcn", bpo::value(&args->stack.rrc_nr.dl_nr_arfcn)->default_value(368500), "DL ARFCN of NR cell") + ("rat.nr.ssb_nr_arfcn", bpo::value(&args->stack.rrc_nr.ssb_nr_arfcn)->default_value(368410), "SSB ARFCN of NR cell") + ("rat.nr.nof_prb", bpo::value(&args->stack.rrc_nr.nof_prb)->default_value(52), "Actual NR carrier bandwidth in PRB") + ("rat.nr.scs", bpo::value(&scs_khz)->default_value("15"), "PDSCH subcarrier spacing in kHz") + ("rat.nr.ssb_scs", bpo::value(&ssb_scs_khz)->default_value("15"), "SSB subcarrier spacing in kHz") ("rrc.feature_group", bpo::value(&args->stack.rrc.feature_group)->default_value(0xe6041000), "Hex value of the featureGroupIndicators field in the" "UECapabilityInformation message. Default 0xe6041000") @@ -235,6 +249,14 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) ("channel.ul.hst.fd_hz", bpo::value(&args->phy.ul_channel_args.hst_fd_hz)->default_value(+750.0f), "Doppler frequency in Hz") ("channel.ul.hst.init_time_s", bpo::value(&args->phy.ul_channel_args.hst_init_time_s)->default_value(0), "Initial time in seconds") + /* CFR section */ + ("cfr.enable", bpo::value(&args->phy.cfr_args.enable)->default_value(args->phy.cfr_args.enable), "CFR enable") + ("cfr.mode", bpo::value(&cfr_mode)->default_value("manual"), "CFR mode") + ("cfr.manual_thres", bpo::value(&args->phy.cfr_args.manual_thres)->default_value(args->phy.cfr_args.manual_thres), "Fixed manual clipping threshold for CFR manual mode") + ("cfr.strength", bpo::value(&args->phy.cfr_args.strength)->default_value(args->phy.cfr_args.strength), "CFR ratio between amplitude-limited vs original signal (0 to 1)") + ("cfr.auto_target_papr", bpo::value(&args->phy.cfr_args.auto_target_papr)->default_value(args->phy.cfr_args.auto_target_papr), "Signal PAPR target (in dB) in CFR auto modes") + ("cfr.ema_alpha", bpo::value(&args->phy.cfr_args.ema_alpha)->default_value(args->phy.cfr_args.ema_alpha), "Alpha coefficient for the power average in auto_ema mode (0 to 1)") + /* PHY section */ ("phy.worker_cpu_mask", bpo::value(&args->phy.worker_cpu_mask)->default_value(-1), @@ -383,6 +405,10 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->phy.force_ul_amplitude)->default_value(0.0), "Forces the peak amplitude in the PUCCH, PUSCH and SRS (set 0.0 to 1.0, set to 0 or negative for disabling)") + ("phy.detect_cp", + bpo::value(&args->phy.detect_cp)->default_value(false), + "enable CP length detection") + ("phy.in_sync_rsrp_dbm_th", bpo::value(&args->phy.in_sync_rsrp_dbm_th)->default_value(-130.0f), "RSRP threshold (in dBm) above which the UE considers to be in-sync") @@ -407,6 +433,15 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->phy.force_N_id_2)->default_value(-1), "Force using a specific PSS (set to -1 to allow all PSSs).") + ("phy.force_N_id_1", + bpo::value(&args->phy.force_N_id_1)->default_value(-1), + "Force using a specific SSS (set to -1 to allow all SSSs).") + + // PHY NR args + ("phy.nr.store_pdsch_ko", + bpo::value(&args->phy.nr_store_pdsch_ko)->default_value(false), + "Dumps the PDSCH baseband samples into a file on KO reception.") + // UE simulation args ("sim.airplane_t_on_ms", bpo::value(&args->stack.nas.sim.airplane_t_on_ms)->default_value(-1), @@ -437,6 +472,14 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->general.metrics_csv_flush_period_sec)->default_value(-1), "Periodicity in s to flush CSV file to disk (-1 for auto)") + ("general.metrics_json_enable", + bpo::value(&args->general.metrics_json_enable)->default_value(false), + "Write UE metrics to a JSON file") + + ("general.metrics_json_filename", + bpo::value(&args->general.metrics_json_filename)->default_value("/tmp/ue_metrics.json"), + "Metrics JSON filename") + ("general.tracing_enable", bpo::value(&args->general.tracing_enable)->default_value(false), "Events tracing") @@ -453,10 +496,6 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) bpo::value(&args->stack.have_tti_time_stats)->default_value(true), "Calculate TTI execution statistics") - // NR params - ("vnf.type", bpo::value(&args->phy.vnf_args.type)->default_value("ue"), "VNF instance type [gnb,ue]") - ("vnf.addr", bpo::value(&args->phy.vnf_args.bind_addr)->default_value("localhost"), "Address to bind VNF interface") - ("vnf.port", bpo::value(&args->phy.vnf_args.bind_port)->default_value(3334), "Bind port") ; // Positional options - config file location @@ -527,6 +566,13 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) args->stack.usim.using_op = vm.count("usim.op"); } + // parse the CFR mode string + args->phy.cfr_args.mode = srsran_cfr_str2mode(cfr_mode.c_str()); + if (args->phy.cfr_args.mode == SRSRAN_CFR_THR_INVALID) { + cout << "Error, invalid CFR mode: " << cfr_mode << endl; + exit(1); + } + // Apply all_level to any unset layers if (vm.count("log.all_level")) { if (!vm.count("log.rf_level")) { @@ -605,6 +651,14 @@ static int parse_args(all_args_t* args, int argc, char* argv[]) srsran_use_standard_symbol_size(use_standard_lte_rates); + args->stack.rrc_nr.scs = srsran_subcarrier_spacing_from_str(scs_khz.c_str()); + args->stack.rrc_nr.ssb_scs = srsran_subcarrier_spacing_from_str(ssb_scs_khz.c_str()); + if (args->stack.rrc_nr.scs == srsran_subcarrier_spacing_invalid || + args->stack.rrc_nr.ssb_scs == srsran_subcarrier_spacing_invalid) { + cout << "Invalid subcarrier spacing config" << endl; + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } @@ -628,8 +682,11 @@ static void* input_loop(void*) metrics_screen->toggle_print(do_metrics); } } else if (key == "rlf") { - simulate_rlf = true; + simulate_rlf.store(true, std::memory_order_relaxed); cout << "Sending Radio Link Failure" << endl; + } else if (key == "flush") { + srslog::flush(); + cout << "Flushed log file buffers" << endl; } else if (key == "q") { // let the signal handler do the job raise(SIGTERM); @@ -645,9 +702,25 @@ static size_t fixup_log_file_maxsize(int x) return (x < 0) ? 0 : size_t(x) * 1024u; } +extern "C" void srsran_dft_exit(); +static void emergency_cleanup_handler(void* data) +{ + srslog::flush(); + if (log_sink) { + log_sink->flush(); + } + srsran_dft_exit(); +} + +static void signal_handler() +{ + running = false; +} + int main(int argc, char* argv[]) { - srsran_register_signal_handler(); + srsran_register_signal_handler(signal_handler); + add_emergency_cleanup_handler(emergency_cleanup_handler, nullptr); srsran_debug_handle_crash(argc, argv); all_args_t args = {}; @@ -684,6 +757,10 @@ int main(int argc, char* argv[]) srsran::check_scaling_governor(args.rf.device_name); + if (mlockall((uint32_t)MCL_CURRENT | (uint32_t)MCL_FUTURE) == -1) { + fprintf(stderr, "Failed to `mlockall`: %d", errno); + } + // Create UE instance. srsue::ue ue; if (ue.init(args)) { @@ -708,6 +785,18 @@ int main(int argc, char* argv[]) } } + // Set up the JSON log channel used by metrics. + srslog::sink& json_sink = + srslog::fetch_file_sink(args.general.metrics_json_filename, 0, false, srslog::create_json_formatter()); + srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {}); + json_channel.set_enabled(args.general.metrics_json_enable); + + srsue::metrics_json json_metrics(json_channel); + if (args.general.metrics_json_enable) { + metricshub.add_listener(&json_metrics); + json_metrics.set_ue_handle(&ue); + } + pthread_t input; pthread_create(&input, nullptr, &input_loop, &args); diff --git a/srsue/src/metrics_csv.cc b/srsue/src/metrics_csv.cc index 81f225ef4..0dcffd615 100644 --- a/srsue/src/metrics_csv.cc +++ b/srsue/src/metrics_csv.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -56,11 +56,13 @@ metrics_csv::~metrics_csv() void metrics_csv::set_ue_handle(ue_metrics_interface* ue_) { + std::lock_guard lock(mutex); ue = ue_; } void metrics_csv::set_flush_period(const uint32_t flush_period_sec_) { + std::lock_guard lock(mutex); flush_period_sec = flush_period_sec_; } diff --git a/srsue/src/metrics_json.cc b/srsue/src/metrics_json.cc new file mode 100644 index 000000000..cd017cbab --- /dev/null +++ b/srsue/src/metrics_json.cc @@ -0,0 +1,244 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/metrics_json.h" +#include "srsran/srslog/context.h" + +using namespace srsue; + +void metrics_json::set_ue_handle(ue_metrics_interface* ue_) +{ + std::lock_guard lock(mutex); + ue = ue_; +} + +namespace { + +/// Shared metrics between containers. +DECLARE_METRIC("pci", metric_pci, uint32_t, ""); +DECLARE_METRIC("dl_bitrate", metric_dl_brate, float, ""); +DECLARE_METRIC("ul_bitrate", metric_ul_brate, float, ""); +DECLARE_METRIC("rsrp", metric_rsrp, float, ""); + +/// MAC container. +DECLARE_METRIC("dl_bler", metric_dl_bler, float, ""); +DECLARE_METRIC("ul_bler", metric_ul_bler, float, ""); +DECLARE_METRIC("ul_buff", metric_ul_buff, uint32_t, ""); +DECLARE_METRIC_SET("mac_container", + mset_mac_container, + metric_dl_brate, + metric_dl_bler, + metric_ul_brate, + metric_ul_bler, + metric_ul_buff); + +/// Carrier container. +DECLARE_METRIC("earfcn", metric_earfcn, uint32_t, ""); +DECLARE_METRIC("pathloss", metric_pathloss, float, ""); +DECLARE_METRIC("cfo", metric_cfo, float, ""); +DECLARE_METRIC("dl_snr", metric_dl_snr, float, ""); +DECLARE_METRIC("dl_mcs", metric_dl_mcs, float, ""); +DECLARE_METRIC("ul_mcs", metric_ul_mcs, float, ""); +DECLARE_METRIC("ul_ta", metric_ul_ta, float, ""); +DECLARE_METRIC("distance_km", metric_distance_km, float, ""); +DECLARE_METRIC("speed_kmph", metric_speed_kmph, float, ""); +DECLARE_METRIC_SET("carrier_container", + mset_carrier_container, + metric_earfcn, + metric_pci, + metric_rsrp, + metric_pathloss, + metric_cfo, + metric_dl_snr, + metric_dl_mcs, + metric_ul_mcs, + metric_ul_ta, + metric_distance_km, + metric_speed_kmph, + mset_mac_container); +DECLARE_METRIC_LIST("carrier_list", mlist_carriers, std::vector); + +/// GW container. +DECLARE_METRIC_SET("gw_container", mset_gw_container, metric_dl_brate, metric_ul_brate); + +/// RRC container. +DECLARE_METRIC("rrc_state", metric_rrc_state, std::string, ""); +DECLARE_METRIC_SET("rrc_container", mset_rrc_container, metric_rrc_state); + +/// Neighbour cell list. +DECLARE_METRIC_SET("neighbour_cell_container", mset_neighbour_cell_container, metric_pci, metric_rsrp, metric_cfo); +DECLARE_METRIC_LIST("neighbour_cell_list", mlist_neighbours, std::vector); + +/// NAS container. +DECLARE_METRIC("emm_state", metric_emm_state, std::string, ""); +DECLARE_METRIC_SET("nas_container", mset_nas_container, metric_emm_state); + +/// RF container. +DECLARE_METRIC("rf_o", metric_rf_o, uint32_t, ""); +DECLARE_METRIC("rf_u", metric_rf_u, uint32_t, ""); +DECLARE_METRIC("rf_l", metric_rf_l, uint32_t, ""); +DECLARE_METRIC_SET("rf_container", mset_rf_container, metric_rf_o, metric_rf_u, metric_rf_l); + +/// System memory container. +DECLARE_METRIC("proc_realmem_percent", metric_proc_rmem_percent, uint32_t, ""); +DECLARE_METRIC("proc_realmem_kB", metric_proc_rmem_kB, uint32_t, ""); +DECLARE_METRIC("proc_vmem_kB", metric_proc_vmem_kB, uint32_t, ""); +DECLARE_METRIC("sys_mem_usage_percent", metric_sys_mem_percent, uint32_t, ""); +DECLARE_METRIC_SET("sys_memory_container", + mset_sys_mem_container, + metric_proc_rmem_percent, + metric_proc_rmem_kB, + metric_proc_vmem_kB, + metric_sys_mem_percent); + +/// System CPU container +DECLARE_METRIC("proc_cpu_usage", metric_proc_cpu_usage, uint32_t, ""); +DECLARE_METRIC("proc_thread_count", metric_thread_count, uint32_t, ""); +DECLARE_METRIC("sys_core_usage", metric_proc_core_usage, uint32_t, ""); +DECLARE_METRIC_SET("cpu_core_container", mset_cpu_core_container, metric_proc_core_usage); +DECLARE_METRIC_LIST("cpu_core_list", mlist_cpu_core_list, std::vector); +DECLARE_METRIC_SET("sys_cpu_container", + mset_sys_cpu_container, + metric_proc_cpu_usage, + metric_thread_count, + mlist_cpu_core_list); + +/// Metrics root object. +DECLARE_METRIC("type", metric_type_tag, std::string, ""); +DECLARE_METRIC("timestamp", metric_timestamp_tag, double, ""); + +/// Metrics context. +using metric_context_t = srslog::build_context_type; + +} // namespace + +/// Returns the current time in seconds with ms precision since UNIX epoch. +static double get_time_stamp() +{ + auto tp = std::chrono::system_clock::now().time_since_epoch(); + return std::chrono::duration_cast(tp).count() * 1e-3; +} + +void metrics_json::set_metrics(const ue_metrics_t& metrics, const uint32_t period_usec) +{ + std::lock_guard lock(mutex); + if (!ue) { + return; + } + + metric_context_t ctx("JSON Metrics"); + + // Fill root object. + ctx.write("metrics"); + + // Fill cc container. + auto& carrier_list = ctx.get(); + carrier_list.resize(metrics.phy.nof_active_cc); + for (uint32_t i = 0, e = carrier_list.size(); i != e; ++i) { + if (metrics.stack.mac[i].nof_tti == 0) { + continue; + } + + auto& carrier = carrier_list[i]; + + // PHY. + carrier.write(metrics.phy.info[i].dl_earfcn); + carrier.write(metrics.phy.info[i].pci); + + carrier.write(metrics.phy.ch[i].rsrp); + carrier.write(metrics.phy.ch[i].pathloss); + + carrier.write(metrics.phy.sync[i].cfo); + + carrier.write(metrics.phy.ch[i].sinr); + carrier.write(metrics.phy.dl[i].mcs); + carrier.write(metrics.phy.ul[i].mcs); + carrier.write(metrics.phy.sync[i].ta_us); + carrier.write(metrics.phy.sync[i].distance_km); + carrier.write(metrics.phy.sync[i].speed_kmph); + + // MAC + carrier.get().write(metrics.stack.mac[i].rx_brate / + metrics.stack.mac[i].nof_tti * 1e-3); + carrier.get().write( + (metrics.stack.mac[i].rx_pkts > 0) ? (float)100 * metrics.stack.mac[i].rx_errors / metrics.stack.mac[i].rx_pkts + : 0); + carrier.get().write(metrics.stack.mac[i].tx_brate / + metrics.stack.mac[i].nof_tti * 1e-3); + carrier.get().write( + (metrics.stack.mac[i].tx_pkts > 0) ? (float)100 * metrics.stack.mac[i].tx_errors / metrics.stack.mac[i].tx_pkts + : 0); + carrier.get().write(metrics.stack.mac[i].ul_buffer); + } + + // Fill GW container. + ctx.get().write(metrics.gw.dl_tput_mbps); + ctx.get().write(metrics.gw.ul_tput_mbps); + + // Fill RRC container. + ctx.get().write(rrc_state_text[metrics.stack.rrc.state]); + + // Fill neighbour list. + auto& neighbour_list = ctx.get(); + neighbour_list.resize(metrics.stack.rrc.neighbour_cells.size()); + for (uint32_t i = 0, e = neighbour_list.size(); i != e; ++i) { + auto& neigbour = neighbour_list[i]; + neigbour.write(metrics.stack.rrc.neighbour_cells[i].pci); + neigbour.write(metrics.stack.rrc.neighbour_cells[i].rsrp); + neigbour.write(metrics.stack.rrc.neighbour_cells[i].cfo_hz); + } + + // Fill NAS container. + ctx.get().write(emm_state_text(metrics.stack.nas.state)); + + // Fill RF container. + ctx.get().write(metrics.rf.rf_o); + ctx.get().write(metrics.rf.rf_u); + ctx.get().write(metrics.rf.rf_l); + + // Fill system memory container. + ctx.get().write(metrics.sys.process_realmem); + ctx.get().write(metrics.sys.process_realmem_kB); + ctx.get().write(metrics.sys.process_virtualmem_kB); + ctx.get().write(metrics.sys.system_mem); + + // Fill system CPU container. + ctx.get().write(metrics.sys.process_cpu_usage); + ctx.get().write(metrics.sys.thread_count); + auto& core_list = ctx.get().get(); + core_list.resize(metrics.sys.cpu_count); + for (uint32_t i = 0, e = core_list.size(); i != e; ++i) { + core_list[i].write(metrics.sys.cpu_load[i]); + } + + // Log the context. + ctx.write(get_time_stamp()); + log_c(ctx); +} diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc index 1ec009582..4f46d723c 100644 --- a/srsue/src/metrics_stdout.cc +++ b/srsue/src/metrics_stdout.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -62,11 +62,13 @@ char const* const prefixes[2][9] = { void metrics_stdout::set_ue_handle(ue_metrics_interface* ue_) { + std::lock_guard lock(mutex); ue = ue_; } void metrics_stdout::toggle_print(bool b) { + std::lock_guard lock(mutex); do_print = b; } @@ -75,22 +77,22 @@ void metrics_stdout::print_table(const bool display_neighbours, const bool is_nr if (is_nr) { if (display_neighbours) { fmt::print( - "---------Signal----------|-Neighbour-|-----------------DL-----------------|-----------UL-----------\n"); + "---------Signal-----------|-Neighbour-|-----------------DL-----------------|-----------UL-----------\n"); fmt::print( - "rat pci rsrp pl cfo | pci rsrp | mcs snr iter brate bler ta_us | mcs buff brate bler\n"); + "rat pci rsrp pl cfo | pci rsrp | mcs snr iter brate bler ta_us | mcs buff brate bler\n"); } else { - fmt::print("---------Signal----------|-----------------DL-----------------|-----------UL-----------\n"); - fmt::print("rat pci rsrp pl cfo | mcs snr iter brate bler ta_us | mcs buff brate bler\n"); + fmt::print("---------Signal-----------|-----------------DL-----------------|-----------UL-----------\n"); + fmt::print("rat pci rsrp pl cfo | mcs snr iter brate bler ta_us | mcs buff brate bler\n"); } } else { if (display_neighbours) { fmt::print( - "---------Signal----------|-Neighbour-|-----------------DL-----------------|-----------UL-----------\n"); + "---------Signal-----------|-Neighbour-|-----------------DL-----------------|-----------UL-----------\n"); fmt::print( - " cc pci rsrp pl cfo | pci rsrp | mcs snr iter brate bler ta_us | mcs buff brate bler\n"); + " cc pci rsrp pl cfo | pci rsrp | mcs snr iter brate bler ta_us | mcs buff brate bler\n"); } else { - fmt::print("---------Signal----------|-----------------DL-----------------|-----------UL-----------\n"); - fmt::print(" cc pci rsrp pl cfo | mcs snr iter brate bler ta_us | mcs buff brate bler\n"); + fmt::print("---------Signal-----------|-----------------DL-----------------|-----------UL-----------\n"); + fmt::print(" cc pci rsrp pl cfo | mcs snr iter brate bler ta_us | mcs buff brate bler\n"); } } table_has_neighbours = display_neighbours; @@ -118,7 +120,7 @@ void metrics_stdout::set_metrics_helper(const phy_metrics_t& phy, } fmt::print(" {:>4}", int(phy.ch[r].rsrp)); - fmt::print(" {:>2}", int(phy.ch[r].pathloss)); + fmt::print(" {:>4}", int(phy.ch[r].pathloss)); fmt::print(" {:>5.5}", float_to_eng_string(phy.sync[r].cfo, 2)); // Find strongest neighbour for this EARFCN (cells are ordered) @@ -142,7 +144,11 @@ void metrics_stdout::set_metrics_helper(const phy_metrics_t& phy, fmt::print(" |"); fmt::print(" {:>2}", int(phy.dl[r].mcs)); - fmt::print(" {:>3}", int(phy.ch[r].sinr)); + if (std::isnan(phy.ch[r].sinr) || std::isinf(phy.ch[r].sinr)) { + fmt::print(" {:>3}", "n/a"); + } else { + fmt::print(" {:>3}", int(phy.ch[r].sinr)); + } fmt::print(" {:>4.1f}", phy.dl[r].fec_iters); fmt::print(" {:>6.6}", float_to_eng_string((float)mac[r].rx_brate / (mac[r].nof_tti * 1e-3), 2)); @@ -170,6 +176,7 @@ void metrics_stdout::set_metrics_helper(const phy_metrics_t& phy, void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t period_usec) { + std::lock_guard lock(mutex); if (ue == nullptr) { return; } @@ -183,7 +190,7 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per return; } - if (metrics.stack.rrc.state != RRC_STATE_CONNECTED) { + if (metrics.stack.rrc.state != RRC_STATE_CONNECTED && metrics.stack.rrc_nr.state != RRC_NR_STATE_CONNECTED) { fmt::print("--- disconnected ---\n"); return; } @@ -195,25 +202,30 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per display_neighbours |= metrics.stack.rrc.neighbour_cells.size() > 0; } - bool is_nr = metrics.phy_nr.nof_active_cc > 0; + bool has_lte = metrics.phy.nof_active_cc > 0; + bool has_nr = metrics.phy_nr.nof_active_cc > 0; // print table header every 10 reports if (++n_reports > 10) { - print_table(display_neighbours, is_nr); + print_table(display_neighbours, has_nr); } // also print table header if neighbours are added/removed in between if (display_neighbours != table_has_neighbours) { - print_table(display_neighbours, is_nr); + print_table(display_neighbours, has_nr); } - for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { - set_metrics_helper(metrics.phy, metrics.stack.mac, metrics.stack.rrc, display_neighbours, r, false, !is_nr); + if (has_lte) { + for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { + set_metrics_helper(metrics.phy, metrics.stack.mac, metrics.stack.rrc, display_neighbours, r, false, !has_nr); + } } - for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) { - // Assumption LTE is followed by the NR carriers. - set_metrics_helper(metrics.phy_nr, metrics.stack.mac_nr, metrics.stack.rrc, display_neighbours, r, true, !is_nr); + if (has_nr) { + for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) { + // Assumption LTE is followed by the NR carriers. + set_metrics_helper(metrics.phy_nr, metrics.stack.mac_nr, metrics.stack.rrc, display_neighbours, r, true, !has_nr); + } } if (metrics.rf.rf_error) { diff --git a/srsue/src/phy/CMakeLists.txt b/srsue/src/phy/CMakeLists.txt index 91266fd07..077b26e5d 100644 --- a/srsue/src/phy/CMakeLists.txt +++ b/srsue/src/phy/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,12 +18,11 @@ # and at http://www.gnu.org/licenses/. # +add_subdirectory(test) + file(GLOB_RECURSE SOURCES "*.cc") add_library(srsue_phy STATIC ${SOURCES}) if(ENABLE_GUI AND SRSGUI_FOUND) target_link_libraries(srsue_phy ${SRSGUI_LIBRARIES}) -endif() - -set(SOURCES_NR "../phy/vnf_phy_nr.cc") -add_library(srsue_phy_nr STATIC ${SOURCES_NR}) +endif() \ No newline at end of file diff --git a/srsue/src/phy/lte/cc_worker.cc b/srsue/src/phy/lte/cc_worker.cc index 7ee696e75..520a55b12 100644 --- a/srsue/src/phy/lte/cc_worker.cc +++ b/srsue/src/phy/lte/cc_worker.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -56,6 +56,8 @@ cc_worker::cc_worker(uint32_t cc_idx_, uint32_t max_prb, srsue::phy_common* phy_ cc_idx = cc_idx_; phy = phy_; + srsran_cfr_cfg_t cfr_config = phy->get_cfr_config(); + signal_buffer_max_samples = 3 * SRSRAN_SF_LEN_PRB(max_prb); for (uint32_t i = 0; i < phy->args->nof_rx_ant; i++) { @@ -81,6 +83,11 @@ cc_worker::cc_worker(uint32_t cc_idx_, uint32_t max_prb, srsue::phy_common* phy_ return; } + if (srsran_ue_ul_set_cfr(&ue_ul, &cfr_config) < SRSRAN_SUCCESS) { + Error("Setting the CFR"); + return; + } + phy->set_ue_dl_cfg(&ue_dl_cfg); phy->set_ue_ul_cfg(&ue_ul_cfg); phy->set_pdsch_cfg(&ue_dl_cfg.cfg.pdsch); @@ -94,6 +101,7 @@ cc_worker::cc_worker(uint32_t cc_idx_, uint32_t max_prb, srsue::phy_common* phy_ chest_default_cfg = ue_dl_cfg.chest_cfg; + srsran_softbuffer_rx_init(&mch_softbuffer, 100); // Set default PHY params reset(); @@ -113,6 +121,7 @@ cc_worker::~cc_worker() free(signal_buffer_rx[i]); } } + srsran_softbuffer_rx_free(&mch_softbuffer); srsran_ue_dl_free(&ue_dl); srsran_ue_ul_free(&ue_ul); } @@ -121,15 +130,15 @@ void cc_worker::reset() { // constructor sets defaults srsran::phy_cfg_t empty_cfg; - set_config_unlocked(empty_cfg); + set_config_nolock(empty_cfg); } -void cc_worker::reset_cell_unlocked() +void cc_worker::reset_cell_nolock() { cell_initiated = false; } -bool cc_worker::set_cell_unlocked(srsran_cell_t cell_) +bool cc_worker::set_cell_nolock(srsran_cell_t cell_) { if (cell.id != cell_.id || !cell_initiated) { cell = cell_; @@ -180,7 +189,7 @@ void cc_worker::set_tti(uint32_t tti) sf_cfg_ul.shortened = false; } -void cc_worker::set_cfo_unlocked(float cfo) +void cc_worker::set_cfo_nolock(float cfo) { ue_ul_cfg.cfo_value = cfo; } @@ -190,17 +199,12 @@ float cc_worker::get_ref_cfo() const return ue_dl.chest_res.cfo; } -void cc_worker::set_tdd_config_unlocked(srsran_tdd_config_t config) +void cc_worker::set_tdd_config_nolock(srsran_tdd_config_t config) { sf_cfg_dl.tdd_config = config; sf_cfg_ul.tdd_config = config; } -void cc_worker::enable_pregen_signals_unlocked(bool enabled) -{ - pregen_enabled = enabled; -} - /************ * * Downlink Functions @@ -263,24 +267,29 @@ bool cc_worker::work_dl_regular() // If found a dci for this carrier, generate a grant, pass it to MAC and decode the associated PDSCH if (has_dl_grant) { - // Read last TB from last retx for this pid - for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { - ue_dl_cfg.cfg.pdsch.grant.last_tbs[i] = phy->last_dl_tbs[dci_dl.pid][cc_idx][i]; - } - // Generate PHY grant - if (srsran_ue_dl_dci_to_pdsch_grant(&ue_dl, &sf_cfg_dl, &ue_dl_cfg, &dci_dl, &ue_dl_cfg.cfg.pdsch.grant)) { - Info("Converting DCI message to DL dci"); - return false; - } + // PDCCH order has no associated PDSCH to decode + if (not dci_dl.is_pdcch_order) { + // Read last TB from last retx for this pid + for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { + ue_dl_cfg.cfg.pdsch.grant.last_tbs[i] = phy->last_dl_tbs[dci_dl.pid][cc_idx][i]; + } + // Generate PHY grant + if (srsran_ue_dl_dci_to_pdsch_grant(&ue_dl, &sf_cfg_dl, &ue_dl_cfg, &dci_dl, &ue_dl_cfg.cfg.pdsch.grant)) { + Info("Converting DCI message to DL dci"); + return false; + } - // Save TB for next retx - for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { - phy->last_dl_tbs[dci_dl.pid][cc_idx][i] = ue_dl_cfg.cfg.pdsch.grant.last_tbs[i]; + // Save TB for next retx + for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { + phy->last_dl_tbs[dci_dl.pid][cc_idx][i] = ue_dl_cfg.cfg.pdsch.grant.last_tbs[i]; + } + + // Set RNTI + ue_dl_cfg.cfg.pdsch.rnti = dci_dl.rnti; + } else { + ue_dl_cfg.cfg.pdsch.rnti = dci_dl.rnti; + ue_dl_cfg.cfg.pdsch.grant.tb[0].tbs = 0; } - - // Set RNTI - ue_dl_cfg.cfg.pdsch.rnti = dci_dl.rnti; - // Generate MAC grant mac_interface_phy_lte::mac_grant_dl_t mac_grant = {}; dl_phy_to_mac_grant(&ue_dl_cfg.cfg.pdsch.grant, &dci_dl, &mac_grant); @@ -294,7 +303,12 @@ bool cc_worker::work_dl_regular() // Decode PDSCH decode_pdsch(ack_resource, &dl_action, dl_ack); - // Informs Stack about the decoding status + // Informs Stack about the decoding status, send NACK if cell is in process of re-selection + if (phy->cell_is_selecting) { + for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { + dl_ack[i] = false; + } + } phy->stack->tb_decoded(cc_idx, mac_grant, dl_ack); } @@ -341,12 +355,12 @@ bool cc_worker::work_dl_mbsfn(srsran_mbsfn_cfg_t mbsfn_cfg) srsran_ra_dl_compute_nof_re(&cell, &sf_cfg_dl, &pmch_cfg.pdsch_cfg.grant); // Send grant to MAC and get action for this TB, then call tb_decoded to unlock MAC - phy->stack->new_mch_dl(pmch_cfg.pdsch_cfg.grant, &dl_action); + new_mch_dl(&dl_action); bool mch_decoded = true; if (!decode_pmch(&dl_action, &mbsfn_cfg)) { mch_decoded = false; } - phy->stack->mch_decoded((uint32_t)pmch_cfg.pdsch_cfg.grant.tb[0].tbs / 8, mch_decoded); + phy->stack->mch_decoded((uint32_t)pmch_cfg.pdsch_cfg.grant.tb[0].tbs / 8, mch_decoded, mch_payload_buffer); } else if (mbsfn_cfg.is_mcch) { // release lock in phy_common phy->set_mch_period_stop(0); @@ -363,9 +377,14 @@ void cc_worker::dl_phy_to_mac_grant(srsran_pdsch_grant_t* srsue::mac_interface_phy_lte::mac_grant_dl_t* mac_grant) { /* Fill MAC dci structure */ - mac_grant->pid = dl_dci->pid; - mac_grant->rnti = dl_dci->rnti; - mac_grant->tti = CURRENT_TTI; + mac_grant->pid = dl_dci->pid; + mac_grant->rnti = dl_dci->rnti; + mac_grant->tti = CURRENT_TTI; + mac_grant->is_pdcch_order = dl_dci->is_pdcch_order; + if (dl_dci->is_pdcch_order) { + mac_grant->preamble_idx = dl_dci->preamble_idx; + mac_grant->prach_mask_idx = dl_dci->prach_mask_idx; + } for (int i = 0; i < SRSRAN_MAX_CODEWORDS; i++) { mac_grant->tb[i].ndi = dl_dci->tb[i].ndi; @@ -563,6 +582,11 @@ void cc_worker::decode_phich() void cc_worker::update_measurements(std::vector& serving_cells, cf_t* rssi_power_buffer) { + // Do not update any measurement if the CC is not configured to prevent false or inaccurate data + if (not phy->cell_state.is_configured(cc_idx)) { + return; + } + phy->update_measurements( cc_idx, ue_dl.chest_res, sf_cfg_dl, ue_dl_cfg.cfg.pdsch.rs_power, serving_cells, rssi_power_buffer); } @@ -793,12 +817,10 @@ bool cc_worker::encode_uplink(mac_interface_phy_lte::tb_action_ul_t* action, srs void cc_worker::set_uci_sr(srsran_uci_data_t* uci_data) { - Debug("set_uci_sr() query: sr_enabled=%d, last_tx_tti=%d", phy->sr_enabled, phy->sr_last_tx_tti); - if (srsran_ue_ul_gen_sr(&ue_ul_cfg, &sf_cfg_ul, uci_data, phy->sr_enabled)) { - if (phy->sr_enabled) { - phy->sr_last_tx_tti = CURRENT_TTI_TX; - phy->sr_enabled = false; - Debug("set_uci_sr() sending SR: sr_enabled=%d, last_tx_tti=%d", phy->sr_enabled, phy->sr_last_tx_tti); + Debug("set_uci_sr() query: sr_enabled=%d, last_tx_tti=%d", phy->sr.is_triggered(), phy->sr.get_last_tx_tti()); + if (srsran_ue_ul_gen_sr(&ue_ul_cfg, &sf_cfg_ul, uci_data, phy->sr.is_triggered())) { + if (phy->sr.set_last_tx_tti(CURRENT_TTI_TX)) { + Debug("set_uci_sr() sending SR: sr_enabled=true, last_tx_tti=%d", CURRENT_TTI_TX); } } } @@ -821,10 +843,16 @@ uint32_t cc_worker::get_wideband_cqi() void cc_worker::set_uci_periodic_cqi(srsran_uci_data_t* uci_data) { + // Load last reported RI + ue_dl_cfg.last_ri = phy->last_ri; + srsran_ue_dl_gen_cqi_periodic(&ue_dl, &ue_dl_cfg, get_wideband_cqi(), CURRENT_TTI_TX, uci_data); // Store serving cell index for logging purposes uci_data->cfg.cqi.scell_index = cc_idx; + + // Store the reported RI + phy->last_ri = ue_dl_cfg.last_ri; } void cc_worker::set_uci_aperiodic_cqi(srsran_uci_data_t* uci_data) @@ -874,23 +902,16 @@ void cc_worker::set_uci_ack(srsran_uci_data_t* uci_data, /* Translates RRC structs into PHY structs */ -void cc_worker::set_config_unlocked(srsran::phy_cfg_t& phy_cfg) +void cc_worker::set_config_nolock(const srsran::phy_cfg_t& phy_cfg) { // Save configuration ue_dl_cfg.cfg = phy_cfg.dl_cfg; ue_ul_cfg.ul_cfg = phy_cfg.ul_cfg; phy->set_pdsch_cfg(&ue_dl_cfg.cfg.pdsch); - - // Update signals - if (pregen_enabled) { - Info("Pre-generating UL signals..."); - srsran_ue_ul_pregen_signals(&ue_ul, &ue_ul_cfg); - Info("Done pre-generating signals worker..."); - } } -void cc_worker::upd_config_dci_unlocked(srsran_dci_cfg_t& dci_cfg) +void cc_worker::upd_config_dci_nolock(const srsran_dci_cfg_t& dci_cfg) { ue_dl_cfg.cfg.dci = dci_cfg; } @@ -910,5 +931,14 @@ int cc_worker::read_pdsch_d(cf_t* pdsch_d) return ue_dl_cfg.cfg.pdsch.grant.nof_re; } +void cc_worker::new_mch_dl(mac_interface_phy_lte::tb_action_dl_t* action) +{ + action->generate_ack = false; + action->tb[0].enabled = true; + action->tb[0].payload = mch_payload_buffer; + action->tb[0].softbuffer.rx = &mch_softbuffer; + srsran_softbuffer_rx_reset_cb(&mch_softbuffer, 1); +} + } // namespace lte } // namespace srsue diff --git a/srsue/src/phy/lte/sf_worker.cc b/srsue/src/phy/lte/sf_worker.cc index 996ecf8ac..ff7fd22a7 100644 --- a/srsue/src/phy/lte/sf_worker.cc +++ b/srsue/src/phy/lte/sf_worker.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -73,15 +73,15 @@ sf_worker::~sf_worker() } } -void sf_worker::reset_cell_unlocked(uint32_t cc_idx) +void sf_worker::reset_cell_nolock(uint32_t cc_idx) { - cc_workers[cc_idx]->reset_cell_unlocked(); + cc_workers[cc_idx]->reset_cell_nolock(); } -bool sf_worker::set_cell_unlocked(uint32_t cc_idx, srsran_cell_t cell_) +bool sf_worker::set_cell_nolock(uint32_t cc_idx, srsran_cell_t cell_) { if (cc_idx < cc_workers.size()) { - if (!cc_workers[cc_idx]->set_cell_unlocked(cell_)) { + if (!cc_workers[cc_idx]->set_cell_nolock(cell_)) { Error("Setting cell for cc=%d", cc_idx); return false; } @@ -112,20 +112,15 @@ uint32_t sf_worker::get_buffer_len() return cc_workers.at(0)->get_buffer_len(); } -void sf_worker::set_tti(uint32_t tti_) +void sf_worker::set_context(const srsran::phy_common_interface::worker_context_t& w_ctx) { - tti = tti_; + context.copy(w_ctx); for (auto& cc_worker : cc_workers) { - cc_worker->set_tti(tti); + cc_worker->set_tti(w_ctx.sf_idx); } - logger.set_context(tti); -} - -void sf_worker::set_tx_time(const srsran::rf_timestamp_t& tx_time_) -{ - tx_time.copy(tx_time_); + logger.set_context(w_ctx.sf_idx); } void sf_worker::set_prach(cf_t* prach_ptr_, float prach_power_) @@ -134,33 +129,26 @@ void sf_worker::set_prach(cf_t* prach_ptr_, float prach_power_) prach_power = prach_power_; } -void sf_worker::set_cfo_unlocked(const uint32_t& cc_idx, float cfo) +void sf_worker::set_cfo_nolock(const uint32_t& cc_idx, float cfo) { - cc_workers[cc_idx]->set_cfo_unlocked(cfo); + cc_workers[cc_idx]->set_cfo_nolock(cfo); } -void sf_worker::set_tdd_config_unlocked(srsran_tdd_config_t config) +void sf_worker::set_tdd_config_nolock(srsran_tdd_config_t config) { for (auto& cc_worker : cc_workers) { - cc_worker->set_tdd_config_unlocked(config); + cc_worker->set_tdd_config_nolock(config); } tdd_config = config; } -void sf_worker::enable_pregen_signals_unlocked(bool enabled) -{ - for (auto& cc_worker : cc_workers) { - cc_worker->enable_pregen_signals_unlocked(enabled); - } -} - -void sf_worker::set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg) +void sf_worker::set_config_nolock(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg) { if (cc_idx < cc_workers.size()) { - cc_workers[cc_idx]->set_config_unlocked(phy_cfg); + cc_workers[cc_idx]->set_config_nolock(phy_cfg); if (cc_idx > 0) { // Update DCI config for PCell - cc_workers[0]->upd_config_dci_unlocked(phy_cfg.dl_cfg.dci); + cc_workers[0]->upd_config_dci_nolock(phy_cfg.dl_cfg.dci); } } else { Error("Setting config for cc=%d; Invalid cc_idx", cc_idx); @@ -169,9 +157,10 @@ void sf_worker::set_config_unlocked(uint32_t cc_idx, srsran::phy_cfg_t phy_cfg) void sf_worker::work_imp() { + uint32_t tti = context.sf_idx; srsran::rf_buffer_t tx_signal_ptr = {}; if (!cell_initiated) { - phy->worker_end(this, false, tx_signal_ptr, tx_time, false); + phy->worker_end(context, false, tx_signal_ptr); return; } @@ -198,6 +187,7 @@ void sf_worker::work_imp() } } } + tx_signal_ptr.set_nof_samples(nof_samples); /***** Uplink Generation + Transmission *******/ @@ -232,7 +222,6 @@ void sf_worker::work_imp() } } } - tx_signal_ptr.set_nof_samples(nof_samples); // Set PRACH buffer signal pointer if (prach_ptr) { @@ -242,7 +231,7 @@ void sf_worker::work_imp() } // Call worker_end to transmit the signal - phy->worker_end(this, tx_signal_ready, tx_signal_ptr, tx_time, false); + phy->worker_end(context, tx_signal_ready, tx_signal_ptr); if (rx_signal_ok) { update_measurements(); diff --git a/srsue/src/phy/lte/worker_pool.cc b/srsue/src/phy/lte/worker_pool.cc index 6787b9c94..9159c6288 100644 --- a/srsue/src/phy/lte/worker_pool.cc +++ b/srsue/src/phy/lte/worker_pool.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,7 +23,34 @@ namespace srsue { namespace lte { -worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers) {} +void worker_pool::phy_cfg_stash_t::set_cfg(const srsran::phy_cfg_t& c) +{ + for (auto it = pending.begin(); it < pending.end(); it++) { + *it = true; + } + + cfg = c; +} + +bool worker_pool::phy_cfg_stash_t::is_pending(uint32_t sf_idx) +{ + if (sf_idx >= (uint32_t)pending.size()) { + return false; + } + return pending[sf_idx]; +} + +const srsran::phy_cfg_t& worker_pool::phy_cfg_stash_t::get_cfg(uint32_t sf_idx) +{ + if (sf_idx < (uint32_t)pending.size()) { + pending[sf_idx] = false; + } + return cfg; +} + +worker_pool::worker_pool(uint32_t max_workers) : + pool(max_workers), phy_cfg_stash{{max_workers, max_workers, max_workers, max_workers, max_workers}} +{} bool worker_pool::init(phy_common* common, int prio) { @@ -48,7 +75,23 @@ void worker_pool::start_worker(sf_worker* w) sf_worker* worker_pool::wait_worker(uint32_t tti) { - return (sf_worker*)pool.wait_worker(tti); + sf_worker* w = (sf_worker*)pool.wait_worker(tti); + if (w == nullptr) { + return w; + } + + // Protect configuration + std::unique_lock lock(phy_cfg_mutex); + + // Iterate all CC searching for a pending configuration + uint32_t worker_id = w->get_id(); + for (uint32_t cc_idx = 0; cc_idx < SRSRAN_MAX_CARRIERS; cc_idx++) { + if (phy_cfg_stash[cc_idx].is_pending(worker_id)) { + w->set_config_nolock(cc_idx, phy_cfg_stash[cc_idx].get_cfg(worker_id)); + } + } + + return w; } sf_worker* worker_pool::wait_worker_id(uint32_t id) @@ -61,5 +104,16 @@ void worker_pool::stop() pool.stop(); } +void worker_pool::set_config(uint32_t cc_idx, const srsran::phy_cfg_t& phy_cfg) +{ + // Protect CC index bounds + if (cc_idx >= SRSRAN_MAX_CARRIERS) { + return; + } + + // Protect configuration + std::unique_lock lock(phy_cfg_mutex); + phy_cfg_stash[cc_idx].set_cfg(phy_cfg); +} }; // namespace lte }; // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index f81a8f22a..d2a4be375 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,39 +20,52 @@ */ #include "srsue/hdr/phy/nr/cc_worker.h" +#include "srsran/common/band_helper.h" #include "srsran/common/buffer_pool.h" +#include "srsran/common/string_helpers.h" #include "srsran/srsran.h" namespace srsue { namespace nr { -cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state* phy_state_) : - cc_idx(cc_idx_), phy(phy_state_), logger(log) + +cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state& phy_state_, const srsran::phy_cfg_nr_t& cfg) : + cc_idx(cc_idx_), phy(phy_state_), cfg(cfg), logger(log) { cf_t* rx_buffer_c[SRSRAN_MAX_PORTS] = {}; // Allocate buffers - buffer_sz = SRSRAN_SF_LEN_PRB(phy->args.dl.nof_max_prb) * 5; - for (uint32_t i = 0; i < phy_state_->args.dl.nof_rx_antennas; i++) { + buffer_sz = SRSRAN_SF_LEN_PRB(phy.args.dl.nof_max_prb) * 5; + for (uint32_t i = 0; i < phy.args.dl.nof_rx_antennas; i++) { rx_buffer[i] = srsran_vec_cf_malloc(buffer_sz); rx_buffer_c[i] = rx_buffer[i]; tx_buffer[i] = srsran_vec_cf_malloc(buffer_sz); } - if (srsran_ue_dl_nr_init(&ue_dl, rx_buffer.data(), &phy_state_->args.dl) < SRSRAN_SUCCESS) { + if (srsran_ue_dl_nr_init(&ue_dl, rx_buffer.data(), &phy.args.dl) < SRSRAN_SUCCESS) { ERROR("Error initiating UE DL NR"); return; } - if (srsran_ue_ul_nr_init(&ue_ul, tx_buffer[0], &phy_state_->args.ul) < SRSRAN_SUCCESS) { + if (srsran_ue_ul_nr_init(&ue_ul, tx_buffer[0], &phy.args.ul) < SRSRAN_SUCCESS) { ERROR("Error initiating UE DL NR"); return; } + + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_measure = true; + ssb_args.enable_decode = true; + ssb_args.enable_search = true; + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + ERROR("Error initiating SSB"); + return; + } } cc_worker::~cc_worker() { srsran_ue_dl_nr_free(&ue_dl); srsran_ue_ul_nr_free(&ue_ul); + srsran_ssb_free(&ssb); for (cf_t* p : rx_buffer) { if (p != nullptr) { free(p); @@ -65,27 +78,35 @@ cc_worker::~cc_worker() } } -bool cc_worker::update_cfg() +void cc_worker::update_cfg(const srsran::phy_cfg_nr_t& new_config) { - if (srsran_ue_dl_nr_set_carrier(&ue_dl, &phy->cfg.carrier) < SRSRAN_SUCCESS) { + cfg = new_config; + configured = false; + + if (srsran_ue_dl_nr_set_carrier(&ue_dl, &cfg.carrier) < SRSRAN_SUCCESS) { ERROR("Error setting carrier"); - return false; + return; } - if (srsran_ue_ul_nr_set_carrier(&ue_ul, &phy->cfg.carrier) < SRSRAN_SUCCESS) { + if (srsran_ue_ul_nr_set_carrier(&ue_ul, &cfg.carrier) < SRSRAN_SUCCESS) { ERROR("Error setting carrier"); - return false; + return; } - srsran_dci_cfg_nr_t dci_cfg = phy->cfg.get_dci_cfg(); - if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &phy->cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) { + srsran_dci_cfg_nr_t dci_cfg = cfg.get_dci_cfg(); + if (srsran_ue_dl_nr_set_pdcch_config(&ue_dl, &cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) { logger.error("Error setting NR PDCCH configuration"); - return false; + return; + } + + srsran_ssb_cfg_t ssb_cfg = cfg.get_ssb_cfg(); + ssb_cfg.srate_hz = srsran_min_symbol_sz_rb(cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(cfg.carrier.scs); + if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { + logger.error("Error setting SSB configuration"); + return; } configured = true; - - return true; } void cc_worker::set_tti(uint32_t tti) @@ -97,7 +118,7 @@ void cc_worker::set_tti(uint32_t tti) cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx) { - if (antenna_idx >= phy->args.dl.nof_rx_antennas) { + if (antenna_idx >= phy.args.dl.nof_rx_antennas) { return nullptr; } @@ -106,7 +127,7 @@ cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx) cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx) { - if (antenna_idx >= phy->args.dl.nof_rx_antennas) { + if (antenna_idx >= phy.args.dl.nof_rx_antennas) { return nullptr; } @@ -121,7 +142,7 @@ uint32_t cc_worker::get_buffer_len() void cc_worker::decode_pdcch_dl() { std::array dci_rx = {}; - srsue::mac_interface_phy_nr::sched_rnti_t rnti = phy->stack->get_dl_sched_rnti_nr(dl_slot_cfg.idx); + srsue::mac_interface_phy_nr::sched_rnti_t rnti = phy.stack->get_dl_sched_rnti_nr(dl_slot_cfg.idx); // Skip search if no valid RNTI is given if (rnti.id == SRSRAN_INVALID_RNTI) { @@ -145,21 +166,25 @@ void cc_worker::decode_pdcch_dl() logger.info("PDCCH: cc=%d, %s", cc_idx, str.data()); } + if (logger.debug.enabled()) { + // log coreset info + srsran_coreset_t* coreset = &ue_dl.cfg.coreset[dci_rx[i].ctx.coreset_id]; + std::array coreset_str; + srsran_coreset_to_str(coreset, coreset_str.data(), coreset_str.size()); + logger.info("PDCCH: coreset=%d, %s", cc_idx, coreset_str.data()); + } + // Enqueue UL grants - phy->set_dl_pending_grant(dl_slot_cfg, dci_rx[i]); + phy.set_dl_pending_grant(cfg, dl_slot_cfg, dci_rx[i]); } if (logger.debug.enabled()) { for (uint32_t i = 0; i < ue_dl.pdcch_info_count; i++) { - const srsran_ue_dl_nr_pdcch_info_t* info = &ue_dl.pdcch_info[i]; - logger.debug("PDCCH: dci=%s, rnti=%x, crst_id=%d, ss_type=%d, ncce=%d, al=%d, EPRE=%+.2f, RSRP=%+.2f, corr=%.3f; " - "nof_bits=%d; crc=%s;", - srsran_dci_format_nr_string(info->dci_ctx.format), - info->dci_ctx.rnti, - info->dci_ctx.coreset_id, - info->dci_ctx.ss_type, - info->dci_ctx.location.ncce, - info->dci_ctx.location.L, + const srsran_ue_dl_nr_pdcch_info_t* info = &ue_dl.pdcch_info[i]; + std::array dci_ctx = {}; + srsran_dci_ctx_to_str(&info->dci_ctx, dci_ctx.data(), (uint32_t)dci_ctx.size()); + logger.debug("PDCCH: %sEPRE=%+.2f, RSRP=%+.2f, corr=%.3f nof_bits=%d crc=%s", + dci_ctx.data(), info->measure.epre_dBfs, info->measure.rsrp_dBfs, info->measure.norm_corr, @@ -172,7 +197,7 @@ void cc_worker::decode_pdcch_dl() void cc_worker::decode_pdcch_ul() { std::array dci_rx = {}; - srsue::mac_interface_phy_nr::sched_rnti_t rnti = phy->stack->get_ul_sched_rnti_nr(ul_slot_cfg.idx); + srsue::mac_interface_phy_nr::sched_rnti_t rnti = phy.stack->get_ul_sched_rnti_nr(ul_slot_cfg.idx); // Skip search if no valid RNTI is given if (rnti.id == SRSRAN_INVALID_RNTI) { @@ -180,15 +205,15 @@ void cc_worker::decode_pdcch_ul() } // Search for grants - int n_dl = + int n_ul = srsran_ue_dl_nr_find_ul_dci(&ue_dl, &dl_slot_cfg, rnti.id, rnti.type, dci_rx.data(), (uint32_t)dci_rx.size()); - if (n_dl < SRSRAN_SUCCESS) { + if (n_ul < SRSRAN_SUCCESS) { logger.error("Error decoding UL NR-PDCCH"); return; } // Iterate over all received grants - for (int i = 0; i < n_dl; i++) { + for (int i = 0; i < n_ul; i++) { // Log found DCI if (logger.info.enabled()) { std::array str; @@ -197,10 +222,289 @@ void cc_worker::decode_pdcch_ul() } // Enqueue UL grants - phy->set_ul_pending_grant(dl_slot_cfg.idx, dci_rx[i]); + phy.set_ul_pending_grant(cfg, dl_slot_cfg, dci_rx[i]); } } +bool cc_worker::decode_pdsch_dl() +{ + // Get DL grant for this TTI, if available + uint32_t pid = 0; + srsran_sch_cfg_nr_t pdsch_cfg = {}; + srsran_harq_ack_resource_t ack_resource = {}; + if (not phy.get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) { + // Early return if no grant was available + return true; + } + // Notify MAC about PDSCH grant + mac_interface_phy_nr::tb_action_dl_t dl_action = {}; + mac_interface_phy_nr::mac_nr_grant_dl_t mac_dl_grant = {}; + mac_dl_grant.rnti = pdsch_cfg.grant.rnti; + mac_dl_grant.pid = pid; + mac_dl_grant.rv = pdsch_cfg.grant.tb[0].rv; + mac_dl_grant.ndi = pdsch_cfg.grant.tb[0].ndi; + mac_dl_grant.tbs = pdsch_cfg.grant.tb[0].tbs / 8; + mac_dl_grant.tti = dl_slot_cfg.idx; + phy.stack->new_grant_dl(0, mac_dl_grant, &dl_action); + + // Abort if MAC says it doesn't need the TB + if (not dl_action.tb.enabled) { + // Force positive ACK + if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { + phy.set_pending_ack(dl_slot_cfg.idx, ack_resource, true); + } + + logger.info("Decoding not required. Skipping PDSCH. ack_tti_tx=%d", TTI_ADD(dl_slot_cfg.idx, ack_resource.k1)); + return true; + } + + // Get data buffer + srsran::unique_byte_buffer_t data = srsran::make_byte_buffer(); + if (data == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return false; + } + data->N_bytes = pdsch_cfg.grant.tb[0].tbs / 8U; + + // Initialise PDSCH Result + srsran_pdsch_res_nr_t pdsch_res = {}; + pdsch_res.tb[0].payload = data->msg; + pdsch_cfg.grant.tb[0].softbuffer.rx = dl_action.tb.softbuffer; + + // Decode actual PDSCH transmission + if (srsran_ue_dl_nr_decode_pdsch(&ue_dl, &dl_slot_cfg, &pdsch_cfg, &pdsch_res) < SRSRAN_SUCCESS) { + ERROR("Error decoding PDSCH"); + return false; + } + + // Logging + if (logger.info.enabled()) { + str_info_t str; + srsran_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, &pdsch_res, str.data(), (uint32_t)str.size()); + + if (logger.debug.enabled()) { + str_extra_t str_extra; + srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra.data(), (uint32_t)str_extra.size()); + logger.info(pdsch_res.tb[0].payload, + pdsch_cfg.grant.tb[0].tbs / 8, + "PDSCH: cc=%d pid=%d %s cfo=%.1f\n%s", + cc_idx, + pid, + str.data(), + ue_dl.chest.cfo, + str_extra.data()); + } else { + logger.info(pdsch_res.tb[0].payload, + pdsch_res.tb[0].crc ? pdsch_cfg.grant.tb[0].tbs / 8 : 0, + "PDSCH: cc=%d pid=%d %s ack_tti_tx=%d", + cc_idx, + pid, + str.data(), + TTI_ADD(dl_slot_cfg.idx, ack_resource.k1)); + } + } + + if (not pdsch_res.tb[0].crc and phy.args.store_pdsch_ko) { + static unsigned unique_filename_id = 0; + unsigned id = ++unique_filename_id; + + fmt::memory_buffer filename; + fmt::format_to(filename, "pdsch_ko_bb_samples_{}.bin", id); + + srsran_filesink_t filesink = {}; + if (srsran_filesink_init(&filesink, (char*)srsran::to_c_str(filename), SRSRAN_COMPLEX_FLOAT_BIN) == 0) { + srsran_filesink_write(&filesink, (void*)rx_buffer[0], ue_dl.fft[0].sf_sz); + srsran_filesink_free(&filesink); + + str_extra_t str_extra; + srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra.data(), (uint32_t)str_extra.size()); + logger.info("PDSCH: KO detected, dumping PDSCH baseband samples into file '%s'" + "\n" + "Information: cc_idx=%d pid=%d slot_idx=%d sf_len=%d\n%s", + srsran::to_c_str(filename), + cc_idx, + pid, + dl_slot_cfg.idx, + ue_dl.fft[0].sf_sz, + str_extra.data()); + } + } + + // Enqueue PDSCH ACK information only if the RNTI is type C + if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { + phy.set_pending_ack(dl_slot_cfg.idx, ack_resource, pdsch_res.tb[0].crc); + } + + // Notify MAC about PDSCH decoding result + mac_interface_phy_nr::tb_action_dl_result_t mac_dl_result = {}; + mac_dl_result.rx_slot_idx = dl_slot_cfg.idx; // Rx TTI for this TB (required for correct Msg3 timing) + mac_dl_result.ack = pdsch_res.tb[0].crc; + mac_dl_result.payload = mac_dl_result.ack ? std::move(data) : nullptr; // only pass data when successful + phy.stack->tb_decoded(cc_idx, mac_dl_grant, std::move(mac_dl_result)); + + if (pdsch_res.tb[0].crc) { + // Generate DL metrics + dl_metrics_t dl_m = {}; + dl_m.mcs = pdsch_cfg.grant.tb[0].mcs; + dl_m.fec_iters = pdsch_res.tb[0].avg_iter; + dl_m.evm = pdsch_res.evm[0]; + phy.set_dl_metrics(dl_m); + } + ch_metrics_t ch_metrics = {}; + ch_metrics.sinr = ue_dl.chest.snr_db; + ch_metrics.sync_err = ue_dl.chest.sync_error; + phy.set_channel_metrics(ch_metrics); + return true; +} + +bool cc_worker::measure_csi() +{ + // Measure SSB CSI + if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) { + srsran_csi_trs_measurements_t meas = {}; + + // Iterate all possible candidates + const std::array& position_in_burst = cfg.ssb.position_in_burst; + for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) { + // Skip SSB candidate if not enabled + if (not position_in_burst[ssb_idx]) { + continue; + } + + // Measure SSB candidate + if (srsran_ssb_csi_measure(&ssb, cfg.carrier.pci, ssb_idx, rx_buffer[0], &meas) < SRSRAN_SUCCESS) { + logger.error("Error measuring SSB"); + return false; + } + + if (logger.debug.enabled()) { + std::array str = {}; + srsran_csi_meas_info(&meas, str.data(), (uint32_t)str.size()); + logger.debug("SSB-CSI: %s", str.data()); + } + + bool hrf = (dl_slot_cfg.idx % SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs) > + SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs) / 2); + srsran_pbch_msg_nr_t pbch_msg = {}; + if (srsran_ssb_decode_pbch(&ssb, cfg.carrier.pci, hrf, ssb_idx, rx_buffer[0], &pbch_msg) < SRSRAN_SUCCESS) { + logger.error("Error decoding PBCH"); + return false; + } + + // Check if PBCH message was decoded + if (pbch_msg.crc) { + // Unpack MIB + srsran_mib_nr_t mib = {}; + if (srsran_pbch_msg_nr_mib_unpack(&pbch_msg, &mib) < SRSRAN_SUCCESS) { + logger.error("Error unpacking PBCH-MIB"); + return false; + } + + // Check if the SFN matches + if (mib.sfn != dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs)) { + logger.error("PBCH-MIB: NR SFN (%d) does not match current SFN (%d)", + mib.sfn, + dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs)); + dl_slot_cfg.idx = mib.sfn * SRSRAN_NSLOTS_PER_FRAME_NR(cfg.carrier.scs); + } + + // Log MIB information + if (logger.debug.enabled()) { + std::array str = {}; + srsran_pbch_msg_nr_mib_info(&mib, str.data(), (uint32_t)str.size()); + logger.debug("PBCH-MIB: %s", str.data()); + } + } else { + // CRC shall never fail if the UE is in sync + logger.warning("PBCH-MIB: CRC failed"); + } + + // Report SSB candidate channel measurement to the PHY state + phy.new_csi_trs_measurement(meas, cfg); + } + } + + // Iterate all NZP-CSI-RS marked as TRS and perform channel measurements + bool estimate_fft = false; + for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) { + // Select NZP-CSI-RS set + const srsran_csi_rs_nzp_set_t& nzp_set = cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; + + // Skip set if not set as TRS (it will be processed later) + if (not nzp_set.trs_info) { + continue; + } + + // Run FFT if not done before in this slot + if (not estimate_fft) { + srsran_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg); + estimate_fft = true; + } + + // Perform measurement, n > 0 is any measurement is performed, n = 0 otherwise + srsran_csi_trs_measurements_t trs_measurements = {}; + int n = srsran_ue_dl_nr_csi_measure_trs(&ue_dl, &dl_slot_cfg, &nzp_set, &trs_measurements); + if (n < SRSRAN_SUCCESS) { + logger.error("Error measuring CSI-RS"); + return false; + } + + // If no measurement performed, skip + if (n == 0) { + continue; + } + + if (logger.debug.enabled()) { + std::array str = {}; + srsran_csi_meas_info(&trs_measurements, str.data(), (uint32_t)str.size()); + logger.debug("NZP-CSI-RS (TRS): id=%d %s", resource_set_id, str.data()); + } + + phy.new_csi_trs_measurement(trs_measurements, cfg, resource_set_id, (uint32_t)n); + } + + // Iterate all NZP-CSI-RS not marked as TRS and perform channel measurements + for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) { + // Select NZP-CSI-RS set + const srsran_csi_rs_nzp_set_t& nzp_set = cfg.pdsch.nzp_csi_rs_sets[resource_set_id]; + + // Skip set if set as TRS (it was processed previously) + if (nzp_set.trs_info) { + continue; + } + + // Run FFT if not done before in this slot + if (not estimate_fft) { + srsran_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg); + estimate_fft = true; + } + + // Perform channel measurement, n > 0 is any measurement is performed, n = 0 otherwise + srsran_csi_channel_measurements_t measurements = {}; + int n = srsran_ue_dl_nr_csi_measure_channel(&ue_dl, &dl_slot_cfg, &nzp_set, &measurements); + if (n < SRSRAN_SUCCESS) { + logger.error("Error measuring CSI-RS"); + return false; + } + + // If no measurement performed, skip + if (n == 0) { + continue; + } + + logger.debug("NZP-CSI-RS: id=%d, rsrp=%+.1f epre=%+.1f snr=%+.1f", + resource_set_id, + measurements.wideband_rsrp_dBm, + measurements.wideband_epre_dBm, + measurements.wideband_snr_db); + + // Report new measurement to the PHY state + phy.new_nzp_csi_rs_channel_measurement(cfg, measurements, resource_set_id); + } + + return true; +} + bool cc_worker::work_dl() { // Do NOT process any DL if it is not configured @@ -209,10 +513,27 @@ bool cc_worker::work_dl() } // Check if it is a DL slot, if not skip - if (!srsran_tdd_nr_is_dl(&phy->cfg.tdd, 0, dl_slot_cfg.idx)) { + if (!srsran_duplex_nr_is_dl(&cfg.duplex, 0, dl_slot_cfg.idx)) { return true; } + // Measure CSI + if (not measure_csi()) { + logger.error("Error measuring, aborting work DL"); + return false; + } + + // Compensate CFO from TRS measurements + if (std::isnormal(phy.args.enable_worker_cfo)) { + float dl_cfo_hz = phy.get_dl_cfo(); + float dl_cfo_norm = -dl_cfo_hz / (1000.0f * ue_ul.ifft.sf_sz); + for (cf_t* b : rx_buffer) { + if (b != nullptr and ue_ul.ifft.sf_sz != 0) { + srsran_vec_apply_cfo(b, dl_cfo_norm, b, ue_ul.ifft.sf_sz); + } + } + } + // Run FFT srsran_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg); @@ -222,104 +543,10 @@ bool cc_worker::work_dl() // Decode PDCCH UL after decode_pdcch_ul(); - // Get DL grant for this TTI, if available - uint32_t pid = 0; - srsran_sch_cfg_nr_t pdsch_cfg = {}; - srsran_pdsch_ack_resource_nr_t ack_resource = {}; - if (phy->get_dl_pending_grant(dl_slot_cfg.idx, pdsch_cfg, ack_resource, pid)) { - // Notify MAC about PDSCH grant - mac_interface_phy_nr::tb_action_dl_t dl_action = {}; - mac_interface_phy_nr::mac_nr_grant_dl_t mac_dl_grant = {}; - mac_dl_grant.rnti = pdsch_cfg.grant.rnti; - mac_dl_grant.pid = pid; - mac_dl_grant.rv = pdsch_cfg.grant.tb[0].rv; - mac_dl_grant.ndi = pdsch_cfg.grant.tb[0].ndi; - mac_dl_grant.tbs = pdsch_cfg.grant.tb[0].tbs / 8; - mac_dl_grant.tti = dl_slot_cfg.idx; - phy->stack->new_grant_dl(0, mac_dl_grant, &dl_action); - - // Early stop if MAC says it doesn't need the TB - if (not dl_action.tb.enabled) { - logger.info("Decoding not required. Skipping PDSCH"); - return true; - } - - // Get data buffer - srsran::unique_byte_buffer_t data = srsran::make_byte_buffer(); - if (data == nullptr) { - logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); - return false; - } - data->N_bytes = pdsch_cfg.grant.tb[0].tbs / 8U; - - // Initialise PDSCH Result - srsran_pdsch_res_nr_t pdsch_res = {}; - pdsch_res.tb[0].payload = data->msg; - pdsch_cfg.grant.tb[0].softbuffer.rx = dl_action.tb.softbuffer; - - // Decode actual PDSCH transmission - if (srsran_ue_dl_nr_decode_pdsch(&ue_dl, &dl_slot_cfg, &pdsch_cfg, &pdsch_res) < SRSRAN_SUCCESS) { - ERROR("Error decoding PDSCH"); - return false; - } - - // Logging - if (logger.info.enabled()) { - str_info_t str; - srsran_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, &pdsch_res, str.data(), (uint32_t)str.size()); - - if (logger.debug.enabled()) { - str_extra_t str_extra; - srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra.data(), (uint32_t)str_extra.size()); - logger.info(pdsch_res.tb[0].payload, - pdsch_cfg.grant.tb[0].tbs / 8, - "PDSCH: cc=%d pid=%d %s\n%s", - cc_idx, - pid, - str.data(), - str_extra.data()); - } else { - logger.info( - pdsch_res.tb[0].payload, pdsch_cfg.grant.tb[0].tbs / 8, "PDSCH: cc=%d pid=%d %s", cc_idx, pid, str.data()); - } - } - - // Enqueue PDSCH ACK information only if the RNTI is type C - if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_c) { - phy->set_pending_ack(dl_slot_cfg.idx, ack_resource, pdsch_res.tb[0].crc); - } - - // Notify MAC about PDSCH decoding result - mac_interface_phy_nr::tb_action_dl_result_t mac_dl_result = {}; - mac_dl_result.ack = pdsch_res.tb[0].crc; - mac_dl_result.payload = mac_dl_result.ack ? std::move(data) : nullptr; // only pass data when successful - phy->stack->tb_decoded(cc_idx, mac_dl_grant, std::move(mac_dl_result)); - - if (pdsch_cfg.grant.rnti_type == srsran_rnti_type_ra) { - phy->rar_grant_tti = dl_slot_cfg.idx; - } - - if (pdsch_res.tb[0].crc) { - // Generate DL metrics - dl_metrics_t dl_m = {}; - dl_m.mcs = pdsch_cfg.grant.tb[0].mcs; - dl_m.fec_iters = pdsch_res.tb[0].avg_iter; - dl_m.evm = pdsch_res.evm[0]; - phy->set_dl_metrics(dl_m); - - // Generate Synch metrics - sync_metrics_t sync_m = {}; - sync_m.cfo = ue_dl.chest.cfo; - phy->set_sync_metrics(sync_m); - - // Generate channel metrics - ch_metrics_t ch_m = {}; - ch_m.n = ue_dl.chest.noise_estimate; - ch_m.sinr = ue_dl.chest.snr_db; - ch_m.rsrp = ue_dl.chest.rsrp_dbm; - ch_m.sync_err = ue_dl.chest.sync_error; - phy->set_channel_metrics(ch_m); - } + // Decode PDSCH + if (not decode_pdsch_dl()) { + logger.error("Error decoding PDSCH, aborting work DL"); + return false; } return true; @@ -327,23 +554,32 @@ bool cc_worker::work_dl() bool cc_worker::work_ul() { + // Gather PDSCH ACK information independently if UL/DL + // If a HARQ ACK Feedback needs to be transmitted in this slot and it is NOT an UL slot, the accumulated HARQ feedback + // for this slot will be flushed + srsran_pdsch_ack_nr_t pdsch_ack = {}; + bool has_ul_ack = phy.get_pending_ack(ul_slot_cfg.idx, pdsch_ack); + // Check if it is a UL slot, if not skip - if (!srsran_tdd_nr_is_ul(&phy->cfg.tdd, 0, ul_slot_cfg.idx)) { + if (!srsran_duplex_nr_is_ul(&cfg.duplex, 0, ul_slot_cfg.idx)) { // No NR signal shall be transmitted srsran_vec_cf_zero(tx_buffer[0], ue_ul.ifft.sf_sz); + + // Check if there is any pending ACK for this DL slot... + if (pdsch_ack.nof_cc > 1) { + // ... in this case log a warning to inform about miss-configuration + logger.warning("Detected HARQ feedback on DL slot"); + } + return true; } srsran_uci_data_nr_t uci_data = {}; uint32_t pid = 0; - // Gather PDSCH ACK information - srsran_pdsch_ack_nr_t pdsch_ack = {}; - bool has_ul_ack = phy->get_pending_ack(ul_slot_cfg.idx, pdsch_ack); - // Request grant to PHY state for this transmit TTI srsran_sch_cfg_nr_t pusch_cfg = {}; - bool has_pusch_grant = phy->get_ul_pending_grant(ul_slot_cfg.idx, pusch_cfg, pid); + bool has_pusch_grant = phy.get_ul_pending_grant(ul_slot_cfg.idx, pusch_cfg, pid); // If PDSCH UL ACK is available, load into UCI if (has_ul_ack) { @@ -351,24 +587,27 @@ bool cc_worker::work_ul() if (logger.debug.enabled()) { std::array str = {}; - if (srsran_ue_dl_nr_ack_info(&pdsch_ack, str.data(), (uint32_t)str.size()) > 0) { + if (srsran_harq_ack_info(&pdsch_ack, str.data(), (uint32_t)str.size()) > 0) { logger.debug("%s", str.data()); } } - if (srsran_ue_dl_nr_gen_ack(&phy->cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) { + if (srsran_harq_ack_pack(&cfg.harq_ack, &pdsch_ack, &uci_data) < SRSRAN_SUCCESS) { ERROR("Filling UCI ACK bits"); return false; } } // Add SR to UCI data only if there is no UL grant! - if (!has_ul_ack) { - phy->get_pending_sr(ul_slot_cfg.idx, uci_data); + if (not has_pusch_grant) { + phy.get_pending_sr(cfg, ul_slot_cfg.idx, uci_data); } // Add CSI reports to UCI data if available - phy->get_periodic_csi(ul_slot_cfg.idx, uci_data); + phy.get_periodic_csi(cfg, ul_slot_cfg, uci_data); + + // Setup frequency offset + srsran_ue_ul_nr_set_freq_offset(&ue_ul, phy.get_ul_cfo()); if (has_pusch_grant) { // Notify MAC about PUSCH found grant @@ -381,7 +620,7 @@ bool cc_worker::work_ul() mac_ul_grant.ndi = pusch_cfg.grant.tb[0].ndi; mac_ul_grant.rv = pusch_cfg.grant.tb[0].rv; mac_ul_grant.is_rar_grant = (pusch_cfg.grant.rnti_type == srsran_rnti_type_ra); - phy->stack->new_grant_ul(0, mac_ul_grant, &ul_action); + phy.stack->new_grant_ul(0, mac_ul_grant, &ul_action); // Don't process further if MAC can't provide PDU if (not ul_action.tb.enabled) { @@ -390,7 +629,7 @@ bool cc_worker::work_ul() } // Set UCI configuration following procedures - srsran_ra_ul_set_grant_uci_nr(&phy->cfg.carrier, &phy->cfg.pusch, &uci_data.cfg, &pusch_cfg); + srsran_ra_ul_set_grant_uci_nr(&cfg.carrier, &cfg.pusch, &uci_data.cfg, &pusch_cfg); // Assigning MAC provided values to PUSCH config structs pusch_cfg.grant.tb[0].softbuffer.tx = ul_action.tb.softbuffer; @@ -437,19 +676,18 @@ bool cc_worker::work_ul() ul_metrics_t ul_m = {}; ul_m.mcs = pusch_cfg.grant.tb[0].mcs; ul_m.power = srsran_convert_power_to_dB(srsran_vec_avg_power_cf(tx_buffer[0], ue_ul.ifft.sf_sz)); - phy->set_ul_metrics(ul_m); + phy.set_ul_metrics(ul_m); } else if (srsran_uci_nr_total_bits(&uci_data.cfg) > 0) { // Get PUCCH resource srsran_pucch_nr_resource_t resource = {}; - if (srsran_ra_ul_nr_pucch_resource(&phy->cfg.pucch, &uci_data.cfg, &resource) < SRSRAN_SUCCESS) { + if (srsran_ra_ul_nr_pucch_resource(&cfg.pucch, &uci_data.cfg, cfg.carrier.nof_prb, &resource) < SRSRAN_SUCCESS) { ERROR("Selecting PUCCH resource"); return false; } // Encode PUCCH message - if (srsran_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &phy->cfg.pucch.common, &resource, &uci_data) < - SRSRAN_SUCCESS) { + if (srsran_ue_ul_nr_encode_pucch(&ue_ul, &ul_slot_cfg, &cfg.pucch.common, &resource, &uci_data) < SRSRAN_SUCCESS) { ERROR("Encoding PUCCH"); return false; } diff --git a/srsue/src/phy/nr/cell_search.cc b/srsue/src/phy/nr/cell_search.cc new file mode 100644 index 000000000..ca74e993f --- /dev/null +++ b/srsue/src/phy/nr/cell_search.cc @@ -0,0 +1,99 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/phy/nr/cell_search.h" +#include "srsran/common/band_helper.h" +#include "srsran/common/buffer_pool.h" +#include "srsran/radio/rf_buffer.h" +#include "srsran/radio/rf_timestamp.h" + +namespace srsue { +namespace nr { +cell_search::cell_search(srslog::basic_logger& logger_) : logger(logger_) {} + +cell_search::~cell_search() +{ + srsran_ssb_free(&ssb); +} + +bool cell_search::init(const args_t& args) +{ + // Prepare SSB initialization arguments + srsran_ssb_args_t ssb_args = {}; + ssb_args.max_srate_hz = args.max_srate_hz; + ssb_args.min_scs = args.ssb_min_scs; + ssb_args.enable_search = true; + ssb_args.enable_decode = true; + + // Initialise SSB + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + logger.error("Cell search: Error initiating SSB"); + return false; + } + + return true; +} + +bool cell_search::start(const cfg_t& cfg) +{ + // Prepare SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = cfg.srate_hz; + ssb_cfg.center_freq_hz = cfg.center_freq_hz; + ssb_cfg.ssb_freq_hz = cfg.ssb_freq_hz; + ssb_cfg.scs = cfg.ssb_scs; + ssb_cfg.pattern = cfg.ssb_pattern; + ssb_cfg.duplex_mode = cfg.duplex_mode; + + // Print SSB configuration, helps debugging gNb and UE + if (logger.info.enabled()) { + std::array ssb_cfg_str = {}; + srsran_ssb_cfg_to_str(&ssb_cfg, ssb_cfg_str.data(), (uint32_t)ssb_cfg_str.size()); + logger.info("Cell search: Setting SSB configuration %s", ssb_cfg_str.data()); + } + + // Configure SSB + if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { + logger.error("Cell search: Error setting SSB configuration"); + return false; + } + return true; +} + +cell_search::ret_t cell_search::run_slot(const cf_t* buffer, uint32_t slot_sz) +{ + cell_search::ret_t ret = {}; + + // Search for SSB + if (srsran_ssb_search(&ssb, buffer, slot_sz + ssb.ssb_sz, &ret.ssb_res) < SRSRAN_SUCCESS) { + logger.error("Error occurred searching SSB"); + ret.result = ret_t::ERROR; + } else if (ret.ssb_res.measurements.snr_dB >= -10.0f and ret.ssb_res.pbch_msg.crc) { + // Consider the SSB is found and decoded if the PBCH CRC matched + ret.result = ret_t::CELL_FOUND; + } else { + ret.result = ret_t::CELL_NOT_FOUND; + } + return ret; +} + +} // namespace nr +} // namespace srsue diff --git a/srsue/src/phy/nr/sf_worker.cc b/srsue/src/phy/nr/sf_worker.cc index 7ff3d7c66..b24998472 100644 --- a/srsue/src/phy/nr/sf_worker.cc +++ b/srsue/src/phy/nr/sf_worker.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -36,22 +36,26 @@ static int plot_worker_id = -1; namespace srsue { namespace nr { -sf_worker::sf_worker(phy_common* phy_, state* phy_state_, srslog::basic_logger& log) : - phy_state(phy_state_), phy(phy_), logger(log) + +sf_worker::sf_worker(srsran::phy_common_interface& common_, + state& phy_state_, + const srsran::phy_cfg_nr_t& cfg, + srslog::basic_logger& log) : + phy_state(phy_state_), common(common_), logger(log), sf_len(SRSRAN_SF_LEN_PRB_NR(cfg.carrier.nof_prb)) { - for (uint32_t i = 0; i < phy_state->args.nof_carriers; i++) { - cc_worker* w = new cc_worker(i, log, phy_state); + for (uint32_t i = 0; i < phy_state.args.nof_carriers; i++) { + cc_worker* w = new cc_worker(i, log, phy_state, cfg); cc_workers.push_back(std::unique_ptr(w)); } } -bool sf_worker::update_cfg(uint32_t cc_idx) +void sf_worker::update_cfg(uint32_t cc_idx, const srsran::phy_cfg_nr_t& new_cfg) { if (cc_idx >= cc_workers.size()) { - return false; + return; } - return cc_workers[cc_idx]->update_cfg(); + cc_workers[cc_idx]->update_cfg(new_cfg); } cf_t* sf_worker::get_buffer(uint32_t cc_idx, uint32_t antenna_idx) @@ -68,19 +72,18 @@ uint32_t sf_worker::get_buffer_len() return cc_workers.at(0)->get_buffer_len(); } -void sf_worker::set_tti(uint32_t tti) +void sf_worker::set_context(const srsran::phy_common_interface::worker_context_t& w_ctx) { - tti_rx = tti; - logger.set_context(tti); + logger.set_context(w_ctx.sf_idx); for (auto& w : cc_workers) { - w->set_tti(tti); + w->set_tti(w_ctx.sf_idx); } + context.copy(w_ctx); } void sf_worker::work_imp() { - srsran::rf_buffer_t tx_buffer = {}; - srsran::rf_timestamp_t dummy_ts = {}; + srsran::rf_buffer_t tx_buffer = {}; // Perform DL processing for (auto& w : cc_workers) { @@ -88,23 +91,17 @@ void sf_worker::work_imp() } // Align workers, wait for previous workers to finish DL processing before starting UL processing - phy_state->dl_ul_semaphore.wait(this); - phy_state->dl_ul_semaphore.release(); + phy_state.dl_ul_semaphore.wait(this); + phy_state.dl_ul_semaphore.release(); // Check if PRACH is available if (prach_ptr != nullptr) { // PRACH is available, set buffer, transmit and return - tx_buffer.set(0, prach_ptr); - - // Notify MAC about PRACH transmission - phy_state->stack->prach_sent(TTI_TX(tti_rx), - srsran_prach_nr_start_symbol_fr1_unpaired(phy_state->cfg.prach.config_idx), - SRSRAN_SLOT_NR_MOD(phy_state->cfg.carrier.scs, TTI_TX(tti_rx)), - 0, - 0); + tx_buffer.set(phy_state.args.rf_channel_offset, prach_ptr); + tx_buffer.set_nof_samples(sf_len); // Transmit NR PRACH - phy->worker_end(this, false, tx_buffer, dummy_ts, true); + common.worker_end(context, true, tx_buffer); // Reset PRACH pointer prach_ptr = nullptr; @@ -114,16 +111,17 @@ void sf_worker::work_imp() // Perform UL processing for (auto& w : cc_workers) { - w->work_ul(); + w.get()->work_ul(); } // Set Tx buffers for (uint32_t i = 0; i < (uint32_t)cc_workers.size(); i++) { - tx_buffer.set(i, cc_workers[i]->get_tx_buffer(0)); + tx_buffer.set(i + phy_state.args.rf_channel_offset, cc_workers[i]->get_tx_buffer(0)); } + tx_buffer.set_nof_samples(sf_len); // Always call worker_end before returning - phy->worker_end(this, false, tx_buffer, dummy_ts, true); + common.worker_end(context, true, tx_buffer); // Tell the plotting thread to draw the plots #ifdef ENABLE_GUI diff --git a/srsue/src/phy/nr/slot_sync.cc b/srsue/src/phy/nr/slot_sync.cc new file mode 100644 index 000000000..38612460a --- /dev/null +++ b/srsue/src/phy/nr/slot_sync.cc @@ -0,0 +1,206 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/phy/nr/slot_sync.h" + +namespace srsue { +namespace nr { + +slot_sync::slot_sync(srslog::basic_logger& logger_) : logger(logger_), sfn_sync_buff(1) {} + +slot_sync::~slot_sync() +{ + srsran_ue_sync_nr_free(&ue_sync_nr); +} + +static int slot_sync_recv_callback(void* ptr, cf_t** buffer, uint32_t nsamples, srsran_timestamp_t* ts) +{ + if (ptr == nullptr) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + slot_sync* sync = (slot_sync*)ptr; + + cf_t* buffer_ptr[SRSRAN_MAX_CHANNELS] = {}; + buffer_ptr[0] = buffer[0]; + + srsran::rf_buffer_t rf_buffer(buffer_ptr, nsamples); + + return sync->recv_callback(rf_buffer, ts); +} + +bool slot_sync::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) +{ + stack = stack_; + radio = radio_; + + srsran_ue_sync_nr_args_t ue_sync_nr_args = {}; + ue_sync_nr_args.max_srate_hz = args.max_srate_hz; + ue_sync_nr_args.min_scs = args.ssb_min_scs; + ue_sync_nr_args.nof_rx_channels = args.nof_rx_channels; + ue_sync_nr_args.disable_cfo = args.disable_cfo; + ue_sync_nr_args.pbch_dmrs_thr = args.pbch_dmrs_thr; + ue_sync_nr_args.cfo_alpha = args.cfo_alpha; + ue_sync_nr_args.recv_obj = this; + ue_sync_nr_args.recv_callback = slot_sync_recv_callback; + + if (srsran_ue_sync_nr_init(&ue_sync_nr, &ue_sync_nr_args) < SRSRAN_SUCCESS) { + logger.error("Error initiating UE SYNC NR object"); + return false; + } + + return true; +} + +int slot_sync::set_sync_cfg(const srsran_ue_sync_nr_cfg_t& cfg) +{ + // Print the configuration, it is essential to make sure the UE synchronizes with the wight cell + if (logger.info.enabled()) { + std::array ssb_cfg_str = {}; + srsran_ssb_cfg_to_str(&cfg.ssb, ssb_cfg_str.data(), (uint32_t)ssb_cfg_str.size()); + logger.info("SYNC: Setting SSB configuration %s Tracking N_id=%d.", ssb_cfg_str.data(), cfg.N_id); + } + + // Set the synchronization configuration + if (srsran_ue_sync_nr_set_cfg(&ue_sync_nr, &cfg) < SRSRAN_SUCCESS) { + logger.error("SYNC: failed to set cell configuration for N_id %d", cfg.N_id); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + +int slot_sync::recv_callback(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_time) +{ + // This function is designed for being called from the UE sync object which will pass a null rx_time in case + // receive dummy samples. So, rf_timestamp points at dummy timestamp in case rx_time is not provided + srsran::rf_timestamp_t dummy_ts = {}; + srsran::rf_timestamp_t& rf_timestamp = (rx_time == nullptr) ? dummy_ts : last_rx_time; + + // Receive + if (not radio->rx_now(data, rf_timestamp)) { + return SRSRAN_ERROR; + } + + srsran_timestamp_t dummy_flat_ts = {}; + + // Load flat timestamp + if (rx_time == nullptr) { + rx_time = &dummy_flat_ts; + } + *rx_time = rf_timestamp.get(0); + + // Save RF timestamp for the stack + stack_tti_ts_new = rf_timestamp.get(0); + + // Run stack if the sync state is not in camping + logger.debug("run_stack_tti: from recv"); + run_stack_tti(); + + logger.debug("SYNC: received %d samples from radio", data.get_nof_samples()); + + return data.get_nof_samples(); +} + +bool slot_sync::run_sfn_sync() +{ + // Run UE SYNC process using the temporal SFN process buffer + srsran_ue_sync_nr_outcome_t outcome = {}; + if (srsran_ue_sync_nr_zerocopy(&ue_sync_nr, sfn_sync_buff.to_cf_t(), &outcome) < SRSRAN_SUCCESS) { + logger.error("SYNC: error in zerocopy"); + return false; + } + + // If in sync, update slot index + if (outcome.in_sync) { + slot_cfg.idx = outcome.sfn * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) + outcome.sf_idx; + } + + // Return true if the PHY in-sync + return outcome.in_sync; +} + +bool slot_sync::run_camping(srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& timestamp) +{ + // Run UE SYNC process using an external baseband buffer + srsran_ue_sync_nr_outcome_t outcome = {}; + if (srsran_ue_sync_nr_zerocopy(&ue_sync_nr, buffer.to_cf_t(), &outcome) < SRSRAN_SUCCESS) { + logger.error("SYNC: error in zerocopy"); + return false; + } + + // If in sync, update slot index + if (outcome.in_sync) { + slot_cfg.idx = outcome.sfn * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) + outcome.sf_idx; + } + + // Set RF timestamp + *timestamp.get_ptr(0) = outcome.timestamp; + + // Return true if the PHY in-sync + return outcome.in_sync; +} + +void slot_sync::run_stack_tti() +{ // check timestamp reset + if (forced_rx_time_init || srsran_timestamp_iszero(&stack_tti_ts) || + srsran_timestamp_compare(&stack_tti_ts_new, &stack_tti_ts) < 0) { + if (srsran_timestamp_compare(&stack_tti_ts_new, &stack_tti_ts) < 0) { + logger.warning("SYNC: radio time seems to be going backwards (rx_time=%f, tti_ts=%f)", + srsran_timestamp_real(&stack_tti_ts_new), + srsran_timestamp_real(&stack_tti_ts)); + // time-stamp will be set to rx time below and run_tti() will be called with MIN_TTI_JUMP + } + + // init tti_ts with last rx time + logger.debug("SYNC: Setting initial TTI time to %f", srsran_timestamp_real(&stack_tti_ts_new)); + srsran_timestamp_copy(&stack_tti_ts, &stack_tti_ts_new); + forced_rx_time_init = false; + } + + // Advance stack in time + if (srsran_timestamp_compare(&stack_tti_ts_new, &stack_tti_ts) >= 0) { + srsran_timestamp_t temp = {}; + srsran_timestamp_copy(&temp, &stack_tti_ts_new); + srsran_timestamp_sub(&temp, stack_tti_ts.full_secs, stack_tti_ts.frac_secs); + int32_t tti_jump = static_cast(srsran_timestamp_uint64(&temp, 1e3)); + tti_jump = SRSRAN_MAX(tti_jump, MIN_TTI_JUMP); + if (tti_jump > MAX_TTI_JUMP) { + logger.warning("SYNC: TTI jump of %d limited to %d", tti_jump, int(MAX_TTI_JUMP)); + tti_jump = SRSRAN_MIN(tti_jump, MAX_TTI_JUMP); + } + + // Run stack + logger.debug("run_stack_tti: calling stack tti=%d, tti_jump=%d", slot_cfg.idx, tti_jump); + stack->run_tti(slot_cfg.idx, tti_jump); + logger.debug("run_stack_tti: stack called"); + } + + // update timestamp + srsran_timestamp_copy(&stack_tti_ts, &stack_tti_ts_new); +} + +srsran_slot_cfg_t slot_sync::get_slot_cfg() +{ + return slot_cfg; +} + +} // namespace nr +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/nr/worker_pool.cc b/srsue/src/phy/nr/worker_pool.cc index fef7ce239..9203a84f1 100644 --- a/srsue/src/phy/nr/worker_pool.cc +++ b/srsue/src/phy/nr/worker_pool.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,20 +19,28 @@ * */ #include "srsue/hdr/phy/nr/worker_pool.h" +#include "srsran/common/band_helper.h" namespace srsue { namespace nr { -worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers), logger(srslog::fetch_basic_logger("PHY-NR")) {} +worker_pool::worker_pool(srslog::basic_logger& logger_, uint32_t max_workers) : pool(max_workers), logger(logger_) {} -bool worker_pool::init(const phy_args_nr_t& args, phy_common* common, stack_interface_phy_nr* stack_, int prio) +bool worker_pool::init(const phy_args_nr_t& args, srsran::phy_common_interface& common, stack_interface_phy_nr* stack_) { phy_state.stack = stack_; phy_state.args = args; - // Set carrier attributes - phy_state.cfg.carrier.pci = 500; - phy_state.cfg.carrier.nof_prb = args.max_nof_prb; + { + std::lock_guard lock(cfg_mutex); + pending_cfgs.resize(args.nof_phy_threads); + for (auto&& b : pending_cfgs) { + b = false; + } + // Set carrier attributes + cfg.carrier.pci = 500; + cfg.carrier.nof_prb = args.max_nof_prb; + } // Set NR arguments phy_state.args.nof_carriers = args.nof_carriers; @@ -48,12 +56,16 @@ bool worker_pool::init(const phy_args_nr_t& args, phy_common* common, stack_inte // Add workers to workers pool and start threads for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - auto& log = srslog::fetch_basic_logger(fmt::format("PHY{}-NR", i)); + auto& log = srslog::fetch_basic_logger(fmt::format("{}PHY{}-NR", args.log.id_preamble, i)); log.set_level(srslog::str_to_basic_level(args.log.phy_level)); log.set_hex_dump_max_size(args.log.phy_hex_limit); - auto w = new sf_worker(common, &phy_state, log); - pool.init_worker(i, w, prio, args.worker_cpu_mask); + sf_worker* w = nullptr; + { + std::lock_guard lock(cfg_mutex); + w = new sf_worker(common, phy_state, cfg, log); + } + pool.init_worker(i, w, args.workers_thread_prio, args.worker_cpu_mask); workers.push_back(std::unique_ptr(w)); } @@ -81,12 +93,58 @@ sf_worker* worker_pool::wait_worker(uint32_t tti) logger.set_context(tti); sf_worker* worker = (sf_worker*)pool.wait_worker(tti); + uint32_t pci = 0; + { + std::lock_guard lock(cfg_mutex); + pci = cfg.carrier.pci; + if (pending_cfgs[worker->get_id()]) { + pending_cfgs[worker->get_id()] = false; + worker->set_cfg(cfg); + } + } + // Generate PRACH if ready - if (prach_buffer->is_ready_to_send(tti, phy_state.cfg.carrier.pci)) { - uint32_t nof_prach_sf = 0; - float prach_target_power = 0.0f; - cf_t* prach_ptr = prach_buffer->generate(0.0f, &nof_prach_sf, &prach_target_power); - worker->set_prach(prach_ptr, prach_target_power); + if (prach_buffer->is_ready_to_send(tti, pci)) { + prach_ptr = prach_buffer->generate(phy_state.get_ul_cfo() / 15000, &prach_nof_sf, &prach_target_power); + + // Scale signal to maximum + { + float* ptr = (float*)prach_ptr; + int max_i = srsran_vec_max_abs_fi(ptr, 2 * sf_sz); + float max = ptr[max_i]; + if (std::isnormal(max)) { + srsran_vec_sc_prod_cfc(prach_ptr, 0.99f / max, prach_ptr, sf_sz * prach_nof_sf); + } + } + + uint32_t config_idx = 0; + srsran_duplex_mode_t mode = SRSRAN_DUPLEX_MODE_FDD; + srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz; + { + std::lock_guard lock(cfg_mutex); + config_idx = cfg.prach.config_idx; + mode = cfg.duplex.mode; + scs = cfg.carrier.scs; + } + + // Notify MAC about PRACH transmission + phy_state.stack->prach_sent( + TTI_TX(tti), srsran_prach_nr_start_symbol(config_idx, mode), SRSRAN_SLOT_NR_MOD(scs, TTI_TX(tti)), 0, 0); + } + + // Set PRACH transmission buffer in workers if it is pending + if (prach_nof_sf > 0) { + // Set worker PRACH buffer + worker->set_prach(&prach_ptr[sf_sz * prach_sf_count], prach_target_power); + + // Increment SF counter + prach_sf_count++; + + // Reset PRACH pending subframe count + if (prach_sf_count >= prach_nof_sf) { + prach_nof_sf = 0; + prach_sf_count = 0; + } } return worker; @@ -97,14 +155,19 @@ void worker_pool::stop() pool.stop(); } -void worker_pool::send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power) +void worker_pool::send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec) { prach_buffer->prepare_to_send(preamble_index); } -int worker_pool::set_ul_grant(std::array packed_ul_grant, - uint16_t rnti, - srsran_rnti_type_t rnti_type) +// called from Stack thread when processing RAR PDU +int worker_pool::set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) { // Copy DCI bits and setup DCI context srsran_dci_msg_nr_t dci_msg = {}; @@ -116,58 +179,101 @@ int worker_pool::set_ul_grant(std::array pac srsran_vec_u8_copy(dci_msg.payload, packed_ul_grant.data(), SRSRAN_RAR_UL_GRANT_NBITS); srsran_dci_ul_nr_t dci_ul = {}; - if (srsran_dci_nr_ul_unpack(NULL, &dci_msg, &dci_ul) < SRSRAN_SUCCESS) { + logger.error("Couldn't unpack UL grant"); return SRSRAN_ERROR; } + // initialize with Rx TTI of RAR + srsran_slot_cfg_t msg3_slot_cfg = {}; + msg3_slot_cfg.idx = rar_slot_idx; + if (logger.info.enabled()) { std::array str; - srsran_dci_ul_nr_to_str(NULL, &dci_ul, str.data(), str.size()); - logger.set_context(phy_state.rar_grant_tti); - logger.info("Setting RAR Grant %s", str.data()); + srsran_dci_nr_t dci = {}; + srsran_dci_ul_nr_to_str(&dci, &dci_ul, str.data(), str.size()); + logger.set_context(msg3_slot_cfg.idx); + logger.info("Setting RAR Grant: %s", str.data()); } - phy_state.set_ul_pending_grant(phy_state.rar_grant_tti, dci_ul); + std::lock_guard lock(cfg_mutex); + phy_state.set_ul_pending_grant(cfg, msg3_slot_cfg, dci_ul); return SRSRAN_SUCCESS; } -bool worker_pool::set_config(const srsran::phy_cfg_nr_t& cfg) -{ - phy_state.cfg = cfg; - logger.info( - "Setting new PHY configuration ARFCN=%d, PCI=%d", cfg.carrier.absolute_frequency_point_a, cfg.carrier.pci); +bool worker_pool::set_config(const srsran::phy_cfg_nr_t& new_cfg) +{ + uint32_t dl_arfcn = srsran::srsran_band_helper().freq_to_nr_arfcn(new_cfg.carrier.dl_center_frequency_hz); + sf_sz = SRSRAN_SF_LEN_PRB_NR(new_cfg.carrier.nof_prb); + + bool carrier_equal; + { + std::lock_guard lock(cfg_mutex); + + // Check if the carrier has changed + carrier_equal = cfg.carrier_is_equal(new_cfg); + + // If the carrier has not changed, reset pending flags. Configuration will be copied when the worker is reserved + // from the real-time thread + for (auto&& b : pending_cfgs) { + b = carrier_equal; + } + + // Update configuration + cfg = new_cfg; + } + + // If the carrier has changed, the configuration cannot be set from the real-time thread + if (not carrier_equal) { + // Configure each worker with the new configuration + for (uint32_t i = 0; i < (uint32_t)workers.size(); i++) { + // Wait for each worker to avoid concurrency issues + sf_worker* w = (sf_worker*)pool.wait_worker_id(i); + if (w == nullptr) { + // Unlikely to happen + continue; + } + + // Configure worker + w->set_cfg(new_cfg); + + // Release worker + w->release(); + + // As the worker has been configured, there is no need to load new configuration from the real-time thread + { + std::lock_guard lock(cfg_mutex); + pending_cfgs[i] = false; + } + } + } + + logger.info("Setting new PHY configuration ARFCN=%d, PCI=%d", dl_arfcn, new_cfg.carrier.pci); // Set carrier information info_metrics_t info = {}; - info.pci = cfg.carrier.pci; - info.dl_earfcn = cfg.carrier.absolute_frequency_ssb; + info.pci = new_cfg.carrier.pci; + info.dl_earfcn = dl_arfcn; phy_state.set_info_metrics(info); // Best effort to convert NR carrier into LTE cell srsran_cell_t cell = {}; - int ret = srsran_carrier_to_cell(&phy_state.cfg.carrier, &cell); + int ret = srsran_carrier_to_cell(&new_cfg.carrier, &cell); if (ret < SRSRAN_SUCCESS) { logger.error("Converting carrier to cell for PRACH (%d)", ret); return false; } - // Request workers to run any procedure related to configuration update - for (auto& w : workers) { - if (not w->update_cfg(0)) { - return false; - } - } - // Best effort to set up NR-PRACH config reused for NR - srsran_prach_cfg_t prach_cfg = cfg.prach; - uint32_t lte_nr_prach_offset = (phy_state.cfg.carrier.nof_prb - cell.nof_prb) / 2; + srsran_prach_cfg_t prach_cfg = new_cfg.prach; + uint32_t lte_nr_prach_offset = (new_cfg.carrier.nof_prb - cell.nof_prb) / 2; if (prach_cfg.freq_offset < lte_nr_prach_offset) { logger.error("prach_cfg.freq_offset=%d is not compatible with LTE", prach_cfg.freq_offset); return false; } prach_cfg.freq_offset -= lte_nr_prach_offset; + prach_cfg.tdd_config.configured = (new_cfg.duplex.mode == SRSRAN_DUPLEX_MODE_TDD); // Set the PRACH configuration if (not prach_buffer->set_cell(cell, prach_cfg)) { @@ -180,7 +286,8 @@ bool worker_pool::set_config(const srsran::phy_cfg_nr_t& cfg) bool worker_pool::has_valid_sr_resource(uint32_t sr_id) { - return phy_state.has_valid_sr_resource(sr_id); + std::lock_guard lock(cfg_mutex); + return phy_state.has_valid_sr_resource(cfg, sr_id); } void worker_pool::clear_pending_grants() diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 2f13b0376..f98bfbcce 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,7 +20,6 @@ */ #include -#include #include "srsran/common/band_helper.h" #include "srsran/common/standard_streams.h" @@ -102,19 +101,11 @@ bool phy::check_args(const phy_args_t& args_) int phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) { + std::unique_lock lock(config_mutex); + stack = stack_; radio = radio_; - init(args_); - - return SRSRAN_SUCCESS; -} - -int phy::init(const phy_args_t& args_) -{ - std::unique_lock lock(config_mutex); - mlockall(MCL_CURRENT | MCL_FUTURE); - args = args_; // Force frequency if given as argument @@ -135,12 +126,12 @@ int phy::init(const phy_args_t& args_) logger_phy.set_hex_dump_max_size(args.log.phy_hex_limit); if (!check_args(args)) { - return false; + return SRSRAN_ERROR; } is_configured = false; start(); - return true; + return SRSRAN_SUCCESS; } // Initializes PHY in a thread @@ -157,9 +148,6 @@ void phy::run_thread() sfsync.init( radio, stack, &prach_buffer, <e_workers, &nr_workers, &common, SF_RECV_THREAD_PRIO, args.sync_cpu_affinity); - // Disable UL signal pregeneration until the attachment - enable_pregen_signals(false); - is_configured = true; config_cond.notify_all(); } @@ -173,7 +161,7 @@ void phy::wait_initialize() } } -bool phy::is_initiated() +bool phy::is_initialized() { return is_configured; } @@ -254,8 +242,6 @@ void phy::configure_prach_params() { Debug("Configuring PRACH parameters"); - prach_cfg.tdd_config = tdd_config; - if (!prach_buffer.set_cell(selected_cell, prach_cfg)) { Error("Configuring PRACH parameters"); } @@ -263,47 +249,58 @@ void phy::configure_prach_params() void phy::set_cells_to_meas(uint32_t earfcn, const std::set& pci) { - // Check if the EARFCN matches with serving cell - uint32_t pcell_earfcn = 0; - sfsync.get_current_cell(nullptr, &pcell_earfcn); - bool available = (pcell_earfcn == earfcn); + uint32_t pcell_earfcn = selected_earfcn; + // As the SCell configuration is performed asynchronously through the cmd_worker, append the command adding the + // measurements to avoid a concurrency issue + cmd_worker.add_cmd([this, earfcn, pci, pcell_earfcn]() { + // Check if the EARFCN matches with serving cell + bool available = (pcell_earfcn == earfcn); - // Find if there is secondary serving cell configured with the specified EARFCN - uint32_t cc_empty = 0; - for (uint32_t cc = 1; cc < args.nof_lte_carriers and not available; cc++) { - // If it is configured... - if (common.cell_state.is_configured(cc)) { - // ... Check if the EARFCN match - if (common.cell_state.get_earfcn(cc) == earfcn) { - available = true; + // Find if there is secondary serving cell configured with the specified EARFCN + uint32_t cc_empty = 0; + for (uint32_t cc = 1; cc < args.nof_lte_carriers and not available; cc++) { + // If it is configured... + if (common.cell_state.is_configured(cc)) { + // ... Check if the EARFCN match + logger_phy.info( + "Setting new SCell measurement cc=%d is configured and earfcn=%d", cc, common.cell_state.get_earfcn(cc)); + if (common.cell_state.get_earfcn(cc) == earfcn) { + available = true; + } + } else { + logger_phy.info("Setting new SCell measurement cc=%d is not configured", cc); + if (cc_empty == 0) { + // ... otherwise, save the CC as non-configured + cc_empty = cc; + } } - } else if (cc_empty == 0) { - // ... otherwise, save the CC as non-configured - cc_empty = cc; - } - } - - // If not available and a non-configured carrier is available, configure it. - if (not available and cc_empty != 0) { - // Copy all attributes from serving cell - srsran_cell_t cell = selected_cell; - - // Select the first PCI in the list - if (not pci.empty()) { - cell.id = *pci.begin(); } - // Configure a the empty carrier as it was CA - set_scell(cell, cc_empty, earfcn); - } + // If not available and a non-configured carrier is available, configure it. + if (not available and cc_empty != 0) { + // Copy all attributes from serving cell + srsran_cell_t cell = selected_cell; - // Finally, set the serving cell measure - sfsync.set_cells_to_meas(earfcn, pci); + // Select the first PCI in the list + if (not pci.empty()) { + cell.id = *pci.begin(); + } + + // Configure a the empty carrier as it was CA + logger_phy.info("Setting new SCell measurement cc_idx=%d, earfcn=%d, pci=%d...", cc_empty, earfcn, cell.id); + set_scell(cell, cc_empty, earfcn, false); + } + + // Finally, set the serving cell measure + sfsync.set_cells_to_meas(earfcn, pci); + }); } void phy::meas_stop() { - sfsync.meas_stop(); + if (is_configured) { + sfsync.meas_stop(); + } } // This function executes one part of the procedure immediatly and returns to continue in the background. @@ -313,10 +310,27 @@ bool phy::cell_select(phy_cell_t cell) { sfsync.scell_sync_stop(); if (sfsync.cell_select_init(cell)) { - reset(); // Update PCI before starting the background command to make sure PRACH gets the updated value selected_cell.id = cell.pci; + + // Update EARCN before starting the background task to make sure is taken into account when finding carriers to + // measure inter-frequency neighbours (see set_cells_to_meas) + selected_earfcn = cell.earfcn; + + // Indicate workers that cell selection is in progress + common.cell_is_selecting = true; + + // Update EARCN before starting the background task to make sure is taken into account when finding carriers to + // measure inter-frequency neighbours (see set_cells_to_meas) + selected_earfcn = cell.earfcn; + cmd_worker_cell.add_cmd([this, cell]() { + // Wait SYNC transitions to IDLE + sfsync.wait_idle(); + + // Reset worker once SYNC is IDLE to flush any PHY state including measurements, pending ACKs and pending grants + reset(); + bool ret = sfsync.cell_select_start(cell); if (ret) { srsran_cell_t sync_cell; @@ -324,6 +338,9 @@ bool phy::cell_select(phy_cell_t cell) selected_cell = sync_cell; } stack->cell_select_complete(ret); + + // Indicate workers that cell selection has finished + common.cell_is_selecting = false; }); return true; } else { @@ -334,15 +351,20 @@ bool phy::cell_select(phy_cell_t cell) // This function executes one part of the procedure immediatly and returns to continue in the background. // When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH -// processing. -bool phy::cell_search() +// processing. If a valid EARFCN (>0) is given, this is used for cell search. +bool phy::cell_search(int earfcn) { sfsync.scell_sync_stop(); if (sfsync.cell_search_init()) { - reset(); - cmd_worker_cell.add_cmd([this]() { + cmd_worker_cell.add_cmd([this, earfcn]() { + // Wait SYNC transitions to IDLE + sfsync.wait_idle(); + + // Reset worker once SYNC is IDLE to flush any PHY state including measurements, pending ACKs and pending grants + reset(); + phy_cell_t found_cell = {}; - rrc_interface_phy_lte::cell_search_ret_t ret = sfsync.cell_search_start(&found_cell); + rrc_interface_phy_lte::cell_search_ret_t ret = sfsync.cell_search_start(&found_cell, earfcn); stack->cell_search_complete(ret, found_cell); }); } else { @@ -398,6 +420,13 @@ void phy::reset() Info("Resetting PHY..."); common.ta.set_base_sec(0); common.reset(); + + // Release mapping of secondary cells + if (radio != nullptr) { + for (uint32_t i = 1; i < args.nof_lte_carriers; i++) { + radio->release_freq(i); + } + } } uint32_t phy::get_current_tti() @@ -407,14 +436,13 @@ uint32_t phy::get_current_tti() void phy::sr_send() { - common.sr_enabled = true; - common.sr_last_tx_tti = -1; - Debug("sr_send(): sr_enabled=%d, last_tx_tti=%d", common.sr_enabled, common.sr_last_tx_tti); + common.sr.trigger(); + Debug("SR is triggered"); } int phy::sr_last_tx_tti() { - return common.sr_last_tx_tti; + return common.sr.get_last_tx_tti(); } void phy::set_rar_grant(uint8_t grant_payload[SRSRAN_RAR_GRANT_LEN], uint16_t rnti) @@ -431,16 +459,9 @@ void phy::start_plot() } } -void phy::enable_pregen_signals(bool enable) -{ - for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - lte_workers[i]->enable_pregen_signals_unlocked(enable); - } -} - bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) { - if (!is_initiated()) { + if (!is_initialized()) { fprintf(stderr, "Error calling set_config(): PHY not initialized\n"); return false; } @@ -449,30 +470,23 @@ bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) if (cc_idx >= args.nof_lte_carriers) { srsran::console("Received SCell configuration for index %d but there are not enough CC workers available\n", cc_idx); - return false; + return true; } Info("Setting configuration"); - // The PRACH configuration shall be updated only if: - // - The new configuration belongs to the primary cell - // - The PRACH configuration is present - if (!cc_idx && config_.prach_cfg_present) { - prach_cfg = config_.prach_cfg; - } - - // Apply configuration after the worker is finished to avoid race conditions + // Apply configurations asynchronously to avoid race conditions cmd_worker.add_cmd([this, config_, cc_idx]() { - logger_phy.info("Setting new PHY configuration cc_idx=%d...", cc_idx); - for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - // set_cell is not protected so run when worker is finished - lte::sf_worker* w = lte_workers.wait_worker_id(i); - if (w) { - w->set_config_unlocked(cc_idx, config_); - w->release(); - } + // The PRACH configuration shall be updated only if: + // - The new configuration belongs to the primary cell + // - The PRACH configuration is present + if (!cc_idx && config_.prach_cfg_present) { + prach_cfg = config_.prach_cfg; + prach_cfg.tdd_config = tdd_config; } - logger_phy.info("Finished setting new PHY configuration cc_idx=%d", cc_idx); + + logger_phy.info("Setting new PHY configuration cc_idx=%d...", cc_idx); + lte_workers.set_config(cc_idx, config_); // It is up to the PRACH component to detect whether the cell or the configuration have changed to reconfigure configure_prach_params(); @@ -483,7 +497,12 @@ bool phy::set_config(const srsran::phy_cfg_t& config_, uint32_t cc_idx) bool phy::set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) { - if (!is_initiated()) { + return set_scell(cell_info, cc_idx, earfcn, true); +} + +bool phy::set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn, bool run_in_background) +{ + if (!is_initialized()) { fprintf(stderr, "Error calling set_config(): PHY not initialized\n"); return false; } @@ -511,45 +530,66 @@ bool phy::set_scell(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn) // Set inter-frequency measurement sfsync.set_inter_frequency_measurement(cc_idx, earfcn, cell_info); - // Store secondary serving cell EARFCN and PCI - common.cell_state.configure(cc_idx, earfcn, cell_info.id); - - // Reset cell configuration - for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - lte_workers[i]->reset_cell_unlocked(cc_idx); - } + // Reset secondary serving cell state, prevents this component carrier from executing any new PHY processing. It does + // not stop any current work + common.cell_state.reset(cc_idx); // Component carrier index zero should be reserved for PCell // Send configuration to workers - cmd_worker.add_cmd([this, cell_info, cc_idx, earfcn, earfcn_is_different]() { - logger_phy.info("Setting new SCell configuration cc_idx=%d, earfcn=%d...", cc_idx, earfcn); - for (uint32_t i = 0; i < args.nof_phy_threads; i++) { - // set_cell is not protected so run when worker is finished - lte::sf_worker* w = lte_workers.wait_worker_id(i); - if (w) { - w->set_cell_unlocked(cc_idx, cell_info); - w->release(); - } - } - - // Change frequency only if the earfcn was modified - if (earfcn_is_different) { - double dl_freq = srsran_band_fd(earfcn) * 1e6; - double ul_freq = srsran_band_fu(common.get_ul_earfcn(earfcn)) * 1e6; - radio->set_rx_freq(cc_idx, dl_freq); - radio->set_tx_freq(cc_idx, ul_freq); - } - - // Set secondary serving cell synchronization - sfsync.scell_sync_set(cc_idx, cell_info); - - logger_phy.info("Finished setting new SCell configuration cc_idx=%d, earfcn=%d", cc_idx, earfcn); - - stack->set_scell_complete(true); - }); + if (run_in_background) { + cmd_worker.add_cmd([this, cell_info, cc_idx, earfcn, earfcn_is_different]() { + set_scell_cmd(cell_info, cc_idx, earfcn, earfcn_is_different); + }); + } else { + set_scell_cmd(cell_info, cc_idx, earfcn, earfcn_is_different); + } return true; } +void phy::set_scell_cmd(srsran_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn, bool earfcn_is_different) +{ + logger_phy.info("Setting new SCell configuration cc_idx=%d, earfcn=%d, pci=%d...", cc_idx, earfcn, cell_info.id); + for (uint32_t i = 0; i < args.nof_phy_threads; i++) { + // set_cell is not protected so run when worker has finished to ensure no PHY processing is done at the time of + // cell setting + lte::sf_worker* w = lte_workers.wait_worker_id(i); + if (w) { + // Reset secondary serving cell configuration, this needs to be done when the sf_worker is reserved to prevent + // resetting the cell while it is working + w->reset_cell_nolock(cc_idx); + + // Set the new cell + w->set_cell_nolock(cc_idx, cell_info); + + // Release the new worker, it should not start processing until the SCell state is set to configured + w->release(); + } + } + + // Reset measurements for the given CC after all workers finished processing and have been configured to ensure the + // measurements are not overwritten + common.reset_measurements(cc_idx); + + // Change frequency only if the earfcn was modified + if (earfcn_is_different) { + double dl_freq = srsran_band_fd(earfcn) * 1e6; + double ul_freq = srsran_band_fu(common.get_ul_earfcn(earfcn)) * 1e6; + radio->set_rx_freq(cc_idx, dl_freq); + radio->set_tx_freq(cc_idx, ul_freq); + } + + // Set secondary serving cell synchronization + sfsync.scell_sync_set(cc_idx, cell_info); + + logger_phy.info( + "Finished setting new SCell configuration cc_idx=%d, earfcn=%d, pci=%d", cc_idx, earfcn, cell_info.id); + + // Configure secondary serving cell, allows this component carrier to execute PHY processing + common.cell_state.configure(cc_idx, earfcn, cell_info.id); + + stack->set_scell_complete(true); +} + void phy::set_config_tdd(srsran_tdd_config_t& tdd_config_) { tdd_config = tdd_config_; @@ -565,7 +605,7 @@ void phy::set_config_tdd(srsran_tdd_config_t& tdd_config_) // set_tdd_config is not protected so run when worker is finished lte::sf_worker* w = lte_workers.wait_worker_id(i); if (w) { - w->set_tdd_config_unlocked(tdd_config); + w->set_tdd_config_nolock(tdd_config); w->release(); } } @@ -610,18 +650,20 @@ void phy::set_mch_period_stop(uint32_t stop) int phy::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) { - if (!nr_workers.init(args_, &common, stack_, WORKERS_THREAD_PRIO)) { + stack_nr = stack_; + if (!nr_workers.init(args_, common, stack_)) { return SRSRAN_ERROR; } return SRSRAN_SUCCESS; } -int phy::set_ul_grant(std::array packed_ul_grant, - uint16_t rnti, - srsran_rnti_type_t rnti_type) +int phy::set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) { - return nr_workers.set_ul_grant(packed_ul_grant, rnti, rnti_type); + return nr_workers.set_rar_grant(rar_slot_idx, packed_ul_grant, rnti, rnti_type); } void phy::send_prach(const uint32_t prach_occasion, @@ -632,11 +674,6 @@ void phy::send_prach(const uint32_t prach_occasion, nr_workers.send_prach(prach_occasion, preamble_index, preamble_received_target_power); } -int phy::tx_request(const phy_interface_mac_nr::tx_request_t& request) -{ - return 0; -} - void phy::set_earfcn(std::vector earfcns) { // Do nothing @@ -644,23 +681,36 @@ void phy::set_earfcn(std::vector earfcns) bool phy::set_config(const srsran::phy_cfg_nr_t& cfg) { - // Derive actual RF frequencies for NR carrier - double abs_freq_point_a_freq = srsran::srsran_band_helper().nr_arfcn_to_freq(cfg.carrier.absolute_frequency_point_a); + // Stash NR configuration + config_nr = cfg; - // for FR1 unit of resources blocks for freq calc is always 180kHz regardless for actual SCS of carrier - // TODO: add offset_to_carrier - double carrier_center_freq = - abs_freq_point_a_freq + - (cfg.carrier.nof_prb / 2 * SRSRAN_SUBC_SPACING_NR(srsran_subcarrier_spacing_t::srsran_subcarrier_spacing_15kHz) * - SRSRAN_NRE); + // Setup carrier configuration asynchronously + cmd_worker.add_cmd([this]() { + srsran::srsran_band_helper band_helper; - for (uint32_t i = 0; i < common.args->nof_nr_carriers; i++) { - logger_phy.info("Tuning channel %d to %.2f GHz", i + common.args->nof_lte_carriers, carrier_center_freq / 1e6); - radio->set_rx_freq(i + common.args->nof_lte_carriers, carrier_center_freq); - radio->set_tx_freq(i + common.args->nof_lte_carriers, carrier_center_freq); - } + // tune radio + for (uint32_t i = 0; i < common.args->nof_nr_carriers; i++) { + logger_phy.info("Tuning Rx channel %d to %.2f MHz", + i + common.args->nof_lte_carriers, + config_nr.carrier.dl_center_frequency_hz / 1e6); + radio->set_rx_freq(i + common.args->nof_lte_carriers, config_nr.carrier.dl_center_frequency_hz); + logger_phy.info("Tuning Tx channel %d to %.2f MHz", + i + common.args->nof_lte_carriers, + config_nr.carrier.ul_center_frequency_hz / 1e6); + radio->set_tx_freq(i + common.args->nof_lte_carriers, config_nr.carrier.ul_center_frequency_hz); + } - return nr_workers.set_config(cfg); + // Set UE configuration + bool ret = nr_workers.set_config(config_nr); + + // Notify PHY config completion + if (stack_nr != nullptr) { + stack_nr->set_phy_config_complete(ret); + } + + return ret; + }); + return true; } bool phy::has_valid_sr_resource(uint32_t sr_id) diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index c543df73b..6a87e7695 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -54,17 +54,25 @@ void phy_common::init(phy_args_t* _args, stack_interface_phy_lte* _stack, rsrp_insync_itf* _chest_loop) { - radio_h = _radio; - stack = _stack; - args = _args; - insync_itf = _chest_loop; - sr_last_tx_tti = -1; + radio_h = _radio; + stack = _stack; + args = _args; + insync_itf = _chest_loop; + sr.reset(); // Instantiate UL channel emulator if (args->ul_channel_args.enable) { ul_channel = srsran::channel_ptr( new srsran::channel(args->ul_channel_args, args->nof_lte_carriers * args->nof_rx_ant, logger)); } + + // Init the CFR config struct with the CFR args + cfr_config.cfr_enable = args->cfr_args.enable; + cfr_config.cfr_mode = args->cfr_args.mode; + cfr_config.alpha = args->cfr_args.strength; + cfr_config.manual_thr = args->cfr_args.manual_thres; + cfr_config.max_papr_db = args->cfr_args.auto_target_papr; + cfr_config.ema_alpha = args->cfr_args.ema_alpha; } void phy_common::set_ue_dl_cfg(srsran_ue_dl_cfg_t* ue_dl_cfg) @@ -315,12 +323,14 @@ bool phy_common::is_any_ul_pending_ack() #define tti_pusch_hi(sf) \ (sf->tti + \ (cell.frame_type == SRSRAN_FDD ? FDD_HARQ_DELAY_UL_MS \ - : I_phich ? 7 : k_pusch[sf->tdd_config.sf_config][sf->tti % 10]) + \ + : I_phich ? 7 \ + : k_pusch[sf->tdd_config.sf_config][sf->tti % 10]) + \ (FDD_HARQ_DELAY_DL_MS - FDD_HARQ_DELAY_UL_MS)) #define tti_pusch_gr(sf) \ (sf->tti + \ (cell.frame_type == SRSRAN_FDD ? FDD_HARQ_DELAY_UL_MS \ - : dci->ul_idx == 1 ? 7 : k_pusch[sf->tdd_config.sf_config][sf->tti % 10]) + \ + : dci->ul_idx == 1 ? 7 \ + : k_pusch[sf->tdd_config.sf_config][sf->tti % 10]) + \ (FDD_HARQ_DELAY_DL_MS - FDD_HARQ_DELAY_UL_MS)) // SF->TTI is at which Format0 dci is received @@ -540,48 +550,53 @@ bool phy_common::get_dl_pending_ack(srsran_ul_sf_cfg_t* sf, uint32_t cc_idx, srs * Each worker uses this function to indicate that all processing is done and data is ready for transmission or * there is no transmission at all (tx_enable). In that case, the end of burst message will be sent to the radio */ -void phy_common::worker_end(void* tx_sem_id, - bool tx_enable, - srsran::rf_buffer_t& buffer, - srsran::rf_timestamp_t& tx_time, - bool is_nr) +void phy_common::worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) { // Wait for the green light to transmit in the current TTI - semaphore.wait(tx_sem_id); + semaphore.wait(w_ctx.worker_ptr); - // If this is for NR, save Tx buffers... - if (is_nr) { - nr_tx_buffer = buffer; - nr_tx_buffer_ready = true; + // For each channel set or combine baseband + if (tx_enable) { + tx_buffer.set_combine(buffer); + + // Flag transmit enabled + tx_enabled = true; + } + + // If the current worker is not the last one, skip transmission + if (not w_ctx.last) { + if (tx_enable) { + reset_last_worker(); + } + + // Release semaphore and let next worker to get in semaphore.release(); + + // If this worker transmitted, hold the worker until last SF worker finishes + if (tx_enable) { + wait_last_worker(); + } + return; } - // ... otherwise, append NR base-band from saved buffer if available - if (nr_tx_buffer_ready) { - // Load NR carrier base-band - for (uint32_t i = 0; i < args->nof_nr_carriers * args->nof_rx_ant; i++) { - uint32_t channel_idx = args->nof_lte_carriers * args->nof_rx_ant + i; - buffer.set(channel_idx, nr_tx_buffer.get(i)); - } - - // Remove NR buffer flag - nr_tx_buffer_ready = false; - - // Make sure it transmits in this TTI - tx_enable = true; - } - - // Add Time Alignment + // Add current time alignment + srsran::rf_timestamp_t tx_time = w_ctx.tx_time; // get transmit time from the last worker tx_time.sub((double)ta.get_sec()); - // For each radio, transmit - if (tx_enable) { + // Check if any worker had a transmission + if (tx_enabled) { + // Set number of samples to the latest transmit buffer + tx_buffer.set_nof_samples(buffer.get_nof_samples()); + + // Run uplink channel emulator if (ul_channel) { - ul_channel->run(buffer.to_cf_t(), buffer.to_cf_t(), buffer.get_nof_samples(), tx_time.get(0)); + ul_channel->run(tx_buffer.to_cf_t(), tx_buffer.to_cf_t(), tx_buffer.get_nof_samples(), tx_time.get(0)); } - radio_h->tx(buffer, tx_time); + // Actual baseband transmission + radio_h->tx(tx_buffer, tx_time); + } else { if (radio_h->is_continuous_tx()) { if (is_pending_tx_end) { @@ -603,6 +618,15 @@ void phy_common::worker_end(void* tx_sem_id, } } + // Notify that last SF worker finished. Releases all the threads waiting. + last_worker(); + + // Reset tx buffer to prevent next SF uses previous data + tx_enabled = false; + for (uint32_t ch = 0; ch < SRSRAN_MAX_CHANNELS; ch++) { + tx_buffer.set(ch, nullptr); + } + // Allow next TTI to transmit semaphore.release(); } @@ -620,8 +644,31 @@ void phy_common::update_cfo_measurement(uint32_t cc_idx, float cfo_hz) { std::unique_lock lock(meas_mutex); - // use SNR EMA coefficient for averaging - avg_cfo_hz[cc_idx] = SRSRAN_VEC_EMA(cfo_hz, avg_cfo_hz[cc_idx], args->snr_ema_coeff); + // Use SNR EMA coefficient for averaging + avg_cfo_hz[cc_idx] = SRSRAN_VEC_SAFE_EMA(cfo_hz, avg_cfo_hz[cc_idx], args->snr_ema_coeff); +} + +void phy_common::reset_measurements(uint32_t cc_idx) +{ + // If the CC index exceeds the maximum number of carriers, reset them all + if (cc_idx >= SRSRAN_MAX_CARRIERS) { + for (uint32_t cc = 0; cc < SRSRAN_MAX_CARRIERS; cc++) { + reset_measurements(cc); + } + } + + // Default all metrics to NAN to prevent providing invalid information on traces and other layers + std::unique_lock lock(meas_mutex); + pathloss[cc_idx] = NAN; + avg_rsrp[cc_idx] = NAN; + avg_rsrp_dbm[cc_idx] = NAN; + avg_rsrq_db[cc_idx] = NAN; + avg_rssi_dbm[cc_idx] = NAN; + avg_cfo_hz[cc_idx] = NAN; + avg_sinr_db[cc_idx] = NAN; + avg_snr_db[cc_idx] = NAN; + avg_noise[cc_idx] = NAN; + avg_rsrp_neigh[cc_idx] = NAN; } void phy_common::update_measurements(uint32_t cc_idx, @@ -643,9 +690,9 @@ void phy_common::update_measurements(uint32_t cc_idx, return; } - // Only worker 0 reads the RSSI sensor + // Only worker 0 updates RX gain offset every 10 ms if (rssi_power_buffer) { - if (!rssi_read_cnt) { + if (!update_rxgain_cnt) { // Average RSSI over all symbols in antenna port 0 (make sure SF length is non-zero) float rssi_dbm = SRSRAN_SF_LEN_PRB(cell.nof_prb) > 0 ? (srsran_convert_power_to_dB( @@ -653,35 +700,31 @@ void phy_common::update_measurements(uint32_t cc_idx, 30) : 0; if (std::isnormal(rssi_dbm)) { - avg_rssi_dbm[0] = SRSRAN_VEC_EMA(rssi_dbm, avg_rssi_dbm[0], args->snr_ema_coeff); + avg_rssi_dbm[0] = SRSRAN_VEC_SAFE_EMA(rssi_dbm, avg_rssi_dbm[0], args->snr_ema_coeff); } rx_gain_offset = get_radio()->get_rx_gain() + args->rx_gain_offset; } - rssi_read_cnt++; - if (rssi_read_cnt == 1000) { - rssi_read_cnt = 0; + update_rxgain_cnt++; + if (update_rxgain_cnt >= update_rxgain_period) { + update_rxgain_cnt = 0; } } // Average RSRQ over DEFAULT_MEAS_PERIOD_MS then sent to RRC float rsrq_db = chest_res.rsrq_db; if (std::isnormal(rsrq_db)) { - if (!(sf_cfg_dl.tti % pcell_report_period) || !std::isnormal(avg_rsrq_db[cc_idx])) { - avg_rsrq_db[cc_idx] = rsrq_db; - } else { - avg_rsrq_db[cc_idx] = SRSRAN_VEC_CMA(rsrq_db, avg_rsrq_db[cc_idx], sf_cfg_dl.tti % pcell_report_period); + // Reset average RSRQ measurement + if (sf_cfg_dl.tti % pcell_report_period == 0) { + avg_rsrq_db[cc_idx] = NAN; } + avg_rsrq_db[cc_idx] = SRSRAN_VEC_SAFE_CMA(rsrq_db, avg_rsrq_db[cc_idx], sf_cfg_dl.tti % pcell_report_period); } // Average RSRP taken from CRS float rsrp_lin = chest_res.rsrp; if (std::isnormal(rsrp_lin)) { - if (!std::isnormal(avg_rsrp[cc_idx])) { - avg_rsrp[cc_idx] = rsrp_lin; - } else { - avg_rsrp[cc_idx] = SRSRAN_VEC_EMA(rsrp_lin, avg_rsrp[cc_idx], snr_ema_coeff); - } + avg_rsrp[cc_idx] = SRSRAN_VEC_SAFE_EMA(rsrp_lin, avg_rsrp[cc_idx], snr_ema_coeff); } /* Correct absolute power measurements by RX gain offset */ @@ -689,11 +732,11 @@ void phy_common::update_measurements(uint32_t cc_idx, // Serving cell RSRP measurements are averaged over DEFAULT_MEAS_PERIOD_MS then sent to RRC if (std::isnormal(rsrp_dbm)) { - if (!(sf_cfg_dl.tti % pcell_report_period) || !std::isnormal(avg_rsrp_dbm[cc_idx])) { - avg_rsrp_dbm[cc_idx] = rsrp_dbm; - } else { - avg_rsrp_dbm[cc_idx] = SRSRAN_VEC_CMA(rsrp_dbm, avg_rsrp_dbm[cc_idx], sf_cfg_dl.tti % pcell_report_period); + // Reset average RSRP measurement + if (sf_cfg_dl.tti % pcell_report_period == 0) { + avg_rsrp_dbm[cc_idx] = NAN; } + avg_rsrp_dbm[cc_idx] = SRSRAN_VEC_SAFE_CMA(rsrp_dbm, avg_rsrp_dbm[cc_idx], sf_cfg_dl.tti % pcell_report_period); } // Compute PL @@ -702,11 +745,7 @@ void phy_common::update_measurements(uint32_t cc_idx, // Average noise float cur_noise = chest_res.noise_estimate; if (std::isnormal(cur_noise)) { - if (!std::isnormal(avg_noise[cc_idx])) { - avg_noise[cc_idx] = cur_noise; - } else { - avg_noise[cc_idx] = SRSRAN_VEC_EMA(cur_noise, avg_noise[cc_idx], snr_ema_coeff); - } + avg_noise[cc_idx] = SRSRAN_VEC_SAFE_EMA(cur_noise, avg_noise[cc_idx], snr_ema_coeff); } // Calculate SINR using CRS from neighbours if are detected @@ -723,20 +762,12 @@ void phy_common::update_measurements(uint32_t cc_idx, // Average sinr in the log domain if (std::isnormal(sinr_db)) { - if (!std::isnormal(avg_sinr_db[cc_idx])) { - avg_sinr_db[cc_idx] = sinr_db; - } else { - avg_sinr_db[cc_idx] = SRSRAN_VEC_EMA(sinr_db, avg_sinr_db[cc_idx], snr_ema_coeff); - } + avg_sinr_db[cc_idx] = SRSRAN_VEC_SAFE_EMA(sinr_db, avg_sinr_db[cc_idx], snr_ema_coeff); } // Average snr in the log domain if (std::isnormal(chest_res.snr_db)) { - if (!std::isnormal(avg_snr_db[cc_idx])) { - avg_snr_db[cc_idx] = chest_res.snr_db; - } else { - avg_snr_db[cc_idx] = SRSRAN_VEC_EMA(chest_res.snr_db, avg_snr_db[cc_idx], snr_ema_coeff); - } + avg_snr_db[cc_idx] = SRSRAN_VEC_SAFE_EMA(chest_res.snr_db, avg_snr_db[cc_idx], snr_ema_coeff); } // Store metrics @@ -751,9 +782,9 @@ void phy_common::update_measurements(uint32_t cc_idx, set_ch_metrics(cc_idx, ch); - // Prepare measurements for serving cells - bool active = cell_state.is_configured(cc_idx); - if (active && ((sf_cfg_dl.tti % pcell_report_period) == pcell_report_period - 1)) { + // Prepare measurements for serving cells - skip if any measurement is invalid assuming pure zeros are not possible + if (std::isnormal(avg_rsrp_dbm[cc_idx]) and + std::isnormal(avg_cfo_hz[cc_idx] and ((sf_cfg_dl.tti % pcell_report_period) == pcell_report_period - 1))) { phy_meas_t meas = {}; meas.rsrp = avg_rsrp_dbm[cc_idx]; meas.rsrq = avg_rsrq_db[cc_idx]; @@ -875,22 +906,20 @@ void phy_common::reset() { reset_radio(); - sr_enabled = false; - cur_pathloss = 0; - cur_pusch_power = 0; - sr_last_tx_tti = -1; - pcell_report_period = 20; + sr.reset(); + { + std::unique_lock lock(meas_mutex); + cur_pathloss = 0; + cur_pusch_power = 0; + } + last_ri = 0; - ZERO_OBJECT(pathloss); - ZERO_OBJECT(avg_sinr_db); - ZERO_OBJECT(avg_snr_db); - ZERO_OBJECT(avg_rsrp); - ZERO_OBJECT(avg_rsrp_dbm); - ZERO_OBJECT(avg_rsrq_db); + // Reset all measurements + reset_measurements(SRSRAN_MAX_CARRIERS); + + // Reset all SCell states cell_state.reset(); - reset_neighbour_cells(); - // Note: Using memset to reset these members is forbidden because they are real objects, not plain arrays. { std::lock_guard lock(pending_dl_ack_mutex); @@ -915,13 +944,6 @@ void phy_common::reset() i = {}; } } - - // Release mapping of secondary cells - if (args != nullptr && radio_h != nullptr) { - for (uint32_t i = 1; i < args->nof_lte_carriers; i++) { - radio_h->release_freq(i); - } - } } /* Convert 6-bit maps to 10-element subframe tables diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc new file mode 100644 index 000000000..ae791fdac --- /dev/null +++ b/srsue/src/phy/phy_nr_sa.cc @@ -0,0 +1,283 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/phy/phy_nr_sa.h" +#include "srsran/common/standard_streams.h" +#include "srsran/srsran.h" + +namespace srsue { + +static void srsran_phy_handler(phy_logger_level_t log_level, void* ctx, char* str) +{ + phy_nr_sa* r = (phy_nr_sa*)ctx; + r->srsran_phy_logger(log_level, str); +} + +void phy_nr_sa::srsran_phy_logger(phy_logger_level_t log_level, char* str) +{ + switch (log_level) { + case LOG_LEVEL_INFO_S: + logger_phy_lib.info(" %s", str); + break; + case LOG_LEVEL_DEBUG_S: + logger_phy_lib.debug(" %s", str); + break; + case LOG_LEVEL_ERROR_S: + logger_phy_lib.error(" %s", str); + break; + default: + break; + } +} + +void phy_nr_sa::set_default_args(phy_args_nr_t& args_) +{ + args_.nof_phy_threads = DEFAULT_WORKERS; + // TODO +} + +bool phy_nr_sa::check_args(const phy_args_nr_t& args_) +{ + if (args_.nof_phy_threads > MAX_WORKERS) { + srsran::console("Error in PHY args: nof_phy_threads must be 1, 2 or 3\n"); + return false; + } + return true; +} + +phy_nr_sa::phy_nr_sa(const char* logname) : + logger(srslog::fetch_basic_logger(logname)), + logger_phy_lib(srslog::fetch_basic_logger("PHY_LIB")), + sync(logger, workers), + workers(logger, 4) +{} + +int phy_nr_sa::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) +{ + args = args_; + stack = stack_; + radio = radio_; + + // Add PHY lib log + auto lib_log_level = srslog::str_to_basic_level(args.log.phy_lib_level); + logger_phy_lib.set_level(lib_log_level); + logger_phy_lib.set_hex_dump_max_size(args.log.phy_hex_limit); + if (lib_log_level != srslog::basic_levels::none) { + srsran_phy_log_register_handler(this, srsran_phy_handler); + } + + // set default logger + logger.set_level(srslog::str_to_basic_level(args.log.phy_level)); + logger.set_hex_dump_max_size(args.log.phy_hex_limit); + + logger.set_context(0); + + if (!check_args(args)) { + return SRSRAN_ERROR; + } + + is_configured = false; + std::thread t([this]() { init_background(); }); + init_thread = std::move(t); + + return SRSRAN_SUCCESS; +} + +void phy_nr_sa::init_background() +{ + nr::sync_sa::args_t sync_args = {}; + sync_args.srate_hz = args.srate_hz; + sync_args.thread_priority = args.slot_recv_thread_prio; + if (not sync.init(sync_args, stack, radio)) { + logger.error("Error initialising SYNC"); + return; + } + workers.init(args, sync, stack); + + is_configured = true; +} + +void phy_nr_sa::stop() +{ + cmd_worker.stop(); + cmd_worker_cell.stop(); + if (is_configured) { + sync.stop(); + workers.stop(); + is_configured = false; + } +} + +bool phy_nr_sa::is_initialized() +{ + return is_configured; +} + +void phy_nr_sa::wait_initialize() +{ + init_thread.join(); +} + +phy_interface_rrc_nr::phy_nr_state_t phy_nr_sa::get_state() +{ + { + switch (sync.get_state()) { + case sync_state::state_t::IDLE: + return phy_interface_rrc_nr::PHY_NR_STATE_IDLE; + case sync_state::state_t::CELL_SEARCH: + return phy_interface_rrc_nr::PHY_NR_STATE_CELL_SEARCH; + case sync_state::state_t::SFN_SYNC: + return phy_interface_rrc_nr::PHY_NR_STATE_CELL_SELECT; + case sync_state::state_t::CAMPING: + return phy_interface_rrc_nr::PHY_NR_STATE_CAMPING; + } + } + return phy_interface_rrc_nr::PHY_NR_STATE_IDLE; +} + +void phy_nr_sa::reset_nr() +{ + sync.reset(); + + sync.cell_go_idle(); +} + +// This function executes one part of the procedure immediately and returns to continue in the background. +// When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH +// processing. +// It will perform cell search procedure in the background and will signal stack with function cell_search_found_cell() +// when finished +bool phy_nr_sa::start_cell_search(const cell_search_args_t& req) +{ + // TODO: verify arguments are valid before starting procedure + + cmd_worker_cell.add_cmd([this, req]() { + logger.info("Cell Search: Going to IDLE"); + sync.cell_go_idle(); + + // Prepare cell search configuration from the request + nr::cell_search::cfg_t cfg = {}; + cfg.srate_hz = args.srate_hz; + cfg.center_freq_hz = req.center_freq_hz; + cfg.ssb_freq_hz = req.ssb_freq_hz; + cfg.ssb_scs = req.ssb_scs; + cfg.ssb_pattern = req.ssb_pattern; + cfg.duplex_mode = req.duplex_mode; + + // Request cell search to lower synchronization instance. + nr::cell_search::ret_t ret = sync.cell_search_run(cfg); + + // Pass result to stack + rrc_interface_phy_nr::cell_search_result_t rrc_cs_ret = {}; + rrc_cs_ret.cell_found = ret.result == nr::cell_search::ret_t::CELL_FOUND; + if (rrc_cs_ret.cell_found) { + rrc_cs_ret.pci = ret.ssb_res.N_id; + rrc_cs_ret.pbch_msg = ret.ssb_res.pbch_msg; + rrc_cs_ret.measurements = ret.ssb_res.measurements; + } + stack->cell_search_found_cell(rrc_cs_ret); + }); + + return true; +} + +// This function executes one part of the procedure immediately and returns to continue in the background. +// When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH +// processing. +// It will perform cell search procedure in the background and will signal stack with function cell_search_found_cell() +// when finished +bool phy_nr_sa::start_cell_select(const cell_select_args_t& req) +{ + // TODO: verify arguments are valid before starting procedure + + logger.info("Cell Select: Going to IDLE"); + sync.cell_go_idle(); + + selected_cell = req.carrier; + + cmd_worker_cell.add_cmd([this, req]() { + // Request cell search to lower synchronization instance and push the result directly to the stack + stack->cell_select_completed(sync.cell_select_run(req)); + }); + + return true; +} + +bool phy_nr_sa::has_valid_sr_resource(uint32_t sr_id) +{ + return workers.has_valid_sr_resource(sr_id); +} + +void phy_nr_sa::clear_pending_grants() +{ + workers.clear_pending_grants(); +} + +void phy_nr_sa::send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec) +{ + workers.send_prach(prach_occasion, preamble_index, preamble_received_target_power); +} + +void phy_nr_sa::set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) +{ + sync.add_ta_cmd_rar(tti, ta_cmd); +} + +void phy_nr_sa::set_timeadv(uint32_t tti, uint32_t ta_cmd) +{ + sync.add_ta_cmd_new(tti, ta_cmd); +} + +int phy_nr_sa::set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) +{ + return workers.set_rar_grant(rar_slot_idx, packed_ul_grant, rnti, rnti_type); +} + +bool phy_nr_sa::set_config(const srsran::phy_cfg_nr_t& cfg) +{ + // Stash NR configuration + config_nr = cfg; + + // Setup carrier configuration asynchronously + cmd_worker.add_cmd([this]() { + // Set UE configuration + bool ret = workers.set_config(config_nr); + + // Pass n_ta_offset to sync + sync.add_ta_offset(config_nr.t_offset); + + // Notify PHY config completion + if (stack != nullptr) { + stack->set_phy_config_complete(ret); + } + + return ret; + }); + return true; +} + +} // namespace srsue diff --git a/srsue/src/phy/prach.cc b/srsue/src/phy/prach.cc index 4ad721cce..01a21a431 100644 --- a/srsue/src/phy/prach.cc +++ b/srsue/src/phy/prach.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -41,15 +41,7 @@ using namespace srsue; void prach::init(uint32_t max_prb) { - for (auto& i : buffer) { - for (auto& j : i) { - j = srsran_vec_cf_malloc(SRSRAN_PRACH_MAX_LEN); - if (!j) { - perror("malloc"); - return; - } - } - } + std::lock_guard lock(mutex); if (srsran_cfo_init(&cfo_h, SRSRAN_PRACH_MAX_LEN)) { ERROR("PRACH: Error initiating CFO"); @@ -58,7 +50,7 @@ void prach::init(uint32_t max_prb) srsran_cfo_set_tol(&cfo_h, 0); - signal_buffer = srsran_vec_cf_malloc(MAX_LEN_SF * 30720U); + signal_buffer = srsran_vec_cf_malloc(SRSRAN_MAX(MAX_LEN_SF * 30720U, SRSRAN_PRACH_MAX_LEN)); if (!signal_buffer) { perror("malloc"); return; @@ -74,16 +66,11 @@ void prach::init(uint32_t max_prb) void prach::stop() { + std::lock_guard lock(mutex); if (!mem_initiated) { return; } - for (auto& i : buffer) { - for (auto& j : i) { - free(j); - } - } - free(signal_buffer); srsran_cfo_free(&cfo_h); srsran_prach_free(&prach_obj); @@ -92,6 +79,8 @@ void prach::stop() bool prach::set_cell(srsran_cell_t cell_, srsran_prach_cfg_t prach_cfg) { + std::lock_guard lock(mutex); + if (!mem_initiated) { ERROR("PRACH: Error must call init() first"); return false; @@ -123,7 +112,6 @@ bool prach::set_cell(srsran_cell_t cell_, srsran_prach_cfg_t prach_cfg) return false; } - buffer_bitmask.reset(); len = prach_obj.N_seq + prach_obj.N_cp; transmitted_tti = -1; cell_initiated = true; @@ -135,27 +123,22 @@ bool prach::set_cell(srsran_cell_t cell_, srsran_prach_cfg_t prach_cfg) bool prach::generate_buffer(uint32_t f_idx) { - if (is_buffer_generated(f_idx, preamble_idx)) { - return true; - } - uint32_t freq_offset = cfg.freq_offset; if (cell.frame_type == SRSRAN_TDD) { freq_offset = srsran_prach_f_ra_tdd( cfg.config_idx, cfg.tdd_config.sf_config, (f_idx / 6) * 10, f_idx % 6, cfg.freq_offset, cell.nof_prb); } - if (srsran_prach_gen(&prach_obj, preamble_idx, freq_offset, buffer[f_idx][preamble_idx])) { + if (srsran_prach_gen(&prach_obj, preamble_idx, freq_offset, signal_buffer)) { Error("Generating PRACH preamble %d", preamble_idx); return false; } - set_buffer_as_generated(f_idx, preamble_idx); - return true; } bool prach::prepare_to_send(uint32_t preamble_idx_, int allowed_subframe_, float target_power_dbm_) { + std::lock_guard lock(mutex); if (preamble_idx_ >= max_preambles) { Error("PRACH: Invalid preamble %d", preamble_idx_); return false; @@ -171,13 +154,18 @@ bool prach::prepare_to_send(uint32_t preamble_idx_, int allowed_subframe_, float bool prach::is_pending() const { - return cell_initiated && preamble_idx >= 0 && unsigned(preamble_idx) < max_preambles; + std::unique_lock lock(mutex, std::try_to_lock); + if (lock.owns_lock()) { + return cell_initiated && preamble_idx >= 0 && unsigned(preamble_idx) < max_preambles; + } + return false; } bool prach::is_ready_to_send(uint32_t current_tti_, uint32_t current_pci) { // Make sure the curernt PCI is the one we configured the PRACH for if (is_pending() && current_pci == cell.id) { + std::lock_guard lock(mutex); // consider the number of subframes the transmission must be anticipated uint32_t tti_tx = TTI_TX(current_tti_); if (srsran_prach_tti_opportunity(&prach_obj, tti_tx, allowed_subframe)) { @@ -191,6 +179,7 @@ bool prach::is_ready_to_send(uint32_t current_tti_, uint32_t current_pci) phy_interface_mac_lte::prach_info_t prach::get_info() const { + std::lock_guard lock(mutex); phy_interface_mac_lte::prach_info_t info = {}; info.preamble_format = prach_obj.config_idx / 16; @@ -209,6 +198,7 @@ phy_interface_mac_lte::prach_info_t prach::get_info() const cf_t* prach::generate(float cfo, uint32_t* nof_sf, float* target_power) { + std::lock_guard lock(mutex); if (!cell_initiated || preamble_idx < 0 || !nof_sf || unsigned(preamble_idx) >= max_preambles || !srsran_cell_isvalid(&cell) || len >= MAX_LEN_SF * 30720 || len == 0) { Error("PRACH: Invalid parameters: cell_initiated=%d, preamble_idx=%d, cell.nof_prb=%d, len=%d", @@ -236,16 +226,11 @@ cf_t* prach::generate(float cfo, uint32_t* nof_sf, float* target_power) return nullptr; } - if (!is_buffer_generated(f_idx, preamble_idx)) { - Error("PRACH Buffer not generated: f_idx=%d preamble_idx=%d", f_idx, preamble_idx); - return nullptr; - } - // Correct CFO before transmission - srsran_cfo_correct(&cfo_h, buffer[f_idx][preamble_idx], signal_buffer, cfo / srsran_symbol_sz(cell.nof_prb)); + srsran_cfo_correct(&cfo_h, signal_buffer, signal_buffer, cfo / srsran_symbol_sz(cell.nof_prb)); // pad guard symbols with zeros - uint32_t nsf = (len - 1) / SRSRAN_SF_LEN_PRB(cell.nof_prb) + 1; + uint32_t nsf = SRSRAN_CEIL(len, SRSRAN_SF_LEN_PRB(cell.nof_prb)); srsran_vec_cf_zero(&signal_buffer[len], (nsf * SRSRAN_SF_LEN_PRB(cell.nof_prb) - len)); *nof_sf = nsf; diff --git a/srsue/src/phy/scell/intra_measure.cc b/srsue/src/phy/scell/intra_measure.cc deleted file mode 100644 index ee8c9284e..000000000 --- a/srsue/src/phy/scell/intra_measure.cc +++ /dev/null @@ -1,257 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ -#include "srsue/hdr/phy/scell/intra_measure.h" - -#define Error(fmt, ...) \ - if (SRSRAN_DEBUG_ENABLED) \ - logger.error(fmt, ##__VA_ARGS__) -#define Warning(fmt, ...) \ - if (SRSRAN_DEBUG_ENABLED) \ - logger.warning(fmt, ##__VA_ARGS__) -#define Info(fmt, ...) \ - if (SRSRAN_DEBUG_ENABLED) \ - logger.info(fmt, ##__VA_ARGS__) -#define Debug(fmt, ...) \ - if (SRSRAN_DEBUG_ENABLED) \ - logger.debug(fmt, ##__VA_ARGS__) - -namespace srsue { -namespace scell { - -intra_measure::intra_measure(srslog::basic_logger& logger) : scell(logger), logger(logger), thread("SYNC_INTRA_MEASURE") -{} - -intra_measure::~intra_measure() -{ - srsran_ringbuffer_free(&ring_buffer); - scell.deinit(); - free(search_buffer); -} - -void intra_measure::init(uint32_t cc_idx_, phy_common* common, meas_itf* new_cell_itf_) -{ - cc_idx = cc_idx_; - new_cell_itf = new_cell_itf_; - - if (common) { - intra_freq_meas_len_ms = common->args->intra_freq_meas_len_ms; - intra_freq_meas_period_ms = common->args->intra_freq_meas_period_ms; - rx_gain_offset_db = common->args->rx_gain_offset; - } - - // Initialise Reference signal measurement - srsran_refsignal_dl_sync_init(&refsignal_dl_sync); - - // Start scell - scell.init(intra_freq_meas_len_ms); - - search_buffer = srsran_vec_cf_malloc(intra_freq_meas_len_ms * SRSRAN_SF_LEN_PRB(SRSRAN_MAX_PRB)); - - // Initialise buffer for the maximum number of PRB - uint32_t max_required_bytes = (uint32_t)sizeof(cf_t) * intra_freq_meas_len_ms * SRSRAN_SF_LEN_PRB(SRSRAN_MAX_PRB); - if (srsran_ringbuffer_init(&ring_buffer, max_required_bytes)) { - return; - } - - state.set_state(internal_state::idle); - start(INTRA_FREQ_MEAS_PRIO); -} - -void intra_measure::stop() -{ - // Notify quit to asynchronous thread. If it is measuring, it will first finish the measure, report to stack and - // then it will finish - state.set_state(internal_state::quit); - - // Wait for the asynchronous thread to finish - wait_thread_finish(); - - srsran_ringbuffer_stop(&ring_buffer); - srsran_refsignal_dl_sync_free(&refsignal_dl_sync); -} - -void intra_measure::set_primary_cell(uint32_t earfcn, srsran_cell_t cell) -{ - current_earfcn = earfcn; - current_sflen = (uint32_t)SRSRAN_SF_LEN_PRB(cell.nof_prb); - serving_cell = cell; -} - -void intra_measure::set_rx_gain_offset(float rx_gain_offset_db_) -{ - rx_gain_offset_db = rx_gain_offset_db_; -} - -void intra_measure::meas_stop() -{ - // Transition state to idle - // Ring-buffer shall not be reset, it will automatically be reset as soon as the FSM transitions to receive - state.set_state(internal_state::idle); - Info("INTRA: Disabled neighbour cell search for EARFCN %d", get_earfcn()); -} - -void intra_measure::set_cells_to_meas(const std::set& pci) -{ - active_pci_mutex.lock(); - active_pci = pci; - active_pci_mutex.unlock(); - state.set_state(internal_state::receive); - logger.info("INTRA: Received list of %zd neighbour cells to measure in EARFCN %d.", pci.size(), current_earfcn); -} - -void intra_measure::write(uint32_t tti, cf_t* data, uint32_t nsamples) -{ - int nbytes = (int)(nsamples * sizeof(cf_t)); - int required_nbytes = (int)(intra_freq_meas_len_ms * current_sflen * sizeof(cf_t)); - uint32_t elapsed_tti = TTI_SUB(tti, last_measure_tti); - - switch (state.get_state()) { - - case internal_state::idle: - case internal_state::measure: - case internal_state::quit: - // Do nothing - break; - case internal_state::wait: - if (elapsed_tti >= intra_freq_meas_period_ms) { - state.set_state(internal_state::receive); - last_measure_tti = tti; - srsran_ringbuffer_reset(&ring_buffer); - } - break; - case internal_state::receive: - // As nbytes might not match the sub-frame size, make sure that buffer does not overflow - nbytes = SRSRAN_MIN(srsran_ringbuffer_space(&ring_buffer), nbytes); - - // Try writing in the buffer - if (srsran_ringbuffer_write(&ring_buffer, data, nbytes) < nbytes) { - Warning("INTRA: Error writing to ringbuffer (EARFCN=%d)", current_earfcn); - - // Transition to wait, so it can keep receiving without stopping the component operation - state.set_state(internal_state::wait); - } else { - // As soon as there are enough samples in the buffer, transition to measure - if (srsran_ringbuffer_status(&ring_buffer) >= required_nbytes) { - state.set_state(internal_state::measure); - } - } - break; - } -} - -void intra_measure::measure_proc() -{ - std::set cells_to_measure = {}; - - // Load cell list to measure - active_pci_mutex.lock(); - cells_to_measure = active_pci; - active_pci_mutex.unlock(); - - // Read data from buffer and find cells in it - srsran_ringbuffer_read(&ring_buffer, search_buffer, (int)intra_freq_meas_len_ms * current_sflen * sizeof(cf_t)); - - // Go to receive before finishing, so new samples can be enqueued before the thread finishes - if (state.get_state() == internal_state::measure) { - // Prevents transition to wait if state has changed while reading the ring-buffer - state.set_state(internal_state::wait); - } - - // Detect new cells using PSS/SSS - std::set detected_cells = scell.find_cells(search_buffer, serving_cell, intra_freq_meas_len_ms); - - // Add detected cells to the list of cells to measure - for (const uint32_t& c : detected_cells) { - cells_to_measure.insert(c); - } - - // Initialise empty neighbour cell list - std::vector neighbour_cells = {}; - - new_cell_itf->cell_meas_reset(cc_idx); - - // Use Cell Reference signal to measure cells in the time domain for all known active PCI - for (const uint32_t& id : cells_to_measure) { - // Do not measure serving cell here since it's measured by workers - if (id == serving_cell.id) { - continue; - } - srsran_cell_t cell = serving_cell; - cell.id = id; - - srsran_refsignal_dl_sync_set_cell(&refsignal_dl_sync, cell); - srsran_refsignal_dl_sync_run(&refsignal_dl_sync, search_buffer, intra_freq_meas_len_ms * current_sflen); - - if (refsignal_dl_sync.found) { - phy_meas_t m = {}; - m.pci = cell.id; - m.earfcn = current_earfcn; - m.rsrp = refsignal_dl_sync.rsrp_dBfs - rx_gain_offset_db; - m.rsrq = refsignal_dl_sync.rsrq_dB; - m.cfo_hz = refsignal_dl_sync.cfo_Hz; - neighbour_cells.push_back(m); - - Info("INTRA: Found neighbour cell: EARFCN=%d, PCI=%03d, RSRP=%5.1f dBm, RSRQ=%5.1f, peak_idx=%5d, " - "CFO=%+.1fHz", - m.earfcn, - m.pci, - m.rsrp, - m.rsrq, - refsignal_dl_sync.peak_index, - refsignal_dl_sync.cfo_Hz); - } - } - - // Send measurements to RRC if any cell found - if (not neighbour_cells.empty()) { - new_cell_itf->new_cell_meas(cc_idx, neighbour_cells); - } -} - -void intra_measure::run_thread() -{ - bool quit = false; - - do { - // Get state - internal_state::state_t s = state.get_state(); - switch (s) { - - case internal_state::idle: - case internal_state::wait: - case internal_state::receive: - // Wait for a different state - state.wait_change(s); - break; - case internal_state::measure: - // Run the measurement process - measure_proc(); - break; - case internal_state::quit: - // Quit loop - quit = true; - break; - } - } while (not quit); -} - -} // namespace scell -} // namespace srsue diff --git a/srsue/src/phy/scell/intra_measure_base.cc b/srsue/src/phy/scell/intra_measure_base.cc new file mode 100644 index 000000000..188ceded2 --- /dev/null +++ b/srsue/src/phy/scell/intra_measure_base.cc @@ -0,0 +1,233 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsue/hdr/phy/scell/intra_measure_base.h" + +#define Log(level, fmt, ...) \ + do { \ + logger.level("INTRA-%s-%d: " fmt, to_string(get_rat()).c_str(), get_earfcn(), ##__VA_ARGS__); \ + } while (false) + +namespace srsue { +namespace scell { + +intra_measure_base::intra_measure_base(srslog::basic_logger& logger, meas_itf& new_cell_itf_) : + logger(logger), context(new_cell_itf_), thread("SYNC_INTRA_MEASURE") +{} + +intra_measure_base::~intra_measure_base() +{ + srsran_ringbuffer_free(&ring_buffer); +} + +void intra_measure_base::init_generic(uint32_t cc_idx_, const args_t& args) +{ + context.cc_idx = cc_idx_; + + context.meas_len_ms = args.len_ms; + context.meas_period_ms = args.period_ms; + context.trigger_tti_period = args.tti_period; + context.trigger_tti_offset = args.tti_offset; + rx_gain_offset_db = args.rx_gain_offset_db; + + // Compute subframe length from the sampling rate if available + if (std::isnormal(args.srate_hz)) { + context.sf_len = (uint32_t)round(args.srate_hz / 1000.0); + } else if (get_rat() == srsran::srsran_rat_t::lte) { + // Select maximum subframe size for LTE + context.sf_len = SRSRAN_SF_LEN_PRB(SRSRAN_MAX_PRB); + } else { + // No maximum subframe length is defined for other RATs + ERROR("A sampling rate was expected for %s. Undefined behaviour.", srsran::to_string(get_rat()).c_str()); + return; + } + + // Calculate the new required bytes + int max_required_bytes = (int)(sizeof(cf_t) * context.meas_len_ms * context.sf_len); + + // Reallocate only if the required capacity exceds the new requirement + if (ring_buffer.capacity < max_required_bytes) { + search_buffer.resize((size_t)context.meas_len_ms * (size_t)context.sf_len); + + srsran_ringbuffer_free(&ring_buffer); + + // Initialise buffer for the maximum number of PRB + if (srsran_ringbuffer_init(&ring_buffer, max_required_bytes) < SRSRAN_SUCCESS) { + ERROR("Error initiating ringbuffer"); + return; + } + } + + if (state.get_state() == internal_state::initial) { + state.set_state(internal_state::idle); + start(INTRA_FREQ_MEAS_PRIO); + } +} + +void intra_measure_base::stop() +{ + // Notify quit to asynchronous thread. If it is measuring, it will first finish the measure, report to stack and + // then it will finish + state.set_state(internal_state::quit); + + // Wait for the asynchronous thread to finish + wait_thread_finish(); + + srsran_ringbuffer_stop(&ring_buffer); +} + +void intra_measure_base::set_rx_gain_offset(float rx_gain_offset_db_) +{ + rx_gain_offset_db = rx_gain_offset_db_; +} + +void intra_measure_base::meas_stop() +{ + // Transition state to idle + // Ring-buffer shall not be reset, it will automatically be reset as soon as the FSM transitions to receive + state.set_state(internal_state::idle); + Log(info, "Disabled neighbour cell search"); +} + +void intra_measure_base::set_cells_to_meas(const std::set& pci) +{ + mutex.lock(); + context.active_pci = pci; + mutex.unlock(); + state.set_state(internal_state::wait_first); + Log(info, "Received list of %zd neighbour cells to measure", pci.size()); +} + +void intra_measure_base::write(cf_t* data, uint32_t nsamples) +{ + int nbytes = (int)(nsamples * sizeof(cf_t)); + + mutex.lock(); + int required_nbytes = ((int)context.meas_len_ms * (int)context.sf_len * (int)sizeof(cf_t)); + mutex.unlock(); + + // As nbytes might not match the sub-frame size, make sure that buffer does not overflow + nbytes = SRSRAN_MIN(srsran_ringbuffer_space(&ring_buffer), nbytes); + + // Try writing in the buffer + if (srsran_ringbuffer_write(&ring_buffer, data, nbytes) < nbytes) { + Log(warning, "Error writing to ringbuffer"); + + // Transition to wait, so it can keep receiving without stopping the component operation + state.set_state(internal_state::wait); + } else { + // As soon as there are enough samples in the buffer, transition to measure + if (srsran_ringbuffer_status(&ring_buffer) >= required_nbytes) { + Log(debug, "Starting search and measurements"); + state.set_state(internal_state::measure); + } + } +} + +void intra_measure_base::run_tti(uint32_t tti, cf_t* data, uint32_t nsamples) +{ + logger.set_context(tti); + + switch (state.get_state()) { + case internal_state::initial: + case internal_state::idle: + case internal_state::measure: + case internal_state::quit: + // Do nothing + break; + case internal_state::wait: + case internal_state::wait_first: + // Check measurement trigger condition + if (receive_tti_trigger(tti)) { + state.set_state(internal_state::receive); + last_measure_tti = tti; + srsran_ringbuffer_reset(&ring_buffer); + + // Write baseband to ensure measurement starts in the right TTI + Log(debug, "Start writing"); + write(data, nsamples); + } + break; + case internal_state::receive: + // Write baseband + write(data, nsamples); + break; + } +} + +void intra_measure_base::measure_proc() +{ + // Grab a copy of the context and pass it to the measure_rat method. + measure_context_t context_copy = get_context(); + + // Read data from buffer and find cells in it + int ret = srsran_ringbuffer_read_timed(&ring_buffer, + search_buffer.data(), + ((int)context_copy.meas_len_ms * (int)context_copy.sf_len * (int)sizeof(cf_t)), + 1000); + + // As this function is called once the ring-buffer has enough data to process, it is not expected to fail + if (ret < SRSRAN_SUCCESS) { + Log(error, "Ringbuffer read returned %d", ret); + return; + } + + // Go to receive before finishing, so new samples can be enqueued before the thread finishes + if (state.get_state() == internal_state::measure) { + // Prevents transition to wait if state has changed while reading the ring-buffer + state.set_state(internal_state::wait); + } + + // Perform measurements for the actual RAT + if (not measure_rat(std::move(context_copy), search_buffer, rx_gain_offset_db)) { + Log(error, "Error measuring RAT"); + } +} + +void intra_measure_base::run_thread() +{ + bool quit = false; + + do { + // Get state + internal_state::state_t s = state.get_state(); + switch (s) { + case internal_state::initial: + case internal_state::idle: + case internal_state::wait: + case internal_state::wait_first: + case internal_state::receive: + // Wait for a different state + state.wait_change(s); + break; + case internal_state::measure: + // Run the measurement process + measure_proc(); + break; + case internal_state::quit: + // Quit loop + quit = true; + break; + } + } while (not quit); +} + +} // namespace scell +} // namespace srsue diff --git a/srsue/src/phy/scell/intra_measure_lte.cc b/srsue/src/phy/scell/intra_measure_lte.cc new file mode 100644 index 000000000..947216391 --- /dev/null +++ b/srsue/src/phy/scell/intra_measure_lte.cc @@ -0,0 +1,130 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsue/hdr/phy/scell/intra_measure_lte.h" + +namespace srsue { +namespace scell { + +#define Log(level, fmt, ...) \ + do { \ + logger.level("INTRA-%s-%d: " fmt, to_string(get_rat()).c_str(), get_earfcn(), ##__VA_ARGS__); \ + } while (false) + +intra_measure_lte::intra_measure_lte(srslog::basic_logger& logger_, meas_itf& new_cell_itf_) : + logger(logger_), scell_rx(logger_), intra_measure_base(logger_, new_cell_itf_) +{} + +intra_measure_lte::~intra_measure_lte() +{ + scell_rx.deinit(); + srsran_refsignal_dl_sync_free(&refsignal_dl_sync); +} + +void intra_measure_lte::init(uint32_t cc_idx, const args_t& args) +{ + init_generic(cc_idx, args); + + // Initialise Reference signal measurement + srsran_refsignal_dl_sync_init(&refsignal_dl_sync, SRSRAN_CP_NORM); + + // Start scell + scell_rx.init(args.len_ms); +} + +void intra_measure_lte::set_primary_cell(uint32_t earfcn, srsran_cell_t cell) +{ + { + std::lock_guard lock(mutex); + serving_cell = cell; + } + current_earfcn = earfcn; + set_current_sf_len((uint32_t)SRSRAN_SF_LEN_PRB(cell.nof_prb)); +} + +bool intra_measure_lte::measure_rat(const measure_context_t& context, std::vector& buffer, float rx_gain_offset) +{ + std::set cells_to_measure = context.active_pci; + + srsran_cell_t serving_cell_copy{}; + { + std::lock_guard lock(mutex); + serving_cell_copy = serving_cell; + } + + // Detect new cells using PSS/SSS + scell_rx.find_cells(buffer.data(), serving_cell_copy, context.meas_len_ms, cells_to_measure); + + // Initialise empty neighbour cell list + std::vector neighbour_cells = {}; + + context.new_cell_itf.cell_meas_reset(context.cc_idx); + + // Use Cell Reference signal to measure cells in the time domain for all known active PCI + for (const uint32_t& id : cells_to_measure) { + // Do not measure serving cell here since it's measured by workers + if (id == serving_cell_copy.id) { + continue; + } + srsran_cell_t cell = serving_cell_copy; + cell.id = id; + + if (srsran_refsignal_dl_sync_set_cell(&refsignal_dl_sync, cell) < SRSRAN_SUCCESS) { + Log(error, "Error setting refsignal DL cell"); + return false; + } + + if (srsran_refsignal_dl_sync_run(&refsignal_dl_sync, buffer.data(), context.meas_len_ms * context.sf_len) < + SRSRAN_SUCCESS) { + Log(error, "Error running refsignal DL measurements"); + return false; + } + + if (refsignal_dl_sync.found) { + phy_meas_t m = {}; + m.rat = srsran::srsran_rat_t::lte; + m.pci = cell.id; + m.earfcn = current_earfcn; + m.rsrp = refsignal_dl_sync.rsrp_dBfs - rx_gain_offset_db; + m.rsrq = refsignal_dl_sync.rsrq_dB; + m.cfo_hz = refsignal_dl_sync.cfo_Hz; + neighbour_cells.push_back(m); + + Log(info, + "Found neighbour cell: PCI=%03d, RSRP=%5.1f dBm, RSRQ=%5.1f, peak_idx=%5d, " + "CFO=%+.1fHz", + m.pci, + m.rsrp, + m.rsrq, + refsignal_dl_sync.peak_index, + refsignal_dl_sync.cfo_Hz); + } + } + + // Send measurements to RRC if any cell found + if (not neighbour_cells.empty()) { + context.new_cell_itf.new_cell_meas(context.cc_idx, neighbour_cells); + } + + return true; +} + +} // namespace scell +} // namespace srsue diff --git a/srsue/src/phy/scell/intra_measure_nr.cc b/srsue/src/phy/scell/intra_measure_nr.cc new file mode 100644 index 000000000..f669de15b --- /dev/null +++ b/srsue/src/phy/scell/intra_measure_nr.cc @@ -0,0 +1,142 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsue/hdr/phy/scell/intra_measure_nr.h" + +#define Log(level, fmt, ...) \ + do { \ + logger.level("INTRA-%s-%d: " fmt, to_string(get_rat()).c_str(), get_earfcn(), ##__VA_ARGS__); \ + } while (false) + +namespace srsue { +namespace scell { + +intra_measure_nr::intra_measure_nr(srslog::basic_logger& logger_, meas_itf& new_meas_itf_) : + logger(logger_), intra_measure_base(logger_, new_meas_itf_) +{} + +intra_measure_nr::~intra_measure_nr() +{ + srsran_ssb_free(&ssb); +} + +bool intra_measure_nr::init(uint32_t cc_idx_, const args_t& args) +{ + cc_idx = cc_idx_; + thr_snr_db = args.thr_snr_db; + + // Initialise generic side + intra_measure_base::args_t base_args = {}; + base_args.srate_hz = args.max_srate_hz; + base_args.len_ms = args.max_len_ms; + base_args.period_ms = 20; // Hard-coded, it does not make a difference at this stage + base_args.rx_gain_offset_db = args.rx_gain_offset_dB; + init_generic(cc_idx, base_args); + + // Initialise SSB + srsran_ssb_args_t ssb_args = {}; + ssb_args.max_srate_hz = args.max_srate_hz; + ssb_args.min_scs = args.min_scs; + ssb_args.enable_search = true; + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + Log(error, "Error initiating SSB"); + return false; + } + + return true; +} + +bool intra_measure_nr::set_config(const config_t& cfg) +{ + // Update ARFCN + current_arfcn = cfg.arfcn; + serving_cell_pci = cfg.serving_cell_pci; + + // Reset performance measurement + perf_count_samples = 0; + perf_count_us = 0; + + // Re-configure generic side + init_generic(cc_idx, cfg); + + // Configure SSB + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = cfg.srate_hz; + ssb_cfg.center_freq_hz = cfg.center_freq_hz; + ssb_cfg.ssb_freq_hz = cfg.ssb_freq_hz; + ssb_cfg.scs = cfg.scs; + if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { + Log(error, "Error configuring SSB"); + return false; + } + + return true; +} + +bool intra_measure_nr::measure_rat(const measure_context_t& context, std::vector& buffer, float rx_gain_offset) +{ + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + + // Search and measure the best cell + srsran_csi_trs_measurements_t meas = {}; + uint32_t N_id = 0; + if (srsran_ssb_csi_search(&ssb, buffer.data(), context.sf_len * context.meas_len_ms, &N_id, &meas) < SRSRAN_SUCCESS) { + Log(error, "Error searching for SSB"); + return false; + } + + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + perf_count_us += std::chrono::duration_cast(end - begin).count(); + perf_count_samples += (uint64_t)context.sf_len * (uint64_t)context.meas_len_ms; + + // Early return if the found PCI matches with the serving cell ID + if (serving_cell_pci == (int)N_id) { + return true; + } + + // Take valid decision if SNR threshold is exceeded + bool valid = (meas.snr_dB >= thr_snr_db); + + // Log finding + if ((logger.info.enabled() and valid) or logger.debug.enabled()) { + std::array str_info = {}; + srsran_csi_rs_measure_info(&meas, str_info.data(), (uint32_t)str_info.size()); + Log(info, "%s neighbour cell: PCI=%03d %s", valid ? "Found" : "Best", N_id, str_info.data()); + } + + // Check threshold + if (valid) { + // Prepare found measurements + std::vector meas_list(1); + meas_list[0].rat = get_rat(); + meas_list[0].rsrp = meas.rsrp_dB + rx_gain_offset_db; + meas_list[0].cfo_hz = meas.cfo_hz; + meas_list[0].earfcn = get_earfcn(); + meas_list[0].pci = N_id; + + // Push measurements to higher layers + context.new_cell_itf.new_cell_meas(cc_idx, meas_list); + } + + return true; +} + +} // namespace scell +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/scell/scell_recv.cc b/srsue/src/phy/scell/scell_recv.cc index e8a26cdd0..68ca6a8f1 100644 --- a/srsue/src/phy/scell/scell_recv.cc +++ b/srsue/src/phy/scell/scell_recv.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -77,18 +77,18 @@ void scell_recv::reset() current_fft_sz = 0; } -std::set -scell_recv::find_cells(const cf_t* input_buffer, const srsran_cell_t serving_cell, const uint32_t nof_sf) +void scell_recv::find_cells(const cf_t* input_buffer, + const srsran_cell_t& serving_cell, + const uint32_t& nof_sf, + std::set& found_cell_ids) { - std::set found_cell_ids = {}; - uint32_t fft_sz = srsran_symbol_sz(serving_cell.nof_prb); uint32_t sf_len = SRSRAN_SF_LEN(fft_sz); if (fft_sz != current_fft_sz) { if (srsran_sync_resize(&sync_find, nof_sf * sf_len, 5 * sf_len, fft_sz)) { logger.error("Error resizing sync nof_sf=%d, sf_len=%d, fft_sz=%d", nof_sf, sf_len, fft_sz); - return found_cell_ids; + return; } current_fft_sz = fft_sz; } @@ -97,7 +97,6 @@ scell_recv::find_cells(const cf_t* input_buffer, const srsran_cell_t serving_cel int cell_id = 0; for (uint32_t n_id_2 = 0; n_id_2 < 3; n_id_2++) { - if (n_id_2 != (serving_cell.id % 3)) { srsran_sync_set_N_id_2(&sync_find, n_id_2); @@ -117,7 +116,7 @@ scell_recv::find_cells(const cf_t* input_buffer, const srsran_cell_t serving_cel sync_res = srsran_sync_find(&sync_find, input_buffer, sf5_cnt * 5 * sf_len, &peak_idx); if (sync_res == SRSRAN_SYNC_ERROR) { logger.error("INTRA: Error calling sync_find()"); - return found_cell_ids; + return; } if (sync_find.peak_value > max_peak && sync_res == SRSRAN_SYNC_FOUND && srsran_sync_sss_detected(&sync_find)) { @@ -153,7 +152,7 @@ scell_recv::find_cells(const cf_t* input_buffer, const srsran_cell_t serving_cel } } } - return found_cell_ids; + return; } } // namespace scell diff --git a/srsue/src/phy/search.cc b/srsue/src/phy/search.cc index e1ab7d942..2d3b2f583 100644 --- a/srsue/src/phy/search.cc +++ b/srsue/src/phy/search.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -55,7 +55,7 @@ search::~search() srsran_ue_cellsearch_free(&cs); } -void search::init(srsran::rf_buffer_t& buffer_, uint32_t nof_rx_channels, search_callback* parent, int force_N_id_2_) +void search::init(srsran::rf_buffer_t& buffer_, uint32_t nof_rx_channels, search_callback* parent, int force_N_id_2_, int force_N_id_1_) { p = parent; @@ -74,6 +74,12 @@ void search::init(srsran::rf_buffer_t& buffer_, uint32_t nof_rx_channels, search p->set_ue_sync_opts(&cs.ue_sync, 0); force_N_id_2 = force_N_id_2_; + force_N_id_1 = force_N_id_1_; +} + +void search::set_cp_en(bool enable) +{ + srsran_set_detect_cp(&cs, enable); } void search::reset() @@ -115,7 +121,7 @@ search::ret_code search::run(srsran_cell_t* cell_, std::array= 0 && force_N_id_2 < 3) { + if (force_N_id_2 >= 0 && force_N_id_2 < SRSRAN_NOF_NID_2) { ret = srsran_ue_cellsearch_scan_N_id_2(&cs, force_N_id_2, &found_cells[force_N_id_2]); max_peak_cell = force_N_id_2; } else { @@ -129,6 +135,38 @@ search::ret_code search::run(srsran_cell_t* cell_, std::array= 0 && force_N_id_1 < SRSRAN_NOF_NID_1) { + /* Note that srsran_ue_cellsearch_scan_N_id_2 only finds the strongest cell for a given N_id_2/PSS within the search + * window. A cell with the desired SSS can be occluded by other cells with the same PSS, if their PSS is stronger and + * within the same search window. + */ + bool N_id_1_found = false; + if (force_N_id_2 >= 0 && force_N_id_2 < SRSRAN_NOF_NID_2) { + // N_id_2 (PSS) was forced, so there is only one search result to check + if (found_cells[max_peak_cell].cell_id / SRSRAN_NOF_NID_2 == (uint32_t)force_N_id_1) { + N_id_1_found = true; + } + } else { + // Go through the results for all N_id_2 (PSS); pick strongest with matching N_id_1 (SSS) + float max_peak_value = -1.0; + for (uint32_t N_id_2 = 0; N_id_2 < SRSRAN_NOF_NID_2; N_id_2++) { + if (found_cells[N_id_2].cell_id / SRSRAN_NOF_NID_2 == (uint32_t)force_N_id_1) { + if (found_cells[N_id_2].peak > max_peak_value) { + N_id_1_found = true; + max_peak_value = found_cells[N_id_2].peak; + max_peak_cell = N_id_2; + } + } + } + } + if (!N_id_1_found) { + Info("SYNC: Could not find any cell with preselected SSS (force_N_id_1=%d)", force_N_id_1); + return CELL_NOT_FOUND; + } + } + // Save result new_cell.id = found_cells[max_peak_cell].cell_id; new_cell.cp = found_cells[max_peak_cell].cp; @@ -163,11 +201,12 @@ search::ret_code search::run(srsran_cell_t* cell_, std::arrayargs->detect_cp); if (worker_com->args->dl_channel_args.enable) { channel_emulator = @@ -90,16 +91,23 @@ void sync::init(srsran::radio_interface_phy* _radio, } // Initialize cell searcher - search_p.init(sf_buffer, nof_rf_channels, this, worker_com->args->force_N_id_2); - + search_p.init(sf_buffer, nof_rf_channels, this, worker_com->args->force_N_id_2, worker_com->args->force_N_id_1); + search_p.set_cp_en(worker_com->args->detect_cp); // Initialize SFN synchronizer, it uses only pcell buffer sfn_p.init(&ue_sync, worker_com->args, sf_buffer, sf_buffer.size()); // Start intra-frequency measurement - for (uint32_t i = 0; i < worker_com->args->nof_lte_carriers; i++) { - scell::intra_measure* q = new scell::intra_measure(phy_logger); - q->init(i, worker_com, this); - intra_freq_meas.push_back(std::unique_ptr(q)); + { + std::lock_guard lock(intra_freq_cfg_mutex); + for (uint32_t i = 0; i < worker_com->args->nof_lte_carriers; i++) { + scell::intra_measure_lte* q = new scell::intra_measure_lte(phy_logger, *this); + scell::intra_measure_base::args_t args = {}; + args.len_ms = worker_com->args->intra_freq_meas_len_ms; + args.period_ms = worker_com->args->intra_freq_meas_period_ms; + args.rx_gain_offset_db = worker_com->args->rx_gain_offset; + q->init(i, args); + intra_freq_meas.push_back(std::unique_ptr(q)); + } } // Allocate Secondary serving cell synchronization @@ -129,16 +137,19 @@ sync::~sync() void sync::stop() { - worker_com->semaphore.wait_all(); + running = false; + + std::lock_guard lock(intra_freq_cfg_mutex); for (auto& q : intra_freq_meas) { q->stop(); } - running = false; // Reset (stop Rx stream) as soon as possible to avoid base-band Rx buffer overflow radio_h->reset(); + // let the sync FSM finish before waiting for workers semaphores to avoid workers locking wait_thread_finish(); + worker_com->semaphore.wait_all(); } void sync::reset() @@ -146,7 +157,7 @@ void sync::reset() in_sync_cnt = 0; out_of_sync_cnt = 0; current_earfcn = -1; - srate_mode = SRATE_NONE; + srate.reset(); sfn_p.reset(); search_p.reset(); } @@ -179,10 +190,10 @@ void sync::reset() * */ -/* A call to cell_search() finds the strongest cell in the set of supported EARFCNs. When the first cell is found, - * returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not found in the - * current frequency it moves to the next one and the next call to cell_search() will look in the next EARFCN in the - * set. If no cells are found in any frequency it returns 0. If error returns -1. +/* A call to cell_search() finds the strongest cell at a given EARFCN or in the set of supported EARFCNs. When the first + * cell is found, returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not + * found in the current frequency it moves to the next one and the next call to cell_search() will look in the next + * EARFCN in the set. If no cells are found in any frequency it returns 0. If error returns -1. * * The first part of the procedure (call to _init()) moves the PHY To IDLE, ensuring that no UL/DL/PRACH will happen * @@ -197,9 +208,7 @@ bool sync::cell_search_init() } // Move state to IDLE - Info("Cell Search: Start EARFCN index=%u/%zd", cellsearch_earfcn_index, worker_com->args->dl_earfcn_list.size()); phy_state.go_idle(); - worker_com->reset(); // Stop all intra-frequency measurement before changing frequency meas_stop(); @@ -209,10 +218,16 @@ bool sync::cell_search_init() return true; } -rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* found_cell) +rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* found_cell, int earfcn) { std::unique_lock ul(rrc_mutex); + if (earfcn < 0) { + Info("Cell Search: Start EARFCN index=%u/%zd", cellsearch_earfcn_index, worker_com->args->dl_earfcn_list.size()); + } else { + Info("Cell Search: Start EARFCN=%d", earfcn); + } + rrc_interface_phy_lte::cell_search_ret_t ret = {}; ret.found = rrc_interface_phy_lte::cell_search_ret_t::ERROR; ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS; @@ -224,23 +239,26 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou rrc_proc_state = PROC_SEARCH_RUNNING; - if (srate_mode != SRATE_FIND) { - srate_mode = SRATE_FIND; + if (srate.set_find()) { radio_h->set_rx_srate(1.92e6); radio_h->set_tx_srate(1.92e6); Info("SYNC: Setting Cell Search sampling rate"); } - try { - if (current_earfcn != (int)worker_com->args->dl_earfcn_list.at(cellsearch_earfcn_index)) { - current_earfcn = (int)worker_com->args->dl_earfcn_list[cellsearch_earfcn_index]; - Info("Cell Search: changing frequency to EARFCN=%d", current_earfcn); - set_frequency(); + if (earfcn < 0) { + try { + if (current_earfcn != (int)worker_com->args->dl_earfcn_list.at(cellsearch_earfcn_index)) { + current_earfcn = (int)worker_com->args->dl_earfcn_list[cellsearch_earfcn_index]; + } + } catch (const std::out_of_range& oor) { + Error("Index %d is not a valid EARFCN element.", cellsearch_earfcn_index); + return ret; } - } catch (const std::out_of_range& oor) { - Error("Index %d is not a valid EARFCN element.", cellsearch_earfcn_index); - return ret; + } else { + current_earfcn = earfcn; } + Info("Cell Search: changing frequency to EARFCN=%d", current_earfcn); + set_frequency(); // Move to CELL SEARCH and wait to finish Info("Cell Search: Setting Cell search state"); @@ -249,10 +267,10 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou // Check return state switch (cell_search_ret) { case search::CELL_FOUND: - phy_logger.info("Cell Search: Found cell with PCI=%d with %d PRB", cell.id, cell.nof_prb); + phy_logger.info("Cell Search: Found cell with PCI=%d with %d PRB", cell.get().id, cell.get().nof_prb); if (found_cell) { found_cell->earfcn = current_earfcn; - found_cell->pci = cell.id; + found_cell->pci = cell.get().id; found_cell->cfo_hz = search_p.get_last_cfo(); } ret.found = rrc_interface_phy_lte::cell_search_ret_t::CELL_FOUND; @@ -268,7 +286,7 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou } cellsearch_earfcn_index++; - if (cellsearch_earfcn_index >= worker_com->args->dl_earfcn_list.size()) { + if (cellsearch_earfcn_index >= worker_com->args->dl_earfcn_list.size() or earfcn < 0) { Info("Cell Search: No more frequencies in the current EARFCN set"); cellsearch_earfcn_index = 0; ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS; @@ -305,7 +323,6 @@ bool sync::cell_select_init(phy_cell_t new_cell) Info("Cell Select: Going to IDLE"); phy_state.go_idle(); - worker_com->reset(); // Stop intra-frequency measurements if need to change frequency if ((int)new_cell.earfcn != current_earfcn) { @@ -329,12 +346,12 @@ bool sync::cell_select_start(phy_cell_t new_cell) rrc_proc_state = PROC_SELECT_RUNNING; + // Reset SFN and cell search FSMs. They can safely be done while it is CAMPING or IDLE sfn_p.reset(); search_p.reset(); - srsran_ue_sync_reset(&ue_sync); - /* Reconfigure cell if necessary */ - cell.id = new_cell.pci; + // Reconfigure cell if necessary + cell.set_pci(new_cell.pci); if (not set_cell(new_cell.cfo_hz)) { Error("Cell Select: Reconfiguring cell"); goto clean_exit; @@ -352,19 +369,16 @@ bool sync::cell_select_start(phy_cell_t new_cell) } // Reconfigure first intra-frequency measurement - intra_freq_meas[0]->set_primary_cell(current_earfcn, cell); + intra_freq_meas[0]->set_primary_cell(current_earfcn, cell.get()); // Reconfigure secondary serving cell synchronization assuming the same BW than the primary // The secondary serving cell synchronization will not resize again when the SCell gets configured for (auto& e : scell_sync) { - e.second->set_bw(cell.nof_prb); + e.second->set_bw(cell.get().nof_prb); } // Change sampling rate if necessary - if (srate_mode != SRATE_CAMP) { - phy_logger.info("Cell Select: Setting CAMPING sampling rate"); - set_sampling_rate(); - } + set_sampling_rate(); // SFN synchronization phy_state.run_sfn_sync(); @@ -386,10 +400,32 @@ bool sync::cell_is_camping() return phy_state.is_camping(); } +bool sync::wait_idle() +{ + // Wait for SYNC thread to transition to IDLE (max. 2000ms) + if (not phy_state.wait_idle(TIMEOUT_TO_IDLE_MS)) { + Error("SYNC: Failed transitioning to IDLE"); + return false; + } + + // Reset UE sync. Attention: doing this reset when the FSM is NOT IDLE can cause PSS/SSS out-of-sync + srsran_ue_sync_reset(&ue_sync); + + // Wait for workers to finish PHY processing + worker_com->semaphore.wait_all(); + + // As workers have finished, make sure the Tx burst is ended + radio_h->tx_end(); + + return true; +} + void sync::run_cell_search_state() { - cell_search_ret = search_p.run(&cell, mib); + srsran_cell_t tmp_cell = cell.get(); + cell_search_ret = search_p.run(&tmp_cell, mib); if (cell_search_ret == search::CELL_FOUND) { + cell.set(tmp_cell); stack->bch_decoded_ok(SYNC_CC_IDX, mib.data(), mib.size() / 8); } phy_state.state_exit(); @@ -397,12 +433,11 @@ void sync::run_cell_search_state() void sync::run_sfn_sync_state() { - srsran_cell_t temp_cell = cell; - switch (sfn_p.run_subframe(&temp_cell, &tti, mib)) { + srsran_cell_t old_cell = cell.get(); + switch (sfn_p.run_subframe(&old_cell, &tti, mib)) { case sfn_sync::SFN_FOUND: - if (memcmp(&cell, &temp_cell, sizeof(srsran_cell_t)) != 0) { - srsran_cell_fprint(stdout, &cell, 0); - srsran_cell_fprint(stdout, &temp_cell, 0); + if (!cell.equals(old_cell)) { + srsran_cell_fprint(stdout, &old_cell, 0); phy_logger.error("Detected cell during SFN synchronization differs from configured cell. Cell reselection to " "cells with different MIB is not supported"); srsran::console("Detected cell during SFN synchronization differs from configured cell. Cell reselection " @@ -451,7 +486,7 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker, // Force decode MIB if required if (force_camping_sfn_sync) { uint32_t _tti = 0; - srsran_cell_t temp_cell = cell; + srsran_cell_t temp_cell = cell.get(); sfn_sync::ret_code ret = sfn_p.decode_mib(&temp_cell, &_tti, &sync_buffer, mib); if (ret == sfn_sync::SFN_FOUND) { @@ -461,7 +496,7 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker, // Disable force_camping_sfn_sync = false; - if (memcmp(&cell, &temp_cell, sizeof(srsran_cell_t)) != 0) { + if (!cell.equals(temp_cell)) { phy_logger.error("Detected cell during SFN synchronization differs from configured cell. Cell " "reselection to cells with different MIB is not supported"); srsran::console("Detected cell during SFN synchronization differs from configured cell. Cell " @@ -476,37 +511,38 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker, Debug("SYNC: Worker %d synchronized", lte_worker->get_id()); - metrics.sfo = srsran_ue_sync_get_sfo(&ue_sync); - metrics.cfo = srsran_ue_sync_get_cfo(&ue_sync); - metrics.ta_us = worker_com->ta.get_usec(); + // Collect and provide metrics from last successful sync + metrics.sfo = sfo; + metrics.cfo = cfo; + metrics.ta_us = worker_com->ta.get_usec(); + metrics.distance_km = worker_com->ta.get_km(); + metrics.speed_kmph = worker_com->ta.get_speed_kmph(tti); for (uint32_t i = 0; i < worker_com->args->nof_lte_carriers; i++) { worker_com->set_sync_metrics(i, metrics); } // Check if we need to TX a PRACH - if (prach_buffer->is_ready_to_send(tti, cell.id)) { + if (prach_buffer->is_ready_to_send(tti, cell.get().id)) { prach_ptr = prach_buffer->generate(get_tx_cfo(), &prach_nof_sf, &prach_power); if (prach_ptr == nullptr) { Error("Generating PRACH"); } } - lte_worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSRAN_SF_LEN_PRB(cell.nof_prb)] : nullptr, prach_power); + lte_worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSRAN_SF_LEN_PRB(cell.get().nof_prb)] : nullptr, + prach_power); // Execute Serving Cell state FSM worker_com->cell_state.run_tti(tti); // Set CFO for all Carriers for (uint32_t cc = 0; cc < worker_com->args->nof_lte_carriers; cc++) { - lte_worker->set_cfo_unlocked(cc, get_tx_cfo()); - worker_com->update_cfo_measurement(cc, srsran_ue_sync_get_cfo(&ue_sync)); + lte_worker->set_cfo_nolock(cc, get_tx_cfo()); + worker_com->update_cfo_measurement(cc, cfo); } - lte_worker->set_tti(tti); - // Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time last_rx_time.add(FDD_HARQ_DELAY_DL_MS * 1e-3); - lte_worker->set_tx_time(last_rx_time); // Advance/reset prach subframe pointer if (prach_ptr) { @@ -517,17 +553,39 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker, } } - // Start NR worker only if present + // Set NR worker context and start if (nr_worker != nullptr) { + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = nr_worker; + context.last = (lte_worker == nullptr); // Set last if standalone + context.tx_time.copy(last_rx_time); + + nr_worker->set_context(context); + + // As UE sync compensates CFO externally based on LTE signal and the NR carrier may estimate the CFO from the LTE + // signal. It is necessary setting an NR external CFO offset to compensate it. + nr_worker_pool->set_ul_ext_cfo(srsran_ue_sync_get_cfo(&ue_sync)); + // NR worker needs to be launched first, phy_common::worker_end expects first the NR worker and the LTE worker. - nr_worker->set_tti(tti); worker_com->semaphore.push(nr_worker); nr_worker_pool->start_worker(nr_worker); } - // Start LTE worker - worker_com->semaphore.push(lte_worker); - lte_worker_pool->start_worker(lte_worker); + // Set LTE worker context and start + if (lte_worker != nullptr) { + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = lte_worker; + context.last = true; + context.tx_time.copy(last_rx_time); + + lte_worker->set_context(context); + + // NR worker needs to be launched first, phy_common::worker_end expects first the NR worker and the LTE worker. + worker_com->semaphore.push(lte_worker); + lte_worker_pool->start_worker(lte_worker); + } } void sync::run_camping_state() { @@ -562,8 +620,18 @@ void sync::run_camping_state() } } + // Apply CFO adjustment if available + if (ref_cfo != 0.0) { + srsran_ue_sync_set_cfo_ref(&ue_sync, ref_cfo); + ref_cfo = 0.0; // reset until value changes again + } + // Primary Cell (PCell) Synchronization - switch (srsran_ue_sync_zerocopy(&ue_sync, sync_buffer.to_cf_t(), lte_worker->get_buffer_len())) { + int sync_result = srsran_ue_sync_zerocopy(&ue_sync, sync_buffer.to_cf_t(), lte_worker->get_buffer_len()); + cfo = srsran_ue_sync_get_cfo(&ue_sync); + sfo = srsran_ue_sync_get_sfo(&ue_sync); + + switch (sync_result) { case 1: run_camping_in_sync_state(lte_worker, nr_worker, sync_buffer); break; @@ -593,8 +661,8 @@ void sync::run_idle_state() { if (radio_h->is_init()) { uint32_t nsamples = 1920; - if (std::isnormal(current_srate) and current_srate > 0.0f) { - nsamples = current_srate / 1000; + if (srate.is_normal()) { + nsamples = srate.get_srate() / 1000; } Debug("Discarding %d samples", nsamples); srsran_timestamp_t rx_time = {}; @@ -615,7 +683,7 @@ void sync::run_idle_state() void sync::run_thread() { - while (running) { + while (running.load(std::memory_order_relaxed)) { phy_lib_logger.set_context(tti); Debug("SYNC: state=%s, tti=%d", phy_state.to_string(), tti); @@ -677,7 +745,7 @@ void sync::in_sync() void sync::out_of_sync() { // Send RRC out-of-sync signal after NOF_OUT_OF_SYNC_SF consecutive subframes - Info("Out-of-sync %d/%d", out_of_sync_cnt, worker_com->args->nof_out_of_sync_events); + Info("Out-of-sync %d/%d", out_of_sync_cnt.load(std::memory_order_relaxed), worker_com->args->nof_out_of_sync_events); out_of_sync_cnt++; if (out_of_sync_cnt == worker_com->args->nof_out_of_sync_events) { Info("Sending to RRC"); @@ -687,9 +755,9 @@ void sync::out_of_sync() } } -void sync::set_cfo(float cfo) +void sync::set_cfo(float cfo_) { - srsran_ue_sync_set_cfo_ref(&ue_sync, cfo); + ref_cfo = cfo_; } void sync::set_agc_enable(bool enable) @@ -718,7 +786,7 @@ void sync::set_agc_enable(bool enable) return; } - // Enable AGC + // Enable AGC (unprotected call to ue_sync must not happen outside of thread calling recv) srsran_ue_sync_start_agc( &ue_sync, callback_set_rx_gain, rf_info->min_rx_gain, rf_info->max_rx_gain, radio_h->get_rx_gain()); search_p.set_agc_enable(true); @@ -726,8 +794,7 @@ void sync::set_agc_enable(bool enable) float sync::get_tx_cfo() { - float cfo = srsran_ue_sync_get_cfo(&ue_sync); - + // Use CFO estimate from last successful sync float ret = cfo * ul_dl_factor; if (worker_com->args->cfo_is_doppler) { @@ -743,7 +810,7 @@ float sync::get_tx_cfo() return ret / 15000; } -void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo) +void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo_) { if (worker_com->args->cfo_integer_enabled) { srsran_ue_sync_set_cfo_i_enable(q, true); @@ -760,9 +827,9 @@ void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo) worker_com->args->cfo_loop_pss_conv); // Disable CP based CFO estimation during find - if (std::isnormal(cfo)) { - srsran_ue_sync_cfo_reset(q, cfo); - q->cfo_current_value = cfo / 15000; + if (std::isnormal(cfo_)) { + srsran_ue_sync_cfo_reset(q, cfo_); + q->cfo_current_value = cfo_ / 15000; q->cfo_is_copied = true; q->cfo_correct_enable_find = true; srsran_sync_set_cfo_cp_enable(&q->sfind, false, 0); @@ -786,43 +853,34 @@ void sync::set_ue_sync_opts(srsran_ue_sync_t* q, float cfo) srsran_sync_set_sss_algorithm(&q->sfind, (sss_alg_t)sss_alg); } -bool sync::set_cell(float cfo) +bool sync::set_cell(float cfo_in) { - // Wait for SYNC thread to transition to IDLE (max. 2000ms) - uint32_t cnt = 0; - while (!phy_state.is_idle() && cnt <= 4000) { - Info("SYNC: PHY state is_idle=%d, cnt=%d", phy_state.is_idle(), cnt); - usleep(500); - cnt++; - } - if (!phy_state.is_idle()) { - Error("Can not change Cell while not in IDLE"); - return false; - } - - if (!srsran_cell_isvalid(&cell)) { - Error("SYNC: Setting cell: invalid cell (nof_prb=%d, pci=%d, ports=%d)", cell.nof_prb, cell.id, cell.nof_ports); + if (!cell.is_valid()) { + Error("SYNC: Setting cell: invalid cell (nof_prb=%d, pci=%d, ports=%d)", + cell.get().nof_prb, + cell.get().id, + cell.get().nof_ports); return false; } // Set cell in all objects - if (srsran_ue_sync_set_cell(&ue_sync, cell)) { + if (srsran_ue_sync_set_cell(&ue_sync, cell.get())) { Error("SYNC: Setting cell: initiating ue_sync"); return false; } - sfn_p.set_cell(cell); - worker_com->set_cell(cell); + sfn_p.set_cell(cell.get()); + worker_com->set_cell(cell.get()); // Reset cell configuration for (uint32_t i = 0; i < worker_com->args->nof_phy_threads; i++) { - (*lte_worker_pool)[i]->reset_cell_unlocked(0); + (*lte_worker_pool)[i]->reset_cell_nolock(0); } bool success = true; for (uint32_t i = 0; i < worker_com->args->nof_phy_threads; i++) { lte::sf_worker* w = lte_worker_pool->wait_worker_id(i); if (w) { - success &= w->set_cell_unlocked(0, cell); + success &= w->set_cell_nolock(0, cell.get()); w->release(); } } @@ -832,7 +890,7 @@ bool sync::set_cell(float cfo) } // Set options defined in expert section - set_ue_sync_opts(&ue_sync, cfo); + set_ue_sync_opts(&ue_sync, cfo_in); // Reset ue_sync and set CFO/gain from search procedure srsran_ue_sync_reset(&ue_sync); @@ -883,22 +941,16 @@ bool sync::set_frequency() void sync::set_sampling_rate() { - float new_srate = (float)srsran_sampling_freq_hz(cell.nof_prb); + float new_srate = (float)srsran_sampling_freq_hz(cell.get().nof_prb); if (new_srate < 0.0) { - Error("Invalid sampling rate for %d PRBs. keeping same.", cell.nof_prb); + Error("Invalid sampling rate for %d PRBs. keeping same.", cell.get().nof_prb); return; } - if (current_srate != new_srate || srate_mode != SRATE_CAMP) { - current_srate = new_srate; - Info("SYNC: Setting sampling rate %.2f MHz", current_srate / 1000000); - - srate_mode = SRATE_CAMP; - radio_h->set_rx_srate(current_srate); - radio_h->set_tx_srate(current_srate); - } else { - Error("Error setting sampling rate for cell with %d PRBs", cell.nof_prb); - } + srate.set_camp(new_srate); + Info("SYNC: Setting sampling rate %.2f MHz", new_srate / 1000000); + radio_h->set_rx_srate(new_srate); + radio_h->set_tx_srate(new_srate); } uint32_t sync::get_current_tti() @@ -909,9 +961,10 @@ uint32_t sync::get_current_tti() void sync::get_current_cell(srsran_cell_t* cell_, uint32_t* earfcn_) { if (cell_) { - *cell_ = cell; + *cell_ = cell.get(); } if (earfcn_) { + std::unique_lock ul(rrc_mutex); *earfcn_ = current_earfcn; } } @@ -946,16 +999,17 @@ int sync::radio_recv_fnc(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_time) } // Execute channel DL emulator - if (channel_emulator and rx_time) { - channel_emulator->set_srate((uint32_t)current_srate); + if (channel_emulator) { + channel_emulator->set_srate((uint32_t)srate.get_srate()); channel_emulator->run(data.to_cf_t(), data.to_cf_t(), data.get_nof_samples(), *rx_time); } // Save signal for Intra-frequency measurement - if (srsran_cell_isvalid(&cell)) { + if (cell.is_valid()) { + std::lock_guard lock(intra_freq_cfg_mutex); for (uint32_t i = 0; (uint32_t)i < intra_freq_meas.size(); i++) { // Feed the exact number of base-band samples for avoiding an invalid buffer read - intra_freq_meas[i]->write(tti, data.get(i, 0, worker_com->args->nof_rx_ant), data.get_nof_samples()); + intra_freq_meas[i]->run_tti(tti, data.get(i, 0, worker_com->args->nof_rx_ant), data.get_nof_samples()); // Update RX gain intra_freq_meas[i]->set_rx_gain_offset(worker_com->get_rx_gain_offset()); @@ -1019,13 +1073,15 @@ void sync::set_rx_gain(float gain) void sync::set_inter_frequency_measurement(uint32_t cc_idx, uint32_t earfcn_, srsran_cell_t cell_) { + std::lock_guard lock(intra_freq_cfg_mutex); if (cc_idx < intra_freq_meas.size()) { intra_freq_meas[cc_idx]->set_primary_cell(earfcn_, cell_); } } void sync::set_cells_to_meas(uint32_t earfcn_, const std::set& pci) { - bool found = false; + std::lock_guard lock(intra_freq_cfg_mutex); + bool found = false; for (size_t i = 0; i < intra_freq_meas.size() and not found; i++) { if (earfcn_ == intra_freq_meas[i]->get_earfcn()) { intra_freq_meas[i]->set_cells_to_meas(pci); @@ -1039,6 +1095,7 @@ void sync::set_cells_to_meas(uint32_t earfcn_, const std::set& pci) void sync::meas_stop() { + std::lock_guard lock(intra_freq_cfg_mutex); for (auto& q : intra_freq_meas) { q->meas_stop(); } diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc new file mode 100644 index 000000000..2b33668a2 --- /dev/null +++ b/srsue/src/phy/sync_sa.cc @@ -0,0 +1,393 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/phy/nr/sync_sa.h" +#include "srsran/radio/rf_buffer.h" + +namespace srsue { +namespace nr { +sync_sa::sync_sa(srslog::basic_logger& logger_, worker_pool& workers_) : + logger(logger_), workers(workers_), slot_synchronizer(logger_), searcher(logger_), ta(logger_), srsran::thread("SYNC") +{} + +sync_sa::~sync_sa() +{ + if (rx_buffer != nullptr) { + free(rx_buffer); + } +} + +bool sync_sa::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) +{ + stack = stack_; + radio = radio_; + srate_hz = args.srate_hz; + slot_sz = (uint32_t)(args.srate_hz / 1000.0f); + + // Initialise cell search internal object + if (not searcher.init(args.get_cell_search())) { + logger.error("Error initialising cell searcher"); + return false; + } + + // Initialise slot synchronizer object + if (not slot_synchronizer.init(args.get_slot_sync(), stack, radio)) { + logger.error("Error initialising slot synchronizer"); + return false; + } + + // Cell bandwidth must be provided at init so set now sampling rate + radio->set_rx_srate(args.srate_hz); + radio->set_tx_srate(args.srate_hz); + + // Compute subframe size + slot_sz = (uint32_t)(args.srate_hz / 1000.0f); + + // Allocate receive buffer + rx_buffer = srsran_vec_cf_malloc(2 * slot_sz); + if (rx_buffer == nullptr) { + logger.error("Error allocating buffer"); + return false; + } + + // Thread control + running = true; + start(args.thread_priority); + + // If reached here it was successful + return true; +} + +void sync_sa::stop() +{ + running = false; + wait_thread_finish(); + radio->reset(); +} + +bool sync_sa::reset() +{ + // Wait worker pool to finish any processing + tti_semaphore.wait_all(); + ta.set_base_sec(0); + + return true; +} + +void sync_sa::add_ta_cmd_rar(uint32_t tti_, uint32_t ta_cmd) +{ + ta.add_ta_cmd_rar(tti_, ta_cmd); +} + +void sync_sa::add_ta_cmd_new(uint32_t tti_, uint32_t ta_cmd) +{ + ta.add_ta_cmd_new(tti_, ta_cmd); +} + +void sync_sa::add_ta_offset(uint32_t ta_offset) +{ + ta.add_ta_offset(ta_offset); +} + +void sync_sa::cell_go_idle() +{ + std::unique_lock ul(rrc_mutex); + phy_state.go_idle(); +} + +bool sync_sa::wait_idle() +{ + // Wait for SYNC thread to transition to IDLE (max. 2000ms) + if (!phy_state.wait_idle(100)) { + return false; + } + + // Reset UE sync. Attention: doing this reset when the FSM is NOT IDLE can cause PSS/SSS out-of-sync + //... + + // Wait for workers to finish PHY processing + tti_semaphore.wait_all(); + + // As workers have finished, make sure the Tx burst is ended + radio->tx_end(); + + return phy_state.is_idle(); +} + +cell_search::ret_t sync_sa::cell_search_run(const cell_search::cfg_t& cfg) +{ + std::unique_lock ul(rrc_mutex); + + cs_ret = {}; + cs_ret.result = cell_search::ret_t::ERROR; + + // Wait the FSM to transition to IDLE + if (!wait_idle()) { + logger.error("Cell Search: SYNC thread didn't transition to IDLE after 100 ms\n"); + return cs_ret; + } + + rrc_proc_state = PROC_SEARCH_RUNNING; + + // tune radio + logger.info("Tuning Rx channel %d to %.2f MHz", 0, cfg.center_freq_hz / 1e6); + radio->set_rx_freq(0, cfg.center_freq_hz); + + if (not searcher.start(cfg)) { + logger.error("Sync: failed to start cell search"); + return cs_ret; + } + + logger.info("Cell Search: Running Cell search state"); + cell_search_nof_trials = 0; + phy_state.run_cell_search(); + + rrc_proc_state = PROC_IDLE; + + return cs_ret; +} + +rrc_interface_phy_nr::cell_select_result_t sync_sa::cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req) +{ + std::unique_lock ul(rrc_mutex); + + // By default, the result is set to error + rrc_interface_phy_nr::cell_select_result_t result = {}; + result.status = rrc_interface_phy_nr::cell_select_result_t::ERROR; + + // Wait the FSM to transition to IDLE + if (!wait_idle()) { + logger.error("Cell Search: SYNC thread didn't transition to IDLE after 100 ms\n"); + + // Return default result with error status + return result; + } + + rrc_proc_state = PROC_SELECT_RUNNING; + + // tune radio + logger.info("Tuning Rx channel %d to %.2f MHz", 0, req.carrier.dl_center_frequency_hz / 1e6); + radio->set_rx_freq(0, req.carrier.dl_center_frequency_hz); + logger.info("Tuning Tx channel %d to %.2f MHz", 0, req.carrier.ul_center_frequency_hz / 1e6); + radio->set_tx_freq(0, req.carrier.ul_center_frequency_hz); + + // Configure cell + srsran_ue_sync_nr_cfg_t cfg = {}; + cfg.N_id = req.carrier.pci; + cfg.ssb = req.ssb_cfg; + cfg.ssb.srate_hz = srate_hz; + if (slot_synchronizer.set_sync_cfg(cfg)) { + logger.error("Cell Search: Failed setting slot synchronizer configuration"); + + // Return default result with error status + return result; + } + + // SFN synchronization + phy_state.run_sfn_sync(); + + // Determine if the procedure was successful if the current state is camping, otherwise it is unsuccessful + if (phy_state.is_camping()) { + logger.info("Cell Select: SFN synchronized. CAMPING..."); + result.status = rrc_interface_phy_nr::cell_select_result_t::SUCCESSFUL; + } else { + logger.info("Cell Select: Could not synchronize SFN"); + result.status = rrc_interface_phy_nr::cell_select_result_t::UNSUCCESSFUL; + } + + rrc_proc_state = PROC_IDLE; + return result; +} + +sync_state::state_t sync_sa::get_state() +{ + return phy_state.get_state(); +} + +void sync_sa::run_state_idle() +{ + if (not radio->is_init()) { + return; + } + + logger.debug("Discarding samples and sending tx_end"); + srsran::rf_buffer_t rf_buffer = {}; + rf_buffer.set_nof_samples(slot_sz); + rf_buffer.set(0, rx_buffer); + if (not slot_synchronizer.recv_callback(rf_buffer, last_rx_time.get_ptr(0))) { + logger.error("SYNC: receiving from radio\n"); + } + radio->tx_end(); +} + +void sync_sa::run_state_cell_search() +{ + // Initialise buffer + if (cell_search_nof_trials == 0) { + srsran_vec_cf_zero(rx_buffer, slot_sz); + } + + // Receive samples + srsran::rf_buffer_t rf_buffer = {}; + rf_buffer.set_nof_samples(slot_sz); + rf_buffer.set(0, rx_buffer + slot_sz); + if (not slot_synchronizer.recv_callback(rf_buffer, last_rx_time.get_ptr(0))) { + logger.error("SYNC: receiving from radio\n"); + } + + // Run Searcher + cs_ret = searcher.run_slot(rx_buffer, slot_sz); + if (cs_ret.result < 0) { + logger.error("Failed to run searcher. Transitioning to IDLE..."); + } + + srsran_vec_cf_copy(rx_buffer, rx_buffer + slot_sz, slot_sz); + + cell_search_nof_trials++; + + // Leave CELL_SEARCH state if error or success and transition to IDLE + if (cs_ret.result == cell_search::ret_t::CELL_FOUND || cell_search_nof_trials >= cell_search_max_trials) { + phy_state.state_exit(); + } +} + +void sync_sa::run_state_sfn_sync() +{ + // Run SFN synchronization + if (slot_synchronizer.run_sfn_sync()) { + tti = slot_synchronizer.get_slot_cfg().idx; + + logger.info("SYNC: SFN synchronised successfully (SFN=%d). Transitioning to IDLE...", + tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz)); + + phy_state.state_exit(true); + return; + } + + // If not synchonized, increment number of trials + sfn_sync_nof_trials++; + + // Abort SFN synchronization if the maximum number of trials is reached + if (sfn_sync_nof_trials >= sfn_sync_max_trials) { + logger.info("SYNC: The SFN sync reached the maximum number of trials (%d). Transitioning to IDLE...", + sfn_sync_nof_trials); + phy_state.state_exit(false); + } +} + +void sync_sa::run_state_cell_camping() +{ + nr::sf_worker* nr_worker = workers.wait_worker(tti); + if (nr_worker == nullptr) { + running = false; + return; + } + + // Receive samples + srsran::rf_buffer_t rf_buffer = {}; + rf_buffer.set_nof_samples(slot_sz); + rf_buffer.set(0, nr_worker->get_buffer(0, 0)); + if (not slot_synchronizer.run_camping(rf_buffer, last_rx_time)) { + logger.error("SYNC: detected out-of-sync... skipping slot ..."); + is_pending_tx_end = true; + nr_worker->release(); + return; + } + + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = nr_worker; + context.last = true; // Set last if standalone + last_rx_time.add(FDD_HARQ_DELAY_DL_MS * 1e-3); + context.tx_time.copy(last_rx_time); + // Apply current TA + context.tx_time.sub((double)ta.get_sec()); + + nr_worker->set_context(context); + + // NR worker needs to be launched first, phy_common::worker_end expects first the NR worker and the LTE worker. + tti_semaphore.push(nr_worker); + workers.start_worker(nr_worker); + + tti = TTI_ADD(tti, 1); +} + +void sync_sa::run_thread() +{ + while (running.load(std::memory_order_relaxed)) { + logger.set_context(tti); + + logger.debug("SYNC: state=%s, tti=%d", phy_state.to_string(), tti); + + switch (phy_state.run_state()) { + case sync_state::IDLE: + run_state_idle(); + break; + case sync_state::CELL_SEARCH: + run_state_cell_search(); + break; + case sync_state::SFN_SYNC: + run_state_sfn_sync(); + break; + case sync_state::CAMPING: + run_state_cell_camping(); + break; + } + } +} +void sync_sa::worker_end(const srsran::phy_common_interface::worker_context_t& w_ctx, + const bool& tx_enable, + srsran::rf_buffer_t& tx_buffer) +{ + // Wait for the green light to transmit in the current TTI + tti_semaphore.wait(w_ctx.worker_ptr); + + // Add current time alignment + srsran::rf_timestamp_t tx_time = w_ctx.tx_time; // get transmit time from the last worker + // todo: tx_time.sub((double)ta.get_sec()); + + // Check if any worker had a transmission + if (tx_enable) { + // Actual baseband transmission + radio->tx(tx_buffer, tx_time); + } else { + if (radio->is_continuous_tx()) { + if (is_pending_tx_end) { + radio->tx_end(); + is_pending_tx_end = false; + } else { + if (!radio->get_is_start_of_burst()) { + srsran::rf_buffer_t zeros_multi; + zeros_multi.set_nof_samples(tx_buffer.get_nof_samples()); + radio->tx(zeros_multi, tx_time); + } + } + } else { + radio->tx_end(); + } + } + + // Allow next TTI to transmit + tti_semaphore.release(); +} + +} // namespace nr +} // namespace srsue diff --git a/srsue/src/phy/test/CMakeLists.txt b/srsue/src/phy/test/CMakeLists.txt new file mode 100644 index 000000000..7762b8570 --- /dev/null +++ b/srsue/src/phy/test/CMakeLists.txt @@ -0,0 +1,96 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +set(CTEST_LABELS "srsue;phy") + +include_directories( + ${Boost_INCLUDE_DIRS} + ${SEC_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR} +) + +link_directories( + ${Boost_LIBRARY_DIRS} + ${SEC_LIBRARY_DIRS} +) + +add_executable(ue_phy_test ue_phy_test.cc) +target_link_libraries(ue_phy_test + srsue_phy + srsran_common + srsran_phy + srsran_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) +# Test disabled, it is not 100 deterministic. +#add_test(ue_phy_test ue_phy_test) + +add_executable(scell_search_test scell_search_test.cc) +target_link_libraries(scell_search_test + srsue_phy + srsran_common + srsran_phy + srsran_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) + +# Test LTE cell search with a complex environment and an odd measurement period +add_lte_test(scell_search_test scell_search_test --duration=5 --cell.nof_prb=6 --active_cell_list=2,3,4,5,6 --simulation_cell_list=1,2,3,4,5,6 --channel_period_s=30 --channel.hst.fd=750 --channel.delay_max=10000 --intra_freq_meas_period_ms=199) + +add_executable(nr_cell_search_test nr_cell_search_test.cc) +target_link_libraries(nr_cell_search_test + srsue_phy + srsran_common + srsran_phy + srsran_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) + +# Test NR cell search without delay +# This test checks the search starts in the configured TTI and the NR PSS is detected correctly inside the SF +add_nr_test(nr_cell_search_test nr_cell_search_test --duration=1 --ssb_period=20 --meas_period_ms=20 --meas_len_ms=1 --simulation_cell_list=500) + +# Test NR cell search with up 1000us delay +# This test checks the search is capable to find a cell with a broad delay +add_nr_test(nr_cell_search_test_delay nr_cell_search_test --duration=1 --ssb_period=20 --meas_period_ms=100 --meas_len_ms=30 --channel.delay_min=0 --channel.delay_max=1000 --simulation_cell_list=500) + +# File test of 10ms captured NR carrier +# Captured using: lib/examples/usrp_capture -a type=b200,master_clock_rate=61.44e6 -g 80 -r 61.44e6 -n 614400 -f 3682.5e6 -o ../srsue/test/phy/n78.fo3675360k.fs6144.data +#add_nr_test(nr_cell_search_test_file nr_cell_search_test --duration=1 --srate=61.44e6 --ssb_arfcn=645024 --carrier_arfcn=645500 --meas_period_ms=10 --meas_len_ms=10 --file.name=${CMAKE_SOURCE_DIR}/n78.fo3675360k.fs6144.data) + +add_executable(nr_cell_search_rf nr_cell_search_rf.cc) +target_link_libraries(nr_cell_search_rf + srsue_phy + srsran_common + srsran_phy + srsran_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) + +# RF based Usage example: nr_sa_cell_search_test --phy.log.level=info --stack.log.level=info --duration=10000 --freq_dl=3.67536e9 --rf.freq_offset=10e3 --rf.rx_gain=90 +add_executable(nr_sa_cell_search_test nr_sa_cell_search_test.cc) +target_link_libraries(nr_sa_cell_search_test + srsue_phy + srsran_common + srsran_phy + srsran_radio + rrc_nr_asn1 + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) \ No newline at end of file diff --git a/srsue/src/phy/test/gnb_emulator.h b/srsue/src/phy/test/gnb_emulator.h new file mode 100644 index 000000000..7cae02a81 --- /dev/null +++ b/srsue/src/phy/test/gnb_emulator.h @@ -0,0 +1,117 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_GNB_EMULATOR_H +#define SRSRAN_GNB_EMULATOR_H + +#include +#include +#include +#include + +class gnb_emulator +{ +private: + uint32_t sf_len = 0; + srsran_carrier_nr_t carrier = {}; + srsran_ssb_t ssb = {}; + srsran::channel channel; + std::vector buffer; + srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB-EMULATOR"); + +public: + struct args_t { + double srate_hz; + srsran_carrier_nr_t carrier; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_pattern_t ssb_pattern; + uint32_t ssb_periodicity_ms; + srsran_duplex_mode_t duplex_mode; + srsran::channel::args_t channel; + std::string log_level = "warning"; + }; + + gnb_emulator(const args_t& args) : channel(args.channel, 1, srslog::fetch_basic_logger("GNB-EMULATOR")) + { + logger.set_level(srslog::str_to_basic_level(args.log_level)); + + srsran_assert( + std::isnormal(args.srate_hz) and args.srate_hz > 0, "Invalid sampling rate (%.2f MHz)", args.srate_hz); + + // Initialise internals + sf_len = args.srate_hz / 1000; + carrier = args.carrier; + buffer.resize(sf_len); + + srsran_ssb_args_t ssb_args = {}; + ssb_args.enable_encode = true; + srsran_assert(srsran_ssb_init(&ssb, &ssb_args) == SRSRAN_SUCCESS, "SSB initialisation failed"); + + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = args.srate_hz; + ssb_cfg.center_freq_hz = args.carrier.dl_center_frequency_hz; + ssb_cfg.ssb_freq_hz = args.carrier.ssb_center_freq_hz; + ssb_cfg.scs = args.ssb_scs; + ssb_cfg.pattern = args.ssb_pattern; + ssb_cfg.duplex_mode = args.duplex_mode; + ssb_cfg.periodicity_ms = args.ssb_periodicity_ms; + srsran_assert(srsran_ssb_set_cfg(&ssb, &ssb_cfg) == SRSRAN_SUCCESS, "SSB set config failed"); + + // Configure channel + channel.set_srate((uint32_t)args.srate_hz); + } + + int work(uint32_t sf_idx, cf_t* baseband_buffer, const srsran::rf_timestamp_t& ts) + { + logger.set_context(sf_idx); + + // Zero buffer + srsran_vec_cf_zero(buffer.data(), sf_len); + + // Check if SSB needs to be sent + if (srsran_ssb_send(&ssb, sf_idx)) { + // Prepare PBCH message + srsran_pbch_msg_nr_t msg = {}; + + // Add SSB + if (srsran_ssb_add(&ssb, carrier.pci, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) { + logger.error("Error adding SSB"); + return SRSRAN_ERROR; + } + } + + // Run channel + cf_t* in[SRSRAN_MAX_CHANNELS] = {}; + cf_t* out[SRSRAN_MAX_CHANNELS] = {}; + in[0] = buffer.data(); + out[0] = buffer.data(); + channel.run(in, out, sf_len, ts.get(0)); + + // Add buffer to baseband buffer + srsran_vec_sum_ccc(baseband_buffer, buffer.data(), baseband_buffer, sf_len); + + return SRSRAN_SUCCESS; + } + + ~gnb_emulator() { srsran_ssb_free(&ssb); } +}; + +#endif // SRSRAN_GNB_EMULATOR_H diff --git a/srsue/src/phy/test/gnb_rf_emulator.h b/srsue/src/phy/test/gnb_rf_emulator.h new file mode 100644 index 000000000..01477da86 --- /dev/null +++ b/srsue/src/phy/test/gnb_rf_emulator.h @@ -0,0 +1,158 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_GNB_RF_EMULATOR_H +#define SRSRAN_GNB_RF_EMULATOR_H + +#include "gnb_emulator.h" +#include +#include +#include +#include +#include + +class gnb_rf_emulator final : public srsran::radio_interface_phy +{ +private: + const uint32_t BUFFER_SIZE_SF = 10; + const std::string LOGNAME = "RF"; + uint32_t sf_len = 0; + srsran_ringbuffer_t ringbuffer = {}; + uint32_t slot_idx = 0; + std::atomic running = {true}; + srsran::rf_timestamp_t ts_write = {}; + std::vector buffer; + std::vector > gnb_vector; + + void run_async_slot() + { + // Early return if not running + if (not running) { + return; + } + + // Zero slot buffer + srsran_vec_cf_zero(buffer.data(), sf_len); + + for (std::shared_ptr& gnb : gnb_vector) { + srsran_assert(gnb->work(slot_idx, buffer.data(), ts_write) == SRSRAN_SUCCESS, "Failed to run gNb emulator"); + } + + // Write slot samples in ringbuffer + srsran_assert(srsran_ringbuffer_write(&ringbuffer, buffer.data(), (int)sizeof(cf_t) * sf_len) > SRSRAN_SUCCESS, + "Error writing in ringbuffer"); + + // Increment time + ts_write.add(0.001f); + } + +public: + struct args_t { + double srate_hz; + srsran_carrier_nr_t base_carrier; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_pattern_t ssb_pattern; + uint32_t ssb_periodicity_ms; + srsran_duplex_mode_t duplex_mode; + std::set pci_list; + float channel_hst_fd_hz = 0.0f; + float channel_hst_period_s = 7.2f; + }; + + gnb_rf_emulator(const args_t& args) + { + srsran_assert( + std::isnormal(args.srate_hz) and args.srate_hz > 0, "Invalid sampling rate (%.2f MHz)", args.srate_hz); + + sf_len = args.srate_hz / 1000; + + for (uint32_t pci : args.pci_list) { + gnb_emulator::args_t gnb_args = {}; + gnb_args.srate_hz = args.srate_hz; + gnb_args.carrier = args.base_carrier; + gnb_args.carrier.pci = pci; + gnb_args.ssb_scs = args.ssb_scs; + gnb_args.ssb_pattern = args.ssb_pattern; + gnb_args.ssb_periodicity_ms = args.ssb_periodicity_ms; + gnb_args.duplex_mode = args.duplex_mode; + + gnb_args.channel.hst_enable = std::isnormal(args.channel_hst_fd_hz) and std::isnormal(args.channel_hst_period_s); + gnb_args.channel.hst_fd_hz = args.channel_hst_fd_hz; + gnb_args.channel.hst_period_s = args.channel_hst_period_s; + gnb_args.channel.enable = gnb_args.channel.hst_enable; + + gnb_vector.emplace_back(std::make_shared(gnb_args)); + } + + srsran_assert(srsran_ringbuffer_init(&ringbuffer, sizeof(cf_t) * BUFFER_SIZE_SF * sf_len) >= SRSRAN_SUCCESS, + "Ringbuffer initialisation failed"); + + buffer.resize(BUFFER_SIZE_SF * sf_len); + } + ~gnb_rf_emulator() = default; + void tx_end() override {} + bool tx(srsran::rf_buffer_interface& tx_buffer, const srsran::rf_timestamp_interface& tx_time) override + { + return false; + } + bool rx_now(srsran::rf_buffer_interface& rx_buffer, srsran::rf_timestamp_interface& rxd_time) override + { + int nbytes = (int)(sizeof(cf_t) * rx_buffer.get_nof_samples()); + cf_t* temp_buffer = rx_buffer.get(0); + + // If the buffer is invalid, use internal temporal buffer + if (temp_buffer == nullptr) { + temp_buffer = buffer.data(); + } + + // As long as there are not enough samples + while (srsran_ringbuffer_status(&ringbuffer) < nbytes and running) { + run_async_slot(); + } + + if (not running) { + return true; + } + + srsran_assert(srsran_ringbuffer_read(&ringbuffer, temp_buffer, nbytes) >= SRSRAN_SUCCESS, + "Error reading from ringbuffer"); + + return true; + } + void set_tx_freq(const uint32_t& carrier_idx, const double& freq) override {} + void set_rx_freq(const uint32_t& carrier_idx, const double& freq) override {} + void release_freq(const uint32_t& carrier_idx) override {} + void set_tx_gain(const float& gain) override {} + void set_rx_gain_th(const float& gain) override {} + void set_rx_gain(const float& gain) override {} + void set_tx_srate(const double& srate) override {} + void set_rx_srate(const double& srate) override {} + void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) override {} + double get_freq_offset() override { return 0; } + float get_rx_gain() override { return 0; } + bool is_continuous_tx() override { return false; } + bool get_is_start_of_burst() override { return false; } + bool is_init() override { return false; } + void reset() override { running = false; } + srsran_rf_info_t* get_info() override { return nullptr; } +}; + +#endif // SRSRAN_GNB_RF_EMULATOR_H diff --git a/srsue/src/phy/test/nr_cell_search_rf.cc b/srsue/src/phy/test/nr_cell_search_rf.cc new file mode 100644 index 000000000..ef57aadd0 --- /dev/null +++ b/srsue/src/phy/test/nr_cell_search_rf.cc @@ -0,0 +1,295 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/band_helper.h" +#include "srsran/common/string_helpers.h" +#include "srsran/common/test_common.h" +#include "srsran/interfaces/phy_interface_types.h" +#include "srsran/radio/radio.h" +#include "srsran/srslog/srslog.h" +#include "srsue/hdr/phy/scell/intra_measure_nr.h" +#include +#include +#include +#include +#include +#include + +struct args_t { + // General + std::string log_level = "warning"; + double srate_hz = 23.04e6; + + // Measurement parameters + uint32_t meas_len_ms = 20; + uint32_t meas_period_ms = 20; + float thr_snr_db = -5.0f; + + // Radio parameters + std::string radio_device_name = "auto"; + std::string radio_device_args = "auto"; + std::string radio_log_level = "info"; + float rx_gain = 80.0f; + double freq_offset_hz = 0; + std::string bands = "78"; +}; + +class meas_itf_listener : public srsue::scell::intra_measure_base::meas_itf +{ +public: + typedef struct { + float rsrp_avg; + float rsrp_min; + float rsrp_max; + float rsrq_avg; + float rsrq_min; + float rsrq_max; + uint32_t arfcn; + uint32_t count; + } cell_meas_t; + + std::map cells; + + void cell_meas_reset(uint32_t cc_idx) override {} + void new_cell_meas(uint32_t cc_idx, const std::vector& meas) override + { + for (const srsue::phy_meas_t& m : meas) { + uint32_t pci = m.pci; + if (!cells.count(pci)) { + cells[pci].rsrp_min = m.rsrp; + cells[pci].rsrp_max = m.rsrp; + cells[pci].rsrp_avg = m.rsrp; + cells[pci].rsrq_min = m.rsrq; + cells[pci].rsrq_max = m.rsrq; + cells[pci].rsrq_avg = m.rsrq; + cells[pci].count = 1; + } else { + cells[pci].rsrp_min = SRSRAN_MIN(cells[pci].rsrp_min, m.rsrp); + cells[pci].rsrp_max = SRSRAN_MAX(cells[pci].rsrp_max, m.rsrp); + cells[pci].rsrp_avg = (m.rsrp + cells[pci].rsrp_avg * cells[pci].count) / (cells[pci].count + 1); + + cells[pci].rsrq_min = SRSRAN_MIN(cells[pci].rsrq_min, m.rsrq); + cells[pci].rsrq_max = SRSRAN_MAX(cells[pci].rsrq_max, m.rsrq); + cells[pci].rsrq_avg = (m.rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1); + cells[pci].count++; + } + cells[pci].arfcn = m.earfcn; + } + } + + void print_stats() + { + printf("\n-- Statistics:\n"); + for (auto& e : cells) { + printf(" pci=%03d; arfcn=%d; count=%3d; rsrp=%+.1f|%+.1f|%+.1fdBfs; rsrq=%+.1f|%+.1f|%+.1fdB;\n", + e.first, + e.second.arfcn, + e.second.count, + e.second.rsrp_min, + e.second.rsrp_avg, + e.second.rsrp_max, + e.second.rsrq_min, + e.second.rsrq_avg, + e.second.rsrq_max); + } + } +}; + +// shorten boost program options namespace +namespace bpo = boost::program_options; + +int parse_args(int argc, char** argv, args_t& args) +{ + int ret = SRSRAN_SUCCESS; + + bpo::options_description options("General options"); + bpo::options_description measure("Measurement options"); + bpo::options_description over_the_air("Mode 1: Over the air options (Default)"); + + // clang-format off + measure.add_options() + ("meas_len_ms", bpo::value(&args.meas_len_ms)->default_value(args.meas_len_ms), "Measurement length") + ("meas_period_ms", bpo::value(&args.meas_period_ms)->default_value(args.meas_period_ms), "Measurement period") + ("thr_snr_db", bpo::value(&args.thr_snr_db)->default_value(args.thr_snr_db), "Detection threshold for SNR in dB") + ("bands", bpo::value(&args.bands)->default_value(args.bands), "band list to measure, comma separated") + ; + + over_the_air.add_options() + ("rf.device_name", bpo::value(&args.radio_device_name)->default_value(args.radio_device_name), "RF Device Name") + ("rf.device_args", bpo::value(&args.radio_device_args)->default_value(args.radio_device_args), "RF Device arguments") + ("rf.log_level", bpo::value(&args.radio_log_level)->default_value(args.radio_log_level), "RF Log level (none, warning, info, debug)") + ("rf.rx_gain", bpo::value(&args.rx_gain)->default_value(args.rx_gain), "RF Receiver gain in dB") + ("rf.freq_offset", bpo::value(&args.freq_offset_hz)->default_value(args.freq_offset_hz), "RF frequency offset in Hz") + ; + + options.add(measure).add(over_the_air).add_options() + ("help,h", "Show this message") + ("log_level", bpo::value(&args.log_level)->default_value(args.log_level), "Intra measurement log level (none, warning, info, debug)") + ("srate", bpo::value(&args.srate_hz)->default_value(args.srate_hz), "Sampling Rate in Hz") + ; + // clang-format on + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + ret = SRSRAN_ERROR; + } + + // help option was given or error - print usage and exit + if (vm.count("help") || ret) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << options << std::endl << std::endl; + ret = SRSRAN_ERROR; + } + + return ret; +} + +int main(int argc, char** argv) +{ + // Parse args + args_t args = {}; + if (parse_args(argc, argv, args) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Initiate logging + srslog::init(); + srslog::basic_logger& logger = srslog::fetch_basic_logger("PHY"); + logger.set_level(srslog::str_to_basic_level(args.log_level)); + + // Deduce base-band parameters + uint32_t sf_len = (uint32_t)round(args.srate_hz / 1000.0); + + // Allocate buffer + std::vector baseband_buffer(sf_len); + + // Create measurement callback + meas_itf_listener rrc; + + // Create measurement object + srsue::scell::intra_measure_nr intra_measure(logger, rrc); + + // Initialise measurement instance + srsue::scell::intra_measure_nr::args_t meas_args = {}; + meas_args.rx_gain_offset_dB = 0.0f; + meas_args.max_len_ms = args.meas_len_ms; + meas_args.max_srate_hz = args.srate_hz; + meas_args.min_scs = srsran_subcarrier_spacing_15kHz; + meas_args.thr_snr_db = args.thr_snr_db; + TESTASSERT(intra_measure.init(0, meas_args)); + + std::set scs_set = {srsran_subcarrier_spacing_15kHz, srsran_subcarrier_spacing_30kHz}; + std::set band_set = {}; + + srsran::string_parse_list(args.bands, ',', band_set); + + // Create Radio + srsran::radio radio; + + auto& radio_logger = srslog::fetch_basic_logger("RF", false); + radio_logger.set_level(srslog::str_to_basic_level(args.radio_log_level)); + + // Init radio + srsran::rf_args_t radio_args = {}; + radio_args.device_args = args.radio_device_args; + radio_args.device_name = args.radio_device_name; + radio_args.nof_carriers = 1; + radio_args.nof_antennas = 1; + radio.init(radio_args, nullptr); + + // Set sampling rate + radio.set_rx_srate(args.srate_hz); + radio.set_rx_gain(args.rx_gain); + + double center_freq_hz = 0.0; + uint32_t tti_count = 0; + + // Iterate + for (const uint16_t& band : band_set) { + for (const srsran_subcarrier_spacing_t& scs : scs_set) { + srsran::srsran_band_helper::sync_raster_t sync_raster = srsran::srsran_band_helper().get_sync_raster(band, scs); + + // Iterate over all GSCN + for (; not sync_raster.end(); sync_raster.next()) { + double ssb_freq_hz = sync_raster.get_frequency(); + + // Set frequency if the deviation from the current frequency is too high + if (std::abs(center_freq_hz - ssb_freq_hz) > (args.srate_hz / 2.0)) { + center_freq_hz = ssb_freq_hz + args.srate_hz / 2.0; + + // Update Rx frequency + radio.set_rx_freq(0, center_freq_hz + args.freq_offset_hz); + } + + logger.info("Measuring SSB frequency %.2f MHz, center %.2f MHz", ssb_freq_hz / 1e6, center_freq_hz / 1e6); + + // Setup measurement + srsue::scell::intra_measure_nr::config_t meas_cfg = {}; + meas_cfg.arfcn = (uint32_t)(ssb_freq_hz / 1e3); + meas_cfg.srate_hz = args.srate_hz; + meas_cfg.len_ms = args.meas_len_ms; + meas_cfg.period_ms = args.meas_period_ms; + meas_cfg.center_freq_hz = center_freq_hz; + meas_cfg.ssb_freq_hz = ssb_freq_hz; + meas_cfg.scs = scs; + meas_cfg.serving_cell_pci = -1; + TESTASSERT(intra_measure.set_config(meas_cfg)); + + srsran::rf_buffer_t radio_buffer(baseband_buffer.data(), sf_len); + srsran::rf_timestamp_t ts = {}; + + // Start measurements + intra_measure.set_cells_to_meas({}); + + for (uint32_t i = 0; i < args.meas_period_ms * 5; i++) { + radio.rx_now(radio_buffer, ts); + + intra_measure.run_tti(tti_count, baseband_buffer.data(), sf_len); + + tti_count = TTI_ADD(tti_count, 1); + } + + // Stop measurements + intra_measure.meas_stop(); + } + } + } + + // Stop radio before it overflows + radio.stop(); + + // make sure last measurement has been received before stopping + intra_measure.wait_meas(); + + // Stop, it will block until the asynchronous thread quits + intra_measure.stop(); + + logger.warning("NR intra frequency performance %d Msps\n", intra_measure.get_perf()); + srslog::flush(); + + rrc.print_stats(); + + return EXIT_SUCCESS; +} diff --git a/srsue/src/phy/test/nr_cell_search_test.cc b/srsue/src/phy/test/nr_cell_search_test.cc new file mode 100644 index 000000000..2f55ef2c7 --- /dev/null +++ b/srsue/src/phy/test/nr_cell_search_test.cc @@ -0,0 +1,553 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/band_helper.h" +#include "srsran/common/string_helpers.h" +#include "srsran/common/test_common.h" +#include "srsran/interfaces/phy_interface_types.h" +#include "srsran/radio/radio.h" +#include "srsran/srslog/srslog.h" +#include "srsue/hdr/phy/scell/intra_measure_nr.h" +#include +#include +#include +#include +#include +#include + +// Test gNb class +class test_gnb +{ +private: + uint32_t pci; + uint32_t sf_len = 0; + srsran_ssb_t ssb = {}; + std::vector signal_buffer = {}; + srslog::basic_logger& logger; + srsran::channel channel; + std::vector buffer; + +public: + struct args_t { + uint32_t pci = 500; + double srate_hz = 11.52e6; + double center_freq_hz = 3.5e9; + double ssb_freq_hz = 3.5e9 - 960e3; + srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; + uint32_t ssb_period_ms = 20; + uint16_t band; + srsran::channel::args_t channel; + std::string log_level = "error"; + + srsran_ssb_pattern_t get_ssb_pattern() const { return srsran::srsran_band_helper().get_ssb_pattern(band, ssb_scs); } + srsran_duplex_mode_t get_duplex_mode() const { return srsran::srsran_band_helper().get_duplex_mode(band); } + }; + + test_gnb(const args_t& args) : + logger(srslog::fetch_basic_logger("PCI=" + std::to_string(args.pci))), channel(args.channel, 1, logger) + { + logger.set_level(srslog::str_to_basic_level(args.log_level)); + + // Initialise internals + pci = args.pci; + sf_len = (uint32_t)round(args.srate_hz / 1000); + + // Allocate buffer + buffer.resize(sf_len); + + // Initialise SSB + srsran_ssb_args_t ssb_args = {}; + ssb_args.max_srate_hz = args.srate_hz; + ssb_args.min_scs = args.ssb_scs; + ssb_args.enable_encode = true; + if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { + logger.error("Error initialising SSB"); + return; + } + + // Configure SSB + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = args.srate_hz; + ssb_cfg.center_freq_hz = args.center_freq_hz; + ssb_cfg.ssb_freq_hz = args.ssb_freq_hz; + ssb_cfg.scs = args.ssb_scs; + ssb_cfg.pattern = args.get_ssb_pattern(); + ssb_cfg.duplex_mode = args.get_duplex_mode(); + ssb_cfg.periodicity_ms = args.ssb_period_ms; + if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { + logger.error("Error configuring SSB"); + return; + } + + // Configure channel + channel.set_srate((uint32_t)args.srate_hz); + } + + int work(uint32_t sf_idx, std::vector& baseband_buffer, const srsran::rf_timestamp_t& ts) + { + logger.set_context(sf_idx); + + // Zero buffer + srsran_vec_cf_zero(buffer.data(), (uint32_t)buffer.size()); + + // Check if SSB needs to be sent + if (srsran_ssb_send(&ssb, sf_idx)) { + // Prepare PBCH message + srsran_pbch_msg_nr_t msg = {}; + + // Add SSB + if (srsran_ssb_add(&ssb, pci, &msg, buffer.data(), buffer.data()) < SRSRAN_SUCCESS) { + logger.error("Error adding SSB"); + return SRSRAN_ERROR; + } + } + + // Run channel + cf_t* in[SRSRAN_MAX_CHANNELS] = {}; + cf_t* out[SRSRAN_MAX_CHANNELS] = {}; + in[0] = buffer.data(); + out[0] = buffer.data(); + channel.run(in, out, (uint32_t)buffer.size(), ts.get(0)); + + // Add buffer to baseband buffer + srsran_vec_sum_ccc(baseband_buffer.data(), buffer.data(), baseband_buffer.data(), (uint32_t)buffer.size()); + + return SRSRAN_SUCCESS; + } + + ~test_gnb() { srsran_ssb_free(&ssb); } +}; + +struct args_t { + // General + std::string log_level = "warning"; + uint32_t duration_s = 1; + double srate_hz = 11.52e6; + uint32_t carier_arfcn = 634240; + uint32_t ssb_arfcn = 634176; + std::string ssb_scs_str = "30"; + + // Measurement parameters + std::set pcis_to_meas; + uint32_t meas_len_ms = 1; + uint32_t meas_period_ms = 20; + float thr_snr_db = 5.0f; + srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; + + // Simulation parameters + std::set pcis_to_simulate; + uint32_t ssb_period_ms = 20; + float channel_delay_min = 0.0f; // Set to non-zero value to stir the delay from zero to this value in usec + float channel_delay_max = 0.0f; // Set to non-zero value to stir the delay from zero to this value in usec + + // On the Fly parameters + std::string radio_device_name = "auto"; + std::string radio_device_args = "auto"; + std::string radio_log_level = "info"; + float rx_gain = 60.0f; + + // File parameters + std::string filename = ""; + double file_freq_offset_hz = 0.0; +}; + +class meas_itf_listener : public srsue::scell::intra_measure_base::meas_itf +{ +public: + typedef struct { + float rsrp_avg; + float rsrp_min; + float rsrp_max; + float rsrq_avg; + float rsrq_min; + float rsrq_max; + uint32_t count; + } cell_meas_t; + + std::map cells; + + void cell_meas_reset(uint32_t cc_idx) override {} + void new_cell_meas(uint32_t cc_idx, const std::vector& meas) override + { + for (const srsue::phy_meas_t& m : meas) { + uint32_t pci = m.pci; + if (!cells.count(pci)) { + cells[pci].rsrp_min = m.rsrp; + cells[pci].rsrp_max = m.rsrp; + cells[pci].rsrp_avg = m.rsrp; + cells[pci].rsrq_min = m.rsrq; + cells[pci].rsrq_max = m.rsrq; + cells[pci].rsrq_avg = m.rsrq; + cells[pci].count = 1; + } else { + cells[pci].rsrp_min = SRSRAN_MIN(cells[pci].rsrp_min, m.rsrp); + cells[pci].rsrp_max = SRSRAN_MAX(cells[pci].rsrp_max, m.rsrp); + cells[pci].rsrp_avg = (m.rsrp + cells[pci].rsrp_avg * cells[pci].count) / (cells[pci].count + 1); + + cells[pci].rsrq_min = SRSRAN_MIN(cells[pci].rsrq_min, m.rsrq); + cells[pci].rsrq_max = SRSRAN_MAX(cells[pci].rsrq_max, m.rsrq); + cells[pci].rsrq_avg = (m.rsrq + cells[pci].rsrq_avg * cells[pci].count) / (cells[pci].count + 1); + cells[pci].count++; + } + } + } + + bool print_stats(args_t args) + { + printf("\n-- Statistics:\n"); + uint32_t true_counts = 0; + uint32_t false_counts = 0; + uint32_t tti_count = (1000 * args.duration_s) / args.meas_period_ms; + uint32_t ideal_true_counts = args.pcis_to_simulate.size() * tti_count; + uint32_t ideal_false_counts = tti_count * cells.size() - ideal_true_counts; + + for (auto& e : cells) { + bool false_alarm = args.pcis_to_simulate.find(e.first) == args.pcis_to_simulate.end(); + + if (args.pcis_to_simulate.empty()) { + false_alarm = (args.pcis_to_meas.count(e.first) == 0); + ideal_true_counts = args.pcis_to_meas.size() * tti_count; + } + + if (false_alarm) { + false_counts += e.second.count; + } else { + true_counts += e.second.count; + } + + printf(" pci=%03d; count=%3d; false=%s; rsrp=%+.1f|%+.1f|%+.1fdBfs; rsrq=%+.1f|%+.1f|%+.1fdB;\n", + e.first, + e.second.count, + false_alarm ? "y" : "n", + e.second.rsrp_min, + e.second.rsrp_avg, + e.second.rsrp_max, + e.second.rsrq_min, + e.second.rsrq_avg, + e.second.rsrq_max); + } + + float prob_detection = (ideal_true_counts) ? (float)true_counts / (float)ideal_true_counts : 0.0f; + float prob_false_alarm = (ideal_false_counts) ? (float)false_counts / (float)ideal_false_counts : 0.0f; + printf("\n"); + printf(" Probability of detection: %.6f\n", prob_detection); + printf(" Probability of false alarm: %.6f\n", prob_false_alarm); + + return (prob_detection >= 0.9f && prob_false_alarm <= 0.1f); + } +}; + +static void pci_list_parse_helper(std::string& list_str, std::set& list) +{ + if (list_str == "all") { + // Add all possible cells + for (int i = 0; i < SRSRAN_NOF_NID_NR; i++) { + list.insert(i); + } + } else if (list_str == "none") { + list.clear(); + } else if (not list_str.empty()) { + // Remove spaces from neightbour cell list + list_str = srsran::string_remove_char(list_str, ' '); + + // Add cell to known cells + srsran::string_parse_list(list_str, ',', list); + } +} + +// shorten boost program options namespace +namespace bpo = boost::program_options; + +int parse_args(int argc, char** argv, args_t& args) +{ + int ret = SRSRAN_SUCCESS; + + std::string active_cell_list = "500"; + std::string simulation_cell_list = ""; + std::string ssb_scs = "30"; + + bpo::options_description options("General options"); + bpo::options_description measure("Measurement options"); + bpo::options_description over_the_air("Mode 1: Over the air options (Default)"); + bpo::options_description simulation("Mode 2: Simulation options (enabled if simulation_cell_list is not empty)"); + bpo::options_description file("Mode 3: File (enabled if filename is provided)"); + + // clang-format off + measure.add_options() + ("meas_len_ms", bpo::value(&args.meas_len_ms)->default_value(args.meas_len_ms), "Measurement length") + ("meas_period_ms", bpo::value(&args.meas_period_ms)->default_value(args.meas_period_ms), "Measurement period") + ("active_cell_list", bpo::value(&active_cell_list)->default_value(active_cell_list), "Comma separated PCI cell list to measure") + ("thr_snr_db", bpo::value(&args.thr_snr_db)->default_value(args.thr_snr_db), "Detection threshold for SNR in dB") + ; + + over_the_air.add_options() + ("rf.device_name", bpo::value(&args.radio_device_name)->default_value(args.radio_device_name), "RF Device Name") + ("rf.device_args", bpo::value(&args.radio_device_args)->default_value(args.radio_device_args), "RF Device arguments") + ("rf.log_level", bpo::value(&args.radio_log_level)->default_value(args.radio_log_level), "RF Log level (none, warning, info, debug)") + ("rf.rx_gain", bpo::value(&args.rx_gain)->default_value(args.rx_gain), "RF Receiver gain in dB") + ; + + simulation.add_options() + ("simulation_cell_list", bpo::value(&simulation_cell_list)->default_value(simulation_cell_list), "Comma separated PCI cell list to simulate") + ("ssb_period", bpo::value(&args.ssb_period_ms)->default_value(args.ssb_period_ms), "SSB period in ms") + ("channel.delay_min", bpo::value(&args.channel_delay_min)->default_value(args.channel_delay_min), "Channel delay minimum in usec.") + ("channel.delay_max", bpo::value(&args.channel_delay_max)->default_value(args.channel_delay_max), "Channel delay maximum in usec. Set to 0 to disable, otherwise it will steer the delay for the duration of the simulation") + ; + + file.add_options() + ("file.name", bpo::value(&args.filename)->default_value(args.filename), "File name providing baseband") + ("file.freq_offset", bpo::value(&args.file_freq_offset_hz)->default_value(args.file_freq_offset_hz), "File name providing baseband") + ; + + options.add(measure).add(over_the_air).add(simulation).add(file).add_options() + ("help,h", "Show this message") + ("log_level", bpo::value(&args.log_level)->default_value(args.log_level), "Intra measurement log level (none, warning, info, debug)") + ("duration", bpo::value(&args.duration_s)->default_value(args.duration_s), "Duration of the test in seconds") + ("srate", bpo::value(&args.srate_hz)->default_value(args.srate_hz), "Sampling Rate in Hz") + ("carrier_arfcn", bpo::value(&args.carier_arfcn)->default_value(args.carier_arfcn), "Carrier center frequency ARFCN") + ("ssb_arfcn", bpo::value(&args.ssb_arfcn)->default_value(args.ssb_arfcn), "SSB center frequency in ARFCN") + ("ssb_scs", bpo::value(&ssb_scs)->default_value(ssb_scs), "SSB subcarrier spacing in kHz") + ; + // clang-format on + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + ret = SRSRAN_ERROR; + } + + // help option was given or error - print usage and exit + if (vm.count("help") || ret) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << options << std::endl << std::endl; + ret = SRSRAN_ERROR; + } + + // Parse PCI lists + pci_list_parse_helper(active_cell_list, args.pcis_to_meas); + pci_list_parse_helper(simulation_cell_list, args.pcis_to_simulate); + + // Parse SSB SCS + args.ssb_scs = srsran_subcarrier_spacing_from_str(ssb_scs.c_str()); + if (args.ssb_scs == srsran_subcarrier_spacing_invalid) { + ret = SRSRAN_ERROR; + } + + return ret; +} + +int main(int argc, char** argv) +{ + int ret; + + // Parse args + args_t args = {}; + if (parse_args(argc, argv, args) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Initiate logging + srslog::init(); + srslog::basic_logger& logger = srslog::fetch_basic_logger("PHY"); + logger.set_level(srslog::str_to_basic_level(args.log_level)); + + // Deduce base-band parameters + double srate_hz = args.srate_hz; + uint32_t sf_len = (uint32_t)round(srate_hz / 1000.0); + double center_freq_hz = srsran::srsran_band_helper().nr_arfcn_to_freq(args.carier_arfcn); + double ssb_freq_hz = srsran::srsran_band_helper().nr_arfcn_to_freq(args.ssb_arfcn); + uint16_t band = srsran::srsran_band_helper().get_band_from_dl_freq_Hz(center_freq_hz); + logger.debug("Band: %d; srate: %.2f MHz; center_freq: %.1f MHz; ssb_freq: %.1f MHz;", + band, + srate_hz / 1e6, + center_freq_hz / 1e6, + ssb_freq_hz / 1e6); + + // Allocate buffer + std::vector baseband_buffer(sf_len); + + // Create measurement callback + meas_itf_listener rrc; + + // Create measurement instance + srsue::scell::intra_measure_nr intra_measure(logger, rrc); + + // Initialise measurement instance + srsue::scell::intra_measure_nr::args_t meas_args = {}; + meas_args.rx_gain_offset_dB = 0.0f; + meas_args.max_len_ms = args.meas_len_ms; + meas_args.max_srate_hz = srate_hz; + meas_args.min_scs = args.ssb_scs; + meas_args.thr_snr_db = args.thr_snr_db; + TESTASSERT(intra_measure.init(0, meas_args)); + + // Setup measurement + srsue::scell::intra_measure_nr::config_t meas_cfg = {}; + meas_cfg.arfcn = args.carier_arfcn; + meas_cfg.srate_hz = srate_hz; + meas_cfg.len_ms = args.meas_len_ms; + meas_cfg.period_ms = args.meas_period_ms; + meas_cfg.tti_period = args.meas_period_ms; + meas_cfg.tti_offset = 0; + meas_cfg.rx_gain_offset_db = 0; + meas_cfg.center_freq_hz = center_freq_hz; + meas_cfg.ssb_freq_hz = ssb_freq_hz; + meas_cfg.scs = srsran_subcarrier_spacing_30kHz; + meas_cfg.serving_cell_pci = -1; + TESTASSERT(intra_measure.set_config(meas_cfg)); + + // Simulation only + std::vector > test_gnb_v; + + // Over-the-air only + std::unique_ptr radio = nullptr; + + // File read only + srsran_filesource_t filesource = {}; + + // Setup raio if the list of PCIs to simulate is empty + if (not args.filename.empty()) { + if (srsran_filesource_init(&filesource, args.filename.c_str(), SRSRAN_COMPLEX_FLOAT) < SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + } else if (args.pcis_to_simulate.empty()) { + // Create radio log + auto& radio_logger = srslog::fetch_basic_logger("RF", false); + radio_logger.set_level(srslog::str_to_basic_level(args.radio_log_level)); + + // Create radio + radio = std::unique_ptr(new srsran::radio); + + // Init radio + srsran::rf_args_t radio_args = {}; + radio_args.device_args = args.radio_device_args; + radio_args.device_name = args.radio_device_name; + radio_args.nof_carriers = 1; + radio_args.nof_antennas = 1; + radio->init(radio_args, nullptr); + + // Set sampling rate + radio->set_rx_srate(srate_hz); + + // Set frequency + radio->set_rx_freq(0, center_freq_hz); + + } else { + // Create test eNb's if radio is not available + for (const uint32_t& pci : args.pcis_to_simulate) { + // Initialise channel and push back + test_gnb::args_t gnb_args = {}; + gnb_args.pci = pci; + gnb_args.srate_hz = srate_hz; + gnb_args.center_freq_hz = center_freq_hz; + gnb_args.ssb_freq_hz = ssb_freq_hz; + gnb_args.ssb_scs = args.ssb_scs; + gnb_args.ssb_period_ms = args.ssb_period_ms; + gnb_args.band = band; + gnb_args.log_level = args.log_level; + gnb_args.channel.delay_enable = std::isnormal(args.channel_delay_max); + gnb_args.channel.delay_min_us = args.channel_delay_min; + gnb_args.channel.delay_max_us = args.channel_delay_max; + gnb_args.channel.delay_period_s = args.duration_s; + gnb_args.channel.delay_init_time_s = 0.0f; + gnb_args.channel.enable = (gnb_args.channel.delay_enable || gnb_args.channel.awgn_enable || + gnb_args.channel.fading_enable || gnb_args.channel.hst_enable); + test_gnb_v.push_back(std::unique_ptr(new test_gnb(gnb_args))); + } + } + + // pass cells to measure to intra_measure object + intra_measure.set_cells_to_meas(args.pcis_to_meas); + + // Run loop + srsran::rf_timestamp_t ts = {}; + for (uint32_t sf_idx = 0; sf_idx < args.duration_s * 1000; sf_idx++) { + logger.set_context(sf_idx); + + // Clean buffer + srsran_vec_cf_zero(baseband_buffer.data(), sf_len); + + if (not args.filename.empty()) { + if (srsran_filesource_read(&filesource, baseband_buffer.data(), (int)sf_len) < SRSRAN_SUCCESS) { + ERROR("Error reading from file"); + srsran_filesource_free(&filesource); + return SRSRAN_ERROR; + } + + srsran_vec_apply_cfo( + baseband_buffer.data(), args.file_freq_offset_hz / args.srate_hz, baseband_buffer.data(), (int)sf_len); + } else if (radio) { + // Receive radio + srsran::rf_buffer_t radio_buffer(baseband_buffer.data(), sf_len); + radio->rx_now(radio_buffer, ts); + } else { + // Run gNb simulator + for (auto& gnb : test_gnb_v) { + gnb->work(sf_idx, baseband_buffer, ts); + } + + // if it measuring, wait for avoiding overflowing + intra_measure.wait_meas(); + + // Increase Time counter + ts.add(0.001); + } + + // Give data to intra measure component + intra_measure.run_tti(sf_idx % 10240, baseband_buffer.data(), sf_len); + if (sf_idx % 1000 == 0) { + logger.info("Done %.1f%%", (double)sf_idx * 100.0 / ((double)args.duration_s * 1000.0)); + } + } + + // make sure last measurement has been received before stopping + if (not radio) { + intra_measure.wait_meas(); + } + + // Stop, it will block until the asynchronous thread quits + intra_measure.stop(); + + logger.warning("NR intra frequency performance %d Msps\n", intra_measure.get_perf()); + + ret = rrc.print_stats(args) ? SRSRAN_SUCCESS : SRSRAN_ERROR; + + if (radio) { + radio->stop(); + } + + if (not args.filename.empty()) { + srsran_filesource_free(&filesource); + } + + srslog::flush(); + + if (ret == SRSRAN_SUCCESS) { + printf("Ok\n"); + } else { + printf("Error\n"); + } + + return ret; +} diff --git a/srsue/src/phy/test/nr_sa_cell_search_test.cc b/srsue/src/phy/test/nr_sa_cell_search_test.cc new file mode 100644 index 000000000..14aa3d9ce --- /dev/null +++ b/srsue/src/phy/test/nr_sa_cell_search_test.cc @@ -0,0 +1,455 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "gnb_rf_emulator.h" +#include "srsran/common/band_helper.h" +#include "srsran/common/crash_handler.h" +#include "srsran/common/string_helpers.h" +#include "srsue/hdr/phy/phy_nr_sa.h" +#include "test/phy/dummy_ue_stack.h" +#include +#include +#include + +struct args_t { + // Generic parameters + double srate_hz = 11.52e6; + srsran_carrier_nr_t base_carrier = SRSRAN_DEFAULT_CARRIER_NR; + srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; + srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_15kHz; + srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + uint32_t duration_ms = 1000; + std::string phy_log_level = "warning"; + std::string stack_log_level = "warning"; + + // Simulation parameters + uint32_t sim_ssb_periodicity_ms = 10; + float sim_channel_hst_fd_hz = 0.0f; + float sim_channel_hst_period_s = 7.2f; + std::set sim_cell_pci; + + // RF parameters + std::string rf_device_name = "auto"; + std::string rf_device_args = "auto"; + std::string rf_log_level = "info"; + float rf_rx_gain_dB = 20.0f; + float rf_freq_offset_Hz = 0.0f; + + void set_ssb_from_band() + { + srsran::srsran_band_helper bands; + + // Deduce band number + uint16_t band = bands.get_band_from_dl_freq_Hz(base_carrier.dl_center_frequency_hz); + srsran_assert(band != UINT16_MAX, "Invalid band"); + + // Deduce point A in Hz + double pointA_Hz = + bands.get_abs_freq_point_a_from_center_freq(base_carrier.nof_prb, base_carrier.dl_center_frequency_hz); + + // Deduce DL center frequency ARFCN + uint32_t pointA_arfcn = bands.freq_to_nr_arfcn(pointA_Hz); + srsran_assert(pointA_arfcn != 0, "Invalid frequency"); + + // Select a valid SSB subcarrier spacing + ssb_scs = bands.get_ssb_scs(band); + + // Deduce SSB center frequency ARFCN + uint32_t ssb_arfcn = bands.get_abs_freq_ssb_arfcn(band, ssb_scs, pointA_arfcn); + srsran_assert(ssb_arfcn, "Invalid SSB center frequency"); + + duplex_mode = bands.get_duplex_mode(band); + ssb_pattern = bands.get_ssb_pattern(band, ssb_scs); + base_carrier.ssb_center_freq_hz = bands.nr_arfcn_to_freq(ssb_arfcn); + } +}; + +// shorten boost program options namespace +namespace bpo = boost::program_options; + +static void pci_list_parse_helper(std::string& list_str, std::set& list) +{ + if (list_str == "all") { + // Add all possible cells + for (int i = 0; i < 504; i++) { + list.insert(i); + } + } else if (list_str == "none") { + // Do nothing + } else if (not list_str.empty()) { + // Remove spaces from neightbour cell list + list_str = srsran::string_remove_char(list_str, ' '); + + // Add cell to known cells + srsran::string_parse_list(list_str, ',', list); + } +} + +int parse_args(int argc, char** argv, args_t& args) +{ + int ret = SRSRAN_SUCCESS; + + std::string simulation_cell_list = ""; + + bpo::options_description options("General options"); + bpo::options_description phy("Physical layer options"); + bpo::options_description stack("Stack options"); + bpo::options_description over_the_air("Mode 1: Over the air options (Default)"); + bpo::options_description simulation("Mode 2: Simulation options (enabled if simulation_cell_list is not empty)"); + + // clang-format off + over_the_air.add_options() + ("rf.device_name", bpo::value(&args.rf_device_name)->default_value(args.rf_device_name), "RF Device Name") + ("rf.device_args", bpo::value(&args.rf_device_args)->default_value(args.rf_device_args), "RF Device arguments") + ("rf.log_level", bpo::value(&args.rf_log_level)->default_value(args.rf_log_level), "RF Log level (none, warning, info, debug)") + ("rf.rx_gain", bpo::value(&args.rf_rx_gain_dB)->default_value(args.rf_rx_gain_dB), "RF Receiver gain in dB") + ("rf.freq_offset", bpo::value(&args.rf_freq_offset_Hz)->default_value(args.rf_freq_offset_Hz), "RF Frequency offset") + ; + + simulation.add_options() + ("sim.pci_list", bpo::value(&simulation_cell_list)->default_value(simulation_cell_list), "Comma separated PCI cell list to simulate") + ("sim.bw", bpo::value(&args.base_carrier.nof_prb)->default_value(args.base_carrier.nof_prb), "Carrier bandwidth in RB") + ("sim.ssb_period", bpo::value(&args.sim_ssb_periodicity_ms)->default_value(args.sim_ssb_periodicity_ms), "SSB period in ms") + ("sim.channel.hst.fd", bpo::value(&args.sim_channel_hst_fd_hz)->default_value(args.sim_channel_hst_fd_hz), "Channel emulator HST maximum frequency") + ("sim.channel.hst.period", bpo::value(&args.sim_channel_hst_period_s)->default_value(args.sim_channel_hst_period_s), "Channel emulator HST period") + ; + + phy.add_options() + ("phy.srate", bpo::value(&args.srate_hz)->default_value(args.srate_hz), "Sampling Rate in Hz") + ("phy.log.level", bpo::value(&args.phy_log_level)->default_value(args.phy_log_level), "Physical layer logging level") + ; + + stack.add_options() + ("stack.log.level", bpo::value(&args.stack_log_level)->default_value(args.stack_log_level), "Stack logging level") + ; + + options.add(over_the_air).add(simulation).add(phy).add(stack).add_options() + ("help,h", "Show this message") + ("duration", bpo::value(&args.duration_ms)->default_value(args.duration_ms), "Duration of the test in milli-seconds") + ("freq_dl", bpo::value(&args.base_carrier.dl_center_frequency_hz)->default_value(args.base_carrier.dl_center_frequency_hz), "Carrier center frequency in Hz") + ; + // clang-format on + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); + bpo::notify(vm); + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + ret = SRSRAN_ERROR; + } + + // help option was given or error - print usage and exit + if (vm.count("help") || ret) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << options << std::endl << std::endl; + ret = SRSRAN_ERROR; + } + + // Parse PCI lists + pci_list_parse_helper(simulation_cell_list, args.sim_cell_pci); + + args.set_ssb_from_band(); + + return ret; +} + +class dummy_ue +{ +private: + ue_dummy_stack stack; + srsue::phy_nr_sa phy; + +public: + struct args_t { + srsue::phy_args_nr_t phy; + ue_dummy_stack::args_t stack; + }; + dummy_ue(const args_t& args, srsran::radio_interface_phy* radio) : stack(args.stack, phy), phy("PHY") + { + srsran_assert(phy.init(args.phy, &stack, radio) == SRSRAN_SUCCESS, "Failed to initialise PHY"); + phy.wait_initialize(); + } + + bool cell_search_read_and_clear() { return stack.get_cell_search_finished(); } + + bool start_cell_search(const srsue::phy_interface_stack_nr::cell_search_args_t& args) + { + return phy.start_cell_search(args); + } + + bool start_cell_select(const srsue::phy_interface_stack_nr::cell_select_args_t& args) + { + return phy.start_cell_select(args); + } + + void run_tti() { stack.tick(); } + void stop() + { + // First transition PHY to IDLE + phy.reset_nr(); + + // Make sure PHY transitioned to IDLE + // ... + + // Stop stack, it will let PHY free run + stack.stop(); + + // Stop PHY + phy.stop(); + } + + const ue_dummy_stack::metrics_t& get_metrics() const { return stack.get_metrics(); } + void reset_metrics() { stack.reset_metrics(); } +}; + +struct cell_search_result_t { + bool found = false; + double ssb_abs_freq_hz = 0.0f; + srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_15kHz; + srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; + srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + srsran_mib_nr_t mib = {}; + uint32_t pci = 0; +}; + +/* + * The following function searches for cells in all possible SSB absolute frequencies within the baseband range. It + * returns the first found cell. + */ +static cell_search_result_t cell_search(const args_t& args, dummy_ue& ue) +{ + cell_search_result_t ret = {}; + + // Base cell search arguments + srsue::phy_nr_sa::cell_search_args_t cs_args = {}; + cs_args.center_freq_hz = args.base_carrier.dl_center_frequency_hz; + cs_args.ssb_scs = args.ssb_scs; + cs_args.ssb_pattern = args.ssb_pattern; + cs_args.duplex_mode = args.duplex_mode; + + // Deduce band number + srsran::srsran_band_helper bands; + uint16_t band = bands.get_band_from_dl_freq_Hz(args.base_carrier.dl_center_frequency_hz); + srsran_assert(band != UINT16_MAX, "Invalid band"); + + // Calculate SSB center frequency boundaries + double ssb_bw_hz = SRSRAN_SSB_BW_SUBC * bands.get_ssb_scs(band); + double ssb_center_freq_min_hz = args.base_carrier.dl_center_frequency_hz - (args.srate_hz * 0.7 - ssb_bw_hz) / 2.0; + double ssb_center_freq_max_hz = args.base_carrier.dl_center_frequency_hz + (args.srate_hz * 0.7 - ssb_bw_hz) / 2.0; + uint32_t ssb_scs_hz = SRSRAN_SUBC_SPACING_NR(args.ssb_scs); + + // Get sync raster + srsran::srsran_band_helper::sync_raster_t ss = bands.get_sync_raster(band, args.ssb_scs); + srsran_assert(ss.valid(), "Invalid synchronization raster"); + + // Iterate every possible frequency in the synchronization raster + while (not ss.end()) { + // Get SSB center frequency + cs_args.ssb_freq_hz = ss.get_frequency(); + + // Advance SSB frequency raster + ss.next(); + + // Calculate frequency offset between the base-band center frequency and the SSB absolute frequency + uint32_t offset_hz = (uint32_t)std::abs(std::round(cs_args.ssb_freq_hz - args.base_carrier.dl_center_frequency_hz)); + + // The SSB absolute frequency is invalid if it is outside the range and the offset is NOT multiple of the subcarrier + // spacing + if ((cs_args.ssb_freq_hz < ssb_center_freq_min_hz) or (cs_args.ssb_freq_hz > ssb_center_freq_max_hz) or + (offset_hz % ssb_scs_hz != 0)) { + // Skip this frequency + continue; + } + + // Transition PHY to cell search + srsran_assert(ue.start_cell_search(cs_args), "Failed cell search start"); + + // Run slot until the PHY reported to the stack + while (not ue.cell_search_read_and_clear()) { + ue.run_tti(); + } + + const ue_dummy_stack::metrics_t& metrics = ue.get_metrics(); + + // Skip printing cell search findings if no SSB is found + if (metrics.cell_search.empty()) { + continue; + } + + // Print found cells + printf("Cells found at SSB center frequency %.2f MHz:\n", cs_args.ssb_freq_hz / 1e6); + printf("| %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s |\n", + "PCI", + "SSB", + "Count", + "RSRP min", + "RSRP avg", + "RSRP max", + "SNR min", + "SNR avg", + "SNR max", + "CFO min", + "CFO avg", + "CFO max"); + + // For each found PCI... + for (auto& pci : metrics.cell_search) { + // For each found beam... + for (auto& ssb : pci.second) { + // Print stats + printf("| %10d | %10d | %10d | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | " + "%+10.1f |\n", + pci.first, + ssb.first, + (uint32_t)ssb.second.count, + ssb.second.rsrp_db_min, + ssb.second.rsrp_db_avg, + ssb.second.rsrp_db_max, + ssb.second.snr_db_min, + ssb.second.snr_db_avg, + ssb.second.snr_db_max, + ssb.second.cfo_hz_min, + ssb.second.cfo_hz_avg, + ssb.second.cfo_hz_max); + + // If this is the first found cell, then set return value + if (not ret.found) { + ret.found = true; + ret.ssb_abs_freq_hz = cs_args.ssb_freq_hz; + ret.ssb_scs = cs_args.ssb_scs; + ret.ssb_pattern = cs_args.ssb_pattern; + ret.duplex_mode = cs_args.duplex_mode; + ret.pci = pci.first; + srsran_assert(srsran_pbch_msg_nr_mib_unpack(&ssb.second.last_result.pbch_msg, &ret.mib) == SRSRAN_SUCCESS, + "Error unpacking MIB"); + } + } + } + + // Reset stack metrics + ue.reset_metrics(); + } + + return ret; +} + +int main(int argc, char** argv) +{ + srsran_debug_handle_crash(argc, argv); + + // Parse Test arguments + args_t args; + srsran_assert(parse_args(argc, argv, args) == SRSRAN_SUCCESS, "Failed to parse arguments"); + + // Initialise logging infrastructure + srslog::init(); + + // Radio can be constructed from different options + std::shared_ptr radio = nullptr; + + // Build radio + if (not args.sim_cell_pci.empty()) { + // Create Radio as gNb emulator if the device RF name is not defined + gnb_rf_emulator::args_t gnb_args = {}; + gnb_args.srate_hz = args.srate_hz; + gnb_args.base_carrier = args.base_carrier; + gnb_args.ssb_pattern = args.ssb_pattern; + gnb_args.ssb_periodicity_ms = args.sim_ssb_periodicity_ms; + gnb_args.ssb_scs = args.ssb_scs; + gnb_args.duplex_mode = args.duplex_mode; + gnb_args.pci_list = args.sim_cell_pci; + gnb_args.channel_hst_fd_hz = args.sim_channel_hst_fd_hz; + gnb_args.channel_hst_period_s = args.sim_channel_hst_period_s; + + radio = std::make_shared(gnb_args); + } else { + // Create an actual radio based on RF + srsran::rf_args_t rf_args = {}; + rf_args.type = "multi"; + rf_args.log_level = args.rf_log_level; + rf_args.srate_hz = args.srate_hz; + rf_args.rx_gain = args.rf_rx_gain_dB; + rf_args.nof_carriers = 1; + rf_args.nof_antennas = 1; + rf_args.device_args = args.rf_device_args; + rf_args.device_name = args.rf_device_name; + rf_args.freq_offset = args.rf_freq_offset_Hz; + + // Instantiate + std::shared_ptr r = std::make_shared(); + srsran_assert(r->init(rf_args, nullptr) == SRSRAN_SUCCESS, "Failed Radio initialisation"); + + // Move to base pointer + radio = std::move(r); + + // Set sampling rate + radio->set_rx_srate(args.srate_hz); + + // Set DL center frequency + radio->set_rx_freq(0, args.base_carrier.dl_center_frequency_hz); + + // Set Rx gain + radio->set_rx_gain(args.rf_rx_gain_dB); + } + + // Create dummy UE + dummy_ue::args_t ue_args = {}; + ue_args.phy.srate_hz = args.srate_hz; + ue_args.phy.log.phy_level = args.phy_log_level; + ue_args.stack.log_level = args.stack_log_level; + dummy_ue ue(ue_args, radio.get()); + + // Perform cell search + cell_search_result_t found_cell = cell_search(args, ue); + + // Perform cell select + if (found_cell.found) { + srsue::phy_interface_stack_nr::cell_select_args_t cs_args = {}; + cs_args.ssb_cfg.srate_hz = args.srate_hz; + cs_args.ssb_cfg.center_freq_hz = args.base_carrier.dl_center_frequency_hz; + cs_args.ssb_cfg.ssb_freq_hz = found_cell.ssb_abs_freq_hz; + cs_args.ssb_cfg.scs = found_cell.ssb_scs; + cs_args.ssb_cfg.pattern = found_cell.ssb_pattern; + cs_args.ssb_cfg.duplex_mode = found_cell.duplex_mode; + cs_args.ssb_cfg.periodicity_ms = 10; + cs_args.carrier = args.base_carrier; + cs_args.carrier.pci = found_cell.pci; + + srsran_assert(ue.start_cell_select(cs_args), "Failed to start cell selection\n"); + + for (uint32_t i = 0; i < 1000; i++) { + ue.run_tti(); + } + } + + // Tear down UE + ue.stop(); + + for (uint32_t i = 0; i < 1000; i++) { + ue.run_tti(); + } + + // Stop Radio + radio->reset(); + + return 0; +} \ No newline at end of file diff --git a/srsue/test/phy/scell_search_test.cc b/srsue/src/phy/test/scell_search_test.cc similarity index 88% rename from srsue/test/phy/scell_search_test.cc rename to srsue/src/phy/test/scell_search_test.cc index a37bba344..f34dfe0ab 100644 --- a/srsue/test/phy/scell_search_test.cc +++ b/srsue/src/phy/test/scell_search_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,8 +20,9 @@ */ #include "srsran/interfaces/phy_interface_types.h" +#include "srsran/srslog/bundled/fmt/chrono.h" #include "srsran/srslog/srslog.h" -#include "srsue/hdr/phy/scell/intra_measure.h" +#include "srsue/hdr/phy/scell/intra_measure_lte.h" #include #include #include @@ -45,6 +46,8 @@ static std::string active_cell_list; static std::string simulation_cell_list; static int phy_lib_log_level; static srsue::phy_args_t phy_args; +static bool enable_json_report; +static std::string json_report_filename; // On the Fly parameters static int earfcn_dl; @@ -221,8 +224,31 @@ public: } }; -class meas_itf_listener : public srsue::scell::intra_measure::meas_itf +/// Cell container metrics. +DECLARE_METRIC("earfcn_dl", metric_earfcn, int, ""); +DECLARE_METRIC("PCI", metric_pci, uint32_t, ""); +DECLARE_METRIC("RSRP", metric_rsrp, float, ""); +DECLARE_METRIC("RSRQ", metric_rsrq, float, ""); +DECLARE_METRIC("false_alarm", metric_false_alarm, std::string, ""); +DECLARE_METRIC_SET("cell_container", + mset_cell_container, + metric_earfcn, + metric_pci, + metric_rsrp, + metric_rsrq, + metric_false_alarm); + +/// Report root object. +DECLARE_METRIC("timestamp", metric_timestamp_tag, std::string, ""); +DECLARE_METRIC_LIST("cell_list", mlist_cell, std::vector); + +/// Report context. +using report_context_t = srslog::build_context_type; + +class meas_itf_listener : public srsue::scell::intra_measure_base::meas_itf { + srslog::log_channel& json_channel; + public: typedef struct { float rsrp_avg; @@ -234,6 +260,8 @@ public: uint32_t count; } cell_meas_t; + explicit meas_itf_listener(srslog::log_channel& json_channel) : json_channel(json_channel) {} + std::map cells; void cell_meas_reset(uint32_t cc_idx) override {} @@ -271,6 +299,14 @@ public: uint32_t ideal_true_counts = (pcis_to_simulate.size() - 1) * tti_count; uint32_t ideal_false_counts = tti_count * cells.size() - ideal_true_counts; + report_context_t ctx("JSON Report"); + auto current_time = fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())); + fmt::memory_buffer ts_buffer; + fmt::format_to(ts_buffer, "{:%F}T{:%T}", current_time, current_time); + ctx.write(srsran::to_c_str(ts_buffer)); + + auto& cell_list = ctx.get(); + for (auto& e : cells) { bool false_alarm = pcis_to_simulate.find(e.first) == pcis_to_simulate.end(); @@ -290,8 +326,19 @@ public: e.second.rsrq_min, e.second.rsrq_avg, e.second.rsrq_max); + + cell_list.emplace_back(); + auto& cell_container = cell_list.back(); + + cell_container.write(e.first); + cell_container.write(earfcn_dl); + cell_container.write(e.second.rsrp_avg); + cell_container.write(e.second.rsrq_avg); + cell_container.write(false_alarm ? "yes" : "no"); } + json_channel(ctx); + float prob_detection = (ideal_true_counts) ? (float)true_counts / (float)ideal_true_counts : 0.0f; float prob_false_alarm = (ideal_false_counts) ? (float)false_counts / (float)ideal_false_counts : 0.0f; printf("\n"); @@ -324,6 +371,8 @@ int parse_args(int argc, char** argv) ("intra_freq_meas_period_ms", bpo::value(&phy_args.intra_freq_meas_period_ms)->default_value(200), "Intra measurement measurement period") ("phy_lib_log_level", bpo::value(&phy_lib_log_level)->default_value(SRSRAN_VERBOSE_NONE), "Phy lib log level (0: none, 1: info, 2: debug)") ("active_cell_list", bpo::value(&active_cell_list)->default_value("10,17,24,31,38,45,52"), "Comma separated neighbour PCI cell list") + ("enable_json_report", bpo::value(&enable_json_report)->default_value(false), "Enable JSON file reporting") + ("json_report_filename", bpo::value(&json_report_filename)->default_value("/tmp/scell_search.json"), "JSON report filename") ; over_the_air.add_options() @@ -403,13 +452,15 @@ int main(int argc, char** argv) // Common for simulation and over-the-air srslog::basic_logger& logger = srslog::fetch_basic_logger("intra_measure"); + srslog::sink& json_sink = srslog::fetch_file_sink(json_report_filename, 0, false, srslog::create_json_formatter()); + srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {}); + json_channel.set_enabled(enable_json_report); srslog::init(); - cf_t* baseband_buffer = srsran_vec_cf_malloc(SRSRAN_SF_LEN_MAX); - srsran::rf_timestamp_t ts = {}; - srsue::scell::intra_measure intra_measure(logger); - meas_itf_listener rrc; - srsue::phy_common common(logger); + cf_t* baseband_buffer = srsran_vec_cf_malloc(SRSRAN_SF_LEN_MAX); + srsran::rf_timestamp_t ts = {}; + meas_itf_listener rrc(json_channel); + srsue::scell::intra_measure_lte intra_measure(logger, rrc); // Simulation only std::vector > test_enb_v; @@ -420,7 +471,6 @@ int main(int argc, char** argv) std::unique_ptr radio = nullptr; // Set Receiver args - common.args = &phy_args; phy_args.estimator_fil_auto = false; phy_args.estimator_fil_order = 4; phy_args.estimator_fil_stddev = 1.0f; @@ -441,7 +491,7 @@ int main(int argc, char** argv) phy_args.rx_gain_offset = rx_gain + 62.0f; // Set phy-lib logging level - srsran_verbose = phy_lib_log_level; + set_srsran_verbose_level(phy_lib_log_level); // Allocate PDSCH data and tx-soft-buffers only if pdsch is enabled and radio is not available for (int i = 0; i < SRSRAN_MAX_TB && serving_cell_pdsch_enable && radio == nullptr; i++) { @@ -478,7 +528,12 @@ int main(int argc, char** argv) logger.set_level(srslog::str_to_basic_level(intra_meas_log_level)); - intra_measure.init(0, &common, &rrc); + srsue::scell::intra_measure_base::args_t args = {}; + args.len_ms = phy_args.intra_freq_meas_len_ms; + args.period_ms = phy_args.intra_freq_meas_period_ms; + args.rx_gain_offset_db = phy_args.rx_gain_offset; + + intra_measure.init(0, args); intra_measure.set_primary_cell(SRSRAN_MAX(earfcn_dl, 0), cell_base); if (earfcn_dl >= 0) { @@ -581,13 +636,13 @@ int main(int argc, char** argv) dci.rnti = serving_cell_pdsch_rnti; dci.is_tdd = false; dci.is_dwpts = false; - dci.is_ra_order = false; + dci.is_pdcch_order = false; dci.tb_cw_swap = false; dci.pconf = false; dci.power_offset = false; dci.tpc_pucch = 0; - dci.ra_preamble = 0; - dci.ra_mask_idx = 0; + dci.preamble_idx = 0; + dci.prach_mask_idx = 0; dci.srs_request = false; dci.srs_request_present = false; dci.cif_present = false; @@ -643,7 +698,7 @@ int main(int argc, char** argv) ts.add(0.001); // Give data to intra measure component - intra_measure.write(sf_idx % 10240, baseband_buffer, SRSRAN_SF_LEN_PRB(cell_base.nof_prb)); + intra_measure.run_tti(sf_idx % 10240, baseband_buffer, SRSRAN_SF_LEN_PRB(cell_base.nof_prb)); if (sf_idx % 1000 == 0) { printf("Done %.1f%%\n", (double)sf_idx * 100.0 / ((double)duration_execution_s * 1000.0)); } diff --git a/srsue/test/phy/ue_phy_test.cc b/srsue/src/phy/test/ue_phy_test.cc similarity index 98% rename from srsue/test/phy/ue_phy_test.cc rename to srsue/src/phy/test/ue_phy_test.cc index 18c2b86b4..c94aff79f 100644 --- a/srsue/test/phy/ue_phy_test.cc +++ b/srsue/src/phy/test/ue_phy_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,10 +21,10 @@ #include #include +#include #include #include #include -#include #include #define CALLBACK(NAME, ...) \ @@ -111,8 +111,7 @@ private: } void tb_decoded(uint32_t cc_idx, mac_grant_dl_t grant, bool* ack) override {} void bch_decoded_ok(uint32_t cc_idx, uint8_t* payload, uint32_t len) override {} - void mch_decoded(uint32_t len, bool crc) override {} - void new_mch_dl(const srsran_pdsch_grant_t& phy_grant, tb_action_dl_t* action) override {} + void mch_decoded(uint32_t len, bool crc, uint8_t* payload) override {} void set_mbsfn_config(uint32_t nof_mbsfn_services) override {} void run_tti(const uint32_t tti, const uint32_t tti_jump) override { @@ -384,6 +383,9 @@ public: phy = std::unique_ptr(new srsue::phy); phy->init(phy_args, &stack, &radio); + // Wait PHY init to end + phy->wait_initialize(); + // Initialise DL baseband buffers for (uint32_t i = 0; i < cell.nof_ports; i++) { enb_dl_buffer[i] = srsran_vec_cf_malloc(sf_len); @@ -395,9 +397,6 @@ public: // Initialise eNb DL srsran_enb_dl_init(&enb_dl, enb_dl_buffer, SRSRAN_MAX_PRB); srsran_enb_dl_set_cell(&enb_dl, cell); - - // Wait PHY init to end - phy->wait_initialize(); } ~phy_test_bench() @@ -517,7 +516,7 @@ int main(int argc, char** argv) phy_test->start(); // 1. Cell search - TESTASSERT(phy_test->get_phy_interface_rrc()->cell_search()); + TESTASSERT(phy_test->get_phy_interface_rrc()->cell_search(-1)); TESTASSERT(phy_test->get_stack()->wait_cell_search(default_timeout)); TESTASSERT(phy_test->get_stack()->cell_search_ret.found == srsue::rrc_interface_phy_lte::cell_search_ret_t::CELL_FOUND); diff --git a/srsue/src/phy/vnf_phy_nr.cc b/srsue/src/phy/vnf_phy_nr.cc deleted file mode 100644 index 591e70ad3..000000000 --- a/srsue/src/phy/vnf_phy_nr.cc +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "srsran/common/basic_vnf_api.h" -#include "srsran/common/test_common.h" -#include "srsran/common/threads.h" -#include "srsue/hdr/phy/vnf_phy_nr.h" - -using namespace std; - -namespace srsue { - -vnf_phy_nr::~vnf_phy_nr() -{ - stop(); -} - -int vnf_phy_nr::init(const srsue::phy_args_t& args_, srsue::stack_interface_phy_nr* stack_) -{ - stack = stack_; - return init(args_); -} - -int vnf_phy_nr::init(const srsue::phy_args_t& args_) -{ - // create VNF - vnf = std::unique_ptr(new srsran::srsran_basic_vnf(args_.vnf_args, stack)); - initialized = true; - return SRSRAN_SUCCESS; -} - -void vnf_phy_nr::set_earfcn(std::vector earfcns) {} - -void vnf_phy_nr::stop() -{ - if (initialized) { - vnf->stop(); - initialized = false; - } -} - -// Start GUI -void vnf_phy_nr::start_plot() {} - -void vnf_phy_nr::wait_initialize() {} - -void vnf_phy_nr::get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) {} - -int vnf_phy_nr::tx_request(const tx_request_t& request) -{ - // send Tx request over basic API - return vnf->tx_request(request); -} -bool vnf_phy_nr::set_config(const srsran::phy_cfg_nr_t& cfg) -{ - return false; -} -bool vnf_phy_nr::has_valid_sr_resource(uint32_t sr_id) -{ - return false; -} - -void vnf_phy_nr::clear_pending_grants() {} - -} // namespace srsue \ No newline at end of file diff --git a/srsue/src/set_net_admin_caps.cc b/srsue/src/set_net_admin_caps.cc index c204011bb..42be32d58 100644 --- a/srsue/src/set_net_admin_caps.cc +++ b/srsue/src/set_net_admin_caps.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/src/stack/CMakeLists.txt b/srsue/src/stack/CMakeLists.txt index 935530716..9b54f7490 100644 --- a/srsue/src/stack/CMakeLists.txt +++ b/srsue/src/stack/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -20,12 +20,13 @@ add_subdirectory(mac_common) add_subdirectory(mac) +add_subdirectory(mac_nr) add_subdirectory(rrc) +add_subdirectory(rrc_nr) add_subdirectory(upper) set(SOURCES ue_stack_lte.cc) add_library(srsue_stack STATIC ${SOURCES}) -add_subdirectory(mac_nr) set(SOURCES ue_stack_nr.cc) add_library(srsue_nr_stack STATIC ${SOURCES}) diff --git a/srsue/src/stack/mac/CMakeLists.txt b/srsue/src/stack/mac/CMakeLists.txt index 731f77cbf..d4eae11c8 100644 --- a/srsue/src/stack/mac/CMakeLists.txt +++ b/srsue/src/stack/mac/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,6 +18,8 @@ # and at http://www.gnu.org/licenses/. # +add_subdirectory(test) + set(SOURCES demux.cc dl_harq.cc mac.cc mux.cc proc_bsr.cc proc_phr.cc proc_ra.cc proc_sr.cc ul_harq.cc) add_library(srsue_mac STATIC ${SOURCES}) target_link_libraries(srsue_mac srsue_mac_common ${ATOMIC_LIBS}) \ No newline at end of file diff --git a/srsue/src/stack/mac/demux.cc b/srsue/src/stack/mac/demux.cc index 27d163395..c8479578d 100644 --- a/srsue/src/stack/mac/demux.cc +++ b/srsue/src/stack/mac/demux.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -156,7 +156,7 @@ bool demux::process_pdus() return pdus.process_pdus(); } -void demux::process_pdu(uint8_t* mac_pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel) +void demux::process_pdu(uint8_t* mac_pdu, uint32_t nof_bytes, srsran::pdu_queue::channel_t channel, int ul_nof_prbs) { Debug("Processing MAC PDU channel %d", channel); switch (channel) { diff --git a/srsue/src/stack/mac/dl_harq.cc b/srsue/src/stack/mac/dl_harq.cc index ba8765ce3..6eb1913af 100644 --- a/srsue/src/stack/mac/dl_harq.cc +++ b/srsue/src/stack/mac/dl_harq.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,7 +34,7 @@ dl_harq_entity::dl_harq_entity(uint8_t cc_idx_) : proc(SRSRAN_MAX_HARQ_PROC), logger(srslog::fetch_basic_logger("MAC")), cc_idx(cc_idx_) {} -bool dl_harq_entity::init(mac_interface_rrc::ue_rnti_t* rntis_, demux* demux_unit_) +bool dl_harq_entity::init(ue_rnti* rntis_, demux* demux_unit_) { demux_unit = demux_unit_; rntis = rntis_; @@ -54,7 +54,7 @@ void dl_harq_entity::new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, { bzero(action, sizeof(mac_interface_phy_lte::tb_action_dl_t)); - if (grant.rnti != rntis->sps_rnti) { + if (grant.rnti != rntis->get_sps_rnti()) { // Set BCCH PID for SI RNTI dl_harq_process* proc_ptr = NULL; if (grant.rnti == SRSRAN_SIRNTI) { @@ -67,8 +67,8 @@ void dl_harq_entity::new_grant_dl(mac_interface_phy_lte::mac_grant_dl_t grant, proc_ptr = &proc[grant.pid]; } // Consider the NDI to have been toggled - if (grant.rnti == rntis->temp_rnti && last_temporal_crnti != rntis->temp_rnti) { - last_temporal_crnti = rntis->temp_rnti; + if (grant.rnti == rntis->get_temp_rnti() && last_temporal_crnti != rntis->get_temp_rnti()) { + last_temporal_crnti = rntis->get_temp_rnti(); proc_ptr->reset_ndi(); Info("Considering NDI in pid=%d to be toggled for first Temporal C-RNTI", grant.pid); } @@ -112,8 +112,14 @@ void dl_harq_entity::set_si_window_start(int si_window_start_) float dl_harq_entity::get_average_retx() { + std::unique_lock lock(retx_cnt_mutex); return average_retx; } +void dl_harq_entity::set_average_retx(uint32_t n_retx) +{ + std::unique_lock lock(retx_cnt_mutex); + average_retx = SRSRAN_VEC_CMA((float)n_retx, average_retx, nof_pkts++); +} dl_harq_entity::dl_harq_process::dl_harq_process() : subproc(SRSRAN_MAX_TB) {} @@ -158,7 +164,9 @@ void dl_harq_entity::dl_harq_process::tb_decoded(mac_interface_phy_lte::mac_gran { /* For each subprocess... */ for (uint32_t i = 0; i < SRSRAN_MAX_TB; i++) { - subproc[i].tb_decoded(grant, &ack[i]); + if (grant.tb[i].tbs) { + subproc[i].tb_decoded(grant, &ack[i]); + } } } @@ -205,12 +213,14 @@ bool dl_harq_entity::dl_harq_process::dl_tb_process::init(int pid, dl_harq_entit return true; } -void dl_harq_entity::dl_harq_process::dl_tb_process::reset(bool lock) +void dl_harq_entity::dl_harq_process::dl_tb_process::reset() { - if (lock) { - mutex.lock(); - } + std::lock_guard lock(mutex); + reset_nolock(); +} +void dl_harq_entity::dl_harq_process::dl_tb_process::reset_nolock() +{ bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_dl_t)); is_first_tb = true; ack = false; @@ -222,10 +232,6 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::reset(bool lock) } payload_buffer_ptr = NULL; } - - if (lock) { - mutex.unlock(); - } } void dl_harq_entity::dl_harq_process::dl_tb_process::reset_ndi() @@ -302,7 +308,8 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::new_grant_dl(mac_interface_ n_retx, n_retx > RESET_DUPLICATE_TIMEOUT ? "yes" : "no"); if (n_retx > RESET_DUPLICATE_TIMEOUT) { - reset(false); + // reset without trying to acquire the mutex again + reset_nolock(); } } @@ -328,7 +335,7 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::tb_decoded(mac_interface_ph harq_entity->pcap->write_dl_crnti( payload_buffer_ptr, cur_grant.tb[tid].tbs, cur_grant.rnti, ack, cur_grant.tti, harq_entity->cc_idx); } - if (cur_grant.rnti == harq_entity->rntis->temp_rnti) { + if (cur_grant.rnti == harq_entity->rntis->get_temp_rnti()) { Debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI)", cur_grant.tb[tid].tbs); harq_entity->demux_unit->push_pdu_temp_crnti(payload_buffer_ptr, cur_grant.tb[tid].tbs); @@ -340,7 +347,7 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::tb_decoded(mac_interface_ph harq_entity->demux_unit->push_pdu(payload_buffer_ptr, cur_grant.tb[tid].tbs, grant.tti); // Compute average number of retransmissions per packet - harq_entity->average_retx = SRSRAN_VEC_CMA((float)n_retx, harq_entity->average_retx, harq_entity->nof_pkts++); + harq_entity->set_average_retx(n_retx); } } @@ -360,11 +367,12 @@ void dl_harq_entity::dl_harq_process::dl_tb_process::tb_decoded(mac_interface_ph cur_grant.tb[tid].ndi); } - mutex.unlock(); - if (ack && is_bcch) { - reset(); + // reset without trying to acquire the mutex again + reset_nolock(); } + + mutex.unlock(); } // Determine if it's a new transmission 5.3.2.2 diff --git a/srsue/src/stack/mac/mac.cc b/srsue/src/stack/mac/mac.cc index f18c61c0e..5b44fe3e9 100644 --- a/srsue/src/stack/mac/mac.cc +++ b/srsue/src/stack/mac/mac.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -50,7 +50,6 @@ mac::mac(const char* logname, ext_task_sched_handle task_sched_) : dl_harq.at(PCELL_CC_IDX) = dl_harq_entity_ptr(new dl_harq_entity(PCELL_CC_IDX)); srsran_softbuffer_rx_init(&pch_softbuffer, 100); - srsran_softbuffer_rx_init(&mch_softbuffer, 100); // Keep initialising members bzero(&metrics, sizeof(mac_metrics_t)); @@ -62,7 +61,6 @@ mac::~mac() stop(); srsran_softbuffer_rx_free(&pch_softbuffer); - srsran_softbuffer_rx_free(&mch_softbuffer); } bool mac::init(phy_interface_mac_lte* phy, rlc_interface_mac* rlc, rrc_interface_mac* rrc) @@ -155,7 +153,10 @@ void mac::reconfiguration(const uint32_t& cc_idx, const bool& enable) // Implement Section 5.9 void mac::reset() { - bzero(&metrics, sizeof(mac_metrics_t)); + { + std::lock_guard lock(metrics_mutex); + bzero(&metrics, sizeof(mac_metrics_t)); + } Info("Resetting MAC"); @@ -207,9 +208,10 @@ void mac::run_tti(const uint32_t tti) sr_procedure.step(tti); phr_procedure.step(); ra_procedure.step(tti); - ra_procedure.update_rar_window(ra_window_start, ra_window_length); + ra_procedure.update_rar_window(ra_window); // Count TTI for metrics + std::lock_guard lock(metrics_mutex); for (uint32_t i = 0; i < SRSRAN_MAX_CARRIERS; i++) { metrics[i].nof_tti++; } @@ -219,13 +221,13 @@ void mac::bcch_start_rx(int si_window_start_, int si_window_length_) { if (si_window_length_ >= 0 && si_window_start_ >= 0) { dl_harq.at(0)->set_si_window_start(si_window_start_); - si_window_length = si_window_length_; - si_window_start = si_window_start_; + si_window.set(si_window_length_, si_window_start_); } else { - si_window_length = 0; - si_window_start = 0; + si_window.reset(); } - Info("SCHED: Searching for DL dci for SI-RNTI window_st=%d, window_len=%d", si_window_start, si_window_length); + Info("SCHED: Searching for DL dci for SI-RNTI window_st=%d, window_len=%d", + si_window.get_start(), + si_window.get_length()); } void mac::bcch_stop_rx() @@ -235,28 +237,25 @@ void mac::bcch_stop_rx() void mac::pcch_start_rx() { - this->p_window_start = 1; + this->p_window.set(0, 1); } void mac::clear_rntis() { - p_window_start = 0; - si_window_start = 0; - ra_window_start = -1; - ra_window_length = -1; - bzero(&uernti, sizeof(ue_rnti_t)); + p_window.reset(); + si_window.reset(); + ra_window.reset(); + uernti.reset(); } -void mac::get_rntis(ue_rnti_t* rntis) +uint16_t mac::get_crnti() { - if (rntis) { - *rntis = uernti; - } + return uernti.get_crnti(); } void mac::set_ho_rnti(uint16_t crnti, uint16_t target_pci) { - uernti.crnti = crnti; + uernti.set_crnti(crnti); if (pcap) { pcap->set_ue_id(target_pci); } @@ -264,71 +263,44 @@ void mac::set_ho_rnti(uint16_t crnti, uint16_t target_pci) uint16_t mac::get_ul_sched_rnti(uint32_t tti) { - if (uernti.temp_rnti && !uernti.crnti) { - return uernti.temp_rnti; + if (uernti.get_temp_rnti() && !uernti.get_crnti()) { + return uernti.get_temp_rnti(); } - if (uernti.crnti) { - return uernti.crnti; + if (uernti.get_crnti()) { + return uernti.get_crnti(); } return SRSRAN_INVALID_RNTI; } -bool mac::is_in_window(uint32_t tti, int* start, int* len) -{ - uint32_t st = (uint32_t)*start; - uint32_t l = (uint32_t)*len; - - if (srsran_tti_interval(tti, st) < l + 5) { - if (tti > st) { - if (tti <= st + l) { - return true; - } else { - *start = 0; - *len = 0; - return false; - } - } else { - if (tti <= (st + l) % 10240) { - return true; - } else { - *start = 0; - *len = 0; - return false; - } - } - } - return false; -} - uint16_t mac::get_dl_sched_rnti(uint32_t tti) { // Priority: SI-RNTI, P-RNTI, RA-RNTI, Temp-RNTI, CRNTI - if (si_window_start > 0) { - if (is_in_window(tti, &si_window_start, &si_window_length)) { - // TODO: This scheduling decision belongs to RRC - if (si_window_length > 1) { // This is not a SIB1 - if ((tti / 10) % 2 == 0 && (tti % 10) == 5) { // Skip subframe #5 for which SFN mod 2 = 0 - return SRSRAN_INVALID_RNTI; - } + if (si_window.is_in_window(tti)) { + // TODO: This scheduling decision belongs to RRC + if (si_window.get_length() > 1) { // This is not a SIB1 + if ((tti / 10) % 2 == 0 && (tti % 10) == 5) { // Skip subframe #5 for which SFN mod 2 = 0 + return SRSRAN_INVALID_RNTI; } - Debug("SCHED: Searching SI-RNTI, tti=%d, window start=%d, length=%d", tti, si_window_start, si_window_length); - return SRSRAN_SIRNTI; } + Debug("SCHED: Searching SI-RNTI, tti=%d, window start=%d, length=%d", + tti, + si_window.get_start(), + si_window.get_length()); + return SRSRAN_SIRNTI; } - if (uernti.rar_rnti && ra_window_start > 0 && ra_window_length > 0 && - is_in_window(tti, &ra_window_start, &ra_window_length)) { - Debug("SCHED: Searching RAR-RNTI=0x%x, tti=%d", uernti.rar_rnti, tti); - return uernti.rar_rnti; + if (uernti.get_rar_rnti() && ra_window.is_in_window(tti)) { + Debug("SCHED: Searching RAR-RNTI=0x%x, tti=%d", uernti.get_rar_rnti(), tti); + return uernti.get_rar_rnti(); } - if (uernti.temp_rnti && !uernti.crnti) { - Debug("SCHED: Searching Temp-RNTI=0x%x", uernti.temp_rnti); - return uernti.temp_rnti; + if (uernti.get_temp_rnti() && !uernti.get_crnti()) { + Debug("SCHED: Searching Temp-RNTI=0x%x", uernti.get_temp_rnti()); + return uernti.get_temp_rnti(); } - if (uernti.crnti) { - Debug("SCHED: Searching C-RNTI=0x%x", uernti.crnti); - return uernti.crnti; + if (uernti.get_crnti()) { + Debug("SCHED: Searching C-RNTI=0x%x", uernti.get_crnti()); + return uernti.get_crnti(); } - if (p_window_start > 0) { + if (p_window.is_set()) { Debug("SCHED: Searching P-RNTI"); return SRSRAN_PRNTI; } @@ -347,7 +319,7 @@ void mac::bch_decoded_ok(uint32_t cc_idx, uint8_t* payload, uint32_t len) buf->set_timestamp(); auto p = stack_task_dispatch_queue.try_push(std::bind( [this](srsran::unique_byte_buffer_t& buf) { rlc_h->write_pdu_bcch_bch(std::move(buf)); }, std::move(buf))); - if (not p.first) { + if (p.is_error()) { Warning("Failed to dispatch rlc::write_pdu_bcch_bch task to stack"); } } else { @@ -359,12 +331,12 @@ void mac::bch_decoded_ok(uint32_t cc_idx, uint8_t* payload, uint32_t len) } } -void mac::mch_decoded(uint32_t len, bool crc) +void mac::mch_decoded(uint32_t len, bool crc, uint8_t* payload) { // Parse MAC header if (crc) { mch_msg.init_rx(len); - mch_msg.parse_packet(mch_payload_buffer); + mch_msg.parse_packet(payload); while (mch_msg.next()) { for (uint32_t i = 0; i < phy_mbsfn_cfg.nof_mbsfn_services; i++) { if (srsran::mch_lcid::MCH_SCHED_INFO == mch_msg.get()->mch_ce_type()) { @@ -378,23 +350,29 @@ void mac::mch_decoded(uint32_t len, bool crc) } } - demux_unit.push_pdu_mch(mch_payload_buffer, len); + demux_unit.push_pdu_mch(payload, len); process_pdus(); if (pcap) { - pcap->write_dl_mch(mch_payload_buffer, len, true, phy_h->get_current_tti(), 0); + pcap->write_dl_mch(payload, len, true, phy_h->get_current_tti(), 0); } + std::lock_guard lock(metrics_mutex); metrics[0].rx_brate += len * 8; } else { + std::lock_guard lock(metrics_mutex); metrics[0].rx_errors++; } + std::lock_guard lock(metrics_mutex); metrics[0].rx_pkts++; } void mac::tb_decoded(uint32_t cc_idx, mac_grant_dl_t grant, bool ack[SRSRAN_MAX_CODEWORDS]) { - if (SRSRAN_RNTI_ISRAR(grant.rnti)) { + if (grant.is_pdcch_order) { + ra_procedure.set_config_ded(grant.preamble_idx, grant.prach_mask_idx); + ra_procedure.start_pdcch_order(); + } else if (SRSRAN_RNTI_ISRAR(grant.rnti)) { if (ack[0]) { ra_procedure.tb_decoded_ok(cc_idx, grant.tti); } @@ -408,7 +386,7 @@ void mac::tb_decoded(uint32_t cc_idx, mac_grant_dl_t grant, bool ack[SRSRAN_MAX_ auto ret = stack_task_dispatch_queue.try_push(std::bind( [this](srsran::unique_byte_buffer_t& pdu) { rlc_h->write_pdu_pcch(std::move(pdu)); }, std::move(pdu))); - if (not ret.first) { + if (ret.is_error()) { Warning("Failed to dispatch rlc::write_pdu_pcch task to stack"); } } else { @@ -428,14 +406,17 @@ void mac::tb_decoded(uint32_t cc_idx, mac_grant_dl_t grant, bool ack[SRSRAN_MAX_ dl_harq.at(cc_idx)->tb_decoded(grant, ack); process_pdus(); - for (uint32_t tb = 0; tb < SRSRAN_MAX_CODEWORDS; tb++) { - if (grant.tb[tb].tbs) { - if (ack[tb]) { - metrics[cc_idx].rx_brate += grant.tb[tb].tbs * 8; - } else { - metrics[cc_idx].rx_errors++; + { + std::lock_guard lock(metrics_mutex); + for (uint32_t tb = 0; tb < SRSRAN_MAX_CODEWORDS; tb++) { + if (grant.tb[tb].tbs) { + if (ack[tb]) { + metrics[cc_idx].rx_brate += grant.tb[tb].tbs * 8; + } else { + metrics[cc_idx].rx_errors++; + } + metrics[cc_idx].rx_pkts++; } - metrics[cc_idx].rx_pkts++; } } } @@ -459,9 +440,12 @@ void mac::new_grant_dl(uint32_t cc_idx, action->tb[0].rv = grant.tb[0].rv; srsran_softbuffer_rx_reset_cb(&pch_softbuffer, 1); } + } else if (grant.is_pdcch_order) { + // if the grant is a PDCCH order then there is no associated PDSCH + action->tb[0].enabled = false; } else if (!(grant.rnti == SRSRAN_SIRNTI && cc_idx != 0)) { // If PDCCH for C-RNTI and RA procedure in Contention Resolution, notify it - if (grant.rnti == uernti.crnti && ra_procedure.is_contention_resolution()) { + if (grant.rnti == uernti.get_crnti() && ra_procedure.is_contention_resolution()) { ra_procedure.pdcch_to_crnti(false); } // Assert DL HARQ entity @@ -482,11 +466,11 @@ void mac::process_pdus() // dispatch work to stack thread auto ret = stack_task_dispatch_queue.try_push([this]() { bool have_data = true; - while (initialized and have_data) { + while (initialized.load(std::memory_order_relaxed) and have_data) { have_data = demux_unit.process_pdus(); } }); - if (not ret.first) { + if (ret.is_error() && initialized.load(std::memory_order_relaxed)) { Warning("Failed to dispatch mac::%s task to stack thread", __func__); } } @@ -529,25 +513,21 @@ void mac::new_grant_ul(uint32_t cc_idx, } ul_harq.at(cc_idx)->new_grant_ul(grant, action); - metrics[cc_idx].tx_pkts++; - if (grant.phich_available) { - if (!grant.hi_value) { - metrics[cc_idx].tx_errors++; - } else { - metrics[cc_idx].tx_brate += ul_harq.at(cc_idx)->get_current_tbs(grant.pid) * 8; + { + std::lock_guard lock(metrics_mutex); + metrics[cc_idx].tx_pkts++; + + if (grant.phich_available) { + if (!grant.hi_value) { + metrics[cc_idx].tx_errors++; + } else { + metrics[cc_idx].tx_brate += ul_harq.at(cc_idx)->get_current_tbs(grant.pid) * 8; + } } - } + } // end of holding metrics mutex } -void mac::new_mch_dl(const srsran_pdsch_grant_t& phy_grant, tb_action_dl_t* action) -{ - action->generate_ack = false; - action->tb[0].enabled = true; - action->tb[0].payload = mch_payload_buffer; - action->tb[0].softbuffer.rx = &mch_softbuffer; - srsran_softbuffer_rx_reset_cb(&mch_softbuffer, 1); -} void mac::setup_timers(int time_alignment_timer) { @@ -589,7 +569,7 @@ void mac::timer_alignment_expire() void mac::set_contention_id(uint64_t uecri) { - uernti.contention_id = uecri; + uernti.set_contention_id(uecri); } void mac::set_rach_ded_cfg(uint32_t preamble_index, uint32_t prach_mask) @@ -659,6 +639,8 @@ void mac::mch_start_rx(uint32_t lcid) void mac::get_metrics(mac_metrics_t m[SRSRAN_MAX_CARRIERS]) { + std::lock_guard lock(metrics_mutex); + int tx_pkts = 0; int tx_errors = 0; int tx_brate = 0; @@ -688,11 +670,11 @@ void mac::get_metrics(mac_metrics_t m[SRSRAN_MAX_CARRIERS]) dl_avg_ret /= dl_avg_ret_count; } - Info("DL retx: %.2f \%%, perpkt: %.2f, UL retx: %.2f \%% perpkt: %.2f", - rx_pkts ? ((float)100 * rx_errors / rx_pkts) : 0.0f, - dl_avg_ret, - tx_pkts ? ((float)100 * tx_errors / tx_pkts) : 0.0f, - ul_harq.at(PCELL_CC_IDX)->get_average_retx()); + Debug("DL retx: %.2f \%%, perpkt: %.2f, UL retx: %.2f \%% perpkt: %.2f", + rx_pkts ? ((float)100 * rx_errors / rx_pkts) : 0.0f, + dl_avg_ret, + tx_pkts ? ((float)100 * tx_errors / tx_pkts) : 0.0f, + ul_harq.at(PCELL_CC_IDX)->get_average_retx()); metrics[PCELL_CC_IDX].ul_buffer = (int)bsr_procedure.get_buffer_state(); memcpy(m, metrics, sizeof(mac_metrics_t) * SRSRAN_MAX_CARRIERS); diff --git a/srsue/src/stack/mac/mux.cc b/srsue/src/stack/mac/mux.cc index 3f0640c54..b60e565f4 100644 --- a/srsue/src/stack/mac/mux.cc +++ b/srsue/src/stack/mac/mux.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -105,11 +105,15 @@ srsran::ul_sch_lcid bsr_format_convert(bsr_proc::bsr_format_t format) } } -// Multiplexing and logical channel priorization as defined in Section 5.4.3 uint8_t* mux::pdu_get(srsran::byte_buffer_t* payload, uint32_t pdu_sz) { std::lock_guard lock(mutex); + return pdu_get_nolock(payload, pdu_sz); +} +// Multiplexing and logical channel priorization as defined in Section 5.4.3 +uint8_t* mux::pdu_get_nolock(srsran::byte_buffer_t* payload, uint32_t pdu_sz) +{ // Logical Channel Procedure payload->clear(); pdu_msg.init_tx(payload, pdu_sz, true); @@ -249,6 +253,7 @@ uint8_t* mux::pdu_get(srsran::byte_buffer_t* payload, uint32_t pdu_sz) void mux::append_crnti_ce_next_tx(uint16_t crnti) { + std::lock_guard lock(mutex); pending_crnti_ce = crnti; } @@ -330,6 +335,7 @@ uint32_t mux::allocate_sdu(uint32_t lcid, srsran::sch_pdu* pdu_msg, int max_sdu_ void mux::msg3_flush() { + std::lock_guard lock(mutex); Debug("Msg3 buffer flushed"); msg3_buff.clear(); msg3_has_been_transmitted = false; @@ -338,17 +344,20 @@ void mux::msg3_flush() bool mux::msg3_is_transmitted() { + std::lock_guard lock(mutex); return msg3_has_been_transmitted; } void mux::msg3_prepare() { + std::lock_guard lock(mutex); msg3_has_been_transmitted = false; msg3_pending = true; } bool mux::msg3_is_pending() { + std::lock_guard lock(mutex); return msg3_pending; } @@ -360,9 +369,10 @@ bool mux::msg3_is_empty() /* Returns a pointer to the Msg3 buffer */ uint8_t* mux::msg3_get(srsran::byte_buffer_t* payload, uint32_t pdu_sz) { + std::lock_guard lock(mutex); if (pdu_sz < msg3_buff.get_tailroom()) { if (msg3_is_empty()) { - if (!pdu_get(&msg3_buff, pdu_sz)) { + if (!pdu_get_nolock(&msg3_buff, pdu_sz)) { Error("Moving PDU from Mux unit to Msg3 buffer"); return NULL; } diff --git a/srsue/src/stack/mac/proc_bsr.cc b/srsue/src/stack/mac/proc_bsr.cc index 8e99c2812..c38146ddc 100644 --- a/srsue/src/stack/mac/proc_bsr.cc +++ b/srsue/src/stack/mac/proc_bsr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -50,6 +50,12 @@ void bsr_proc::init(sr_proc* sr_, rlc_interface_mac* rlc_, srsran::ext_task_sche void bsr_proc::print_state() { + if (!logger.info.enabled()) { + return; + } + + std::lock_guard lock(mutex); + char str[128]; str[0] = '\0'; int n = 0; @@ -75,6 +81,8 @@ void bsr_proc::set_trigger(bsr_trigger_type_t new_trigger) void bsr_proc::reset() { + std::lock_guard lock(mutex); + timer_periodic.stop(); timer_retx.stop(); @@ -122,6 +130,8 @@ void bsr_proc::timer_expired(uint32_t timer_id) uint32_t bsr_proc::get_buffer_state() { + std::lock_guard lock(mutex); + uint32_t buffer = 0; for (int i = 0; i < NOF_LCG; i++) { buffer += get_buffer_state_lcg(i); diff --git a/srsue/src/stack/mac/proc_phr.cc b/srsue/src/stack/mac/proc_phr.cc index fa5dd840a..8eb127640 100644 --- a/srsue/src/stack/mac/proc_phr.cc +++ b/srsue/src/stack/mac/proc_phr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -50,6 +50,7 @@ void phr_proc::init(phy_interface_mac_lte* phy_h_, srsran::ext_task_sched_handle void phr_proc::reset() { + std::lock_guard lock(mutex); timer_periodic.stop(); timer_prohibit.stop(); phr_is_triggered = false; @@ -57,6 +58,8 @@ void phr_proc::reset() void phr_proc::set_config(srsran::phr_cfg_t& cfg) { + std::lock_guard lock(mutex); + phr_cfg = cfg; // First stop timers. If enabled==false or value is Inf, won't be re-started @@ -99,6 +102,7 @@ bool phr_proc::pathloss_changed() void phr_proc::start_periodic_timer() { + std::lock_guard lock(mutex); if (phr_cfg.enabled && phr_cfg.periodic_timer > 0) { timer_periodic.run(); } @@ -107,6 +111,7 @@ void phr_proc::start_periodic_timer() /* Trigger PHR when timers exires */ void phr_proc::timer_expired(uint32_t timer_id) { + std::lock_guard lock(mutex); if (!phr_cfg.enabled) { Warning("PHR: %s timer triggered but PHR has been disabled", timer_id == timer_periodic.id() ? "Periodic" : "Prohibit"); @@ -128,6 +133,8 @@ void phr_proc::timer_expired(uint32_t timer_id) void phr_proc::step() { + std::lock_guard lock(mutex); + if (phr_cfg.enabled && initiated) { if (pathloss_changed() && timer_prohibit.is_expired()) { Info("PHR: Triggered by pathloss difference. cur_pathloss_db=%d", last_pathloss_db); @@ -138,6 +145,7 @@ void phr_proc::step() bool phr_proc::generate_phr_on_ul_grant(float* phr) { + std::lock_guard lock(mutex); if (phr_is_triggered) { if (phr) { *phr = phy_h->get_phr(); @@ -158,6 +166,7 @@ bool phr_proc::generate_phr_on_ul_grant(float* phr) bool phr_proc::is_extended() { + std::lock_guard lock(mutex); return phr_cfg.extended; } } // namespace srsue diff --git a/srsue/src/stack/mac/proc_ra.cc b/srsue/src/stack/mac/proc_ra.cc index 44c6524a9..bb32955a0 100644 --- a/srsue/src/stack/mac/proc_ra.cc +++ b/srsue/src/stack/mac/proc_ra.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,13 +32,11 @@ namespace srsue { -const char* state_str[] = {"RA: INIT: ", - "RA: PDCCH: ", - "RA: Rx: ", +const char* state_str[] = {"RA: IDLE: ", + "RA: PDCCH: ", + "RA: Rx: ", "RA: Backoff: ", - "RA: ConRes: ", - "RA: WaitComplt: ", - "RA: Complt: "}; + "RA: ConRes: "}; #define rError(fmt, ...) logger.error("%s" fmt, state_str[state], ##__VA_ARGS__) #define rInfo(fmt, ...) logger.info("%s" fmt, state_str[state], ##__VA_ARGS__) @@ -54,7 +52,7 @@ int delta_preamble_db_table[5] = {0, 0, -3, -3, 8}; // Initializes memory and pointers to other objects void ra_proc::init(phy_interface_mac_lte* phy_h_, rrc_interface_mac* rrc_, - mac_interface_rrc::ue_rnti_t* rntis_, + ue_rnti* rntis_, srsran::timer_handler::unique_timer* time_alignment_timer_, mux* mux_unit_, srsran::ext_task_sched_handle* task_sched_) @@ -159,11 +157,14 @@ void ra_proc::state_pdcch_setup() ra_tti = info.tti_ra; ra_rnti = 1 + (ra_tti % 10) + (10 * info.f_id); rInfo("seq=%d, ra-rnti=0x%x, ra-tti=%d, f_id=%d", sel_preamble.load(), ra_rnti, info.tti_ra, info.f_id); - srsran::console( - "Random Access Transmission: seq=%d, tti=%d, ra-rnti=0x%x\n", sel_preamble.load(), info.tti_ra, ra_rnti); - rar_window_st = ra_tti + 3; - rntis->rar_rnti = ra_rnti; - state = RESPONSE_RECEPTION; + srsran::console("Random Access Transmission%s: seq=%d, tti=%d, ra-rnti=0x%x\n", + (started_by_pdcch) ? " (PDCCH order)" : "", + sel_preamble.load(), + info.tti_ra, + ra_rnti); + rar_window_st = ra_tti + 3; + rntis->set_rar_rnti(ra_rnti); + state = RESPONSE_RECEPTION; } else { rDebug("preamble not yet transmitted"); } @@ -228,7 +229,8 @@ void ra_proc::initialization() transmitted_contention_id = 0; preambleTransmissionCounter = 1; mux_unit->msg3_flush(); - backoff_param_ms = 0; + backoff_param_ms = 0; + transmitted_crnti = 0; resource_selection(); } @@ -312,7 +314,7 @@ void ra_proc::preamble_transmission() (preambleTransmissionCounter - 1) * rach_cfg.powerRampingStep; phy_h->prach_send(sel_preamble, sel_maskIndex - 1, received_target_power_dbm); - rntis->rar_rnti = SRSRAN_INVALID_RNTI; + rntis->clear_rar_rnti(); ra_tti = 0; rar_received = false; backoff_interval_start = -1; @@ -404,7 +406,7 @@ void ra_proc::tb_decoded_ok(const uint8_t cc_idx, const uint32_t tti) uint8_t grant[srsran::rar_subh::RAR_GRANT_LEN] = {}; rar_pdu_msg.get()->get_sched_grant(grant); - rntis->rar_rnti = SRSRAN_INVALID_RNTI; + rntis->clear_rar_rnti(); phy_h->set_rar_grant(grant, rar_pdu_msg.get()->get_temp_crnti()); current_ta = rar_pdu_msg.get()->get_ta_cmd(); @@ -414,15 +416,17 @@ void ra_proc::tb_decoded_ok(const uint8_t cc_idx, const uint32_t tti) rar_pdu_msg.get()->get_ta_cmd(), rar_pdu_msg.get()->get_temp_crnti()); + // Save Temp-CRNTI before generating the reply + rntis->set_temp_rnti(rar_pdu_msg.get()->get_temp_crnti()); + // Perform actions when preamble was selected by UE MAC if (preambleIndex <= 0) { mux_unit->msg3_prepare(); - rntis->temp_rnti = rar_pdu_msg.get()->get_temp_crnti(); // If this is the first successfully received RAR within this procedure, Msg3 is empty if (mux_unit->msg3_is_empty()) { // Save transmitted C-RNTI (if any) - transmitted_crnti = rntis->crnti; + transmitted_crnti = rntis->get_crnti(); // If we have a C-RNTI, tell Mux unit to append C-RNTI CE if no CCCH SDU transmission if (transmitted_crnti) { @@ -432,7 +436,7 @@ void ra_proc::tb_decoded_ok(const uint8_t cc_idx, const uint32_t tti) } // Save transmitted UE contention id, as defined by higher layers - transmitted_contention_id = rntis->contention_id; + transmitted_contention_id = rntis->get_contention_id(); task_queue.push([this]() { rDebug("Waiting for Contention Resolution"); @@ -455,7 +459,7 @@ void ra_proc::tb_decoded_ok(const uint8_t cc_idx, const uint32_t tti) */ void ra_proc::response_error() { - rntis->temp_rnti = 0; + rntis->clear_temp_rnti(); preambleTransmissionCounter++; contention_resolution_timer.stop(); if (preambleTransmissionCounter >= rach_cfg.preambleTransMax + 1) { @@ -489,16 +493,16 @@ void ra_proc::complete() { // Start looking for PDCCH CRNTI if (!transmitted_crnti) { - rntis->crnti = rntis->temp_rnti; + rntis->set_crnti_to_temp(); } - rntis->temp_rnti = 0; + rntis->clear_temp_rnti(); mux_unit->msg3_flush(); rrc->ra_completed(); - srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->crnti, current_ta); - rInfo("Random Access Complete. c-rnti=0x%x, ta=%d", rntis->crnti, current_ta); + srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", rntis->get_crnti(), current_ta); + rInfo("Random Access Complete. c-rnti=0x%x, ta=%d", rntis->get_crnti(), current_ta); state = IDLE; } @@ -522,7 +526,7 @@ void ra_proc::start_pdcch_order() rInfo("Starting PRACH by PDCCH order"); initialization(); } else { - logger.warning("Trying to start PRACH by MAC order in invalid state (%s)", state_str[state]); + logger.warning("Trying to start PRACH by PDCCH order in invalid state (%s)", state_str[state]); } } @@ -542,14 +546,14 @@ void ra_proc::timer_expired(uint32_t timer_id) */ bool ra_proc::contention_resolution_id_received(uint64_t rx_contention_id) { - task_queue.push([this, rx_contention_id]() { contention_resolution_id_received_unsafe(rx_contention_id); }); + task_queue.push([this, rx_contention_id]() { contention_resolution_id_received_nolock(rx_contention_id); }); return (transmitted_contention_id == rx_contention_id); } /* * Performs the actions defined in 5.1.5 for Temporal C-RNTI Contention Resolution */ -bool ra_proc::contention_resolution_id_received_unsafe(uint64_t rx_contention_id) +bool ra_proc::contention_resolution_id_received_nolock(uint64_t rx_contention_id) { bool uecri_successful = false; @@ -597,17 +601,15 @@ void ra_proc::pdcch_to_crnti(bool is_new_uplink_transmission) } // Called from the Stack thread -void ra_proc::update_rar_window(int& rar_window_start, int& rar_window_length) +void ra_proc::update_rar_window(rnti_window_safe& ra_window) { if (state != RESPONSE_RECEPTION) { // reset RAR window params to default values to disable RAR search - rar_window_start = -1; - rar_window_length = -1; + ra_window.reset(); } else { - rar_window_length = rach_cfg.responseWindowSize; - rar_window_start = rar_window_st; + ra_window.set(rach_cfg.responseWindowSize, rar_window_st); } - rDebug("rar_window_start=%d, rar_window_length=%d", rar_window_start, rar_window_length); + rDebug("rar_window_start=%d, rar_window_length=%d", ra_window.get_start(), ra_window.get_length()); } // Restart timer at each Msg3 HARQ retransmission (5.1.5) diff --git a/srsue/src/stack/mac/proc_sr.cc b/srsue/src/stack/mac/proc_sr.cc index 8a883fa9f..b53a68918 100644 --- a/srsue/src/stack/mac/proc_sr.cc +++ b/srsue/src/stack/mac/proc_sr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -48,6 +48,7 @@ void sr_proc::init(ra_proc* ra_, phy_interface_mac_lte* phy_h_, rrc_interface_ma void sr_proc::reset() { + std::lock_guard lock(mutex); is_pending_sr = false; } @@ -84,37 +85,56 @@ void sr_proc::set_config(srsran::sr_cfg_t& cfg) void sr_proc::step(uint32_t tti) { - if (initiated) { - if (is_pending_sr) { - if (sr_cfg.enabled) { - if (sr_counter < sr_cfg.dsr_transmax) { - if (sr_counter == 0 || need_tx(tti)) { - sr_counter++; - Info("SR: Signalling PHY sr_counter=%d", sr_counter); - phy_h->sr_send(); - } - } else { - if (need_tx(tti)) { - Info("SR: Releasing PUCCH/SRS resources, sr_counter=%d, dsr_transmax=%d", - sr_counter, - sr_cfg.dsr_transmax); - srsran::console("Scheduling request failed: releasing RRC connection...\n"); - rrc->release_pucch_srs(); - ra->start_mac_order(); - reset(); + bool do_mac_order = false; + bool do_release_pucch = false; + bool do_sr = false; + + { + std::lock_guard lock(mutex); + if (initiated) { + if (is_pending_sr) { + if (sr_cfg.enabled) { + if (sr_counter < sr_cfg.dsr_transmax) { + if (sr_counter == 0 || need_tx(tti)) { + sr_counter++; + Info("SR: Signalling PHY sr_counter=%d", sr_counter); + do_sr = true; + } + } else { + if (need_tx(tti)) { + Info("SR: Releasing PUCCH/SRS resources, sr_counter=%d, dsr_transmax=%d", + sr_counter, + sr_cfg.dsr_transmax); + srsran::console("Scheduling request failed: releasing RRC connection...\n"); + do_mac_order = true; + do_release_pucch = true; + is_pending_sr = false; + } } + } else if (ra->is_idle()) { + Info("SR: PUCCH not configured. Starting RA procedure"); + do_mac_order = true; + is_pending_sr = false; } - } else if (ra->is_idle()) { - Info("SR: PUCCH not configured. Starting RA procedure"); - ra->start_mac_order(); - reset(); } } } + + // These calls go out of this component, so call them with mutex unlocked + if (do_sr) { + phy_h->sr_send(); + } + if (do_release_pucch) { + rrc->release_pucch_srs(); + } + if (do_mac_order) { + ra->start_mac_order(); + } } void sr_proc::start() { + std::lock_guard lock(mutex); if (initiated) { if (!is_pending_sr) { sr_counter = 0; diff --git a/srsue/test/CMakeLists.txt b/srsue/src/stack/mac/test/CMakeLists.txt similarity index 67% rename from srsue/test/CMakeLists.txt rename to srsue/src/stack/mac/test/CMakeLists.txt index 5a0e89e14..1598227b2 100644 --- a/srsue/test/CMakeLists.txt +++ b/srsue/src/stack/mac/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,17 +18,6 @@ # and at http://www.gnu.org/licenses/. # -add_subdirectory(phy) -add_subdirectory(upper) - -if (ENABLE_TTCN3) - add_subdirectory(ttcn3) -endif (ENABLE_TTCN3) - -add_executable(metrics_test metrics_test.cc ../src/metrics_stdout.cc ../src/metrics_csv.cc) -target_link_libraries(metrics_test srsran_phy srsran_common) -add_test(metrics_test metrics_test -o ${CMAKE_CURRENT_BINARY_DIR}/ue_metrics.csv) - add_executable(mac_test mac_test.cc) target_link_libraries(mac_test srsue_mac srsue_phy srsran_common srsran_mac srsran_phy srsran_radio srsran_asn1 rrc_asn1 ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES}) add_test(mac_test mac_test) \ No newline at end of file diff --git a/srsue/test/mac_test.cc b/srsue/src/stack/mac/test/mac_test.cc similarity index 99% rename from srsue/test/mac_test.cc rename to srsue/src/stack/mac/test/mac_test.cc index a95ae0a6b..8ccae6634 100644 --- a/srsue/test/mac_test.cc +++ b/srsue/src/stack/mac/test/mac_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,7 +22,8 @@ #include "srsran/asn1/rrc/rr_common.h" #include "srsran/asn1/rrc_utils.h" #include "srsran/common/mac_pcap.h" -#include "srsran/common/test_common.h" +#include "srsran/common/tsan_options.h" +#include "srsran/support/srsran_test.h" #include "srsran/test/ue_test_interfaces.h" #include "srsue/hdr/stack/mac/mac.h" #include "srsue/hdr/stack/mac/mux.h" @@ -45,7 +46,7 @@ public: rlc_dummy() : received_bytes(0) {} bool has_data_locked(const uint32_t lcid) final { return ul_queues[lcid] > 0; } uint32_t get_buffer_state(const uint32_t lcid) final { return ul_queues[lcid]; } - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final { if (!read_enable || nof_bytes < read_min) { return 0; @@ -2338,6 +2339,7 @@ int mac_random_access_test() my_test.rar_nof_invalid_rapid = 0; my_test.check_ra_successful = true; my_test.temp_rnti++; // Temporal C-RNTI has to change to avoid duplicate + my_test.crnti = 0; TESTASSERT(!run_mac_ra_test(my_test, &mac, &phy, &tti, &stack)); stack.run_tti(tti++); TESTASSERT(rrc.ho_finish_successful); diff --git a/srsue/src/stack/mac/ul_harq.cc b/srsue/src/stack/mac/ul_harq.cc index ded1d8c2a..3058f7bd8 100644 --- a/srsue/src/stack/mac/ul_harq.cc +++ b/srsue/src/stack/mac/ul_harq.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,7 +35,7 @@ ul_harq_entity::ul_harq_entity(const uint8_t cc_idx_) : proc(SRSRAN_MAX_HARQ_PROC), logger(srslog::fetch_basic_logger("MAC")), cc_idx(cc_idx_) {} -bool ul_harq_entity::init(mac_interface_rrc_common::ue_rnti_t* rntis_, ra_proc* ra_procedure_, mux* mux_unit_) +bool ul_harq_entity::init(ue_rnti* rntis_, ra_proc* ra_procedure_, mux* mux_unit_) { mux_unit = mux_unit_; ra_procedure = ra_procedure_; @@ -66,6 +66,7 @@ void ul_harq_entity::reset_ndi() void ul_harq_entity::set_config(srsran::ul_harq_cfg_t& harq_cfg_) { + std::lock_guard lock(config_mutex); harq_cfg = harq_cfg_; } @@ -84,12 +85,12 @@ void ul_harq_entity::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant, Error("Invalid PID: %d", grant.pid); return; } - if (grant.rnti == rntis->crnti || grant.rnti == rntis->temp_rnti || SRSRAN_RNTI_ISRAR(grant.rnti)) { - if (grant.rnti == rntis->crnti && proc[grant.pid].is_sps()) { + if (grant.rnti == rntis->get_crnti() || grant.rnti == rntis->get_temp_rnti() || SRSRAN_RNTI_ISRAR(grant.rnti)) { + if (grant.rnti == rntis->get_crnti() && proc[grant.pid].is_sps()) { grant.tb.ndi = true; } proc[grant.pid].new_grant_ul(grant, action); - } else if (grant.rnti == rntis->sps_rnti) { + } else if (grant.rnti == rntis->get_sps_rnti()) { if (grant.tb.ndi) { grant.tb.ndi = proc[grant.pid].get_ndi(); proc[grant.pid].new_grant_ul(grant, action); @@ -112,7 +113,7 @@ int ul_harq_entity::get_current_tbs(uint32_t pid) float ul_harq_entity::get_average_retx() { - return average_retx; + return average_retx.load(std::memory_order_relaxed); } ul_harq_entity::ul_harq_process::ul_harq_process() : logger(srslog::fetch_basic_logger("MAC")) @@ -120,8 +121,6 @@ ul_harq_entity::ul_harq_process::ul_harq_process() : logger(srslog::fetch_basic_ pdu_ptr = NULL; payload_buffer = NULL; - bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_ul_t)); - harq_feedback = false; is_initiated = false; is_grant_configured = false; @@ -163,12 +162,12 @@ void ul_harq_entity::ul_harq_process::reset() current_tx_nb = 0; current_irv = 0; is_grant_configured = false; - bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_ul_t)); + cur_grant.reset(); } void ul_harq_entity::ul_harq_process::reset_ndi() { - cur_grant.tb.ndi = false; + cur_grant.set_ndi(false); } void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant, @@ -183,28 +182,32 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr // Get maximum retransmissions uint32_t max_retx; - if (grant.rnti == harq_entity->rntis->temp_rnti) { - max_retx = harq_entity->harq_cfg.max_harq_msg3_tx; - } else { - max_retx = harq_entity->harq_cfg.max_harq_tx; + { + std::lock_guard lock(harq_entity->config_mutex); + if (grant.rnti == harq_entity->rntis->get_temp_rnti()) { + max_retx = harq_entity->harq_cfg.max_harq_msg3_tx; + } else { + max_retx = harq_entity->harq_cfg.max_harq_tx; + } } // Check maximum retransmissions, do not consider last retx ACK if (current_tx_nb >= max_retx && !grant.hi_value) { Info("UL %d: Maximum number of ReTX reached (%d). Discarding TB.", pid, max_retx); - if (grant.rnti == harq_entity->rntis->temp_rnti) { + if (grant.rnti == harq_entity->rntis->get_temp_rnti()) { harq_entity->ra_procedure->harq_max_retx(); } reset(); - } else if (grant.rnti == harq_entity->rntis->temp_rnti && current_tx_nb && !grant.hi_value) { + } else if (grant.rnti == harq_entity->rntis->get_temp_rnti() && current_tx_nb && !grant.hi_value) { harq_entity->ra_procedure->harq_retx(); } } // Reset HARQ process if TB has changed if (harq_feedback && has_grant() && grant.tb.ndi_present) { - if (grant.tb.tbs != cur_grant.tb.tbs && cur_grant.tb.tbs > 0 && grant.tb.tbs > 0) { - Debug("UL %d: Reset due to change of dci size last_grant=%d, new_grant=%d", pid, cur_grant.tb.tbs, grant.tb.tbs); + uint32_t cur_grant_tbs = cur_grant.get_tbs(); + if (grant.tb.tbs != cur_grant_tbs && cur_grant_tbs > 0 && grant.tb.tbs > 0) { + Debug("UL %d: Reset due to change of dci size last_grant=%d, new_grant=%d", pid, cur_grant_tbs, grant.tb.tbs); reset(); } } @@ -215,9 +218,9 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr action->tb.enabled = true; // Decide if adaptive retx or new tx. 3 checks in 5.4.2.1 - } else if ((grant.rnti != harq_entity->rntis->temp_rnti && + } else if ((grant.rnti != harq_entity->rntis->get_temp_rnti() && grant.tb.ndi != get_ndi()) || // If not addressed to T-CRNTI and NDI toggled - (grant.rnti == harq_entity->rntis->crnti && + (grant.rnti == harq_entity->rntis->get_crnti() && !has_grant()) || // If addressed to C-RNTI and buffer is empty (grant.is_rar)) // Grant received in a RAR { @@ -254,7 +257,7 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr } } - if (grant.rnti == harq_entity->rntis->crnti && harq_entity->ra_procedure->is_contention_resolution()) { + if (grant.rnti == harq_entity->rntis->get_crnti() && harq_entity->ra_procedure->is_contention_resolution()) { harq_entity->ra_procedure->pdcch_to_crnti(true); } } else if (has_grant()) { @@ -265,10 +268,10 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr } if (harq_entity->pcap) { uint16_t rnti; - if (grant.rnti == harq_entity->rntis->temp_rnti && harq_entity->rntis->temp_rnti) { - rnti = harq_entity->rntis->temp_rnti; + if (grant.rnti == harq_entity->rntis->get_temp_rnti() && harq_entity->rntis->get_temp_rnti()) { + rnti = harq_entity->rntis->get_temp_rnti(); } else { - rnti = harq_entity->rntis->crnti; + rnti = harq_entity->rntis->get_crnti(); } harq_entity->pcap->write_ul_crnti(pdu_ptr, grant.tb.tbs, rnti, get_nof_retx(), grant.tti_tx, harq_entity->cc_idx); } @@ -291,7 +294,7 @@ bool ul_harq_entity::ul_harq_process::has_grant() bool ul_harq_entity::ul_harq_process::get_ndi() { - return cur_grant.tb.ndi; + return cur_grant.get_ndi(); } bool ul_harq_entity::ul_harq_process::is_sps() @@ -306,7 +309,7 @@ uint32_t ul_harq_entity::ul_harq_process::get_nof_retx() int ul_harq_entity::ul_harq_process::get_current_tbs() { - return cur_grant.tb.tbs; + return cur_grant.get_tbs(); } // Retransmission with or w/o dci (Section 5.4.2.2) @@ -323,29 +326,29 @@ void ul_harq_entity::ul_harq_process::generate_retx(mac_interface_phy_lte::mac_g Info("UL %d: Adaptive retx=%d, RV=%d, TBS=%d, HI=%s, ndi=%d, prev_ndi=%d", pid, - current_tx_nb, + current_tx_nb.load(std::memory_order_relaxed), get_rv(), grant.tb.tbs, harq_feedback ? "ACK" : "NACK", grant.tb.ndi, - cur_grant.tb.ndi); + cur_grant.get_ndi()); - cur_grant = grant; + cur_grant.set(grant); harq_feedback = false; generate_tx(action); // Reset the RV received in this grant - cur_grant.tb.rv = -1; + cur_grant.set_rv(-1); // HARQ entity requests a non-adaptive transmission } else if (!harq_feedback) { // Non-adaptive retx are only sent if HI=NACK. If HI=ACK but no dci was received do not reset PID Info("UL %d: Non-Adaptive retx=%d, RV=%d, TBS=%d, HI=%s", pid, - current_tx_nb, + current_tx_nb.load(std::memory_order_relaxed), get_rv(), - cur_grant.tb.tbs, + cur_grant.get_tbs(), harq_feedback ? "ACK" : "NACK"); generate_tx(action); @@ -357,20 +360,23 @@ void ul_harq_entity::ul_harq_process::generate_new_tx(mac_interface_phy_lte::mac mac_interface_phy_lte::tb_action_ul_t* action) { // Compute average number of retransmissions per packet considering previous packet - harq_entity->average_retx = SRSRAN_VEC_CMA((float)current_tx_nb, harq_entity->average_retx, harq_entity->nof_pkts++); - cur_grant = grant; - harq_feedback = false; - is_grant_configured = true; - current_tx_nb = 0; - current_irv = 0; + harq_entity->average_retx.store(SRSRAN_VEC_CMA((float)current_tx_nb, + harq_entity->average_retx.load(std::memory_order_relaxed), + harq_entity->nof_pkts++), + std::memory_order_relaxed); + cur_grant.set(grant); + harq_feedback = false; + is_grant_configured = true; + current_tx_nb = 0; + current_irv = 0; - action->is_rar = grant.is_rar || (grant.rnti == harq_entity->rntis->temp_rnti); + action->is_rar = grant.is_rar || (grant.rnti == harq_entity->rntis->get_temp_rnti()); Info("UL %d: New TX%s, RV=%d, TBS=%d", pid, - grant.rnti == harq_entity->rntis->temp_rnti ? " for Msg3" : "", + grant.rnti == harq_entity->rntis->get_temp_rnti() ? " for Msg3" : "", get_rv(), - cur_grant.tb.tbs); + cur_grant.get_tbs()); generate_tx(action); } @@ -382,7 +388,8 @@ void ul_harq_entity::ul_harq_process::generate_tx(mac_interface_phy_lte::tb_acti action->current_tx_nb = current_tx_nb; action->expect_ack = true; - action->tb.rv = cur_grant.tb.rv > 0 ? cur_grant.tb.rv : get_rv(); + int rv = cur_grant.get_rv(); + action->tb.rv = rv > 0 ? rv : get_rv(); action->tb.enabled = true; action->tb.payload = pdu_ptr; action->tb.softbuffer.tx = &softbuffer; diff --git a/srsue/src/stack/mac_common/CMakeLists.txt b/srsue/src/stack/mac_common/CMakeLists.txt index 06a7c1f2e..35d78c407 100644 --- a/srsue/src/stack/mac_common/CMakeLists.txt +++ b/srsue/src/stack/mac_common/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsue/src/stack/mac_common/mac_common.cc b/srsue/src/stack/mac_common/mac_common.cc index 3c6dfda24..f5e016def 100644 --- a/srsue/src/stack/mac_common/mac_common.cc +++ b/srsue/src/stack/mac_common/mac_common.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/src/stack/mac_nr/CMakeLists.txt b/srsue/src/stack/mac_nr/CMakeLists.txt index 2b98a9b3d..04fb12672 100644 --- a/srsue/src/stack/mac_nr/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsue/src/stack/mac_nr/demux_nr.cc b/srsue/src/stack/mac_nr/demux_nr.cc index a5ada0e45..39bac0f6b 100644 --- a/srsue/src/stack/mac_nr/demux_nr.cc +++ b/srsue/src/stack/mac_nr/demux_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,6 +21,7 @@ #include "srsue/hdr/stack/mac_nr/demux_nr.h" #include "srsran/common/buffer_pool.h" +#include "srsran/common/string_helpers.h" #include "srsran/interfaces/ue_rlc_interfaces.h" namespace srsue { @@ -29,44 +30,83 @@ demux_nr::demux_nr(srslog::basic_logger& logger_) : logger(logger_) {} demux_nr::~demux_nr() {} -int32_t demux_nr::init(rlc_interface_mac* rlc_) +int32_t demux_nr::init(rlc_interface_mac* rlc_, phy_interface_mac_nr* phy_) { rlc = rlc_; + phy = phy_; return SRSRAN_SUCCESS; } +uint64_t demux_nr::get_received_crueid() +{ + return received_crueid; +} + // Enqueues PDU and returns quickly void demux_nr::push_pdu(srsran::unique_byte_buffer_t pdu, uint32_t tti) { pdu_queue.push(std::move(pdu)); } +void demux_nr::push_bcch(srsran::unique_byte_buffer_t pdu) +{ + bcch_queue.push(std::move(pdu)); +} + +/* Demultiplexing of MAC PDU associated with a Temporal C-RNTI. The PDU will + * remain in buffer until demultiplex_pending_pdu() is called. + * This features is provided to enable the Random Access Procedure to decide + * whether the PDU shall pass to upper layers or not, which depends on the + * Contention Resolution result. + * + * Warning: this function does some processing here assuming ACK deadline is not an + * issue here because Temp C-RNTI messages have small payloads + */ +void demux_nr::push_pdu_temp_crnti(srsran::unique_byte_buffer_t pdu, uint32_t tti) +{ + received_crueid = 0; + handle_pdu(rx_pdu_tcrnti, std::move(pdu)); +} + void demux_nr::process_pdus() { + // Handle first BCCH + while (not bcch_queue.empty()) { + srsran::unique_byte_buffer_t pdu = bcch_queue.wait_pop(); + logger.debug(pdu->msg, pdu->N_bytes, "Handling MAC BCCH PDU (%d B)", pdu->N_bytes); + rlc->write_pdu_bcch_dlsch(pdu->msg, pdu->N_bytes); + } + // Then user PDUs while (not pdu_queue.empty()) { srsran::unique_byte_buffer_t pdu = pdu_queue.wait_pop(); - handle_pdu(std::move(pdu)); + handle_pdu(rx_pdu, std::move(pdu)); } } /// Handling of DLSCH PDUs only -void demux_nr::handle_pdu(srsran::unique_byte_buffer_t pdu) +void demux_nr::handle_pdu(srsran::mac_sch_pdu_nr& pdu_buffer, srsran::unique_byte_buffer_t pdu) { - logger.info(pdu->msg, pdu->N_bytes, "Handling MAC PDU (%d B)", pdu->N_bytes); + logger.debug(pdu->msg, pdu->N_bytes, "Handling MAC PDU (%d B)", pdu->N_bytes); - rx_pdu.init_rx(); - if (rx_pdu.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) { + pdu_buffer.init_rx(); + if (pdu_buffer.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) { return; } - for (uint32_t i = 0; i < rx_pdu.get_num_subpdus(); ++i) { - srsran::mac_sch_subpdu_nr subpdu = rx_pdu.get_subpdu(i); - logger.info("Handling subPDU %d/%d: rnti=0x%x lcid=%d, sdu_len=%d", - i + 1, - rx_pdu.get_num_subpdus(), - subpdu.get_c_rnti(), - subpdu.get_lcid(), - subpdu.get_sdu_length()); + if (logger.info.enabled()) { + fmt::memory_buffer str_buffer; + pdu_buffer.to_string(str_buffer); + logger.info("%s", srsran::to_c_str(str_buffer)); + } + + for (uint32_t i = 0; i < pdu_buffer.get_num_subpdus(); ++i) { + srsran::mac_sch_subpdu_nr subpdu = pdu_buffer.get_subpdu(i); + logger.debug("Handling subPDU %d/%d: rnti=0x%x lcid=%d, sdu_len=%d", + i + 1, + pdu_buffer.get_num_subpdus(), + subpdu.get_c_rnti(), + subpdu.get_lcid(), + subpdu.get_sdu_length()); // Handle Timing Advance CE switch (subpdu.get_lcid()) { @@ -74,10 +114,12 @@ void demux_nr::handle_pdu(srsran::unique_byte_buffer_t pdu) logger.info("DRX CE not implemented."); break; case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::TA_CMD: - logger.info("Timing Advance CE not implemented."); + logger.info("Received TA=%d.", subpdu.get_ta().ta_command); + phy->set_timeadv(0, subpdu.get_ta().ta_command); break; case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CON_RES_ID: - logger.info("Contention Resolution CE not implemented."); + received_crueid = subpdu.get_ue_con_res_id_ce_packed(); + logger.info("Received Contention Resolution ID 0x%lx", subpdu.get_ue_con_res_id_ce_packed()); break; default: if (subpdu.is_sdu()) { diff --git a/srsue/src/stack/mac_nr/dl_harq_nr.cc b/srsue/src/stack/mac_nr/dl_harq_nr.cc index b23cac552..9fbf59ff6 100644 --- a/srsue/src/stack/mac_nr/dl_harq_nr.cc +++ b/srsue/src/stack/mac_nr/dl_harq_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,6 +35,14 @@ dl_harq_entity_nr::dl_harq_entity_nr(uint8_t cc_idx_, // Init broadcast HARQ process bcch_proc.init(-1); pthread_rwlock_init(&rwlock, NULL); + + // Create default number of processes + for (uint32_t i = 0; i < cfg.nof_procs; i++) { + harq_procs[i] = std::unique_ptr(new dl_harq_process_nr(this)); + if (!harq_procs.at(i)->init(i)) { + logger.error("Error while initializing DL-HARQ process %d", i); + } + } } dl_harq_entity_nr::~dl_harq_entity_nr() @@ -51,17 +59,19 @@ int32_t dl_harq_entity_nr::set_config(const srsran::dl_harq_cfg_nr_t& cfg_) return SRSRAN_ERROR; } - // clear old processees - for (auto& proc : harq_procs) { - proc = nullptr; - } - - // Allocate and init configured HARQ processes - for (uint32_t i = 0; i < cfg.nof_procs; i++) { - harq_procs[i] = std::unique_ptr(new dl_harq_process_nr(this)); - if (!harq_procs.at(i)->init(i)) { - logger.error("Error while initializing DL-HARQ process %d", i); - return SRSRAN_ERROR; + if (cfg_.nof_procs < cfg.nof_procs) { + // clear old processes if not needed + for (uint32_t i = cfg.nof_procs - 1; i < cfg_.nof_procs; i++) { + harq_procs[i] = nullptr; + } + } else { + // Add new processes + for (uint32_t i = cfg.nof_procs; i < cfg_.nof_procs; i++) { + harq_procs[i] = std::unique_ptr(new dl_harq_process_nr(this)); + if (!harq_procs.at(i)->init(i)) { + logger.error("Error while initializing DL-HARQ process %d", i); + return SRSRAN_ERROR; + } } } @@ -135,8 +145,9 @@ void dl_harq_entity_nr::reset() dl_harq_entity_nr::dl_harq_metrics_t dl_harq_entity_nr::get_metrics() { - dl_harq_metrics_t tmp = metrics; - metrics = {}; + std::lock_guard lock(metrics_mutex); + dl_harq_metrics_t tmp = metrics; + metrics = {}; return tmp; } @@ -227,21 +238,24 @@ void dl_harq_entity_nr::dl_harq_process_nr::tb_decoded(const mac_nr_grant_dl_t& if (acked and result.payload != nullptr) { if (is_bcch) { - logger.warning("Delivering PDU=%d bytes to Dissassemble and Demux unit (BCCH) not implemented", grant.tbs); - reset(); + harq_entity->demux_unit->push_bcch(std::move(result.payload)); } else { if (grant.rnti == harq_entity->mac->get_temp_crnti()) { logger.debug("Delivering PDU=%d bytes to Dissassemble and Demux unit (Temporal C-RNTI) not implemented", grant.tbs); + harq_entity->demux_unit->push_pdu_temp_crnti(std::move(result.payload), grant.tti); + result.ack = harq_entity->mac->received_contention_id(harq_entity->demux_unit->get_received_crueid()); } else { logger.debug("Delivering PDU=%d bytes to Dissassemble and Demux unit", grant.tbs); harq_entity->demux_unit->push_pdu(std::move(result.payload), grant.tti); } } + std::lock_guard lock(harq_entity->metrics_mutex); harq_entity->metrics.rx_ok++; harq_entity->metrics.rx_brate += grant.tbs * 8; } else { + std::lock_guard lock(harq_entity->metrics_mutex); harq_entity->metrics.rx_ko++; } diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index ad7d28d85..7a1553c4d 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,6 +21,7 @@ #include "srsue/hdr/stack/mac_nr/mac_nr.h" #include "srsran/interfaces/ue_rlc_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" #include "srsran/mac/mac_rar_pdu_nr.h" #include "srsue/hdr/stack/mac_nr/proc_ra_nr.h" @@ -73,7 +74,7 @@ int mac_nr::init(const mac_nr_args_t& args_, return SRSRAN_ERROR; } - if (demux.init(rlc) != SRSRAN_SUCCESS) { + if (demux.init(rlc, phy) != SRSRAN_SUCCESS) { logger.error("Couldn't initialize demux unit."); return SRSRAN_ERROR; } @@ -105,6 +106,22 @@ void mac_nr::stop() void mac_nr::reset() { logger.info("Resetting MAC-NR"); + + // TODO: Implement all the steps in 5.9 + proc_bsr.reset(); + proc_sr.reset(); + proc_ra.reset(); + mux.reset(); + for (const auto& cc : dl_harq) { + if (cc != nullptr) { + cc->reset(); + } + } + for (const auto& cc : ul_harq) { + if (cc != nullptr) { + cc->reset(); + } + } } void mac_nr::run_tti(const uint32_t tti) @@ -155,13 +172,16 @@ void mac_nr::update_buffer_states() mac_interface_phy_nr::sched_rnti_t mac_nr::get_ul_sched_rnti_nr(const uint32_t tti) { - return {c_rnti, srsran_rnti_type_c}; + if (has_temp_crnti() && has_crnti() == false) { + logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", rntis.get_temp_rnti()); + return {rntis.get_temp_rnti(), srsran_rnti_type_c}; + } + return {rntis.get_crnti(), srsran_rnti_type_c}; } bool mac_nr::is_si_opportunity() { - // TODO: ask RRC if we need SI - return false; + return search_bcch; } bool mac_nr::is_paging_opportunity() @@ -184,9 +204,9 @@ mac_interface_phy_nr::sched_rnti_t mac_nr::get_dl_sched_rnti_nr(const uint32_t t return {proc_ra.get_rar_rnti(), srsran_rnti_type_ra}; } - if (proc_ra.has_temp_crnti() && has_crnti() == false) { - logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", proc_ra.get_temp_crnti()); - return {proc_ra.get_temp_crnti(), srsran_rnti_type_c}; + if (has_temp_crnti() && has_crnti() == false) { + logger.debug("SCHED: Searching temp C-RNTI=0x%x (proc_ra)", rntis.get_temp_rnti()); + return {rntis.get_temp_rnti(), srsran_rnti_type_c}; } if (has_crnti()) { @@ -198,19 +218,34 @@ mac_interface_phy_nr::sched_rnti_t mac_nr::get_dl_sched_rnti_nr(const uint32_t t return {SRSRAN_INVALID_RNTI, srsran_rnti_type_c}; } -bool mac_nr::has_crnti() +bool mac_nr::has_temp_crnti() { - return c_rnti != SRSRAN_INVALID_RNTI; -} - -uint16_t mac_nr::get_crnti() -{ - return c_rnti; + return rntis.get_temp_rnti() != SRSRAN_INVALID_RNTI; } uint16_t mac_nr::get_temp_crnti() { - return proc_ra.get_temp_crnti(); + return rntis.get_temp_rnti(); +} + +void mac_nr::set_temp_crnti(uint16_t temp_crnti) +{ + rntis.set_temp_rnti(temp_crnti); +} + +void mac_nr::set_crnti_to_temp() +{ + rntis.set_crnti_to_temp(); +} + +bool mac_nr::has_crnti() +{ + return rntis.get_crnti() != SRSRAN_INVALID_RNTI; +} + +uint16_t mac_nr::get_crnti() +{ + return rntis.get_crnti(); } srsran::mac_sch_subpdu_nr::lcg_bsr_t mac_nr::generate_sbsr() @@ -218,6 +253,11 @@ srsran::mac_sch_subpdu_nr::lcg_bsr_t mac_nr::generate_sbsr() return proc_bsr.generate_sbsr(); } +void mac_nr::set_padding_bytes(uint32_t nof_bytes) +{ + proc_bsr.set_padding_bytes(nof_bytes); +} + void mac_nr::bch_decoded_ok(uint32_t tti, srsran::unique_byte_buffer_t payload) { // Send MIB to RLC @@ -293,7 +333,7 @@ void mac_nr::new_grant_dl(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, void mac_nr::tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) { - logger.debug("tb_decoded(): cc_idx=%d, tti=%d, rnti=%d, pid=%d, tbs=%d, ndi=%d, rv=%d, result=%s", + logger.debug("tb_decoded(): cc_idx=%d, tti=%d, rnti=0x%X, pid=%d, tbs=%d, ndi=%d, rv=%d, result=%s", cc_idx, grant.tti, grant.rnti, @@ -306,7 +346,9 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, t write_pcap(cc_idx, grant, result); if (proc_ra.has_rar_rnti() && grant.rnti == proc_ra.get_rar_rnti()) { - proc_ra.handle_rar_pdu(result); + if (result.ack && result.payload != nullptr) { + proc_ra.handle_rar_pdu(result); + } } else { // Assert HARQ entity if (dl_harq.at(cc_idx) == nullptr) { @@ -316,11 +358,16 @@ void mac_nr::tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, t dl_harq.at(cc_idx)->tb_decoded(grant, std::move(result)); } + + // If proc ra is in contention resolution (RA connection request procedure) + if (proc_ra.is_contention_resolution() && grant.rnti == rntis.get_temp_rnti()) { + proc_ra.received_contention_resolution(contention_res_successful); + } } void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) { - logger.debug("new_grant_ul(): cc_idx=%d, tti=%d, rnti=%d, pid=%d, tbs=%d, ndi=%d, rv=%d, is_rar=%d", + logger.debug("new_grant_ul(): cc_idx=%d, tti=%d, rnti=0x%X, pid=%d, tbs=%d, ndi=%d, rv=%d, is_rar=%d", cc_idx, grant.tti, grant.rnti, @@ -333,8 +380,8 @@ void mac_nr::new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, // Clear UL action *action = {}; - // if proc ra is in contention resolution and c_rnti == grant.c_rnti resolve contention resolution - if (proc_ra.is_contention_resolution() && grant.rnti == c_rnti) { + // if proc ra is in contention resolution and c_rnti == grant.c_rnti (RA connection resume procedure) + if (proc_ra.is_contention_resolution() && grant.rnti == get_crnti()) { proc_ra.pdcch_to_crnti(); } @@ -427,21 +474,26 @@ int mac_nr::set_config(const srsran::sr_cfg_nr_t& sr_cfg) return proc_sr.set_config(sr_cfg); } -void mac_nr::set_config(const srsran::rach_nr_cfg_t& rach_cfg) +void mac_nr::set_config(const srsran::rach_cfg_nr_t& rach_cfg) { proc_ra.set_config(rach_cfg); } void mac_nr::set_contention_id(uint64_t ue_identity) { - contention_id = ue_identity; + rntis.set_contention_id(ue_identity); +} + +void mac_nr::bcch_search(bool enabled) +{ + search_bcch = enabled; } bool mac_nr::set_crnti(const uint16_t c_rnti_) { if (is_valid_crnti(c_rnti_)) { logger.info("Setting C-RNTI to 0x%X", c_rnti_); - c_rnti = c_rnti_; + rntis.set_crnti(c_rnti_); return true; } else { logger.warning("Failed to set C-RNTI, 0x%X is not valid.", c_rnti_); @@ -502,6 +554,16 @@ void mac_nr::get_metrics(mac_metrics_t m[SRSRAN_MAX_CARRIERS]) metrics = {}; } +void mac_nr::rrc_ra_problem() +{ + rrc->ra_problem(); +} + +void mac_nr::rrc_ra_completed() +{ + rrc->ra_completed(); +} + /** * Called from the main stack thread to process received PDUs */ @@ -510,9 +572,10 @@ void mac_nr::process_pdus() demux.process_pdus(); } -uint64_t mac_nr::get_contention_id() +bool mac_nr::received_contention_id(uint64_t rx_contention_id) { - return 0xdeadbeef; // TODO when rebased on PR + contention_res_successful = rntis.get_contention_id() == rx_contention_id; + return contention_res_successful; } // TODO same function as for mac_eutra diff --git a/srsue/src/stack/mac_nr/mux_nr.cc b/srsue/src/stack/mac_nr/mux_nr.cc index b6bd4b88c..3164de240 100644 --- a/srsue/src/stack/mac_nr/mux_nr.cc +++ b/srsue/src/stack/mac_nr/mux_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,6 +22,7 @@ #include "srsue/hdr/stack/mac_nr/mux_nr.h" #include "srsran/common/buffer_pool.h" #include "srsran/interfaces/ue_rlc_interfaces.h" + namespace srsue { mux_nr::mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger_) : mac(mac_), logger(logger_) {} @@ -43,6 +44,12 @@ int32_t mux_nr::init(rlc_interface_mac* rlc_) return SRSRAN_SUCCESS; } +void mux_nr::reset() +{ + std::lock_guard lock(mutex); + this->logical_channels.clear(); +} + int mux_nr::setup_lcid(const srsran::logical_channel_config_t& config) { std::lock_guard lock(mutex); @@ -70,73 +77,93 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) logger.debug("Building new MAC PDU (%d B)", max_pdu_len); tx_pdu.init_tx(phy_tx_pdu.get(), max_pdu_len, true); - if (msg3_is_pending()) { - // If Msg3 is pending, pack it - // Use the CRNTI which is provided in the RRC reconfiguration (only for DC mode maybe other) + if (msg3_is_pending() && mac.has_crnti()) { tx_pdu.add_crnti_ce(mac.get_crnti()); - tx_pdu.add_sbsr_ce(mac.generate_sbsr()); msg3_transmitted(); - } else { - // Pack normal UL data PDU - int32_t remaining_len = tx_pdu.get_remaing_len(); // local variable to reserv space for CEs + } - if (add_bsr_ce == sbsr_ce) { - // reserve space for SBSR - remaining_len -= 2; - } + // Pack normal UL data PDU + int32_t remaining_len = tx_pdu.get_remaing_len(); // local variable to reserve space for CEs - // First add MAC SDUs - for (const auto& lc : logical_channels) { - // TODO: Add proper priority handling - logger.debug("Adding SDUs for LCID=%d (max %d B)", lc.lcid, remaining_len); - while (remaining_len >= MIN_RLC_PDU_LEN) { - // read RLC PDU - rlc_buff->clear(); - uint8_t* rd = rlc_buff->msg; + if (!msg3_is_pending() && add_bsr_ce == sbsr_ce) { + // reserve space for SBSR + remaining_len -= 2; + } - // Determine space for RLC - remaining_len -= remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2; + // First add MAC SDUs + for (const auto& lc : logical_channels) { + // TODO: Add proper priority handling + logger.debug("Adding SDUs for LCID=%d (max %d B)", lc.lcid, remaining_len); + while (remaining_len >= MIN_RLC_PDU_LEN) { + // read RLC PDU + rlc_buff->clear(); + uint8_t* rd = rlc_buff->msg; - // Read PDU from RLC - int pdu_len = rlc->read_pdu(lc.lcid, rd, remaining_len); + // Determine space for RLC + int32_t subpdu_header_len = (remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2); - if (pdu_len > remaining_len) { - logger.error("Can't add SDU of %d B. Available space %d B", pdu_len, remaining_len); - break; - } else { - // Add SDU if RLC has something to tx - if (pdu_len > 0) { - rlc_buff->N_bytes = pdu_len; - logger.debug(rlc_buff->msg, rlc_buff->N_bytes, "Read %d B from RLC", rlc_buff->N_bytes); + // Read PDU from RLC (account for subPDU header) + int pdu_len = rlc->read_pdu(lc.lcid, rd, remaining_len - subpdu_header_len); - // add to MAC PDU and pack - if (tx_pdu.add_sdu(lc.lcid, rlc_buff->msg, rlc_buff->N_bytes) != SRSRAN_SUCCESS) { - logger.error("Error packing MAC PDU"); - break; - } - } else { + if (pdu_len > remaining_len) { + logger.error("Can't add SDU of %d B. Available space %d B", pdu_len, remaining_len); + break; + } else { + // Add SDU if RLC has something to tx + if (pdu_len > 0) { + rlc_buff->N_bytes = pdu_len; + logger.debug(rlc_buff->msg, rlc_buff->N_bytes, "Read %d B from RLC", rlc_buff->N_bytes); + + // add to MAC PDU and pack + if (tx_pdu.add_sdu(lc.lcid, rlc_buff->msg, rlc_buff->N_bytes) != SRSRAN_SUCCESS) { + logger.error("Error packing MAC PDU"); break; } - remaining_len -= pdu_len; - logger.debug("%d B remaining PDU", remaining_len); + if (lc.lcid == 0 && msg3_is_pending()) { + // TODO: + msg3_transmitted(); + } + + } else { + // couldn't read PDU from RLC + break; } + + remaining_len -= (pdu_len + subpdu_header_len); + logger.debug("%d B remaining PDU", remaining_len); } } - - // Second add fixed-sized MAC CEs (e.g. SBSR) - if (add_bsr_ce == sbsr_ce) { - tx_pdu.add_sbsr_ce(mac.generate_sbsr()); - add_bsr_ce = no_bsr; - } - - // Lastly, add variable-sized MAC CEs } + // check if + if (add_bsr_ce == no_bsr) { + // tell BSR proc we still have space in PDU and let it decide to create a padding BSR + mac.set_padding_bytes(tx_pdu.get_remaing_len()); + } + + // Second add fixed-sized MAC CEs (e.g. SBSR) + if (add_bsr_ce == sbsr_ce) { + tx_pdu.add_sbsr_ce(mac.generate_sbsr()); + add_bsr_ce = no_bsr; + } else if (add_bsr_ce == lbsr_ce) { + // TODO: implement LBSR support + tx_pdu.add_sbsr_ce(mac.generate_sbsr()); + add_bsr_ce = no_bsr; + } + + // Lastly, add variable-sized MAC CEs + // Pack PDU tx_pdu.pack(); - logger.debug(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes); + if (logger.info.enabled()) { + // log pretty printed PDU + fmt::memory_buffer buff; + tx_pdu.to_string(buff); + logger.info("%s", srsran::to_c_str(buff)); + logger.debug(phy_tx_pdu->msg, phy_tx_pdu->N_bytes, "Generated MAC PDU (%d B)", phy_tx_pdu->N_bytes); + } return phy_tx_pdu; } @@ -172,14 +199,15 @@ bool mux_nr::msg3_is_empty() return msg3_buff->N_bytes == 0; } -void mux_nr::generate_bsr_mac_ce(const bsr_interface_mux_nr::bsr_format_nr_t& format) +void mux_nr::generate_bsr_mac_ce(const srsran::bsr_format_nr_t& format) { switch (format) { - case bsr_interface_mux_nr::SHORT_BSR: + case srsran::SHORT_BSR: add_bsr_ce = sbsr_ce; break; - case bsr_interface_mux_nr::LONG_BSR: + case srsran::LONG_BSR: add_bsr_ce = lbsr_ce; + break; default: logger.error("MUX can only be instructred to generate short or long BSRs."); } diff --git a/srsue/src/stack/mac_nr/proc_bsr_nr.cc b/srsue/src/stack/mac_nr/proc_bsr_nr.cc index 2fd7d69be..05b31f90a 100644 --- a/srsue/src/stack/mac_nr/proc_bsr_nr.cc +++ b/srsue/src/stack/mac_nr/proc_bsr_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -25,67 +25,7 @@ namespace srsue { -// TS 38.321, Table 6.1.3.1-1 Buffer size levels (in bytes) for 5-bit Buffer Size field, all values <= except marked -static const uint32_t buffer_size_levels_5bit_max_idx = 31; -static uint32_t buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx + 1] = { - /* == */ 0, 10, 14, 20, 28, 38, 53, 74, 102, 142, 198, - 276, 384, 535, 745, 1038, 1446, 2014, 2806, 3909, 5446, 7587, - 10570, 14726, 20516, 28581, 39818, 55474, 77284, 107669, 150000, /* > */ 150000}; - -// TS 38.321, Table 6.1.3.1-2: Buffer size levels (in bytes) for 8-bit Buffer Size field, all values <= except marked -static const uint32_t buffer_size_levels_8bit_max_idx = 254; -static uint32_t buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx + 1] = { - /* == */ 0, 10, 11, 12, 13, - 14, 15, 16, 17, 18, - 19, 20, 22, 23, 25, - 26, 28, 30, 32, 34, - 36, 38, 40, 43, 46, - 49, 52, 55, 59, 62, - 66, 71, 75, 80, 85, - 91, 97, 103, 110, 117, - 124, 132, 141, 150, 160, - 170, 181, 193, 205, 218, - 233, 248, 264, 281, 299, - 318, 339, 361, 384, 409, - 436, 464, 494, 526, 560, - 597, 635, 677, 720, 767, - 817, 870, 926, 987, 1051, - 1119, 1191, 1269, 1351, 1439, - 1532, 1631, 1737, 1850, 1970, - 2098, 2234, 2379, 2533, 2698, - 2873, 3059, 3258, 3469, 3694, - 3934, 4189, 4461, 4751, 5059, - 5387, 5737, 6109, 6506, 6928, - 7378, 7857, 8367, 8910, 9488, - 10104, 10760, 11458, 12202, 12994, - 13838, 14736, 15692, 16711, 17795, - 18951, 20181, 21491, 22885, 24371, - 25953, 27638, 29431, 31342, 33376, - 35543, 37850, 40307, 42923, 45709, - 48676, 51836, 55200, 58784, 62599, - 66663, 70990, 75598, 80505, 85730, - 91295, 97221, 103532, 110252, 117409, - 125030, 133146, 141789, 150992, 160793, - 171231, 182345, 194182, 206786, 220209, - 234503, 249725, 265935, 283197, 301579, - 321155, 342002, 364202, 387842, 413018, - 439827, 468377, 498780, 531156, 565634, - 602350, 641449, 683087, 727427, 774645, - 824928, 878475, 935498, 996222, 1060888, - 1129752, 1203085, 1281179, 1364342, 1452903, - 1547213, 1647644, 1754595, 1868488, 1989774, - 2118933, 2256475, 2402946, 2558924, 2725027, - 2901912, 3090279, 3290873, 3504487, 3731968, - 3974215, 4232186, 4506902, 4799451, 5110989, - 5442750, 5796046, 6172275, 6572925, 6999582, - 7453933, 7937777, 8453028, 9001725, 9586039, - 10208280, 10870913, 11576557, 12328006, 13128233, - 13980403, 14887889, 15854280, 16883401, 17979324, - 19146385, 20389201, 21712690, 23122088, 24622972, - 26221280, 27923336, 29735875, 31666069, 33721553, - 35910462, 38241455, 40723756, 43367187, 46182206, - 49179951, 52372284, 55771835, 59392055, 63247269, - 67352729, 71724679, 76380419, 81338368, /* > */ 81338368}; +using namespace srsran; int32_t proc_bsr_nr::init(proc_sr_nr* sr_, mux_interface_bsr_nr* mux_, @@ -129,9 +69,13 @@ void proc_bsr_nr::set_trigger(bsr_trigger_type_t new_trigger) void proc_bsr_nr::reset() { + std::lock_guard lock(mutex); + timer_periodic.stop(); timer_retx.stop(); + lcg_priorities.clear(); + triggered_bsr_type = NONE; } @@ -205,8 +149,15 @@ bool proc_bsr_nr::check_new_data(const mac_buffer_states_t& new_buffer_state) return false; } +/** + * @brief Called by Mux through PHY worker whenever a new SBSR CE + * needs to be included in a MAC PDU. + * + * @return SBSR CE for subPDU containing the buffer state of the highest priority LCG + */ srsran::mac_sch_subpdu_nr::lcg_bsr_t proc_bsr_nr::generate_sbsr() { + std::lock_guard lock(mutex); srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = {}; if (buffer_state.nof_lcgs_with_data > 0) { sbsr.lcg_id = buffer_state.last_non_zero_lcg; @@ -271,8 +222,8 @@ void proc_bsr_nr::new_grant_ul(uint32_t grant_size) sr->reset(); } -// This function is called by MUX only if Regular BSR has not been triggered before -bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes) +// This function is called by MUX only if no BSR has been triggered before +void proc_bsr_nr::set_padding_bytes(uint32_t nof_bytes) { std::lock_guard lock(mutex); @@ -281,16 +232,17 @@ bool proc_bsr_nr::generate_padding_bsr(uint32_t nof_padding_bytes) const uint32_t LBSR_CE_SUBHEADER_LEN = 1; // if the number of padding bits is equal to or larger than the size of the Short BSR plus its subheader but smaller // than the size of the Long BSR plus its subheader - if (nof_padding_bytes >= SBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(SHORT_BSR, true) && - nof_padding_bytes <= LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(LONG_BSR, true)) { - // generate padding BSR - set_trigger(PADDING); - // generate_bsr(bsr, nof_padding_bytes); - set_trigger(NONE); - return true; + if (nof_bytes >= (SBSR_CE_SUBHEADER_LEN + + srsran::mac_sch_subpdu_nr::sizeof_ce(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_BSR, true)) && + nof_bytes < (LBSR_CE_SUBHEADER_LEN + + srsran::mac_sch_subpdu_nr::sizeof_ce(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR, true))) { + // generate short padding BSR + mux->generate_bsr_mac_ce(SHORT_BSR); + } else if (nof_bytes >= (LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce( + srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR, true))) { + // report Long BSR if more than one LCG has data to send + mux->generate_bsr_mac_ce(LONG_BSR); } - - return false; } int proc_bsr_nr::setup_lcid(uint32_t lcid, uint32_t new_lcg, uint32_t priority) diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index 8aa2699d7..df06f07b8 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -53,7 +53,7 @@ void proc_ra_nr::init(phy_interface_mac_nr* phy_, srsran::ext_task_sched_handle* } /* Sets a new configuration. The configuration is applied by initialization() function */ -void proc_ra_nr::set_config(const srsran::rach_nr_cfg_t& rach_cfg_) +void proc_ra_nr::set_config(const srsran::rach_cfg_nr_t& rach_cfg_) { if (state != IDLE) { logger.warning("Wrong state for ra reponse reception %s (expected state %s)", @@ -63,7 +63,7 @@ void proc_ra_nr::set_config(const srsran::rach_nr_cfg_t& rach_cfg_) } rach_cfg = rach_cfg_; configured = true; - logger.info("Set RACH common config (Config Index %d, preambleTransMax %d, Repsonse Window %d)", + logger.info("Set RACH common config (Config Index %d, preambleTransMax %d, Response Window %d)", rach_cfg.prach_ConfigurationIndex, rach_cfg.preambleTransMax, rach_cfg.ra_responseWindow); @@ -95,6 +95,7 @@ void proc_ra_nr::start_by_mac() bool proc_ra_nr::is_rar_opportunity(uint32_t tti) { + std::lock_guard lock(mutex); // TODO replace second "&&"" by rar_timeout_timer.running if timer thread safe and delayed starting (tti+3) if (state == WAITING_FOR_RESPONSE_RECEPTION && ra_window_start > 0 && ra_window_length > 0 && mac_nr::is_in_window(tti, &ra_window_start, &ra_window_length)) { @@ -106,6 +107,7 @@ bool proc_ra_nr::is_rar_opportunity(uint32_t tti) uint16_t proc_ra_nr::get_rar_rnti() { + std::lock_guard lock(mutex); if (rar_rnti == SRSRAN_INVALID_RNTI || state != WAITING_FOR_RESPONSE_RECEPTION) { logger.error("Requested ra rnti is invalid. Anyway we return an invalid ra rnti"); return SRSRAN_INVALID_RNTI; @@ -115,27 +117,24 @@ uint16_t proc_ra_nr::get_rar_rnti() bool proc_ra_nr::has_rar_rnti() { + std::lock_guard lock(mutex); if (rar_rnti != SRSRAN_INVALID_RNTI) { return true; } return false; } -bool proc_ra_nr::has_temp_crnti() +void proc_ra_nr::received_contention_resolution(bool is_successful) { - return temp_crnti != SRSRAN_INVALID_RNTI; -} - -uint16_t proc_ra_nr::get_temp_crnti() -{ - return temp_crnti; + std::lock_guard lock(mutex); + ra_contention_resolution(is_successful); } void proc_ra_nr::timer_expired(uint32_t timer_id) { if (prach_send_timer.id() == timer_id) { logger.warning("PRACH Send timer expired. PRACH was not transmitted within %d ttis by phy. (TODO)", - prach_send_timer.duration()); + prach_send_timer.duration()); ra_error(); } else if (rar_timeout_timer.id() == timer_id) { logger.warning("RAR Timer expired. RA response not received within the response window"); @@ -156,10 +155,10 @@ void proc_ra_nr::ra_procedure_initialization() { mac.msg3_flush(); preamble_transmission_counter = 1; - preamble_power_ramping_step = rach_cfg.powerRampingStep; - scaling_factor_bi = 1; + preamble_power_ramping_step = rach_cfg.powerRampingStep; + scaling_factor_bi = 1; preamble_backoff = 0; - preambleTransMax = rach_cfg.preambleTransMax; + preambleTransMax = rach_cfg.preambleTransMax; ra_resource_selection(); } @@ -189,6 +188,7 @@ void proc_ra_nr::ra_preamble_transmission() // 5.1.4 Random Access Preamble transmission void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::tb_action_dl_result_t& tb) { + std::lock_guard lock(mutex); if (state != WAITING_FOR_RESPONSE_RECEPTION) { logger.warning( "Wrong state for ra reponse reception %s (expected state %s)", @@ -205,20 +205,26 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::tb_action_dl_ logger.warning("Error unpacking RAR PDU"); return; } - logger.info("%s", pdu.to_string()); + + fmt::memory_buffer buff; + pdu.to_string(buff); + logger.info("%s", srsran::to_c_str(buff)); for (auto& subpdu : pdu.get_subpdus()) { if (subpdu.has_rapid() && subpdu.get_rapid() == preamble_index) { logger.debug("PROC RA NR: Setting UL grant and prepare Msg3"); - temp_crnti = subpdu.get_temp_crnti(); + mac.set_temp_crnti(subpdu.get_temp_crnti()); // Set Temporary-C-RNTI if provided, otherwise C-RNTI is ok - phy->set_ul_grant(subpdu.get_ul_grant(), temp_crnti, srsran_rnti_type_ra); + phy->set_rar_grant(tb.rx_slot_idx, subpdu.get_ul_grant(), subpdu.get_temp_crnti(), srsran_rnti_type_ra); + + // Apply TA CMD + current_ta = subpdu.get_ta(); + phy->set_timeadv_rar(tb.rx_slot_idx, current_ta); // reset all parameters that are used before rar rar_rnti = SRSRAN_INVALID_RNTI; mac.msg3_prepare(); - current_ta = subpdu.get_ta(); // Set Backoff parameter if (subpdu.has_backoff()) { @@ -237,7 +243,7 @@ void proc_ra_nr::ra_response_reception(const mac_interface_phy_nr::tb_action_dl_ // TS 38.321 Section 5.1.5 2 ways to resolve contention resolution // if the C-RNTI MAC CE was included in Msg3: (only this one is implemented) -void proc_ra_nr::ra_contention_resolution() +void proc_ra_nr::ra_contention_resolution(bool received_con_res_matches_ue_id) { if (state != WAITING_FOR_CONTENTION_RESOLUTION) { logger.warning( @@ -246,8 +252,12 @@ void proc_ra_nr::ra_contention_resolution() srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_CONTENTION_RESOLUTION)); return; } - if (started_by == initiators_t::RRC || started_by == initiators_t::MAC) { - logger.info("PDCCH to C-RNTI received with a new UL grant of transmission"); + if (started_by == initiators_t::RRC || started_by == initiators_t::MAC || received_con_res_matches_ue_id) { + if (received_con_res_matches_ue_id) { + logger.info("Received CONRES ID matches transmitted UE ID"); + } else { + logger.info("PDCCH to C-RNTI received with a new UL grant of transmission"); + } contention_resolution_timer.stop(); state = WAITING_FOR_COMPLETION; ra_completion(); @@ -256,19 +266,6 @@ void proc_ra_nr::ra_contention_resolution() } } -// or else if the CCCH SDU was included in Msg3 and the PDCCH transmission is addressed to its TEMPORARY_C-RNTI: -void proc_ra_nr::ra_contention_resolution(uint64_t rx_contention_id) -{ - if (state != WAITING_FOR_CONTENTION_RESOLUTION) { - logger.warning( - "Wrong state for ra contention resolution by phy %s (expected state %s)", - srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, state), - srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_CONTENTION_RESOLUTION)); - return; - } - // TODO -} - void proc_ra_nr::ra_completion() { if (state != WAITING_FOR_COMPLETION) { @@ -277,38 +274,53 @@ void proc_ra_nr::ra_completion() srsran::enum_to_text(state_str_nr, (uint32_t)ra_state_t::MAX_RA_STATES, WAITING_FOR_COMPLETION)); return; } + + // Start looking for PDCCH CRNTI + if (!mac.get_crnti()) { + // promote temp RNTI to new C-RNTI + mac.set_crnti_to_temp(); + mac.set_temp_crnti(SRSRAN_INVALID_RNTI); + } + srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac.get_crnti(), current_ta); logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac.get_crnti(), current_ta); - temp_crnti = SRSRAN_INVALID_RNTI; + + mac.rrc_ra_completed(); reset(); } void proc_ra_nr::ra_error() { - temp_crnti = SRSRAN_INVALID_RNTI; + std::lock_guard lock(mutex); preamble_transmission_counter++; contention_resolution_timer.stop(); + mac.set_temp_crnti(SRSRAN_INVALID_RNTI); uint32_t backoff_wait; bool ra_procedure_completed = false; // true = (unsuccessfully) completed, false = uncompleted if (preamble_transmission_counter >= rach_cfg.preambleTransMax + 1) { logger.warning("Maximum number of transmissions reached (%d)", rach_cfg.preambleTransMax); // if the Random Access Preamble is transmitted on the SpCell assumption (TODO) - mac.rrc_ra_problem(); // indicate a Random Access problem to upper layers; + mac.rrc_ra_problem(); // indicate a Random Access problem to upper layers; if (started_by == initiators_t::MAC) { // if this Random Access procedure was triggered for SI request - ra_procedure_completed = true; // consider the Random Access procedure unsuccessfully completed. + ra_procedure_completed = true; // consider the Random Access procedure unsuccessfully completed. reset(); } } else { - // if the Random Access procedure is not completed + // try again, if RA failed if (preamble_backoff) { backoff_wait = rand() % preamble_backoff; } else { backoff_wait = 0; } - logger.warning("Backoff wait interval %d", backoff_wait); - backoff_timer.set(backoff_wait, [this](uint32_t tid) { timer_expired(tid); }); - backoff_timer.run(); + logger.debug("Backoff wait interval %d", backoff_wait); + + if (backoff_wait > 0) { + backoff_timer.set(backoff_wait, [this](uint32_t tid) { timer_expired(tid); }); + backoff_timer.run(); + } else { + timer_expired(backoff_timer.id()); + } } } @@ -364,7 +376,7 @@ void proc_ra_nr::handle_rar_pdu(mac_interface_phy_nr::tb_action_dl_result_t& res // Called from PHY thread, defer actions therefore. void proc_ra_nr::pdcch_to_crnti() { - task_queue.push([this]() { ra_contention_resolution(); }); + task_queue.push([this]() { ra_contention_resolution(false); }); } bool proc_ra_nr::is_contention_resolution() @@ -376,8 +388,10 @@ void proc_ra_nr::reset() { state = IDLE; started_by = initiators_t::initiators_t_NULLTYPE; + mac.set_temp_crnti(SRSRAN_INVALID_RNTI); prach_send_timer.stop(); rar_timeout_timer.stop(); contention_resolution_timer.stop(); } + } // namespace srsue diff --git a/srsue/src/stack/mac_nr/proc_sr_nr.cc b/srsue/src/stack/mac_nr/proc_sr_nr.cc index eddb150ac..182473ea9 100644 --- a/srsue/src/stack/mac_nr/proc_sr_nr.cc +++ b/srsue/src/stack/mac_nr/proc_sr_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -30,6 +30,7 @@ proc_sr_nr::proc_sr_nr(srslog::basic_logger& logger) : logger(logger) {} int32_t proc_sr_nr::init(mac_interface_sr_nr* mac_, phy_interface_mac_nr* phy_, rrc_interface_mac* rrc_) { + std::lock_guard lock(mutex); rrc = rrc_; mac = mac_; phy = phy_; @@ -41,14 +42,23 @@ int32_t proc_sr_nr::init(mac_interface_sr_nr* mac_, phy_interface_mac_nr* phy_, } void proc_sr_nr::reset() +{ + std::lock_guard lock(mutex); + reset_nolock(); +} + +void proc_sr_nr::reset_nolock() { is_pending_sr = false; } int32_t proc_sr_nr::set_config(const srsran::sr_cfg_nr_t& cfg_) { - // disable by default - cfg.enabled = false; + { + std::lock_guard lock(mutex); + // disable by default + cfg.enabled = false; + } if (cfg_.num_items != 1) { logger.error("Only one SR config supported. Disabling SR."); @@ -71,14 +81,18 @@ int32_t proc_sr_nr::set_config(const srsran::sr_cfg_nr_t& cfg_) logger.info("SR: Disabling procedure"); } - // store config - cfg = cfg_; + { + std::lock_guard lock(mutex); + // store config + cfg = cfg_; + } return SRSRAN_SUCCESS; } void proc_sr_nr::step(uint32_t tti) { + std::lock_guard lock(mutex); if (!initiated) { return; } @@ -93,7 +107,7 @@ void proc_sr_nr::step(uint32_t tti) // 2> initiate a Random Access procedure (see clause 5.1) on the SpCell and cancel the pending SR. logger.info("SR: PUCCH not configured. Starting RA procedure"); mac->start_ra(); - reset(); + reset_nolock(); return; } @@ -111,12 +125,13 @@ void proc_sr_nr::step(uint32_t tti) // ... TODO mac->start_ra(); - reset(); + reset_nolock(); } } bool proc_sr_nr::sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) { + std::lock_guard lock(mutex); // 2> when the MAC entity has an SR transmission occasion on the valid PUCCH resource for SR configured; and if (!initiated || !cfg.enabled || !is_pending_sr) { return false; @@ -156,6 +171,7 @@ bool proc_sr_nr::sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, boo void proc_sr_nr::start() { + std::lock_guard lock(mutex); if (initiated) { if (not is_pending_sr) { logger.info("SR: Starting procedure"); diff --git a/srsue/src/stack/mac_nr/test/CMakeLists.txt b/srsue/src/stack/mac_nr/test/CMakeLists.txt index 3c9d60b55..4c84aad4b 100644 --- a/srsue/src/stack/mac_nr/test/CMakeLists.txt +++ b/srsue/src/stack/mac_nr/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -19,11 +19,11 @@ # add_executable(proc_ra_nr_test proc_ra_nr_test.cc) -target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common) +target_link_libraries(proc_ra_nr_test srsue_mac_nr srsran_common ${ATOMIC_LIBS}) add_test(proc_ra_nr_test proc_ra_nr_test) add_executable(proc_bsr_nr_test proc_bsr_nr_test.cc) -target_link_libraries(proc_bsr_nr_test srsue_mac_nr srsran_common) +target_link_libraries(proc_bsr_nr_test srsue_mac_nr srsran_common ${ATOMIC_LIBS}) add_test(proc_bsr_nr_test proc_bsr_nr_test) add_executable(proc_sr_nr_test proc_sr_nr_test.cc) @@ -31,5 +31,5 @@ target_link_libraries(proc_sr_nr_test srsue_mac_nr srsran_common) add_test(proc_sr_nr_test proc_sr_nr_test) add_executable(mac_nr_test mac_nr_test.cc) -target_link_libraries(mac_nr_test srsue_mac_nr srsran_common) +target_link_libraries(mac_nr_test srsue_mac_nr srsran_common ${ATOMIC_LIBS}) add_test(mac_nr_test mac_nr_test) \ No newline at end of file diff --git a/srsue/src/stack/mac_nr/test/mac_nr_test.cc b/srsue/src/stack/mac_nr/test/mac_nr_test.cc index e15ff0d51..9365b1dc4 100644 --- a/srsue/src/stack/mac_nr/test/mac_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/mac_nr_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -44,8 +44,10 @@ public: preamble_index = preamble_index_; preamble_received_target_power = preamble_received_target_power_; } - int tx_request(const tx_request_t& request) override { return 0; } - int set_ul_grant(std::array, uint16_t rnti, srsran_rnti_type_t rnti_type) override + int set_rar_grant(uint32_t rar_slot_idx, + std::array, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override { return 0; } @@ -59,6 +61,9 @@ public: bool has_valid_sr_resource(uint32_t sr_id) override { return false; } void clear_pending_grants() override {} + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final{}; + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final{}; + private: uint32_t prach_occasion = 0; uint32_t preamble_index = 0; @@ -101,14 +106,18 @@ public: rlc_dummy() : received_bytes(0) {} bool has_data_locked(const uint32_t lcid) final { return ul_queues[lcid] > 0; } uint32_t get_buffer_state(const uint32_t lcid) final { return ul_queues[lcid]; } - int read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final + uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) final { if (!read_enable || nof_bytes < read_min) { return 0; } - if (read_len > 0 && read_len < (int32_t)nof_bytes) { - nof_bytes = read_len; + if (read_len.size() >= read_len_idx) { + int32_t tmp_read_len = read_len.at(read_len_idx); + if (read_len.at(read_len_idx) > 0 && tmp_read_len < (int32_t)nof_bytes) { + nof_bytes = tmp_read_len; + } + read_len_idx = (read_len_idx + 1) % read_len.size(); } uint32_t len = SRSRAN_MIN(ul_queues[lcid], nof_bytes); @@ -133,7 +142,7 @@ public: uint32_t get_received_pdus() { return received_pdus; } void disable_read() { read_enable = false; } - void set_read_len(uint32_t len) { read_len = len; } + void set_read_len(const std::vector& read_len_) { read_len = read_len_; } void set_read_min(uint32_t len) { read_min = len; } void reset_queues() { @@ -144,7 +153,8 @@ public: private: bool read_enable = true; - int32_t read_len = -1; // read all + std::vector read_len = {-1}; // read all + uint32_t read_len_idx = 0; uint32_t read_min = 0; // minimum "grant size" for read_pdu() to return data uint32_t received_bytes = 0; uint32_t received_pdus = 0; @@ -217,10 +227,12 @@ int mac_nr_ul_logical_channel_prioritization_test1() // PDU layout (20B in total) // - 2 B MAC subheader for SCH LCID=4 // - 10 B sduPDU + // - 1 B subheader SBRS (padding BSR) + // - 1 B SBSR (BSR will report bytes to transmit because BSR state isn't updated after packing PDU yet) // - 1 B subheader padding - // - 7 B padding + // - 5 B padding const uint8_t tv[] = {0x04, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + 0x04, 0x04, 0x3d, 0xc1, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00}; // dummy layers dummy_phy phy; @@ -429,6 +441,176 @@ int mac_nr_ul_logical_channel_prioritization_test2() return SRSRAN_SUCCESS; } +// Correct packing of MAC PDU with multiple subHeader with 16bit L field +int mac_nr_ul_logical_channel_prioritization_test3() +{ + // PDU layout (4737 B in total) + // TV skipped because it is very big + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + const uint16_t crnti = 0x1001; + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + mac.set_crnti(crnti); + + stack.init(&mac, &phy); + + // generate config (default DRB2 config for EN-DC) + std::vector lcids; + srsran::logical_channel_config_t config = {}; + config.lcid = 4; + config.lcg = 6; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 11; + lcids.push_back(config); + + // setup LCIDs in MAC + for (auto& channel : lcids) { + mac.setup_lcid(channel); + } + + // write muliple SDUs to DRB2 + const uint32_t sdu_len = 1502; + for (uint32_t i = 0; i < 10; ++i) { + rlc.write_sdu(4, sdu_len); + } + std::vector read_len; + read_len.push_back(80); + read_len.push_back(1480); + read_len.push_back(1480); + read_len.push_back(1480); + read_len.push_back(203); + rlc.set_read_len(read_len); + + // run TTI to setup Bj, BSR should be generated + stack.run_tti(0); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.tti = 0; + mac_grant.tbs = 4737; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(0); + mac.stop(); + + return SRSRAN_SUCCESS; +} + +// Correct packing of MAC PDU with two SDUs for different LCIDs +int mac_nr_ul_logical_channel_prioritization_test4() +{ + // PDU layout (24 B in total) + const uint8_t tv[] = {0x04, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x0a, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05}; + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + const uint16_t crnti = 0x1001; + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + mac.set_crnti(crnti); + + stack.init(&mac, &phy); + + // generate config for two DRBs + std::vector lcids; + srsran::logical_channel_config_t config = {}; + config.lcid = 4; + config.lcg = 6; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 11; + lcids.push_back(config); + + config.lcid = 5; + config.lcg = 7; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 12; + lcids.push_back(config); + + // setup LCIDs in MAC + for (auto& channel : lcids) { + mac.setup_lcid(channel); + } + + // write one SDU to each DRB + const uint32_t sdu_len = 10; + rlc.write_sdu(4, sdu_len); + rlc.write_sdu(5, sdu_len); + + // run TTI to setup Bj, BSR should be generated + stack.run_tti(0); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.tti = 0; + mac_grant.tbs = 24; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0); + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(0); + mac.stop(); + + return SRSRAN_SUCCESS; +} + // Basic test for periodic BSR transmission int mac_nr_ul_periodic_bsr_test() { @@ -714,8 +896,13 @@ int main() TESTASSERT(msg3_test() == SRSRAN_SUCCESS); TESTASSERT(mac_nr_ul_logical_channel_prioritization_test1() == SRSRAN_SUCCESS); TESTASSERT(mac_nr_ul_logical_channel_prioritization_test2() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_logical_channel_prioritization_test3() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_logical_channel_prioritization_test4() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_periodic_bsr_test() == SRSRAN_SUCCESS); TESTASSERT(mac_nr_dl_retx_test() == SRSRAN_SUCCESS); + srslog::flush(); + return SRSRAN_SUCCESS; } diff --git a/srsue/src/stack/mac_nr/test/proc_bsr_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_bsr_nr_test.cc index a27cc9a43..6a0b183e4 100644 --- a/srsue/src/stack/mac_nr/test/proc_bsr_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_bsr_nr_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc index c407f299b..b7a84391e 100644 --- a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,11 +38,6 @@ public: preamble_index = preamble_index_; preamble_received_target_power = preamble_received_target_power_; } - int tx_request(const tx_request_t& request) override { return 0; } - int set_ul_grant(std::array, uint16_t rnti, srsran_rnti_type_t rnti_type) override - { - return 0; - } void get_last_send_prach(uint32_t* prach_occasion_, uint32_t* preamble_index_, int* preamble_received_target_power_) { @@ -53,6 +48,16 @@ public: bool has_valid_sr_resource(uint32_t sr_id) override { return false; } void clear_pending_grants() override {} + int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override + { + return -1; + } + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final {} + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final {} + private: uint32_t prach_occasion = 0; uint32_t preamble_index = 0; @@ -70,6 +75,8 @@ public: crnti = c_rnti; return true; } + void set_temp_crnti(uint16_t c_rnti) {} + void set_crnti_to_temp() {} bool msg3_is_transmitted() { return true; } void msg3_flush() {} @@ -79,6 +86,7 @@ public: void msga_flush(){}; // RRC RA problem void rrc_ra_problem() { logger.warning("Dummy MAC RRC ra problem"); } + void rrc_ra_completed() { logger.info("Dummy MAC RRC ra completed"); } private: uint16_t crnti = SRSRAN_INVALID_RNTI; @@ -96,7 +104,7 @@ int proc_ra_normal_test() proc_ra_nr.init(&dummy_phy, &ext_task_sched_h); TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false); - srsran::rach_nr_cfg_t rach_cfg; + srsran::rach_cfg_nr_t rach_cfg; rach_cfg.powerRampingStep = 4; rach_cfg.prach_ConfigurationIndex = 16; rach_cfg.PreambleReceivedTargetPower = -110; @@ -133,10 +141,10 @@ int proc_ra_normal_test() mac_interface_phy_nr::mac_nr_grant_dl_t grant; grant.rnti = 0x16; grant.tti = rach_cfg.ra_responseWindow + tti_start + 3; - grant.pid = 0x0; + grant.pid = 0x0; uint8_t mac_dl_rar_pdu[] = {0x40, 0x06, 0x68, 0x03, 0x21, 0x46, 0x46, 0x02, 0x00, 0x00, 0x00}; - mac_interface_phy_nr::tb_action_dl_result_t result = {}; - result.payload = srsran::make_byte_buffer(); + mac_interface_phy_nr::tb_action_dl_result_t result = {}; + result.payload = srsran::make_byte_buffer(); TESTASSERT(result.payload != nullptr); result.payload.get()->append_bytes(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu)); proc_ra_nr.handle_rar_pdu(result); @@ -158,7 +166,7 @@ int proc_ra_timeout_test() proc_ra_nr.init(&dummy_phy, &ext_task_sched_h); TESTASSERT(proc_ra_nr.is_rar_opportunity(1) == false); - srsran::rach_nr_cfg_t rach_cfg; + srsran::rach_cfg_nr_t rach_cfg; rach_cfg.powerRampingStep = 4; rach_cfg.prach_ConfigurationIndex = 16; rach_cfg.PreambleReceivedTargetPower = -110; diff --git a/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc index 0d6aa6a5e..8127a6941 100644 --- a/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_sr_nr_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -39,8 +39,10 @@ public: preamble_index = preamble_index_; preamble_received_target_power = preamble_received_target_power_; } - int tx_request(const tx_request_t& request) override { return 0; } - int set_ul_grant(std::array, uint16_t rnti, srsran_rnti_type_t rnti_type) override + int set_rar_grant(uint32_t rar_slot_idx, + std::array, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override { return 0; } @@ -54,6 +56,9 @@ public: bool has_valid_sr_resource(uint32_t sr_id) override { return false; } void clear_pending_grants() override {} + void set_timeadv_rar(uint32_t tti, uint32_t ta_cmd) final{}; + void set_timeadv(uint32_t tti, uint32_t ta_cmd) final{}; + private: uint32_t prach_occasion = 0; uint32_t preamble_index = 0; diff --git a/srsue/src/stack/mac_nr/ul_harq_nr.cc b/srsue/src/stack/mac_nr/ul_harq_nr.cc index 52b640d9b..54e9eabda 100644 --- a/srsue/src/stack/mac_nr/ul_harq_nr.cc +++ b/srsue/src/stack/mac_nr/ul_harq_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -115,6 +115,7 @@ int ul_harq_entity_nr::get_current_tbs(uint32_t pid) ul_harq_entity_nr::ul_harq_metrics_t ul_harq_entity_nr::get_metrics() { + std::lock_guard lock(metrics_mutex); ul_harq_entity_nr::ul_harq_metrics_t tmp = metrics; metrics = {}; return tmp; @@ -152,11 +153,12 @@ void ul_harq_entity_nr::ul_harq_process_nr::reset() nof_retx = 0; harq_buffer = nullptr; current_grant = {}; + reset_ndi(); } void ul_harq_entity_nr::ul_harq_process_nr::reset_ndi() { - current_grant.ndi = false; + current_grant.ndi = NDI_NOT_SET; } uint8_t ul_harq_entity_nr::ul_harq_process_nr::get_ndi() @@ -213,7 +215,7 @@ void ul_harq_entity_nr::ul_harq_process_nr::new_grant_ul(const mac_interface_phy // retransmission if (harq_buffer == nullptr) { // ignore the UL grant - logger.info("UL %d: HARQ buffer empty. Ignoring grant."); + logger.info("UL %d: HARQ buffer empty. Ignoring grant.", pid); return; } @@ -234,11 +236,21 @@ int ul_harq_entity_nr::ul_harq_process_nr::get_current_tbs() return current_grant.tbs; } +void ul_harq_entity_nr::ul_harq_process_nr::save_grant(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant) +{ + current_grant = grant; + // When determining if NDI has been toggled compared to the value in the previous transmission the MAC entity shall + // ignore NDI received in all uplink grants on PDCCH for its Temporary C-RNTI. + if (grant.is_rar_grant || grant.rnti == harq_entity->mac->get_temp_crnti()) { + reset_ndi(); + } +} + // New transmission (Section 5.4.2.2) void ul_harq_entity_nr::ul_harq_process_nr::generate_new_tx(const mac_interface_phy_nr::mac_nr_grant_ul_t& grant, mac_interface_phy_nr::tb_action_ul_t* action) { - current_grant = grant; + save_grant(grant); nof_retx = 0; logger.info("UL %d: New TX%s, rv=%d, tbs=%d", @@ -249,6 +261,7 @@ void ul_harq_entity_nr::ul_harq_process_nr::generate_new_tx(const mac_interface_ generate_tx(action); + std::lock_guard lock(harq_entity->metrics_mutex); harq_entity->metrics.tx_ok++; } @@ -259,11 +272,12 @@ void ul_harq_entity_nr::ul_harq_process_nr::generate_retx(const mac_interface_ph logger.info("UL %d: Retx=%d, rv=%d, tbs=%d", pid, nof_retx, grant.rv, grant.tbs); // overwrite original grant - current_grant = grant; + save_grant(grant); generate_tx(action); // increment Tx error count + std::lock_guard lock(harq_entity->metrics_mutex); harq_entity->metrics.tx_ko++; } @@ -271,7 +285,11 @@ void ul_harq_entity_nr::ul_harq_process_nr::generate_retx(const mac_interface_ph void ul_harq_entity_nr::ul_harq_process_nr::generate_tx(mac_interface_phy_nr::tb_action_ul_t* action) { nof_retx++; - harq_entity->metrics.tx_brate += current_grant.tbs * 8; + + { + std::lock_guard lock(harq_entity->metrics_mutex); + harq_entity->metrics.tx_brate += current_grant.tbs * 8; + } action->tb.rv = current_grant.rv; action->tb.enabled = true; diff --git a/srsue/src/stack/rrc/CMakeLists.txt b/srsue/src/stack/rrc/CMakeLists.txt index 19210a4e0..de8541fb6 100644 --- a/srsue/src/stack/rrc/CMakeLists.txt +++ b/srsue/src/stack/rrc/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,9 +18,7 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc rrc_cell.cc phy_controller.cc) -add_library(srsue_rrc STATIC ${SOURCES}) - -set(SOURCES rrc_nr.cc) -add_library(srsue_rrc_nr STATIC ${SOURCES}) +add_subdirectory(test) +set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc rrc_cell.cc rrc_rlf_report.cc phy_controller.cc) +add_library(srsue_rrc STATIC ${SOURCES}) \ No newline at end of file diff --git a/srsue/src/stack/rrc/phy_controller.cc b/srsue/src/stack/rrc/phy_controller.cc index 42ce10f8a..2cbd3f336 100644 --- a/srsue/src/stack/rrc/phy_controller.cc +++ b/srsue/src/stack/rrc/phy_controller.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -175,13 +175,13 @@ void phy_controller::selecting_cell::wait_in_sync::enter(selecting_cell* f) *************************************/ //! Searches for a cell in the current frequency and retrieves SIB1 if not retrieved yet -bool phy_controller::start_cell_search(srsran::event_observer observer) +bool phy_controller::start_cell_search(srsran::event_observer observer, int earfcn) { if (is_in_state()) { fsmInfo("Cell search already launched."); return true; } - trigger(cell_search_cmd{}); + trigger(cell_search_cmd{earfcn}); if (not is_in_state()) { fsmWarning("Failed to launch cell search"); return false; @@ -195,10 +195,10 @@ void phy_controller::cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t trigger(cell_srch_res{cs_ret, found_cell}); } -void phy_controller::searching_cell::enter(phy_controller* f) +void phy_controller::searching_cell::enter(phy_controller* f, const cell_search_cmd& ev) { otherfsmInfo(f, "Initiating Cell search"); - f->phy->cell_search(); + f->phy->cell_search(ev.earfcn); } void phy_controller::handle_cell_search_res(searching_cell& s, const cell_srch_res& result) diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 8ad751683..dc1495f3f 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -41,7 +41,7 @@ #include #include -bool simulate_rlf = false; +std::atomic simulate_rlf{false}; using namespace srsran; using namespace asn1::rrc; @@ -62,7 +62,6 @@ rrc::rrc(stack_interface_rrc* stack_, srsran::task_sched_handle task_sched_) : task_sched(task_sched_), state(RRC_STATE_IDLE), last_state(RRC_STATE_CONNECTED), - drb_up(false), logger(srslog::fetch_basic_logger("RRC")), measurements(new rrc_meas()), cell_searcher(this), @@ -75,6 +74,7 @@ rrc::rrc(stack_interface_rrc* stack_, srsran::task_sched_handle task_sched_) : plmn_searcher(this), cell_reselector(this), connection_reest(this), + conn_setup_proc(this), ho_handler(this), conn_recfg_proc(this), meas_cells_nr(task_sched_), @@ -148,6 +148,8 @@ void rrc::init(phy_interface_rrc_lte* phy_, t311 = task_sched.get_unique_timer(); t304 = task_sched.get_unique_timer(); + var_rlf_report.init(task_sched); + transaction_id = 0; cell_clean_cnt = 0; @@ -199,11 +201,6 @@ bool rrc::is_connected() return (RRC_STATE_CONNECTED == state); } -bool rrc::have_drb() -{ - return drb_up; -} - /* * * RRC State Machine @@ -215,9 +212,9 @@ void rrc::run_tti() return; } - if (simulate_rlf) { + if (simulate_rlf.load(std::memory_order_relaxed)) { radio_link_failure_process(); - simulate_rlf = false; + simulate_rlf.store(false, std::memory_order_relaxed); } // Process pending PHY measurements in IDLE/CONNECTED @@ -373,6 +370,9 @@ void rrc::set_config_complete(bool status) { // Signal Reconfiguration Procedure that PHY configuration has completed phy_ctrl->set_config_complete(); + if (conn_setup_proc.is_busy()) { + conn_setup_proc.trigger(status); + } if (conn_recfg_proc.is_busy()) { conn_recfg_proc.trigger(status); } @@ -466,7 +466,8 @@ void rrc::process_new_cell_meas(const std::vector& meas) bool neighbour_added = meas_cells.process_new_cell_meas(meas, filter); // Instruct measurements subclass to update phy with new cells to measure based on strongest neighbours - if (state == RRC_STATE_CONNECTED && neighbour_added) { + // Avoid updating PHY while HO procedure is busy + if (state == RRC_STATE_CONNECTED && neighbour_added && !ho_handler.is_busy()) { measurements->update_phy(); } } @@ -685,6 +686,9 @@ void rrc::radio_link_failure_process() // TODO: Generate and store failure report srsran::console("Warning: Detected Radio-Link Failure\n"); + // Store the information in VarRLF-Report + var_rlf_report.set_failure(meas_cells, rrc_rlf_report::rlf); + if (state == RRC_STATE_CONNECTED) { if (security_is_activated) { logger.info("Detected Radio-Link Failure with active AS security. Starting ConnectionReestablishment..."); @@ -724,6 +728,11 @@ void rrc::max_retx_attempted() radio_link_failure_push_cmd(); } +void rrc::protocol_failure() +{ + logger.warning("RLC protocol failure detected"); +} + void rrc::timer_expired(uint32_t timeout_id) { if (timeout_id == t310.id()) { @@ -780,13 +789,10 @@ bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg, bool* return true; } - bool endc_release_and_add_r15 = false; - bool nr_secondary_cell_group_cfg_r15_present = false; - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15; - bool sk_counter_r15_present = false; - uint32_t sk_counter_r15 = 0; - bool nr_radio_bearer_cfg1_r15_present = false; - asn1::dyn_octstring nr_radio_bearer_cfg1_r15; + bool endc_release_and_add_r15 = false; + + asn1::rrc_nr::rrc_recfg_s rrc_nr_reconf = {}; + rrc_nr_reconf.crit_exts.set_rrc_recfg(); switch (rrc_conn_recfg_v1510_ies->nr_cfg_r15.type()) { case setup_opts::options::release: @@ -795,8 +801,28 @@ bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg, bool* case setup_opts::options::setup: endc_release_and_add_r15 = rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().endc_release_and_add_r15; if (rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present) { - nr_secondary_cell_group_cfg_r15_present = true; - nr_secondary_cell_group_cfg_r15 = rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15; + asn1::cbit_ref bref0(rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.data(), + rrc_conn_recfg_v1510_ies->nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.size()); + + asn1::rrc_nr::rrc_recfg_s secondary_cell_group_r15; + if (secondary_cell_group_r15.unpack(bref0) != SRSASN_SUCCESS) { + logger.error("Could not unpack secondary cell group r15."); + return false; + } + + if (secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.size() > 0) { + asn1::cbit_ref bref1(secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.data(), + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.size()); + + asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; + if (cell_group_cfg.unpack(bref1) != SRSASN_SUCCESS) { + logger.error("Could not unpack secondary cell group config."); + return false; + } + + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group = + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group; + } } break; default: @@ -804,22 +830,29 @@ bool rrc::nr_reconfiguration_proc(const rrc_conn_recfg_r8_ies_s& rx_recfg, bool* break; } if (rrc_conn_recfg_v1510_ies->sk_counter_r15_present) { - sk_counter_r15_present = true; - sk_counter_r15 = rrc_conn_recfg_v1510_ies->sk_counter_r15; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter = + rrc_conn_recfg_v1510_ies->sk_counter_r15; } if (rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15_present) { - nr_radio_bearer_cfg1_r15_present = true; - nr_radio_bearer_cfg1_r15 = rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15; + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present = true; + asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_conf = {}; + asn1::cbit_ref bref(rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15.data(), + rrc_conn_recfg_v1510_ies->nr_radio_bearer_cfg1_r15.size()); + if (radio_bearer_conf.unpack(bref) != SRSASN_SUCCESS) { + logger.error("Could not unpack radio bearer config."); + return false; + } + + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg = radio_bearer_conf; } *has_5g_nr_reconfig = true; - return rrc_nr->rrc_reconfiguration(endc_release_and_add_r15, - nr_secondary_cell_group_cfg_r15_present, - nr_secondary_cell_group_cfg_r15, - sk_counter_r15_present, - sk_counter_r15, - nr_radio_bearer_cfg1_r15_present, - nr_radio_bearer_cfg1_r15); + + return rrc_nr->rrc_reconfiguration(endc_release_and_add_r15, rrc_nr_reconf); } /******************************************************************************* * @@ -863,6 +896,9 @@ void rrc::send_con_restablish_request(reest_cause_e cause, uint16_t crnti, uint1 // Clean reestablishment type reestablishment_successful = false; + // set the reestablishmentCellId in the VarRLF-Report to the global cell identity of the selected cell; + var_rlf_report.set_reest_gci(meas_cells.serving_cell().get_cell_id_bit(), meas_cells.serving_cell().get_plmn_asn1(0)); + if (cause.value != reest_cause_opts::ho_fail) { if (cause.value != reest_cause_opts::other_fail) { pci = meas_cells.serving_cell().get_pci(); @@ -951,6 +987,15 @@ void rrc::send_con_restablish_complete() ul_dcch_msg.msg.set_c1().set_rrc_conn_reest_complete().crit_exts.set_rrc_conn_reest_complete_r8(); ul_dcch_msg.msg.c1().rrc_conn_reest_complete().rrc_transaction_id = transaction_id; + // Include rlf-InfoAvailable + if (var_rlf_report.has_info()) { + ul_dcch_msg.msg.c1().rrc_conn_reest_complete().crit_exts.rrc_conn_reest_complete_r8().non_crit_ext_present = true; + ul_dcch_msg.msg.c1() + .rrc_conn_reest_complete() + .crit_exts.rrc_conn_reest_complete_r8() + .non_crit_ext.rlf_info_available_r9_present = true; + } + send_ul_dcch_msg(srb_to_lcid(lte_srb::srb1), ul_dcch_msg); reestablishment_successful = true; @@ -967,6 +1012,13 @@ void rrc::send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg) ul_dcch_msg.msg.c1().rrc_conn_setup_complete().rrc_transaction_id = transaction_id; + // Include rlf-InfoAvailable + if (var_rlf_report.has_info()) { + rrc_conn_setup_complete->non_crit_ext_present = true; + rrc_conn_setup_complete->non_crit_ext.non_crit_ext_present = true; + rrc_conn_setup_complete->non_crit_ext.non_crit_ext.rlf_info_available_r10_present = true; + } + rrc_conn_setup_complete->sel_plmn_id = 1; rrc_conn_setup_complete->ded_info_nas.resize(nas_msg->N_bytes); memcpy(rrc_conn_setup_complete->ded_info_nas.data(), nas_msg->msg, nas_msg->N_bytes); // TODO Check! @@ -1011,6 +1063,13 @@ void rrc::send_rrc_con_reconfig_complete(bool contains_nr_complete) &ul_dcch_msg.msg.set_c1().set_rrc_conn_recfg_complete().crit_exts.set_rrc_conn_recfg_complete_r8(); ul_dcch_msg.msg.c1().rrc_conn_recfg_complete().rrc_transaction_id = transaction_id; + // Include rlf-InfoAvailable + if (var_rlf_report.has_info()) { + rrc_conn_recfg_complete_r8->non_crit_ext_present = true; + rrc_conn_recfg_complete_r8->non_crit_ext.non_crit_ext_present = true; + rrc_conn_recfg_complete_r8->non_crit_ext.non_crit_ext.rlf_info_available_r10_present = true; + } + if (contains_nr_complete == true) { logger.debug("Preparing RRC Connection Reconfig Complete with NR Complete"); @@ -1061,6 +1120,10 @@ void rrc::start_go_idle() void rrc::ho_failed() { ho_handler.trigger(ho_proc::t304_expiry{}); + + // Store the information in VarRLF-Report + var_rlf_report.set_failure(meas_cells, rrc_rlf_report::hof); + start_con_restablishment(reest_cause_e::ho_fail); } @@ -1100,11 +1163,48 @@ void rrc::handle_rrc_con_reconfig(uint32_t lcid, const rrc_conn_recfg_s& reconfi } /* Actions upon reception of RRCConnectionRelease 5.3.8.3 */ -void rrc::rrc_connection_release(const std::string& cause) +void rrc::handle_rrc_connection_release(const asn1::rrc::rrc_conn_release_s& release) { + std::string cause = release.crit_exts.c1().rrc_conn_release_r8().release_cause.to_string(); // Save idleModeMobilityControlInfo, etc. srsran::console("Received RRC Connection Release (releaseCause: %s)\n", cause.c_str()); - start_go_idle(); + + if (has_nr_dc()) { + rrc_nr->rrc_release(); + } + + // delay actions by 60ms as per 5.3.8.3 + task_sched.defer_callback(60, [this]() { start_go_idle(); }); + + uint32_t earfcn = 0; + if (release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info_present) { + switch (release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type()) { + case asn1::rrc::redirected_carrier_info_c::types_opts::options::eutra: + earfcn = release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.eutra(); + break; + default: + srsran::console("Ignoring RedirectedCarrierInfo with unsupported type (%s)\n", + release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string()); + break; + } + if (earfcn != 0) { + srsran::console("RedirectedCarrierInfo present (type %s, earfcn: %d) - Redirecting\n", + release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string(), + earfcn); + logger.info("RedirectedCarrierInfo present (type %s, earfcn: %d) - Redirecting", + release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string(), + earfcn); + + // delay actions by 60ms as per 5.3.8.3 + task_sched.defer_callback(60, [this, earfcn]() { start_rrc_redirect(earfcn); }); + } + } +} + +void rrc::start_rrc_redirect(uint32_t new_dl_earfcn) +{ + cell_search_earfcn = (int)new_dl_earfcn; + plmn_search(); } /// TS 36.331, 5.3.12 - UE actions upon leaving RRC_CONNECTED @@ -1113,7 +1213,6 @@ void rrc::leave_connected() srsran::console("RRC IDLE\n"); logger.info("Leaving RRC_CONNECTED state"); state = RRC_STATE_IDLE; - drb_up = false; security_is_activated = false; // 1> reset MAC; @@ -1127,6 +1226,7 @@ void rrc::leave_connected() rlc->reset(); pdcp->reset(); set_mac_default(); + stack->reset_eps_bearers(); // 1> indicate the release of the RRC connection to upper layers together with the release cause; nas->left_rrc_connected(); @@ -1620,6 +1720,11 @@ void rrc::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) process_pdu(lcid, std::move(pdu)); } +void rrc::notify_pdcp_integrity_error(uint32_t lcid) +{ + logger.warning("Received integrity protection failure indication, lcid=%u", lcid); +} + void rrc::process_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) { logger.debug("RX PDU, LCID: %d", lcid); @@ -1754,7 +1859,11 @@ void rrc::parse_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu) handle_ue_capability_enquiry(c1->ue_cap_enquiry()); break; case dl_dcch_msg_type_c::c1_c_::types::rrc_conn_release: - rrc_connection_release(c1->rrc_conn_release().crit_exts.c1().rrc_conn_release_r8().release_cause.to_string()); + handle_rrc_connection_release(c1->rrc_conn_release()); + break; + case dl_dcch_msg_type_c::c1_c_::types::ue_info_request_r9: + transaction_id = c1->ue_info_request_r9().rrc_transaction_id; + handle_ue_info_request(c1->ue_info_request_r9()); break; default: logger.error("The provided DL-CCCH message type is not recognized or supported"); @@ -1904,7 +2013,7 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) ca_mimo_params_ul.supported_mimo_cap_ul_r10_present = false; band_params_r10_s band_params; - band_params.band_eutra_r10 = args.supported_bands[i]; + band_params.band_eutra_r10 = args.supported_bands[k]; band_params.band_params_dl_r10_present = true; band_params.band_params_dl_r10.push_back(ca_mimo_params_dl); band_params.band_params_ul_r10_present = true; @@ -2070,24 +2179,34 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) irat_params_nr_r15.en_dc_r15_present = true; irat_params_nr_r15.supported_band_list_en_dc_r15_present = true; - supported_band_nr_r15_s supported_band_nr_r15; - supported_band_nr_r15.band_nr_r15 = 78; + uint32_t nof_supported_nr_bands = args.supported_bands_nr.size(); + irat_params_nr_r15.supported_band_list_en_dc_r15.resize(nof_supported_nr_bands); + for (uint32_t k = 0; k < nof_supported_nr_bands; k++) { + irat_params_nr_r15.supported_band_list_en_dc_r15[k].band_nr_r15 = args.supported_bands_nr[k]; + } - irat_params_nr_r15.supported_band_list_en_dc_r15.push_back(supported_band_nr_r15); ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext.irat_params_nr_r15_present = true; ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext.irat_params_nr_r15 = irat_params_nr_r15; + ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext.non_crit_ext_present = true; + + // 15.10 + ue_eutra_cap_v1510_ies_s* ue_cap_enquiry_v1510_ies = &ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext; + ue_cap_enquiry_v1510_ies->pdcp_params_nr_r15_present = true; + ue_cap_enquiry_v1510_ies->pdcp_params_nr_r15.sn_size_lo_r15_present = true; } // Pack caps and copy to cap info uint8_t buf[64] = {}; asn1::bit_ref bref(buf, sizeof(buf)); - cap.pack(bref); + if (cap.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Error packing EUTRA capabilities"); + return; + } bref.align_bytes_zero(); auto cap_len = (uint32_t)bref.distance_bytes(buf); info->ue_cap_rat_container_list[rat_idx].ue_cap_rat_container.resize(cap_len); memcpy(info->ue_cap_rat_container_list[rat_idx].ue_cap_rat_container.data(), buf, cap_len); rat_idx++; - } else if (enquiry.crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[i] == rat_type_e::eutra_nr && has_nr_dc()) { info->ue_cap_rat_container_list[rat_idx] = get_eutra_nr_capabilities(); logger.info("Including EUTRA-NR capabilities in UE Capability Info (%d B)", @@ -2127,6 +2246,42 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) send_ul_dcch_msg(srb_to_lcid(lte_srb::srb1), ul_dcch_msg); } +/******************************************************************************* + * + * + * + * UEInformationRequest message + * + * + * + *******************************************************************************/ +void rrc::handle_ue_info_request(const ue_info_request_r9_s& request) +{ + logger.debug("Preparing UEInformationResponse message"); + + ul_dcch_msg_s ul_dcch_msg; + ue_info_resp_r9_ies_s* resp = + &ul_dcch_msg.msg.set_c1().set_ue_info_resp_r9().crit_exts.set_c1().set_ue_info_resp_r9(); + ul_dcch_msg.msg.c1().ue_info_resp_r9().rrc_transaction_id = transaction_id; + + // if rach-ReportReq is set to true, set the contents of the rach-Report in the UEInformationResponse message as + // follows + if (request.crit_exts.c1().ue_info_request_r9().rach_report_req_r9) { + // todo... + } + + // Include rlf-Report if rlf-ReportReq is set to true + if (request.crit_exts.c1().ue_info_request_r9().rlf_report_req_r9 && var_rlf_report.has_info()) { + resp->rlf_report_r9_present = true; + resp->rlf_report_r9 = var_rlf_report.get_report(); + + // fixme: should be cleared upon successful delivery + var_rlf_report.clear(); + } + + send_ul_dcch_msg(srb_to_lcid(lte_srb::srb1), ul_dcch_msg); +} + /******************************************************************************* * * @@ -2337,6 +2492,8 @@ void rrc::apply_phy_scell_config(const scell_to_add_mod_r10_s& scell_config, boo logger.error("Adding SCell cc_idx=%d", scell_config.scell_idx_r10); } else if (!phy_ctrl->set_cell_config(scell_cfg, scell_config.scell_idx_r10)) { logger.error("Setting SCell configuration for cc_idx=%d", scell_config.scell_idx_r10); + } else { + meas_cells.set_scell_cc_idx(scell_config.scell_idx_r10, earfcn, scell.id); } } @@ -2539,16 +2696,12 @@ void rrc::handle_con_setup(const rrc_conn_setup_s& setup) t302.stop(); srsran::console("RRC Connected\n"); - // Apply the Radio Resource configuration - apply_rr_config_dedicated(&setup.crit_exts.c1().rrc_conn_setup_r8().rr_cfg_ded); - - nas->set_barring(srsran::barring_t::none); - - if (dedicated_info_nas.get()) { - send_con_setup_complete(std::move(dedicated_info_nas)); - } else { - logger.error("Pending to transmit a ConnectionSetupComplete but no dedicatedInfoNAS was in queue"); + // defer transmission of Setup Complete until PHY reconfiguration has been completed + if (not conn_setup_proc.launch(&setup.crit_exts.c1().rrc_conn_setup_r8().rr_cfg_ded, std::move(dedicated_info_nas))) { + logger.error("Failed to initiate connection setup procedure"); + return; } + callback_list.add_proc(conn_setup_proc); } /* Reception of RRCConnectionReestablishment by the UE 5.3.7.5 */ @@ -2660,25 +2813,57 @@ void rrc::add_drb(const drb_to_add_mod_s& drb_cnfg) } mac->setup_lcid(lcid, log_chan_group, priority, prioritized_bit_rate, bucket_size_duration); - drbs[drb_cnfg.drb_id] = drb_cnfg; - drb_up = true; - logger.info("Added DRB Id %d (LCID=%d)", drb_cnfg.drb_id, lcid); - // Update LCID if gw is running - if (gw->is_running()) { - gw->update_lcid(drb_cnfg.eps_bearer_id, lcid); + uint8_t eps_bearer_id = 5; // default? + if (drb_cnfg.eps_bearer_id_present) { + eps_bearer_id = drb_cnfg.eps_bearer_id; } + + // register EPS bearer over LTE PDCP + stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::lte, lcid); + + drbs[drb_cnfg.drb_id] = drb_cnfg; + logger.info("Added DRB Id %d (LCID=%d)", drb_cnfg.drb_id, lcid); } void rrc::release_drb(uint32_t drb_id) { if (drbs.find(drb_id) != drbs.end()) { logger.info("Releasing DRB Id %d", drb_id); + + // remvove RLC and PDCP for this LCID + uint32_t lcid = get_lcid_for_drb_id(drb_id); + rlc->del_bearer(lcid); + pdcp->del_bearer(lcid); + // TODO: implement bearer removal at MAC + + // remove EPS bearer associated with this DRB from Stack (GW will trigger service request if needed) + stack->remove_eps_bearer(get_eps_bearer_id_for_drb_id(drb_id)); drbs.erase(drb_id); } else { logger.error("Couldn't release DRB Id %d. Doesn't exist.", drb_id); } } +/** + * @brief check if this DRB id exists and return it's LCID + * + * if the DRB couldn't be found, 0 is returned. This is an invalid + * LCID for DRB and the caller should handle it. + */ +uint32_t rrc::get_lcid_for_drb_id(const uint32_t& drb_id) +{ + uint32_t lcid = 0; + if (drbs.find(drb_id) != drbs.end()) { + asn1::rrc::drb_to_add_mod_s drb_cnfg = drbs[drb_id]; + if (drb_cnfg.lc_ch_id_present) { + lcid = drb_cnfg.lc_ch_id; + } else { + lcid = srsran::MAX_LTE_SRB_ID + drb_cnfg.drb_id; + } + } + return lcid; +} + uint32_t rrc::get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) { // check if this bearer id exists and return it's LCID @@ -2695,6 +2880,17 @@ uint32_t rrc::get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) return lcid; } +uint32_t rrc::get_eps_bearer_id_for_drb_id(const uint32_t& drb_id) +{ + // check if this bearer id exists and return it's LCID + for (auto& drb : drbs) { + if (drb.first == drb_id) { + return drb.second.eps_bearer_id; + } + } + return 0; +} + uint32_t rrc::get_drb_id_for_eps_bearer(const uint32_t& eps_bearer_id) { // check if this bearer id exists and return it's LCID @@ -2708,10 +2904,7 @@ uint32_t rrc::get_drb_id_for_eps_bearer(const uint32_t& eps_bearer_id) bool rrc::has_nr_dc() { - bool has_nr_dc = false; - if (args.release >= 15) - has_nr_dc = true; - return has_nr_dc; + return (args.release >= 15); } void rrc::add_mrb(uint32_t lcid, uint32_t port) diff --git a/srsue/src/stack/rrc/rrc_cell.cc b/srsue/src/stack/rrc/rrc_cell.cc index 66e5ffbdb..723f021bd 100644 --- a/srsue/src/stack/rrc/rrc_cell.cc +++ b/srsue/src/stack/rrc/rrc_cell.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -42,6 +42,15 @@ srsran::plmn_id_t meas_cell_eutra::get_plmn(uint32_t idx) const } } +asn1::rrc::plmn_id_s meas_cell_eutra::get_plmn_asn1(uint32_t idx) const +{ + if (idx < sib1.cell_access_related_info.plmn_id_list.size() && has_valid_sib1) { + return sib1.cell_access_related_info.plmn_id_list[idx].plmn_id; + } else { + return {}; + } +} + void meas_cell_eutra::set_sib1(const asn1::rrc::sib_type1_s& sib1_) { sib1 = sib1_; @@ -71,6 +80,12 @@ void meas_cell_eutra::set_sib13(const asn1::rrc::sib_type13_r9_s& sib13_) has_valid_sib13 = true; } +void meas_cell_nr::set_sib1(const asn1::rrc_nr::sib1_s& sib1_) +{ + sib1 = sib1_; + has_valid_sib1 = true; +} + bool meas_cell::is_sib_scheduled(uint32_t sib_index) const { return sib_info_map.find(sib_index) != sib_info_map.end(); @@ -171,6 +186,41 @@ uint16_t meas_cell_eutra::get_mnc() const return 0; } +uint16_t meas_cell_nr::get_mcc() const +{ + uint16_t mcc = 0; + if (has_valid_sib1) { + if (sib1.cell_access_related_info.plmn_id_list.size() > 0) { + // PLMN ID list is nested twice + if (sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list.size() > 0) { + if (sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list[0].mcc_present) { + if (srsran::bytes_to_mcc(&sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list[0].mcc[0], &mcc)) { + // successfully read MCC + } + } + } + } + } + return mcc; +} + +uint16_t meas_cell_nr::get_mnc() const +{ + uint16_t mnc = 0; + if (has_valid_sib1) { + if (sib1.cell_access_related_info.plmn_id_list.size() > 0) { + if (sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list.size() > 0) { + if (srsran::bytes_to_mnc(&sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list[0].mnc[0], + &mnc, + sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list[0].mnc.size())) { + // successfully read MNC + } + } + } + } + return mnc; +} + /********************************************* * Neighbour Cell List ********************************************/ @@ -393,6 +443,25 @@ int meas_cell_list::set_serving_cell(phy_cell_t phy_cell, bool discard_servin return SRSRAN_SUCCESS; } +template +void meas_cell_list::set_scell_cc_idx(uint32_t cc_idx, uint32_t earfcn, uint32_t pci) +{ + current_cell_pci_earfcn[cc_idx].first = earfcn; + current_cell_pci_earfcn[cc_idx].second = pci; +} + +template +bool meas_cell_list::get_scell_cc_idx(uint32_t earfcn, uint32_t& pci) +{ + for (auto& cell : current_cell_pci_earfcn) { + if (cell.first == earfcn) { + pci = cell.second; + return true; + } + } + return false; +} + template bool meas_cell_list::process_new_cell_meas(const std::vector& meas, const std::function& filter_meas) diff --git a/srsue/src/stack/rrc/rrc_meas.cc b/srsue/src/stack/rrc/rrc_meas.cc index 5dbdf21da..16c089dbf 100644 --- a/srsue/src/stack/rrc/rrc_meas.cc +++ b/srsue/src/stack/rrc/rrc_meas.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,9 +20,9 @@ */ #include "srsue/hdr/stack/rrc/rrc_meas.h" +#include "srsran/asn1/obj_id_cmp_utils.h" #include "srsran/asn1/rrc/dl_dcch_msg.h" #include "srsran/interfaces/ue_phy_interfaces.h" -#include "srsran/rrc/rrc_cfg_utils.h" #include "srsue/hdr/stack/rrc/rrc.h" /************************************************************************ @@ -127,7 +127,6 @@ bool rrc::rrc_meas::parse_meas_config(const rrc_conn_recfg_r8_ies_s* mob_reconf_ void rrc::rrc_meas::ho_reest_actions(const uint32_t src_earfcn, const uint32_t dst_earfcn) { meas_cfg.ho_reest_finish(src_earfcn, dst_earfcn); - update_phy(); } void rrc::rrc_meas::run_tti() @@ -139,50 +138,6 @@ void rrc::rrc_meas::run_tti() meas_cfg.report_triggers(); } -uint8_t rrc::rrc_meas::value_to_range(const report_cfg_eutra_s::trigger_quant_opts::options quant, const float value) -{ - uint8_t range = 0; - switch (quant) { - case report_cfg_eutra_s::trigger_quant_opts::rsrp: - if (value < -140) { - range = 0; - } else if (value < -44) { - range = 1u + (uint8_t)(value + 140); - } else { - range = 97; - } - break; - case report_cfg_eutra_s::trigger_quant_opts::rsrq: - if (value < -19.5) { - range = 0; - } else if (value < -3) { - range = 1u + (uint8_t)(2 * (value + 19.5)); - } else { - range = 34; - } - break; - default: - break; - } - return range; -} - -float rrc::rrc_meas::range_to_value(const report_cfg_eutra_s::trigger_quant_opts::options quant, const uint8_t range) -{ - float val = 0; - switch (quant) { - case report_cfg_eutra_s::trigger_quant_opts::rsrp: - val = -140 + (float)range; - break; - case report_cfg_eutra_s::trigger_quant_opts::rsrq: - val = -19.5f + (float)range / 2; - break; - default: - break; - } - return val; -} - // For thresholds, the actual value is (field value – 156) dBm, except for field value 127, in which case the actual // value is infinity. float rrc::rrc_meas::range_to_value_nr(const asn1::rrc::thres_nr_r15_c::types_opts::options type, const uint8_t range) @@ -277,12 +232,10 @@ void rrc::rrc_meas::var_meas_report_list::generate_report_eutra(meas_results_s* for (auto& cell : var_meas.cell_triggered_list) { // report neighbour cells only if (cell.pci == serv_cell->get_pci() && cell.earfcn == serv_cell->get_earfcn()) { - logger.info("MEAS: skipping serving cell in report neighbour=%d, pci=%d, earfcn=%d, rsrp=%+.1f, rsrq=%+.1f", + logger.info("MEAS: skipping serving cell in report neighbour=%d, pci=%d, earfcn=%d", neigh_list.size(), cell.pci, - var_meas.carrier_freq, - rrc_ptr->get_cell_rsrp(var_meas.carrier_freq, cell.pci), - rrc_ptr->get_cell_rsrq(var_meas.carrier_freq, cell.pci)); + var_meas.carrier_freq); continue; } if (neigh_list.size() <= var_meas.report_cfg_eutra.max_report_cells) { @@ -312,8 +265,8 @@ void rrc::rrc_meas::var_meas_report_list::generate_report_eutra(meas_results_s* break; } rc.pci = (uint16_t)cell.pci; - rc.meas_result.rsrp_result = value_to_range(report_cfg_eutra_s::trigger_quant_opts::rsrp, rsrp_value); - rc.meas_result.rsrq_result = value_to_range(report_cfg_eutra_s::trigger_quant_opts::rsrq, rsrq_value); + rc.meas_result.rsrp_result = rrc_value_to_range(quant_rsrp, rsrp_value); + rc.meas_result.rsrq_result = rrc_value_to_range(quant_rsrq, rsrq_value); logger.info("MEAS: Adding to report neighbour=%d, pci=%d, earfcn=%d, rsrp=%+.1f, rsrq=%+.1f", neigh_list.size(), @@ -379,21 +332,28 @@ void rrc::rrc_meas::var_meas_report_list::generate_report_interrat(meas_results_ rc.pci_r15 = (uint16_t)cell.pci; // Set quantity to report - if (var_meas.report_cfg_inter.report_quant_cell_nr_r15->ss_rsrp == true) { + if (var_meas.report_cfg_inter.report_quant_cell_nr_r15.is_present()) { + if (var_meas.report_cfg_inter.report_quant_cell_nr_r15->ss_rsrp == true) { + rc.meas_result_cell_r15.rsrp_result_r15_present = true; + rc.meas_result_cell_r15.rsrp_result_r15 = + value_to_range_nr(asn1::rrc::thres_nr_r15_c::types_opts::options::nr_rsrp_r15, rsrp_value); + } + if (var_meas.report_cfg_inter.report_quant_cell_nr_r15->ss_rsrq == true) { + rc.meas_result_cell_r15.rsrq_result_r15_present = true; + rc.meas_result_cell_r15.rsrq_result_r15 = + value_to_range_nr(asn1::rrc::thres_nr_r15_c::types_opts::options::nr_rsrq_r15, rsrq_value); + } + if (var_meas.report_cfg_inter.report_quant_cell_nr_r15->ss_sinr == true) { + rc.meas_result_cell_r15.rs_sinr_result_r15_present = true; + rc.meas_result_cell_r15.rs_sinr_result_r15 = + value_to_range_nr(asn1::rrc::thres_nr_r15_c::types_opts::options::nr_sinr_r15, 1.0); + } + } else { + logger.warning("Report quantity for NR cells not present in measurement config. Sending RSRP anyway."); rc.meas_result_cell_r15.rsrp_result_r15_present = true; rc.meas_result_cell_r15.rsrp_result_r15 = value_to_range_nr(asn1::rrc::thres_nr_r15_c::types_opts::options::nr_rsrp_r15, rsrp_value); } - if (var_meas.report_cfg_inter.report_quant_cell_nr_r15->ss_rsrq == true) { - rc.meas_result_cell_r15.rsrq_result_r15_present = true; - rc.meas_result_cell_r15.rsrq_result_r15 = - value_to_range_nr(asn1::rrc::thres_nr_r15_c::types_opts::options::nr_rsrq_r15, rsrq_value); - } - if (var_meas.report_cfg_inter.report_quant_cell_nr_r15->ss_sinr == true) { - rc.meas_result_cell_r15.rs_sinr_result_r15_present = true; - rc.meas_result_cell_r15.rs_sinr_result_r15 = - value_to_range_nr(asn1::rrc::thres_nr_r15_c::types_opts::options::nr_sinr_r15, 1.0); - } logger.info("MEAS: Adding to report neighbour=%d, pci=%d, earfcn=%d, rsrp=%+.1f, rsrq=%+.1f", neigh_list.size(), @@ -449,11 +409,9 @@ void rrc::rrc_meas::var_meas_report_list::generate_report(const uint32_t measId) meas_results_s* report = &ul_dcch_msg.msg.c1().meas_report().crit_exts.c1().meas_report_r8().meas_results; - report->meas_id = (uint8_t)measId; - report->meas_result_pcell.rsrp_result = - value_to_range(report_cfg_eutra_s::trigger_quant_opts::rsrp, serv_cell->get_rsrp()); - report->meas_result_pcell.rsrq_result = - value_to_range(report_cfg_eutra_s::trigger_quant_opts::rsrq, serv_cell->get_rsrq()); + report->meas_id = (uint8_t)measId; + report->meas_result_pcell.rsrp_result = rrc_value_to_range(quant_rsrp, serv_cell->get_rsrp()); + report->meas_result_pcell.rsrq_result = rrc_value_to_range(quant_rsrq, serv_cell->get_rsrq()); logger.info("MEAS: Generate report MeasId=%d, Pcell rsrp=%f rsrq=%f", report->meas_id, @@ -807,16 +765,39 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i float Ofs, float Ocs) { + auto asn1_quant_convert = [](report_cfg_eutra_s::trigger_quant_e_ q) { + if (q == report_cfg_eutra_s::trigger_quant_opts::rsrp) { + return quant_rsrp; + } else { + return quant_rsrq; + } + }; + + eutra_event_s::event_id_c_ event_id = report_cfg.trigger_type.event().event_id; + + // For A1/A2 events, get serving cell from current carrier + if (event_id.type().value < eutra_event_s::event_id_c_::types::event_a3 && + meas_obj.carrier_freq != serv_cell->get_earfcn()) { + uint32_t scell_pci = 0; + if (!rrc_ptr->meas_cells.get_scell_cc_idx(meas_obj.carrier_freq, scell_pci)) { + logger.error("MEAS: Could not find serving cell for carrier earfcn=%d", meas_obj.carrier_freq); + return; + } + serv_cell = rrc_ptr->meas_cells.get_neighbour_cell_handle(meas_obj.carrier_freq, scell_pci); + if (!serv_cell) { + logger.error( + "MEAS: Could not find serving cell for carrier earfcn=%d and pci=%d", meas_obj.carrier_freq, scell_pci); + return; + } + } + double hyst = 0.5 * report_cfg.trigger_type.event().hysteresis; float Ms = is_rsrp(report_cfg.trigger_quant.value) ? serv_cell->get_rsrp() : serv_cell->get_rsrq(); - if (!std::isnormal(Ms)) { logger.debug("MEAS: Serving cell Ms=%f invalid when evaluating triggers", Ms); return; } - eutra_event_s::event_id_c_ event_id = report_cfg.trigger_type.event().event_id; - if (report_cfg.trigger_type.type() == report_cfg_eutra_s::trigger_type_c_::types::event) { // A1 & A2 are for serving cell only if (event_id.type().value < eutra_event_s::event_id_c_::types::event_a3) { @@ -825,17 +806,21 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i bool exit_condition = false; if (event_id.type() == eutra_event_s::event_id_c_::types::event_a1) { if (event_id.event_a1().a1_thres.type().value == thres_eutra_c::types::thres_rsrp) { - thresh = range_to_value(report_cfg.trigger_quant, event_id.event_a1().a1_thres.thres_rsrp()); + thresh = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant), + event_id.event_a1().a1_thres.thres_rsrp()); } else { - thresh = range_to_value(report_cfg.trigger_quant, event_id.event_a1().a1_thres.thres_rsrq()); + thresh = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant), + event_id.event_a1().a1_thres.thres_rsrq()); } enter_condition = Ms - hyst > thresh; exit_condition = Ms + hyst < thresh; } else { if (event_id.event_a2().a2_thres.type() == thres_eutra_c::types::thres_rsrp) { - thresh = range_to_value(report_cfg.trigger_quant, event_id.event_a2().a2_thres.thres_rsrp()); + thresh = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant), + event_id.event_a2().a2_thres.thres_rsrp()); } else { - thresh = range_to_value(report_cfg.trigger_quant, event_id.event_a2().a2_thres.thres_rsrq()); + thresh = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant), + event_id.event_a2().a2_thres.thres_rsrq()); } enter_condition = Ms + hyst < thresh; exit_condition = Ms - hyst > thresh; @@ -886,7 +871,7 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i } else { range = event_id.event_a4().a4_thres.thres_rsrq(); } - thresh = range_to_value(report_cfg.trigger_quant.value, range); + thresh = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant.value), range); enter_condition = Mn + Ofn + Ocn - hyst > thresh; exit_condition = Mn + Ofn + Ocn + hyst < thresh; break; @@ -901,8 +886,8 @@ void rrc::rrc_meas::var_meas_cfg::eval_triggers_eutra(uint32_t meas_i } else { range2 = event_id.event_a5().a5_thres2.thres_rsrq(); } - th1 = range_to_value(report_cfg.trigger_quant.value, range); - th2 = range_to_value(report_cfg.trigger_quant.value, range2); + th1 = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant.value), range); + th2 = rrc_range_to_value(asn1_quant_convert(report_cfg.trigger_quant.value), range2); enter_condition = (Ms + hyst < th1) && (Mn + Ofn + Ocn - hyst > th2); exit_condition = (Ms - hyst > th1) && (Mn + Ofn + Ocn + hyst < th2); break; @@ -1472,19 +1457,18 @@ void rrc::rrc_meas::var_meas_cfg::log_debug_trigger_value_eutra(const eutra_even switch (e.type()) { case eutra_event_s::event_id_c_::types_opts::event_a1: logger.debug("MEAS: A1-threshold=%.1f dBm", - range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a1().a1_thres.thres_rsrp())); + rrc_range_to_value(quant_rsrp, e.event_a1().a1_thres.thres_rsrp())); break; case eutra_event_s::event_id_c_::types_opts::event_a2: logger.debug("MEAS: A2-threshold=%.1f dBm", - range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a2().a2_thres.thres_rsrp())); + rrc_range_to_value(quant_rsrp, e.event_a2().a2_thres.thres_rsrp())); break; case eutra_event_s::event_id_c_::types_opts::event_a3: - logger.debug("MEAS: A3-offset=%.1f dB", - range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a3().a3_offset)); + logger.debug("MEAS: A3-offset=%.1f dB", rrc_range_to_value(quant_rsrp, e.event_a3().a3_offset)); break; case eutra_event_s::event_id_c_::types_opts::event_a4: logger.debug("MEAS: A4-threshold=%.1f dBm", - range_to_value(report_cfg_eutra_s::trigger_quant_opts::rsrp, e.event_a4().a4_thres.thres_rsrp())); + rrc_range_to_value(quant_rsrp, e.event_a4().a4_thres.thres_rsrp())); break; default: logger.debug("MEAS: Unsupported"); @@ -1599,7 +1583,7 @@ bool rrc::rrc_meas::var_meas_cfg::parse_meas_config(const meas_cfg_s* cfg, bool // set the parameter s-Measure within VarMeasConfig to the lowest value of the RSRP ranges indicated by the // received value of s-Measure if (cfg->s_measure) { - s_measure_value = range_to_value(report_cfg_eutra_s::trigger_quant_opts::options::rsrp, cfg->s_measure); + s_measure_value = rrc_range_to_value(quant_rsrp, cfg->s_measure); } } diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc deleted file mode 100644 index 378585641..000000000 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ /dev/null @@ -1,1571 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsue/hdr/stack/rrc/rrc_nr.h" -#include "srsran/common/security.h" -#include "srsran/common/standard_streams.h" -#include "srsran/interfaces/ue_pdcp_interfaces.h" -#include "srsran/interfaces/ue_rlc_interfaces.h" -#include "srsue/hdr/stack/upper/usim.h" - -#define Error(fmt, ...) rrc_ptr->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Warning(fmt, ...) rrc_ptr->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Info(fmt, ...) rrc_ptr->logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Debug(fmt, ...) rrc_ptr->logger.debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) - -using namespace asn1::rrc_nr; -using namespace asn1; -using namespace srsran; -namespace srsue { - -const char* rrc_nr::rrc_nr_state_text[] = {"IDLE", "CONNECTED", "CONNECTED-INACTIVE"}; - -rrc_nr::rrc_nr(srsran::task_sched_handle task_sched_) : - logger(srslog::fetch_basic_logger("RRC-NR")), task_sched(task_sched_), conn_recfg_proc(this) -{} - -rrc_nr::~rrc_nr() = default; - -void rrc_nr::init(phy_interface_rrc_nr* phy_, - mac_interface_rrc_nr* mac_, - rlc_interface_rrc* rlc_, - pdcp_interface_rrc* pdcp_, - gw_interface_rrc* gw_, - rrc_eutra_interface_rrc_nr* rrc_eutra_, - usim_interface_rrc_nr* usim_, - srsran::timer_handler* timers_, - stack_interface_rrc* stack_, - const rrc_nr_args_t& args_) -{ - phy = phy_; - rlc = rlc_; - pdcp = pdcp_; - gw = gw_; - mac = mac_; - rrc_eutra = rrc_eutra_; - usim = usim_; - timers = timers_; - stack = stack_; - args = args_; - - running = true; - sim_measurement_timer = task_sched.get_unique_timer(); -} - -void rrc_nr::stop() -{ - running = false; -} - -void rrc_nr::init_core_less() -{ - logger.info("Creating dummy DRB on LCID=%d", args.coreless.drb_lcid); - srsran::rlc_config_t rlc_cnfg = srsran::rlc_config_t::default_rlc_um_nr_config(6); - rlc->add_bearer(args.coreless.drb_lcid, rlc_cnfg); - - srsran::pdcp_config_t pdcp_cnfg{args.coreless.drb_lcid, - srsran::PDCP_RB_IS_DRB, - srsran::SECURITY_DIRECTION_DOWNLINK, - srsran::SECURITY_DIRECTION_UPLINK, - srsran::PDCP_SN_LEN_18, - srsran::pdcp_t_reordering_t::ms500, - srsran::pdcp_discard_timer_t::ms100, - false, - srsran_rat_t::nr}; - - pdcp->add_bearer(args.coreless.drb_lcid, pdcp_cnfg); - return; -} -void rrc_nr::get_metrics(rrc_nr_metrics_t& m) {} - -const char* rrc_nr::get_rb_name(uint32_t lcid) -{ - if (is_nr_srb(lcid)) { - return get_srb_name(nr_lcid_to_srb(lcid)); - } - if (lcid_drb.find(lcid) != lcid_drb.end()) { - return get_drb_name(lcid_drb[lcid]); - } - logger.warning("Unable to find lcid: %d. Return invalid LCID"); - return "invalid LCID"; -} - -// Timeout callback interface -void rrc_nr::timer_expired(uint32_t timeout_id) -{ - logger.debug("Handling Timer Expired"); - if (timeout_id == sim_measurement_timer.id()) { - logger.debug("Triggered simulated measurement"); - - phy_meas_nr_t sim_meas = {}; - std::vector phy_meas_nr; - sim_meas.rsrp = -60.0; - sim_meas.rsrq = -60.0; - sim_meas.cfo_hz = 1.0; - sim_meas.arfcn_nr = sim_measurement_carrier_freq_r15; - sim_meas.pci_nr = args.sim_nr_meas_pci; - phy_meas_nr.push_back(sim_meas); - rrc_eutra->new_cell_meas_nr(phy_meas_nr); - - auto timer_expire_func = [this](uint32_t tid) { timer_expired(tid); }; - sim_measurement_timer.set(sim_measurement_timer_duration_ms, timer_expire_func); - sim_measurement_timer.run(); - } -} - -void rrc_nr::srsran_rrc_log(const char* str) {} - -template -void rrc_nr::log_rrc_message(const std::string& source, - direction_t dir, - const srsran::byte_buffer_t* pdu, - const T& msg, - const std::string& msg_type) -{ - if (logger.debug.enabled()) { - asn1::json_writer json_writer; - msg.to_json(json_writer); - logger.debug(pdu->msg, - pdu->N_bytes, - "%s - %s %s (%d B)", - source.c_str(), - (dir == Rx) ? "Rx" : "Tx", - msg_type.c_str(), - pdu->N_bytes); - logger.debug("Content:%s", json_writer.to_string().c_str()); - } else if (logger.info.enabled()) { - logger.info("%s - %s %s (%d B)", source.c_str(), (dir == Rx) ? "Rx" : "Tx", msg_type.c_str(), pdu->N_bytes); - } -} - -template -void rrc_nr::log_rrc_message(const std::string& source, - direction_t dir, - dyn_octstring oct, - const T& msg, - const std::string& msg_type) -{ - if (logger.debug.enabled()) { - asn1::json_writer json_writer; - msg.to_json(json_writer); - logger.debug(oct.data(), - oct.size(), - "%s - %s %s (%d B)", - source.c_str(), - (dir == Rx) ? "Rx" : "Tx", - msg_type.c_str(), - oct.size()); - logger.debug("Content:%s", json_writer.to_string().c_str()); - } else if (logger.info.enabled()) { - logger.info("%s - %s %s (%d B)", source.c_str(), (dir == Rx) ? "Rx" : "Tx", msg_type.c_str(), oct.size()); - } -} - -bool rrc_nr::add_lcid_drb(uint32_t lcid, uint32_t drb_id) -{ - if (lcid_drb.find(lcid) != lcid_drb.end()) { - logger.error("Couldn't add DRB to LCID (%d). DRB %d already exists.", lcid, drb_id); - return false; - } else { - logger.info("Adding lcid %d and radio bearer ID %d", lcid, drb_id); - lcid_drb[lcid] = nr_drb_id_to_drb(drb_id); - } - return true; -} - -uint32_t rrc_nr::get_lcid_for_drbid(uint32_t drb_id) -{ - for (auto& rb : lcid_drb) { - if (rb.second == nr_drb_id_to_drb(drb_id)) { - return rb.first; - } - } - logger.error("Couldn't find LCID for DRB. DRB %d does exist.", drb_id); - return 0; -} - -// PHY interface -void rrc_nr::in_sync() {} -void rrc_nr::out_of_sync() {} - -// MAC interface -void rrc_nr::run_tti(uint32_t tti) {} - -// PDCP interface -void rrc_nr::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} -void rrc_nr::write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) {} -void rrc_nr::write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) {} -void rrc_nr::write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {} -void rrc_nr::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} - -void rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) -{ - struct ue_mrdc_cap_s mrdc_cap; - - band_combination_s band_combination; - - for (const auto& band : args.supported_bands_eutra) { - struct band_params_c band_param_eutra; - band_param_eutra.set_eutra(); - band_param_eutra.eutra().ca_bw_class_dl_eutra_present = true; - band_param_eutra.eutra().ca_bw_class_ul_eutra_present = true; - band_param_eutra.eutra().band_eutra = band; - band_param_eutra.eutra().ca_bw_class_dl_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; - band_param_eutra.eutra().ca_bw_class_ul_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; - band_combination.band_list.push_back(band_param_eutra); - } - - // TODO check if band is requested - for (const auto& band : args.supported_bands_nr) { - struct band_params_c band_param_nr; - band_param_nr.set_nr(); - band_param_nr.nr().ca_bw_class_dl_nr_present = true; - band_param_nr.nr().ca_bw_class_ul_nr_present = true; - band_param_nr.nr().band_nr = band; - band_param_nr.nr().ca_bw_class_dl_nr = asn1::rrc_nr::ca_bw_class_nr_opts::options::a; - band_param_nr.nr().ca_bw_class_ul_nr = asn1::rrc_nr::ca_bw_class_nr_opts::options::a; - band_combination.band_list.push_back(band_param_nr); - } - - mrdc_cap.rf_params_mrdc.supported_band_combination_list.push_back(band_combination); - mrdc_cap.rf_params_mrdc.supported_band_combination_list_present = true; - - mrdc_cap.rf_params_mrdc.ext = true; - - // RF Params MRDC applied_freq_band_list_filt - for (const auto& band : args.supported_bands_eutra) { - freq_band_info_c band_info_eutra; - band_info_eutra.set_band_info_eutra(); - band_info_eutra.band_info_eutra().ca_bw_class_dl_eutra_present = false; - band_info_eutra.band_info_eutra().ca_bw_class_ul_eutra_present = false; - band_info_eutra.band_info_eutra().band_eutra = band; - mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_eutra); - } - - for (const auto& band : args.supported_bands_nr) { - freq_band_info_c band_info_nr; - band_info_nr.set_band_info_nr(); - band_info_nr.band_info_nr().band_nr = band; - mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_nr); - } - - mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt_present = true; - - // rf_params_mrdc supported band combination list v1540 - - band_combination_list_v1540_l* band_combination_list_v1450 = new band_combination_list_v1540_l(); - band_combination_v1540_s band_combination_v1540; - - band_params_v1540_s band_params_a; - band_params_a.srs_tx_switch_present = true; - band_params_a.srs_carrier_switch_present = false; - band_params_a.srs_tx_switch.supported_srs_tx_port_switch = - band_params_v1540_s::srs_tx_switch_s_::supported_srs_tx_port_switch_opts::not_supported; - band_combination_v1540.band_list_v1540.push_back(band_params_a); - - band_params_v1540_s band_params_b; - band_params_b.srs_tx_switch_present = true; - band_params_b.srs_tx_switch.supported_srs_tx_port_switch = - band_params_v1540_s::srs_tx_switch_s_::supported_srs_tx_port_switch_opts::t1r2; - band_params_b.srs_carrier_switch_present = false; - band_combination_v1540.band_list_v1540.push_back(band_params_b); - - // clang-format off - band_combination_v1540.ca_params_nr_v1540_present = false; - band_combination_v1540.ca_params_nr_v1540.simul_csi_reports_all_cc_present = true; - band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.max_num_simul_nzp_csi_rs_act_bwp_all_cc_present = true; - band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.max_num_simul_nzp_csi_rs_act_bwp_all_cc = 5; - band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.total_num_ports_simul_nzp_csi_rs_act_bwp_all_cc_present = true; - band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.total_num_ports_simul_nzp_csi_rs_act_bwp_all_cc = 32; - // clang-format on - band_combination_list_v1450->push_back(band_combination_v1540); - mrdc_cap.rf_params_mrdc.supported_band_combination_list_v1540.reset(band_combination_list_v1450); - - feature_set_combination_l feature_set_combination; - - feature_sets_per_band_l feature_sets_per_band; - - feature_set_c feature_set_eutra; - feature_set_eutra.set_eutra(); - feature_set_eutra.eutra().dl_set_eutra = 1; - feature_set_eutra.eutra().ul_set_eutra = 1; - feature_sets_per_band.push_back(feature_set_eutra); - feature_set_combination.push_back(feature_sets_per_band); - - for (const auto& band : args.supported_bands_nr) { - feature_sets_per_band.resize(0); - feature_set_c feature_set_nr; - feature_set_nr.set_nr(); - feature_set_nr.nr().dl_set_nr = 1; - feature_set_nr.nr().ul_set_nr = 1; - feature_sets_per_band.push_back(feature_set_nr); - feature_set_combination.push_back(feature_sets_per_band); - } - - mrdc_cap.feature_set_combinations.push_back(feature_set_combination); - - mrdc_cap.feature_set_combinations_present = true; - - // Pack mrdc_cap - asn1::bit_ref bref(eutra_nr_caps_pdu->msg, eutra_nr_caps_pdu->get_tailroom()); - mrdc_cap.pack(bref); - eutra_nr_caps_pdu->N_bytes = bref.distance_bytes(); - -#if 0 - uint8_t eutra_nr_cap_raw[] = {0x01, 0x1c, 0x04, 0x81, 0x60, 0x00, 0x1c, 0x4d, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x40, 0x04, 0x04, 0xd0, 0x10, 0x74, 0x06, 0x14, 0xe8, 0x1b, 0x10, - 0x78, 0x00, 0x00, 0x20, 0x00, 0x10, 0x08, 0x08, 0x01, 0x00, 0x20}; - if (sizeof(eutra_nr_cap_raw) <= 2048) { - memcpy(eutra_nr_caps_pdu->msg, eutra_nr_cap_raw, sizeof(eutra_nr_cap_raw)); - eutra_nr_caps_pdu->N_bytes = sizeof(eutra_nr_cap_raw); - } -#endif - - logger.debug( - eutra_nr_caps_pdu->msg, eutra_nr_caps_pdu->N_bytes, "EUTRA-NR capabilities (%u B)", eutra_nr_caps_pdu->N_bytes); - - return; -} - -bool rrc_nr::rrc_reconfiguration(bool endc_release_and_add_r15, - bool nr_secondary_cell_group_cfg_r15_present, - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - bool sk_counter_r15_present, - uint32_t sk_counter_r15, - bool nr_radio_bearer_cfg1_r15_present, - asn1::dyn_octstring nr_radio_bearer_cfg1_r15) -{ - if (not conn_recfg_proc.launch(reconf_initiator_t::mcg_srb1, - endc_release_and_add_r15, - nr_secondary_cell_group_cfg_r15_present, - nr_secondary_cell_group_cfg_r15, - sk_counter_r15_present, - sk_counter_r15, - nr_radio_bearer_cfg1_r15_present, - nr_radio_bearer_cfg1_r15)) { - logger.error("Unable to launch NR RRC reconfiguration procedure"); - return false; - } else { - callback_list.add_proc(conn_recfg_proc); - } - return true; -} - -void rrc_nr::get_nr_capabilities(srsran::byte_buffer_t* nr_caps_pdu) -{ - struct ue_nr_cap_s nr_cap; - - nr_cap.access_stratum_release = access_stratum_release_opts::rel15; - // PDCP - nr_cap.pdcp_params.max_num_rohc_context_sessions = pdcp_params_s::max_num_rohc_context_sessions_opts::cs2; - - for (const auto& band : args.supported_bands_nr) { - band_nr_s band_nr; - band_nr.band_nr = band; - band_nr.ue_pwr_class_present = true; - band_nr.ue_pwr_class = band_nr_s::ue_pwr_class_opts::pc3; - nr_cap.rf_params.supported_band_list_nr.push_back(band_nr); - } - - nr_cap.rlc_params_present = true; - nr_cap.rlc_params.um_with_short_sn_present = true; - nr_cap.rlc_params.um_with_long_sn_present = true; - nr_cap.pdcp_params.short_sn_present = args.pdcp_short_sn_support; - - // Pack nr_caps - asn1::bit_ref bref(nr_caps_pdu->msg, nr_caps_pdu->get_tailroom()); - nr_cap.pack(bref); - nr_caps_pdu->N_bytes = bref.distance_bytes(); - -#if 0 - uint8_t nr_cap_raw[] = { - 0xe1, 0x00, 0x00, 0x00, 0x01, 0x47, 0x7a, 0x03, 0x02, 0x00, 0x00, 0x01, 0x40, 0x48, 0x07, 0x06, 0x0e, 0x02, 0x0c, - 0x00, 0x02, 0x13, 0x60, 0x10, 0x73, 0xe4, 0x20, 0xf0, 0x00, 0x80, 0xc1, 0x30, 0x08, 0x0c, 0x00, 0x00, 0x0a, 0x05, - 0x89, 0xba, 0xc2, 0x19, 0x43, 0x40, 0x88, 0x10, 0x74, 0x18, 0x60, 0x4c, 0x04, 0x41, 0x6c, 0x90, 0x14, 0x06, 0x0c, - 0x78, 0xc7, 0x3e, 0x42, 0x0f, 0x00, 0x58, 0x0c, 0x0e, 0x0e, 0x02, 0x21, 0x3c, 0x84, 0xfc, 0x4d, 0xe0, 0x00, 0x12, - 0x00, 0x00, 0x00, 0x00, 0xe5, 0x4d, 0x00, 0x01, 0x00, 0x00, 0x04, 0x18, 0x60, 0x00, 0x34, 0xaa, 0x60}; - if (sizeof(nr_cap_raw) <= 2048) { - memcpy(nr_caps_pdu->msg, nr_cap_raw, sizeof(nr_cap_raw)); - nr_caps_pdu->N_bytes = sizeof(nr_cap_raw); - } -#endif - - logger.debug(nr_caps_pdu->msg, nr_caps_pdu->N_bytes, "NR capabilities (%u B)", nr_caps_pdu->N_bytes); - return; -}; - -void rrc_nr::phy_meas_stop() -{ - // possbile race condition for sim_measurement timer, which might be set at the same moment as stopped => fix with - // phy integration - logger.debug("Stopping simulated measurements"); - sim_measurement_timer.stop(); -} - -void rrc_nr::phy_set_cells_to_meas(uint32_t carrier_freq_r15) -{ - logger.debug("Measuring phy cell %d ", carrier_freq_r15); - // Start timer for fake measurements - auto timer_expire_func = [this](uint32_t tid) { timer_expired(tid); }; - sim_measurement_carrier_freq_r15 = carrier_freq_r15; - sim_measurement_timer.set(sim_measurement_timer_duration_ms, timer_expire_func); - sim_measurement_timer.run(); -} - -bool rrc_nr::configure_sk_counter(uint16_t sk_counter) -{ - logger.info("Configure new SK counter %d. Update Key for secondary gnb", sk_counter); - if (usim->generate_nr_context(sk_counter, &sec_cfg) == false) { - return false; - } - return true; -} - -bool rrc_nr::is_config_pending() -{ - if (conn_recfg_proc.is_busy()) { - return true; - } - return false; -} - -bool rrc_nr::apply_rlc_add_mod(const rlc_bearer_cfg_s& rlc_bearer_cfg) -{ - uint32_t lc_ch_id = 0; - uint32_t drb_id = 0; - uint32_t srb_id = 0; - rlc_config_t rlc_cfg; - - lc_ch_id = rlc_bearer_cfg.lc_ch_id; - if (rlc_bearer_cfg.served_radio_bearer_present == true) { - if (rlc_bearer_cfg.served_radio_bearer.type() == rlc_bearer_cfg_s::served_radio_bearer_c_::types::drb_id) { - drb_id = rlc_bearer_cfg.served_radio_bearer.drb_id(); - add_lcid_drb(lc_ch_id, drb_id); - } - } else { - logger.warning("In RLC bearer cfg does not contain served radio bearer"); - return false; - } - - if (rlc_bearer_cfg.rlc_cfg_present == true) { - rlc_cfg = srsran::make_rlc_config_t(rlc_bearer_cfg.rlc_cfg); - if (rlc_bearer_cfg.rlc_cfg.type() == asn1::rrc_nr::rlc_cfg_c::types::um_bi_dir) { - if (rlc_bearer_cfg.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present && - rlc_bearer_cfg.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present && - rlc_bearer_cfg.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len != - rlc_bearer_cfg.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len) { - logger.warning("NR RLC sequence number length is not the same in uplink and downlink"); - return false; - } - } else { - logger.warning("NR RLC type is not unacknowledged mode bidirectional"); - return false; - } - } else { - logger.warning("In RLC bearer cfg does not contain rlc cfg"); - return false; - } - - // Setup RLC - rlc->add_bearer(lc_ch_id, rlc_cfg); - - if (rlc_bearer_cfg.mac_lc_ch_cfg_present == true && rlc_bearer_cfg.mac_lc_ch_cfg.ul_specific_params_present) { - logical_channel_config_t logical_channel_cfg; - logical_channel_cfg = srsran::make_mac_logical_channel_cfg_t(lc_ch_id, rlc_bearer_cfg.mac_lc_ch_cfg); - mac->setup_lcid(logical_channel_cfg); - } - return true; -} -bool rrc_nr::apply_mac_cell_group(const mac_cell_group_cfg_s& mac_cell_group_cfg) -{ - if (mac_cell_group_cfg.sched_request_cfg_present) { - if (mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list_present) { - if (mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list.size() == 1) { - const sched_request_to_add_mod_s& asn1_cfg = - mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list[0]; - sr_cfg_nr_t sr_cfg = {}; - sr_cfg.enabled = true; - sr_cfg.num_items = 1; - sr_cfg.item[0].sched_request_id = asn1_cfg.sched_request_id; - sr_cfg.item[0].trans_max = asn1_cfg.sr_trans_max.to_number(); - if (asn1_cfg.sr_prohibit_timer_present) { - sr_cfg.item[0].prohibit_timer = asn1_cfg.sr_trans_max; - } - if (mac->set_config(sr_cfg) != SRSRAN_SUCCESS) { - logger.error("Couldn't configure SR procedure."); - return false; - } - } else { - logger.warning("Only handling 1 scheduling request index to add"); - return false; - } - } - - if (mac_cell_group_cfg.sched_request_cfg.sched_request_to_release_list_present) { - logger.warning("Not handling sched request to release list"); - return false; - } - } - if (mac_cell_group_cfg.sched_request_cfg_present) - - if (mac_cell_group_cfg.bsr_cfg_present) { - logger.debug("Handling MAC BSR config"); - srsran::bsr_cfg_nr_t bsr_cfg = {}; - bsr_cfg.periodic_timer = mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.to_number(); - bsr_cfg.retx_timer = mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.to_number(); - if (mac->set_config(bsr_cfg) != SRSRAN_SUCCESS) { - return false; - } - } - - if (mac_cell_group_cfg.tag_cfg_present) { - if (mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list_present) { - for (uint32_t i = 0; i < mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list.size(); i++) { - tag_cfg_nr_t tag_cfg_nr = {}; - tag_cfg_nr.tag_id = mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list[i].tag_id; - tag_cfg_nr.time_align_timer = mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list[i].time_align_timer.to_number(); - if (mac->add_tag_config(tag_cfg_nr) != SRSRAN_SUCCESS) { - logger.warning("Unable to add TAG config with tag_id %d", tag_cfg_nr.tag_id); - return false; - } - } - } - if (mac_cell_group_cfg.tag_cfg.tag_to_release_list_present) { - for (uint32_t i = 0; i < mac_cell_group_cfg.tag_cfg.tag_to_release_list.size(); i++) { - uint32_t tag_id = mac_cell_group_cfg.tag_cfg.tag_to_release_list[i]; - if (mac->remove_tag_config(tag_id) != SRSRAN_SUCCESS) { - logger.warning("Unable to release TAG config with tag_id %d", tag_id); - return false; - } - } - } - } - - if (mac_cell_group_cfg.phr_cfg_present) { - if (mac_cell_group_cfg.phr_cfg.type() == setup_release_c::types_opts::setup) { - phr_cfg_nr_t phr_cfg_nr; - if (make_mac_phr_cfg_t(mac_cell_group_cfg.phr_cfg.setup(), &phr_cfg_nr) != true) { - logger.warning("Unable to build PHR config"); - return false; - } - if (mac->set_config(phr_cfg_nr) != SRSRAN_SUCCESS) { - logger.warning("Unable to set PHR config"); - return false; - } - } - } - - if (mac_cell_group_cfg.skip_ul_tx_dynamic) { - logger.warning("Not handling phr cfg in skip_ul_tx_dynamic cell group config"); - } - return true; -} - -bool rrc_nr::apply_sp_cell_init_dl_pdcch(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg) -{ - if (pdcch_cfg.search_spaces_to_add_mod_list_present) { - for (uint32_t i = 0; i < pdcch_cfg.search_spaces_to_add_mod_list.size(); i++) { - srsran_search_space_t search_space; - if (make_phy_search_space_cfg(pdcch_cfg.search_spaces_to_add_mod_list[i], &search_space) == true) { - phy_cfg.pdcch.search_space[search_space.id] = search_space; - phy_cfg.pdcch.search_space_present[search_space.id] = true; - } else { - logger.warning("Warning while building search_space structure"); - return false; - } - } - } else { - logger.warning("Option search_spaces_to_add_mod_list not present"); - return false; - } - if (pdcch_cfg.ctrl_res_set_to_add_mod_list_present) { - for (uint32_t i = 0; i < pdcch_cfg.ctrl_res_set_to_add_mod_list.size(); i++) { - srsran_coreset_t coreset; - if (make_phy_coreset_cfg(pdcch_cfg.ctrl_res_set_to_add_mod_list[i], &coreset) == true) { - phy_cfg.pdcch.coreset[coreset.id] = coreset; - phy_cfg.pdcch.coreset_present[coreset.id] = true; - } else { - logger.warning("Warning while building coreset structure"); - return false; - } - } - } else { - logger.warning("Option ctrl_res_set_to_add_mod_list not present"); - } - return true; -} - -bool rrc_nr::apply_sp_cell_init_dl_pdsch(const asn1::rrc_nr::pdsch_cfg_s& pdsch_cfg) -{ - if (pdsch_cfg.dmrs_dl_for_pdsch_map_type_a_present) { - if (pdsch_cfg.dmrs_dl_for_pdsch_map_type_a.type() == setup_release_c::types_opts::setup) { - srsran_dmrs_sch_add_pos_t srsran_dmrs_sch_add_pos; - if (make_phy_dmrs_dl_additional_pos(pdsch_cfg.dmrs_dl_for_pdsch_map_type_a.setup(), &srsran_dmrs_sch_add_pos) == - true) { - phy_cfg.pdsch.dmrs_typeA.additional_pos = srsran_dmrs_sch_add_pos; - phy_cfg.pdsch.dmrs_typeA.present = true; - } else { - logger.warning("Warning while build srsran_dmrs_sch_add_pos structure"); - return false; - } - } else { - logger.warning("Option dmrs_dl_for_pdsch_map_type_a not of type setup"); - return false; - } - } else { - logger.warning("Option dmrs_dl_for_pdsch_map_type_a not present"); - return false; - } - - srsran_resource_alloc_t resource_alloc; - if (make_phy_pdsch_alloc_type(pdsch_cfg, &resource_alloc) == true) { - phy_cfg.pdsch.alloc = resource_alloc; - } - - if (pdsch_cfg.zp_csi_rs_res_to_add_mod_list_present) { - for (uint32_t i = 0; i < pdsch_cfg.zp_csi_rs_res_to_add_mod_list.size(); i++) { - srsran_csi_rs_zp_resource_t zp_csi_rs_resource; - if (make_phy_zp_csi_rs_resource(pdsch_cfg.zp_csi_rs_res_to_add_mod_list[i], &zp_csi_rs_resource) == true) { - // temporally store csi_rs_zp_res - csi_rs_zp_res[zp_csi_rs_resource.id] = zp_csi_rs_resource; - } else { - logger.warning("Warning while building zp_csi_rs resource"); - return false; - } - } - } else { - logger.warning("Option zp_csi_rs_res_to_add_mod_list not present"); - return false; - } - - if (pdsch_cfg.p_zp_csi_rs_res_set_present) { - if (pdsch_cfg.p_zp_csi_rs_res_set.type() == setup_release_c::types_opts::setup) { - for (uint32_t i = 0; i < pdsch_cfg.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list.size(); i++) { - uint8_t res = pdsch_cfg.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list[i]; - // use temporally stored values to assign - if (csi_rs_zp_res.find(res) == csi_rs_zp_res.end()) { - logger.warning("Can not find p_zp_csi_rs_res in temporally stored csi_rs_zp_res"); - return false; - } - phy_cfg.pdsch.p_zp_csi_rs_set.data[i] = csi_rs_zp_res[res]; - phy_cfg.pdsch.p_zp_csi_rs_set.count += 1; - } - } else { - logger.warning("Option p_zp_csi_rs_res_set not of type setup"); - return false; - } - } else { - logger.warning("Option p_zp_csi_rs_res_set not present"); - return false; - } - return true; -} - -bool rrc_nr::apply_res_csi_report_cfg(const asn1::rrc_nr::csi_report_cfg_s& csi_report_cfg) -{ - uint32_t report_cfg_id = csi_report_cfg.report_cfg_id; - srsran_csi_hl_report_cfg_t srsran_csi_hl_report_cfg; - if (make_phy_csi_report(csi_report_cfg, &srsran_csi_hl_report_cfg) == true) { - phy_cfg.csi.reports[report_cfg_id] = srsran_csi_hl_report_cfg; - } else { - logger.warning("Warning while building report structure"); - return false; - } - if (csi_report_cfg.report_cfg_type.type() == csi_report_cfg_s::report_cfg_type_c_::types_opts::options::periodic) { - if (csi_report_cfg.report_cfg_type.periodic().pucch_csi_res_list.size() > 0) { - uint32_t res_id = csi_report_cfg.report_cfg_type.periodic() - .pucch_csi_res_list[0] - .pucch_res; // TODO: support and check more items - if (res_list_present[res_id] == true) { - phy_cfg.csi.reports[report_cfg_id].periodic.resource = res_list[res_id]; - } else { - logger.error("Resources set not present for assigning pucch sets (res_id %d)", res_id); - return false; - } - } else { - logger.warning("List size to small: pucch_csi_res_list.size() < 0"); - return false; - } - } - return true; -} - -bool rrc_nr::apply_csi_meas_cfg(const asn1::rrc_nr::csi_meas_cfg_s& csi_meas_cfg) -{ - if (csi_meas_cfg.csi_report_cfg_to_add_mod_list_present) { - for (uint32_t i = 0; i < csi_meas_cfg.csi_report_cfg_to_add_mod_list.size(); i++) { - if (apply_res_csi_report_cfg(csi_meas_cfg.csi_report_cfg_to_add_mod_list[i]) == false) { - return false; - } - } - } else { - logger.warning("Option csi_report_cfg_to_add_mod_list not present"); - return false; - } - - if (csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list_present) { - for (uint32_t i = 0; i < csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.size(); i++) { - srsran_csi_rs_nzp_resource_t csi_rs_nzp_resource; - if (make_phy_nzp_csi_rs_resource(csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list[i], &csi_rs_nzp_resource) == true) { - // temporally store csi_rs_zp_res - csi_rs_nzp_res[csi_rs_nzp_resource.id] = csi_rs_nzp_resource; - } else { - logger.warning("Warning while building phy_nzp_csi_rs resource"); - return false; - } - } - } else { - logger.warning("Option nzp_csi_rs_res_to_add_mod_list not present"); - return false; - } - - if (csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list_present) { - for (uint32_t i = 0; i < csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.size(); i++) { - uint8_t set_id = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list[i].nzp_csi_res_set_id; - for (uint32_t j = 0; j < csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list[i].nzp_csi_rs_res.size(); j++) { - uint8_t res = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list[i].nzp_csi_rs_res[j]; - // use temporally stored values to assign - if (csi_rs_nzp_res.find(res) == csi_rs_nzp_res.end()) { - logger.warning("Can not find p_zp_csi_rs_res in temporally stored csi_rs_zp_res"); - return false; - } - phy_cfg.pdsch.nzp_csi_rs_sets[set_id].data[j] = csi_rs_nzp_res[res]; - phy_cfg.pdsch.nzp_csi_rs_sets[set_id].count += 1; - } - if (csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list[i].trs_info_present) { - phy_cfg.pdsch.nzp_csi_rs_sets[set_id].trs_info = true; - } - } - } else { - logger.warning("Option p_zp_csi_rs_res_set not present"); - return false; - } - - return true; -} - -bool rrc_nr::apply_dl_common_cfg(const asn1::rrc_nr::dl_cfg_common_s& dl_cfg_common) -{ - if (dl_cfg_common.init_dl_bwp_present) { - if (dl_cfg_common.freq_info_dl_present) { - if (make_phy_carrier_cfg(dl_cfg_common.freq_info_dl, &phy_cfg.carrier) == false) { - logger.warning("Warning while making carrier phy config"); - return false; - } - } else { - logger.warning("Option freq_info_dl not present"); - return false; - } - if (dl_cfg_common.init_dl_bwp.pdsch_cfg_common_present) { - if (dl_cfg_common.init_dl_bwp.pdsch_cfg_common.type() == - asn1::rrc_nr::setup_release_c::types_opts::setup) { - const pdcch_cfg_common_s& pdcch_cfg_common = dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(); - if (pdcch_cfg_common.common_ctrl_res_set_present) { - srsran_coreset_t coreset; - if (make_phy_coreset_cfg(pdcch_cfg_common.common_ctrl_res_set, &coreset) == true) { - phy_cfg.pdcch.coreset[coreset.id] = coreset; - phy_cfg.pdcch.coreset_present[coreset.id] = true; - } else { - logger.warning("Warning while building coreset structure"); - return false; - } - } else { - logger.warning("Option common_ctrl_res_set not present"); - return false; - } - if (pdcch_cfg_common.common_search_space_list_present) { - for (uint32_t i = 0; i < pdcch_cfg_common.common_search_space_list.size(); i++) { - srsran_search_space_t search_space; - if (make_phy_search_space_cfg(pdcch_cfg_common.common_search_space_list[i], &search_space) == true) { - phy_cfg.pdcch.search_space[search_space.id] = search_space; - phy_cfg.pdcch.search_space_present[search_space.id] = true; - } else { - logger.warning("Warning while building search_space structure"); - return false; - } - } - } else { - logger.warning("Option common_search_space_list not present"); - return false; - } - if (pdcch_cfg_common.ra_search_space_present) { - if (phy_cfg.pdcch.search_space_present[pdcch_cfg_common.ra_search_space] == true) { - // phy_cfg.pdcch.ra_rnti = 0x16; //< Supposed to be deduced from PRACH configuration - phy_cfg.pdcch.ra_search_space = phy_cfg.pdcch.search_space[pdcch_cfg_common.ra_search_space]; - phy_cfg.pdcch.ra_search_space_present = true; - phy_cfg.pdcch.ra_search_space.type = srsran_search_space_type_common_1; - } else { - logger.warning("Search space %d not presenet for random access search space", - pdcch_cfg_common.ra_search_space); - } - } else { - logger.warning("Option ra_search_space not present"); - return false; - } - } else { - logger.warning("Option pdsch_cfg_common not of type setup"); - return false; - } - } else { - logger.warning("Option pdsch_cfg_common not present"); - return false; - } - if (dl_cfg_common.init_dl_bwp.pdsch_cfg_common_present) { - if (dl_cfg_common.init_dl_bwp.pdsch_cfg_common.type() == setup_release_c::types::setup) { - pdsch_cfg_common_s pdsch_cfg_common = dl_cfg_common.init_dl_bwp.pdsch_cfg_common.setup(); - if (pdsch_cfg_common.pdsch_time_domain_alloc_list_present) { - for (uint32_t i = 0; i < pdsch_cfg_common.pdsch_time_domain_alloc_list.size(); i++) { - srsran_sch_time_ra_t common_time_ra; - if (make_phy_common_time_ra(pdsch_cfg_common.pdsch_time_domain_alloc_list[i], &common_time_ra) == true) { - phy_cfg.pdsch.common_time_ra[i] = common_time_ra; - phy_cfg.pdsch.nof_common_time_ra = i + 1; - } else { - logger.warning("Warning while building common_time_ra structure"); - return false; - } - } - } else { - logger.warning("Option pdsch_time_domain_alloc_list not present"); - return false; - } - } else { - logger.warning("Option pdsch_cfg_common not of type setup"); - return false; - } - } else { - logger.warning("Option pdsch_cfg_common not present"); - return false; - } - } else { - logger.warning("Option init_dl_bwp not present"); - return false; - } - return true; -} - -bool rrc_nr::apply_ul_common_cfg(const asn1::rrc_nr::ul_cfg_common_s& ul_cfg_common) -{ - if (ul_cfg_common.init_ul_bwp_present) { - if (ul_cfg_common.init_ul_bwp.rach_cfg_common_present) { - if (ul_cfg_common.init_ul_bwp.rach_cfg_common.type() == setup_release_c::types_opts::setup) { - rach_nr_cfg_t rach_nr_cfg = make_mac_rach_cfg(ul_cfg_common.init_ul_bwp.rach_cfg_common.setup()); - mac->set_config(rach_nr_cfg); - - // Make the RACH configuration for PHY - if (not make_phy_rach_cfg(ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), &phy_cfg.prach)) { - logger.warning("Error parsing rach_cfg_common"); - return false; - } - - } else { - logger.warning("Option rach_cfg_common not of type setup"); - return false; - } - } else { - logger.warning("Option rach_cfg_common not present"); - return false; - } - if (ul_cfg_common.init_ul_bwp.pusch_cfg_common_present) { - if (ul_cfg_common.init_ul_bwp.pusch_cfg_common.type() == setup_release_c::types_opts::setup) { - if (ul_cfg_common.init_ul_bwp.pusch_cfg_common.setup().pusch_time_domain_alloc_list_present) { - for (uint32_t i = 0; - i < ul_cfg_common.init_ul_bwp.pusch_cfg_common.setup().pusch_time_domain_alloc_list.size(); - i++) { - srsran_sch_time_ra_t common_time_ra; - if (make_phy_common_time_ra( - ul_cfg_common.init_ul_bwp.pusch_cfg_common.setup().pusch_time_domain_alloc_list[i], - &common_time_ra) == true) { - phy_cfg.pusch.common_time_ra[i] = common_time_ra; - phy_cfg.pusch.nof_common_time_ra = i + 1; - } else { - logger.warning("Warning while building common_time_ra structure"); - } - } - } else { - logger.warning("Option pusch_time_domain_alloc_list not present"); - return false; - } - } else { - logger.warning("Option pusch_cfg_common not of type setup"); - return false; - } - } else { - logger.warning("Option pusch_cfg_common not present"); - return false; - } - if (ul_cfg_common.init_ul_bwp.pucch_cfg_common_present) { - if (ul_cfg_common.init_ul_bwp.pucch_cfg_common.type() == setup_release_c::types_opts::setup) { - logger.info("PUCCH cfg commont setup not handled"); - } else { - logger.warning("Option pucch_cfg_common not of type setup"); - return false; - } - } else { - logger.warning("Option pucch_cfg_common not present"); - return false; - } - } else { - logger.warning("Option init_ul_bwp not present"); - return false; - } - return true; -} - -bool rrc_nr::apply_sp_cell_ded_ul_pucch(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg) -{ - // determine format 2 max code rate - uint32_t format_2_max_code_rate = 0; - if (pucch_cfg.format2_present && pucch_cfg.format2.type() == setup_release_c::types::setup) { - if (pucch_cfg.format2.setup().max_code_rate_present) { - if (make_phy_max_code_rate(pucch_cfg.format2.setup(), &format_2_max_code_rate) == false) { - logger.warning("Warning while building format_2_max_code_rate"); - } - } - } else { - logger.warning("Option format2 not present or not of type setup"); - return false; - } - - // now look up resource and assign into internal struct - if (pucch_cfg.res_to_add_mod_list_present) { - for (uint32_t i = 0; i < pucch_cfg.res_to_add_mod_list.size(); i++) { - uint32_t res_id = pucch_cfg.res_to_add_mod_list[i].pucch_res_id; - if (make_phy_res_config(pucch_cfg.res_to_add_mod_list[i], format_2_max_code_rate, &res_list[res_id]) == true) { - res_list_present[res_id] = true; - } else { - logger.warning("Warning while building pucch_nr_resource structure"); - return false; - } - } - } else { - logger.warning("Option res_to_add_mod_list not present"); - return false; - } - - // Check first all resource lists and - phy_cfg.pucch.enabled = true; - if (pucch_cfg.res_set_to_add_mod_list_present) { - for (uint32_t i = 0; i < pucch_cfg.res_set_to_add_mod_list.size(); i++) { - uint32_t set_id = pucch_cfg.res_set_to_add_mod_list[i].pucch_res_set_id; - phy_cfg.pucch.sets[set_id].nof_resources = pucch_cfg.res_set_to_add_mod_list[i].res_list.size(); - for (uint32_t j = 0; j < pucch_cfg.res_set_to_add_mod_list[i].res_list.size(); j++) { - uint32_t res_id = pucch_cfg.res_set_to_add_mod_list[i].res_list[j]; - if (res_list_present[res_id] == true) { - phy_cfg.pucch.sets[set_id].resources[j] = res_list[res_id]; - } else { - logger.error( - "Resources set not present for assign pucch sets (res_id %d, setid %d, j %d)", res_id, set_id, j); - } - } - } - } - - if (pucch_cfg.sched_request_res_to_add_mod_list_present) { - for (uint32_t i = 0; i < pucch_cfg.sched_request_res_to_add_mod_list.size(); i++) { - uint32_t res_id = pucch_cfg.sched_request_res_to_add_mod_list[i].sched_request_res_id; - srsran_pucch_nr_sr_resource_t srsran_pucch_nr_sr_resource; - if (make_phy_sr_resource(pucch_cfg.sched_request_res_to_add_mod_list[i], &srsran_pucch_nr_sr_resource) == - true) { // TODO: fix that if indexing is solved - phy_cfg.pucch.sr_resources[res_id] = srsran_pucch_nr_sr_resource; - - // Set PUCCH resource - if (pucch_cfg.sched_request_res_to_add_mod_list[i].res_present) { - uint32_t pucch_res_id = pucch_cfg.sched_request_res_to_add_mod_list[i].res; - if (res_list_present[res_id]) { - phy_cfg.pucch.sr_resources[res_id].resource = res_list[pucch_res_id]; - } else { - logger.warning("Warning SR's PUCCH resource is invalid (%d)", pucch_res_id); - phy_cfg.pucch.sr_resources[res_id].configured = false; - } - } else { - logger.warning("Warning SR resource is present but no PUCCH resource is assigned to it"); - phy_cfg.pucch.sr_resources[res_id].configured = false; - } - - } else { - logger.warning("Warning while building srsran_pucch_nr_sr_resource structure"); - return false; - } - } - } else { - logger.warning("Option sched_request_res_to_add_mod_list not present"); - return false; - } - - if (pucch_cfg.dl_data_to_ul_ack_present) { - for (uint32_t i = 0; i < pucch_cfg.dl_data_to_ul_ack.size(); i++) { - phy_cfg.harq_ack.dl_data_to_ul_ack[i] = pucch_cfg.dl_data_to_ul_ack[i]; - } - phy_cfg.harq_ack.nof_dl_data_to_ul_ack = pucch_cfg.dl_data_to_ul_ack.size(); - } else { - logger.warning("Option dl_data_to_ul_ack not present"); - return false; - } - - return true; -}; - -bool rrc_nr::apply_sp_cell_ded_ul_pusch(const asn1::rrc_nr::pusch_cfg_s& pusch_cfg) -{ - srsran_resource_alloc_t resource_alloc; - if (make_phy_pusch_alloc_type(pusch_cfg, &resource_alloc) == true) { - phy_cfg.pusch.alloc = resource_alloc; - } - - if (pusch_cfg.dmrs_ul_for_pusch_map_type_a_present) { - if (pusch_cfg.dmrs_ul_for_pusch_map_type_a.type() == setup_release_c::types_opts::setup) { - srsran_dmrs_sch_add_pos_t srsran_dmrs_sch_add_pos; - if (make_phy_dmrs_ul_additional_pos(pusch_cfg.dmrs_ul_for_pusch_map_type_a.setup(), &srsran_dmrs_sch_add_pos) == - true) { - phy_cfg.pusch.dmrs_typeA.additional_pos = srsran_dmrs_sch_add_pos; - phy_cfg.pusch.dmrs_typeA.present = true; - } else { - logger.warning("Warning while build srsran_dmrs_sch_add_pos structure"); - return false; - } - } else { - logger.warning("Option dmrs_ul_for_pusch_map_type_a not of type setup"); - return false; - } - } else { - logger.warning("Option dmrs_ul_for_pusch_map_type_a not present"); - return false; - } - if (pusch_cfg.uci_on_pusch_present) { - if (pusch_cfg.uci_on_pusch.type() == setup_release_c::types_opts::setup) { - if (pusch_cfg.uci_on_pusch.setup().beta_offsets_present) { - if (pusch_cfg.uci_on_pusch.setup().beta_offsets.type() == - uci_on_pusch_s::beta_offsets_c_::types_opts::semi_static) { - srsran_beta_offsets_t beta_offsets; - if (make_phy_beta_offsets(pusch_cfg.uci_on_pusch.setup().beta_offsets.semi_static(), &beta_offsets) == true) { - phy_cfg.pusch.beta_offsets = beta_offsets; - } else { - logger.warning("Warning while building beta_offsets structure"); - return false; - } - } else { - logger.warning("Option beta_offsets not of type semi_static"); - return false; - } - if (make_phy_pusch_scaling(pusch_cfg.uci_on_pusch.setup(), &phy_cfg.pusch.scaling) == false) { - logger.warning("Warning while building scaling structure"); - return false; - } - } else { - logger.warning("Option beta_offsets not present"); - return false; - } - } else { - logger.warning("Option uci_on_pusch of type setup"); - return false; - } - } else { - logger.warning("Option uci_on_pusch not present"); - return false; - } - return true; -}; - -bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) -{ - if (sp_cell_cfg.recfg_with_sync_present) { - const recfg_with_sync_s& recfg_with_sync = sp_cell_cfg.recfg_with_sync; - mac->set_crnti(recfg_with_sync.new_ue_id); - if (recfg_with_sync.sp_cell_cfg_common_present) { - if (recfg_with_sync.sp_cell_cfg_common.pci_present) { - phy_cfg.carrier.pci = recfg_with_sync.sp_cell_cfg_common.pci; - phy_cfg.carrier.max_mimo_layers = 1; // TODO: flatten - } else { - logger.warning("Option PCI not present"); - return false; - } - if (recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present) { - if (apply_ul_common_cfg(recfg_with_sync.sp_cell_cfg_common.ul_cfg_common) == false) { - return false; - } - } else { - logger.warning("Secondary primary cell ul cfg common not present"); - return false; - } - if (recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present) { - if (apply_dl_common_cfg(recfg_with_sync.sp_cell_cfg_common.dl_cfg_common) == false) { - return false; - } - } else { - logger.warning("DL cfg common not present"); - return false; - } - if (recfg_with_sync.sp_cell_cfg_common.tdd_ul_dl_cfg_common_present) { - srsran_tdd_config_nr_t tdd; - if (make_phy_tdd_cfg(recfg_with_sync.sp_cell_cfg_common.tdd_ul_dl_cfg_common, &tdd) == true) { - phy_cfg.tdd = tdd; - } else { - logger.warning("Warning while building tdd structure"); - return false; - } - } else { - logger.warning("TDD UL DL config not present"); - return false; - } - } - } else { - logger.warning("Reconfig with with sync not present"); - return false; - } - - // Dedicated config - if (sp_cell_cfg.sp_cell_cfg_ded_present) { - // Dedicated Downlink - if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.type() == - setup_release_c::types_opts::setup) { - if (apply_sp_cell_init_dl_pdcch(sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup()) == false) { - return false; - } - } else { - logger.warning("Option pdcch_cfg not of type setup"); - return false; - } - } else { - logger.warning("Option pdcch_cfg not present"); - return false; - } - if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg.type() == - setup_release_c::types_opts::setup) { - apply_sp_cell_init_dl_pdsch(sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg.setup()); - } else { - logger.warning("Option pdsch_cfg_cfg not of type setup"); - return false; - } - } else { - logger.warning("Option pdsch_cfg not present"); - return false; - } - } else { - logger.warning("Option init_dl_bwp not present"); - return false; - } - // Dedicated Uplink - if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg.type() == - setup_release_c::types_opts::setup) { - if (apply_sp_cell_ded_ul_pucch(sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg.setup()) == false) { - return false; - } - } else { - logger.warning("Option pucch_cfg not of type setup"); - return false; - } - } else { - logger.warning("Option pucch_cfg not present"); - return false; - } - if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg.type() == - setup_release_c::types_opts::setup) { - if (apply_sp_cell_ded_ul_pusch(sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg.setup()) == false) { - return false; - } - } else { - logger.warning("Option pusch_cfg not of type setup"); - return false; - } - } else { - logger.warning("Option pusch_cfg not present"); - return false; - } - } else { - logger.warning("Option init_ul_bwp not present"); - return false; - } - } else { - logger.warning("Option ul_cfg not present"); - return false; - } - - if (sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.type() == - setup_release_c::types_opts::setup) { - dl_harq_cfg_nr_t dl_harq_cfg_nr; - if (make_mac_dl_harq_cfg_nr_t(sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup(), &dl_harq_cfg_nr) == - false) { - logger.warning("Failed to make dl_harq_cfg_nr config"); - return false; - } - mac->set_config(dl_harq_cfg_nr); - } - } else { - logger.warning("Option pdsch_serving_cell_cfg not present"); - return false; - } - - if (sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present) { - if (sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.type() == setup_release_c::types_opts::setup) { - if (apply_csi_meas_cfg(sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup()) == false) { - return false; - } - } else { - logger.warning("Option csi_meas_cfg not of type setup"); - return false; - } - } else { - logger.warning("Option csi_meas_cfg not present"); - return false; - } - - } else { - logger.warning("Option sp_cell_cfg_ded not present"); - return false; - } - phy->set_config(phy_cfg); - mac->start_ra_procedure(); - return true; -} - -bool rrc_nr::apply_phy_cell_group_cfg(const phys_cell_group_cfg_s& phys_cell_group_cfg) -{ - srsran_ue_dl_nr_harq_ack_cfg_t harq_ack; - if (make_phy_harq_ack_cfg(phys_cell_group_cfg, &harq_ack) == true) { - phy_cfg.harq_ack = harq_ack; - } else { - logger.warning("Warning while building harq_ack structure"); - return false; - } - return true; -} - -bool rrc_nr::apply_cell_group_cfg(const cell_group_cfg_s& cell_group_cfg) -{ - if (cell_group_cfg.rlc_bearer_to_add_mod_list_present) { - for (uint32_t i = 0; i < cell_group_cfg.rlc_bearer_to_add_mod_list.size(); i++) { - if (apply_rlc_add_mod(cell_group_cfg.rlc_bearer_to_add_mod_list[i]) == false) { - return false; - } - } - } - if (cell_group_cfg.mac_cell_group_cfg_present) { - if (apply_mac_cell_group(cell_group_cfg.mac_cell_group_cfg) == false) { - return false; - } - } - if (cell_group_cfg.phys_cell_group_cfg_present) { - if (apply_phy_cell_group_cfg(cell_group_cfg.phys_cell_group_cfg) == false) { - return false; - } - } - if (cell_group_cfg.sp_cell_cfg_present) { - if (apply_sp_cell_cfg(cell_group_cfg.sp_cell_cfg) == false) { - return false; - } - } - return true; -} - -bool rrc_nr::apply_drb_release(const uint8_t drb) -{ - uint32_t lcid = get_lcid_for_drbid(drb); - if (lcid == 0) { - logger.warning("Can not release bearer with lcid %d and drb %d", lcid, drb); - return false; - } - logger.info("Releasing bearer DRB: %d LCID: %d", drb, lcid); - pdcp->del_bearer(lcid); - // TODO - // 2> if the UE is operating in EN-DC - // 3> if a new bearer is not added either with NR or E-UTRA with same eps-BearerIdentity: - // 4> indicate the release of the DRB and the eps-BearerIdentity of the released DRB to upper layers. - return true; -} - -bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg) -{ - if (!drb_cfg.pdcp_cfg_present) { - logger.error("Cannot add DRB - incomplete configuration"); - return false; - } - - uint32_t lcid = get_lcid_for_drbid(drb_cfg.drb_id); - - // Setup PDCP - if (!(drb_cfg.pdcp_cfg.drb_present == true)) { - logger.error("PDCP config does not contain DRB config"); - return false; - } - - if (!(drb_cfg.cn_assoc_present == true)) { - logger.error("DRB config does not contain an associated cn"); - return false; - } - - if (!(drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::eps_bearer_id)) { - logger.error("CN association type not supported %s ", drb_cfg.cn_assoc.type().to_string()); - return false; - } - uint32_t eps_bearer_id = drb_cfg.cn_assoc.eps_bearer_id(); - drb_eps_bearer_id[drb_cfg.drb_id] = eps_bearer_id; - - if (drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl_present && drb_cfg.pdcp_cfg.drb.pdcp_sn_size_ul_present && - (drb_cfg.pdcp_cfg.drb.pdcp_sn_size_ul.to_number() != drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl.to_number())) { - logger.warning("PDCP SN size in UL and DL are not the same. make_drb_pdcp_config_t will use the DL SN size %d ", - drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl.to_number()); - } - - srsran::pdcp_config_t pdcp_cfg = make_drb_pdcp_config_t(drb_cfg.drb_id, true, drb_cfg.pdcp_cfg); - pdcp->add_bearer(lcid, pdcp_cfg); - gw->update_lcid(eps_bearer_id, lcid); - return true; -} - -bool rrc_nr::apply_security_cfg(const security_cfg_s& security_cfg) -{ - // TODO derive correct keys - if (security_cfg.key_to_use_present) { - if (security_cfg.key_to_use.value != security_cfg_s::key_to_use_opts::options::secondary) { - logger.warning("Only secondary key supported yet"); - return false; - } - } - - if (security_cfg.security_algorithm_cfg_present) { - switch (security_cfg.security_algorithm_cfg.ciphering_algorithm) { - case ciphering_algorithm_e::nea0: - sec_cfg.cipher_algo = CIPHERING_ALGORITHM_ID_EEA0; - break; - case ciphering_algorithm_e::nea1: - sec_cfg.cipher_algo = CIPHERING_ALGORITHM_ID_128_EEA1; - break; - case ciphering_algorithm_e::nea2: - sec_cfg.cipher_algo = CIPHERING_ALGORITHM_ID_128_EEA2; - break; - case ciphering_algorithm_e::nea3: - sec_cfg.cipher_algo = CIPHERING_ALGORITHM_ID_128_EEA3; - break; - default: - logger.warning("Unsupported algorithm %s", security_cfg.security_algorithm_cfg.ciphering_algorithm.to_string()); - return false; - } - - if (security_cfg.security_algorithm_cfg.integrity_prot_algorithm_present) { - switch (security_cfg.security_algorithm_cfg.integrity_prot_algorithm) { - case integrity_prot_algorithm_e::nia0: - sec_cfg.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; - break; - case integrity_prot_algorithm_e::nia1: - sec_cfg.integ_algo = INTEGRITY_ALGORITHM_ID_128_EIA1; - break; - case integrity_prot_algorithm_e::nia2: - sec_cfg.integ_algo = INTEGRITY_ALGORITHM_ID_128_EIA2; - break; - case integrity_prot_algorithm_e::nia3: - sec_cfg.integ_algo = INTEGRITY_ALGORITHM_ID_128_EIA3; - break; - default: - logger.warning("Unsupported algorithm %s", - security_cfg.security_algorithm_cfg.integrity_prot_algorithm.to_string()); - return false; - } - } - if (usim->update_nr_context(&sec_cfg) == false) { - return false; - } - } - - // Apply security config for all known NR lcids - for (auto& lcid : lcid_drb) { - pdcp->config_security(lcid.first, sec_cfg); - pdcp->enable_encryption(lcid.first); - } - return true; -} - -bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg) -{ - if (radio_bearer_cfg.drb_to_add_mod_list_present) { - for (uint32_t i = 0; i < radio_bearer_cfg.drb_to_add_mod_list.size(); i++) { - if (apply_drb_add_mod(radio_bearer_cfg.drb_to_add_mod_list[i]) == false) { - return false; - } - } - } - if (radio_bearer_cfg.drb_to_release_list_present) { - for (uint32_t i = 0; i < radio_bearer_cfg.drb_to_release_list.size(); i++) { - if (apply_drb_release(radio_bearer_cfg.drb_to_release_list[i]) == false) { - return false; - } - } - } - if (radio_bearer_cfg.security_cfg_present) { - if (apply_security_cfg(radio_bearer_cfg.security_cfg) == false) { - return false; - } - } - return true; -} -// RLC interface -void rrc_nr::max_retx_attempted() {} - -// MAC interface -void rrc_nr::ra_completed() {} -void rrc_nr::ra_problem() -{ - rrc_eutra->nr_scg_failure_information(scg_failure_cause_t::random_access_problem); -} - -void rrc_nr::release_pucch_srs() {} - -// STACK interface -void rrc_nr::cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell) -{} - -/* Procedures */ -rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr* parent_) : rrc_ptr(parent_), initiator(nr) {} - -proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator_t initiator_, - const bool endc_release_and_add_r15, - const bool nr_secondary_cell_group_cfg_r15_present, - const asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - const bool sk_counter_r15_present, - const uint32_t sk_counter_r15, - const bool nr_radio_bearer_cfg1_r15_present, - const asn1::dyn_octstring nr_radio_bearer_cfg1_r15) -{ - Info("Starting..."); - initiator = initiator_; - - rrc_recfg_s rrc_recfg; - cell_group_cfg_s cell_group_cfg; - radio_bearer_cfg_s radio_bearer_cfg; - asn1::SRSASN_CODE err; - - if (nr_secondary_cell_group_cfg_r15_present) { - cbit_ref bref(nr_secondary_cell_group_cfg_r15.data(), nr_secondary_cell_group_cfg_r15.size()); - err = rrc_recfg.unpack(bref); - if (err != asn1::SRSASN_SUCCESS) { - Error("Could not unpack NR reconfiguration message."); - return proc_outcome_t::error; - } - - rrc_ptr->log_rrc_message( - "RRC NR Reconfiguration", Rx, nr_secondary_cell_group_cfg_r15, rrc_recfg, "NR Secondary Cell Group Cfg R15"); - - if (rrc_recfg.crit_exts.type() != asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg) { - Error("Reconfiguration does not contain Secondary Cell Group Config"); - return proc_outcome_t::error; - } - - if (not rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group_present) { - Error("Reconfiguration does not contain Secondary Cell Group Config"); - return proc_outcome_t::error; - } - - cbit_ref bref0(rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.data(), - rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group.size()); - - err = cell_group_cfg.unpack(bref0); - if (err != asn1::SRSASN_SUCCESS) { - Error("Could not unpack cell group message message."); - return proc_outcome_t::error; - } - - rrc_ptr->log_rrc_message("RRC NR Reconfiguration", - Rx, - rrc_recfg.crit_exts.rrc_recfg().secondary_cell_group, - cell_group_cfg, - "Secondary Cell Group Config"); - - Info("Applying Cell Group Cfg"); - if (!rrc_ptr->apply_cell_group_cfg(cell_group_cfg)) { - return proc_outcome_t::error; - } - } - - if (sk_counter_r15_present) { - Info("Applying Cell Group Cfg"); - if (!rrc_ptr->configure_sk_counter((uint16_t)sk_counter_r15)) { - return proc_outcome_t::error; - } - } - - if (nr_radio_bearer_cfg1_r15_present) { - cbit_ref bref1(nr_radio_bearer_cfg1_r15.data(), nr_radio_bearer_cfg1_r15.size()); - - err = radio_bearer_cfg.unpack(bref1); - if (err != asn1::SRSASN_SUCCESS) { - Error("Could not unpack radio bearer config."); - return proc_outcome_t::error; - } - - rrc_ptr->log_rrc_message( - "RRC NR Reconfiguration", Rx, nr_radio_bearer_cfg1_r15, radio_bearer_cfg, "Radio Bearer Config R15"); - - Info("Applying Radio Bearer Cfg"); - if (!rrc_ptr->apply_radio_bearer_cfg(radio_bearer_cfg)) { - return proc_outcome_t::error; - } - } - - return proc_outcome_t::success; -} - -proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::react(const bool& config_complete) -{ - if (not config_complete) { - Error("NR reconfiguration failed"); - return proc_outcome_t::error; - } - - // TODO phy ctrl - // in case there are scell to configure, wait for second phy configuration - // if (not rrc_ptr->phy_ctrl->is_config_pending()) { - // return proc_outcome_t::yield; - // } - - Info("Reconfig NR return successful"); - return proc_outcome_t::success; -} - -void rrc_nr::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& result) -{ - if (result.is_success()) { - Info("Finished %s successfully", name()); - srsran::console("RRC NR reconfiguration successful.\n"); - rrc_ptr->rrc_eutra->nr_rrc_con_reconfig_complete(true); - } else { - // 5.3.5.8.2 Inability to comply with RRCReconfiguration - switch (initiator) { - case reconf_initiator_t::mcg_srb1: - rrc_ptr->rrc_eutra->nr_notify_reconfiguration_failure(); - break; - default: - Warning("Reconfiguration failure not implemented for initiator %d", initiator); - break; - } - srsran::console("RRC NR reconfiguration failed.\n"); - Warning("Finished %s with failure", name()); - } - return; -} - -} // namespace srsue diff --git a/srsue/src/stack/rrc/rrc_procedures.cc b/srsue/src/stack/rrc/rrc_procedures.cc index 6698f710d..8ff1013fa 100644 --- a/srsue/src/stack/rrc/rrc_procedures.cc +++ b/srsue/src/stack/rrc/rrc_procedures.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -50,7 +50,7 @@ proc_outcome_t rrc::cell_search_proc::init() { Info("Starting..."); state = state_t::phy_cell_search; - if (not rrc_ptr->phy_ctrl->start_cell_search(rrc_ptr->cell_searcher)) { + if (not rrc_ptr->phy_ctrl->start_cell_search(rrc_ptr->cell_searcher, rrc_ptr->cell_search_earfcn)) { Warning("Failed to initiate Cell Search."); return proc_outcome_t::error; } @@ -943,6 +943,60 @@ srsran::proc_outcome_t rrc::connection_request_proc::react(const cell_selection_ } } +/****************************************** + * Connection Setup Procedure + *****************************************/ + +// Simple procedure mainly do defer the transmission of the SetupComplete until all PHY reconfiguration are done +rrc::connection_setup_proc::connection_setup_proc(srsue::rrc* parent_) : + rrc_ptr(parent_), logger(srslog::fetch_basic_logger("RRC")) +{} + +srsran::proc_outcome_t rrc::connection_setup_proc::init(const asn1::rrc::rr_cfg_ded_s* cnfg_, + srsran::unique_byte_buffer_t dedicated_info_nas_) +{ + Info("Starting..."); + + if (dedicated_info_nas_.get() == nullptr) { + rrc_ptr->logger.error("Connection Setup Failed, no dedicatedInfoNAS available"); + return proc_outcome_t::error; + } + + dedicated_info_nas = std::move(dedicated_info_nas_); + + // Apply the Radio Resource configuration + if (!rrc_ptr->apply_rr_config_dedicated(cnfg_)) { + return proc_outcome_t::error; + } + + rrc_ptr->nas->set_barring(srsran::barring_t::none); + + // No phy config was scheduled, run config completion immediately + if (rrc_ptr->phy_ctrl->is_config_pending()) { + return react(true); + } + return proc_outcome_t::yield; +} + +srsran::proc_outcome_t rrc::connection_setup_proc::react(const bool& config_complete) +{ + if (not config_complete) { + rrc_ptr->logger.error("Connection Setup Failed"); + return proc_outcome_t::error; + } + + rrc_ptr->send_con_setup_complete(std::move(dedicated_info_nas)); + return proc_outcome_t::success; +} + +void rrc::connection_setup_proc::then(const srsran::proc_state_t& result) +{ + if (result.is_success()) { + rrc_ptr->logger.info("Finished %s successfully", name()); + return; + } +} + /****************************************** * Connection Reconfiguration Procedure *****************************************/ @@ -1211,11 +1265,14 @@ proc_outcome_t rrc::go_idle_proc::step() void rrc::go_idle_proc::then(const srsran::proc_state_t& result) { - if (rrc_ptr->nas->is_registered() and not rrc_ptr->cell_reselector.launch()) { - rrc_ptr->logger.error("Failed to initiate a Cell Reselection procedure..."); - return; + // only start cell reselection if no RRC redirect is present (redirect will trigger a cell search) + if (rrc_ptr->cell_search_earfcn < 0) { + if (rrc_ptr->nas->is_registered() and not rrc_ptr->cell_reselector.launch()) { + rrc_ptr->logger.error("Failed to initiate a Cell Reselection procedure..."); + return; + } + rrc_ptr->callback_list.add_proc(rrc_ptr->cell_reselector); } - rrc_ptr->callback_list.add_proc(rrc_ptr->cell_reselector); } /************************************** @@ -1316,13 +1373,11 @@ rrc::connection_reest_proc::connection_reest_proc(srsue::rrc* rrc_) : rrc_ptr(rr proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause) { // Save Current RNTI before MAC Reset - mac_interface_rrc::ue_rnti_t uernti; - rrc_ptr->mac->get_rntis(&uernti); - size_t nof_scells_active = rrc_ptr->phy_ctrl->current_config_scells().count(); + uint16_t crnti = rrc_ptr->mac->get_crnti(); + size_t nof_scells_active = rrc_ptr->phy_ctrl->current_config_scells().count(); // 5.3.7.1 - Conditions for Reestablishment procedure - if (not rrc_ptr->security_is_activated or rrc_ptr->state != RRC_STATE_CONNECTED or - uernti.crnti == SRSRAN_INVALID_RNTI) { + if (not rrc_ptr->security_is_activated or rrc_ptr->state != RRC_STATE_CONNECTED or crnti == SRSRAN_INVALID_RNTI) { Warning("Conditions are NOT met to start procedure."); return proc_outcome_t::error; } @@ -1334,16 +1389,16 @@ proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause) reest_source_pci = rrc_ptr->ho_handler.get()->ho_src_cell.pci; reest_source_freq = rrc_ptr->ho_handler.get()->ho_src_cell.earfcn; } else { - reest_rnti = uernti.crnti; + reest_rnti = crnti; reest_source_pci = rrc_ptr->meas_cells.serving_cell().get_pci(); // needed for reestablishment with another cell reest_source_freq = rrc_ptr->meas_cells.serving_cell().get_earfcn(); } reest_cellid = rrc_ptr->meas_cells.find_cell(reest_source_freq, reest_source_pci)->get_cell_id(); Info("Starting... cause: \"%s\", UE context: {C-RNTI=0x%x, PCI=%d, CELL ID=%d}", - reest_cause == asn1::rrc::reest_cause_opts::recfg_fail - ? "Reconfiguration failure" - : cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure" : "Other failure", + reest_cause == asn1::rrc::reest_cause_opts::recfg_fail ? "Reconfiguration failure" + : cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure" + : "Other failure", reest_rnti, reest_source_pci, reest_cellid); @@ -1509,6 +1564,8 @@ srsran::proc_outcome_t rrc::connection_reest_proc::react(const asn1::rrc::rrc_co // 1> perform the measurement related actions as specified in 5.5.6.1; rrc_ptr->measurements->ho_reest_actions(rrc_ptr->get_serving_cell()->get_earfcn(), rrc_ptr->get_serving_cell()->get_earfcn()); + // Update PHY measurements after HO Reestablishment actions. + rrc_ptr->measurements->update_phy(); // 1> submit the RRCConnectionReestablishmentComplete message to lower layers for transmission, upon which the // procedure ends; @@ -1606,15 +1663,16 @@ srsran::proc_outcome_t rrc::ho_proc::init(const asn1::rrc::rrc_conn_recfg_s& rrc // Save serving cell and current configuration ho_src_cell = rrc_ptr->meas_cells.serving_cell().phy_cell; - mac_interface_rrc::ue_rnti_t uernti; - rrc_ptr->mac->get_rntis(&uernti); - ho_src_rnti = uernti.crnti; + ho_src_rnti = rrc_ptr->mac->get_crnti(); // Section 5.3.5.4 rrc_ptr->t310.stop(); rrc_ptr->t304.set(mob_ctrl_info->t304.to_number(), [this](uint32_t tid) { rrc_ptr->timer_expired(tid); }); rrc_ptr->t304.run(); + // Indicate RLF-Report that a new HO has been received + rrc_ptr->var_rlf_report.received_ho_command(rrc_ptr->meas_cells.serving_cell().get_cell_id_bit()); + // starting at start synchronising to the DL of the target PCell rrc_ptr->set_serving_cell(target_cell, false); Info("Starting cell selection of target cell PCI=%d EARFCN=%d", target_cell.pci, target_cell.earfcn); @@ -1691,22 +1749,22 @@ srsran::proc_outcome_t rrc::ho_proc::init(const asn1::rrc::rrc_conn_recfg_s& rrc // perform the measurement related actions as specified in 5.5.6.1; rrc_ptr->measurements->ho_reest_actions(ho_src_cell.earfcn, target_earfcn); - // if the RRCConnectionReconfiguration message includes the measConfig: - if (not rrc_ptr->measurements->parse_meas_config(&recfg_r8, true, ho_src_cell.earfcn)) { - Error("Parsing measurementConfig. TODO: Send ReconfigurationReject"); - return proc_outcome_t::yield; // wait for t304 expiry - } + // Do not update PHY measurements here since it will be updated after the HO procedure finishes - // Have RRCReconfComplete message ready when Msg3 is sent - rrc_ptr->send_rrc_con_reconfig_complete(); + // Note: We delay the enqueuing of RRC Reconf Complete message to avoid that the message goes in an UL grant + // directed at the old RNTI. + rrc_ptr->task_sched.defer_callback(4, [this]() { + // Have RRCReconfComplete message ready when Msg3 is sent + rrc_ptr->send_rrc_con_reconfig_complete(); - // SCell addition/removal can take some time to compute. Enqueue in a background task and do it in the end. - rrc_ptr->apply_scell_config(&recfg_r8, false); + // SCell addition/removal can take some time to compute. Enqueue in a background task and do it in the end. + rrc_ptr->apply_scell_config(&recfg_r8, false); - // Send PDCP status report if configured - rrc_ptr->pdcp->send_status_report(); + // Send PDCP status report if configured + rrc_ptr->pdcp->send_status_report(); - Info("Finished HO configuration. Waiting PHY to synchronize with target cell"); + Info("Finished HO configuration. Waiting PHY to synchronize with target cell"); + }); return proc_outcome_t::yield; } @@ -1728,6 +1786,12 @@ srsran::proc_outcome_t rrc::ho_proc::react(ra_completed_ev ev) if (ev.success) { Info("Random Access completed. Applying final configuration and finishing procedure"); + // if the RRCConnectionReconfiguration message includes the measConfig: + if (not rrc_ptr->measurements->parse_meas_config(&recfg_r8, true, ho_src_cell.earfcn)) { + Error("Parsing measurementConfig. TODO: Send ReconfigurationReject"); + return proc_outcome_t::yield; // wait for t304 expiry + } + // TS 36.331, sec. 5.3.5.4, last "1>" rrc_ptr->t304.stop(); rrc_ptr->apply_rr_config_dedicated_on_ho_complete(recfg_r8.rr_cfg_ded); @@ -1745,6 +1809,8 @@ void rrc::ho_proc::then(const srsran::proc_state_t& result) srsran::console("HO %ssuccessful\n", result.is_success() ? "" : "un"); rrc_ptr->t304.stop(); + + rrc_ptr->measurements->update_phy(); } } // namespace srsue diff --git a/srsue/src/stack/rrc/rrc_rlf_report.cc b/srsue/src/stack/rrc/rrc_rlf_report.cc new file mode 100644 index 000000000..e4ddf9e59 --- /dev/null +++ b/srsue/src/stack/rrc/rrc_rlf_report.cc @@ -0,0 +1,145 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/stack/rrc/rrc_rlf_report.h" +#include "srsran/rrc/rrc_common.h" + +namespace srsue { + +void rrc_rlf_report::init(srsran::task_sched_handle task_sched) +{ + timer_conn_failure = task_sched.get_unique_timer(); +} + +// Returns true if VarRLF-Report structure has info available +bool rrc_rlf_report::has_info() +{ + return has_event; +} + +// Called upon T304 expiry (type == hof) or Detection of radio link failure (type == rlf) +void rrc_rlf_report::set_failure(meas_cell_list& meas_cells, failure_type_t type) +{ + has_event = true; + + // clear the information included in VarRLF-Report, if any + rlf_report = {}; + + // set the plmn-Identity to the RPLMN + + // set the measResultLastServCell to include the RSRP and RSRQ, if available, of the PCell based on + // measurements collected up to the moment the UE detected radio link failure + rlf_report.meas_result_last_serv_cell_r9.rsrp_result_r9 = + rrc_value_to_range(srsran::quant_rsrp, meas_cells.serving_cell().get_rsrp()); + rlf_report.meas_result_last_serv_cell_r9.rsrq_result_r9 = + rrc_value_to_range(srsran::quant_rsrq, meas_cells.serving_cell().get_rsrq()); + rlf_report.meas_result_last_serv_cell_r9.rsrq_result_r9_present = true; + + // set the measResultNeighCells to include the best measured cells, other than the PCell, ordered such that + // the best cell is listed first, and based on measurements collected up to the moment the UE detected radio + // link failure + if (meas_cells.nof_neighbours() > 0) { + rlf_report.meas_result_neigh_cells_r9_present = true; + rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present = true; + rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.clear(); + meas_cells.sort_neighbour_cells(); + // It is not clear how the sorting and grouping of cells per frequency must be done. + // We use a separate MeasResultList2EUTRA-r9 struct for each pci/frequency pair + for (const auto& f : meas_cells) { + meas_result2_eutra_r9_s meas2 = {}; + meas2.carrier_freq_r9 = f->get_earfcn(); + meas_result_eutra_s meas = {}; + meas.pci = f->get_pci(); + meas.meas_result.rsrp_result_present = true; + meas.meas_result.rsrq_result_present = true; + meas.meas_result.rsrp_result = rrc_value_to_range(srsran::quant_rsrp, f->get_rsrp()); + meas.meas_result.rsrq_result = rrc_value_to_range(srsran::quant_rsrq, f->get_rsrq()); + meas2.meas_result_list_r9.push_back(meas); + rlf_report.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.push_back(meas2); + } + } + + // set the failedPCellId to the global cell identity, if available, and otherwise to the physical cell identity and + // carrier frequency of the PCell where radio link failure is detected; + rlf_report.failed_pcell_id_r10.set_present(true); + if (meas_cells.serving_cell().has_sib1()) { + rlf_report.failed_pcell_id_r10->set_cell_global_id_r10().cell_id = meas_cells.serving_cell().get_cell_id_bit(); + rlf_report.failed_pcell_id_r10->cell_global_id_r10().plmn_id = meas_cells.serving_cell().get_plmn_asn1(0); + } else { + rlf_report.failed_pcell_id_r10->set_pci_arfcn_r10(); + rlf_report.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 = meas_cells.serving_cell().get_pci(); + rlf_report.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = meas_cells.serving_cell().get_earfcn(); + } + + // if an RRCConnectionReconfiguration message including the mobilityControlInfo was received before the + // connection failure + if (timer_conn_failure.is_running()) { + timer_conn_failure.stop(); + + // include previousPCellId and set it to the global cell identity of the PCell where the last + // RRCConnectionReconfiguration including the mobilityControlInfo message was received; + rlf_report.prev_pcell_id_r10.set_present(true); + rlf_report.prev_pcell_id_r10->cell_id = ho_gci; + rlf_report.prev_pcell_id_r10->plmn_id = meas_cells.serving_cell().get_plmn_asn1(0); + + // set the timeConnFailure to the elapsed time since reception of the last + // RRCConnectionReconfiguration message including the mobilityControlInfo; + rlf_report.time_conn_fail_r10_present = true; + rlf_report.time_conn_fail_r10 = timer_conn_failure.time_elapsed() / 100; // 1 unit = 100 ms + } + + // set the connectionFailureType + rlf_report.conn_fail_type_r10_present = true; + rlf_report.conn_fail_type_r10 = + type == rlf ? rlf_report_r9_s::conn_fail_type_r10_opts::rlf : rlf_report_r9_s::conn_fail_type_r10_opts::hof; + + rlf_report.ext = true; +} + +void rrc_rlf_report::set_reest_gci(const asn1::fixed_bitstring<28>& gci, const asn1::rrc::plmn_id_s& plmn_id) +{ + rlf_report.reest_cell_id_r10.set_present(true); + rlf_report.reest_cell_id_r10->cell_id = gci; + rlf_report.reest_cell_id_r10->plmn_id = plmn_id; +} + +void rrc_rlf_report::received_ho_command(const asn1::fixed_bitstring<28>& current_gci) +{ + if (timer_conn_failure.is_valid()) { + timer_conn_failure.stop(); + timer_conn_failure.run(); + ho_gci = current_gci; + } +} + +rlf_report_r9_s rrc_rlf_report::get_report() +{ + return rlf_report; +} + +// Clears VarRLF-Report contents +void rrc_rlf_report::clear() +{ + has_event = false; + rlf_report = {}; +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/rrc/test/CMakeLists.txt b/srsue/src/stack/rrc/test/CMakeLists.txt new file mode 100644 index 000000000..be6e5f52a --- /dev/null +++ b/srsue/src/stack/rrc/test/CMakeLists.txt @@ -0,0 +1,39 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(rrc_reconfig_test rrc_reconfig_test.cc) +target_link_libraries(rrc_reconfig_test srsue_upper srsran_phy srsran_asn1 rrc_asn1 srslog) +add_test(rrc_reconfig_test rrc_reconfig_test) + +add_executable(rrc_meas_test rrc_meas_test.cc) +target_link_libraries(rrc_meas_test srsue_rrc srsue_upper srsran_pdcp srsran_phy rrc_asn1 rrc_nr_asn1) +add_test(rrc_meas_test rrc_meas_test) + +add_executable(rrc_phy_ctrl_test rrc_phy_ctrl_test.cc) +target_link_libraries(rrc_phy_ctrl_test srsran_common srsue_rrc ${ATOMIC_LIBS}) +add_test(rrc_phy_ctrl_test rrc_phy_ctrl_test) + +add_executable(rrc_cell_test rrc_cell_test.cc) +target_link_libraries(rrc_cell_test srsue_rrc srsue_upper srsran_pdcp srsran_phy rrc_asn1 rrc_nr_asn1) +add_test(rrc_cell_test rrc_cell_test) + +add_executable(rrc_rlf_report_test rrc_rlf_report_test.cc) +target_link_libraries(rrc_rlf_report_test srsue_rrc srsue_upper srsran_pdcp srsran_phy rrc_asn1 rrc_nr_asn1) +add_test(rrc_rlf_report_test rrc_rlf_report_test) \ No newline at end of file diff --git a/srsue/test/upper/rrc_cell_test.cc b/srsue/src/stack/rrc/test/rrc_cell_test.cc similarity index 97% rename from srsue/test/upper/rrc_cell_test.cc rename to srsue/src/stack/rrc/test/rrc_cell_test.cc index 3d68eaf8f..579695f15 100644 --- a/srsue/test/upper/rrc_cell_test.cc +++ b/srsue/src/stack/rrc/test/rrc_cell_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -66,6 +66,7 @@ int test_add_neighbours() TESTASSERT(list.get_neighbour_cell_handle(0, 0) == nullptr); phy_meas_t pmeas; + pmeas.rat = srsran::srsran_rat_t::lte; pmeas.cfo_hz = 4; pmeas.rsrp = -20; pmeas.pci = 1; diff --git a/srsue/test/upper/rrc_meas_test.cc b/srsue/src/stack/rrc/test/rrc_meas_test.cc similarity index 96% rename from srsue/test/upper/rrc_meas_test.cc rename to srsue/src/stack/rrc/test/rrc_meas_test.cc index 81523a2d9..3a9c1ea9d 100644 --- a/srsue/test/upper/rrc_meas_test.cc +++ b/srsue/src/stack/rrc/test/rrc_meas_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -26,7 +26,7 @@ #include "srsran/upper/pdcp.h" #include "srsue/hdr/stack/rrc/rrc.h" #include "srsue/hdr/stack/rrc/rrc_meas.h" -#include "srsue/hdr/stack/rrc/rrc_nr.h" +#include "srsue/hdr/stack/rrc_nr/rrc_nr.h" #include "srsue/hdr/stack/upper/nas.h" #include @@ -49,7 +49,7 @@ public: void set_config_mbsfn_sib2(srsran::mbsfn_sf_cfg_t* cfg_list, uint32_t nof_cfgs) override {} void set_config_mbsfn_sib13(const srsran::sib13_t& sib13) override {} void set_config_mbsfn_mcch(const srsran::mcch_msg_t& mcch) override {} - bool cell_search() override { return true; } + bool cell_search(int earfcn) override { return true; } bool cell_is_camping() override { return true; } void deactivate_scells() override {} bool cell_select(phy_cell_t cell) override @@ -57,7 +57,6 @@ public: last_selected_cell = cell; return true; } - void enable_pregen_signals(bool enable) override {} void set_cells_to_meas(uint32_t earfcn, const std::set& pci) override { @@ -111,9 +110,9 @@ class mac_test : public srsue::mac_interface_rrc { public: srsran::task_sched_handle task_sched; - rrc* rrc_ptr; + srsue::rrc* rrc_ptr; - mac_test(rrc* rrc_, srsran::task_sched_handle task_sched_) : rrc_ptr(rrc_), task_sched(task_sched_) {} + mac_test(srsue::rrc* rrc_, srsran::task_sched_handle task_sched_) : rrc_ptr(rrc_), task_sched(task_sched_) {} int get_dlsch_with_sib1(bcch_dl_sch_msg_s& dlsch_msg) { @@ -165,9 +164,9 @@ public: void set_config(srsran::sr_cfg_t& sr_cfg) override {} void set_rach_ded_cfg(uint32_t preamble_index, uint32_t prach_mask) override {} - void get_rntis(ue_rnti_t* rntis) override {} - void set_contention_id(uint64_t uecri) override {} - void set_ho_rnti(uint16_t crnti, uint16_t target_pci) override {} + uint16_t get_crnti() override { return 0; } + void set_contention_id(uint64_t uecri) override {} + void set_ho_rnti(uint16_t crnti, uint16_t target_pci) override {} void reconfiguration(const uint32_t& cc_idx, const bool& enable) override {} void reset() override {} @@ -177,27 +176,22 @@ class rrc_nr_test final : public srsue::rrc_nr_interface_rrc { public: ~rrc_nr_test() = default; - void get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps) override{}; - void get_nr_capabilities(srsran::byte_buffer_t* nr_cap) override{}; + int get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps) override { return SRSRAN_SUCCESS; }; + int get_nr_capabilities(srsran::byte_buffer_t* nr_cap) override { return SRSRAN_SUCCESS; }; void phy_set_cells_to_meas(uint32_t carrier_freq_r15) override{}; void phy_meas_stop() override{}; - bool rrc_reconfiguration(bool endc_release_and_add_r15, - bool nr_secondary_cell_group_cfg_r15_present, - asn1::dyn_octstring nr_secondary_cell_group_cfg_r15, - bool sk_counter_r15_present, - uint32_t sk_counter_r15, - bool nr_radio_bearer_cfg1_r15_present, - asn1::dyn_octstring nr_radio_bearer_cfg1_r15) override + bool rrc_reconfiguration(bool endc_release_and_add_r15, const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf) override { return false; - }; + } + void rrc_release() override {} bool is_config_pending() override { return false; }; }; class nas_test : public srsue::nas { public: - nas_test(srsran::task_sched_handle t) : srsue::nas(t) {} + nas_test(srsran::task_sched_handle t) : srsue::nas(srslog::fetch_basic_logger("NAS"), t) {} bool is_registered() override { return false; } }; @@ -254,7 +248,7 @@ private: bool meas_res_received = false; }; -class rrc_test : public rrc +class rrc_test : public srsue::rrc { stack_test_dummy* stack = nullptr; @@ -683,18 +677,18 @@ int meas_obj_test() rrc_meas_logger.info("Test7: PHY finds new neighbours in frequency 1 and 2, check RRC instructs to search them"); std::vector phy_meas = {}; - phy_meas.push_back({0, 0, 0.0f, 1, 31}); - phy_meas.push_back({-1, 0, 0.0f, 1, 32}); - phy_meas.push_back({-2, 0, 0.0f, 1, 33}); - phy_meas.push_back({-3, 0, 0.0f, 1, 34}); + phy_meas.push_back({srsran::srsran_rat_t::lte, 0, 0, 0.0f, 1, 31}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -1, 0, 0.0f, 1, 32}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -2, 0, 0.0f, 1, 33}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -3, 0, 0.0f, 1, 34}); rrctest.new_cell_meas(phy_meas); rrctest.run_tti(1); phy_meas = {}; - phy_meas.push_back({-4, 0, 0.0f, 1, 35}); - phy_meas.push_back({-5, 0, 0.0f, 1, 36}); - phy_meas.push_back({-6, 0, 0.0f, 1, 37}); - phy_meas.push_back({1, 0, 0.0f, 1, 30}); - phy_meas.push_back({0, 0, 0.0f, 2, 31}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -4, 0, 0.0f, 1, 35}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -5, 0, 0.0f, 1, 36}); + phy_meas.push_back({srsran::srsran_rat_t::lte, -6, 0, 0.0f, 1, 37}); + phy_meas.push_back({srsran::srsran_rat_t::lte, 1, 0, 0.0f, 1, 30}); + phy_meas.push_back({srsran::srsran_rat_t::lte, 0, 0, 0.0f, 2, 31}); rrctest.new_cell_meas(phy_meas); rrctest.run_tti(1); @@ -815,7 +809,7 @@ void send_report(rrc_test& rrctest, if (earfcn.size() == pci.size()) { e = earfcn[i]; } - phy_meas.push_back({r, -5, 0.0f, e, pci[i]}); + phy_meas.push_back({srsran::srsran_rat_t::lte, r, -5, 0.0f, e, pci[i]}); } rrctest.new_cell_meas(phy_meas); rrctest.run_tti(1); diff --git a/srsue/test/upper/rrc_phy_ctrl_test.cc b/srsue/src/stack/rrc/test/rrc_phy_ctrl_test.cc similarity index 98% rename from srsue/test/upper/rrc_phy_ctrl_test.cc rename to srsue/src/stack/rrc/test/rrc_phy_ctrl_test.cc index afbbca024..00e881869 100644 --- a/srsue/test/upper/rrc_phy_ctrl_test.cc +++ b/srsue/src/stack/rrc/test/rrc_phy_ctrl_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -90,7 +90,7 @@ int test_phy_ctrl_fsm() TESTASSERT(phy_ctrl.is_in_sync()); // TEST: Correct initiation of Cell Search state - TESTASSERT(phy_ctrl.start_cell_search(csearch_tester)); + TESTASSERT(phy_ctrl.start_cell_search(csearch_tester, -1)); TESTASSERT(not phy_ctrl.is_in_sync()); // TEST: Cell Search only listens to a cell search result event diff --git a/srsue/test/upper/rrc_reconfig_test.cc b/srsue/src/stack/rrc/test/rrc_reconfig_test.cc similarity index 99% rename from srsue/test/upper/rrc_reconfig_test.cc rename to srsue/src/stack/rrc/test/rrc_reconfig_test.cc index 5535c0338..e9f5b0a0a 100644 --- a/srsue/test/upper/rrc_reconfig_test.cc +++ b/srsue/src/stack/rrc/test/rrc_reconfig_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/src/stack/rrc/test/rrc_rlf_report_test.cc b/srsue/src/stack/rrc/test/rrc_rlf_report_test.cc new file mode 100644 index 000000000..d6569b5cf --- /dev/null +++ b/srsue/src/stack/rrc/test/rrc_rlf_report_test.cc @@ -0,0 +1,205 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsue/hdr/stack/rrc/rrc_cell.h" +#include "srsue/hdr/stack/rrc/rrc_rlf_report.h" + +using namespace srsue; + +int test_single() +{ + srsran::task_scheduler task_sched; + rrc_rlf_report rlf_report; + meas_cell_list list{&task_sched}; + + phy_meas_t pmeas{}; + pmeas.rsrp = -20; + pmeas.pci = 1; + pmeas.earfcn = 3400; + pmeas.rsrq = -10; + + list.add_meas_cell(pmeas); + list.set_serving_cell(phy_cell_t{1, 3400}, false); + + rlf_report.set_failure(list, rrc_rlf_report::rlf); + + asn1::json_writer jw; + asn1::rrc::rlf_report_r9_s out = rlf_report.get_report(); + out.to_json(jw); + printf("test_single: %s\n", jw.to_string().c_str()); + + TESTASSERT(!out.meas_result_neigh_cells_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.size() == 0); + TESTASSERT(out.failed_pcell_id_r10.is_present()); + TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 == 1); + TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = 3400); + TESTASSERT(out.conn_fail_type_r10_present); + TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::rlf); + + return SRSRAN_SUCCESS; +} + +int test_neighbours() +{ + srsran::task_scheduler task_sched; + rrc_rlf_report rlf_report; + meas_cell_list list{&task_sched}; + + phy_meas_t pmeas{}; + pmeas.rsrp = -20; + pmeas.pci = 1; + pmeas.earfcn = 3400; + pmeas.rsrq = -10; + + list.add_meas_cell(pmeas); + pmeas.pci = 4; + list.add_meas_cell(pmeas); + pmeas.pci = 6; + list.add_meas_cell(pmeas); + list.set_serving_cell(phy_cell_t{4, 3400}, false); + + TESTASSERT(!rlf_report.has_info()); + + rlf_report.set_failure(list, rrc_rlf_report::hof); + + asn1::json_writer jw; + asn1::rrc::rlf_report_r9_s out = rlf_report.get_report(); + out.to_json(jw); + printf("test_neighbours: %s\n", jw.to_string().c_str()); + + TESTASSERT(out.meas_result_neigh_cells_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9.size() == 2); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[0].carrier_freq_r9 = 3400); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[0].meas_result_list_r9[0].pci == 1); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[1].carrier_freq_r9 = 3400); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9[1].meas_result_list_r9[0].pci == 6); + TESTASSERT(out.failed_pcell_id_r10.is_present()); + TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().pci_r10 == 4); + TESTASSERT(out.failed_pcell_id_r10->pci_arfcn_r10().carrier_freq_r10 = 3400); + TESTASSERT(out.conn_fail_type_r10_present); + TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof); + + TESTASSERT(rlf_report.has_info()); + + return SRSRAN_SUCCESS; +} + +int test_reest() +{ + srsran::task_scheduler task_sched; + rrc_rlf_report rlf_report; + meas_cell_list list{&task_sched}; + + phy_meas_t pmeas{}; + pmeas.rsrp = -20; + pmeas.pci = 1; + pmeas.earfcn = 3400; + pmeas.rsrq = -10; + + list.add_meas_cell(pmeas); + pmeas.pci = 4; + list.add_meas_cell(pmeas); + pmeas.pci = 6; + list.add_meas_cell(pmeas); + list.set_serving_cell(phy_cell_t{4, 3400}, false); + + TESTASSERT(!rlf_report.has_info()); + + rlf_report.set_failure(list, rrc_rlf_report::hof); + rlf_report.set_reest_gci(list.serving_cell().get_cell_id_bit(), list.serving_cell().get_plmn_asn1(0)); + + asn1::json_writer jw; + asn1::rrc::rlf_report_r9_s out = rlf_report.get_report(); + out.to_json(jw); + printf("test_reest: %s\n", jw.to_string().c_str()); + + TESTASSERT(out.meas_result_neigh_cells_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present); + TESTASSERT(out.failed_pcell_id_r10.is_present()); + TESTASSERT(out.conn_fail_type_r10_present); + TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof); + TESTASSERT(out.reest_cell_id_r10.is_present()); + + TESTASSERT(rlf_report.has_info()); + + return SRSRAN_SUCCESS; +} + +int test_ho() +{ + srsran::task_scheduler task_sched; + rrc_rlf_report rlf_report; + meas_cell_list list{&task_sched}; + + rlf_report.init(&task_sched); + + phy_meas_t pmeas{}; + pmeas.rsrp = -20; + pmeas.pci = 1; + pmeas.earfcn = 3400; + pmeas.rsrq = -10; + + list.add_meas_cell(pmeas); + pmeas.pci = 4; + list.add_meas_cell(pmeas); + pmeas.pci = 6; + list.add_meas_cell(pmeas); + list.set_serving_cell(phy_cell_t{4, 3400}, false); + + TESTASSERT(!rlf_report.has_info()); + + rlf_report.received_ho_command(list.serving_cell().get_cell_id_bit()); + for (int i = 0; i < 1000; i++) { + task_sched.tic(); + task_sched.run_pending_tasks(); + } + rlf_report.set_failure(list, rrc_rlf_report::hof); + rlf_report.set_reest_gci(list.serving_cell().get_cell_id_bit(), list.serving_cell().get_plmn_asn1(0)); + + asn1::json_writer jw; + asn1::rrc::rlf_report_r9_s out = rlf_report.get_report(); + out.to_json(jw); + printf("test_ho: %s\n", jw.to_string().c_str()); + + TESTASSERT(out.meas_result_neigh_cells_r9_present); + TESTASSERT(out.meas_result_neigh_cells_r9.meas_result_list_eutra_r9_present); + TESTASSERT(out.failed_pcell_id_r10.is_present()); + TESTASSERT(out.conn_fail_type_r10_present); + TESTASSERT(out.conn_fail_type_r10.value == asn1::rrc::rlf_report_r9_s::conn_fail_type_r10_e_::hof); + TESTASSERT(out.reest_cell_id_r10.is_present()); + + TESTASSERT(rlf_report.has_info()); + + return SRSRAN_SUCCESS; +} + +int main() +{ + TESTASSERT(test_single() == SRSRAN_SUCCESS); + TESTASSERT(test_neighbours() == SRSRAN_SUCCESS); + TESTASSERT(test_reest() == SRSRAN_SUCCESS); + TESTASSERT(test_ho() == SRSRAN_SUCCESS); + printf("Success\n"); + + return SRSRAN_SUCCESS; +} diff --git a/srsue/src/stack/rrc_nr/CMakeLists.txt b/srsue/src/stack/rrc_nr/CMakeLists.txt new file mode 100644 index 000000000..408a04b10 --- /dev/null +++ b/srsue/src/stack/rrc_nr/CMakeLists.txt @@ -0,0 +1,24 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_subdirectory(test) + +set(SOURCES rrc_nr.cc rrc_nr_procedures.cc ../rrc/rrc_cell.cc) +add_library(srsue_rrc_nr STATIC ${SOURCES}) \ No newline at end of file diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc new file mode 100644 index 000000000..7c5f393c0 --- /dev/null +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -0,0 +1,2374 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/stack/rrc_nr/rrc_nr.h" +#include "srsran/common/band_helper.h" +#include "srsran/common/security.h" +#include "srsran/common/standard_streams.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_rlc_interfaces.h" +#include "srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h" +#include "srsue/hdr/stack/upper/usim.h" + +using namespace asn1::rrc_nr; +using namespace asn1; +using namespace srsran; + +namespace srsue { + +const static char* rrc_nr_state_text[] = {"IDLE", "CONNECTED", "CONNECTED-INACTIVE"}; + +rrc_nr::rrc_nr(srsran::task_sched_handle task_sched_) : + logger(srslog::fetch_basic_logger("RRC-NR")), + task_sched(task_sched_), + conn_recfg_proc(*this), + conn_setup_proc(*this), + setup_req_proc(*this), + cell_selector(*this), + meas_cells(task_sched_) +{ + set_phy_default_config(); +} + +rrc_nr::~rrc_nr() = default; + +int rrc_nr::init(phy_interface_rrc_nr* phy_, + mac_interface_rrc_nr* mac_, + rlc_interface_rrc* rlc_, + pdcp_interface_rrc* pdcp_, + sdap_interface_rrc* sdap_, + gw_interface_rrc* gw_, + nas_5g_interface_rrc_nr* nas_, + rrc_eutra_interface_rrc_nr* rrc_eutra_, + usim_interface_rrc_nr* usim_, + srsran::timer_handler* timers_, + stack_interface_rrc* stack_, + const rrc_nr_args_t& args_) +{ + phy = phy_; + rlc = rlc_; + pdcp = pdcp_; + sdap = sdap_; + gw = gw_; + nas = nas_; + mac = mac_; + rrc_eutra = rrc_eutra_; + usim = usim_; + stack = stack_; + args = args_; + + // allocate RRC timers + t300 = task_sched.get_unique_timer(); + t301 = task_sched.get_unique_timer(); + t302 = task_sched.get_unique_timer(); + t304 = task_sched.get_unique_timer(); + t310 = task_sched.get_unique_timer(); + t311 = task_sched.get_unique_timer(); + + if (rrc_eutra == nullptr) { + // SA mode + plmn_is_selected = true; + + // setup inital HARQ config + srsran::dl_harq_cfg_nr_t harq_cfg = {}; + harq_cfg.nof_procs = 8; + mac->set_config(harq_cfg); + + // Setup SRB0 + logical_channel_config_t lch = {}; + mac->setup_lcid(lch); + + // Carrier config + srsran::srsran_band_helper bands; + phy_cfg.carrier.dl_center_frequency_hz = bands.nr_arfcn_to_freq(args.dl_nr_arfcn); + phy_cfg.carrier.ul_center_frequency_hz = bands.nr_arfcn_to_freq(bands.get_ul_arfcn_from_dl_arfcn(args.dl_nr_arfcn)); + phy_cfg.carrier.ssb_center_freq_hz = bands.nr_arfcn_to_freq(args.ssb_nr_arfcn); + phy_cfg.carrier.nof_prb = args.nof_prb; + phy_cfg.carrier.max_mimo_layers = 1; + phy_cfg.carrier.scs = args.scs; + phy_cfg.duplex.mode = bands.get_duplex_mode(bands.get_band_from_dl_arfcn(args.dl_nr_arfcn)); + + // SSB configuration + phy_cfg.ssb.periodicity_ms = 10; + phy_cfg.ssb.position_in_burst[0] = true; + phy_cfg.ssb.scs = args.ssb_scs; + } + + running = true; + sim_measurement_timer = task_sched.get_unique_timer(); + return SRSRAN_SUCCESS; +} + +void rrc_nr::stop() +{ + running = false; +} + +void rrc_nr::get_metrics(rrc_nr_metrics_t& m) +{ + m.state = state; +} + +const char* rrc_nr::get_rb_name(uint32_t lcid) +{ + if (is_nr_srb(lcid)) { + return get_srb_name(nr_lcid_to_srb(lcid)); + } + if (lcid_drb.find(lcid) != lcid_drb.end()) { + return get_drb_name(lcid_drb[lcid]); + } + logger.warning("Unable to find lcid: %d. Return invalid LCID"); + return "invalid LCID"; +} + +// Timeout callback interface +void rrc_nr::timer_expired(uint32_t timeout_id) +{ + logger.debug("Handling Timer Expired"); + if (timeout_id == sim_measurement_timer.id() && rrc_eutra != nullptr) { + logger.debug("Triggered simulated measurement"); + + phy_meas_nr_t sim_meas = {}; + std::vector phy_meas_nr; + sim_meas.rsrp = -60.0; + sim_meas.rsrq = -60.0; + sim_meas.cfo_hz = 1.0; + sim_meas.arfcn_nr = sim_measurement_carrier_freq_r15; + sim_meas.pci_nr = args.sim_nr_meas_pci; + phy_meas_nr.push_back(sim_meas); + rrc_eutra->new_cell_meas_nr(phy_meas_nr); + + auto timer_expire_func = [this](uint32_t tid) { timer_expired(tid); }; + sim_measurement_timer.set(sim_measurement_timer_duration_ms, timer_expire_func); + sim_measurement_timer.run(); + } +} + +void rrc_nr::srsran_rrc_log(const char* str) {} + +template +void rrc_nr::log_rrc_message(const std::string& source, + direction_t dir, + const srsran::byte_buffer_t* pdu, + const T& msg, + const std::string& msg_type) +{ + if (logger.debug.enabled()) { + asn1::json_writer json_writer; + msg.to_json(json_writer); + logger.debug(pdu->msg, + pdu->N_bytes, + "%s - %s %s (%d B)", + source.c_str(), + (dir == Rx) ? "Rx" : "Tx", + msg_type.c_str(), + pdu->N_bytes); + logger.debug("Content:%s", json_writer.to_string().c_str()); + } else if (logger.info.enabled()) { + logger.info("%s - %s %s (%d B)", source.c_str(), (dir == Rx) ? "Rx" : "Tx", msg_type.c_str(), pdu->N_bytes); + } +} + +template +void rrc_nr::log_rrc_message(const std::string& source, + direction_t dir, + dyn_octstring oct, + const T& msg, + const std::string& msg_type) +{ + if (logger.debug.enabled()) { + asn1::json_writer json_writer; + msg.to_json(json_writer); + logger.debug(oct.data(), + oct.size(), + "%s - %s %s (%d B)", + source.c_str(), + (dir == Rx) ? "Rx" : "Tx", + msg_type.c_str(), + oct.size()); + logger.debug("Content:%s", json_writer.to_string().c_str()); + } else if (logger.info.enabled()) { + logger.info("%s - %s %s (%d B)", source.c_str(), (dir == Rx) ? "Rx" : "Tx", msg_type.c_str(), oct.size()); + } +} + +bool rrc_nr::add_lcid_drb(uint32_t lcid, uint32_t drb_id) +{ + if (lcid_drb.find(lcid) != lcid_drb.end()) { + logger.error("Couldn't add DRB to LCID (%d). DRB %d already exists.", lcid, drb_id); + return false; + } else { + logger.info("Adding lcid %d and radio bearer ID %d", lcid, drb_id); + lcid_drb[lcid] = nr_drb_id_to_drb(drb_id); + } + return true; +} + +uint32_t rrc_nr::get_lcid_for_drbid(uint32_t drb_id) +{ + for (auto& rb : lcid_drb) { + if (rb.second == nr_drb_id_to_drb(drb_id)) { + return rb.first; + } + } + logger.error("Couldn't find LCID for DRB. DRB %d does exist.", drb_id); + return 0; +} + +// PHY interface +void rrc_nr::in_sync() {} +void rrc_nr::out_of_sync() {} + +// MAC interface +void rrc_nr::run_tti(uint32_t tti) {} + +// PDCP interface +void rrc_nr::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) +{ + logger.debug("RX PDU, LCID: %d", lcid); + switch (static_cast(lcid)) { + case nr_srb::srb0: + decode_dl_ccch(std::move(pdu)); + break; + case nr_srb::srb1: + case nr_srb::srb2: + decode_dl_dcch(lcid, std::move(pdu)); + break; + default: + logger.error("RX PDU with invalid bearer id: %d", lcid); + break; + } +} + +void rrc_nr::decode_dl_ccch(unique_byte_buffer_t pdu) +{ + asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); + asn1::rrc_nr::dl_ccch_msg_s dl_ccch_msg; + if (dl_ccch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or + dl_ccch_msg.msg.type().value != dl_ccch_msg_type_c::types_opts::c1) { + logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack DL-CCCH message (%d B)", pdu->N_bytes); + return; + } + log_rrc_message( + get_rb_name(srb_to_lcid(nr_srb::srb0)), Rx, pdu.get(), dl_ccch_msg, dl_ccch_msg.msg.c1().type().to_string()); + + dl_ccch_msg_type_c::c1_c_* c1 = &dl_ccch_msg.msg.c1(); + switch (dl_ccch_msg.msg.c1().type().value) { + case dl_ccch_msg_type_c::c1_c_::types::rrc_reject: { + // 5.3.15 + const auto& reject = c1->rrc_reject(); + srsran::console("Received RRC Reject\n"); + + t300.stop(); + + if (reject.crit_exts.rrc_reject().wait_time_present) { + // nas->set_barring(srsran::barring_t::all); + t302.set(reject.crit_exts.rrc_reject().wait_time * 1000, [this](uint32_t tid) { timer_expired(tid); }); + t302.run(); + } else { + // Perform the actions upon expiry of T302 if wait time is zero + // nas->set_barring(srsran::barring_t::none); + // start_go_idle(); + } + } break; + case dl_ccch_msg_type_c::c1_c_::types::rrc_setup: { + transaction_id = c1->rrc_setup().rrc_transaction_id; + rrc_setup_s rrc_setup_copy = c1->rrc_setup(); + task_sched.defer_task([this, rrc_setup_copy]() { handle_rrc_setup(rrc_setup_copy); }); + break; + } + default: + logger.error("The provided DL-CCCH message type is not recognized"); + break; + } +} + +void rrc_nr::decode_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu) +{ + asn1::cbit_ref bref(pdu->msg, pdu->N_bytes); + asn1::rrc_nr::dl_dcch_msg_s dl_dcch_msg; + if (dl_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or + dl_dcch_msg.msg.type().value != dl_dcch_msg_type_c::types_opts::c1) { + logger.error(pdu->msg, pdu->N_bytes, "Failed to unpack DL-DCCH message (%d B)", pdu->N_bytes); + return; + } + log_rrc_message(get_rb_name(lcid), Rx, pdu.get(), dl_dcch_msg, dl_dcch_msg.msg.c1().type().to_string()); + + dl_dcch_msg_type_c::c1_c_* c1 = &dl_dcch_msg.msg.c1(); + switch (dl_dcch_msg.msg.c1().type().value) { + // TODO: ADD missing cases + case dl_dcch_msg_type_c::c1_c_::types::rrc_recfg: { + rrc_recfg_s recfg = c1->rrc_recfg(); + task_sched.defer_task([this, recfg]() { handle_rrc_reconfig(recfg); }); + break; + } + case dl_dcch_msg_type_c::c1_c_::types::dl_info_transfer: { + dl_info_transfer_s dl_info_transfer = c1->dl_info_transfer(); + task_sched.defer_task([this, dl_info_transfer]() { handle_dl_info_transfer(dl_info_transfer); }); + break; + } + case dl_dcch_msg_type_c::c1_c_::types::security_mode_cmd: { + security_mode_cmd_s smc = c1->security_mode_cmd(); + task_sched.defer_task([this, smc]() { handle_security_mode_command(smc); }); + break; + } + case dl_dcch_msg_type_c::c1_c_::types::rrc_release: { + rrc_release_s rrc_release = c1->rrc_release(); + task_sched.defer_task([this, rrc_release]() { handle_rrc_release(rrc_release); }); + break; + } + case dl_dcch_msg_type_c::c1_c_::types::ue_cap_enquiry: { + ue_cap_enquiry_s ue_cap_enquiry = c1->ue_cap_enquiry(); + task_sched.defer_task([this, ue_cap_enquiry]() { handle_ue_capability_enquiry(ue_cap_enquiry); }); + break; + } + default: + logger.error("The provided DL-DCCH message type is not recognized or supported."); + break; + } +} + +void rrc_nr::write_pdu_bcch_bch(srsran::unique_byte_buffer_t pdu) {} +void rrc_nr::write_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) +{ + decode_pdu_bcch_dlsch(std::move(pdu)); +} + +void rrc_nr::decode_pdu_bcch_dlsch(srsran::unique_byte_buffer_t pdu) +{ + // Stop BCCH search after successful reception of 1 BCCH block + // mac->bcch_stop_rx(); + + bcch_dl_sch_msg_s dlsch_msg; + asn1::cbit_ref dlsch_bref(pdu->msg, pdu->N_bytes); + asn1::SRSASN_CODE err = dlsch_msg.unpack(dlsch_bref); + + if (err != asn1::SRSASN_SUCCESS or dlsch_msg.msg.type().value != bcch_dl_sch_msg_type_c::types_opts::c1) { + logger.error(pdu->msg, pdu->N_bytes, "Could not unpack BCCH DL-SCH message (%d B).", pdu->N_bytes); + return; + } + + log_rrc_message("BCCH-DLSCH", Rx, pdu.get(), dlsch_msg, dlsch_msg.msg.c1().type().to_string()); + + if (dlsch_msg.msg.c1().type() == bcch_dl_sch_msg_type_c::c1_c_::types::sib_type1) { + logger.info("Processing SIB1 (1/1)"); + handle_sib1(dlsch_msg.msg.c1().sib_type1()); + } +} + +void rrc_nr::set_phy_default_config() +{ + phy_cfg = {}; + + // uses default values provided in 38.311 TS 138 331 V16.6 page 361 + if (make_phy_beta_offsets({}, &phy_cfg.pusch.beta_offsets) == false) { + logger.warning("Couldn't set default beta_offsets config"); + } + + // no default value provided, asume factor 1.0 + uci_on_pusch_s uci_on_pusch = {}; + uci_on_pusch.scaling = uci_on_pusch_s::scaling_opts::f1; + if (make_phy_pusch_scaling(uci_on_pusch, &phy_cfg.pusch.scaling) == false) { + logger.warning("Couldn't set default scaling config"); + } + + // no default value specified, use dynamic + phys_cell_group_cfg_s phys_cell_group_cfg = {}; + phys_cell_group_cfg.pdsch_harq_ack_codebook = phys_cell_group_cfg_s::pdsch_harq_ack_codebook_opts::dynamic_value; + if (make_phy_harq_ack_cfg(phys_cell_group_cfg, &phy_cfg.harq_ack) == false) { + logger.warning("Couldn't set default HARQ ack config"); + } +} + +void rrc_nr::handle_sib1(const sib1_s& sib1) +{ + if (meas_cells.serving_cell().has_sib1()) { + logger.info("SIB1 already processed"); + return; + } + + meas_cells.serving_cell().set_sib1(sib1); + + logger.info("SIB1 received, CellID=%d", meas_cells.serving_cell().get_cell_id() & 0xfff); + + // clang-format off + // unhandled fields: + // - cellSelectionInfo + // - cellAccessRelatedInfo + // - connEstFailureControl + // - servingCellConfigCommon: + // - downlinkConfigCommon.frequencyInfoDL.frequencyBandList + // - downlinkConfigCommon.frequencyInfoDL.offsetToPointA + // - downlinkConfigCommon.initialDownlinkBWP.genericParameters + // - downlinkConfigCommon.initialDownlinkBWP.pdcch-ConfigCommon.commonSearchSpaceList.searchSpaceSIB1 + // - downlinkConfigCommon.initialDownlinkBWP.pdcch-ConfigCommon.commonSearchSpaceList.search_space_other_sys_info + // - downlinkConfigCommon.initialDownlinkBWP.pdcch-ConfigCommon.commonSearchSpaceList.paging_search_space + // - downlinkConfigCommon.bcch-Config + // - downlinkConfigCommon.pcch-Config + // - uplinkConfigCommon.frequencyInfoUL.frequencyBandList + // - uplinkConfigCommon.frequencyInfoUL.p_max + // - uplinkConfigCommon.initialUplinkBWP.genericParameters + // - uplinkConfigCommon.initialUplinkBWP.rach-ConfigCommon.rach-ConfigGeneric.msg1-FDM + // - uplinkConfigCommon.initialUplinkBWP.rach-ConfigCommon.ssb_per_rach_occasion_and_cb_preambs_per_ssb + // - uplinkConfigCommon.initialUplinkBWP.rach-ConfigCommon.restricted_set_cfg + // - uplinkConfigCommon.initialUplinkBWP.pusch-ConfigCommon.pusch-TimeDomainResourceAllocationList.p0-NominalWithGrant + // - ss-PBCH-BlockPower + // clang-format on + + // ue-TimersAndConstants + auto timer_expire_func = [this](uint32_t tid) { timer_expired(tid); }; + t300.set(sib1.ue_timers_and_consts.t300.to_number(), timer_expire_func); + t301.set(sib1.ue_timers_and_consts.t301.to_number(), timer_expire_func); + t310.set(sib1.ue_timers_and_consts.t310.to_number(), timer_expire_func); + t311.set(sib1.ue_timers_and_consts.t311.to_number(), timer_expire_func); + N310 = sib1.ue_timers_and_consts.n310.to_number(); + N311 = sib1.ue_timers_and_consts.n311.to_number(); + + logger.info("Set Constants and Timers: N310=%d, N311=%d, t300=%d, t301=%d, t310=%d, t311=%d", + N310, + N311, + t300.duration(), + t301.duration(), + t310.duration(), + t311.duration()); + + // Apply RACH and timeAlginmentTimer configuration + mac_cfg_nr_t mac_cfg = {}; + make_mac_rach_cfg(sib1.serving_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), &mac_cfg.rach_cfg); + mac_cfg.time_alignment_timer = sib1.serving_cell_cfg_common.ul_cfg_common.time_align_timer_common.to_number(); + + mac->set_config(mac_cfg.rach_cfg); + + // Apply PDSCH Config Common + if (sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_cfg_common.setup() + .pdsch_time_domain_alloc_list.size() > 0) { + if (not fill_phy_pdsch_cfg_common(sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdsch_cfg_common.setup(), + &phy_cfg.pdsch)) { + logger.warning("Could not set PDSCH config."); + } + } + + // Apply PUSCH Config Common + if (not fill_phy_pusch_cfg_common(sib1.serving_cell_cfg_common.ul_cfg_common.init_ul_bwp.pusch_cfg_common.setup(), + &phy_cfg.pusch)) { + logger.warning("Could not set PUSCH config."); + } + + // Apply PUCCH Config Common + fill_phy_pucch_cfg_common(sib1.serving_cell_cfg_common.ul_cfg_common.init_ul_bwp.pucch_cfg_common.setup(), + &phy_cfg.pucch.common); + + // Apply RACH Config Common + if (not make_phy_rach_cfg(sib1.serving_cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), + sib1.serving_cell_cfg_common.tdd_ul_dl_cfg_common_present ? SRSRAN_DUPLEX_MODE_TDD + : SRSRAN_DUPLEX_MODE_FDD, + &phy_cfg.prach)) { + logger.warning("Could not set phy rach config."); + return; + } + + // Apply PDCCH Config Common + fill_phy_pdcch_cfg_common(sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(), + &phy_cfg.pdcch); + + // Apply Carrier Config + fill_phy_carrier_cfg(sib1.serving_cell_cfg_common, &phy_cfg.carrier); + + // Apply SSB Config + fill_phy_ssb_cfg(sib1.serving_cell_cfg_common, &phy_cfg.ssb); + + // Apply n-TimingAdvanceOffset + if (sib1.serving_cell_cfg_common.n_timing_advance_offset_present) { + switch (sib1.serving_cell_cfg_common.n_timing_advance_offset.value) { + case serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n0: + phy_cfg.t_offset = 0; + break; + case serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n25600: + phy_cfg.t_offset = 25600; + break; + case serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n39936: + phy_cfg.t_offset = 39936; + break; + default: + logger.error("Invalid n_ta_offset option"); + break; + } + } else { + phy_cfg.t_offset = 25600; + } + + phy_cfg_state = PHY_CFG_STATE_SA_SIB_CFG; + if (not phy->set_config(phy_cfg)) { + logger.warning("Could not set phy config."); + return; + } + + // Notify cell selector of successful SIB1 reception + cell_selector.trigger(true); +} + +void rrc_nr::write_pdu_pcch(srsran::unique_byte_buffer_t pdu) {} +void rrc_nr::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} +void rrc_nr::notify_pdcp_integrity_error(uint32_t lcid) {} + +// NAS interface +int rrc_nr::write_sdu(srsran::unique_byte_buffer_t sdu) +{ + if (state == RRC_NR_STATE_IDLE) { + logger.warning("Received ULInformationTransfer SDU when in IDLE"); + return SRSRAN_ERROR; + } + send_ul_info_transfer(std::move(sdu)); + return SRSRAN_SUCCESS; +} + +bool rrc_nr::is_connected() +{ + return state == RRC_NR_STATE_CONNECTED; +} + +int rrc_nr::connection_request(srsran::nr_establishment_cause_t cause, srsran::unique_byte_buffer_t dedicated_info_nas_) +{ + if (not setup_req_proc.launch(cause, std::move(dedicated_info_nas_))) { + logger.error("Failed to initiate setup request procedure"); + return SRSRAN_ERROR; + } + callback_list.add_proc(setup_req_proc); + return SRSRAN_SUCCESS; +} + +uint16_t rrc_nr::get_mcc() +{ + return meas_cells.serving_cell().get_mcc(); +} + +uint16_t rrc_nr::get_mnc() +{ + return meas_cells.serving_cell().get_mnc(); +} + +// Senders +void rrc_nr::send_ul_info_transfer(unique_byte_buffer_t nas_msg) +{ + logger.debug("Preparing UL Info Transfer"); + + ul_dcch_msg_s ul_dcch_msg; + ul_info_transfer_ies_s* ul_info_transfer = + &ul_dcch_msg.msg.set_c1().set_ul_info_transfer().crit_exts.set_ul_info_transfer(); + + // Try to resize target buffer first + ul_info_transfer->ded_nas_msg.resize(nas_msg->N_bytes); + + // check we have enough space in target buffer + if (nas_msg->N_bytes > ul_info_transfer->ded_nas_msg.size()) { + logger.error("NAS message too big to send in UL Info transfer (%d > %d).", + nas_msg->N_bytes, + ul_info_transfer->ded_nas_msg.size()); + return; + } + + // copy message content + memcpy(ul_info_transfer->ded_nas_msg.data(), nas_msg->msg, nas_msg->N_bytes); + + // send message + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); +} + +void rrc_nr::send_security_mode_complete() +{ + ul_dcch_msg_s ul_dcch_msg; + auto& smc = ul_dcch_msg.msg.set_c1().set_security_mode_complete().crit_exts.set_security_mode_complete(); + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); +} + +void rrc_nr::send_setup_request(srsran::nr_establishment_cause_t cause) +{ + logger.debug("Preparing RRC Setup Request"); + + // Prepare SetupRequest packet + ul_ccch_msg_s ul_ccch_msg; + rrc_setup_request_ies_s* rrc_setup_req = &ul_ccch_msg.msg.set_c1().set_rrc_setup_request().rrc_setup_request; + + // TODO: implement ng_minus5_g_s_tmsi_part1 + rrc_setup_req->ue_id.set_random_value(); + // TODO use proper RNG + uint64_t random_id = 0; + for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes + random_id |= ((uint64_t)rand() & 0xFF) << i * 8; + } + rrc_setup_req->ue_id.random_value().from_number(random_id, rrc_setup_req->ue_id.random_value().length()); + rrc_setup_req->establishment_cause = (establishment_cause_opts::options)cause; + + send_ul_ccch_msg(ul_ccch_msg); +} + +void rrc_nr::send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return; + } + + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + if (msg.pack(bref) != SRSASN_SUCCESS) { + logger.error("Coulnd't pack UL-CCCH message."); + return; + } + bref.align_bytes_zero(); + pdu->N_bytes = (uint32_t)bref.distance_bytes(pdu->msg); + pdu->set_timestamp(); + + // Set UE contention resolution ID in MAC + uint64_t uecri = 0; + uint8_t* ue_cri_ptr = (uint8_t*)&uecri; + uint32_t nbytes = 6; + for (uint32_t i = 0; i < nbytes; i++) { + ue_cri_ptr[nbytes - i - 1] = pdu->msg[i]; + } + + logger.debug("Setting UE contention resolution ID: %" PRIu64 "", uecri); + mac->set_contention_id(uecri); + + uint32_t lcid = 0; + log_rrc_message(get_rb_name(lcid), Tx, pdu.get(), msg, msg.msg.c1().type().to_string()); + + rlc->write_sdu(lcid, std::move(pdu)); +} + +void rrc_nr::send_ul_dcch_msg(uint32_t lcid, const ul_dcch_msg_s& msg) +{ + // Reset and reuse sdu buffer if provided + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return; + } + + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + msg.pack(bref); + bref.align_bytes_zero(); + pdu->N_bytes = (uint32_t)bref.distance_bytes(pdu->msg); + pdu->set_timestamp(); + + if (msg.msg.type() == ul_dcch_msg_type_c::types_opts::options::c1) { + log_rrc_message(get_rb_name(lcid), Tx, pdu.get(), msg, msg.msg.c1().type().to_string()); + } + + pdcp->write_sdu(lcid, std::move(pdu)); +} + +void rrc_nr::send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg) +{ + logger.debug("Preparing RRC Connection Setup Complete"); + + // Prepare ConnectionSetupComplete packet + asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; + rrc_setup_complete_ies_s* rrc_setup_complete = + &ul_dcch_msg.msg.set_c1().set_rrc_setup_complete().crit_exts.set_rrc_setup_complete(); + + ul_dcch_msg.msg.c1().rrc_setup_complete().rrc_transaction_id = transaction_id; + + rrc_setup_complete->sel_plmn_id = 1; + rrc_setup_complete->registered_amf_present = false; + rrc_setup_complete->guami_type_present = false; + rrc_setup_complete->ng_minus5_g_s_tmsi_value_present = false; + + rrc_setup_complete->ded_nas_msg.resize(nas_msg->N_bytes); + memcpy(rrc_setup_complete->ded_nas_msg.data(), nas_msg->msg, nas_msg->N_bytes); + + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); +} + +void rrc_nr::send_rrc_reconfig_complete() +{ + logger.debug("Preparing RRC Connection Reconfig Complete"); + + asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; + auto& rrc_reconfig_complete = ul_dcch_msg.msg.set_c1().set_rrc_recfg_complete().crit_exts.set_rrc_recfg_complete(); + + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); +} + +int rrc_nr::send_ue_capability_info(const asn1::rrc_nr::ue_cap_enquiry_s& msg) +{ + transaction_id = msg.rrc_transaction_id; + + ue_cap_enquiry_ies_s ue_cap_enquiry_ies = msg.crit_exts.ue_cap_enquiry(); + + asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; + + auto& ue_cap_info = ul_dcch_msg.msg.set_c1().set_ue_cap_info().crit_exts.set_ue_cap_info(); + ul_dcch_msg.msg.c1().ue_cap_info().rrc_transaction_id = msg.rrc_transaction_id; + + for (auto ue_cap_rat_request : ue_cap_enquiry_ies.ue_cap_rat_request_list) { + if (ue_cap_rat_request.rat_type.value == rat_type_opts::nr) { + ue_cap_info.ue_cap_rat_container_list_present = true; + ue_cap_rat_container_s ue_cap_rat_container; + ue_cap_rat_container.rat_type.value = rat_type_opts::nr; + + ue_nr_cap_s ue_cap; + ue_cap.access_stratum_release = access_stratum_release_opts::rel15; + + // RLC params + ue_cap.rlc_params_present = true; + ue_cap.rlc_params.am_with_short_sn_present = true; + ue_cap.rlc_params.um_with_short_sn_present = true; + ue_cap.rlc_params.um_with_long_sn_present = true; + + // PDCP parameters + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0000 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0001 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0002 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0003 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0004 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0006 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0101 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0102 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0103 = false; + ue_cap.pdcp_params.supported_rohc_profiles.profile0x0104 = false; + ue_cap.pdcp_params.max_num_rohc_context_sessions.value = pdcp_params_s::max_num_rohc_context_sessions_opts::cs2; + + if (args.pdcp_short_sn_support) { + ue_cap.pdcp_params.short_sn_present = true; + } + // PHY Parameters + ue_cap.phy_params.phy_params_common_present = true; + + // RF Parameters + for (const auto band : args.supported_bands_nr) { + band_nr_s band_nr; + band_nr.band_nr = band; + ue_cap.rf_params.supported_band_list_nr.push_back(band_nr); + + // supportedBandCombinationList + band_combination_s band_combination; + band_params_c band_params; + band_params.set_nr().band_nr = band; + band_combination.band_list.push_back(band_params); + ue_cap.rf_params.supported_band_combination_list.push_back(band_combination); + } + // featureSets + ue_cap.feature_sets_present = true; + feature_set_dl_per_cc_s feature_set_dl_per_cc; + feature_set_ul_per_cc_s feature_set_ul_per_cc; + + feature_set_dl_per_cc.supported_bw_dl.set_fr1().value = supported_bw_c::fr1_opts::mhz10; + feature_set_ul_per_cc.supported_bw_ul.set_fr1().value = supported_bw_c::fr1_opts::mhz10; + + switch (args.scs) { + case srsran_subcarrier_spacing_15kHz: + feature_set_dl_per_cc.supported_subcarrier_spacing_dl = subcarrier_spacing_opts::khz15; + feature_set_ul_per_cc.supported_subcarrier_spacing_ul = subcarrier_spacing_opts::khz15; + break; + case srsran_subcarrier_spacing_30kHz: + feature_set_dl_per_cc.supported_subcarrier_spacing_dl = subcarrier_spacing_opts::khz30; + feature_set_ul_per_cc.supported_subcarrier_spacing_ul = subcarrier_spacing_opts::khz30; + break; + default: + logger.warning("Unsupported subcarrier spacing value"); + } + + ue_cap.feature_sets.feature_sets_dl_per_cc.push_back(feature_set_dl_per_cc); + ue_cap.feature_sets.feature_sets_ul_per_cc.push_back(feature_set_ul_per_cc); + +#if 1 + ue_cap_rat_container.ue_cap_rat_container.resize(512); + asn1::bit_ref bref_pack(ue_cap_rat_container.ue_cap_rat_container.data(), + ue_cap_rat_container.ue_cap_rat_container.size()); + + if (ue_cap.pack(bref_pack) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack UE NR Capabilities in UE Capability Info"); + return SRSRAN_ERROR; + } + ue_cap_rat_container.ue_cap_rat_container.resize(bref_pack.distance_bytes()); +#else + // hard-coded capabilities from third-party + ue_cap_rat_container.ue_cap_rat_container.from_string("E1A01000074F5A03020000C0A0241262C001206A0609B00C39F30C7942" + "C0E098040623809506C4DD608D21A08107CA01165B262A87813E43" + "9F40CF88E3C639F30C7942C0E070F09C0013C0070004F0001601C00140" + "A836036B04690D04083E500892D931541439F11C78C73E618F2858" + "1C0E1E04FE0000003F80000000A00E05"); +#endif + + ue_cap_info.ue_cap_rat_container_list.push_back(ue_cap_rat_container); + } + } + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); + return SRSASN_SUCCESS; +} + +// EUTRA-RRC interface +int rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) +{ + struct ue_mrdc_cap_s mrdc_cap; + + band_combination_s band_combination; + + for (const auto& band : args.supported_bands_eutra) { + struct band_params_c band_param_eutra; + band_param_eutra.set_eutra(); + band_param_eutra.eutra().ca_bw_class_dl_eutra_present = true; + band_param_eutra.eutra().ca_bw_class_ul_eutra_present = true; + band_param_eutra.eutra().band_eutra = band; + band_param_eutra.eutra().ca_bw_class_dl_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; + band_param_eutra.eutra().ca_bw_class_ul_eutra = asn1::rrc_nr::ca_bw_class_eutra_opts::options::a; + band_combination.band_list.push_back(band_param_eutra); + } + + // TODO check if band is requested + for (const auto& band : args.supported_bands_nr) { + struct band_params_c band_param_nr; + band_param_nr.set_nr(); + band_param_nr.nr().ca_bw_class_dl_nr_present = true; + band_param_nr.nr().ca_bw_class_ul_nr_present = true; + band_param_nr.nr().band_nr = band; + band_param_nr.nr().ca_bw_class_dl_nr = asn1::rrc_nr::ca_bw_class_nr_opts::options::a; + band_param_nr.nr().ca_bw_class_ul_nr = asn1::rrc_nr::ca_bw_class_nr_opts::options::a; + band_combination.band_list.push_back(band_param_nr); + } + + mrdc_cap.rf_params_mrdc.supported_band_combination_list.push_back(band_combination); + + mrdc_cap.rf_params_mrdc.ext = true; + + // RF Params MRDC applied_freq_band_list_filt + for (const auto& band : args.supported_bands_eutra) { + freq_band_info_c band_info_eutra; + band_info_eutra.set_band_info_eutra(); + band_info_eutra.band_info_eutra().ca_bw_class_dl_eutra_present = false; + band_info_eutra.band_info_eutra().ca_bw_class_ul_eutra_present = false; + band_info_eutra.band_info_eutra().band_eutra = band; + mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_eutra); + } + + for (const auto& band : args.supported_bands_nr) { + freq_band_info_c band_info_nr; + band_info_nr.set_band_info_nr(); + band_info_nr.band_info_nr().band_nr = band; + mrdc_cap.rf_params_mrdc.applied_freq_band_list_filt.push_back(band_info_nr); + } + + // rf_params_mrdc supported band combination list v1540 + + band_combination_list_v1540_l* band_combination_list_v1450 = new band_combination_list_v1540_l(); + band_combination_v1540_s band_combination_v1540; + + band_params_v1540_s band_params_a; + band_params_a.srs_tx_switch_present = true; + band_params_a.srs_carrier_switch_present = false; + band_params_a.srs_tx_switch.supported_srs_tx_port_switch = + band_params_v1540_s::srs_tx_switch_s_::supported_srs_tx_port_switch_opts::not_supported; + band_combination_v1540.band_list_v1540.push_back(band_params_a); + + band_params_v1540_s band_params_b; + band_params_b.srs_tx_switch_present = true; + band_params_b.srs_tx_switch.supported_srs_tx_port_switch = + band_params_v1540_s::srs_tx_switch_s_::supported_srs_tx_port_switch_opts::t1r2; + band_params_b.srs_carrier_switch_present = false; + band_combination_v1540.band_list_v1540.push_back(band_params_b); + + // clang-format off + band_combination_v1540.ca_params_nr_v1540_present = false; + band_combination_v1540.ca_params_nr_v1540.simul_csi_reports_all_cc_present = true; + band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.max_num_simul_nzp_csi_rs_act_bwp_all_cc_present = true; + band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.max_num_simul_nzp_csi_rs_act_bwp_all_cc = 5; + band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.total_num_ports_simul_nzp_csi_rs_act_bwp_all_cc_present = true; + band_combination_v1540.ca_params_nr_v1540.csi_rs_im_reception_for_feedback_per_band_comb.total_num_ports_simul_nzp_csi_rs_act_bwp_all_cc = 32; + // clang-format on + band_combination_list_v1450->push_back(band_combination_v1540); + mrdc_cap.rf_params_mrdc.supported_band_combination_list_v1540.reset(band_combination_list_v1450); + + feature_set_combination_l feature_set_combination; + + feature_sets_per_band_l feature_sets_per_band; + + feature_set_c feature_set_eutra; + feature_set_eutra.set_eutra(); + feature_set_eutra.eutra().dl_set_eutra = 1; + feature_set_eutra.eutra().ul_set_eutra = 1; + feature_sets_per_band.push_back(feature_set_eutra); + feature_set_combination.push_back(feature_sets_per_band); + + for (const auto& band : args.supported_bands_nr) { + feature_sets_per_band.resize(0); + feature_set_c feature_set_nr; + feature_set_nr.set_nr(); + feature_set_nr.nr().dl_set_nr = 1; + feature_set_nr.nr().ul_set_nr = 1; + feature_sets_per_band.push_back(feature_set_nr); + feature_set_combination.push_back(feature_sets_per_band); + } + + mrdc_cap.feature_set_combinations.push_back(feature_set_combination); + + // Pack mrdc_cap + asn1::bit_ref bref(eutra_nr_caps_pdu->msg, eutra_nr_caps_pdu->get_tailroom()); + mrdc_cap.pack(bref); + eutra_nr_caps_pdu->N_bytes = bref.distance_bytes(); + +#if 0 + uint8_t eutra_nr_cap_raw[] = {0x01, 0x1c, 0x04, 0x81, 0x60, 0x00, 0x1c, 0x4d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x40, 0x04, 0x04, 0xd0, 0x10, 0x74, 0x06, 0x14, 0xe8, 0x1b, 0x10, + 0x78, 0x00, 0x00, 0x20, 0x00, 0x10, 0x08, 0x08, 0x01, 0x00, 0x20}; + if (sizeof(eutra_nr_cap_raw) <= 2048) { + memcpy(eutra_nr_caps_pdu->msg, eutra_nr_cap_raw, sizeof(eutra_nr_cap_raw)); + eutra_nr_caps_pdu->N_bytes = sizeof(eutra_nr_cap_raw); + } +#endif + + logger.debug( + eutra_nr_caps_pdu->msg, eutra_nr_caps_pdu->N_bytes, "EUTRA-NR capabilities (%u B)", eutra_nr_caps_pdu->N_bytes); + + return SRSRAN_SUCCESS; +} + +bool rrc_nr::rrc_reconfiguration(bool endc_release_and_add_r15, const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf) +{ + if (not conn_recfg_proc.launch(reconf_initiator_t::mcg_srb1, endc_release_and_add_r15, rrc_nr_reconf)) { + logger.error("Unable to launch NR RRC reconfiguration procedure"); + return false; + } else { + callback_list.add_proc(conn_recfg_proc); + } + return true; +} + +void rrc_nr::rrc_release() +{ + rlc->reset(); + pdcp->reset(); + mac->reset(); + lcid_drb.clear(); + + // Apply actions only applicable in SA mode + if (rrc_eutra == nullptr) { + stack->reset_eps_bearers(); + } +} + +int rrc_nr::get_nr_capabilities(srsran::byte_buffer_t* nr_caps_pdu) +{ + struct ue_nr_cap_s nr_cap; + + nr_cap.access_stratum_release = access_stratum_release_opts::rel15; + // PDCP + nr_cap.pdcp_params.max_num_rohc_context_sessions = pdcp_params_s::max_num_rohc_context_sessions_opts::cs2; + + for (const auto& band : args.supported_bands_nr) { + band_nr_s band_nr; + band_nr.band_nr = band; + band_nr.ue_pwr_class_present = true; + band_nr.ue_pwr_class = band_nr_s::ue_pwr_class_opts::pc3; + band_nr.pusch_minus256_qam_present = true; + nr_cap.rf_params.supported_band_list_nr.push_back(band_nr); + } + + nr_cap.rlc_params_present = true; + nr_cap.rlc_params.um_with_short_sn_present = true; + nr_cap.rlc_params.um_with_long_sn_present = true; + nr_cap.pdcp_params.short_sn_present = args.pdcp_short_sn_support; + nr_cap.phy_params.phy_params_fr1_present = true; + nr_cap.phy_params.phy_params_fr1.pdsch_minus256_qam_fr1_present = true; + + // Pack nr_caps + asn1::bit_ref bref(nr_caps_pdu->msg, nr_caps_pdu->get_tailroom()); + nr_cap.pack(bref); + nr_caps_pdu->N_bytes = bref.distance_bytes(); + +#if 0 + uint8_t nr_cap_raw[] = { + 0xe1, 0x00, 0x00, 0x00, 0x01, 0x47, 0x7a, 0x03, 0x02, 0x00, 0x00, 0x01, 0x40, 0x48, 0x07, 0x06, 0x0e, 0x02, 0x0c, + 0x00, 0x02, 0x13, 0x60, 0x10, 0x73, 0xe4, 0x20, 0xf0, 0x00, 0x80, 0xc1, 0x30, 0x08, 0x0c, 0x00, 0x00, 0x0a, 0x05, + 0x89, 0xba, 0xc2, 0x19, 0x43, 0x40, 0x88, 0x10, 0x74, 0x18, 0x60, 0x4c, 0x04, 0x41, 0x6c, 0x90, 0x14, 0x06, 0x0c, + 0x78, 0xc7, 0x3e, 0x42, 0x0f, 0x00, 0x58, 0x0c, 0x0e, 0x0e, 0x02, 0x21, 0x3c, 0x84, 0xfc, 0x4d, 0xe0, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0xe5, 0x4d, 0x00, 0x01, 0x00, 0x00, 0x04, 0x18, 0x60, 0x00, 0x34, 0xaa, 0x60}; + if (sizeof(nr_cap_raw) <= 2048) { + memcpy(nr_caps_pdu->msg, nr_cap_raw, sizeof(nr_cap_raw)); + nr_caps_pdu->N_bytes = sizeof(nr_cap_raw); + } +#endif + + logger.debug(nr_caps_pdu->msg, nr_caps_pdu->N_bytes, "NR capabilities (%u B)", nr_caps_pdu->N_bytes); + return SRSRAN_SUCCESS; +}; + +void rrc_nr::phy_meas_stop() +{ + // possbile race condition for sim_measurement timer, which might be set at the same moment as stopped => fix + // with phy integration + logger.debug("Stopping simulated measurements"); + sim_measurement_timer.stop(); +} + +void rrc_nr::phy_set_cells_to_meas(uint32_t carrier_freq_r15) +{ + logger.debug("Measuring phy cell %d ", carrier_freq_r15); + // Start timer for fake measurements + auto timer_expire_func = [this](uint32_t tid) { timer_expired(tid); }; + sim_measurement_carrier_freq_r15 = carrier_freq_r15; + sim_measurement_timer.set(sim_measurement_timer_duration_ms, timer_expire_func); + sim_measurement_timer.run(); +} + +bool rrc_nr::configure_sk_counter(uint16_t sk_counter) +{ + logger.info("Configure new SK counter %d. Update Key for secondary gnb", sk_counter); + if (usim->generate_nr_context(sk_counter, &sec_cfg) == false) { + return false; + } + security_is_activated = true; + return true; +} + +bool rrc_nr::is_config_pending() +{ + if (conn_recfg_proc.is_busy()) { + return true; + } + return false; +} + +bool rrc_nr::apply_rlc_add_mod(const rlc_bearer_cfg_s& rlc_bearer_cfg) +{ + uint32_t lc_ch_id = 0; + uint32_t drb_id = 0; + uint32_t srb_id = 0; + rlc_config_t rlc_cfg; + // We set this to true if below we detect it's a DRB + bool is_drb = false; + + lc_ch_id = rlc_bearer_cfg.lc_ch_id; + if (rlc_bearer_cfg.served_radio_bearer_present == true) { + if (rlc_bearer_cfg.served_radio_bearer.type() == rlc_bearer_cfg_s::served_radio_bearer_c_::types::drb_id) { + drb_id = rlc_bearer_cfg.served_radio_bearer.drb_id(); + add_lcid_drb(lc_ch_id, drb_id); + is_drb = true; + } else if (rlc_bearer_cfg.served_radio_bearer.type() == rlc_bearer_cfg_s::served_radio_bearer_c_::types::srb_id) { + srb_id = rlc_bearer_cfg.served_radio_bearer.srb_id(); + } + } else { + logger.error("In RLC bearer cfg does not contain served radio bearer"); + return false; + } + + if (rlc_bearer_cfg.rlc_cfg_present == true) { + uint8_t bearer_id = static_cast(is_drb ? drb_id : srb_id); + if (srsran::make_rlc_config_t(rlc_bearer_cfg.rlc_cfg, bearer_id, &rlc_cfg) != SRSRAN_SUCCESS) { + logger.error("Failed to build RLC config"); + return false; + } + } else if (not is_drb) { + logger.debug("Using default RLC configs for SRB%d", srb_id); + rlc_cfg = rlc_config_t::default_rlc_am_nr_config(); + } else { + logger.error("In RLC bearer cfg does not contain rlc cfg"); + return false; + } + + // Setup RLC + rlc->add_bearer(lc_ch_id, rlc_cfg); + + if (rlc_bearer_cfg.mac_lc_ch_cfg_present == true && rlc_bearer_cfg.mac_lc_ch_cfg.ul_specific_params_present) { + logical_channel_config_t logical_channel_cfg; + logical_channel_cfg = srsran::make_mac_logical_channel_cfg_t(lc_ch_id, rlc_bearer_cfg.mac_lc_ch_cfg); + mac->setup_lcid(logical_channel_cfg); + } else { + logger.error("Bearer config for LCID %d does not contain mac-LogicalChannelConfig.", lc_ch_id); + return false; + } + return true; +} + +bool rrc_nr::apply_mac_cell_group(const mac_cell_group_cfg_s& mac_cell_group_cfg) +{ + if (mac_cell_group_cfg.sched_request_cfg_present) { + if (mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list.size() > 0) { + if (mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list.size() == 1) { + const sched_request_to_add_mod_s& asn1_cfg = + mac_cell_group_cfg.sched_request_cfg.sched_request_to_add_mod_list[0]; + sr_cfg_nr_t sr_cfg = {}; + sr_cfg.enabled = true; + sr_cfg.num_items = 1; + sr_cfg.item[0].sched_request_id = asn1_cfg.sched_request_id; + sr_cfg.item[0].trans_max = asn1_cfg.sr_trans_max.to_number(); + if (asn1_cfg.sr_prohibit_timer_present) { + sr_cfg.item[0].prohibit_timer = asn1_cfg.sr_trans_max; + } + if (mac->set_config(sr_cfg) != SRSRAN_SUCCESS) { + logger.error("Couldn't configure SR procedure."); + return false; + } + } else { + logger.warning("Only handling 1 scheduling request index to add"); + return false; + } + } + + if (mac_cell_group_cfg.sched_request_cfg.sched_request_to_release_list.size() > 0) { + logger.warning("Not handling sched request to release list"); + return false; + } + } + if (mac_cell_group_cfg.sched_request_cfg_present) + + if (mac_cell_group_cfg.bsr_cfg_present) { + logger.debug("Handling MAC BSR config"); + srsran::bsr_cfg_nr_t bsr_cfg = {}; + bsr_cfg.periodic_timer = mac_cell_group_cfg.bsr_cfg.periodic_bsr_timer.to_number(); + bsr_cfg.retx_timer = mac_cell_group_cfg.bsr_cfg.retx_bsr_timer.to_number(); + if (mac->set_config(bsr_cfg) != SRSRAN_SUCCESS) { + return false; + } + } + + if (mac_cell_group_cfg.tag_cfg_present) { + if (mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list.size() > 0) { + for (uint32_t i = 0; i < mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list.size(); i++) { + tag_cfg_nr_t tag_cfg_nr = {}; + tag_cfg_nr.tag_id = mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list[i].tag_id; + tag_cfg_nr.time_align_timer = mac_cell_group_cfg.tag_cfg.tag_to_add_mod_list[i].time_align_timer.to_number(); + if (mac->add_tag_config(tag_cfg_nr) != SRSRAN_SUCCESS) { + logger.warning("Unable to add TAG config with tag_id %d", tag_cfg_nr.tag_id); + return false; + } + } + } + if (mac_cell_group_cfg.tag_cfg.tag_to_release_list.size() > 0) { + for (uint32_t i = 0; i < mac_cell_group_cfg.tag_cfg.tag_to_release_list.size(); i++) { + uint32_t tag_id = mac_cell_group_cfg.tag_cfg.tag_to_release_list[i]; + if (mac->remove_tag_config(tag_id) != SRSRAN_SUCCESS) { + logger.warning("Unable to release TAG config with tag_id %d", tag_id); + return false; + } + } + } + } + + if (mac_cell_group_cfg.phr_cfg_present) { + if (mac_cell_group_cfg.phr_cfg.is_setup()) { + phr_cfg_nr_t phr_cfg_nr; + if (make_mac_phr_cfg_t(mac_cell_group_cfg.phr_cfg.setup(), &phr_cfg_nr) != true) { + logger.warning("Unable to build PHR config"); + return false; + } + if (mac->set_config(phr_cfg_nr) != SRSRAN_SUCCESS) { + logger.warning("Unable to set PHR config"); + return false; + } + } + } + + if (mac_cell_group_cfg.skip_ul_tx_dynamic) { + logger.warning("Not handling phr cfg in skip_ul_tx_dynamic cell group config"); + } + return true; +} + +bool rrc_nr::apply_sp_cell_init_dl_pdcch(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg) +{ + if (pdcch_cfg.search_spaces_to_add_mod_list.size() > 0) { + for (uint32_t i = 0; i < pdcch_cfg.search_spaces_to_add_mod_list.size(); i++) { + srsran_search_space_t search_space; + if (make_phy_search_space_cfg(pdcch_cfg.search_spaces_to_add_mod_list[i], &search_space) == true) { + phy_cfg.pdcch.search_space[search_space.id] = search_space; + phy_cfg.pdcch.search_space_present[search_space.id] = true; + } else { + logger.warning("Warning while building search_space structure id=%d", i); + return false; + } + } + } else { + logger.warning("Option search_spaces_to_add_mod_list not present"); + return false; + } + if (pdcch_cfg.ctrl_res_set_to_add_mod_list.size() > 0) { + for (uint32_t i = 0; i < pdcch_cfg.ctrl_res_set_to_add_mod_list.size(); i++) { + srsran_coreset_t coreset; + if (make_phy_coreset_cfg(pdcch_cfg.ctrl_res_set_to_add_mod_list[i], &coreset) == true) { + phy_cfg.pdcch.coreset[coreset.id] = coreset; + phy_cfg.pdcch.coreset_present[coreset.id] = true; + } else { + logger.warning("Warning while building coreset structure"); + return false; + } + } + } else { + logger.warning("Option ctrl_res_set_to_add_mod_list not present"); + } + return true; +} + +bool rrc_nr::apply_sp_cell_init_dl_pdsch(const asn1::rrc_nr::pdsch_cfg_s& pdsch_cfg) +{ + if (pdsch_cfg.mcs_table_present) { + switch (pdsch_cfg.mcs_table) { + case pdsch_cfg_s::mcs_table_opts::qam256: + phy_cfg.pdsch.mcs_table = srsran_mcs_table_256qam; + break; + case pdsch_cfg_s::mcs_table_opts::qam64_low_se: + phy_cfg.pdsch.mcs_table = srsran_mcs_table_qam64LowSE; + break; + case pdsch_cfg_s::mcs_table_opts::nulltype: + logger.warning("Warning while selecting pdsch mcs_table"); + return false; + } + } else { + // If the field is absent the UE applies the value 64QAM. + phy_cfg.pdsch.mcs_table = srsran_mcs_table_64qam; + } + + if (pdsch_cfg.dmrs_dl_for_pdsch_map_type_a_present) { + if (pdsch_cfg.dmrs_dl_for_pdsch_map_type_a.type() == setup_release_c::types_opts::setup) { + srsran_dmrs_sch_add_pos_t srsran_dmrs_sch_add_pos; + if (make_phy_dmrs_dl_additional_pos(pdsch_cfg.dmrs_dl_for_pdsch_map_type_a.setup(), &srsran_dmrs_sch_add_pos) == + true) { + phy_cfg.pdsch.dmrs_typeA.additional_pos = srsran_dmrs_sch_add_pos; + phy_cfg.pdsch.dmrs_typeA.present = true; + } else { + logger.warning("Warning while build srsran_dmrs_sch_add_pos structure"); + return false; + } + } else { + logger.warning("Option dmrs_dl_for_pdsch_map_type_a not of type setup"); + return false; + } + } else { + logger.warning("Option dmrs_dl_for_pdsch_map_type_a not present"); + return false; + } + + srsran_resource_alloc_t resource_alloc; + if (make_phy_pdsch_alloc_type(pdsch_cfg, &resource_alloc) == true) { + phy_cfg.pdsch.alloc = resource_alloc; + } + + if (pdsch_cfg.zp_csi_rs_res_to_add_mod_list.size() > 0) { + for (uint32_t i = 0; i < pdsch_cfg.zp_csi_rs_res_to_add_mod_list.size(); i++) { + srsran_csi_rs_zp_resource_t zp_csi_rs_resource; + if (make_phy_zp_csi_rs_resource(pdsch_cfg.zp_csi_rs_res_to_add_mod_list[i], &zp_csi_rs_resource) == true) { + // temporally store csi_rs_zp_res + csi_rs_zp_res[zp_csi_rs_resource.id] = zp_csi_rs_resource; + } else { + logger.warning("Warning while building zp_csi_rs resource"); + return false; + } + } + } + + if (pdsch_cfg.p_zp_csi_rs_res_set_present) { + // check if resources have been processed + if (pdsch_cfg.zp_csi_rs_res_to_add_mod_list.size() == 0) { + logger.warning("Can't build ZP-CSI config, option zp_csi_rs_res_to_add_mod_list not present"); + return false; + } + if (pdsch_cfg.p_zp_csi_rs_res_set.type() == setup_release_c::types_opts::setup) { + for (uint32_t i = 0; i < pdsch_cfg.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list.size(); i++) { + uint8_t res = pdsch_cfg.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list[i]; + // use temporally stored values to assign + if (csi_rs_zp_res.find(res) == csi_rs_zp_res.end()) { + logger.warning("Can not find p_zp_csi_rs_res in temporally stored csi_rs_zp_res"); + return false; + } + phy_cfg.pdsch.p_zp_csi_rs_set.data[i] = csi_rs_zp_res[res]; + phy_cfg.pdsch.p_zp_csi_rs_set.count += 1; + } + } else { + logger.warning("Option p_zp_csi_rs_res_set not of type setup"); + return false; + } + } + return true; +} + +bool rrc_nr::apply_res_csi_report_cfg(const asn1::rrc_nr::csi_report_cfg_s& csi_report_cfg) +{ + uint32_t report_cfg_id = csi_report_cfg.report_cfg_id; + srsran_csi_hl_report_cfg_t srsran_csi_hl_report_cfg; + if (make_phy_csi_report(csi_report_cfg, &srsran_csi_hl_report_cfg) == true) { + phy_cfg.csi.reports[report_cfg_id] = srsran_csi_hl_report_cfg; + } else { + logger.warning("Warning while building report structure"); + return false; + } + if (csi_report_cfg.report_cfg_type.type() == csi_report_cfg_s::report_cfg_type_c_::types_opts::options::periodic) { + if (csi_report_cfg.report_cfg_type.periodic().pucch_csi_res_list.size() > 0) { + uint32_t res_id = csi_report_cfg.report_cfg_type.periodic() + .pucch_csi_res_list[0] + .pucch_res; // TODO: support and check more items + if (pucch_res_list.contains(res_id)) { + phy_cfg.csi.reports[report_cfg_id].periodic.resource = pucch_res_list[res_id]; + } else { + logger.error("Resources set not present for assigning pucch sets (res_id %d)", res_id); + return false; + } + } else { + logger.warning("List size to small: pucch_csi_res_list.size() < 0"); + return false; + } + } + return true; +} + +bool rrc_nr::apply_csi_meas_cfg(const asn1::rrc_nr::csi_meas_cfg_s& csi_meas_cfg) +{ + for (uint32_t i = 0; i < csi_meas_cfg.csi_report_cfg_to_add_mod_list.size(); i++) { + if (apply_res_csi_report_cfg(csi_meas_cfg.csi_report_cfg_to_add_mod_list[i]) == false) { + return false; + } + } + + for (uint32_t i = 0; i < csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.size(); i++) { + srsran_csi_rs_nzp_resource_t csi_rs_nzp_resource; + if (make_phy_nzp_csi_rs_resource(csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list[i], &csi_rs_nzp_resource) == true) { + // temporally store csi_rs_zp_res + csi_rs_nzp_res[csi_rs_nzp_resource.id] = csi_rs_nzp_resource; + } else { + logger.warning("Warning while building phy_nzp_csi_rs resource"); + return false; + } + } + + for (uint32_t i = 0; i < csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.size(); i++) { + uint8_t set_id = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list[i].nzp_csi_res_set_id; + for (uint32_t j = 0; j < csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list[i].nzp_csi_rs_res.size(); j++) { + uint8_t res = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list[i].nzp_csi_rs_res[j]; + // use temporally stored values to assign + if (csi_rs_nzp_res.find(res) == csi_rs_nzp_res.end()) { + logger.warning("Can not find nzp_csi_rs_res in temporally stored csi_rs_nzp_res"); + return false; + } + phy_cfg.pdsch.nzp_csi_rs_sets[set_id].data[j] = csi_rs_nzp_res[res]; + phy_cfg.pdsch.nzp_csi_rs_sets[set_id].count += 1; + } + if (csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list[i].trs_info_present) { + phy_cfg.pdsch.nzp_csi_rs_sets[set_id].trs_info = true; + } + } + + return true; +} + +bool rrc_nr::apply_dl_common_cfg(const asn1::rrc_nr::dl_cfg_common_s& dl_cfg_common) +{ + if (dl_cfg_common.freq_info_dl_present) { + if (make_phy_carrier_cfg(dl_cfg_common.freq_info_dl, &phy_cfg.carrier) == false) { + logger.warning("Warning while making carrier phy config"); + return false; + } + } else { + logger.warning("Option freq_info_dl not present, S-UL not supported."); + return false; + } + if (dl_cfg_common.init_dl_bwp_present) { + if (dl_cfg_common.init_dl_bwp.pdsch_cfg_common_present) { + if (dl_cfg_common.init_dl_bwp.pdsch_cfg_common.is_setup()) { + const pdcch_cfg_common_s& pdcch_cfg_common = dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(); + + // Load CORESET Zero + if (pdcch_cfg_common.ctrl_res_set_zero_present) { + // Get pointA and SSB absolute frequencies + double pointA_abs_freq_Hz = + phy_cfg.carrier.dl_center_frequency_hz - + phy_cfg.carrier.nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs) / 2; + double ssb_abs_freq_Hz = phy_cfg.carrier.ssb_center_freq_hz; + + // Calculate integer SSB to pointA frequency offset in Hz + uint32_t ssb_pointA_freq_offset_Hz = + (ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0; + srsran_subcarrier_spacing_t ssb_scs = phy_cfg.ssb.scs; + + // Select PDCCH subcarrrier spacing from PDCCH BWP + srsran_subcarrier_spacing_t pdcch_scs = phy_cfg.carrier.scs; + + // Make CORESET Zero from provided field and given subcarrier spacing + srsran_coreset_t coreset0 = {}; + if (srsran_coreset_zero(phy_cfg.carrier.pci, + ssb_pointA_freq_offset_Hz, + ssb_scs, + pdcch_scs, + pdcch_cfg_common.ctrl_res_set_zero, + &coreset0) < SRSASN_SUCCESS) { + logger.warning("Not possible to create CORESET Zero (ssb_scs=%s, pdcch_scs=%s, idx=%d)", + srsran_subcarrier_spacing_to_str(ssb_scs), + srsran_subcarrier_spacing_to_str(pdcch_scs), + pdcch_cfg_common.ctrl_res_set_zero); + return false; + } + + // Write CORESET Zero in index 0 + phy_cfg.pdcch.coreset[0] = coreset0; + phy_cfg.pdcch.coreset_present[0] = true; + } + + if (pdcch_cfg_common.common_ctrl_res_set_present) { + srsran_coreset_t coreset; + if (make_phy_coreset_cfg(pdcch_cfg_common.common_ctrl_res_set, &coreset) == true) { + phy_cfg.pdcch.coreset[coreset.id] = coreset; + phy_cfg.pdcch.coreset_present[coreset.id] = true; + } else { + logger.warning("Warning while building coreset structure"); + return false; + } + } else { + logger.warning("Option common_ctrl_res_set not present"); + return false; + } + if (pdcch_cfg_common.common_search_space_list.size() > 0) { + for (uint32_t i = 0; i < pdcch_cfg_common.common_search_space_list.size(); i++) { + srsran_search_space_t search_space; + if (make_phy_search_space_cfg(pdcch_cfg_common.common_search_space_list[i], &search_space) == true) { + phy_cfg.pdcch.search_space[search_space.id] = search_space; + phy_cfg.pdcch.search_space_present[search_space.id] = true; + } else { + logger.warning("Warning while building search_space structure for common search space"); + return false; + } + } + } else { + logger.warning("Option common_search_space_list not present"); + return false; + } + if (pdcch_cfg_common.ra_search_space_present) { + if (phy_cfg.pdcch.search_space_present[pdcch_cfg_common.ra_search_space] == true) { + phy_cfg.pdcch.ra_search_space = phy_cfg.pdcch.search_space[pdcch_cfg_common.ra_search_space]; + phy_cfg.pdcch.ra_search_space_present = true; + phy_cfg.pdcch.ra_search_space.type = srsran_search_space_type_common_1; + } else { + logger.warning("Search space %d not presenet for random access search space", + pdcch_cfg_common.ra_search_space); + } + } else { + logger.warning("Option ra_search_space not present"); + return false; + } + } else { + logger.warning("Option pdsch_cfg_common not of type setup"); + return false; + } + } else { + logger.warning("Option pdsch_cfg_common not present"); + return false; + } + if (dl_cfg_common.init_dl_bwp.pdsch_cfg_common_present) { + if (dl_cfg_common.init_dl_bwp.pdsch_cfg_common.type() == setup_release_c::types::setup) { + pdsch_cfg_common_s pdsch_cfg_common = dl_cfg_common.init_dl_bwp.pdsch_cfg_common.setup(); + if (pdsch_cfg_common.pdsch_time_domain_alloc_list.size() > 0) { + for (uint32_t i = 0; i < pdsch_cfg_common.pdsch_time_domain_alloc_list.size(); i++) { + srsran_sch_time_ra_t common_time_ra; + if (make_phy_common_time_ra(pdsch_cfg_common.pdsch_time_domain_alloc_list[i], &common_time_ra) == true) { + phy_cfg.pdsch.common_time_ra[i] = common_time_ra; + phy_cfg.pdsch.nof_common_time_ra = i + 1; + } else { + logger.warning("Warning while building common_time_ra structure"); + return false; + } + } + } else { + logger.warning("Option pdsch_time_domain_alloc_list not present"); + return false; + } + } else { + logger.warning("Option pdsch_cfg_common not of type setup"); + return false; + } + } else { + logger.warning("Option pdsch_cfg_common not present"); + return false; + } + } else { + logger.warning("Option init_dl_bwp not present"); + return false; + } + return true; +} + +bool rrc_nr::apply_ul_common_cfg(const asn1::rrc_nr::ul_cfg_common_s& ul_cfg_common) +{ + srsran::srsran_band_helper bands; + + if (ul_cfg_common.freq_info_ul_present && ul_cfg_common.freq_info_ul.absolute_freq_point_a_present) { + // Update UL frequency point if provided + phy_cfg.carrier.ul_center_frequency_hz = bands.get_center_freq_from_abs_freq_point_a( + phy_cfg.carrier.nof_prb, ul_cfg_common.freq_info_ul.absolute_freq_point_a); + } + if (ul_cfg_common.init_ul_bwp_present) { + if (ul_cfg_common.init_ul_bwp.rach_cfg_common_present) { + if (ul_cfg_common.init_ul_bwp.rach_cfg_common.type() == setup_release_c::types_opts::setup) { + rach_cfg_nr_t rach_cfg_nr = {}; + make_mac_rach_cfg(ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), &rach_cfg_nr); + mac->set_config(rach_cfg_nr); + + // Make the RACH configuration for PHY + if (not make_phy_rach_cfg( + ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(), SRSRAN_DUPLEX_MODE_FDD, &phy_cfg.prach)) { + logger.warning("Error parsing rach_cfg_common"); + return false; + } + + } else { + logger.warning("Option rach_cfg_common not of type setup"); + return false; + } + } else { + logger.warning("Option rach_cfg_common not present"); + return false; + } + if (ul_cfg_common.init_ul_bwp.pusch_cfg_common_present) { + if (ul_cfg_common.init_ul_bwp.pusch_cfg_common.type() == setup_release_c::types_opts::setup) { + if (ul_cfg_common.init_ul_bwp.pusch_cfg_common.setup().pusch_time_domain_alloc_list.size() > 0) { + for (uint32_t i = 0; + i < ul_cfg_common.init_ul_bwp.pusch_cfg_common.setup().pusch_time_domain_alloc_list.size(); + i++) { + srsran_sch_time_ra_t common_time_ra; + if (make_phy_common_time_ra( + ul_cfg_common.init_ul_bwp.pusch_cfg_common.setup().pusch_time_domain_alloc_list[i], + &common_time_ra) == true) { + phy_cfg.pusch.common_time_ra[i] = common_time_ra; + phy_cfg.pusch.nof_common_time_ra = i + 1; + } else { + logger.warning("Warning while building common_time_ra structure"); + } + } + } else { + logger.warning("Option pusch_time_domain_alloc_list not present"); + return false; + } + } else { + logger.warning("Option pusch_cfg_common not of type setup"); + return false; + } + } else { + logger.warning("Option pusch_cfg_common not present"); + return false; + } + if (ul_cfg_common.init_ul_bwp.pucch_cfg_common_present) { + if (ul_cfg_common.init_ul_bwp.pucch_cfg_common.type() == setup_release_c::types_opts::setup) { + logger.info("PUCCH cfg common setup not handled"); + } else { + logger.warning("Option pucch_cfg_common not of type setup"); + return false; + } + } else { + logger.warning("Option pucch_cfg_common not present"); + return false; + } + } else { + logger.warning("Option init_ul_bwp in spCellConfigCommon not present"); + return false; + } + return true; +} + +bool rrc_nr::apply_sp_cell_ded_ul_pucch(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg) +{ + // determine format 2 max code rate + uint32_t format_2_max_code_rate = 0; + if (pucch_cfg.format2_present && pucch_cfg.format2.type() == setup_release_c::types::setup) { + if (pucch_cfg.format2.setup().max_code_rate_present) { + if (make_phy_max_code_rate(pucch_cfg.format2.setup(), &format_2_max_code_rate) == false) { + logger.warning("Warning while building format_2_max_code_rate"); + } + } + } else { + logger.warning("Option format2 not present or not of type setup"); + return false; + } + + // now look up resource and assign into internal struct + if (pucch_cfg.res_to_add_mod_list.size() > 0) { + for (uint32_t i = 0; i < pucch_cfg.res_to_add_mod_list.size(); i++) { + uint32_t res_id = pucch_cfg.res_to_add_mod_list[i].pucch_res_id; + pucch_res_list.insert(res_id, {}); + if (!make_phy_res_config(pucch_cfg.res_to_add_mod_list[i], format_2_max_code_rate, &pucch_res_list[res_id])) { + logger.warning("Warning while building pucch_nr_resource structure"); + return false; + } + } + } else { + logger.warning("Option res_to_add_mod_list not present"); + return false; + } + + // Check first all resource lists and + phy_cfg.pucch.enabled = true; + if (pucch_cfg.res_set_to_add_mod_list.size() > 0) { + for (uint32_t i = 0; i < pucch_cfg.res_set_to_add_mod_list.size(); i++) { + uint32_t set_id = pucch_cfg.res_set_to_add_mod_list[i].pucch_res_set_id; + phy_cfg.pucch.sets[set_id].nof_resources = pucch_cfg.res_set_to_add_mod_list[i].res_list.size(); + for (uint32_t j = 0; j < pucch_cfg.res_set_to_add_mod_list[i].res_list.size(); j++) { + uint32_t res_id = pucch_cfg.res_set_to_add_mod_list[i].res_list[j]; + if (pucch_res_list.contains(res_id)) { + phy_cfg.pucch.sets[set_id].resources[j] = pucch_res_list[res_id]; + } else { + logger.error( + "Resources set not present for assign pucch sets (res_id %d, setid %d, j %d)", res_id, set_id, j); + } + } + } + } + + if (pucch_cfg.sched_request_res_to_add_mod_list.size() > 0) { + for (uint32_t i = 0; i < pucch_cfg.sched_request_res_to_add_mod_list.size(); i++) { + uint32_t sr_res_id = pucch_cfg.sched_request_res_to_add_mod_list[i].sched_request_res_id; + srsran_pucch_nr_sr_resource_t srsran_pucch_nr_sr_resource; + if (make_phy_sr_resource(pucch_cfg.sched_request_res_to_add_mod_list[i], &srsran_pucch_nr_sr_resource) == + true) { // TODO: fix that if indexing is solved + phy_cfg.pucch.sr_resources[sr_res_id] = srsran_pucch_nr_sr_resource; + + // Set PUCCH resource + if (pucch_cfg.sched_request_res_to_add_mod_list[i].res_present) { + uint32_t pucch_res_id = pucch_cfg.sched_request_res_to_add_mod_list[i].res; + if (pucch_res_list.contains(pucch_res_id)) { + phy_cfg.pucch.sr_resources[sr_res_id].resource = pucch_res_list[pucch_res_id]; + } else { + logger.warning("Warning SR (%d) PUCCH resource is invalid (%d)", sr_res_id, pucch_res_id); + phy_cfg.pucch.sr_resources[sr_res_id].configured = false; + return false; + } + } else { + logger.warning("Warning SR resource is present but no PUCCH resource is assigned to it"); + phy_cfg.pucch.sr_resources[sr_res_id].configured = false; + return false; + } + + } else { + logger.warning("Warning while building srsran_pucch_nr_sr_resource structure"); + return false; + } + } + } else { + logger.warning("Option sched_request_res_to_add_mod_list not present"); + return false; + } + + if (pucch_cfg.dl_data_to_ul_ack.size() > 0) { + for (uint32_t i = 0; i < pucch_cfg.dl_data_to_ul_ack.size(); i++) { + phy_cfg.harq_ack.dl_data_to_ul_ack[i] = pucch_cfg.dl_data_to_ul_ack[i]; + } + phy_cfg.harq_ack.nof_dl_data_to_ul_ack = pucch_cfg.dl_data_to_ul_ack.size(); + } else { + logger.warning("Option dl_data_to_ul_ack not present"); + return false; + } + + return true; +}; + +bool rrc_nr::apply_sp_cell_ded_ul_pusch(const asn1::rrc_nr::pusch_cfg_s& pusch_cfg) +{ + if (pusch_cfg.mcs_table_present) { + switch (pusch_cfg.mcs_table) { + case pusch_cfg_s::mcs_table_opts::qam256: + phy_cfg.pusch.mcs_table = srsran_mcs_table_256qam; + break; + case pusch_cfg_s::mcs_table_opts::qam64_low_se: + phy_cfg.pusch.mcs_table = srsran_mcs_table_qam64LowSE; + break; + case pusch_cfg_s::mcs_table_opts::nulltype: + logger.warning("Warning while selecting pusch mcs_table"); + return false; + } + } else { + // If the field is absent the UE applies the value 64QAM. + phy_cfg.pusch.mcs_table = srsran_mcs_table_64qam; + } + + srsran_resource_alloc_t resource_alloc; + if (make_phy_pusch_alloc_type(pusch_cfg, &resource_alloc) == true) { + phy_cfg.pusch.alloc = resource_alloc; + } + + if (pusch_cfg.dmrs_ul_for_pusch_map_type_a_present) { + if (pusch_cfg.dmrs_ul_for_pusch_map_type_a.type() == setup_release_c::types_opts::setup) { + srsran_dmrs_sch_add_pos_t srsran_dmrs_sch_add_pos; + if (make_phy_dmrs_ul_additional_pos(pusch_cfg.dmrs_ul_for_pusch_map_type_a.setup(), &srsran_dmrs_sch_add_pos) == + true) { + phy_cfg.pusch.dmrs_typeA.additional_pos = srsran_dmrs_sch_add_pos; + phy_cfg.pusch.dmrs_typeA.present = true; + } else { + logger.warning("Warning while build srsran_dmrs_sch_add_pos structure"); + return false; + } + } else { + logger.warning("Option dmrs_ul_for_pusch_map_type_a not of type setup"); + return false; + } + } else { + logger.warning("Option dmrs_ul_for_pusch_map_type_a not present"); + return false; + } + if (pusch_cfg.uci_on_pusch_present) { + if (pusch_cfg.uci_on_pusch.type() == setup_release_c::types_opts::setup) { + if (pusch_cfg.uci_on_pusch.setup().beta_offsets_present) { + if (pusch_cfg.uci_on_pusch.setup().beta_offsets.type() == + uci_on_pusch_s::beta_offsets_c_::types_opts::semi_static) { + srsran_beta_offsets_t beta_offsets; + if (make_phy_beta_offsets(pusch_cfg.uci_on_pusch.setup().beta_offsets.semi_static(), &beta_offsets) == true) { + phy_cfg.pusch.beta_offsets = beta_offsets; + } else { + logger.warning("Warning while building beta_offsets structure"); + return false; + } + } else { + logger.warning("Option beta_offsets not of type semi_static"); + return false; + } + if (pusch_cfg.uci_on_pusch_present) { + if (make_phy_pusch_scaling(pusch_cfg.uci_on_pusch.setup(), &phy_cfg.pusch.scaling) == false) { + logger.warning("Warning while building scaling structure"); + return false; + } + } + } else { + logger.warning("Option beta_offsets not present"); + return false; + } + } else { + logger.warning("Option uci_on_pusch of type setup"); + return false; + } + } else { + logger.warning("Option uci_on_pusch not present"); + return false; + } + return true; +}; + +bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) +{ + update_sp_cell_cfg(sp_cell_cfg); + + return true; +} + +bool rrc_nr::update_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) +{ + // NSA specific handling to defer CSI, SR, SRS config until after RA (see TS 38.331, Section 5.3.5.3) + srsran_csi_hl_cfg_t prev_csi = phy_cfg.csi; + + if (sp_cell_cfg.recfg_with_sync_present) { + const recfg_with_sync_s& recfg_with_sync = sp_cell_cfg.recfg_with_sync; + mac->set_crnti(recfg_with_sync.new_ue_id); + if (recfg_with_sync.sp_cell_cfg_common_present) { + if (recfg_with_sync.sp_cell_cfg_common.pci_present) { + phy_cfg.carrier.pci = recfg_with_sync.sp_cell_cfg_common.pci; + phy_cfg.carrier.max_mimo_layers = 1; // TODO: flatten + } else { + logger.warning("Option PCI not present"); + return false; + } + // Read essential DL carrier settings + if (recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present) { + if (apply_dl_common_cfg(recfg_with_sync.sp_cell_cfg_common.dl_cfg_common) == false) { + return false; + } + } else { + logger.warning("Secondary primary cell dl cfg common not present"); + return false; + } + if (recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present) { + if (apply_ul_common_cfg(recfg_with_sync.sp_cell_cfg_common.ul_cfg_common) == false) { + return false; + } + } else { + logger.warning("Secondary primary cell ul cfg common not present"); + return false; + } + // Build SSB config + phy_cfg_nr_t::ssb_cfg_t ssb_cfg = {}; + if (make_phy_ssb_cfg(phy_cfg.carrier, recfg_with_sync.sp_cell_cfg_common, &ssb_cfg) == true) { + phy_cfg.ssb = ssb_cfg; + } else { + logger.warning("Warning while building SSB config structure"); + return false; + } + if (recfg_with_sync.sp_cell_cfg_common.tdd_ul_dl_cfg_common_present) { + logger.debug("TDD UL DL config present, using TDD"); + srsran_duplex_config_nr_t duplex; + if (make_phy_tdd_cfg(recfg_with_sync.sp_cell_cfg_common.tdd_ul_dl_cfg_common, &duplex)) { + phy_cfg.duplex = duplex; + phy_cfg.prach.tdd_config.configured = true; + } else { + logger.warning("Warning while building duplex structure"); + return false; + } + } else { + logger.debug("TDD UL DL config not present, using FDD"); + } + } + } else { + // for SA this is not sent + logger.debug("Reconfig with sync not present"); + } + + // Dedicated config + if (sp_cell_cfg.sp_cell_cfg_ded_present) { + // Dedicated Downlink + if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.type() == + setup_release_c::types_opts::setup) { + if (apply_sp_cell_init_dl_pdcch(sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup()) == false) { + return false; + } + } else { + logger.warning("Option pdcch_cfg not of type setup"); + return false; + } + } else { + logger.warning("Option pdcch_cfg not present"); + return false; + } + if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg.type() == + setup_release_c::types_opts::setup) { + if (apply_sp_cell_init_dl_pdsch(sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg.setup()) == false) { + logger.error("Couldn't apply PDSCH config for initial DL BWP in SpCell Cfg dedicated"); + return false; + }; + } else { + logger.warning("Option pdsch_cfg_cfg not of type setup"); + return false; + } + } else { + logger.warning("Option pdsch_cfg not present"); + return false; + } + } else { + logger.warning("Option init_dl_bwp not present"); + return false; + } + // Dedicated Uplink + if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg.type() == + setup_release_c::types_opts::setup) { + if (apply_sp_cell_ded_ul_pucch(sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg.setup()) == false) { + return false; + } + } else { + logger.warning("Option pucch_cfg not of type setup"); + return false; + } + } else { + logger.warning("Option pucch_cfg for initial UL BWP in spCellConfigDedicated not present"); + return false; + } + if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg.type() == + setup_release_c::types_opts::setup) { + if (apply_sp_cell_ded_ul_pusch(sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pusch_cfg.setup()) == false) { + return false; + } + } else { + logger.warning("Option pusch_cfg not of type setup"); + return false; + } + } else { + logger.warning("Option pusch_cfg in spCellConfigDedicated not present"); + return false; + } + } else { + logger.warning("Option init_ul_bwp in spCellConfigDedicated not present"); + return false; + } + } else { + logger.warning("Option ul_cfg in spCellConfigDedicated not present"); + return false; + } + + if (sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.type() == + setup_release_c::types_opts::setup) { + dl_harq_cfg_nr_t dl_harq_cfg_nr; + if (make_mac_dl_harq_cfg_nr_t(sp_cell_cfg.sp_cell_cfg_ded.pdsch_serving_cell_cfg.setup(), &dl_harq_cfg_nr) == + false) { + logger.warning("Failed to make dl_harq_cfg_nr config"); + return false; + } + mac->set_config(dl_harq_cfg_nr); + } + } else { + logger.debug("Option pdsch_serving_cell_cfg not present"); + } + + if (sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present) { + if (sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.type() == setup_release_c::types_opts::setup) { + if (apply_csi_meas_cfg(sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup()) == false) { + return false; + } + } else { + logger.warning("Option csi_meas_cfg not of type setup"); + return false; + } + } else { + logger.warning("Option csi_meas_cfg in spCellConfigDedicated not present"); + } + + } else { + logger.warning("Option sp_cell_cfg_ded not present"); + return false; + } + + // Configure PHY + if (sp_cell_cfg.recfg_with_sync_present) { + // defer CSI config until after RA complete + srsran::phy_cfg_nr_t current_phycfg = phy_cfg; + current_phycfg.csi = prev_csi; + phy_cfg_state = PHY_CFG_STATE_NSA_APPLY_SP_CELL; + phy->set_config(current_phycfg); + } else { + // apply full config immediately + phy_cfg_state = PHY_CFG_STATE_SA_FULL_CFG; + phy->set_config(phy_cfg); + } + + return true; +} + +bool rrc_nr::apply_phy_cell_group_cfg(const phys_cell_group_cfg_s& phys_cell_group_cfg) +{ + srsran_harq_ack_cfg_hl_t harq_ack; + if (make_phy_harq_ack_cfg(phys_cell_group_cfg, &harq_ack) == true) { + phy_cfg.harq_ack = harq_ack; + } else { + logger.warning("Warning while building harq_ack structure"); + return false; + } + return true; +} + +bool rrc_nr::apply_cell_group_cfg(const cell_group_cfg_s& cell_group_cfg) +{ + update_cell_group_cfg(cell_group_cfg); + + return true; +} + +bool rrc_nr::update_cell_group_cfg(const cell_group_cfg_s& cell_group_cfg) +{ + if (cell_group_cfg.rlc_bearer_to_add_mod_list.size() > 0) { + for (uint32_t i = 0; i < cell_group_cfg.rlc_bearer_to_add_mod_list.size(); i++) { + if (apply_rlc_add_mod(cell_group_cfg.rlc_bearer_to_add_mod_list[i]) == false) { + return false; + } + } + } + if (cell_group_cfg.mac_cell_group_cfg_present) { + if (apply_mac_cell_group(cell_group_cfg.mac_cell_group_cfg) == false) { + return false; + } + } + if (cell_group_cfg.phys_cell_group_cfg_present) { + if (apply_phy_cell_group_cfg(cell_group_cfg.phys_cell_group_cfg) == false) { + return false; + } + } + if (cell_group_cfg.sp_cell_cfg_present) { + if (update_sp_cell_cfg(cell_group_cfg.sp_cell_cfg) == false) { + return false; + } + } + return true; +} + +bool rrc_nr::apply_drb_release(const uint8_t drb) +{ + uint32_t lcid = get_lcid_for_drbid(drb); + if (lcid == 0) { + logger.warning("Can not release bearer with lcid %d and drb %d", lcid, drb); + return false; + } + logger.info("Releasing bearer DRB: %d LCID: %d", drb, lcid); + pdcp->del_bearer(lcid); + // TODO + // 2> if the UE is operating in EN-DC + // 3> if a new bearer is not added either with NR or E-UTRA with same eps-BearerIdentity: + // 4> indicate the release of the DRB and the eps-BearerIdentity of the released DRB to upper layers. + return true; +} + +bool rrc_nr::apply_srb_add_mod(const srb_to_add_mod_s& srb_cfg) +{ + logger.debug("Applying SRB Add/Mod to SRB%d", srb_cfg.srb_id); + if (srb_cfg.pdcp_cfg_present) { + logger.error("Cannot add SRB - only default configuration supported."); + return false; + } + + srsran::pdcp_config_t pdcp_cfg = srsran::make_nr_srb_pdcp_config_t(srb_cfg.srb_id, true); + pdcp->add_bearer(srb_cfg.srb_id, pdcp_cfg); + + return true; +} + +bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg) +{ + logger.debug("Applying DRB Add/Mod to DRB%d", drb_cfg.drb_id); + if (!drb_cfg.pdcp_cfg_present) { + logger.error("Cannot add DRB - incomplete configuration"); + return false; + } + + uint32_t lcid = get_lcid_for_drbid(drb_cfg.drb_id); + if (lcid == 0) { + logger.error("Cannot find valid LCID for DRB %d", drb_cfg.drb_id); + return false; + } + + // Setup PDCP + if (!(drb_cfg.pdcp_cfg.drb_present == true)) { + logger.error("PDCP config does not contain DRB config"); + return false; + } + + if (!(drb_cfg.cn_assoc_present == true)) { + logger.error("DRB config does not contain an associated cn"); + return false; + } + + if (drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::eps_bearer_id) { + // register EPS bearer over NR PDCP + uint32_t eps_bearer_id = drb_cfg.cn_assoc.eps_bearer_id(); + drb_eps_bearer_id[drb_cfg.drb_id] = eps_bearer_id; + stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::nr, lcid); + } else if (drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::sdap_cfg) { + const auto& sdap_cfg = drb_cfg.cn_assoc.sdap_cfg(); + + // Check supported configuration + if (sdap_cfg.sdap_hdr_dl.value == sdap_cfg_s::sdap_hdr_dl_opts::present || !sdap_cfg.default_drb || + sdap_cfg.mapped_qos_flows_to_add.size() != 1) { + logger.error( + "Configuring SDAP: only UL headder is supported. Default DRB must be set and number of QoS flows must be 1"); + return false; + } + + sdap_interface_rrc::bearer_cfg_t sdap_bearer_cfg = {}; + sdap_bearer_cfg.add_downlink_header = sdap_cfg.sdap_hdr_dl.value == sdap_cfg_s::sdap_hdr_dl_opts::present; + sdap_bearer_cfg.add_uplink_header = sdap_cfg.sdap_hdr_ul.value == sdap_cfg_s::sdap_hdr_ul_opts::present; + sdap_bearer_cfg.is_data = true; + sdap_bearer_cfg.qfi = sdap_cfg.mapped_qos_flows_to_add[0]; + + if (not sdap->set_bearer_cfg(lcid, sdap_bearer_cfg)) { + logger.error("Configuring SDAP"); + return false; + } + + uint32_t pdu_session_id = drb_cfg.cn_assoc.sdap_cfg().pdu_session; + // Register PDU session as "EPS bearer" in bearer manager + stack->add_eps_bearer(pdu_session_id, srsran::srsran_rat_t::nr, lcid); + } else { + logger.error("CN association type not supported %s", drb_cfg.cn_assoc.type().to_string()); + return false; + } + + if (drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl_present && drb_cfg.pdcp_cfg.drb.pdcp_sn_size_ul_present && + (drb_cfg.pdcp_cfg.drb.pdcp_sn_size_ul.to_number() != drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl.to_number())) { + logger.warning("PDCP SN size in UL and DL are not the same. make_drb_pdcp_config_t will use the DL SN size %d ", + drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl.to_number()); + } + + if (not security_is_activated) { + logger.error("Trying to setup DRB%d, but security is not activated", drb_cfg.drb_id); + return false; + } + srsran::pdcp_config_t pdcp_cfg = make_drb_pdcp_config_t(drb_cfg.drb_id, true, drb_cfg.pdcp_cfg); + pdcp->add_bearer(lcid, pdcp_cfg); + + // Use already configured sec config, if no other sec config present in the RadioBearerConfig + pdcp->config_security(lcid, sec_cfg); + pdcp->enable_encryption(lcid, DIRECTION_TXRX); + if (drb_cfg.pdcp_cfg.drb.integrity_protection_present) { + pdcp->enable_integrity(lcid, DIRECTION_TXRX); + } + return true; +} + +bool rrc_nr::apply_security_cfg(const security_cfg_s& security_cfg) +{ + logger.debug("Applying Security config"); + if (security_cfg.key_to_use_present) { + if (security_cfg.key_to_use.value != security_cfg_s::key_to_use_opts::options::secondary) { + logger.warning("Only secondary key supported yet"); + return false; + } + } + + if (security_cfg.security_algorithm_cfg_present) { + switch (security_cfg.security_algorithm_cfg.ciphering_algorithm) { + case ciphering_algorithm_e::nea0: + sec_cfg.cipher_algo = CIPHERING_ALGORITHM_ID_EEA0; + break; + default: + logger.error("Ciphering not supported by PDCP-NR at the moment %s. Requested algorithm=%s", + security_cfg.security_algorithm_cfg.ciphering_algorithm.to_string()); + srsran::console("Ciphering not supported by PDCP-NR at the moment. Requested algorithm=%s\n", + security_cfg.security_algorithm_cfg.ciphering_algorithm.to_string()); + return false; + } + + if (security_cfg.security_algorithm_cfg.integrity_prot_algorithm_present) { + switch (security_cfg.security_algorithm_cfg.integrity_prot_algorithm) { + case integrity_prot_algorithm_e::nia0: + sec_cfg.integ_algo = INTEGRITY_ALGORITHM_ID_EIA0; + break; + default: + logger.error("Integrity protection not supported by PDCP-NR at the moment. Requested algorithm=%s.", + security_cfg.security_algorithm_cfg.ciphering_algorithm.to_string()); + srsran::console("Integrity protection not supported by PDCP-NR at the moment. Requested algorithm %s.\n", + security_cfg.security_algorithm_cfg.ciphering_algorithm.to_string()); + return false; + } + } + if (usim->update_nr_context(&sec_cfg) == false) { + return false; + } + } + + // Apply security config for all known NR lcids + for (auto& lcid : lcid_drb) { + logger.debug("Applying PDCP security config. LCID=%d", lcid.first); + pdcp->config_security(lcid.first, sec_cfg); + pdcp->enable_encryption(lcid.first); + } + return true; +} + +bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg) +{ + for (const auto& srb : radio_bearer_cfg.srb_to_add_mod_list) { + if (apply_srb_add_mod(srb) == false) { + logger.error("Couldn't apply config for SRB%d.", srb.srb_id); + return false; + } + } + for (const auto& drb : radio_bearer_cfg.drb_to_add_mod_list) { + if (apply_drb_add_mod(drb) == false) { + return false; + } + } + for (const auto& drb : radio_bearer_cfg.drb_to_release_list) { + if (apply_drb_release(drb) == false) { + return false; + } + } + + if (radio_bearer_cfg.security_cfg_present) { + if (apply_security_cfg(radio_bearer_cfg.security_cfg) == false) { + return false; + } + } else { + logger.debug("No Security Config Present"); + } + return true; +} + +bool rrc_nr::handle_rrc_setup(const rrc_setup_s& setup) +{ + // Unpack masterCellGroup into container + asn1::cbit_ref bref_cg(setup.crit_exts.rrc_setup().master_cell_group.data(), + setup.crit_exts.rrc_setup().master_cell_group.size()); + + asn1::rrc_nr::cell_group_cfg_s cell_group; + if (cell_group.unpack(bref_cg) != asn1::SRSASN_SUCCESS) { + logger.error("Could not unpack master cell group config."); + return false; + } + asn1::json_writer js; + cell_group.to_json(js); + logger.debug("Containerized MasterCellGroup: %s", js.to_string().c_str()); + + state = RRC_NR_STATE_CONNECTED; + srsran::console("RRC Connected\n"); + + // defer transmission of Setup Complete until PHY reconfiguration has been completed + if (not conn_setup_proc.launch( + setup.crit_exts.rrc_setup().radio_bearer_cfg, cell_group, std::move(dedicated_info_nas))) { + logger.error("Failed to initiate connection setup procedure"); + return false; + } + callback_list.add_proc(conn_setup_proc); + return true; +} + +void rrc_nr::handle_rrc_reconfig(const rrc_recfg_s& reconfig) +{ + transaction_id = reconfig.rrc_transaction_id; + + if (not conn_recfg_proc.launch(nr, false, reconfig)) { + logger.error("Unable to launch connection reconfiguration procedure"); + return; + } + callback_list.add_proc(conn_recfg_proc); +} +void rrc_nr::handle_ue_capability_enquiry(const ue_cap_enquiry_s& ue_cap_enquiry) +{ + logger.info("Received UE Capability Enquiry"); + send_ue_capability_info(ue_cap_enquiry); +} + +void rrc_nr::handle_dl_info_transfer(const dl_info_transfer_s& dl_info_transfer) +{ + transaction_id = dl_info_transfer.rrc_transaction_id; + + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return; + } + if (pdu->get_tailroom() < dl_info_transfer.crit_exts.dl_info_transfer().ded_nas_msg.size()) { + logger.error("DL Info Transfer too big (%d > %d)", + dl_info_transfer.crit_exts.dl_info_transfer().ded_nas_msg.size(), + pdu->get_tailroom()); + return; + } + pdu->N_bytes = dl_info_transfer.crit_exts.dl_info_transfer().ded_nas_msg.size(); + memcpy(pdu->msg, dl_info_transfer.crit_exts.dl_info_transfer().ded_nas_msg.data(), pdu->N_bytes); + nas->write_pdu(std::move(pdu)); +} + +void rrc_nr::handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_s& smc) +{ + transaction_id = smc.rrc_transaction_id; + + const auto& sec_algo_cfg = smc.crit_exts.security_mode_cmd().security_cfg_smc.security_algorithm_cfg; + sec_cfg.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)sec_algo_cfg.ciphering_algorithm.value; + if (sec_algo_cfg.integrity_prot_algorithm_present) { + sec_cfg.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM)sec_algo_cfg.integrity_prot_algorithm.value; + } else { + logger.error("Missing Integrity Algorithm Config"); + } + + logger.info("Received Security Mode Command nea: %s, nia: %s", + ciphering_algorithm_id_nr_text[sec_cfg.cipher_algo], + integrity_algorithm_id_nr_text[sec_cfg.integ_algo]); + + // Generate AS security keys + generate_as_keys(); + security_is_activated = true; + + // Configure PDCP for security + uint32_t lcid = srb_to_lcid(nr_srb::srb1); + pdcp->config_security(lcid, sec_cfg); + pdcp->enable_integrity(lcid, DIRECTION_TXRX); + send_security_mode_complete(); + pdcp->enable_encryption(lcid, DIRECTION_TXRX); +} + +void rrc_nr::handle_rrc_release(const asn1::rrc_nr::rrc_release_s& msg) +{ + logger.info("Received RRC Release"); + srsran::console("Received RRC Release\n"); + + rrc_release(); +} + +// Security helper used by Security Mode Command and Mobility handling routines +void rrc_nr::generate_as_keys() +{ + as_key_t k_amf = {}; + nas->get_k_amf(k_amf); + logger.debug(k_amf.data(), 32, "UE K_amf"); + logger.debug("Generating K_gnb with UL NAS COUNT: %d", nas->get_ul_nas_count()); + usim->generate_nr_as_keys(k_amf, nas->get_ul_nas_count(), &sec_cfg); + logger.info(sec_cfg.k_rrc_enc.data(), 32, "RRC encryption key - k_rrc_enc"); + logger.info(sec_cfg.k_rrc_int.data(), 32, "RRC integrity key - k_rrc_int"); + logger.info(sec_cfg.k_up_enc.data(), 32, "UP encryption key - k_up_enc"); +} + +// RLC interface +void rrc_nr::max_retx_attempted() {} +void rrc_nr::protocol_failure() {} + +// MAC interface +void rrc_nr::ra_completed() +{ + logger.info("RA completed."); + if (rrc_eutra) { + logger.debug("Applying remaining CSI configuration."); + phy_cfg_state = PHY_CFG_STATE_NSA_RA_COMPLETED; + phy->set_config(phy_cfg); + } else { + phy_cfg_state = PHY_CFG_STATE_NONE; + } +} + +void rrc_nr::ra_problem() +{ + if (rrc_eutra) { + rrc_eutra->nr_scg_failure_information(scg_failure_cause_t::random_access_problem); + } else { + // TODO: handle RA problem + } +} + +void rrc_nr::release_pucch_srs() {} + +// STACK interface +void rrc_nr::cell_search_found_cell(const rrc_interface_phy_nr::cell_search_result_t& result) +{ + cell_selector.trigger(result); +} + +void rrc_nr::cell_select_completed(const rrc_interface_phy_nr::cell_select_result_t& result) +{ + cell_selector.trigger(result); +} + +void rrc_nr::set_phy_config_complete(bool status) +{ + // inform procedures if they are running + if (conn_setup_proc.is_busy()) { + conn_setup_proc.trigger(status); + } + + if (conn_recfg_proc.is_busy()) { + conn_recfg_proc.trigger(status); + } + + switch (phy_cfg_state) { + case PHY_CFG_STATE_NONE: + logger.warning("PHY configuration completed without a clear state."); + break; + case PHY_CFG_STATE_SA_MIB_CFG: + logger.info("PHY configuration with MIB parameters completed."); + break; + case PHY_CFG_STATE_SA_SIB_CFG: + logger.info("PHY configuration with SIB parameters completed."); + break; + case PHY_CFG_STATE_SA_FULL_CFG: + logger.info("PHY configuration completed."); + break; + case PHY_CFG_STATE_NSA_APPLY_SP_CELL: + // Start RA procedure + logger.info("PHY configuration completed. Starting RA procedure."); + mac->start_ra_procedure(); + break; + case PHY_CFG_STATE_NSA_RA_COMPLETED: + logger.info("Remaining CSI configuration completed."); + break; + } + phy_cfg_state = PHY_CFG_STATE_NONE; +} + +} // namespace srsue diff --git a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc new file mode 100644 index 000000000..c576b39fd --- /dev/null +++ b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc @@ -0,0 +1,567 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h" +#include "srsran/common/standard_streams.h" + +#define Error(fmt, ...) rrc_handle.logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Warning(fmt, ...) rrc_handle.logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Info(fmt, ...) rrc_handle.logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Debug(fmt, ...) rrc_handle.logger.debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) + +using namespace asn1::rrc_nr; +using namespace asn1; +using namespace srsran; + +namespace srsue { + +rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr& parent_) : rrc_handle(parent_), initiator(nr) +{} + +proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator_t initiator_, + const bool endc_release_and_add_r15, + const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf) +{ + Info("Starting..."); + initiator = initiator_; + + asn1::json_writer js; + rrc_nr_reconf.to_json(js); + Debug("RRC NR Reconfiguration: %s", js.to_string().c_str()); + + if (rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group.size() > 0) { + if (rrc_nr_reconf.crit_exts.type() != asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg) { + Error("Reconfiguration does not contain Secondary Cell Group Config."); + return proc_outcome_t::error; + } + + cbit_ref bref0(rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group.data(), + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group.size()); + + cell_group_cfg_s secondary_cell_group_cfg; + if (secondary_cell_group_cfg.unpack(bref0) != asn1::SRSASN_SUCCESS) { + Error("Could not unpack Secondary Cell Group Config."); + return proc_outcome_t::error; + } + + asn1::json_writer js1; + secondary_cell_group_cfg.to_json(js1); + Debug("Secondary Cell Group: %s", js1.to_string().c_str()); + + Info("Applying Secondary Cell Group Cfg."); + if (!rrc_handle.apply_cell_group_cfg(secondary_cell_group_cfg)) { + return proc_outcome_t::error; + } + } + + if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present) { + Info("Applying SK Counter"); + if (!rrc_handle.configure_sk_counter( + (uint16_t)rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter)) { + return proc_outcome_t::error; + } + } + + if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.master_cell_group.size() > 0) { + cbit_ref bref1(rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.master_cell_group.data(), + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.master_cell_group.size()); + + cell_group_cfg_s master_cell_group_cfg; + if (master_cell_group_cfg.unpack(bref1) != asn1::SRSASN_SUCCESS) { + Error("Could not unpack Master Cell Group Config."); + return proc_outcome_t::error; + } + + asn1::json_writer js2; + master_cell_group_cfg.to_json(js2); + Debug("Master Cell Group: %s", js2.to_string().c_str()); + + Info("Applying Master Cell Group Cfg."); + if (!rrc_handle.apply_cell_group_cfg(master_cell_group_cfg)) { + return proc_outcome_t::error; + } + } + + if (rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present) { + Info("Applying Radio Bearer Cfg."); + if (!rrc_handle.apply_radio_bearer_cfg(rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg)) { + return proc_outcome_t::error; + } + } + + // only send reconfig complete in SA mode + if (rrc_handle.rrc_eutra == nullptr) { + rrc_handle.send_rrc_reconfig_complete(); + } + + // Handle NAS messages + if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size() > 0) { + for (uint32_t i = 0; i < rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size(); ++i) { + srsran::unique_byte_buffer_t nas_pdu = srsran::make_byte_buffer(); + if (nas_pdu != nullptr) { + memcpy(nas_pdu->msg, + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].data(), + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size()); + nas_pdu->N_bytes = rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size(); + rrc_handle.nas->write_pdu(std::move(nas_pdu)); + } else { + rrc_handle.logger.error("Couldn't allocate SDU in %s.", __FUNCTION__); + return proc_outcome_t::error; + } + } + } + + return proc_outcome_t::success; +} + +proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::react(const bool& config_complete) +{ + if (not config_complete) { + Error("NR reconfiguration failed"); + return proc_outcome_t::error; + } + + // TODO phy ctrl + // in case there are scell to configure, wait for second phy configuration + // if (not rrc_ptr->phy_ctrl->is_config_pending()) { + // return proc_outcome_t::yield; + // } + + Info("Reconfig NR return successful"); + return proc_outcome_t::success; +} + +void rrc_nr::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& result) +{ + if (result.is_success()) { + Info("Finished %s successfully", name()); + srsran::console("RRC NR reconfiguration successful.\n"); + if (rrc_handle.rrc_eutra) { + rrc_handle.rrc_eutra->nr_rrc_con_reconfig_complete(true); + } + } else { + // 5.3.5.8.2 Inability to comply with RRCReconfiguration + switch (initiator) { + case reconf_initiator_t::mcg_srb1: + if (rrc_handle.rrc_eutra) { + rrc_handle.rrc_eutra->nr_notify_reconfiguration_failure(); + } + break; + default: + Warning("Reconfiguration failure not implemented for initiator %d", initiator); + break; + } + srsran::console("RRC NR reconfiguration failed.\n"); + Warning("Finished %s with failure", name()); + } + return; +} + +/************************************** + * RRC Setup Request Procedure + *************************************/ + +rrc_nr::setup_request_proc::setup_request_proc(rrc_nr& parent_) : + rrc_handle(parent_), logger(srslog::fetch_basic_logger("RRC-NR")) +{} + +proc_outcome_t rrc_nr::setup_request_proc::init(srsran::nr_establishment_cause_t cause_, + srsran::unique_byte_buffer_t dedicated_info_nas_) +{ + cause = cause_; + dedicated_info_nas = std::move(dedicated_info_nas_); + + if (!rrc_handle.plmn_is_selected) { + Error("Trying to connect but PLMN not selected."); + return proc_outcome_t::error; + } + + if (rrc_handle.state != RRC_NR_STATE_IDLE) { + logger.warning("Requested RRC connection establishment while not in IDLE"); + return proc_outcome_t::error; + } + + // TODO: add T302 handling + + Info("Initiation of Setup request procedure"); + + cell_search_ret = rrc_cell_search_result_t::no_cell; + + state = state_t::cell_selection; + if (rrc_handle.cell_selector.is_idle()) { + // No one is running cell selection + if (not rrc_handle.cell_selector.launch()) { + Error("Failed to initiate cell selection procedure..."); + return proc_outcome_t::error; + } + rrc_handle.callback_list.add_proc(rrc_handle.cell_selector); + } else { + Info("Cell selection proc already on-going. Wait for its result"); + } + return proc_outcome_t::yield; +} + +proc_outcome_t rrc_nr::setup_request_proc::step() +{ + if (state == state_t::cell_selection) { + // NOTE: cell selection will signal back with an event trigger + return proc_outcome_t::yield; + } + + if (state == state_t::config_serving_cell) { + // TODO: start serving cell config and start T300 + + // start T300 + rrc_handle.t300.run(); + + // Send setup request message to lower layers + rrc_handle.send_setup_request(cause); + + // Save dedicatedInfoNAS SDU, if needed (TODO: this should be passed to procedure without temp storage) + if (dedicated_info_nas.get()) { + if (rrc_handle.dedicated_info_nas.get()) { + Warning("Received a new dedicatedInfoNAS SDU but there was one still in queue. Removing it."); + rrc_handle.dedicated_info_nas.reset(); + } + + Debug("Updating dedicatedInfoNAS in RRC"); + rrc_handle.dedicated_info_nas = std::move(dedicated_info_nas); + } else { + Debug("dedicatedInfoNAS has already been provided to RRC."); + } + + Info("Waiting for RRCSetup/Reject or expiry"); + state = state_t::wait_t300; + return step(); + + } else if (state == state_t::wait_t300) { + // Wait until t300 stops due to RRCConnectionSetup/Reject or expiry + if (rrc_handle.t300.is_running()) { + return proc_outcome_t::yield; + } + + if (rrc_handle.state == RRC_NR_STATE_CONNECTED) { + // Received ConnectionSetup + return proc_outcome_t::success; + } + } + + return proc_outcome_t::error; +} + +void rrc_nr::setup_request_proc::then(const srsran::proc_state_t& result) +{ + if (result.is_error()) { + logger.warning("Could not finish setup request. Deallocating dedicatedInfoNAS PDU"); + dedicated_info_nas.reset(); + rrc_handle.dedicated_info_nas.reset(); + } else { + Info("Finished connection request procedure successfully."); + } + // TODO: signal back to NAS + // rrc_handle.nas->connection_request_completed(result.is_success()); +} + +srsran::proc_outcome_t rrc_nr::setup_request_proc::react(const cell_selection_proc::cell_selection_complete_ev& e) +{ + if (state != state_t::cell_selection) { + // ignore if we are not expecting an cell selection result + return proc_outcome_t::yield; + } + if (e.is_error()) { + return proc_outcome_t::error; + } + cell_search_ret = *e.value(); + // .. and SI acquisition + // TODO @ismagom use appropiate PHY interface + if (true /*rrc_handle.phy->cell_is_camping()*/) { + // TODO: Set default configurations + // rrc_handle.set_phy_default(); + // rrc_handle.set_mac_default(); + + // CCCH configuration applied already at start + // timeAlignmentCommon applied in configure_serving_cell + + Info("Configuring serving cell..."); + state = state_t::config_serving_cell; + + // Skip SI acquisition + return step(); + } +} + +/****************************************** + * Connection Setup Procedure + *****************************************/ + +// Simple procedure mainly do defer the transmission of the SetupComplete until all PHY reconfiguration are done +rrc_nr::connection_setup_proc::connection_setup_proc(srsue::rrc_nr& parent_) : + rrc_handle(parent_), logger(srslog::fetch_basic_logger("RRC-NR")) +{} + +srsran::proc_outcome_t rrc_nr::connection_setup_proc::init(const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_cfg_, + const asn1::rrc_nr::cell_group_cfg_s& cell_group_, + srsran::unique_byte_buffer_t dedicated_info_nas_) +{ + Info("Starting..."); + + if (dedicated_info_nas_ == nullptr) { + logger.error("Connection Setup Failed, no dedicatedInfoNAS available"); + return proc_outcome_t::error; + } + + dedicated_info_nas = std::move(dedicated_info_nas_); + + // Stop T300 + rrc_handle.t300.stop(); + + // Apply the Cell Group configuration + if (!rrc_handle.update_cell_group_cfg(cell_group_)) { + return proc_outcome_t::error; + } + + // Apply the Radio Bearer configuration + if (!rrc_handle.apply_radio_bearer_cfg(radio_bearer_cfg_)) { + return proc_outcome_t::error; + } + + return proc_outcome_t::yield; +} + +srsran::proc_outcome_t rrc_nr::connection_setup_proc::react(const bool& config_complete) +{ + if (not config_complete) { + logger.error("Connection Setup Failed"); + return proc_outcome_t::error; + } + + rrc_handle.send_con_setup_complete(std::move(dedicated_info_nas)); + return proc_outcome_t::success; +} + +void rrc_nr::connection_setup_proc::then(const srsran::proc_state_t& result) +{ + if (result.is_success()) { + logger.info("Finished %s successfully", name()); + return; + } +} + +/************************************** + * Combined Cell Search/Selection Procedure + *************************************/ + +rrc_nr::cell_selection_proc::cell_selection_proc(rrc_nr& parent_) : rrc_handle(parent_) {} + +// Starts PHY's cell search in the current ARFCN +proc_outcome_t rrc_nr::cell_selection_proc::init() +{ + Info("Starting..."); + state = state_t::phy_cell_search; + + // TODO: add full cell selection + // Start cell search + phy_interface_rrc_nr::cell_search_args_t cs_args = {}; + cs_args.center_freq_hz = rrc_handle.phy_cfg.carrier.dl_center_frequency_hz; + cs_args.ssb_freq_hz = rrc_handle.phy_cfg.carrier.ssb_center_freq_hz; + cs_args.ssb_scs = rrc_handle.phy_cfg.ssb.scs; + cs_args.ssb_pattern = rrc_handle.phy_cfg.ssb.pattern; + cs_args.duplex_mode = rrc_handle.phy_cfg.duplex.mode; + if (not rrc_handle.phy->start_cell_search(cs_args)) { + Error("Failed to initiate Cell Search."); + return proc_outcome_t::error; + } + + return proc_outcome_t::yield; +} + +proc_outcome_t rrc_nr::cell_selection_proc::step() +{ + switch (state) { + case state_t::phy_cell_search: + case state_t::phy_cell_select: + case state_t::sib_acquire: + // Waits for cell select/search to complete + return proc_outcome_t::yield; + } + return proc_outcome_t::yield; +} + +// Handles result of PHY's cell search and triggers PHY cell select when new cell was found +proc_outcome_t +rrc_nr::cell_selection_proc::handle_cell_search_result(const rrc_interface_phy_nr::cell_search_result_t& result) +{ + if (!result.cell_found) { + Info("Cell search did not find any cell."); + return proc_outcome_t::error; + } + + // Convert Cell measurement in Text + std::array csi_info_str = {}; + srsran_csi_meas_info_short(&result.measurements, csi_info_str.data(), (uint32_t)csi_info_str.size()); + + // Unpack MIB and convert to text + srsran_mib_nr_t mib = {}; + std::array mib_info_str = {}; + if (srsran_pbch_msg_nr_mib_unpack(&result.pbch_msg, &mib) == SRSASN_SUCCESS) { + // Convert to text + srsran_pbch_msg_nr_mib_info(&mib, mib_info_str.data(), (uint32_t)mib_info_str.size()); + } else { + // It could be the PBCH does not carry MIB + strcpy(mib_info_str.data(), "No MIB found"); + Error("No MIB found\n"); + return proc_outcome_t::error; + } + + // Check unsupported settings + if (mib.cell_barred) { + Error("Cell barred"); + return proc_outcome_t::error; + } + if (mib.scs_common != srsran_subcarrier_spacing_15kHz) { + Error("Unsupported SCS %s", srsran_subcarrier_spacing_to_str(mib.scs_common)); + return proc_outcome_t::error; + } + + // Logs the PCI, cell measurements and decoded MIB + Info("Cell search found ARFCN=%d PCI=%d %s %s", + result.ssb_arfcn, + result.pci, + csi_info_str.data(), + mib_info_str.data()); + + // Apply MIB settings + srsran::phy_cfg_nr_t& phy_cfg = rrc_handle.phy_cfg; + phy_cfg.pdsch.typeA_pos = mib.dmrs_typeA_pos; + phy_cfg.pdsch.scs_cfg = mib.scs_common; + phy_cfg.carrier.pci = result.pci; + + // Get pointA and SSB absolute frequencies + double pointA_abs_freq_Hz = phy_cfg.carrier.dl_center_frequency_hz - + phy_cfg.carrier.nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs) / 2; + double ssb_abs_freq_Hz = phy_cfg.carrier.ssb_center_freq_hz; + // Calculate integer SSB to pointA frequency offset in Hz + uint32_t ssb_pointA_freq_offset_Hz = + (ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0; + + // Create coreset0 + if (srsran_coreset_zero(phy_cfg.carrier.pci, + ssb_pointA_freq_offset_Hz, + phy_cfg.ssb.scs, + phy_cfg.carrier.scs, + mib.coreset0_idx, + &phy_cfg.pdcch.coreset[0])) { + Error("Error generating coreset0"); + return proc_outcome_t::error; + } + phy_cfg.pdcch.coreset_present[0] = true; + + // Create SearchSpace0 + make_phy_search_space0_cfg(&phy_cfg.pdcch.search_space[0]); + phy_cfg.pdcch.search_space_present[0] = true; + + // Set dummy offset to pass PRACH config check, real value is provided in SIB1 + phy_cfg.prach.freq_offset = 1; + + // Update PHY configuration + rrc_handle.phy_cfg_state = PHY_CFG_STATE_SA_MIB_CFG; + if (not rrc_handle.phy->set_config(phy_cfg)) { + Error("Setting PHY configuration"); + return proc_outcome_t::error; + } + + phy_interface_rrc_nr::cell_select_args_t cs_args = {}; + cs_args.carrier = rrc_handle.phy_cfg.carrier; + cs_args.ssb_cfg = rrc_handle.phy_cfg.get_ssb_cfg(); + + // Transition to cell selection ignoring the cell search result + state = state_t::phy_cell_select; + if (not rrc_handle.phy->start_cell_select(cs_args)) { + Error("Could not set start cell search."); + return proc_outcome_t::error; + } + return proc_outcome_t::yield; +} + +proc_outcome_t rrc_nr::cell_selection_proc::react(const rrc_interface_phy_nr::cell_select_result_t& event) +{ + if (state != state_t::phy_cell_select) { + Warning("Received unexpected cell select result"); + return proc_outcome_t::yield; + } + + if (event.status != rrc_interface_phy_nr::cell_select_result_t::SUCCESSFUL) { + Error("Couldn't select new serving cell"); + phy_search_result.cell_found = false; + rrc_search_result = rrc_nr::rrc_cell_search_result_t::no_cell; + return proc_outcome_t::error; + } + + rrc_search_result = rrc_nr::rrc_cell_search_result_t::same_cell; + + // PHY is now camping on serving cell + Info("Cell selection completed. Starting SIB1 acquisition"); + + // Transition to cell selection ignoring the cell search result + state = state_t::sib_acquire; + rrc_handle.mac->bcch_search(true); + return proc_outcome_t::yield; +} + +proc_outcome_t rrc_nr::cell_selection_proc::react(const bool sib1_found) +{ + if (state != state_t::sib_acquire) { + Warning("Received unexpected cell select result"); + return proc_outcome_t::yield; + } + + Info("SIB1 acquired successfully"); + rrc_handle.mac->bcch_search(false); + + return proc_outcome_t::success; +} + +proc_outcome_t rrc_nr::cell_selection_proc::react(const rrc_interface_phy_nr::cell_search_result_t& event) +{ + if (state != state_t::phy_cell_search) { + Error("Received unexpected cell search result"); + return proc_outcome_t::error; + } + phy_search_result = event; + + if (phy_search_result.cell_found) { + return handle_cell_search_result(phy_search_result); + } + return proc_outcome_t::error; +} + +void rrc_nr::cell_selection_proc::then(const cell_selection_complete_ev& proc_result) const +{ + Info("Completed with %s.", proc_result.is_success() ? "success" : "failure"); + // Inform Connection Request Procedure + rrc_handle.task_sched.defer_task([this, proc_result]() { + if (rrc_handle.setup_req_proc.is_busy()) { + rrc_handle.setup_req_proc.trigger(proc_result); + } + }); +} + +} // namespace srsue diff --git a/srsue/src/stack/rrc_nr/test/CMakeLists.txt b/srsue/src/stack/rrc_nr/test/CMakeLists.txt new file mode 100644 index 000000000..4c1701224 --- /dev/null +++ b/srsue/src/stack/rrc_nr/test/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(ue_rrc_nr_test ue_rrc_nr_test.cc) +target_link_libraries(ue_rrc_nr_test srsue_rrc srsue_rrc_nr srsue_upper srsran_common srsran_pdcp srsran_phy rrc_asn1 rrc_nr_asn1) \ No newline at end of file diff --git a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc new file mode 100644 index 000000000..18b15f702 --- /dev/null +++ b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc @@ -0,0 +1,615 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/test_common.h" +#include "srsran/interfaces/ue_gw_interfaces.h" +#include "srsran/interfaces/ue_interfaces.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/interfaces/ue_rlc_interfaces.h" +#include "srsran/interfaces/ue_usim_interfaces.h" +#include "srsue/hdr/stack/rrc/rrc.h" +#include "srsue/hdr/stack/rrc_nr/rrc_nr.h" + +using namespace srsue; + +class dummy_phy : public phy_interface_rrc_nr +{ + bool set_config(const srsran::phy_cfg_nr_t& cfg) override { return true; } + phy_nr_state_t get_state() override { return PHY_NR_STATE_IDLE; }; + void reset_nr() override{}; + bool start_cell_search(const cell_search_args_t& req) override { return false; }; + bool start_cell_select(const cell_select_args_t& req) override { return false; }; +}; + +class dummy_mac : public mac_interface_rrc_nr +{ + void reset() {} + int setup_lcid(const srsran::logical_channel_config_t& config) { return SRSRAN_SUCCESS; } + int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) { return SRSRAN_SUCCESS; } + int set_config(const srsran::sr_cfg_nr_t& sr_cfg) { return SRSRAN_SUCCESS; } + int set_config(const srsran::dl_harq_cfg_nr_t& dl_hrq_cfg) { return SRSRAN_SUCCESS; } + void set_config(const srsran::rach_cfg_nr_t& rach_cfg) {} + int add_tag_config(const srsran::tag_cfg_nr_t& tag_cfg) { return SRSRAN_SUCCESS; } + int set_config(const srsran::phr_cfg_nr_t& phr_cfg) { return SRSRAN_SUCCESS; } + int remove_tag_config(const uint32_t tag_id) { return SRSRAN_SUCCESS; } + void bcch_search(bool) {} + + void start_ra_procedure() {} + + void set_contention_id(const uint64_t ue_identity){}; + + bool set_crnti(const uint16_t crnti) { return true; }; +}; + +class dummy_rlc : public rlc_interface_rrc +{ + void reset() {} + void reestablish() {} + void reestablish(uint32_t lcid) {} + int add_bearer(uint32_t lcid, const srsran::rlc_config_t& cnfg) { return SRSRAN_SUCCESS; } + int add_bearer_mrb(uint32_t lcid) { return SRSRAN_SUCCESS; } + void del_bearer(uint32_t lcid) {} + void suspend_bearer(uint32_t lcid) {} + void resume_bearer(uint32_t lcid) {} + void change_lcid(uint32_t old_lcid, uint32_t new_lcid) {} + bool has_bearer(uint32_t lcid) { return true; } + bool has_data(const uint32_t lcid) { return true; } + bool is_suspended(const uint32_t lcid) { return true; } + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) + { + last_lcid = lcid; + last_sdu = std::move(sdu); + } + +public: + uint32_t last_lcid = 99; + srsran::unique_byte_buffer_t last_sdu; +}; + +class dummy_pdcp : public pdcp_interface_rrc +{ + void set_enabled(uint32_t lcid, bool enabled){}; + void reestablish(){}; + void reestablish(uint32_t lcid){}; + void reset(){}; + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu, int sn = -1){}; + int add_bearer(uint32_t lcid, const srsran::pdcp_config_t& cnfg) { return SRSRAN_SUCCESS; }; + void del_bearer(uint32_t lcid){}; + void change_lcid(uint32_t old_lcid, uint32_t new_lcid){}; + void config_security(uint32_t lcid, const srsran::as_security_config_t& sec_cfg){}; + void config_security_all(const srsran::as_security_config_t& sec_cfg){}; + void enable_integrity(uint32_t lcid, srsran::srsran_direction_t direction){}; + void enable_encryption(uint32_t lcid, + srsran::srsran_direction_t direction = srsran::srsran_direction_t::DIRECTION_TXRX){}; + void send_status_report(){}; + void send_status_report(uint32_t lcid){}; +}; + +class dummy_sdap : public sdap_interface_pdcp_nr, public sdap_interface_gw_nr, public sdap_interface_rrc +{ + void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final{}; + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) final{}; + bool set_bearer_cfg(uint32_t lcid, const sdap_interface_rrc::bearer_cfg_t& cfg) final { return true; }; +}; + +class dummy_gw : public gw_interface_rrc +{ + void add_mch_port(uint32_t lcid, uint32_t port){}; + bool is_running() { return true; }; +}; + +class dummy_eutra : public rrc_eutra_interface_rrc_nr +{ + void new_cell_meas_nr(const std::vector& meas){}; + void nr_rrc_con_reconfig_complete(bool status){}; + void nr_notify_reconfiguration_failure(){}; + void nr_scg_failure_information(const srsran::scg_failure_cause_t cause){}; +}; + +class dummy_nas : public nas_5g_interface_rrc_nr +{ + int write_pdu(srsran::unique_byte_buffer_t pdu) { return SRSRAN_SUCCESS; }; + int get_k_amf(srsran::as_key_t& k_amf) { return SRSRAN_SUCCESS; }; + uint32_t get_ul_nas_count() { return SRSRAN_SUCCESS; }; +}; + +class dummy_sim : public usim_interface_rrc_nr +{ + void generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg){}; + bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) { return true; } + bool update_nr_context(srsran::as_security_config_t* sec_cfg) { return true; } +}; + +class dummy_stack : public stack_interface_rrc +{ + srsran::tti_point get_current_tti() final { return srsran::tti_point(); }; + void add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) final{}; + void remove_eps_bearer(uint8_t eps_bearer_id) final{}; + void reset_eps_bearers() final{}; +}; + +int rrc_nr_cap_request_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + srsue::rrc_nr rrc_nr(task_sched_handle); + srsran::byte_buffer_t caps; + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; + dummy_gw dummy_gw; + dummy_nas dummy_nas; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args = {}; + + rrc_nr_args.supported_bands_eutra.push_back(7); + rrc_nr_args.supported_bands_nr.push_back(78); + + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_sdap, + &dummy_gw, + &dummy_nas, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + TESTASSERT(rrc_nr.get_eutra_nr_capabilities(&caps) == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr.get_nr_capabilities(&caps) == SRSRAN_SUCCESS); + return SRSRAN_SUCCESS; +} + +int rrc_nsa_reconfig_tdd_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + srsue::rrc_nr rrc_nr(task_sched_handle); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; + dummy_gw dummy_gw; + dummy_nas dummy_nas; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args = {}; + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_sdap, + &dummy_gw, + &dummy_nas, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + uint8_t msg[] = { + 0x20, 0x12, 0xaa, 0x00, 0x02, 0x00, 0x80, 0x23, 0x00, 0x01, 0xfb, 0x54, 0x94, 0x10, 0x43, 0xc6, 0x40, 0x62, 0x04, + 0x40, 0x60, 0xae, 0x20, 0x58, 0xe0, 0x3e, 0xa4, 0x1d, 0x02, 0x60, 0x19, 0x00, 0x82, 0x28, 0x01, 0x64, 0x29, 0xdc, + 0x6f, 0xa3, 0x49, 0xad, 0x40, 0x02, 0x69, 0x35, 0x89, 0x00, 0x00, 0x00, 0x66, 0xc6, 0xd9, 0x22, 0x51, 0x00, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xc1, 0x10, 0x80, 0x01, 0x24, 0x42, 0x00, 0x68, 0x0a, 0x36, 0x00, 0x9a, 0x4d, + 0x62, 0x40, 0x00, 0x00, 0x19, 0xb8, 0xdb, 0x24, 0x48, 0x01, 0x00, 0x04, 0x17, 0x12, 0x8c, 0x78, 0x01, 0x25, 0x18, + 0x83, 0x70, 0xc6, 0xe3, 0xa2, 0x47, 0x01, 0x80, 0x22, 0x07, 0x03, 0x00, 0x10, 0x1e, 0x23, 0x00, 0xd2, 0x4b, 0x81, + 0xb5, 0x00, 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0xe1, 0x10, 0x40, 0x00, 0x92, 0x22, 0x4a, 0x00, 0x00, + 0x90, 0x40, 0x00, 0x04, 0x0d, 0x3a, 0x00, 0x08, 0x02, 0x91, 0x8a, 0x92, 0x42, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4e, 0x04, 0x08, 0x10, 0x20, 0x40, 0x81, 0x02, 0x08, 0x00, 0x00, 0x21, 0x40, 0x00, 0x23, 0x34, 0x1c, + 0x01, 0x0c, 0xc8, 0x50, 0x09, 0x08, 0x60, 0x51, 0x00, 0xab, 0x2a, 0x22, 0x24, 0x40, 0x02, 0x90, 0x5f, 0xfd, 0x29, + 0x49, 0x8c, 0x63, 0x62, 0x45, 0x6a, 0x00, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x04, 0x10, 0x00, 0x71, 0x00, 0x04, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x8a, 0x04, 0x94, 0x0b, 0xc3, 0xe0, 0x06, 0x80, 0x40, 0x00}; + + asn1::cbit_ref bref(msg, sizeof(msg)); + asn1::rrc::dl_dcch_msg_s dl_dcch_msg; + + TESTASSERT(dl_dcch_msg.unpack(bref) == asn1::SRSASN_SUCCESS); + TESTASSERT(dl_dcch_msg.msg.type().value == dl_dcch_msg_type_c::types_opts::c1); + + dl_dcch_msg_type_c::c1_c_* c1 = &dl_dcch_msg.msg.c1(); + rrc_conn_recfg_r8_ies_s rx_recfg = c1->rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8(); + + const asn1::rrc::rrc_conn_recfg_v1510_ies_s rrc_conn_recfg_v1510_ies = + rx_recfg.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + + bool endc_release_and_add_r15 = false; + + asn1::rrc_nr::rrc_recfg_s rrc_nr_reconf = {}; + rrc_nr_reconf.crit_exts.set_rrc_recfg(); + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_cfg_r15.type() == setup_opts::options::setup); + + endc_release_and_add_r15 = rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().endc_release_and_add_r15; + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present); + + asn1::cbit_ref bref0(rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.data(), + rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.size()); + + asn1::rrc_nr::rrc_recfg_s secondary_cell_group_r15; + TESTASSERT(secondary_cell_group_r15.unpack(bref0) == asn1::SRSASN_SUCCESS); + TESTASSERT(secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.size() > 0); + + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group = + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group; + + TESTASSERT(rrc_conn_recfg_v1510_ies.sk_counter_r15_present); + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter = + rrc_conn_recfg_v1510_ies.sk_counter_r15; + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15_present); + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present = true; + asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_conf = {}; + asn1::cbit_ref bref2(rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15.data(), + rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15.size()); + TESTASSERT(radio_bearer_conf.unpack(bref2) == asn1::SRSASN_SUCCESS); + + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg = radio_bearer_conf; + + rrc_nr.rrc_reconfiguration(endc_release_and_add_r15, rrc_nr_reconf); + + task_sched.run_pending_tasks(); + return SRSRAN_SUCCESS; +} + +int rrc_nsa_reconfig_fdd_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + srsue::rrc_nr rrc_nr(task_sched_handle); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; + dummy_gw dummy_gw; + dummy_nas dummy_nas; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args = {}; + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_sdap, + &dummy_gw, + &dummy_nas, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + uint8_t msg[] = { + 0x20, 0x12, 0xaa, 0x00, 0x02, 0x00, 0x80, 0x23, 0x00, 0x01, 0xfb, 0x54, 0x94, 0x10, 0x43, 0xc6, 0x40, 0x62, 0x04, + 0x40, 0x60, 0xae, 0x20, 0x58, 0xe0, 0x3e, 0xa4, 0x1d, 0x02, 0x60, 0x19, 0x00, 0x82, 0x28, 0x01, 0x64, 0x29, 0xdc, + 0x6f, 0xa3, 0x49, 0xad, 0x40, 0x02, 0x69, 0x35, 0x89, 0x00, 0x00, 0x00, 0x66, 0xc6, 0xd9, 0x22, 0x51, 0x00, 0xff, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xc1, 0x10, 0x80, 0x01, 0x24, 0x42, 0x00, 0x68, 0x0a, 0x36, 0x00, 0x9a, 0x4d, + 0x62, 0x40, 0x00, 0x00, 0x19, 0xb8, 0xdb, 0x24, 0x48, 0x01, 0x00, 0x04, 0x17, 0x12, 0x8c, 0x78, 0x01, 0x25, 0x18, + 0x83, 0x70, 0xc6, 0xe3, 0xa2, 0x47, 0x01, 0x80, 0x22, 0x07, 0x03, 0x00, 0x10, 0x1e, 0x23, 0x00, 0xd2, 0x4b, 0x81, + 0xb5, 0x00, 0x02, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x06, 0xe1, 0x10, 0x40, 0x00, 0x92, 0x22, 0x4a, 0x00, 0x00, + 0x90, 0x40, 0x00, 0x04, 0x0d, 0x3a, 0x00, 0x08, 0x02, 0x91, 0x8a, 0x92, 0x42, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x4e, 0x04, 0x08, 0x10, 0x20, 0x40, 0x81, 0x02, 0x08, 0x00, 0x00, 0x21, 0x40, 0x00, 0x23, 0x34, 0x1c, + 0x01, 0x0c, 0xc8, 0x50, 0x09, 0x08, 0x60, 0x51, 0x00, 0xab, 0x2a, 0x22, 0x24, 0x40, 0x02, 0x90, 0x5f, 0xfd, 0x29, + 0x49, 0x8c, 0x63, 0x62, 0x45, 0x6a, 0x00, 0x00, 0x01, 0x80, 0x0c, 0x00, 0x04, 0x10, 0x00, 0x71, 0x00, 0x04, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x8a, 0x04, 0x94, 0x0b, 0xc3, 0xe0, 0x06, 0x80, 0x40, 0x00}; + + asn1::cbit_ref bref(msg, sizeof(msg)); + asn1::rrc::dl_dcch_msg_s dl_dcch_msg; + + TESTASSERT(dl_dcch_msg.unpack(bref) == asn1::SRSASN_SUCCESS); + TESTASSERT(dl_dcch_msg.msg.type().value == dl_dcch_msg_type_c::types_opts::c1); + + dl_dcch_msg_type_c::c1_c_* c1 = &dl_dcch_msg.msg.c1(); + rrc_conn_recfg_r8_ies_s rx_recfg = c1->rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8(); + + const asn1::rrc::rrc_conn_recfg_v1510_ies_s rrc_conn_recfg_v1510_ies = + rx_recfg.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + + bool endc_release_and_add_r15 = false; + + asn1::rrc_nr::rrc_recfg_s rrc_nr_reconf = {}; + rrc_nr_reconf.crit_exts.set_rrc_recfg(); + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_cfg_r15.type() == setup_opts::options::setup); + + endc_release_and_add_r15 = rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().endc_release_and_add_r15; + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15_present); + + asn1::cbit_ref bref0(rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.data(), + rrc_conn_recfg_v1510_ies.nr_cfg_r15.setup().nr_secondary_cell_group_cfg_r15.size()); + + asn1::rrc_nr::rrc_recfg_s secondary_cell_group_r15; + TESTASSERT(secondary_cell_group_r15.unpack(bref0) == asn1::SRSASN_SUCCESS); + TESTASSERT(secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group.size() > 0); + + rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group = + secondary_cell_group_r15.crit_exts.rrc_recfg().secondary_cell_group; + + TESTASSERT(rrc_conn_recfg_v1510_ies.sk_counter_r15_present); + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present = true; + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter = + rrc_conn_recfg_v1510_ies.sk_counter_r15; + + TESTASSERT(rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15_present); + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present = true; + asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_conf = {}; + asn1::cbit_ref bref2(rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15.data(), + rrc_conn_recfg_v1510_ies.nr_radio_bearer_cfg1_r15.size()); + TESTASSERT(radio_bearer_conf.unpack(bref2) == asn1::SRSASN_SUCCESS); + + rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg = radio_bearer_conf; + + rrc_nr.rrc_reconfiguration(endc_release_and_add_r15, rrc_nr_reconf); + + task_sched.run_pending_tasks(); + return SRSRAN_SUCCESS; +} + +int rrc_nr_setup_request_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + srsue::rrc_nr rrc_nr(task_sched_handle); + srsran::byte_buffer_t caps; + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; + dummy_gw dummy_gw; + dummy_nas dummy_nas; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args = {}; + + rrc_nr_args.supported_bands_nr.push_back(78); + + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_sdap, + &dummy_gw, + &dummy_nas, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + rrc_nr.connection_request(srsran::nr_establishment_cause_t::mt_Access, nullptr); + task_sched.run_pending_tasks(); + + TESTASSERT(dummy_rlc.last_lcid == 0); // SRB0 transmission + TESTASSERT(dummy_rlc.last_sdu->N_bytes == 6); // RRC Setup Request is 6 Bytes long + + return SRSRAN_SUCCESS; +} + +int rrc_nr_sib1_decoding_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + srsue::rrc_nr rrc_nr(task_sched_handle); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; + dummy_gw dummy_gw; + dummy_nas dummy_nas; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args = {}; + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_sdap, + &dummy_gw, + &dummy_nas, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + uint8_t msg[] = {0x74, 0x81, 0x01, 0x70, 0x10, 0x40, 0x04, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x33, 0x60, 0x38, + 0x05, 0x01, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x06, 0x6c, 0x6d, 0x92, 0x21, 0xf3, 0x70, 0x40, 0x20, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x41, 0x06, 0x80, 0xa0, 0x90, 0x9c, 0x20, 0x08, 0x55, 0x19, 0x40, + 0x00, 0x00, 0x33, 0xa1, 0xc6, 0xd9, 0x22, 0x40, 0x00, 0x00, 0x20, 0xb8, 0x94, 0x63, 0xc0, 0x09, + 0x28, 0x44, 0x1b, 0x7e, 0xad, 0x8e, 0x1d, 0x00, 0x9e, 0x2d, 0xa3, 0x0a}; + + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + memcpy(pdu->msg, msg, sizeof(msg)); + pdu->N_bytes = sizeof(msg); + + rrc_nr.write_pdu_bcch_dlsch(std::move(pdu)); + task_sched.run_pending_tasks(); + + return SRSRAN_SUCCESS; +} + +int rrc_nr_setup_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + srsue::rrc_nr rrc_nr(task_sched_handle); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; + dummy_gw dummy_gw; + dummy_nas dummy_nas; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args = {}; + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_sdap, + &dummy_gw, + &dummy_nas, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + uint8_t msg[] = {0x20, 0x40, 0x04, 0x05, 0x9a, 0xe0, 0x05, 0x80, 0x08, 0x8b, 0xd7, 0x63, 0x80, 0x83, 0x0f, 0x00, 0x03, + 0xa0, 0x10, 0x45, 0x41, 0xc2, 0x0a, 0x20, 0x92, 0x40, 0x0c, 0xa8, 0x00, 0x17, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x37, 0x08, 0x82, 0x00, 0x04, 0x91, 0x12, 0x50, 0x00, 0x04, 0x82, 0x00, 0x00, 0x20, 0x69, + 0x84, 0x0c, 0x55, 0x92, 0x10, 0x70, 0x00, 0x41, 0x03, 0x08, 0x14, 0x30, 0x72, 0x71, 0x02, 0x45, 0x0b, + 0x18, 0x34, 0x70, 0xf2, 0x38, 0x01, 0x98, 0x00, 0x85, 0x00, 0xc0, 0x8c, 0xc0, 0x05, 0x28, 0x06, 0x08, + 0x66, 0x00, 0x31, 0x40, 0x30, 0x63, 0x30, 0x01, 0x0a, 0x03, 0x84, 0x19, 0x80, 0x0a, 0x50, 0x1c, 0x28, + 0xcc, 0x00, 0x62, 0x80, 0xe1, 0x86, 0x60, 0x02, 0x14, 0x0b, 0x0e, 0x33, 0x00, 0x14, 0xa0, 0x58, 0x80, + 0x08, 0xc9, 0x04, 0x31, 0x20, 0x11, 0x92, 0x09, 0x62, 0x80, 0x23, 0x24, 0x14, 0xc5, 0x80, 0x46, 0x48, + 0x2d, 0x8c, 0x00, 0x8c, 0x90, 0x63, 0x1a, 0x01, 0x19, 0x20, 0xd6, 0x38, 0x02, 0x32, 0x41, 0xcc, 0x78, + 0xc8, 0x02, 0x82, 0x19, 0x01, 0x98, 0x00, 0xc5, 0x02, 0xc8, 0x8c, 0x80, 0x28, 0x25, 0x02, 0x42, 0x18, + 0x14, 0x40, 0x20, 0x91, 0x00, 0x0a, 0x41, 0x7f, 0xf4, 0xa5, 0x26, 0x31, 0x8d, 0x80}; + + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + memcpy(pdu->msg, msg, sizeof(msg)); + pdu->N_bytes = sizeof(msg); + + rrc_nr.write_pdu(0, std::move(pdu)); + task_sched.run_pending_tasks(); + + return SRSRAN_SUCCESS; +} + +int rrc_nr_reconfig_test() +{ + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + srsue::rrc_nr rrc_nr(task_sched_handle); + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_sdap dummy_sdap; + dummy_gw dummy_gw; + dummy_nas dummy_nas; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args = {}; + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_sdap, + &dummy_gw, + &dummy_nas, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + + uint8_t msg[] = {0x04, 0x8a, 0x80, 0x40, 0x9a, 0x01, 0xe0, 0x02, 0x05, 0xe1, 0xf0, 0x05, 0x00, 0x9a, 0x00, 0x15, 0x84, + 0x88, 0x8b, 0xd7, 0x63, 0x80, 0x83, 0x2f, 0x00, 0x05, 0x8e, 0x03, 0xea, 0x41, 0xd0, 0x23, 0x00, 0x20, + 0x25, 0x5f, 0x80, 0xa2, 0xef, 0x22, 0xc8, 0x40, 0xdf, 0x80, 0x1a, 0x00, 0x40, 0x21, 0x8b, 0x80, 0x40, + 0x70, 0x84, 0xc0, 0x02, 0x40, 0x40, 0x01, 0x8c, 0x4c, 0x40, 0x40, 0x7f, 0xc0, 0x41, 0x82, 0xc0, 0x00, + 0x42, 0xc0, 0x00, 0x4a, 0x43, 0x40, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x82, 0x8b, 0x40, + 0x01, 0x88, 0x80, 0x40, 0x5e, 0x40, 0x01, 0x80, 0x48, 0x10, 0x40, 0x40, 0x42, 0x5e, 0xc0, 0x12, 0x20, + 0x20, 0x08, 0x44, 0x00, 0x80, 0x00, 0x04, 0x20, 0x41, 0x82, 0x02, 0x02, 0x02, 0x20, 0xc1, 0x82, 0x02, + 0x01, 0x01, 0x00, 0x00, 0xc4, 0x08, 0x00, 0x52, 0x18, 0x12, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x22, 0x22, 0x00, 0x00, 0xc4, 0x08, 0x00, 0x52, 0x18, 0x12, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x22, 0x11, 0x00, 0x03, 0x41, 0x02, 0x02, 0x02, 0x02, 0x00, 0x03, 0x41, 0x02, + 0x02, 0x01, 0x01, 0x09, 0x41, 0xc1, 0x9c, 0xdc, 0x9c, 0xd8, 0x5c, 0x1b, 0x84, 0x80, 0x40}; + + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + memcpy(pdu->msg, msg, sizeof(msg)); + pdu->N_bytes = sizeof(msg); + + rrc_nr.write_pdu(1, std::move(pdu)); + task_sched.run_pending_tasks(); + + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + srslog::init(); + + TESTASSERT(rrc_nr_cap_request_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nsa_reconfig_tdd_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nsa_reconfig_fdd_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr_setup_request_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr_sib1_decoding_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr_setup_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr_reconfig_test() == SRSRAN_SUCCESS); + + return SRSRAN_SUCCESS; +} diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index ab8e77ea7..db2415b82 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -34,7 +34,6 @@ using namespace srsran; namespace srsue { ue_stack_lte::ue_stack_lte() : - running(false), args(), stack_logger(srslog::fetch_basic_logger("STCK", false)), mac_logger(srslog::fetch_basic_logger("MAC")), @@ -43,19 +42,25 @@ ue_stack_lte::ue_stack_lte() : rrc_logger(srslog::fetch_basic_logger("RRC", false)), usim_logger(srslog::fetch_basic_logger("USIM", false)), nas_logger(srslog::fetch_basic_logger("NAS", false)), + nas5g_logger(srslog::fetch_basic_logger("NAS5G", false)), mac_nr_logger(srslog::fetch_basic_logger("MAC-NR")), rrc_nr_logger(srslog::fetch_basic_logger("RRC-NR", false)), + rlc_nr_logger(srslog::fetch_basic_logger("RLC-NR", false)), + pdcp_nr_logger(srslog::fetch_basic_logger("PDCP-NR", false)), mac_pcap(), mac_nr_pcap(), - usim(nullptr), - phy(nullptr), rlc("RLC"), mac("MAC", &task_sched), rrc(this, &task_sched), + rlc_nr("RLC-NR"), mac_nr(&task_sched), rrc_nr(&task_sched), pdcp(&task_sched, "PDCP"), - nas(&task_sched), + pdcp_nr(&task_sched, "PDCP-NR"), + sdap("SDAP-NR"), + sdap_pdcp(&pdcp_nr, &sdap), + nas(srslog::fetch_basic_logger("NAS", false), &task_sched), + nas_5g(srslog::fetch_basic_logger("NAS5G", false), &task_sched), thread("STACK"), task_sched(512, 64), tti_tprof("tti_tprof", "STCK", TTI_STAT_PERIOD) @@ -124,11 +129,17 @@ int ue_stack_lte::init(const stack_args_t& args_) nas_logger.set_level(srslog::str_to_basic_level(args.log.nas_level)); nas_logger.set_hex_dump_max_size(args.log.nas_hex_limit); + nas5g_logger.set_level(srslog::str_to_basic_level(args.log.nas_level)); + nas5g_logger.set_hex_dump_max_size(args.log.nas_hex_limit); mac_nr_logger.set_level(srslog::str_to_basic_level(args.log.mac_level)); mac_nr_logger.set_hex_dump_max_size(args.log.mac_hex_limit); rrc_nr_logger.set_level(srslog::str_to_basic_level(args.log.rrc_level)); rrc_nr_logger.set_hex_dump_max_size(args.log.rrc_hex_limit); - + pdcp_nr_logger.set_level(srslog::str_to_basic_level(args.log.pdcp_level)); + pdcp_nr_logger.set_hex_dump_max_size(args.log.pdcp_hex_limit); + rlc_nr_logger.set_level(srslog::str_to_basic_level(args.log.rlc_level)); + rlc_nr_logger.set_hex_dump_max_size(args.log.rlc_hex_limit); + // Set up pcap // parse pcap trace list std::vector pcap_list; @@ -193,6 +204,7 @@ int ue_stack_lte::init(const stack_args_t& args_) if (args.pkt_trace.nas_pcap.enable) { if (nas_pcap.open(args.pkt_trace.nas_pcap.filename.c_str()) == SRSRAN_SUCCESS) { nas.start_pcap(&nas_pcap); + nas_5g.start_pcap(&nas_pcap); stack_logger.info("Open nas pcap file %s", args.pkt_trace.nas_pcap.filename.c_str()); } else { stack_logger.error("Can not open pcap file %s", args.pkt_trace.nas_pcap.filename.c_str()); @@ -210,15 +222,38 @@ int ue_stack_lte::init(const stack_args_t& args_) sync_task_queue = task_sched.make_task_queue(args.sync_queue_size); mac.init(phy, &rlc, &rrc); - rlc.init(&pdcp, &rrc, &rrc_nr, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */); - pdcp.init(&rlc, &rrc, &rrc_nr, gw); + rlc.init(&pdcp, &rrc, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */); nas.init(usim.get(), &rrc, gw, args.nas); + if (!args.sa_mode) { + pdcp.init(&rlc, &rrc, gw); + } else { + pdcp.init(&rlc, &rrc, &sdap_pdcp); + sdap.init(&sdap_pdcp, gw); + } + mac_nr_args_t mac_nr_args = {}; - mac_nr.init(mac_nr_args, phy_nr, &rlc, &rrc_nr); - rrc_nr.init(phy_nr, &mac_nr, &rlc, &pdcp, gw, &rrc, usim.get(), task_sched.get_timer_handler(), nullptr, args.rrc_nr); + mac_nr.init(mac_nr_args, phy_nr, &rlc_nr, &rrc_nr); + rlc_nr.init(&pdcp_nr, &rrc_nr, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */); + pdcp_nr.init(&rlc_nr, &rrc_nr, gw); + rrc_nr.init(phy_nr, + &mac_nr, + &rlc_nr, + &pdcp_nr, + &sdap, + gw, + &nas_5g, + args.sa_mode ? nullptr : &rrc, + usim.get(), + task_sched.get_timer_handler(), + this, + args.rrc_nr); rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc); + if (args.sa_mode) { + nas_5g.init(usim.get(), &rrc_nr, gw, args.nas_5g); + } + running = true; start(STACK_MAIN_THREAD_PRIO); @@ -239,6 +274,7 @@ void ue_stack_lte::stop_impl() usim->stop(); nas.stop(); + nas_5g.stop(); rrc.stop(); rlc.stop(); @@ -262,48 +298,67 @@ void ue_stack_lte::stop_impl() bool ue_stack_lte::switch_on() { if (running) { - ue_task_queue.try_push([this]() { nas.switch_on(); }); + stack_logger.info("Triggering NAS switch on"); + if (!ue_task_queue.try_push([this]() { + if (args.sa_mode) { + nas_5g.switch_on(); + } else { + nas.switch_on(); + } + })) { + stack_logger.error("Triggering NAS switch on: ue_task_queue is full\n"); + } + } else { + stack_logger.error("Triggering NAS switch on: stack is not running\n"); } return true; } bool ue_stack_lte::switch_off() { - // generate detach request with switch-off flag - nas.switch_off(); - - // wait for max. 5s for it to be sent (according to TS 24.301 Sec 25.5.2.2) - int cnt = 0, timeout_ms = 5000; - while (not rrc.srbs_flushed() && ++cnt <= timeout_ms) { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + if (running) { + ue_task_queue.try_push([this]() { + // generate detach request with switch-off flag + nas.switch_off(); + }); } - bool detach_sent = true; - if (not rrc.srbs_flushed()) { - srslog::fetch_basic_logger("NAS").warning("Detach couldn't be sent after %dms.", timeout_ms); - detach_sent = false; - } - - return detach_sent; + return true; } bool ue_stack_lte::enable_data() { - // perform attach request - srsran::console("Turning off airplane mode.\n"); - return nas.enable_data(); + if (running) { + ue_task_queue.try_push([this]() { + // perform attach request + srsran::console("Turning off airplane mode.\n"); + nas.enable_data(); + }); + } + return true; } bool ue_stack_lte::disable_data() { - // generate detach request - srsran::console("Turning on airplane mode.\n"); - return nas.disable_data(); + if (running) { + ue_task_queue.try_push([this]() { + // generate detach request + srsran::console("Turning on airplane mode.\n"); + nas.disable_data(); + }); + } + return true; } bool ue_stack_lte::start_service_request() { if (running) { - ue_task_queue.try_push([this]() { nas.start_service_request(srsran::establishment_cause_t::mo_data); }); + ue_task_queue.try_push([this]() { + if (args.sa_mode) { + nas_5g.start_service_request(); + } else { + nas.start_service_request(srsran::establishment_cause_t::mo_data); + } + }); } return true; } @@ -319,6 +374,7 @@ bool ue_stack_lte::get_metrics(stack_metrics_t* metrics) rlc.get_metrics(metrics.rlc, metrics.mac[0].nof_tti); nas.get_metrics(&metrics.nas); rrc.get_metrics(metrics.rrc); + rrc_nr.get_metrics(metrics.rrc_nr); pending_stack_metrics.push(metrics); }); // wait for result @@ -337,26 +393,68 @@ void ue_stack_lte::run_thread() * Stack Interfaces **********************************************************************************************************************/ +/******************** + * RRC Interface + *******************/ + +void ue_stack_lte::add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat_t rat, uint32_t lcid) +{ + bearers.add_eps_bearer(eps_bearer_id, rat, lcid); +} + +void ue_stack_lte::remove_eps_bearer(uint8_t eps_bearer_id) +{ + bearers.remove_eps_bearer(eps_bearer_id); +} + /******************** * GW Interface *******************/ /** - * Push GW SDU to stack - * @param lcid + * GW calls write_sdu() to push SDU for EPS bearer to stack. + * If the EPS bearer ID is valid it will deliver the PDU to the + * registered PDCP entity. + * + * @param eps_bearer_id * @param sdu - * @param blocking */ -void ue_stack_lte::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) +void ue_stack_lte::write_sdu(uint32_t eps_bearer_id, srsran::unique_byte_buffer_t sdu) { - auto task = [this, lcid](srsran::unique_byte_buffer_t& sdu) { pdcp.write_sdu(lcid, std::move(sdu)); }; - bool ret = gw_queue_id.try_push(std::bind(task, std::move(sdu))).first; + auto bearer = bearers.get_radio_bearer(eps_bearer_id); + + auto task = [this, eps_bearer_id, bearer](srsran::unique_byte_buffer_t& sdu) { + // route SDU to PDCP entity + if (bearer.rat == srsran_rat_t::lte) { + pdcp.write_sdu(bearer.lcid, std::move(sdu)); + } else if (bearer.rat == srsran_rat_t::nr) { + if (args.sa_mode) { + sdap.write_sdu(bearer.lcid, std::move(sdu)); + } else { + pdcp_nr.write_sdu(bearer.lcid, std::move(sdu)); + } + } else { + stack_logger.warning("Can't deliver SDU for EPS bearer %d. Dropping it.", eps_bearer_id); + } + }; + + bool ret = gw_queue_id.try_push(std::bind(task, std::move(sdu))).has_value(); if (not ret) { - pdcp_logger.info("GW SDU with lcid=%d was discarded.", lcid); + pdcp_logger.info("GW SDU with lcid=%d was discarded.", bearer.lcid); ul_dropped_sdus++; } } +bool ue_stack_lte::has_active_radio_bearer(uint32_t eps_bearer_id) +{ + return bearers.has_active_radio_bearer(eps_bearer_id); +} + +void ue_stack_lte::reset_eps_bearers() +{ + bearers.reset(); +} + /** * Check whether nas is attached * @return bool wether NAS is in EMM_REGISTERED @@ -434,6 +532,7 @@ void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump) rrc.run_tti(); rrc_nr.run_tti(tti); nas.run_tti(); + nas_5g.run_tti(); if (args.have_tti_time_stats) { std::chrono::nanoseconds dur = tti_tprof.stop(); @@ -449,5 +548,18 @@ void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump) stack_logger.warning("Detected slow task processing (sync_queue_len=%zd).", sync_task_queue.size()); } } +void ue_stack_lte::set_phy_config_complete(bool status) +{ + cfg_task_queue.push([this, status]() { rrc_nr.set_phy_config_complete(status); }); +} + +void ue_stack_lte::cell_search_found_cell(const cell_search_result_t& result) +{ + cfg_task_queue.push([this, result]() { rrc_nr.cell_search_found_cell(result); }); +} +void ue_stack_lte::cell_select_completed(const rrc_interface_phy_nr::cell_select_result_t& result) +{ + cfg_task_queue.push([this, result]() { rrc_nr.cell_select_completed(result); }); +} } // namespace srsue diff --git a/srsue/src/stack/ue_stack_nr.cc b/srsue/src/stack/ue_stack_nr.cc index f7c452019..efd7f8789 100644 --- a/srsue/src/stack/ue_stack_nr.cc +++ b/srsue/src/stack/ue_stack_nr.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,6 +21,7 @@ #include "srsue/hdr/stack/ue_stack_nr.h" #include "srsran/srsran.h" +#include "srsue/hdr/stack/rrc_nr/rrc_nr.h" using namespace srsran; @@ -84,11 +85,18 @@ int ue_stack_nr::init(const stack_args_t& args_) rrc_nr_args_t rrc_args = {}; rrc_args.log_level = args.log.rrc_level; rrc_args.log_hex_limit = args.log.rrc_hex_limit; - rrc_args.coreless.drb_lcid = 4; - rrc_args.coreless.ip_addr = "192.168.1.3"; - rrc->init( - phy, mac.get(), rlc.get(), pdcp.get(), gw, nullptr, nullptr, task_sched.get_timer_handler(), this, rrc_args); - rrc->init_core_less(); + rrc->init(phy, + mac.get(), + rlc.get(), + pdcp.get(), + sdap.get(), + gw, + nullptr, + nullptr, + nullptr, + task_sched.get_timer_handler(), + this, + rrc_args); running = true; start(STACK_MAIN_THREAD_PRIO); @@ -119,8 +127,13 @@ void ue_stack_nr::stop_impl() bool ue_stack_nr::switch_on() { // statically setup TUN (will be done through RRC later) - char* err_str = nullptr; - if (gw->setup_if_addr(5, 4, LIBLTE_MME_PDN_TYPE_IPV4, htonl(inet_addr("192.168.1.3")), nullptr, err_str)) { + char* err_str = nullptr; + struct in_addr in_addr; + if (inet_pton(AF_INET, "192.168.1.3", &in_addr.s_addr) != 1) { + perror("inet_pton"); + return false; + } + if (gw->setup_if_addr(5, LIBLTE_MME_PDN_TYPE_IPV4, htonl(in_addr.s_addr), nullptr, err_str)) { printf("Error configuring TUN interface\n"); } return true; @@ -163,9 +176,9 @@ void ue_stack_nr::run_thread() void ue_stack_nr::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { if (pdcp != nullptr) { - std::pair ret = gw_task_queue.try_push(std::bind( + auto ret = gw_task_queue.try_push(std::bind( [this, lcid](srsran::unique_byte_buffer_t& sdu) { pdcp->write_sdu(lcid, std::move(sdu)); }, std::move(sdu))); - if (not ret.first) { + if (ret.is_error()) { pdcp_logger.warning("GW SDU with lcid=%d was discarded.", lcid); } } @@ -188,7 +201,7 @@ void ue_stack_nr::out_of_sync() // pending_tasks.push(sync_task_queue, task_t{[this](task_t*) { rrc.out_of_sync(); }}); } -void ue_stack_nr::run_tti(uint32_t tti) +void ue_stack_nr::run_tti(uint32_t tti, uint32_t tti_jump) { sync_task_queue.push([this, tti]() { run_tti_impl(tti); }); } @@ -200,4 +213,19 @@ void ue_stack_nr::run_tti_impl(uint32_t tti) task_sched.tic(); } +void ue_stack_nr::set_phy_config_complete(bool status) +{ + sync_task_queue.push([this, status]() { rrc->set_phy_config_complete(status); }); +} + +void ue_stack_nr::cell_search_found_cell(const cell_search_result_t& result) +{ + sync_task_queue.push([this, result]() { rrc->cell_search_found_cell(result); }); +} + +void ue_stack_nr::cell_select_completed(const rrc_interface_phy_nr::cell_select_result_t& result) +{ + sync_task_queue.push([this, result]() { rrc->cell_select_completed(result); }); +} + } // namespace srsue diff --git a/srsue/src/stack/upper/CMakeLists.txt b/srsue/src/stack/upper/CMakeLists.txt index 4ca2307d8..368163c7f 100644 --- a/srsue/src/stack/upper/CMakeLists.txt +++ b/srsue/src/stack/upper/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,14 +18,19 @@ # and at http://www.gnu.org/licenses/. # -set(SOURCES nas.cc nas_emm_state.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc) +add_subdirectory(test) + +set(SOURCES nas.cc nas_emm_state.cc nas_idle_procedures.cc gw.cc usim_base.cc usim.cc tft_packet_filter.cc nas_base.cc nas_5g_procedures.cc nas_5g.cc nas_5gmm_state.cc sdap.cc) if(HAVE_PCSC) list(APPEND SOURCES "pcsc_usim.cc") endif(HAVE_PCSC) add_library(srsue_upper STATIC ${SOURCES}) +target_link_libraries(srsue_upper ${ATOMIC_LIBS} srsran_asn1) + +target_link_libraries(srsue_upper nas_5g_msg) if(HAVE_PCSC) target_link_libraries(srsue_upper ${PCSCLITE_LIBRARY}) -endif(HAVE_PCSC) +endif(HAVE_PCSC) \ No newline at end of file diff --git a/srsue/src/stack/upper/gw.cc b/srsue/src/stack/upper/gw.cc index 3a44837a3..b83ec4426 100644 --- a/srsue/src/stack/upper/gw.cc +++ b/srsue/src/stack/upper/gw.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -38,7 +38,7 @@ namespace srsue { -gw::gw() : thread("GW"), logger(srslog::fetch_basic_logger("GW", false)), tft_matcher(logger) {} +gw::gw(srslog::basic_logger& logger_) : thread("GW"), logger(logger_), tft_matcher(logger) {} int gw::init(const gw_args_t& args_, stack_interface_gw* stack_) { @@ -62,18 +62,31 @@ int gw::init(const gw_args_t& args_, stack_interface_gw* stack_) return SRSRAN_ERROR; } - mbsfn_sock_addr.sin_family = AF_INET; - mbsfn_sock_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + mbsfn_sock_addr.sin_family = AF_INET; + if (inet_pton(mbsfn_sock_addr.sin_family, "127.0.0.1", &mbsfn_sock_addr.sin_addr.s_addr) != 1) { + perror("inet_pton"); + return false; + } return SRSRAN_SUCCESS; } +gw::~gw() +{ + if (tun_fd > 0) { + close(tun_fd); + } +} + void gw::stop() { if (run_enable) { run_enable = false; if (if_up) { - close(tun_fd); + if_up = false; + if (running) { + thread_cancel(); + } // Wait thread to exit gracefully otherwise might leave a mutex locked int cnt = 0; @@ -81,9 +94,6 @@ void gw::stop() usleep(10000); cnt++; } - if (running) { - thread_cancel(); - } wait_thread_finish(); current_ip_addr = 0; @@ -97,6 +107,8 @@ void gw::stop() void gw::get_metrics(gw_metrics_t& m, const uint32_t nof_tti) { + std::lock_guard lock(gw_mutex); + std::chrono::duration secs = std::chrono::high_resolution_clock::now() - metrics_tp; double dl_tput_mbps_real_time = (dl_tput_bytes * 8 / (double)1e6) / secs.count(); @@ -106,11 +118,11 @@ void gw::get_metrics(gw_metrics_t& m, const uint32_t nof_tti) m.dl_tput_mbps = (nof_tti > 0) ? ((dl_tput_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0; m.ul_tput_mbps = (nof_tti > 0) ? ((ul_tput_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0; - logger.info("gw_rx_rate_mbps=%4.2f (real=%4.2f), gw_tx_rate_mbps=%4.2f (real=%4.2f)", - m.dl_tput_mbps, - dl_tput_mbps_real_time, - m.ul_tput_mbps, - ul_tput_mbps_real_time); + logger.debug("gw_rx_rate_mbps=%4.2f (real=%4.2f), gw_tx_rate_mbps=%4.2f (real=%4.2f)", + m.dl_tput_mbps, + dl_tput_mbps_real_time, + m.ul_tput_mbps, + ul_tput_mbps_real_time); // reset counters and store time metrics_tp = std::chrono::high_resolution_clock::now(); @@ -124,9 +136,14 @@ void gw::get_metrics(gw_metrics_t& m, const uint32_t nof_tti) void gw::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) { logger.info(pdu->msg, pdu->N_bytes, "RX PDU. Stack latency: %ld us", pdu->get_latency_us().count()); - dl_tput_bytes += pdu->N_bytes; + { + std::unique_lock lock(gw_mutex); + dl_tput_bytes += pdu->N_bytes; + } if (!if_up) { - logger.warning("TUN/TAP not up - dropping gw RX message"); + if (run_enable) { + logger.warning("TUN/TAP not up - dropping gw RX message"); + } } else if (pdu->N_bytes < 20) { // Packet not large enough to hold IPv4 Header logger.warning("Packet to small to hold IPv4 header. Dropping packet with %d B", pdu->N_bytes); @@ -152,7 +169,10 @@ void gw::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) "RX MCH PDU (%d B). Stack latency: %ld us", pdu->N_bytes, pdu->get_latency_us().count()); - dl_tput_bytes += pdu->N_bytes; + { + std::unique_lock lock(gw_mutex); + dl_tput_bytes += pdu->N_bytes; + } // Hack to drop initial 2 bytes pdu->msg += 2; @@ -161,7 +181,9 @@ void gw::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) memcpy(&dst_addr.s_addr, &pdu->msg[16], 4); if (!if_up) { - logger.warning("TUN/TAP not up - dropping gw RX message"); + if (run_enable) { + logger.warning("TUN/TAP not up - dropping gw RX message"); + } } else { int n = write(tun_fd, pdu->msg, pdu->N_bytes); if (n > 0 && (pdu->N_bytes != (uint32_t)n)) { @@ -174,14 +196,16 @@ void gw::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) /******************************************************************************* NAS interface *******************************************************************************/ -int gw::setup_if_addr(uint32_t eps_bearer_id, - uint32_t lcid, - uint8_t pdn_type, - uint32_t ip_addr, - uint8_t* ipv6_if_addr, - char* err_str) +int gw::setup_if_addr(uint32_t eps_bearer_id, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_addr, char* err_str) { int err; + + // Make sure the worker thread is terminated before spawning a new one. + if (running) { + run_enable = false; + thread_cancel(); + wait_thread_finish(); + } if (pdn_type == LIBLTE_MME_PDN_TYPE_IPV4 || pdn_type == LIBLTE_MME_PDN_TYPE_IPV4V6) { err = setup_if_addr4(ip_addr, err_str); if (err != SRSRAN_SUCCESS) { @@ -195,46 +219,39 @@ int gw::setup_if_addr(uint32_t eps_bearer_id, } } - eps_lcid[eps_bearer_id] = lcid; - default_lcid = lcid; - tft_matcher.set_default_lcid(lcid); + default_eps_bearer_id = static_cast(eps_bearer_id); // Setup a thread to receive packets from the TUN device + run_enable = true; start(GW_THREAD_PRIO); + return SRSRAN_SUCCESS; } +int gw::deactivate_eps_bearer(const uint32_t eps_bearer_id) +{ + std::lock_guard lock(gw_mutex); + + // only deactivation of default bearer + if (eps_bearer_id == static_cast(default_eps_bearer_id)) { + logger.debug("Deactivating EPS bearer %d", eps_bearer_id); + default_eps_bearer_id = NOT_ASSIGNED; + return SRSRAN_SUCCESS; + } else { + // delete TFT template (if any) for this bearer + tft_matcher.delete_tft_for_eps_bearer(eps_bearer_id); + return SRSRAN_SUCCESS; + } +} bool gw::is_running() { return running; } -int gw::update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid) +int gw::apply_traffic_flow_template(const uint8_t& erab_id, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) { - auto it = eps_lcid.find(eps_bearer_id); - if (it != eps_lcid.end()) { - uint32_t old_lcid = eps_lcid[eps_bearer_id]; - logger.debug("Found EPS bearer %d. Update old lcid %d to new lcid %d", eps_bearer_id, old_lcid, new_lcid); - eps_lcid[eps_bearer_id] = new_lcid; - if (old_lcid == default_lcid) { - logger.debug("Defaulting new lcid %d", new_lcid); - default_lcid = new_lcid; - tft_matcher.set_default_lcid(new_lcid); - } - // TODO: update need filters if not the default lcid - } else { - logger.error("Did not found EPS bearer %d for updating LCID.", eps_bearer_id); - return SRSRAN_ERROR; - } - return SRSRAN_SUCCESS; -} - -int gw::apply_traffic_flow_template(const uint8_t& erab_id, - const uint8_t& lcid, - const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) -{ - return tft_matcher.apply_traffic_flow_template(erab_id, lcid, tft); + return tft_matcher.apply_traffic_flow_template(erab_id, tft); } void gw::set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms) @@ -289,63 +306,75 @@ void gw::run_thread() break; } - // Check if IP version makes sense and get packtet length - struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; - struct ipv6hdr* ip6_pkt = (struct ipv6hdr*)pdu->msg; - uint16_t pkt_len = 0; - pdu->N_bytes = idx + N_bytes; - if (ip_pkt->version == 4) { - pkt_len = ntohs(ip_pkt->tot_len); - } else if (ip_pkt->version == 6) { - pkt_len = ntohs(ip6_pkt->payload_len) + 40; - } else { - logger.error(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet."); - continue; - } - logger.debug("IPv%d packet total length: %d Bytes", int(ip_pkt->version), pkt_len); - - // Check if entire packet was received - if (pkt_len == pdu->N_bytes) { - logger.info(pdu->msg, pdu->N_bytes, "TX PDU"); - - // Make sure UE is attached - while (run_enable && !stack->is_registered() && register_wait < REGISTER_WAIT_TOUT) { - if (!register_wait) { - logger.info("UE is not attached, waiting for NAS attach (%d/%d)", register_wait, REGISTER_WAIT_TOUT); - } - usleep(100000); - register_wait++; - } - register_wait = 0; - - // If we are still not attached by this stage, drop packet - if (run_enable && !stack->is_registered()) { + { + std::unique_lock lock(gw_mutex); + // Check if IP version makes sense and get packtet length + struct iphdr* ip_pkt = (struct iphdr*)pdu->msg; + struct ipv6hdr* ip6_pkt = (struct ipv6hdr*)pdu->msg; + uint16_t pkt_len = 0; + pdu->N_bytes = idx + N_bytes; + if (ip_pkt->version == 4) { + pkt_len = ntohs(ip_pkt->tot_len); + } else if (ip_pkt->version == 6) { + pkt_len = ntohs(ip6_pkt->payload_len) + 40; + } else { + logger.error(pdu->msg, pdu->N_bytes, "Unsupported IP version. Dropping packet."); continue; } + logger.debug("IPv%d packet total length: %d Bytes", int(ip_pkt->version), pkt_len); - // Wait for service request if necessary - while (run_enable && !stack->is_lcid_enabled(default_lcid) && service_wait < SERVICE_WAIT_TOUT) { - if (!service_wait) { - logger.info( - "UE does not have service, waiting for NAS service request (%d/%d)", service_wait, SERVICE_WAIT_TOUT); - stack->start_service_request(); + // Check if entire packet was received + if (pkt_len == pdu->N_bytes) { + logger.info(pdu->msg, pdu->N_bytes, "TX PDU"); + + // Make sure UE is attached and has default EPS bearer activated + while (run_enable && default_eps_bearer_id == NOT_ASSIGNED && register_wait < REGISTER_WAIT_TOUT) { + if (!register_wait) { + logger.info("UE is not attached, waiting for NAS attach (%d/%d)", register_wait, REGISTER_WAIT_TOUT); + } + lock.unlock(); + std::this_thread::sleep_for(std::chrono::microseconds(100)); + lock.lock(); + register_wait++; } - usleep(100000); - service_wait++; - } - service_wait = 0; + register_wait = 0; - // Quit before writing packet if necessary - if (!run_enable) { - break; - } + // If we are still not attached by this stage, drop packet + if (run_enable && default_eps_bearer_id == NOT_ASSIGNED) { + continue; + } - uint8_t lcid = tft_matcher.check_tft_filter_match(pdu); - // Send PDU directly to PDCP - if (stack->is_lcid_enabled(lcid)) { + if (!run_enable) { + break; + } + + // Beyond this point we should have a activated default EPS bearer + srsran_assert(default_eps_bearer_id != NOT_ASSIGNED, "Default EPS bearer not activated"); + + uint8_t eps_bearer_id = default_eps_bearer_id; + tft_matcher.check_tft_filter_match(pdu, eps_bearer_id); + + // Wait for service request if necessary + while (run_enable && !stack->has_active_radio_bearer(eps_bearer_id) && service_wait < SERVICE_WAIT_TOUT) { + if (!service_wait) { + logger.info( + "UE does not have service, waiting for NAS service request (%d/%d)", service_wait, SERVICE_WAIT_TOUT); + stack->start_service_request(); + } + usleep(100000); + service_wait++; + } + service_wait = 0; + + // Quit before writing packet if necessary + if (!run_enable) { + break; + } + + // Send PDU directly to PDCP pdu->set_timestamp(); ul_tput_bytes += pdu->N_bytes; - stack->write_sdu(lcid, std::move(pdu)); + stack->write_sdu(eps_bearer_id, std::move(pdu)); do { pdu = srsran::make_byte_buffer(); if (!pdu) { @@ -354,18 +383,16 @@ void gw::run_thread() } } while (!pdu); idx = 0; + } else { + idx += N_bytes; + logger.debug("Entire packet not read from socket. Total Length %d, N_Bytes %d.", ip_pkt->tot_len, pdu->N_bytes); } - } else { - idx += N_bytes; - logger.debug("Entire packet not read from socket. Total Length %d, N_Bytes %d.", ip_pkt->tot_len, pdu->N_bytes); - } + } // end of holdering gw_mutex } running = false; logger.info("GW IP receiver thread exiting."); } - - /**************************/ /* TUN Interface Helpers */ /**************************/ @@ -453,6 +480,9 @@ int gw::setup_if_addr4(uint32_t ip_addr, char* err_str) } } + if (sock > 0) { + close(sock); + } // Setup the IP address sock = socket(AF_INET, SOCK_DGRAM, 0); ifr.ifr_addr.sa_family = AF_INET; @@ -463,8 +493,15 @@ int gw::setup_if_addr4(uint32_t ip_addr, char* err_str) close(tun_fd); return SRSRAN_ERROR_CANT_START; } - ifr.ifr_netmask.sa_family = AF_INET; - ((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr(args.tun_dev_netmask.c_str()); + ifr.ifr_netmask.sa_family = AF_INET; + if (inet_pton(ifr.ifr_netmask.sa_family, + args.tun_dev_netmask.c_str(), + &((struct sockaddr_in*)&ifr.ifr_netmask)->sin_addr.s_addr) != 1) { + logger.error("Invalid tun_dev_netmask: %s", args.tun_dev_netmask.c_str()); + srsran::console("Invalid tun_dev_netmask: %s\n", args.tun_dev_netmask.c_str()); + perror("inet_pton"); + return SRSRAN_ERROR_CANT_START; + } if (0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) { err_str = strerror(errno); logger.debug("Failed to set socket netmask: %s", err_str); @@ -497,6 +534,9 @@ int gw::setup_if_addr6(uint8_t* ipv6_if_id, char* err_str) } } + if (sock > 0) { + close(sock); + } // Setup the IP address sock = socket(AF_INET6, SOCK_DGRAM, 0); ifr.ifr_addr.sa_family = AF_INET6; diff --git a/srsue/src/stack/upper/nas.cc b/srsue/src/stack/upper/nas.cc index f5677e631..c328ada51 100644 --- a/srsue/src/stack/upper/nas.cc +++ b/srsue/src/stack/upper/nas.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -35,6 +35,10 @@ #include "srsue/hdr/stack/upper/nas.h" #include "srsue/hdr/stack/upper/nas_idle_procedures.h" +#define LTE_MAC_OFFSET 1 +#define LTE_SEQ_OFFSET 5 +#define LTE_NAS_BEARER 0 + using namespace srsran; namespace srsue { @@ -43,7 +47,8 @@ namespace srsue { * NAS ********************************************************************/ -nas::nas(srsran::task_sched_handle task_sched_) : +nas::nas(srslog::basic_logger& logger_, srsran::task_sched_handle task_sched_) : + nas_base(logger_, LTE_MAC_OFFSET, LTE_SEQ_OFFSET, LTE_NAS_BEARER), plmn_searcher(this), task_sched(task_sched_), t3402(task_sched_.get_unique_timer()), @@ -51,11 +56,10 @@ nas::nas(srsran::task_sched_handle task_sched_) : t3411(task_sched_.get_unique_timer()), t3421(task_sched_.get_unique_timer()), reattach_timer(task_sched_.get_unique_timer()), - airplane_mode_sim_timer(task_sched_.get_unique_timer()), - logger(srslog::fetch_basic_logger("NAS")) + airplane_mode_sim_timer(task_sched_.get_unique_timer()) {} -void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& cfg_) +int nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& cfg_) { usim = usim_; rrc = rrc_; @@ -67,38 +71,30 @@ void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_ } // parse and sanity check EIA list - std::vector cap_list; - srsran::string_parse_list(cfg_.eia, ',', cap_list); - if (cap_list.empty()) { - logger.error("Empty EIA list. Select at least one EIA algorithm."); - } - for (std::vector::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) { - if (*it != 0 && *it < 4) { - eia_caps[*it] = true; - } else { - logger.error("EIA%d is not a valid EIA algorithm.", *it); - } + if (parse_security_algorithm_list(cfg_.eia, eia_caps) != SRSRAN_SUCCESS) { + logger.warning("Failed to parse integrity protection algorithm list: Defaulting to EIA1-128, EIA2-128, EIA3-128"); + eia_caps[0] = false; + eia_caps[1] = true; + eia_caps[2] = true; + eia_caps[3] = true; } // parse and sanity check EEA list - srsran::string_parse_list(cfg_.eea, ',', cap_list); - if (cap_list.empty()) { - logger.error("Empty EEA list. Select at least one EEA algorithm."); - } - for (std::vector::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) { - if (*it < 4) { - eea_caps[*it] = true; - } else { - logger.error("EEA%d is not a valid EEA algorithm.", *it); - } + if (parse_security_algorithm_list(cfg_.eea, eea_caps) != SRSRAN_SUCCESS) { + logger.warning("Failed to parse encryption algorithm list: Defaulting to EEA0, EEA1-128, EEA2-128, EEA3-128"); + eea_caps[0] = true; + eea_caps[1] = true; + eea_caps[2] = true; + eea_caps[3] = true; } cfg = cfg_; - if ((read_ctxt_file(&ctxt))) { - usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, ctxt.cipher_algo, ctxt.integ_algo); - logger.debug(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); - logger.debug(k_nas_int, 32, "NAS integrity key - k_nas_int"); + if ((read_ctxt_file(&ctxt, &ctxt_base))) { + usim->generate_nas_keys( + ctxt.k_asme, ctxt_base.k_nas_enc, ctxt_base.k_nas_int, ctxt_base.cipher_algo, ctxt_base.integ_algo); + logger.debug(ctxt_base.k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + logger.debug(ctxt_base.k_nas_int, 32, "NAS integrity key - k_nas_int"); } // Configure timers @@ -114,6 +110,7 @@ void nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_ } running = true; + return SRSRAN_SUCCESS; } nas::~nas() {} @@ -121,7 +118,7 @@ nas::~nas() {} void nas::stop() { running = false; - write_ctxt_file(ctxt); + write_ctxt_file(ctxt, ctxt_base); } void nas::get_metrics(nas_metrics_t* m) @@ -137,7 +134,7 @@ void nas::run_tti() // Process PLMN selection ongoing procedures callbacks.run(); - // Transmit intiating messages if necessary + // Transmit initiating messages if necessary switch (state.get_state()) { case emm_state_t::state_t::deregistered: // TODO Make sure cell selection is finished after transitioning from another state (if required) @@ -169,32 +166,36 @@ void nas::run_tti() } } -/******************************************************************************* - * FSM Helperse - ******************************************************************************/ -void nas::enter_emm_null() +// Helper method to inform GW about remove default EPS bearer +void nas::clear_eps_bearer() { // Deactivate EPS bearer according to Sec. 5.5.2.2.2 logger.debug("Clearing EPS bearer context"); + for (const auto& bearer : eps_bearer) { + gw->deactivate_eps_bearer(bearer.second.eps_bearer_id); + } eps_bearer.clear(); +} + +/******************************************************************************* + * FSM Helpers + ******************************************************************************/ +void nas::enter_emm_null() +{ + clear_eps_bearer(); state.set_null(); } void nas::enter_emm_deregistered_initiated() { - // Deactivate EPS bearer according to Sec. 5.5.2.2.2 - logger.debug("Clearing EPS bearer context"); - eps_bearer.clear(); + clear_eps_bearer(); state.set_deregistered_initiated(); } void nas::enter_emm_deregistered(emm_state_t::deregistered_substate_t substate) { // TODO Start cell selection. - - // Deactivate EPS bearer according to Sec. 5.5.2.2.2 - logger.debug("Clearing EPS bearer context"); - eps_bearer.clear(); + clear_eps_bearer(); state.set_deregistered(substate); } @@ -233,6 +234,9 @@ void nas::timer_expired(uint32_t timeout_id) logger.warning("Reattach timer expired: trying to attach again"); start_attach_request(srsran::establishment_cause_t::mo_sig); } else if (timeout_id == airplane_mode_sim_timer.id()) { + logger.debug("Airplane mode simulation timer expired after %dms, airplane mode is currently %s.", + airplane_mode_sim_timer.time_elapsed(), + airplane_mode_state == DISABLED ? "disabled" : "enabled"); if (airplane_mode_state == DISABLED) { // Enabling air-plane mode send_detach_request(true); @@ -487,8 +491,6 @@ void nas::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) logger.error("Not handling NAS message with integrity check error"); return; } - case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT: - break; default: logger.error("Not handling NAS message with SEC_HDR_TYPE=%02X", sec_hdr_type); return; @@ -507,12 +509,9 @@ void nas::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS) { switch (msg_type) { case LIBLTE_MME_MSG_TYPE_IDENTITY_REQUEST: // special case for IMSI is checked in parse_identity_request() - case LIBLTE_MME_MSG_TYPE_EMM_INFORMATION: - case LIBLTE_MME_MSG_TYPE_EMM_STATUS: case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST: case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REJECT: case LIBLTE_MME_MSG_TYPE_ATTACH_REJECT: - case LIBLTE_MME_MSG_TYPE_DETACH_REQUEST: case LIBLTE_MME_MSG_TYPE_DETACH_ACCEPT: case LIBLTE_MME_MSG_TYPE_TRACKING_AREA_UPDATE_REJECT: case LIBLTE_MME_MSG_TYPE_SERVICE_REJECT: @@ -539,7 +538,7 @@ void nas::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) parse_attach_accept(lcid, std::move(pdu)); break; case LIBLTE_MME_MSG_TYPE_ATTACH_REJECT: - parse_attach_reject(lcid, std::move(pdu)); + parse_attach_reject(lcid, std::move(pdu), sec_hdr_type); break; case LIBLTE_MME_MSG_TYPE_AUTHENTICATION_REQUEST: parse_authentication_request(lcid, std::move(pdu), sec_hdr_type); @@ -554,7 +553,7 @@ void nas::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) parse_security_mode_command(lcid, std::move(pdu)); break; case LIBLTE_MME_MSG_TYPE_SERVICE_REJECT: - parse_service_reject(lcid, std::move(pdu)); + parse_service_reject(lcid, std::move(pdu), sec_hdr_type); break; case LIBLTE_MME_MSG_TYPE_ESM_INFORMATION_REQUEST: parse_esm_information_request(lcid, std::move(pdu)); @@ -690,193 +689,6 @@ void nas::select_plmn() } } -/******************************************************************************* - * Security - ******************************************************************************/ - -void nas::integrity_generate(uint8_t* key_128, - uint32_t count, - uint8_t direction, - uint8_t* msg, - uint32_t msg_len, - uint8_t* mac) -{ - switch (ctxt.integ_algo) { - case INTEGRITY_ALGORITHM_ID_EIA0: - break; - case INTEGRITY_ALGORITHM_ID_128_EIA1: - security_128_eia1(key_128, - count, - 0, // Bearer always 0 for NAS - direction, - msg, - msg_len, - mac); - break; - case INTEGRITY_ALGORITHM_ID_128_EIA2: - security_128_eia2(key_128, - count, - 0, // Bearer always 0 for NAS - direction, - msg, - msg_len, - mac); - break; - case INTEGRITY_ALGORITHM_ID_128_EIA3: - security_128_eia3(key_128, - count, - 0, // Bearer always 0 for NAS - direction, - msg, - msg_len, - mac); - break; - default: - break; - } -} - -// This function depends to a valid k_nas_int. -// This key is generated in the security mode command. -bool nas::integrity_check(byte_buffer_t* pdu) -{ - if (pdu == nullptr) { - logger.error("Invalid PDU"); - return false; - } - - if (pdu->N_bytes > 5) { - uint8_t exp_mac[4] = {0}; - uint8_t* mac = &pdu->msg[1]; - - // generate expected MAC - uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5]; - integrity_generate( - &k_nas_int[16], count_est, SECURITY_DIRECTION_DOWNLINK, &pdu->msg[5], pdu->N_bytes - 5, &exp_mac[0]); - - // Check if expected mac equals the sent mac - for (int i = 0; i < 4; i++) { - if (exp_mac[i] != mac[i]) { - logger.warning("Integrity check failure. Local: count=%d, [%02x %02x %02x %02x], " - "Received: count=%d, [%02x %02x %02x %02x]", - count_est, - exp_mac[0], - exp_mac[1], - exp_mac[2], - exp_mac[3], - pdu->msg[5], - mac[0], - mac[1], - mac[2], - mac[3]); - return false; - } - } - logger.info("Integrity check ok. Local: count=%d, Received: count=%d [%02x %02x %02x %02x]", - count_est, - pdu->msg[5], - mac[0], - mac[1], - mac[2], - mac[3]); - - // Updated local count (according to TS 24.301 Sec. 4.4.3.3) - if (pdu->msg[5] != ctxt.rx_count) { - logger.info("Update local count to received value %d", pdu->msg[5]); - ctxt.rx_count = count_est; - } - return true; - } else { - logger.error("Invalid integrity check PDU size (%d)", pdu->N_bytes); - return false; - } -} - -void nas::cipher_encrypt(byte_buffer_t* pdu) -{ - byte_buffer_t pdu_tmp; - switch (ctxt.cipher_algo) { - case CIPHERING_ALGORITHM_ID_EEA0: - break; - case CIPHERING_ALGORITHM_ID_128_EEA1: - security_128_eea1(&k_nas_enc[16], - pdu->msg[5], - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_UPLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &pdu_tmp.msg[6]); - memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); - break; - case CIPHERING_ALGORITHM_ID_128_EEA2: - security_128_eea2(&k_nas_enc[16], - pdu->msg[5], - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_UPLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &pdu_tmp.msg[6]); - memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); - break; - case CIPHERING_ALGORITHM_ID_128_EEA3: - security_128_eea3(&k_nas_enc[16], - pdu->msg[5], - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_UPLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &pdu_tmp.msg[6]); - memcpy(&pdu->msg[6], &pdu_tmp.msg[6], pdu->N_bytes - 6); - break; - default: - logger.error("Ciphering algorithm not known"); - break; - } -} - -void nas::cipher_decrypt(byte_buffer_t* pdu) -{ - byte_buffer_t tmp_pdu; - switch (ctxt.cipher_algo) { - case CIPHERING_ALGORITHM_ID_EEA0: - break; - case CIPHERING_ALGORITHM_ID_128_EEA1: - security_128_eea1(&k_nas_enc[16], - pdu->msg[5], - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_DOWNLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &tmp_pdu.msg[6]); - memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); - break; - case CIPHERING_ALGORITHM_ID_128_EEA2: - security_128_eea2(&k_nas_enc[16], - pdu->msg[5], - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_DOWNLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &tmp_pdu.msg[6]); - logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); - memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); - break; - case CIPHERING_ALGORITHM_ID_128_EEA3: - security_128_eea3(&k_nas_enc[16], - pdu->msg[5], - 0, // Bearer always 0 for NAS - SECURITY_DIRECTION_DOWNLINK, - &pdu->msg[6], - pdu->N_bytes - 6, - &tmp_pdu.msg[6]); - logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); - memcpy(&pdu->msg[6], &tmp_pdu.msg[6], pdu->N_bytes - 6); - break; - default: - logger.error("Ciphering algorithms not known"); - break; - } -} bool nas::check_cap_replay(LIBLTE_MME_UE_SECURITY_CAPABILITIES_STRUCT* caps) { @@ -908,8 +720,12 @@ int nas::apply_security_config(srsran::unique_byte_buffer_t& pdu, uint8_t sec_hd cipher_encrypt(pdu.get()); } if (sec_hdr_type >= LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY) { - integrity_generate( - &k_nas_int[16], ctxt.tx_count, SECURITY_DIRECTION_UPLINK, &pdu->msg[5], pdu->N_bytes - 5, &pdu->msg[1]); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[5], + pdu->N_bytes - 5, + &pdu->msg[1]); } } else { logger.error("Invalid PDU size %d", pdu->N_bytes); @@ -1007,6 +823,14 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu) liblte_mme_unpack_activate_default_eps_bearer_context_request_msg(&attach_accept.esm_msg, &act_def_eps_bearer_context_req); + // make sure we don't already have a default EPS bearer activated + for (const auto& bearer : eps_bearer) { + if (bearer.second.type == DEFAULT_EPS_BEARER) { + logger.error("Only one default EPS bearer supported."); + return; + } + } + if ((cfg.apn_protocol == "ipv4" && LIBLTE_MME_PDN_TYPE_IPV6 == act_def_eps_bearer_context_req.pdn_addr.pdn_type) || (cfg.apn_protocol == "ipv6" && LIBLTE_MME_PDN_TYPE_IPV4 == act_def_eps_bearer_context_req.pdn_addr.pdn_type)) { logger.error("Failed to attach -- Mismatch between PDN protocol and PDN type in attach accept."); @@ -1042,7 +866,6 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu) // Setup GW char* err_str = nullptr; if (gw->setup_if_addr(act_def_eps_bearer_context_req.eps_bearer_id, - rrc->get_lcid_for_eps_bearer(act_def_eps_bearer_context_req.eps_bearer_id), LIBLTE_MME_PDN_TYPE_IPV4, ip_addr, nullptr, @@ -1075,7 +898,6 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu) // Setup GW char* err_str = nullptr; if (gw->setup_if_addr(act_def_eps_bearer_context_req.eps_bearer_id, - rrc->get_lcid_for_eps_bearer(act_def_eps_bearer_context_req.eps_bearer_id), LIBLTE_MME_PDN_TYPE_IPV6, 0, ipv6_if_id, @@ -1126,7 +948,6 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu) char* err_str = nullptr; if (gw->setup_if_addr(act_def_eps_bearer_context_req.eps_bearer_id, - rrc->get_lcid_for_eps_bearer(act_def_eps_bearer_context_req.eps_bearer_id), LIBLTE_MME_PDN_TYPE_IPV4V6, ip_addr, ipv6_if_id, @@ -1198,10 +1019,10 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu) enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search); } - ctxt.rx_count++; + ctxt_base.rx_count++; } -void nas::parse_attach_reject(uint32_t lcid, unique_byte_buffer_t pdu) +void nas::parse_attach_reject(uint32_t lcid, unique_byte_buffer_t pdu, const uint8_t sec_hdr_type) { LIBLTE_MME_ATTACH_REJECT_MSG_STRUCT attach_rej; ZERO_OBJECT(attach_rej); @@ -1210,6 +1031,13 @@ void nas::parse_attach_reject(uint32_t lcid, unique_byte_buffer_t pdu) logger.warning("Received Attach Reject. Cause= %02X", attach_rej.emm_cause); srsran::console("Received Attach Reject. Cause= %02X\n", attach_rej.emm_cause); + // do not accept if the message is not protected when the EMM cause is #25 (TS 24.301 Sec. 4.4.4.2) + if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS && + attach_rej.emm_cause == LIBLTE_MME_EMM_CAUSE_NOT_AUTHORIZED_FOR_THIS_CSG) { + logger.error("Not handling NAS Attach Reject message with EMM cause #25 without integrity protection!"); + return; + } + // stop T3410 if (t3410.is_running()) { logger.debug("Stopping T3410"); @@ -1253,7 +1081,7 @@ void nas::parse_authentication_request(uint32_t lcid, unique_byte_buffer_t pdu, logger.info("Received Authentication Request"); liblte_mme_unpack_authentication_request_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &auth_req); - ctxt.rx_count++; + ctxt_base.rx_count++; // Generate authentication response using RAND, AUTN & KSI-ASME uint16 mcc, mnc; @@ -1268,6 +1096,7 @@ void nas::parse_authentication_request(uint32_t lcid, unique_byte_buffer_t pdu, logger.debug(auth_req.autn, 16, "Authentication request AUTN"); auth_result_t auth_result = usim->generate_authentication_response(auth_req.rand, auth_req.autn, mcc, mnc, res, &res_len, ctxt.k_asme); + logger.debug(res, res_len, "Authentication request RES"); if (LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE == auth_req.nas_ksi.tsc_flag) { ctxt.ksi = auth_req.nas_ksi.nas_ksi; } else { @@ -1308,7 +1137,7 @@ void nas::parse_identity_request(unique_byte_buffer_t pdu, const uint8_t sec_hdr liblte_mme_unpack_identity_request_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &id_req); logger.info("Received Identity Request. ID type: %d", id_req.id_type); - ctxt.rx_count++; + ctxt_base.rx_count++; // do not respond if request is not protected (TS 24.301 Sec. 4.4.4.2) if (sec_hdr_type >= LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY || @@ -1365,27 +1194,31 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu) // Reset counters (as per 24.301 5.4.3.2), only needed for initial security mode command if (auth_request) { - ctxt.rx_count = 0; - ctxt.tx_count = 0; + ctxt_base.rx_count = 0; + ctxt_base.tx_count = 0; auth_request = false; } - ctxt.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eea; - ctxt.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eia; + ctxt_base.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eea; + ctxt_base.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eia; // Check capabilities - if (!eea_caps[ctxt.cipher_algo] || !eia_caps[ctxt.integ_algo]) { + if (!eea_caps[ctxt_base.cipher_algo] || !eia_caps[ctxt_base.integ_algo]) { logger.warning("Sending Security Mode Reject due to security capabilities mismatch"); send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH); return; } // Generate NAS keys - usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, ctxt.cipher_algo, ctxt.integ_algo); - logger.info(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); - logger.info(k_nas_int, 32, "NAS integrity key - k_nas_int"); + usim->generate_nas_keys( + ctxt.k_asme, ctxt_base.k_nas_enc, ctxt_base.k_nas_int, ctxt_base.cipher_algo, ctxt_base.integ_algo); + logger.info(ctxt_base.k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + logger.info(ctxt_base.k_nas_int, 32, "NAS integrity key - k_nas_int"); - logger.debug("Generating integrity check. integ_algo:%d, count_dl:%d, lcid:%d", ctxt.integ_algo, ctxt.rx_count, lcid); + logger.debug("Generating integrity check. integ_algo:%d, count_dl:%d, lcid:%d", + ctxt_base.integ_algo, + ctxt_base.rx_count, + lcid); if (not integrity_check(pdu.get())) { logger.warning("Sending Security Mode Reject due to integrity check failure"); @@ -1393,7 +1226,7 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu) return; } - ctxt.rx_count++; + ctxt_base.rx_count++; LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sec_mode_comp = {}; if (sec_mode_cmd.imeisv_req_present && LIBLTE_MME_IMEISV_REQUESTED == sec_mode_cmd.imeisv_req) { @@ -1413,7 +1246,7 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu) // Pack and send response pdu->clear(); liblte_mme_pack_security_mode_complete_msg( - &sec_mode_comp, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + &sec_mode_comp, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); } @@ -1424,15 +1257,15 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu) } logger.info( - "Sending Security Mode Complete nas_current_ctxt.tx_count=%d, RB=%s", ctxt.tx_count, rrc->get_rb_name(lcid)); + "Sending Security Mode Complete ctxt_base.tx_count=%d, RB=%s", ctxt_base.tx_count, rrc->get_rb_name(lcid)); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; // switch security header for all following messages current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED; } -void nas::parse_service_reject(uint32_t lcid, unique_byte_buffer_t pdu) +void nas::parse_service_reject(uint32_t lcid, unique_byte_buffer_t pdu, const uint8_t sec_hdr_type) { LIBLTE_MME_SERVICE_REJECT_MSG_STRUCT service_reject; if (liblte_mme_unpack_service_reject_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &service_reject)) { @@ -1441,6 +1274,14 @@ void nas::parse_service_reject(uint32_t lcid, unique_byte_buffer_t pdu) } srsran::console("Received service reject with EMM cause=0x%x.\n", service_reject.emm_cause); + + // do not accept if the message is not protected when the EMM cause is #25 (TS 24.301 Sec. 4.4.4.2) + if (sec_hdr_type == LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS && + service_reject.emm_cause == LIBLTE_MME_EMM_CAUSE_NOT_AUTHORIZED_FOR_THIS_CSG) { + logger.error("Not handling NAS Service Reject message with EMM cause #25 without integrity protection!"); + return; + } + if (service_reject.t3446_present) { logger.info( "Received service reject with EMM cause=0x%x and t3446=%d", service_reject.emm_cause, service_reject.t3446); @@ -1465,7 +1306,7 @@ void nas::parse_esm_information_request(uint32_t lcid, unique_byte_buffer_t pdu) logger.info("ESM information request received for beaser=%d, transaction_id=%d", esm_info_req.eps_bearer_id, esm_info_req.proc_transaction_id); - ctxt.rx_count++; + ctxt_base.rx_count++; // send response send_esm_information_response(esm_info_req.proc_transaction_id); @@ -1478,14 +1319,14 @@ void nas::parse_emm_information(uint32_t lcid, unique_byte_buffer_t pdu) std::string str = emm_info_str(&emm_info); logger.info("Received EMM Information: %s", str.c_str()); srsran::console("%s\n", str.c_str()); - ctxt.rx_count++; + ctxt_base.rx_count++; } void nas::parse_detach_request(uint32_t lcid, unique_byte_buffer_t pdu) { LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT detach_request; liblte_mme_unpack_detach_request_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &detach_request); - ctxt.rx_count++; + ctxt_base.rx_count++; logger.info("Received detach request (type=%d). NAS State: %s", detach_request.detach_type.type_of_detach, @@ -1526,7 +1367,7 @@ void nas::parse_activate_dedicated_eps_bearer_context_request(uint32_t lcid, uni request.linked_eps_bearer_id, request.proc_transaction_id); - ctxt.rx_count++; + ctxt_base.rx_count++; LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft = &request.tft; logger.info("Traffic Flow Template: TFT OP code 0x%x, Filter list size %d, Parameter list size %d", tft->tft_op_code, @@ -1561,7 +1402,7 @@ void nas::parse_activate_dedicated_eps_bearer_context_request(uint32_t lcid, uni } // apply packet filters to GW - gw->apply_traffic_flow_template(request.eps_bearer_id, rrc->get_lcid_for_eps_bearer(request.eps_bearer_id), tft); + gw->apply_traffic_flow_template(request.eps_bearer_id, tft); send_activate_dedicated_eps_bearer_context_accept(request.proc_transaction_id, request.eps_bearer_id); } @@ -1577,7 +1418,7 @@ void nas::parse_deactivate_eps_bearer_context_request(unique_byte_buffer_t pdu) request.proc_transaction_id, request.esm_cause); - ctxt.rx_count++; + ctxt_base.rx_count++; // check if bearer exists if (eps_bearer.find(request.eps_bearer_id) == eps_bearer.end()) { @@ -1590,6 +1431,9 @@ void nas::parse_deactivate_eps_bearer_context_request(unique_byte_buffer_t pdu) eps_bearer_map_t::iterator it = eps_bearer.find(request.eps_bearer_id); eps_bearer.erase(it); + // inform GW about removed EPS bearer + gw->deactivate_eps_bearer(request.eps_bearer_id); + logger.info("Removed EPS bearer context (eps_bearer_id=%d)", request.eps_bearer_id); send_deactivate_eps_bearer_context_accept(request.proc_transaction_id, request.eps_bearer_id); @@ -1605,16 +1449,28 @@ void nas::parse_modify_eps_bearer_context_request(srsran::unique_byte_buffer_t p request.eps_bearer_id, request.proc_transaction_id); - ctxt.rx_count++; + ctxt_base.rx_count++; // check if bearer exists - if (eps_bearer.find(request.eps_bearer_id) == eps_bearer.end()) { + const auto it = eps_bearer.find(request.eps_bearer_id); + if (it == eps_bearer.end()) { logger.error("EPS bearer doesn't exist (eps_bearer_id=%d)", request.eps_bearer_id); // fixme: send proper response return; } - // fixme: carry out modification + LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft = &request.tft; + logger.info("Traffic Flow Template: TFT OP code 0x%x, Filter list size %d, Parameter list size %d", + tft->tft_op_code, + tft->packet_filter_list_size, + tft->parameter_list_size); + + // modify/apply packet filters to GW + if (gw->apply_traffic_flow_template(request.eps_bearer_id, tft) != SRSRAN_SUCCESS) { + logger.error("Couldn't modify TFT"); + return; + } + logger.info("Modified EPS bearer context (eps_bearer_id=%d)", request.eps_bearer_id); send_modify_eps_bearer_context_accept(request.proc_transaction_id, request.eps_bearer_id); @@ -1624,7 +1480,7 @@ void nas::parse_activate_test_mode(uint32_t lcid, unique_byte_buffer_t pdu) { logger.info("Received Activate test mode"); - ctxt.rx_count++; + ctxt_base.rx_count++; send_activate_test_mode_complete(); } @@ -1647,7 +1503,7 @@ void nas::parse_close_ue_test_loop(uint32_t lcid, unique_byte_buffer_t pdu) break; } - ctxt.rx_count++; + ctxt_base.rx_count++; send_close_ue_test_loop_complete(); } @@ -1656,7 +1512,7 @@ void nas::parse_emm_status(uint32_t lcid, unique_byte_buffer_t pdu) { LIBLTE_MME_EMM_STATUS_MSG_STRUCT emm_status; liblte_mme_unpack_emm_status_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &emm_status); - ctxt.rx_count++; + ctxt_base.rx_count++; switch (emm_status.emm_cause) { case LIBLTE_MME_ESM_CAUSE_INVALID_EPS_BEARER_IDENTITY: @@ -1750,7 +1606,7 @@ void nas::gen_attach_request(srsran::unique_byte_buffer_t& msg) // According to Sec 4.4.5, the attach request is always unciphered, even if a context exists liblte_mme_pack_attach_request_msg( - &attach_req, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)msg.get()); + &attach_req, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)msg.get()); if (apply_security_config(msg, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY)) { logger.error("Error applying NAS security."); @@ -1770,8 +1626,8 @@ void nas::gen_attach_request(srsran::unique_byte_buffer_t& msg) } if (have_ctxt) { - set_k_enb_count(ctxt.tx_count); - ctxt.tx_count++; + set_k_enb_count(ctxt_base.tx_count); + ctxt_base.tx_count++; } // stop T3411 and T3402 @@ -1803,11 +1659,11 @@ void nas::gen_service_request(srsran::unique_byte_buffer_t& msg) msg->msg[0] = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4u) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); msg->N_bytes++; msg->msg[1] = (ctxt.ksi & 0x07u) << 5u; - msg->msg[1] |= ctxt.tx_count & 0x1Fu; + msg->msg[1] |= ctxt_base.tx_count & 0x1Fu; msg->N_bytes++; uint8_t mac[4]; - integrity_generate(&k_nas_int[16], ctxt.tx_count, SECURITY_DIRECTION_UPLINK, &msg->msg[0], 2, &mac[0]); + integrity_generate(&ctxt_base.k_nas_int[16], ctxt_base.tx_count, SECURITY_DIRECTION_UPLINK, &msg->msg[0], 2, &mac[0]); // Set the short MAC msg->msg[2] = mac[2]; msg->N_bytes++; @@ -1817,8 +1673,8 @@ void nas::gen_service_request(srsran::unique_byte_buffer_t& msg) if (pcap != nullptr) { pcap->write_nas(msg->msg, msg->N_bytes); } - set_k_enb_count(ctxt.tx_count); - ctxt.tx_count++; + set_k_enb_count(ctxt_base.tx_count); + ctxt_base.tx_count++; } void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT* msg) @@ -1922,8 +1778,10 @@ void nas::send_detach_request(bool switch_off) detach_request.nas_ksi.tsc_flag = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; detach_request.nas_ksi.nas_ksi = ctxt.ksi; logger.info("Sending detach request with GUTI"); // If sent as an Initial UE message, it cannot be ciphered - liblte_mme_pack_detach_request_msg( - &detach_request, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + liblte_mme_pack_detach_request_msg(&detach_request, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, + ctxt_base.tx_count, + (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -1940,7 +1798,7 @@ void nas::send_detach_request(bool switch_off) usim->get_imsi_vec(detach_request.eps_mobile_id.imsi, 15); logger.info("Sending detach request with IMSI"); liblte_mme_pack_detach_request_msg( - &detach_request, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + &detach_request, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -1948,7 +1806,7 @@ void nas::send_detach_request(bool switch_off) } if (switch_off) { - enter_emm_deregistered_initiated(); + enter_emm_deregistered(emm_state_t::deregistered_substate_t::null); } else { // we are expecting a response from the core state.set_deregistered_initiated(); @@ -1966,7 +1824,7 @@ void nas::send_detach_request(bool switch_off) } } - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_attach_complete(const uint8_t& transaction_id_, const uint8_t& eps_bearer_id) @@ -1987,7 +1845,7 @@ void nas::send_attach_complete(const uint8_t& transaction_id_, const uint8_t& ep return; } liblte_mme_pack_attach_complete_msg( - &attach_complete, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + &attach_complete, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); // Write NAS pcap if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -2003,7 +1861,7 @@ void nas::send_attach_complete(const uint8_t& transaction_id_, const uint8_t& ep logger.info("Sending Attach Complete"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_detach_accept() @@ -2016,7 +1874,8 @@ void nas::send_detach_accept() LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT detach_accept; bzero(&detach_accept, sizeof(detach_accept)); - liblte_mme_pack_detach_accept_msg(&detach_accept, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + liblte_mme_pack_detach_accept_msg( + &detach_accept, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -2046,7 +1905,7 @@ void nas::send_authentication_response(const uint8_t* res, const size_t res_len) } auth_res.res_len = res_len; liblte_mme_pack_authentication_response_msg( - &auth_res, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + &auth_res, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -2059,7 +1918,7 @@ void nas::send_authentication_response(const uint8_t* res, const size_t res_len) logger.info("Sending Authentication Response"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_authentication_failure(const uint8_t cause, const uint8_t* auth_fail_param) @@ -2118,7 +1977,8 @@ void nas::send_identity_response(const uint8 id_type) return; } - liblte_mme_pack_identity_response_msg(&id_resp, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + liblte_mme_pack_identity_response_msg( + &id_resp, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); // add security if needed if (apply_security_config(pdu, current_sec_hdr)) { @@ -2131,7 +1991,7 @@ void nas::send_identity_response(const uint8 id_type) } rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_service_request() @@ -2146,11 +2006,11 @@ void nas::send_service_request() msg->msg[0] = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4u) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); msg->N_bytes++; msg->msg[1] = (ctxt.ksi & 0x07u) << 5u; - msg->msg[1] |= ctxt.tx_count & 0x1Fu; + msg->msg[1] |= ctxt_base.tx_count & 0x1Fu; msg->N_bytes++; uint8_t mac[4]; - integrity_generate(&k_nas_int[16], ctxt.tx_count, SECURITY_DIRECTION_UPLINK, &msg->msg[0], 2, &mac[0]); + integrity_generate(&ctxt_base.k_nas_int[16], ctxt_base.tx_count, SECURITY_DIRECTION_UPLINK, &msg->msg[0], 2, &mac[0]); // Set the short MAC msg->msg[2] = mac[2]; msg->N_bytes++; @@ -2163,7 +2023,7 @@ void nas::send_service_request() logger.info("Sending service request"); rrc->write_sdu(std::move(msg)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_esm_information_response(const uint8 proc_transaction_id) @@ -2263,7 +2123,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) } if (liblte_mme_pack_esm_information_response_msg( - &esm_info_resp, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { + &esm_info_resp, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { logger.error("Error packing ESM information response."); return; } @@ -2280,7 +2140,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) logger.info(pdu->msg, pdu->N_bytes, "Sending ESM information response"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; chap_id++; } @@ -2299,7 +2159,7 @@ void nas::send_activate_dedicated_eps_bearer_context_accept(const uint8_t& proc_ accept.proc_transaction_id = proc_transaction_id; if (liblte_mme_pack_activate_dedicated_eps_bearer_context_accept_msg( - &accept, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { + &accept, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { logger.error("Error packing Activate Dedicated EPS Bearer context accept."); return; } @@ -2320,7 +2180,7 @@ void nas::send_activate_dedicated_eps_bearer_context_accept(const uint8_t& proc_ accept.proc_transaction_id); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_deactivate_eps_bearer_context_accept(const uint8_t& proc_transaction_id, const uint8_t& eps_bearer_id) @@ -2337,7 +2197,7 @@ void nas::send_deactivate_eps_bearer_context_accept(const uint8_t& proc_transact accept.proc_transaction_id = proc_transaction_id; if (liblte_mme_pack_deactivate_eps_bearer_context_accept_msg( - &accept, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { + &accept, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { logger.error("Error packing Activate EPS Bearer context accept."); return; } @@ -2358,7 +2218,7 @@ void nas::send_deactivate_eps_bearer_context_accept(const uint8_t& proc_transact accept.proc_transaction_id); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_modify_eps_bearer_context_accept(const uint8_t& proc_transaction_id, const uint8_t& eps_bearer_id) @@ -2375,7 +2235,7 @@ void nas::send_modify_eps_bearer_context_accept(const uint8_t& proc_transaction_ accept.proc_transaction_id = proc_transaction_id; if (liblte_mme_pack_modify_eps_bearer_context_accept_msg( - &accept, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { + &accept, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { logger.error("Error packing Modify EPS Bearer context accept."); return; } @@ -2396,7 +2256,7 @@ void nas::send_modify_eps_bearer_context_accept(const uint8_t& proc_transaction_ accept.proc_transaction_id); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_activate_test_mode_complete() @@ -2408,7 +2268,7 @@ void nas::send_activate_test_mode_complete() } if (liblte_mme_pack_activate_test_mode_complete_msg( - (LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), current_sec_hdr, ctxt.tx_count)) { + (LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), current_sec_hdr, ctxt_base.tx_count)) { logger.error("Error packing activate test mode complete."); return; } @@ -2425,7 +2285,7 @@ void nas::send_activate_test_mode_complete() logger.info(pdu->msg, pdu->N_bytes, "Sending Activate test mode complete"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_close_ue_test_loop_complete() @@ -2437,7 +2297,7 @@ void nas::send_close_ue_test_loop_complete() } if (liblte_mme_pack_close_ue_test_loop_complete_msg( - (LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), current_sec_hdr, ctxt.tx_count)) { + (LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), current_sec_hdr, ctxt_base.tx_count)) { logger.error("Error packing close UE test loop complete."); return; } @@ -2454,17 +2314,17 @@ void nas::send_close_ue_test_loop_complete() logger.info(pdu->msg, pdu->N_bytes, "Sending Close UE test loop complete"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } /******************************************************************************* * Security context persistence file ******************************************************************************/ -bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_) +bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_, nas_sec_base_ctxt* ctxt_base_) { std::ifstream file; - if (ctxt_ == nullptr) { + if (ctxt_ == nullptr || ctxt_base_ == nullptr) { return false; } @@ -2490,16 +2350,16 @@ bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_) if (!readvar(file, "mme_code=", &ctxt_->guti.mme_code)) { return false; } - if (!readvar(file, "tx_count=", &ctxt_->tx_count)) { + if (!readvar(file, "tx_count=", &ctxt_base_->tx_count)) { return false; } - if (!readvar(file, "rx_count=", &ctxt_->rx_count)) { + if (!readvar(file, "rx_count=", &ctxt_base_->rx_count)) { return false; } - if (!readvar(file, "int_alg=", &ctxt_->integ_algo)) { + if (!readvar(file, "int_alg=", &ctxt_base_->integ_algo)) { return false; } - if (!readvar(file, "enc_alg=", &ctxt_->cipher_algo)) { + if (!readvar(file, "enc_alg=", &ctxt_base_->cipher_algo)) { return false; } if (!readvar(file, "ksi=", &ctxt_->ksi)) { @@ -2522,10 +2382,10 @@ bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_) "ksi: %x, k_asme: %s, tx_count: %x, rx_count: %x, int_alg: %d, enc_alg: %d", ctxt_->ksi, hex_to_string(ctxt_->k_asme, 32).c_str(), - ctxt_->tx_count, - ctxt_->rx_count, - ctxt_->integ_algo, - ctxt_->cipher_algo); + ctxt_base_->tx_count, + ctxt_base_->rx_count, + ctxt_base_->integ_algo, + ctxt_base_->cipher_algo); have_guti = true; have_ctxt = true; @@ -2541,7 +2401,7 @@ bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_) return false; } -bool nas::write_ctxt_file(nas_sec_ctxt ctxt_) +bool nas::write_ctxt_file(nas_sec_ctxt ctxt_, nas_sec_base_ctxt ctxt_base_) { if (!have_guti || !have_ctxt) { return false; @@ -2554,10 +2414,10 @@ bool nas::write_ctxt_file(nas_sec_ctxt ctxt_) file << "mnc=" << (int)ctxt_.guti.mnc << std::endl; file << "mme_group_id=" << (int)ctxt_.guti.mme_group_id << std::endl; file << "mme_code=" << (int)ctxt_.guti.mme_code << std::endl; - file << "tx_count=" << (int)ctxt_.tx_count << std::endl; - file << "rx_count=" << (int)ctxt_.rx_count << std::endl; - file << "int_alg=" << (int)ctxt_.integ_algo << std::endl; - file << "enc_alg=" << (int)ctxt_.cipher_algo << std::endl; + file << "tx_count=" << (int)ctxt_base_.tx_count << std::endl; + file << "rx_count=" << (int)ctxt_base_.rx_count << std::endl; + file << "int_alg=" << (int)ctxt_base_.integ_algo << std::endl; + file << "enc_alg=" << (int)ctxt_base_.cipher_algo << std::endl; file << "ksi=" << (int)ctxt_.ksi << std::endl; file << "k_asme=" << hex_to_string(ctxt_.k_asme, 32) << std::endl; @@ -2573,10 +2433,10 @@ bool nas::write_ctxt_file(nas_sec_ctxt ctxt_) "ksi: %x, k_asme: %s, tx_count: %x, rx_count: %x, int_alg: %d, enc_alg: %d", ctxt_.ksi, hex_to_string(ctxt_.k_asme, 32).c_str(), - ctxt_.tx_count, - ctxt_.rx_count, - ctxt_.integ_algo, - ctxt_.cipher_algo); + ctxt_base_.tx_count, + ctxt_base_.rx_count, + ctxt_base_.integ_algo, + ctxt_base_.cipher_algo); file.close(); return true; } diff --git a/srsue/src/stack/upper/nas_5g.cc b/srsue/src/stack/upper/nas_5g.cc new file mode 100644 index 000000000..1f8ac1ec1 --- /dev/null +++ b/srsue/src/stack/upper/nas_5g.cc @@ -0,0 +1,1367 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/stack/upper/nas_5g.h" +#include "srsran/asn1/nas_5g_ies.h" +#include "srsran/asn1/nas_5g_msg.h" +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/security.h" +#include "srsran/common/standard_streams.h" +#include "srsran/common/string_helpers.h" +#include "srsran/interfaces/ue_gw_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" +#include "srsran/interfaces/ue_usim_interfaces.h" +#include "srsue/hdr/stack/upper/nas_5g_procedures.h" + +#include +#include +#include + +#define MAC_5G_OFFSET 2 +#define SEQ_5G_OFFSET 6 +#define NAS_5G_BEARER 1 + +using namespace srsran; +using namespace srsran::nas_5g; + +namespace srsue { + +/********************************************************************* + * NAS 5G (NR) + ********************************************************************/ + +nas_5g::nas_5g(srslog::basic_logger& logger_, srsran::task_sched_handle task_sched_) : + nas_base(logger_, MAC_5G_OFFSET, SEQ_5G_OFFSET, NAS_5G_BEARER), + task_sched(task_sched_), + t3502(task_sched_.get_unique_timer()), + t3510(task_sched_.get_unique_timer()), + t3511(task_sched_.get_unique_timer()), + t3521(task_sched_.get_unique_timer()), + reregistration_timer(task_sched_.get_unique_timer()), + registration_proc(this), + state(logger_), + pdu_session_establishment_proc(this, logger_) +{ + // Configure timers + t3502.set(t3502_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); + t3510.set(t3510_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); + t3511.set(t3511_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); + t3521.set(t3521_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); + reregistration_timer.set(reregistration_timer_duration_ms, [this](uint32_t tid) { timer_expired(tid); }); +} + +nas_5g::~nas_5g() {} + +void nas_5g::stop() +{ + running = false; +} + +int nas_5g::init(usim_interface_nas* usim_, + rrc_nr_interface_nas_5g* rrc_nr_, + gw_interface_nas* gw_, + const nas_5g_args_t& cfg_) +{ + usim = usim_; + rrc_nr = rrc_nr_; + gw = gw_; + cfg = cfg_; + + // parse and sanity check EIA list + if (parse_security_algorithm_list(cfg_.ia5g, ia5g_caps) != SRSRAN_SUCCESS) { + logger.warning("Failed to parse integrity algorithm list: Defaulting to 5G-EI1-128, 5G-EI2-128, 5G-EI3-128"); + ia5g_caps[0] = false; + ia5g_caps[1] = true; + ia5g_caps[2] = true; + ia5g_caps[3] = true; + } + + // parse and sanity check EEA list + if (parse_security_algorithm_list(cfg_.ea5g, ea5g_caps) != SRSRAN_SUCCESS) { + logger.warning( + "Failed to parse encryption algorithm list: Defaulting to 5G-EA0, 5G-EA1-128, 5G-EA2-128, 5G-EA3-128"); + ea5g_caps[0] = true; + ea5g_caps[1] = true; + ea5g_caps[2] = true; + ea5g_caps[3] = true; + } + + if (init_pdu_sessions(cfg.pdu_session_cfgs) != SRSRAN_SUCCESS) { + logger.warning("Failure while configuring pdu sessions"); + } + + running = true; + return SRSRAN_SUCCESS; +} + +void nas_5g::run_tti() +{ + // Process PLMN selection ongoing procedures + callbacks.run(); + + // Transmit initiating messages if necessary + switch (state.get_state()) { + case mm5g_state_t::state_t::deregistered: + // TODO Make sure cell selection is finished after transitioning from another state (if required) + // Make sure the RRC is finished transitioning to RRC Idle + if (reregistration_timer.is_running()) { + logger.debug("Waiting for re-attach timer to expire to attach again."); + return; + } + switch (state.get_deregistered_substate()) { + case mm5g_state_t::deregistered_substate_t::plmn_search: + case mm5g_state_t::deregistered_substate_t::normal_service: + case mm5g_state_t::deregistered_substate_t::initial_registration_needed: + registration_proc.launch(); + break; + case mm5g_state_t::deregistered_substate_t::attempting_to_registration: + case mm5g_state_t::deregistered_substate_t::no_supi: + case mm5g_state_t::deregistered_substate_t::no_cell_available: + case mm5g_state_t::deregistered_substate_t::e_call_inactive: + logger.debug("Attempting to registration (not implemented) %s", state.get_full_state_text().c_str()); + default: + break; + } + case mm5g_state_t::state_t::registered: + break; + case mm5g_state_t::state_t::deregistered_initiated: + break; + default: + break; + } +} + +int nas_5g::write_pdu(srsran::unique_byte_buffer_t pdu) +{ + logger.info(pdu->msg, pdu->N_bytes, "DL PDU (length %d)", pdu->N_bytes); + + nas_5gs_msg nas_msg; + + if (nas_msg.unpack_outer_hdr(pdu) != SRSRAN_SUCCESS) { + logger.error("Unable to unpack outer NAS header"); + return SRSRAN_ERROR; + } + + switch (nas_msg.hdr.security_header_type) { + case nas_5gs_hdr::security_header_type_opts::plain_5gs_nas_message: + break; + case nas_5gs_hdr::security_header_type_opts::integrity_protected: + if (integrity_check(pdu.get()) == false) { + logger.error("Not handling NAS message with integrity check error"); + return SRSRAN_ERROR; + } + break; + case nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered: + if (integrity_check(pdu.get()) == false) { + logger.error("Not handling NAS message with integrity check error"); + return SRSRAN_ERROR; + } else { + cipher_decrypt(pdu.get()); + } + break; + case nas_5gs_hdr::security_header_type_opts::integrity_protected_with_new_5G_nas_context: + break; + case nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered_with_new_5G_nas_context: + return SRSRAN_ERROR; + default: + logger.error("Not handling NAS message with unkown security header"); + break; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu->msg, pdu->N_bytes); + } + + logger.info(pdu->msg, pdu->N_bytes, "Decrypted DL PDU (length %d)", pdu->N_bytes); + + // Parse the message header + if (nas_msg.unpack(pdu) != SRSRAN_SUCCESS) { + logger.error("Unable to unpack complete NAS pdu"); + return SRSRAN_ERROR; + } + + switch (nas_msg.hdr.message_type) { + case msg_opts::options::registration_accept: + handle_registration_accept(nas_msg.registration_accept()); + break; + case msg_opts::options::registration_reject: + handle_registration_reject(nas_msg.registration_reject()); + break; + case msg_opts::options::authentication_reject: + handle_authentication_reject(nas_msg.authentication_reject()); + break; + case msg_opts::options::authentication_request: + handle_authentication_request(nas_msg.authentication_request()); + break; + case msg_opts::options::identity_request: + handle_identity_request(nas_msg.identity_request()); + break; + case msg_opts::options::security_mode_command: + handle_security_mode_command(nas_msg.security_mode_command(), std::move(pdu)); + break; + case msg_opts::options::service_accept: + handle_service_accept(nas_msg.service_accept()); + break; + case msg_opts::options::service_reject: + handle_service_reject(nas_msg.service_reject()); + break; + case msg_opts::options::deregistration_accept_ue_terminated: + handle_deregistration_accept_ue_terminated(nas_msg.deregistration_accept_ue_terminated()); + break; + case msg_opts::options::deregistration_request_ue_terminated: + handle_deregistration_request_ue_terminated(nas_msg.deregistration_request_ue_terminated()); + break; + case msg_opts::options::dl_nas_transport: + handle_dl_nas_transport(nas_msg.dl_nas_transport()); + break; + case msg_opts::options::deregistration_accept_ue_originating: + handle_deregistration_accept_ue_originating(nas_msg.deregistration_accept_ue_originating()); + break; + case msg_opts::options::configuration_update_command: + handle_configuration_update_command(nas_msg.configuration_update_command()); + break; + default: + logger.error( + "Not handling NAS message type: %s (0x%02x)", nas_msg.hdr.message_type.to_string(), nas_msg.hdr.message_type); + break; + } + return SRSRAN_SUCCESS; +} + +/******************************************************************************* + * Senders + ******************************************************************************/ + +int nas_5g::send_registration_request() +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + initial_registration_request_stored.hdr.extended_protocol_discriminator = + nas_5gs_hdr::extended_protocol_discriminator_opts::extended_protocol_discriminator_5gmm; + registration_request_t& reg_req = initial_registration_request_stored.set_registration_request(); + + reg_req.registration_type_5gs.follow_on_request_bit = + registration_type_5gs_t::follow_on_request_bit_type_::options::follow_on_request_pending; + reg_req.registration_type_5gs.registration_type = + registration_type_5gs_t::registration_type_type_::options::initial_registration; + mobile_identity_5gs_t::suci_s& suci = reg_req.mobile_identity_5gs.set_suci(); + suci.supi_format = mobile_identity_5gs_t::suci_s::supi_format_type_::options::imsi; + usim->get_home_mcc_bytes(suci.mcc.data(), suci.mcc.size()); + usim->get_home_mnc_bytes(suci.mnc.data(), suci.mnc.size()); + + suci.scheme_output.resize(5); + usim->get_home_msin_bcd(suci.scheme_output.data(), 5); + logger.info("Requesting IMSI attach (IMSI=%s)", usim->get_imsi_str().c_str()); + + reg_req.ue_security_capability_present = true; + fill_security_caps(reg_req.ue_security_capability); + + if (initial_registration_request_stored.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack registration request"); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + // start T3510 + logger.debug("Starting T3410. Timeout in %d ms.", t3510.duration()); + t3510.run(); + + logger.info("Sending Registration Request"); + if (rrc_nr->is_connected() == true) { + rrc_nr->write_sdu(std::move(pdu)); + } else { + logger.debug("Initiating RRC NR Connection"); + if (rrc_nr->connection_request(nr_establishment_cause_t::mo_Signalling, std::move(pdu)) != SRSRAN_SUCCESS) { + logger.warning("Error starting RRC NR connection"); + return SRSRAN_ERROR; + } + } + + if (has_sec_ctxt) { + set_k_gnb_count(ctxt_base.tx_count); + ctxt_base.tx_count++; + } + + state.set_registered_initiated(); + + return SRSRAN_SUCCESS; +} + +int nas_5g::send_registration_complete() +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + registration_complete_t& reg_comp = nas_msg.set_registration_complete(); + nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack registration complete."); + return SRSRAN_ERROR; + } + + cipher_encrypt(pdu.get()); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[SEQ_5G_OFFSET], + pdu->N_bytes - SEQ_5G_OFFSET, + &pdu->msg[MAC_5G_OFFSET]); + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + logger.info("Sending Registration Complete"); + rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; + return SRSRAN_SUCCESS; +} + +int nas_5g::send_authentication_response(const uint8_t res[16]) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + authentication_response_t& auth_resp = nas_msg.set_authentication_response(); + auth_resp.authentication_response_parameter_present = true; + auth_resp.authentication_response_parameter.res.resize(16); + memcpy(auth_resp.authentication_response_parameter.res.data(), res, 16); + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack authentication response"); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + logger.info("Sending Authentication Response"); + rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; + + return SRSRAN_SUCCESS; +} + +int nas_5g::send_security_mode_reject(const cause_5gmm_t::cause_5gmm_type_::options cause) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + security_mode_reject_t& security_mode_reject = nas_msg.set_security_mode_reject(); + security_mode_reject.cause_5gmm.cause_5gmm = cause; + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack authentication response"); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + logger.info("Sending Authentication Response"); + rrc_nr->write_sdu(std::move(pdu)); + + return SRSRAN_SUCCESS; +} + +int nas_5g::send_security_mode_complete(const srsran::nas_5g::security_mode_command_t& security_mode_command) +{ + uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT; + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + security_mode_complete_t& security_mode_complete = nas_msg.set_security_mode_complete(); + + if (security_mode_command.imeisv_request_present) { + security_mode_complete.imeisv_present = true; + mobile_identity_5gs_t::imeisv_s& imeisv = security_mode_complete.imeisv.set_imeisv(); + usim->get_imei_vec(imeisv.imeisv.data(), 15); + imeisv.imeisv[14] = ue_svn_oct1; + imeisv.imeisv[15] = ue_svn_oct2; + } + // TODO: Save TMSI + registration_request_t& modified_registration_request = initial_registration_request_stored.registration_request(); + modified_registration_request.capability_5gmm_present = true; + modified_registration_request.requested_nssai_present = true; + modified_registration_request.update_type_5gs_present = true; + + s_nssai_t s_nssai{}; + s_nssai.type = s_nssai_t::SST_type_::options::sst; + s_nssai.sst = 1; + modified_registration_request.requested_nssai.s_nssai_list = {s_nssai}; + + modified_registration_request.capability_5gmm.lpp = 0; + modified_registration_request.capability_5gmm.ho_attach = 0; + modified_registration_request.capability_5gmm.s1_mode = 0; + + modified_registration_request.update_type_5gs.ng_ran_rcu.value = + update_type_5gs_t::NG_RAN_RCU_type::options::ue_radio_capability_update_not_needed; + modified_registration_request.update_type_5gs.sms_requested.value = + update_type_5gs_t::SMS_requested_type::options::sms_over_nas_not_supported; + + security_mode_complete.nas_message_container_present = true; + initial_registration_request_stored.pack(security_mode_complete.nas_message_container.nas_message_container); + + nas_msg.hdr.security_header_type = + nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered_with_new_5G_nas_context; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack security mode complete"); + return SRSRAN_ERROR; + } + + cipher_encrypt(pdu.get()); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[SEQ_5G_OFFSET], + pdu->N_bytes - SEQ_5G_OFFSET, + &pdu->msg[MAC_5G_OFFSET]); + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + has_sec_ctxt = true; + logger.info("Sending Security Mode Complete"); + rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; + + return SRSRAN_SUCCESS; +} + +int nas_5g::send_authentication_failure(const cause_5gmm_t::cause_5gmm_type_::options cause, const uint8_t res[16]) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + authentication_failure_t& auth_fail = nas_msg.set_authentication_failure(); + auth_fail.cause_5gmm.cause_5gmm = cause; + + if (cause == cause_5gmm_t::cause_5gmm_type::synch_failure) { + auth_fail.authentication_failure_parameter_present = true; + auth_fail.authentication_failure_parameter.auth_failure.resize(14); + memcpy(auth_fail.authentication_failure_parameter.auth_failure.data(), res, 14); + } + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack authentication failure."); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + logger.info("Sending Authentication Failure"); + rrc_nr->write_sdu(std::move(pdu)); + + return SRSRAN_SUCCESS; +} + +uint32_t nas_5g::allocate_next_proc_trans_id() +{ + uint32_t i = 0; + for (auto pdu_trans_id : pdu_trans_ids) { + i++; + if (pdu_trans_id == false) { + pdu_trans_id = true; + break; + } + } + // TODO if Trans ID exhausted + return i; +} + +void nas_5g::release_proc_trans_id(uint32_t proc_id) +{ + if (proc_id < MAX_TRANS_ID) { + pdu_trans_ids[proc_id] = false; + } + return; +} + +int nas_5g::send_pdu_session_establishment_request(uint32_t transaction_identity, + uint16_t pdu_session_id, + const pdu_session_cfg_t& pdu_session_cfg) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + nas_msg.hdr.pdu_session_identity = pdu_session_id; + nas_msg.hdr.procedure_transaction_identity = transaction_identity; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; + + pdu_session_establishment_request_t& pdu_ses_est_req = nas_msg.set_pdu_session_establishment_request(); + pdu_ses_est_req.integrity_protection_maximum_data_rate.max_data_rate_upip_downlink.value = + integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_downlink_type_::options::full_data_rate; + pdu_ses_est_req.integrity_protection_maximum_data_rate.max_data_rate_upip_uplink.value = + integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_uplink_type_::options::full_data_rate; + + pdu_ses_est_req.pdu_session_type_present = true; + pdu_ses_est_req.pdu_session_type.pdu_session_type_value = + static_cast(pdu_session_cfg.apn_type); + + pdu_ses_est_req.ssc_mode_present = true; + pdu_ses_est_req.ssc_mode.ssc_mode_value = ssc_mode_t::SSC_mode_value_type_::options::ssc_mode_1; + + // TODO set the capability and extended protocol configuration + pdu_ses_est_req.capability_5gsm_present = false; + pdu_ses_est_req.extended_protocol_configuration_options_present = false; + + // Build up the Envelope for the PDU session request + nas_5gs_msg env_nas_msg; + env_nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered; + + // TODO move that seq number setting to the security part + env_nas_msg.hdr.sequence_number = ctxt_base.tx_count; + + ul_nas_transport_t& ul_nas_msg = env_nas_msg.set_ul_nas_transport(); + ul_nas_msg.payload_container_type.payload_container_type.value = + payload_container_type_t::Payload_container_type_type_::options::n1_sm_information; + + // Pack the pdu session est request into the envelope + if (nas_msg.pack(ul_nas_msg.payload_container.payload_container_contents) != SRSASN_SUCCESS) { + logger.error("Failed to pack PDU Session Establishment Request."); + return SRSRAN_ERROR; + } + + ul_nas_msg.pdu_session_id_present = true; + ul_nas_msg.pdu_session_id.pdu_session_identity_2_value = pdu_session_id; + + ul_nas_msg.request_type_present = true; + ul_nas_msg.request_type.request_type_value = request_type_t::Request_type_value_type_::options::initial_request; + + ul_nas_msg.s_nssai_present = true; + ul_nas_msg.s_nssai.type = s_nssai_t::SST_type_::options::sst; + ul_nas_msg.s_nssai.sst = 1; + + ul_nas_msg.dnn_present = true; + ul_nas_msg.dnn.dnn_value.resize(pdu_session_cfg.apn_name.size() + 1); + ul_nas_msg.dnn.dnn_value.data()[0] = static_cast(pdu_session_cfg.apn_name.size()); + + memcpy(ul_nas_msg.dnn.dnn_value.data() + 1, pdu_session_cfg.apn_name.data(), pdu_session_cfg.apn_name.size()); + + if (env_nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack UL NAS transport."); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + cipher_encrypt(pdu.get()); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[SEQ_5G_OFFSET], + pdu->N_bytes - SEQ_5G_OFFSET, + &pdu->msg[MAC_5G_OFFSET]); + + logger.info("Sending PDU Session Establishment Request in UL NAS transport."); + rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; + + return SRSRAN_SUCCESS; +} + +int nas_5g::send_deregistration_request_ue_originating(bool switch_off) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + deregistration_request_ue_originating_t& deregistration_request = nas_msg.set_deregistration_request_ue_originating(); + nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; + + // Note 5.5.2.2.2 : AMF does not send a Deregistration Accept NAS message if De-registration type IE indicates "switch + // off" + if (switch_off) { + deregistration_request.de_registration_type.switch_off.value = + de_registration_type_t::switch_off_type_::options::switch_off; + state.set_deregistered(mm5g_state_t::deregistered_substate_t::null); + } else { + deregistration_request.de_registration_type.switch_off.value = + de_registration_type_t::switch_off_type_::options::normal_de_registration; + // In this case we need to wait for the response by the core + state.set_deregistered_initiated(); + } + + mobile_identity_5gs_t::suci_s& suci = deregistration_request.mobile_identity_5gs.set_suci(); + suci.supi_format = mobile_identity_5gs_t::suci_s::supi_format_type_::options::imsi; + usim->get_home_mcc_bytes(suci.mcc.data(), suci.mcc.size()); + usim->get_home_mnc_bytes(suci.mnc.data(), suci.mnc.size()); + suci.scheme_output.resize(5); + + deregistration_request.ng_ksi.nas_key_set_identifier.value = + key_set_identifier_t::nas_key_set_identifier_type_::options::no_key_is_available_or_reserved; + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack Deregistration Request (UE Originating)."); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + logger.info("Sending Deregistration Request (UE Originating)"); + cipher_encrypt(pdu.get()); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[SEQ_5G_OFFSET], + pdu->N_bytes - SEQ_5G_OFFSET, + &pdu->msg[MAC_5G_OFFSET]); + + rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; + reset_pdu_sessions(); + // TODO: Consider reworking ctxt / 5G ctxt release + + return SRSASN_SUCCESS; +} + +int nas_5g::send_identity_response(srsran::nas_5g::identity_type_5gs_t::identity_types_::options identity_type) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + identity_response_t& identity_response = nas_msg.set_identity_response(); + nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; + + switch (identity_type) { + case (identity_type_5gs_t::identity_types_::suci): { + srsran::nas_5g::mobile_identity_5gs_t::suci_s& suci = identity_response.mobile_identity.set_suci(); + suci.supi_format = mobile_identity_5gs_t::suci_s::supi_format_type_::options::imsi; + usim->get_home_mcc_bytes(suci.mcc.data(), suci.mcc.size()); + usim->get_home_mnc_bytes(suci.mnc.data(), suci.mnc.size()); + suci.scheme_output.resize(5); + usim->get_home_msin_bcd(suci.scheme_output.data(), 5); + } break; + case (identity_type_5gs_t::identity_types_::guti_5g): { + srsran::nas_5g::mobile_identity_5gs_t::guti_5g_s& guti = identity_response.mobile_identity.set_guti_5g(); + guti = guti_5g; + } break; + case (identity_type_5gs_t::identity_types_::imei): { + srsran::nas_5g::mobile_identity_5gs_t::imei_s& imei = identity_response.mobile_identity.set_imei(); + usim->get_imei_vec(imei.imei.data(), 15); + } break; + case (identity_type_5gs_t::identity_types_::imeisv): { + srsran::nas_5g::mobile_identity_5gs_t::imeisv_s& imeisv = identity_response.mobile_identity.set_imeisv(); + usim->get_imei_vec(imeisv.imeisv.data(), 15); + imeisv.imeisv[14] = ue_svn_oct1; + imeisv.imeisv[15] = ue_svn_oct2; + } break; + default: + logger.warning("Unhandled identity type for identity response"); + return SRSRAN_ERROR; + } + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack Identity Response."); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + cipher_encrypt(pdu.get()); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[SEQ_5G_OFFSET], + pdu->N_bytes - SEQ_5G_OFFSET, + &pdu->msg[MAC_5G_OFFSET]); + + logger.info("Sending Identity Response"); + rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; + + return SRSRAN_SUCCESS; +} + +int nas_5g::send_configuration_update_complete() +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + nas_5gs_msg nas_msg; + configuration_update_complete_t& config_update_complete = nas_msg.set_configuration_update_complete(); + nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack Identity Response."); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + cipher_encrypt(pdu.get()); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[SEQ_5G_OFFSET], + pdu->N_bytes - SEQ_5G_OFFSET, + &pdu->msg[MAC_5G_OFFSET]); + + logger.info("Sending Configuration Update Complete"); + rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; + return SRSRAN_SUCCESS; +} + +// Message handler +int nas_5g::handle_registration_accept(registration_accept_t& registration_accept) +{ + ctxt_base.rx_count++; + if (state.get_state() != mm5g_state_t::state_t::registered_initiated) { + logger.warning("Not compatibale with current state %s", state.get_full_state_text()); + return SRSRAN_ERROR; + } + + bool send_reg_complete = false; + logger.info("Handling Registration Accept"); + if (registration_accept.guti_5g_present) { + guti_5g = registration_accept.guti_5g.guti_5g(); + send_reg_complete = true; + } + + // TODO: reset counters and everything what is needed by the specification + t3521.set(registration_accept.t3512_value.timer_value); + registration_proc.run(); + state.set_registered(mm5g_state_t::registered_substate_t::normal_service); + + if (send_reg_complete == true) { + send_registration_complete(); + } + // TODO: use the state machine to trigger that transition + trigger_pdu_session_est(); + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_registration_reject(registration_reject_t& registration_reject) +{ + logger.info("Handling Registration Reject"); + has_sec_ctxt = false; + ctxt_base.rx_count++; + state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); + + switch (registration_reject.cause_5gmm.cause_5gmm.value) { + case (cause_5gmm_t::cause_5gmm_type_::options::illegal_ue): + logger.error("Registration Reject: Illegal UE"); + break; + case (cause_5gmm_t::cause_5gmm_type_::options::plmn_not_allowed): + logger.error("Registration Reject: PLMN not allowed"); + break; + case (cause_5gmm_t::cause_5gmm_type_::options::ue_security_capabilities_mismatch): + logger.error("Registration Reject: UE security capabilities mismatch"); + break; + case (cause_5gmm_t::cause_5gmm_type_::options::mac_failure): + logger.error("Registration Reject: MAC Failure"); + break; + case (cause_5gmm_t::cause_5gmm_type_::options::maximum_number_of_pdu_sessions_reached_): + logger.error("Registration Reject: Maximum number of pdu sessions reached"); + break; + default: + logger.error("Unhandled Registration Reject cause"); + } + + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_authentication_request(authentication_request_t& authentication_request) +{ + logger.info("Handling Authentication Request"); + ctxt_base.rx_count++; + // Generate authentication response using RAND, AUTN & KSI-ASME + plmn_id_t plmn_id; + usim->get_home_plmn_id(&plmn_id); + + if (authentication_request.authentication_parameter_rand_present == false) { + logger.error("authentication_parameter_rand_present is not present"); + return SRSRAN_ERROR; + } + + if (authentication_request.authentication_parameter_autn_present == false) { + logger.error("authentication_parameter_autn_present is not present"); + return SRSRAN_ERROR; + } + + initial_sec_command = true; + uint8_t res_star[16]; + + logger.info(authentication_request.authentication_parameter_rand.rand.data(), + authentication_request.authentication_parameter_rand.rand.size(), + "Authentication request RAND"); + + logger.info(authentication_request.authentication_parameter_autn.autn.data(), + authentication_request.authentication_parameter_rand.rand.size(), + "Authentication request AUTN"); + + logger.info("Serving network name %s", plmn_id.to_serving_network_name_string().c_str()); + auth_result_t auth_result = + usim->generate_authentication_response_5g(authentication_request.authentication_parameter_rand.rand.data(), + authentication_request.authentication_parameter_autn.autn.data(), + plmn_id.to_serving_network_name_string().c_str(), + authentication_request.abba.abba_contents.data(), + authentication_request.abba.abba_contents.size(), + res_star, + ctxt_5g.k_amf); + + logger.info(ctxt_5g.k_amf, 32, "Generated k_amf:"); + + if (auth_result == AUTH_OK) { + logger.info("Network authentication successful"); + send_authentication_response(res_star); + logger.info(res_star, 16, "Generated res_star (%d):", 16); + + } else if (auth_result == AUTH_FAILED) { + logger.error("Network authentication failure"); + send_authentication_failure(cause_5gmm_t::cause_5gmm_type::mac_failure, res_star); + } else if (auth_result == AUTH_SYNCH_FAILURE) { + logger.error("Network authentication synchronization failure"); + send_authentication_failure(cause_5gmm_t::cause_5gmm_type::synch_failure, res_star); + } else { + logger.error("Unhandled authentication failure cause"); + } + + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_authentication_reject(srsran::nas_5g::authentication_reject_t& authentication_reject) +{ + logger.info("Handling Authentication Reject"); + has_sec_ctxt = false; + ctxt_base.rx_count++; + state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_identity_request(identity_request_t& identity_request) +{ + logger.info("Handling Identity Request"); + ctxt_base.rx_count++; + send_identity_response(identity_request.identity_type.type_of_identity.value); + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_service_accept(srsran::nas_5g::service_accept_t& service_accept) +{ + logger.info("Handling Service Accept"); + ctxt_base.rx_count++; + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_service_reject(srsran::nas_5g::service_reject_t& service_reject) +{ + logger.info("Handling Service Reject"); + has_sec_ctxt = false; + ctxt_base.rx_count++; + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_security_mode_command(security_mode_command_t& security_mode_command, + srsran::unique_byte_buffer_t pdu) +{ + logger.info("Handling Security Mode Command"); + ctxt_base.cipher_algo = + (CIPHERING_ALGORITHM_ID_ENUM)security_mode_command.selected_nas_security_algorithms.ciphering_algorithm.value; + ctxt_base.integ_algo = + (INTEGRITY_ALGORITHM_ID_ENUM) + security_mode_command.selected_nas_security_algorithms.integrity_protection_algorithm.value; + + // Check replayed ue security capabilities + if (!check_replayed_ue_security_capabilities(security_mode_command.replayed_ue_security_capabilities)) { + logger.warning("Sending Security Mode Reject due to security capabilities mismatch"); + send_security_mode_reject(cause_5gmm_t::cause_5gmm_type_::ue_security_capabilities_mismatch); + return SRSRAN_ERROR; + } + + if (initial_sec_command) { + set_k_gnb_count(0); + ctxt_base.tx_count = 0; + initial_sec_command = false; + } + + // Generate NAS keys + logger.debug(ctxt_5g.k_amf, 32, "K AMF"); + logger.debug("cipher_algo %d, integ_algo %d", ctxt_base.cipher_algo, ctxt_base.integ_algo); + + usim->generate_nas_keys_5g( + ctxt_5g.k_amf, ctxt_base.k_nas_enc, ctxt_base.k_nas_int, ctxt_base.cipher_algo, ctxt_base.integ_algo); + logger.info(ctxt_base.k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + logger.info(ctxt_base.k_nas_int, 32, "NAS integrity key - k_nas_int"); + + logger.debug("Generating integrity check. integ_algo:%d, count_dl:%d", ctxt_base.integ_algo, ctxt_base.rx_count); + + if (not integrity_check(pdu.get())) { + logger.warning("Sending Security Mode Reject due to integrity check failure"); + send_security_mode_reject(cause_5gmm_t::cause_5gmm_type_::options::mac_failure); + return SRSRAN_ERROR; + } + + send_security_mode_complete(security_mode_command); + ctxt_base.rx_count++; + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_deregistration_accept_ue_terminated( + deregistration_accept_ue_terminated_t& deregistration_accept_ue_terminated) +{ + logger.info("Handling Deregistration Accept UE Terminated"); + ctxt_base.rx_count++; + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_deregistration_request_ue_terminated( + deregistration_request_ue_terminated_t& deregistration_request_ue_terminated) +{ + logger.info("Handling Deregistration Request UE Terminated"); + ctxt_base.rx_count++; + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_dl_nas_transport(srsran::nas_5g::dl_nas_transport_t& dl_nas_transport) +{ + logger.info("Handling DL NAS transport"); + ctxt_base.rx_count++; + switch (dl_nas_transport.payload_container_type.payload_container_type) { + case payload_container_type_t::Payload_container_type_type_::options::n1_sm_information: + return handle_n1_sm_information(dl_nas_transport.payload_container.payload_container_contents); + break; + default: + logger.warning("Not handling payload container %x", + dl_nas_transport.payload_container_type.payload_container_type.value); + break; + } + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_n1_sm_information(std::vector payload_container_contents) +{ + logger.info(payload_container_contents.data(), + payload_container_contents.size(), + "Payload contents (length %d)", + payload_container_contents.size()); + + nas_5gs_msg nas_msg; + nas_msg.unpack(payload_container_contents); + + switch (nas_msg.hdr.message_type) { + case msg_opts::options::pdu_session_establishment_accept: + pdu_session_establishment_proc.trigger(nas_msg.pdu_session_establishment_accept()); + break; + case msg_opts::options::pdu_session_establishment_reject: + pdu_session_establishment_proc.trigger(nas_msg.pdu_session_establishment_reject()); + break; + default: + logger.error( + "Not handling NAS message type: %s (0x%02x)", nas_msg.hdr.message_type.to_string(), nas_msg.hdr.message_type); + break; + } + return SRSRAN_SUCCESS; +} + +int nas_5g::handle_deregistration_accept_ue_originating( + srsran::nas_5g::deregistration_accept_ue_originating_t& deregistration_accept_ue_originating) +{ + logger.info("Received Deregistration Accept (UE Originating)"); + ctxt_base.rx_count++; + if (state.get_state() != mm5g_state_t::state_t::deregistered_initiated) { + logger.warning("Received deregistration accept while not in deregistered initiated state"); + } + + state.set_deregistered(mm5g_state_t::deregistered_substate_t::null); + return SRSASN_SUCCESS; +} + +int nas_5g::handle_configuration_update_command( + srsran::nas_5g::configuration_update_command_t& configuration_update_command) +{ + logger.info("Handling Configuration Update Command"); + ctxt_base.rx_count++; + send_configuration_update_complete(); + return SRSRAN_SUCCESS; +} + +/******************************************************************************* + * NAS Timers + ******************************************************************************/ +void nas_5g::timer_expired(uint32_t timeout_id) +{ + // TODO +} + +/******************************************************************************* + * UE Stack & RRC Interface + ******************************************************************************/ +bool nas_5g::is_registered() +{ + return state.get_state() == mm5g_state_t::state_t::registered; +} + +int nas_5g::switch_on() +{ + logger.info("Switching on"); + state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); + return SRSRAN_SUCCESS; +} + +int nas_5g::switch_off() +{ + logger.info("Switching off"); + send_deregistration_request_ue_originating(true); + return SRSRAN_SUCCESS; +} + +int nas_5g::enable_data() +{ + logger.info("Enabling data services"); + return switch_on(); +} + +int nas_5g::disable_data() +{ + logger.info("Disabling data services"); + // TODO + return SRSRAN_SUCCESS; +} + +int nas_5g::start_service_request() +{ + logger.info("Service Request"); + // TODO + return SRSRAN_SUCCESS; +} + +int nas_5g::reset_pdu_sessions() +{ + for (auto pdu_session : pdu_sessions) { + pdu_session.established = false; + pdu_session.pdu_session_id = 0; + } + return SRSRAN_SUCCESS; +} + +void nas_5g::get_metrics(nas_5g_metrics_t& metrics) +{ + metrics.nof_active_pdu_sessions = num_of_est_pdu_sessions(); + metrics.state = state.get_state(); +} + +int nas_5g::get_k_amf(as_key_t& k_amf) +{ + if (not has_sec_ctxt) { + logger.error("K_amf requested before a valid NAS security context was established"); + return SRSRAN_ERROR; + } + + std::copy(std::begin(ctxt_5g.k_amf), std::end(ctxt_5g.k_amf), k_amf.begin()); + return SRSRAN_SUCCESS; +} + +uint32_t nas_5g::get_ul_nas_count() +{ + return ctxt_5g.k_gnb_count; +} + +void nas_5g::set_k_gnb_count(uint32_t count) +{ + ctxt_5g.k_gnb_count = count; +} + +/******************************************************************************* + * Helpers + ******************************************************************************/ + +void nas_5g::fill_security_caps(srsran::nas_5g::ue_security_capability_t& sec_caps) +{ + if (ia5g_caps[0] == true) { + sec_caps.ia0_5g_supported = true; + } + if (ia5g_caps[1] == true) { + sec_caps.ia1_128_5g_supported = true; + } + if (ia5g_caps[2] == true) { + sec_caps.ia2_128_5g_supported = true; + } + if (ia5g_caps[3] == true) { + sec_caps.ia3_128_5g_supported = true; + } + if (ia5g_caps[4] == true) { + sec_caps.ia4_5g_supported = true; + } + if (ia5g_caps[5] == true) { + sec_caps.ia5_5g_supported = true; + } + if (ia5g_caps[6] == true) { + sec_caps.ia6_5g_supported = true; + } + if (ia5g_caps[7] == true) { + sec_caps.ia7_5g_supported = true; + } + + if (ea5g_caps[0] == true) { + sec_caps.ea0_5g_supported = true; + } + if (ea5g_caps[1] == true) { + sec_caps.ea1_128_5g_supported = true; + } + if (ea5g_caps[2] == true) { + sec_caps.ea2_128_5g_supported = true; + } + if (ea5g_caps[3] == true) { + sec_caps.ea3_128_5g_supported = true; + } + if (ea5g_caps[4] == true) { + sec_caps.ea4_5g_supported = true; + } + if (ea5g_caps[5] == true) { + sec_caps.ea5_5g_supported = true; + } + if (ea5g_caps[6] == true) { + sec_caps.ea6_5g_supported = true; + } + if (ea5g_caps[7] == true) { + sec_caps.ea7_5g_supported = true; + } +} + +bool nas_5g::check_replayed_ue_security_capabilities(srsran::nas_5g::ue_security_capability_t& caps) +{ + if (caps.ia0_5g_supported != ia5g_caps[0] || caps.ea0_5g_supported != ea5g_caps[0]) { + return false; + } + if (caps.ia1_128_5g_supported != ia5g_caps[1] || caps.ea1_128_5g_supported != ea5g_caps[1]) { + return false; + } + if (caps.ia2_128_5g_supported != ia5g_caps[2] || caps.ea2_128_5g_supported != ea5g_caps[2]) { + return false; + } + if (caps.ia3_128_5g_supported != ia5g_caps[3] || caps.ea3_128_5g_supported != ea5g_caps[3]) { + return false; + } + if (caps.ia4_5g_supported != ia5g_caps[4] || caps.ea4_5g_supported != ea5g_caps[4]) { + return false; + } + if (caps.ia5_5g_supported != ia5g_caps[5] || caps.ea5_5g_supported != ea5g_caps[5]) { + return false; + } + if (caps.ia6_5g_supported != ia5g_caps[6] || caps.ea6_5g_supported != ea5g_caps[6]) { + return false; + } + if (caps.ia7_5g_supported != ia5g_caps[7] || caps.ea7_5g_supported != ea5g_caps[7]) { + return false; + } + + return true; +} + +/******************************************************************************* + * Helpers for Session Management + ******************************************************************************/ + +int nas_5g::trigger_pdu_session_est() +{ + if (unestablished_pdu_sessions() == true) { + pdu_session_cfg_t pdu_session_cfg; + uint16_t pdu_session_id; + get_unestablished_pdu_session(pdu_session_id, pdu_session_cfg); + pdu_session_establishment_proc.launch(pdu_session_id, pdu_session_cfg); + } + return SRSRAN_SUCCESS; +} + +int nas_5g::init_pdu_sessions(std::vector pdu_session_cfgs) +{ + uint16_t i = 0; + for (auto pdu_session_cfg : pdu_session_cfgs) { + pdu_sessions[i].configured = true; + pdu_sessions[i].pdu_session_id = i + 1; + pdu_sessions[i].pdu_session_cfg = pdu_session_cfg; + } + return SRSRAN_SUCCESS; +} + +uint32_t nas_5g::num_of_est_pdu_sessions() +{ + uint32_t i = 0; + for (auto pdu_session : pdu_sessions) { + if (pdu_session.established == true) { + i++; + } + } + return i; +} + +int nas_5g::configure_pdu_session(uint16_t pdu_session_id) +{ + for (auto pdu_session : pdu_sessions) { + if (pdu_session.pdu_session_id == pdu_session_id) { + pdu_session.established = true; + } + } + return SRSRAN_SUCCESS; +} + +bool nas_5g::unestablished_pdu_sessions() +{ + for (auto pdu_session : pdu_sessions) { + if (pdu_session.configured == true && pdu_session.established == false) { + return true; + } + } + return false; +} + +int nas_5g::get_unestablished_pdu_session(uint16_t& pdu_session_id, pdu_session_cfg_t& pdu_session_cfg) +{ + for (auto pdu_session : pdu_sessions) { + if (pdu_session.configured == true && pdu_session.established == false) { + pdu_session_id = pdu_session.pdu_session_id; + pdu_session_cfg = pdu_session.pdu_session_cfg; + } + } + return SRSRAN_SUCCESS; +} + +int nas_5g::add_pdu_session(uint16_t pdu_session_id, + uint16_t pdu_session_type, + srsran::nas_5g::pdu_address_t pdu_address) +{ + char* err_str = nullptr; + + // Copy IPv4 + uint32_t ip_addr = 0; + + ip_addr |= pdu_address.ipv4.data()[0] << 24u; + ip_addr |= pdu_address.ipv4.data()[1] << 16u; + ip_addr |= pdu_address.ipv4.data()[2] << 8u; + ip_addr |= pdu_address.ipv4.data()[3]; + + // Copy IPv6 + uint8_t ipv6_if_id[8] = {}; + memcpy(ipv6_if_id, pdu_address.ipv6.data(), 8); + + if (!(pdu_session_type == LIBLTE_MME_PDN_TYPE_IPV4V6 || pdu_session_type == LIBLTE_MME_PDN_TYPE_IPV4 || + pdu_session_type == LIBLTE_MME_PDN_TYPE_IPV6)) { + logger.warning("PDU session typed expected to be of IPV4 or IPV6 or IPV4V6"); + return SRSRAN_ERROR; + } + + if (gw->setup_if_addr(pdu_session_id, pdu_session_type, ip_addr, ipv6_if_id, err_str)) { + logger.error("%s - %s", gw_setup_failure_str.c_str(), err_str ? err_str : ""); + srsran::console("%s\n", gw_setup_failure_str.c_str()); + return SRSRAN_ERROR; + } + + if (pdu_session_type == LIBLTE_MME_PDN_TYPE_IPV4V6 || pdu_session_type == LIBLTE_MME_PDN_TYPE_IPV4) { + logger.info("PDU Session Establishment successful. IP: %u.%u.%u.%u", + pdu_address.ipv4.data()[0], + pdu_address.ipv4.data()[1], + pdu_address.ipv4.data()[2], + pdu_address.ipv4.data()[3]); + + srsran::console("PDU Session Establishment successful. IP: %u.%u.%u.%u\n", + pdu_address.ipv4.data()[0], + pdu_address.ipv4.data()[1], + pdu_address.ipv4.data()[2], + pdu_address.ipv4.data()[3]); + } + + if (pdu_session_type == LIBLTE_MME_PDN_TYPE_IPV4V6 || pdu_session_type == LIBLTE_MME_PDN_TYPE_IPV6) { + logger.info("PDU Session Establishment successful. IPv6 interface id: %02x%02x:%02x%02x:%02x%02x:%02x%02x", + pdu_address.ipv6.data()[0], + pdu_address.ipv6.data()[1], + pdu_address.ipv6.data()[2], + pdu_address.ipv6.data()[3], + pdu_address.ipv6.data()[4], + pdu_address.ipv6.data()[5], + pdu_address.ipv6.data()[6], + pdu_address.ipv6.data()[7]); + + srsran::console("PDU Session Establishment successful. IPv6 interface id: %02x%02x:%02x%02x:%02x%02x:%02x%02x\n", + pdu_address.ipv6.data()[0], + pdu_address.ipv6.data()[1], + pdu_address.ipv6.data()[2], + pdu_address.ipv6.data()[3], + pdu_address.ipv6.data()[4], + pdu_address.ipv6.data()[5], + pdu_address.ipv6.data()[6], + pdu_address.ipv6.data()[7]); + } + + return SRSRAN_SUCCESS; +} + +} // namespace srsue diff --git a/srsue/src/stack/upper/nas_5g_procedures.cc b/srsue/src/stack/upper/nas_5g_procedures.cc new file mode 100644 index 000000000..0d388a18f --- /dev/null +++ b/srsue/src/stack/upper/nas_5g_procedures.cc @@ -0,0 +1,101 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/stack/upper/nas_5g_procedures.h" +#include "srsran/common/standard_streams.h" +#include +#include +#include +#include + +using namespace srsran; + +namespace srsue { + +nas_5g::registration_procedure::registration_procedure(nas_5g_interface_procedures* parent_nas_) : + parent_nas(parent_nas_) +{} + +srsran::proc_outcome_t nas_5g::registration_procedure::init() +{ + parent_nas->send_registration_request(); + return srsran::proc_outcome_t::yield; +} +srsran::proc_outcome_t nas_5g::registration_procedure::step() +{ + return srsran::proc_outcome_t::success; +} + +// PDU Sessions Establishment Procedure +nas_5g::pdu_session_establishment_procedure::pdu_session_establishment_procedure( + nas_5g_interface_procedures* parent_nas_, + srslog::basic_logger& logger_) : + logger(logger_), parent_nas(parent_nas_) +{} + +srsran::proc_outcome_t nas_5g::pdu_session_establishment_procedure::init(const uint16_t pdu_session_id_, + const pdu_session_cfg_t& pdu_session_cfg) +{ + // Get PDU transaction identity + transaction_identity = parent_nas->allocate_next_proc_trans_id(); + pdu_session_id = pdu_session_id_; + parent_nas->send_pdu_session_establishment_request(transaction_identity, pdu_session_id, pdu_session_cfg); + + return srsran::proc_outcome_t::yield; +} + +srsran::proc_outcome_t nas_5g::pdu_session_establishment_procedure::react( + const srsran::nas_5g::pdu_session_establishment_accept_t& pdu_session_est_accept) +{ + // TODO check the pdu session values + if (pdu_session_est_accept.dnn_present == false) { + logger.warning("Expected DNN in PDU session establishment accept"); + return proc_outcome_t::error; + } + if (pdu_session_est_accept.pdu_address_present == false) { + logger.warning("Expected PDU Address in PDU session establishment accept"); + return proc_outcome_t::error; + } + if (parent_nas->add_pdu_session(pdu_session_id, + pdu_session_est_accept.selected_pdu_session_type.pdu_session_type_value, + pdu_session_est_accept.pdu_address) != SRSRAN_SUCCESS) { + logger.warning("Adding PDU session failed\n"); + return srsran::proc_outcome_t::error; + } + return srsran::proc_outcome_t::success; +} + +srsran::proc_outcome_t nas_5g::pdu_session_establishment_procedure::react( + const srsran::nas_5g::pdu_session_establishment_reject_t& session_est_reject) +{ + logger.error("PDU Session Establishment Reject with cause: %s", + session_est_reject.cause_5gsm.cause_value.to_string()); + srsran::console("PDU Session Establishment Reject with cause: %s\n", + session_est_reject.cause_5gsm.cause_value.to_string()); + return srsran::proc_outcome_t::error; +} + +srsran::proc_outcome_t nas_5g::pdu_session_establishment_procedure::step() +{ + return srsran::proc_outcome_t::success; +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/upper/nas_5gmm_state.cc b/srsue/src/stack/upper/nas_5gmm_state.cc new file mode 100644 index 000000000..c29a48cc1 --- /dev/null +++ b/srsue/src/stack/upper/nas_5gmm_state.cc @@ -0,0 +1,157 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ +#include "srsue/hdr/stack/upper/nas_5gmm_state.h" + +namespace srsue { + +// FSM setters +void mm5g_state_t::set_null() +{ + state = state_t::null; + deregistered_substate = deregistered_substate_t::null; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_deregistered(deregistered_substate_t substate) +{ + state = state_t::deregistered; + deregistered_substate = substate; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_deregistered_initiated() +{ + state = state_t::deregistered_initiated; + deregistered_substate = deregistered_substate_t::null; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_registered(registered_substate_t substate) +{ + state = state_t::registered; + deregistered_substate = deregistered_substate_t::null; + registered_substate = substate; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_registered_initiated() +{ + state = state_t::registered_initiated; + deregistered_substate = deregistered_substate_t::null; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +void mm5g_state_t::set_service_request_initiated() +{ + state = state_t::service_request_initiated; + deregistered_substate = deregistered_substate_t::null; + registered_substate = registered_substate_t::null; + logger.debug("Changed to mm5g state: %s", get_full_state_text().c_str()); +} + +const std::string mm5g_state_t::get_full_state_text() +{ + if (state == state_t::deregistered) { + return mm5g_state_text(state) + std::string(", with substate ") + + mm5g_deregistered_substate_text(deregistered_substate); + } else if (state == state_t::registered) { + return mm5g_state_text(state) + std::string(", with substate ") + + mm5g_registered_substate_text(registered_substate); + } + + return mm5g_state_text(state); +} + +/* + * Logging helper functions + */ +const char* mm5g_state_text(mm5g_state_t::state_t type) +{ + switch (type) { + case mm5g_state_t::state_t::null: + return "NULL"; + case mm5g_state_t::state_t::deregistered: + return "DEREGISTERED"; + case mm5g_state_t::state_t::registered_initiated: + return "REGISTERED-INITIATED"; + case mm5g_state_t::state_t::registered: + return "REGISTERED"; + case mm5g_state_t::state_t::deregistered_initiated: + return "DEREGISTERED-INITIATED"; + case mm5g_state_t::state_t::service_request_initiated: + return "SERVICE-REQUEST-INITIATED"; + } + return "INVALID"; +} + +const char* mm5g_deregistered_substate_text(mm5g_state_t::deregistered_substate_t type) +{ + switch (type) { + case mm5g_state_t::deregistered_substate_t::null: + return "NULL"; + case mm5g_state_t::deregistered_substate_t::normal_service: + return "NORMAL-SERVICE"; + case mm5g_state_t::deregistered_substate_t::limited_service: + return "LIMITED-SERVICE"; + case mm5g_state_t::deregistered_substate_t::attempting_to_registration: + return "ATTEMPTING-TO-REGISTRATION"; + case mm5g_state_t::deregistered_substate_t::plmn_search: + return "PLMN-SEARCH"; + case mm5g_state_t::deregistered_substate_t::no_supi: + return "NO-SUPI"; + case mm5g_state_t::deregistered_substate_t::no_cell_available: + return "NO-CELL-AVAILABLE"; + case mm5g_state_t::deregistered_substate_t::e_call_inactive: + return "eCALL-INACTIVE"; + case mm5g_state_t::deregistered_substate_t::initial_registration_needed: + return "ATTACH-NEEDED"; + } + return "INVALID"; +} + +const char* mm5g_registered_substate_text(mm5g_state_t::registered_substate_t type) +{ + switch (type) { + case mm5g_state_t::registered_substate_t::null: + return "NULL"; + case mm5g_state_t::registered_substate_t::normal_service: + return "NORMAL-SERVICE"; + case mm5g_state_t::registered_substate_t::non_allowed_service: + return "NON-ALLOWED-SERVICE"; + case mm5g_state_t::registered_substate_t::attempting_registration_update: + return "ATTEMPTING-REGISTRATION-UPDATE"; + case mm5g_state_t::registered_substate_t::limited_service: + return "LIMITED-SERVICE"; + case mm5g_state_t::registered_substate_t::plmn_search: + return "PLMN-SEARCH"; + case mm5g_state_t::registered_substate_t::no_cell_available: + return "NO-CELL-AVAILABLE"; + case mm5g_state_t::registered_substate_t::update_needed: + return "UPDATE-NEEDED"; + } + return "INVALID"; +} + +} // namespace srsue diff --git a/srsue/src/stack/upper/nas_base.cc b/srsue/src/stack/upper/nas_base.cc new file mode 100644 index 000000000..0cb487316 --- /dev/null +++ b/srsue/src/stack/upper/nas_base.cc @@ -0,0 +1,235 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/stack/upper/nas_base.h" + +using namespace srsran; +namespace srsue { +nas_base::nas_base(srslog::basic_logger& logger_, uint32_t mac_offset_, uint32_t seq_offset_, uint32_t bearer_id_) : + logger(logger_), mac_offset(mac_offset_), seq_offset(seq_offset_), bearer_id(bearer_id_) +{} + +int nas_base::parse_security_algorithm_list(std::string algorithm_string, bool* algorithm_caps) +{ + // parse and sanity check security algorithm list + std::vector cap_list; + srsran::string_parse_list(algorithm_string, ',', cap_list); + if (cap_list.empty()) { + logger.error("Empty security list. Select at least one security algorithm."); + return SRSRAN_ERROR; + } + for (std::vector::const_iterator it = cap_list.begin(); it != cap_list.end(); ++it) { + if (*it < 4) { + algorithm_caps[*it] = true; + } else { + logger.error("EEA/EIA/5G-EA/5G-IA %d is not a valid algorithm.", *it); + return SRSRAN_ERROR; + } + } + return SRSRAN_SUCCESS; +} + +/******************************************************************************* + * Security + ******************************************************************************/ + +void nas_base::integrity_generate(uint8_t* key_128, + uint32_t count, + uint8_t direction, + uint8_t* msg, + uint32_t msg_len, + uint8_t* mac) +{ + switch (ctxt_base.integ_algo) { + case INTEGRITY_ALGORITHM_ID_EIA0: + break; + case INTEGRITY_ALGORITHM_ID_128_EIA1: + security_128_eia1(key_128, count, bearer_id, direction, msg, msg_len, mac); + break; + case INTEGRITY_ALGORITHM_ID_128_EIA2: + security_128_eia2(key_128, count, bearer_id, direction, msg, msg_len, mac); + break; + case INTEGRITY_ALGORITHM_ID_128_EIA3: + security_128_eia3(key_128, count, bearer_id, direction, msg, msg_len, mac); + break; + default: + break; + } +} + +// This function depends to a valid k_nas_int. +// This key is generated in the security mode command. +bool nas_base::integrity_check(byte_buffer_t* pdu) +{ + if (pdu == nullptr) { + logger.error("Invalid PDU"); + return false; + } + + if (pdu->N_bytes > seq_offset) { + uint8_t exp_mac[4] = {0}; + uint8_t* mac = &pdu->msg[mac_offset]; + + // generate expected MAC + uint32_t count_est = (ctxt_base.rx_count & 0x00FFFF00u) | pdu->msg[seq_offset]; + integrity_generate(&ctxt_base.k_nas_int[16], + count_est, + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[seq_offset], + pdu->N_bytes - seq_offset, + &exp_mac[0]); + + // Check if expected mac equals the sent mac + for (int i = 0; i < 4; i++) { + if (exp_mac[i] != mac[i]) { + logger.warning("Integrity check failure. Local: count=%d, [%02x %02x %02x %02x], " + "Received: count=%d, [%02x %02x %02x %02x]", + count_est, + exp_mac[0], + exp_mac[1], + exp_mac[2], + exp_mac[3], + pdu->msg[seq_offset], + mac[0], + mac[1], + mac[2], + mac[3]); + return false; + } + } + logger.info("Integrity check ok. Local: count=%d, Received: count=%d [%02x %02x %02x %02x]", + count_est, + pdu->msg[seq_offset], + mac[0], + mac[1], + mac[2], + mac[3]); + + // Updated local count (according to TS 24.301 Sec. 4.4.3.3) + if (count_est != ctxt_base.rx_count) { + logger.info("Update local count to estimated count %d", count_est); + ctxt_base.rx_count = count_est; + } + return true; + } else { + logger.error("Invalid integrity check PDU size (%d)", pdu->N_bytes); + return false; + } +} + +void nas_base::cipher_encrypt(byte_buffer_t* pdu) +{ + byte_buffer_t pdu_tmp; + + if (ctxt_base.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { + logger.debug("Encrypting PDU. count=%d", ctxt_base.tx_count); + } + + switch (ctxt_base.cipher_algo) { + case CIPHERING_ALGORITHM_ID_EEA0: + break; + case CIPHERING_ALGORITHM_ID_128_EEA1: + security_128_eea1(&ctxt_base.k_nas_enc[16], + ctxt_base.tx_count, + bearer_id, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[seq_offset + 1], + pdu->N_bytes - seq_offset + 1, + &pdu_tmp.msg[seq_offset + 1]); + memcpy(&pdu->msg[seq_offset + 1], &pdu_tmp.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); + break; + case CIPHERING_ALGORITHM_ID_128_EEA2: + security_128_eea2(&ctxt_base.k_nas_enc[16], + ctxt_base.tx_count, + bearer_id, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[seq_offset + 1], + pdu->N_bytes - seq_offset + 1, + &pdu_tmp.msg[seq_offset + 1]); + memcpy(&pdu->msg[seq_offset + 1], &pdu_tmp.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); + break; + case CIPHERING_ALGORITHM_ID_128_EEA3: + security_128_eea3(&ctxt_base.k_nas_enc[16], + ctxt_base.tx_count, + bearer_id, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[seq_offset + 1], + pdu->N_bytes - seq_offset + 1, + &pdu_tmp.msg[seq_offset + 1]); + memcpy(&pdu->msg[seq_offset + 1], &pdu_tmp.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); + break; + default: + logger.error("Ciphering algorithm not known"); + break; + } +} + +void nas_base::cipher_decrypt(byte_buffer_t* pdu) +{ + byte_buffer_t tmp_pdu; + + uint32_t count_est = (ctxt_base.rx_count & 0x00FFFF00u) | pdu->msg[5]; + if (ctxt_base.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { + logger.debug("Decrypting PDU. Local: count=%d, Received: count=%d", ctxt_base.rx_count, count_est); + } + + switch (ctxt_base.cipher_algo) { + case CIPHERING_ALGORITHM_ID_EEA0: + break; + case CIPHERING_ALGORITHM_ID_128_EEA1: + security_128_eea1(&ctxt_base.k_nas_enc[16], + count_est, + bearer_id, + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[seq_offset + 1], + pdu->N_bytes - seq_offset + 1, + &tmp_pdu.msg[seq_offset + 1]); + memcpy(&pdu->msg[seq_offset + 1], &tmp_pdu.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); + break; + case CIPHERING_ALGORITHM_ID_128_EEA2: + security_128_eea2(&ctxt_base.k_nas_enc[16], + count_est, + bearer_id, + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[seq_offset + 1], + pdu->N_bytes - seq_offset + 1, + &tmp_pdu.msg[seq_offset + 1]); + logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); + memcpy(&pdu->msg[seq_offset + 1], &tmp_pdu.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); + break; + case CIPHERING_ALGORITHM_ID_128_EEA3: + security_128_eea3(&ctxt_base.k_nas_enc[16], + count_est, + bearer_id, + SECURITY_DIRECTION_DOWNLINK, + &pdu->msg[seq_offset + 1], + pdu->N_bytes - seq_offset + 1, + &tmp_pdu.msg[seq_offset + 1]); + logger.debug(tmp_pdu.msg, pdu->N_bytes, "Decrypted"); + memcpy(&pdu->msg[seq_offset + 1], &tmp_pdu.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); + break; + default: + logger.error("Ciphering algorithms not known"); + break; + } +} + +} // namespace srsue \ No newline at end of file diff --git a/srsue/src/stack/upper/nas_emm_state.cc b/srsue/src/stack/upper/nas_emm_state.cc index 7c2d15836..6b76e74c3 100644 --- a/srsue/src/stack/upper/nas_emm_state.cc +++ b/srsue/src/stack/upper/nas_emm_state.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/src/stack/upper/nas_idle_procedures.cc b/srsue/src/stack/upper/nas_idle_procedures.cc index 3f06aeb4d..2b7d7f714 100644 --- a/srsue/src/stack/upper/nas_idle_procedures.cc +++ b/srsue/src/stack/upper/nas_idle_procedures.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/src/stack/upper/pcsc_usim.cc b/srsue/src/stack/upper/pcsc_usim.cc index c17f86f88..045eb351b 100644 --- a/srsue/src/stack/upper/pcsc_usim.cc +++ b/srsue/src/stack/upper/pcsc_usim.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,12 +32,7 @@ using namespace srsran; namespace srsue { -pcsc_usim::pcsc_usim(srslog::basic_logger& logger) : usim_base(logger), sc(logger) -{ - bzero(ck, CK_LEN); - bzero(ik, IK_LEN); - bzero(auts, IK_LEN); -} +pcsc_usim::pcsc_usim(srslog::basic_logger& logger) : usim_base(logger), sc(logger) {} pcsc_usim::~pcsc_usim() { @@ -152,7 +147,7 @@ auth_result_t pcsc_usim::generate_authentication_response(uint8_t* rand, logger.debug(ak, AK_LEN, "AK:"); logger.debug(sqn, SQN_LEN, "SQN:"); logger.debug("mcc=%d, mnc=%d", mcc, mnc); - security_generate_k_asme(ck, ik, ak, sqn, mcc, mnc, k_asme); + security_generate_k_asme(ck, ik, sqn, mcc, mnc, k_asme); logger.info(k_asme, KEY_LEN, "K_ASME:"); ret = AUTH_OK; @@ -160,6 +155,76 @@ auth_result_t pcsc_usim::generate_authentication_response(uint8_t* rand, return ret; } +auth_result_t pcsc_usim::generate_authentication_response_5g(uint8_t* rand, + uint8_t* autn_enb, + const char* serving_network_name, + uint8_t* abba, + uint32_t abba_len, + uint8_t* res_star, + uint8_t* k_amf) +{ + uint8_t res[16]; + uint8_t k_ausf[32]; + uint8_t k_seaf[32]; + int res_len; + + auth_result_t ret = AUTH_FAILED; + if (!initiated) { + ERROR("USIM not initiated!"); + return ret; + } + + // Use RAND and AUTN to compute RES, CK, IK using SIM card + switch (sc.umts_auth(rand, autn_enb, res, &res_len, ik, ck, auts)) { + case 0: + logger.info("SCARD: USIM authentication successful."); + break; + case -1: + logger.error("SCARD: Failure during USIM UMTS authentication"); + return ret; + case -2: + logger.info("SCARD: USIM synchronization failure, AUTS generated"); + logger.debug(auts, AKA_AUTS_LEN, "AUTS"); + memcpy(res_star, auts, AKA_AUTS_LEN); + res_len = AKA_AUTS_LEN; + return AUTH_SYNCH_FAILURE; + default: + logger.warning("SCARD: Unknown USIM failure."); + return ret; + } + + // TODO: Extract ak and seq from auts + memset(ak, 0x00, AK_LEN); + + // Extract sqn from autn + uint8_t sqn[SQN_LEN]; + for (int i = 0; i < 6; i++) { + sqn[i] = autn_enb[i] ^ ak[i]; + } + + // Generate K_asme + logger.debug(ck, CK_LEN, "CK:"); + logger.debug(ik, IK_LEN, "IK:"); + logger.debug(ak, AK_LEN, "AK:"); + logger.debug(sqn, SQN_LEN, "SQN:"); + logger.debug("SSN=%s", serving_network_name); + // Generate RES STAR + security_generate_res_star(ck, ik, serving_network_name, rand, res, res_len, res_star); + logger.debug(res_star, 16, "RES STAR"); + // Generate K_ausf + security_generate_k_ausf(ck, ik, sqn, serving_network_name, k_ausf); + logger.debug(k_ausf, 32, "K AUSF"); + // Generate K_seaf + security_generate_k_seaf(k_ausf, serving_network_name, k_seaf); + logger.debug(k_seaf, 32, "K SEAF"); + // Generate K_amf + security_generate_k_amf(k_ausf, imsi_str.c_str(), abba, abba_len, k_amf); + logger.debug(k_amf, 32, "K AMF"); + + ret = AUTH_OK; + return ret; +} + std::string pcsc_usim::get_mnc_str(const uint8_t* imsi_vec, std::string mcc_str) { uint32_t mcc_len = 3; diff --git a/srsue/src/stack/upper/sdap.cc b/srsue/src/stack/upper/sdap.cc new file mode 100644 index 000000000..576d334e9 --- /dev/null +++ b/srsue/src/stack/upper/sdap.cc @@ -0,0 +1,85 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsue/hdr/stack/upper/sdap.h" + +namespace srsue { + +sdap::sdap(const char* logname) : logger(srslog::fetch_basic_logger(logname)) {} + +bool sdap::init(pdcp_interface_sdap_nr* pdcp_, srsue::gw_interface_pdcp* gw_) +{ + m_pdcp = pdcp_; + m_gw = gw_; + + running = true; + return true; +} + +void sdap::stop() +{ + if (running) { + running = false; + } +} + +void sdap::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) +{ + if (!running) { + return; + } + m_gw->write_pdu(lcid, std::move(pdu)); +} + +void sdap::write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) +{ + if (!running) { + return; + } + if (lcid < bearers.size()) { + if (bearers[lcid].add_uplink_header) { + if (pdu->get_headroom() > 1) { + pdu->msg -= 1; + pdu->N_bytes += 1; + pdu->msg[0] = ((bearers[lcid].is_data ? 1 : 0) << 7) | (bearers[lcid].qfi & 0x3f); + } else { + logger.error("Not enough headroom in PDU to add header\n"); + } + } + } + m_pdcp->write_sdu(lcid, std::move(pdu)); +} + +bool sdap::set_bearer_cfg(uint32_t lcid, const sdap_interface_rrc::bearer_cfg_t& cfg) +{ + if (lcid >= bearers.size()) { + logger.error("Error setting configuration: invalid lcid=%d\n", lcid); + return false; + } + if (cfg.add_downlink_header) { + logger.error("Error setting configuration: downlink header not supported\n"); + return false; + } + bearers[lcid] = cfg; + return true; +} + +} // namespace srsue diff --git a/srsue/test/upper/CMakeLists.txt b/srsue/src/stack/upper/test/CMakeLists.txt similarity index 54% rename from srsue/test/upper/CMakeLists.txt rename to srsue/src/stack/upper/test/CMakeLists.txt index da5d2538a..f94bce2f1 100644 --- a/srsue/test/upper/CMakeLists.txt +++ b/srsue/src/stack/upper/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -19,45 +19,30 @@ # add_executable(usim_test usim_test.cc) -target_link_libraries(usim_test srsue_upper srsran_upper srsran_phy rrc_asn1) +target_link_libraries(usim_test srsue_upper srsran_phy rrc_asn1 srslog) add_test(usim_test usim_test) if(HAVE_PCSC) add_executable(pcsc_usim_test pcsc_usim_test.cc) - target_link_libraries(pcsc_usim_test srsue_upper srsran_upper srsran_phy) + target_link_libraries(pcsc_usim_test srsue_upper srsran_phy srsran_common srslog) endif(HAVE_PCSC) -add_executable(rrc_reconfig_test rrc_reconfig_test.cc) -target_link_libraries(rrc_reconfig_test srsue_upper srsran_upper srsran_phy rrc_asn1) -add_test(rrc_reconfig_test rrc_reconfig_test) - -add_executable(rrc_meas_test rrc_meas_test.cc) -target_link_libraries(rrc_meas_test srsue_rrc srsue_upper srsran_upper srsran_phy rrc_asn1 rrc_nr_asn1) -add_test(rrc_meas_test rrc_meas_test) - add_executable(nas_test nas_test.cc) -target_link_libraries(nas_test srsue_upper srsran_upper srsran_phy rrc_asn1) +target_link_libraries(nas_test srsue_upper srsran_common srsran_phy rrc_asn1 srsran_asn1) add_test(nas_test nas_test) +add_executable(nas_5g_test nas_5g_test.cc) +target_link_libraries(nas_5g_test srsue_upper srsran_phy rrc_asn1) +add_test(nas_5g_test nas_5g_test) + add_executable(gw_test gw_test.cc) -target_link_libraries(gw_test srsue_upper srsran_upper srsran_phy) +target_link_libraries(gw_test srsue_upper srsran_common srsran_phy) add_test(gw_test gw_test) add_executable(tft_test tft_test.cc) -target_link_libraries(tft_test srsue_upper srsran_upper srsran_phy) +target_link_libraries(tft_test srsue_upper srsran_common srsran_phy) add_test(tft_test tft_test) -add_executable(rrc_phy_ctrl_test rrc_phy_ctrl_test.cc) -target_link_libraries(rrc_phy_ctrl_test srsran_common srsue_rrc) -add_test(rrc_phy_ctrl_test rrc_phy_ctrl_test) - -add_executable(rrc_cell_test rrc_cell_test.cc) -target_link_libraries(rrc_cell_test srsue_rrc srsue_upper srsran_upper srsran_phy rrc_asn1 rrc_nr_asn1) -add_test(rrc_cell_test rrc_cell_test) - -add_executable(ue_rrc_nr_test ue_rrc_nr_test.cc) -target_link_libraries(ue_rrc_nr_test srsue_rrc_nr srsue_upper srsran_common srsran_upper srsran_phy rrc_asn1 rrc_nr_asn1) - ######################################################################## # Option to run command after build (useful for remote builds) ######################################################################## diff --git a/srsue/test/upper/gw_test.cc b/srsue/src/stack/upper/test/gw_test.cc similarity index 76% rename from srsue/test/upper/gw_test.cc rename to srsue/src/stack/upper/test/gw_test.cc index a56f2ce5e..81a8ce045 100644 --- a/srsue/test/upper/gw_test.cc +++ b/srsue/src/stack/upper/test/gw_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -32,17 +32,17 @@ public: bool is_registered() { return true; } bool start_service_request() { return true; }; void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { return; } - bool is_lcid_enabled(uint32_t lcid) { return true; } + bool has_active_radio_bearer(uint32_t eps_bearer_id) { return true; } }; -int gw_change_lcid_test() +int gw_test() { srsue::gw_args_t gw_args; gw_args.tun_dev_name = "tun1"; gw_args.log.gw_level = "debug"; gw_args.log.gw_hex_limit = 100000; test_stack_dummy stack; - srsue::gw gw; + srsue::gw gw(srslog::fetch_basic_logger("GW")); gw.init(gw_args, &stack); uint32_t eps_bearer_id = 5; @@ -52,8 +52,12 @@ int gw_change_lcid_test() char* err_str = nullptr; int rtn = 0; - rtn = gw.setup_if_addr( - eps_bearer_id, old_lcid, LIBLTE_MME_PDN_TYPE_IPV4, htonl(inet_addr("192.168.56.32")), nullptr, err_str); + struct in_addr in_addr; + if (inet_pton(AF_INET, "192.168.56.32", &in_addr.s_addr) != 1) { + perror("inet_pton"); + return SRSRAN_ERROR; + } + rtn = gw.setup_if_addr(eps_bearer_id, LIBLTE_MME_PDN_TYPE_IPV4, htonl(in_addr.s_addr), nullptr, err_str); if (rtn != SRSRAN_SUCCESS) { srslog::fetch_basic_logger("TEST", false) @@ -62,8 +66,8 @@ int gw_change_lcid_test() return SRSRAN_SUCCESS; } - TESTASSERT(gw.update_lcid(eps_bearer_id, new_lcid) == SRSRAN_SUCCESS); - TESTASSERT(gw.update_lcid(non_existing_eps_bearer_id, new_lcid) == SRSRAN_ERROR); + TESTASSERT(gw.deactivate_eps_bearer(eps_bearer_id) == SRSRAN_SUCCESS); + TESTASSERT(gw.deactivate_eps_bearer(non_existing_eps_bearer_id) == SRSRAN_ERROR); gw.stop(); return SRSRAN_SUCCESS; } @@ -72,7 +76,7 @@ int main(int argc, char** argv) { srslog::init(); - TESTASSERT(gw_change_lcid_test() == SRSRAN_SUCCESS); + TESTASSERT(gw_test() == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } diff --git a/srsue/src/stack/upper/test/nas_5g_test.cc b/srsue/src/stack/upper/test/nas_5g_test.cc new file mode 100644 index 000000000..d4e0e33ee --- /dev/null +++ b/srsue/src/stack/upper/test/nas_5g_test.cc @@ -0,0 +1,124 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/common/bcd_helpers.h" +#include "srsran/common/test_common.h" +#include "srsran/common/tsan_options.h" +#include "srsran/interfaces/ue_pdcp_interfaces.h" +#include "srsran/srslog/srslog.h" +#include "srsran/test/ue_test_interfaces.h" +#include "srsue/hdr/stack/upper/gw.h" +#include "srsue/hdr/stack/upper/nas_5g.h" +#include "srsue/hdr/stack/upper/test/nas_test_common.h" + +using namespace srsue; +using namespace srsran; + +#define HAVE_PCAP 0 + +int amf_attach_request_test(srsran::nas_pcap* pcap) +{ + int ret = SRSRAN_ERROR; + + rrc_nr_dummy rrc_nr_dummy; + pdcp_dummy pdcp_dummy; + + srsue::usim usim(srslog::fetch_basic_logger("USIM")); + usim_args_t args; + args.mode = "soft"; + args.algo = "xor"; + args.imei = "353490069873319"; + args.imsi = "001010123456789"; + args.k = "00112233445566778899aabbccddeeff"; + args.op = "63BFA50EE6523365FF14C1F45F88737D"; + usim.init(&args); + + nas_5g_args_t nas_5g_cfg; + nas_5g_cfg.force_imsi_attach = true; + pdu_session_cfg_t pdu_session; + pdu_session.apn_name = "test123"; + + nas_5g_cfg.pdu_session_cfgs.push_back(pdu_session); + + nas_5g_cfg.eia = "0,1,2,3"; + nas_5g_cfg.eea = "0,1,2,3"; + + nas_5g_cfg.ia5g = "0,1,2,3"; + nas_5g_cfg.ea5g = "0,1,2,3"; + + test_stack_dummy stack(&pdcp_dummy); + srsue::nas_5g nas_5g(srslog::fetch_basic_logger("NAS-5G"), &stack.task_sched); + srsue::gw gw(srslog::fetch_basic_logger("GW")); + + if (pcap != nullptr) { + nas_5g.start_pcap(pcap); + } + + gw_args_t gw_args; + gw_args.tun_dev_name = "tun0"; + gw_args.log.gw_level = "debug"; + gw_args.log.gw_hex_limit = 100000; + + gw.init(gw_args, &stack); + stack.init(&nas_5g); + + nas_5g.init(&usim, &rrc_nr_dummy, &gw, nas_5g_cfg); + rrc_nr_dummy.init(&nas_5g); + + // trigger test + stack.switch_on(); + stack.stop(); + + gw.stop(); + ret = SRSRAN_SUCCESS; + + return ret; +} + +int main(int argc, char** argv) +{ + // Setup logging. + auto& rrc_logger = srslog::fetch_basic_logger("RRC", false); + rrc_logger.set_level(srslog::basic_levels::debug); + rrc_logger.set_hex_dump_max_size(100000); + auto& nas_logger = srslog::fetch_basic_logger("NAS", false); + nas_logger.set_level(srslog::basic_levels::debug); + nas_logger.set_hex_dump_max_size(100000); + auto& usim_logger = srslog::fetch_basic_logger("USIM", false); + usim_logger.set_level(srslog::basic_levels::debug); + usim_logger.set_hex_dump_max_size(100000); + auto& gw_logger = srslog::fetch_basic_logger("GW", false); + gw_logger.set_level(srslog::basic_levels::debug); + gw_logger.set_hex_dump_max_size(100000); + + // Start the log backend. + srslog::init(); +#if HAVE_PCAP + srsran::nas_pcap pcap; + pcap.open("nas_5g_test.pcap", 0, srsran::srsran_rat_t::nr); + TESTASSERT(amf_attach_request_test(&pcap) == SRSRAN_SUCCESS); + pcap.close(); +#else + TESTASSERT(amf_attach_request_test(nullptr) == SRSRAN_SUCCESS); +#endif // HAVE_PCAP + + return SRSRAN_SUCCESS; +} diff --git a/srsue/test/upper/nas_test.cc b/srsue/src/stack/upper/test/nas_test.cc similarity index 58% rename from srsue/test/upper/nas_test.cc rename to srsue/src/stack/upper/test/nas_test.cc index efd192851..73622c1fe 100644 --- a/srsue/test/upper/nas_test.cc +++ b/srsue/src/stack/upper/test/nas_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,16 +20,19 @@ */ #include "srsran/common/bcd_helpers.h" -#include "srsran/common/test_common.h" +#include "srsran/common/tsan_options.h" #include "srsran/interfaces/ue_pdcp_interfaces.h" #include "srsran/srslog/srslog.h" +#include "srsran/support/srsran_test.h" #include "srsran/test/ue_test_interfaces.h" #include "srsue/hdr/stack/upper/gw.h" #include "srsue/hdr/stack/upper/nas.h" +#include "srsue/hdr/stack/upper/test/nas_test_common.h" #include "srsue/hdr/stack/upper/usim.h" #include "srsue/hdr/stack/upper/usim_base.h" using namespace srsue; +using namespace srsran; static_assert(alignof(LIBLTE_BYTE_MSG_STRUCT) == alignof(byte_buffer_t), "liblte buffer and byte buffer members misaligned"); @@ -40,225 +43,6 @@ static_assert(offsetof(LIBLTE_BYTE_MSG_STRUCT, header) == offsetof(byte_buffer_t static_assert(sizeof(LIBLTE_BYTE_MSG_STRUCT) <= offsetof(byte_buffer_t, msg), "liblte buffer and byte buffer members misaligned"); -#define LCID 1 - -uint8_t auth_request_pdu[] = {0x07, 0x52, 0x01, 0x0c, 0x63, 0xa8, 0x54, 0x13, 0xe6, 0xa4, 0xce, 0xd9, - 0x86, 0xfb, 0xe5, 0xce, 0x9b, 0x62, 0x5e, 0x10, 0x67, 0x57, 0xb3, 0xc2, - 0xb9, 0x70, 0x90, 0x01, 0x0c, 0x72, 0x8a, 0x67, 0x57, 0x92, 0x52, 0xb8}; - -uint8_t sec_mode_command_pdu[] = {0x37, 0x4e, 0xfd, 0x57, 0x11, 0x00, 0x07, 0x5d, 0x02, 0x01, 0x02, 0xf0, 0x70, 0xc1}; - -uint8_t attach_accept_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x42, 0x01, 0x3e, 0x06, 0x00, 0x00, - 0xf1, 0x10, 0x00, 0x01, 0x00, 0x2a, 0x52, 0x01, 0xc1, 0x01, 0x04, 0x1b, 0x07, - 0x74, 0x65, 0x73, 0x74, 0x31, 0x32, 0x33, 0x06, 0x6d, 0x6e, 0x63, 0x30, 0x30, - 0x31, 0x06, 0x6d, 0x63, 0x63, 0x30, 0x30, 0x31, 0x04, 0x67, 0x70, 0x72, 0x73, - 0x05, 0x01, 0xc0, 0xa8, 0x05, 0x02, 0x27, 0x01, 0x80, 0x50, 0x0b, 0xf6, 0x00, - 0xf1, 0x10, 0x80, 0x01, 0x01, 0x35, 0x16, 0x6d, 0xbc, 0x64, 0x01, 0x00}; - -uint8_t esm_info_req_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x5a, 0xd9}; - -uint8_t activate_dedicated_eps_bearer_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0xc5, 0x05, - 0x01, 0x01, 0x07, 0x21, 0x31, 0x00, 0x03, 0x40, 0x08, 0xae, - 0x5d, 0x02, 0x00, 0xc2, 0x81, 0x34, 0x01, 0x4d}; - -uint8_t deactivate_eps_bearer_pdu[] = {0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0xcd, 0x24}; - -uint16 mcc = 61441; -uint16 mnc = 65281; - -using namespace srsran; - -namespace srsran { - -// fake classes -class pdcp_dummy : public rrc_interface_pdcp, public pdcp_interface_gw -{ -public: - void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) {} - void write_pdu_bcch_bch(unique_byte_buffer_t pdu) {} - void write_pdu_bcch_dlsch(unique_byte_buffer_t pdu) {} - void write_pdu_pcch(unique_byte_buffer_t pdu) {} - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} - const char* get_rb_name(uint32_t lcid) { return "lcid"; } - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} - bool is_lcid_enabled(uint32_t lcid) { return false; } -}; - -class rrc_dummy : public rrc_interface_nas -{ -public: - rrc_dummy() : last_sdu_len(0) - { - plmns[0].plmn_id.from_number(mcc, mnc); - plmns[0].tac = 0xffff; - } - void init(nas* nas_) { nas_ptr = nas_; } - void write_sdu(unique_byte_buffer_t sdu) - { - last_sdu_len = sdu->N_bytes; - // printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); - // srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); - } - const char* get_rb_name(uint32_t lcid) { return "lcid"; } - uint32_t get_last_sdu_len() { return last_sdu_len; } - void reset() { last_sdu_len = 0; } - - bool plmn_search() - { - nas_ptr->plmn_search_completed(plmns, 1); - return true; - } - void plmn_select(srsran::plmn_id_t plmn_id){}; - void set_ue_identity(srsran::s_tmsi_t s_tmsi) {} - bool connection_request(srsran::establishment_cause_t cause, srsran::unique_byte_buffer_t sdu) - { - printf("NAS generated SDU (len=%d):\n", sdu->N_bytes); - last_sdu_len = sdu->N_bytes; - srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes); - is_connected_flag = true; - nas_ptr->connection_request_completed(true); - return true; - } - bool is_connected() { return is_connected_flag; } - - uint16_t get_mcc() { return mcc; } - uint16_t get_mnc() { return mnc; } - void enable_capabilities() {} - uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) { return 0; } - void paging_completed(bool outcome) {} - bool has_nr_dc() { return false; } - -private: - nas* nas_ptr; - uint32_t last_sdu_len; - nas_interface_rrc::found_plmn_t plmns[nas_interface_rrc::MAX_FOUND_PLMNS]; - bool is_connected_flag = false; -}; - -class test_stack_dummy : public srsue::stack_test_dummy, public stack_interface_gw, public thread -{ -public: - test_stack_dummy(pdcp_interface_gw* pdcp_) : pdcp(pdcp_), thread("DUMMY STACK") {} - void init(srsue::nas* nas_) - { - nas = nas_; - start(-1); - } - bool switch_on() { return true; } - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) { pdcp->write_sdu(lcid, std::move(sdu)); } - bool is_lcid_enabled(uint32_t lcid) { return pdcp->is_lcid_enabled(lcid); } - - bool is_registered() { return true; } - - bool start_service_request() { return true; } - - void run_thread() - { - std::unique_lock lk(init_mutex); - running = true; - init_cv.notify_all(); - lk.unlock(); - while (running) { - task_sched.tic(); - task_sched.run_pending_tasks(); - nas->run_tti(); - } - } - void stop() - { - std::unique_lock lk(init_mutex); - while (not running) { - init_cv.wait(lk); - } - running = false; - wait_thread_finish(); - } - pdcp_interface_gw* pdcp = nullptr; - srsue::nas* nas = nullptr; - bool running = false; - std::mutex init_mutex; - std::condition_variable init_cv; -}; - -class gw_dummy : public gw_interface_nas, public gw_interface_pdcp -{ - int setup_if_addr(uint32_t eps_bearer_id, - uint32_t lcid, - uint8_t pdn_type, - uint32_t ip_addr, - uint8_t* ipv6_if_id, - char* err_str) - { - return SRSRAN_SUCCESS; - } - int apply_traffic_flow_template(const uint8_t& eps_bearer_id, - const uint8_t& lcid, - const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) - { - return SRSRAN_SUCCESS; - } - void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu) {} - void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} - void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms = 0) {} -}; - -} // namespace srsran - -int security_command_test() -{ - int ret = SRSRAN_ERROR; - stack_test_dummy stack; - - rrc_dummy rrc_dummy; - gw_dummy gw; - - usim_args_t args; - args.algo = "xor"; - args.imei = "353490069873319"; - args.imsi = "001010123456789"; - args.k = "00112233445566778899aabbccddeeff"; - args.op = "63BFA50EE6523365FF14C1F45F88737D"; - args.using_op = true; - - // init USIM - srsue::usim usim(srslog::fetch_basic_logger("USIM")); - usim.init(&args); - - { - srsue::nas nas(&stack.task_sched); - nas_args_t cfg; - cfg.eia = "1,2,3"; - cfg.eea = "0,1,2,3"; - nas.init(&usim, &rrc_dummy, &gw, cfg); - rrc_dummy.init(&nas); - - // push auth request PDU to NAS to generate security context - byte_buffer_pool* pool = byte_buffer_pool::get_instance(); - unique_byte_buffer_t tmp = srsran::make_byte_buffer(); - TESTASSERT(tmp != nullptr); - memcpy(tmp->msg, auth_request_pdu, sizeof(auth_request_pdu)); - tmp->N_bytes = sizeof(auth_request_pdu); - nas.write_pdu(LCID, std::move(tmp)); - - // TODO: add check for authentication response - rrc_dummy.reset(); - - // reuse buffer for security mode command - tmp = srsran::make_byte_buffer(); - TESTASSERT(tmp != nullptr); - memcpy(tmp->msg, sec_mode_command_pdu, sizeof(sec_mode_command_pdu)); - tmp->N_bytes = sizeof(sec_mode_command_pdu); - nas.write_pdu(LCID, std::move(tmp)); - - // check length of generated NAS SDU - if (rrc_dummy.get_last_sdu_len() > 3) { - ret = SRSRAN_SUCCESS; - } - } - - return ret; -} - int mme_attach_request_test() { int ret = SRSRAN_ERROR; @@ -279,11 +63,13 @@ int mme_attach_request_test() { nas_args_t nas_cfg; nas_cfg.force_imsi_attach = true; + nas_cfg.eia = "1,2,3"; + nas_cfg.eea = "0,1,2,3"; nas_cfg.apn_name = "test123"; - test_stack_dummy stack(&pdcp_dummy); - srsue::nas nas(&stack.task_sched); - srsue::gw gw; + test_stack_dummy stack(&pdcp_dummy); + srsue::nas nas(srslog::fetch_basic_logger("NAS"), &stack.task_sched); + srsue::gw gw(srslog::fetch_basic_logger("GW")); nas.init(&usim, &rrc_dummy, &gw, nas_cfg); rrc_dummy.init(&nas); @@ -326,6 +112,61 @@ int mme_attach_request_test() return ret; } +int security_command_test() +{ + int ret = SRSRAN_ERROR; + stack_test_dummy stack; + + rrc_dummy rrc_dummy; + gw_dummy gw; + + usim_args_t args; + args.algo = "xor"; + args.imei = "353490069873319"; + args.imsi = "001010123456789"; + args.k = "00112233445566778899aabbccddeeff"; + args.op = "63BFA50EE6523365FF14C1F45F88737D"; + args.using_op = true; + + // init USIM + srsue::usim usim(srslog::fetch_basic_logger("USIM")); + usim.init(&args); + + { + srsue::nas nas(srslog::fetch_basic_logger("NAS"), &stack.task_sched); + nas_args_t cfg; + cfg.eia = "1,2,3"; + cfg.eea = "0,1,2,3"; + nas.init(&usim, &rrc_dummy, &gw, cfg); + rrc_dummy.init(&nas); + + // push auth request PDU to NAS to generate security context + byte_buffer_pool* pool = byte_buffer_pool::get_instance(); + unique_byte_buffer_t tmp = srsran::make_byte_buffer(); + TESTASSERT(tmp != nullptr); + memcpy(tmp->msg, auth_request_pdu, sizeof(auth_request_pdu)); + tmp->N_bytes = sizeof(auth_request_pdu); + nas.write_pdu(LCID, std::move(tmp)); + + // TODO: add check for authentication response + rrc_dummy.reset(); + + // reuse buffer for security mode command + tmp = srsran::make_byte_buffer(); + TESTASSERT(tmp != nullptr); + memcpy(tmp->msg, sec_mode_command_pdu, sizeof(sec_mode_command_pdu)); + tmp->N_bytes = sizeof(sec_mode_command_pdu); + nas.write_pdu(LCID, std::move(tmp)); + + // check length of generated NAS SDU + if (rrc_dummy.get_last_sdu_len() > 3) { + ret = SRSRAN_SUCCESS; + } + } + + return ret; +} + int esm_info_request_test() { int ret = SRSRAN_ERROR; @@ -347,11 +188,13 @@ int esm_info_request_test() usim.init(&args); { - srsue::nas nas(&stack.task_sched); + srsue::nas nas(srslog::fetch_basic_logger("NAS-5G"), &stack.task_sched); nas_args_t cfg; cfg.apn_name = "srsran"; cfg.apn_user = "srsuser"; cfg.apn_pass = "srspass"; + cfg.eia = "1,2,3"; + cfg.eea = "0,1,2,3"; cfg.force_imsi_attach = true; nas.init(&usim, &rrc_dummy, &gw, cfg); @@ -389,9 +232,11 @@ int dedicated_eps_bearer_test() srsue::usim usim(srslog::fetch_basic_logger("USIM")); usim.init(&args); - srsue::nas nas(&stack.task_sched); + srsue::nas nas(srslog::fetch_basic_logger("NAS"), &stack.task_sched); nas_args_t cfg = {}; cfg.force_imsi_attach = true; // make sure we get a fresh security context + cfg.eia = "1,2,3"; + cfg.eea = "0,1,2,3"; nas.init(&usim, &rrc_dummy, &gw, cfg); // push dedicated EPS bearer PDU to NAS @@ -466,25 +311,10 @@ int main(int argc, char** argv) // Start the log backend. srslog::init(); - if (security_command_test()) { - printf("Security command test failed.\n"); - return -1; - } + TESTASSERT(mme_attach_request_test() == SRSRAN_SUCCESS); + TESTASSERT(security_command_test() == SRSRAN_SUCCESS); + TESTASSERT(esm_info_request_test() == SRSRAN_SUCCESS); + TESTASSERT(dedicated_eps_bearer_test() == SRSRAN_SUCCESS); - if (mme_attach_request_test()) { - printf("Attach request test failed.\n"); - return -1; - } - - if (esm_info_request_test()) { - printf("ESM info request test failed.\n"); - return -1; - } - - if (dedicated_eps_bearer_test()) { - printf("Dedicated EPS bearer test failed.\n"); - return -1; - } - - return 0; + return SRSRAN_SUCCESS; } diff --git a/srsue/test/upper/pcsc_usim_test.cc b/srsue/src/stack/upper/test/pcsc_usim_test.cc similarity index 97% rename from srsue/test/upper/pcsc_usim_test.cc rename to srsue/src/stack/upper/test/pcsc_usim_test.cc index b52d9b28a..90ad0e218 100644 --- a/srsue/test/upper/pcsc_usim_test.cc +++ b/srsue/src/stack/upper/test/pcsc_usim_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/upper/tft_test.cc b/srsue/src/stack/upper/test/tft_test.cc similarity index 96% rename from srsue/test/upper/tft_test.cc rename to srsue/src/stack/upper/test/tft_test.cc index e8b397cd2..8f2a597c6 100644 --- a/srsue/test/upper/tft_test.cc +++ b/srsue/src/stack/upper/test/tft_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -108,7 +108,6 @@ uint8_t ipv6_unmatched_packet_lport[] = { 0x80, 0x61, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77}; #define EPS_BEARER_ID 6 -#define LCID 1 int tft_filter_test_ipv6_combined() { @@ -159,7 +158,7 @@ int tft_filter_test_ipv6_combined() packet_filter.filter_size = sizeof(ipv6_filter); memcpy(packet_filter.filter, ipv6_filter, sizeof(ipv6_filter)); - srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger); + srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger); // Check filter TESTASSERT(filter.match(ip_msg1)); @@ -208,7 +207,7 @@ int tft_filter_test_single_local_port() packet_filter.filter_size = 3; memcpy(packet_filter.filter, filter_message, 3); - srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger); + srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger); // Check filter TESTASSERT(filter.match(ip_msg1)); @@ -254,7 +253,7 @@ int tft_filter_test_single_remote_port() packet_filter.filter_size = 3; memcpy(packet_filter.filter, filter_message, 3); - srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger); + srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger); // Check filter TESTASSERT(filter.match(ip_msg1)); @@ -303,7 +302,7 @@ int tft_filter_test_ipv4_local_addr() packet_filter.filter_size = filter_size; memcpy(packet_filter.filter, filter_message, filter_size); - srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger); + srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger); // Check filter TESTASSERT(filter.match(ip_msg1)); @@ -351,7 +350,7 @@ int tft_filter_test_ipv4_remote_addr() packet_filter.filter_size = filter_size; memcpy(packet_filter.filter, filter_message, filter_size); - srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger); + srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger); // Check filter TESTASSERT(filter.match(ip_msg1)); @@ -400,7 +399,7 @@ int tft_filter_test_ipv4_tos() packet_filter.filter_size = filter_size; memcpy(packet_filter.filter, filter_message, filter_size); - srsue::tft_packet_filter_t filter(EPS_BEARER_ID, LCID, packet_filter, logger); + srsue::tft_packet_filter_t filter(EPS_BEARER_ID, packet_filter, logger); // Check filter TESTASSERT(filter.match(ip_msg1)); diff --git a/srsue/test/upper/usim_test.cc b/srsue/src/stack/upper/test/usim_test.cc similarity index 64% rename from srsue/test/upper/usim_test.cc rename to srsue/src/stack/upper/test/usim_test.cc index cd3fc4030..63e0585f3 100644 --- a/srsue/test/upper/usim_test.cc +++ b/srsue/src/stack/upper/test/usim_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsran/common/test_common.h" +#include "srsran/support/srsran_test.h" #include "srsue/hdr/stack/upper/usim.h" using namespace srsue; @@ -60,12 +60,8 @@ static uint8_t autn_enb[] = static constexpr uint16_t mcc = 208; static constexpr uint16_t mnc = 93; -int main(int argc, char** argv) +int gen_auth_response_test() { - auto& logger = srslog::fetch_basic_logger("USIM", false); - // Start the log backend. - srslog::init(); - uint8_t res[16]; int res_len; uint8_t k_asme[32]; @@ -78,8 +74,60 @@ int main(int argc, char** argv) args.using_op = true; args.op = "11111111111111111111111111111111"; + auto& logger = srslog::fetch_basic_logger("USIM", false); srsue::usim usim(logger); usim.init(&args); TESTASSERT(usim.generate_authentication_response(rand_enb, autn_enb, mcc, mnc, res, &res_len, k_asme) == AUTH_OK); + return SRSRAN_SUCCESS; +} + +int mcc_mnc_msin_test() +{ + usim_args_t args; + args.algo = "milenage"; + args.imei = "356092040793011"; + args.imsi = "208930000000001"; + args.k = "8BAF473F2F8FD09487CCCBD7097C6862"; + args.using_op = true; + args.op = "11111111111111111111111111111111"; + + auto& logger = srslog::fetch_basic_logger("USIM", false); + srsue::usim usim(logger); + usim.init(&args); + + uint8_t mcc_correct[] = {0x2, 0x0, 0x8}; + uint8_t mnc_correct[] = {0x9, 0x3, 0xf}; + uint8_t msin_correct_bcd[] = {0x00, 0x00, 0x00, 0x00, 0x10}; + + uint8_t mcc_test[3]; + usim.get_home_mcc_bytes(mcc_test, 3); + + TESTASSERT(mcc_correct[0] == mcc_test[0]); + TESTASSERT(mcc_correct[1] == mcc_test[1]); + TESTASSERT(mcc_correct[2] == mcc_test[2]); + + uint8_t mnc_test[3]; + usim.get_home_mnc_bytes(mnc_test, 3); + TESTASSERT(mnc_correct[0] == mnc_test[0]); + TESTASSERT(mnc_correct[1] == mnc_test[1]); + TESTASSERT(mnc_correct[2] == mnc_test[2]); + + uint8_t msin_test[5]; + usim.get_home_msin_bcd(msin_test, 5); + + TESTASSERT(msin_correct_bcd[0] == msin_test[0]); + TESTASSERT(msin_correct_bcd[1] == msin_test[1]); + TESTASSERT(msin_correct_bcd[2] == msin_test[2]); + TESTASSERT(msin_correct_bcd[3] == msin_test[3]); + TESTASSERT(msin_correct_bcd[4] == msin_test[4]); + return SRSRAN_SUCCESS; +} + +int main(int argc, char** argv) +{ + // Start the log backend. + srslog::init(); + TESTASSERT(gen_auth_response_test() == SRSRAN_SUCCESS); + TESTASSERT(mcc_mnc_msin_test() == SRSRAN_SUCCESS); } diff --git a/srsue/src/stack/upper/tft_packet_filter.cc b/srsue/src/stack/upper/tft_packet_filter.cc index d1010c88e..c640dd318 100644 --- a/srsue/src/stack/upper/tft_packet_filter.cc +++ b/srsue/src/stack/upper/tft_packet_filter.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -33,11 +33,9 @@ extern "C" { namespace srsue { tft_packet_filter_t::tft_packet_filter_t(uint8_t eps_bearer_id_, - uint8_t lcid_, const LIBLTE_MME_PACKET_FILTER_STRUCT& tft, srslog::basic_logger& logger) : eps_bearer_id(eps_bearer_id_), - lcid(lcid_), id(tft.id), eval_precedence(tft.eval_precedence), active_filters(0), @@ -389,31 +387,61 @@ bool tft_packet_filter_t::match_port(const srsran::unique_byte_buffer_t& pdu) return true; } -uint8_t tft_pdu_matcher::check_tft_filter_match(const srsran::unique_byte_buffer_t& pdu) +void tft_pdu_matcher::reset() +{ + tft_filter_map.clear(); +} + +/** + * Checks whether the provided PDU matches any configured TFT. + * If it finds a match, it updates the eps_bearer_id parameter. + * @param pdu Reference to the PDU to check. + * @param eps_bearer_id Reference to variable to store EPS bearer ID. + * @return SRSRAN_SUCCESS if a reference could be found, SRSRAN_ERROR otherwise. + */ +int tft_pdu_matcher::check_tft_filter_match(const srsran::unique_byte_buffer_t& pdu, uint8_t& eps_bearer_id) { std::lock_guard lock(tft_mutex); - uint8_t lcid = default_lcid; for (std::pair& filter_pair : tft_filter_map) { bool match = filter_pair.second.match(pdu); if (match) { - lcid = filter_pair.second.lcid; - logger.debug("Found filter match -- EPS bearer Id %d, LCID %d", filter_pair.second.eps_bearer_id, lcid); - break; + eps_bearer_id = filter_pair.second.eps_bearer_id; + logger.debug("Found filter match -- EPS bearer Id %d", filter_pair.second.eps_bearer_id); + return SRSRAN_SUCCESS; } } - return lcid; + return SRSRAN_ERROR; } -int tft_pdu_matcher::apply_traffic_flow_template(const uint8_t& erab_id, - const uint8_t& lcid, +/** + * @brief Deletes all registered TFT for a given EPS bearer ID + * + * @param eps_bearer_id The EPS bearer ID + */ +void tft_pdu_matcher::delete_tft_for_eps_bearer(const uint8_t eps_bearer_id) +{ + std::lock_guard lock(tft_mutex); + auto old_filter = std::find_if( + tft_filter_map.begin(), tft_filter_map.end(), [&](const std::pair& filter) { + return filter.second.eps_bearer_id == eps_bearer_id; + }); + if (old_filter != tft_filter_map.end()) { + logger.debug("Deleting TFT for EPS bearer %d", eps_bearer_id); + tft_filter_map.erase(old_filter); + } +} + +int tft_pdu_matcher::apply_traffic_flow_template(const uint8_t& eps_bearer_id, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) { std::lock_guard lock(tft_mutex); switch (tft->tft_op_code) { case LIBLTE_MME_TFT_OPERATION_CODE_CREATE_NEW_TFT: for (int i = 0; i < tft->packet_filter_list_size; i++) { - logger.info("New packet filter for TFT"); - tft_packet_filter_t filter(erab_id, lcid, tft->packet_filter_list[i], logger); + logger.info("New TFT for eps_bearer_id=%d, eval_precedence=%d", + eps_bearer_id, + tft->packet_filter_list[i].eval_precedence); + tft_packet_filter_t filter(eps_bearer_id, tft->packet_filter_list[i], logger); auto it = tft_filter_map.insert(std::make_pair(filter.eval_precedence, filter)); if (it.second == false) { logger.error("Error inserting TFT Packet Filter"); @@ -421,6 +449,30 @@ int tft_pdu_matcher::apply_traffic_flow_template(const uint8_t& } } break; + case LIBLTE_MME_TFT_OPERATION_CODE_REPLACE_PACKET_FILTERS_IN_EXISTING_TFT: + for (int i = 0; i < tft->packet_filter_list_size; i++) { + // erase old filter if it exists + auto old_filter = std::find_if( + tft_filter_map.begin(), tft_filter_map.end(), [&](const std::pair& filter) { + return filter.second.id == tft->packet_filter_list[i].id; + }); + if (old_filter == tft_filter_map.end()) { + logger.error("Error couldn't find TFT with id %d", tft->packet_filter_list[i].id); + return SRSRAN_ERROR_CANT_START; + } + + // release old filter + tft_filter_map.erase(old_filter); + + // Add new filter + tft_packet_filter_t new_filter(eps_bearer_id, tft->packet_filter_list[i], logger); + auto it = tft_filter_map.insert(std::make_pair(new_filter.eval_precedence, new_filter)); + if (it.second == false) { + logger.error("Error inserting TFT Packet Filter"); + return SRSRAN_ERROR_CANT_START; + } + } + break; default: logger.error("Unhandled TFT OP code"); return SRSRAN_ERROR_CANT_START; @@ -428,9 +480,4 @@ int tft_pdu_matcher::apply_traffic_flow_template(const uint8_t& return SRSRAN_SUCCESS; } -void tft_pdu_matcher::set_default_lcid(const uint8_t lcid) -{ - default_lcid = lcid; -} - } // namespace srsue diff --git a/srsue/src/stack/upper/usim.cc b/srsue/src/stack/upper/usim.cc index 0428271ce..73c2d7a00 100644 --- a/srsue/src/stack/upper/usim.cc +++ b/srsue/src/stack/upper/usim.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -110,24 +110,68 @@ auth_result_t usim::generate_authentication_response(uint8_t* rand, int* res_len, uint8_t* k_asme_) { + auth_result_t auth_result; + uint8_t ak_xor_sqn[6]; + if (auth_algo_xor == auth_algo) { - return gen_auth_res_xor(rand, autn_enb, mcc, mnc, res, res_len, k_asme_); + auth_result = gen_auth_res_xor(rand, autn_enb, res, res_len, ak_xor_sqn); } else { - return gen_auth_res_milenage(rand, autn_enb, mcc, mnc, res, res_len, k_asme_); + auth_result = gen_auth_res_milenage(rand, autn_enb, res, res_len, ak_xor_sqn); } + + if (auth_result == AUTH_OK) { + // Generate K_asme + security_generate_k_asme(ck, ik, ak_xor_sqn, mcc, mnc, k_asme_); + } + return auth_result; +} + +auth_result_t usim::generate_authentication_response_5g(uint8_t* rand, + uint8_t* autn_enb, + const char* serving_network_name, + uint8_t* abba, + uint32_t abba_len, + uint8_t* res_star, + uint8_t* k_amf) +{ + auth_result_t auth_result; + uint8_t ak_xor_sqn[6]; + uint8_t res[16]; + uint8_t k_ausf[32]; + uint8_t k_seaf[32]; + int res_len; + + if (auth_algo_xor == auth_algo) { + auth_result = gen_auth_res_xor(rand, autn_enb, res, &res_len, ak_xor_sqn); + } else { + auth_result = gen_auth_res_milenage(rand, autn_enb, res, &res_len, ak_xor_sqn); + } + + if (auth_result == AUTH_OK) { + // Generate RES STAR + security_generate_res_star(ck, ik, serving_network_name, rand, res, res_len, res_star); + logger.debug(res_star, 16, "RES STAR"); + // Generate K_ausf + security_generate_k_ausf(ck, ik, ak_xor_sqn, serving_network_name, k_ausf); + logger.debug(k_ausf, 32, "K AUSF"); + // Generate K_seaf + security_generate_k_seaf(k_ausf, serving_network_name, k_seaf); + logger.debug(k_seaf, 32, "K SEAF"); + // Generate K_seaf + logger.debug(abba, abba_len, "ABBA:"); + logger.debug("IMSI: %s", imsi_str.c_str()); + security_generate_k_amf(k_seaf, imsi_str.c_str(), abba, abba_len, k_amf); + logger.debug(k_amf, 32, "K AMF"); + } + return auth_result; } /******************************************************************************* Helpers *******************************************************************************/ -auth_result_t usim::gen_auth_res_milenage(uint8_t* rand, - uint8_t* autn_enb, - uint16_t mcc, - uint16_t mnc, - uint8_t* res, - int* res_len, - uint8_t* k_asme) +auth_result_t +usim::gen_auth_res_milenage(uint8_t* rand, uint8_t* autn_enb, uint8_t* res, int* res_len, uint8_t* ak_xor_sqn) { auth_result_t result = AUTH_OK; uint32_t i; @@ -168,37 +212,34 @@ auth_result_t usim::gen_auth_res_milenage(uint8_t* rand, } } - // Generate K_asme - security_generate_k_asme(ck, ik, ak, sqn, mcc, mnc, k_asme); + for (i = 0; i < 6; i++) { + ak_xor_sqn[i] = sqn[i] ^ ak[i]; + } + + logger.debug(ck, CK_LEN, "CK:"); + logger.debug(ik, IK_LEN, "IK:"); + logger.debug(ak, AK_LEN, "AK:"); + logger.debug(sqn, 6, "sqn:"); + logger.debug(amf, 2, "amf:"); + logger.debug(mac, 8, "mac:"); return result; } // 3GPP TS 34.108 version 10.0.0 Section 8 -auth_result_t usim::gen_auth_res_xor(uint8_t* rand, - uint8_t* autn_enb, - uint16_t mcc, - uint16_t mnc, - uint8_t* res, - int* res_len, - uint8_t* k_asme_) +auth_result_t usim::gen_auth_res_xor(uint8_t* rand, uint8_t* autn_enb, uint8_t* res, int* res_len, uint8_t* ak_xor_sqn) { auth_result_t result = AUTH_OK; uint8_t sqn[6]; - uint8_t xdout[16]; - uint8_t cdout[8]; + uint8_t res_[16]; + + logger.debug(k, 16, "K:"); // Use RAND and K to compute RES, CK, IK and AK - for (uint32_t i = 0; i < 16; i++) { - xdout[i] = k[i] ^ rand[i]; - } - for (uint32_t i = 0; i < 16; i++) { - res[i] = xdout[i]; - ck[i] = xdout[(i + 1) % 16]; - ik[i] = xdout[(i + 2) % 16]; - } - for (uint32_t i = 0; i < 6; i++) { - ak[i] = xdout[i + 3]; + security_xor_f2345(k, rand, res_, ck, ik, ak); + + for (uint32_t i = 0; i < 8; i++) { + res[i] = res_[i]; } *res_len = 8; @@ -212,18 +253,8 @@ auth_result_t usim::gen_auth_res_xor(uint8_t* rand, amf[i] = autn_enb[6 + i]; } - // Generate cdout - for (uint32_t i = 0; i < 6; i++) { - cdout[i] = sqn[i]; - } - for (uint32_t i = 0; i < 2; i++) { - cdout[6 + i] = amf[i]; - } - // Generate MAC - for (uint32_t i = 0; i < 8; i++) { - mac[i] = xdout[i] ^ cdout[i]; - } + security_xor_f1(k, rand, sqn, amf, mac); // Construct AUTN for (uint32_t i = 0; i < 6; i++) { @@ -243,8 +274,16 @@ auth_result_t usim::gen_auth_res_xor(uint8_t* rand, } } - // Generate K_asme - security_generate_k_asme(ck, ik, ak, sqn, mcc, mnc, k_asme_); + logger.debug(ck, CK_LEN, "CK:"); + logger.debug(ik, IK_LEN, "IK:"); + logger.debug(ak, AK_LEN, "AK:"); + logger.debug(sqn, 6, "sqn:"); + logger.debug(amf, 2, "amf:"); + logger.debug(mac, 8, "mac:"); + + for (uint32_t i = 0; i < 6; i++) { + ak_xor_sqn[i] = sqn[i] ^ ak[i]; + } return result; } diff --git a/srsue/src/stack/upper/usim_base.cc b/srsue/src/stack/upper/usim_base.cc index 04a83273d..ef0728abe 100644 --- a/srsue/src/stack/upper/usim_base.cc +++ b/srsue/src/stack/upper/usim_base.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,6 +20,7 @@ */ #include "srsue/hdr/stack/upper/usim_base.h" +#include "srsran/common/bcd_helpers.h" #include "srsue/hdr/stack/upper/usim.h" #ifdef HAVE_PCSC @@ -130,6 +131,88 @@ bool usim_base::get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) return true; } +bool usim_base::get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return false; + } + + if (NULL == mcc_ || n < 3) { + logger.error("Invalid parameters to get_home_mcc_bytes"); + return false; + } + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + std::string mcc_str = get_mcc_str(imsi_vec); + uint16_t mcc_bcd = 0; + + srsran::string_to_mcc(mcc_str, &mcc_bcd); + srsran::mcc_to_bytes(mcc_bcd, mcc_); + + return true; +} + +bool usim_base::get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return false; + } + + if (NULL == mnc_ || n < 3) { + logger.error("Invalid parameters to get_home_mcc_bytes"); + return false; + } + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + std::string mcc_str = get_mcc_str(imsi_vec); + std::string mnc_str = get_mnc_str(imsi_vec, mcc_str); + + uint16_t mnc_bcd = 0; + uint8_t len = 0; + + srsran::string_to_mnc(mnc_str, &mnc_bcd); + srsran::mnc_to_bytes(mnc_bcd, mnc_, &len); + + if (len == 2) { + mnc_[2] = 0xf; + } + + return true; +} + +bool usim_base::get_home_msin_bcd(uint8_t* msin_, uint32_t n) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return false; + } + + if (NULL == msin_ || n < 5) { + logger.error("Invalid parameters to get_home_mcc_bytes"); + return false; + } + + srsran::plmn_id_t tmp_plmn; + get_home_plmn_id(&tmp_plmn); + + uint8_t imsi_vec[15]; + get_imsi_vec(imsi_vec, 15); + + int total_msin_len = (tmp_plmn.nof_mnc_digits + 3) / 2; + int j = 0; + for (int i = tmp_plmn.nof_mnc_digits + 3; i < 15; i += 2) { + msin_[j] = (imsi_vec[i]) | (imsi_vec[i + 1] << 4); + j++; + } + return true; +} + void usim_base::generate_nas_keys(uint8_t* k_asme, uint8_t* k_nas_enc, uint8_t* k_nas_int, @@ -148,6 +231,7 @@ void usim_base::generate_nas_keys(uint8_t* k_asme, /* * RRC Interface */ + void usim_base::generate_as_keys(uint8_t* k_asme_, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) { if (!initiated) { @@ -274,10 +358,61 @@ void usim_base::restore_keys_from_failed_ho(srsran::as_security_config_t* as_ctx return; } +bool usim_base::generate_nas_keys_5g(uint8_t* k_amf, + uint8_t* k_nas_enc, + uint8_t* k_nas_int, + srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return false; + } + // Generate K_nas_enc and K_nas_int + security_generate_k_nas_5g(k_amf, cipher_algo, integ_algo, k_nas_enc, k_nas_int); + return true; +} + /* * NR RRC Interface */ +void usim_base::generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return; + } + + logger.info("Generating NR AS Keys. NAS UL COUNT %d", count_ul); + logger.debug(k_amf.data(), 32, "K_amf"); + + // Generate K_gnb + srsran::security_generate_k_gnb(k_amf, count_ul, k_gnb_ctx.k_gnb); + logger.info(k_gnb_ctx.k_gnb.data(), 32, "K_gnb"); + + // Save initial k_gnb + k_gnb_initial = k_gnb_ctx.k_gnb; + + security_generate_k_nr_rrc(k_gnb_ctx.k_gnb.data(), + sec_cfg->cipher_algo, + sec_cfg->integ_algo, + sec_cfg->k_rrc_enc.data(), + sec_cfg->k_rrc_int.data()); + + security_generate_k_nr_up(k_gnb_ctx.k_gnb.data(), + sec_cfg->cipher_algo, + sec_cfg->integ_algo, + sec_cfg->k_up_enc.data(), + sec_cfg->k_up_int.data()); + + logger.info(k_gnb_ctx.k_gnb.data(), 32, "Initial K_gnb"); + logger.info(sec_cfg->k_rrc_int.data(), sec_cfg->k_rrc_int.size(), "NR K_RRC_int"); + logger.info(sec_cfg->k_rrc_enc.data(), sec_cfg->k_rrc_enc.size(), "NR K_RRC_enc"); + logger.info(sec_cfg->k_up_int.data(), sec_cfg->k_up_int.size(), "NR K_UP_int"); + logger.info(sec_cfg->k_up_enc.data(), sec_cfg->k_up_enc.size(), "NR K_UP_enc"); +} + bool usim_base::generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) { if (!initiated) { @@ -286,7 +421,7 @@ bool usim_base::generate_nr_context(uint16_t sk_counter, srsran::as_security_con } logger.info("Generating Keys. SCG Counter %d", sk_counter); - srsran::security_generate_sk_gnb(k_enb_ctx.k_enb.data(), k_gnb_ctx.sk_gnb.data(), sk_counter); + srsran::security_generate_sk_gnb(k_enb_ctx.k_enb.data(), sk_counter, k_gnb_ctx.sk_gnb.data()); logger.info(k_gnb_ctx.sk_gnb.data(), 32, "k_sk_gnb"); if (update_nr_context(sec_cfg) == false) { return false; diff --git a/srsue/src/test/CMakeLists.txt b/srsue/src/test/CMakeLists.txt new file mode 100644 index 000000000..c650e9716 --- /dev/null +++ b/srsue/src/test/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +if (ENABLE_TTCN3) + add_subdirectory(ttcn3) +endif (ENABLE_TTCN3) + +add_executable(metrics_test metrics_test.cc ../metrics_stdout.cc ../metrics_csv.cc) +target_link_libraries(metrics_test srsran_phy srsran_common) +add_test(metrics_test metrics_test -o ${CMAKE_CURRENT_BINARY_DIR}/ue_metrics.csv) \ No newline at end of file diff --git a/srsue/test/metrics_test.cc b/srsue/src/test/metrics_test.cc similarity index 56% rename from srsue/test/metrics_test.cc rename to srsue/src/test/metrics_test.cc index c5dfa8585..e9d0e9e69 100644 --- a/srsue/test/metrics_test.cc +++ b/srsue/src/test/metrics_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,7 +20,7 @@ */ #include "srsran/common/metrics_hub.h" -#include "srsran/srsran.h" +#include "srsran/common/test_common.h" #include "srsue/hdr/metrics_csv.h" #include "srsue/hdr/metrics_stdout.h" #include "srsue/hdr/ue_metrics_interface.h" @@ -34,10 +34,12 @@ using namespace srsue; namespace srsue { -char* csv_file_name = NULL; +static char* csv_file_name = NULL; +static float period = 1.0; +static int duration_s = 5; // fake classes -class ue_dummy : public ue_metrics_interface +class eutra_ue_dummy : public ue_metrics_interface { public: bool get_metrics(ue_metrics_t* m) @@ -72,6 +74,51 @@ public: m->phy.nof_active_cc = 1; m->phy.ch[0].rsrp = -10.0f; m->phy.ch[0].pathloss = 32; + + m->stack.rrc.state = (rand() % 2 == 0) ? RRC_STATE_CONNECTED : RRC_STATE_IDLE; + + return true; + } +}; + +class nsa_ue_dummy : public ue_metrics_interface +{ +public: + bool get_metrics(ue_metrics_t* m) + { + *m = {}; + + // fill dummy values + m->rf.rf_o = 10; + m->phy.nof_active_cc = 1; + m->phy.ch[0].rsrp = -10.0f; + m->phy.ch[0].pathloss = 74; + m->stack.mac[0].rx_pkts = 100; + m->stack.mac[0].rx_errors = 0; + m->stack.mac[0].rx_brate = 200; + m->stack.mac[0].nof_tti = 1; + + m->phy.info[1].pci = UINT32_MAX; + m->stack.mac[1].rx_pkts = 100; + m->stack.mac[1].rx_errors = 100; + m->stack.mac[1].rx_brate = 150; + m->stack.mac[1].nof_tti = 1; + + // random neighbour cells + if (rand() % 2 == 0) { + phy_meas_t neighbor = {}; + neighbor.pci = 8; + neighbor.rsrp = -33; + m->stack.rrc.neighbour_cells.push_back(neighbor); + m->stack.rrc.neighbour_cells.push_back(neighbor); // need to add twice since we use CA + } + + m->phy.nof_active_cc = 1; + m->phy.ch[0].rsrp = -10.0f; + m->phy.ch[0].pathloss = 32; + + // NR + m->phy_nr.nof_active_cc = 1; m->stack.mac_nr[0].rx_pkts = 100; m->stack.mac_nr[0].rx_errors = 2; m->stack.mac_nr[0].rx_brate = 223; @@ -83,6 +130,31 @@ public: } }; +class sa_ue_dummy : public ue_metrics_interface +{ +public: + bool get_metrics(ue_metrics_t* m) + { + *m = {}; + + // fill dummy values + m->rf.rf_o = 10; + m->phy.nof_active_cc = 0; + + // NR + m->phy_nr.nof_active_cc = 1; + m->phy_nr.info[0].pci = 501; + m->stack.mac_nr[0].rx_pkts = 100; + m->stack.mac_nr[0].rx_errors = 2; + m->stack.mac_nr[0].rx_brate = 223; + m->stack.mac_nr[0].nof_tti = 1; + + m->stack.rrc_nr.state = (rand() % 2 == 0) ? RRC_NR_STATE_CONNECTED : RRC_NR_STATE_IDLE; + + return true; + } +}; + } // namespace srsue void usage(char* prog) @@ -110,11 +182,35 @@ void parse_args(int argc, char** argv) } } +int ue_test(std::string name, ue_metrics_interface* ue) +{ + // the default metrics type for stdout output + metrics_stdout metrics_screen; + metrics_screen.set_ue_handle(ue); + + // the CSV file writer + metrics_csv metrics_file(csv_file_name); + metrics_file.set_ue_handle(ue); + + // create metrics hub and register metrics for stdout + srsran::metrics_hub metricshub; + metricshub.init(ue, period); + metricshub.add_listener(&metrics_screen); + metricshub.add_listener(&metrics_file); + + // enable printing + metrics_screen.toggle_print(true); + + std::cout << "Running " << name << " UE test for " << duration_s << " seconds .." << std::endl; + usleep(duration_s * 1e6); + + metricshub.stop(); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { - float period = 1.0; - ue_dummy ue; - if (argc < 3) { usage(argv[0]); exit(-1); @@ -122,26 +218,14 @@ int main(int argc, char** argv) parse_args(argc, argv); - // the default metrics type for stdout output - metrics_stdout metrics_screen; - metrics_screen.set_ue_handle(&ue); + eutra_ue_dummy eutra_ue; + TESTASSERT_SUCCESS(ue_test("EUTRA", &eutra_ue)); - // the CSV file writer - metrics_csv metrics_file(csv_file_name); - metrics_file.set_ue_handle(&ue); + nsa_ue_dummy nsa_ue; + TESTASSERT_SUCCESS(ue_test("NSA", &nsa_ue)); - // create metrics hub and register metrics for stdout - srsran::metrics_hub metricshub; - metricshub.init(&ue, period); - metricshub.add_listener(&metrics_screen); - metricshub.add_listener(&metrics_file); + sa_ue_dummy sa_ue; + TESTASSERT_SUCCESS(ue_test("SA", &sa_ue)); - // enable printing - metrics_screen.toggle_print(true); - - std::cout << "Running for 5 seconds .." << std::endl; - usleep(5e6); - - metricshub.stop(); - return 0; + return SRSRAN_SUCCESS; } diff --git a/srsue/test/ttcn3/CMakeLists.txt b/srsue/src/test/ttcn3/CMakeLists.txt similarity index 93% rename from srsue/test/ttcn3/CMakeLists.txt rename to srsue/src/test/ttcn3/CMakeLists.txt index 9dc670ea7..2b8980a2b 100644 --- a/srsue/test/ttcn3/CMakeLists.txt +++ b/srsue/src/test/ttcn3/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # diff --git a/srsue/test/ttcn3/README.md b/srsue/src/test/ttcn3/README.md similarity index 100% rename from srsue/test/ttcn3/README.md rename to srsue/src/test/ttcn3/README.md diff --git a/srsue/test/ttcn3/hdr/dut_utils.h b/srsue/src/test/ttcn3/hdr/dut_utils.h similarity index 97% rename from srsue/test/ttcn3/hdr/dut_utils.h rename to srsue/src/test/ttcn3/hdr/dut_utils.h index 528a16ae4..3fd5266e6 100644 --- a/srsue/test/ttcn3/hdr/dut_utils.h +++ b/srsue/src/test/ttcn3/hdr/dut_utils.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h similarity index 87% rename from srsue/test/ttcn3/hdr/lte_ttcn3_phy.h rename to srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h index aafe3f2d4..72a164968 100644 --- a/srsue/test/ttcn3/hdr/lte_ttcn3_phy.h +++ b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,9 +22,10 @@ #ifndef SRSUE_TTCN3_LTE_PHY_H #define SRSUE_TTCN3_LTE_PHY_H +#include "srsran/common/task_scheduler.h" #include "srsran/interfaces/ue_interfaces.h" #include "srsran/interfaces/ue_phy_interfaces.h" -#include "srsue/hdr/phy/ue_lte_phy_base.h" +#include "srsue/hdr/phy/ue_phy_base.h" #include "srsue/hdr/ue.h" #include "ttcn3_interfaces.h" #include @@ -34,7 +35,7 @@ using namespace srsran; namespace srsue { -class lte_ttcn3_phy : public ue_lte_phy_base +class lte_ttcn3_phy : public ue_phy_base, public phy_interface_stack_lte { public: void set_cells_to_meas(uint32_t earfcn, const std::set& pci) override; @@ -50,12 +51,9 @@ public: int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_); - int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) override; - - // ue_phy_base interface - int init(const phy_args_t& args_) override; void stop() override; void wait_initialize() override; + bool is_initialized() override; void start_plot() override; void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) override; std::string get_type() override; @@ -64,7 +62,6 @@ public: void set_cell_map(const cell_list_t& cells_); // phy_interface_rrc_lte - void enable_pregen_signals(bool enable) override; void deactivate_scells() override; void set_activation_deactivation_scell(uint32_t cmd, uint32_t tti) override; bool set_config(const srsran::phy_cfg_t& config, uint32_t cc_idx = 0) override; @@ -81,7 +78,7 @@ public: void set_mch_period_stop(uint32_t stop) override{}; // Cell search and selection procedures - bool cell_search() override; + bool cell_search(int earfcn) override; bool cell_select(phy_cell_t cell) override; bool cell_is_camping() override; @@ -105,10 +102,6 @@ public: void new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t ul_mac_grant); void new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t, const uint8_t* data); - // Radio interface - void radio_overflow() override; - void radio_failure() override; - void run_tti(); private: @@ -129,10 +122,10 @@ private: int prach_tti_tx = -1; - int sr_tx_tti = -1; - bool sr_pending = false; + std::atomic sr_tx_tti = {-1}; + std::atomic sr_pending = {false}; - std::mutex mutex; + std::mutex phy_mutex; srsran::task_scheduler task_sched; diff --git a/srsue/test/ttcn3/hdr/swappable_sink.h b/srsue/src/test/ttcn3/hdr/swappable_sink.h similarity index 97% rename from srsue/test/ttcn3/hdr/swappable_sink.h rename to srsue/src/test/ttcn3/hdr/swappable_sink.h index 56ca64a61..0d78f13c6 100644 --- a/srsue/test/ttcn3/hdr/swappable_sink.h +++ b/srsue/src/test/ttcn3/hdr/swappable_sink.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_common.h b/srsue/src/test/ttcn3/hdr/ttcn3_common.h similarity index 95% rename from srsue/test/ttcn3/hdr/ttcn3_common.h rename to srsue/src/test/ttcn3/hdr/ttcn3_common.h index 0aba8e2a4..94ab2d4d8 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_common.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_common.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_drb_interface.h b/srsue/src/test/ttcn3/hdr/ttcn3_drb_interface.h similarity index 98% rename from srsue/test/ttcn3/hdr/ttcn3_drb_interface.h rename to srsue/src/test/ttcn3/hdr/ttcn3_drb_interface.h index b4dc6536f..7c4b3bbc0 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_drb_interface.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_drb_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_helpers.h b/srsue/src/test/ttcn3/hdr/ttcn3_helpers.h similarity index 99% rename from srsue/test/ttcn3/hdr/ttcn3_helpers.h rename to srsue/src/test/ttcn3/hdr/ttcn3_helpers.h index 3d36c1c2f..74ceb1827 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_helpers.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_helpers.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_interfaces.h b/srsue/src/test/ttcn3/hdr/ttcn3_interfaces.h similarity index 99% rename from srsue/test/ttcn3/hdr/ttcn3_interfaces.h rename to srsue/src/test/ttcn3/hdr/ttcn3_interfaces.h index b174335eb..6d67d22a0 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_interfaces.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_interfaces.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_ip_ctrl_interface.h b/srsue/src/test/ttcn3/hdr/ttcn3_ip_ctrl_interface.h similarity index 96% rename from srsue/test/ttcn3/hdr/ttcn3_ip_ctrl_interface.h rename to srsue/src/test/ttcn3/hdr/ttcn3_ip_ctrl_interface.h index a82a3f029..b6137fbbb 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_ip_ctrl_interface.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_ip_ctrl_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_ip_sock_interface.h b/srsue/src/test/ttcn3/hdr/ttcn3_ip_sock_interface.h similarity index 98% rename from srsue/test/ttcn3/hdr/ttcn3_ip_sock_interface.h rename to srsue/src/test/ttcn3/hdr/ttcn3_ip_sock_interface.h index f302077f6..a4c062024 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_ip_sock_interface.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_ip_sock_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_port_handler.h b/srsue/src/test/ttcn3/hdr/ttcn3_port_handler.h similarity index 96% rename from srsue/test/ttcn3/hdr/ttcn3_port_handler.h rename to srsue/src/test/ttcn3/hdr/ttcn3_port_handler.h index ed1b8adea..8cdb288eb 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_port_handler.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_port_handler.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -29,6 +29,7 @@ #include "srsran/common/epoll_helper.h" #include "srsran/common/standard_streams.h" +#include "srsran/common/network_utils.h" #include "srsran/srslog/srslog.h" #include "ttcn3_common.h" #include @@ -188,10 +189,10 @@ public: // Port bind struct sockaddr_in bind_addr = {}; - bind_addr.sin_family = AF_INET; - inet_pton(AF_INET, net_ip.c_str(), &(bind_addr.sin_addr)); - bind_addr.sin_port = htons(net_port); - + if (not srsran::net_utils::set_sockaddr(&bind_addr, net_ip.c_str(), net_port)) { + srsran::console("Invalid net_ip: %s\n", net_ip.c_str()); + return SRSRAN_ERROR; + } int one = 1; setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); ret = bind(sock_fd, (struct sockaddr*)&bind_addr, sizeof(bind_addr)); diff --git a/srsue/test/ttcn3/hdr/ttcn3_srb_interface.h b/srsue/src/test/ttcn3/hdr/ttcn3_srb_interface.h similarity index 99% rename from srsue/test/ttcn3/hdr/ttcn3_srb_interface.h rename to srsue/src/test/ttcn3/hdr/ttcn3_srb_interface.h index 329a522bc..0391916dc 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_srb_interface.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_srb_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_sys_interface.h b/srsue/src/test/ttcn3/hdr/ttcn3_sys_interface.h similarity index 99% rename from srsue/test/ttcn3/hdr/ttcn3_sys_interface.h rename to srsue/src/test/ttcn3/hdr/ttcn3_sys_interface.h index afc337586..a28064677 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_sys_interface.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_sys_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/hdr/ttcn3_syssim.h b/srsue/src/test/ttcn3/hdr/ttcn3_syssim.h similarity index 95% rename from srsue/test/ttcn3/hdr/ttcn3_syssim.h rename to srsue/src/test/ttcn3/hdr/ttcn3_syssim.h index 06ea9f9e2..ea654e693 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_syssim.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_syssim.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -23,9 +23,9 @@ #define SRSUE_TTCN3_SYSSIM_H #include "srsran/mac/pdu_queue.h" +#include "srsran/rlc/rlc.h" #include "srsran/test/ue_test_interfaces.h" #include "srsran/upper/pdcp.h" -#include "srsran/upper/rlc.h" #include "ttcn3_common.h" #include "ttcn3_drb_interface.h" #include "ttcn3_ip_ctrl_interface.h" @@ -34,6 +34,7 @@ #include "ttcn3_sys_interface.h" #include "ttcn3_ue.h" #include "ttcn3_ut_interface.h" +#include #include class ttcn3_syssim : public syssim_interface_phy, @@ -112,7 +113,7 @@ public: uint32_t get_tti(); - void process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel); + void process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel, int ul_nof_prbs); void set_cell_config(const ttcn3_helpers::timing_info_t timing, const cell_config_t cell); void set_cell_config_impl(const cell_config_t cell); @@ -158,6 +159,7 @@ public: // RRC interface for PDCP, PDCP calls RRC to push RRC SDU void write_pdu(uint32_t lcid, unique_byte_buffer_t pdu); + void notify_pdcp_integrity_error(uint32_t lcid); // Not supported right now void write_pdu_bcch_bch(unique_byte_buffer_t pdu); @@ -165,6 +167,7 @@ public: void write_pdu_pcch(unique_byte_buffer_t pdu); void write_pdu_mch(uint32_t lcid, unique_byte_buffer_t pdu); void max_retx_attempted(); + void protocol_failure(); const char* get_rb_name(uint32_t lcid); @@ -176,6 +179,8 @@ public: bool sdu_queue_is_full(uint32_t lcid); + bool is_suspended(uint32_t lcid); + void set_as_security(const ttcn3_helpers::timing_info_t timing, const std::string cell_name, std::array k_rrc_enc_, @@ -232,8 +237,8 @@ private: all_args_t args = {}; // Simulator vars - ttcn3_ue* ue = nullptr; - bool running = false; + ttcn3_ue* ue = nullptr; + std::atomic running = {false}; typedef enum { UE_SWITCH_ON = 0, UE_SWITCH_OFF, ENABLE_DATA, DISABLE_DATA } ss_events_t; block_queue event_queue; @@ -248,7 +253,7 @@ private: uint32_t prach_preamble_index = 0; uint16_t dl_rnti = 0; int force_lcid = -1; - srsue::stack_test_dummy stack; + srsue::stack_test_dummy stack; // helper to run queue pending tasks bool last_dl_ndi[SRSRAN_FDD_NOF_HARQ] = {}; bool last_ul_ndi[SRSRAN_FDD_NOF_HARQ] = {}; @@ -282,6 +287,9 @@ private: std::vector cells; int32_t pcell_idx = -1; + // Main mutex to protect access from syssim's main thread (epoll handlers) and calls from UE's stack thread + std::mutex syssim_mutex; + // Internal function void update_cell_map(); bool syssim_has_cell(std::string cell_name); diff --git a/srsue/test/ttcn3/hdr/ttcn3_ue.h b/srsue/src/test/ttcn3/hdr/ttcn3_ue.h similarity index 86% rename from srsue/test/ttcn3/hdr/ttcn3_ue.h rename to srsue/src/test/ttcn3/hdr/ttcn3_ue.h index 638aff2e6..2f77fee6f 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_ue.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_ue.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -62,16 +62,15 @@ public: void write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu); void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu); int setup_if_addr(uint32_t eps_bearer_id, - uint32_t lcid, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str); - int update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid); bool is_running(); + int deactivate_eps_bearer(const uint32_t eps_bearer_id); + int apply_traffic_flow_template(const uint8_t& eps_bearer_id, - const uint8_t& lcid, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft); void set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms_ = 0); @@ -80,7 +79,7 @@ public: void send_queued_data(); - void loop_back_pdu_with_tft(uint32_t input_lcid, srsran::unique_byte_buffer_t pdu); + void loop_back_pdu_with_tft(srsran::unique_byte_buffer_t pdu); private: std::unique_ptr phy; @@ -91,9 +90,11 @@ private: test_loop_mode_state_t test_loop_mode = TEST_LOOP_INACTIVE; srsran::timer_handler::unique_timer pdu_delay_timer; - std::map > pdu_queue; // A PDU queue for each DRB + block_queue pdu_queue; // PDU for UL data tft_pdu_matcher tft_matcher; + int default_eps_bearer_id = -1; + all_args_t args = {}; }; diff --git a/srsue/test/ttcn3/hdr/ttcn3_ut_interface.h b/srsue/src/test/ttcn3/hdr/ttcn3_ut_interface.h similarity index 98% rename from srsue/test/ttcn3/hdr/ttcn3_ut_interface.h rename to srsue/src/test/ttcn3/hdr/ttcn3_ut_interface.h index e25250f32..1402d480f 100644 --- a/srsue/test/ttcn3/hdr/ttcn3_ut_interface.h +++ b/srsue/src/test/ttcn3/hdr/ttcn3_ut_interface.h @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/test/ttcn3/src/CMakeLists.txt b/srsue/src/test/ttcn3/src/CMakeLists.txt similarity index 82% rename from srsue/test/ttcn3/src/CMakeLists.txt rename to srsue/src/test/ttcn3/src/CMakeLists.txt index 4320c132b..972567569 100644 --- a/srsue/test/ttcn3/src/CMakeLists.txt +++ b/srsue/src/test/ttcn3/src/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -25,12 +25,15 @@ set(LINK_LIBRARIES srsue_stack srsue_upper srsue_rrc srsue_rrc_nr - srsran_upper + srsran_gtpu + srsran_pdcp + srsran_rlc srsran_common srsran_mac srsran_phy srsran_radio - srsran_upper + srsran_gtpu + srsran_pdcp srsue_phy srsue_mac srsue_mac_nr @@ -41,4 +44,4 @@ set(LINK_LIBRARIES srsue_stack target_link_libraries(ttcn3_dut ${LINK_LIBRARIES}) -include_directories(${PROJECT_SOURCE_DIR}/srsue/test/ttcn3/hdr) +include_directories(${PROJECT_SOURCE_DIR}/srsue/src/test/ttcn3/hdr) diff --git a/srsue/test/ttcn3/src/lte_ttcn3_phy.cc b/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc similarity index 83% rename from srsue/test/ttcn3/src/lte_ttcn3_phy.cc rename to srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc index 68b0a6694..72ea7096c 100644 --- a/srsue/test/ttcn3/src/lte_ttcn3_phy.cc +++ b/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -36,17 +36,6 @@ int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_ stack = stack_; syssim = syssim_; - return init(args_); -} - -int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) -{ - return init(args_); -} - -// ue_phy_base interface -int lte_ttcn3_phy::init(const phy_args_t& args_) -{ logger.set_level(srslog::str_to_basic_level(args_.log.phy_level)); logger.set_hex_dump_max_size(-1); @@ -55,6 +44,11 @@ int lte_ttcn3_phy::init(const phy_args_t& args_) void lte_ttcn3_phy::stop(){}; +bool lte_ttcn3_phy::is_initialized() +{ + return true; +} + void lte_ttcn3_phy::wait_initialize() {} void lte_ttcn3_phy::start_plot() {} @@ -64,17 +58,12 @@ void lte_ttcn3_phy::get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* // The interface for the SS void lte_ttcn3_phy::set_cell_map(const cell_list_t& cells_) { - std::lock_guard lock(mutex); + std::lock_guard lock(phy_mutex); cells = cells_; } void lte_ttcn3_phy::set_config_tdd(srsran_tdd_config_t& tdd_config) {} -void lte_ttcn3_phy::enable_pregen_signals(bool enable) -{ - logger.debug("%s not implemented.", __FUNCTION__); -} - void lte_ttcn3_phy::deactivate_scells() { logger.debug("%s not implemented.", __FUNCTION__); @@ -107,9 +96,9 @@ void lte_ttcn3_phy::meas_stop() {} // configured by the SS, including the ones that we should not even detect because // their power is too weak. The cell search should only report the cells that // are actually visible though. -bool lte_ttcn3_phy::cell_search() +bool lte_ttcn3_phy::cell_search(int earfcn) { - std::lock_guard lock(mutex); + std::lock_guard lock(phy_mutex); logger.info("Running cell search in PHY"); @@ -149,28 +138,44 @@ bool lte_ttcn3_phy::cell_search() return true; } +// Called from RRC/Stack thread bool lte_ttcn3_phy::cell_select(phy_cell_t rrc_cell) { - // try to find RRC cell in current cell map - for (auto& cell : cells) { - if (cell.info.id == rrc_cell.pci && cell.earfcn == rrc_cell.earfcn) { - if (cell.power >= SUITABLE_CELL_RS_EPRE) { - pcell = cell; - pcell_set = true; - syssim->select_cell(pcell.info); - logger.info("Select PCell with %.2f on PCI=%d on EARFCN=%d.", cell.power, rrc_cell.pci, rrc_cell.earfcn); - } else { - pcell_set = false; - logger.error("Power of selected cell too low (%.2f < %.2f)", cell.power, SUITABLE_CELL_RS_EPRE); - } + bool ret = false; - stack->cell_select_complete(pcell_set); - return true; + { + std::lock_guard lock(phy_mutex); + // try to find RRC cell in current cell map + for (auto& cell : cells) { + if (cell.info.id == rrc_cell.pci && cell.earfcn == rrc_cell.earfcn) { + if (cell.power >= SUITABLE_CELL_RS_EPRE) { + pcell = cell; + pcell_set = true; + logger.info("Select PCell with %.2f on PCI=%d on EARFCN=%d.", cell.power, rrc_cell.pci, rrc_cell.earfcn); + } else { + pcell_set = false; + logger.error("Power of selected cell too low (%.2f < %.2f)", cell.power, SUITABLE_CELL_RS_EPRE); + } + // update return value + ret = pcell_set; + break; + } } } - logger.error("Couldn't find RRC cell with PCI=%d on EARFCN=%d in cell map.", rrc_cell.pci, rrc_cell.earfcn); - return false; + if (ret) { + // cell has been selected + syssim->select_cell(pcell.info); + } else { + logger.error( + "Couldn't find (suitable) RRC cell with PCI=%d on EARFCN=%d in cell map.", rrc_cell.pci, rrc_cell.earfcn); + } + + // inform stack about result asynchronously + task_sched.defer_task([this, ret]() { stack->cell_select_complete(ret); }); + + // regardless of actual result, return True to tell RRC that we entered the cell select state at PHY + return true; } bool lte_ttcn3_phy::cell_is_camping() @@ -183,15 +188,19 @@ bool lte_ttcn3_phy::cell_is_camping() } // The interface for MAC (called from Stack thread context) - void lte_ttcn3_phy::prach_send(uint32_t preamble_idx, int allowed_subframe, float target_power_dbm, float ta_base_sec) { - std::lock_guard lock(mutex); - logger.info("Sending PRACH with preamble %d on PCID=%d", preamble_idx, pcell.info.id); - prach_tti_tx = current_tti; - ra_trans_cnt++; + uint32_t pcell_pci = 0; + { + std::lock_guard lock(phy_mutex); + pcell_pci = pcell.info.id; - syssim->prach_indication(preamble_idx, pcell.info.id); + logger.info("Sending PRACH with preamble %d on PCID=%d", preamble_idx, pcell_pci); + prach_tti_tx = current_tti; + ra_trans_cnt++; + } + + syssim->prach_indication(preamble_idx, pcell_pci); }; std::string lte_ttcn3_phy::get_type() @@ -201,7 +210,7 @@ std::string lte_ttcn3_phy::get_type() phy_interface_mac_lte::prach_info_t lte_ttcn3_phy::prach_get_info() { - std::lock_guard lock(mutex); + std::lock_guard lock(phy_mutex); prach_info_t info = {}; if (prach_tti_tx != -1) { info.is_transmitted = true; @@ -245,7 +254,7 @@ void lte_ttcn3_phy::set_rar_grant(uint8_t grant_payload[SRSRAN_RAR_GRANT_LEN], u // Called from the SYSSIM to configure the current TTI void lte_ttcn3_phy::set_current_tti(uint32_t tti) { - std::lock_guard lock(mutex); + std::lock_guard lock(phy_mutex); current_tti = tti; run_tti(); @@ -254,6 +263,7 @@ void lte_ttcn3_phy::set_current_tti(uint32_t tti) // Called from MAC to retrieve the current TTI uint32_t lte_ttcn3_phy::get_current_tti() { + std::lock_guard lock(phy_mutex); return current_tti; } @@ -273,7 +283,7 @@ float lte_ttcn3_phy::get_pathloss_db() // Calling function hold mutex void lte_ttcn3_phy::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t ul_mac_grant) { - std::lock_guard lock(mutex); + std::lock_guard lock(phy_mutex); mac_interface_phy_lte::tb_action_ul_t ul_action = {}; @@ -289,7 +299,7 @@ void lte_ttcn3_phy::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t ul_mac_gr // Provides DL grant, copy data into DL action and pass up to MAC void lte_ttcn3_phy::new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t dl_grant, const uint8_t* data) { - std::lock_guard lock(mutex); + std::lock_guard lock(phy_mutex); if (data == nullptr) { logger.error("Invalid data buffer passed"); @@ -328,16 +338,6 @@ void lte_ttcn3_phy::new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t dl stack->tb_decoded(cc_idx, dl_grant, dl_ack); } -void lte_ttcn3_phy::radio_overflow() -{ - logger.debug("%s not implemented.", __FUNCTION__); -} - -void lte_ttcn3_phy::radio_failure() -{ - logger.debug("%s not implemented.", __FUNCTION__); -} - // Calling function set_tti() is holding mutex void lte_ttcn3_phy::run_tti() { diff --git a/srsue/test/ttcn3/src/ttcn3_dut.cc b/srsue/src/test/ttcn3/src/ttcn3_dut.cc similarity index 94% rename from srsue/test/ttcn3/src/ttcn3_dut.cc rename to srsue/src/test/ttcn3/src/ttcn3_dut.cc index d168abd43..decd847f8 100644 --- a/srsue/test/ttcn3/src/ttcn3_dut.cc +++ b/srsue/src/test/ttcn3/src/ttcn3_dut.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -20,6 +20,7 @@ */ #include "srsran/build_info.h" +#include "srsran/common/tsan_options.h" #include "srsran/srslog/srslog.h" #include "srsue/hdr/ue.h" #include "swappable_sink.h" @@ -81,6 +82,8 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[]) all_args_t all_args = {}; + all_args.stack.pkt_trace.enable = "mac,nas"; + all_args.stack.pkt_trace.mac_pcap.enable = args->mac_pcap.enable; all_args.stack.pkt_trace.mac_pcap.filename = args->mac_pcap.filename; @@ -92,6 +95,8 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[]) all_args.log.all_hex_limit = args->log_hex_level; all_args.phy.log.phy_level = args->log_level; + + all_args.stack.log.stack_level = args->log_level; all_args.stack.log.mac_level = args->log_level; all_args.stack.log.rlc_level = args->log_level; all_args.stack.log.pdcp_level = args->log_level; @@ -100,6 +105,7 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[]) all_args.stack.log.gw_level = args->log_level; all_args.stack.log.usim_level = args->log_level; all_args.phy.log.phy_hex_limit = args->log_hex_level; + all_args.stack.log.stack_hex_limit = args->log_hex_level; all_args.stack.log.mac_hex_limit = args->log_hex_level; all_args.stack.log.rlc_hex_limit = args->log_hex_level; all_args.stack.log.pdcp_hex_limit = args->log_hex_level; @@ -108,7 +114,7 @@ all_args_t parse_args(ttcn3_dut_args_t* args, int argc, char* argv[]) all_args.stack.log.gw_hex_limit = args->log_hex_level; all_args.stack.log.usim_hex_limit = args->log_hex_level; - all_args.stack.sync_queue_size = 1; + all_args.stack.sync_queue_size = MULTIQUEUE_DEFAULT_CAPACITY; return all_args; } diff --git a/srsue/test/ttcn3/src/ttcn3_syssim.cc b/srsue/src/test/ttcn3/src/ttcn3_syssim.cc similarity index 97% rename from srsue/test/ttcn3/src/ttcn3_syssim.cc rename to srsue/src/test/ttcn3/src/ttcn3_syssim.cc index 46d39d034..70a50dd00 100644 --- a/srsue/test/ttcn3/src/ttcn3_syssim.cc +++ b/srsue/src/test/ttcn3/src/ttcn3_syssim.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,13 +19,13 @@ * */ -#include "srsue/test/ttcn3/hdr/ttcn3_syssim.h" +#include "ttcn3_syssim.h" #include "dut_utils.h" #include "srsran/mac/pdu_queue.h" +#include "srsran/rlc/rlc.h" #include "srsran/srslog/srslog.h" #include "srsran/test/ue_test_interfaces.h" #include "srsran/upper/pdcp.h" -#include "srsran/upper/rlc.h" #include "swappable_sink.h" #include "ttcn3_common.h" #include "ttcn3_drb_interface.h" @@ -58,10 +58,12 @@ ttcn3_syssim::ttcn3_syssim(ttcn3_ue* ue_) : mac_msg_dl(20, ss_mac_logger), pdus(logger), ue(ue_), - signal_handler(&running), + signal_handler(running), timer_handler(create_tti_timer(), [&](uint64_t res) { new_tti_indication(res); }) { - if (ue->init(all_args_t{}, this, "INIT_TEST") != SRSRAN_SUCCESS) { + all_args_t args = {}; + args.stack.sync_queue_size = MULTIQUEUE_DEFAULT_CAPACITY; + if (ue->init(args, this, "INIT_TEST") != SRSRAN_SUCCESS) { ue->stop(); fprintf(stderr, "Couldn't initialize UE.\n"); } @@ -202,6 +204,8 @@ int ttcn3_syssim::add_port_handler() ///< Function called by epoll timer handler when TTI timer expires void ttcn3_syssim::new_tti_indication(uint64_t res) { + std::lock_guard lock(syssim_mutex); + tti = (tti + 1) % 10240; logger.set_context(tti); @@ -253,8 +257,8 @@ void ttcn3_syssim::new_tti_indication(uint64_t res) // DL/UL processing if UE has selected cell dl_rnti = ue->get_dl_sched_rnti(tti); - if (SRSRAN_RNTI_ISSI(dl_rnti)) { - // deliver SIBs one after another + if (SRSRAN_RNTI_ISSI(dl_rnti) && (tti % 2 == 0)) { + // deliver SIBs one after another in every other TTI mac_interface_phy_lte::mac_grant_dl_t dl_grant = {}; dl_grant.tti = tti; dl_grant.pid = get_pid(tti); @@ -262,7 +266,7 @@ void ttcn3_syssim::new_tti_indication(uint64_t res) dl_grant.tb[0].tbs = cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->N_bytes; dl_grant.tb[0].ndi = get_ndi_for_new_dl_tx(tti); ue->new_tb(dl_grant, cells[pcell_idx]->sibs[cells[pcell_idx]->sib_idx]->msg); - logger.info("Delivered SIB%d for pcell_idx=%d", cells[pcell_idx]->sib_idx, pcell_idx); + logger.info("Delivered SIB%d for pcell_idx=%d", cells[pcell_idx]->sib_idx + 1, pcell_idx); cells[pcell_idx]->sib_idx = (cells[pcell_idx]->sib_idx + 1) % cells[pcell_idx]->sibs.size(); } else if (SRSRAN_RNTI_ISRAR(dl_rnti)) { if (prach_tti != -1) { @@ -393,6 +397,7 @@ void ttcn3_syssim::stop() void ttcn3_syssim::reset() { + std::lock_guard lock(syssim_mutex); logger.info("Resetting SS"); cells.clear(); pcell_idx = -1; @@ -489,9 +494,11 @@ void ttcn3_syssim::disable_data() event_queue.push(DISABLE_DATA); } -// Called from PHY but always from the SS main thread with lock being hold +// Called from PHY through RA procedure running on Stack thread void ttcn3_syssim::prach_indication(uint32_t preamble_index_, const uint32_t& cell_id) { + std::lock_guard lock(syssim_mutex); + // verify that UE intends to send PRACH on current Pcell if (cells[pcell_idx]->config.phy_cell.id != cell_id) { logger.error( @@ -768,7 +775,7 @@ uint32_t ttcn3_syssim::get_tti() return tti; } -void ttcn3_syssim::process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel) {} +void ttcn3_syssim::process_pdu(uint8_t* buff, uint32_t len, pdu_queue::channel_t channel, int ul_nof_prbs) {} void ttcn3_syssim::set_cell_config(const ttcn3_helpers::timing_info_t timing, const cell_config_t cell) { @@ -1143,6 +1150,15 @@ void ttcn3_syssim::max_retx_attempted() { logger.error("%s not implemented.", __FUNCTION__); } +void ttcn3_syssim::protocol_failure() +{ + logger.error("%s not implemented.", __FUNCTION__); +} + +void ttcn3_syssim::notify_pdcp_integrity_error(uint32_t lcid) +{ + logger.error("%s not implemented.", __FUNCTION__); +} const char* ttcn3_syssim::get_rb_name(uint32_t lcid) { @@ -1184,6 +1200,11 @@ bool ttcn3_syssim::sdu_queue_is_full(uint32_t lcid) return false; } +bool ttcn3_syssim::is_suspended(uint32_t lcid) +{ + return false; +} + void ttcn3_syssim::set_as_security(const ttcn3_helpers::timing_info_t timing, const std::string cell_name, std::array k_rrc_enc_, @@ -1261,8 +1282,10 @@ void ttcn3_syssim::release_as_security_impl(const std::string cell_name) cell->pending_bearer_config.clear(); } +// Called from PHY in Stack context void ttcn3_syssim::select_cell(srsran_cell_t phy_cell) { + std::lock_guard lock(syssim_mutex); // find matching cell in SS cell list for (uint32_t i = 0; i < cells.size(); ++i) { if (cells[i]->config.phy_cell.id == phy_cell.id) { diff --git a/srsue/test/ttcn3/src/ttcn3_ue.cc b/srsue/src/test/ttcn3/src/ttcn3_ue.cc similarity index 80% rename from srsue/test/ttcn3/src/ttcn3_ue.cc rename to srsue/src/test/ttcn3/src/ttcn3_ue.cc index bbc1c2654..7bddde55f 100644 --- a/srsue/test/ttcn3/src/ttcn3_ue.cc +++ b/srsue/src/test/ttcn3/src/ttcn3_ue.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -19,7 +19,7 @@ * */ -#include "srsue/test/ttcn3/hdr/ttcn3_ue.h" +#include "ttcn3_ue.h" #include "lte_ttcn3_phy.h" #include "srsue/hdr/stack/ue_stack_lte.h" #include @@ -59,24 +59,18 @@ int ttcn3_ue::init(all_args_t args, syssim_interface_phy* syssim_, const std::st args.phy.dl_earfcn = "3400"; args.rf.type = "none"; - args.stack.type = "lte"; args.phy.type = "lte_ttcn3"; // Instantiate layers and stack together our UE - if (args.stack.type == "lte") { - stack = std::unique_ptr(new ue_stack_lte); - if (!stack) { - srsran::console("Error creating LTE stack instance.\n"); - return SRSRAN_ERROR; - } + stack = std::unique_ptr(new ue_stack_lte); + if (!stack) { + srsran::console("Error creating LTE stack instance.\n"); + return SRSRAN_ERROR; + } - phy = std::unique_ptr(new srsue::lte_ttcn3_phy); - if (!phy) { - srsran::console("Error creating LTE PHY instance.\n"); - return SRSRAN_ERROR; - } - } else { - srsran::console("Invalid stack type %s. Supported values are [lte].\n", args.stack.type.c_str()); + phy = std::unique_ptr(new srsue::lte_ttcn3_phy); + if (!phy) { + srsran::console("Error creating LTE PHY instance.\n"); return SRSRAN_ERROR; } @@ -117,6 +111,7 @@ bool ttcn3_ue::switch_on() bool ttcn3_ue::switch_off() { + tft_matcher.reset(); // empty all TFTs return stack->switch_off(); } @@ -162,6 +157,7 @@ void ttcn3_ue::add_mch_port(uint32_t lcid, uint32_t port) {} void ttcn3_ue::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) { logger.debug(pdu->msg, pdu->N_bytes, "Rx PDU (%d B) on lcid=%d", pdu->N_bytes, lcid); + switch (test_loop_mode) { case TEST_LOOP_INACTIVE: logger.warning("Test loop inactive. Dropping PDU."); @@ -172,13 +168,13 @@ void ttcn3_ue::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) case TEST_LOOP_MODE_B_ACTIVE: // Section 5.4.4 in TS 36.509 if (pdu_delay_timer.is_running()) { - pdu_queue[lcid].push(std::move(pdu)); + pdu_queue.push(std::move(pdu)); } else { if (pdu_delay_timer.is_set()) { - pdu_queue[lcid].push(std::move(pdu)); + pdu_queue.push(std::move(pdu)); pdu_delay_timer.run(); // timer is already set } else { - loop_back_pdu_with_tft(lcid, std::move(pdu)); + loop_back_pdu_with_tft(std::move(pdu)); } } break; @@ -189,12 +185,12 @@ void ttcn3_ue::write_pdu(uint32_t lcid, srsran::unique_byte_buffer_t pdu) } void ttcn3_ue::write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {} int ttcn3_ue::setup_if_addr(uint32_t eps_bearer_id, - uint32_t lcid, uint8_t pdn_type, uint32_t ip_addr, uint8_t* ipv6_if_id, char* err_str) { + default_eps_bearer_id = static_cast(eps_bearer_id); return 0; } @@ -203,16 +199,18 @@ bool ttcn3_ue::is_running() return true; } -int ttcn3_ue::update_lcid(uint32_t eps_bearer_id, uint32_t new_lcid) +int ttcn3_ue::deactivate_eps_bearer(const uint32_t eps_bearer_id) { + if (default_eps_bearer_id == static_cast(eps_bearer_id)) { + default_eps_bearer_id = -1; + } return SRSRAN_SUCCESS; } int ttcn3_ue::apply_traffic_flow_template(const uint8_t& eps_bearer_id, - const uint8_t& lcid, const LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft) { - return tft_matcher.apply_traffic_flow_template(eps_bearer_id, lcid, tft); + return tft_matcher.apply_traffic_flow_template(eps_bearer_id, tft); } void ttcn3_ue::set_test_loop_mode(const test_loop_mode_state_t mode, const uint32_t ip_pdu_delay_ms_) @@ -258,18 +256,27 @@ void ttcn3_ue::send_queued_data() return; } - for (auto& bearer_pdu_queue : pdu_queue) { - logger.info("Delivering %zd buffered PDUs for LCID=%d", bearer_pdu_queue.second.size(), bearer_pdu_queue.first); - while (not bearer_pdu_queue.second.empty()) { - srsran::unique_byte_buffer_t pdu; - bearer_pdu_queue.second.try_pop(&pdu); - loop_back_pdu_with_tft(bearer_pdu_queue.first, std::move(pdu)); - } + logger.info("Delivering %zd buffered UL PDUs", pdu_queue.size()); + while (not pdu_queue.empty()) { + srsran::unique_byte_buffer_t pdu; + pdu_queue.try_pop(&pdu); + loop_back_pdu_with_tft(std::move(pdu)); } } -void ttcn3_ue::loop_back_pdu_with_tft(uint32_t input_lcid, srsran::unique_byte_buffer_t pdu) +void ttcn3_ue::loop_back_pdu_with_tft(srsran::unique_byte_buffer_t pdu) { - logger.info(pdu->msg, pdu->N_bytes, "Rx PDU (%d B) on lcid=%d, looping back", pdu->N_bytes, input_lcid); - stack->write_sdu(input_lcid, std::move(pdu)); + if (default_eps_bearer_id == -1) { + logger.error("No default EPS bearer activated. Dropping PDU."); + } + + uint8_t target_eps_bearer_id = default_eps_bearer_id; + if (tft_matcher.check_tft_filter_match(pdu, target_eps_bearer_id) == SRSRAN_SUCCESS) { + logger.debug("Updated EPS bearer"); + } + + logger.info( + pdu->msg, pdu->N_bytes, "Rx PDU (%d B) looping back on eps_bearer_id=%d", pdu->N_bytes, target_eps_bearer_id); + + stack->write_sdu(target_eps_bearer_id, std::move(pdu)); } diff --git a/srsue/test/ttcn3/test/CMakeLists.txt b/srsue/src/test/ttcn3/test/CMakeLists.txt similarity index 85% rename from srsue/test/ttcn3/test/CMakeLists.txt rename to srsue/src/test/ttcn3/test/CMakeLists.txt index 2a256d0ca..e8ccf538c 100644 --- a/srsue/test/ttcn3/test/CMakeLists.txt +++ b/srsue/src/test/ttcn3/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -18,7 +18,7 @@ # and at http://www.gnu.org/licenses/. # -include_directories(${PROJECT_SOURCE_DIR}/srsue/test/ttcn3/hdr) +include_directories(${PROJECT_SOURCE_DIR}/srsue/src/test/ttcn3/hdr) add_executable(rapidjson_test rapidjson_test.cc) target_link_libraries(rapidjson_test srsran_common) diff --git a/srsue/test/ttcn3/test/rapidjson_test.cc b/srsue/src/test/ttcn3/test/rapidjson_test.cc similarity index 97% rename from srsue/test/ttcn3/test/rapidjson_test.cc rename to srsue/src/test/ttcn3/test/rapidjson_test.cc index ee967033d..9cffeb1b0 100644 --- a/srsue/test/ttcn3/test/rapidjson_test.cc +++ b/srsue/src/test/ttcn3/test/rapidjson_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -22,7 +22,6 @@ #include "srsran/common/test_common.h" #include "ttcn3_helpers.h" #include -#include #include #include diff --git a/srsue/test/ttcn3/test/ttcn3_if_handler_test.cc b/srsue/src/test/ttcn3/test/ttcn3_if_handler_test.cc similarity index 96% rename from srsue/test/ttcn3/test/ttcn3_if_handler_test.cc rename to srsue/src/test/ttcn3/test/ttcn3_if_handler_test.cc index 208add09a..47060a709 100644 --- a/srsue/test/ttcn3/test/ttcn3_if_handler_test.cc +++ b/srsue/src/test/ttcn3/test/ttcn3_if_handler_test.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index 5e76e7198..4da91afaa 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -1,5 +1,5 @@ /** - * Copyright 2013-2021 Software Radio Systems Limited + * Copyright 2013-2022 Software Radio Systems Limited * * This file is part of srsRAN. * @@ -21,12 +21,14 @@ #include "srsue/hdr/ue.h" #include "srsran/build_info.h" +#include "srsran/common/standard_streams.h" #include "srsran/common/string_helpers.h" #include "srsran/radio/radio.h" #include "srsran/radio/radio_null.h" #include "srsran/srsran.h" +#include "srsue/hdr/phy/dummy_phy.h" #include "srsue/hdr/phy/phy.h" -#include "srsue/hdr/phy/vnf_phy_nr.h" +#include "srsue/hdr/phy/phy_nr_sa.h" #include "srsue/hdr/stack/ue_stack_lte.h" #include "srsue/hdr/stack/ue_stack_nr.h" #include @@ -63,113 +65,112 @@ int ue::init(const all_args_t& args_) } // Instantiate layers and stack together our UE - if (args.stack.type == "lte") { - std::unique_ptr lte_stack(new ue_stack_lte); - if (!lte_stack) { - srsran::console("Error creating LTE stack instance.\n"); + std::unique_ptr lte_stack(new ue_stack_lte); + if (!lte_stack) { + srsran::console("Error creating LTE stack instance.\n"); + return SRSRAN_ERROR; + } + + std::unique_ptr gw_ptr(new gw(srslog::fetch_basic_logger("GW", false))); + if (!gw_ptr) { + srsran::console("Error creating a GW instance.\n"); + return SRSRAN_ERROR; + } + + std::unique_ptr lte_radio = std::unique_ptr(new srsran::radio); + if (!lte_radio) { + srsran::console("Error creating radio multi instance.\n"); + return SRSRAN_ERROR; + } + + srsue::phy_args_nr_t phy_args_nr = {}; + phy_args_nr.max_nof_prb = args.phy.nr_max_nof_prb; + phy_args_nr.rf_channel_offset = args.phy.nof_lte_carriers; + phy_args_nr.nof_carriers = args.phy.nof_nr_carriers; + phy_args_nr.nof_phy_threads = args.phy.nof_phy_threads; + phy_args_nr.worker_cpu_mask = args.phy.worker_cpu_mask; + phy_args_nr.log = args.phy.log; + phy_args_nr.store_pdsch_ko = args.phy.nr_store_pdsch_ko; + phy_args_nr.srate_hz = args.rf.srate_hz; + + // init layers + if (args.phy.nof_lte_carriers == 0) { + // SA mode + std::unique_ptr nr_phy = std::unique_ptr(new srsue::phy_nr_sa("PHY-SA")); + if (!nr_phy) { + srsran::console("Error creating NR PHY instance.\n"); return SRSRAN_ERROR; } - std::unique_ptr gw_ptr(new gw()); - if (!gw_ptr) { - srsran::console("Error creating a GW instance.\n"); + // In SA mode, pass the NR SA phy to the radio + if (lte_radio->init(args.rf, nr_phy.get())) { + srsran::console("Error initializing radio.\n"); + return SRSRAN_ERROR; + } + if (nr_phy->init(phy_args_nr, lte_stack.get(), lte_radio.get())) { + srsran::console("Error initializing PHY NR SA.\n"); + ret = SRSRAN_ERROR; + } + + std::unique_ptr dummy_lte_phy = std::unique_ptr(new srsue::dummy_phy); + if (!dummy_lte_phy) { + srsran::console("Error creating dummy LTE PHY instance.\n"); return SRSRAN_ERROR; } + // In SA mode, pass NR PHY pointer to stack + args.stack.sa_mode = true; + if (lte_stack->init(args.stack, dummy_lte_phy.get(), nr_phy.get(), gw_ptr.get())) { + srsran::console("Error initializing stack.\n"); + ret = SRSRAN_ERROR; + } + phy = std::move(nr_phy); + dummy_phy = std::move(dummy_lte_phy); + } else { + // LTE or NSA mode std::unique_ptr lte_phy = std::unique_ptr(new srsue::phy); if (!lte_phy) { srsran::console("Error creating LTE PHY instance.\n"); return SRSRAN_ERROR; } - std::unique_ptr lte_radio = std::unique_ptr(new srsran::radio); - if (!lte_radio) { - srsran::console("Error creating radio multi instance.\n"); - return SRSRAN_ERROR; - } - - // init layers if (lte_radio->init(args.rf, lte_phy.get())) { srsran::console("Error initializing radio.\n"); return SRSRAN_ERROR; } - // from here onwards do not exit immediately if something goes wrong as sub-layers may already use interfaces if (lte_phy->init(args.phy, lte_stack.get(), lte_radio.get())) { srsran::console("Error initializing PHY.\n"); ret = SRSRAN_ERROR; } - - srsue::phy_args_nr_t phy_args_nr = {}; - phy_args_nr.max_nof_prb = args.phy.nr_max_nof_prb; - phy_args_nr.nof_carriers = args.phy.nof_nr_carriers; - phy_args_nr.nof_phy_threads = args.phy.nof_phy_threads; - phy_args_nr.worker_cpu_mask = args.phy.worker_cpu_mask; - phy_args_nr.log = args.phy.log; - if (lte_phy->init(phy_args_nr, lte_stack.get(), lte_radio.get())) { - srsran::console("Error initializing NR PHY.\n"); - ret = SRSRAN_ERROR; + if (args.phy.nof_nr_carriers > 0) { + if (lte_phy->init(phy_args_nr, lte_stack.get(), lte_radio.get())) { + srsran::console("Error initializing NR PHY.\n"); + ret = SRSRAN_ERROR; + } } - if (lte_stack->init(args.stack, lte_phy.get(), lte_phy.get(), gw_ptr.get())) { srsran::console("Error initializing stack.\n"); ret = SRSRAN_ERROR; } + phy = std::move(lte_phy); + } - if (gw_ptr->init(args.gw, lte_stack.get())) { - srsran::console("Error initializing GW.\n"); - ret = SRSRAN_ERROR; - } - - // move ownership - stack = std::move(lte_stack); - gw_inst = std::move(gw_ptr); - phy = std::move(lte_phy); - radio = std::move(lte_radio); - } else if (args.stack.type == "nr") { - logger.info("Initializing NR stack"); - std::unique_ptr nr_stack(new srsue::ue_stack_nr()); - std::unique_ptr nr_radio(new srsran::radio_null); - std::unique_ptr nr_phy(new srsue::vnf_phy_nr); - std::unique_ptr gw_ptr(new gw()); - - // Init layers - if (nr_radio->init(args.rf, nullptr)) { - srsran::console("Error initializing radio.\n"); - return SRSRAN_ERROR; - } - - if (nr_phy->init(args.phy, nr_stack.get())) { - srsran::console("Error initializing PHY.\n"); - return SRSRAN_ERROR; - } - - if (nr_stack->init(args.stack, nr_phy.get(), gw_ptr.get())) { - srsran::console("Error initializing stack.\n"); - return SRSRAN_ERROR; - } - - if (gw_ptr->init(args.gw, nr_stack.get())) { - srsran::console("Error initializing GW.\n"); - return SRSRAN_ERROR; - } - - // move ownership - stack = std::move(nr_stack); - gw_inst = std::move(gw_ptr); - phy = std::move(nr_phy); - radio = std::move(nr_radio); - } else { - srsran::console("Invalid stack type %s. Supported values are [lte].\n", args.stack.type.c_str()); + if (gw_ptr->init(args.gw, lte_stack.get())) { + srsran::console("Error initializing GW.\n"); ret = SRSRAN_ERROR; } + // move ownership + stack = std::move(lte_stack); + gw_inst = std::move(gw_ptr); + radio = std::move(lte_radio); + if (phy) { srsran::console("Waiting PHY to initialize ... "); phy->wait_initialize(); srsran::console("done!\n"); } - return ret; } @@ -264,6 +265,7 @@ int ue::parse_args(const all_args_t& args_) if (not args.stack.rrc_nr.supported_bands_nr_str.empty()) { // Populates supported bands srsran::string_parse_list(args.stack.rrc_nr.supported_bands_nr_str, ',', args.stack.rrc_nr.supported_bands_nr); + args.stack.rrc.supported_bands_nr = args.stack.rrc_nr.supported_bands_nr; } else { logger.error("Error: rat.nr.bands list is empty"); srsran::console("Error: rat.nr.bands list is empty\n"); @@ -277,6 +279,47 @@ int ue::parse_args(const all_args_t& args_) // Consider Carrier Aggregation support if more than one args.stack.rrc.support_ca = (args.phy.nof_lte_carriers > 1); + // Make sure fix sampling rate is set for SA mode + if (args.phy.nof_lte_carriers == 0 and not std::isnormal(args.rf.srate_hz)) { + srsran::console("Error. NR Standalone PHY requires a fixed RF sampling rate.\n"); + return SRSRAN_ERROR; + } + + // SA params + if (args.phy.nof_lte_carriers == 0 && args.phy.nof_nr_carriers > 0) { + // Update NAS-5G args + args.stack.nas_5g.ia5g = args.stack.nas.eia; + args.stack.nas_5g.ea5g = args.stack.nas.eea; + args.stack.nas_5g.pdu_session_cfgs.push_back({args.stack.nas.apn_name}); + } + + // Validate the CFR args + srsran_cfr_cfg_t cfr_test_cfg = {}; + cfr_test_cfg.cfr_enable = args.phy.cfr_args.enable; + cfr_test_cfg.cfr_mode = args.phy.cfr_args.mode; + cfr_test_cfg.alpha = args.phy.cfr_args.strength; + cfr_test_cfg.manual_thr = args.phy.cfr_args.manual_thres; + cfr_test_cfg.max_papr_db = args.phy.cfr_args.auto_target_papr; + cfr_test_cfg.ema_alpha = args.phy.cfr_args.ema_alpha; + + if (!srsran_cfr_params_valid(&cfr_test_cfg)) { + srsran::console("Invalid CFR parameters: cfr_mode=%d, alpha=%.2f, manual_thr=%.2f, \n " + "max_papr_db=%.2f, ema_alpha=%.2f\n", + cfr_test_cfg.cfr_mode, + cfr_test_cfg.alpha, + cfr_test_cfg.manual_thr, + cfr_test_cfg.max_papr_db, + cfr_test_cfg.ema_alpha); + + logger.error("Invalid CFR parameters: cfr_mode=%d, alpha=%.2f, manual_thr=%.2f, max_papr_db=%.2f, ema_alpha=%.2f\n", + cfr_test_cfg.cfr_mode, + cfr_test_cfg.alpha, + cfr_test_cfg.manual_thr, + cfr_test_cfg.max_papr_db, + cfr_test_cfg.ema_alpha); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; } @@ -310,7 +353,26 @@ bool ue::switch_off() if (gw_inst) { gw_inst->stop(); } - return stack->switch_off(); + + // send switch off + stack->switch_off(); + + // wait for max. 5s for it to be sent (according to TS 24.301 Sec 25.5.2.2) + int cnt = 0, timeout_s = 5; + stack_metrics_t metrics = {}; + stack->get_metrics(&metrics); + + while (metrics.rrc.state != RRC_STATE_IDLE && ++cnt <= timeout_s) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + stack->get_metrics(&metrics); + } + + if (metrics.rrc.state != RRC_STATE_IDLE) { + srslog::fetch_basic_logger("NAS").warning("Detach couldn't be sent after %ds.", timeout_s); + return false; + } + + return true; } void ue::start_plot() @@ -320,7 +382,7 @@ void ue::start_plot() bool ue::get_metrics(ue_metrics_t* m) { - bzero(m, sizeof(ue_metrics_t)); + *m = {}; phy->get_metrics(srsran::srsran_rat_t::lte, &m->phy); phy->get_metrics(srsran::srsran_rat_t::nr, &m->phy_nr); radio->get_metrics(&m->rf); diff --git a/srsue/test/phy/CMakeLists.txt b/srsue/test/phy/CMakeLists.txt deleted file mode 100644 index cc9338e08..000000000 --- a/srsue/test/phy/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -# -# Copyright 2013-2021 Software Radio Systems Limited -# -# This file is part of srsRAN -# -# srsRAN is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of -# the License, or (at your option) any later version. -# -# srsRAN is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# A copy of the GNU Affero General Public License can be found in -# the LICENSE file in the top-level directory of this distribution -# and at http://www.gnu.org/licenses/. -# - -set(CTEST_LABELS "srsue;phy") - -include_directories( - ${Boost_INCLUDE_DIRS} - ${SEC_INCLUDE_DIRS} - ${PROJECT_SOURCE_DIR} -) - -link_directories( - ${Boost_LIBRARY_DIRS} - ${SEC_LIBRARY_DIRS} -) - -add_executable(ue_phy_test ue_phy_test.cc) -target_link_libraries(ue_phy_test - srsue_phy - srsran_common - srsran_phy - srsran_radio - ${CMAKE_THREAD_LIBS_INIT} - ${Boost_LIBRARIES}) -# Test disabled, it is not 100 deterministic. -#add_test(ue_phy_test ue_phy_test) - -add_executable(scell_search_test scell_search_test.cc) -target_link_libraries(scell_search_test - srsue_phy - srsran_common - srsran_phy - srsran_radio - ${CMAKE_THREAD_LIBS_INIT} - ${Boost_LIBRARIES}) -add_lte_test(scell_search_test scell_search_test --duration=5 --cell.nof_prb=6 --active_cell_list=2,3,4,5,6 --simulation_cell_list=1,2,3,4,5,6 --channel_period_s=30 --channel.hst.fd=750 --channel.delay_max=10000) diff --git a/srsue/test/upper/ue_rrc_nr_test.cc b/srsue/test/upper/ue_rrc_nr_test.cc deleted file mode 100644 index 0f06bf555..000000000 --- a/srsue/test/upper/ue_rrc_nr_test.cc +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/common/test_common.h" -#include "srsue/hdr/stack/rrc/rrc_nr.h" - -using namespace srsue; - -int rrc_nr_cap_request_test() -{ - srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC"); - logger.set_level(srslog::basic_levels::debug); - logger.set_hex_dump_max_size(-1); - srsran::task_scheduler task_sched{512, 100}; - srsran::task_sched_handle task_sched_handle(&task_sched); - rrc_nr rrc_nr(task_sched_handle); - srsran::byte_buffer_t caps; - rrc_nr.get_eutra_nr_capabilities(&caps); - rrc_nr.get_nr_capabilities(&caps); - return SRSRAN_SUCCESS; -} - -int main(int argc, char** argv) -{ - TESTASSERT(rrc_nr_cap_request_test() == SRSRAN_SUCCESS); - return SRSRAN_SUCCESS; -} diff --git a/srsue/ue.conf.example b/srsue/ue.conf.example index 7c1cf96e9..5bae8ce76 100644 --- a/srsue/ue.conf.example +++ b/srsue/ue.conf.example @@ -6,18 +6,19 @@ # RF configuration # # freq_offset: Uplink and Downlink optional frequency offset (in Hz) -# tx_gain: Transmit gain (dB). +# tx_gain: Transmit gain (dB). # rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# srate: Optional fixed sampling rate (Hz), corresponding to cell bandwidth. Must be set for 5G-SA. # # nof_antennas: Number of antennas per carrier (all carriers have the same number of antennas) -# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" -# device_args: Arguments for the device driver. Options are "auto" or any string. +# device_name: Device driver family. Supported options: "auto" (uses first found), "UHD" or "bladeRF" +# device_args: Arguments for the device driver. Options are "auto" or any string. # Default for UHD: "recv_frame_size=9232,send_frame_size=9232" # Default for bladeRF: "" # device_args_2: Arguments for the RF device driver 2. # device_args_3: Arguments for the RF device driver 3. # time_adv_nsamples: Transmission time advance (in number of samples) to compensate for RF delay -# from antenna to timestamp insertion. +# from antenna to timestamp insertion. # Default "auto". B210 USRP: 100 samples, bladeRF: 27. # continuous_tx: Transmit samples continuously to the radio or on bursts (auto/yes/no). # Default is auto (yes for UHD, no for rest) @@ -26,6 +27,7 @@ freq_offset = 0 tx_gain = 80 #rx_gain = 40 +#srate = 11.52e6 #nof_antennas = 1 @@ -45,10 +47,10 @@ tx_gain = 80 ##################################################################### # EUTRA RAT configuration -# +# # dl_earfcn: Downlink EARFCN list. # -# Optional parameters: +# Optional parameters: # dl_freq: Override DL frequency corresponding to dl_earfcn # ul_freq: Override UL frequency corresponding to dl_earfcn # nof_carriers: Number of carriers @@ -59,8 +61,8 @@ dl_earfcn = 3350 ##################################################################### # NR RAT configuration -# -# Optional parameters: +# +# Optional parameters: # bands: List of support NR bands seperated by a comma (default 78) # nof_carriers: Number of NR carriers (must be at least 1 for NR support) ##################################################################### @@ -71,19 +73,19 @@ dl_earfcn = 3350 ##################################################################### # Packet capture configuration # -# Packet capture is supported at the MAC, MAC_NR, and NAS layer. -# MAC-layer packets are captured to file a the compact format decoded -# by the Wireshark. For decoding, use the UDP dissector and the UDP -# heuristic dissection. Edit the preferences (Edit > Preferences > -# Protocols > DLT_USER) for DLT_USER to add an entry for DLT=149 with +# Packet capture is supported at the MAC, MAC_NR, and NAS layer. +# MAC-layer packets are captured to file a the compact format decoded +# by the Wireshark. For decoding, use the UDP dissector and the UDP +# heuristic dissection. Edit the preferences (Edit > Preferences > +# Protocols > DLT_USER) for DLT_USER to add an entry for DLT=149 with # Protocol=udp. Further, enable the heuristic dissection in UDP under: # Analyze > Enabled Protocols > MAC-LTE > mac_lte_udp and MAC-NR > mac_nr_udp # For more information see: https://wiki.wireshark.org/MAC-LTE # Using the same filename for mac_filename and mac_nr_filename writes both -# MAC-LTE and MAC-NR to the same file allowing a better analysis. +# MAC-LTE and MAC-NR to the same file allowing a better analysis. # NAS-layer packets are dissected with DLT=148, and Protocol = nas-eps. # -# enable: Enable packet captures of layers (mac/mac_nr/nas/none) multiple option list +# enable: Enable packet captures of layers (mac/mac_nr/nas/none) multiple option list # mac_filename: File path to use for MAC packet capture # mac_nr_filename: File path to use for MAC NR packet capture # nas_filename: File path to use for NAS packet capture @@ -136,12 +138,12 @@ file_max_size = -1 ##################################################################### [usim] mode = soft -algo = xor -#opc = 63BFA50EE6523365FF14C1F45F88737D +algo = milenage +opc = 63BFA50EE6523365FF14C1F45F88737D k = 00112233445566778899aabbccddeeff -imsi = 001010123456789 +imsi = 001010123456780 imei = 353490069873319 -#reader = +#reader = #pin = 1234 ##################################################################### @@ -159,7 +161,7 @@ imei = 353490069873319 ##################################################################### [rrc] #ue_category = 4 -#release = 15 +#release = 8 #feature_group = 0xe6041000 #mbms_service_id = -1 #mbms_service_port = 4321 @@ -173,9 +175,9 @@ imei = 353490069873319 # pass: Password for CHAP authentication # force_imsi_attach: Whether to always perform an IMSI attach # eia: List of integrity algorithms included in UE capabilities -# Supported: 1 - Snow3G, 2 - AES +# Supported: 1 - Snow3G, 2 - AES, 3 - ZUC # eea: List of ciphering algorithms included in UE capabilities -# Supported: 0 - NULL, 1 - Snow3G, 2 - AES +# Supported: 0 - NULL, 1 - Snow3G, 2 - AES, 3 - ZUC ##################################################################### [nas] #apn = internetinternet @@ -183,8 +185,8 @@ imei = 353490069873319 #user = srsuser #pass = srspass #force_imsi_attach = false -#eia = 1,2 -#eea = 0,1,2 +#eia = 1,2,3 +#eea = 0,1,2,3 ##################################################################### # GW configuration @@ -303,18 +305,18 @@ enable = false # # rx_gain_offset: RX Gain offset to add to rx_gain to calibrate RSRP readings # prach_gain: PRACH gain (dB). If defined, forces a gain for the tranmsission of PRACH only., -# Default is to use tx_gain in [rf] section. -# cqi_max: Upper bound on the maximum CQI to be reported. Default 15. +# Default is to use tx_gain in [rf] section. +# cqi_max: Upper bound on the maximum CQI to be reported. Default 15. # cqi_fixed: Fixes the reported CQI to a constant value. Default disabled. # snr_ema_coeff: Sets the SNR exponential moving average coefficient (Default 0.1) # snr_estim_alg: Sets the noise estimation algorithm. (Default refs) -# Options: pss: use difference between received and known pss signal, +# Options: pss: use difference between received and known pss signal, # refs: use difference between noise references and noiseless (after filtering) # empty: use empty subcarriers in the boarder of pss/sss signal # pdsch_max_its: Maximum number of turbo decoder iterations (Default 4) # pdsch_meas_evm: Measure PDSCH EVM, increases CPU load (default false) # nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 3) -# equalizer_mode: Selects equalizer mode. Valid modes are: "mmse", "zf" or any +# equalizer_mode: Selects equalizer mode. Valid modes are: "mmse", "zf" or any # non-negative real number to indicate a regularized zf coefficient. # Default is MMSE. # correct_sync_error: Channel estimator measures and pre-compensates time synchronization error. Increases CPU usage, @@ -322,7 +324,7 @@ enable = false # sfo_ema: EMA coefficient to average sample offsets used to compute SFO # sfo_correct_period: Period in ms to correct sample time to adjust for SFO # sss_algorithm: Selects the SSS estimation algorithm. Can choose between -# {full, partial, diff}. +# {full, partial, diff}. # estimator_fil_auto: The channel estimator smooths the channel estimate with an adaptative filter. # estimator_fil_stddev: Sets the channel estimator smooth gaussian filter standard deviation. # estimator_fil_order: Sets the channel estimator smooth gaussian filter order (even values perform better). @@ -343,6 +345,9 @@ enable = false # nof_in_sync_events: Number of PHY in-sync events before sending an in-sync event to RRC # nof_out_of_sync_events: Number of PHY out-sync events before sending an out-sync event to RRC # +# force_N_id_2: Force using a specific PSS (set to -1 to allow all PSSs). +# force_N_id_1: Force using a specific SSS (set to -1 to allow all SSSs). +# ##################################################################### [phy] #rx_gain_offset = 62 @@ -367,12 +372,52 @@ enable = false #pdsch_csi_enabled = true #pdsch_8bit_decoder = false #force_ul_amplitude = 0 +#detect_cp = false #in_sync_rsrp_dbm_th = -130.0 #in_sync_snr_db_th = 3.0 #nof_in_sync_events = 10 #nof_out_of_sync_events = 20 +#force_N_id_2 = 1 +#force_N_id_1 = 10 + +##################################################################### +# PHY NR specific configuration options +# +# store_pdsch_ko: Dumps the PDSCH baseband samples into a file on KO reception +# +##################################################################### +[phy.nr] +#store_pdsch_ko = false + +##################################################################### +# CFR configuration options +# +# The CFR module provides crest factor reduction for the transmitted signal. +# +# enable: Enable or disable the CFR. Default: disabled +# +# mode: manual: CFR threshold is set by cfr_manual_thres (default). +# auto_ema: CFR threshold is adaptive based on the signal PAPR. Power avg. with Exponential Moving Average. +# The time constant of the averaging can be tweaked with the ema_alpha parameter. +# auto_cma: CFR threshold is adaptive based on the signal PAPR. Power avg. with Cumulative Moving Average. +# Use with care, as CMA's increasingly slow response may be unsuitable for most use cases. +# +# strength: Ratio between amplitude-limited vs unprocessed signal (0 to 1). Default: 1 +# manual_thres: Fixed manual clipping threshold for CFR manual mode. Default: 2 +# auto_target_papr: Signal PAPR target (in dB) in CFR auto modes. output PAPR can be higher due to peak smoothing. Default: 7 +# ema_alpha: Alpha coefficient for the power average in auto_ema mode. Default: 1/7 +# +##################################################################### +[cfr] +#enable = false +#mode = manual +#manual_thres = 2.0 +#strength = 1.0 +#auto_target_papr = 7.0 +#ema_alpha = 0.0143 + ##################################################################### # Simulation configuration options # @@ -391,26 +436,32 @@ enable = false ##################################################################### # General configuration options # -# metrics_csv_enable: Write UE metrics to CSV file. +# metrics_csv_enable: Write UE metrics to CSV file. # -# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# metrics_period_secs: Sets the period at which metrics are requested from the UE. # -# metrics_csv_filename: File path to use for CSV metrics. +# metrics_csv_filename: File path to use for CSV metrics. # -# tracing_enable: Write source code tracing information to a file. +# tracing_enable: Write source code tracing information to a file. # -# tracing_filename: File path to use for tracing information. +# tracing_filename: File path to use for tracing information. # -# tracing_buffcapacity: Maximum capacity in bytes the tracing framework can store. +# tracing_buffcapacity: Maximum capacity in bytes the tracing framework can store. # -# have_tti_time_stats: Calculate TTI execution statistics using system clock +# have_tti_time_stats: Calculate TTI execution statistics using system clock +# +# metrics_json_enable: Write UE metrics to JSON file. +# +# metrics_json_filename: File path to use for JSON metrics. # ##################################################################### [general] -#metrics_csv_enable = false -#metrics_period_secs = 1 -#metrics_csv_filename = /tmp/ue_metrics.csv -#have_tti_time_stats = true -#tracing_enable = true -#tracing_filename = /tmp/ue_tracing.log -#tracing_buffcapacity = 1000000 +#metrics_csv_enable = false +#metrics_period_secs = 1 +#metrics_csv_filename = /tmp/ue_metrics.csv +#have_tti_time_stats = true +#tracing_enable = true +#tracing_filename = /tmp/ue_tracing.log +#tracing_buffcapacity = 1000000 +#metrics_json_enable = false +#metrics_json_filename = /tmp/ue_metrics.json diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 776fc00e0..d6ae6a6af 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -31,3 +31,5 @@ if (ZEROMQ_FOUND AND ENABLE_ZMQ_TEST) endforeach (cell_n_prb) endforeach (num_cc) endif (ZEROMQ_FOUND AND ENABLE_ZMQ_TEST) + +add_subdirectory(phy) \ No newline at end of file diff --git a/test/phy/CMakeLists.txt b/test/phy/CMakeLists.txt new file mode 100644 index 000000000..04ae8038b --- /dev/null +++ b/test/phy/CMakeLists.txt @@ -0,0 +1,148 @@ +# +# Copyright 2013-2022 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) + # gNb options + set(NR_PHY_TEST_GNB_NOF_THREADS 1) + set(NR_PHY_TEST_GNB_PHY_LOG_LEVEL "error") + set(NR_PHY_TEST_GNB_STACK_LOG_LEVEL "error") + + # UE options + set(NR_PHY_TEST_UE_NOF_THREADS 1) + set(NR_PHY_TEST_UE_PHY_LOG_LEVEL "error") + + # Build common arguments + set(NR_PHY_TEST_COMMON_ARGS + --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} + --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} + --ue.phy.log.level=${NR_PHY_TEST_UE_PHY_LOG_LEVEL} + --gnb.phy.log.level=${NR_PHY_TEST_GNB_PHY_LOG_LEVEL} + --gnb.stack.log.level=${NR_PHY_TEST_GNB_STACK_LOG_LEVEL}) + + add_executable(nr_phy_test nr_phy_test.cc) + target_link_libraries(nr_phy_test + srsue_phy + srsran_common + rrc_nr_asn1 + srsran_phy + srsran_radio + srsenb_phy + srsgnb_mac + srsran_mac + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${ATOMIC_LIBS}) + # For each supported bandwidth + foreach (NR_PHY_TEST_BW "10MHz" "20MHz") + # For dummy and real scheduler + foreach (NR_PHY_TEST_MAC_DUMMY "dummymac" "realmac") + # For each supported frame structure + foreach (NR_PHY_TEST_DUPLEX "FDD" "6D+4U" "FR1.15-1") + set(NR_PHY_TEST_DURATION_MS 50) + + # DL flooding only + foreach (NR_PHY_TEST_PDSCH "default" "ts38101/5.2-1") + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_MAC_DUMMY}_${NR_PHY_TEST_DUPLEX}_dl_${NR_PHY_TEST_PDSCH} nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX},pdsch=${NR_PHY_TEST_PDSCH} + --duration=${NR_PHY_TEST_DURATION_MS} + --gnb.stack.pdsch.slots=all + --gnb.stack.pdsch.start=0 # Start at RB 0 + --gnb.stack.pdsch.length=52 # Full 10 MHz BW + --gnb.stack.pdsch.mcs=27 # Maximum MCS + --gnb.stack.pusch.slots=none + --gnb.stack.use_dummy_mac=${NR_PHY_TEST_MAC_DUMMY} # Use real/dummy NR MAC + ${NR_PHY_TEST_COMMON_ARGS} + ) + endforeach () + + # UL flooding + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_MAC_DUMMY}_${NR_PHY_TEST_DUPLEX}_ul_only nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX} + --duration=${NR_PHY_TEST_DURATION_MS} + --gnb.stack.pdsch.slots=none + --gnb.stack.pusch.slots=all + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + --gnb.stack.use_dummy_mac=${NR_PHY_TEST_MAC_DUMMY} # Use real/dummy NR MAC + ${NR_PHY_TEST_COMMON_ARGS} + ) + + # DL and UL flooding + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_MAC_DUMMY}_${NR_PHY_TEST_DUPLEX}_bidir nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX} + --duration=${NR_PHY_TEST_DURATION_MS} + --gnb.stack.pdsch.slots=all + --gnb.stack.pdsch.start=0 # Start at RB 0 + --gnb.stack.pdsch.length=52 # Full 10 MHz BW + --gnb.stack.pdsch.mcs=28 # Maximum MCS + --gnb.stack.pusch.slots=all + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + --gnb.stack.use_dummy_mac=${NR_PHY_TEST_MAC_DUMMY} # Use real/dummy NR MAC + ${NR_PHY_TEST_COMMON_ARGS} + ) + endforeach () + endforeach () + + # Test PRACH transmission and detection + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_prach_fdd nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},duplex=FDD + --duration=1000 # 1000 slots + --gnb.stack.pdsch.slots=none # No PDSCH + --gnb.stack.pusch.slots=none # No PUSCH + --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} + --ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames + --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} + ) + + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_prach_tdd nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},duplex=6D+4U + --duration=1000 # 1000 slots + --gnb.stack.pdsch.slots=none # No PDSCH + --gnb.stack.pusch.slots=none # No PUSCH + --ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames + ${NR_PHY_TEST_COMMON_ARGS} + ) + + # Test scheduling request + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_sr nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW} + --duration=1000 # 1000 slots + --gnb.stack.pdsch.slots=none # No PDSCH + --gnb.stack.pusch.slots=none # No PUSCH + --ue.stack.sr.period=4 # Transmit SR every 4 opportunities + ${NR_PHY_TEST_COMMON_ARGS} + ) + + # Test scheduling request multiplexed with HARQ ACK feedback + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_sr_harq nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW} + --duration=1000 # 1000 slots + --gnb.stack.pdsch.slots=all # All PDSCH + --gnb.stack.pusch.slots=none # No PUSCH + --ue.stack.sr.period=4 # Transmit SR every 4 opportunities + ${NR_PHY_TEST_COMMON_ARGS} + ) + endforeach () +endif () diff --git a/test/phy/dummy_gnb_stack.h b/test/phy/dummy_gnb_stack.h new file mode 100644 index 000000000..56c819269 --- /dev/null +++ b/test/phy/dummy_gnb_stack.h @@ -0,0 +1,759 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_DUMMY_GNB_STACK_H +#define SRSRAN_DUMMY_GNB_STACK_H + +#include "dummy_rx_harq_proc.h" +#include "dummy_tx_harq_proc.h" +#include "srsenb/test/common/rlc_test_dummy.h" +#include "srsgnb/hdr/stack/common/test/dummy_nr_classes.h" +#include "srsgnb/hdr/stack/mac/mac_nr.h" +#include "srsgnb/hdr/stack/mac/sched_nr.h" +#include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h" +#include "srsran/srslog/srslog.h" +#include +#include +#include +#include +#include +#include +#include +#include + +class gnb_dummy_stack : public srsenb::stack_interface_phy_nr +{ + const static uint32_t NUMEROLOGY_IDX = 0; + +public: + struct prach_metrics_t { + uint32_t count; + float avg_ta = 0.0f; + float min_ta = +INFINITY; + float max_ta = -INFINITY; + }; + struct pucch_metrics_t { + float epre_db_avg = 0.0f; + float epre_db_min = +INFINITY; + float epre_db_max = -INFINITY; + float rsrp_db_avg = 0.0f; + float rsrp_db_min = +INFINITY; + float rsrp_db_max = -INFINITY; + float snr_db_avg = 0.0f; + float snr_db_min = +INFINITY; + float snr_db_max = -INFINITY; + float ta_us_avg = 0.0f; + float ta_us_min = +INFINITY; + float ta_us_max = -INFINITY; + uint32_t count = 0; + }; + + struct metrics_t { + std::map prach = {}; ///< PRACH metrics indexed with premable index + srsenb::mac_ue_metrics_t mac = {}; ///< MAC metrics + uint32_t sr_count = 0; ///< SR counter + uint32_t cqi_count = 0; ///< CQI opportunity counter + uint32_t cqi_valid_count = 0; ///< Valid CQI counter + pucch_metrics_t pucch = {}; + pucch_metrics_t pusch = {}; + }; + +private: + srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK"); + bool use_dummy_mac = true; + const uint16_t rnti = 0x1234; + struct { + srsran::circular_array dci_location = {}; + uint32_t mcs = 0; + uint32_t freq_res = 0; + std::set slots = {}; + } dl, ul; + srsran::circular_array dl_data_to_ul_ack; + uint32_t ss_id = 0; + srsran::phy_cfg_nr_t phy_cfg = {}; + bool valid = false; + + srsran::task_scheduler task_sched; + srsenb::rrc_nr_dummy rrc_obj; + srsenb::rlc_dummy rlc_obj; + std::unique_ptr mac; + srslog::basic_logger& sched_logger; + bool autofill_sch_bsr = false; + bool wait_preamble = false; + std::atomic enable_user_sched = {false}; + + std::mutex metrics_mutex; + metrics_t metrics = {}; + + // HARQ feedback + class pending_ack_t + { + private: + std::mutex mutex; + srsran_pdsch_ack_nr_t ack = {}; + + public: + pending_ack_t() = default; + void push_ack(srsran_harq_ack_resource_t& ack_resource) + { + // Prepare ACK information + srsran_harq_ack_m_t ack_m = {}; + ack_m.resource = ack_resource; + ack_m.present = true; + + std::unique_lock lock(mutex); + + ack.nof_cc = 1; + + srsran_harq_ack_insert_m(&ack, &ack_m); + } + + srsran_pdsch_ack_nr_t get_ack() + { + std::unique_lock lock(mutex); + srsran_pdsch_ack_nr_t ret = ack; + ack = {}; + return ret; + } + uint32_t get_dai() + { + std::unique_lock lock(mutex); + return ack.cc[0].M % 4; + } + }; + std::array pending_ack = {}; + + // PUSCH state + class pending_pusch_t + { + private: + std::mutex mutex; + srsran_sch_cfg_nr_t pusch = {}; + bool valid = false; + uint32_t pid = 0; + + public: + pending_pusch_t() = default; + void push(const uint32_t& pid_, const srsran_sch_cfg_nr_t& pusch_) + { + std::unique_lock lock(mutex); + pusch = pusch_; + pid = pid_; + valid = true; + } + + bool pop(uint32_t& pid_, srsran_sch_cfg_nr_t& pusch_) + { + std::unique_lock lock(mutex); + bool ret = valid; + pusch_ = pusch; + pid_ = pid; + valid = false; + return ret; + } + }; + std::array pending_pusch = {}; + + dummy_tx_harq_entity tx_harq_proc; + dummy_rx_harq_entity rx_harq_proc; + + bool schedule_pdsch(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) + { + if (dl.slots.count(SRSRAN_SLOT_NR_MOD(srsran_subcarrier_spacing_15kHz, slot_cfg.idx)) == 0 or + not enable_user_sched) { + return true; + } + + // Instantiate PDCCH and PDSCH + pdcch_dl_t pdcch = {}; + pdsch_t pdsch = {}; + + // Second TB is not used + pdsch.data[1] = nullptr; + + // Fill DCI configuration + pdcch.dci_cfg = phy_cfg.get_dci_cfg(); + + // Fill DCI context + if (not phy_cfg.get_dci_ctx_pdsch_rnti_c(ss_id, dl.dci_location[slot_cfg.idx], rnti, pdcch.dci.ctx)) { + logger.error("Error filling PDSCH DCI context"); + return false; + } + + uint32_t harq_feedback = dl_data_to_ul_ack[slot_cfg.idx]; + uint32_t harq_ack_slot_idx = TTI_ADD(slot_cfg.idx, harq_feedback); + + // Fill DCI fields + srsran_dci_dl_nr_t& dci = pdcch.dci; + dci.freq_domain_assigment = dl.freq_res; + dci.time_domain_assigment = 0; + dci.mcs = dl.mcs; + dci.rv = 0; + dci.ndi = (slot_cfg.idx / SRSRAN_NOF_SF_X_FRAME) % 2; + dci.pid = slot_cfg.idx % SRSRAN_NOF_SF_X_FRAME; + dci.dai = pending_ack[harq_ack_slot_idx % pending_ack.size()].get_dai(); + dci.tpc = 1; + dci.pucch_resource = 0; + if (dci.ctx.format == srsran_dci_format_nr_1_0) { + dci.harq_feedback = dl_data_to_ul_ack[slot_cfg.idx] - 1; + } else { + dci.harq_feedback = slot_cfg.idx; + } + + // Create PDSCH configuration + if (not phy_cfg.get_pdsch_cfg(slot_cfg, dci, pdsch.sch)) { + logger.error("Error converting DCI to grant"); + return false; + } + + // Set TBS + // Select grant and set data + pdsch.data[0] = tx_harq_proc[slot_cfg.idx].get_tb(pdsch.sch.grant.tb[0].tbs); + + // Set softbuffer + pdsch.sch.grant.tb[0].softbuffer.tx = &tx_harq_proc[slot_cfg.idx].get_softbuffer(dci.ndi); + + // Reset Tx softbuffer always + srsran_softbuffer_tx_reset(pdsch.sch.grant.tb[0].softbuffer.tx); + + // Push scheduling results + dl_sched.pdcch_dl.push_back(pdcch); + dl_sched.pdsch.push_back(pdsch); + + // Generate PDSCH HARQ Feedback + srsran_harq_ack_resource_t ack_resource = {}; + if (not phy_cfg.get_pdsch_ack_resource(dci, ack_resource)) { + logger.error("Error getting ack resource"); + return false; + } + + // Calculate PUCCH slot and push resource + pending_ack[harq_ack_slot_idx % pending_ack.size()].push_ack(ack_resource); + + return true; + } + + bool schedule_pusch(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) + { + if (ul.slots.count(SRSRAN_SLOT_NR_MOD(srsran_subcarrier_spacing_15kHz, slot_cfg.idx + 4)) == 0 or + not enable_user_sched) { + return true; + } + + // Instantiate PDCCH + pdcch_ul_t pdcch = {}; + + // Fill DCI configuration + pdcch.dci_cfg = phy_cfg.get_dci_cfg(); + + // Fill DCI context + if (not phy_cfg.get_dci_ctx_pusch_rnti_c(ss_id, ul.dci_location[slot_cfg.idx], rnti, pdcch.dci.ctx)) { + logger.error("Error filling PDSCH DCI context"); + return false; + } + + // Fill DCI fields + srsran_dci_ul_nr_t& dci = pdcch.dci; + dci.freq_domain_assigment = ul.freq_res; + dci.time_domain_assigment = 0; + dci.freq_hopping_flag = 0; + dci.mcs = ul.mcs; + dci.rv = 0; + dci.ndi = (slot_cfg.idx / SRSRAN_NOF_SF_X_FRAME) % 2; + dci.pid = slot_cfg.idx % SRSRAN_NOF_SF_X_FRAME; + dci.tpc = 1; + + // Create PDSCH configuration + srsran_sch_cfg_nr_t pusch_cfg = {}; + if (not phy_cfg.get_pusch_cfg(slot_cfg, dci, pusch_cfg)) { + logger.error("Error converting DCI to grant"); + return false; + } + + // Set softbuffer + pusch_cfg.grant.tb[0].softbuffer.rx = &rx_harq_proc[dci.pid].get_softbuffer(dci.ndi, pusch_cfg.grant.tb[0].tbs); + + // Push scheduling results + dl_sched.pdcch_ul.push_back(pdcch); + + // Set pending PUSCH + pending_pusch[TTI_TX(slot_cfg.idx) % pending_pusch.size()].push(dci.pid, pusch_cfg); + + return true; + } + + bool handle_uci_data(const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value) + { + std::unique_lock lock(metrics_mutex); + + // Process HARQ-ACK + for (uint32_t i = 0; i < cfg.ack.count; i++) { + const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i]; + bool is_ok = (value.ack[i] == 1) and value.valid; + uint32_t tb_count = (ack_bit->tb0 ? 1 : 0) + (ack_bit->tb1 ? 1 : 0); + metrics.mac.tx_brate += tx_harq_proc[ack_bit->pid].get_tbs(); + metrics.mac.tx_pkts += tb_count; + if (not is_ok) { + metrics.mac.tx_errors += tb_count; + logger.debug("NACK received!"); + } + } + + // Process SR + if (value.valid and value.sr > 0) { + metrics.sr_count++; + } + + // Process CQI + for (uint32_t i = 0; i < cfg.nof_csi; i++) { + // Increment CQI opportunity + metrics.cqi_count++; + + // Skip if invalid or not supported CSI report + if (not value.valid or cfg.csi[i].cfg.quantity != SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI or + cfg.csi[i].cfg.freq_cfg != SRSRAN_CSI_REPORT_FREQ_WIDEBAND) { + continue; + } + + // Add statistics + metrics.mac.dl_cqi = + SRSRAN_VEC_SAFE_CMA(value.csi->wideband_cri_ri_pmi_cqi.cqi, metrics.mac.dl_cqi, metrics.cqi_count); + metrics.cqi_valid_count++; + } + + return true; + } + +public: + struct args_t { + srsran::phy_cfg_nr_t phy_cfg; ///< Physical layer configuration + std::string use_dummy_mac = "dummymac"; ///< Use dummy or real NR scheduler + bool wait_preamble = false; ///< Whether a UE is created automatically or the stack waits for a PRACH + uint16_t rnti = 0x1234; ///< C-RNTI + uint32_t ss_id = 1; ///< Search Space identifier + uint32_t pdcch_aggregation_level = 0; ///< PDCCH aggregation level + uint32_t pdcch_dl_candidate = 0; ///< PDCCH DL DCI candidate index + uint32_t pdcch_ul_candidate = 1; ///< PDCCH UL DCI candidate index + struct { + uint32_t rb_start = 0; ///< Start frequency domain resource block + uint32_t rb_length = 10; ///< Number of frequency domain resource blocks + uint32_t mcs = 10; ///< Modulation code scheme + std::string slots = ""; ///< Slot list, empty string means no scheduling + } pdsch, pusch; + std::string log_level = "warning"; + }; + + gnb_dummy_stack(const args_t& args) : + rnti(args.rnti), + phy_cfg(args.phy_cfg), + ss_id(args.ss_id), + use_dummy_mac(args.use_dummy_mac == "dummymac"), + sched_logger(srslog::fetch_basic_logger("MAC")), + wait_preamble(args.wait_preamble) + { + logger.set_level(srslog::str_to_basic_level(args.log_level)); + sched_logger.set_level(srslog::str_to_basic_level(args.log_level)); + srslog::fetch_basic_logger("MAC-NR").set_level(srslog::str_to_basic_level(args.log_level)); + + autofill_sch_bsr = args.pdsch.slots != "" and args.pdsch.slots != "none"; + + // create sched object + mac.reset(new srsenb::mac_nr{&task_sched}); + srsenb::mac_nr_args_t mac_args{}; + mac_args.sched_cfg.pdsch_enabled = args.pdsch.slots != "" and args.pdsch.slots != "none"; + mac_args.sched_cfg.pusch_enabled = args.pusch.slots != "" and args.pusch.slots != "none"; + mac_args.sched_cfg.fixed_dl_mcs = args.pdsch.mcs; + mac_args.sched_cfg.fixed_ul_mcs = args.pusch.mcs; + mac->init(mac_args, nullptr, nullptr, &rlc_obj, &rrc_obj); + std::vector cells_cfg = srsenb::get_default_cells_cfg(1, phy_cfg); + mac->cell_cfg(cells_cfg); + + dl.mcs = args.pdsch.mcs; + ul.mcs = args.pusch.mcs; + + if (args.pdsch.slots != "none" and not args.pdsch.slots.empty()) { + if (args.pdsch.slots == "all") { + for (uint32_t n = 1; n < SRSRAN_NSLOTS_PER_FRAME_NR(phy_cfg.carrier.scs); n++) { + dl.slots.insert(n); + } + } else { + srsran::string_parse_list(args.pdsch.slots, ',', dl.slots); + } + } + if (args.pusch.slots != "none" and not args.pusch.slots.empty()) { + if (args.pusch.slots == "all") { + for (uint32_t n = 0; n < SRSRAN_NSLOTS_PER_FRAME_NR(phy_cfg.carrier.scs); n++) { + ul.slots.insert(n); + } + } else { + srsran::string_parse_list(args.pusch.slots, ',', ul.slots); + } + } + + // Select DCI locations + for (uint32_t slot = 0; slot < SRSRAN_NOF_SF_X_FRAME; slot++) { + srsran::bounded_vector locations; + + if (not phy_cfg.get_dci_locations(slot, rnti, args.ss_id, args.pdcch_aggregation_level, locations)) { + logger.error( + "Error generating locations for slot %d and aggregation level %d", slot, args.pdcch_aggregation_level); + return; + } + + // DCI DL + if (args.pdcch_dl_candidate >= locations.size()) { + logger.error("Candidate index %d exceeds the number of candidates %d for aggregation level %d", + args.pdcch_dl_candidate, + (uint32_t)locations.size(), + args.pdcch_aggregation_level); + return; + } + dl.dci_location[slot] = locations[args.pdcch_dl_candidate]; + + // DCI UL + if (args.pdcch_ul_candidate >= locations.size()) { + logger.error("Candidate index %d exceeds the number of candidates %d for aggregation level %d", + args.pdcch_ul_candidate, + (uint32_t)locations.size(), + args.pdcch_aggregation_level); + return; + } + ul.dci_location[slot] = locations[args.pdcch_ul_candidate]; + } + + // Select DL frequency domain resources + dl.freq_res = srsran_ra_nr_type1_riv(args.phy_cfg.carrier.nof_prb, args.pdsch.rb_start, args.pdsch.rb_length); + + // Select DL frequency domain resources + ul.freq_res = srsran_ra_nr_type1_riv(args.phy_cfg.carrier.nof_prb, args.pusch.rb_start, args.pusch.rb_length); + + // Setup DL Data to ACK timing + for (uint32_t i = 0; i < SRSRAN_NOF_SF_X_FRAME; i++) { + if (args.phy_cfg.duplex.mode == SRSRAN_DUPLEX_MODE_TDD) { + dl_data_to_ul_ack[i] = args.phy_cfg.harq_ack.dl_data_to_ul_ack[i % args.phy_cfg.duplex.tdd.pattern1.period_ms]; + } else { + dl_data_to_ul_ack[i] = args.phy_cfg.harq_ack.dl_data_to_ul_ack[i % args.phy_cfg.harq_ack.nof_dl_data_to_ul_ack]; + } + } + + // If reached this point the configuration is valid + valid = true; + } + + ~gnb_dummy_stack() = default; + + void stop() + { + if (not use_dummy_mac) { + mac->stop(); + } + } + + bool is_valid() const { return valid; } + + void start_scheduling() + { + // add UE to scheduler + if (not use_dummy_mac and not wait_preamble) { + srsenb::sched_nr_ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1, phy_cfg); + ue_cfg.lc_ch_to_add.emplace_back(); + ue_cfg.lc_ch_to_add.back().lcid = 4; + ue_cfg.lc_ch_to_add.back().cfg.direction = srsenb::mac_lc_ch_cfg_t::BOTH; + + mac->reserve_rnti(0, ue_cfg); + } + + enable_user_sched = true; + } + + int slot_indication(const srsran_slot_cfg_t& slot_cfg) override { return 0; } + + dl_sched_t* get_dl_sched(const srsran_slot_cfg_t& slot_cfg) override + { + logger.set_context(slot_cfg.idx); + sched_logger.set_context(slot_cfg.idx); + + if (not use_dummy_mac) { + if (autofill_sch_bsr) { + mac->rlc_buffer_state(rnti, 0, 100000, 0); + mac->ul_bsr(rnti, 0, 100000); + } + + dl_sched_t* dl_res = mac->get_dl_sched(slot_cfg); + if (dl_res == nullptr) { + return nullptr; + } + + for (pdsch_t& pdsch : dl_res->pdsch) { + // Set TBS + // Select grant and set data + pdsch.data[0] = tx_harq_proc[slot_cfg.idx].get_tb(pdsch.sch.grant.tb[0].tbs); + pdsch.data[1] = nullptr; + } + + return dl_res; + } + dl_sched_t& dl_sched = dl_scheds[slot_cfg.idx]; + dl_sched = {}; + + // Check if it is TDD DL slot and PDSCH mask, if no PDSCH shall be scheduled, do not set any grant and skip + if (not srsran_duplex_nr_is_dl(&phy_cfg.duplex, phy_cfg.carrier.scs, slot_cfg.idx)) { + return nullptr; + } + + if (not schedule_pdsch(slot_cfg, dl_sched)) { + logger.error("Error scheduling PDSCH"); + return nullptr; + } + + // Schedule SSB before UL + for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) { + if (phy_cfg.ssb.position_in_burst[ssb_idx]) { + srsran_mib_nr_t mib = {}; + mib.ssb_idx = ssb_idx; + mib.sfn = slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(phy_cfg.carrier.scs); + mib.hrf = (slot_cfg.idx % SRSRAN_NSLOTS_PER_FRAME_NR(phy_cfg.carrier.scs)) >= + SRSRAN_NSLOTS_PER_FRAME_NR(phy_cfg.carrier.scs) / 2; + + mac_interface_phy_nr::ssb_t ssb = {}; + if (srsran_pbch_msg_nr_mib_pack(&mib, &ssb.pbch_msg) < SRSRAN_SUCCESS) { + logger.error("Error Packing MIB in slot %d", slot_cfg.idx); + continue; + } + ssb.pbch_msg.ssb_idx = (uint32_t)ssb_idx; + dl_sched.ssb.push_back(ssb); + } + } + + // Check if the UL slot is valid, if not skip UL scheduling + if (not srsran_duplex_nr_is_ul(&phy_cfg.duplex, phy_cfg.carrier.scs, TTI_TX(slot_cfg.idx))) { + return &dl_sched; + } + + if (not schedule_pusch(slot_cfg, dl_sched)) { + logger.error("Error scheduling PUSCH"); + return nullptr; + } + + // Schedule NZP-CSI-RS, iterate all NZP-CSI-RS sets + for (const srsran_csi_rs_nzp_set_t& set : phy_cfg.pdsch.nzp_csi_rs_sets) { + // For each NZP-CSI-RS resource available in the set + for (uint32_t i = 0; i < set.count; i++) { + // Select resource + const srsran_csi_rs_nzp_resource_t& nzp_csi_resource = set.data[i]; + + // Check if the resource is scheduled for this slot + if (srsran_csi_rs_send(&nzp_csi_resource.periodicity, &slot_cfg)) { + dl_sched.nzp_csi_rs.push_back(nzp_csi_resource); + } + } + } + + return &dl_sched; + } + + ul_sched_t* get_ul_sched(const srsran_slot_cfg_t& slot_cfg) override + { + logger.set_context(slot_cfg.idx); + sched_logger.set_context(slot_cfg.idx); + + if (not use_dummy_mac) { + ul_sched_t* ul_res = mac->get_ul_sched(slot_cfg); + return ul_res; + } + ul_sched_t& ul_sched = ul_scheds[slot_cfg.idx]; + ul_sched.pucch.clear(); + ul_sched.pusch.clear(); + + // Get ACK information + srsran_pdsch_ack_nr_t ack = pending_ack[slot_cfg.idx % pending_ack.size()].get_ack(); + bool has_ack = ack.nof_cc > 0; + + if (has_ack) { + if (logger.debug.enabled()) { + std::array str = {}; + if (srsran_harq_ack_info(&ack, str.data(), (uint32_t)str.size()) > 0) { + logger.debug("HARQ feedback:\n%s", str.data()); + } + } + } + mac_interface_phy_nr::pusch_t pusch = {}; + bool has_pusch = pending_pusch[slot_cfg.idx % pending_pusch.size()].pop(pusch.pid, pusch.sch); + + srsran_uci_cfg_nr_t uci_cfg = {}; + if (not phy_cfg.get_uci_cfg(slot_cfg, ack, uci_cfg)) { + logger.error("Error getting UCI configuration"); + return nullptr; + } + + // Schedule PUSCH + if (has_pusch) { + // If has PUSCH, no SR shall be received + uci_cfg.o_sr = 0; + + // Put UCI configuration in PUSCH config + if (not phy_cfg.get_pusch_uci_cfg(slot_cfg, uci_cfg, pusch.sch)) { + logger.error("Error setting UCI configuration in PUSCH"); + return nullptr; + } + + ul_sched.pusch.push_back(pusch); + } else if ((uci_cfg.ack.count > 0 || uci_cfg.nof_csi > 0 || uci_cfg.o_sr > 0) and enable_user_sched) { + // If any UCI information is triggered, schedule PUCCH + ul_sched.pucch.emplace_back(); + + uci_cfg.pucch.rnti = rnti; + + mac_interface_phy_nr::pucch_t& pucch = ul_sched.pucch.back(); + pucch.candidates.emplace_back(); + pucch.candidates.back().uci_cfg = uci_cfg; + if (not phy_cfg.get_pucch_uci_cfg(slot_cfg, uci_cfg, pucch.pucch_cfg, pucch.candidates.back().resource)) { + logger.error("Error getting UCI CFG"); + return nullptr; + } + + // If this slot has a SR opportunity and the selected PUCCH format is 1, consider positive SR. + if (uci_cfg.o_sr > 0 and uci_cfg.ack.count > 0 and + pucch.candidates.back().resource.format == SRSRAN_PUCCH_NR_FORMAT_1) { + // Set SR negative + if (uci_cfg.o_sr > 0) { + uci_cfg.sr_positive_present = false; + } + + // Append new resource + pucch.candidates.emplace_back(); + pucch.candidates.back().uci_cfg = uci_cfg; + if (not phy_cfg.get_pucch_uci_cfg(slot_cfg, uci_cfg, pucch.pucch_cfg, pucch.candidates.back().resource)) { + logger.error("Error getting UCI CFG"); + return nullptr; + } + } + } + + return &ul_sched; + } + + int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override + { + if (not use_dummy_mac) { + mac->pucch_info(slot_cfg, pucch_info); + } else { + // Handle UCI data + if (not handle_uci_data(pucch_info.uci_data.cfg, pucch_info.uci_data.value)) { + logger.error("Error handling UCI data from PUCCH reception"); + return SRSRAN_ERROR; + } + } + + // Skip next steps if uci data is invalid + if (not pucch_info.uci_data.value.valid) { + return SRSRAN_SUCCESS; + } + + // Handle PHY metrics + std::unique_lock lock(metrics_mutex); + metrics.pucch.epre_db_avg = SRSRAN_VEC_CMA(pucch_info.csi.epre_dB, metrics.pucch.epre_db_avg, metrics.pucch.count); + metrics.pucch.epre_db_min = SRSRAN_MIN(metrics.pucch.epre_db_min, pucch_info.csi.epre_dB); + metrics.pucch.epre_db_max = SRSRAN_MAX(metrics.pucch.epre_db_max, pucch_info.csi.epre_dB); + metrics.pucch.rsrp_db_avg = SRSRAN_VEC_CMA(pucch_info.csi.rsrp_dB, metrics.pucch.rsrp_db_avg, metrics.pucch.count); + metrics.pucch.rsrp_db_min = SRSRAN_MIN(metrics.pucch.rsrp_db_min, pucch_info.csi.rsrp_dB); + metrics.pucch.rsrp_db_max = SRSRAN_MAX(metrics.pucch.rsrp_db_max, pucch_info.csi.rsrp_dB); + metrics.pucch.snr_db_avg = SRSRAN_VEC_CMA(pucch_info.csi.snr_dB, metrics.pucch.snr_db_avg, metrics.pucch.count); + metrics.pucch.snr_db_min = SRSRAN_MIN(metrics.pucch.snr_db_min, pucch_info.csi.snr_dB); + metrics.pucch.snr_db_max = SRSRAN_MAX(metrics.pucch.snr_db_max, pucch_info.csi.snr_dB); + metrics.pucch.ta_us_avg = SRSRAN_VEC_CMA(pucch_info.csi.delay_us, metrics.pucch.ta_us_avg, metrics.pucch.count); + metrics.pucch.ta_us_min = SRSRAN_MIN(metrics.pucch.ta_us_min, pucch_info.csi.delay_us); + metrics.pucch.ta_us_max = SRSRAN_MAX(metrics.pucch.ta_us_max, pucch_info.csi.delay_us); + metrics.pucch.count++; + + return SRSRAN_SUCCESS; + } + + int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override + { + if (not use_dummy_mac) { + mac->pusch_info(slot_cfg, pusch_info); + } else { + // Handle UCI data + if (not handle_uci_data(pusch_info.uci_cfg, pusch_info.pusch_data.uci)) { + logger.error("Error handling UCI data from PUCCH reception"); + return SRSRAN_ERROR; + } + + // Handle UL-SCH metrics + std::unique_lock lock(metrics_mutex); + if (not pusch_info.pusch_data.tb[0].crc) { + metrics.mac.rx_errors++; + } + metrics.mac.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs(); + metrics.mac.rx_pkts++; + } + + // Handle PHY metrics + metrics.pusch.epre_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.epre_dB, metrics.pusch.epre_db_avg, metrics.pusch.count); + metrics.pusch.epre_db_min = SRSRAN_MIN(metrics.pusch.epre_db_min, pusch_info.csi.epre_dB); + metrics.pusch.epre_db_max = SRSRAN_MAX(metrics.pusch.epre_db_max, pusch_info.csi.epre_dB); + metrics.pusch.rsrp_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.rsrp_dB, metrics.pusch.rsrp_db_avg, metrics.pusch.count); + metrics.pusch.rsrp_db_min = SRSRAN_MIN(metrics.pusch.rsrp_db_min, pusch_info.csi.rsrp_dB); + metrics.pusch.rsrp_db_max = SRSRAN_MAX(metrics.pusch.rsrp_db_max, pusch_info.csi.rsrp_dB); + metrics.pusch.snr_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.snr_dB, metrics.pusch.snr_db_avg, metrics.pusch.count); + metrics.pusch.snr_db_min = SRSRAN_MIN(metrics.pusch.snr_db_min, pusch_info.csi.snr_dB); + metrics.pusch.snr_db_max = SRSRAN_MAX(metrics.pusch.snr_db_max, pusch_info.csi.snr_dB); + metrics.pusch.ta_us_avg = SRSRAN_VEC_CMA(pusch_info.csi.delay_us, metrics.pusch.ta_us_avg, metrics.pusch.count); + metrics.pusch.ta_us_min = SRSRAN_MIN(metrics.pusch.ta_us_min, pusch_info.csi.delay_us); + metrics.pusch.ta_us_max = SRSRAN_MAX(metrics.pusch.ta_us_max, pusch_info.csi.delay_us); + metrics.pusch.count++; + + return SRSRAN_SUCCESS; + } + + void rach_detected(const rach_info_t& rach_info) override + { + if (not use_dummy_mac) { + mac->rach_detected(rach_info); + task_sched.run_pending_tasks(); + } + + std::unique_lock lock(metrics_mutex); + prach_metrics_t& prach_metrics = metrics.prach[rach_info.preamble]; + prach_metrics.avg_ta = SRSRAN_VEC_SAFE_CMA((float)rach_info.time_adv, prach_metrics.avg_ta, prach_metrics.count); + prach_metrics.min_ta = SRSRAN_MIN((float)rach_info.time_adv, prach_metrics.min_ta); + prach_metrics.max_ta = SRSRAN_MAX((float)rach_info.time_adv, prach_metrics.max_ta); + prach_metrics.count++; + } + + metrics_t get_metrics() + { + std::unique_lock lock(metrics_mutex); + if (not use_dummy_mac) { + srsenb::mac_metrics_t mac_metrics; + mac->get_metrics(mac_metrics); + metrics.mac = mac_metrics.ues[0]; + } + return metrics; + } + +private: + srsran::circular_array dl_scheds; + srsran::circular_array ul_scheds; +}; + +#endif // SRSRAN_DUMMY_GNB_STACK_H diff --git a/test/phy/dummy_phy_common.h b/test/phy/dummy_phy_common.h new file mode 100644 index 000000000..d111478b9 --- /dev/null +++ b/test/phy/dummy_phy_common.h @@ -0,0 +1,248 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_DUMMY_PHY_COMMON_H +#define SRSRAN_DUMMY_PHY_COMMON_H + +#include +#include +#include +#include +#include + +class phy_common : public srsran::phy_common_interface +{ +private: + const uint32_t RINGBUFFER_TIMEOUT_MS = 10; + std::atomic quit = {false}; + srslog::basic_logger& logger; + double srate_hz; + uint64_t write_ts = 0; + uint64_t read_ts = 0; + std::vector zero_buffer; ///< Zero buffer for Tx + std::vector sink_buffer; ///< Dummy buffer for Rx + std::mutex ringbuffers_mutex; + std::vector ringbuffers; + srsran::tti_semaphore semaphore; + + void write_zero_padding(uint32_t nof_zeros) + { + // Skip if no pading is required + if (nof_zeros == 0) { + return; + } + + logger.debug("Padding %d zeros", nof_zeros); + + // For each ringbuffer, padd zero + int nof_bytes = (int)(nof_zeros * sizeof(cf_t)); + for (srsran_ringbuffer_t& rb : ringbuffers) { + // If quit is flagged, return instantly + if (quit) { + return; + } + + // Actual write + int err = SRSRAN_SUCCESS; + do { + err = srsran_ringbuffer_write_timed(&rb, zero_buffer.data(), nof_bytes, RINGBUFFER_TIMEOUT_MS); + if (err < SRSRAN_SUCCESS and err != SRSRAN_ERROR_TIMEOUT) { + logger.error("Error writing zeros in ringbuffer"); + } + } while (err < SRSRAN_SUCCESS and not quit); + } + + // Increment write timestamp + write_ts += nof_zeros; + } + + void write_baseband(srsran::rf_buffer_t& buffer) + { + // skip if baseband is not available + if (buffer.get_nof_samples() == 0) { + return; + } + + // For each ringbuffer, write + int nof_bytes = (int)(buffer.get_nof_samples() * sizeof(cf_t)); + uint32_t channel_idx = 0; + for (srsran_ringbuffer_t& rb : ringbuffers) { + // If quit is flagged, return instantly + if (quit) { + return; + } + + // Extract channel buffer pointer + cf_t* channel_buffer = buffer.get(channel_idx); + + // If the pointer is not set, use the zero buffer + if (channel_buffer == nullptr) { + channel_buffer = zero_buffer.data(); + } + + // Actual write + int err = SRSRAN_SUCCESS; + do { + err = srsran_ringbuffer_write_timed(&rb, channel_buffer, nof_bytes, RINGBUFFER_TIMEOUT_MS); + if (err < SRSRAN_SUCCESS and err != SRSRAN_ERROR_TIMEOUT) { + logger.error("Error writing zeros in ringbuffer"); + } + } while (err < SRSRAN_SUCCESS and not quit); + + // Increment channel counter + channel_idx++; + } + + // Increment write timestamp + write_ts += buffer.get_nof_samples(); + } + + void read_baseband(std::vector& buffers, uint32_t nof_samples) + { + // For each ringbuffer, read + int nof_bytes = (int)(nof_samples * sizeof(cf_t)); + uint32_t channel_idx = 0; + for (srsran_ringbuffer_t& rb : ringbuffers) { + // If quit is flagged, return instantly + if (quit) { + return; + } + + // Extract channel buffer pointer + cf_t* channel_buffer = buffers[channel_idx]; + + // If the pointer is not set, use the zero buffer + if (channel_buffer == nullptr) { + channel_buffer = sink_buffer.data(); + } + + // Actual write + int err = SRSRAN_SUCCESS; + do { + err = srsran_ringbuffer_read_timed(&rb, channel_buffer, nof_bytes, RINGBUFFER_TIMEOUT_MS); + if (err < SRSRAN_SUCCESS and err != SRSRAN_ERROR_TIMEOUT) { + logger.error("Error reading zeros in ringbuffer"); + } + } while (err < SRSRAN_SUCCESS and not quit); + + // Increment channel counter + channel_idx++; + } + } + +public: + struct args_t { + double srate_hz = 11.52e6; + uint32_t buffer_sz_ms = 10; ///< Buffer size in milliseconds + uint32_t nof_channels = 1; + + args_t(double srate_hz_, uint32_t buffer_sz_ms_, uint32_t nof_channels_) : + srate_hz(srate_hz_), buffer_sz_ms(buffer_sz_ms_), nof_channels(nof_channels_) + {} + }; + + phy_common(const args_t& args, srslog::basic_logger& logger_) : srate_hz(args.srate_hz), logger(logger_) + { + uint32_t buffer_sz = std::ceil((double)args.buffer_sz_ms * srate_hz * 1e-3); + uint32_t buffer_sz_bytes = sizeof(cf_t) * buffer_sz; + + // Allocate data buffer + zero_buffer.resize(buffer_sz); + sink_buffer.resize(buffer_sz); + + // Allocate ring buffers + ringbuffers.resize(args.nof_channels); + + // Initialise buffers + for (srsran_ringbuffer_t& rb : ringbuffers) { + if (srsran_ringbuffer_init(&rb, buffer_sz_bytes) < SRSRAN_SUCCESS) { + logger.error("Error ringbuffer init"); + } + } + } + + ~phy_common() + { + for (srsran_ringbuffer_t& rb : ringbuffers) { + srsran_ringbuffer_free(&rb); + } + } + + void push_semaphore(void* worker_ptr) { semaphore.push(worker_ptr); } + + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override + { + // Synchronize worker + semaphore.wait(w_ctx.worker_ptr); + + // Protect internal buffers and states + std::unique_lock lock(ringbuffers_mutex); + + uint64_t tx_ts = srsran_timestamp_uint64(&w_ctx.tx_time.get(0), srate_hz); + + // Check transmit timestamp is not in the past + if (tx_ts < write_ts) { + logger.error("Tx time (%f) is %d samples in the past", + srsran_timestamp_real(&w_ctx.tx_time.get(0)), + (uint32_t)(write_ts - tx_ts)); + semaphore.release(); + return; + } + + // Write zero padding if necessary + write_zero_padding((uint32_t)(tx_ts - write_ts)); + + // Write baseband + if (tx_enable) { + write_baseband(buffer); + } else { + write_zero_padding(buffer.get_nof_samples()); + } + + // Release semaphore, so next worker can be used + semaphore.release(); + } + + void read(std::vector& buffers, uint32_t nof_samples, srsran::rf_timestamp_t& timestamp) + { + // Protect internal buffers and states + std::unique_lock lock(ringbuffers_mutex); + + // Detect if zero padding is necessary + if (read_ts + nof_samples > write_ts) { + uint32_t nof_zero_pading = (uint32_t)((read_ts + nof_samples) - write_ts); + write_zero_padding(nof_zero_pading); + } + + // Actual baseband read + read_baseband(buffers, nof_samples); + + // Write timestamp + srsran_timestamp_init_uint64(timestamp.get_ptr(0), read_ts, srate_hz); + + // Increment Rx timestamp + read_ts += nof_samples; + } + + void stop() { quit = true; } +}; + +#endif // SRSRAN_DUMMY_PHY_COMMON_H diff --git a/test/phy/dummy_rx_harq_proc.h b/test/phy/dummy_rx_harq_proc.h new file mode 100644 index 000000000..2ef987111 --- /dev/null +++ b/test/phy/dummy_rx_harq_proc.h @@ -0,0 +1,71 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_DUMMY_RX_HARQ_PROC_H +#define SRSRAN_DUMMY_RX_HARQ_PROC_H + +#include +#include +#include +#include +#include +#include + +class dummy_rx_harq_proc +{ +private: + srsran::byte_buffer_t data; + srsran_softbuffer_rx_t softbuffer = {}; + std::atomic tbs = {0}; + bool first = true; + uint32_t ndi = 0; + +public: + dummy_rx_harq_proc() : data(0) + { + // Initialise softbuffer + if (srsran_softbuffer_rx_init_guru(&softbuffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < + SRSRAN_SUCCESS) { + ERROR("Error Tx buffer"); + } + } + + ~dummy_rx_harq_proc() { srsran_softbuffer_rx_free(&softbuffer); } + + srsran_softbuffer_rx_t& get_softbuffer(uint32_t ndi_, uint32_t tbs_) + { + if (ndi != ndi_ || first) { + srsran_softbuffer_rx_reset(&softbuffer); + ndi = ndi_; + tbs = tbs_; + first = false; + } + + return softbuffer; + } + + uint32_t get_tbs() const { return tbs; } +}; + +class dummy_rx_harq_entity : public srsran::circular_array +{}; + +#endif // SRSRAN_DUMMY_RX_HARQ_PROC_H diff --git a/test/phy/dummy_tx_harq_proc.h b/test/phy/dummy_tx_harq_proc.h new file mode 100644 index 000000000..ccc13f8c6 --- /dev/null +++ b/test/phy/dummy_tx_harq_proc.h @@ -0,0 +1,101 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_TX_DUMMY_HARQ_PROC_H +#define SRSRAN_TX_DUMMY_HARQ_PROC_H + +#include +#include +#include +#include +#include +#include + +class dummy_tx_harq_proc +{ +private: + mutable std::mutex mutex; + srsran_random_t random_gen = nullptr; + srsran::byte_buffer_t data; + srsran_softbuffer_tx_t softbuffer = {}; + uint32_t tbs = 0; + bool first = true; + uint32_t ndi = 0; + +public: + dummy_tx_harq_proc() + { + // Initialise softbuffer + if (srsran_softbuffer_tx_init_guru(&softbuffer, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) < + SRSRAN_SUCCESS) { + ERROR("Error Tx buffer"); + } + } + + void init(uint32_t pid) { random_gen = srsran_random_init(pid * 1234); } + + ~dummy_tx_harq_proc() + { + srsran_softbuffer_tx_free(&softbuffer); + srsran_random_free(random_gen); + } + + srsran::byte_buffer_t* get_tb(uint32_t tbs_) + { + std::unique_lock lock(mutex); + tbs = tbs_; + srsran_random_byte_vector(random_gen, data.msg, tbs / 8); + return &data; + } + + srsran_softbuffer_tx_t& get_softbuffer(uint32_t ndi_) + { + std::unique_lock lock(mutex); + + if (ndi_ != ndi || first) { + srsran_softbuffer_tx_reset(&softbuffer); + ndi = ndi_; + first = false; + } + + return softbuffer; + } + + uint32_t get_tbs() const + { + std::unique_lock lock(mutex); + return tbs; + } +}; + +class dummy_tx_harq_entity : public srsran::circular_array +{ +public: + dummy_tx_harq_entity() : srsran::circular_array() + { + uint32_t pid = 0; + for (dummy_tx_harq_proc& proc : *this) { + proc.init(pid++); + } + } +}; + +#endif // SRSRAN_TX_DUMMY_HARQ_PROC_H diff --git a/test/phy/dummy_ue_phy.h b/test/phy/dummy_ue_phy.h new file mode 100644 index 000000000..a8a2c3a58 --- /dev/null +++ b/test/phy/dummy_ue_phy.h @@ -0,0 +1,74 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_DUMMY_UE_PHY_H +#define SRSRAN_DUMMY_UE_PHY_H + +#include "srsue/hdr/phy/nr/worker_pool.h" + +class ue_dummy_phy : public srsue::phy_interface_stack_nr +{ +public: + ue_dummy_phy(const char* logname, uint32_t max_workers) : + logger(srslog::fetch_basic_logger(logname)), workers(logger, max_workers) + {} + bool init(const srsue::phy_args_nr_t& args_, + srsran::phy_common_interface& common, + srsue::stack_interface_phy_nr* stack_, + int prio) + { + return workers.init(args_, common, stack_, prio); + } + void stop() { workers.stop(); } + srsue::nr::sf_worker* wait_worker(uint32_t tti) { return workers.wait_worker(tti); } + void start_worker(srsue::nr::sf_worker* w) { workers.start_worker(w); } + void get_metrics(srsue::phy_metrics_t& m) { return workers.get_metrics(m); } + + int set_rar_grant(uint32_t rar_slot_idx, + std::array packed_ul_grant, + uint16_t rnti, + srsran_rnti_type_t rnti_type) override + { + return workers.set_rar_grant(rar_slot_idx, packed_ul_grant, rnti, rnti_type); + } + void send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec = 0.0f) override + {} + + bool has_valid_sr_resource(uint32_t sr_id) override { return workers.has_valid_sr_resource(sr_id); } + void clear_pending_grants() override { return workers.clear_pending_grants(); } + + bool set_config(const srsran::phy_cfg_nr_t& cfg) override { return workers.set_config(cfg); } + phy_nr_state_t get_state() override { return PHY_NR_STATE_IDLE; } + void reset_nr() override {} + + bool start_cell_search(const cell_search_args_t& req) override { return false; } + + bool start_cell_select(const cell_select_args_t& req) override { return false; } + +private: + srslog::basic_logger& logger; + srsue::nr::worker_pool workers; +}; + +#endif // SRSRAN_DUMMY_UE_PHY_H diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h new file mode 100644 index 000000000..67f8edfcd --- /dev/null +++ b/test/phy/dummy_ue_stack.h @@ -0,0 +1,314 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_DUMMY_UE_STACK_H +#define SRSRAN_DUMMY_UE_STACK_H + +#include "dummy_rx_harq_proc.h" +#include "dummy_tx_harq_proc.h" +#include "srsran/asn1/rrc_nr.h" +#include "srsran/interfaces/ue_nr_interfaces.h" + +class ue_dummy_stack : public srsue::stack_interface_phy_nr +{ +public: + struct prach_metrics_t { + uint32_t count; + }; + + struct cell_search_metrics_t { + // Last cell search result for the PCI and SSB candidate + srsue::stack_interface_phy_nr::cell_search_result_t last_result; + + // Signal Measurements + float epre_db_avg = 0.0f; + float epre_db_min = +INFINITY; + float epre_db_max = -INFINITY; + float rsrp_db_avg = 0.0f; + float rsrp_db_min = +INFINITY; + float rsrp_db_max = -INFINITY; + float snr_db_avg = 0.0f; + float snr_db_min = +INFINITY; + float snr_db_max = -INFINITY; + float cfo_hz_avg = 0.0f; + float cfo_hz_min = +INFINITY; + float cfo_hz_max = -INFINITY; + uint32_t count = 0; + }; + + struct metrics_t { + std::map > cell_search; + std::map prach = {}; ///< PRACH metrics indexed with premable index + uint32_t sr_count = 0; ///< Counts number of transmitted SR + }; + +private: + srslog::basic_logger& logger = srslog::fetch_basic_logger("UE-STCK"); + std::mutex rnti_mutex; + srsran_random_t random_gen = srsran_random_init(0x1323); + srsran_rnti_type_t dl_rnti_type = srsran_rnti_type_c; + uint16_t rnti = 0; + bool valid = false; + uint32_t sr_period = 0; + uint32_t sr_count = 0; + uint32_t prach_period = 0; + uint32_t prach_preamble = 0; + bool prach_pending = false; + metrics_t metrics = {}; + srsue::phy_interface_stack_nr& phy; + + // Atributes to flag configuration PHy complete + bool configuration_complete = false; + std::mutex configuration_complete_mutex; + std::condition_variable configuration_complete_cvar; + + // Attributes for throttling PHY and avoiding PHY free-running + bool pending_tti = false; + std::mutex pending_tti_mutex; + std::condition_variable pending_tti_cvar; + std::atomic running = {true}; + + dummy_tx_harq_entity tx_harq_proc; + dummy_rx_harq_entity rx_harq_proc; + + std::atomic cell_search_finished = {false}; + std::atomic cell_select_finished = {false}; + cell_select_result_t cell_select_result = {}; + +public: + struct args_t { + uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions + uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable. + uint32_t prach_period = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable. + std::string log_level = "warning"; + }; + ue_dummy_stack(const args_t& args, srsue::phy_interface_stack_nr& phy_) : + rnti(args.rnti), sr_period(args.sr_period), prach_period(args.prach_period), phy(phy_) + { + logger.set_level(srslog::str_to_basic_level(args.log_level)); + valid = true; + } + ~ue_dummy_stack() { srsran_random_free(random_gen); } + + void in_sync() override {} + void out_of_sync() override {} + void run_tti(const uint32_t tti, const uint32_t tti_jump) override + { + // Wait for tick from test bench + std::unique_lock lock(pending_tti_mutex); + while (not pending_tti and running) { + pending_tti_cvar.wait_for(lock, std::chrono::milliseconds(1)); + } + + // Let the tick proceed + pending_tti = false; + pending_tti_cvar.notify_all(); + + // Run PRACH + if (prach_period != 0) { + uint32_t slot_idx = tti % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz); + uint32_t sfn = tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz); + if (not prach_pending and slot_idx == 0 and sfn % prach_period == 0) { + prach_preamble = srsran_random_uniform_int_dist(random_gen, 0, 63); + phy.send_prach(0, prach_preamble, 0.0f, 0.0f); + prach_pending = true; + } + } + } + void tick() + { + // Wait for TTI to get processed + std::unique_lock lock(pending_tti_mutex); + while (pending_tti and running) { + pending_tti_cvar.wait_for(lock, std::chrono::milliseconds(1)); + } + + // Let the TTI proceed + pending_tti = true; + pending_tti_cvar.notify_all(); + } + + void stop() + { + running = false; + pending_tti_cvar.notify_all(); + } + sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override + { + std::unique_lock lock(rnti_mutex); + return {rnti, dl_rnti_type}; + } + sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override + { + std::unique_lock lock(rnti_mutex); + return {rnti, srsran_rnti_type_c}; + } + void new_grant_dl(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_t* action) override + { + action->tb.enabled = true; + action->tb.softbuffer = &rx_harq_proc[grant.pid].get_softbuffer(grant.ndi, grant.tbs); + } + void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) override {} + void new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) override + { + if (action == nullptr) { + return; + } + action->tb.enabled = true; + action->tb.payload = tx_harq_proc[grant.pid].get_tb(grant.tbs); + action->tb.softbuffer = &tx_harq_proc[grant.pid].get_softbuffer(grant.ndi); + } + void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) override + { + std::unique_lock lock(rnti_mutex); + dl_rnti_type = srsran_rnti_type_ra; + rnti = 1 + s_id + 14 * t_id + 14 * 80 * f_id + 14 * 80 * 8 * ul_carrier_id; + metrics.prach[prach_preamble].count++; + prach_pending = false; + } + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) override + { + if (sr_period == 0) { + return false; + } + + if (sr_count >= (sr_period - 1) and not ul_sch_tx) { + metrics.sr_count++; + sr_count = 0; + return true; + } + + sr_count++; + return false; + } + bool is_valid() const { return valid; } + + const metrics_t& get_metrics() const { return metrics; } + void reset_metrics() + { + metrics.cell_search.clear(); + metrics.prach.clear(); + metrics.sr_count = 0; + } + + void set_phy_config_complete(bool status) override + { + std::unique_lock lock(configuration_complete_mutex); + configuration_complete = true; + configuration_complete_cvar.notify_all(); + } + + void wait_phy_config_complete() + { + std::unique_lock lock(configuration_complete_mutex); + while (not configuration_complete) { + configuration_complete_cvar.wait(lock); + } + configuration_complete = false; + } + + bool get_cell_search_finished() + { + bool ret = cell_search_finished; + + cell_search_finished = false; + + return ret; + } + + void cell_search_found_cell(const cell_search_result_t& result) override + { + if (not result.cell_found) { + logger.info("Cell search finished without detecting any cell"); + + // Flag as cell search is done + cell_search_finished = true; + + return; + } + + // Pack PBCH message bits + std::array bit_pack_pbch_msg = {}; + asn1::cbit_ref cbit(bit_pack_pbch_msg.data(), bit_pack_pbch_msg.size()); + srsran_bit_pack_vector((uint8_t*)result.pbch_msg.payload, bit_pack_pbch_msg.data(), SRSRAN_PBCH_MSG_NR_SZ); + + // Unpack MIB with ASN1 + asn1::rrc_nr::bcch_bch_msg_s bcch; + bcch.unpack(cbit); + + // Convert MIB to JSON + asn1::json_writer json; + bcch.to_json(json); + + // Unpack MIB with C lib + srsran_mib_nr_t mib_c = {}; + srsran_pbch_msg_nr_mib_unpack(&result.pbch_msg, &mib_c); + + // Convert MIB from C lib to info + std::array mib_info = {}; + srsran_pbch_msg_nr_mib_info(&mib_c, mib_info.data(), (uint32_t)mib_info.size()); + + // Convert CSI to string + std::array csi_info = {}; + srsran_csi_meas_info_short(&result.measurements, csi_info.data(), (uint32_t)csi_info.size()); + + logger.info( + "Cell found pci=%d %s %s ASN1: %s", result.pci, mib_info.data(), csi_info.data(), json.to_string().c_str()); + + cell_search_metrics_t& m = metrics.cell_search[result.pci][result.pbch_msg.ssb_idx]; + m.last_result = result; + m.epre_db_min = SRSRAN_MIN(m.epre_db_min, result.measurements.epre_dB); + m.epre_db_max = SRSRAN_MAX(m.epre_db_max, result.measurements.epre_dB); + m.epre_db_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.epre_dB, m.epre_db_avg, m.count); + m.rsrp_db_min = SRSRAN_MIN(m.rsrp_db_min, result.measurements.rsrp_dB); + m.rsrp_db_max = SRSRAN_MAX(m.rsrp_db_max, result.measurements.rsrp_dB); + m.rsrp_db_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.rsrp_dB, m.rsrp_db_avg, m.count); + m.snr_db_min = SRSRAN_MIN(m.snr_db_min, result.measurements.snr_dB); + m.snr_db_max = SRSRAN_MAX(m.snr_db_max, result.measurements.snr_dB); + m.snr_db_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.snr_dB, m.snr_db_avg, m.count); + m.cfo_hz_min = SRSRAN_MIN(m.cfo_hz_min, result.measurements.cfo_hz); + m.cfo_hz_max = SRSRAN_MAX(m.cfo_hz_max, result.measurements.cfo_hz); + m.cfo_hz_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.cfo_hz, m.cfo_hz_avg, m.count); + m.count++; + + // Flag as cell search is done + cell_search_finished = true; + } + + bool get_cell_select_finished() + { + bool ret = cell_select_finished; + + cell_select_finished = false; + + return ret; + } + + cell_select_result_t get_cell_select_result() { return cell_select_result; } + + void cell_select_completed(const cell_select_result_t& result) override + { + cell_select_result = result; + cell_select_finished = true; + } +}; + +#endif // SRSRAN_DUMMY_UE_STACK_H diff --git a/test/phy/nr_phy_test.cc b/test/phy/nr_phy_test.cc new file mode 100644 index 000000000..5dec0ab54 --- /dev/null +++ b/test/phy/nr_phy_test.cc @@ -0,0 +1,474 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "dummy_gnb_stack.h" +#include "dummy_ue_stack.h" +#include "srsran/common/phy_cfg_nr_default.h" +#include "srsran/common/test_common.h" +#include "test_bench.h" +#include +#include + +// shorten boost program options namespace +namespace bpo = boost::program_options; + +static double assert_sr_detection_min = 1.000; +static double assert_cqi_detection_min = 1.000; +static double assert_pusch_bler_max = 0.000; +static double assert_pdsch_bler_max = 0.000; +static double assert_prach_detection_min = 1.000; +static double assert_prach_ta_min = 0.000; +static double assert_prach_ta_max = 0.000; +static double assert_pucch_snr_min = 0.000; + +test_bench::args_t::args_t(int argc, char** argv) +{ + std::string config_file; + std::string reference_cfg_str; + bpo::options_description options; + bpo::options_description options_tb("Test bench options"); + bpo::options_description options_gnb_stack("gNb stack and scheduling related options"); + bpo::options_description options_gnb_phy("gNb PHY related options"); + bpo::options_description options_ue_stack("UE stack options"); + bpo::options_description options_ue_phy("UE PHY options"); + bpo::options_description options_ue_rf("UE RF options"); + bpo::options_description options_assertion("Test assertions"); + bpo::options_description options_conf_file("Configuration file"); + + uint16_t rnti = 17921; + + gnb_stack.pdsch.slots = "0,1,2,3,4,5"; + gnb_stack.pusch.slots = "6,7,8,9"; + + // clang-format off + options_tb.add_options() + ("rnti", bpo::value(&rnti)->default_value(rnti), "UE RNTI") + ("duration", bpo::value(&durations_slots)->default_value(durations_slots), "Test duration in slots") + ("lib.log.level", bpo::value(&phy_lib_log_level)->default_value(phy_lib_log_level), "PHY library log level") + ("reference", bpo::value(&reference_cfg_str)->default_value(reference_cfg_str), "Reference PHY configuration arguments") + ("dl_channel.awgn_enable", bpo::value(&dl_channel.awgn_enable)->default_value(dl_channel.awgn_enable), "DL Channel AWGN enable / disable") + ("dl_channel.awgn_snr", bpo::value(&dl_channel.awgn_snr_dB)->default_value(dl_channel.awgn_snr_dB), "DL Channel AWGN SNR in dB") + ("ul_channel.awgn_enable", bpo::value(&ul_channel.awgn_enable)->default_value(ul_channel.awgn_enable), "UL Channel AWGN enable / disable") + ("ul_channel.awgn_snr", bpo::value(&ul_channel.awgn_snr_dB)->default_value(ul_channel.awgn_snr_dB), "UL Channel AWGN SNR in dB") + ("ul_channel.signal_power_dBfs", bpo::value(&ul_channel.awgn_signal_power_dBfs)->default_value(ul_channel.awgn_signal_power_dBfs), "UL Channel expected signal power") + ("channel.cfo", bpo::value(&ul_channel.hst_fd_hz)->default_value(0), "Channel HST Doppler frequency") +; + + options_gnb_stack.add_options() + ("gnb.stack.pdcch.aggregation_level", bpo::value(&gnb_stack.pdcch_aggregation_level)->default_value(gnb_stack.pdcch_aggregation_level), "PDCCH aggregation level") + ("gnb.stack.pdsch.candidate", bpo::value(&gnb_stack.pdcch_dl_candidate)->default_value(gnb_stack.pdcch_dl_candidate), "PDCCH candidate index for PDSCH") + ("gnb.stack.pdsch.start", bpo::value(&gnb_stack.pdsch.rb_start)->default_value(0), "PDSCH scheduling frequency allocation start") + ("gnb.stack.pdsch.length", bpo::value(&gnb_stack.pdsch.rb_length)->default_value(gnb_stack.pdsch.rb_length), "PDSCH scheduling frequency allocation length") + ("gnb.stack.pdsch.slots", bpo::value(&gnb_stack.pdsch.slots)->default_value(gnb_stack.pdsch.slots), "Slots enabled for PDSCH") + ("gnb.stack.pdsch.mcs", bpo::value(&gnb_stack.pdsch.mcs)->default_value(gnb_stack.pdsch.mcs), "PDSCH scheduling modulation code scheme") + ("gnb.stack.pusch.candidate", bpo::value(&gnb_stack.pdcch_ul_candidate)->default_value(gnb_stack.pdcch_ul_candidate), "PDCCH candidate index for PUSCH") + ("gnb.stack.pusch.start", bpo::value(&gnb_stack.pusch.rb_start)->default_value(0), "PUSCH scheduling frequency allocation start") + ("gnb.stack.pusch.length", bpo::value(&gnb_stack.pusch.rb_length)->default_value(gnb_stack.pusch.rb_length), "PUSCH scheduling frequency allocation length") + ("gnb.stack.pusch.slots", bpo::value(&gnb_stack.pusch.slots)->default_value(gnb_stack.pusch.slots), "Slots enabled for PUSCH") + ("gnb.stack.pusch.mcs", bpo::value(&gnb_stack.pusch.mcs)->default_value(gnb_stack.pusch.mcs), "PUSCH scheduling modulation code scheme") + ("gnb.stack.log.level", bpo::value(&gnb_stack.log_level)->default_value(gnb_stack.log_level), "Stack log level") + ("gnb.stack.use_dummy_mac", bpo::value(&gnb_stack.use_dummy_mac)->default_value("dummymac"), "Use dummy or real NR scheduler (dummymac or realmac)") + ; + + options_gnb_phy.add_options() + ("gnb.phy.nof_threads", bpo::value(&gnb_phy.nof_phy_threads)->default_value(1), "Number of threads") + ("gnb.phy.log.level", bpo::value(&gnb_phy.log.phy_level)->default_value("warning"), "gNb PHY log level") + ("gnb.phy.log.hex_limit", bpo::value(&gnb_phy.log.phy_hex_limit)->default_value(0), "gNb PHY log hex limit") + ("gnb.phy.log.id_preamble", bpo::value(&gnb_phy.log.id_preamble)->default_value("GNB/"), "gNb PHY log ID preamble") + ("gnb.phy.pusch.max_iter", bpo::value(&gnb_phy.pusch_max_its)->default_value(10), "PUSCH LDPC max number of iterations") + ; + + options_ue_phy.add_options() + ("ue.phy.fix_wideband_cqi", bpo::value(&ue_phy.fix_wideband_cqi)->default_value(15), "Fix wideband CQI value, set to 0 or greater than 15 to disable") + ("ue.phy.nof_threads", bpo::value(&ue_phy.nof_phy_threads)->default_value(1), "Number of threads") + ("ue.phy.log.level", bpo::value(&ue_phy.log.phy_level)->default_value("warning"), "UE PHY log level") + ("ue.phy.log.hex_limit", bpo::value(&ue_phy.log.phy_hex_limit)->default_value(0), "UE PHY log hex limit") + ("ue.phy.log.id_preamble", bpo::value(&ue_phy.log.id_preamble)->default_value(" UE/"), "UE PHY log ID preamble") + ; + + options_ue_rf.add_options() + ("ue.rf.log.level", bpo::value(&ue_radio_log_level)->default_value("warning"), "UE RF log level") + ; + + options_ue_stack.add_options() + ("ue.stack.sr.period", bpo::value(&ue_stack.sr_period)->default_value(ue_stack.sr_period), "SR period in number of opportunities. Set 0 to disable and 1 for all.") + ("ue.stack.prach.period", bpo::value(&ue_stack.prach_period)->default_value(ue_stack.prach_period), "PRACH period in SFN. Set 0 to disable and 1 for all.") + ; + + options_assertion.add_options() + ("assert.sr.detection.min", bpo::value(&assert_sr_detection_min)->default_value(assert_sr_detection_min), "Scheduling request minimum detection threshold") + ("assert.cqi.detection.min", bpo::value(&assert_cqi_detection_min)->default_value(assert_cqi_detection_min), "CQI report minimum detection threshold") + ("assert.pusch.bler.max", bpo::value(&assert_pusch_bler_max)->default_value(assert_pusch_bler_max), "PUSCH maximum BLER threshold") + ("assert.pdsch.bler.max", bpo::value(&assert_pdsch_bler_max)->default_value(assert_pdsch_bler_max), "PDSCH maximum BLER threshold") + ("assert.prach.ta.min", bpo::value(&assert_prach_ta_min)->default_value(assert_prach_ta_min), "PRACH estimated TA minimum value threshold") + ("assert.prach.ta.max", bpo::value(&assert_prach_ta_max)->default_value(assert_prach_ta_max), "PRACH estimated TA maximum value threshold") + ("assert.pucch.snr.min", bpo::value(&assert_pucch_snr_min)->default_value(assert_pucch_snr_min), "PUCCH DMRS minimum SNR allowed threshold") + ; + + options_conf_file.add_options() + ("config_file", bpo::value(&config_file), "Configuration file") + ; + bpo::positional_options_description p; + p.add("config_file", -1); + + options.add(options_tb).add(options_assertion).add(options_gnb_stack).add(options_gnb_phy).add(options_ue_stack) + .add(options_ue_phy).add(options_ue_rf).add(options_conf_file).add_options() + ("help", "Show this message") + ; + // clang-format on + + bpo::variables_map vm; + try { + bpo::store(bpo::command_line_parser(argc, argv).options(options).positional(p).run(), vm); + bpo::notify(vm); + + // Apply the High Speed Train args to the DL channel as well + ul_channel.hst_enable = std::isnormal(ul_channel.hst_fd_hz); + dl_channel.hst_enable = ul_channel.hst_enable; + dl_channel.hst_fd_hz = ul_channel.hst_fd_hz; + } catch (bpo::error& e) { + std::cerr << e.what() << std::endl; + return; + } + + // help option was given or error - print usage and exit + if (vm.count("help")) { + std::cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << std::endl << std::endl; + std::cout << options_tb << std::endl << options_assertion << std::endl; + std::cout << options_gnb_phy << std::endl << options_gnb_stack << std::endl; + std::cout << options_ue_phy << std::endl << options_ue_stack << std::endl; + exit(0); + } + + // if config file given + if (vm.count("config_file") != 0U) { + std::cout << "Reading configuration file " << config_file << "..." << std::endl; + std::ifstream conf(config_file.c_str(), std::ios::in); + if (conf.fail()) { + std::cout << "Failed to read configuration file " << config_file << " - exiting" << std::endl; + exit(1); + } + + // parse config file and handle errors gracefully + try { + bpo::store(bpo::parse_config_file(conf, options), vm); + bpo::notify(vm); + } catch (const boost::program_options::error& e) { + std::cerr << e.what() << std::endl; + exit(1); + } + } + + // Load default reference configuration + phy_cfg = srsran::phy_cfg_nr_default_t(srsran::phy_cfg_nr_default_t::reference_cfg_t(reference_cfg_str)); + + // Calculate the DL signal power from the number of PRBs + dl_channel.awgn_signal_power_dBfs = srsran_gnb_dl_get_maximum_signal_power_dBfs(phy_cfg.carrier.nof_prb); + + // Reverses the Doppler shift for the UL + ul_channel.hst_init_time_s = 0.5 * dl_channel.hst_period_s; + + // Calculate sampling rate in Hz + srate_hz = (double)(srsran_min_symbol_sz_rb(phy_cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs)); + ue_phy.srate_hz = srate_hz; + + cell_list.resize(1); + cell_list[0].carrier = phy_cfg.carrier; + cell_list[0].rf_port = 0; + cell_list[0].cell_id = 0; + + ue_stack.rnti = rnti; + + gnb_stack.rnti = rnti; + gnb_stack.phy_cfg = phy_cfg; + gnb_stack.wait_preamble = ue_stack.prach_period > 0; + + if (gnb_stack.pdsch.rb_length == 0) { + gnb_stack.pdsch.rb_length = phy_cfg.carrier.nof_prb; + gnb_stack.pdsch.rb_start = 0; + } + + if (gnb_stack.pusch.rb_length == 0) { + gnb_stack.pusch.rb_length = phy_cfg.carrier.nof_prb; + gnb_stack.pdsch.rb_start = 0; + } + + // Disable RT priority in the UE PHY + ue_phy.workers_thread_prio = -1; + ue_phy.slot_recv_thread_prio = -1; + + // Flag configuration as valid + valid = true; +} + +int main(int argc, char** argv) +{ + srslog::init(); + + // Parse test bench arguments + test_bench::args_t args(argc, argv); + + // Parse arguments + TESTASSERT(args.valid); + + // Create test bench + test_bench tb(args); + + // Assert bench is initialised correctly + TESTASSERT(tb.is_initialised()); + + // Start cell selection procedure + srsue::rrc_interface_phy_nr::cell_select_result_t cs_res = + tb.run_cell_select(args.phy_cfg.carrier, args.phy_cfg.get_ssb_cfg()); + srsran_assert(cs_res.status == srsue::rrc_interface_phy_nr::cell_select_result_t::SUCCESSFUL, + "Failed to perform cell selection"); + + // Run per TTI basis + while (tb.run_tti()) { + ; // Do nothing + } + + // Stop test bench + tb.stop(); + + // Flush log + srslog::flush(); + + // Retrieve MAC metrics + test_bench::metrics_t metrics = tb.get_metrics(); + + // Print PRACH + double prach_detection = 0.0; + double prach_ta = 0.0; + uint32_t prach_tx_count = 0; + uint32_t prach_rx_count = 0; + if (metrics.ue_stack.prach.size() > 0) { + srsran::console("PRACH:\n"); + srsran::console( + " +------------+------------+------------+------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %10s | %10s | %10s | %10s | %10s | %10s |\n", + "Preamble", + "Transmit'd", + "Received", + "Detection", + "Avg TA", + "Min TA", + "Max TA"); + srsran::console( + " +------------+------------+------------+------------+------------+------------+------------+\n"); + + for (const auto& p : metrics.ue_stack.prach) { + // Ensure the detected count matches with transmission + // TESTASSERT(metrics.gnb_stack.prach.count(p.first)); + // TESTASSERT(metrics.gnb_stack.prach[p.first].count == p.second.count); + TESTASSERT(p.second.count != 0); + prach_tx_count += p.second.count; + + gnb_dummy_stack::prach_metrics_t gnb_prach = {}; + if (metrics.gnb_stack.prach.count(p.first) > 0) { + gnb_prach = metrics.gnb_stack.prach[p.first]; + prach_ta = SRSRAN_VEC_SAFE_CMA(gnb_prach.avg_ta, prach_ta, prach_rx_count); + } else { + gnb_prach.avg_ta = NAN; + gnb_prach.min_ta = NAN; + gnb_prach.max_ta = NAN; + } + prach_rx_count += gnb_prach.count; + + double detection = (double)gnb_prach.count / (double)p.second.count; + + srsran::console(" | %10d | %10d | %10d | %10.3f | %10.1f | %10.1f | %10.1f |\n", + p.first, + p.second.count, + gnb_prach.count, + detection, + gnb_prach.avg_ta, + gnb_prach.min_ta, + gnb_prach.max_ta); + } + srsran::console( + " +------------+------------+------------+------------+------------+------------+------------+\n"); + } + if (prach_tx_count > 0) { + prach_detection = (double)prach_rx_count / (double)prach_tx_count; + } + + // Print PUCCH + if (metrics.gnb_stack.pucch.count > 0) { + srsran::console("PUCCH DMRS Receiver metrics:\n"); + srsran::console(" +------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %10s | %10s | %10s |\n", "Measure", "Average", "Min", "Max"); + srsran::console(" +------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "EPRE (dB)", + metrics.gnb_stack.pucch.epre_db_avg, + metrics.gnb_stack.pucch.epre_db_min, + metrics.gnb_stack.pucch.epre_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "RSRP (dB)", + metrics.gnb_stack.pucch.rsrp_db_avg, + metrics.gnb_stack.pucch.rsrp_db_min, + metrics.gnb_stack.pucch.rsrp_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "SINR (dB)", + metrics.gnb_stack.pucch.snr_db_avg, + metrics.gnb_stack.pucch.snr_db_min, + metrics.gnb_stack.pucch.snr_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "TA (us)", + metrics.gnb_stack.pucch.ta_us_avg, + metrics.gnb_stack.pucch.ta_us_min, + metrics.gnb_stack.pucch.ta_us_max); + srsran::console(" +------------+------------+------------+------------+\n"); + } + + // Print PDSCH metrics if scheduled + double pdsch_bler = 0.0; + if (metrics.gnb_stack.mac.tx_pkts > 0) { + pdsch_bler = (double)metrics.gnb_stack.mac.tx_errors / (double)metrics.gnb_stack.mac.tx_pkts; + + float pdsch_shed_rate = 0.0f; + pdsch_shed_rate = (float)metrics.gnb_stack.mac.tx_brate / (float)metrics.gnb_stack.mac.tx_pkts / 1000.0f; + + srsran::console("PDSCH:\n"); + srsran::console(" Count: %d\n", metrics.gnb_stack.mac.tx_pkts); + srsran::console(" BLER: %f\n", pdsch_bler); + srsran::console(" Sched Rate: %f Mbps\n", pdsch_shed_rate); + srsran::console(" Net Rate: %f Mbps\n", (1.0f - pdsch_bler) * pdsch_shed_rate); + srsran::console(" Retx Rate: %f Mbps\n", pdsch_bler * pdsch_shed_rate); + srsran::console("\n"); + } + + // Print PUSCH metrics if scheduled + double pusch_bler = 0.0; + if (metrics.gnb_stack.mac.rx_pkts > 0) { + if (metrics.gnb_stack.mac.rx_pkts != 0) { + pusch_bler = (double)metrics.gnb_stack.mac.rx_errors / (double)metrics.gnb_stack.mac.rx_pkts; + } + + float pusch_shed_rate = 0.0f; + if (metrics.gnb_stack.mac.rx_pkts != 0) { + pusch_shed_rate = (float)metrics.gnb_stack.mac.rx_brate / (float)metrics.gnb_stack.mac.rx_pkts / 1000.0f; + } + + srsran::console("PUSCH:\n"); + srsran::console(" Count: %d\n", metrics.gnb_stack.mac.rx_pkts); + srsran::console(" BLER: %f\n", pusch_bler); + srsran::console(" Sched Rate: %f Mbps\n", pusch_shed_rate); + srsran::console(" Net Rate: %f Mbps\n", (1.0f - pusch_bler) * pusch_shed_rate); + srsran::console(" Retx Rate: %f Mbps\n", pusch_bler * pusch_shed_rate); + srsran::console("\n"); + } + + // Print PUSCH + if (metrics.gnb_stack.pusch.count > 0) { + srsran::console("PUSCH DMRS Receiver metrics:\n"); + srsran::console(" +------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %10s | %10s | %10s |\n", "Measure", "Average", "Min", "Max"); + srsran::console(" +------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "EPRE (dB)", + metrics.gnb_stack.pusch.epre_db_avg, + metrics.gnb_stack.pusch.epre_db_min, + metrics.gnb_stack.pusch.epre_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "RSRP (dB)", + metrics.gnb_stack.pusch.rsrp_db_avg, + metrics.gnb_stack.pusch.rsrp_db_min, + metrics.gnb_stack.pusch.rsrp_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "SINR (dB)", + metrics.gnb_stack.pusch.snr_db_avg, + metrics.gnb_stack.pusch.snr_db_min, + metrics.gnb_stack.pusch.snr_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "TA (us)", + metrics.gnb_stack.pusch.ta_us_avg, + metrics.gnb_stack.pusch.ta_us_min, + metrics.gnb_stack.pusch.ta_us_max); + srsran::console(" +------------+------------+------------+------------+\n"); + } + + srsran::console("UCI stats:\n"); + srsran::console(" +------------+------------+------------+------------+------------+\n"); + srsran::console( + " | %10s | %10s | %10s | %10s | %10s |\n", "Field", "Transmit'd", "Received", "Detection", "Avg. Val."); + srsran::console(" +------------+------------+------------+------------+------------+\n"); + + // Print SR + double sr_detection = 0.0; + if (metrics.ue_stack.sr_count > 0) { + sr_detection = (double)metrics.gnb_stack.sr_count / (double)metrics.ue_stack.sr_count; + srsran::console(" | %10s | %10d | %10d | %10.5f | %10s |\n", + "SR", + metrics.ue_stack.sr_count, + metrics.gnb_stack.sr_count, + sr_detection, + "-"); + } + + // Print SR + double cqi_detection = 0.0; + if (metrics.gnb_stack.cqi_count > 0) { + cqi_detection = (double)metrics.gnb_stack.cqi_valid_count / (double)metrics.gnb_stack.cqi_count; + srsran::console(" | %10s | %10d | %10d | %10.5f | %10.5f |\n", + "CQI", + metrics.gnb_stack.cqi_count, + metrics.gnb_stack.cqi_valid_count, + cqi_detection, + metrics.gnb_stack.mac.dl_cqi); + } + srsran::console(" +------------+------------+------------+------------+------------+\n"); + + // Assert metrics + srsran_assert(metrics.gnb_stack.mac.tx_pkts == 0 or pdsch_bler <= assert_pdsch_bler_max, + "PDSCH BLER (%f) exceeds the assertion maximum (%f)", + pdsch_bler, + assert_pusch_bler_max); + srsran_assert(metrics.gnb_stack.mac.rx_pkts == 0 or pusch_bler <= assert_pusch_bler_max, + "PUSCH BLER (%f) exceeds the assertion maximum (%f)", + pusch_bler, + assert_pusch_bler_max); + srsran_assert(metrics.ue_stack.sr_count == 0 or sr_detection >= assert_sr_detection_min, + "SR detection probability (%f) did not reach the assertion minimum (%f)", + sr_detection, + assert_sr_detection_min); + srsran_assert(metrics.gnb_stack.cqi_count == 0 or cqi_detection >= assert_cqi_detection_min, + "CQI report detection probability (%f) did not reach the assertion minimum (%f)", + cqi_detection, + assert_sr_detection_min); + srsran_assert(prach_tx_count == 0 or prach_detection >= assert_prach_detection_min, + "PRACH detection probability (%f) did not reach the assertion minimum (%f)", + prach_detection, + assert_prach_detection_min); + srsran_assert(prach_tx_count == 0 or (prach_ta >= assert_prach_ta_min and prach_ta <= assert_prach_ta_max), + "PRACH TA average measurement %f is higher than minimum (%d) or above maximum (%f)", + prach_ta, + assert_prach_ta_min, + assert_prach_ta_max); + srsran_assert(metrics.gnb_stack.pucch.count == 0 or (metrics.gnb_stack.pucch.snr_db_min >= assert_pucch_snr_min), + "Minimum PUCCH DMRS SNR %f is below the minimum (%d)", + metrics.gnb_stack.pucch.snr_db_min, + assert_pucch_snr_min); + + // If reached here, the test is successful + return SRSRAN_SUCCESS; +} diff --git a/test/phy/test_bench.h b/test/phy/test_bench.h new file mode 100644 index 000000000..1a4392f60 --- /dev/null +++ b/test/phy/test_bench.h @@ -0,0 +1,276 @@ +/** + * Copyright 2013-2022 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSRAN_TEST_BENCH_H +#define SRSRAN_TEST_BENCH_H + +#include "dummy_phy_common.h" +#include "srsenb/hdr/phy/nr/worker_pool.h" +#include "srsran/radio/radio_dummy.h" +#include "srsue/hdr/phy/nr/worker_pool.h" +#include "srsue/hdr/phy/phy_nr_sa.h" + +class test_bench +{ +private: + const std::string UE_PHY_COM_LOG_NAME = "UE /PHY/COM"; + const std::string GNB_PHY_COM_LOG_NAME = "GNB/PHY/COM"; + const std::string CHANNEL_LOG_NAME = "CHANNEL"; + uint32_t slot_idx = 0; + uint64_t slot_count = 0; + uint64_t duration_slots = 0; + gnb_dummy_stack gnb_stack; + srsenb::nr::worker_pool gnb_phy; + phy_common gnb_phy_com; + ue_dummy_stack ue_stack; + srsue::phy_nr_sa ue_phy; + srsran::radio_dummy ue_radio; + srsran::rf_timestamp_t gnb_rx_time = {}; + + bool initialised = false; + uint32_t sf_sz = 0; + srsran::rf_buffer_t rf_buffer; + // Channel simulator + srsran::channel dl_channel; + srsran::channel ul_channel; + +public: + struct args_t { + double srate_hz = (double)(768 * SRSRAN_SUBC_SPACING_NR(0)); + uint32_t nof_channels = 1; + uint32_t buffer_sz_ms = 10; + bool valid = false; + srsran::phy_cfg_nr_t phy_cfg = {}; + srsenb::phy_cell_cfg_list_nr_t cell_list = {}; + srsenb::nr::worker_pool::args_t gnb_phy; + gnb_dummy_stack::args_t gnb_stack; + srsue::phy_args_nr_t ue_phy; + ue_dummy_stack::args_t ue_stack; + std::string gnb_phy_com_log_level = "info"; + std::string ue_radio_log_level = "info"; + std::string phy_lib_log_level = "none"; + uint64_t durations_slots = 100; + + // channel simulator args + srsran::channel::args_t dl_channel; + srsran::channel::args_t ul_channel; + args_t(int argc, char** argv); + }; + + struct metrics_t { + gnb_dummy_stack::metrics_t gnb_stack = {}; + ue_dummy_stack::metrics_t ue_stack = {}; + srsue::phy_metrics_t ue_phy = {}; + }; + + test_bench(const args_t& args) : + gnb_stack(args.gnb_stack), + gnb_phy(gnb_phy_com, gnb_stack, srslog::get_default_sink(), args.gnb_phy.nof_phy_threads), + ue_stack(args.ue_stack, ue_phy), + ue_phy("PHY"), + ue_radio(), + gnb_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), + srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)), + sf_sz((uint32_t)std::round(args.srate_hz * 1e-3)), + duration_slots(args.durations_slots), + dl_channel(args.dl_channel, 1, srslog::fetch_basic_logger(CHANNEL_LOG_NAME, srslog::get_default_sink(), false)), + ul_channel(args.ul_channel, 1, srslog::fetch_basic_logger(CHANNEL_LOG_NAME, srslog::get_default_sink(), false)), + rf_buffer(1) + { + srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.gnb_phy_com_log_level)); + srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.gnb_phy_com_log_level)); + srslog::fetch_basic_logger(CHANNEL_LOG_NAME).set_level(srslog::basic_levels::error); + + if (not gnb_phy.init(args.gnb_phy, args.cell_list)) { + return; + } + + srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {}; + common_cfg.carrier = args.phy_cfg.carrier; + common_cfg.pdcch = args.phy_cfg.pdcch; + common_cfg.prach = args.phy_cfg.prach; + common_cfg.duplex_mode = args.phy_cfg.duplex.mode; + common_cfg.ssb = args.phy_cfg.get_ssb_cfg(); + + if (gnb_phy.set_common_cfg(common_cfg) < SRSRAN_SUCCESS) { + return; + } + + // Initialise radio + srsran::rf_args_t rf_args = {}; + rf_args.nof_antennas = 1; + rf_args.nof_carriers = 1; + rf_args.srate_hz = args.srate_hz; + rf_args.log_level = args.ue_radio_log_level; + if (ue_radio.init(rf_args, &ue_phy) != SRSRAN_SUCCESS) { + return; + } + + // Initialise UE PHY + if (ue_phy.init(args.ue_phy, &ue_stack, &ue_radio) != SRSRAN_SUCCESS) { + return; + } + + // Wait for PHY to initialise + ue_phy.wait_initialize(); + + // Set UE configuration + if (not ue_phy.set_config(args.phy_cfg)) { + return; + } + + // Wait for UE to notify stack that the configuration is completed + ue_stack.wait_phy_config_complete(); + + // Make sure PHY log is not set by UE or gNb PHY + set_handler_enabled(false); + if (args.phy_lib_log_level == "info") { + set_srsran_verbose_level(SRSRAN_VERBOSE_INFO); + } else if (args.phy_lib_log_level == "debug") { + set_srsran_verbose_level(SRSRAN_VERBOSE_DEBUG); + } else { + set_srsran_verbose_level(SRSRAN_VERBOSE_NONE); + } + + // Configure channel + dl_channel.set_srate((uint32_t)args.srate_hz); + ul_channel.set_srate((uint32_t)args.srate_hz); + initialised = true; + } + + srsue::rrc_interface_phy_nr::cell_select_result_t run_cell_select(const srsran_carrier_nr_t& carrier, + const srsran_ssb_cfg_t& ssb_cfg) + { + // Prepare return value + srsue::rrc_interface_phy_nr::cell_select_result_t ret = {}; + + // Prepare cell selection arguments + srsue::phy_interface_rrc_nr::cell_select_args_t cs_args = {}; + cs_args.carrier = carrier; + cs_args.ssb_cfg = ssb_cfg; + + // Start cell selection procedure + if (not ue_phy.start_cell_select(cs_args)) { + // Return unsuccessful cell select result + return {}; + } + + // Run test bench until the cell selection is completed + while (not ue_stack.get_cell_select_finished()) { + run_tti(); + } + + // It is now the right time to start scheduling + gnb_stack.start_scheduling(); + + // Reset slot counting + slot_count = 0; + + return ue_stack.get_cell_select_result(); + } + + void stop() + { + ue_stack.stop(); + ue_radio.stop(); + gnb_phy_com.stop(); + gnb_phy.stop(); + ue_phy.stop(); + gnb_stack.stop(); + } + + ~test_bench() = default; + + bool is_initialised() + { + return ue_stack.is_valid() and ue_radio.is_init() and ue_phy.is_initialized() and gnb_stack.is_valid() and + initialised; + } + + bool run_tti() + { + // Get gNb worker + srsenb::nr::slot_worker* gnb_worker = gnb_phy.wait_worker(slot_idx); + if (gnb_worker == nullptr) { + return false; + } + + // Feed gNb the UE transmitted signal + std::vector gnb_rx_buffers(1); + gnb_rx_buffers[0] = gnb_worker->get_buffer_rx(0); + ue_radio.read_tx(gnb_rx_buffers.data(), sf_sz); + + // Run the UL channel simulator + ul_channel.run(gnb_rx_buffers.data(), gnb_rx_buffers.data(), (uint32_t)sf_sz, gnb_rx_time.get(0)); + + // Set gNb TX time + srsran::rf_timestamp_t gnb_time = gnb_rx_time; + gnb_time.add(TX_ENB_DELAY * 1e-3); + + // Advance gNb Rx time + gnb_rx_time.add(1e-3); + + // Set gNb context + srsran::phy_common_interface::worker_context_t gnb_context; + gnb_context.sf_idx = slot_idx; + gnb_context.worker_ptr = gnb_worker; + gnb_context.last = true; // Set last if standalone + gnb_context.tx_time.copy(gnb_time); + gnb_worker->set_context(gnb_context); + + // Start gNb work + gnb_phy_com.push_semaphore(gnb_worker); + gnb_phy.start_worker(gnb_worker); + + // Feed UE the gNb transmitted signal + srsran::rf_timestamp_t ue_time = {}; + std::vector ue_rx_buffers(1); + ue_rx_buffers[0] = rf_buffer.get(0); + gnb_phy_com.read(ue_rx_buffers, sf_sz, ue_time); + + // Run the DL channel simulator + dl_channel.run(ue_rx_buffers.data(), ue_rx_buffers.data(), (uint32_t)sf_sz, ue_time.get(0)); + + // Write signal in UE radio buffer, this triggers UE to work + ue_radio.write_rx(ue_rx_buffers.data(), sf_sz); + + // Throttle UE PHY by running stack tick + ue_stack.tick(); + + // Increment slot index, the slot index shall be continuous + slot_idx = (slot_idx + 1) % (1024 * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz)); + + // Increment slot counter and determine end of execution + slot_count++; + return slot_count <= duration_slots; + } + + metrics_t get_metrics() + { + metrics_t metrics = {}; + metrics.gnb_stack = gnb_stack.get_metrics(); + metrics.ue_stack = ue_stack.get_metrics(); + ue_phy.get_metrics(srsran::srsran_rat_t::nr, &metrics.ue_phy); // get the metrics from the ue_phy + return metrics; + } +}; + +#endif // SRSRAN_TEST_BENCH_H diff --git a/test/run_lte.sh b/test/run_lte.sh index cea490c73..39a393154 100755 --- a/test/run_lte.sh +++ b/test/run_lte.sh @@ -1,7 +1,7 @@ #!/bin/bash # -# Copyright 2013-2021 Software Radio Systems Limited +# Copyright 2013-2022 Software Radio Systems Limited # # This file is part of srsRAN # @@ -299,7 +299,7 @@ epc_args="$build_path/../srsepc/epc.conf.example \ --log.filename=./${nof_prb}prb_epc.log" enb_args="$build_path/../srsenb/enb.conf.example \ --enb_files.sib_config=$build_path/../srsenb/sib.conf.example \ - --enb_files.drb_config=$build_path/../srsenb/drb.conf.example \ + --enb_files.rb_config=$build_path/../srsenb/rb.conf.example \ --rf.device_name=zmq \ --log.all_level=info \ --enb.n_prb=$nof_prb \ @@ -312,8 +312,8 @@ ue_args="$build_path/../srsue/ue.conf.example \ --gw.netns=$ue_netns \ --log.all_level=info \ --log.filename=./${nof_prb}prb_ue.log \ - --pcap.enable=true \ - --pcap.filename=./${nof_prb}prb_ue.pcap" + --pcap.enable=mac \ + --pcap.mac_filename=./${nof_prb}prb_ue.pcap" if ([ "$num_cc" == "2" ]) then