diff --git a/CMakeLists.txt b/CMakeLists.txt index 631b3dfc4..ff2ba916f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -204,8 +204,8 @@ endif(CMAKE_COMPILER_IS_GNUCXX) if(CMAKE_COMPILER_IS_GNUCC) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-write-strings -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE -g") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-comment -Wno-write-strings -Wno-format-extra-args -Winline -Wno-unused-result -Wno-format -std=c99 -D_GNU_SOURCE -g") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-comment -Wno-reorder -Wno-unused-but-set-variable -Wno-unused-variable") if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") find_package(SSE) @@ -321,3 +321,14 @@ if(NOT DISABLE_SRSUE) else(NOT DISABLE_SRSUE) message(STATUS "Building without srsUE") endif(NOT DISABLE_SRSUE) + +if(NOT DISABLE_SRSENB) + if(RF_FOUND) + message(STATUS "Building with srsENB") + add_subdirectory(srsenb) + else(RF_FOUND) + message(STATUS "Building without srsENB due to missing RF driver") + endif(RF_FOUND) +else(NOT DISABLE_SRSENB) + message(STATUS "Building without srsENB") +endif(NOT DISABLE_SRSENB) diff --git a/cmake/modules/FindLibConfig.cmake b/cmake/modules/FindLibConfig.cmake new file mode 100644 index 000000000..1d7acbdeb --- /dev/null +++ b/cmake/modules/FindLibConfig.cmake @@ -0,0 +1,75 @@ +# Find the CUnit includes and library +# +# This module defines +# LIBCONFIG_INCLUDE_DIR, where to find cppunit include files, etc. +# LIBCONFIG_LIBRARIES, the libraries to link against to use CppUnit. +# LIBCONFIG_STATIC_LIBRARIY_PATH +# LIBCONFIG_FOUND, If false, do not try to use CppUnit. + +# also defined, but not for general use are +# LIBCONFIG_LIBRARY, where to find the CUnit library. + +#MESSAGE("Searching for libconfig library") + +FIND_PATH(LIBCONFIG_INCLUDE_DIR libconfig.h + /usr/local/include + /usr/include +) + +FIND_PATH(LIBCONFIGPP_INCLUDE_DIR libconfig.h++ + /usr/local/include + /usr/include +) + +FIND_LIBRARY(LIBCONFIG_LIBRARY config + /usr/local/lib + /usr/lib +) + +FIND_LIBRARY(LIBCONFIGPP_LIBRARY config++ + /usr/local/lib + /usr/lib +) + +FIND_LIBRARY(LIBCONFIG_STATIC_LIBRARY "libconfig${CMAKE_STATIC_LIBRARY_SUFFIX}" + /usr/local/lib + /usr/lib +) + +FIND_LIBRARY(LIBCONFIGPP_STATIC_LIBRARY "libconfig++${CMAKE_STATIC_LIBRARY_SUFFIX}" + /usr/local/lib + /usr/lib +) + + +IF(LIBCONFIG_INCLUDE_DIR) + IF(LIBCONFIG_LIBRARY) + SET(LIBCONFIG_FOUND TRUE) + SET(LIBCONFIG_LIBRARIES ${LIBCONFIG_LIBRARY}) + SET(LIBCONFIG_STATIC_LIBRARY_PATH ${LIBCONFIG_STATIC_LIBRARY}) + ENDIF(LIBCONFIG_LIBRARY) +ENDIF(LIBCONFIG_INCLUDE_DIR) + +IF(LIBCONFIGPP_INCLUDE_DIR) + IF(LIBCONFIGPP_LIBRARY) + SET(LIBCONFIGPP_FOUND TRUE) + SET(LIBCONFIGPP_LIBRARIES ${LIBCONFIGPP_LIBRARY}) + SET(LIBCONFIGPP_STATIC_LIBRARY_PATH ${LIBCONFIGPP_STATIC_LIBRARY}) + ENDIF(LIBCONFIGPP_LIBRARY) +ENDIF(LIBCONFIGPP_INCLUDE_DIR) + +IF (LIBCONFIGPP_FOUND) + IF (NOT LibConfig_FIND_QUIETLY) + MESSAGE(STATUS "Found LibConfig++: ${LIBCONFIGPP_LIBRARIES}" ) + MESSAGE(STATUS "static LibConfig++ path: ${LIBCONFIGPP_STATIC_LIBRARY_PATH}") + MESSAGE(STATUS "Found LibConfig: ${LIBCONFIG_LIBRARIES}") + MESSAGE(STATUS "static LibConfig path: ${LIBCONFIG_STATIC_LIBRARY_PATH}") + ENDIF (NOT LibConfig_FIND_QUIETLY) +ELSE (LIBCONFIGPP_FOUND) + IF (LibConfig_FIND_REQUIRED) + MESSAGE(SEND_ERROR "Could NOT find LibConfig") + ENDIF (LibConfig_FIND_REQUIRED) +ENDIF (LIBCONFIGPP_FOUND) + +MARK_AS_ADVANCED(LIBCONFIG_INCLUDE_DIR LIBCONFIG_LIBRARIES) +MARK_AS_ADVANCED(LIBCONFIGPP_INCLUDE_DIR LIBCONFIGPP_LIBRARIES) diff --git a/cmake/modules/FindSCTP.cmake b/cmake/modules/FindSCTP.cmake new file mode 100644 index 000000000..1ce75edff --- /dev/null +++ b/cmake/modules/FindSCTP.cmake @@ -0,0 +1,38 @@ +# - Try to find sctp +# +# Once done this will define +# SCTP_FOUND - System has mbedtls +# SCTP_INCLUDE_DIRS - The mbedtls include directories +# SCTP_LIBRARIES - The mbedtls library + +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_SCTP sctp) + +#find Mbedtls +FIND_PATH( + SCTP_INCLUDE_DIRS + NAMES netinet/sctp.h + HINTS ${PC_SCTP_INCLUDEDIR} + ${CMAKE_INSTALL_PREFIX}/include + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + SCTP_LIBRARIES + NAMES sctp + HINTS ${PC_SCTP_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +message(STATUS "SCTP LIBRARIES: " ${SCTP_LIBRARIES}) +message(STATUS "SCTP INCLUDE DIRS: " ${SCTP_INCLUDE_DIRS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SCTP DEFAULT_MSG SCTP_LIBRARIES SCTP_INCLUDE_DIRS) +MARK_AS_ADVANCED(SCTP_LIBRARIES SCTP_INCLUDE_DIRS) diff --git a/srsenb/CMakeLists.txt b/srsenb/CMakeLists.txt new file mode 100644 index 000000000..ed7ed1f1d --- /dev/null +++ b/srsenb/CMakeLists.txt @@ -0,0 +1,77 @@ +# +# Copyright 2013-2017 Software Radio Systems Limited +# +# This file is part of srsLTE +# +# srsLTE is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsLTE is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General 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(LibConfig REQUIRED) +find_package(SCTP REQUIRED) + +if(STATIC_LIBCONFIGPP) + set(LIBCONFIGPP_LIBRARIES "${LIBCONFIGPP_STATIC_LIBRARY_PATH}") +endif(STATIC_LIBCONFIGPP) + +######################################################################## +# Find boost +######################################################################## +set(BOOST_REQUIRED_COMPONENTS + program_options + system +) +if(UNIX AND EXISTS "/usr/lib64") + list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix +endif(UNIX AND EXISTS "/usr/lib64") +set(Boost_ADDITIONAL_VERSIONS + "1.35.0" "1.35" "1.36.0" "1.36" "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39" + "1.40.0" "1.40" "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44" + "1.45.0" "1.45" "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49" + "1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53" "1.54.0" "1.54" + "1.55.0" "1.55" "1.56.0" "1.56" "1.57.0" "1.57" "1.58.0" "1.58" "1.59.0" "1.59" + "1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64" + "1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69" +) +find_package(Boost "1.35" COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) + +if(NOT Boost_FOUND) + message(FATAL_ERROR "Boost required to compile srsENB") +endif() + +######################################################################## +# Find dependencies +######################################################################## +find_package(Threads REQUIRED) + + +######################################################################## +# Setup the include and linker paths +######################################################################## +include_directories( + ${Boost_INCLUDE_DIRS} + ${POLAR_INCLUDE_DIRS} + ${PROJECT_SOURCE_DIR}/srsenb/hdr +) + +link_directories( + ${Boost_LIBRARY_DIRS} + ${POLAR_LIBRARY_DIRS} +) + +######################################################################## +# Add subdirectories +######################################################################## +add_subdirectory(src) +add_subdirectory(test) diff --git a/srsenb/drb.conf.example b/srsenb/drb.conf.example new file mode 100644 index 000000000..2d649bc53 --- /dev/null +++ b/srsenb/drb.conf.example @@ -0,0 +1,56 @@ + +// All times are in ms. Use -1 for infinity, where available + +qci_config = ( + +{ + qci=7; + pdcp_config = { + discard_timer = 100; + pdcp_sn_size = 12; + } + rlc_config = { + ul_um = { + sn_field_length = 10; + }; + dl_um = { + sn_field_length = 10; + t_reordering = 80; + }; + }; + logical_channel_config = { + priority = 11; + prioritized_bit_rate = -1; + bucket_size_duration = 100; + log_chan_group = 3; + }; +}, +{ + qci=9; + pdcp_config = { + discard_timer = -1; + status_report_required = false; + } + rlc_config = { + ul_am = { + t_poll_retx = 200; + poll_pdu = 16; + poll_byte = -1; + max_retx_thresh = 8; + }; + dl_am = { + t_reordering = 80; + t_status_prohibit = 35; + }; + }; + logical_channel_config = { + priority = 3; + prioritized_bit_rate = 8; + bucket_size_duration = 50; + log_chan_group = 3; + }; +} + +); + + diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example new file mode 100644 index 000000000..104d9f0bf --- /dev/null +++ b/srsenb/enb.conf.example @@ -0,0 +1,162 @@ +##################################################################### +# srsENB configuration file +##################################################################### + +##################################################################### +# eNB configuration +# +# enb_id: 20-bit eNB identifier. +# cell_id: 8-bit cell identifier. +# tac: 16-bit Tracking Area Code. +# mcc: Mobile Country Code +# mnc: Mobile Network Code +# mme_addr: IP address of MME for S1 connnection +# gtp_bind_addr: Local IP address to bind for GTP connection +# +##################################################################### +[enb] +enb_id = 0x19B +cell_id = 0x01 +phy_cell_id = 1 +tac = 0x0007 +mcc = 001 +mnc = 01 +mme_addr = 127.0.1.100 +gtp_bind_addr = 127.0.1.1 +n_prb = 25 + +##################################################################### +# eNB configuration files +# +# sib_config: SIB1, SIB2 and SIB3 configuration file +# rr_config: Radio Resources configuration file +# drb_config: DRB configuration file +##################################################################### +[enb_files] +sib_config = sib.conf +rr_config = rr.conf +drb_config = drb.conf + +##################################################################### +# RF configuration +# +# dl_earfcn: EARFCN code for DL +# tx_gain: Transmit gain (dB). +# rx_gain: Optional receive gain (dB). If disabled, AGC if enabled +# +# Optional parameters: +# 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: "" +# #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. +# burst_preamble_us: Preamble length to transmit before start of burst. +# Default "auto". B210 USRP: 400 us, bladeRF: 0 us. +##################################################################### +[rf] +dl_earfcn = 3400 +tx_gain = 60 +rx_gain = 50 + +#device_name = auto +#device_args = auto +#time_adv_nsamples = auto +#burst_preamble_us = auto + + +##################################################################### +# MAC-layer packet capture configuration +# +# Packets are captured to file in the compact format decoded by +# the Wireshark mac-lte-framed dissector and with DLT 147. +# To use the dissector, edit the preferences for DLT_USER to +# add an entry with DLT=147, Payload Protocol=mac-lte-framed. +# For more information see: https://wiki.wireshark.org/MAC-LTE +# +# enable: Enable MAC layer packet captures (true/false) +# filename: File path to use for packet captures +##################################################################### +[pcap] +enable = false +filename = /tmp/enb.pcap + +##################################################################### +# Log configuration +# +# Log levels can be set for individual layers. "all_level" sets log +# level for all layers unless otherwise configured. +# Format: e.g. phy_level = info +# +# In the same way, packet hex dumps can be limited for each level. +# "all_hex_limit" sets the hex limit for all layers unless otherwise +# configured. +# Format: e.g. phy_hex_limit = 32 +# +# Logging layers: phy, mac, rlc, pdcp, rrc, nas, gtpu, usim, all +# Logging levels: debug, info, warning, error, none +# +# filename: File path to use for log output +##################################################################### +[log] +all_level = info +all_hex_limit = 32 +filename = /tmp/enb.log + +[gui] +enable = false + +##################################################################### +# Scheduler configuration options +# +# pdsch_mcs: Optional fixed PDSCH MCS (ignores reported CQIs if specified) +# 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 +# #nof_ctrl_symbols: Number of control symbols +# +##################################################################### +[scheduler] +#pdsch_mcs = -1 +#pdsch_max_mcs = -1 +#pusch_mcs = -1 +#pusch_max_mcs = -1 +#nof_ctrl_symbols = 3 + +##################################################################### +# Expert configuration options +# +# pdsch_max_its: Maximum number of turbo decoder iterations (Default 4) +# nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 2) +# metrics_period_secs: Sets the period at which metrics are requested from the UE. +# pregenerate_signals: Pregenerate uplink signals after attach. Improves CPU performance. +# tx_amplitude: Transmit amplitude factor (set 0-1 to reduce PAPR) +# link_failure_nof_err: Number of PUSCH failures after which a radio-link failure is triggered. +# a link failure is when SNR<0 and CRC=KO +##################################################################### +[expert] +#pdsch_max_its = 4 +#nof_phy_threads = 2 +#pregenerate_signals = false +#tx_amplitude = 0.8 +#link_failure_nof_err = 10 +#rrc_inactivity_timer = 5000 + +##################################################################### +# Manual RF calibration +# +# Applies DC offset and IQ imbalance to TX and RX modules. +# Currently this configuration is only used if the detected device is a bladeRF +# +# tx_corr_dc_gain: TX DC offset gain correction +# tx_corr_dc_phase: TX DC offset phase correction +# tx_corr_iq_i: TX IQ imbalance inphase correction +# tx_corr_iq_q: TX IQ imbalance quadrature correction +# same can be configured for rx_* +##################################################################### +[rf_calibration] +tx_corr_dc_gain = 20 +tx_corr_dc_phase = 184 +tx_corr_iq_i = 19 +tx_corr_iq_q = 97 diff --git a/srsenb/hdr/CMakeLists.txt b/srsenb/hdr/CMakeLists.txt new file mode 100644 index 000000000..83b16737b --- /dev/null +++ b/srsenb/hdr/CMakeLists.txt @@ -0,0 +1,5 @@ + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in + ${PROJECT_BINARY_DIR}/version.h +) diff --git a/srsenb/hdr/cfg_parser.h b/srsenb/hdr/cfg_parser.h new file mode 100644 index 000000000..145ee3716 --- /dev/null +++ b/srsenb/hdr/cfg_parser.h @@ -0,0 +1,17 @@ + + +#ifndef CFG_PARSER_H +#define CFG_PARSER_H + +#include "enb.h" + +namespace srsenb { +class cfg_parser +{ +public: + void parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common); +}; + +} + +#endif // CFG_PARSER_H diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h new file mode 100644 index 000000000..331014da9 --- /dev/null +++ b/srsenb/hdr/enb.h @@ -0,0 +1,185 @@ + +/****************************************************************************** + * File: enb.h + * Description: Top-level eNodeB class. Creates and links all + * layers and helpers. + *****************************************************************************/ + +#ifndef ENB_H +#define ENB_H + +#include +#include +#include + +#include "phy/phy.h" +#include "mac/mac.h" +#include "upper/rrc.h" +#include "upper/gtpu.h" +#include "upper/s1ap.h" +#include "upper/rlc.h" +#include "upper/pdcp.h" + +#include "srslte/radio/radio.h" + +#include "srslte/common/bcd_helpers.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/common/logger.h" +#include "srslte/common/log_filter.h" +#include "srslte/common/mac_pcap.h" +#include "srslte/interfaces/sched_interface.h" +#include "srslte/interfaces/enb_metrics_interface.h" + +namespace srsenb { + +/******************************************************************************* + eNodeB Parameters +*******************************************************************************/ + +typedef struct { + s1ap_args_t s1ap; + uint32_t n_prb; + uint32_t pci; +}enb_args_t; + +typedef struct { + std::string sib_config; + std::string rr_config; + std::string drb_config; +} enb_files_t; + +typedef struct { + uint32_t dl_earfcn; + uint32_t ul_earfcn; + float dl_freq; + float ul_freq; + float rx_gain; + float tx_gain; + std::string device_name; + std::string device_args; + std::string time_adv_nsamples; + std::string burst_preamble; +}rf_args_t; + +typedef struct { + bool enable; + std::string filename; +}pcap_args_t; + +typedef struct { + std::string phy_level; + std::string mac_level; + std::string rlc_level; + std::string pdcp_level; + std::string rrc_level; + std::string gtpu_level; + std::string s1ap_level; + std::string all_level; + int phy_hex_limit; + int mac_hex_limit; + int rlc_hex_limit; + int pdcp_hex_limit; + int rrc_hex_limit; + int gtpu_hex_limit; + int s1ap_hex_limit; + int all_hex_limit; + std::string filename; +}log_args_t; + +typedef struct { + bool enable; +}gui_args_t; + +typedef struct { + phy_args_t phy; + mac_args_t mac; + uint32_t rrc_inactivity_timer; + float metrics_period_secs; +}expert_args_t; + +typedef struct { + enb_args_t enb; + enb_files_t enb_files; + rf_args_t rf; + rf_cal_t rf_cal; + pcap_args_t pcap; + log_args_t log; + gui_args_t gui; + expert_args_t expert; +}all_args_t; + +/******************************************************************************* + Main UE class +*******************************************************************************/ + +class enb + :public enb_metrics_interface +{ +public: + static enb* get_instance(void); + static void cleanup(void); + + bool init(all_args_t *args_); + void stop(); + void start_plot(); + + static void rf_msg(srslte_rf_error_t error); + void handle_rf_msg(srslte_rf_error_t error); + + // eNodeB metrics interface + bool get_metrics(enb_metrics_t &m); + + void pregenerate_signals(bool enable); + + +private: + static enb *instance; + enb(); + virtual ~enb(); + + srslte::radio radio; + srsenb::phy phy; + srsenb::mac mac; + srslte::mac_pcap mac_pcap; + srsenb::rlc rlc; + srsenb::pdcp pdcp; + srsenb::rrc rrc; + srsenb::gtpu gtpu; + srsenb::s1ap s1ap; + + srslte::logger logger; + srslte::log_filter rf_log; + std::vector phy_log; + srslte::log_filter mac_log; + srslte::log_filter rlc_log; + srslte::log_filter pdcp_log; + srslte::log_filter rrc_log; + srslte::log_filter gtpu_log; + srslte::log_filter s1ap_log; + + srslte::byte_buffer_pool *pool; + + all_args_t *args; + bool started; + rf_metrics_t rf_metrics; + + srslte::LOG_LEVEL_ENUM level(std::string l); + + bool check_srslte_version(); + int parse_sib1(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data); + int parse_sib2(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *data); + int parse_sib3(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *data); + int parse_sib4(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data); + int parse_sib9(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *data); + int parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common); + int parse_rr(all_args_t *args, rrc_cfg_t *rrc_cfg); + int parse_drb(all_args_t *args, rrc_cfg_t *rrc_cfg); + bool sib_is_present(LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info, uint32_t nof_sched_info, LIBLTE_RRC_SIB_TYPE_ENUM sib_num); + int parse_cell_cfg(all_args_t *args, srslte_cell_t *cell); +}; + +} // namespace srsenb + +#endif // UE_H + diff --git a/srsenb/hdr/mac/mac.h b/srsenb/hdr/mac/mac.h new file mode 100644 index 000000000..86bd8432a --- /dev/null +++ b/srsenb/hdr/mac/mac.h @@ -0,0 +1,206 @@ + +#ifndef MAC_H +#define MAC_H + +#include +#include "srslte/common/log.h" +#include "srslte/common/timers.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/sched_interface.h" +#include "srslte/common/tti_sync_cv.h" +#include "srslte/common/threads.h" +#include "srslte/common/tti_sync_cv.h" +#include "srslte/common/mac_pcap.h" +#include "mac/scheduler.h" +#include "mac/scheduler_metric.h" +#include "srslte/interfaces/enb_metrics_interface.h" +#include "mac/ue.h" + +namespace srsenb { + +class pdu_process_handler +{ +public: + virtual bool process_pdus() = 0; +}; + +typedef struct { + sched_interface::sched_args_t sched; + int link_failure_nof_err; +} mac_args_t; + +class mac + :public mac_interface_phy, + public mac_interface_rlc, + public mac_interface_rrc, + public srslte::mac_interface_timers, + public pdu_process_handler +{ +public: + mac(); + bool init(mac_args_t *args, srslte_cell_t *cell, phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac *rrc, srslte::log *log_h); + void stop(); + + void start_pcap(srslte::mac_pcap* pcap_); + + /******** Interface from PHY (PHY -> MAC) ****************/ + int sr_detected(uint32_t tti, uint16_t rnti); + int rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv); + + int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value); + int snr_info(uint32_t tti, uint16_t rnti, float snr); + int ack_info(uint32_t tti, uint16_t rnti, bool ack); + int crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc_res); + + int get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res); + int get_ul_sched(uint32_t tti, ul_sched_t *ul_sched_res); + + void rl_failure(uint16_t rnti); + void rl_ok(uint16_t rnti); + void tti_clock(); + + /******** Interface from RRC (RRC -> MAC) ****************/ + /* Provides cell configuration including SIB periodicity, etc. */ + int cell_cfg(sched_interface::cell_cfg_t *cell_cfg); + void reset(); + + /* Manages UE scheduling context */ + int ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t *cfg); + int ue_rem(uint16_t rnti); + + // Indicates that the PHY config dedicated has been enabled or not + void phy_config_enabled(uint16_t rnti, bool enabled); + + /* Manages UE bearers and associated configuration */ + int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t *cfg); + int bearer_ue_rem(uint16_t rnti, uint32_t lc_id); + int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue); + + bool process_pdus(); + + void timer_expired(uint32_t timer_id); + + srslte::timers::timer* get(uint32_t timer_id); + u_int32_t get_unique_id(); + + uint32_t get_current_tti(); + void get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS]); + + enum { + HARQ_RTT, + TIME_ALIGNMENT, + CONTENTION_TIMER, + BSR_TIMER_PERIODIC, + BSR_TIMER_RETX, + PHR_TIMER_PERIODIC, + PHR_TIMER_PROHIBIT, + NOF_MAC_TIMERS + } mac_timers_t; + + static const int MAC_NOF_UPPER_TIMERS = 20; + +private: + + void log_step_ul(uint32_t tti); + void log_step_dl(uint32_t tti); + + static const int MAX_LOCATIONS = 20; + static const uint32_t cfi = 3; + srslte_dci_location_t locations[MAX_LOCATIONS]; + + static const int MAC_PDU_THREAD_PRIO = 3; + + + + // Interaction with PHY + phy_interface_mac *phy_h; + rlc_interface_mac *rlc_h; + rrc_interface_mac *rrc_h; + srslte::log *log_h; + + srslte_cell_t cell; + mac_args_t args; + + uint32_t tti; + bool started; + + /* Scheduler unit */ + sched scheduler; + dl_metric_rr sched_metric_dl_rr; + ul_metric_rr sched_metric_ul_rr; + + /* Map of active UEs */ + std::map ue_db; + uint16_t last_rnti; + + uint8_t* assemble_rar(sched_interface::dl_sched_rar_grant_t *grants, uint32_t nof_grants, int rar_idx, uint32_t pdu_len); + uint8_t* assemble_si(uint32_t index); + + const static int rar_payload_len = 128; + std::vector rar_pdu_msg; + uint8_t rar_payload[sched_interface::MAX_RAR_LIST][rar_payload_len]; + + typedef struct { + uint32_t preamble_idx; + uint32_t ta_cmd; + uint16_t temp_crnti; + } pending_rar_t; + + const static int MAX_PENDING_RARS = 64; + pending_rar_t pending_rars[MAX_PENDING_RARS]; + + const static int NOF_BCCH_DLSCH_MSG=sched_interface::MAX_SIBS; + uint8_t bcch_dlsch_payload[sched_interface::MAX_SIB_PAYLOAD_LEN]; + + const static int pcch_payload_buffer_len = 1024; + uint8_t pcch_payload_buffer[pcch_payload_buffer_len]; + srslte_softbuffer_tx_t bcch_softbuffer_tx[NOF_BCCH_DLSCH_MSG]; + srslte_softbuffer_tx_t pcch_softbuffer_tx; + srslte_softbuffer_tx_t rar_softbuffer_tx; + + /* Functions for MAC Timers */ + srslte::timers timers_db; + void setup_timers(); + + // pointer to MAC PCAP object + srslte::mac_pcap* pcap; + + + /* Class to run upper-layer timers with normal priority */ + class upper_timers : public thread { + public: + upper_timers() : timers_db(MAC_NOF_UPPER_TIMERS),ttisync(10240) {start();} + void tti_clock(); + void stop(); + void reset(); + srslte::timers::timer* get(uint32_t timer_id); + uint32_t get_unique_id(); + private: + void run_thread(); + srslte::timers timers_db; + srslte::tti_sync_cv ttisync; + bool running; + }; + upper_timers upper_timers_thread; + + /* Class to process MAC PDUs from DEMUX unit */ + class pdu_process : public thread { + public: + pdu_process(pdu_process_handler *h); + void notify(); + void stop(); + private: + void run_thread(); + bool running; + bool have_data; + pthread_mutex_t mutex; + pthread_cond_t cvar; + pdu_process_handler *handler; + }; + pdu_process pdu_process_thread; + +}; + +} // namespace srsue + +#endif // MAC_H diff --git a/srsenb/hdr/mac/mac_metrics.h b/srsenb/hdr/mac/mac_metrics.h new file mode 100644 index 000000000..cef120c86 --- /dev/null +++ b/srsenb/hdr/mac/mac_metrics.h @@ -0,0 +1,27 @@ + + +#ifndef ENB_MAC_METRICS_H +#define ENB_MAC_METRICS_H + + +namespace srsenb { + +// MAC metrics per user + +struct mac_metrics_t +{ + uint16_t rnti; + int tx_pkts; + int tx_errors; + int tx_brate; + int rx_pkts; + int rx_errors; + int rx_brate; + int ul_buffer; + int dl_buffer; + float phr; +}; + +} // namespace srsenb + +#endif // ENB_MAC_METRICS_H diff --git a/srsenb/hdr/mac/scheduler.h b/srsenb/hdr/mac/scheduler.h new file mode 100644 index 000000000..9a21e52c2 --- /dev/null +++ b/srsenb/hdr/mac/scheduler.h @@ -0,0 +1,202 @@ + +#ifndef SCHED_H +#define SCHED_H + +#include +#include "srslte/common/log.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/sched_interface.h" +#include "scheduler_ue.h" +#include "scheduler_harq.h" +#include + +namespace srsenb { + + +class sched : public sched_interface +{ + + +public: + + + /************************************************************* + * + * Scheduling metric interface definition + * + ************************************************************/ + + class metric_dl + { + public: + + /* Virtual methods for user metric calculation */ + virtual void new_tti(std::map &ue_db, uint32_t start_rb, uint32_t nof_rb, uint32_t nof_ctrl_symbols, uint32_t tti) = 0; + virtual dl_harq_proc* get_user_allocation(sched_ue *user) = 0; + }; + + + class metric_ul + { + public: + + /* Virtual methods for user metric calculation */ + virtual void new_tti(std::map &ue_db, uint32_t nof_rb, uint32_t tti) = 0; + virtual ul_harq_proc* get_user_allocation(sched_ue *user) = 0; + virtual void update_allocation(ul_harq_proc::ul_alloc_t alloc) = 0; + }; + + + + /************************************************************* + * + * FAPI-like Interface + * + ************************************************************/ + + sched(); + + void init(rrc_interface_mac *rrc, srslte::log *log); + void set_metric(metric_dl *dl_metric, metric_ul *ul_metric); + int cell_cfg(cell_cfg_t *cell_cfg); + void set_sched_cfg(sched_args_t *sched_cfg); + int reset(); + + int ue_cfg(uint16_t rnti, ue_cfg_t *ue_cfg); + int ue_rem(uint16_t rnti); + bool ue_exists(uint16_t rnti); + + void phy_config_enabled(uint16_t rnti, bool enabled); + + int bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, ue_bearer_cfg_t *cfg); + int bearer_ue_rem(uint16_t rnti, uint32_t lc_id); + + uint32_t get_ul_buffer(uint16_t rnti); + uint32_t get_dl_buffer(uint16_t rnti); + + int dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue); + int dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code); + + int dl_ack_info(uint32_t tti, uint16_t rnti, bool ack); + int dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size); + int dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value); + + int ul_crc_info(uint32_t tti, uint16_t rnti, bool crc); + int ul_sr_info(uint32_t tti, uint16_t rnti); + int ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr); + int ul_recv_len(uint16_t rnti, uint32_t lcid, uint32_t len); + int ul_phr(uint16_t rnti, int phr); + int ul_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi, uint32_t ul_ch_code); + + int dl_sched(uint32_t tti, dl_sched_res_t *sched_result); + int ul_sched(uint32_t tti, ul_sched_res_t *sched_result); + + + /* Custom TPC functions + */ + void tpc_inc(uint16_t rnti); + void tpc_dec(uint16_t rnti); + + + + static uint32_t get_rvidx(uint32_t retx_idx) { + const static int rv_idx[4] = {0, 2, 3, 1}; + return rv_idx[retx_idx%4]; + } + + + + static void generate_cce_location(srslte_regs_t *regs, sched_ue::sched_dci_cce_t *location, + uint32_t cfi, uint32_t sf_idx = 0, uint16_t rnti = 0); + +private: + + metric_dl *dl_metric; + metric_ul *ul_metric; + srslte::log *log_h; + rrc_interface_mac *rrc; + + cell_cfg_t cfg; + sched_args_t sched_cfg; + + const static int MAX_PRB = 100; + const static int MAX_RBG = 25; + const static int MAX_CCE = 128; + + // This is for computing DCI locations + srslte_regs_t regs; + bool used_cce[MAX_CCE]; + + typedef struct { + int buf_rar; + uint16_t rnti; + uint32_t ra_id; + uint32_t rar_tti; + } sched_rar_t; + + typedef struct { + bool is_in_window; + uint32_t window_start; + uint32_t n_tx; + } sched_sib_t; + + + int dl_sched_bc(dl_sched_bc_t bc[MAX_BC_LIST]); + int dl_sched_rar(dl_sched_rar_t rar[MAX_RAR_LIST]); + int dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST]); + + + int generate_format1a(uint32_t rb_start, uint32_t l_crb, uint32_t tbs, uint32_t rv, srslte_ra_dl_dci_t *dci); + bool generate_dci(srslte_dci_location_t *sched_location, sched_ue::sched_dci_cce_t *locations, uint32_t aggr_level, sched_ue *user = NULL); + + + std::map ue_db; + + sched_sib_t pending_sibs[MAX_SIBS]; + + + typedef struct { + bool enabled; + uint16_t rnti; + uint32_t L; + uint32_t n_prb; + uint32_t mcs; + } pending_msg3_t; + + const static int SCHED_MAX_PENDING_RAR = 8; + sched_rar_t pending_rar[SCHED_MAX_PENDING_RAR]; + pending_msg3_t pending_msg3[10]; + + // Allowed DCI locations for SIB and RAR per CFI + sched_ue::sched_dci_cce_t common_locations[3]; + sched_ue::sched_dci_cce_t rar_locations[3][10]; + + uint32_t bc_aggr_level; + uint32_t rar_aggr_level; + + uint32_t pdsch_re[10]; + uint32_t avail_rbg; + uint32_t P; + uint32_t start_rbg; + uint32_t si_n_rbg; + uint32_t rar_n_rb; + uint32_t nof_rbg; + uint32_t sf_idx; + uint32_t sfn; + uint32_t current_tti; + uint32_t current_cfi; + + bool configured; + + pthread_mutex_t mutex; + + +}; + + + + + +} + +#endif diff --git a/srsenb/hdr/mac/scheduler_harq.h b/srsenb/hdr/mac/scheduler_harq.h new file mode 100644 index 000000000..2e04bdee3 --- /dev/null +++ b/srsenb/hdr/mac/scheduler_harq.h @@ -0,0 +1,102 @@ + +#ifndef SCHED_HARQ_H +#define SCHED_HARQ_H + +#include +#include "srslte/common/log.h" +#include "srslte/interfaces/sched_interface.h" + +namespace srsenb { + +class harq_proc +{ +public: + void config(uint32_t id, uint32_t max_retx, srslte::log* log_h); + void set_max_retx(uint32_t max_retx); + void reset(); + uint32_t get_id(); + bool is_empty(); + + void new_retx(uint32_t tti, int *mcs, int *tbs); + + bool get_ack(); + void set_ack(bool ack); + + uint32_t nof_tx(); + uint32_t nof_retx(); + uint32_t get_tti(); + bool get_ndi(); + +protected: + + void new_tx_common(uint32_t tti, int mcs, int tbs); + bool has_pending_retx_common(); + + bool ack; + bool active; + bool ndi; + uint32_t id; + uint32_t max_retx; + uint32_t n_rtx; + uint32_t tx_cnt; + int tti; + int last_mcs; + int last_tbs; + + srslte::log* log_h; + + private: + bool ack_received; +}; + +class dl_harq_proc : public harq_proc +{ +public: + void new_tx(uint32_t tti, int mcs, int tbs, uint32_t n_cce); + uint32_t get_rbgmask(); + void set_rbgmask(uint32_t new_mask); + bool has_pending_retx(uint32_t tti); + int get_tbs(); + uint32_t get_n_cce(); +private: + uint32_t rbgmask; + uint32_t nof_rbg; + uint32_t n_cce; +}; + +class ul_harq_proc : public harq_proc +{ +public: + + typedef struct { + uint32_t RB_start; + uint32_t L; + } ul_alloc_t; + + void new_tx(uint32_t tti, int mcs, int tbs); + + ul_alloc_t get_alloc(); + void set_alloc(ul_alloc_t alloc); + void same_alloc(); + bool is_adaptive_retx(); + + bool has_pending_ack(); + uint32_t get_pending_data(); + + void set_rar_mcs(uint32_t mcs); + bool get_rar_mcs(int* mcs); + +private: + ul_alloc_t allocation; + bool need_ack; + int pending_data; + uint32_t rar_mcs; + bool has_rar_mcs; + bool is_adaptive; + bool is_msg3; +}; + +} + + +#endif diff --git a/srsenb/hdr/mac/scheduler_metric.h b/srsenb/hdr/mac/scheduler_metric.h new file mode 100644 index 000000000..9f893ff81 --- /dev/null +++ b/srsenb/hdr/mac/scheduler_metric.h @@ -0,0 +1,62 @@ +#ifndef SCHED_METRIC_H +#define SCHED_METRIC_H + +#include "mac/scheduler.h" + +namespace srsenb { + +class dl_metric_rr : public sched::metric_dl +{ +public: + void new_tti(std::map &ue_db, uint32_t start_rb, uint32_t nof_rb, uint32_t nof_ctrl_symbols, uint32_t tti); + dl_harq_proc* get_user_allocation(sched_ue *user); +private: + + const static int MAX_RBG = 25; + + bool new_allocation(uint32_t nof_rbg, uint32_t* rbgmask); + void update_allocation(uint32_t new_mask); + bool allocation_is_valid(uint32_t mask); + + + uint32_t get_required_rbg(sched_ue *user, uint32_t tti); + uint32_t count_rbg(uint32_t mask); + uint32_t calc_rbg_mask(bool mask[25]); + + bool used_rb[MAX_RBG]; + + uint32_t nof_users_with_data; + + uint32_t current_tti; + uint32_t total_rb; + uint32_t used_rb_mask; + uint32_t nof_ctrl_symbols; + uint32_t available_rb; +}; + +class ul_metric_rr : public sched::metric_ul +{ +public: + void new_tti(std::map &ue_db, uint32_t nof_rb, uint32_t tti); + ul_harq_proc* get_user_allocation(sched_ue *user); + void update_allocation(ul_harq_proc::ul_alloc_t alloc); +private: + + const static int MAX_PRB = 100; + + bool new_allocation(uint32_t L, ul_harq_proc::ul_alloc_t *alloc); + bool allocation_is_valid(ul_harq_proc::ul_alloc_t alloc); + + uint32_t nof_users_with_data; + + bool used_rb[MAX_PRB]; + uint32_t current_tti; + uint32_t nof_rb; + uint32_t available_rb; +}; + + +} + +#endif + diff --git a/srsenb/hdr/mac/scheduler_ue.h b/srsenb/hdr/mac/scheduler_ue.h new file mode 100644 index 000000000..d08e29dc3 --- /dev/null +++ b/srsenb/hdr/mac/scheduler_ue.h @@ -0,0 +1,156 @@ + +#ifndef SCHED_UE_H +#define SCHED_UE_H + +#include +#include "srslte/common/log.h" +#include "srslte/interfaces/sched_interface.h" + +#include "scheduler_harq.h" + +namespace srsenb { + +class sched_ue { + +public: + + // used by sched_metric + uint32_t ue_idx; + + typedef struct { + uint32_t cce_start[4][6]; + uint32_t nof_loc[4]; + } sched_dci_cce_t; + + /************************************************************* + * + * FAPI-like Interface + * + ************************************************************/ + sched_ue(); + void reset(); + void phy_config_enabled(uint32_t tti, bool enabled); + void set_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg, sched_interface::cell_cfg_t *cell_cfg, + srslte_regs_t *regs, srslte::log *log_h); + + void set_bearer_cfg(uint32_t lc_id, srsenb::sched_interface::ue_bearer_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 lc_id, uint32_t bsr); + void ul_phr(int phr); + void mac_buffer_state(uint32_t ce_code); + void ul_recv_len(uint32_t lcid, uint32_t len); + void set_ul_cqi(uint32_t tti, uint32_t cqi, uint32_t ul_ch_code); + void set_dl_cqi(uint32_t tti, uint32_t cqi); + int set_ack_info(uint32_t tti, bool ack); + void set_ul_crc(uint32_t tti, bool crc_res); + +/******************************************************* + * Custom functions + *******************************************************/ + + void tpc_inc(); + void tpc_dec(); + + void set_max_mcs(int mcs_ul, int mcs_dl); + void set_fixed_mcs(int mcs_ul, int mcs_dl); + + + +/******************************************************* + * Functions used by scheduler metric objects + *******************************************************/ + + uint32_t get_required_prb_dl(uint32_t req_bytes, uint32_t nof_ctrl_symbols); + uint32_t get_required_prb_ul(uint32_t req_bytes); + + uint32_t get_pending_dl_new_data(uint32_t tti); + uint32_t get_pending_ul_new_data(uint32_t tti); + + dl_harq_proc *get_pending_dl_harq(uint32_t tti); + dl_harq_proc *get_empty_dl_harq(); + ul_harq_proc *get_ul_harq(uint32_t tti); + +/******************************************************* + * Functions used by the scheduler object + *******************************************************/ + + void set_sr(); + void unset_sr(); + + int generate_format1(dl_harq_proc *h, sched_interface::dl_sched_data_t *data, uint32_t tti, uint32_t cfi); + int generate_format0(ul_harq_proc *h, sched_interface::ul_sched_data_t *data, uint32_t tti, bool cqi_request); + + uint32_t get_aggr_level(uint32_t nof_bits); + sched_dci_cce_t *get_locations(uint32_t current_cfi, uint32_t sf_idx); + + bool needs_cqi(uint32_t tti, bool will_send = false); + uint32_t get_max_retx(); + + bool get_pucch_sched(uint32_t current_tti, uint32_t prb_idx[2], uint32_t *L); + bool pucch_sr_collision(uint32_t current_tti, uint32_t n_cce); + +private: + + typedef struct { + sched_interface::ue_bearer_cfg_t cfg; + int buf_tx; + int buf_retx; + int bsr; + } ue_bearer_t; + + bool is_sr_triggered(); + uint32_t get_pending_ul_old_data(); + int alloc_pdu(int tbs, sched_interface::dl_sched_pdu_t* pdu); + + static uint32_t format1_count_prb(uint32_t bitmask, uint32_t cell_nof_prb); + static int cqi_to_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t max_mcs, uint32_t *mcs); + static int alloc_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t req_bytes, uint32_t max_mcs, int *mcs); + + static bool bearer_is_ul(ue_bearer_t *lch); + static bool bearer_is_dl(ue_bearer_t *lch); + + bool is_first_dl_tx(); + + + sched_interface::ue_cfg_t cfg; + srslte_cell_t cell; + srslte::log* log_h; + + /* Buffer states */ + bool sr; + int buf_mac; + int buf_ul; + ue_bearer_t lch[sched_interface::MAX_LC]; + + int power_headroom; + uint32_t dl_cqi; + uint32_t dl_cqi_tti; + uint32_t cqi_request_tti; + uint32_t ul_cqi; + uint32_t ul_cqi_tti; + uint16_t rnti; + uint32_t max_mcs_dl; + uint32_t max_mcs_ul; + int fixed_mcs_ul; + int fixed_mcs_dl; + + int next_tpc_pusch; + int next_tpc_pucch; + + // Allowed DCI locations per CFI and per subframe + sched_dci_cce_t dci_locations[3][10]; + + const static int SCHED_MAX_HARQ_PROC = 8; + dl_harq_proc dl_harq[SCHED_MAX_HARQ_PROC]; + ul_harq_proc ul_harq[SCHED_MAX_HARQ_PROC]; + + bool phy_config_dedicated_enabled; + +}; + +} + + +#endif diff --git a/srsenb/hdr/mac/ue.h b/srsenb/hdr/mac/ue.h new file mode 100644 index 000000000..5e3d424b7 --- /dev/null +++ b/srsenb/hdr/mac/ue.h @@ -0,0 +1,117 @@ + +#ifndef UE_H +#define UE_H + +#include "srslte/common/log.h" +#include "srslte/common/pdu.h" +#include "srslte/common/mac_pcap.h" +#include "srslte/common/pdu_queue.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/sched_interface.h" +#include +#include "mac/mac_metrics.h" + +namespace srsenb { + +class ue : public srslte::read_pdu_interface, + public srslte::pdu_queue::process_callback +{ +public: + + ue() : mac_msg_dl(20), mac_msg_ul(20), pdus(128) { + rlc = NULL; + log_h = NULL; + rnti = 0; + pcap = NULL; + nof_failures = 0; + phr_counter = 0; + is_phy_added = false; + for (int i=0;i +#include +#include + +#include "srslte/interfaces/enb_metrics_interface.h" + +namespace srsenb { + +class metrics_stdout +{ +public: + metrics_stdout(); + + bool init(enb_metrics_interface *u, float report_period_secs=1.0); + void stop(); + void toggle_print(bool b); + static void* metrics_thread_start(void *m); + void metrics_thread_run(); + +private: + void print_metrics(); + void print_disconnect(); + std::string float_to_string(float f, int digits); + std::string float_to_eng_string(float f, int digits); + std::string int_to_eng_string(int f, int digits); + + enb_metrics_interface *enb_; + + bool started; + bool do_print; + pthread_t metrics_thread; + enb_metrics_t metrics; + float metrics_report_period; // seconds + uint8_t n_reports; +}; + +} // namespace srsenb + +#endif // METRICS_STDOUT_H diff --git a/srsenb/hdr/parser.h b/srsenb/hdr/parser.h new file mode 100644 index 000000000..730061cb6 --- /dev/null +++ b/srsenb/hdr/parser.h @@ -0,0 +1,285 @@ + +#ifndef PARSER_H +#define PARSER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace srsenb { + +using namespace libconfig; + +class parser +{ +public: + + class field_itf + { + public: + virtual ~field_itf(){} + virtual int parse(Setting &root) = 0; + virtual const char* get_name() = 0; + }; + + template + class field_enum_str : public field_itf + { + public: + field_enum_str(const char* name_, T *store_ptr_, const char (*value_str_)[20], uint32_t nof_items_, bool *enabled_value_ = NULL) + { + name = name_; + store_ptr = store_ptr_; + value_str = value_str_; + nof_items = nof_items_; + enabled_value = enabled_value_; + } + + const char* get_name() { + return name; + } + + int parse(Setting &root) + { + std::string val; + if (root.exists(name)) { + + if (enabled_value) { + *enabled_value = true; + } + + if (root.lookupValue(name, val)) { + bool found = false; + // find value + for (uint32_t i=0;i + class field_enum_num : public field_itf + { + public: + field_enum_num(const char* name_, T *store_ptr_, const S *value_str_, uint32_t nof_items_, bool *enabled_value_ = NULL) + { + name = name_; + store_ptr = store_ptr_; + value_str = value_str_; + nof_items = nof_items_; + enabled_value = enabled_value_; + } + + const char* get_name() { + return name; + } + + int parse(Setting &root) + { + S val; + if (root.exists(name)) { + + if (enabled_value) { + *enabled_value = true; + } + if (parser::lookupValue(root, name, &val)) { + bool found = false; + // find value + for (uint32_t i=0;i + class field : public field_itf + { + public: + field(const char* name_, T *store_ptr_, bool *enabled_value_ = NULL) + { + name = name_; + store_ptr = store_ptr_; + enabled_value = enabled_value_; + } + + const char* get_name() { + return name; + } + + int parse(Setting &root) + { + if (root.exists(name)) { + if (enabled_value) { + *enabled_value = true; + } + if (!parser::lookupValue(root, name, store_ptr)) { + return -1; + } else { + return 0; + } + } else { + if (enabled_value) { + *enabled_value = false; + return 0; + } else { + return -1; + } + } + } + private: + const char* name; + T *store_ptr; + bool *enabled_value; + }; + + class section + { + public: + section(std::string name); + ~section(); + void set_optional(bool *enabled_value); + void add_subsection(section *s); + void add_field(field_itf *f); + int parse(Setting &root); + private: + std::string name; + bool *enabled_value; + std::list sub_sections; + std::list fields; + }; + + + parser(std::string filename); + int parse(); + void add_section(section *s); + + static int parse_section(std::string filename, section *s); + + static bool lookupValue(Setting &root, const char *name, std::string *val) { + return root.lookupValue(name, *val); + } + static bool lookupValue(Setting &root, const char *name, uint8_t *val) { + uint32_t t; + bool r = root.lookupValue(name, t); + *val = (uint8_t) t; + return r; + } + static bool lookupValue(Setting &root, const char *name, uint16_t *val) { + uint32_t t; + bool r = root.lookupValue(name, t); + *val = (uint16_t) t; + return r; + } + static bool lookupValue(Setting &root, const char *name, uint32_t *val) { + uint32_t t; + bool r = root.lookupValue(name, t); + *val = t; + return r; + } + static bool lookupValue(Setting &root, const char *name, int8_t *val) { + int32_t t; + bool r = root.lookupValue(name, t); + *val = (int8_t) t; + return r; + } + static bool lookupValue(Setting &root, const char *name, int16_t *val) { + int32_t t; + bool r = root.lookupValue(name, t); + *val = (int16_t) t; + return r; + } + static bool lookupValue(Setting &root, const char *name, int32_t *val) { + int32_t t; + bool r = root.lookupValue(name, t); + *val = t; + return r; + } + static bool lookupValue(Setting &root, const char *name, double *val) { + double t; + bool r = root.lookupValue(name, t); + *val = t; + return r; + } + static bool lookupValue(Setting &root, const char *name, bool *val) { + bool t; + bool r = root.lookupValue(name, t); + *val = t; + return r; + } + + +private: + std::list sections; + std::string filename; +}; +} +#endif // PARSER_H diff --git a/srsenb/hdr/phy/phch_common.h b/srsenb/hdr/phy/phch_common.h new file mode 100644 index 000000000..379d2e758 --- /dev/null +++ b/srsenb/hdr/phy/phch_common.h @@ -0,0 +1,85 @@ + + +#ifndef ENBPHCHCOMMON_H +#define ENBPHCHCOMMON_H + +#include +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/enb_metrics_interface.h" + +#include "srslte/common/log.h" +#include "srslte/common/threads.h" +#include "srslte/common/thread_pool.h" +#include "srslte/radio/radio.h" + +namespace srsenb { + +typedef struct { + float max_prach_offset_us; + int pusch_max_its; + float tx_amplitude; + int nof_phy_threads; + std::string equalizer_mode; + float estimator_fil_w; + bool pregenerate_signals; +} phy_args_t; + +class phch_common +{ +public: + + + phch_common(uint32_t max_mutex_) : tx_mutex(max_mutex_) { + max_mutex = max_mutex_; + params.max_prach_offset_us = 20; + } + + bool init(srslte_cell_t *cell, srslte::radio *radio_handler, mac_interface_phy *mac); + void reset(); + void stop(); + + void set_nof_mutex(uint32_t nof_mutex); + + void worker_end(uint32_t tx_mutex_cnt, cf_t *buffer, uint32_t nof_samples, srslte_timestamp_t tx_time); + + // Common objects + srslte_cell_t cell; + srslte_refsignal_dmrs_pusch_cfg_t pusch_cfg; + srslte_pusch_hopping_cfg_t hopping_cfg; + srslte_pucch_cfg_t pucch_cfg; + phy_args_t params; + + srslte::radio *radio; + mac_interface_phy *mac; + + // Common objects for schedulign grants + mac_interface_phy::ul_sched_t ul_grants[10]; + mac_interface_phy::dl_sched_t dl_grants[10]; + + // Map of pending ACKs for each user + typedef struct { + bool is_pending[10]; + uint16_t n_pdcch[10]; + } pending_ack_t; + std::map pending_ack; + + void ack_add_rnti(uint16_t rnti); + void ack_rem_rnti(uint16_t rnti); + void ack_clear(uint32_t sf_idx); + void ack_set_pending(uint32_t sf_idx, uint16_t rnti, uint32_t n_pdcch); + bool ack_is_pending(uint32_t sf_idx, uint16_t rnti, uint32_t *last_n_pdcch = NULL); + +private: + std::vector tx_mutex; + bool is_first_tx; + bool is_first_of_burst; + + uint32_t nof_workers; + uint32_t nof_mutex; + uint32_t max_mutex; + +}; + +} // namespace srsenb + +#endif // UEPHY_H diff --git a/srsenb/hdr/phy/phch_worker.h b/srsenb/hdr/phy/phch_worker.h new file mode 100644 index 000000000..25ad3c359 --- /dev/null +++ b/srsenb/hdr/phy/phch_worker.h @@ -0,0 +1,123 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General 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 ENBPHYWORKER_H +#define ENBPHYWORKER_H + +#include + +#include "srslte/srslte.h" +#include "phy/phch_common.h" + +#define LOG_EXECTIME + +namespace srsenb { + +class phch_worker : public srslte::thread_pool::worker +{ +public: + + phch_worker(); + void init(phch_common *phy, srslte::log *log_h); + void reset(); + + cf_t *get_buffer_rx(); + void set_time(uint32_t tti, uint32_t tx_mutex_cnt, srslte_timestamp_t tx_time); + + int add_rnti(uint16_t rnti); + void rem_rnti(uint16_t rnti); + uint32_t get_nof_rnti(); + + /* These are used by the GUI plotting tools */ + int read_ce_abs(float *ce_abs); + int read_pusch_d(cf_t *pusch_d); + void start_plot(); + + + void set_config_dedicated(uint16_t rnti, + srslte_uci_cfg_t *uci_cfg, + srslte_pucch_sched_t *pucch_sched, + srslte_refsignal_srs_cfg_t *srs_cfg, + uint32_t I_sr, bool pucch_cqi, uint32_t pmi_idx, bool pucch_cqi_ack); + + uint32_t get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); + +private: + + const static float PUSCH_RL_SNR_DB_TH = 1.0; + const static float PUCCH_RL_CORR_TH = 0.1; + + void work_imp(); + + int encode_pdsch(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants, uint32_t sf_idx); + int decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch, uint32_t tti_rx); + int encode_phich(srslte_enb_dl_phich_t *acks, uint32_t nof_acks, uint32_t sf_idx); + int encode_pdcch_dl(srslte_enb_dl_pdsch_t *grants, uint32_t nof_grants, uint32_t sf_idx); + int encode_pdcch_ul(srslte_enb_ul_pusch_t *grants, uint32_t nof_grants, uint32_t sf_idx); + int decode_pucch(uint32_t tti_rx); + + + /* Common objects */ + srslte::log *log_h; + phch_common *phy; + bool initiated; + cf_t *signal_buffer_rx; + cf_t *signal_buffer_tx; + uint32_t tti_rx, tti_tx, tti_sched_ul, sf_rx, sf_tx, sf_sched_ul, tx_mutex_cnt; + + srslte_enb_dl_t enb_dl; + srslte_enb_ul_t enb_ul; + + srslte_timestamp_t tx_time; + + // Class to store user information + class ue { + public: + ue() : I_sr(0), I_sr_en(false), cqi_en(false), pucch_cqi_ack(false), pmi_idx(0), has_grant_tti(0) {bzero(&metrics, sizeof(phy_metrics_t));} + uint32_t I_sr; + uint32_t pmi_idx; + bool I_sr_en; + bool cqi_en; + bool pucch_cqi_ack; + int has_grant_tti; + uint32_t rnti; + srslte_enb_ul_phich_info_t phich_info; + void metrics_read(phy_metrics_t *metrics); + void metrics_dl(uint32_t mcs); + void metrics_ul(uint32_t mcs, float rssi, float sinr, uint32_t turbo_iters); + private: + phy_metrics_t metrics; + }; + std::map ue_db; + + // mutex to protect worker_imp() from configuration interface + pthread_mutex_t mutex; +}; + +} // namespace srsenb + +#endif // ENBPHYWORKER_H + diff --git a/srsenb/hdr/phy/phy.h b/srsenb/hdr/phy/phy.h new file mode 100644 index 000000000..dc623a872 --- /dev/null +++ b/srsenb/hdr/phy/phy.h @@ -0,0 +1,75 @@ + +#ifndef ENBPHY_H +#define ENBPHY_H + +#include "srslte/common/log.h" +#include "phy/txrx.h" +#include "phy/phch_worker.h" +#include "phy/phch_common.h" +#include "srslte/radio/radio.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/common/task_dispatcher.h" +#include "srslte/common/trace.h" +#include "srslte/interfaces/enb_metrics_interface.h" + +namespace srsenb { + +typedef struct { + srslte_cell_t cell; + LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT prach_cnfg; + LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT pdsch_cnfg; + LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT pusch_cnfg; + LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT pucch_cnfg; + LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT srs_ul_cnfg; +} phy_cfg_t; + +class phy : public phy_interface_mac, + public phy_interface_rrc +{ +public: + + phy(); + bool init(phy_args_t *args, phy_cfg_t *common_cfg, srslte::radio *radio_handler, mac_interface_phy *mac, srslte::log* log_h); + bool init(phy_args_t *args, phy_cfg_t *common_cfg, srslte::radio *radio_handler, mac_interface_phy *mac, std::vector log_vec); + void stop(); + + /* MAC->PHY interface */ + int add_rnti(uint16_t rnti); + void rem_rnti(uint16_t rnti); + + static uint32_t tti_to_SFN(uint32_t tti); + static uint32_t tti_to_subf(uint32_t tti); + + void start_plot(); + void set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated); + + void get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]); + +private: + + uint32_t nof_workers; + + const static int MAX_WORKERS = 4; + const static int DEFAULT_WORKERS = 2; + + const static int PRACH_WORKER_THREAD_PRIO = 80; + const static int SF_RECV_THREAD_PRIO = 1; + const static int WORKERS_THREAD_PRIO = 0; + + srslte::radio *radio_handler; + + srslte::thread_pool workers_pool; + std::vector workers; + phch_common workers_common; + prach_worker prach; + txrx tx_rx; + + srslte_prach_cfg_t prach_cfg; + + void parse_config(phy_cfg_t* cfg); + +}; + +} // namespace srsenb + +#endif // UEPHY_H diff --git a/srsenb/hdr/phy/phy_metrics.h b/srsenb/hdr/phy/phy_metrics.h new file mode 100644 index 000000000..e97ee4d81 --- /dev/null +++ b/srsenb/hdr/phy/phy_metrics.h @@ -0,0 +1,34 @@ + +#ifndef ENB_PHY_METRICS_H +#define ENB_PHY_METRICS_H + + +namespace srsenb { + +// PHY metrics per user + +struct ul_metrics_t +{ + float n; + float sinr; + float rssi; + float turbo_iters; + float mcs; + int n_samples; +}; + +struct dl_metrics_t +{ + float mcs; + int n_samples; +}; + +struct phy_metrics_t +{ + dl_metrics_t dl; + ul_metrics_t ul; +}; + +} // namespace srsenb + +#endif // ENB_PHY_METRICS_H diff --git a/srsenb/hdr/phy/prach_worker.h b/srsenb/hdr/phy/prach_worker.h new file mode 100644 index 000000000..ba3b21502 --- /dev/null +++ b/srsenb/hdr/phy/prach_worker.h @@ -0,0 +1,49 @@ +#ifndef PRACH_WORKER_H +#define PRACH_WORKER_H + +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/common/log.h" +#include "srslte/common/threads.h" + +namespace srsenb { + +class prach_worker : thread +{ +public: + prach_worker() : initiated(false),max_prach_offset_us(0) {} + + int init(srslte_cell_t *cell, srslte_prach_cfg_t *prach_cfg, mac_interface_phy *mac, srslte::log *log_h, int priority); + int new_tti(uint32_t tti, cf_t *buffer); + void set_max_prach_offset_us(float delay_us); + void stop(); + +private: + void run_thread(); + int run_tti(uint32_t tti); + + uint32_t prach_nof_det; + uint32_t prach_indices[165]; + float prach_offsets[165]; + float prach_p2avg[165]; + + srslte_cell_t cell; + srslte_prach_cfg_t prach_cfg; + srslte_prach_t prach; + + pthread_mutex_t mutex; + pthread_cond_t cvar; + + cf_t *signal_buffer_rx; + + srslte::log* log_h; + mac_interface_phy *mac; + float max_prach_offset_us; + bool initiated; + uint32_t pending_tti; + int processed_tti; + bool running; + uint32_t nof_sf; + uint32_t sf_cnt; +}; +} +#endif // PRACH_WORKER_H diff --git a/srsenb/hdr/phy/txrx.h b/srsenb/hdr/phy/txrx.h new file mode 100644 index 000000000..8fe0131ca --- /dev/null +++ b/srsenb/hdr/phy/txrx.h @@ -0,0 +1,52 @@ + + +#ifndef ENBTXRX_H +#define ENBTXRX_H + +#include "srslte/common/log.h" +#include "srslte/common/threads.h" +#include "srslte/common/thread_pool.h" +#include "srslte/radio/radio.h" +#include "phy/phch_common.h" +#include "phy/prach_worker.h" + +namespace srsenb { + +typedef _Complex float cf_t; + +class txrx : public thread +{ +public: + txrx(); + bool init(srslte::radio *radio_handler, + srslte::thread_pool *_workers_pool, + phch_common *worker_com, + prach_worker *prach, + srslte::log *log_h, + uint32_t prio); + void stop(); + + const static int MUTEX_X_WORKER = 4; + +private: + + void run_thread(); + + srslte::radio *radio_h; + srslte::log *log_h; + srslte::thread_pool *workers_pool; + prach_worker *prach; + phch_common *worker_com; + + uint32_t tx_mutex_cnt; + uint32_t nof_tx_mutex; + + // Main system TTI counter + uint32_t tti; + + bool running; +}; + +} // namespace srsenb + +#endif // UEPHY_H diff --git a/srsenb/hdr/upper/common_enb.h b/srsenb/hdr/upper/common_enb.h new file mode 100644 index 000000000..47d868acc --- /dev/null +++ b/srsenb/hdr/upper/common_enb.h @@ -0,0 +1,140 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2015 Software Radio Systems Limited + * + */ + +#ifndef COMMON_ENB_H +#define COMMON_ENB_H + +/******************************************************************************* + INCLUDES +*******************************************************************************/ + +#include + +namespace srsenb { + +#define ENB_METRICS_MAX_USERS 64 + +#define SRSENB_RRC_MAX_N_PLMN_IDENTITIES 6 + +#define SRSENB_N_SRB 3 +#define SRSENB_N_DRB 8 +#define SRSENB_N_RADIO_BEARERS 11 + +// Cat 3 UE - Max number of DL-SCH transport block bits received within a TTI +// 3GPP 36.306 Table 4.1.1 +#define SRSENB_MAX_BUFFER_SIZE_BITS 102048 +#define SRSENB_MAX_BUFFER_SIZE_BYTES 12756 +#define SRSENB_BUFFER_HEADER_OFFSET 1024 + +/****************************************************************************** + * Convert PLMN to BCD-coded MCC and MNC. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 represented as 0xF001 + * MNC 01 represented as 0xFF01 + * PLMN encoded as per TS 36.413 sec 9.2.3.8 + *****************************************************************************/ +inline void s1ap_plmn_to_mccmnc(uint32_t plmn, uint16_t *mcc, uint16_t *mnc) +{ + uint8_t nibbles[6]; + nibbles[0] = (plmn & 0xF00000) >> 20; + nibbles[1] = (plmn & 0x0F0000) >> 16; + nibbles[2] = (plmn & 0x00F000) >> 12; + nibbles[3] = (plmn & 0x000F00) >> 8; + nibbles[4] = (plmn & 0x0000F0) >> 4; + nibbles[5] = (plmn & 0x00000F); + + *mcc = 0xF000; + *mnc = 0xF000; + *mcc |= nibbles[1] << 8; // MCC digit 1 + *mcc |= nibbles[0] << 4; // MCC digit 2 + *mcc |= nibbles[3]; // MCC digit 3 + + if(nibbles[2] == 0xF) { + // 2-digit MNC + *mnc |= 0x0F00; // MNC digit 1 + *mnc |= nibbles[5] << 4; // MNC digit 2 + *mnc |= nibbles[4]; // MNC digit 3 + } else { + // 3-digit MNC + *mnc |= nibbles[5] << 8; // MNC digit 1 + *mnc |= nibbles[4] << 4; // MNC digit 2 + *mnc |= nibbles[2] ; // MNC digit 3 + } +} + +/****************************************************************************** + * Convert BCD-coded MCC and MNC to PLMN. + * Digits are represented by 4-bit nibbles. Unused nibbles are filled with 0xF. + * MNC 001 represented as 0xF001 + * MNC 01 represented as 0xFF01 + * PLMN encoded as per TS 36.413 sec 9.2.3.8 + *****************************************************************************/ +inline void s1ap_mccmnc_to_plmn(uint16_t mcc, uint16_t mnc, uint32_t *plmn) +{ + uint8_t nibbles[6]; + nibbles[1] = (mcc & 0x0F00) >> 8; // MCC digit 1 + nibbles[0] = (mcc & 0x00F0) >> 4; // MCC digit 2 + nibbles[3] = (mcc & 0x000F); // MCC digit 3 + + if((mnc & 0xFF00) == 0xFF00) { + // 2-digit MNC + nibbles[2] = 0x0F; // MNC digit 1 + nibbles[5] = (mnc & 0x00F0) >> 4; // MNC digit 2 + nibbles[4] = (mnc & 0x000F); // MNC digit 3 + } else { + // 3-digit MNC + nibbles[5] = (mnc & 0x0F00) >> 8; // MNC digit 1 + nibbles[4] = (mnc & 0x00F0) >> 4; // MNC digit 2 + nibbles[2] = (mnc & 0x000F); // MNC digit 3 + } + + *plmn = 0x000000; + *plmn |= nibbles[0] << 20; + *plmn |= nibbles[1] << 16; + *plmn |= nibbles[2] << 12; + *plmn |= nibbles[3] << 8; + *plmn |= nibbles[4] << 4; + *plmn |= nibbles[5]; +} + +/****************************************************************************** + * Safe conversions between byte buffers and integer types. + * Note: these don't perform endian conversion - use e.g. htonl/ntohl if required + *****************************************************************************/ + +inline void uint8_to_uint32(uint8_t *buf, uint32_t *i) +{ + *i = (uint32_t)buf[0] << 24 | + (uint32_t)buf[1] << 16 | + (uint32_t)buf[2] << 8 | + (uint32_t)buf[3]; +} + +inline void uint32_to_uint8(uint32_t i, uint8_t *buf) +{ + buf[0] = (i >> 24) & 0xFF; + buf[1] = (i >> 16) & 0xFF; + buf[2] = (i >> 8) & 0xFF; + buf[3] = i & 0xFF; +} + +inline void uint8_to_uint16(uint8_t *buf, uint16_t *i) +{ + *i = (uint32_t)buf[0] << 8 | + (uint32_t)buf[1]; +} + +inline void uint16_to_uint8(uint16_t i, uint8_t *buf) +{ + buf[0] = (i >> 8) & 0xFF; + buf[1] = i & 0xFF; +} + +} // namespace srsenb + +#endif // COMMON_ENB_H diff --git a/srsenb/hdr/upper/gtpu.h b/srsenb/hdr/upper/gtpu.h new file mode 100644 index 000000000..e0fdedb8f --- /dev/null +++ b/srsenb/hdr/upper/gtpu.h @@ -0,0 +1,103 @@ + +#include +#include + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "upper/common_enb.h" +#include "srslte/common/threads.h" +#include "srslte/srslte.h" +#include "srslte/interfaces/enb_interfaces.h" + +#ifndef GTPU_H +#define GTPU_H + +namespace srsenb { + +/**************************************************************************** + * GTPU Header + * Ref: 3GPP TS 29.281 v10.1.0 Section 5 + * + * | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | + * + * 1 | Version |PT | * | E | S |PN | + * 2 | Message Type | + * 3 | Length (1st Octet) | + * 4 | Length (2nd Octet) | + * 5 | TEID (1st Octet) | + * 6 | TEID (2nd Octet) | + * 7 | TEID (3rd Octet) | + * 8 | TEID (4th Octet) | + ***************************************************************************/ + +#define GTPU_HEADER_LEN 8 + +typedef struct{ + uint8_t flags; // Only support 0x30 - v1, PT1 (GTP), no other flags + uint8_t message_type; // Only support 0xFF - T-PDU type + uint16_t length; + uint32_t teid; +}gtpu_header_t; + +class gtpu + :public gtpu_interface_rrc + ,public gtpu_interface_pdcp + ,public thread +{ +public: + + bool init(std::string gtp_bind_addr_, std::string mme_addr_, pdcp_interface_gtpu *pdcp_, srslte::log *gtpu_log_); + void stop(); + + // gtpu_interface_rrc + void add_bearer(uint16_t rnti, uint32_t lcid, uint32_t teid_out, uint32_t *teid_in); + void rem_bearer(uint16_t rnti, uint32_t lcid); + void rem_user(uint16_t rnti); + + // gtpu_interface_pdcp + void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu); + + +private: + static const int THREAD_PRIO = 7; + static const int GTPU_PORT = 2152; + srslte::byte_buffer_pool *pool; + bool running; + bool run_enable; + + std::string gtp_bind_addr; + std::string mme_addr; + srsenb::pdcp_interface_gtpu *pdcp; + srslte::log *gtpu_log; + + typedef struct{ + uint32_t teids_in[SRSENB_N_RADIO_BEARERS]; + uint32_t teids_out[SRSENB_N_RADIO_BEARERS]; + }bearer_map; + std::map rnti_bearers; + + srslte_netsink_t snk; + srslte_netsource_t src; + + void run_thread(); + + pthread_mutex_t mutex; + + /**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 29.281 v10.1.0 Section 5 + ***************************************************************************/ + bool gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu); + bool gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header); + + /**************************************************************************** + * TEID to RNIT/LCID helper functions + ***************************************************************************/ + void teidin_to_rntilcid(uint32_t teidin, uint16_t *rnti, uint16_t *lcid); + void rntilcid_to_teidin(uint16_t rnti, uint16_t lcid, uint32_t *teidin); +}; + + +} // namespace srsenb + +#endif // GTPU_H diff --git a/srsenb/hdr/upper/pdcp.h b/srsenb/hdr/upper/pdcp.h new file mode 100644 index 000000000..2f0ec6ab8 --- /dev/null +++ b/srsenb/hdr/upper/pdcp.h @@ -0,0 +1,89 @@ + +#include +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/upper/pdcp.h" + +#ifndef PDCP_ENB_H +#define PDCP_ENB_H + +namespace srsenb { + +class pdcp : public pdcp_interface_rlc, + public pdcp_interface_gtpu, + public pdcp_interface_rrc +{ +public: + + void init(rlc_interface_pdcp *rlc_, rrc_interface_pdcp *rrc_, gtpu_interface_pdcp *gtpu_, srslte::log *pdcp_log_); + void stop(); + + // pdcp_interface_rlc + void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); + + // pdcp_interface_rrc + void reset(uint16_t rnti); + void add_user(uint16_t rnti); + void rem_user(uint16_t rnti); + void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); + void add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT *cnfg=NULL); + void config_security(uint16_t rnti, + uint32_t lcid, + uint8_t *k_rrc_enc_, + uint8_t *k_rrc_int_, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_); + +private: + + class user_interface_rlc : public srsue::rlc_interface_pdcp + { + public: + uint16_t rnti; + srsenb::rlc_interface_pdcp *rlc; + // rlc_interface_pdcp + void write_sdu(uint32_t lcid, srslte::byte_buffer_t *sdu); + }; + + class user_interface_gtpu : public srsue::gw_interface_pdcp + { + public: + uint16_t rnti; + srsenb::gtpu_interface_pdcp *gtpu; + // gw_interface_pdcp + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); + }; + + class user_interface_rrc : public srsue::rrc_interface_pdcp + { + public: + uint16_t rnti; + srsenb::rrc_interface_pdcp *rrc; + // rrc_interface_pdcp + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu); + void write_pdu_bcch_bch(srslte::byte_buffer_t *pdu); + void write_pdu_bcch_dlsch(srslte::byte_buffer_t *pdu); + void write_pdu_pcch(srslte::byte_buffer_t *pdu); + }; + + class user_interface + { + public: + user_interface_rlc rlc_itf; + user_interface_gtpu gtpu_itf; + user_interface_rrc rrc_itf; + srslte::pdcp *pdcp; + }; + + std::map users; + + rlc_interface_pdcp *rlc; + rrc_interface_pdcp *rrc; + gtpu_interface_pdcp *gtpu; + srslte::log *log_h; + srslte::byte_buffer_pool *pool; +}; + +} + +#endif // PDCP_ENB_H diff --git a/srsenb/hdr/upper/rlc.h b/srsenb/hdr/upper/rlc.h new file mode 100644 index 000000000..1e7f8d783 --- /dev/null +++ b/srsenb/hdr/upper/rlc.h @@ -0,0 +1,71 @@ + +#include +#include "srslte/interfaces/ue_interfaces.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/upper/rlc.h" + +#ifndef RLC_ENB_H +#define RLC_ENB_H + +namespace srsenb { + +class rlc : public rlc_interface_mac, + public rlc_interface_rrc, + public rlc_interface_pdcp +{ +public: + + void init(pdcp_interface_rlc *pdcp_, rrc_interface_rlc *rrc_, mac_interface_rlc *mac_, + srslte::mac_interface_timers *mac_timers_, srslte::log *log_h); + void stop(); + + // rlc_interface_rrc + void reset(uint16_t rnti); + 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); + void add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT *cnfg); + + // rlc_interface_pdcp + void write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *sdu); + + // rlc_interface_mac + 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 srsue::ue_interface + { + public: + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu); + void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu); + void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu); + void write_pdu_pcch(srslte::byte_buffer_t *sdu); + void max_retx_attempted(); + uint16_t rnti; + + srsenb::pdcp_interface_rlc *pdcp; + srsenb::rrc_interface_rlc *rrc; + srslte::rlc *rlc; + srsenb::rlc *parent; + }; + + std::map users; + + mac_interface_rlc *mac; + pdcp_interface_rlc *pdcp; + rrc_interface_rlc *rrc; + srslte::log *log_h; + srslte::byte_buffer_pool *pool; + srslte::mac_interface_timers *mac_timers; +}; + +} + +#endif // RLC_H diff --git a/srsenb/hdr/upper/rrc.h b/srsenb/hdr/upper/rrc.h new file mode 100644 index 000000000..c6ff10560 --- /dev/null +++ b/srsenb/hdr/upper/rrc.h @@ -0,0 +1,315 @@ +#ifndef RRC_H +#define RRC_H + +#include +#include +#include "srslte/common/buffer_pool.h" +#include "srslte/common/common.h" +#include "srslte/common/block_queue.h" +#include "srslte/common/threads.h" +#include "srslte/common/timeout.h" +#include "srslte/common/log.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "upper/common_enb.h" +#include "rrc_metrics.h" + +namespace srsenb { + +typedef struct { + uint32_t period; + LIBLTE_RRC_DSR_TRANS_MAX_ENUM dsr_max; + uint32_t nof_prb; + uint32_t sf_mapping[80]; + uint32_t nof_subframes; +} rrc_cfg_sr_t; + +typedef enum { + RRC_CFG_CQI_MODE_PERIODIC = 0, + RRC_CFG_CQI_MODE_APERIODIC, + RRC_CFG_CQI_MODE_N_ITEMS +} rrc_cfg_cqi_mode_t; + +static const char rrc_cfg_cqi_mode_text[RRC_CFG_CQI_MODE_N_ITEMS][20] = {"periodic", "aperiodic"}; + +typedef struct { + uint32_t sf_mapping[80]; + uint32_t nof_subframes; + uint32_t nof_prb; + uint32_t period; + bool simultaneousAckCQI; + rrc_cfg_cqi_mode_t mode; +} rrc_cfg_cqi_t; + +typedef struct { + bool configured; + LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT lc_cfg; + LIBLTE_RRC_PDCP_CONFIG_STRUCT pdcp_cfg; + LIBLTE_RRC_RLC_CONFIG_STRUCT rlc_cfg; +} rrc_cfg_qci_t; + +#define MAX_NOF_QCI 10 + +typedef struct { + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT sibs[LIBLTE_RRC_MAX_SIB]; + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT mac_cnfg; + LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT pusch_cfg; + rrc_cfg_sr_t sr_cfg; + rrc_cfg_cqi_t cqi_cfg; + rrc_cfg_qci_t qci_cfg[MAX_NOF_QCI]; + srslte_cell_t cell; + uint32_t inactivity_timeout_ms; +}rrc_cfg_t; + +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 rrc : public rrc_interface_pdcp, + public rrc_interface_mac, + public rrc_interface_rlc, + public rrc_interface_s1ap, + public thread +{ +public: + + rrc() : act_monitor(this), cnotifier(NULL) {} + + void init(rrc_cfg_t *cfg, + phy_interface_rrc *phy, + mac_interface_rrc *mac, + rlc_interface_rrc *rlc, + pdcp_interface_rrc *pdcp, + s1ap_interface_rrc *s1ap, + gtpu_interface_rrc *gtpu, + srslte::log *log_rrc); + + void stop(); + void get_metrics(rrc_metrics_t &m); + + // rrc_interface_mac + void rl_failure(uint16_t rnti); + void add_user(uint16_t rnti); + void upd_user(uint16_t new_rnti, uint16_t old_rnti); + void set_activity_user(uint16_t rnti); + bool is_paging_opportunity(uint32_t tti, uint32_t *payload_len); + + // rrc_interface_rlc + void read_pdu_bcch_dlsch(uint32_t sib_idx, uint8_t *payload); + void read_pdu_pcch(uint8_t *payload, uint32_t buffer_size); + void max_retx_attempted(uint16_t rnti); + + // rrc_interface_s1ap + void write_dl_info(uint16_t rnti, srslte::byte_buffer_t *sdu); + void release_complete(uint16_t rnti); + bool setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg); + bool setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg); + bool release_erabs(uint32_t rnti); + void add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID); + + // rrc_interface_pdcp + void write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu); + + void parse_sibs(); + uint32_t get_nof_users(); + + // Notifier for user connect + class connect_notifier { + public: + virtual void user_connected(uint16_t rnti) = 0; + }; + void set_connect_notifer(connect_notifier *cnotifier); + + class activity_monitor : public thread + { + public: + activity_monitor(rrc* parent_); + void stop(); + private: + rrc* parent; + bool running; + void run_thread(); + }; + + class ue + { + public: + ue(); + bool is_connected(); + bool is_idle(); + bool is_timeout(); + void set_activity(); + + rrc_state_t get_state(); + + void send_connection_setup(bool is_setup = true); + void send_connection_reest(); + void send_connection_release(); + void send_connection_reest_rej(); + void send_connection_reconf(srslte::byte_buffer_t *sdu); + void send_connection_reconf_new_bearer(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e); + void send_connection_reconf_upd(srslte::byte_buffer_t *pdu); + void send_security_mode_command(); + void send_ue_cap_enquiry(); + void parse_ul_dcch(uint32_t lcid, srslte::byte_buffer_t* pdu); + + void handle_rrc_con_req(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *msg); + void handle_rrc_con_reest_req(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *msg); + void handle_rrc_con_setup_complete(LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *msg, srslte::byte_buffer_t *pdu); + void handle_security_mode_complete(LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *msg); + void handle_security_mode_failure(LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *msg); + void handle_ue_cap_info(LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *msg); + + void set_bitrates(LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *rates); + void set_security_capabilities(LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *caps); + void set_security_key(uint8_t* key, uint32_t length); + + bool setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *e); + bool setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e); + bool release_erabs(); + + void notify_s1ap_ue_ctxt_setup_complete(); + void notify_s1ap_ue_erab_setup_response(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e); + + int sr_allocate(uint32_t period, uint32_t *I_sr, uint32_t *N_pucch_sr); + void sr_get(uint32_t *I_sr, uint32_t *N_pucch_sr); + int sr_free(); + + int cqi_allocate(uint32_t period, uint32_t *pmi_idx, uint32_t *n_pucch); + void cqi_get(uint32_t *pmi_idx, uint32_t *n_pucch); + int cqi_free(); + + void send_dl_ccch(LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg); + void send_dl_dcch(LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg, srslte::byte_buffer_t *pdu = NULL); + + uint16_t rnti; + rrc *parent; + + bool connect_notified; + + private: + + struct timeval t_last_activity; + + // S-TMSI for this UE + bool has_tmsi; + uint32_t m_tmsi; + uint8_t mmec; + + uint8_t transaction_id; + rrc_state_t state; + + std::map srbs; + std::map drbs; + + uint8_t k_enb[32]; // Provided by MME + uint8_t k_rrc_enc[32]; + uint8_t k_rrc_int[32]; + uint8_t k_up_enc[32]; + uint8_t k_up_int[32]; // Not used: only for relay nodes (3GPP 33.401 Annex A.7) + + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + + LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT bitrates; + LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT security_capabilities; + LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT eutra_capabilities; + + typedef struct { + uint8_t id; + LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT qos_params; + LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT address; + uint32_t teid_out; + uint32_t teid_in; + }erab_t; + std::map erabs; + int sr_sched_sf_idx; + int sr_sched_prb_idx; + bool sr_allocated; + uint32_t sr_N_pucch; + uint32_t sr_I; + uint32_t cqi_pucch; + uint32_t cqi_idx; + bool cqi_allocated; + int cqi_sched_sf_idx; + bool cqi_sched_prb_idx; + int get_drbid_config(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb, int drbid); + }; + + +private: + + std::map users; + + std::map pending_paging; + + activity_monitor act_monitor; + + LIBLTE_BYTE_MSG_STRUCT sib_buffer[LIBLTE_RRC_MAX_SIB]; + + // user connect notifier + connect_notifier *cnotifier; + + void rem_user(uint16_t rnti); + uint32_t generate_sibs(); + void config_mac(); + void parse_ul_dcch(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t *pdu); + void parse_ul_ccch(uint16_t rnti, srslte::byte_buffer_t *pdu); + void configure_security(uint16_t rnti, + uint32_t lcid, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo); + + srslte::byte_buffer_pool *pool; + srslte::bit_buffer_t bit_buf; + srslte::bit_buffer_t bit_buf_paging; + srslte::byte_buffer_t erab_info; + + phy_interface_rrc *phy; + mac_interface_rrc *mac; + rlc_interface_rrc *rlc; + pdcp_interface_rrc *pdcp; + gtpu_interface_rrc *gtpu; + s1ap_interface_rrc *s1ap; + srslte::log *rrc_log; + + typedef struct{ + uint16_t rnti; + uint32_t lcid; + srslte::byte_buffer_t* pdu; + }rrc_pdu; + + const static uint32_t LCID_REM_USER = 0xffff0001; + + bool running; + static const int RRC_THREAD_PRIO = 7; + srslte::block_queue rx_pdu_queue; + + typedef struct { + uint32_t nof_users[100][80]; + } sr_sched_t; + + sr_sched_t sr_sched; + sr_sched_t cqi_sched; + + rrc_cfg_t cfg; + uint32_t nof_si_messages; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT sib2; + + void run_thread(); + void rem_user_thread(uint16_t rnti); + pthread_mutex_t user_mutex; + + pthread_mutex_t paging_mutex; +}; + +} // namespace srsenb + +#endif // RRC_H diff --git a/srsenb/hdr/upper/rrc_metrics.h b/srsenb/hdr/upper/rrc_metrics.h new file mode 100644 index 000000000..7fa24fe55 --- /dev/null +++ b/srsenb/hdr/upper/rrc_metrics.h @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2016 Software Radio Systems Limited + * + */ + +#ifndef ENB_RRC_METRICS_H +#define ENB_RRC_METRICS_H + +#include "upper/common_enb.h" + +namespace srsenb { + +typedef enum{ + RRC_STATE_IDLE = 0, + RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE, + RRC_STATE_WAIT_FOR_SECURITY_MODE_COMPLETE, + RRC_STATE_WAIT_FOR_UE_CAP_INFO, + RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE, + RRC_STATE_REGISTERED, + RRC_STATE_RELEASE_REQUEST, + RRC_STATE_N_ITEMS, +}rrc_state_t; + +struct rrc_ue_metrics_t +{ + rrc_state_t state; +}; + +struct rrc_metrics_t +{ + uint16_t n_ues; + rrc_ue_metrics_t ues[ENB_METRICS_MAX_USERS]; +}; + +} // namespace srsenb + +#endif // ENB_S1AP_METRICS_H diff --git a/srsenb/hdr/upper/s1ap.h b/srsenb/hdr/upper/s1ap.h new file mode 100644 index 000000000..6f1bdf0e0 --- /dev/null +++ b/srsenb/hdr/upper/s1ap.h @@ -0,0 +1,133 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2016 Software Radio Systems Limited + * + */ + +#ifndef S1AP_H +#define S1AP_H + +#include + +#include "srslte/common/buffer_pool.h" +#include "srslte/common/log.h" +#include "srslte/common/common.h" +#include "srslte/common/msg_queue.h" +#include "srslte/common/threads.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "upper/common_enb.h" + +#include "srslte/asn1/liblte_s1ap.h" +#include "s1ap_metrics.h" + +namespace srsenb { + +typedef struct { + uint32_t enb_id; // 20-bit id (lsb bits) + uint8_t cell_id; // 8-bit cell id + uint16_t tac; // 16-bit tac + uint16_t mcc; // BCD-coded with 0xF filler + uint16_t mnc; // BCD-coded with 0xF filler + std::string mme_addr; + std::string gtp_bind_addr; + std::string enb_name; +}s1ap_args_t; + +typedef struct { + uint32_t rnti; + uint32_t eNB_UE_S1AP_ID; + uint32_t MME_UE_S1AP_ID; + bool release_requested; + uint16_t stream_id; +}ue_ctxt_t; + +class s1ap + :public s1ap_interface_rrc + ,public thread +{ +public: + bool init(s1ap_args_t args_, rrc_interface_s1ap *rrc_, srslte::log *s1ap_log_); + void stop(); + void get_metrics(s1ap_metrics_t &m); + + void run_thread(); + + // RRC interface + void initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu); + void initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu, uint32_t m_tmsi, uint8_t mmec); + void write_pdu(uint16_t rnti, srslte::byte_buffer_t *pdu); + bool user_exists(uint16_t rnti); + void user_inactivity(uint16_t rnti); + bool user_link_lost(uint16_t rnti); + void release_eutran(uint16_t rnti); + void ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res); + void ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res); + //void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps); + +private: + static const int S1AP_THREAD_PRIO = 7; + static const int MME_PORT = 36412; + static const int ADDR_FAMILY = AF_INET; + static const int SOCK_TYPE = SOCK_STREAM; + static const int PROTO = IPPROTO_SCTP; + static const int PPID = 18; + static const int NONUE_STREAM_ID = 0; + + rrc_interface_s1ap *rrc; + s1ap_args_t args; + srslte::log *s1ap_log; + srslte::byte_buffer_pool *pool; + + bool mme_connected; + bool running; + int socket_fd; // SCTP socket file descriptor + struct sockaddr_in mme_addr; // MME address + uint32_t next_eNB_UE_S1AP_ID; // Next ENB-side UE identifier + uint16_t next_ue_stream_id; // Next UE SCTP stream identifier + + // Protocol IEs sent with every UL S1AP message + LIBLTE_S1AP_TAI_STRUCT tai; + LIBLTE_S1AP_EUTRAN_CGI_STRUCT eutran_cgi; + + LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT s1setupresponse; + + std::map ue_ctxt_map; + std::map enbid_to_rnti_map; + + void build_tai_cgi(); + bool connect_mme(); + bool setup_s1(); + + bool handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu); + bool handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg); + bool handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg); + bool handle_unsuccessfuloutcome(LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg); + bool handle_paging(LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg); + + bool handle_s1setupresponse(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *msg); + bool handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *msg); + bool handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg); + bool handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg); + bool handle_s1setupfailure(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg); + bool handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg); + + bool send_initialuemessage(uint16_t rnti, srslte::byte_buffer_t *pdu, bool has_tmsi, uint32_t m_tmsi=0, uint8_t mmec=0); + bool send_ulnastransport(uint16_t rnti, srslte::byte_buffer_t *pdu); + bool send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *cause); + bool send_uectxtreleasecomplete(uint16_t rnti, uint32_t mme_ue_id, uint32_t enb_ue_id); + bool send_initial_ctxt_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res_); + bool send_initial_ctxt_setup_failure(uint16_t rnti); + bool send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res_); + //bool send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) + + bool find_mme_ue_id(uint32_t mme_ue_id, uint16_t *rnti, uint32_t *enb_ue_id); + std::string get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c); + +}; + +} // namespace srsenb + + +#endif // S1AP_H diff --git a/srsenb/hdr/upper/s1ap_metrics.h b/srsenb/hdr/upper/s1ap_metrics.h new file mode 100644 index 000000000..42ef216c2 --- /dev/null +++ b/srsenb/hdr/upper/s1ap_metrics.h @@ -0,0 +1,28 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2016 Software Radio Systems Limited + * + */ + +#ifndef ENB_S1AP_METRICS_H +#define ENB_S1AP_METRICS_H + + +namespace srsenb { + +typedef enum{ + S1AP_ATTACHING = 0, // Attempting to create S1 connection + S1AP_READY, // S1 connected + S1AP_ERROR // Failure +}S1AP_STATUS_ENUM; + +struct s1ap_metrics_t +{ + S1AP_STATUS_ENUM status; +}; + +} // namespace srsenb + +#endif // ENB_S1AP_METRICS_H diff --git a/srsenb/rr.conf.example b/srsenb/rr.conf.example new file mode 100644 index 000000000..ca32a62da --- /dev/null +++ b/srsenb/rr.conf.example @@ -0,0 +1,50 @@ +mac_cnfg = +{ + phr_cnfg = + { + dl_pathloss_change = "3dB"; // Valid: 1, 3, 6 or INFINITY + periodic_phr_timer = 100; + prohibit_phr_timer = 0; + }; + ulsch_cnfg = + { + max_harq_tx = 5; + periodic_bsr_timer = 20; // in ms + retx_bsr_timer = 320; // in ms + }; + + time_alignment_timer = -1; // -1 is infinity +}; + +phy_cnfg = +{ + phich_cnfg = + { + duration = "Normal"; + resources = "1/6"; + }; + + pusch_cnfg_ded = + { + beta_offset_ack_idx = 5; + beta_offset_ri_idx = 12; + beta_offset_cqi_idx = 15; + }; + + // PUCCH-SR resources are scheduled on time-frequeny domain first, then multiplexed in the same resource. + sched_request_cnfg = + { + dsr_trans_max = 4; + period = 40; // in ms + subframe = [0]; // vector of subframe indices allowed for SR transmissions + nof_prb = 2; // number of PRBs on each extreme used for SR (total prb is twice this number) + }; + cqi_report_cnfg = + { + mode = "periodic"; // periodic uses pucch2, aperiodic is 3-0 + period = 40; // Both periodic and aperiodic in ms + subframe = [1]; // In periodic: vector of subframe indices allowed for SR transmissions + nof_prb = 2; // In periodic: number of PRBs on each extreme used for SR (total prb is twice this number) + simultaneousAckCQI = true; // In periodic: indicates if ACK and CQI shall be transmitted simultaneously (Format2A) + }; +}; diff --git a/srsenb/sib.conf.example b/srsenb/sib.conf.example new file mode 100644 index 000000000..ed6ccd2ca --- /dev/null +++ b/srsenb/sib.conf.example @@ -0,0 +1,122 @@ +sib1 = +{ + intra_freq_reselection = "Allowed"; + q_rx_lev_min = -130; + //p_max = 3; + cell_barred = "Not Barred" + si_window_length = 20; + sched_info = + ( + { + si_periodicity = 32; + si_mapping_info = []; // comma-separated array of SIB-indexes (from 3 to 13). + // Leave empty or commented to just scheduler sib2 + } + ); + system_info_value_tag = 0; +}; + +sib2 = +{ + rr_config_common_sib = + { + rach_cnfg = + { + num_ra_preambles = 4 + preamble_init_rx_target_pwr = -108; + pwr_ramping_step = 6; // in dB + preamble_trans_max = 7; + ra_resp_win_size = 8; // in ms + mac_con_res_timer = 64; // in ms + max_harq_msg3_tx = 1; + preambles_group_a_cnfg = + { + size_of_ra = 4; + msg_size = 56; + msg_pwr_offset_group_b = -1; + }; + }; + bcch_cnfg = + { + modification_period_coeff = 2; // in ms + }; + pcch_cnfg = + { + default_paging_cycle = 128; // in ms + nB = "1"; + }; + prach_cnfg = + { + root_sequence_index = 128; + prach_cnfg_info = + { + high_speed_flag = false; + prach_config_index = 53; + prach_freq_offset = 11; + zero_correlation_zone_config = 11; + }; + }; + pdsch_cnfg = + { + p_b = 0; + rs_power = -4; + }; + pusch_cnfg = + { + n_sb = 1; + hopping_mode = "inter-subframe"; + pusch_hopping_offset = 2; + enable_64_qam = false; + ul_rs = + { + cyclic_shift = 0; + group_assignment_pusch = 0; + group_hopping_enabled = false; + sequence_hopping_enabled = false; + }; + }; + pucch_cnfg = + { + delta_pucch_shift = 1; + n_rb_cqi = 1; + n_cs_an = 0; + n1_pucch_an = 2; + }; + ul_pwr_ctrl = + { + p0_nominal_pusch = -86; + alpha = 1.0; + p0_nominal_pucch = -108; + delta_flist_pucch = + { + format_1 = 0; + format_1b = 3; + format_2 = 0; + format_2a = 0; + format_2b = 0; + }; + delta_preamble_msg3 = 4; + }; + ul_cp_length = "Normal"; + }; + + ue_timers_and_constants = + { + t300 = 2000; // in ms + t301 = 100; // in ms + t310 = 1000; // in ms + n310 = 1; + t311 = 1000; // in ms + n311 = 1; + }; + + freqInfo = + { + ul_carrier_freq_present = false; + ul_bw_present = false; + additional_spectrum_emission = 1; + }; + + time_alignment_timer = "INFINITY"; // use "sf500", "sf750", etc. +}; + diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt new file mode 100644 index 000000000..fab9e0eef --- /dev/null +++ b/srsenb/src/CMakeLists.txt @@ -0,0 +1,45 @@ + +add_subdirectory(phy) +add_subdirectory(mac) +add_subdirectory(upper) + + +# Link libstdc++ and libgcc +if(STATIC_LIB) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -static-libgcc") +endif(STATIC_LIB) + + +if (RPATH) + SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +endif (RPATH) + + +add_executable(enb main.cc enb.cc parser.cc enb_cfg_parser.cc metrics_stdout.cc) +target_link_libraries(enb srsenb_upper + srsenb_mac + srsenb_phy + srslte_common + srslte_phy + srslte_upper + srslte_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${POLAR_LIBRARIES} + ${LIBCONFIGPP_LIBRARIES} + ${SCTP_LIBRARIES}) + +set_target_properties(enb PROPERTIES INSTALL_RPATH ".") + + + +######################################################################## +# 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 enb 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/srsenb/src/enb.cc b/srsenb/src/enb.cc new file mode 100644 index 000000000..f44c1ca43 --- /dev/null +++ b/srsenb/src/enb.cc @@ -0,0 +1,283 @@ + + +#include +#include +#include "enb.h" + +namespace srsenb { + +enb* enb::instance = NULL; +boost::mutex enb_instance_mutex; + + +enb* enb::get_instance(void) +{ + boost::mutex::scoped_lock lock(enb_instance_mutex); + if(NULL == instance) { + instance = new enb(); + } + return(instance); +} +void enb::cleanup(void) +{ + boost::mutex::scoped_lock lock(enb_instance_mutex); + if(NULL != instance) { + delete instance; + instance = NULL; + } +} + +enb::enb() + :started(false) +{ + pool = srslte::byte_buffer_pool::get_instance(); +} + +enb::~enb() +{ + srslte::byte_buffer_pool::cleanup(); +} + +bool enb::init(all_args_t *args_) +{ + args = args_; + + logger.init(args->log.filename); + rf_log.init("RF ", &logger); + + // Create array of pointers to phy_logs + for (int i=0;iexpert.phy.nof_phy_threads;i++) { + srslte::log_filter *mylog = new srslte::log_filter; + char tmp[16]; + sprintf(tmp, "PHY%d",i); + mylog->init(tmp, &logger, true); + phy_log.push_back((void*) mylog); + } + mac_log.init("MAC ", &logger, true); + rlc_log.init("RLC ", &logger); + pdcp_log.init("PDCP", &logger); + rrc_log.init("RRC ", &logger); + gtpu_log.init("GTPU", &logger); + s1ap_log.init("S1AP", &logger); + + // Init logs + logger.log("\n\n"); + rf_log.set_level(srslte::LOG_LEVEL_INFO); + for (int i=0;iexpert.phy.nof_phy_threads;i++) { + ((srslte::log_filter*) phy_log[i])->set_level(level(args->log.phy_level)); + } + mac_log.set_level(level(args->log.mac_level)); + rlc_log.set_level(level(args->log.rlc_level)); + pdcp_log.set_level(level(args->log.pdcp_level)); + rrc_log.set_level(level(args->log.rrc_level)); + gtpu_log.set_level(level(args->log.gtpu_level)); + s1ap_log.set_level(level(args->log.s1ap_level)); + + for (int i=0;iexpert.phy.nof_phy_threads;i++) { + ((srslte::log_filter*) phy_log[i])->set_hex_limit(args->log.phy_hex_limit); + } + mac_log.set_hex_limit(args->log.mac_hex_limit); + rlc_log.set_hex_limit(args->log.rlc_hex_limit); + pdcp_log.set_hex_limit(args->log.pdcp_hex_limit); + rrc_log.set_hex_limit(args->log.rrc_hex_limit); + gtpu_log.set_hex_limit(args->log.gtpu_hex_limit); + s1ap_log.set_hex_limit(args->log.s1ap_hex_limit); + + // Set up pcap and trace + if(args->pcap.enable) + { + mac_pcap.open(args->pcap.filename.c_str()); + mac.start_pcap(&mac_pcap); + } + + // Init layers + + /* Start Radio */ + char *dev_name = NULL; + if (args->rf.device_name.compare("auto")) { + dev_name = (char*) args->rf.device_name.c_str(); + } + + char *dev_args = NULL; + if (args->rf.device_args.compare("auto")) { + dev_args = (char*) args->rf.device_args.c_str(); + } + + if(!radio.init(dev_args, dev_name)) + { + printf("Failed to find device %s with args %s\n", + args->rf.device_name.c_str(), args->rf.device_args.c_str()); + return false; + } + + // Set RF options + if (args->rf.time_adv_nsamples.compare("auto")) { + radio.set_tx_adv(atoi(args->rf.time_adv_nsamples.c_str())); + } + if (args->rf.burst_preamble.compare("auto")) { + radio.set_burst_preamble(atof(args->rf.burst_preamble.c_str())); + } + + radio.set_manual_calibration(&args->rf_cal); + + radio.set_rx_gain(args->rf.rx_gain); + radio.set_tx_gain(args->rf.tx_gain); + + if (args->rf.dl_freq < 0) { + args->rf.dl_freq = 1e6*srslte_band_fd(args->rf.dl_earfcn); + if (args->rf.dl_freq < 0) { + fprintf(stderr, "Error getting DL frequency for EARFCN=%d\n", args->rf.dl_earfcn); + return false; + } + } + if (args->rf.ul_freq < 0) { + if (args->rf.ul_earfcn == 0) { + args->rf.ul_earfcn = srslte_band_ul_earfcn(args->rf.dl_earfcn); + } + args->rf.ul_freq = 1e6*srslte_band_fu(args->rf.ul_earfcn); + if (args->rf.ul_freq < 0) { + fprintf(stderr, "Error getting UL frequency for EARFCN=%d\n", args->rf.dl_earfcn); + return false; + } + } + ((srslte::log_filter*) phy_log[0])->console("Setting frequency: DL=%.1f Mhz, UL=%.1f MHz\n", args->rf.dl_freq/1e6, args->rf.ul_freq/1e6); + + radio.set_tx_freq(args->rf.dl_freq); + radio.set_rx_freq(args->rf.ul_freq); + + radio.register_error_handler(rf_msg); + + srslte_cell_t cell_cfg; + phy_cfg_t phy_cfg; + rrc_cfg_t rrc_cfg; + + if (parse_cell_cfg(args, &cell_cfg)) { + fprintf(stderr, "Error parsing Cell configuration\n"); + return false; + } + if (parse_sibs(args, &rrc_cfg, &phy_cfg)) { + fprintf(stderr, "Error parsing SIB configuration\n"); + return false; + } + if (parse_rr(args, &rrc_cfg)) { + fprintf(stderr, "Error parsing Radio Resources configuration\n"); + return false; + } + if (parse_drb(args, &rrc_cfg)) { + fprintf(stderr, "Error parsing DRB configuration\n"); + return false; + } + rrc_cfg.inactivity_timeout_ms = args->expert.rrc_inactivity_timer; + + // Copy cell struct to rrc and phy + memcpy(&rrc_cfg.cell, &cell_cfg, sizeof(srslte_cell_t)); + memcpy(&phy_cfg.cell, &cell_cfg, sizeof(srslte_cell_t)); + + // Init all layers + phy.init(&args->expert.phy, &phy_cfg, &radio, &mac, phy_log); + mac.init(&args->expert.mac, &cell_cfg, &phy, &rlc, &rrc, &mac_log); + rlc.init(&pdcp, &rrc, &mac, &mac, &rlc_log); + pdcp.init(&rlc, &rrc, >pu, &pdcp_log); + rrc.init(&rrc_cfg, &phy, &mac, &rlc, &pdcp, &s1ap, >pu, &rrc_log); + s1ap.init(args->enb.s1ap, &rrc, &s1ap_log); + gtpu.init(args->enb.s1ap.gtp_bind_addr, args->enb.s1ap.mme_addr, &pdcp, >pu_log); + + started = true; + return true; +} + +void enb::pregenerate_signals(bool enable) +{ + //phy.enable_pregen_signals(enable); +} + +void enb::stop() +{ + if(started) + { + mac.stop(); + phy.stop(); + usleep(1e5); + + rlc.stop(); + pdcp.stop(); + gtpu.stop(); + rrc.stop(); + + usleep(1e5); + if(args->pcap.enable) + { + mac_pcap.close(); + } + radio.stop(); + started = false; + } +} + +void enb::start_plot() { + phy.start_plot(); +} + +bool enb::get_metrics(enb_metrics_t &m) +{ + m.rf = rf_metrics; + bzero(&rf_metrics, sizeof(rf_metrics_t)); + rf_metrics.rf_error = false; // Reset error flag + + phy.get_metrics(m.phy); + mac.get_metrics(m.mac); + rrc.get_metrics(m.rrc); + s1ap.get_metrics(m.s1ap); + + m.running = started; + return true; +} + +void enb::rf_msg(srslte_rf_error_t error) +{ + enb *u = enb::get_instance(); + u->handle_rf_msg(error); +} + +void enb::handle_rf_msg(srslte_rf_error_t error) +{ + if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OVERFLOW) { + rf_metrics.rf_o++; + rf_metrics.rf_error = true; + rf_log.warning("Overflow\n"); + }else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_UNDERFLOW) { + rf_metrics.rf_u++; + rf_metrics.rf_error = true; + rf_log.warning("Underflow\n"); + } else if(error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_LATE) { + rf_metrics.rf_l++; + rf_metrics.rf_error = true; + rf_log.warning("Late\n"); + } else if (error.type == srslte_rf_error_t::SRSLTE_RF_ERROR_OTHER) { + std::string str(error.msg); + str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); + str.erase(std::remove(str.begin(), str.end(), '\r'), str.end()); + str.push_back('\n'); + rf_log.info(str); + } +} + +srslte::LOG_LEVEL_ENUM enb::level(std::string l) +{ + boost::to_upper(l); + if("NONE" == l){ + return srslte::LOG_LEVEL_NONE; + }else if("ERROR" == l){ + return srslte::LOG_LEVEL_ERROR; + }else if("WARNING" == l){ + return srslte::LOG_LEVEL_WARNING; + }else if("INFO" == l){ + return srslte::LOG_LEVEL_INFO; + }else if("DEBUG" == l){ + return srslte::LOG_LEVEL_DEBUG; + }else{ + return srslte::LOG_LEVEL_NONE; + } +} + +} // namespace srsenb diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc new file mode 100644 index 000000000..b7838e4b1 --- /dev/null +++ b/srsenb/src/enb_cfg_parser.cc @@ -0,0 +1,1100 @@ + +#include "srslte/asn1/liblte_common.h" +#include "srslte/asn1/liblte_rrc.h" +#include "cfg_parser.h" +#include "srslte/srslte.h" + +#include "parser.h" +#include "enb_cfg_parser.h" + +namespace srsenb { + +int enb::parse_cell_cfg(all_args_t *args, srslte_cell_t *cell) { + cell->id = args->enb.pci; + cell->cp = SRSLTE_CP_NORM; + cell->nof_ports = 1; + cell->nof_prb = args->enb.n_prb; + + LIBLTE_RRC_PHICH_CONFIG_STRUCT phichcfg; + + parser::section phy_cnfg("phy_cnfg"); + parser::section phich_cnfg("phich_cnfg"); + phy_cnfg.add_subsection(&phich_cnfg); + phich_cnfg.add_field( + new parser::field_enum_str + ("duration", &phichcfg.dur, liblte_rrc_phich_duration_text, LIBLTE_RRC_PHICH_DURATION_N_ITEMS) + ); + phich_cnfg.add_field( + new parser::field_enum_str + ("resources", &phichcfg.res, liblte_rrc_phich_resource_text, LIBLTE_RRC_PHICH_RESOURCE_N_ITEMS) + ); + parser::parse_section(args->enb_files.rr_config, &phy_cnfg); + + cell->phich_length = (srslte_phich_length_t) phichcfg.dur; + cell->phich_resources = (srslte_phich_resources_t) phichcfg.res; + + if (!srslte_cell_isvalid(cell)) { + fprintf(stderr, "Invalid cell parameters: nof_prb=%d, cell_id=%d\n", args->enb.n_prb, args->enb.s1ap.cell_id); + return -1; + } + + return 0; +} + +int field_sched_info::parse(libconfig::Setting &root) +{ + data->N_sched_info = root.getLength(); + for (uint32_t i=0;iN_sched_info;i++) { + uint32_t periodicity = 0; + if (!root[i].lookupValue("si_periodicity", periodicity)) { + fprintf(stderr, "Missing field si_periodicity in sched_info=%d\n", i); + return -1; + } + int k=0; + while(ksched_info[i].si_periodicity = (LIBLTE_RRC_SI_PERIODICITY_ENUM) k; + if (root[i].exists("si_mapping_info")) { + data->sched_info[i].N_sib_mapping_info = root[i]["si_mapping_info"].getLength(); + if (data->sched_info[i].N_sib_mapping_info < LIBLTE_RRC_MAX_SIB) { + for (uint32_t j=0;jsched_info[i].N_sib_mapping_info;j++) { + uint32_t sib_index = root[i]["si_mapping_info"][j]; + if (sib_index >= 3 && sib_index <= 13) { + data->sched_info[i].sib_mapping_info[j].sib_type = (LIBLTE_RRC_SIB_TYPE_ENUM) (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; + } + } + } else { + fprintf(stderr, "Number of si_mapping_info values exceeds maximum (%d)\n", LIBLTE_RRC_MAX_SIB); + return -1; + } + } else { + data->sched_info[i].N_sib_mapping_info = 0; + } + } + return 0; +} + + +int field_intra_neigh_cell_list::parse(libconfig::Setting &root) +{ + data->intra_freq_neigh_cell_list_size = root.getLength(); + for (uint32_t i=0;iintra_freq_neigh_cell_list_size && iintra_freq_neigh_cell_list[i].q_offset_range = (LIBLTE_RRC_Q_OFFSET_RANGE_ENUM) k; + + 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; + } + data->intra_freq_neigh_cell_list[i].phys_cell_id = (uint16) phys_cell_id; + } + return 0; +} + +int field_intra_black_cell_list::parse(libconfig::Setting &root) +{ + data->intra_freq_black_cell_list_size = root.getLength(); + for (uint32_t i=0;iintra_freq_black_cell_list_size && iintra_freq_black_cell_list[i].range = (LIBLTE_RRC_PHYS_CELL_ID_RANGE_ENUM) k; + + int start = 0; + if (!root[i].lookupValue("start", start)) { + fprintf(stderr, "Missing field start in black_cell=%d\n", i); + return -1; + } + data->intra_freq_black_cell_list[i].start = (uint16) start; + } + return 0; +} + + +int enb::parse_sib1(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data) +{ + parser::section sib1("sib1"); + + sib1.add_field( + new parser::field_enum_str + ("intra_freq_reselection", &data->intra_freq_reselection, liblte_rrc_intra_freq_reselection_text, LIBLTE_RRC_INTRA_FREQ_RESELECTION_N_ITEMS) + ); + sib1.add_field( + new parser::field("q_rx_lev_min", &data->q_rx_lev_min) + ); + sib1.add_field( + new parser::field("p_max", &data->p_max, &data->p_max_present) + ); + sib1.add_field( + new parser::field_enum_str + ("cell_barred", &data->cell_barred, liblte_rrc_cell_barred_text, LIBLTE_RRC_CELL_BARRED_N_ITEMS) + ); + sib1.add_field( + new parser::field_enum_num + ("si_window_length", &data->si_window_length, liblte_rrc_si_window_length_num, LIBLTE_RRC_SI_WINDOW_LENGTH_N_ITEMS) + ); + sib1.add_field( + new parser::field("system_info_value_tag", &data->system_info_value_tag) + ); + + // sched_info subsection uses a custom field class + parser::section sched_info("sched_info"); + sib1.add_subsection(&sched_info); + sched_info.add_field(new field_sched_info(data)); + + // Run parser with single section + return parser::parse_section(filename, &sib1); +} + +int enb::parse_sib2(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *data) +{ + parser::section sib2("sib2"); + + sib2.add_field( + new parser::field_enum_str + ("time_alignment_timer", &data->time_alignment_timer, + liblte_rrc_time_alignment_timer_text, LIBLTE_RRC_TIME_ALIGNMENT_TIMER_N_ITEMS) + ); + + parser::section freqinfo("freqInfo"); + sib2.add_subsection(&freqinfo); + freqinfo.add_field( + new parser::field + ("additional_spectrum_emission", &data->additional_spectrum_emission) + ); + freqinfo.add_field( + new parser::field ("ul_carrier_freq_present", &data->arfcn_value_eutra.present) + ); + freqinfo.add_field( + new parser::field ("ul_bw_present", &data->ul_bw.present) + ); + + // AC barring configuration + parser::section acbarring("ac_barring"); + sib2.add_subsection(&acbarring); + acbarring.set_optional(&data->ac_barring_info_present); + + acbarring.add_field( + new parser::field("ac_barring_for_emergency", &data->ac_barring_for_emergency) + ); + + parser::section acbarring_signalling("ac_barring_for_mo_signalling"); + acbarring.add_subsection(&acbarring_signalling); + acbarring_signalling.set_optional(&data->ac_barring_for_mo_signalling.enabled); + + acbarring_signalling.add_field( + new parser::field_enum_num + ("factor", &data->ac_barring_for_mo_signalling.factor, + liblte_rrc_ac_barring_factor_num, LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS) + ); + acbarring_signalling.add_field( + new parser::field_enum_num + ("time", &data->ac_barring_for_mo_signalling.time, + liblte_rrc_ac_barring_time_num, LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS) + ); + acbarring_signalling.add_field( + new parser::field("for_special_ac", &data->ac_barring_for_mo_signalling.for_special_ac) + ); + + parser::section acbarring_data("ac_barring_for_mo_data"); + acbarring.add_subsection(&acbarring_data); + acbarring_data.set_optional(&data->ac_barring_for_mo_data.enabled); + + acbarring_data.add_field( + new parser::field_enum_num + ("factor", &data->ac_barring_for_mo_data.factor, + liblte_rrc_ac_barring_factor_num, LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS) + ); + acbarring_data.add_field( + new parser::field_enum_num + ("time", &data->ac_barring_for_mo_data.time, + liblte_rrc_ac_barring_time_num, LIBLTE_RRC_AC_BARRING_FACTOR_N_ITEMS) + ); + acbarring_data.add_field( + new parser::field("for_special_ac", &data->ac_barring_for_mo_data.for_special_ac) + ); + + + // UE timers and constants + parser::section uetimers("ue_timers_and_constants"); + sib2.add_subsection(&uetimers); + uetimers.add_field( + new parser::field_enum_num + ("t300", &data->ue_timers_and_constants.t300, liblte_rrc_t300_num, LIBLTE_RRC_T300_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("t301", &data->ue_timers_and_constants.t301, liblte_rrc_t301_num, LIBLTE_RRC_T301_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("t310", &data->ue_timers_and_constants.t310, liblte_rrc_t310_num, LIBLTE_RRC_T310_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("n310", &data->ue_timers_and_constants.n310, liblte_rrc_n310_num, LIBLTE_RRC_N310_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("t311", &data->ue_timers_and_constants.t311, liblte_rrc_t311_num, LIBLTE_RRC_T311_N_ITEMS) + ); + uetimers.add_field( + new parser::field_enum_num + ("n311", &data->ue_timers_and_constants.n311, liblte_rrc_n311_num, LIBLTE_RRC_N311_N_ITEMS) + ); + + + + + // Radio-resource configuration section + parser::section rr_config("rr_config_common_sib"); + sib2.add_subsection(&rr_config); + + rr_config.add_field( + new parser::field_enum_str + ("ul_cp_length", &data->rr_config_common_sib.ul_cp_length, + liblte_rrc_ul_cp_length_text, LIBLTE_RRC_UL_CP_LENGTH_N_ITEMS) + ); + + // RACH configuration + parser::section rach_cnfg("rach_cnfg"); + rr_config.add_subsection(&rach_cnfg); + + rach_cnfg.add_field( + new parser::field_enum_num + ("num_ra_preambles", &data->rr_config_common_sib.rach_cnfg.num_ra_preambles, + liblte_rrc_number_of_ra_preambles_num, LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("preamble_init_rx_target_pwr", &data->rr_config_common_sib.rach_cnfg.preamble_init_rx_target_pwr, + liblte_rrc_preamble_initial_received_target_power_num, LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("pwr_ramping_step", &data->rr_config_common_sib.rach_cnfg.pwr_ramping_step, + liblte_rrc_power_ramping_step_num, LIBLTE_RRC_POWER_RAMPING_STEP_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("preamble_trans_max", &data->rr_config_common_sib.rach_cnfg.preamble_trans_max, + liblte_rrc_preamble_trans_max_num, LIBLTE_RRC_PREAMBLE_TRANS_MAX_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("ra_resp_win_size", &data->rr_config_common_sib.rach_cnfg.ra_resp_win_size, + liblte_rrc_ra_response_window_size_num, LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field_enum_num + ("mac_con_res_timer", &data->rr_config_common_sib.rach_cnfg.mac_con_res_timer, + liblte_rrc_mac_contention_resolution_timer_num, LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_N_ITEMS) + ); + rach_cnfg.add_field( + new parser::field("max_harq_msg3_tx", &data->rr_config_common_sib.rach_cnfg.max_harq_msg3_tx) + ); + + parser::section groupa_cnfg("preambles_group_a_cnfg"); + rach_cnfg.add_subsection(&groupa_cnfg); + groupa_cnfg.set_optional(&data->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.present); + groupa_cnfg.add_field( + new parser::field_enum_num + ("size_of_ra", &data->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.size_of_ra, + liblte_rrc_size_of_ra_preambles_group_a_num, LIBLTE_RRC_SIZE_OF_RA_PREAMBLES_GROUP_A_N_ITEMS) + ); + groupa_cnfg.add_field( + new parser::field_enum_num + ("msg_size", &data->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.msg_size, + liblte_rrc_message_size_group_a_num, LIBLTE_RRC_MESSAGE_SIZE_GROUP_A_N_ITEMS) + ); + groupa_cnfg.add_field( + new parser::field_enum_num + ("msg_pwr_offset_group_b", &data->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.msg_pwr_offset_group_b, + liblte_rrc_message_power_offset_group_b_num, LIBLTE_RRC_MESSAGE_POWER_OFFSET_GROUP_B_N_ITEMS) + ); + + + + // BCCH configuration + parser::section bcch_cnfg("bcch_cnfg"); + rr_config.add_subsection(&bcch_cnfg); + bcch_cnfg.add_field( + new parser::field_enum_num + ("modification_period_coeff", &data->rr_config_common_sib.bcch_cnfg.modification_period_coeff, + liblte_rrc_modification_period_coeff_num, LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N_ITEMS) + ); + + // PCCH configuration + parser::section pcch_cnfg("pcch_cnfg"); + rr_config.add_subsection(&pcch_cnfg); + pcch_cnfg.add_field( + new parser::field_enum_num + ("default_paging_cycle", &data->rr_config_common_sib.pcch_cnfg.default_paging_cycle, + liblte_rrc_default_paging_cycle_num, LIBLTE_RRC_DEFAULT_PAGING_CYCLE_N_ITEMS) + ); + pcch_cnfg.add_field( + new parser::field_enum_str + ("nB", &data->rr_config_common_sib.pcch_cnfg.nB, + liblte_rrc_nb_text, LIBLTE_RRC_NB_N_ITEMS) + ); + + // PRACH configuration + parser::section prach_cnfg("prach_cnfg"); + rr_config.add_subsection(&prach_cnfg); + prach_cnfg.add_field( + new parser::field("root_sequence_index", &data->rr_config_common_sib.prach_cnfg.root_sequence_index) + ); + parser::section prach_cnfg_info("prach_cnfg_info"); + prach_cnfg.add_subsection(&prach_cnfg_info); + prach_cnfg_info.add_field( + new parser::field("high_speed_flag", &data->rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag) + ); + prach_cnfg_info.add_field( + new parser::field("prach_config_index", &data->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index) + ); + prach_cnfg_info.add_field( + new parser::field("prach_freq_offset", &data->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset) + ); + prach_cnfg_info.add_field( + new parser::field("zero_correlation_zone_config", &data->rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config) + ); + + // PDSCH configuration + parser::section pdsch_cnfg("pdsch_cnfg"); + rr_config.add_subsection(&pdsch_cnfg); + pdsch_cnfg.add_field( + new parser::field("p_b", &data->rr_config_common_sib.pdsch_cnfg.p_b) + ); + pdsch_cnfg.add_field( + new parser::field("rs_power", &data->rr_config_common_sib.pdsch_cnfg.rs_power) + ); + + // PUSCH configuration + parser::section pusch_cnfg("pusch_cnfg"); + rr_config.add_subsection(&pusch_cnfg); + pusch_cnfg.add_field( + new parser::field("n_sb", &data->rr_config_common_sib.pusch_cnfg.n_sb) + ); + pusch_cnfg.add_field( + new parser::field_enum_str + ("hopping_mode", &data->rr_config_common_sib.pusch_cnfg.hopping_mode, + liblte_rrc_hopping_mode_text, LIBLTE_RRC_HOOPPING_MODE_N_ITEMS) + ); + pusch_cnfg.add_field( + new parser::field + ("pusch_hopping_offset", &data->rr_config_common_sib.pusch_cnfg.pusch_hopping_offset) + ); + pusch_cnfg.add_field( + new parser::field + ("enable_64_qam", &data->rr_config_common_sib.pusch_cnfg.enable_64_qam) + ); + + // PUSCH-ULRS configuration + parser::section ulrs_cnfg("ul_rs"); + pusch_cnfg.add_subsection(&ulrs_cnfg); + ulrs_cnfg.add_field( + new parser::field + ("cyclic_shift", &data->rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift) + ); + ulrs_cnfg.add_field( + new parser::field + ("group_assignment_pusch", &data->rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch) + ); + ulrs_cnfg.add_field( + new parser::field + ("group_hopping_enabled", &data->rr_config_common_sib.pusch_cnfg.ul_rs.group_hopping_enabled) + ); + ulrs_cnfg.add_field( + new parser::field + ("sequence_hopping_enabled", &data->rr_config_common_sib.pusch_cnfg.ul_rs.sequence_hopping_enabled) + ); + + // PUCCH configuration + parser::section pucch_cnfg("pucch_cnfg"); + rr_config.add_subsection(&pucch_cnfg); + pucch_cnfg.add_field( + new parser::field_enum_num + ("delta_pucch_shift", &data->rr_config_common_sib.pucch_cnfg.delta_pucch_shift, + liblte_rrc_delta_pucch_shift_num,LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS) + ); + pucch_cnfg.add_field( + new parser::field + ("n_rb_cqi", &data->rr_config_common_sib.pucch_cnfg.n_rb_cqi) + ); + pucch_cnfg.add_field( + new parser::field + ("n_cs_an", &data->rr_config_common_sib.pucch_cnfg.n_cs_an) + ); + pucch_cnfg.add_field( + new parser::field + ("n1_pucch_an", &data->rr_config_common_sib.pucch_cnfg.n1_pucch_an) + ); + + // UL PWR Ctrl configuration + parser::section ul_pwr_ctrl("ul_pwr_ctrl"); + rr_config.add_subsection(&ul_pwr_ctrl); + ul_pwr_ctrl.add_field( + new parser::field + ("p0_nominal_pusch", &data->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pusch) + ); + ul_pwr_ctrl.add_field( + new parser::field_enum_num + ("alpha", &data->rr_config_common_sib.ul_pwr_ctrl.alpha, + liblte_rrc_ul_power_control_alpha_num, LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_N_ITEMS) + ); + ul_pwr_ctrl.add_field( + new parser::field + ("p0_nominal_pucch", &data->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pucch) + ); + ul_pwr_ctrl.add_field( + new parser::field + ("delta_preamble_msg3", &data->rr_config_common_sib.ul_pwr_ctrl.delta_preamble_msg3) + ); + + // Delta Flist PUCCH + parser::section delta_flist("delta_flist_pucch"); + ul_pwr_ctrl.add_subsection(&delta_flist); + delta_flist.add_field( + new parser::field_enum_num + ("format_1", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1, + liblte_rrc_delta_f_pucch_format_1_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_N_ITEMS) + ); + delta_flist.add_field( + new parser::field_enum_num + ("format_1b", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1b, + liblte_rrc_delta_f_pucch_format_1b_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_N_ITEMS) + ); + delta_flist.add_field( + new parser::field_enum_num + ("format_2", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2, + liblte_rrc_delta_f_pucch_format_2_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_N_ITEMS) + ); + delta_flist.add_field( + new parser::field_enum_num + ("format_2a", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2a, + liblte_rrc_delta_f_pucch_format_2a_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_N_ITEMS) + ); + delta_flist.add_field( + new parser::field_enum_num + ("format_2b", &data->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2b, + liblte_rrc_delta_f_pucch_format_2b_num, LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_N_ITEMS) + ); + + // Run parser with single section + return parser::parse_section(filename, &sib2); +} + +int enb::parse_sib3(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *data) +{ + parser::section sib3("sib3"); + + // CellReselectionInfoCommon + parser::section resel_common("cell_reselection_common"); + sib3.add_subsection(&resel_common); + + resel_common.add_field( + new parser::field_enum_num + ("q_hyst", &data->q_hyst, + liblte_rrc_q_hyst_num, LIBLTE_RRC_Q_HYST_N_ITEMS) + ); + + parser::section speed_resel("speed_state_resel_params"); + resel_common.add_subsection(&speed_resel); + resel_common.set_optional(&data->speed_state_resel_params.present); + + + parser::section q_hyst_sf("q_hyst_sf"); + speed_resel.add_subsection(&q_hyst_sf); + q_hyst_sf.add_field( + new parser::field_enum_num + ("medium", &data->speed_state_resel_params.q_hyst_sf.medium, + liblte_rrc_sf_medium_num, LIBLTE_RRC_SF_MEDIUM_N_ITEMS) + ); + q_hyst_sf.add_field( + new parser::field_enum_num + ("high", &data->speed_state_resel_params.q_hyst_sf.high, + liblte_rrc_sf_high_num, LIBLTE_RRC_SF_HIGH_N_ITEMS) + ); + + + parser::section mob_params("mobility_state_params"); + speed_resel.add_subsection(&mob_params); + mob_params.add_field( + new parser::field_enum_num + ("t_eval", &data->speed_state_resel_params.mobility_state_params.t_eval, + liblte_rrc_t_evaluation_num, LIBLTE_RRC_T_EVALUATION_N_ITEMS) + ); + mob_params.add_field( + new parser::field_enum_num + ("t_hyst_normal", &data->speed_state_resel_params.mobility_state_params.t_hyst_normal, + liblte_rrc_t_hyst_normal_num, LIBLTE_RRC_T_HYST_NORMAL_N_ITEMS) + ); + mob_params.add_field( + new parser::field + ("n_cell_change_medium", &data->speed_state_resel_params.mobility_state_params.n_cell_change_medium) + ); + mob_params.add_field( + new parser::field + ("n_cell_change_high", &data->speed_state_resel_params.mobility_state_params.n_cell_change_high) + ); + + // CellReselectionServingFreqInfo + parser::section resel_serving("cell_reselection_serving"); + sib3.add_subsection(&resel_serving); + + resel_serving.add_field( + new parser::field ("s_non_intra_search", &data->s_non_intra_search, &data->s_non_intra_search_present) + ); + resel_serving.add_field( + new parser::field("thresh_serving_low", &data->thresh_serving_low) + ); + resel_serving.add_field( + new parser::field("cell_resel_prio", &data->cell_resel_prio) + ); + + + // intraFreqCellReselectionInfo + parser::section intra_freq("intra_freq_reselection"); + sib3.add_subsection(&intra_freq); + + intra_freq.add_field( + new parser::field("q_rx_lev_min", &data->q_rx_lev_min) + ); + intra_freq.add_field( + new parser::field("p_max", &data->p_max, &data->p_max_present) + ); + intra_freq.add_field( + new parser::field("s_intra_search", &data->s_intra_search, &data->s_intra_search_present) + ); + intra_freq.add_field( + new parser::field_enum_num + ("allowed_meas_bw", &data->allowed_meas_bw, + liblte_rrc_allowed_meas_bandwidth_num, LIBLTE_RRC_ALLOWED_MEAS_BANDWIDTH_N_ITEMS, + &data->allowed_meas_bw_present) + ); + intra_freq.add_field( + new parser::field("presence_ant_port_1", &data->presence_ant_port_1) + ); + intra_freq.add_field( + new parser::field("neigh_cell_cnfg", &data->neigh_cell_cnfg) + ); + intra_freq.add_field( + new parser::field("t_resel_eutra", &data->t_resel_eutra) + ); + parser::section t_resel_eutra_sf("t_resel_eutra_sf"); + intra_freq.add_subsection(&t_resel_eutra_sf); + t_resel_eutra_sf.set_optional(&data->t_resel_eutra_sf_present); + + t_resel_eutra_sf.add_field( + new parser::field_enum_num + ("sf_medium", &data->t_resel_eutra_sf.sf_medium, + liblte_rrc_sssf_medium_num, LIBLTE_RRC_SSSF_MEDIUM_N_ITEMS) + ); + t_resel_eutra_sf.add_field( + new parser::field_enum_num + ("sf_high", &data->t_resel_eutra_sf.sf_high, + liblte_rrc_sssf_high_num, LIBLTE_RRC_SSSF_HIGH_N_ITEMS) + ); + + // Run parser with single section + return parser::parse_section(filename, &sib3); +} + +int enb::parse_sib4(std::string filename, LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data) +{ + parser::section sib4("sib4"); + + // csg-PhysCellIdRange + parser::section csg_range("csg_phys_cell_id_range"); + sib4.add_subsection(&csg_range); + csg_range.set_optional(&data->csg_phys_cell_id_range_present); + csg_range.add_field( + new parser::field_enum_num + ("range", &data->csg_phys_cell_id_range.range, + liblte_rrc_phys_cell_id_range_num, LIBLTE_RRC_PHYS_CELL_ID_RANGE_N_ITEMS) + ); + csg_range.add_field( + new parser::field("start", &data->csg_phys_cell_id_range.start) + ); + + // intraFreqNeighCellList + parser::section intra_neigh("intra_freq_neigh_cell_list"); + sib4.add_subsection(&intra_neigh); + bool dummy_bool = false; + intra_neigh.set_optional(&dummy_bool); + intra_neigh.add_field(new field_intra_neigh_cell_list(data)); + + // intraFreqBlackCellList + parser::section intra_black("intra_freq_black_cell_list"); + sib4.add_subsection(&intra_black); + intra_black.set_optional(&dummy_bool); + intra_black.add_field(new field_intra_black_cell_list(data)); + + // Run parser with single section + return parser::parse_section(filename, &sib4); +} + + +uint32_t HexToBytes(const std::string& str, uint8_t *char_value, uint32_t buff_len) { + uint32_t i=0; + for (i = 0; i < str.length() && i("hnb_name", &hnb_name, &name_enabled)); + sib9.add_field(new parser::field("hex_value", &hex_value, &hex_enabled)); + + // Run parser with single section + if (!parser::parse_section(filename, &sib9)) { + data->hnb_name_present = true; + if (name_enabled) { + strncpy((char*) data->hnb_name, hnb_name.c_str(), 48); + data->hnb_name_size = strnlen(hnb_name.c_str(), 48); + } else if (hex_enabled) { + data->hnb_name_size = HexToBytes(hex_value, data->hnb_name, 48); + } else { + data->hnb_name_present = false; + } + return 0; + } else { + return -1; + } +} + +int enb::parse_sibs(all_args_t *args, rrc_cfg_t *rrc_cfg, phy_cfg_t *phy_config_common) +{ + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1 = &rrc_cfg->sibs[0].sib.sib1; + rrc_cfg->sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2 = &rrc_cfg->sibs[1].sib.sib2; + rrc_cfg->sibs[1].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3 = &rrc_cfg->sibs[2].sib.sib3; + rrc_cfg->sibs[2].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *sib4 = &rrc_cfg->sibs[3].sib.sib4; + rrc_cfg->sibs[3].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT *sib9 = &rrc_cfg->sibs[8].sib.sib9; + rrc_cfg->sibs[8].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9; + + + // Read SIB1 configuration from file + bzero(sib1, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT)); + if (parse_sib1(args->enb_files.sib_config, sib1)) { + return -1; + } + + // Fill rest of data from enb config + sib1->cell_id = args->enb.s1ap.enb_id; + sib1->tracking_area_code = args->enb.s1ap.tac; + sib1->freq_band_indicator = srslte_band_get_band(args->rf.dl_earfcn); + sib1->N_plmn_ids = 1; + sib1->plmn_id[0].id.mcc = args->enb.s1ap.mcc; + sib1->plmn_id[0].id.mnc = args->enb.s1ap.mnc; + sib1->plmn_id[0].resv_for_oper = LIBLTE_RRC_NOT_RESV_FOR_OPER; + sib1->cell_barred = LIBLTE_RRC_CELL_NOT_BARRED; + sib1->q_rx_lev_min_offset = 0; + + // Generate SIB2 + bzero(sib2, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT)); + if (parse_sib2(args->enb_files.sib_config, sib2)) { + return -1; + } + + // SRS not yet supported + sib2->rr_config_common_sib.srs_ul_cnfg.present = false; + if (sib2->ul_bw.present) { + switch(args->enb.n_prb) { + case 6: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N6; + break; + case 15: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N15; + break; + case 25: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N25; + break; + case 50: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N50; + break; + case 75: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N75; + break; + case 100: + sib2->ul_bw.bw = LIBLTE_RRC_UL_BW_N100; + break; + } + } + if (sib2->arfcn_value_eutra.present) { + sib2->arfcn_value_eutra.value = args->rf.ul_earfcn; + } + + // Generate SIB3 if defined in mapping info + if (sib_is_present(sib1->sched_info, sib1->N_sched_info, LIBLTE_RRC_SIB_TYPE_3)) { + bzero(sib3, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT)); + if (parse_sib3(args->enb_files.sib_config, sib3)) { + return -1; + } + } + + // Generate SIB4 if defined in mapping info + if (sib_is_present(sib1->sched_info, sib1->N_sched_info, LIBLTE_RRC_SIB_TYPE_4)) { + bzero(sib4, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT)); + if (parse_sib4(args->enb_files.sib_config, sib4)) { + return -1; + } + } + + // Generate SIB9 if defined in mapping info + if (sib_is_present(sib1->sched_info, sib1->N_sched_info, LIBLTE_RRC_SIB_TYPE_9)) { + bzero(sib9, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_9_STRUCT)); + if (parse_sib9(args->enb_files.sib_config, sib9)) { + return -1; + } + } + + // Copy PHY common configuration + bzero(phy_config_common, sizeof(phy_cfg_t)); + memcpy(&phy_config_common->prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + memcpy(&phy_config_common->pdsch_cnfg, &sib2->rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_config_common->pusch_cnfg, &sib2->rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_config_common->pucch_cnfg, &sib2->rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_config_common->srs_ul_cnfg, &sib2->rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); + + return 0; +} + +bool enb::sib_is_present(LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info, uint32_t nof_sched_info, LIBLTE_RRC_SIB_TYPE_ENUM sib_num) +{ + for (uint32_t i=0;imac_cnfg.phr_cnfg.setup_present); + phr_cnfg.add_field( + new parser::field_enum_str + ("dl_pathloss_change", &rrc_cfg->mac_cnfg.phr_cnfg.dl_pathloss_change, + liblte_rrc_dl_pathloss_change_text, LIBLTE_RRC_DL_PATHLOSS_CHANGE_N_ITEMS) + ); + phr_cnfg.add_field( + new parser::field_enum_num + ("periodic_phr_timer", &rrc_cfg->mac_cnfg.phr_cnfg.periodic_phr_timer, + liblte_rrc_periodic_phr_timer_num, LIBLTE_RRC_PERIODIC_PHR_TIMER_N_ITEMS) + ); + phr_cnfg.add_field( + new parser::field_enum_num + ("prohibit_phr_timer", &rrc_cfg->mac_cnfg.phr_cnfg.prohibit_phr_timer, + liblte_rrc_prohibit_phr_timer_num, LIBLTE_RRC_PROHIBIT_PHR_TIMER_N_ITEMS) + ); + + parser::section ulsch_cnfg("ulsch_cnfg"); + mac_cnfg.add_subsection(&ulsch_cnfg); + + rrc_cfg->mac_cnfg.ulsch_cnfg.tti_bundling = false; + ulsch_cnfg.add_field( + new parser::field_enum_num + ("max_harq_tx", &rrc_cfg->mac_cnfg.ulsch_cnfg.max_harq_tx, + liblte_rrc_max_harq_tx_num, LIBLTE_RRC_MAX_HARQ_TX_N_ITEMS, + &rrc_cfg->mac_cnfg.ulsch_cnfg.max_harq_tx_present) + ); + ulsch_cnfg.add_field( + new parser::field_enum_num + ("periodic_bsr_timer", &rrc_cfg->mac_cnfg.ulsch_cnfg.periodic_bsr_timer, + liblte_rrc_periodic_bsr_timer_num, LIBLTE_RRC_PERIODIC_BSR_TIMER_N_ITEMS, + &rrc_cfg->mac_cnfg.ulsch_cnfg.periodic_bsr_timer_present) + ); + + ulsch_cnfg.add_field( + new parser::field_enum_num + ("retx_bsr_timer", &rrc_cfg->mac_cnfg.ulsch_cnfg.retx_bsr_timer, + liblte_rrc_retransmission_bsr_timer_num, LIBLTE_RRC_RETRANSMISSION_BSR_TIMER_N_ITEMS) + ); + + mac_cnfg.add_field( + new parser::field_enum_num + ("time_alignment_timer", &rrc_cfg->mac_cnfg.time_alignment_timer, + liblte_rrc_time_alignment_timer_num, LIBLTE_RRC_TIME_ALIGNMENT_TIMER_N_ITEMS) + ); + + + /* PHY config section */ + parser::section phy_cfg("phy_cnfg"); + + parser::section pusch_cnfg_ded("pusch_cnfg_ded"); + phy_cfg.add_subsection(&pusch_cnfg_ded); + + pusch_cnfg_ded.add_field(new parser::field ("beta_offset_ack_idx", &rrc_cfg->pusch_cfg.beta_offset_ack_idx)); + pusch_cnfg_ded.add_field(new parser::field ("beta_offset_ri_idx", &rrc_cfg->pusch_cfg.beta_offset_ri_idx)); + pusch_cnfg_ded.add_field(new parser::field ("beta_offset_cqi_idx", &rrc_cfg->pusch_cfg.beta_offset_cqi_idx)); + + parser::section sched_request_cnfg("sched_request_cnfg"); + phy_cfg.add_subsection(&sched_request_cnfg); + + sched_request_cnfg.add_field( + new parser::field_enum_num + ("dsr_trans_max", &rrc_cfg->sr_cfg.dsr_max, + liblte_rrc_dsr_trans_max_num, LIBLTE_RRC_DSR_TRANS_MAX_N_ITEMS) + ); + sched_request_cnfg.add_field(new parser::field ("period", &rrc_cfg->sr_cfg.period)); + sched_request_cnfg.add_field(new parser::field ("nof_prb", &rrc_cfg->sr_cfg.nof_prb)); + sched_request_cnfg.add_field(new field_sf_mapping(rrc_cfg->sr_cfg.sf_mapping, &rrc_cfg->sr_cfg.nof_subframes)); + + parser::section cqi_report_cnfg("cqi_report_cnfg"); + phy_cfg.add_subsection(&cqi_report_cnfg); + + cqi_report_cnfg.add_field( + new parser::field_enum_str + ("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 ("nof_prb", &rrc_cfg->cqi_cfg.nof_prb)); + 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)); + + // Run parser with two sections + parser p(args->enb_files.rr_config); + p.add_section(&mac_cnfg); + p.add_section(&phy_cfg); + return p.parse(); +} + +int field_sf_mapping::parse(libconfig::Setting &root) +{ + *nof_subframes = root["subframe"].getLength(); + for (uint32_t i=0;i<*nof_subframes;i++) { + sf_mapping[i] = root["subframe"][i]; + } + return 0; +} + + + + + + + +int enb::parse_drb(all_args_t* args, rrc_cfg_t* rrc_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); +} + +int field_qci::parse(libconfig::Setting &root) +{ + uint32_t nof_qci = root.getLength(); + + bzero(cfg, sizeof(rrc_cfg_qci_t)*MAX_NOF_QCI); + + for (uint32_t i=0;i discard_timer + ("discard_timer", &cfg[qci].pdcp_cfg.discard_timer, + liblte_rrc_discard_timer_num, LIBLTE_RRC_DISCARD_TIMER_N_ITEMS); + if (discard_timer.parse(q["pdcp_config"])) { + cfg[qci].pdcp_cfg.discard_timer_present = false; + } else { + cfg[qci].pdcp_cfg.discard_timer_present = true; + } + + parser::field_enum_num pdcp_sn_size + ("pdcp_sn_size", &cfg[qci].pdcp_cfg.rlc_um_pdcp_sn_size, + liblte_rrc_pdcp_sn_size_num, LIBLTE_RRC_PDCP_SN_SIZE_N_ITEMS); + + if (pdcp_sn_size.parse(q["pdcp_config"])) { + cfg[qci].pdcp_cfg.rlc_um_pdcp_sn_size_present = false; + } else { + cfg[qci].pdcp_cfg.rlc_um_pdcp_sn_size_present = true; + } + + if (q["pdcp_config"].lookupValue("status_report_required", cfg[qci].pdcp_cfg.rlc_am_status_report_required)) { + cfg[qci].pdcp_cfg.rlc_am_status_report_required_present = true; + } else { + cfg[qci].pdcp_cfg.rlc_am_status_report_required_present = false; + } + + // Parse RLC section + if (q["rlc_config"].exists("ul_am")) { + cfg[qci].rlc_cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_AM; + } else if (q["rlc_config"].exists("ul_um") && q["rlc_config"].exists("dl_um")) { + cfg[qci].rlc_cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + } else if (q["rlc_config"].exists("ul_um") && !q["rlc_config"].exists("dl_um")) { + cfg[qci].rlc_cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_UNI_UL; + } else if (!q["rlc_config"].exists("ul_um") && q["rlc_config"].exists("dl_um")) { + cfg[qci].rlc_cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_UNI_DL; + } else { + fprintf(stderr, "Invalid combination of UL/DL UM/AM for qci=%d\n", qci); + return -1; + } + + // Parse RLC-UM section + if (q["rlc_config"].exists("ul_um")) { + + LIBLTE_RRC_UL_UM_RLC_STRUCT *rlc_cfg = &cfg[qci].rlc_cfg.ul_um_bi_rlc; + if (cfg[qci].rlc_cfg.rlc_mode == LIBLTE_RRC_RLC_MODE_UM_UNI_UL) { + rlc_cfg = &cfg[qci].rlc_cfg.ul_um_bi_rlc; + } + + parser::field_enum_num sn_field_len + ("sn_field_length", &rlc_cfg->sn_field_len, + liblte_rrc_sn_field_length_num, LIBLTE_RRC_SN_FIELD_LENGTH_N_ITEMS); + sn_field_len.parse(q["rlc_config"]["ul_um"]); + } + + if (q["rlc_config"].exists("dl_um")) { + + LIBLTE_RRC_DL_UM_RLC_STRUCT *rlc_cfg = &cfg[qci].rlc_cfg.dl_um_bi_rlc; + if (cfg[qci].rlc_cfg.rlc_mode == LIBLTE_RRC_RLC_MODE_UM_UNI_DL) { + rlc_cfg = &cfg[qci].rlc_cfg.dl_um_bi_rlc; + } + + parser::field_enum_num sn_field_len + ("sn_field_length", &rlc_cfg->sn_field_len, + liblte_rrc_sn_field_length_num, LIBLTE_RRC_SN_FIELD_LENGTH_N_ITEMS); + sn_field_len.parse(q["rlc_config"]["dl_um"]); + + parser::field_enum_num t_reordering + ("t_reordering", &rlc_cfg->t_reordering, + liblte_rrc_t_reordering_num, LIBLTE_RRC_T_REORDERING_N_ITEMS); + t_reordering.parse(q["rlc_config"]["dl_um"]); + } + + // Parse RLC-AM section + if (q["rlc_config"].exists("ul_am")) { + LIBLTE_RRC_UL_AM_RLC_STRUCT *rlc_cfg = &cfg[qci].rlc_cfg.ul_am_rlc; + + parser::field_enum_num t_poll_retx + ("t_poll_retx", &rlc_cfg->t_poll_retx, + liblte_rrc_t_poll_retransmit_num, LIBLTE_RRC_T_POLL_RETRANSMIT_N_ITEMS); + t_poll_retx.parse(q["rlc_config"]["ul_am"]); + + parser::field_enum_num poll_pdu + ("poll_pdu", &rlc_cfg->poll_pdu, + liblte_rrc_poll_pdu_num, LIBLTE_RRC_POLL_PDU_N_ITEMS); + poll_pdu.parse(q["rlc_config"]["ul_am"]); + + parser::field_enum_num poll_byte + ("poll_byte", &rlc_cfg->poll_byte, + liblte_rrc_poll_byte_num, LIBLTE_RRC_POLL_BYTE_N_ITEMS); + poll_byte.parse(q["rlc_config"]["ul_am"]); + + parser::field_enum_num max_retx_thresh + ("max_retx_thresh", &rlc_cfg->max_retx_thresh, + liblte_rrc_max_retx_threshold_num, LIBLTE_RRC_MAX_RETX_THRESHOLD_N_ITEMS); + max_retx_thresh.parse(q["rlc_config"]["ul_am"]); + } + + if (q["rlc_config"].exists("dl_am")) { + LIBLTE_RRC_DL_AM_RLC_STRUCT *rlc_cfg = &cfg[qci].rlc_cfg.dl_am_rlc; + + parser::field_enum_num t_reordering + ("t_reordering", &rlc_cfg->t_reordering, + liblte_rrc_t_reordering_num, LIBLTE_RRC_T_REORDERING_N_ITEMS); + t_reordering.parse(q["rlc_config"]["dl_am"]); + + parser::field_enum_num t_status_prohibit + ("t_status_prohibit", &rlc_cfg->t_status_prohibit, + liblte_rrc_t_status_prohibit_num, LIBLTE_RRC_T_STATUS_PROHIBIT_N_ITEMS); + t_status_prohibit.parse(q["rlc_config"]["dl_am"]); + } + + + // 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; + } + LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT *lc_cfg = &cfg[qci].lc_cfg; + + parser::field priority ("priority", &lc_cfg->priority); + priority.parse(q["logical_channel_config"]); + + parser::field_enum_num prioritized_bit_rate + ("prioritized_bit_rate", &lc_cfg->prioritized_bit_rate, + liblte_rrc_prioritized_bit_rate_num, LIBLTE_RRC_PRIORITIZED_BIT_RATE_N_ITEMS); + prioritized_bit_rate.parse(q["logical_channel_config"]); + + parser::field_enum_num bucket_size_duration + ("bucket_size_duration", &lc_cfg->bucket_size_duration, + liblte_rrc_bucket_size_duration_num, LIBLTE_RRC_BUCKET_SIZE_DURATION_N_ITEMS); + bucket_size_duration.parse(q["logical_channel_config"]); + + parser::field log_chan_group ("log_chan_group", &lc_cfg->log_chan_group); + if (log_chan_group.parse(q["logical_channel_config"])) { + lc_cfg->log_chan_group_present = false; + } else { + lc_cfg->log_chan_group_present = true; + } + + + + + cfg[qci].configured = true; + } + + return 0; +} + +} diff --git a/srsenb/src/enb_cfg_parser.h b/srsenb/src/enb_cfg_parser.h new file mode 100644 index 000000000..d18b5e2fb --- /dev/null +++ b/srsenb/src/enb_cfg_parser.h @@ -0,0 +1,94 @@ +#ifndef ENB_CFG_PARSER_SIB1_H +#define ENB_CFG_PARSER_SIB1_H + +#include +#include +#include +#include +#include +#include +#include "parser.h" + +#include "upper/rrc.h" +#include "srslte/asn1/liblte_rrc.h" + +namespace srsenb { + +using namespace libconfig; + +class field_sched_info : public parser::field_itf +{ +public: + field_sched_info(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data_) { data = data_; } + ~field_sched_info() {} + int parse(Setting &root); + const char* get_name() { + return "sched_info"; + } + +private: + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *data; +}; + +class field_intra_neigh_cell_list : public parser::field_itf +{ +public: + field_intra_neigh_cell_list(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data_) { data = data_; } + ~field_intra_neigh_cell_list(){} + int parse(Setting &root); + const char* get_name() { + return "intra_neigh_cell_list"; + } + +private: + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data; +}; + +class field_intra_black_cell_list : public parser::field_itf +{ +public: + field_intra_black_cell_list(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data_) { data = data_; } + ~field_intra_black_cell_list(){} + int parse(Setting &root); + const char* get_name() { + return "intra_black_cell_list"; + } + +private: + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_4_STRUCT *data; +}; + +class field_sf_mapping : public parser::field_itf +{ +public: + field_sf_mapping(uint32_t *sf_mapping_, uint32_t *nof_subframes_) { sf_mapping = sf_mapping_; nof_subframes = nof_subframes_; } + ~field_sf_mapping(){} + int parse(Setting &root); + const char* get_name() { + return "sf_mapping"; + } + +private: + uint32_t *sf_mapping; + uint32_t *nof_subframes; +}; + +class field_qci : public parser::field_itf +{ +public: + field_qci(rrc_cfg_qci_t *cfg_) { cfg = cfg_; } + ~field_qci(){} + const char* get_name() { + return "field_cqi"; + } + + int parse(Setting &root); +private: + rrc_cfg_qci_t *cfg; +}; + + +} + +#endif + diff --git a/srsenb/src/mac/CMakeLists.txt b/srsenb/src/mac/CMakeLists.txt new file mode 100644 index 000000000..4369d11a4 --- /dev/null +++ b/srsenb/src/mac/CMakeLists.txt @@ -0,0 +1,5 @@ +file(GLOB SOURCES "*.cc") +add_library(srsenb_mac SHARED ${SOURCES}) +target_link_libraries(srsenb_mac) + + diff --git a/srsenb/src/mac/mac.cc b/srsenb/src/mac/mac.cc new file mode 100644 index 000000000..7ef26596f --- /dev/null +++ b/srsenb/src/mac/mac.cc @@ -0,0 +1,732 @@ + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +#include +#include +#include +#include + +#include "srslte/common/log.h" +#include "mac/mac.h" + +//#define WRITE_SIB_PCAP + +namespace srsenb { + +mac::mac() : timers_db((uint32_t) NOF_MAC_TIMERS), + rar_pdu_msg(sched_interface::MAX_RAR_LIST), + pdu_process_thread(this) +{ + started = false; + pcap = NULL; +} + +bool mac::init(mac_args_t *args_, srslte_cell_t *cell_, phy_interface_mac *phy, rlc_interface_mac *rlc, rrc_interface_mac *rrc, srslte::log *log_h_) +{ + started = false; + + if (cell_ && phy && rlc && log_h_ && args_) { + phy_h = phy; + rlc_h = rlc; + rrc_h = rrc; + log_h = log_h_; + + memcpy(&args, args_, sizeof(mac_args_t)); + memcpy(&cell, cell_, sizeof(srslte_cell_t)); + + scheduler.init(rrc, log_h); + // Set default scheduler (RR) + scheduler.set_metric(&sched_metric_dl_rr, &sched_metric_ul_rr); + + // Set default scheduler configuration + scheduler.set_sched_cfg(&args.sched); + + // Init softbuffer for SI messages + for (int i=0;iadd_rnti(SRSLTE_SIRNTI); + + /* Setup P-RNTI in PHY */ + phy_h->add_rnti(SRSLTE_PRNTI); + + /* Setup RA-RNTI in PHY */ + for (int i=0;i<10;i++) { + phy_h->add_rnti(1+i); + } +} + +uint32_t mac::get_unique_id() +{ + return upper_timers_thread.get_unique_id(); +} + +/* Front-end to upper-layer timers */ +srslte::timers::timer* mac::get(uint32_t timer_id) +{ + return upper_timers_thread.get(timer_id); +} + + +void mac::start_pcap(srslte::mac_pcap* pcap_) +{ + pcap = pcap_; + // Set pcap in all UEs for UL messages + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + ue *u = iter->second; + u->start_pcap(pcap); + } +} + +/******************************************************** + * + * RLC interface + * + *******************************************************/ +int mac::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) +{ + if (ue_db.count(rnti)) { + return scheduler.dl_rlc_buffer_state(rnti, lc_id, tx_queue, retx_queue); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } +} + +int mac::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t* cfg) +{ + if (ue_db.count(rnti)) { + return scheduler.bearer_ue_cfg(rnti, lc_id, cfg); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } +} + +int mac::bearer_ue_rem(uint16_t rnti, uint32_t lc_id) +{ + if (ue_db.count(rnti)) { + return scheduler.bearer_ue_rem(rnti, lc_id); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } +} + +void mac::phy_config_enabled(uint16_t rnti, bool enabled) +{ + scheduler.phy_config_enabled(rnti, enabled); +} + +// Update UE configuration +int mac::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t* cfg) +{ + if (ue_db.count(rnti)) { + + // Add RNTI to the PHY (pregerate signals) now instead of after PRACH + if (!ue_db[rnti]->is_phy_added) { + ue_db[rnti]->is_phy_added = true; + Info("Registering rnti=0x%x to PHY...\n", rnti); + // Register new user in PHY + if (phy_h->add_rnti(rnti)) { + Error("Registering new ue rnti=0x%x to PHY\n", rnti); + } + Info("Done registering rnti=0x%x to PHY...\n", rnti); + } + + // Update Scheduler configuration + if (scheduler.ue_cfg(rnti, cfg)) { + Error("Registering new UE rnti=0x%x to SCHED\n", rnti); + return -1; + } + return 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } +} + +// Removes UE from DB +int mac::ue_rem(uint16_t rnti) +{ + if (ue_db.count(rnti)) { + scheduler.ue_rem(rnti); + phy_h->rem_rnti(rnti); + delete ue_db[rnti]; + ue_db.erase(rnti); + Info("User rnti=0x%x removed from MAC/PHY\n", rnti); + return 0; + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } +} + +int mac::cell_cfg(sched_interface::cell_cfg_t* cell_cfg) +{ + return scheduler.cell_cfg(cell_cfg); +} + +void mac::get_metrics(mac_metrics_t metrics[ENB_METRICS_MAX_USERS]) +{ + int cnt=0; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + ue *u = iter->second; + u->metrics_read(&metrics[cnt]); + cnt++; + } +} + + + + +/******************************************************** + * + * PHY interface + * + *******************************************************/ + +void mac::rl_failure(uint16_t rnti) +{ + if (ue_db.count(rnti)) { + uint32_t nof_fails = ue_db[rnti]->rl_failure(); + if (nof_fails >= (uint32_t) args.link_failure_nof_err && args.link_failure_nof_err > 0) { + Info("Detected PUSCH failure for rnti=0x%x\n", rnti); + rrc_h->rl_failure(rnti); + ue_db[rnti]->rl_failure_reset(); + } + } else { + Error("User rnti=0x%x not found\n", rnti); + } +} + +void mac::rl_ok(uint16_t rnti) +{ + if (ue_db.count(rnti)) { + ue_db[rnti]->rl_failure_reset(); + } else { + Error("User rnti=0x%x not found\n", rnti); + } +} + +int mac::ack_info(uint32_t tti, uint16_t rnti, bool ack) +{ + log_h->step(tti); + uint32_t nof_bytes = scheduler.dl_ack_info(tti, rnti, ack); + ue_db[rnti]->metrics_tx(ack, nof_bytes); + + if (ack) { + if (nof_bytes > 64) { // do not count RLC status messages only + rrc_h->set_activity_user(rnti); + log_h->debug("DL activity rnti=0x%x, n_bytes=%d\n", rnti, nof_bytes); + } + } + return 0; +} + +int mac::crc_info(uint32_t tti, uint16_t rnti, uint32_t nof_bytes, bool crc) +{ + log_h->step(tti); + + if (ue_db.count(rnti)) { + ue_db[rnti]->set_tti(tti); + + ue_db[rnti]->metrics_rx(crc, nof_bytes); + + // push the pdu through the queue if received correctly + if (crc) { + ue_db[rnti]->push_pdu(tti, nof_bytes); + pdu_process_thread.notify(); + if (nof_bytes > 64) { // do not count RLC status messages only + rrc_h->set_activity_user(rnti); + log_h->debug("UL activity rnti=0x%x, n_bytes=%d\n", rnti, nof_bytes); + } + } else { + ue_db[rnti]->deallocate_pdu(tti); + } + + return scheduler.ul_crc_info(tti, rnti, crc); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } +} + +int mac::cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +{ + log_h->step(tti); + + if (ue_db.count(rnti)) { + scheduler.dl_cqi_info(tti, rnti, cqi_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } + return 0; +} + +int mac::snr_info(uint32_t tti, uint16_t rnti, float snr) +{ + log_h->step(tti); + + if (ue_db.count(rnti)) { + uint32_t cqi = srslte_cqi_from_snr(snr); + scheduler.ul_cqi_info(tti, rnti, cqi, 0); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } + return 0; +} + +int mac::sr_detected(uint32_t tti, uint16_t rnti) +{ + log_h->step(tti); + + if (ue_db.count(rnti)) { + scheduler.ul_sr_info(tti, rnti); + } else { + Error("User rnti=0x%x not found\n", rnti); + return -1; + } + return 0; +} + +int mac::rach_detected(uint32_t tti, uint32_t preamble_idx, uint32_t time_adv) +{ + log_h->step(tti); + + // Find empty slot for pending rars + uint32_t ra_id=0; + while(pending_rars[ra_id].temp_crnti && ra_idconfig(last_rnti, cell.nof_prb, &scheduler, rrc_h, rlc_h, log_h); + + // Set PCAP if available + if (pcap) { + ue_db[last_rnti]->start_pcap(pcap); + } + // Save RA info + pending_rars[ra_id].preamble_idx = preamble_idx; + pending_rars[ra_id].ta_cmd = time_adv; + pending_rars[ra_id].temp_crnti = last_rnti; + + // Add new user to the scheduler so that it can RX/TX SRB0 + sched_interface::ue_cfg_t uecfg; + bzero(&uecfg, sizeof(sched_interface::ue_cfg_t)); + uecfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + if (scheduler.ue_cfg(last_rnti, &uecfg)) { + Error("Registering new user rnti=0x%x to SCHED\n", last_rnti); + return -1; + } + + // Register new user in RRC + rrc_h->add_user(last_rnti); + + // Trigger scheduler RACH + scheduler.dl_rach_info(tti, ra_id, last_rnti, 7); + + log_h->info("RACH: tti=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", + tti, preamble_idx, time_adv, last_rnti); + log_h->console("RACH: tti=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n", + tti, preamble_idx, time_adv, last_rnti); + + // Increae RNTI counter + last_rnti++; + if (last_rnti >= 60000) { + last_rnti = 70; + } + return 0; +} + +int mac::get_dl_sched(uint32_t tti, dl_sched_t *dl_sched_res) +{ + log_step_dl(tti); + + if (!started) { + return 0; + } + + if (!dl_sched_res) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Run scheduler with current info + sched_interface::dl_sched_res_t sched_result; + bzero(&sched_result, sizeof(sched_interface::dl_sched_res_t)); + if (scheduler.dl_sched(tti, &sched_result) < 0) { + Error("Running scheduler\n"); + return SRSLTE_ERROR; + } + + int n = 0; + + // Copy data grants + for (uint32_t i=0;isched_grants[n].rnti = rnti; + memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.data[i].dci, sizeof(srslte_ra_dl_dci_t)); + memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.data[i].dci_location, sizeof(srslte_dci_location_t)); + + dl_sched_res->sched_grants[n].softbuffer = ue_db[rnti]->get_tx_softbuffer(sched_result.data[i].dci.harq_process); + + // Get PDU if it's a new transmission + if (sched_result.data[i].nof_pdu_elems > 0) { + dl_sched_res->sched_grants[n].data = ue_db[rnti]->generate_pdu(sched_result.data[i].pdu, + sched_result.data[i].nof_pdu_elems, + sched_result.data[i].tbs); + srslte_softbuffer_tx_reset_tbs(dl_sched_res->sched_grants[n].softbuffer, sched_result.data[i].tbs); + + if (pcap) { + pcap->write_dl_crnti(dl_sched_res->sched_grants[n].data, sched_result.data[i].tbs, rnti, true, tti); + } + + } else { + dl_sched_res->sched_grants[n].data = NULL; + } + n++; + } + + // Copy RAR grants + for (uint32_t i=0;isched_grants[n].rnti = sched_result.rar[i].rarnti; + memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.rar[i].dci, sizeof(srslte_ra_dl_dci_t)); + memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.rar[i].dci_location, sizeof(srslte_dci_location_t)); + + // Set softbuffer (there are no retx in RAR but a softbuffer is required) + dl_sched_res->sched_grants[n].softbuffer = &rar_softbuffer_tx; + srslte_softbuffer_tx_reset_tbs(&rar_softbuffer_tx, sched_result.rar[i].tbs); // TBS is usually 54-bit + + // Assemble PDU + dl_sched_res->sched_grants[n].data = assemble_rar(sched_result.rar[i].grants, sched_result.rar[i].nof_grants, i, sched_result.rar[i].tbs); + + + if (pcap) { + pcap->write_dl_ranti(dl_sched_res->sched_grants[n].data, sched_result.data[i].tbs, dl_sched_res->sched_grants[n].rnti, true, tti); + } + + n++; + } + + // Copy SI and Paging grants + for (uint32_t i=0;isched_grants[n].rnti = (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH ) ? SRSLTE_SIRNTI : SRSLTE_PRNTI; + memcpy(&dl_sched_res->sched_grants[n].grant, &sched_result.bc[i].dci, sizeof(srslte_ra_dl_dci_t)); + memcpy(&dl_sched_res->sched_grants[n].location, &sched_result.bc[i].dci_location, sizeof(srslte_dci_location_t)); + + // Set softbuffer + if (sched_result.bc[i].type == sched_interface::dl_sched_bc_t::BCCH) { + dl_sched_res->sched_grants[n].softbuffer = &bcch_softbuffer_tx[sched_result.bc[i].index]; + if (sched_result.bc[i].dci.rv_idx == 0) { + srslte_softbuffer_tx_reset_tbs(dl_sched_res->sched_grants[n].softbuffer, sched_result.bc[i].tbs*8); + } + dl_sched_res->sched_grants[n].data = assemble_si(sched_result.bc[i].index); +#ifdef WRITE_SIB_PCAP + if (pcap) { + pcap->write_dl_sirnti(dl_sched_res->sched_grants[n].data, sched_result.bc[i].tbs, true, tti); + } +#endif + } else { + dl_sched_res->sched_grants[n].softbuffer = &pcch_softbuffer_tx; + srslte_softbuffer_tx_reset_tbs(dl_sched_res->sched_grants[n].softbuffer, sched_result.bc[i].tbs*8); + dl_sched_res->sched_grants[n].data = pcch_payload_buffer; + rlc_h->read_pdu_pcch(pcch_payload_buffer, pcch_payload_buffer_len); + + if (pcap) { + pcap->write_dl_pch(dl_sched_res->sched_grants[n].data, sched_result.bc[i].tbs, true, tti); + } + } + + n++; + } + + dl_sched_res->nof_grants = n; + + // Number of CCH symbols + dl_sched_res->cfi = sched_result.cfi; + + return SRSLTE_SUCCESS; +} + +uint8_t* mac::assemble_rar(sched_interface::dl_sched_rar_grant_t* grants, uint32_t nof_grants, int rar_idx, uint32_t pdu_len) +{ + uint8_t grant_buffer[64]; + if (pdu_len < rar_payload_len) { + srslte::rar_pdu *pdu = &rar_pdu_msg[rar_idx]; + pdu->init_tx(rar_payload[rar_idx], pdu_len); + for (uint32_t i=0;inew_subh()) { + /* Search pending RAR */ + int idx = grants[i].ra_id; + pdu->get()->set_rapid(pending_rars[idx].preamble_idx); + pdu->get()->set_ta_cmd(pending_rars[idx].ta_cmd); + pdu->get()->set_temp_crnti(pending_rars[idx].temp_crnti); + pdu->get()->set_sched_grant(grant_buffer); + bzero(&pending_rars[idx], sizeof(pending_rar_t)); + } + } + pdu->write_packet(rar_payload[rar_idx]); + return rar_payload[rar_idx]; + } else { + Error("Assembling RAR: pdu_len > rar_payload_len (%d>%d)\n", pdu_len, rar_payload_len); + return NULL; + } +} + +uint8_t* mac::assemble_si(uint32_t index) +{ + rlc_h->read_pdu_bcch_dlsch(index, bcch_dlsch_payload); + return bcch_dlsch_payload; +} + +int mac::get_ul_sched(uint32_t tti, ul_sched_t *ul_sched_res) +{ + + log_step_ul(tti); + + if (!started) { + return 0; + } + + if (!ul_sched_res) { + return SRSLTE_ERROR_INVALID_INPUTS; + } + + // Run scheduler with current info + sched_interface::ul_sched_res_t sched_result; + bzero(&sched_result, sizeof(sched_interface::ul_sched_res_t)); + if (scheduler.ul_sched(tti, &sched_result)<0) { + Error("Running scheduler\n"); + return SRSLTE_ERROR; + } + + // Copy DCI grants + ul_sched_res->nof_grants = 0; + int n = 0; + for (uint32_t i=0;i 0) { + // Get UE + uint16_t rnti = sched_result.pusch[i].rnti; + + // Copy grant info + ul_sched_res->sched_grants[n].rnti = rnti; + ul_sched_res->sched_grants[n].current_tx_nb = sched_result.pusch[i].current_tx_nb; + ul_sched_res->sched_grants[n].needs_pdcch = sched_result.pusch[i].needs_pdcch; + memcpy(&ul_sched_res->sched_grants[n].grant, &sched_result.pusch[i].dci, sizeof(srslte_ra_ul_dci_t)); + memcpy(&ul_sched_res->sched_grants[n].location, &sched_result.pusch[i].dci_location, sizeof(srslte_dci_location_t)); + + ul_sched_res->sched_grants[n].softbuffer = ue_db[rnti]->get_rx_softbuffer(tti); + + if (sched_result.pusch[n].current_tx_nb == 0) { + srslte_softbuffer_rx_reset_tbs(ul_sched_res->sched_grants[n].softbuffer, sched_result.pusch[i].tbs*8); + } + ul_sched_res->sched_grants[n].data = ue_db[rnti]->request_buffer(tti, sched_result.pusch[i].tbs); + ul_sched_res->nof_grants++; + n++; + + } else { + Warning("Grant %d for rnti=0x%x has zero TBS\n", i, sched_result.pusch[i].rnti); + } + } + + // Copy PHICH actions + for (uint32_t i=0;iphich[i].ack = sched_result.phich[i].phich == sched_interface::ul_sched_phich_t::ACK; + ul_sched_res->phich[i].rnti = sched_result.phich[i].rnti; + } + ul_sched_res->nof_phich = sched_result.nof_phich_elems; + return SRSLTE_SUCCESS; +} + +void mac::log_step_ul(uint32_t tti) +{ + int tti_ul = tti-8; + if (tti_ul < 0) { + tti_ul += 10240; + } + log_h->step(tti_ul); +} + +void mac::log_step_dl(uint32_t tti) +{ + int tti_dl = tti-4; + if (tti_dl < 0) { + tti_dl += 10240; + } + log_h->step(tti_dl); +} + +void mac::tti_clock() +{ + upper_timers_thread.tti_clock(); +} + +/******************************************************** + * + * Class to run upper-layer timers with normal priority + * + *******************************************************/ +void mac::upper_timers::run_thread() +{ + running=true; + ttisync.set_producer_cntr(0); + ttisync.resync(); + while(running) { + ttisync.wait(); + timers_db.step_all(); + } +} +srslte::timers::timer* mac::upper_timers::get(uint32_t timer_id) +{ + return timers_db.get(timer_id%MAC_NOF_UPPER_TIMERS); +} + +uint32_t mac::upper_timers::get_unique_id() +{ + return timers_db.get_unique_id(); +} + +void mac::upper_timers::stop() +{ + running=false; + ttisync.increase(); + wait_thread_finish(); +} +void mac::upper_timers::reset() +{ + timers_db.stop_all(); +} + +void mac::upper_timers::tti_clock() +{ + ttisync.increase(); +} + + + +/******************************************************** + * + * Class that runs a thread to process DL MAC PDUs from + * DEMU unit + * + *******************************************************/ +mac::pdu_process::pdu_process(pdu_process_handler *h) +{ + handler = h; + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cvar, NULL); + have_data = false; + start(MAC_PDU_THREAD_PRIO); +} + +void mac::pdu_process::stop() +{ + pthread_mutex_lock(&mutex); + running = false; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + + wait_thread_finish(); +} + +void mac::pdu_process::notify() +{ + pthread_mutex_lock(&mutex); + have_data = true; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); +} + +void mac::pdu_process::run_thread() +{ + running = true; + while(running) { + have_data = handler->process_pdus(); + if (!have_data) { + pthread_mutex_lock(&mutex); + while(!have_data && running) { + pthread_cond_wait(&cvar, &mutex); + } + pthread_mutex_unlock(&mutex); + } + } +} + +bool mac::process_pdus() +{ + bool ret = false; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + ue *u = iter->second; + uint16_t rnti = iter->first; + ret = ret | u->process_pdus(); + } + return ret; +} + + + + + +} + + + diff --git a/srsenb/src/mac/scheduler.cc b/srsenb/src/mac/scheduler.cc new file mode 100644 index 000000000..5a9a8a6e5 --- /dev/null +++ b/srsenb/src/mac/scheduler.cc @@ -0,0 +1,938 @@ + +#include + +#include "srslte/srslte.h" +#include "srslte/common/pdu.h" +#include "mac/scheduler.h" + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +namespace srsenb { + + +/******************************************************* + * + * Initialization and sched configuration functions + * + *******************************************************/ +sched::sched() +{ + log_h = NULL; + pthread_mutex_init(&mutex, NULL); + reset(); +} + +void sched::init(rrc_interface_mac *rrc_, srslte::log* log) +{ + sched_cfg.pdsch_max_mcs = 28; + sched_cfg.pdsch_mcs = -1; + sched_cfg.pusch_max_mcs = 28; + sched_cfg.pusch_mcs = -1; + sched_cfg.nof_ctrl_symbols = 3; + log_h = log; + rrc = rrc_; + reset(); +} + +int sched::reset() +{ + bzero(pending_rar, sizeof(sched_rar_t)*SCHED_MAX_PENDING_RAR); + bzero(pending_sibs, sizeof(sched_sib_t)*MAX_SIBS); + ue_db.clear(); + configured = false; + return 0; +} + +void sched::set_sched_cfg(sched_interface::sched_args_t* sched_cfg_) +{ + if (sched_cfg_) { + memcpy(&sched_cfg, sched_cfg_, sizeof(sched_args_t)); + } +} + +void sched::set_metric(sched::metric_dl* dl_metric_, sched::metric_ul* ul_metric_) +{ + dl_metric = dl_metric_; + ul_metric = ul_metric_; +} + +int sched::cell_cfg(sched_interface::cell_cfg_t* cell_cfg) +{ + // Basic cell config checks + if (cell_cfg->si_window_ms == 0) { + Error("SCHED: Invalid si-window length 0 ms\n"); + return -1; + } + + pthread_mutex_lock(&mutex); + + memcpy(&cfg, cell_cfg, sizeof(sched_interface::cell_cfg_t)); + + // Get DCI locations + srslte_regs_init(®s, cfg.cell); + + P = srslte_ra_type0_P(cfg.cell.nof_prb); + si_n_rbg = 4/P; + rar_n_rb = 3; + nof_rbg = (uint32_t) ceil((float) cfg.cell.nof_prb/P); + + // Compute Common locations for DCI for each CFI + for (uint32_t cfi=0;cfi<3;cfi++) { + generate_cce_location(®s, &common_locations[cfi], cfi+1); + } + + // Compute UE locations for RA-RNTI + for (int cfi=0;cfi<3;cfi++) { + for (int sf_idx=0;sf_idx<10;sf_idx++) { + uint16_t ra_rnti = 1+sf_idx; + generate_cce_location(®s, &rar_locations[cfi][sf_idx], cfi+1, sf_idx); + } + } + configured = true; + + pthread_mutex_unlock(&mutex); + + return 0; +} + + +/******************************************************* + * + * FAPI-like main sched interface. Wrappers to UE object + * + *******************************************************/ + +int sched::ue_cfg(uint16_t rnti, sched_interface::ue_cfg_t *ue_cfg) +{ + pthread_mutex_lock(&mutex); + + // Add or config user + ue_db[rnti].set_cfg(rnti, ue_cfg, &cfg, ®s, log_h); + ue_db[rnti].set_max_mcs(sched_cfg.pusch_max_mcs, sched_cfg.pdsch_max_mcs); + ue_db[rnti].set_fixed_mcs(sched_cfg.pusch_mcs, sched_cfg.pdsch_mcs); + + pthread_mutex_unlock(&mutex); + return 0; +} + +int sched::ue_rem(uint16_t rnti) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db.erase(rnti); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +bool sched::ue_exists(uint16_t rnti) +{ + return (ue_db.count(rnti) == 1); +} + +void sched::phy_config_enabled(uint16_t rnti, bool enabled) +{ + pthread_mutex_lock(&mutex); + if (ue_db.count(rnti)) { + ue_db[rnti].phy_config_enabled(current_tti, enabled); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_mutex_unlock(&mutex); +} + +int sched::bearer_ue_cfg(uint16_t rnti, uint32_t lc_id, sched_interface::ue_bearer_cfg_t *cfg) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].set_bearer_cfg(lc_id, cfg); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::bearer_ue_rem(uint16_t rnti, uint32_t lc_id) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].rem_bearer(lc_id); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +uint32_t sched::get_dl_buffer(uint16_t rnti) +{ + pthread_mutex_lock(&mutex); + uint32_t ret = 0; + if (ue_db.count(rnti)) { + ret = ue_db[rnti].get_pending_dl_new_data(current_tti); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_mutex_unlock(&mutex); + return ret; +} + +uint32_t sched::get_ul_buffer(uint16_t rnti) +{ + pthread_mutex_lock(&mutex); + uint32_t ret = 0; + if (ue_db.count(rnti)) { + ret = ue_db[rnti].get_pending_ul_new_data(current_tti); + } else { + Error("User rnti=0x%x not found\n", rnti); + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::dl_rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].dl_buffer_state(lc_id, tx_queue, retx_queue); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::dl_mac_buffer_state(uint16_t rnti, uint32_t ce_code) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].mac_buffer_state(ce_code); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::dl_ack_info(uint32_t tti, uint16_t rnti, bool ack) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ret = ue_db[rnti].set_ack_info(tti, ack); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::ul_crc_info(uint32_t tti, uint16_t rnti, bool crc) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].set_ul_crc(tti, crc); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::dl_cqi_info(uint32_t tti, uint16_t rnti, uint32_t cqi_value) +{ + pthread_mutex_lock(&mutex); + int ret = 0; + if (ue_db.count(rnti)) { + ue_db[rnti].set_dl_cqi(tti, cqi_value); + } else { + Error("User rnti=0x%x not found\n", rnti); + ret = -1; + } + pthread_mutex_unlock(&mutex); + return ret; +} + +int sched::dl_rach_info(uint32_t tti, uint32_t ra_id, uint16_t rnti, uint32_t estimated_size) +{ + for (int i=0;i 0) { + x = (i-1)*cfg.si_window_ms; + sf = x%10; + } + if ((sfn%(cfg.sibs[i].period_rf)) == x/10 && sf_idx == sf) { + pending_sibs[i].is_in_window = true; + pending_sibs[i].window_start = current_tti; + pending_sibs[i].n_tx = 0; + } + } else { + if (i > 0) { + if (srslte_tti_interval(current_tti, pending_sibs[i].window_start) > cfg.si_window_ms) { + pending_sibs[i].is_in_window = false; + pending_sibs[i].window_start = 0; + } + } else { + // SIB1 is always in window + if (pending_sibs[0].n_tx == 4) { + pending_sibs[0].n_tx = 0; + } + } + } + } + } + + for (int i=0;i si_n_rbg) + { + uint32_t nof_tx = 4; + if (i > 0) { + if (cfg.si_window_ms <= 10) { + nof_tx = 1; + } else if (cfg.si_window_ms <= 20) { + nof_tx = 2; + } else if (cfg.si_window_ms <= 30) { + nof_tx = 3; + } else { + nof_tx = 4; + } + } + uint32_t n_sf = (current_tti-pending_sibs[i].window_start); + if ((i == 0 && (sfn%2) == 0 && sf_idx == 5) || + (i > 0 && n_sf >= (cfg.si_window_ms/nof_tx)*pending_sibs[i].n_tx && sf_idx==1)) + { + uint32_t rv = get_rvidx(pending_sibs[i].n_tx); + + // Try to allocate DCI first + if (generate_dci(&bc[nof_bc_elems].dci_location, &common_locations[current_cfi-1], bc_aggr_level)) { + if (generate_format1a(start_rbg*P, si_n_rbg*P, cfg.sibs[i].len, rv, &bc[nof_bc_elems].dci) >= 0) { + bc[nof_bc_elems].index = i; + bc[nof_bc_elems].type = sched_interface::dl_sched_bc_t::BCCH; + bc[nof_bc_elems].tbs = cfg.sibs[i].len; + + Debug("SCHED: SIB%d, start_rb=%d, n_rb=%d, rv=%d, len=%d, period=%d\n", + i+1, start_rbg*P, si_n_rbg*P, rv, cfg.sibs[i].len, cfg.sibs[i].period_rf); + + pending_sibs[i].n_tx++; + + nof_bc_elems++; + avail_rbg -= si_n_rbg; + start_rbg += si_n_rbg; + } else { + Error("Could not allocate DCI Format1A for SIB%d, len=%d\n", i+1, cfg.sibs[i].len); + } + } else { + Warning("SCHED: Could not schedule DCI for SIB=%d, L=%d\n", i+1, bc_aggr_level); + } + } + } + } + + // Schedule Paging + if (rrc) { + uint32_t paging_payload = 0; + if (rrc->is_paging_opportunity(current_tti, &paging_payload)) { + if (avail_rbg > si_n_rbg && paging_payload) + { + if (generate_dci(&bc[nof_bc_elems].dci_location, &common_locations[current_cfi-1], bc_aggr_level)) { + int tbs = generate_format1a(start_rbg*P, si_n_rbg*P, paging_payload, 0, &bc[nof_bc_elems].dci); + if (tbs > 0) { + + bc[nof_bc_elems].type = sched_interface::dl_sched_bc_t::PCCH; + bc[nof_bc_elems].tbs = tbs; + nof_bc_elems++; + + Info("SCHED: PCH start_rb=%d, tbs=%d\n", start_rbg, tbs); + + avail_rbg -= si_n_rbg; + start_rbg += si_n_rbg; + + } + } + } + } + } + + return nof_bc_elems; +} + + +// Schedules RAR +int sched::dl_sched_rar(dl_sched_rar_t rar[MAX_RAR_LIST]) +{ + + int nof_rar_elems = 0; + for (uint32_t i=0;i 0 && avail_rbg >= rar_n_rb) + { + /* Check if we are still within the RAR window, otherwise discard it */ + if (current_tti <= (pending_rar[i].rar_tti + cfg.prach_rar_window + 3)%10240 && current_tti >= pending_rar[i].rar_tti + 3) + { + // Try to schedule DCI for this RAR + if (generate_dci(&rar[nof_rar_elems].dci_location, &rar_locations[current_cfi-1][sf_idx], rar_aggr_level)) { + + /* Find all pending RARs with same transmission TTI */ + uint32_t tti = pending_rar[i].rar_tti; + uint32_t rar_sfidx = (tti+1)%10; + uint32_t buf_rar = 0; + uint32_t nof_grants = 0; + for (int j=0;jinfo("SCHED: RAR, ra_id=%d, rnti=0x%x, rarnti_idx=%d, start_rb=%d, n_rb=%d, rar_grant_rba=%d, rar_grant_mcs=%d\n", + pending_rar[j].ra_id, pending_rar[j].rnti, rar_sfidx, start_rbg*P, rar_n_rb, + rar[nof_rar_elems].grants[nof_grants].grant.rba, + rar[nof_rar_elems].grants[nof_grants].grant.trunc_mcs); + } else { + log_h->warning("Only 1 RA is responded at a time. Found %d for TTI=%d\n", nof_grants+1, tti); + } + nof_grants++; + } + } + + rar[nof_rar_elems].nof_grants = nof_grants; + rar[nof_rar_elems].rarnti = rar_sfidx; + + if (generate_format1a(start_rbg*P, rar_n_rb, buf_rar, 0, &rar[nof_rar_elems].dci) >= 0) { + rar[nof_rar_elems].tbs = buf_rar; + nof_rar_elems++; + avail_rbg -= rar_n_rb; + start_rbg += rar_n_rb; + } else { + Error("SCHED: Allocating Format1A grant\n"); + } + + } else { + log_h->console("SCHED: Could not schedule DCI for RAR tti=%d, L=%d\n", pending_rar[i].rar_tti, rar_aggr_level); + } + } else { + log_h->console("SCHED: Could not transmit RAR within the window (RA TTI=%d, Window=%d, Now=%d)\n", + pending_rar[i].rar_tti, cfg.prach_rar_window, current_tti); + pending_rar[i].buf_rar = 0; + pending_rar[i].rar_tti = 0; + } + } + } + return nof_rar_elems; +} + +// Schedules data to users +int sched::dl_sched_data(dl_sched_data_t data[MAX_DATA_LIST]) +{ + uint32_t nof_ctrl_symbols = (cfg.cell.nof_prb<10)?(current_cfi+1):current_cfi; + dl_metric->new_tti(ue_db, start_rbg, avail_rbg, nof_ctrl_symbols, current_tti); + + int nof_data_elems = 0; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + dl_harq_proc *h = dl_metric->get_user_allocation(user); + + if (h) { + // Try to schedule DCI first + if (generate_dci(&data[nof_data_elems].dci_location, + user->get_locations(current_cfi, sf_idx), + user->get_aggr_level(srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT1, cfg.cell.nof_prb, cfg.cell.nof_ports)), user)) + { + bool is_newtx = h->is_empty(); + int tbs = user->generate_format1(h, &data[nof_data_elems], current_tti, current_cfi); + if (tbs >= 0) { + log_h->info("SCHED: DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, n_rtx=%d, tbs=%d, buffer=%d\n", + !is_newtx?"retx":"tx", rnti, h->get_id(), h->get_rbgmask(), + data[nof_data_elems].dci_location.L, data[nof_data_elems].dci_location.ncce, h->nof_retx(), + tbs, user->get_pending_dl_new_data(current_tti)); + nof_data_elems++; + } else { + log_h->warning("SCHED: Error DL %s rnti=0x%x, pid=%d, mask=0x%x, dci=%d,%d, tbs=%d, buffer=%d\n", + !is_newtx?"retx":"tx", rnti, h->get_id(), h->get_rbgmask(), + data[nof_data_elems].dci_location.L, data[nof_data_elems].dci_location.ncce, + tbs, user->get_pending_dl_new_data(current_tti)); + } + } else { + Warning("SCHED: Could not schedule DL DCI for rnti=0x%x, pid=%d\n", rnti, h->get_id()); + } + } + } + + return nof_data_elems; +} + +// Downlink Scheduler +int sched::dl_sched(uint32_t tti, sched_interface::dl_sched_res_t* sched_result) +{ + if (!configured) { + return 0; + } + pthread_mutex_lock(&mutex); + + /* If ul_sched() not yet called this tti, reset CCE state */ + if (current_tti != tti) { + bzero(used_cce, MAX_CCE*sizeof(bool)); + } + + /* Initialize variables */ + current_tti = tti; + sfn = tti/10; + sf_idx = tti%10; + avail_rbg = nof_rbg; + start_rbg = 0; + current_cfi = sched_cfg.nof_ctrl_symbols; + bc_aggr_level = 2; + rar_aggr_level = 2; + bzero(sched_result, sizeof(sched_interface::dl_sched_res_t)); + + /* Schedule Broadcast data */ + sched_result->nof_bc_elems += dl_sched_bc(sched_result->bc); + + /* Schedule RAR */ + sched_result->nof_rar_elems += dl_sched_rar(sched_result->rar); + + /* Schedule pending RLC data */ + sched_result->nof_data_elems += dl_sched_data(sched_result->data); + + /* Set CFI */ + sched_result->cfi = current_cfi; + + pthread_mutex_unlock(&mutex); + return 0; +} + +// Uplink sched +int sched::ul_sched(uint32_t tti, srsenb::sched_interface::ul_sched_res_t* sched_result) +{ + if (!configured) { + return 0; + } + + pthread_mutex_lock(&mutex); + + /* If dl_sched() not yet called this tti (this tti is +4ms advanced), reset CCE state */ + if ((current_tti+4)%10240 != tti) { + bzero(used_cce, MAX_CCE*sizeof(bool)); + } + + /* Initialize variables */ + current_tti = tti; + sfn = tti/10; + if (tti > 4) { + sf_idx = (tti-4)%10; + } else { + sf_idx = (tti+10240-4)%10; + } + int nof_dci_elems = 0; + int nof_phich_elems = 0; + + // current_cfi is set in dl_sched() + bzero(sched_result, sizeof(sched_interface::ul_sched_res_t)); + + // Get HARQ process for this TTI + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + ul_harq_proc *h = user->get_ul_harq(current_tti); + + /* Indicate PHICH acknowledgment if needed */ + if (h->has_pending_ack()) { + sched_result->phich[nof_phich_elems].phich = h->get_ack()?ul_sched_phich_t::ACK:ul_sched_phich_t::NACK; + sched_result->phich[nof_phich_elems].rnti = rnti; + nof_phich_elems++; + } + } + + ul_metric->new_tti(ue_db, cfg.cell.nof_prb, current_tti); + + // Update available allocation if there's a pending RAR + if (pending_msg3[tti%10].enabled) { + ul_harq_proc::ul_alloc_t msg3 = {pending_msg3[tti%10].n_prb, pending_msg3[tti%10].L}; + ul_metric->update_allocation(msg3); + } + + // Allocate PUCCH resources + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + uint32_t prb_idx[2] = {0, 0}; + uint32_t L = 0; + if (user->get_pucch_sched(current_tti, prb_idx, &L)) { + for (int i=0;i<2;i++) { + ul_harq_proc::ul_alloc_t pucch = {prb_idx[i], L}; + ul_metric->update_allocation(pucch); + } + } + } + + // Now allocate PUSCH + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + ul_harq_proc *h = NULL; + + // Check if there are pending Msg3 transmissions + bool is_rar = false; + if (pending_msg3[tti%10].enabled && pending_msg3[tti%10].rnti == rnti) { + h = user->get_ul_harq(tti); + if (h) { + ul_harq_proc::ul_alloc_t alloc; + alloc.L = pending_msg3[tti%10].L; + alloc.RB_start = pending_msg3[tti%10].n_prb; + h->set_alloc(alloc); + h->set_rar_mcs(pending_msg3[tti%10].mcs); + is_rar = true; + pending_msg3[tti%10].enabled = false; + } else { + Warning("No HARQ pid available for transmission of Msg3\n"); + } + } else { + h = ul_metric->get_user_allocation(user); + } + if (h) + { + ul_harq_proc::ul_alloc_t alloc = h->get_alloc(); + bool is_newtx = h->is_empty(); + bool needs_pdcch = !h->is_adaptive_retx() && !is_rar; + + // Set number of retx + if (is_newtx) { + if (is_rar) { + h->set_max_retx(cfg.maxharq_msg3tx); + } else { + h->set_max_retx(user->get_max_retx()); + } + } + + // Generate PDCCH except for RAR and non-adaptive retx + if (needs_pdcch) { + uint32_t aggr_level = user->get_aggr_level(srslte_dci_format_sizeof(SRSLTE_DCI_FORMAT0, cfg.cell.nof_prb, cfg.cell.nof_ports)); + if (!generate_dci(&sched_result->pusch[nof_dci_elems].dci_location, + user->get_locations(current_cfi, sf_idx), + aggr_level)) + { + log_h->warning("SCHED: Could not schedule UL DCI rnti=0x%x, pid=%d, L=%d\n", + rnti, h->get_id(), aggr_level); + sched_result->pusch[nof_dci_elems].needs_pdcch = false; + } else { + sched_result->pusch[nof_dci_elems].needs_pdcch = true; + } + } else { + sched_result->pusch[nof_dci_elems].needs_pdcch = false; + } + + // Generate grant unless DCI could not be generated and was required + if (sched_result->pusch[nof_dci_elems].needs_pdcch == needs_pdcch) { + uint32_t pending_data_before = user->get_pending_ul_new_data(current_tti); + if (user->generate_format0(h, &sched_result->pusch[nof_dci_elems], current_tti, user->needs_cqi(tti, true)) > 0) + { + + if (is_newtx) { + // Un-trigger SR + user->unset_sr(); + } + + log_h->info("SCHED: %s %s rnti=0x%x, pid=%d, dci=%d,%d, grant=%d,%d, n_rtx=%d, tbs=%d, bsr=%d (%d)\n", + is_rar?"RAR":"UL", + is_newtx?"tx":"retx", + rnti, h->get_id(), + sched_result->pusch[nof_dci_elems].dci_location.L, sched_result->pusch[nof_dci_elems].dci_location.ncce, + alloc.RB_start, alloc.L, h->nof_retx(), sched_result->pusch[nof_dci_elems].tbs, + user->get_pending_ul_new_data(current_tti),pending_data_before); + + nof_dci_elems++; + } else { + log_h->warning("SCHED: Error %s %s rnti=0x%x, pid=%d, dci=%d,%d, grant=%d,%d, tbs=%d, bsr=%d\n", + is_rar?"RAR":"UL", + is_newtx?"tx":"retx", + rnti, h->get_id(), + sched_result->pusch[nof_dci_elems].dci_location.L, sched_result->pusch[nof_dci_elems].dci_location.ncce, + alloc.RB_start, alloc.L, sched_result->pusch[nof_dci_elems].tbs, + user->get_pending_ul_new_data(current_tti)); + } + } + } + } + + sched_result->nof_dci_elems = nof_dci_elems; + sched_result->nof_phich_elems = nof_phich_elems; + + pthread_mutex_unlock(&mutex); + + return SRSLTE_SUCCESS; +} + + +/******************************************************* + * + * Helper functions + * + *******************************************************/ + +void sched::generate_cce_location(srslte_regs_t *regs_, sched_ue::sched_dci_cce_t* location, + uint32_t cfi, uint32_t sf_idx, uint16_t rnti) +{ + srslte_dci_location_t loc[64]; + uint32_t nloc = 0; + if (rnti == 0) { + nloc = srslte_pdcch_common_locations_ncce(srslte_regs_pdcch_ncce(regs_, cfi), + loc, 64); + } else { + nloc = srslte_pdcch_ue_locations_ncce(srslte_regs_pdcch_ncce(regs_, cfi), + loc, 64, sf_idx, rnti); + } + + for (uint32_t l=0;l<=3;l++) { + int n=0; + for (uint32_t i=0;icce_start[l][n] = loc[i].ncce; + n++; + } + } + location->nof_loc[l] = n; + } + +} + + +#define NCCE(L) (1<nof_loc[aggr_level] && !allocated) { + uint32_t ncce = locations->cce_start[aggr_level][ncand]; + bool used = false; + if (user) { + used = user->pucch_sr_collision(current_tti, ncce); + } + for (int j=0;jL = aggr_level; + sched_location->ncce = locations->cce_start[aggr_level][ncand]; + } + + return allocated; +} + +int sched::generate_format1a(uint32_t rb_start, uint32_t l_crb, uint32_t tbs_bytes, uint32_t rv, srslte_ra_dl_dci_t *dci) +{ + /* Calculate I_tbs for this TBS */ + int tbs = tbs_bytes*8; + int i; + int mcs = -1; + for (i=0;i<27;i++) { + if (srslte_ra_tbs_from_idx(i, 2) >= tbs) { + dci->type2_alloc.n_prb1a = srslte_ra_type2_t::SRSLTE_RA_TYPE2_NPRB1A_2; + mcs = i; + tbs = srslte_ra_tbs_from_idx(i, 2); + break; + } else if (srslte_ra_tbs_from_idx(i, 3) >= tbs) { + dci->type2_alloc.n_prb1a = srslte_ra_type2_t::SRSLTE_RA_TYPE2_NPRB1A_3; + mcs = i; + tbs = srslte_ra_tbs_from_idx(i, 3); + break; + } + } + if (i == 28) { + Error("Can't allocate Format 1A for TBS=%d\n", tbs); + return -1; + } + + dci->alloc_type = SRSLTE_RA_ALLOC_TYPE2; + dci->type2_alloc.mode = srslte_ra_type2_t::SRSLTE_RA_TYPE2_LOC; + dci->type2_alloc.L_crb = l_crb; + dci->type2_alloc.RB_start = rb_start; + dci->harq_process = 0; + dci->mcs_idx = mcs; + dci->rv_idx = rv; + dci->tb_en[0] = true; + dci->tb_en[1] = false; + + return tbs; +} + + + + + +} + + diff --git a/srsenb/src/mac/scheduler_harq.cc b/srsenb/src/mac/scheduler_harq.cc new file mode 100644 index 000000000..e567568d0 --- /dev/null +++ b/srsenb/src/mac/scheduler_harq.cc @@ -0,0 +1,238 @@ + +#include +#include + +#include "srslte/srslte.h" +#include "srslte/common/pdu.h" +#include "mac/scheduler.h" + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +namespace srsenb { + +/****************************************************** + * + * These classes manage the HARQ Processes. + * There is a common class and two child classes for UL and DL. + * + ******************************************************/ + +void harq_proc::config(uint32_t id_, uint32_t max_retx_, srslte::log* log_h_) +{ + log_h = log_h_; + id = id_; + max_retx = max_retx_; + ndi = false; +} + +void harq_proc::set_max_retx(uint32_t max_retx_) { + log_h->debug("Set max_retx=%d pid=%d\n", max_retx_, id); + max_retx = max_retx_; +} + +uint32_t harq_proc::get_id() +{ + return id; +} + +void harq_proc::reset() +{ + active = false; + ack = true; + ack_received = false; + n_rtx = 0; + tti = 0; + last_mcs = -1; + last_tbs = -1; + tx_cnt = 0; +} + +bool harq_proc::is_empty() +{ + return !active || (active && ack && ack_received); +} + +bool harq_proc::has_pending_retx_common() +{ + return !ack && n_rtx < max_retx; +} + +uint32_t harq_proc::get_tti() +{ + return tti; +} + +bool harq_proc::get_ack() +{ + return ack; +} + +void harq_proc::set_ack(bool ack_) +{ + ack = ack_; + ack_received = true; + log_h->debug("ACK=%d received pid=%d, n_rtx=%d, max_retx=%d\n", ack_, id, n_rtx, max_retx); + if (n_rtx >= max_retx) { + Warning("SCHED: discarting TB pid=%d, tti=%d, maximum number of retx exceeded (%d)\n", id, tti, max_retx); + active = false; + } +} + +void harq_proc::new_tx_common(uint32_t tti_, int mcs, int tbs) +{ + reset(); + ndi = !ndi; + tti = tti_; + tx_cnt++; + last_mcs = mcs; + last_tbs = tbs; + + if (max_retx) { + active = true; + } else { + active = false; // Can reuse this process if no retx are allowed + } +} + +void harq_proc::new_retx(uint32_t tti_, int *mcs, int *tbs) +{ + ack_received = false; + tti = tti_; + n_rtx++; + if (mcs) { + *mcs = last_mcs; + } + if (tbs) { + *tbs = last_tbs; + } +} + +uint32_t harq_proc::nof_tx() +{ + return tx_cnt; +} + +uint32_t harq_proc::nof_retx() +{ + return n_rtx; +} + +bool harq_proc::get_ndi() +{ + return ndi; +} + +/****************************************************** + * UE::DL HARQ class * + ******************************************************/ + +void dl_harq_proc::new_tx(uint32_t tti, int mcs, int tbs, uint32_t n_cce_) +{ + n_cce = n_cce_; + new_tx_common(tti, mcs, tbs); +} + +uint32_t dl_harq_proc::get_n_cce() +{ + return n_cce; +} + +uint32_t dl_harq_proc::get_rbgmask() +{ + return rbgmask; +} + +void dl_harq_proc::set_rbgmask(uint32_t new_mask) +{ + rbgmask = new_mask; +} + +bool dl_harq_proc::has_pending_retx(uint32_t current_tti) +{ + return srslte_tti_interval(current_tti, tti) >= 8 && has_pending_retx_common(); +} + +int dl_harq_proc::get_tbs() +{ + return last_tbs; +} + + + +/****************************************************** + * UE::UL HARQ class * + ******************************************************/ + +ul_harq_proc::ul_alloc_t ul_harq_proc::get_alloc() +{ + return allocation; +} + +void ul_harq_proc::set_alloc(ul_harq_proc::ul_alloc_t alloc) +{ + is_adaptive = false; + memcpy(&allocation, &alloc, sizeof(ul_alloc_t)); +} + +void ul_harq_proc::same_alloc() +{ + is_adaptive = true; +} + +bool ul_harq_proc::is_adaptive_retx() +{ + return is_adaptive; +} + +void ul_harq_proc::new_tx(uint32_t tti_, int mcs, int tbs) +{ + need_ack = true; + new_tx_common(tti_, mcs, tbs); + pending_data = tbs; +} + + +bool ul_harq_proc::has_pending_ack() +{ + bool ret = need_ack; + + // Reset if already received a positive ACK + if (active && ack) { + active = false; + } + if (!active) { + pending_data = 0; + need_ack = false; + } + return ret; +} + +uint32_t ul_harq_proc::get_pending_data() +{ + return pending_data; +} + +void ul_harq_proc::set_rar_mcs(uint32_t mcs) +{ + rar_mcs = mcs; + has_rar_mcs = true; +} + +bool ul_harq_proc::get_rar_mcs(int *mcs) +{ + if (has_rar_mcs) { + if (mcs) { + *mcs = (int) rar_mcs; + } + has_rar_mcs = false; + return true; + } + return false; +} + + + +} \ No newline at end of file diff --git a/srsenb/src/mac/scheduler_metric.cc b/srsenb/src/mac/scheduler_metric.cc new file mode 100644 index 000000000..520ce8339 --- /dev/null +++ b/srsenb/src/mac/scheduler_metric.cc @@ -0,0 +1,310 @@ + +#include + +#include "srslte/srslte.h" +#include "mac/scheduler_metric.h" + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +namespace srsenb { + + + +/***************************************************************** + * + * Downlink Metric + * + *****************************************************************/ + +uint32_t dl_metric_rr::calc_rbg_mask(bool mask[MAX_RBG]) +{ + // Build RBG bitmask + uint32_t rbg_bitmask = 0; + for (uint32_t n=0;n 0) { + if ((mask & 1) == 1) { + count++; + } + mask >>= 1; + } + return count; +} + +uint32_t dl_metric_rr::get_required_rbg(sched_ue *user, uint32_t tti) +{ + dl_harq_proc *h = user->get_pending_dl_harq(tti); + if (h) { + return count_rbg(h->get_rbgmask()); + } + uint32_t pending_data = user->get_pending_dl_new_data(current_tti); + return user->get_required_prb_dl(pending_data, nof_ctrl_symbols); +} + +void dl_metric_rr::new_tti(std::map &ue_db, uint32_t start_rb, uint32_t nof_rb, uint32_t nof_ctrl_symbols_, uint32_t tti) +{ + + total_rb = start_rb+nof_rb; + for (uint32_t i=0;i::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + if (user->get_pending_dl_new_data(current_tti) || user->get_pending_dl_harq(current_tti)) { + user->ue_idx = nof_users_with_data; + nof_users_with_data++; + } + } +} + +bool dl_metric_rr::new_allocation(uint32_t nof_rbg, uint32_t *rbgmask) { + bool mask_bit[MAX_RBG]; + bzero(mask_bit, sizeof(bool)*MAX_RBG); + + for (uint32_t i=0;i 0;i++) { + if (used_rb[i]) { + mask_bit[i] = false; + } else { + mask_bit[i] = true; + nof_rbg--; + } + } + if (rbgmask) { + *rbgmask = calc_rbg_mask(mask_bit); + } + return (nof_rbg == 0); +} + +void dl_metric_rr::update_allocation(uint32_t new_mask) { + used_rb_mask |= new_mask; + for (uint32_t n=0;nget_pending_dl_new_data(current_tti); + dl_harq_proc *h = user->get_pending_dl_harq(current_tti); + + // Time-domain RR scheduling + if (pending_data || h) { + if (nof_users_with_data) { + if (nof_users_with_data == 2) { + } + if ((current_tti%nof_users_with_data) != user->ue_idx) { + return NULL; + } + } + } + + // Schedule retx if we have space + if (h) { + uint32_t retx_mask = h->get_rbgmask(); + // If can schedule the same mask, do it + if (!allocation_is_valid(retx_mask)) { + update_allocation(retx_mask); + return h; + } + // If not, try to find another mask in the current tti + uint32_t nof_rbg = count_rbg(retx_mask); + if (nof_rbg < available_rb) { + if (new_allocation(nof_rbg, &retx_mask)) { + update_allocation(retx_mask); + h->set_rbgmask(retx_mask); + return h; + } + } + } + // If could not schedule the reTx, or there wasn't any pending retx, find an empty PID + h = user->get_empty_dl_harq(); + if (h) { + // Allocate resources based on pending data + if (pending_data) { + uint32_t pending_rb = user->get_required_prb_dl(pending_data, nof_ctrl_symbols); + uint32_t newtx_mask = 0; + new_allocation(pending_rb, &newtx_mask); + if (newtx_mask) { + update_allocation(newtx_mask); + h->set_rbgmask(newtx_mask); + return h; + } + } + } + return NULL; +} + + + + + + + + + +/***************************************************************** + * + * Uplink Metric + * + *****************************************************************/ + +void ul_metric_rr::new_tti(std::map &ue_db, uint32_t nof_rb_, uint32_t tti) +{ + current_tti = tti; + nof_rb = nof_rb_; + available_rb = nof_rb_; + bzero(used_rb, nof_rb*sizeof(bool)); + + nof_users_with_data = 0; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + sched_ue *user = (sched_ue*) &iter->second; + if (user->get_pending_ul_new_data(current_tti) || !user->get_ul_harq(current_tti)->is_empty()) { + user->ue_idx = nof_users_with_data; + nof_users_with_data++; + } + } + +} + +bool ul_metric_rr::allocation_is_valid(ul_harq_proc::ul_alloc_t alloc) +{ + if (alloc.RB_start+alloc.L > nof_rb) { + return false; + } + for (uint32_t n=alloc.RB_start;nL < L;n++) { + if (!used_rb[n] && alloc->L == 0) { + alloc->RB_start = n; + } + if (!used_rb[n]) { + alloc->L++; + } else if (alloc->L > 0) { + // avoid edges + if (n < 3) { + alloc->RB_start = 0; + alloc->L = 0; + } else { + break; + } + } + } + if (!alloc->L) { + return 0; + } + + // Make sure L is allowed by SC-FDMA modulation + while (!srslte_dft_precoding_valid_prb(alloc->L)) { + alloc->L--; + } + return alloc->L == L; +} + +void ul_metric_rr::update_allocation(ul_harq_proc::ul_alloc_t alloc) +{ + if (alloc.L > available_rb) { + return; + } + if (alloc.RB_start + alloc.L > nof_rb) { + return; + } + for (uint32_t n=alloc.RB_start;nget_pending_ul_new_data(current_tti); + ul_harq_proc *h = user->get_ul_harq(current_tti); + + if (pending_data || !h->is_empty()) { + if (nof_users_with_data) { + if ((current_tti%nof_users_with_data) != user->ue_idx) { + return NULL; + } + } + } + + // Schedule retx if we have space + + if (!h->is_empty()) { + + ul_harq_proc::ul_alloc_t alloc = h->get_alloc(); + + // If can schedule the same mask, do it + if (allocation_is_valid(alloc)) { + update_allocation(alloc); + h->same_alloc(); + return h; + } + + // If not, try to find another mask in the current tti + if (new_allocation(alloc.L, &alloc)) { + update_allocation(alloc); + h->set_alloc(alloc); + return h; + } + } + // If could not schedule the reTx, or there wasn't any pending retx, find an empty PID + if (h->is_empty()) { + // Allocate resources based on pending data + if (pending_data) { + uint32_t pending_rb = user->get_required_prb_ul(pending_data); + ul_harq_proc::ul_alloc_t alloc; + new_allocation(pending_rb, &alloc); + if (alloc.L) { + update_allocation(alloc); + h->set_alloc(alloc); + return h; + } + } + } + return NULL; +} + + + +} diff --git a/srsenb/src/mac/scheduler_ue.cc b/srsenb/src/mac/scheduler_ue.cc new file mode 100644 index 000000000..b101e8150 --- /dev/null +++ b/srsenb/src/mac/scheduler_ue.cc @@ -0,0 +1,762 @@ + +#include +#include + +#include "srslte/srslte.h" +#include "srslte/common/pdu.h" +#include "mac/scheduler_ue.h" +#include "mac/scheduler.h" + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +/****************************************************** + * UE class * + ******************************************************/ + +namespace srsenb { + + +/******************************************************* + * + * Initialization and configuration functions + * + *******************************************************/ + +sched_ue::sched_ue() +{ + reset(); +} + +void sched_ue::set_cfg(uint16_t rnti_, sched_interface::ue_cfg_t *cfg_, sched_interface::cell_cfg_t *cell_cfg, + srslte_regs_t *regs, srslte::log *log_h_) +{ + reset(); + + rnti = rnti_; + log_h = log_h_; + memcpy(&cell, &cell_cfg->cell, sizeof(srslte_cell_t)); + + max_mcs_dl = 28; + max_mcs_ul = 28; + + if (cfg_) { + memcpy(&cfg, cfg_, sizeof(sched_interface::ue_cfg_t)); + } + + Info("SCHED: Added user rnti=0x%x\n", rnti); + for (int i=0;i 4) { + len -= 4; + } + if (lcid < sched_interface::MAX_LC) { + if (bearer_is_ul(&lch[lcid])) { + if (lch[lcid].bsr > (int) len) { + lch[lcid].bsr -= len; + } else { + lch[lcid].bsr = 0; + } + } + } +} + +void sched_ue::set_ul_crc(uint32_t tti, bool crc_res) +{ + get_ul_harq(tti)->set_ack(crc_res); +} + +void sched_ue::set_dl_cqi(uint32_t tti, uint32_t cqi) +{ + dl_cqi = cqi; + dl_cqi_tti = tti; +} + +void sched_ue::set_ul_cqi(uint32_t tti, uint32_t cqi, uint32_t ul_ch_code) +{ + ul_cqi = cqi; + ul_cqi_tti = tti; +} + +void sched_ue::tpc_inc() { + if (power_headroom > 0) { + next_tpc_pusch = 3; + next_tpc_pucch = 3; + } + log_h->info("SCHED: Set TCP=%d for rnti=0x%x\n", next_tpc_pucch, rnti); +} + +void sched_ue::tpc_dec() { + next_tpc_pusch = 0; + next_tpc_pucch = 0; + log_h->info("SCHED: Set TCP=%d for rnti=0x%x\n", next_tpc_pucch, rnti); +} + +/******************************************************* + * + * Functions used to generate DCI grants + * + *******************************************************/ + + +// Generates a Format1 grant +int sched_ue::generate_format1(dl_harq_proc *h, + sched_interface::dl_sched_data_t *data, + uint32_t tti, + uint32_t cfi) +{ + srslte_ra_dl_dci_t *dci = &data->dci; + bzero(dci, sizeof(srslte_ra_dl_dci_t)); + + uint32_t sf_idx = tti%10; + + int mcs = 0; + int tbs = 0; + + dci->alloc_type = SRSLTE_RA_ALLOC_TYPE0; + dci->type0_alloc.rbg_bitmask = h->get_rbgmask(); + + + // If this is the first transmission for this UE, make room for MAC Contention Resolution ID + bool need_conres_ce = false; + if (is_first_dl_tx()) { + need_conres_ce = true; + } + if (h->is_empty()) { + + uint32_t req_bytes = get_pending_dl_new_data(tti); + + uint32_t nof_prb = format1_count_prb(h->get_rbgmask(), cell.nof_prb); + srslte_ra_dl_grant_t grant; + srslte_ra_dl_dci_to_grant_prb_allocation(dci, &grant, cell.nof_prb); + uint32_t nof_ctrl_symbols = cfi+(cell.nof_prb<10?1:0); + uint32_t nof_re = srslte_ra_dl_grant_nof_re(&grant, cell, sf_idx, nof_ctrl_symbols); + if (fixed_mcs_dl < 0) { + tbs = alloc_tbs(dl_cqi, nof_prb, nof_re, req_bytes, max_mcs_dl, &mcs); + } else { + tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_dl), nof_prb); + mcs = fixed_mcs_dl; + } + + h->new_tx(tti, mcs, tbs, data->dci_location.ncce); + + Debug("SCHED: Alloc format1 new mcs=%d, tbs=%d, nof_prb=%d, req_bytes=%d\n", mcs, tbs, nof_prb, req_bytes); + } else { + h->new_retx(tti, &mcs, &tbs); + Debug("SCHED: Alloc format1 previous mcs=%d, tbs=%d\n", mcs, tbs); + } + + // Allocate MAC ConRes CE + if (need_conres_ce) { + data->pdu[0].lcid = srslte::sch_subh::CON_RES_ID; + data->nof_pdu_elems++; + Info("SCHED: Added MAC Contention Resolution CE for rnti=0x%x\n", rnti); + } + + int rem_tbs = tbs; + int x = 0; + do { + x = alloc_pdu(rem_tbs, &data->pdu[data->nof_pdu_elems]); + rem_tbs -= x; + if (x) { + data->nof_pdu_elems++; + } + } while(rem_tbs > 0 && x > 0); + + data->rnti = rnti; + + if (tbs > 0) { + dci->harq_process = h->get_id(); + dci->mcs_idx = mcs; + dci->rv_idx = sched::get_rvidx(h->nof_retx()); + dci->ndi = h->get_ndi(); + dci->tpc_pucch = next_tpc_pucch; + next_tpc_pucch = 1; + data->tbs = tbs; + dci->tb_en[0] = true; + dci->tb_en[1] = false; + } + return tbs; +} + + +int sched_ue::generate_format0(ul_harq_proc *h, + sched_interface::ul_sched_data_t *data, + uint32_t tti, + bool cqi_request) +{ + srslte_ra_ul_dci_t *dci = &data->dci; + bzero(dci, sizeof(srslte_ra_ul_dci_t)); + + int mcs = 0; + int tbs = 0; + + ul_harq_proc::ul_alloc_t allocation = h->get_alloc(); + + if (h->get_rar_mcs(&mcs)) { + tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(mcs), allocation.L)/8; + h->new_tx(tti, mcs, tbs); + } else if (h->is_empty()) { + + uint32_t req_bytes = get_pending_ul_new_data(tti); + + uint32_t N_srs = 0; + uint32_t nof_re = (2*(SRSLTE_CP_NSYMB(cell.cp)-1) - N_srs)*allocation.L*SRSLTE_NRE; + if (fixed_mcs_ul < 0) { + tbs = alloc_tbs(ul_cqi, allocation.L, nof_re, req_bytes, max_mcs_ul, &mcs); + } else { + tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(fixed_mcs_ul), allocation.L); + mcs = fixed_mcs_ul; + } + + h->new_tx(tti, mcs, tbs); + + } else { + h->new_retx(tti, &mcs, NULL); + tbs = srslte_ra_tbs_from_idx(srslte_ra_tbs_idx_from_mcs(mcs), allocation.L)/8; + } + + data->rnti = rnti; + data->tbs = tbs; + + if (tbs > 0) { + dci->type2_alloc.L_crb = allocation.L; + dci->type2_alloc.RB_start = allocation.RB_start; + dci->mcs_idx = mcs; + dci->rv_idx = sched::get_rvidx(h->nof_retx()); + dci->ndi = h->get_ndi(); + dci->cqi_request = cqi_request; + dci->freq_hop_fl = srslte_ra_ul_dci_t::SRSLTE_RA_PUSCH_HOP_DISABLED; + dci->tpc_pusch = next_tpc_pusch; + next_tpc_pusch = 1; + } + + return tbs; +} + +/******************************************************* + * + * Functions used by scheduler or scheduler metric objects + * + *******************************************************/ + +bool sched_ue::bearer_is_ul(ue_bearer_t *lch) { + return lch->cfg.direction == sched_interface::ue_bearer_cfg_t::UL || lch->cfg.direction == sched_interface::ue_bearer_cfg_t::BOTH; +} + +bool sched_ue::bearer_is_dl(ue_bearer_t *lch) { + return lch->cfg.direction == sched_interface::ue_bearer_cfg_t::DL || lch->cfg.direction == sched_interface::ue_bearer_cfg_t::BOTH; +} + +uint32_t sched_ue::get_max_retx() { + return cfg.maxharq_tx; +} + +bool sched_ue::is_first_dl_tx() +{ + for (int i=0;i 0) { + return false; + } + } + return true; +} + +bool sched_ue::needs_cqi(uint32_t tti, bool will_be_sent) +{ + bool ret = false; + if (phy_config_dedicated_enabled && + cfg.aperiodic_cqi_period && + get_pending_dl_new_data(tti) > 0) + { + uint32_t interval = srslte_tti_interval(tti, dl_cqi_tti); + bool needscqi = interval >= cfg.aperiodic_cqi_period; + if (needscqi) { + uint32_t interval_sent = srslte_tti_interval(tti, cqi_request_tti); + if (interval_sent >= 16) { + if (will_be_sent) { + cqi_request_tti = tti; + } + Debug("SCHED: Needs_cqi, last_sent=%d, will_be_sent=%d\n", cqi_request_tti, will_be_sent); + ret = true; + } + } + } + return ret; +} + +uint32_t sched_ue::get_pending_dl_new_data(uint32_t tti) +{ + uint32_t pending_data = 0; + for (int i=0;i pending_ul_data) { + pending_data -= pending_ul_data; + } else { + pending_data = 0; + } + return pending_data; +} + +uint32_t sched_ue::get_pending_ul_old_data() +{ + uint32_t pending_data = 0; + for (int i=0;i 0) { + nbytes = tbs; + } else if (tbs < 0) { + return 0; + } + } + return n; +} + +uint32_t sched_ue::get_required_prb_ul(uint32_t req_bytes) +{ + int mcs = 0; + int tbs = 0; + uint32_t nbytes = 0; + uint32_t N_srs = 0; + + uint32_t n = 0; + if (req_bytes == 0) { + return 0; + } + + for (n=1;n 0) { + nbytes = tbs; + } + } + + while (!srslte_dft_precoding_valid_prb(n)) { + n++; + } + + return n; +} + +bool sched_ue::is_sr_triggered() +{ + return sr; +} + +/* Gets HARQ process with oldest pending retx */ +dl_harq_proc* sched_ue::get_pending_dl_harq(uint32_t tti) +{ + int oldest_idx=-1; + uint32_t oldest_tti = 0; + for (int i=0;i oldest_tti) { + oldest_idx = i; + oldest_tti = x; + } + } + } + if (oldest_idx >= 0) { + return &dl_harq[oldest_idx]; + } else { + return NULL; + } +} + +dl_harq_proc* sched_ue::get_empty_dl_harq() +{ + for (int i=0;i max_coderate); + Debug("SCHED: CQI=%d, l=%d, nof_bits=%d, coderate=%.2f, max_coderate=%.2f\n", dl_cqi, l, nof_bits, coderate, max_coderate); + return l; +} + +sched_ue::sched_dci_cce_t* sched_ue::get_locations(uint32_t cfi, uint32_t sf_idx) +{ + if (cfi > 0 && cfi <= 3) { + return &dci_locations[cfi-1][sf_idx]; + } else { + Error("SCHED: Invalid CFI=%d\n", cfi); + return &dci_locations[0][sf_idx]; + } +} + +/* Allocates first available RLC PDU */ +int sched_ue::alloc_pdu(int tbs_bytes, sched_interface::dl_sched_pdu_t* pdu) +{ + // TODO: Implement lcid priority (now lowest index is lowest priority) + int x = 0; + int i = 0; + for (i=0;ilcid = i-1; + pdu->nbytes = x; + Debug("SCHED: Allocated lcid=%d, nbytes=%d, tbs_bytes=%d\n", pdu->lcid, pdu->nbytes, tbs_bytes); + } + return x; +} + +uint32_t sched_ue::format1_count_prb(uint32_t bitmask, uint32_t cell_nof_prb) { + uint32_t P = srslte_ra_type0_P(cell_nof_prb); + uint32_t nb = (int) ceilf((float) cell_nof_prb / P); + + uint32_t nof_prb = 0; + for (uint32_t i = 0; i < nb; i++) { + if (bitmask & (1 << (nb - i - 1))) { + for (uint32_t j = 0; j < P; j++) { + if (i*P+j < cell_nof_prb) { + nof_prb++; + } + } + } + } + return nof_prb; +} + +int sched_ue::cqi_to_tbs(uint32_t cqi, uint32_t nof_prb, uint32_t nof_re, uint32_t max_mcs, uint32_t *mcs) { + float max_coderate = srslte_cqi_to_coderate(cqi); + int sel_mcs = max_mcs+1; + float coderate = 99; + int tbs = 0; + + do { + sel_mcs--; + uint32_t tbs_idx = srslte_ra_tbs_idx_from_mcs(sel_mcs); + tbs = srslte_ra_tbs_from_idx(tbs_idx, nof_prb); + coderate = srslte_pdsch_coderate(tbs, nof_re); + } while(sel_mcs > 0 && coderate >= max_coderate); + if (mcs) { + *mcs = (uint32_t) sel_mcs; + } + return tbs; +} + +/* In this scheduler we tend to use all the available bandwidth and select the MCS + * that approximates the minimum between the capacity and the requested rate + */ +int sched_ue::alloc_tbs(uint32_t cqi, + uint32_t nof_prb, + uint32_t nof_re, + uint32_t req_bytes, + uint32_t max_mcs, + int *mcs) +{ + uint32_t sel_mcs = 0; + int tbs = cqi_to_tbs(cqi, nof_prb, nof_re, max_mcs, &sel_mcs)/8; + + /* If less bytes are requested, lower the MCS */ + if (tbs > (int) req_bytes && req_bytes > 0) { + uint32_t req_tbs_idx = srslte_ra_tbs_to_table_idx(req_bytes*8, nof_prb); + uint32_t req_mcs = srslte_ra_mcs_from_tbs_idx(req_tbs_idx); + if (req_mcs < sel_mcs) { + sel_mcs = req_mcs; + tbs = srslte_ra_tbs_from_idx(req_tbs_idx, nof_prb)/8; + } + } + // Avoid the unusual case n_prb=1, mcs=6 tbs=328 (used in voip) + if (nof_prb == 1 && sel_mcs == 6) { + sel_mcs--; + } + + if (mcs && tbs >= 0) { + *mcs = (int) sel_mcs; + } + + return tbs; +} + + +} diff --git a/srsenb/src/mac/ue.cc b/srsenb/src/mac/ue.cc new file mode 100644 index 000000000..b6c5b7403 --- /dev/null +++ b/srsenb/src/mac/ue.cc @@ -0,0 +1,366 @@ +#include +#include + +#include "srslte/interfaces/enb_interfaces.h" +#include "mac/ue.h" + +#define Error(fmt, ...) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + + +namespace srsenb { + +void ue::config(uint16_t rnti_, uint32_t nof_prb, sched_interface *sched_, rrc_interface_mac *rrc_, rlc_interface_mac *rlc_, srslte::log *log_h_) +{ + rnti = rnti_; + rlc = rlc_; + rrc = rrc_; + log_h = log_h_; + sched = sched_; + pdus.init(this, log_h); + + for (int i=0;i 0) { + if (!pending_buffers[tti%NOF_HARQ_PROCESSES]) { + ret = pdus.request(len); + pending_buffers[tti%NOF_HARQ_PROCESSES] = ret; + } else { + log_h->console("Error requesting buffer for pid %d, not pushed yet\n", tti%NOF_HARQ_PROCESSES); + log_h->error("Requesting buffer for pid %d, not pushed yet\n", tti%NOF_HARQ_PROCESSES); + } + } else { + log_h->warning("Requesting buffer for zero bytes\n"); + } + pthread_mutex_unlock(&mutex); + return ret; +} + +bool ue::process_pdus() +{ + return pdus.process_pdus(); +} + +void ue::set_tti(uint32_t tti) { + last_tti = tti; +} + +#include + +void ue::process_pdu(uint8_t* pdu, uint32_t nof_bytes, uint32_t tstamp) +{ + // Unpack ULSCH MAC PDU + mac_msg_ul.init_rx(nof_bytes, true); + mac_msg_ul.parse_packet(pdu); + + if (pcap) { + pcap->write_ul_crnti(pdu, nof_bytes, rnti, true, last_tti); + } + + while(mac_msg_ul.next()) { + assert(mac_msg_ul.get()); + if (mac_msg_ul.get()->is_sdu()) + { + // Route logical channel + log_h->debug_hex(mac_msg_ul.get()->get_sdu_ptr(), mac_msg_ul.get()->get_payload_size(), + "PDU: rnti=0x%x, lcid=%d, %d bytes\n", + rnti, mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_payload_size()); + + + /* In some cases, an uplink transmission with only CQI has all zeros and gets routed to RRC + * Compute the checksum if lcid=0 and avoid routing in that case + */ + bool route_pdu = true; + if (mac_msg_ul.get()->get_sdu_lcid() == 0) { + uint8_t *x = mac_msg_ul.get()->get_sdu_ptr(); + uint32_t sum = 0; + for (uint32_t i=0;iget_payload_size();i++) { + sum += x[i]; + } + if (sum == 0) { + route_pdu = false; + Warning("Received all zero PDU\n"); + } + } + + if (route_pdu) { + rlc->write_pdu(rnti, + mac_msg_ul.get()->get_sdu_lcid(), + mac_msg_ul.get()->get_sdu_ptr(), + mac_msg_ul.get()->get_payload_size()); + } + + // Indicate scheduler to update BSR counters + sched->ul_recv_len(rnti, mac_msg_ul.get()->get_sdu_lcid(), mac_msg_ul.get()->get_payload_size()); + + // Save contention resolution if lcid == 0 + if (mac_msg_ul.get()->get_sdu_lcid() == 0 && route_pdu) { + uint32_t nbytes = srslte::sch_subh::MAC_CE_CONTRES_LEN; + if (mac_msg_ul.get()->get_payload_size() >= nbytes) { + uint8_t *ue_cri_ptr = (uint8_t*) &conres_id; + uint8_t *pkt_ptr = mac_msg_ul.get()->get_sdu_ptr(); // Warning here: we want to include the + for (uint32_t i=0;iget_payload_size()); + } + } + } else { + // Process MAC Control Element + if (!process_ce(mac_msg_ul.get())) { + Warning("Received Subheader with invalid or unkonwn LCID\n"); + } + } + } + + Debug("MAC PDU processed\n"); + +} + +void ue::deallocate_pdu(uint32_t tti) +{ + if (pending_buffers[tti%NOF_HARQ_PROCESSES]) { + pdus.deallocate(pending_buffers[tti%NOF_HARQ_PROCESSES]); + pending_buffers[tti%NOF_HARQ_PROCESSES] = NULL; + } else { + log_h->console("Error deallocating buffer for pid=%d. Not requested\n", tti%NOF_HARQ_PROCESSES); + } +} + +void ue::push_pdu(uint32_t tti, uint32_t len) +{ + if (pending_buffers[tti%NOF_HARQ_PROCESSES]) { + pdus.push(pending_buffers[tti%NOF_HARQ_PROCESSES], len); + pending_buffers[tti%NOF_HARQ_PROCESSES] = NULL; + } else { + log_h->console("Error pushing buffer for pid=%d. Not requested\n", tti%NOF_HARQ_PROCESSES); + } +} + +bool ue::process_ce(srslte::sch_subh *subh) { + uint32_t buff_size[4] = {0, 0, 0, 0}; + uint32_t idx = 0; + float phr = 0; + uint16_t old_rnti = 0; + switch(subh->ce_type()) { + case srslte::sch_subh::PHR_REPORT: + phr = subh->get_phr(); + Info("CE: Received PHR from rnti=0x%x, value=%.0f\n", rnti, phr); + sched->ul_phr(rnti, (int) phr); + metrics_phr(phr); + break; + case srslte::sch_subh::CRNTI: + old_rnti = subh->get_c_rnti(); + Info("CE: Received C-RNTI from temp_rnti=0x%x, rnti=0x%x\n", rnti, old_rnti); + if (sched->ue_exists(old_rnti)) { + rrc->upd_user(rnti, old_rnti); + rnti = old_rnti; + } else { + Error("Updating user C-RNTI: rnti=0x%x already released\n", old_rnti); + } + break; + case srslte::sch_subh::TRUNC_BSR: + case srslte::sch_subh::SHORT_BSR: + case srslte::sch_subh::LONG_BSR: + idx = subh->get_bsr(buff_size); + if (idx > 0) { + // Indicate BSR to scheduler + sched->ul_bsr(rnti, idx, buff_size[idx]); + Info("CE: Received BSR rnti=0x%x, lcid=%d, value=%d\n", rnti, idx, buff_size[idx]); + } else if (idx == 0) { + // TODO: map lcid group to lcid + for (int i=0;i<4;i++) { + sched->ul_bsr(rnti, i, buff_size[i]); + } + Info("CE: Received Long BSR rnti=0x%x, value=%d,%d,%d,%d\n", rnti, + buff_size[0], buff_size[1], buff_size[2], buff_size[3]); + } else { + printf("Error!\n"); + } + break; + case srslte::sch_subh::PADDING: + Debug("CE: Received padding for rnti=0x%x\n", rnti); + break; + default: + Error("CE: Invalid lcid=0x%x\n", subh->ce_type()); + break; + } + return true; +} + + +int ue::read_pdu(uint32_t lcid, uint8_t *payload, uint32_t requested_bytes) +{ + return rlc->read_pdu(rnti, lcid, payload, requested_bytes); +} + +void ue::allocate_sdu(srslte::sch_pdu *pdu, uint32_t lcid, uint32_t total_sdu_len) +{ + int sdu_space = pdu->get_sdu_space(); + if (sdu_space > 0) { + int sdu_len = SRSLTE_MIN(total_sdu_len, (uint32_t) sdu_space); + int n=1; + while(sdu_len > 0 && n > 0) { + if (pdu->new_subh()) { // there is space for a new subheader + log_h->debug("SDU: set_sdu(), lcid=%d, sdu_len=%d, sdu_space=%d\n", lcid, sdu_len, sdu_space); + n = pdu->get()->set_sdu(lcid, sdu_len, this); + if (n > 0) { // new SDU could be added + sdu_len -= n; + log_h->debug("SDU: rnti=0x%x, lcid=%d, nbytes=%d, rem_len=%d\n", + rnti, lcid, n, sdu_len); + } else { + Debug("Could not add SDU lcid=%d nbytes=%d, space=%d\n", lcid, sdu_len, sdu_space); + pdu->del_subh(); + } + } else { + n=0; + } + } + } +} + +void ue::allocate_ce(srslte::sch_pdu *pdu, uint32_t lcid) +{ + switch((srslte::sch_subh::cetype) lcid) { + case srslte::sch_subh::CON_RES_ID: + if (pdu->new_subh()) { + if (pdu->get()->set_con_res_id(conres_id)) { + Info("CE: Added Contention Resolution ID=0x%lx\n", conres_id); + } else { + Error("CE: Setting Contention Resolution ID CE\n"); + } + } else { + Error("CE: Setting Contention Resolution ID CE. No space for a subheader\n"); + } + break; + default: + Error("CE: Allocating CE=0x%x. Not supported\n", lcid); + break; + } +} + +uint8_t* ue::generate_pdu(sched_interface::dl_sched_pdu_t pdu[sched_interface::MAX_RLC_PDU_LIST], + uint32_t nof_pdu_elems, uint32_t grant_size) +{ + uint8_t *ret = NULL; + pthread_mutex_lock(&mutex); + if (rlc) + { + mac_msg_dl.init_tx(tx_payload_buffer, grant_size, false); + for (uint32_t i=0;iget_ul_buffer(rnti); + metrics.dl_buffer = sched->get_dl_buffer(rnti); + + memcpy(metrics_, &metrics, sizeof(mac_metrics_t)); + + phr_counter = 0; + bzero(&metrics, sizeof(mac_metrics_t)); +} + +void ue::metrics_phr(float phr) { + metrics.phr = SRSLTE_VEC_CMA(phr, metrics.phr, phr_counter); + phr_counter++; +} + +void ue::metrics_rx(bool crc, uint32_t tbs) +{ + if (crc) { + metrics.rx_brate += tbs*8; + } else { + metrics.rx_errors++; + } + metrics.rx_pkts++; +} + +void ue::metrics_tx(bool crc, uint32_t tbs) +{ + if (crc) { + metrics.tx_brate += tbs*8; + } else { + metrics.tx_errors++; + } + metrics.tx_pkts++; +} + + +} + diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc new file mode 100644 index 000000000..070c19426 --- /dev/null +++ b/srsenb/src/main.cc @@ -0,0 +1,353 @@ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "enb.h" +#include "metrics_stdout.h" + +using namespace std; +using namespace srsenb; +namespace bpo = boost::program_options; + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ +string config_file; + +void parse_args(all_args_t *args, int argc, char* argv[]) { + + string enb_id; + string cell_id; + string tac; + string mcc; + string mnc; + + // 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"); + common.add_options() + + ("enb.enb_id", bpo::value(&enb_id)->default_value("0x0"), "eNodeB ID") + ("enb.name", bpo::value(&args->enb.s1ap.enb_name)->default_value("srsenb01"), "eNodeB Name") + ("enb.cell_id", bpo::value(&cell_id)->default_value("0x0"), "Cell ID") + ("enb.tac", bpo::value(&tac)->default_value("0x0"), "Tracking Area Code") + ("enb.mcc", bpo::value(&mcc)->default_value("001"), "Mobile Country Code") + ("enb.mnc", bpo::value(&mnc)->default_value("01"), "Mobile Network Code") + ("enb.mme_addr", bpo::value(&args->enb.s1ap.mme_addr)->default_value("127.0.0.1"),"IP address of MME for S1 connnection") + ("enb.gtp_bind_addr", bpo::value(&args->enb.s1ap.gtp_bind_addr)->default_value("192.168.3.1"), "Local IP address to bind for GTP connection") + ("enb.phy_cell_id", bpo::value(&args->enb.pci)->default_value(0), "Physical Cell Identity (PCI)") + ("enb.n_prb", bpo::value(&args->enb.n_prb)->default_value(25), "Number of PRB") + + ("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") + + ("rf.dl_earfcn", bpo::value(&args->rf.dl_earfcn)->default_value(3400), "Downlink EARFCN") + ("rf.ul_earfcn", bpo::value(&args->rf.ul_earfcn)->default_value(0), "Uplink EARFCN (Default based on Downlink EARFCN)") + ("rf.rx_gain", bpo::value(&args->rf.rx_gain)->default_value(50), "Front-end receiver gain") + ("rf.tx_gain", bpo::value(&args->rf.tx_gain)->default_value(70), "Front-end transmitter gain") + ("rf.dl_freq", bpo::value(&args->rf.dl_freq)->default_value(-1), "Downlink Frequency (if positive overrides EARFCN)") + ("rf.ul_freq", bpo::value(&args->rf.ul_freq)->default_value(-1), "Uplink Frequency (if positive overrides EARFCN)") + + ("rf.device_name", bpo::value(&args->rf.device_name)->default_value("auto"), "Front-end device name") + ("rf.device_args", bpo::value(&args->rf.device_args)->default_value("auto"), "Front-end device arguments") + ("rf.time_adv_nsamples", bpo::value(&args->rf.time_adv_nsamples)->default_value("auto"), "Transmission time advance") + ("rf.burst_preamble_us", bpo::value(&args->rf.burst_preamble)->default_value("auto"), "Transmission time advance") + + ("pcap.enable", bpo::value(&args->pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") + ("pcap.filename", bpo::value(&args->pcap.filename)->default_value("ue.pcap"), "MAC layer capture filename") + + ("gui.enable", bpo::value(&args->gui.enable)->default_value(false), "Enable GUI plots") + + ("log.phy_level", bpo::value(&args->log.phy_level), "PHY log level") + ("log.phy_hex_limit", bpo::value(&args->log.phy_hex_limit), "PHY log hex dump limit") + ("log.mac_level", bpo::value(&args->log.mac_level), "MAC log level") + ("log.mac_hex_limit", bpo::value(&args->log.mac_hex_limit), "MAC log hex dump limit") + ("log.rlc_level", bpo::value(&args->log.rlc_level), "RLC log level") + ("log.rlc_hex_limit", bpo::value(&args->log.rlc_hex_limit), "RLC log hex dump limit") + ("log.pdcp_level", bpo::value(&args->log.pdcp_level), "PDCP log level") + ("log.pdcp_hex_limit",bpo::value(&args->log.pdcp_hex_limit), "PDCP log hex dump limit") + ("log.rrc_level", bpo::value(&args->log.rrc_level), "RRC log level") + ("log.rrc_hex_limit", bpo::value(&args->log.rrc_hex_limit), "RRC log hex dump limit") + ("log.gtpu_level", bpo::value(&args->log.gtpu_level), "GTPU log level") + ("log.gtpu_hex_limit",bpo::value(&args->log.gtpu_hex_limit), "GTPU log hex dump limit") + ("log.s1ap_level", bpo::value(&args->log.s1ap_level), "S1AP log level") + ("log.s1ap_hex_limit",bpo::value(&args->log.s1ap_hex_limit), "S1AP log hex dump limit") + + ("log.all_level", bpo::value(&args->log.all_level)->default_value("info"), "ALL log level") + ("log.all_hex_limit", bpo::value(&args->log.all_hex_limit)->default_value(32), "ALL log hex dump limit") + + ("log.filename", bpo::value(&args->log.filename)->default_value("/tmp/ue.log"),"Log filename") + + /* MCS section */ + ("scheduler.pdsch_mcs", + bpo::value(&args->expert.mac.sched.pdsch_mcs)->default_value(-1), + "Optional fixed PDSCH MCS (ignores reported CQIs if specified)") + ("scheduler.pdsch_max_mcs", + bpo::value(&args->expert.mac.sched.pdsch_max_mcs)->default_value(-1), + "Optional PDSCH MCS limit") + ("scheduler.pusch_mcs", + bpo::value(&args->expert.mac.sched.pusch_mcs)->default_value(-1), + "Optional fixed PUSCH MCS (ignores reported CQIs if specified)") + ("scheduler.pusch_max_mcs", + bpo::value(&args->expert.mac.sched.pusch_max_mcs)->default_value(16), + "Optional PUSCH MCS limit") + ("scheduler.nof_ctrl_symbols", + bpo::value(&args->expert.mac.sched.nof_ctrl_symbols)->default_value(3), + "Number of control symbols") + + + /* Expert section */ + + ("expert.metrics_period_secs", + bpo::value(&args->expert.metrics_period_secs)->default_value(1.0), + "Periodicity for metrics in seconds") + + ("expert.pregenerate_signals", + bpo::value(&args->expert.phy.pregenerate_signals)->default_value(false), + "Pregenerate uplink signals after attach. Improves CPU performance.") + + ("expert.pusch_max_its", + bpo::value(&args->expert.phy.pusch_max_its)->default_value(4), + "Maximum number of turbo decoder iterations") + + ("expert.tx_amplitude", + bpo::value(&args->expert.phy.tx_amplitude)->default_value(0.8), + "Transmit amplitude factor") + + ("expert.nof_phy_threads", + bpo::value(&args->expert.phy.nof_phy_threads)->default_value(2), + "Number of PHY threads") + + ("expert.link_failure_nof_err", + bpo::value(&args->expert.mac.link_failure_nof_err)->default_value(10), + "Number of PUSCH failures after which a radio-link failure is triggered") + + ("expert.max_prach_offset_us", + bpo::value(&args->expert.phy.max_prach_offset_us)->default_value(30), + "Maximum allowed RACH offset (in us)") + + ("expert.equalizer_mode", + bpo::value(&args->expert.phy.equalizer_mode)->default_value("mmse"), + "Equalizer mode") + + ("expert.estimator_fil_w", + bpo::value(&args->expert.phy.estimator_fil_w)->default_value(0.1), + "Chooses the coefficients for the 3-tap channel estimator centered filter.") + + ("expert.rrc_inactivity_timer", + bpo::value(&args->expert.rrc_inactivity_timer)->default_value(5000), + "Inactivity timer in ms") + + + ("rf_calibration.tx_corr_dc_gain", bpo::value(&args->rf_cal.tx_corr_dc_gain)->default_value(0.0), "TX DC offset gain correction") + ("rf_calibration.tx_corr_dc_phase", bpo::value(&args->rf_cal.tx_corr_dc_phase)->default_value(0.0), "TX DC offset phase correction") + ("rf_calibration.tx_corr_iq_i", bpo::value(&args->rf_cal.tx_corr_iq_i)->default_value(0.0), "TX IQ imbalance inphase correction") + ("rf_calibration.tx_corr_iq_q", bpo::value(&args->rf_cal.tx_corr_iq_q)->default_value(0.0), "TX IQ imbalance quadrature correction") + + ; + + // Positional options - config file location + bpo::options_description position("Positional options"); + position.add_options() + ("config_file", bpo::value< string >(&config_file), "eNodeB configuration file") + ; + bpo::positional_options_description p; + p.add("config_file", -1); + + // these options are allowed on the command line + bpo::options_description cmdline_options; + cmdline_options.add(common).add(position).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).positional(p).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); + } + + // print version number and exit + // print version number and exit + if (vm.count("version")) { + cout << "Version " << + srslte_get_version_major() << "." << + srslte_get_version_minor() << "." << + srslte_get_version_patch() << endl; + exit(0); + } + + // no config file given - print usage and exit + if (!vm.count("config_file")) { + cout << "Error: Configuration file not provided" << endl; + cout << "Usage: " << argv[0] << " [OPTIONS] config_file" << endl << endl; + exit(0); + } else { + cout << "Reading configuration file " << config_file << "..." << endl; + ifstream conf(config_file.c_str(), ios::in); + if(conf.fail()) { + cout << "Failed to read configuration file " << config_file << " - exiting" << endl; + exit(1); + } + bpo::store(bpo::parse_config_file(conf, common), vm); + bpo::notify(vm); + } + + // Convert hex strings + { + std::stringstream sstr; + sstr << std::hex << vm["enb.enb_id"].as(); + sstr >> args->enb.s1ap.enb_id; + } + { + std::stringstream sstr; + sstr << std::hex << vm["enb.cell_id"].as(); + uint16_t tmp; // Need intermediate uint16_t as uint8_t is treated as char + sstr >> tmp; + args->enb.s1ap.cell_id = tmp; + } + { + std::stringstream sstr; + sstr << std::hex << vm["enb.tac"].as(); + sstr >> args->enb.s1ap.tac; + } + + // Convert MCC/MNC strings + if(!srslte::string_to_mcc(mcc, &args->enb.s1ap.mcc)) { + cout << "Error parsing enb.mcc:" << mcc << " - must be a 3-digit string." << endl; + } + if(!srslte::string_to_mnc(mnc, &args->enb.s1ap.mnc)) { + cout << "Error parsing enb.mnc:" << mnc << " - must be a 2 or 3-digit string." << endl; + } + + + // Apply all_level to any unset layers + if (vm.count("log.all_level")) { + if(!vm.count("log.phy_level")) { + args->log.phy_level = args->log.all_level; + } + if(!vm.count("log.mac_level")) { + args->log.mac_level = args->log.all_level; + } + if(!vm.count("log.rlc_level")) { + args->log.rlc_level = args->log.all_level; + } + if(!vm.count("log.pdcp_level")) { + args->log.pdcp_level = args->log.all_level; + } + if(!vm.count("log.rrc_level")) { + args->log.rrc_level = args->log.all_level; + } + if(!vm.count("log.gtpu_level")) { + args->log.gtpu_level = args->log.all_level; + } + if(!vm.count("log.s1ap_level")) { + args->log.s1ap_level = args->log.all_level; + } + } + + // Apply all_hex_limit to any unset layers + if (vm.count("log.all_hex_limit")) { + if(!vm.count("log.phy_hex_limit")) { + args->log.phy_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.mac_hex_limit")) { + args->log.mac_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.rlc_hex_limit")) { + args->log.rlc_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.pdcp_hex_limit")) { + args->log.pdcp_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.rrc_hex_limit")) { + args->log.rrc_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.gtpu_hex_limit")) { + args->log.gtpu_hex_limit = args->log.all_hex_limit; + } + if(!vm.count("log.s1ap_hex_limit")) { + args->log.s1ap_hex_limit = args->log.all_hex_limit; + } + } +} + +static bool running = true; +static bool do_metrics = false; + +void sig_int_handler(int signo) +{ + running = false; +} + +void *input_loop(void *m) +{ + metrics_stdout *metrics = (metrics_stdout*) m; + char key; + while(running) { + cin >> key; + if('t' == key) { + 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); + } + } + return NULL; +} + +int main(int argc, char *argv[]) +{ + signal(SIGINT, sig_int_handler); + all_args_t args; + metrics_stdout metrics; + enb *enb = enb::get_instance(); + + cout << "--- Software Radio Systems LTE eNodeB ---" << endl << endl; + + parse_args(&args, argc, argv); + if(!enb->init(&args)) { + exit(1); + } + metrics.init(enb, args.expert.metrics_period_secs); + + pthread_t input; + pthread_create(&input, NULL, &input_loop, &metrics); + + bool plot_started = false; + bool signals_pregenerated = false; + while(running) { + if (!plot_started && args.gui.enable) { + enb->start_plot(); + plot_started = true; + } + sleep(1); + } + pthread_cancel(input); + metrics.stop(); + enb->stop(); + cout << "--- exiting ---" << endl; + exit(0); +} diff --git a/srsenb/src/metrics_stdout.cc b/srsenb/src/metrics_stdout.cc new file mode 100644 index 000000000..746abe5be --- /dev/null +++ b/srsenb/src/metrics_stdout.cc @@ -0,0 +1,201 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2015 The srsUE Developers. See the + * COPYRIGHT file at the top-level directory of this distribution. + * + * \section LICENSE + * + * This file is part of the srsUE library. + * + * srsUE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsUE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General 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 "metrics_stdout.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +namespace srsenb{ + +char const * const prefixes[2][9] = +{ + { "", "m", "u", "n", "p", "f", "a", "z", "y", }, + { "", "k", "M", "G", "T", "P", "E", "Z", "Y", }, +}; + +metrics_stdout::metrics_stdout() + :started(false) + ,do_print(false) + ,n_reports(10) +{ +} + +bool metrics_stdout::init(enb_metrics_interface *u, float report_period_secs) +{ + enb_ = u; + metrics_report_period = report_period_secs; + + started = true; + pthread_create(&metrics_thread, NULL, &metrics_thread_start, this); + return true; +} + +void metrics_stdout::stop() +{ + if(started) + { + started = false; + pthread_join(metrics_thread, NULL); + } +} + +void metrics_stdout::toggle_print(bool b) +{ + do_print = b; +} + +void* metrics_stdout::metrics_thread_start(void *m_) +{ + metrics_stdout *m = (metrics_stdout*)m_; + m->metrics_thread_run(); + return NULL; +} + +void metrics_stdout::metrics_thread_run() +{ + while(started) + { + usleep(metrics_report_period*1e6); + if(enb_->get_metrics(metrics)) { + if (metrics.rrc.n_ues > 0) { + print_metrics(); + } + } else { + print_disconnect(); + } + } +} + +void metrics_stdout::print_metrics() +{ + if(!do_print) + return; + + if(++n_reports > 10) + { + n_reports = 0; + cout << endl; + cout << "------DL-------------------UL----------------" << endl; + cout << "rnti mcs brate bler snr phr turbo mcs brate bler" << endl; + } + if (metrics.rrc.n_ues > 0) { + + for (int i=0;i metrics.mac[i].tx_pkts) { + printf("tx caution errors %d > %d\n", metrics.mac[i].tx_errors, metrics.mac[i].tx_pkts); + } + if (metrics.mac[i].rx_errors > metrics.mac[i].rx_pkts) { + printf("rx caution errors %d > %d\n", metrics.mac[i].rx_errors, metrics.mac[i].rx_pkts); + } + + cout << std::hex << metrics.mac[i].rnti << " "; + cout << float_to_string(metrics.phy[i].dl.mcs, 2); + if (metrics.mac[i].tx_brate > 0 && metrics_report_period) { + cout << float_to_eng_string((float) metrics.mac[i].tx_brate/metrics_report_period, 2); + } else { + cout << float_to_string(0, 2); + } + if (metrics.mac[i].tx_pkts > 0 && metrics.mac[i].tx_errors) { + cout << float_to_string((float) 100*metrics.mac[i].tx_errors/metrics.mac[i].tx_pkts, 2) << "%"; + } else { + cout << float_to_string(0, 2) << "%"; + } + cout << float_to_string(metrics.phy[i].ul.sinr, 2); + cout << float_to_string(metrics.mac[i].phr, 2); + cout << float_to_string(metrics.phy[i].ul.turbo_iters, 2); + cout << float_to_string(metrics.phy[i].ul.mcs, 2); + if (metrics.mac[i].rx_brate > 0 && metrics_report_period) { + cout << float_to_eng_string((float) metrics.mac[i].rx_brate/metrics_report_period, 2); + } else { + cout << float_to_string(0, 2); + } + if (metrics.mac[i].rx_pkts > 0 && metrics.mac[i].rx_errors > 0) { + cout << float_to_string((float) 100*metrics.mac[i].rx_errors/metrics.mac[i].rx_pkts, 2) << "%"; + } else { + cout << float_to_string(0, 2) << "%"; + } + cout << endl; + } + } else { + cout << "--- No users ---" << endl; + } + 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); + } + +} + +void metrics_stdout::print_disconnect() +{ + if(do_print) { + cout << "--- disconnected ---" << endl; + } +} + +std::string metrics_stdout::float_to_string(float f, int digits) +{ + std::ostringstream os; + const int precision = (f == 0.0) ? digits-1 : digits - log10(fabs(f))-2*DBL_EPSILON; + os << std::setw(6) << std::fixed << std::setprecision(precision) << f; + return os.str(); +} + +std::string metrics_stdout::float_to_eng_string(float f, int digits) +{ + const int degree = (f == 0.0) ? 0 : lrint( floor( log10( fabs( f ) ) / 3) ); + + std::string factor; + + if ( abs( degree ) < 9 ) + { + if(degree < 0) + factor = prefixes[0][ abs( degree ) ]; + else + factor = prefixes[1][ abs( degree ) ]; + } else { + return "failed"; + } + + const double scaled = f * pow( 1000.0, -degree ); + if (degree != 0) { + return float_to_string(scaled, digits) + factor; + } else { + return " " + float_to_string(scaled, digits) + factor; + } +} + +} // namespace srsue diff --git a/srsenb/src/parser.cc b/srsenb/src/parser.cc new file mode 100644 index 000000000..25629adef --- /dev/null +++ b/srsenb/src/parser.cc @@ -0,0 +1,120 @@ +#include "parser.h" +#include + +namespace srsenb { + +using namespace libconfig; + +int parser::parse_section(std::string filename, parser::section *s) +{ + parser p(filename); + p.add_section(s); + return p.parse(); +} + +parser::parser(std::string filename_) +{ + filename = filename_; +} + +void parser::add_section(parser::section* s) +{ + sections.push_back(s); +} + +int parser::parse() +{ + // open file + Config cfg; + + try + { + cfg.readFile(filename.c_str()); + } + catch(const FileIOException &fioex) + { + std::cerr << "I/O error while reading file: " << filename << std::endl; + return(-1); + } + catch(const ParseException &pex) + { + std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine() + << " - " << pex.getError() << std::endl; + return(-1); + } + + for (std::list::iterator ci = sections.begin(); ci != sections.end(); ++ci) { + section *s = *ci; + if (s->parse(cfg.getRoot())) { + return -1; + } + } + return 0; +} + +parser::section::section(std::string name_) +{ + name = name_; + enabled_value = NULL; +} + +// Fields are allocated dynamically, free all fields added to the section +parser::section::~section() +{ + for (std::list::iterator ci = fields.begin(); ci != fields.end(); ++ci) { + delete *ci; + } +} + +void parser::section::add_field(field_itf* f) +{ + fields.push_back(f); +} + +void parser::section::add_subsection(parser::section* s) +{ + sub_sections.push_back(s); +} + +void parser::section::set_optional(bool* enabled_value_) +{ + enabled_value = enabled_value_; +} + +int parser::section::parse(Setting &root) +{ + try { + for (std::list::iterator ci = fields.begin(); ci != fields.end(); ++ci) { + field_itf *f = *ci; + if (f->parse(root[name.c_str()])) { + fprintf(stderr, "Error parsing field %s in section %s\n", f->get_name(), name.c_str()); + return -1; + } + } + for (std::list::iterator ci = sub_sections.begin(); ci != sub_sections.end(); ++ci) { + section *s = *ci; + if (s->parse(root[name.c_str()])) { + fprintf(stderr, "Error parsing section %s\n", name.c_str()); + return -1; + } + } + if (enabled_value) { + *enabled_value = true; + } + } catch(const SettingNotFoundException ex) { + if (enabled_value) { + *enabled_value = false; + return 0; + } else { + std::cerr << "Error section " << name.c_str() << " not found." << std::endl; + return -1; + } + } + return 0; +} + + + + + +} \ No newline at end of file diff --git a/srsenb/src/phy/CMakeLists.txt b/srsenb/src/phy/CMakeLists.txt new file mode 100644 index 000000000..c0582cd51 --- /dev/null +++ b/srsenb/src/phy/CMakeLists.txt @@ -0,0 +1,8 @@ +file(GLOB SOURCES "*.cc") +add_library(srsenb_phy SHARED ${SOURCES}) +target_link_libraries(srsenb_phy ${SRSLTE_LIBRARIES} ) + +if(ENABLE_GUI AND SRSGUI_FOUND) + target_link_libraries(srsenb_phy ${SRSGUI_LIBRARIES}) +endif(ENABLE_GUI AND SRSGUI_FOUND) + diff --git a/srsenb/src/phy/phch_common.cc b/srsenb/src/phy/phch_common.cc new file mode 100644 index 000000000..c5abb17ea --- /dev/null +++ b/srsenb/src/phy/phch_common.cc @@ -0,0 +1,115 @@ + +#include "srslte/common/threads.h" +#include "srslte/common/log.h" + +#include "phy/txrx.h" + +#include +#include + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +using namespace std; + + +namespace srsenb { + +void phch_common::set_nof_mutex(uint32_t nof_mutex_) { + nof_mutex = nof_mutex_; + assert(nof_mutex <= max_mutex); +} + +void phch_common::reset() { + bzero(ul_grants, sizeof(mac_interface_phy::ul_sched_t)*10); + bzero(dl_grants, sizeof(mac_interface_phy::dl_sched_t)*10); +} + +bool phch_common::init(srslte_cell_t *cell_, srslte::radio* radio_h_, mac_interface_phy *mac_) +{ + radio = radio_h_; + mac = mac_; + memcpy(&cell, cell_, sizeof(srslte_cell_t)); + + is_first_of_burst = true; + is_first_tx = true; + for (uint32_t i=0;iset_tti(tx_mutex_cnt); + radio->tx(buffer, nof_samples, tx_time); + + // Trigger next transmission + pthread_mutex_unlock(&tx_mutex[(tx_mutex_cnt+1)%nof_mutex]); + + // Trigger MAC clock + mac->tti_clock(); +} + +void phch_common::ack_clear(uint32_t sf_idx) +{ + for(std::map::iterator iter=pending_ack.begin(); iter!=pending_ack.end(); ++iter) { + pending_ack_t *p = (pending_ack_t*) &iter->second; + p->is_pending[sf_idx] = false; + } +} + +void phch_common::ack_add_rnti(uint16_t rnti) +{ + for (int sf_idx=0;sf_idx<10;sf_idx++) { + pending_ack[rnti].is_pending[sf_idx] = false; + } +} + +void phch_common::ack_rem_rnti(uint16_t rnti) +{ + pending_ack.erase(rnti); +} + +void phch_common::ack_set_pending(uint32_t sf_idx, uint16_t rnti, uint32_t last_n_pdcch) +{ + if (pending_ack.count(rnti)) { + pending_ack[rnti].is_pending[sf_idx] = true; + pending_ack[rnti].n_pdcch[sf_idx] = last_n_pdcch; + } +} + +bool phch_common::ack_is_pending(uint32_t sf_idx, uint16_t rnti, uint32_t *last_n_pdcch) +{ + if (pending_ack.count(rnti)) { + bool ret = pending_ack[rnti].is_pending[sf_idx]; + pending_ack[rnti].is_pending[sf_idx] = false; + + if (ret && last_n_pdcch) { + *last_n_pdcch = pending_ack[rnti].n_pdcch[sf_idx]; + } + return ret; + } else { + return false; + } +} + +} diff --git a/srsenb/src/phy/phch_worker.cc b/srsenb/src/phy/phch_worker.cc new file mode 100644 index 000000000..c703867f8 --- /dev/null +++ b/srsenb/src/phy/phch_worker.cc @@ -0,0 +1,796 @@ + +#include + +#include "srslte/common/threads.h" +#include "srslte/common/log.h" + +#include "phy/phch_worker.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +using namespace std; + +// Enable this to log SI +//#define LOG_THIS(a) 1 + +// Enable this one to skip SI-RNTI +#define LOG_THIS(rnti) (rnti != 0xFFFF) + + +/* Define GUI-related things */ +#ifdef ENABLE_GUI +#include "srsgui/srsgui.h" +#include +void init_plots(srsenb::phch_worker *worker); +pthread_t plot_thread; +sem_t plot_sem; +static int plot_worker_id = -1; +#else +#warning Compiling without srsGUI support +#endif +/*********************************************/ + + + +//#define DEBUG_WRITE_FILE + +namespace srsenb { + + +phch_worker::phch_worker() +{ + phy = NULL; + reset(); +} + +#ifdef DEBUG_WRITE_FILE +FILE *f; +#endif + +void phch_worker::init(phch_common* phy_, srslte::log *log_h_) +{ + phy = phy_; + log_h = log_h_; + + pthread_mutex_init(&mutex, NULL); + + // Init cell here + signal_buffer_rx = (cf_t*) srslte_vec_malloc(2*SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t)); + if (!signal_buffer_rx) { + fprintf(stderr, "Error allocating memory\n"); + return; + } + signal_buffer_tx = (cf_t*) srslte_vec_malloc(2*SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t)); + if (!signal_buffer_tx) { + fprintf(stderr, "Error allocating memory\n"); + return; + } + if (srslte_enb_dl_init(&enb_dl, phy->cell)) { + fprintf(stderr, "Error initiating ENB DL\n"); + return; + } + + if (srslte_enb_ul_init(&enb_ul, + phy->cell, + NULL, + &phy->pusch_cfg, + &phy->hopping_cfg, + &phy->pucch_cfg)) + { + fprintf(stderr, "Error initiating ENB DL\n"); + return; + } + + srslte_pucch_set_threshold(&enb_ul.pucch, 0.8, 0.5); + srslte_sch_set_max_noi(&enb_ul.pusch.ul_sch, phy->params.pusch_max_its); + srslte_enb_dl_set_amp(&enb_dl, phy->params.tx_amplitude); + + Info("Worker %d configured cell %d PRB\n", get_id(), phy->cell.nof_prb); + + initiated = true; + +#ifdef DEBUG_WRITE_FILE + f = fopen("test.dat", "w"); +#endif +} + +void phch_worker::reset() +{ + initiated = false; + ue_db.clear(); +} + +cf_t* phch_worker::get_buffer_rx() +{ + return signal_buffer_rx; +} + +void phch_worker::set_time(uint32_t tti_, uint32_t tx_mutex_cnt_, srslte_timestamp_t tx_time_) +{ + tti_rx = tti_; + tti_tx = (tti_ + 4)%10240; + tti_sched_ul = (tti_ + 8)%10240; + sf_rx = tti_rx%10; + sf_tx = tti_tx%10; + sf_sched_ul = tti_sched_ul%10; + tx_mutex_cnt = tx_mutex_cnt_; + memcpy(&tx_time, &tx_time_, sizeof(srslte_timestamp_t)); +} + +int phch_worker::add_rnti(uint16_t rnti) +{ + + if (srslte_enb_dl_add_rnti(&enb_dl, rnti)) { + return -1; + } + if (srslte_enb_ul_add_rnti(&enb_ul, rnti)) { + return -1; + } + + // Create user + ue_db[rnti].rnti = rnti; + + return SRSLTE_SUCCESS; + +} + +uint32_t phch_worker::get_nof_rnti() { + return ue_db.size(); +} + +void phch_worker::set_config_dedicated(uint16_t rnti, + srslte_uci_cfg_t *uci_cfg, + srslte_pucch_sched_t *pucch_sched, + srslte_refsignal_srs_cfg_t *srs_cfg, + uint32_t I_sr, bool pucch_cqi, uint32_t pmi_idx, bool pucch_cqi_ack) +{ + pthread_mutex_lock(&mutex); + if (ue_db.count(rnti)) { + pucch_sched->N_pucch_1 = phy->pucch_cfg.n1_pucch_an; + srslte_enb_ul_cfg_ue(&enb_ul, rnti, uci_cfg, pucch_sched, srs_cfg); + + ue_db[rnti].I_sr = I_sr; + ue_db[rnti].I_sr_en = true; + + if (pucch_cqi) { + ue_db[rnti].pmi_idx = pmi_idx; + ue_db[rnti].cqi_en = true; + ue_db[rnti].pucch_cqi_ack = pucch_cqi_ack; + } else { + ue_db[rnti].pmi_idx = 0; + ue_db[rnti].cqi_en = false; + } + + } else { + Error("Setting config dedicated: rnti=0x%x does not exist\n"); + } + pthread_mutex_unlock(&mutex); +} + +void phch_worker::rem_rnti(uint16_t rnti) +{ + pthread_mutex_lock(&mutex); + if (ue_db.count(rnti)) { + ue_db.erase(rnti); + + srslte_enb_dl_rem_rnti(&enb_dl, rnti); + srslte_enb_ul_rem_rnti(&enb_ul, rnti); + + // remove any pending grant for each subframe + for (uint32_t i=0;i<10;i++) { + for (uint32_t j=0;jul_grants[i].nof_grants;j++) { + if (phy->ul_grants[i].sched_grants[j].rnti == rnti) { + phy->ul_grants[i].sched_grants[j].rnti = 0; + } + } + for (uint32_t j=0;jdl_grants[i].nof_grants;j++) { + if (phy->dl_grants[i].sched_grants[j].rnti == rnti) { + phy->dl_grants[i].sched_grants[j].rnti = 0; + } + } + } + } else { + Error("Removing user: rnti=0x%x does not exist\n", rnti); + } + pthread_mutex_unlock(&mutex); +} + +void phch_worker::work_imp() +{ + uint32_t sf_ack; + + pthread_mutex_lock(&mutex); + + mac_interface_phy::ul_sched_t *ul_grants = phy->ul_grants; + mac_interface_phy::dl_sched_t *dl_grants = phy->dl_grants; + mac_interface_phy *mac = phy->mac; + + log_h->step(tti_rx); + + Debug("Worker %d running\n", get_id()); + + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + uint16_t rnti = (uint16_t) iter->first; + ue_db[rnti].has_grant_tti = -1; + } + + // Process UL signal + srslte_enb_ul_fft(&enb_ul, signal_buffer_rx); + + // Decode pending UL grants for the tti they were scheduled + decode_pusch(ul_grants[sf_rx].sched_grants, ul_grants[sf_rx].nof_grants, sf_rx); + + // Decode remaining PUCCH ACKs not associated with PUSCH transmission and SR signals + decode_pucch(tti_rx); + + // Get DL scheduling for the TX TTI from MAC + if (mac->get_dl_sched(tti_tx, &dl_grants[sf_tx]) < 0) { + Error("Getting DL scheduling from MAC\n"); + goto unlock; + } + + if (dl_grants[sf_tx].cfi < 1 || dl_grants[sf_tx].cfi > 3) { + Error("Invalid CFI=%d\n", dl_grants[sf_tx].cfi); + goto unlock; + } + + // Get UL scheduling for the TX TTI from MAC + if (mac->get_ul_sched(tti_sched_ul, &ul_grants[sf_sched_ul]) < 0) { + Error("Getting UL scheduling from MAC\n"); + goto unlock; + } + + // Put base signals (references, PBCH, PCFICH and PSS/SSS) into the resource grid + srslte_enb_dl_clear_sf(&enb_dl); + srslte_enb_dl_set_cfi(&enb_dl, dl_grants[sf_tx].cfi); + srslte_enb_dl_put_base(&enb_dl, tti_tx); + + // Put UL/DL grants to resource grid. PDSCH data will be encoded as well. + encode_pdcch_dl(dl_grants[sf_tx].sched_grants, dl_grants[sf_tx].nof_grants, sf_tx); + encode_pdcch_ul(ul_grants[sf_sched_ul].sched_grants, ul_grants[sf_sched_ul].nof_grants, sf_tx); + encode_pdsch(dl_grants[sf_tx].sched_grants, dl_grants[sf_tx].nof_grants, sf_tx); + + // Put pending PHICH HARQ ACK/NACK indications into subframe + encode_phich(ul_grants[sf_sched_ul].phich, ul_grants[sf_sched_ul].nof_phich, sf_tx); + + // Prepare for receive ACK for DL grants in sf_tx+4 + sf_ack = (sf_tx+4)%10; + phy->ack_clear(sf_ack); + for (uint32_t i=0;i= SRSLTE_CRNTI_START && dl_grants[sf_tx].sched_grants[i].rnti <= SRSLTE_CRNTI_END) { + phy->ack_set_pending(sf_ack, dl_grants[sf_tx].sched_grants[i].rnti, dl_grants[sf_tx].sched_grants[i].location.ncce); + } + } + + // Generate signal and transmit + srslte_enb_dl_gen_signal(&enb_dl, signal_buffer_tx); + Debug("Sending to radio\n"); + phy->worker_end(tx_mutex_cnt, signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb), tx_time); + +#ifdef DEBUG_WRITE_FILE + fwrite(signal_buffer_tx, SRSLTE_SF_LEN_PRB(phy->cell.nof_prb)*sizeof(cf_t), 1, f); +#endif + +#ifdef DEBUG_WRITE_FILE + if (tti_tx == 10) { + fclose(f); + exit(-1); + } +#endif + + /* Tell the plotting thread to draw the plots */ +#ifdef ENABLE_GUI + if ((int) get_id() == plot_worker_id) { + sem_post(&plot_sem); + } +#endif + +unlock: + pthread_mutex_unlock(&mutex); + +} + + +int phch_worker::decode_pusch(srslte_enb_ul_pusch_t *grants, uint32_t nof_pusch, uint32_t tti) +{ + srslte_uci_data_t uci_data; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + + uint32_t wideband_cqi_value = 0; + + uint32_t n_rb_ho = 0; + for (uint32_t i=0;iack_is_pending(sf_rx, rnti)) { + uci_data.uci_ack_len = 1; + } + // Configure PUSCH CQI channel + srslte_cqi_value_t cqi_value; + bool cqi_enabled = false; + if (ue_db[rnti].cqi_en && srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) { + cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND; + cqi_enabled = true; + } else if (grants[i].grant.cqi_request) { + cqi_value.type = SRSLTE_CQI_TYPE_SUBBAND_HL; + cqi_value.subband_hl.N = (phy->cell.nof_prb > 7) ? srslte_cqi_hl_get_no_subbands(phy->cell.nof_prb) : 0; + cqi_enabled = true; + } + if (cqi_enabled) { + uci_data.uci_cqi_len = srslte_cqi_size(&cqi_value); + Info("cqi enabled len=%d\n", uci_data.uci_cqi_len); + } + + // mark this tti as having an ul grant to avoid pucch + ue_db[rnti].has_grant_tti = tti_rx; + + srslte_ra_ul_grant_t phy_grant; + int res = -1; + if (!srslte_ra_ul_dci_to_grant(&grants[i].grant, enb_ul.cell.nof_prb, n_rb_ho, &phy_grant, tti%8)) { + res = srslte_enb_ul_get_pusch(&enb_ul, &phy_grant, grants[i].softbuffer, + rnti, grants[i].rv_idx, + grants[i].current_tx_nb, + grants[i].data, + &uci_data, + tti); + } else { + Error("Computing PUSCH grant\n"); + return SRSLTE_ERROR; + } + + #ifdef LOG_EXECTIME + gettimeofday(&t[2], NULL); + get_time_interval(t); + snprintf(timestr, 64, ", dec_time=%4d us", (int) t[0].tv_usec); + #endif + + bool crc_res = (res == 0); + + // Save PHICH scheduling for this user. Each user can have just 1 PUSCH grant per TTI + ue_db[rnti].phich_info.n_prb_lowest = enb_ul.pusch_cfg.grant.n_prb_tilde[0]; + ue_db[rnti].phich_info.n_dmrs = phy_grant.ncs_dmrs; + + + + char cqi_str[64]; + if (cqi_enabled) { + srslte_cqi_value_unpack(uci_data.uci_cqi, &cqi_value); + if (ue_db[rnti].cqi_en) { + wideband_cqi_value = cqi_value.wideband.wideband_cqi; + } else if (grants[i].grant.cqi_request) { + wideband_cqi_value = cqi_value.subband_hl.wideband_cqi; + } + snprintf(cqi_str, 64, ", cqi=%d", wideband_cqi_value); + } + + float snr_db = 10*log10(srslte_chest_ul_get_snr(&enb_ul.chest)); + + /* + if (!crc_res && enb_ul.pusch_cfg.grant.L_prb == 1 && enb_ul.pusch_cfg.grant.n_prb[0] == 0 && snr_db > 5) { + srslte_vec_save_file("sf_symbols", enb_ul.sf_symbols, sizeof(cf_t)*SRSLTE_SF_LEN_RE(25, SRSLTE_CP_NORM)); + srslte_vec_save_file("ce", enb_ul.ce, sizeof(cf_t)*SRSLTE_SF_LEN_RE(25, SRSLTE_CP_NORM)); + srslte_vec_save_file("d", enb_ul.pusch.d, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re); + srslte_vec_save_file("ce2", enb_ul.pusch.ce, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re); + srslte_vec_save_file("z", enb_ul.pusch.z, sizeof(cf_t)*enb_ul.pusch_cfg.nbits.nof_re); + printf("saved sf_idx=%d, mcs=%d, tbs=%d, rnti=%d, rv=%d, snr=%.1f\n", tti%10, + grants[i].grant.mcs_idx, enb_ul.pusch_cfg.cb_segm.tbs, rnti, grants[i].rv_idx, snr_db); + exit(-1); + } + */ + log_h->info_hex(grants[i].data, phy_grant.mcs.tbs/8, + "PUSCH: rnti=0x%x, prb=(%d,%d), tbs=%d, mcs=%d, rv=%d, snr=%.1f dB, n_iter=%d, crc=%s%s%s%s\n", + rnti, phy_grant.n_prb[0], phy_grant.n_prb[0]+phy_grant.L_prb, + phy_grant.mcs.tbs/8, phy_grant.mcs.idx, grants[i].grant.rv_idx, + snr_db, + srslte_pusch_last_noi(&enb_ul.pusch), + crc_res?"OK":"KO", + uci_data.uci_ack_len>0?(uci_data.uci_ack?", ack=1":", ack=0"):"", + uci_data.uci_cqi_len>0?cqi_str:"", + timestr); + + // Notify MAC of RL status + if (grants[i].grant.rv_idx == 0) { + if (res && snr_db < PUSCH_RL_SNR_DB_TH) { + Debug("PUSCH: Radio-Link failure snr=%.1f dB\n", snr_db); + phy->mac->rl_failure(rnti); + } else { + phy->mac->rl_ok(rnti); + } + } + + // Notify MAC new received data and HARQ Indication value + phy->mac->crc_info(tti_rx, rnti, phy_grant.mcs.tbs/8, crc_res); + if (uci_data.uci_ack_len) { + phy->mac->ack_info(tti_rx, rnti, uci_data.uci_ack && (crc_res || snr_db > PUSCH_RL_SNR_DB_TH)); + } + + // Notify MAC of UL SNR and DL CQI + if (snr_db >= PUSCH_RL_SNR_DB_TH) { + phy->mac->snr_info(tti_rx, rnti, snr_db); + } + if (uci_data.uci_cqi_len>0 && crc_res) { + phy->mac->cqi_info(tti_rx, rnti, wideband_cqi_value); + } + + // Save metrics stats + ue_db[rnti].metrics_ul(phy_grant.mcs.idx, 0, snr_db, srslte_pusch_last_noi(&enb_ul.pusch)); + } + } + return SRSLTE_SUCCESS; +} + + +int phch_worker::decode_pucch(uint32_t tti_rx) +{ + uint32_t sf_rx = tti_rx%10; + srslte_uci_data_t uci_data; + + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + uint16_t rnti = (uint16_t) iter->first; + + if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END && ue_db[rnti].has_grant_tti != (int) tti_rx) { + // Check if user needs to receive PUCCH + bool needs_pucch = false, needs_ack=false, needs_sr=false, needs_cqi=false; + uint32_t last_n_pdcch = 0; + bzero(&uci_data, sizeof(srslte_uci_data_t)); + + if (ue_db[rnti].I_sr_en) { + if (srslte_ue_ul_sr_send_tti(ue_db[rnti].I_sr, tti_rx)) { + needs_pucch = true; + needs_sr = true; + uci_data.scheduling_request = true; + } + } + if (phy->ack_is_pending(sf_rx, rnti, &last_n_pdcch)) { + needs_pucch = true; + needs_ack = true; + uci_data.uci_ack_len = 1; + } + srslte_cqi_value_t cqi_value; + if (ue_db[rnti].cqi_en && (ue_db[rnti].pucch_cqi_ack || !needs_ack)) { + if (srslte_cqi_send(ue_db[rnti].pmi_idx, tti_rx)) { + needs_pucch = true; + needs_cqi = true; + cqi_value.type = SRSLTE_CQI_TYPE_WIDEBAND; + uci_data.uci_cqi_len = srslte_cqi_size(&cqi_value); + } + } + + if (needs_pucch) { + if (srslte_enb_ul_get_pucch(&enb_ul, rnti, last_n_pdcch, sf_rx, &uci_data)) { + fprintf(stderr, "Error getting PUCCH\n"); + return SRSLTE_ERROR; + } + if (uci_data.uci_ack_len > 0) { + phy->mac->ack_info(tti_rx, rnti, uci_data.uci_ack && (srslte_pucch_get_last_corr(&enb_ul.pucch) >= PUCCH_RL_CORR_TH)); + } + if (uci_data.scheduling_request) { + phy->mac->sr_detected(tti_rx, rnti); + } + + char cqi_str[64]; + if (uci_data.uci_cqi_len) { + srslte_cqi_value_unpack(uci_data.uci_cqi, &cqi_value); + phy->mac->cqi_info(tti_rx, rnti, cqi_value.wideband.wideband_cqi); + sprintf(cqi_str, ", cqi=%d", cqi_value.wideband.wideband_cqi); + } + log_h->info("PUCCH: rnti=0x%x, corr=%.2f, n_pucch=%d, n_prb=%d%s%s%s\n", + rnti, + srslte_pucch_get_last_corr(&enb_ul.pucch), + enb_ul.pucch.last_n_pucch, enb_ul.pucch.last_n_prb, + needs_ack?(uci_data.uci_ack?", ack=1":", ack=0"):"", + needs_sr?(uci_data.scheduling_request?", sr=yes":", sr=no"):"", + needs_cqi?cqi_str:""); + + + // Notify MAC of RL status + if (!needs_sr) { + if (srslte_pucch_get_last_corr(&enb_ul.pucch) < PUCCH_RL_CORR_TH) { + Debug("PUCCH: Radio-Link failure corr=%.1f\n", srslte_pucch_get_last_corr(&enb_ul.pucch)); + phy->mac->rl_failure(rnti); + } else { + phy->mac->rl_ok(rnti); + } + } + } + } + } + return 0; +} + + +int phch_worker::encode_phich(srslte_enb_dl_phich_t *acks, uint32_t nof_acks, uint32_t sf_idx) +{ + for (uint32_t i=0;iinfo_hex(ptr, len, + "PDSCH: rnti=0x%x, l_crb=%2d, %s, harq=%d, tbs=%d, mcs=%d, rv=%d, tti_tx=%d\n", + rnti, phy_grant.nof_prb, grant_str, grants[i].grant.harq_process, + phy_grant.mcs.tbs/8, phy_grant.mcs.idx, grants[i].grant.rv_idx, tti_tx); + } + if (srslte_enb_dl_put_pdsch(&enb_dl, &phy_grant, grants[i].softbuffer, rnti, grants[i].grant.rv_idx, sf_idx, + grants[i].data)) + { + fprintf(stderr, "Error putting PDSCH %d\n",i); + return SRSLTE_ERROR; + } + + // Save metrics stats + ue_db[rnti].metrics_dl(phy_grant.mcs.idx); + } + } + return SRSLTE_SUCCESS; +} + + + +/************ METRICS interface ********************/ +uint32_t phch_worker::get_metrics(phy_metrics_t metrics[ENB_METRICS_MAX_USERS]) +{ + uint32_t cnt=0; + for(std::map::iterator iter=ue_db.begin(); iter!=ue_db.end(); ++iter) { + ue *u = (ue*) &iter->second; + uint16_t rnti = iter->first; + if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) { + u->metrics_read(&metrics[cnt]); + cnt++; + } + } + return cnt; +} + +void phch_worker::ue::metrics_read(phy_metrics_t* metrics_) +{ + memcpy(metrics_, &metrics, sizeof(phy_metrics_t)); + bzero(&metrics, sizeof(phy_metrics_t)); +} + +void phch_worker::ue::metrics_dl(uint32_t mcs) +{ + metrics.dl.mcs = SRSLTE_VEC_CMA(mcs, metrics.dl.mcs, metrics.dl.n_samples); + metrics.dl.n_samples++; +} + +void phch_worker::ue::metrics_ul(uint32_t mcs, float rssi, float sinr, uint32_t turbo_iters) +{ + metrics.ul.mcs = SRSLTE_VEC_CMA((float) mcs, metrics.ul.mcs, metrics.ul.n_samples); + metrics.ul.sinr = SRSLTE_VEC_CMA((float) sinr, metrics.ul.sinr, metrics.ul.n_samples); + metrics.ul.rssi = SRSLTE_VEC_CMA((float) sinr, metrics.ul.rssi, metrics.ul.n_samples); + metrics.ul.turbo_iters = SRSLTE_VEC_CMA((float) turbo_iters, metrics.ul.turbo_iters, metrics.ul.n_samples); + metrics.ul.n_samples++; +} + + + + + + + + + + + + + + + +void phch_worker::start_plot() { +#ifdef ENABLE_GUI + if (plot_worker_id == -1) { + plot_worker_id = get_id(); + log_h->console("Starting plot for worker_id=%d\n", plot_worker_id); + init_plots(this); + } else { + log_h->console("Trying to start a plot but already started by worker_id=%d\n", plot_worker_id); + } +#else + log_h->console("Trying to start a plot but plots are disabled (ENABLE_GUI constant in phch_worker.cc)\n"); +#endif +} + + +int phch_worker::read_ce_abs(float *ce_abs) { + uint32_t i=0; + int sz = srslte_symbol_sz(phy->cell.nof_prb); + bzero(ce_abs, sizeof(float)*sz); + int g = (sz - 12*phy->cell.nof_prb)/2; + for (i = 0; i < 12*phy->cell.nof_prb; i++) { + ce_abs[g+i] = 20 * log10(cabs(enb_ul.ce[i])); + if (isinf(ce_abs[g+i])) { + ce_abs[g+i] = -80; + } + } + return sz; +} + +int phch_worker::read_pusch_d(cf_t* pdsch_d) +{ + int nof_re = 400;//enb_ul.pusch_cfg.nbits.nof_re + memcpy(pdsch_d, enb_ul.pusch.d, nof_re*sizeof(cf_t)); + return nof_re; +} + + +} + + +/*********************************************************** + * + * PLOT TO VISUALIZE THE CHANNEL RESPONSEE + * + ***********************************************************/ + + +#ifdef ENABLE_GUI +plot_real_t pce; +plot_scatter_t pconst; +#define SCATTER_PUSCH_BUFFER_LEN (20*6*SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)) +#define SCATTER_PUSCH_PLOT_LEN 4000 +float tmp_plot[SCATTER_PUSCH_BUFFER_LEN]; +cf_t tmp_plot2[SRSLTE_SF_LEN_RE(SRSLTE_MAX_PRB, SRSLTE_CP_NORM)]; + +void *plot_thread_run(void *arg) { + srsenb::phch_worker *worker = (srsenb::phch_worker*) arg; + + sdrgui_init_title("srsENB"); + plot_real_init(&pce); + plot_real_setTitle(&pce, (char*) "Channel Response - Magnitude"); + plot_real_setLabels(&pce, (char*) "Index", (char*) "dB"); + plot_real_setYAxisScale(&pce, -40, 40); + + plot_scatter_init(&pconst); + plot_scatter_setTitle(&pconst, (char*) "PUSCH - Equalized Symbols"); + plot_scatter_setXAxisScale(&pconst, -4, 4); + plot_scatter_setYAxisScale(&pconst, -4, 4); + + plot_real_addToWindowGrid(&pce, (char*)"srsenb", 0, 0); + plot_scatter_addToWindowGrid(&pconst, (char*)"srsenb", 0, 1); + + int n; + int readed_pusch_re=0; + while(1) { + sem_wait(&plot_sem); + + n = worker->read_pusch_d(tmp_plot2); + plot_scatter_setNewData(&pconst, tmp_plot2, n); + n = worker->read_ce_abs(tmp_plot); + plot_real_setNewData(&pce, tmp_plot, n); + + } + return NULL; +} + + +void init_plots(srsenb::phch_worker *worker) { + + if (sem_init(&plot_sem, 0, 0)) { + perror("sem_init"); + exit(-1); + } + + pthread_attr_t attr; + struct sched_param param; + param.sched_priority = 0; + pthread_attr_init(&attr); + pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED); + pthread_attr_setschedpolicy(&attr, SCHED_OTHER); + pthread_attr_setschedparam(&attr, ¶m); + if (pthread_create(&plot_thread, &attr, plot_thread_run, worker)) { + perror("pthread_create"); + exit(-1); + } +} +#endif + + + + diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc new file mode 100644 index 000000000..2bf0b6d3d --- /dev/null +++ b/srsenb/src/phy/phy.cc @@ -0,0 +1,212 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "srslte/common/threads.h" +#include "srslte/common/log.h" +#include "phy/phy.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +using namespace std; + + +namespace srsenb { + +phy::phy() : workers_pool(MAX_WORKERS), + workers(MAX_WORKERS), + workers_common(txrx::MUTEX_X_WORKER*MAX_WORKERS) +{ +} + +void phy::parse_config(phy_cfg_t* cfg) +{ + + // PRACH configuration + prach_cfg.config_idx = cfg->prach_cnfg.prach_cnfg_info.prach_config_index; + prach_cfg.hs_flag = cfg->prach_cnfg.prach_cnfg_info.high_speed_flag; + prach_cfg.root_seq_idx = cfg->prach_cnfg.root_sequence_index; + prach_cfg.zero_corr_zone = cfg->prach_cnfg.prach_cnfg_info.zero_correlation_zone_config; + prach_cfg.freq_offset = cfg->prach_cnfg.prach_cnfg_info.prach_freq_offset; + + // PUSCH DMRS configuration + workers_common.pusch_cfg.cyclic_shift = cfg->pusch_cnfg.ul_rs.cyclic_shift; + workers_common.pusch_cfg.delta_ss = cfg->pusch_cnfg.ul_rs.group_assignment_pusch; + workers_common.pusch_cfg.group_hopping_en = cfg->pusch_cnfg.ul_rs.group_hopping_enabled; + workers_common.pusch_cfg.sequence_hopping_en = cfg->pusch_cnfg.ul_rs.sequence_hopping_enabled; + + // PUSCH hopping configuration + workers_common.hopping_cfg.hop_mode = cfg->pusch_cnfg.hopping_mode == LIBLTE_RRC_HOPPING_MODE_INTRA_AND_INTER_SUBFRAME ? + srslte_pusch_hopping_cfg_t::SRSLTE_PUSCH_HOP_MODE_INTRA_SF : + srslte_pusch_hopping_cfg_t::SRSLTE_PUSCH_HOP_MODE_INTER_SF; ; + workers_common.hopping_cfg.n_sb = cfg->pusch_cnfg.n_sb; + workers_common.hopping_cfg.hopping_offset = cfg->pusch_cnfg.pusch_hopping_offset; + + // PUCCH configuration + workers_common.pucch_cfg.delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[cfg->pucch_cnfg.delta_pucch_shift%LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS]; + workers_common.pucch_cfg.N_cs = cfg->pucch_cnfg.n_cs_an; + workers_common.pucch_cfg.n_rb_2 = cfg->pucch_cnfg.n_rb_cqi; + workers_common.pucch_cfg.srs_configured = false; + workers_common.pucch_cfg.n1_pucch_an = cfg->pucch_cnfg.n1_pucch_an;; +} + +bool phy::init(phy_args_t *args, + phy_cfg_t *cfg, + srslte::radio* radio_handler_, + mac_interface_phy *mac, + srslte::log* log_h) +{ + std::vector log_vec; + for (int i=0;inof_phy_threads;i++) { + log_vec[i] = (void*) log_h; + } + init(args, cfg, radio_handler_, mac, log_vec); + return true; +} + +bool phy::init(phy_args_t *args, + phy_cfg_t *cfg, + srslte::radio* radio_handler_, + mac_interface_phy *mac, + std::vector log_vec) +{ + + mlockall(MCL_CURRENT | MCL_FUTURE); + + radio_handler = radio_handler_; + nof_workers = args->nof_phy_threads; + + workers_common.params = *args; + + workers_common.init(&cfg->cell, radio_handler, mac); + + parse_config(cfg); + + // Add workers to workers pool and start threads + for (uint32_t i=0;icell, &prach_cfg, mac, (srslte::log*) log_vec[0], PRACH_WORKER_THREAD_PRIO); + 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(radio_handler, &workers_pool, &workers_common, &prach, (srslte::log*) log_vec[0], SF_RECV_THREAD_PRIO); + + return true; +} + +void phy::stop() +{ + tx_rx.stop(); + workers_common.stop(); + workers_pool.stop(); + prach.stop(); +} + +uint32_t phy::tti_to_SFN(uint32_t tti) { + return tti/10; +} + +uint32_t phy::tti_to_subf(uint32_t tti) { + return tti%10; +} + +/***** MAC->PHY interface **********/ +int phy::add_rnti(uint16_t rnti) +{ + if (rnti >= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) { + workers_common.ack_add_rnti(rnti); + } + for (uint32_t i=0;i= SRSLTE_CRNTI_START && rnti <= SRSLTE_CRNTI_END) { + workers_common.ack_rem_rnti(rnti); + } + for (uint32_t i=0;iPHY interface **********/ + +void phy::set_config_dedicated(uint16_t rnti, LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT* dedicated) +{ + // Parse RRC config + srslte_uci_cfg_t uci_cfg; + srslte_pucch_sched_t pucch_sched; + + /* PUSCH UCI configuration */ + bzero(&uci_cfg, sizeof(srslte_uci_cfg_t)); + uci_cfg.I_offset_ack = dedicated->pusch_cnfg_ded.beta_offset_ack_idx; + uci_cfg.I_offset_cqi = dedicated->pusch_cnfg_ded.beta_offset_cqi_idx; + uci_cfg.I_offset_ri = dedicated->pusch_cnfg_ded.beta_offset_ri_idx; + + /* PUCCH Scheduling configuration */ + bzero(&pucch_sched, sizeof(srslte_pucch_sched_t)); + pucch_sched.n_pucch_2 = dedicated->cqi_report_cnfg.report_periodic.pucch_resource_idx; + pucch_sched.n_pucch_sr = dedicated->sched_request_cnfg.sr_pucch_resource_idx; + + for (uint32_t i=0;isched_request_cnfg.sr_cnfg_idx, + dedicated->cqi_report_cnfg.report_periodic_setup_present, + dedicated->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, + dedicated->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi); + } +} + +// Start GUI +void phy::start_plot() { + ((phch_worker) workers[0]).start_plot(); +} + +} diff --git a/srsenb/src/phy/prach_worker.cc b/srsenb/src/phy/prach_worker.cc new file mode 100644 index 000000000..7f88f22d5 --- /dev/null +++ b/srsenb/src/phy/prach_worker.cc @@ -0,0 +1,134 @@ +#include "phy/prach_worker.h" + + +namespace srsenb { + +int prach_worker::init(srslte_cell_t *cell_, srslte_prach_cfg_t *prach_cfg_, mac_interface_phy* mac_, srslte::log* log_h_, int priority) +{ + log_h = log_h_; + mac = mac_; + memcpy(&prach_cfg, prach_cfg_, sizeof(srslte_prach_cfg_t)); + memcpy(&cell, cell_, sizeof(srslte_cell_t)); + + max_prach_offset_us = 50; + + pthread_mutex_init(&mutex, NULL); + pthread_cond_init(&cvar, NULL); + + if (srslte_prach_init_cfg(&prach, &prach_cfg, cell.nof_prb)) { + fprintf(stderr, "Error initiating PRACH\n"); + return -1; + } + + srslte_prach_set_detect_factor(&prach, 60); + + nof_sf = (uint32_t) ceilf(prach.T_tot*1000); + + signal_buffer_rx = (cf_t*) srslte_vec_malloc(sizeof(cf_t)*nof_sf*SRSLTE_SF_LEN_PRB(cell.nof_prb)); + if (!signal_buffer_rx) { + perror("malloc"); + return -1; + } + + start(priority); + initiated = true; + + pending_tti = 0; + processed_tti = 0; + return 0; +} + +void prach_worker::stop() +{ + pthread_mutex_lock(&mutex); + processed_tti = 99999; + running = false; + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + + wait_thread_finish(); +} + +void prach_worker::set_max_prach_offset_us(float delay_us) +{ + max_prach_offset_us = delay_us; +} + +int prach_worker::new_tti(uint32_t tti_rx, cf_t* buffer_rx) +{ + // Save buffer only if it's a PRACH TTI + if (srslte_prach_tti_opportunity(&prach, tti_rx, -1) || sf_cnt) { + memcpy(&signal_buffer_rx[sf_cnt*SRSLTE_SF_LEN_PRB(cell.nof_prb)], buffer_rx, sizeof(cf_t)*SRSLTE_SF_LEN_PRB(cell.nof_prb)); + sf_cnt++; + if (sf_cnt == nof_sf) { + sf_cnt = 0; + if ((int) pending_tti != processed_tti) { + log_h->warning("PRACH thread did not finish processing TTI=%d\n", pending_tti); + } + pthread_mutex_lock(&mutex); + if (tti_rx+1 > nof_sf) { + pending_tti = tti_rx+1-nof_sf; + } else { + pending_tti = 10240+(tti_rx+1-nof_sf); + } + pthread_cond_signal(&cvar); + pthread_mutex_unlock(&mutex); + } + } + return 0; +} + + +int prach_worker::run_tti(uint32_t tti_rx) +{ + if (srslte_prach_tti_opportunity(&prach, tti_rx, -1)) + { + // Detect possible PRACHs + if (srslte_prach_detect_offset(&prach, + prach_cfg.freq_offset, + &signal_buffer_rx[prach.N_cp], + nof_sf*SRSLTE_SF_LEN_PRB(cell.nof_prb)-prach.N_cp, + prach_indices, + prach_offsets, + prach_p2avg, + &prach_nof_det)) + { + log_h->error("Error detecting PRACH\n"); + return SRSLTE_ERROR; + } + + if (prach_nof_det) { + for (uint32_t i=0;iinfo("PRACH: %d/%d, preamble=%d, offset=%.1f us, peak2avg=%.1f, max_offset=%.1f us\n", + i, prach_nof_det, prach_indices[i], prach_offsets[i]*1e6, prach_p2avg[i], max_prach_offset_us); + + if (prach_offsets[i]*1e6 < max_prach_offset_us) { + mac->rach_detected(tti_rx, prach_indices[i], (uint32_t) (prach_offsets[i]*1e6)); + } + } + } + } + return 0; +} + +void prach_worker::run_thread() +{ + running = true; + while(running) { + pthread_mutex_lock(&mutex); + while(processed_tti == (int) pending_tti) { + pthread_cond_wait(&cvar, &mutex); + } + pthread_mutex_unlock(&mutex); + log_h->debug("Processing pending_tti=%d\n", pending_tti); + if (running) { + if (run_tti(pending_tti)) { + running = false; + } + processed_tti = pending_tti; + } + } +} + + +} diff --git a/srsenb/src/phy/txrx.cc b/srsenb/src/phy/txrx.cc new file mode 100644 index 000000000..a137f5aea --- /dev/null +++ b/srsenb/src/phy/txrx.cc @@ -0,0 +1,117 @@ +#include + +#include "srslte/common/threads.h" +#include "srslte/common/log.h" + +#include "phy/txrx.h" +#include "phy/phch_worker.h" + +#define Error(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->error_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Warning(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->warning_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Info(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->info_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define Debug(fmt, ...) if (SRSLTE_DEBUG_ENABLED) log_h->debug_line(__FILE__, __LINE__, fmt, ##__VA_ARGS__) + +using namespace std; + + +namespace srsenb { + +txrx::txrx() +{ + running = false; + radio_h = NULL; + log_h = NULL; + workers_pool = NULL; + worker_com = NULL; +} + +bool txrx::init(srslte::radio* radio_h_, srslte::thread_pool* workers_pool_, phch_common* worker_com_, prach_worker *prach_, srslte::log* log_h_, uint32_t prio_) +{ + radio_h = radio_h_; + log_h = log_h_; + workers_pool = workers_pool_; + worker_com = worker_com_; + prach = prach_; + tx_mutex_cnt = 0; + running = true; + + nof_tx_mutex = MUTEX_X_WORKER*workers_pool->get_nof_workers(); + worker_com->set_nof_mutex(nof_tx_mutex); + + start(prio_); + return true; +} + +void txrx::stop() +{ + running = false; + wait_thread_finish(); +} + +void txrx::run_thread() +{ + phch_worker *worker = NULL; + cf_t *buffer = NULL; + srslte_timestamp_t rx_time, tx_time; + uint32_t sf_len = SRSLTE_SF_LEN_PRB(worker_com->cell.nof_prb); + + float samp_rate = srslte_sampling_freq_hz(worker_com->cell.nof_prb); + if (30720%((int) samp_rate/1000) == 0) { + radio_h->set_master_clock_rate(30.72e6); + } else { + radio_h->set_master_clock_rate(23.04e6); + } + + log_h->console("Setting Sampling frequency %.2f MHz\n", (float) samp_rate/1000000); + + // Configure radio + radio_h->set_rx_srate(samp_rate); + radio_h->set_tx_srate(samp_rate); + + log_h->info("Starting RX/TX thread nof_prb=%d, sf_len=%d\n",worker_com->cell.nof_prb, sf_len); + + // Start streaming RX samples + radio_h->start_rx(); + + // Set TTI so that first TX is at tti=0 + tti = 10235; + + printf("\n==== eNodeB started ===\n"); + printf("Type to view trace\n"); + // Main loop + while (running) { + tti = (tti+1)%10240; + worker = (phch_worker*) workers_pool->wait_worker(tti); + if (worker) { + buffer = worker->get_buffer_rx(); + + radio_h->rx_now(buffer, sf_len, &rx_time); + + /* Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time */ + srslte_timestamp_copy(&tx_time, &rx_time); + srslte_timestamp_add(&tx_time, 0, 4e-3); + + Debug("Settting TTI=%d, tx_mutex=%d, tx_time=%d:%f to worker %d\n", + tti, tx_mutex_cnt, + tx_time.full_secs, tx_time.frac_secs, + worker->get_id()); + + worker->set_time(tti, tx_mutex_cnt, tx_time); + tx_mutex_cnt = (tx_mutex_cnt+1)%nof_tx_mutex; + + // Trigger phy worker execution + workers_pool->start_worker(worker); + + // Trigger prach worker execution + prach->new_tti(tti, buffer); + + } else { + // wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here + running = false; + } + } +} + + + +} diff --git a/srsenb/src/upper/CMakeLists.txt b/srsenb/src/upper/CMakeLists.txt new file mode 100644 index 000000000..5f9007c50 --- /dev/null +++ b/srsenb/src/upper/CMakeLists.txt @@ -0,0 +1,3 @@ +file(GLOB SOURCES "*.cc") +add_library(srsenb_upper SHARED ${SOURCES}) +target_link_libraries(srsenb_upper ${SRSLTE_LIBRARIES}) diff --git a/srsenb/src/upper/gtpu.cc b/srsenb/src/upper/gtpu.cc new file mode 100644 index 000000000..090c0ccce --- /dev/null +++ b/srsenb/src/upper/gtpu.cc @@ -0,0 +1,240 @@ +#include "upper/gtpu.h" +#include + +using namespace srslte; + +namespace srsenb { + +bool gtpu::init(std::string gtp_bind_addr_, std::string mme_addr_, srsenb::pdcp_interface_gtpu* pdcp_, srslte::log* gtpu_log_) +{ + pdcp = pdcp_; + gtpu_log = gtpu_log_; + gtp_bind_addr = gtp_bind_addr_; + mme_addr = mme_addr_; + + pthread_mutex_init(&mutex, NULL); + + pool = byte_buffer_pool::get_instance(); + + if(0 != srslte_netsource_init(&src, gtp_bind_addr.c_str(), GTPU_PORT, SRSLTE_NETSOURCE_UDP)) { + gtpu_log->error("Failed to create source socket on %s:%d", gtp_bind_addr.c_str(), GTPU_PORT); + return false; + } + if(0 != srslte_netsink_init(&snk, mme_addr.c_str(), GTPU_PORT, SRSLTE_NETSINK_UDP)) { + gtpu_log->error("Failed to create sink socket on %s:%d", mme_addr.c_str(), GTPU_PORT); + return false; + } + + srslte_netsink_set_nonblocking(&snk); + + // Setup a thread to receive packets from the src socket + start(THREAD_PRIO); + return true; + +} + +void gtpu::stop() +{ + if(run_enable) { + run_enable = false; + // Wait thread to exit gracefully otherwise might leave a mutex locked + int cnt=0; + while(running && cnt<100) { + usleep(10000); + cnt++; + } + if (running) { + thread_cancel(); + } + wait_thread_finish(); + } + + srslte_netsink_free(&snk); + srslte_netsource_free(&src); +} + +// gtpu_interface_pdcp +void gtpu::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* pdu) +{ + gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "TX PDU, RNTI: 0x%x, LCID: %d", rnti, lcid); + gtpu_header_t header; + header.flags = 0x30; + header.message_type = 0xFF; + header.length = pdu->N_bytes; + header.teid = rnti_bearers[rnti].teids_out[lcid]; + + gtpu_write_header(&header, pdu); + srslte_netsink_write(&snk, pdu->msg, pdu->N_bytes); + pool->deallocate(pdu); +} + +// gtpu_interface_rrc +void gtpu::add_bearer(uint16_t rnti, uint32_t lcid, uint32_t teid_out, uint32_t *teid_in) +{ + // Allocate a TEID for the incoming tunnel + rntilcid_to_teidin(rnti, lcid, teid_in); + gtpu_log->info("Adding bearer for rnti: 0x%x, lcid: %d, teid_out: 0x%x, teid_in: 0x%x\n", rnti, lcid, teid_out, *teid_in); + + // Initialize maps if it's a new RNTI + if(rnti_bearers.count(rnti) == 0) { + for(int i=0;iinfo("Removing bearer for rnti: 0x%x, lcid: %d\n", rnti, lcid); + + rnti_bearers[rnti].teids_in[lcid] = 0; + rnti_bearers[rnti].teids_out[lcid] = 0; + + // Remove RNTI if all bearers are removed + bool rem = true; + for(int i=0;iallocate(); + run_enable = true; + + running=true; + while(run_enable) { + pdu->reset(); + gtpu_log->debug("Waiting for read...\n"); + pdu->N_bytes = srslte_netsource_read(&src, pdu->msg, SRSENB_MAX_BUFFER_SIZE_BYTES - SRSENB_BUFFER_HEADER_OFFSET); + + + gtpu_header_t header; + gtpu_read_header(pdu, &header); + + uint16_t rnti = 0; + uint16_t lcid = 0; + teidin_to_rntilcid(header.teid, &rnti, &lcid); + + pthread_mutex_lock(&mutex); + bool user_exists = (rnti_bearers.count(rnti) > 0); + pthread_mutex_unlock(&mutex); + + if(!user_exists) { + gtpu_log->error("Unrecognized RNTI for DL PDU: 0x%x - dropping packet\n", rnti); + continue; + } + + if(lcid < SRSENB_N_SRB || lcid >= SRSENB_N_RADIO_BEARERS) { + gtpu_log->error("Invalid LCID for DL PDU: %d - dropping packet\n", lcid); + continue; + } + + gtpu_log->info_hex(pdu->msg, pdu->N_bytes, "RX GTPU PDU rnti=0x%x, lcid=%d", rnti, lcid); + + pdcp->write_sdu(rnti, lcid, pdu); + do { + pdu = pool->allocate(); + if (!pdu) { + gtpu_log->console("GTPU Buffer pool empty. Trying again...\n"); + usleep(10000); + } + } while(!pdu); + } + running=false; +} + +/**************************************************************************** + * Header pack/unpack helper functions + * Ref: 3GPP TS 29.281 v10.1.0 Section 5 + ***************************************************************************/ + +bool gtpu::gtpu_write_header(gtpu_header_t *header, srslte::byte_buffer_t *pdu) +{ + if(header->flags != 0x30) { + gtpu_log->error("gtpu_write_header - Unhandled header flags: 0x%x\n", header->flags); + return false; + } + if(header->message_type != 0xFF) { + gtpu_log->error("gtpu_write_header - Unhandled message type: 0x%x\n", header->message_type); + return false; + } + if(pdu->get_headroom() < GTPU_HEADER_LEN) { + gtpu_log->error("gtpu_write_header - No room in PDU for header\n"); + return false; + } + + pdu->msg -= GTPU_HEADER_LEN; + pdu->N_bytes += GTPU_HEADER_LEN; + + uint8_t *ptr = pdu->msg; + + *ptr = header->flags; + ptr++; + *ptr = header->message_type; + ptr++; + uint16_to_uint8(header->length, ptr); + ptr += 2; + uint32_to_uint8(header->teid, ptr); + + return true; +} + +bool gtpu::gtpu_read_header(srslte::byte_buffer_t *pdu, gtpu_header_t *header) +{ + uint8_t *ptr = pdu->msg; + + pdu->msg += GTPU_HEADER_LEN; + pdu->N_bytes -= GTPU_HEADER_LEN; + + header->flags = *ptr; + ptr++; + header->message_type = *ptr; + ptr++; + uint8_to_uint16(ptr, &header->length); + ptr += 2; + uint8_to_uint32(ptr, &header->teid); + + if(header->flags != 0x30) { + gtpu_log->error("gtpu_read_header - Unhandled header flags: 0x%x\n", header->flags); + return false; + } + if(header->message_type != 0xFF) { + gtpu_log->error("gtpu_read_header - Unhandled message type: 0x%x\n", header->message_type); + return false; + } + + return true; +} + +/**************************************************************************** + * TEID to RNIT/LCID helper functions + ***************************************************************************/ +void gtpu::teidin_to_rntilcid(uint32_t teidin, uint16_t *rnti, uint16_t *lcid) +{ + *lcid = teidin & 0xFFFF; + *rnti = (teidin >> 16) & 0xFFFF; +} + +void gtpu::rntilcid_to_teidin(uint16_t rnti, uint16_t lcid, uint32_t *teidin) +{ + *teidin = (rnti << 16) | lcid; +} + +} // namespace srsenb diff --git a/srsenb/src/upper/pdcp.cc b/srsenb/src/upper/pdcp.cc new file mode 100644 index 000000000..178ddabd2 --- /dev/null +++ b/srsenb/src/upper/pdcp.cc @@ -0,0 +1,122 @@ +#include "upper/pdcp.h" + +namespace srsenb { + +void pdcp::init(rlc_interface_pdcp* rlc_, rrc_interface_pdcp* rrc_, gtpu_interface_pdcp* gtpu_, srslte::log* pdcp_log_) +{ + rlc = rlc_; + rrc = rrc_; + gtpu = gtpu_; + log_h = pdcp_log_; + + pool = srslte::byte_buffer_pool::get_instance(); +} + +void pdcp::stop() +{ + for(std::map::iterator iter=users.begin(); iter!=users.end(); ++iter) { + rem_user((uint32_t) iter->first); + } + users.clear(); +} + +void pdcp::add_user(uint16_t rnti) +{ + if (users.count(rnti) == 0) { + srslte::pdcp *obj = new srslte::pdcp; + obj->init(&users[rnti].rlc_itf, &users[rnti].rrc_itf, &users[rnti].gtpu_itf, log_h, SECURITY_DIRECTION_DOWNLINK); + users[rnti].rlc_itf.rnti = rnti; + users[rnti].gtpu_itf.rnti = rnti; + users[rnti].rrc_itf.rnti = rnti; + + users[rnti].rrc_itf.rrc = rrc; + users[rnti].rlc_itf.rlc = rlc; + users[rnti].gtpu_itf.gtpu = gtpu; + users[rnti].pdcp = obj; + } +} + +void pdcp::rem_user(uint16_t rnti) +{ + if (users.count(rnti)) { + users[rnti].pdcp->stop(); + delete users[rnti].pdcp; + users[rnti].pdcp = NULL; + users.erase(rnti); + } +} + +void pdcp::add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_PDCP_CONFIG_STRUCT* cnfg) +{ + if (users.count(rnti)) { + users[rnti].pdcp->add_bearer(lcid, cnfg); + } +} + + +void pdcp::reset(uint16_t rnti) +{ + if (users.count(rnti)) { + users[rnti].pdcp->reset(); + } +} + +void pdcp::config_security(uint16_t rnti, uint32_t lcid, uint8_t* k_rrc_enc_, uint8_t* k_rrc_int_, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo_, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo_) +{ + if (users.count(rnti)) { + users[rnti].pdcp->config_security(lcid, k_rrc_enc_, k_rrc_int_, cipher_algo_, integ_algo_); + } +} + +void pdcp::write_pdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + if (users.count(rnti)) { + users[rnti].pdcp->write_pdu(lcid, sdu); + } else { + pool->deallocate(sdu); + } +} + +void pdcp::write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + if (users.count(rnti)) { + users[rnti].pdcp->write_sdu(lcid, sdu); + } else { + pool->deallocate(sdu); + } +} + +void pdcp::user_interface_gtpu::write_pdu(uint32_t lcid, srslte::byte_buffer_t *pdu) +{ + gtpu->write_pdu(rnti, lcid, pdu); +} + +void pdcp::user_interface_rlc::write_sdu(uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + rlc->write_sdu(rnti, lcid, sdu); +} + +void pdcp::user_interface_rrc::write_pdu(uint32_t lcid, srslte::byte_buffer_t* pdu) +{ + rrc->write_pdu(rnti, lcid, pdu); +} + +void pdcp::user_interface_rrc::write_pdu_bcch_bch(srslte::byte_buffer_t* pdu) +{ + fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti); +} + +void pdcp::user_interface_rrc::write_pdu_bcch_dlsch(srslte::byte_buffer_t* pdu) +{ + fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti); +} + +void pdcp::user_interface_rrc::write_pdu_pcch(srslte::byte_buffer_t* pdu) +{ + fprintf(stderr, "Error: Received PCCH from ue=%d\n", rnti); +} + + +} diff --git a/srsenb/src/upper/rlc.cc b/srsenb/src/upper/rlc.cc new file mode 100644 index 000000000..4fdbf5db2 --- /dev/null +++ b/srsenb/src/upper/rlc.cc @@ -0,0 +1,161 @@ +#include "upper/rlc.h" + +namespace srsenb { + +void rlc::init(pdcp_interface_rlc* pdcp_, rrc_interface_rlc* rrc_, mac_interface_rlc *mac_, + srslte::mac_interface_timers *mac_timers_, srslte::log* log_h_) +{ + pdcp = pdcp_; + rrc = rrc_, + log_h = log_h_; + mac = mac_; + mac_timers = mac_timers_; + + pool = srslte::byte_buffer_pool::get_instance(); + +} + +void rlc::stop() +{ + for(std::map::iterator iter=users.begin(); iter!=users.end(); ++iter) { + rem_user((uint32_t) iter->first); + } + users.clear(); +} + +void rlc::add_user(uint16_t rnti) +{ + if (users.count(rnti) == 0) { + srslte::rlc *obj = new srslte::rlc; + obj->init(&users[rnti], &users[rnti], &users[rnti], log_h, mac_timers); + users[rnti].rnti = rnti; + users[rnti].pdcp = pdcp; + users[rnti].rrc = rrc; + users[rnti].rlc = obj; + users[rnti].parent = this; + } +} + +void rlc::rem_user(uint16_t rnti) +{ + if (users.count(rnti)) { + users[rnti].rlc->stop(); + delete users[rnti].rlc; + users[rnti].rlc = NULL; + users.erase(rnti); + } +} + +void rlc::reset(uint16_t rnti) +{ + if (users.count(rnti)) { + users[rnti].rlc->reset(); + } +} + +void rlc::clear_buffer(uint16_t rnti) +{ + if (users.count(rnti)) { + log_h->info("Clearing buffer rnti=0x%x\n", rnti); + users[rnti].rlc->reset(); + for (int i=0;irlc_buffer_state(rnti, i, 0, 0); + } + } +} + +void rlc::add_bearer(uint16_t rnti, uint32_t lcid) +{ + if (users.count(rnti)) { + users[rnti].rlc->add_bearer(lcid); + } +} + +void rlc::add_bearer(uint16_t rnti, uint32_t lcid, LIBLTE_RRC_RLC_CONFIG_STRUCT* cnfg) +{ + if (users.count(rnti)) { + users[rnti].rlc->add_bearer(lcid, cnfg); + } +} + +void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) +{ + rrc->read_pdu_pcch(payload, buffer_size); +} + +int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) +{ + int ret = users[rnti].rlc->read_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 PDU is read + uint32_t tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); + uint32_t retx_queue = 0; + log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); + mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); + + return ret; +} + +void rlc::write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) +{ + if (users.count(rnti)) { + users[rnti].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].rlc->get_total_buffer_state(lcid); + uint32_t retx_queue = 0; + log_h->debug("Buffer state PDCP: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); + 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 + rrc->read_pdu_bcch_dlsch(sib_index, payload); +} + +void rlc::write_sdu(uint16_t rnti, uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + if (users.count(rnti)) { + users[rnti].rlc->write_sdu(lcid, sdu); + + // 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 tx_queue = users[rnti].rlc->get_total_buffer_state(lcid); + uint32_t retx_queue = 0; + log_h->info("Buffer state: rnti=0x%x, lcid=%d, tx_queue=%d\n", rnti, lcid, tx_queue); + mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); + } else { + pool->deallocate(sdu); + } +} + +void rlc::user_interface::max_retx_attempted() +{ + rrc->max_retx_attempted(rnti); +} + +void rlc::user_interface::write_pdu(uint32_t lcid, srslte::byte_buffer_t* sdu) +{ + pdcp->write_pdu(rnti, lcid, sdu); +} + +void rlc::user_interface::write_pdu_bcch_bch(srslte::byte_buffer_t* sdu) +{ + fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti); +} + +void rlc::user_interface::write_pdu_bcch_dlsch(srslte::byte_buffer_t* sdu) +{ + fprintf(stderr, "Error: Received BCCH from ue=%d\n", rnti); +} + +void rlc::user_interface::write_pdu_pcch(srslte::byte_buffer_t* sdu) +{ + fprintf(stderr, "Error: Received PCCH from ue=%d\n", rnti); +} + +} diff --git a/srsenb/src/upper/rrc.cc b/srsenb/src/upper/rrc.cc new file mode 100644 index 000000000..69d547fdd --- /dev/null +++ b/srsenb/src/upper/rrc.cc @@ -0,0 +1,1612 @@ + +#include "srslte/asn1/liblte_mme.h" +#include "upper/rrc.h" + +using srslte::rb_id_text; +using srslte::byte_buffer_t; +using srslte::bit_buffer_t; +using srslte::rb_id_t; + +namespace srsenb { + +void rrc::init(rrc_cfg_t *cfg_, + phy_interface_rrc* phy_, + mac_interface_rrc* mac_, + rlc_interface_rrc* rlc_, + pdcp_interface_rrc* pdcp_, + s1ap_interface_rrc *s1ap_, + gtpu_interface_rrc* gtpu_, + srslte::log* log_rrc) +{ + phy = phy_; + mac = mac_; + rlc = rlc_; + pdcp = pdcp_; + gtpu = gtpu_; + s1ap = s1ap_; + rrc_log = log_rrc; + cnotifier = NULL; + + running = false; + pool = srslte::byte_buffer_pool::get_instance(); + + memcpy(&cfg, cfg_, sizeof(rrc_cfg_t)); + nof_si_messages = generate_sibs(); + config_mac(); + + pthread_mutex_init(&user_mutex, NULL); + pthread_mutex_init(&paging_mutex, NULL); + + bzero(&sr_sched, sizeof(sr_sched_t)); + + start(RRC_THREAD_PRIO); +} + +rrc::activity_monitor::activity_monitor(rrc* parent_) +{ + running = true; + parent = parent_; + start(RRC_THREAD_PRIO); +} + +void rrc::activity_monitor::stop() +{ + if (running) { + running = false; + thread_cancel(); + wait_thread_finish(); + } +} + +void rrc::set_connect_notifer(connect_notifier *cnotifier) +{ + this->cnotifier = cnotifier; +} + +void rrc::stop() +{ + if(running) { + running = false; + thread_cancel(); + wait_thread_finish(); + } + act_monitor.stop(); + users.clear(); + pthread_mutex_destroy(&user_mutex); + pthread_mutex_destroy(&paging_mutex); +} + +void rrc::get_metrics(rrc_metrics_t &m) +{ + pthread_mutex_lock(&user_mutex); + m.n_ues = 0; + for(std::map::iterator iter=users.begin(); m.n_ues < ENB_METRICS_MAX_USERS &&iter!=users.end(); ++iter) { + ue *u = (ue*) &iter->second; + m.ues[m.n_ues++].state = u->get_state(); + } + pthread_mutex_unlock(&user_mutex); +} + +uint32_t rrc::generate_sibs() +{ + uint32_t nof_messages = 1+cfg.sibs[0].sib.sib1.N_sched_info; + LIBLTE_RRC_SCHEDULING_INFO_STRUCT *sched_info = cfg.sibs[0].sib.sib1.sched_info; + + // Allocate DSLCH msg structs + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT *msg = (LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT*)calloc(nof_messages, sizeof(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT)); + + // Copy SIB1 + msg[0].N_sibs = 1; + memcpy(&msg[0].sibs[0], &cfg.sibs[0], sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_STRUCT)); + + // Copy rest of SIBs + for (uint32_t i=1;icell_cfg(&sched_cfg); +} + + +void rrc::read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t* payload) +{ + if (sib_index < LIBLTE_RRC_MAX_SIB) { + memcpy(payload, sib_buffer[sib_index].msg, sib_buffer[sib_index].N_bytes); + } +} + +void rrc::rl_failure(uint16_t rnti) +{ + rrc_log->info("Radio-Link failure detected rnti=0x%x\n", rnti); + if (s1ap->user_exists(rnti)) { + if (!s1ap->user_link_lost(rnti)) { + rrc_log->info("Removing rnti=0x%x\n", rnti); + rem_user_thread(rnti); + } + } else { + rrc_log->warning("User rnti=0x%x context not existing in S1AP. Removing user\n", rnti); + rem_user_thread(rnti); + } +} + +void rrc::add_user(uint16_t rnti) +{ + pthread_mutex_lock(&user_mutex); + if (users.count(rnti) == 0) { + users[rnti].parent = this; + users[rnti].rnti = rnti; + rlc->add_user(rnti); + pdcp->add_user(rnti); + rrc_log->info("Added new user rnti=0x%x\n", rnti); + } else { + rrc_log->error("Adding user rnti=0x%x (already exists)\n"); + } + pthread_mutex_unlock(&user_mutex); +} + +void rrc::rem_user(uint16_t rnti) +{ + pthread_mutex_lock(&user_mutex); + if (users.count(rnti) == 1) { + rrc_log->console("Disconnecting rnti=0x%x.\n", rnti); + rrc_log->info("Disconnecting rnti=0x%x.\n", rnti); + /* **Caution** order of removal here is imporant: from bottom to top */ + mac->ue_rem(rnti); // MAC handles PHY + rlc->rem_user(rnti); + pdcp->rem_user(rnti); + gtpu->rem_user(rnti); + users[rnti].sr_free(); + users[rnti].cqi_free(); + users.erase(rnti); + rrc_log->info("Removed user rnti=0x%x\n", rnti); + } else { + rrc_log->error("Removing user rnti=0x%x (does not exist)\n", rnti); + } + pthread_mutex_unlock(&user_mutex); +} + +// Function called by MAC after the reception of a C-RNTI CE indicating that the UE still has a +// valid RNTI +void rrc::upd_user(uint16_t new_rnti, uint16_t old_rnti) +{ + // Remove new_rnti + rem_user_thread(new_rnti); + + // Send Reconfiguration to old_rnti if is RRC_CONNECT or RRC Release if already released here + if (users.count(old_rnti) == 1) { + if (users[old_rnti].is_connected()) { + users[old_rnti].send_connection_reconf_upd(pool->allocate()); + } else { + users[old_rnti].send_connection_release(); + } + } +} + +void rrc::set_activity_user(uint16_t rnti) +{ + if (users.count(rnti) == 1) { + users[rnti].set_activity(); + } +} + +void rrc::rem_user_thread(uint16_t rnti) +{ + if (users.count(rnti) == 1) { + rrc_pdu p = {rnti, LCID_REM_USER, NULL}; + rx_pdu_queue.push(p); + } +} + +uint32_t rrc::get_nof_users() { + return users.size(); +} + +void rrc::max_retx_attempted(uint16_t rnti) +{ + +} + +/******************************************************************************* + PDCP interface +*******************************************************************************/ +void rrc::write_pdu(uint16_t rnti, uint32_t lcid, byte_buffer_t* pdu) +{ + rrc_pdu p = {rnti, lcid, pdu}; + rx_pdu_queue.push(p); +} + +/******************************************************************************* + S1AP interface +*******************************************************************************/ +void rrc::write_dl_info(uint16_t rnti, byte_buffer_t* sdu) +{ + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + bzero(&dl_dcch_msg, sizeof(LIBLTE_RRC_DL_DCCH_MSG_STRUCT)); + + if (users.count(rnti) == 1) { + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_DL_INFO_TRANSFER; + memcpy(dl_dcch_msg.msg.dl_info_transfer.dedicated_info.msg, sdu->msg, sdu->N_bytes); + dl_dcch_msg.msg.dl_info_transfer.dedicated_info.N_bytes = sdu->N_bytes; + + sdu->reset(); + + users[rnti].send_dl_dcch(&dl_dcch_msg, sdu); + + } else { + rrc_log->error("Rx SDU for unknown rnti=0x%x\n", rnti); + } +} + +void rrc::release_complete(uint16_t rnti) +{ + rrc_log->info("Received Release Complete rnti=0x%x\n", rnti); + if (users.count(rnti) == 1) { + if (!users[rnti].is_idle()) { + rlc->clear_buffer(rnti); + users[rnti].send_connection_release(); + // There is no RRCReleaseComplete message from UE thus sleep to enable all retx in PHY +50% + usleep(1.5*8*1e3*cfg.mac_cnfg.ulsch_cnfg.max_harq_tx); + } + rem_user(rnti); + } else { + + rrc_log->error("Received ReleaseComplete for unknown rnti=0x%x\n", rnti); + } +} + +bool rrc::setup_ue_ctxt(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg) +{ + rrc_log->info("Adding initial context for 0x%x\n", rnti); + + if(users.count(rnti) == 0) { + rrc_log->warning("Unrecognised rnti: 0x%x\n", rnti); + return false; + } + + if(msg->CSFallbackIndicator_present) { + rrc_log->warning("Not handling CSFallbackIndicator\n"); + } + if(msg->AdditionalCSFallbackIndicator_present) { + rrc_log->warning("Not handling AdditionalCSFallbackIndicator\n"); + } + if(msg->CSGMembershipStatus_present) { + rrc_log->warning("Not handling CSGMembershipStatus\n"); + } + if(msg->GUMMEI_ID_present) { + rrc_log->warning("Not handling GUMMEI_ID\n"); + } + if(msg->HandoverRestrictionList_present) { + rrc_log->warning("Not handling HandoverRestrictionList\n"); + } + if(msg->ManagementBasedMDTAllowed_present) { + rrc_log->warning("Not handling ManagementBasedMDTAllowed\n"); + } + if(msg->ManagementBasedMDTPLMNList_present) { + rrc_log->warning("Not handling ManagementBasedMDTPLMNList\n"); + } + if(msg->MME_UE_S1AP_ID_2_present) { + rrc_log->warning("Not handling MME_UE_S1AP_ID_2\n"); + } + if(msg->RegisteredLAI_present) { + rrc_log->warning("Not handling RegisteredLAI\n"); + } + if(msg->SRVCCOperationPossible_present) { + rrc_log->warning("Not handling SRVCCOperationPossible\n"); + } + if(msg->SubscriberProfileIDforRFP_present) { + rrc_log->warning("Not handling SubscriberProfileIDforRFP\n"); + } + if(msg->TraceActivation_present) { + rrc_log->warning("Not handling TraceActivation\n"); + } + if(msg->UERadioCapability_present) { + rrc_log->warning("Not handling UERadioCapability\n"); + } + + // UEAggregateMaximumBitrate + users[rnti].set_bitrates(&msg->uEaggregateMaximumBitrate); + + // UESecurityCapabilities + users[rnti].set_security_capabilities(&msg->UESecurityCapabilities); + + // SecurityKey + uint8_t key[32]; + liblte_pack(msg->SecurityKey.buffer, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN, key); + users[rnti].set_security_key(key, LIBLTE_S1AP_SECURITYKEY_BIT_STRING_LEN/8); + + // Send RRC security mode command + users[rnti].send_security_mode_command(); + + // Setup E-RABs + users[rnti].setup_erabs(&msg->E_RABToBeSetupListCtxtSUReq); + + return true; +} + +bool rrc::setup_ue_erabs(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg) +{ + rrc_log->info("Setting up erab(s) for 0x%x\n", rnti); + + if(users.count(rnti) == 0) { + rrc_log->warning("Unrecognised rnti: 0x%x\n", rnti); + return false; + } + + if(msg->uEaggregateMaximumBitrate_present) { + // UEAggregateMaximumBitrate + users[rnti].set_bitrates(&msg->uEaggregateMaximumBitrate); + } + + // Setup E-RABs + users[rnti].setup_erabs(&msg->E_RABToBeSetupListBearerSUReq); + + return true; +} + +bool rrc::release_erabs(uint32_t rnti) +{ + rrc_log->info("Releasing E-RABs for 0x%x\n", rnti); + + if(users.count(rnti) == 0) { + rrc_log->warning("Unrecognised rnti: 0x%x\n", rnti); + return false; + } + + return users[rnti].release_erabs(); +} + +void rrc::add_paging_id(uint32_t ueid, LIBLTE_S1AP_UEPAGINGID_STRUCT UEPagingID) +{ + pthread_mutex_lock(&paging_mutex); + if (pending_paging.count(ueid) == 0) { + pending_paging[ueid] = UEPagingID; + } else { + rrc_log->warning("Received Paging for UEID=%d but not yet transmitted\n", ueid); + } + pthread_mutex_unlock(&paging_mutex); +} + +// Described in Section 7 of 36.304 +bool rrc::is_paging_opportunity(uint32_t tti, uint32_t *payload_len) +{ + int sf_pattern[4][3] = {{9, 4, 0}, {-1, 9, 4}, {-1, -1, 5}, {-1, -1, 9}}; + + if (pending_paging.empty()) { + return false; + } + + pthread_mutex_lock(&paging_mutex); + + LIBLTE_RRC_PCCH_MSG_STRUCT pcch_msg; + bzero(&pcch_msg, sizeof(LIBLTE_RRC_PCCH_MSG_STRUCT)); + + // Default paging cycle, should get DRX from user + uint32_t T = liblte_rrc_default_paging_cycle_num[cfg.sibs[1].sib.sib2.rr_config_common_sib.pcch_cnfg.default_paging_cycle]; + uint32_t Nb = T*liblte_rrc_nb_num[cfg.sibs[1].sib.sib2.rr_config_common_sib.pcch_cnfg.nB]; + + uint32_t N = T1?Nb/T:1; + uint32_t sfn = tti/10; + + std::vector ue_to_remove; + + int n=0; + for(std::map::iterator iter=pending_paging.begin(); n < LIBLTE_RRC_MAX_PAGE_REC && iter!=pending_paging.end(); ++iter) { + LIBLTE_S1AP_UEPAGINGID_STRUCT u = (LIBLTE_S1AP_UEPAGINGID_STRUCT) iter->second; + uint32_t ueid = ((uint32_t) iter->first)%1024; + uint32_t i_s = (ueid/N) % Ns; + + if ((sfn % T) == (T/N) * (ueid % N)) { + + int sf_idx = sf_pattern[i_s%4][(Ns-1)%3]; + if (sf_idx < 0) { + rrc_log->error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d\n", Ns, i_s, ueid); + } else if ((uint32_t) sf_idx == (tti%10)) { + + if (u.choice_type == LIBLTE_S1AP_UEPAGINGID_CHOICE_IMSI) { + pcch_msg.paging_record_list[n].ue_identity.ue_identity_type = LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_IMSI; + memcpy(pcch_msg.paging_record_list[n].ue_identity.imsi, u.choice.iMSI.buffer, u.choice.iMSI.n_octets); + pcch_msg.paging_record_list[n].ue_identity.imsi_size = u.choice.iMSI.n_octets; + printf("Warning IMSI paging not tested\n"); + } else { + pcch_msg.paging_record_list[n].ue_identity.ue_identity_type = LIBLTE_RRC_PAGING_UE_IDENTITY_TYPE_S_TMSI; + pcch_msg.paging_record_list[n].ue_identity.s_tmsi.mmec = u.choice.s_TMSI.mMEC.buffer[0]; + uint32_t m_tmsi = 0; + for (int i=0;iinfo("Assembled paging for ue_id=%d, tti=%d\n", ueid, tti); + } + } + } + + for (uint32_t i=0;i 0) { + pcch_msg.paging_record_list_size = n; + liblte_rrc_pack_pcch_msg(&pcch_msg, (LIBLTE_BIT_MSG_STRUCT*)&bit_buf_paging); + uint32_t N_bytes = (bit_buf_paging.N_bits-1)/8+1; + + if (payload_len) { + *payload_len = N_bytes; + } + rrc_log->info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes, nbits=%d\n", + pcch_msg.paging_record_list_size, N_bytes, bit_buf_paging.N_bits); + return true; + } + + return false; +} + + +void rrc::read_pdu_pcch(uint8_t *payload, uint32_t buffer_size) +{ + uint32_t N_bytes = (bit_buf_paging.N_bits-1)/8+1; + if (N_bytes <= buffer_size) { + srslte_bit_pack_vector(bit_buf_paging.msg, payload, bit_buf_paging.N_bits); + } +} + +/******************************************************************************* + Parsers +*******************************************************************************/ + +void rrc::parse_ul_ccch(uint16_t rnti, byte_buffer_t *pdu) +{ + uint16_t old_rnti = 0; + + LIBLTE_RRC_UL_CCCH_MSG_STRUCT ul_ccch_msg; + bzero(&ul_ccch_msg, sizeof(LIBLTE_RRC_UL_CCCH_MSG_STRUCT)); + + srslte_bit_unpack_vector(pdu->msg, bit_buf.msg, pdu->N_bytes*8); + bit_buf.N_bits = pdu->N_bytes*8; + liblte_rrc_unpack_ul_ccch_msg((LIBLTE_BIT_MSG_STRUCT*)&bit_buf, &ul_ccch_msg); + + rrc_log->info_hex(pdu->msg, pdu->N_bytes, + "SRB0 - Rx: %s", + liblte_rrc_ul_ccch_msg_type_text[ul_ccch_msg.msg_type]); + + switch(ul_ccch_msg.msg_type) { + case LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REQ: + if (users.count(rnti)) { + users[rnti].handle_rrc_con_req(&ul_ccch_msg.msg.rrc_con_req); + } else { + rrc_log->error("Received ConnectionSetup for rnti=0x%x without context\n", rnti); + } + break; + case LIBLTE_RRC_UL_CCCH_MSG_TYPE_RRC_CON_REEST_REQ: + rrc_log->debug("rnti=0x%x, phyid=0x%x, smac=0x%x, cause=%s\n", + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.c_rnti, ul_ccch_msg.msg.rrc_con_reest_req.ue_id.phys_cell_id, + ul_ccch_msg.msg.rrc_con_reest_req.ue_id.short_mac_i, liblte_rrc_con_reest_req_cause_text[ul_ccch_msg.msg.rrc_con_reest_req.cause] + ); + if (users[rnti].is_idle()) { + old_rnti = ul_ccch_msg.msg.rrc_con_reest_req.ue_id.c_rnti; + if (users.count(old_rnti)) { + rrc_log->error("Not supported: ConnectionReestablishment. Sending Connection Reject\n", old_rnti); + users[rnti].send_connection_reest_rej(); + rem_user_thread(old_rnti); + } else { + rrc_log->error("Received ConnectionReestablishment for rnti=0x%x without context\n", old_rnti); + users[rnti].send_connection_reest_rej(); + } + // remove temporal rnti + rem_user_thread(rnti); + } else { + rrc_log->error("Received ReestablishmentRequest from an rnti=0x%x not in IDLE\n", rnti); + } + break; + default: + rrc_log->error("UL CCCH message not recognised\n"); + break; + } + + pool->deallocate(pdu); +} + +void rrc::parse_ul_dcch(uint16_t rnti, uint32_t lcid, byte_buffer_t *pdu) +{ + if (users.count(rnti)) { + users[rnti].parse_ul_dcch(lcid, pdu); + } else { + rrc_log->error("Processing %s: Unkown rnti=0x%x\n", rb_id_text[lcid], rnti); + } +} + +/******************************************************************************* + RRC thread +*******************************************************************************/ + +void rrc::run_thread() +{ + rrc_pdu p; + running = true; + + while(running) { + p = rx_pdu_queue.wait_pop(); + if (p.pdu) { + rrc_log->info_hex(p.pdu->msg, p.pdu->N_bytes, "Rx %s PDU", rb_id_text[p.lcid]); + } + switch(p.lcid) + { + case srslte::RB_ID_SRB0: + parse_ul_ccch(p.rnti, p.pdu); + break; + case srslte::RB_ID_SRB1: + case srslte::RB_ID_SRB2: + parse_ul_dcch(p.rnti, p.lcid, p.pdu); + break; + case LCID_REM_USER: + usleep(10000); + rem_user(p.rnti); + break; + default: + rrc_log->error("Rx PDU with invalid bearer id: %s", p.lcid); + break; + } + } +} +void rrc::activity_monitor::run_thread() +{ + while(running) + { + usleep(10000); + pthread_mutex_lock(&parent->user_mutex); + uint16_t rem_rnti = 0; + for(std::map::iterator iter=parent->users.begin(); rem_rnti == 0 && iter!=parent->users.end(); ++iter) { + ue *u = (ue*) &iter->second; + uint16_t rnti = (uint16_t) iter->first; + + if (parent->cnotifier && u->is_connected() && !u->connect_notified) { + parent->cnotifier->user_connected(rnti); + u->connect_notified = true; + } + + if (u->is_timeout()) { + parent->rrc_log->info("User rnti=0x%x timed out. Exists in s1ap=%s\n", rnti, parent->s1ap->user_exists(rnti)?"yes":"no"); + rem_rnti = rnti; + } + } + pthread_mutex_unlock(&parent->user_mutex); + if (rem_rnti) { + if (parent->s1ap->user_exists(rem_rnti)) { + parent->s1ap->user_inactivity(rem_rnti); + } else { + parent->rem_user(rem_rnti); + } + } + } +} + +/******************************************************************************* + RRC::UE Helpers +*******************************************************************************/ + +void rrc::configure_security(uint16_t rnti, + uint32_t lcid, + uint8_t *k_rrc_enc, + uint8_t *k_rrc_int, + uint8_t *k_up_enc, + uint8_t *k_up_int, + srslte::CIPHERING_ALGORITHM_ID_ENUM cipher_algo, + srslte::INTEGRITY_ALGORITHM_ID_ENUM integ_algo) +{ + // TODO: add k_up_enc, k_up_int support to PDCP + pdcp->config_security(rnti, lcid, k_rrc_enc, k_rrc_int, cipher_algo, integ_algo); +} + + + + +/******************************************************************************* + UE class +*******************************************************************************/ +rrc::ue::ue() +{ + parent = NULL; + set_activity(); + sr_allocated = false; + has_tmsi = false; + connect_notified = false; + transaction_id = 0; + state = RRC_STATE_IDLE; +} + +rrc_state_t rrc::ue::get_state() +{ + return state; +} + +void rrc::ue::set_activity() +{ + gettimeofday(&t_last_activity, NULL); + if (parent) { + if (parent->rrc_log) { + parent->rrc_log->debug("Activity registered rnti=0x%x\n", rnti); + } + } +} + +bool rrc::ue::is_connected() { + return state == RRC_STATE_REGISTERED; +} + +bool rrc::ue::is_idle() { + return state == RRC_STATE_IDLE; +} + +bool rrc::ue::is_timeout() +{ + + if (!parent) { + return false; + } + + struct timeval t[3]; + uint32_t deadline_s = 0; + uint32_t deadline_us = 0; + const char *deadline_str = NULL; + memcpy(&t[1], &t_last_activity, sizeof(struct timeval)); + gettimeofday(&t[2], NULL); + get_time_interval(t); + + switch(state) { + case RRC_STATE_IDLE: + deadline_s = 0; + deadline_us = (parent->sib2.rr_config_common_sib.rach_cnfg.max_harq_msg3_tx + 1)* 8 * 1000; + deadline_str = "RRCConnectionSetup"; + break; + case RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE: + deadline_s = 1; + deadline_us = 0; + deadline_str = "RRCConnectionSetupComplete"; + break; + case RRC_STATE_RELEASE_REQUEST: + deadline_s = 4; + deadline_us = 0; + deadline_str = "RRCReleaseRequest"; + break; + default: + deadline_s = parent->cfg.inactivity_timeout_ms/1000; + deadline_us = (parent->cfg.inactivity_timeout_ms%1000)*1000; + deadline_str = "Activity"; + break; + } + + if (deadline_str) { + uint64_t deadline = deadline_s*1e6 + deadline_us; + uint64_t elapsed = t[0].tv_sec*1e6 + t[0].tv_usec; + if (elapsed > deadline) { + parent->rrc_log->warning("User rnti=0x%x expired %s deadline: %d:%d>%d:%d us\n", + rnti, deadline_str, + t[0].tv_sec, t[0].tv_usec, + deadline_s, deadline_us); + memcpy(&t_last_activity, &t[2], sizeof(struct timeval)); + state = RRC_STATE_RELEASE_REQUEST; + return true; + } + } + return false; +} + +void rrc::ue::parse_ul_dcch(uint32_t lcid, byte_buffer_t *pdu) +{ + + set_activity(); + + LIBLTE_RRC_UL_DCCH_MSG_STRUCT ul_dcch_msg; + bzero(&ul_dcch_msg, sizeof(LIBLTE_RRC_UL_DCCH_MSG_STRUCT)); + + srslte_bit_unpack_vector(pdu->msg, parent->bit_buf.msg, pdu->N_bytes*8); + parent->bit_buf.N_bits = pdu->N_bytes*8; + liblte_rrc_unpack_ul_dcch_msg((LIBLTE_BIT_MSG_STRUCT*)&parent->bit_buf, &ul_dcch_msg); + + parent->rrc_log->info_hex(pdu->msg, pdu->N_bytes, + "%s - Rx %s\n", + rb_id_text[lcid], liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]); + transaction_id = 0; + pdu->reset(); + + switch(ul_dcch_msg.msg_type) { + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_SETUP_COMPLETE: + handle_rrc_con_setup_complete(&ul_dcch_msg.msg.rrc_con_setup_complete, pdu); + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_UL_INFO_TRANSFER: + memcpy(pdu->msg, ul_dcch_msg.msg.ul_info_transfer.dedicated_info.msg, ul_dcch_msg.msg.ul_info_transfer.dedicated_info.N_bytes); + pdu->N_bytes = ul_dcch_msg.msg.ul_info_transfer.dedicated_info.N_bytes; + parent->s1ap->write_pdu(rnti, pdu); + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_RRC_CON_RECONFIG_COMPLETE: + parent->rrc_log->console("User 0x%x connected\n", rnti); + state = RRC_STATE_REGISTERED; + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_COMPLETE: + handle_security_mode_complete(&ul_dcch_msg.msg.security_mode_complete); + // Skipping send_ue_cap_enquiry() procedure for now + // state = RRC_STATE_WAIT_FOR_UE_CAP_INFO; + notify_s1ap_ue_ctxt_setup_complete(); + send_connection_reconf(pdu); + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_SECURITY_MODE_FAILURE: + handle_security_mode_failure(&ul_dcch_msg.msg.security_mode_failure); + break; + case LIBLTE_RRC_UL_DCCH_MSG_TYPE_UE_CAPABILITY_INFO: + handle_ue_cap_info(&ul_dcch_msg.msg.ue_capability_info); + send_connection_reconf(pdu); + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; + break; + default: + parent->rrc_log->error("Msg: %s not supported\n", liblte_rrc_ul_dcch_msg_type_text[ul_dcch_msg.msg_type]); + break; + } +} + +void rrc::ue::handle_rrc_con_req(LIBLTE_RRC_CONNECTION_REQUEST_STRUCT *msg) +{ + set_activity(); + + if(msg->ue_id_type == LIBLTE_RRC_CON_REQ_UE_ID_TYPE_S_TMSI) { + mmec = msg->ue_id.s_tmsi.mmec; + m_tmsi = msg->ue_id.s_tmsi.m_tmsi; + has_tmsi = true; + } + send_connection_setup(); + state = RRC_STATE_WAIT_FOR_CON_SETUP_COMPLETE; +} + +void rrc::ue::handle_rrc_con_reest_req(LIBLTE_RRC_CONNECTION_REESTABLISHMENT_REQUEST_STRUCT *msg) +{ + //TODO: Check Short-MAC-I value + parent->rrc_log->error("Not Supported: ConnectionReestablishment. \n"); + +} + +void rrc::ue::handle_rrc_con_setup_complete(LIBLTE_RRC_CONNECTION_SETUP_COMPLETE_STRUCT *msg, srslte::byte_buffer_t *pdu) +{ + parent->rrc_log->info("RRCConnectionSetupComplete transaction ID: %d\n", msg->rrc_transaction_id); + + // 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 + + memcpy(pdu->msg, msg->dedicated_info_nas.msg, msg->dedicated_info_nas.N_bytes); + pdu->N_bytes = msg->dedicated_info_nas.N_bytes; + + if(has_tmsi) { + parent->s1ap->initial_ue(rnti, pdu, m_tmsi, mmec); + } else { + parent->s1ap->initial_ue(rnti, pdu); + } + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; +} + +void rrc::ue::handle_security_mode_complete(LIBLTE_RRC_SECURITY_MODE_COMPLETE_STRUCT *msg) +{ + parent->rrc_log->info("SecurityModeComplete transaction ID: %d\n", msg->rrc_transaction_id); +} + +void rrc::ue::handle_security_mode_failure(LIBLTE_RRC_SECURITY_MODE_FAILURE_STRUCT *msg) +{ + parent->rrc_log->info("SecurityModeFailure transaction ID: %d\n", msg->rrc_transaction_id); +} + +void rrc::ue::handle_ue_cap_info(LIBLTE_RRC_UE_CAPABILITY_INFORMATION_STRUCT *msg) +{ + parent->rrc_log->info("UECapabilityInformation transaction ID: %d\n", msg->rrc_transaction_id); + for(uint32_t i=0; iN_ue_caps; i++) { + if(msg->ue_capability_rat[i].rat_type != LIBLTE_RRC_RAT_TYPE_EUTRA) { + parent->rrc_log->warning("Not handling UE capability information for RAT type %s\n", + liblte_rrc_rat_type_text[msg->ue_capability_rat[i].rat_type]); + } else { + memcpy(&eutra_capabilities, &msg->ue_capability_rat[0], sizeof(LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT)); + parent->rrc_log->info("UE rnti: 0x%x category: %d\n", rnti, msg->ue_capability_rat[0].eutra_capability.ue_category); + } + } + + // TODO: Add liblte_rrc support for unpacking UE cap info and repacking into + // inter-node UERadioAccessCapabilityInformation (36.331 v10.0.0 Section 10.2.2). + // This is then passed to S1AP for transfer to EPC. + // parent->s1ap->ue_capabilities(rnti, &eutra_capabilities); +} + +void rrc::ue::set_bitrates(LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT *rates) +{ + memcpy(&bitrates, rates, sizeof(LIBLTE_S1AP_UEAGGREGATEMAXIMUMBITRATE_STRUCT)); +} + +void rrc::ue::set_security_capabilities(LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT *caps) +{ + memcpy(&security_capabilities, caps, sizeof(LIBLTE_S1AP_UESECURITYCAPABILITIES_STRUCT)); +} + +void rrc::ue::set_security_key(uint8_t* key, uint32_t length) +{ + memcpy(k_enb, key, length); + + // Select algos (TODO: use security capabilities and config preferences) + cipher_algo = srslte::CIPHERING_ALGORITHM_ID_EEA0; + integ_algo = srslte::INTEGRITY_ALGORITHM_ID_128_EIA1; + + // Generate K_rrc_enc and K_rrc_int + security_generate_k_rrc( k_enb, + cipher_algo, + integ_algo, + k_rrc_enc, + k_rrc_int); + + // Generate K_up_enc and K_up_int + security_generate_k_up( k_enb, + cipher_algo, + integ_algo, + k_up_enc, + k_up_int); + + parent->configure_security(rnti, srslte::RB_ID_SRB1, + k_rrc_enc, k_rrc_int, + k_up_enc, k_up_int, + cipher_algo, integ_algo); +} + +bool rrc::ue::setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTCTXTSUREQ_STRUCT *e) +{ + for(uint32_t i=0; ilen; i++) { + LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT *erab = &e->buffer[i]; + if(erab->ext) { + parent->rrc_log->warning("Not handling LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT extensions\n"); + } + if(erab->iE_Extensions_present) { + parent->rrc_log->warning("Not handling LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT extensions\n"); + } + + uint8_t id = erab->e_RAB_ID.E_RAB_ID; + erabs[id].id = id; + memcpy(&erabs[id].qos_params, &erab->e_RABlevelQoSParameters, sizeof(LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT)); + memcpy(&erabs[id].address, &erab->transportLayerAddress, sizeof(LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT)); + uint8_to_uint32(erab->gTP_TEID.buffer, &erabs[id].teid_out); + + uint8_t lcid = id - 2; // Map e.g. E-RAB 5 to LCID 3 (==DRB1) + parent->gtpu->add_bearer(rnti, lcid, erabs[id].teid_out, &(erabs[id].teid_in)); + + if(erab->nAS_PDU_present) { + memcpy(parent->erab_info.msg, erab->nAS_PDU.buffer, erab->nAS_PDU.n_octets); + parent->erab_info.N_bytes = erab->nAS_PDU.n_octets; + } + } + return true; +} + +bool rrc::ue::setup_erabs(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e) +{ + for(uint32_t i=0; ilen; i++) { + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *erab = &e->buffer[i]; + if(erab->ext) { + parent->rrc_log->warning("Not handling LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT extensions\n"); + } + if(erab->iE_Extensions_present) { + parent->rrc_log->warning("Not handling LIBLTE_S1AP_E_RABTOBESETUPITEMCTXTSUREQ_STRUCT extensions\n"); + } + + uint8_t id = erab->e_RAB_ID.E_RAB_ID; + erabs[id].id = id; + memcpy(&erabs[id].qos_params, &erab->e_RABlevelQoSParameters, sizeof(LIBLTE_S1AP_E_RABLEVELQOSPARAMETERS_STRUCT)); + memcpy(&erabs[id].address, &erab->transportLayerAddress, sizeof(LIBLTE_S1AP_TRANSPORTLAYERADDRESS_STRUCT)); + uint8_to_uint32(erab->gTP_TEID.buffer, &erabs[id].teid_out); + + uint8_t lcid = id - 2; // Map e.g. E-RAB 5 to LCID 3 (==DRB1) + parent->gtpu->add_bearer(rnti, lcid, erabs[id].teid_out, &(erabs[id].teid_in)); + + memcpy(parent->erab_info.msg, erab->nAS_PDU.buffer, erab->nAS_PDU.n_octets); + parent->erab_info.N_bytes = erab->nAS_PDU.n_octets; + } + // Work in progress + notify_s1ap_ue_erab_setup_response(e); + send_connection_reconf_new_bearer(e); + return true; +} + +bool rrc::ue::release_erabs() +{ + typedef std::map::iterator it_t; + for(it_t it=erabs.begin(); it!=erabs.end(); ++it) { + // TODO: notify GTPU layer + } + erabs.clear(); + return true; +} + +void rrc::ue::notify_s1ap_ue_ctxt_setup_complete() +{ + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT res; + res.E_RABSetupListCtxtSURes.len = 0; + res.E_RABFailedToSetupListCtxtSURes.len = 0; + + typedef std::map::iterator it_t; + for(it_t it=erabs.begin(); it!=erabs.end(); ++it) { + uint32_t j = res.E_RABSetupListCtxtSURes.len++; + res.E_RABSetupListCtxtSURes.buffer[j].ext = false; + res.E_RABSetupListCtxtSURes.buffer[j].iE_Extensions_present = false; + res.E_RABSetupListCtxtSURes.buffer[j].e_RAB_ID.ext = false; + res.E_RABSetupListCtxtSURes.buffer[j].e_RAB_ID.E_RAB_ID = it->second.id; + uint32_to_uint8(it->second.teid_in, res.E_RABSetupListCtxtSURes.buffer[j].gTP_TEID.buffer); + } + + parent->s1ap->ue_ctxt_setup_complete(rnti, &res); +} + +void rrc::ue::notify_s1ap_ue_erab_setup_response(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e) +{ + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT res; + res.E_RABSetupListBearerSURes.len = 0; + res.E_RABFailedToSetupListBearerSURes.len = 0; + + for(uint32_t i=0; ilen; i++) { + res.E_RABSetupListBearerSURes_present = true; + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *erab = &e->buffer[i]; + uint8_t id = erab->e_RAB_ID.E_RAB_ID; + uint32_t j = res.E_RABSetupListBearerSURes.len++; + res.E_RABSetupListBearerSURes.buffer[j].ext = false; + res.E_RABSetupListBearerSURes.buffer[j].iE_Extensions_present = false; + res.E_RABSetupListBearerSURes.buffer[j].e_RAB_ID.ext = false; + res.E_RABSetupListBearerSURes.buffer[j].e_RAB_ID.E_RAB_ID = id; + uint32_to_uint8(erabs[id].teid_in, res.E_RABSetupListBearerSURes.buffer[j].gTP_TEID.buffer); + } + + parent->s1ap->ue_erab_setup_complete(rnti, &res); +} + +void rrc::ue::send_connection_reest_rej() +{ + LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg; + bzero(&dl_ccch_msg, sizeof(LIBLTE_RRC_DL_CCCH_MSG_STRUCT)); + + dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST_REJ; + + send_dl_ccch(&dl_ccch_msg); + +} + +void rrc::ue::send_connection_setup(bool is_setup) +{ + LIBLTE_RRC_DL_CCCH_MSG_STRUCT dl_ccch_msg; + bzero(&dl_ccch_msg, sizeof(LIBLTE_RRC_DL_CCCH_MSG_STRUCT)); + + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT* rr_cfg = NULL; + if (is_setup) { + dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_SETUP; + dl_ccch_msg.msg.rrc_con_setup.rrc_transaction_id = (transaction_id++)%4; + rr_cfg = &dl_ccch_msg.msg.rrc_con_setup.rr_cnfg; + } else { + dl_ccch_msg.msg_type = LIBLTE_RRC_DL_CCCH_MSG_TYPE_RRC_CON_REEST; + dl_ccch_msg.msg.rrc_con_reest.rrc_transaction_id = (transaction_id++)%4; + rr_cfg = &dl_ccch_msg.msg.rrc_con_reest.rr_cnfg; + } + + + // Add SRB1 to cfg + rr_cfg->srb_to_add_mod_list_size = 1; + rr_cfg->srb_to_add_mod_list[0].srb_id = 1; + rr_cfg->srb_to_add_mod_list[0].lc_cnfg_present = true; + rr_cfg->srb_to_add_mod_list[0].lc_default_cnfg_present = true; + rr_cfg->srb_to_add_mod_list[0].rlc_cnfg_present = true; + rr_cfg->srb_to_add_mod_list[0].rlc_default_cnfg_present = true; + + // mac-MainConfig + rr_cfg->mac_main_cnfg_present = true; + LIBLTE_RRC_MAC_MAIN_CONFIG_STRUCT *mac_cfg = &rr_cfg->mac_main_cnfg.explicit_value; + mac_cfg->ulsch_cnfg_present = true; + memcpy(&mac_cfg->ulsch_cnfg, &parent->cfg.mac_cnfg.ulsch_cnfg, sizeof(LIBLTE_RRC_ULSCH_CONFIG_STRUCT)); + mac_cfg->drx_cnfg_present = false; + mac_cfg->phr_cnfg_present = true; + memcpy(&mac_cfg->phr_cnfg, &parent->cfg.mac_cnfg.phr_cnfg, sizeof(LIBLTE_RRC_PHR_CONFIG_STRUCT)); + mac_cfg->time_alignment_timer = parent->cfg.mac_cnfg.time_alignment_timer; + + // physicalConfigDedicated + rr_cfg->phy_cnfg_ded_present = true; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cfg = &rr_cfg->phy_cnfg_ded; + bzero(phy_cfg, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + phy_cfg->pusch_cnfg_ded_present = true; + memcpy(&phy_cfg->pusch_cnfg_ded, &parent->cfg.pusch_cfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_DEDICATED_STRUCT)); + phy_cfg->sched_request_cnfg_present = true; + phy_cfg->sched_request_cnfg.setup_present = true; + phy_cfg->sched_request_cnfg.dsr_trans_max = parent->cfg.sr_cfg.dsr_max; + + if (is_setup) { + if (sr_allocate(parent->cfg.sr_cfg.period, &phy_cfg->sched_request_cnfg.sr_cnfg_idx, &phy_cfg->sched_request_cnfg.sr_pucch_resource_idx)) { + parent->rrc_log->error("Allocating SR resources for rnti=%d\n", rnti); + return; + } + } else { + phy_cfg->sched_request_cnfg.sr_cnfg_idx = sr_I; + phy_cfg->sched_request_cnfg.sr_pucch_resource_idx = sr_N_pucch; + } + // Power control + phy_cfg->ul_pwr_ctrl_ded_present = true; + phy_cfg->ul_pwr_ctrl_ded.p0_ue_pusch = 0; + phy_cfg->ul_pwr_ctrl_ded.delta_mcs_en = LIBLTE_RRC_DELTA_MCS_ENABLED_EN0; + phy_cfg->ul_pwr_ctrl_ded.accumulation_en = true; + phy_cfg->ul_pwr_ctrl_ded.p0_ue_pucch = 0, + phy_cfg->ul_pwr_ctrl_ded.p_srs_offset = 3; + + phy_cfg->pdsch_cnfg_ded_present = true; + phy_cfg->pdsch_cnfg_ded = LIBLTE_RRC_PDSCH_CONFIG_P_A_DB_0; + + phy_cfg->cqi_report_cnfg_present = true; + if(parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_APERIODIC) { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic_present = true; + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30; + } else { + phy_cfg->cqi_report_cnfg.report_periodic_present = true; + phy_cfg->cqi_report_cnfg.report_periodic_setup_present = true; + phy_cfg->cqi_report_cnfg.report_periodic.format_ind_periodic = LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_WIDEBAND_CQI; + phy_cfg->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi = parent->cfg.cqi_cfg.simultaneousAckCQI; + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = false; + if (is_setup) { + if (cqi_allocate(parent->cfg.cqi_cfg.period, + &phy_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx, + &phy_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx)) + { + parent->rrc_log->error("Allocating CQI resources for rnti=%d\n", rnti); + return; + } + } else { + phy_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx = cqi_pucch; + phy_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx = cqi_idx; + } + } + phy_cfg->cqi_report_cnfg.nom_pdsch_rs_epre_offset = 0; + + + // Add SRB1 to Scheduler + srsenb::sched_interface::ue_cfg_t sched_cfg; + bzero(&sched_cfg, sizeof(srsenb::sched_interface::ue_cfg_t)); + sched_cfg.maxharq_tx = liblte_rrc_max_harq_tx_num[parent->cfg.mac_cnfg.ulsch_cnfg.max_harq_tx]; + sched_cfg.continuous_pusch = false; + sched_cfg.aperiodic_cqi_period = parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_APERIODIC?parent->cfg.cqi_cfg.period:0; + sched_cfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + sched_cfg.ue_bearers[1].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + sched_cfg.sr_I = sr_I; + sched_cfg.sr_N_pucch = sr_N_pucch; + sched_cfg.sr_enabled = true; + sched_cfg.cqi_pucch = cqi_pucch; + sched_cfg.cqi_idx = cqi_idx; + sched_cfg.cqi_enabled = parent->cfg.cqi_cfg.mode == RRC_CFG_CQI_MODE_PERIODIC; + sched_cfg.pucch_cfg.delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[parent->sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift%LIBLTE_RRC_DELTA_PUCCH_SHIFT_N_ITEMS]; + sched_cfg.pucch_cfg.N_cs = parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an; + sched_cfg.pucch_cfg.n_rb_2 = parent->sib2.rr_config_common_sib.pucch_cnfg.n_rb_cqi; + + // Configure MAC + parent->mac->ue_cfg(rnti, &sched_cfg); + + // Configure SRB1 in RLC and PDCP + parent->rlc->add_bearer(rnti, 1); + parent->pdcp->add_bearer(rnti, 1); + + // Configure PHY layer + parent->phy->set_config_dedicated(rnti, phy_cfg); + parent->mac->phy_config_enabled(rnti, true); + + rr_cfg->drb_to_add_mod_list_size = 0; + rr_cfg->drb_to_release_list_size = 0; + rr_cfg->rlf_timers_and_constants_present = false; + rr_cfg->sps_cnfg_present = false; + + send_dl_ccch(&dl_ccch_msg); + +} + + +void rrc::ue::send_connection_reest() +{ + send_connection_setup(false); +} + + +void rrc::ue::send_connection_release() +{ + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RELEASE; + dl_dcch_msg.msg.rrc_con_release.rrc_transaction_id = (transaction_id++)%4; + dl_dcch_msg.msg.rrc_con_release.release_cause = LIBLTE_RRC_RELEASE_CAUSE_OTHER; + + send_dl_dcch(&dl_dcch_msg); +} + +int rrc::ue::get_drbid_config(LIBLTE_RRC_DRB_TO_ADD_MOD_STRUCT *drb, int drb_id) +{ + uint32_t lc_id = drb_id + 2; + uint32_t erab_id = lc_id + 2; + uint32_t qci = erabs[erab_id].qos_params.qCI.QCI; + + if (qci >= MAX_NOF_QCI) { + parent->rrc_log->error("Invalid QCI=%d for ERAB_id=%d, DRB_id=%d\n", qci, erab_id, drb_id); + return -1; + } + + if (!parent->cfg.qci_cfg[qci].configured) { + parent->rrc_log->error("QCI=%d not configured\n", qci); + return -1; + } + + // Add DRB1 to the message + drb->drb_id = drb_id; + drb->lc_id = lc_id; + drb->lc_id_present = true; + drb->eps_bearer_id = erab_id; + drb->eps_bearer_id_present = true; + + drb->lc_cnfg_present = true; + drb->lc_cnfg.ul_specific_params_present = true; + drb->lc_cnfg.log_chan_sr_mask_present = false; + drb->lc_cnfg.ul_specific_params.log_chan_group_present = true; + memcpy(&drb->lc_cnfg.ul_specific_params, &parent->cfg.qci_cfg[qci].lc_cfg, sizeof(LIBLTE_RRC_UL_SPECIFIC_PARAMETERS_STRUCT)); + + drb->pdcp_cnfg_present = true; + memcpy(&drb->pdcp_cnfg, &parent->cfg.qci_cfg[qci].pdcp_cfg, sizeof(LIBLTE_RRC_PDCP_CONFIG_STRUCT)); + + drb->rlc_cnfg_present = true; + memcpy(&drb->rlc_cnfg, &parent->cfg.qci_cfg[qci].rlc_cfg, sizeof(LIBLTE_RRC_RLC_CONFIG_STRUCT)); + + return 0; +} + +void rrc::ue::send_connection_reconf_upd(srslte::byte_buffer_t *pdu) +{ + + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + bzero(&dl_dcch_msg, sizeof(LIBLTE_RRC_DL_DCCH_MSG_STRUCT)); + + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG; + dl_dcch_msg.msg.rrc_con_reconfig.rrc_transaction_id = (transaction_id++)%4; + + LIBLTE_RRC_RR_CONFIG_DEDICATED_STRUCT* rr_cfg = &dl_dcch_msg.msg.rrc_con_reconfig.rr_cnfg_ded; + + dl_dcch_msg.msg.rrc_con_reconfig.rr_cnfg_ded_present = true; + + rr_cfg->phy_cnfg_ded_present = true; + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT *phy_cfg = &rr_cfg->phy_cnfg_ded; + bzero(phy_cfg, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + phy_cfg->sched_request_cnfg_present = true; + phy_cfg->sched_request_cnfg.setup_present = true; + phy_cfg->sched_request_cnfg.dsr_trans_max = parent->cfg.sr_cfg.dsr_max; + + phy_cfg->cqi_report_cnfg_present = true; + if (cqi_allocated) { + phy_cfg->cqi_report_cnfg.report_periodic_present = true; + phy_cfg->cqi_report_cnfg.report_periodic_setup_present = true; + phy_cfg->cqi_report_cnfg.report_periodic.format_ind_periodic = LIBLTE_RRC_CQI_FORMAT_INDICATOR_PERIODIC_WIDEBAND_CQI; + phy_cfg->cqi_report_cnfg.report_periodic.simult_ack_nack_and_cqi = false; + phy_cfg->cqi_report_cnfg.report_periodic.ri_cnfg_idx_present = false; + phy_cfg->cqi_report_cnfg.report_periodic.pucch_resource_idx = cqi_pucch; + phy_cfg->cqi_report_cnfg.report_periodic.pmi_cnfg_idx = cqi_idx; + } else { + phy_cfg->cqi_report_cnfg.report_mode_aperiodic_present = true; + phy_cfg->cqi_report_cnfg.report_mode_aperiodic = LIBLTE_RRC_CQI_REPORT_MODE_APERIODIC_RM30; + phy_cfg->cqi_report_cnfg.nom_pdsch_rs_epre_offset = 0; + } + + sr_get(&phy_cfg->sched_request_cnfg.sr_cnfg_idx, &phy_cfg->sched_request_cnfg.sr_pucch_resource_idx); + + pdu->reset(); + + send_dl_dcch(&dl_dcch_msg, pdu); + + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; + +} + +void rrc::ue::send_connection_reconf(srslte::byte_buffer_t *pdu) +{ + + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG; + dl_dcch_msg.msg.rrc_con_reconfig.rrc_transaction_id = (transaction_id++)%4; + + LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT* conn_reconf = &dl_dcch_msg.msg.rrc_con_reconfig; + conn_reconf->rr_cnfg_ded_present = true; + conn_reconf->rr_cnfg_ded.mac_main_cnfg_present = false; + conn_reconf->rr_cnfg_ded.phy_cnfg_ded_present = false; + conn_reconf->rr_cnfg_ded.rlf_timers_and_constants_present = false; + conn_reconf->rr_cnfg_ded.sps_cnfg_present = false; + conn_reconf->rr_cnfg_ded.drb_to_release_list_size = 0; + conn_reconf->meas_cnfg_present = false; + conn_reconf->mob_ctrl_info_present = false; + conn_reconf->sec_cnfg_ho_present = false; + + // Add SRB2 to the message + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list_size = 1; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].srb_id = 2; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].lc_cnfg_present = true; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].lc_default_cnfg_present = true; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].rlc_cnfg_present = true; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list[0].rlc_default_cnfg_present = true; + + // Get DRB1 configuration + if (get_drbid_config(&conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0], 1)) { + parent->rrc_log->error("Getting DRB1 configuration\n"); + } else { + conn_reconf->rr_cnfg_ded.drb_to_add_mod_list_size = 1; + } + + // Add SRB2 and DRB1 to the scheduler + srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg; + bearer_cfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + parent->mac->bearer_ue_cfg(rnti, 2, &bearer_cfg); + parent->mac->bearer_ue_cfg(rnti, 3, &bearer_cfg); + + // Configure SRB2 in RLC and PDCP + parent->rlc->add_bearer(rnti, 2); + parent->pdcp->add_bearer(rnti, 2); + + // Configure DRB1 in RLC + parent->rlc->add_bearer(rnti, 3, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].rlc_cnfg); + // Configure DRB1 in PDCP + parent->pdcp->add_bearer(rnti, 3, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[0].pdcp_cnfg); + // DRB1 has already been configured in GTPU through bearer setup + + // Add NAS Attach accept + conn_reconf->N_ded_info_nas = 1; + conn_reconf->ded_info_nas_list[0].N_bytes = parent->erab_info.N_bytes; + memcpy(conn_reconf->ded_info_nas_list[0].msg, parent->erab_info.msg, parent->erab_info.N_bytes); + + // Reuse same PDU + pdu->reset(); + + send_dl_dcch(&dl_dcch_msg, pdu); + + state = RRC_STATE_WAIT_FOR_CON_RECONF_COMPLETE; +} + +void rrc::ue::send_connection_reconf_new_bearer(LIBLTE_S1AP_E_RABTOBESETUPLISTBEARERSUREQ_STRUCT *e) +{ + srslte::byte_buffer_t *pdu = parent->pool->allocate(); + + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_RRC_CON_RECONFIG; + dl_dcch_msg.msg.rrc_con_reconfig.rrc_transaction_id = (transaction_id++)%4; + + LIBLTE_RRC_CONNECTION_RECONFIGURATION_STRUCT* conn_reconf = &dl_dcch_msg.msg.rrc_con_reconfig; + conn_reconf->rr_cnfg_ded_present = true; + conn_reconf->rr_cnfg_ded.mac_main_cnfg_present = false; + conn_reconf->rr_cnfg_ded.phy_cnfg_ded_present = false; + conn_reconf->rr_cnfg_ded.rlf_timers_and_constants_present = false; + conn_reconf->rr_cnfg_ded.sps_cnfg_present = false; + conn_reconf->rr_cnfg_ded.drb_to_release_list_size = 0; + conn_reconf->rr_cnfg_ded.srb_to_add_mod_list_size = 0; + conn_reconf->rr_cnfg_ded.drb_to_add_mod_list_size = 0; + conn_reconf->meas_cnfg_present = false; + conn_reconf->mob_ctrl_info_present = false; + conn_reconf->sec_cnfg_ho_present = false; + + for(uint32_t i=0; ilen; i++) { + LIBLTE_S1AP_E_RABTOBESETUPITEMBEARERSUREQ_STRUCT *erab = &e->buffer[i]; + uint8_t id = erab->e_RAB_ID.E_RAB_ID; + uint8_t lcid = id - 2; // Map e.g. E-RAB 5 to LCID 3 (==DRB1) + + // Get DRB configuration + if (get_drbid_config(&conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[i], lcid)) { + parent->rrc_log->error("Getting DRB configuration\n"); + } else { + conn_reconf->rr_cnfg_ded.drb_to_add_mod_list_size++; + } + + // Add DRB to the scheduler + srsenb::sched_interface::ue_bearer_cfg_t bearer_cfg; + bearer_cfg.direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + parent->mac->bearer_ue_cfg(rnti, lcid, &bearer_cfg); + + // Configure DRB in RLC + parent->rlc->add_bearer(rnti, lcid, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[i].rlc_cnfg); + // Configure DRB in PDCP + parent->pdcp->add_bearer(rnti, lcid, &conn_reconf->rr_cnfg_ded.drb_to_add_mod_list[i].pdcp_cnfg); + // DRB has already been configured in GTPU through bearer setup + + // Add NAS message + conn_reconf->ded_info_nas_list[conn_reconf->N_ded_info_nas].N_bytes = parent->erab_info.N_bytes; + memcpy(conn_reconf->ded_info_nas_list[conn_reconf->N_ded_info_nas].msg, parent->erab_info.msg, parent->erab_info.N_bytes); + conn_reconf->N_ded_info_nas++; + } + + send_dl_dcch(&dl_dcch_msg, pdu); +} + +void rrc::ue::send_security_mode_command() +{ + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_SECURITY_MODE_COMMAND; + + LIBLTE_RRC_SECURITY_MODE_COMMAND_STRUCT* comm = &dl_dcch_msg.msg.security_mode_cmd; + comm->rrc_transaction_id = (transaction_id++)%4; + + // TODO: select these based on UE capabilities and preference order + comm->sec_algs.cipher_alg = (LIBLTE_RRC_CIPHERING_ALGORITHM_ENUM)cipher_algo; + comm->sec_algs.int_alg = (LIBLTE_RRC_INTEGRITY_PROT_ALGORITHM_ENUM)integ_algo; + + send_dl_dcch(&dl_dcch_msg); +} + +void rrc::ue::send_ue_cap_enquiry() +{ + LIBLTE_RRC_DL_DCCH_MSG_STRUCT dl_dcch_msg; + dl_dcch_msg.msg_type = LIBLTE_RRC_DL_DCCH_MSG_TYPE_UE_CAPABILITY_ENQUIRY; + + LIBLTE_RRC_UE_CAPABILITY_ENQUIRY_STRUCT* enq = &dl_dcch_msg.msg.ue_cap_enquiry; + enq->rrc_transaction_id = (transaction_id++)%4; + + enq->N_ue_cap_reqs = 1; + enq->ue_capability_request[0] = LIBLTE_RRC_RAT_TYPE_EUTRA; + + send_dl_dcch(&dl_dcch_msg); +} + +/********************** HELPERS ***************************/ + +void rrc::ue::send_dl_ccch(LIBLTE_RRC_DL_CCCH_MSG_STRUCT *dl_ccch_msg) +{ + // Allocate a new PDU buffer, pack the message and send to PDCP + byte_buffer_t *pdu = parent->pool->allocate(); + if (pdu) { + liblte_rrc_pack_dl_ccch_msg(dl_ccch_msg, (LIBLTE_BIT_MSG_STRUCT*) &parent->bit_buf); + srslte_bit_pack_vector(parent->bit_buf.msg, pdu->msg, parent->bit_buf.N_bits); + pdu->N_bytes = 1+(parent->bit_buf.N_bits-1)/8; + parent->rrc_log->info_hex(pdu->msg, pdu->N_bytes, + "SRB0 - rnti=0x%x, Sending: %s\n", + rnti, + liblte_rrc_dl_ccch_msg_type_text[dl_ccch_msg->msg_type]); + + parent->pdcp->write_sdu(rnti, srslte::RB_ID_SRB0, pdu); + + } else { + parent->rrc_log->error("Allocating pdu\n"); + } +} + +void rrc::ue::send_dl_dcch(LIBLTE_RRC_DL_DCCH_MSG_STRUCT *dl_dcch_msg, byte_buffer_t *pdu) +{ + if (!pdu) { + pdu = parent->pool->allocate(); + } + if (pdu) { + liblte_rrc_pack_dl_dcch_msg(dl_dcch_msg, (LIBLTE_BIT_MSG_STRUCT*) &parent->bit_buf); + srslte_bit_pack_vector(parent->bit_buf.msg, pdu->msg, parent->bit_buf.N_bits); + pdu->N_bytes = 1+(parent->bit_buf.N_bits-1)/8; + parent->rrc_log->info_hex(pdu->msg, pdu->N_bytes, + "SRB1 - rnti=0x%x, Sending: %s\n", + rnti, + liblte_rrc_dl_dcch_msg_type_text[dl_dcch_msg->msg_type]); + + parent->pdcp->write_sdu(rnti, srslte::RB_ID_SRB1, pdu); + + } else { + parent->rrc_log->error("Allocating pdu\n"); + } +} + +int rrc::ue::sr_free() +{ + if (sr_allocated) { + if (parent->sr_sched.nof_users[sr_sched_prb_idx][sr_sched_sf_idx] > 0) { + parent->sr_sched.nof_users[sr_sched_prb_idx][sr_sched_sf_idx]--; + } else { + parent->rrc_log->warning("Removing SR resources: no users in time-frequency slot (%d, %d)\n", sr_sched_prb_idx, sr_sched_sf_idx); + } + parent->rrc_log->info("Deallocated SR resources for time-frequency slot (%d, %d)\n", sr_sched_prb_idx, sr_sched_sf_idx); + } + return 0; +} + +void rrc::ue::sr_get(uint32_t *I_sr, uint32_t *N_pucch_sr) +{ + *I_sr = sr_I; + *N_pucch_sr = sr_N_pucch; +} + +int rrc::ue::sr_allocate(uint32_t period, uint32_t *I_sr, uint32_t *N_pucch_sr) +{ + uint32_t c = SRSLTE_CP_ISNORM(parent->cfg.cell.cp)?3:2; + uint32_t delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[parent->sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift]; + + 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; + uint32_t min_users = 1e6; + for (uint32_t i=0;icfg.sr_cfg.nof_prb;i++) { + for (uint32_t j=0;jcfg.sr_cfg.nof_subframes;j++) { + if (parent->sr_sched.nof_users[i][j] < min_users) { + i_min = i; + j_min = j; + min_users = parent->sr_sched.nof_users[i][j]; + } + } + } + + if (parent->sr_sched.nof_users[i_min][j_min] > max_users) { + parent->rrc_log->error("Not enough PUCCH resources to allocate Scheduling Request\n"); + return -1; + } + + // Compute I_sr + if (period != 5 && period != 10 && period != 20 && period != 40 && period != 80) { + parent->rrc_log->error("Invalid SchedulingRequest period %d ms\n", period); + return -1; + } + if (parent->cfg.sr_cfg.sf_mapping[j_min] < period) { + if (period > 5) { + *I_sr = period - 5 + parent->cfg.sr_cfg.sf_mapping[j_min]; + } else { + *I_sr = period + parent->cfg.sr_cfg.sf_mapping[j_min]; + } + } else { + parent->rrc_log->error("Allocating SR: invalid sf_idx=%d for period=%d\n", parent->cfg.sr_cfg.sf_mapping[j_min], period); + return -1; + } + + // Compute N_pucch_sr + *N_pucch_sr = i_min*max_users + parent->sr_sched.nof_users[i_min][j_min]; + if (parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an) { + *N_pucch_sr += parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an; + } + + // Allocate user + parent->sr_sched.nof_users[i_min][j_min]++; + sr_sched_prb_idx = i_min; + sr_sched_sf_idx = j_min; + sr_allocated = true; + sr_I = *I_sr; + sr_N_pucch = *N_pucch_sr; + + parent->rrc_log->info("Allocated SR resources for time-frequency slot (%d, %d), N_pucch_sr=%d, I_sr=%d\n", + sr_sched_prb_idx, sr_sched_sf_idx, *N_pucch_sr, *I_sr); + + return 0; +} + + +int rrc::ue::cqi_free() +{ + if (cqi_allocated) { + if (parent->cqi_sched.nof_users[cqi_sched_prb_idx][cqi_sched_sf_idx] > 0) { + parent->cqi_sched.nof_users[cqi_sched_prb_idx][cqi_sched_sf_idx]--; + } else { + parent->rrc_log->warning("Removing CQI resources: no users in time-frequency slot (%d, %d)\n", cqi_sched_prb_idx, cqi_sched_sf_idx); + } + parent->rrc_log->info("Deallocated CQI resources for time-frequency slot (%d, %d)\n", cqi_sched_prb_idx, cqi_sched_sf_idx); + } + return 0; +} + +void rrc::ue::cqi_get(uint32_t *pmi_idx, uint32_t *n_pucch) +{ + *pmi_idx = cqi_idx; + *n_pucch = cqi_pucch; +} + +int rrc::ue::cqi_allocate(uint32_t period, uint32_t *pmi_idx, uint32_t *n_pucch) +{ + uint32_t c = SRSLTE_CP_ISNORM(parent->cfg.cell.cp)?3:2; + uint32_t delta_pucch_shift = liblte_rrc_delta_pucch_shift_num[parent->sib2.rr_config_common_sib.pucch_cnfg.delta_pucch_shift]; + + 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; + uint32_t min_users = 1e6; + for (uint32_t i=0;icfg.cqi_cfg.nof_prb;i++) { + for (uint32_t j=0;jcfg.cqi_cfg.nof_subframes;j++) { + if (parent->cqi_sched.nof_users[i][j] < min_users) { + i_min = i; + j_min = j; + min_users = parent->cqi_sched.nof_users[i][j]; + } + } + } + + if (parent->cqi_sched.nof_users[i_min][j_min] > max_users) { + parent->rrc_log->error("Not enough PUCCH resources to allocate Scheduling Request\n"); + return -1; + } + + // Compute I_sr + if (period != 2 && period != 5 && period != 10 && period != 20 && period != 40 && period != 80 && + period != 160 && period != 32 && period != 64 && period != 128) { + parent->rrc_log->error("Invalid CQI Report period %d ms\n", period); + return -1; + } + if (parent->cfg.cqi_cfg.sf_mapping[j_min] < period) { + if (period != 32 && period != 64 && period != 128) { + if (period > 2) { + *pmi_idx = period - 3 + parent->cfg.cqi_cfg.sf_mapping[j_min]; + } else { + *pmi_idx = parent->cfg.cqi_cfg.sf_mapping[j_min]; + } + } else { + if (period == 32) { + *pmi_idx = 318 + parent->cfg.cqi_cfg.sf_mapping[j_min]; + } else if (period == 64) { + *pmi_idx = 350 + parent->cfg.cqi_cfg.sf_mapping[j_min]; + } else if (period == 64) { + *pmi_idx = 414 + parent->cfg.cqi_cfg.sf_mapping[j_min]; + } + } + } else { + parent->rrc_log->error("Allocating SR: invalid sf_idx=%d for period=%d\n", parent->cfg.cqi_cfg.sf_mapping[j_min], period); + return -1; + } + + // Compute n_pucch_2 + *n_pucch = i_min*max_users + parent->cqi_sched.nof_users[i_min][j_min]; + if (parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an) { + *n_pucch += parent->sib2.rr_config_common_sib.pucch_cnfg.n_cs_an; + } + + // Allocate user + parent->cqi_sched.nof_users[i_min][j_min]++; + cqi_sched_prb_idx = i_min; + cqi_sched_sf_idx = j_min; + cqi_allocated = true; + cqi_idx = *pmi_idx; + cqi_pucch = *n_pucch; + + parent->rrc_log->info("Allocated CQI resources for time-frequency slot (%d, %d), n_pucch_2=%d, pmi_cfg_idx=%d\n", + cqi_sched_prb_idx, cqi_sched_sf_idx, *n_pucch, *pmi_idx); + + return 0; +} + + + + +} diff --git a/srsenb/src/upper/s1ap.cc b/srsenb/src/upper/s1ap.cc new file mode 100644 index 000000000..7bd39611f --- /dev/null +++ b/srsenb/src/upper/s1ap.cc @@ -0,0 +1,1037 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2016 Software Radio Systems Limited + * + */ + +#include "upper/s1ap.h" +#include "upper/common_enb.h" + +#include +#include +#include //for close(), sleep() +#include +#include +#include +#include +#include //for inet_ntop() + +namespace srsenb{ + +bool s1ap::init(s1ap_args_t args_, rrc_interface_s1ap *rrc_, srslte::log *s1ap_log_) +{ + rrc = rrc_; + args = args_; + s1ap_log = s1ap_log_; + + pool = srslte::byte_buffer_pool::get_instance(); + mme_connected = false; + running = false; + next_eNB_UE_S1AP_ID = 1; + next_ue_stream_id = 1; + + build_tai_cgi(); + + start(S1AP_THREAD_PRIO); + + return true; +} + +void s1ap::stop() +{ + if(running) { + running = false; + thread_cancel(); + wait_thread_finish(); + } + + if(close(socket_fd) == -1) { + s1ap_log->error("Failed to close SCTP socket\n"); + } + return; +} + +void s1ap::get_metrics(s1ap_metrics_t &m) +{ + if(!running) { + m.status = S1AP_ERROR; + return; + } + if(mme_connected) { + m.status = S1AP_READY; + }else{ + m.status = S1AP_ATTACHING; + } + return; +} + +void s1ap::run_thread() +{ + srslte::byte_buffer_t *pdu = pool->allocate(); + + uint32_t sz = SRSUE_MAX_BUFFER_SIZE_BYTES - SRSUE_BUFFER_HEADER_OFFSET; + running = true; + + // Connect to MME + while(running && !connect_mme()) { + s1ap_log->error("Failed to connect to MME - retrying in 10 seconds\n"); + s1ap_log->console("Failed to connect to MME - retrying in 10 seconds\n"); + sleep(10); + } + if(!setup_s1()) { + s1ap_log->error("S1 setup failed\n"); + s1ap_log->console("S1 setup failed\n"); + running = false; + return; + } + + // S1AP rx loop + while(running) { + pdu->reset(); + pdu->N_bytes = recv(socket_fd, pdu->msg, sz, 0); + + if(pdu->N_bytes <= 0) { + mme_connected = false; + do { + s1ap_log->error("Disconnected - attempting reconnection in 10 seconds\n"); + s1ap_log->console("Disconnected - attempting reconnection in 10 seconds\n"); + sleep(10); + } while(running && !connect_mme()); + + if(!setup_s1()) { + s1ap_log->error("S1 setup failed\n"); + s1ap_log->console("S1 setup failed\n"); + running = false; + return; + } + } + + s1ap_log->info_hex(pdu->msg, pdu->N_bytes, "Received S1AP PDU"); + handle_s1ap_rx_pdu(pdu); + } +} + +// Generate common S1AP protocol IEs from config args +void s1ap::build_tai_cgi() +{ + uint32_t plmn; + uint32_t tmp32; + uint16_t tmp16; + + // TAI + tai.ext = false; + tai.iE_Extensions_present = false; + s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tmp32 = htonl(plmn); + tai.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1]; + tai.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2]; + tai.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3]; + tmp16 = htons(args.tac); + memcpy(tai.tAC.buffer, (uint8_t*)&tmp16, 2); + + // EUTRAN_CGI + eutran_cgi.ext = false; + eutran_cgi.iE_Extensions_present = false; + s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tmp32 = htonl(plmn); + eutran_cgi.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1]; + eutran_cgi.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2]; + eutran_cgi.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3]; + + tmp32 = htonl(args.enb_id); + uint8_t enb_id_bits[4*8]; + liblte_unpack((uint8_t*)&tmp32, 4, enb_id_bits); + uint8_t cell_id_bits[1*8]; + liblte_unpack(&args.cell_id, 1, cell_id_bits); + memcpy(eutran_cgi.cell_ID.buffer, &enb_id_bits[32-LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN); + memcpy(&eutran_cgi.cell_ID.buffer[LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], cell_id_bits, 8); +} + +/******************************************************************************* +/* RRC interface +********************************************************************************/ +void s1ap::initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu) +{ + ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++; + ue_ctxt_map[rnti].stream_id = next_ue_stream_id++; + ue_ctxt_map[rnti].release_requested = false; + enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti; + send_initialuemessage(rnti, pdu, false); +} + +void s1ap::initial_ue(uint16_t rnti, srslte::byte_buffer_t *pdu, uint32_t m_tmsi, uint8_t mmec) +{ + ue_ctxt_map[rnti].eNB_UE_S1AP_ID = next_eNB_UE_S1AP_ID++; + ue_ctxt_map[rnti].stream_id = next_ue_stream_id++; + ue_ctxt_map[rnti].release_requested = false; + enbid_to_rnti_map[ue_ctxt_map[rnti].eNB_UE_S1AP_ID] = rnti; + send_initialuemessage(rnti, pdu, true, m_tmsi, mmec); +} + +void s1ap::write_pdu(uint16_t rnti, srslte::byte_buffer_t *pdu) +{ + s1ap_log->info_hex(pdu->msg, pdu->N_bytes, "Received RRC SDU"); + + if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) { + s1ap_log->warning("User RNTI:0x%x context not found\n", rnti); + return; + } + + send_ulnastransport(rnti, pdu); +} + +void s1ap::user_inactivity(uint16_t rnti) +{ + s1ap_log->info("User inactivity - RNTI:0x%x\n", rnti); + + if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) { + s1ap_log->warning("User RNTI:0x%x context not found\n", rnti); + return; + } + + if(ue_ctxt_map[rnti].release_requested) { + s1ap_log->warning("UE context for RNTI:0x%x is in zombie state. Releasing...\n", rnti); + ue_ctxt_map.erase(rnti); + rrc->release_complete(rnti); + return; + } + + LIBLTE_S1AP_CAUSE_STRUCT cause; + cause.ext = false; + cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; + cause.choice.radioNetwork.ext = false; + cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_USER_INACTIVITY; + + ue_ctxt_map[rnti].release_requested = true; + send_uectxtreleaserequest(rnti, &cause); +} + + +void s1ap::release_eutran(uint16_t rnti) +{ + s1ap_log->info("Release by EUTRAN - RNTI:0x%x\n", rnti); + + if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) { + s1ap_log->warning("User RNTI:0x%x context not found\n", rnti); + return; + } + + if(ue_ctxt_map[rnti].release_requested) { + return; + } + + LIBLTE_S1AP_CAUSE_STRUCT cause; + cause.ext = false; + cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; + cause.choice.radioNetwork.ext = false; + cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_RELEASE_DUE_TO_EUTRAN_GENERATED_REASON; + + ue_ctxt_map[rnti].release_requested = true; + send_uectxtreleaserequest(rnti, &cause); +} + +bool s1ap::user_exists(uint16_t rnti) +{ + return ue_ctxt_map.end() != ue_ctxt_map.find(rnti); +} + +bool s1ap::user_link_lost(uint16_t rnti) +{ + s1ap_log->info("User link lost - RNTI:0x%x\n", rnti); + + if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) { + s1ap_log->warning("User RNTI:0x%x context not found\n", rnti); + return false; + } + + if(ue_ctxt_map[rnti].release_requested) { + return false; + } + + LIBLTE_S1AP_CAUSE_STRUCT cause; + cause.ext = false; + cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; + cause.choice.radioNetwork.ext = false; + cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_RADIO_CONNECTION_WITH_UE_LOST; + + ue_ctxt_map[rnti].release_requested = true; + return send_uectxtreleaserequest(rnti, &cause); +} + +void s1ap::ue_ctxt_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res) +{ + if(res->E_RABSetupListCtxtSURes.len > 0) { + send_initial_ctxt_setup_response(rnti, res); + } else { + send_initial_ctxt_setup_failure(rnti); + } +} + +void s1ap::ue_erab_setup_complete(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res) +{ + send_erab_setup_response(rnti, res); +} + +//void ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) +//{ + +//} + +/******************************************************************************* +/* S1AP connection helpers +********************************************************************************/ + +bool s1ap::connect_mme() +{ + socket_fd = 0; + + s1ap_log->info("Connecting to MME %s:%d\n", args.mme_addr.c_str(), MME_PORT); + + if((socket_fd = socket(ADDR_FAMILY, SOCK_TYPE, PROTO)) == -1) { + s1ap_log->error("Failed to create S1AP socket\n"); + return false; + } + + // Bind to the local address + struct sockaddr_in local_addr; + memset(&local_addr, 0, sizeof(struct sockaddr_in)); + local_addr.sin_family = ADDR_FAMILY; + local_addr.sin_port = 0; // Any local port will do + if(inet_pton(AF_INET, args.gtp_bind_addr.c_str(), &(local_addr.sin_addr)) != 1) { + s1ap_log->error("Error converting IP address (%s) to sockaddr_in structure\n", args.gtp_bind_addr.c_str()); + return false; + } + bind(socket_fd, (struct sockaddr *)&local_addr, sizeof(local_addr)); + + // Connect to the MME address + memset(&mme_addr, 0, sizeof(struct sockaddr_in)); + mme_addr.sin_family = ADDR_FAMILY; + mme_addr.sin_port = htons(MME_PORT); + if(inet_pton(AF_INET, args.mme_addr.c_str(), &(mme_addr.sin_addr)) != 1) { + s1ap_log->error("Error converting IP address (%s) to sockaddr_in structure\n", args.mme_addr.c_str()); + return false; + } + + if(connect(socket_fd, (struct sockaddr*)&mme_addr, sizeof(mme_addr)) == -1) { + s1ap_log->error("Failed to establish socket connection to MME\n"); + return false; + } + + s1ap_log->info("SCTP socket established with MME\n"); + return true; +} + +bool s1ap::setup_s1() +{ + uint32_t tmp32; + uint16_t tmp16; + srslte::byte_buffer_t msg; + LIBLTE_S1AP_S1AP_PDU_STRUCT pdu; + + pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &pdu.choice.initiatingMessage; + + init->procedureCode = LIBLTE_S1AP_PROC_ID_S1SETUP; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_S1SETUPREQUEST; + + LIBLTE_S1AP_MESSAGE_S1SETUPREQUEST_STRUCT *s1setup = &init->choice.S1SetupRequest; + s1setup->ext = false; + s1setup->CSG_IdList_present = false; + + s1setup->Global_ENB_ID.ext = false; + s1setup->Global_ENB_ID.iE_Extensions_present = false; + uint32_t plmn; + s1ap_mccmnc_to_plmn(args.mcc, args.mnc, &plmn); + tmp32 = htonl(plmn); + s1setup->Global_ENB_ID.pLMNidentity.buffer[0] = ((uint8_t*)&tmp32)[1]; + s1setup->Global_ENB_ID.pLMNidentity.buffer[1] = ((uint8_t*)&tmp32)[2]; + s1setup->Global_ENB_ID.pLMNidentity.buffer[2] = ((uint8_t*)&tmp32)[3]; + + s1setup->Global_ENB_ID.ext = false; + s1setup->Global_ENB_ID.eNB_ID.ext = false; + s1setup->Global_ENB_ID.eNB_ID.choice_type = LIBLTE_S1AP_ENB_ID_CHOICE_MACROENB_ID; + tmp32 = htonl(args.enb_id); + uint8_t enb_id_bits[4*8]; + liblte_unpack((uint8_t*)&tmp32, 4, enb_id_bits); + memcpy(s1setup->Global_ENB_ID.eNB_ID.choice.macroENB_ID.buffer, &enb_id_bits[32-LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN], LIBLTE_S1AP_MACROENB_ID_BIT_STRING_LEN); + + s1setup->eNBname_present = true; + s1setup->eNBname.ext = false; + if(args.enb_name.length() >= 150) { + args.enb_name.resize(150-1); + } + memcpy(s1setup->eNBname.buffer, args.enb_name.c_str(), args.enb_name.length()); + s1setup->eNBname.n_octets = args.enb_name.length(); + + s1setup->SupportedTAs.len = 1; + s1setup->SupportedTAs.buffer[0].ext = false; + s1setup->SupportedTAs.buffer[0].iE_Extensions_present = false; + tmp16 = htons(args.tac); + memcpy(s1setup->SupportedTAs.buffer[0].tAC.buffer, (uint8_t*)&tmp16, 2); + s1setup->SupportedTAs.buffer[0].broadcastPLMNs.len = 1; + tmp32 = htonl(plmn); + s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[0] = ((uint8_t*)&tmp32)[1]; + s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[1] = ((uint8_t*)&tmp32)[2]; + s1setup->SupportedTAs.buffer[0].broadcastPLMNs.buffer[0].buffer[2] = ((uint8_t*)&tmp32)[3]; + + s1setup->DefaultPagingDRX.ext = false; + s1setup->DefaultPagingDRX.e = LIBLTE_S1AP_PAGINGDRX_V128; // Todo: add to args, config file + + liblte_s1ap_pack_s1ap_pdu(&pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending s1SetupRequest"); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, NONUE_STREAM_ID, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send s1SetupRequest\n"); + return false; + } + + return true; +} + +/******************************************************************************* +/* S1AP message handlers +********************************************************************************/ + +bool s1ap::handle_s1ap_rx_pdu(srslte::byte_buffer_t *pdu) +{ + LIBLTE_S1AP_S1AP_PDU_STRUCT rx_pdu; + + if(liblte_s1ap_unpack_s1ap_pdu((LIBLTE_BYTE_MSG_STRUCT*)pdu, &rx_pdu) != LIBLTE_SUCCESS) { + s1ap_log->error("Failed to unpack received PDU\n"); + return false; + } + + switch(rx_pdu.choice_type) { + case LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE: + return handle_initiatingmessage(&rx_pdu.choice.initiatingMessage); + break; + case LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME: + return handle_successfuloutcome(&rx_pdu.choice.successfulOutcome); + break; + case LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME: + return handle_unsuccessfuloutcome(&rx_pdu.choice.unsuccessfulOutcome); + break; + default: + s1ap_log->error("Unhandled PDU type %d\n", rx_pdu.choice_type); + return false; + } + + return true; +} + +bool s1ap::handle_initiatingmessage(LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *msg) +{ + switch(msg->choice_type) { + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_DOWNLINKNASTRANSPORT: + return handle_dlnastransport(&msg->choice.DownlinkNASTransport); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALCONTEXTSETUPREQUEST: + return handle_initialctxtsetuprequest(&msg->choice.InitialContextSetupRequest); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASECOMMAND: + return handle_uectxtreleasecommand(&msg->choice.UEContextReleaseCommand); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_PAGING: + return handle_paging(&msg->choice.Paging); + case LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_E_RABSETUPREQUEST: + return handle_erabsetuprequest(&msg->choice.E_RABSetupRequest); + default: + s1ap_log->error("Unhandled intiating message: %s\n", liblte_s1ap_initiatingmessage_choice_text[msg->choice_type]); + } + return true; +} + +bool s1ap::handle_successfuloutcome(LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *msg) +{ + switch(msg->choice_type) { + case LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_S1SETUPRESPONSE: + return handle_s1setupresponse(&msg->choice.S1SetupResponse); + default: + s1ap_log->error("Unhandled successful outcome message: %s\n", liblte_s1ap_successfuloutcome_choice_text[msg->choice_type]); + } + return true; +} + +bool s1ap::handle_unsuccessfuloutcome(LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *msg) +{ + switch(msg->choice_type) { + case LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_S1SETUPFAILURE: + return handle_s1setupfailure(&msg->choice.S1SetupFailure); + default: + s1ap_log->error("Unhandled unsuccessful outcome message: %s\n", liblte_s1ap_unsuccessfuloutcome_choice_text[msg->choice_type]); + } + return true; +} + +bool s1ap::handle_s1setupresponse(LIBLTE_S1AP_MESSAGE_S1SETUPRESPONSE_STRUCT *msg) +{ + s1ap_log->info("Received S1SetupResponse\n"); + s1setupresponse = *msg; + mme_connected = true; + return true; +} + +bool s1ap::handle_dlnastransport(LIBLTE_S1AP_MESSAGE_DOWNLINKNASTRANSPORT_STRUCT *msg) +{ + s1ap_log->info("Received DownlinkNASTransport\n"); + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) { + s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); + return false; + } + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]; + ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + + if(msg->HandoverRestrictionList_present) { + s1ap_log->warning("Not handling HandoverRestrictionList\n"); + } + if(msg->SubscriberProfileIDforRFP_present) { + s1ap_log->warning("Not handling SubscriberProfileIDforRFP\n"); + } + + srslte::byte_buffer_t *pdu = pool->allocate(); + memcpy(pdu->msg, msg->NAS_PDU.buffer, msg->NAS_PDU.n_octets); + pdu->N_bytes = msg->NAS_PDU.n_octets; + rrc->write_dl_info(rnti, pdu); + return true; +} + +bool s1ap::handle_initialctxtsetuprequest(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPREQUEST_STRUCT *msg) +{ + s1ap_log->info("Received InitialContextSetupRequest\n"); + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) { + s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); + return false; + } + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]; + if(msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) { + s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n", + ue_ctxt_map[rnti].MME_UE_S1AP_ID, + msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID); + ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + } + + // Setup UE ctxt in RRC + if(!rrc->setup_ue_ctxt(rnti, msg)) { + return false; + } + + return true; +} + +bool s1ap::handle_paging(LIBLTE_S1AP_MESSAGE_PAGING_STRUCT *msg) +{ + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + uint8_t *ptr = msg->UEIdentityIndexValue.buffer; + uint32_t ueid = srslte_bit_pack(&ptr, 10); + + rrc->add_paging_id(ueid, msg->UEPagingID); + return true; +} + +bool s1ap::handle_erabsetuprequest(LIBLTE_S1AP_MESSAGE_E_RABSETUPREQUEST_STRUCT *msg) +{ + s1ap_log->info("Received ERABSetupRequest\n"); + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + + if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID)) { + s1ap_log->warning("eNB_UE_S1AP_ID not found - discarding message\n"); + return false; + } + uint16_t rnti = enbid_to_rnti_map[msg->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID]; + if(msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID != ue_ctxt_map[rnti].MME_UE_S1AP_ID) { + s1ap_log->warning("MME_UE_S1AP_ID has changed - old:%d, new:%d\n", + ue_ctxt_map[rnti].MME_UE_S1AP_ID, + msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID); + ue_ctxt_map[rnti].MME_UE_S1AP_ID = msg->MME_UE_S1AP_ID.MME_UE_S1AP_ID; + } + + // Setup UE ctxt in RRC + if(!rrc->setup_ue_erabs(rnti, msg)) { + return false; + } + + return true; +} + +bool s1ap::handle_uectxtreleasecommand(LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMMAND_STRUCT *msg) +{ + s1ap_log->info("Received UEContextReleaseCommand\n"); + if(msg->ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + if(msg->UE_S1AP_IDs.ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + + uint16_t rnti; + if(msg->UE_S1AP_IDs.choice_type == LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_UE_S1AP_ID_PAIR) { + + if(msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.ext) { + s1ap_log->warning("Not handling S1AP message extension\n"); + } + if(msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.iE_Extensions_present) { + s1ap_log->warning("Not handling S1AP message iE_Extensions\n"); + } + uint32_t enb_ue_id = msg->UE_S1AP_IDs.choice.uE_S1AP_ID_pair.eNB_UE_S1AP_ID.ENB_UE_S1AP_ID; + if(enbid_to_rnti_map.end() == enbid_to_rnti_map.find(enb_ue_id)) { + s1ap_log->warning("eNB_UE_S1AP_ID:%d not found - discarding message\n", enb_ue_id); + return false; + } + rnti = enbid_to_rnti_map[enb_ue_id]; + enbid_to_rnti_map.erase(enb_ue_id); + + } else { // LIBLTE_S1AP_UE_S1AP_IDS_CHOICE_MME_UE_S1AP_ID + + uint32_t mme_ue_id = msg->UE_S1AP_IDs.choice.mME_UE_S1AP_ID.MME_UE_S1AP_ID; + uint32_t enb_ue_id; + if(!find_mme_ue_id(mme_ue_id, &rnti, &enb_ue_id)) { + s1ap_log->warning("UE for MME_UE_S1AP_ID:%d not found - discarding message\n", mme_ue_id); + return false; + } + enbid_to_rnti_map.erase(enb_ue_id); + } + + if(ue_ctxt_map.end() == ue_ctxt_map.find(rnti)) { + s1ap_log->warning("UE context for RNTI:0x%x not found - discarding message\n", rnti); + return false; + } + + rrc->release_erabs(rnti); + send_uectxtreleasecomplete(rnti, ue_ctxt_map[rnti].MME_UE_S1AP_ID, ue_ctxt_map[rnti].eNB_UE_S1AP_ID); + ue_ctxt_map.erase(rnti); + s1ap_log->info("UE context for RNTI:0x%x released\n", rnti); + rrc->release_complete(rnti); + return true; +} + +bool s1ap::handle_s1setupfailure(LIBLTE_S1AP_MESSAGE_S1SETUPFAILURE_STRUCT *msg) { + std::string cause = get_cause(&msg->Cause); + s1ap_log->error("S1 Setup Failure. Cause: %s\n", cause.c_str()); + s1ap_log->console("S1 Setup Failure. Cause: %s\n", cause.c_str()); + return true; +} + +/******************************************************************************* +/* S1AP message senders +********************************************************************************/ + +bool s1ap::send_initialuemessage(uint16_t rnti, srslte::byte_buffer_t *pdu, bool has_tmsi, uint32_t m_tmsi, uint8_t mmec) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t msg; + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALUEMESSAGE; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_INITIALUEMESSAGE; + + LIBLTE_S1AP_MESSAGE_INITIALUEMESSAGE_STRUCT *initue = &init->choice.InitialUEMessage; + initue->ext = false; + initue->CellAccessMode_present = false; + initue->CSG_Id_present = false; + initue->GUMMEIType_present = false; + initue->GUMMEI_ID_present = false; + initue->GW_TransportLayerAddress_present = false; + initue->LHN_ID_present = false; + initue->RelayNode_Indicator_present = false; + initue->SIPTO_L_GW_TransportLayerAddress_present = false; + initue->S_TMSI_present = false; + initue->Tunnel_Information_for_BBF_present = false; + + // S_TMSI + if(has_tmsi) { + initue->S_TMSI_present = true; + initue->S_TMSI.ext = false; + initue->S_TMSI.iE_Extensions_present = false; + + uint32_to_uint8(m_tmsi, initue->S_TMSI.m_TMSI.buffer); + initue->S_TMSI.mMEC.buffer[0] = mmec; + } + + // ENB_UE_S1AP_ID + initue->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + // NAS_PDU + memcpy(initue->NAS_PDU.buffer, pdu->msg, pdu->N_bytes); + initue->NAS_PDU.n_octets = pdu->N_bytes; + + // TAI + memcpy(&initue->TAI, &tai, sizeof(LIBLTE_S1AP_TAI_STRUCT)); + + // EUTRAN_CGI + memcpy(&initue->EUTRAN_CGI, &eutran_cgi, sizeof(LIBLTE_S1AP_EUTRAN_CGI_STRUCT)); + + // RRC Establishment Cause + initue->RRC_Establishment_Cause.ext = false; + initue->RRC_Establishment_Cause.e = LIBLTE_S1AP_RRC_ESTABLISHMENT_CAUSE_MO_SIGNALLING; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending InitialUEMessage for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send InitialUEMessage for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_ulnastransport(uint16_t rnti, srslte::byte_buffer_t *pdu) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t msg; + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_UPLINKNASTRANSPORT; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UPLINKNASTRANSPORT; + + LIBLTE_S1AP_MESSAGE_UPLINKNASTRANSPORT_STRUCT *ultx = &init->choice.UplinkNASTransport; + ultx->ext = false; + ultx->GW_TransportLayerAddress_present = false; + ultx->LHN_ID_present = false; + ultx->SIPTO_L_GW_TransportLayerAddress_present = false; + + // MME_UE_S1AP_ID + ultx->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + // ENB_UE_S1AP_ID + ultx->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + // NAS_PDU + memcpy(ultx->NAS_PDU.buffer, pdu->msg, pdu->N_bytes); + ultx->NAS_PDU.n_octets = pdu->N_bytes; + + // EUTRAN_CGI + memcpy(&ultx->EUTRAN_CGI, &eutran_cgi, sizeof(LIBLTE_S1AP_EUTRAN_CGI_STRUCT)); + + // TAI + memcpy(&ultx->TAI, &tai, sizeof(LIBLTE_S1AP_TAI_STRUCT)); + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UplinkNASTransport for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_uectxtreleaserequest(uint16_t rnti, LIBLTE_S1AP_CAUSE_STRUCT *cause) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t msg; + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + + LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; + init->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASEREQUEST; + init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECONTEXTRELEASEREQUEST; + + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASEREQUEST_STRUCT *req = &init->choice.UEContextReleaseRequest; + req->ext = false; + req->GWContextReleaseIndication_present = false; + + // MME_UE_S1AP_ID + req->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + // ENB_UE_S1AP_ID + req->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + // Cause + memcpy(&req->Cause, cause, sizeof(LIBLTE_S1AP_CAUSE_STRUCT)); + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UEContextReleaseRequest for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send UEContextReleaseRequest for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_uectxtreleasecomplete(uint16_t rnti, uint32_t mme_ue_id, uint32_t enb_ue_id) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t msg; + + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_UECONTEXTRELEASE; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_UECONTEXTRELEASECOMPLETE; + + LIBLTE_S1AP_MESSAGE_UECONTEXTRELEASECOMPLETE_STRUCT *comp = &succ->choice.UEContextReleaseComplete; + comp->ext = false; + comp->CriticalityDiagnostics_present = false; + comp->UserLocationInformation_present = false; + + comp->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = enb_ue_id; + comp->MME_UE_S1AP_ID.MME_UE_S1AP_ID = mme_ue_id; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); + s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UEContextReleaseComplete for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send UEContextReleaseComplete for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_initial_ctxt_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res_) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t *buf = pool->allocate(); + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPRESPONSE; + + // Copy in the provided response message + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT *res = &succ->choice.InitialContextSetupResponse; + memcpy(res, res_, sizeof(LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPRESPONSE_STRUCT)); + + // Fill in the GTP bind address for all bearers + for(uint32_t i=0;iE_RABSetupListCtxtSURes.len; i++) { + uint8_t addr[4]; + inet_pton(AF_INET, args.gtp_bind_addr.c_str(), addr); + liblte_unpack(addr, 4, res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.buffer); + res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.n_bits = 32; + res->E_RABSetupListCtxtSURes.buffer[i].transportLayerAddress.ext = false; + } + + // Fill in the MME and eNB IDs + res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending InitialContextSetupResponse for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send InitialContextSetupResponse for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_erab_setup_response(uint16_t rnti, LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res_) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t *buf = pool->allocate(); + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_SUCCESSFULOUTCOME; + + LIBLTE_S1AP_SUCCESSFULOUTCOME_STRUCT *succ = &tx_pdu.choice.successfulOutcome; + succ->procedureCode = LIBLTE_S1AP_PROC_ID_E_RABSETUP; + succ->choice_type = LIBLTE_S1AP_SUCCESSFULOUTCOME_CHOICE_E_RABSETUPRESPONSE; + + // Copy in the provided response message + LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT *res = &succ->choice.E_RABSetupResponse; + memcpy(res, res_, sizeof(LIBLTE_S1AP_MESSAGE_E_RABSETUPRESPONSE_STRUCT)); + + // Fill in the GTP bind address for all bearers + for(uint32_t i=0;iE_RABSetupListBearerSURes.len; i++) { + uint8_t addr[4]; + inet_pton(AF_INET, args.gtp_bind_addr.c_str(), addr); + liblte_unpack(addr, 4, res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.buffer); + res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.n_bits = 32; + res->E_RABSetupListBearerSURes.buffer[i].transportLayerAddress.ext = false; + } + + // Fill in the MME and eNB IDs + res->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + res->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)buf); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending E_RABSetupResponse for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send E_RABSetupResponse for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + +bool s1ap::send_initial_ctxt_setup_failure(uint16_t rnti) +{ + if(!mme_connected) { + return false; + } + srslte::byte_buffer_t *buf = pool->allocate(); + LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; + tx_pdu.ext = false; + tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_UNSUCCESSFULOUTCOME; + + LIBLTE_S1AP_UNSUCCESSFULOUTCOME_STRUCT *unsucc = &tx_pdu.choice.unsuccessfulOutcome; + unsucc->procedureCode = LIBLTE_S1AP_PROC_ID_INITIALCONTEXTSETUP; + unsucc->choice_type = LIBLTE_S1AP_UNSUCCESSFULOUTCOME_CHOICE_INITIALCONTEXTSETUPFAILURE; + + LIBLTE_S1AP_MESSAGE_INITIALCONTEXTSETUPFAILURE_STRUCT *fail = &unsucc->choice.InitialContextSetupFailure; + fail->ext = false; + fail->CriticalityDiagnostics_present = false; + + fail->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; + fail->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; + + fail->Cause.ext = false; + fail->Cause.choice_type = LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK; + fail->Cause.choice.radioNetwork.ext = false; + fail->Cause.choice.radioNetwork.e = LIBLTE_S1AP_CAUSERADIONETWORK_UNSPECIFIED; + + liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&buf); + s1ap_log->info_hex(buf->msg, buf->N_bytes, "Sending InitialContextSetupFailure for RNTI:0x%x", rnti); + + ssize_t n_sent = sctp_sendmsg(socket_fd, buf->msg, buf->N_bytes, + (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), + htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); + if(n_sent == -1) { + s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti); + return false; + } + + return true; +} + + +//bool s1ap::send_ue_capabilities(uint16_t rnti, LIBLTE_RRC_UE_EUTRA_CAPABILITY_STRUCT *caps) +//{ +// srslte::byte_buffer_t msg; + +// LIBLTE_S1AP_S1AP_PDU_STRUCT tx_pdu; +// tx_pdu.ext = false; +// tx_pdu.choice_type = LIBLTE_S1AP_S1AP_PDU_CHOICE_INITIATINGMESSAGE; + +// LIBLTE_S1AP_INITIATINGMESSAGE_STRUCT *init = &tx_pdu.choice.initiatingMessage; +// init->procedureCode = LIBLTE_S1AP_PROC_ID_UPLINKNASTRANSPORT; +// init->choice_type = LIBLTE_S1AP_INITIATINGMESSAGE_CHOICE_UECAPABILITYINFOINDICATION; + +// LIBLTE_S1AP_MESSAGE_UECAPABILITYINFOINDICATION_STRUCT *caps = &init->choice.UECapabilityInfoIndication; +// caps->ext = false; +// caps->MME_UE_S1AP_ID.MME_UE_S1AP_ID = ue_ctxt_map[rnti].MME_UE_S1AP_ID; +// caps->eNB_UE_S1AP_ID.ENB_UE_S1AP_ID = ue_ctxt_map[rnti].eNB_UE_S1AP_ID; +// // TODO: caps->UERadioCapability. + +// liblte_s1ap_pack_s1ap_pdu(&tx_pdu, (LIBLTE_BYTE_MSG_STRUCT*)&msg); +// s1ap_log->info_hex(msg.msg, msg.N_bytes, "Sending UERadioCapabilityInfo for RNTI:0x%x", rnti); + +// ssize_t n_sent = sctp_sendmsg(socket_fd, msg.msg, msg.N_bytes, +// (struct sockaddr*)&mme_addr, sizeof(struct sockaddr_in), +// htonl(PPID), 0, ue_ctxt_map[rnti].stream_id, 0, 0); +// if(n_sent == -1) { +// s1ap_log->error("Failed to send UplinkNASTransport for RNTI:0x%x\n", rnti); +// return false; +// } + +// return true; +//} + +/******************************************************************************* +/* General helpers +********************************************************************************/ + +bool s1ap::find_mme_ue_id(uint32_t mme_ue_id, uint16_t *rnti, uint32_t *enb_ue_id) +{ + typedef std::map::iterator it_t; + for(it_t it=ue_ctxt_map.begin(); it!=ue_ctxt_map.end(); ++it) { + if(it->second.MME_UE_S1AP_ID == mme_ue_id) { + *rnti = it->second.rnti; + *enb_ue_id = it->second.eNB_UE_S1AP_ID; + return true; + } + } + return false; +} + +std::string s1ap::get_cause(LIBLTE_S1AP_CAUSE_STRUCT *c) +{ + std::string cause = liblte_s1ap_cause_choice_text[c->choice_type]; + cause += " - "; + switch(c->choice_type) { + case LIBLTE_S1AP_CAUSE_CHOICE_RADIONETWORK: + cause += liblte_s1ap_causeradionetwork_text[c->choice.radioNetwork.e]; + break; + case LIBLTE_S1AP_CAUSE_CHOICE_TRANSPORT: + cause += liblte_s1ap_causetransport_text[c->choice.transport.e]; + break; + case LIBLTE_S1AP_CAUSE_CHOICE_NAS: + cause += liblte_s1ap_causenas_text[c->choice.nas.e]; + break; + case LIBLTE_S1AP_CAUSE_CHOICE_PROTOCOL: + cause += liblte_s1ap_causeprotocol_text[c->choice.protocol.e]; + break; + case LIBLTE_S1AP_CAUSE_CHOICE_MISC: + cause += liblte_s1ap_causemisc_text[c->choice.misc.e]; + break; + default: + cause += "unkown"; + break; + } + return cause; +} + +} // namespace srsenb diff --git a/srsenb/test/CMakeLists.txt b/srsenb/test/CMakeLists.txt new file mode 100644 index 000000000..6a86702b2 --- /dev/null +++ b/srsenb/test/CMakeLists.txt @@ -0,0 +1,3 @@ + +add_subdirectory(mac) +add_subdirectory(upper) \ No newline at end of file diff --git a/srsenb/test/mac/CMakeLists.txt b/srsenb/test/mac/CMakeLists.txt new file mode 100644 index 000000000..9048b4e59 --- /dev/null +++ b/srsenb/test/mac/CMakeLists.txt @@ -0,0 +1,9 @@ + +# Scheduler test +add_executable(scheduler_test scheduler_test.cc) +target_link_libraries(scheduler_test srsenb_mac + srsenb_phy + srslte_common + srslte_phy + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES}) diff --git a/srsenb/test/mac/scheduler_test.cc b/srsenb/test/mac/scheduler_test.cc new file mode 100644 index 000000000..53bb28e18 --- /dev/null +++ b/srsenb/test/mac/scheduler_test.cc @@ -0,0 +1,136 @@ + +#include + +#include "mac/mac.h" +#include "phy/phy.h" + +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/interfaces/sched_interface.h" +#include "srslte/common/log_stdout.h" +#include "srslte/radio/radio.h" +#include "srslte/phy/utils/debug.h" + + + + +uint8_t sib1_payload[18] = {0x60,0x40,0x04,0x03,0x00,0x01,0x1a,0x2d,0x00,0x18,0x02,0x81,0x80,0x42,0x0c,0x80,0x00,0x00}; +uint8_t sib2_payload[41] = {0x00,0x80,0x1c,0x31,0x18,0x6f,0xe1,0x20,0x00,0x35,0x84,0x8c, + 0xe2,0xd0,0x00,0x02,0x00,0x78,0xee,0x31,0x6a,0xa5,0x37,0x30, + 0xa0,0x70,0xc9,0x49,0xfa,0x8d,0xd2,0x78,0x1a,0x02,0x77,0x4a, + 0x92,0x40,0x00,0x00,0x00}; + +// Define dummy RLC always transmitts +class rlc : public srsenb::rlc_interface_mac +{ +public: + uint32_t get_buffer_state(uint16_t rnti, uint32_t lcid) + { + return 1; + } + + int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) + { + for (uint32_t i=0;i 50) { + running = false; + } + my_sched.dl_sched(tti, &sched_result_dl); + my_sched.ul_sched(tti, &sched_result_ul); + tti = (tti+1)%10240; + if (tti >= 4) { + my_sched.ul_crc_info(tti, rnti, tti%2); + } + } + + +} diff --git a/srsenb/test/upper/CMakeLists.txt b/srsenb/test/upper/CMakeLists.txt new file mode 100644 index 000000000..7d11679e7 --- /dev/null +++ b/srsenb/test/upper/CMakeLists.txt @@ -0,0 +1,17 @@ +# IP tx/rx program test +add_executable(ip_test_enb ip_test.cc) +target_link_libraries(ip_test_enb srsenb_upper + srsenb_mac + srsenb_phy + srslte_common + srslte_phy + srslte_upper + srslte_radio + ${CMAKE_THREAD_LIBS_INIT} + ${Boost_LIBRARIES} + ${POLAR_LIBRARIES}) + +# Simple PLMN -> MCC/MNC test +add_executable(plmn_test plmn_test.cc) +target_link_libraries(plmn_test srsenb_upper srslte_asn1 ) + diff --git a/srsenb/test/upper/ip_test.cc b/srsenb/test/upper/ip_test.cc new file mode 100644 index 000000000..5261e7ef5 --- /dev/null +++ b/srsenb/test/upper/ip_test.cc @@ -0,0 +1,646 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mac/mac.h" +#include "phy/phy.h" +#include "srslte/common/threads.h" +#include "srslte/interfaces/enb_interfaces.h" +#include "srslte/common/common.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/logger.h" +#include "srslte/common/log_filter.h" +#include "srslte/upper/rlc.h" +#include "srslte/radio/radio.h" +#include "srslte/phy/utils/debug.h" + +#define START_TUNTAP +#define USE_RADIO + +/********************************************************************** + * Program arguments processing + ***********************************************************************/ + +#define LCID 3 + +typedef struct { + float rx_freq; + float tx_freq; + float rx_gain; + float tx_gain; + bool enable_gui; + int time_adv; + std::string ip_address; +}prog_args_t; + +uint32_t srsapps_verbose = 1; + +prog_args_t prog_args; + +void args_default(prog_args_t *args) { + args->rx_freq = 2.505e9; + args->tx_freq = 2.625e9; + args->rx_gain = 50.0; + args->tx_gain = 70.0; + args->enable_gui = false; + args->time_adv = -1; // calibrated for b210 + args->ip_address = "192.168.3.1"; +} + +void usage(prog_args_t *args, char *prog) { + printf("Usage: %s [gGIrfFdv] \n", prog); + printf("\t-f RX frequency [Default %.1f MHz]\n", args->rx_freq/1e6); + printf("\t-F TX frequency [Default %.1f MHz]\n", args->tx_freq/1e6); + printf("\t-g RX gain [Default %.1f]\n", args->rx_gain); + printf("\t-G TX gain [Default %.1f]\n", args->tx_gain); + printf("\t-I IP address [Default %s]\n", args->ip_address.c_str()); + printf("\t-t time advance (in samples) [Default %d]\n", args->time_adv); + printf("\t-d Enable gui [Default disabled]\n"); + printf("\t-v [increase verbosity, default none]\n"); +} + +void parse_args(prog_args_t *args, int argc, char **argv) { + int opt; + args_default(args); + while ((opt = getopt(argc, argv, "gGfFItdv")) != -1) { + switch (opt) { + case 'd': + args->enable_gui = true; + break; + case 'g': + args->rx_gain = atof(argv[optind]); + break; + case 'G': + args->tx_gain = atof(argv[optind]); + break; + case 'f': + args->rx_freq = atof(argv[optind]); + break; + case 'F': + args->tx_freq = atof(argv[optind]); + break; + case 'I': + args->ip_address = argv[optind]; + break; + case 't': + args->time_adv = atoi(argv[optind]); + break; + case 'v': + srsapps_verbose++; + break; + default: + usage(args, argv[0]); + exit(-1); + } + } + if (args->rx_freq < 0 || args->tx_freq < 0) { + usage(args, argv[0]); + exit(-1); + } +} + +LIBLTE_BYTE_MSG_STRUCT sib_buffer[2]; + +int setup_if_addr(char *ip_addr); + +class tester : public srsue::pdcp_interface_rlc, + public srsue::rrc_interface_rlc, + public srsue::ue_interface, + public srsenb::rlc_interface_mac, + public srsenb::rrc_interface_mac, + public thread +{ +public: + + tester() { + rnti = 0; + } + + void init(srslte::rlc *rlc_, srsenb::mac *mac_, srsenb::phy *phy_, srslte::log *log_h_, std::string ip_address) { + log_h = log_h_; + rlc = rlc_; + mac = mac_; + phy = phy_; + + tun_fd = 0; + +#ifdef START_TUNTAP + if (init_tuntap((char*) ip_address.c_str())) { + log_h->error("Initiating IP address\n"); + } +#endif + + pool = srslte::byte_buffer_pool::get_instance(); + + // Start reader thread + running=true; + start(); + } + + void write_pdu_bcch_bch(srslte::byte_buffer_t *sdu) {} + void write_pdu_bcch_dlsch(srslte::byte_buffer_t *sdu) {} + void write_pdu_pcch(srslte::byte_buffer_t *sdu) {} + void max_retx_attempted(){} + void add_user(uint16_t rnti) {} + void release_user(uint16_t rnti) {} + void upd_user(uint16_t rnti, uint16_t old_rnti) {} + void set_activity_user(uint16_t rnti) {} + bool is_paging_opportunity(uint32_t tti, uint32_t *payload_len) {return false;} + void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) {} + + void write_pdu(uint32_t lcid, srslte::byte_buffer_t *sdu) + { + int n = write(tun_fd, sdu->msg, sdu->N_bytes); + if (n != (int) sdu->N_bytes) { + log_h->error("TUN/TAP write failure n=%d, nof_bytes=%d\n", n, sdu->N_bytes); + return; + } + log_h->debug_hex(sdu->msg, sdu->N_bytes, + "Wrote %d bytes to TUN/TAP\n", + sdu->N_bytes); + pool->deallocate(sdu); + } + + int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) + { + return rlc->read_pdu(lcid, payload, nof_bytes); + } + + void read_pdu_bcch_dlsch(uint32_t sib_index, uint8_t payload[srsenb::sched_interface::MAX_SIB_PAYLOAD_LEN]) + { + if (sib_index < 2) { + memcpy(payload, sib_buffer[sib_index].msg, sib_buffer[sib_index].N_bytes); + } + } + + void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) + { + srslte::byte_buffer_t *sdu = NULL; + log_h->info("Received PDU rnti=0x%x, lcid=%d, nof_bytes=%d\n", rnti, lcid, nof_bytes); + switch(lcid) { + case LCID: + rlc->write_pdu(lcid, payload, nof_bytes); + break; + case 0: + log_h->info("Received ConnectionRequest from rnti=0x%x\n", rnti); + + // Configure User in MAC + srsenb::sched_interface::ue_cfg_t uecfg; + bzero(&uecfg, sizeof(srsenb::sched_interface::ue_cfg_t)); + uecfg.maxharq_tx = 5; + uecfg.continuous_pusch = false; + uecfg.ue_bearers[0].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + uecfg.ue_bearers[LCID].direction = srsenb::sched_interface::ue_bearer_cfg_t::BOTH; + mac->ue_cfg(rnti, &uecfg); + + // configure DRB1 as UM + LIBLTE_RRC_RLC_CONFIG_STRUCT cfg; + bzero(&cfg, sizeof(LIBLTE_RRC_RLC_CONFIG_STRUCT)); + cfg.rlc_mode = LIBLTE_RRC_RLC_MODE_UM_BI; + cfg.dl_um_bi_rlc.t_reordering = LIBLTE_RRC_T_REORDERING_MS100; + cfg.dl_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + cfg.ul_um_bi_rlc.sn_field_len = LIBLTE_RRC_SN_FIELD_LENGTH_SIZE10; + rlc->add_bearer(LCID, &cfg); + + // Send dummy ConnectionSetup. MAC will send contention resolution ID automatically. + log_h->info("Sending ConnectionSetup\n"); + sdu = pool->allocate(); + sdu->msg[0] = 0xab; + sdu->N_bytes = 1; + rlc->write_sdu(0, sdu); + + // Indicate RLC status to mac + mac->rlc_buffer_state(rnti, 0, 1, 0); + + LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT dedicated; + bzero(&dedicated, sizeof(LIBLTE_RRC_PHYSICAL_CONFIG_DEDICATED_STRUCT)); + dedicated.pusch_cnfg_ded.beta_offset_ack_idx = 5; + dedicated.pusch_cnfg_ded.beta_offset_ri_idx = 12; + dedicated.pusch_cnfg_ded.beta_offset_cqi_idx = 15; + dedicated.pusch_cnfg_ded_present = true; + dedicated.sched_request_cnfg.dsr_trans_max = LIBLTE_RRC_DSR_TRANS_MAX_N4; + dedicated.sched_request_cnfg.sr_pucch_resource_idx = 0; + dedicated.sched_request_cnfg.sr_cnfg_idx = 35; + dedicated.sched_request_cnfg_present = true; + phy->set_config_dedicated(rnti, &dedicated); + + usleep(500); + break; + default: + log_h->error("Received message for lcid=%d\n", lcid); + break; + } + } + + void rl_failure(uint16_t rnti) + { + log_h->console("Disconnecting rnti=0x%x.\n", rnti); + mac->ue_rem(rnti); + rlc->reset(); + } + +private: + int tun_fd; + bool running; + srslte::log *log_h; + srslte::byte_buffer_pool *pool; + srslte::rlc *rlc; + srsenb::mac *mac; + srsenb::phy *phy; + uint16_t rnti; + bool read_enable; + + int init_tuntap(char *ip_address) { + read_enable = true; + tun_fd = setup_if_addr(ip_address); + if (tun_fd<0) { + fprintf(stderr, "Error setting up IP %s\n", ip_address); + return -1; + } + printf("Created tun/tap interface at IP %s\n", ip_address); + return 0; + } + + void run_thread() { + struct iphdr *ip_pkt; + uint32_t idx = 0; + int32_t N_bytes = 0; + srslte::byte_buffer_t *pdu = pool->allocate(); + + log_h->info("TUN/TAP reader thread running\n"); + + int first=1; + while(running) { + if (tun_fd > 0) { + pdu->msg[0] = 0x0; + N_bytes = read(tun_fd, &pdu->msg[idx], SRSUE_MAX_BUFFER_SIZE_BYTES-SRSUE_BUFFER_HEADER_OFFSET - idx); + } + if(N_bytes > 0) + { + if (read_enable && pdu->msg[0] != 0x60) { + + pdu->N_bytes = idx + N_bytes; + ip_pkt = (struct iphdr*)pdu->msg; + + log_h->debug_hex(pdu->msg, pdu->N_bytes, + "Read %d bytes from TUN/TAP\n", + N_bytes); + + // Check if entire packet was received + if(ntohs(ip_pkt->tot_len) == pdu->N_bytes) + { + // Send PDU directly to RLC + pdu->set_timestamp(); + rlc->write_sdu(LCID, pdu); + + // Indicate RLC status to mac + mac->rlc_buffer_state(rnti, LCID, rlc->get_buffer_state(LCID), 0); + + pdu = pool->allocate(); + idx = 0; + } else{ + idx += N_bytes; + } + } + }else{ + log_h->error("Failed to read from TUN interface - gw receive thread exiting.\n"); + break; + } + } + } +}; + + +// Create classes +srslte::logger logger; +srslte::log_filter log_phy; +srslte::log_filter log_mac; +srslte::log_filter log_rlc; +srslte::log_filter log_tester; +srsenb::phy my_phy; +srsenb::mac my_mac; +srslte::rlc my_rlc; +srslte::radio my_radio; + +// Local classes for testing +tester my_tester; + + +void generate_cell_configuration(srsenb::sched_interface::cell_cfg_t *mac_cfg, srsenb::phy_cfg_t *phy_cfg) +{ + // Main cell configuration + srslte_cell_t cell; + cell.id = 0; + cell.cp = SRSLTE_CP_NORM; + cell.nof_ports = 1; + cell.nof_prb = 25; + cell.phich_length = SRSLTE_PHICH_NORM; + cell.phich_resources = SRSLTE_PHICH_R_1; + + // Generate SIB1 + LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT msg[2]; + bzero(&msg[0], sizeof(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT)); + bzero(&msg[1], sizeof(LIBLTE_RRC_BCCH_DLSCH_MSG_STRUCT)); + + msg[0].N_sibs = 1; + msg[0].sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_1_STRUCT *sib1 = &msg[0].sibs[0].sib.sib1; + + sib1->cell_id = 0x1234; + sib1->tracking_area_code = 0x1234; + sib1->freq_band_indicator = 2; + sib1->N_plmn_ids = 1; + sib1->plmn_id[0].id.mcc = 1; + sib1->plmn_id[0].id.mnc = 1; + sib1->plmn_id[0].resv_for_oper = LIBLTE_RRC_NOT_RESV_FOR_OPER; + sib1->cell_barred = LIBLTE_RRC_CELL_NOT_BARRED; + sib1->intra_freq_reselection = LIBLTE_RRC_INTRA_FREQ_RESELECTION_ALLOWED; + sib1->q_rx_lev_min = -140; + sib1->q_rx_lev_min_offset = 1; + sib1->p_max = 10; + sib1->p_max_present = true; + sib1->si_window_length = LIBLTE_RRC_SI_WINDOW_LENGTH_MS40; + sib1->N_sched_info = 1; + sib1->sched_info[0].si_periodicity = LIBLTE_RRC_SI_PERIODICITY_RF16; + sib1->sched_info[0].N_sib_mapping_info = 0; + sib1->system_info_value_tag = 8; + + // Generate SIB2 + msg[1].N_sibs = 2; + msg[1].sibs[0].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2; + msg[1].sibs[1].sib_type = LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3; + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_2_STRUCT *sib2 = &msg[1].sibs[0].sib.sib2; + + // RACH configuration + sib2->rr_config_common_sib.rach_cnfg.num_ra_preambles = LIBLTE_RRC_NUMBER_OF_RA_PREAMBLES_N64; + sib2->rr_config_common_sib.rach_cnfg.preambles_group_a_cnfg.present = false; + sib2->rr_config_common_sib.rach_cnfg.preamble_init_rx_target_pwr = LIBLTE_RRC_PREAMBLE_INITIAL_RECEIVED_TARGET_POWER_DBM_N90; + sib2->rr_config_common_sib.rach_cnfg.pwr_ramping_step = LIBLTE_RRC_POWER_RAMPING_STEP_DB6; + sib2->rr_config_common_sib.rach_cnfg.preamble_trans_max = LIBLTE_RRC_PREAMBLE_TRANS_MAX_N10; + sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size = LIBLTE_RRC_RA_RESPONSE_WINDOW_SIZE_SF10; + sib2->rr_config_common_sib.rach_cnfg.mac_con_res_timer = LIBLTE_RRC_MAC_CONTENTION_RESOLUTION_TIMER_SF40; + sib2->rr_config_common_sib.rach_cnfg.max_harq_msg3_tx = 4; + + // BCCH + sib2->rr_config_common_sib.bcch_cnfg.modification_period_coeff = LIBLTE_RRC_MODIFICATION_PERIOD_COEFF_N16; + + // PCCH + sib2->rr_config_common_sib.pcch_cnfg.default_paging_cycle = LIBLTE_RRC_DEFAULT_PAGING_CYCLE_RF128; + sib2->rr_config_common_sib.pcch_cnfg.nB = LIBLTE_RRC_NB_ONE_THIRTY_SECOND_T; + + // PRACH Configuration + sib2->rr_config_common_sib.prach_cnfg.root_sequence_index = 41; + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.high_speed_flag = false; + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_config_index = 4; + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.prach_freq_offset = 2; + sib2->rr_config_common_sib.prach_cnfg.prach_cnfg_info.zero_correlation_zone_config = 11; + + // PDSCH configuration + sib2->rr_config_common_sib.pdsch_cnfg.p_b = 0; + sib2->rr_config_common_sib.pdsch_cnfg.rs_power = -5; + + // PUSCH configuration + sib2->rr_config_common_sib.pusch_cnfg.n_sb = 1; + sib2->rr_config_common_sib.pusch_cnfg.hopping_mode = LIBLTE_RRC_HOPPING_MODE_INTER_SUBFRAME; + sib2->rr_config_common_sib.pusch_cnfg.pusch_hopping_offset = 4; + sib2->rr_config_common_sib.pusch_cnfg.enable_64_qam = false; + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.cyclic_shift = 0; + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_assignment_pusch = 0; + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.group_hopping_enabled = false; + sib2->rr_config_common_sib.pusch_cnfg.ul_rs.sequence_hopping_enabled = false; + + // PUCCH configuration + sib2->rr_config_common_sib.pucch_cnfg.delta_pucch_shift = LIBLTE_RRC_DELTA_PUCCH_SHIFT_DS2; + sib2->rr_config_common_sib.pucch_cnfg.n_rb_cqi = 2; + sib2->rr_config_common_sib.pucch_cnfg.n_cs_an = 0; + sib2->rr_config_common_sib.pucch_cnfg.n1_pucch_an = 12; + + // SRS configuration + sib2->rr_config_common_sib.srs_ul_cnfg.present = false; + + // UL power control + sib2->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pusch = -80; + sib2->rr_config_common_sib.ul_pwr_ctrl.alpha = LIBLTE_RRC_UL_POWER_CONTROL_ALPHA_1; + sib2->rr_config_common_sib.ul_pwr_ctrl.p0_nominal_pucch = -80; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1 = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1_0; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_1b = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_1B_5; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2 = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2_2; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2a = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2A_2; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_flist_pucch.format_2b = LIBLTE_RRC_DELTA_F_PUCCH_FORMAT_2B_2; + sib2->rr_config_common_sib.ul_pwr_ctrl.delta_preamble_msg3 = 4; + + sib2->rr_config_common_sib.ul_cp_length = LIBLTE_RRC_UL_CP_LENGTH_1; + + sib2->ue_timers_and_constants.t300 = LIBLTE_RRC_T300_MS1000; + sib2->ue_timers_and_constants.t301 = LIBLTE_RRC_T301_MS1000; + sib2->ue_timers_and_constants.n310 = LIBLTE_RRC_N310_N10; + sib2->ue_timers_and_constants.t311 = LIBLTE_RRC_T311_MS1000; + sib2->ue_timers_and_constants.n311 = LIBLTE_RRC_N311_N1; + + sib2->time_alignment_timer = LIBLTE_RRC_TIME_ALIGNMENT_TIMER_INFINITY; + sib2->additional_spectrum_emission = 1; + sib2->arfcn_value_eutra.present = false; + sib2->ul_bw.present = false; + + LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT *sib3 = &msg[1].sibs[1].sib.sib3; + + bzero(sib3, sizeof(LIBLTE_RRC_SYS_INFO_BLOCK_TYPE_3_STRUCT)); + sib3->q_hyst = LIBLTE_RRC_Q_HYST_DB_2; + sib3->s_non_intra_search = 6; + sib3->s_non_intra_search_present = true; + sib3->thresh_serving_low = 4; + sib3->cell_resel_prio = 6; + sib3->q_rx_lev_min = -122; + sib3->p_max = 23; + sib3->p_max_present = true; + sib3->s_intra_search = 10; + sib3->s_intra_search_present = true; + sib3->presence_ant_port_1 = true; + sib3->neigh_cell_cnfg = 1; + sib3->t_resel_eutra = 1; + + // Genreate payload + LIBLTE_BIT_MSG_STRUCT bitbuffer[2]; + for (int i=0;i<2;i++) { + liblte_rrc_pack_bcch_dlsch_msg(&msg[i], &bitbuffer[i]); + srslte_bit_pack_vector(bitbuffer[i].msg, sib_buffer[i].msg, bitbuffer[i].N_bits); + sib_buffer[i].N_bytes = (bitbuffer[i].N_bits-1)/8+1; + } + + // Fill MAC scheduler configuration + bzero(mac_cfg, sizeof(srsenb::sched_interface::cell_cfg_t)); + memcpy(&mac_cfg->cell, &cell, sizeof(srslte_cell_t)); + mac_cfg->sibs[0].len = sib_buffer[0].N_bytes; + mac_cfg->sibs[0].period_rf = 8; // Fixed to 8 rf + mac_cfg->sibs[1].len = sib_buffer[1].N_bytes; + mac_cfg->sibs[1].period_rf = liblte_rrc_si_periodicity_num[sib1->sched_info[0].si_periodicity]; + mac_cfg->si_window_ms = liblte_rrc_si_window_length_num[sib1->si_window_length]; + + + mac_cfg->prach_rar_window = liblte_rrc_ra_response_window_size_num[sib2->rr_config_common_sib.rach_cnfg.ra_resp_win_size]; + + // Copy PHY common configuration + bzero(phy_cfg, sizeof(srsenb::phy_cfg_t)); + memcpy(&phy_cfg->cell, &cell, sizeof(srslte_cell_t)); + memcpy(&phy_cfg->prach_cnfg, &sib2->rr_config_common_sib.prach_cnfg, sizeof(LIBLTE_RRC_PRACH_CONFIG_SIB_STRUCT)); + memcpy(&phy_cfg->pdsch_cnfg, &sib2->rr_config_common_sib.pdsch_cnfg, sizeof(LIBLTE_RRC_PDSCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_cfg->pusch_cnfg, &sib2->rr_config_common_sib.pusch_cnfg, sizeof(LIBLTE_RRC_PUSCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_cfg->pucch_cnfg, &sib2->rr_config_common_sib.pucch_cnfg, sizeof(LIBLTE_RRC_PUCCH_CONFIG_COMMON_STRUCT)); + memcpy(&phy_cfg->srs_ul_cnfg, &sib2->rr_config_common_sib.srs_ul_cnfg, sizeof(LIBLTE_RRC_SRS_UL_CONFIG_COMMON_STRUCT)); +} + +int main(int argc, char *argv[]) +{ + + parse_args(&prog_args, argc, argv); + + logger.init("/tmp/ip_test.log"); + log_phy.init("PHY ", &logger, true); + log_mac.init("MAC ", &logger, true); + log_rlc.init("RLC ", &logger); + log_tester.init("TEST", &logger); + logger.log("\n\n"); + + if (srsapps_verbose == 1) { + log_phy.set_level(srslte::LOG_LEVEL_INFO); + log_phy.set_hex_limit(100); + log_mac.set_level(srslte::LOG_LEVEL_DEBUG); + log_mac.set_hex_limit(100); + log_rlc.set_level(srslte::LOG_LEVEL_DEBUG); + log_rlc.set_hex_limit(1000); + log_tester.set_level(srslte::LOG_LEVEL_DEBUG); + log_tester.set_hex_limit(100); + printf("Log level info\n"); + } + if (srsapps_verbose == 2) { + log_phy.set_level(srslte::LOG_LEVEL_DEBUG); + log_phy.set_hex_limit(100); + log_mac.set_level(srslte::LOG_LEVEL_DEBUG); + log_mac.set_hex_limit(100); + log_rlc.set_level(srslte::LOG_LEVEL_DEBUG); + log_rlc.set_hex_limit(100); + log_tester.set_level(srslte::LOG_LEVEL_DEBUG); + log_tester.set_hex_limit(100); + srslte_verbose = SRSLTE_VERBOSE_DEBUG; + printf("Log level debug\n"); + } + + // Init Radio and PHY +#ifdef USE_RADIO + my_radio.init(); +#else + my_radio.init(NULL, (char*) "dummy"); +#endif + my_radio.set_tx_freq(prog_args.tx_freq); + my_radio.set_tx_gain(prog_args.tx_gain); + my_radio.set_rx_freq(prog_args.rx_freq); + my_radio.set_rx_gain(prog_args.rx_gain); + //my_radio.set_tx_adv_neg(true); + if (prog_args.time_adv >= 0) { + printf("Setting TA=%d samples\n", prog_args.time_adv); + my_radio.set_tx_adv(prog_args.time_adv); + } + + // Configuure cell + srsenb::phy_cfg_t phy_cfg; + srsenb::sched_interface::cell_cfg_t mac_cfg; + srsenb::mac_args_t mac_args; + srsenb::phy_args_t phy_args; + + mac_args.link_failure_nof_err = 10; + phy_args.equalizer_mode = "mmse"; + phy_args.estimator_fil_w = 0.2; + phy_args.max_prach_offset_us = 50; + phy_args.nof_phy_threads = 1; + phy_args.pusch_max_its = 5; + + generate_cell_configuration(&mac_cfg, &phy_cfg); + + my_phy.init(&phy_args, &phy_cfg, &my_radio, &my_mac, &log_phy); + my_mac.init(&mac_args, &mac_cfg.cell, &my_phy, &my_tester, &my_tester, &log_mac); + my_rlc.init(&my_tester, &my_tester, &my_tester, &log_rlc, &my_mac); + my_tester.init(&my_rlc, &my_mac, &my_phy, &log_tester, prog_args.ip_address); + + if (prog_args.enable_gui) { + sleep(1); + my_phy.start_plot(); + } + + bool running = true; + while(running) { + printf("Main running\n"); + sleep(1); + } + my_phy.stop(); + my_mac.stop(); +} + + + + +/******************* This is copied from srsue gw **********************/ +int setup_if_addr(char *ip_addr) +{ + + char *dev = (char*) "tun_srsenb"; + + // Construct the TUN device + int tun_fd = open("/dev/net/tun", O_RDWR); + if(0 > tun_fd) + { + perror("open"); + return(-1); + } + + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_flags = IFF_TUN | IFF_NO_PI; + strncpy(ifr.ifr_ifrn.ifrn_name, dev, IFNAMSIZ); + if(0 > ioctl(tun_fd, TUNSETIFF, &ifr)) + { + perror("ioctl1"); + return -1; + } + + // Bring up the interface + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if(0 > ioctl(sock, SIOCGIFFLAGS, &ifr)) + { + perror("socket"); + return -1; + } + ifr.ifr_flags |= IFF_UP | IFF_RUNNING; + if(0 > ioctl(sock, SIOCSIFFLAGS, &ifr)) + { + perror("ioctl2"); + return -1; + } + + // Setup the IP address + sock = socket(AF_INET, SOCK_DGRAM, 0); + ifr.ifr_addr.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = inet_addr(ip_addr); + if(0 > ioctl(sock, SIOCSIFADDR, &ifr)) + { + perror("ioctl"); + return -1; + } + ifr.ifr_netmask.sa_family = AF_INET; + ((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr.s_addr = inet_addr("255.255.255.0"); + if(0 > ioctl(sock, SIOCSIFNETMASK, &ifr)) + { + perror("ioctl"); + return -1; + } + + return(tun_fd); +} diff --git a/srsenb/test/upper/plmn_test.cc b/srsenb/test/upper/plmn_test.cc new file mode 100644 index 000000000..ddf68d130 --- /dev/null +++ b/srsenb/test/upper/plmn_test.cc @@ -0,0 +1,77 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2016 Software Radio Systems Limited + * + * + */ + +#include +#include "upper/common_enb.h" +#include "srslte/asn1/liblte_rrc.h" + +void rrc_plmn_test() +{ + LIBLTE_RRC_PLMN_IDENTITY_STRUCT plmn_in, plmn_out; + plmn_in.mcc = 0xF123; + plmn_in.mnc = 0xFF45; + + // 2-digit MNC test + uint8_t bit_buf[32]; + uint8_t *ie_ptr = bit_buf; + + liblte_rrc_pack_plmn_identity_ie(&plmn_in, &ie_ptr); + uint8_t byte_buf[4]; + liblte_pack(bit_buf, 22, byte_buf); + uint8_t ref[3] = {0x89, 0x19, 0x05}; + for(int i=0;i<3;i++) { + assert(ref[i] == byte_buf[i]); + } + ie_ptr = bit_buf; + liblte_rrc_unpack_plmn_identity_ie(&ie_ptr, &plmn_out); + assert(plmn_in.mcc == plmn_out.mcc); + assert(plmn_in.mnc == plmn_out.mnc); + + // 3-digit MNC test + plmn_in.mnc = 0xF456; + ie_ptr = bit_buf; + liblte_rrc_pack_plmn_identity_ie(&plmn_in, &ie_ptr); + liblte_pack(bit_buf, 26, byte_buf); + uint8_t ref2[4] = {0x89, 0x1D, 0x15, 0x02}; + for(int i=0;i<3;i++) { + assert(ref2[i] == byte_buf[i]); + } + ie_ptr = bit_buf; + liblte_rrc_unpack_plmn_identity_ie(&ie_ptr, &plmn_out); + assert(plmn_in.mcc == plmn_out.mcc); + assert(plmn_in.mnc == plmn_out.mnc); +} + +void s1ap_plmn_test() +{ + uint16_t mcc = 0xF123; + uint16_t mnc = 0xFF45; + uint32_t plmn; + + // 2-digit MNC test + srsenb::s1ap_mccmnc_to_plmn(mcc, mnc, &plmn); + assert(plmn == 0x21F354); + srsenb::s1ap_plmn_to_mccmnc(plmn, &mcc, &mnc); + assert(mcc == 0xF123); + assert(mnc == 0xFF45); + + // 3-digit MNC test + mnc = 0xF456; + srsenb::s1ap_mccmnc_to_plmn(mcc, mnc, &plmn); + assert(plmn == 0x216354); + srsenb::s1ap_plmn_to_mccmnc(plmn, &mcc, &mnc); + assert(mcc == 0xF123); + assert(mnc == 0xF456); +} + +int main(int argc, char **argv) +{ + rrc_plmn_test(); + s1ap_plmn_test(); +}