From 613003ac53852f850b3ac80d30ea53d34d90d069 Mon Sep 17 00:00:00 2001 From: Francisco Paisana Date: Wed, 3 Jun 2020 19:59:25 +0100 Subject: [PATCH] add srsenb::rrc class --- lib/include/srslte/asn1/rrc_asn1.h | 3 +- lib/include/srslte/asn1/rrc_nr_asn1_utils.h | 55 +++ lib/src/asn1/CMakeLists.txt | 2 +- lib/src/asn1/rrc_nr_asn1_utils.cc | 88 +++++ srsenb/hdr/parser.h | 20 +- srsenb/hdr/stack/rrc/rrc_config.h | 15 +- srsenb/hdr/stack/rrc/rrc_config_common.h | 49 +++ srsenb/hdr/stack/rrc/rrc_nr.h | 176 +++++++++ srsenb/src/stack/rrc/CMakeLists.txt | 4 + srsenb/src/stack/rrc/rrc_nr.cc | 400 ++++++++++++++++++++ 10 files changed, 785 insertions(+), 27 deletions(-) create mode 100644 lib/include/srslte/asn1/rrc_nr_asn1_utils.h create mode 100644 lib/src/asn1/rrc_nr_asn1_utils.cc create mode 100644 srsenb/hdr/stack/rrc/rrc_config_common.h create mode 100644 srsenb/hdr/stack/rrc/rrc_nr.h create mode 100644 srsenb/src/stack/rrc/rrc_nr.cc diff --git a/lib/include/srslte/asn1/rrc_asn1.h b/lib/include/srslte/asn1/rrc_asn1.h index e791386a6..372dfe00b 100644 --- a/lib/include/srslte/asn1/rrc_asn1.h +++ b/lib/include/srslte/asn1/rrc_asn1.h @@ -16590,8 +16590,7 @@ struct rlc_cfg_v1510_s { // RLC-Config-v1530 ::= CHOICE struct rlc_cfg_v1530_c { - struct setup_s_ { - }; + struct setup_s_ {}; typedef setup_e types; // choice methods diff --git a/lib/include/srslte/asn1/rrc_nr_asn1_utils.h b/lib/include/srslte/asn1/rrc_nr_asn1_utils.h new file mode 100644 index 000000000..e653c21ab --- /dev/null +++ b/lib/include/srslte/asn1/rrc_nr_asn1_utils.h @@ -0,0 +1,55 @@ +/* + * Copyright 2013-2020 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/. + * + */ + +#ifndef SRSLTE_RRC_NR_ASN1_UTILS_H +#define SRSLTE_RRC_NR_ASN1_UTILS_H + +#include "srslte/interfaces/rrc_interface_types.h" +#include "srslte/interfaces/sched_interface.h" + +/************************ + * Forward declarations + ***********************/ +namespace asn1 { +namespace rrc_nr { + +struct plmn_id_s; +struct sib1_s; + +} // namespace rrc_nr +} // namespace asn1 + +/************************ + * Conversion Helpers + ***********************/ +namespace srslte { + +plmn_id_t make_plmn_id_t(const asn1::rrc_nr::plmn_id_s& asn1_type); +void to_asn1(asn1::rrc_nr::plmn_id_s* asn1_type, const plmn_id_t& cfg); + +} // namespace srslte + +namespace srsenb { + +int set_sched_cell_cfg_sib1(srsenb::sched_interface::cell_cfg_t* sched_cfg, const asn1::rrc_nr::sib1_s& sib1); +} + +#endif // SRSLTE_RRC_NR_ASN1_UTILS_H diff --git a/lib/src/asn1/CMakeLists.txt b/lib/src/asn1/CMakeLists.txt index 47293ecb3..4e83d6972 100644 --- a/lib/src/asn1/CMakeLists.txt +++ b/lib/src/asn1/CMakeLists.txt @@ -47,7 +47,7 @@ target_link_libraries(s1ap_asn1 asn1_utils srslte_common) if (ENABLE_5GNR) # RRC NR ASN1 - add_library(rrc_nr_asn1 STATIC rrc_nr_asn1.cc) + add_library(rrc_nr_asn1 STATIC rrc_nr_asn1.cc rrc_nr_asn1_utils.cc) target_compile_options(rrc_nr_asn1 PRIVATE "-Os") target_link_libraries(rrc_nr_asn1 asn1_utils srslte_common) diff --git a/lib/src/asn1/rrc_nr_asn1_utils.cc b/lib/src/asn1/rrc_nr_asn1_utils.cc new file mode 100644 index 000000000..b98a9e6cf --- /dev/null +++ b/lib/src/asn1/rrc_nr_asn1_utils.cc @@ -0,0 +1,88 @@ +/* + * Copyright 2013-2020 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/. + * + */ + +#include "srslte/asn1/rrc_nr_asn1_utils.h" +#include "srslte/asn1/rrc_nr_asn1.h" +#include "srslte/config.h" +#include + +namespace srslte { + +/*************************** + * PLMN ID + **************************/ + +bool plmn_is_valid(const asn1::rrc_nr::plmn_id_s& asn1_type) +{ + return asn1_type.mcc_present and (asn1_type.mnc.size() == 3 or asn1_type.mnc.size() == 2); +} + +plmn_id_t make_plmn_id_t(const asn1::rrc_nr::plmn_id_s& asn1_type) +{ + if (not plmn_is_valid(asn1_type)) { + return {}; + } + plmn_id_t plmn; + std::copy(&asn1_type.mcc[0], &asn1_type.mcc[3], &plmn.mcc[0]); + plmn.nof_mnc_digits = asn1_type.mnc.size(); + std::copy(&asn1_type.mnc[0], &asn1_type.mnc[plmn.nof_mnc_digits], &plmn.mnc[0]); + + return plmn; +} + +void to_asn1(asn1::rrc_nr::plmn_id_s* asn1_type, const plmn_id_t& cfg) +{ + asn1_type->mcc_present = true; + std::copy(&cfg.mcc[0], &cfg.mcc[3], &asn1_type->mcc[0]); + asn1_type->mnc.resize(cfg.nof_mnc_digits); + std::copy(&cfg.mnc[0], &cfg.mnc[cfg.nof_mnc_digits], &asn1_type->mnc[0]); +} + +} // namespace srslte + +namespace srsenb { + +int set_sched_cell_cfg_sib1(srsenb::sched_interface::cell_cfg_t* sched_cfg, const asn1::rrc_nr::sib1_s& sib1) +{ + bzero(sched_cfg, sizeof(srsenb::sched_interface::cell_cfg_t)); + + // set SIB1 and SIB2+ period + sched_cfg->sibs[0].period_rf = 16; // SIB1 is always 16 rf + for (uint32_t i = 0; i < sib1.si_sched_info.sched_info_list.size(); i++) { + sched_cfg->sibs[i + 1].period_rf = sib1.si_sched_info.sched_info_list[i].si_periodicity.to_number(); + } + + // si-WindowLength + sched_cfg->si_window_ms = sib1.si_sched_info.si_win_len.to_number(); + + // setup PRACH + if (not sib1.si_sched_info.si_request_cfg.rach_occasions_si_present) { + asn1::log_error("Expected RA Resp Win present\n"); + return SRSLTE_ERROR; + } + sched_cfg->prach_rar_window = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.ra_resp_win.to_number(); + sched_cfg->prach_freq_offset = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.msg1_freq_start; + sched_cfg->maxharq_msg3tx = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.preamb_trans_max; + + return SRSLTE_SUCCESS; +} + +} // namespace srsenb \ No newline at end of file diff --git a/srsenb/hdr/parser.h b/srsenb/hdr/parser.h index 439ede0a5..bd3f7aa59 100644 --- a/srsenb/hdr/parser.h +++ b/srsenb/hdr/parser.h @@ -54,11 +54,11 @@ public: 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) + field_enum_str(const char* name_, + T* store_ptr_, + const char** value_str_, + uint32_t nof_items_, + bool* enabled_value_ = NULL) { name = name_; store_ptr = store_ptr_; @@ -111,11 +111,11 @@ public: } private: - const char* name; - T* store_ptr; - const char (*value_str)[20]; - uint32_t nof_items; - bool* enabled_value; + const char* name; + T* store_ptr; + const char** value_str; + uint32_t nof_items; + bool* enabled_value; }; template diff --git a/srsenb/hdr/stack/rrc/rrc_config.h b/srsenb/hdr/stack/rrc/rrc_config.h index 9e8755d19..81233322d 100644 --- a/srsenb/hdr/stack/rrc/rrc_config.h +++ b/srsenb/hdr/stack/rrc/rrc_config.h @@ -22,6 +22,7 @@ #ifndef SRSLTE_RRC_CONFIG_H #define SRSLTE_RRC_CONFIG_H +#include "rrc_config_common.h" #include "srslte/asn1/rrc_asn1.h" #include "srslte/common/security.h" #include "srslte/interfaces/enb_rrc_interface_types.h" @@ -36,20 +37,6 @@ struct rrc_cfg_sr_t { uint32_t nof_subframes; }; -enum rrc_cfg_cqi_mode_t { RRC_CFG_CQI_MODE_PERIODIC = 0, RRC_CFG_CQI_MODE_APERIODIC, RRC_CFG_CQI_MODE_N_ITEMS }; - -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; - uint32_t m_ri; - bool simultaneousAckCQI; - rrc_cfg_cqi_mode_t mode; -} rrc_cfg_cqi_t; - typedef struct { bool configured; asn1::rrc::lc_ch_cfg_s::ul_specific_params_s_ lc_cfg; diff --git a/srsenb/hdr/stack/rrc/rrc_config_common.h b/srsenb/hdr/stack/rrc/rrc_config_common.h new file mode 100644 index 000000000..06598c839 --- /dev/null +++ b/srsenb/hdr/stack/rrc/rrc_config_common.h @@ -0,0 +1,49 @@ +/* + * Copyright 2013-2020 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/. + * + */ + +#ifndef SRSLTE_RRC_CONFIG_COMMON_H +#define SRSLTE_RRC_CONFIG_COMMON_H + +#include + +namespace srsenb { + +enum rrc_cfg_cqi_mode_t { RRC_CFG_CQI_MODE_PERIODIC = 0, RRC_CFG_CQI_MODE_APERIODIC, RRC_CFG_CQI_MODE_N_ITEMS }; +static const char* rrc_cfg_cqi_mode_text[] = {"periodic", "aperiodic"}; + +inline const char* to_string(rrc_cfg_cqi_mode_t mode) +{ + return mode < RRC_CFG_CQI_MODE_N_ITEMS ? rrc_cfg_cqi_mode_text[mode] : "invalid CQI mode"; +} + +struct rrc_cfg_cqi_t { + uint32_t sf_mapping[80]; + uint32_t nof_subframes; + uint32_t nof_prb; + uint32_t period; + uint32_t m_ri; + bool simultaneousAckCQI; + rrc_cfg_cqi_mode_t mode; +}; + +} // namespace srsenb + +#endif // SRSLTE_RRC_CONFIG_COMMON_H diff --git a/srsenb/hdr/stack/rrc/rrc_nr.h b/srsenb/hdr/stack/rrc/rrc_nr.h new file mode 100644 index 000000000..471e07110 --- /dev/null +++ b/srsenb/hdr/stack/rrc/rrc_nr.h @@ -0,0 +1,176 @@ +/* + * Copyright 2013-2020 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/. + * + */ + +#ifndef SRSENB_RRC_NR_H +#define SRSENB_RRC_NR_H + +#include "rrc_config_common.h" +#include "rrc_metrics.h" +#include "srsenb/hdr/stack/enb_stack_base.h" +#include "srslte/asn1/rrc_nr_asn1.h" +#include "srslte/common/block_queue.h" +#include "srslte/common/buffer_pool.h" +#include "srslte/common/common.h" +#include "srslte/common/logmap.h" +#include "srslte/common/threads.h" +#include "srslte/common/timeout.h" +#include "srslte/interfaces/gnb_interfaces.h" +#include "srsue/hdr/stack/upper/gw.h" +#include +#include + +namespace srsenb { + +enum class rrc_nr_state_t { RRC_IDLE, RRC_INACTIVE, RRC_CONNECTED }; + +// TODO: Make this common to NR and LTE +struct rrc_nr_cfg_sr_t { + uint32_t period; + // asn1::rrc::sched_request_cfg_c::setup_s_::dsr_trans_max_e_ dsr_max; + uint32_t nof_prb; + uint32_t sf_mapping[80]; + uint32_t nof_subframes; +}; +// Expert arguments to create GW without core NW +struct core_less_args_t { + std::string ip_addr; + srsue::gw_args_t gw_args; + uint8_t drb_lcid; + uint16_t rnti; +}; + +struct rrc_nr_cfg_t { + asn1::rrc_nr::mib_s mib; + asn1::rrc_nr::sib1_s sib1; + asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_item_c_ sibs[ASN1_RRC_NR_MAX_SIB]; + uint32_t nof_sibs; + rrc_nr_cfg_sr_t sr_cfg; + rrc_cfg_cqi_t cqi_cfg; + srslte_cell_t cell; + + std::string log_level; + uint32_t log_hex_limit; + + srsenb::core_less_args_t coreless; +}; + +class rrc_nr final : public rrc_interface_pdcp_nr, + public rrc_interface_mac_nr, + public rrc_interface_rlc_nr, + public rrc_interface_ngap_nr +{ +public: + explicit rrc_nr(srslte::timer_handler* timers_); + + void init(const rrc_nr_cfg_t& cfg, + phy_interface_stack_nr* phy, + mac_interface_rrc_nr* mac, + rlc_interface_rrc_nr* rlc, + pdcp_interface_rrc_nr* pdcp, + ngap_interface_rrc_nr* ngap_, + gtpu_interface_rrc_nr* gtpu); + + void stop(); + + void get_metrics(srsenb::rrc_metrics_t& m); + + rrc_nr_cfg_t update_default_cfg(const rrc_nr_cfg_t& rrc_cfg); + void add_user(uint16_t rnti); + void config_mac(); + uint32_t generate_sibs(); + int read_pdu_bcch_bch(const uint32_t tti, srslte::unique_byte_buffer_t& buffer) final; + int read_pdu_bcch_dlsch(uint32_t sib_index, srslte::unique_byte_buffer_t& buffer) final; + + // RLC interface + // TODO + void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) {} + void max_retx_attempted(uint16_t rnti) {} + + // PDCP interface + void write_pdu(uint16_t rnti, uint32_t lcid, srslte::unique_byte_buffer_t pdu) final; + + class ue + { + public: + ue(rrc_nr* parent_, uint16_t rnti_); + + void send_connection_setup(); + void send_dl_ccch(asn1::rrc_nr::dl_ccch_msg_s* dl_dcch_msg); + + // getters + bool is_connected() { return state == rrc_nr_state_t::RRC_CONNECTED; } + bool is_idle() { return state == rrc_nr_state_t::RRC_IDLE; } + bool is_inactive() { return state == rrc_nr_state_t::RRC_INACTIVE; } + + // setters + + private: + enum class progress_state_t { WAIT_FOR_CON_SETUP_COMPLETE, NONE }; + srslte::byte_buffer_pool* pool; + rrc_nr* parent; + uint16_t rnti; + + // state + rrc_nr_state_t state = rrc_nr_state_t::RRC_IDLE; + progress_state_t prog_state = progress_state_t::NONE; + uint8_t transaction_id = 0; + srslte::timer_handler::unique_timer rrc_setup_periodic_timer; + }; + +private: + rrc_nr_cfg_t cfg = {}; + + // interfaces + phy_interface_stack_nr* phy = nullptr; + mac_interface_rrc_nr* mac = nullptr; + rlc_interface_rrc_nr* rlc = nullptr; + pdcp_interface_rrc_nr* pdcp = nullptr; + gtpu_interface_rrc_nr* gtpu = nullptr; + ngap_interface_rrc_nr* ngap = nullptr; + + // args + srslte::byte_buffer_pool* pool = nullptr; + srslte::log_ref m_log; + srslte::timer_handler* timers = nullptr; + + // derived + uint32_t slot_dur_ms = 0; + + // vars + std::map > users; + bool running = false; + std::vector sib_buffer; + srslte::unique_byte_buffer_t mib_buffer = nullptr; + + uint32_t nof_si_messages = 0; + + // Private Methods + void handle_pdu(uint16_t rnti, uint32_t lcid, srslte::unique_byte_buffer_t pdu); + + // logging + typedef enum { Rx = 0, Tx } direction_t; + template + void log_rrc_message(const std::string& source, direction_t dir, const srslte::byte_buffer_t* pdu, const T& msg); +}; + +} // namespace srsenb + +#endif // SRSENB_RRC_NR_H \ No newline at end of file diff --git a/srsenb/src/stack/rrc/CMakeLists.txt b/srsenb/src/stack/rrc/CMakeLists.txt index 3a0d7cf59..45291e169 100644 --- a/srsenb/src/stack/rrc/CMakeLists.txt +++ b/srsenb/src/stack/rrc/CMakeLists.txt @@ -21,3 +21,7 @@ set(SOURCES rrc.cc rrc_ue.cc rrc_mobility.cc rrc_cell_cfg.cc rrc_bearer_cfg.cc) add_library(srsenb_rrc STATIC ${SOURCES}) +if (ENABLE_5GNR) + set(SOURCES rrc_nr.cc) + add_library(srsgnb_rrc STATIC ${SOURCES}) +endif () diff --git a/srsenb/src/stack/rrc/rrc_nr.cc b/srsenb/src/stack/rrc/rrc_nr.cc new file mode 100644 index 000000000..43e156442 --- /dev/null +++ b/srsenb/src/stack/rrc/rrc_nr.cc @@ -0,0 +1,400 @@ +/* + * Copyright 2013-2020 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/. + * + */ + +#include "srsenb/hdr/stack/rrc/rrc_nr.h" +#include "srsenb/hdr/stack/upper/common_enb.h" +#include "srslte/asn1/rrc_nr_asn1_utils.h" +#include "srslte/interfaces/nr_common_interface_types.h" + +using namespace asn1::rrc_nr; + +namespace srsenb { + +rrc_nr::rrc_nr(srslte::timer_handler* timers_) : + m_log("RRC"), + pool(srslte::byte_buffer_pool::get_instance()), + timers(timers_) +{} + +void rrc_nr::init(const rrc_nr_cfg_t& cfg_, + phy_interface_stack_nr* phy_, + mac_interface_rrc_nr* mac_, + rlc_interface_rrc_nr* rlc_, + pdcp_interface_rrc_nr* pdcp_, + ngap_interface_rrc_nr* ngap_, + gtpu_interface_rrc_nr* gtpu_) +{ + phy = phy_; + mac = mac_; + rlc = rlc_; + pdcp = pdcp_; + gtpu = gtpu_; + ngap = ngap_; + + // FIXME: overwriting because we are not passing config right now + cfg = update_default_cfg(cfg_); + + // config logging + m_log->set_level(cfg.log_level); + m_log->set_hex_limit(cfg.log_hex_limit); + + // derived + slot_dur_ms = 1; + + nof_si_messages = generate_sibs(); + config_mac(); + + // add dummy user + m_log->info("Creating dummy DRB for RNTI=%d on LCID=%d\n", cfg.coreless.rnti, cfg.coreless.drb_lcid); + add_user(cfg.coreless.rnti); + srslte::rlc_config_t rlc_cnfg = srslte::rlc_config_t::default_rlc_um_nr_config(6); + rlc->add_bearer(cfg.coreless.rnti, cfg.coreless.drb_lcid, rlc_cnfg); + srslte::pdcp_config_t pdcp_cnfg{cfg.coreless.drb_lcid, + srslte::PDCP_RB_IS_DRB, + srslte::SECURITY_DIRECTION_DOWNLINK, + srslte::SECURITY_DIRECTION_UPLINK, + srslte::PDCP_SN_LEN_18, + srslte::pdcp_t_reordering_t::ms500, + srslte::pdcp_discard_timer_t::infinity}; + pdcp->add_bearer(cfg.coreless.rnti, cfg.coreless.drb_lcid, pdcp_cnfg); + + m_log->info("Started\n"); + + running = true; +} + +void rrc_nr::stop() +{ + if (running) { + running = false; + } + users.clear(); +} + +template +void rrc_nr::log_rrc_message(const std::string& source, + const direction_t dir, + const srslte::byte_buffer_t* pdu, + const T& msg) +{ + if (m_log->get_level() == srslte::LOG_LEVEL_INFO) { + m_log->info("%s - %s %s (%d B)\n", + source.c_str(), + dir == Tx ? "Tx" : "Rx", + msg.msg.c1().type().to_string().c_str(), + pdu->N_bytes); + } else if (m_log->get_level() >= srslte::LOG_LEVEL_DEBUG) { + asn1::json_writer json_writer; + msg.to_json(json_writer); + m_log->debug_hex(pdu->msg, + pdu->N_bytes, + "%s - %s %s (%d B)\n", + source.c_str(), + dir == Tx ? "Tx" : "Rx", + msg.msg.c1().type().to_string().c_str(), + pdu->N_bytes); + m_log->debug("Content:\n%s\n", json_writer.to_string().c_str()); + } +} + +rrc_nr_cfg_t rrc_nr::update_default_cfg(const rrc_nr_cfg_t& current) +{ + // NOTE: This function is temporary. + rrc_nr_cfg_t cfg_default = current; + + // Fill MIB + cfg_default.mib.sub_carrier_spacing_common.value = mib_s::sub_carrier_spacing_common_opts::scs15or60; + cfg_default.mib.ssb_subcarrier_offset = 0; + cfg_default.mib.intra_freq_resel.value = mib_s::intra_freq_resel_opts::allowed; + cfg_default.mib.cell_barred.value = mib_s::cell_barred_opts::not_barred; + cfg_default.mib.pdcch_cfg_sib1.search_space_zero = 0; + cfg_default.mib.pdcch_cfg_sib1.ctrl_res_set_zero = 0; + cfg_default.mib.dmrs_type_a_position.value = mib_s::dmrs_type_a_position_opts::pos2; + cfg_default.mib.sys_frame_num.from_number(0); + + cfg_default.cell.nof_prb = 25; + cfg_default.cell.nof_ports = 1; + cfg_default.cell.id = 0; + cfg_default.cell.cp = SRSLTE_CP_NORM; + cfg_default.cell.frame_type = SRSLTE_FDD; + cfg_default.cell.phich_length = SRSLTE_PHICH_NORM; + cfg_default.cell.phich_resources = SRSLTE_PHICH_R_1; + + // Fill SIB1 + cfg_default.sib1.cell_access_related_info.plmn_id_list.resize(1); + cfg_default.sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list.resize(1); + srslte::plmn_id_t plmn; + plmn.from_string("90170"); + srslte::to_asn1(&cfg_default.sib1.cell_access_related_info.plmn_id_list[0].plmn_id_list[0], plmn); + cfg_default.sib1.cell_access_related_info.plmn_id_list[0].cell_id.from_number(1); + cfg_default.sib1.cell_access_related_info.plmn_id_list[0].cell_reserved_for_oper.value = + plmn_id_info_s::cell_reserved_for_oper_opts::not_reserved; + cfg_default.sib1.si_sched_info_present = true; + cfg_default.sib1.si_sched_info.si_request_cfg.rach_occasions_si_present = true; + cfg_default.sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.ra_resp_win.value = + rach_cfg_generic_s::ra_resp_win_opts::sl8; + cfg_default.sib1.si_sched_info.si_win_len.value = si_sched_info_s::si_win_len_opts::s20; + cfg_default.sib1.si_sched_info.sched_info_list.resize(1); + cfg_default.sib1.si_sched_info.sched_info_list[0].si_broadcast_status.value = + sched_info_s::si_broadcast_status_opts::broadcasting; + cfg_default.sib1.si_sched_info.sched_info_list[0].si_periodicity.value = sched_info_s::si_periodicity_opts::rf16; + cfg_default.sib1.si_sched_info.sched_info_list[0].sib_map_info.resize(1); + // scheduling of SI messages + cfg_default.sib1.si_sched_info.sched_info_list[0].sib_map_info[0].type.value = sib_type_info_s::type_opts::sib_type2; + cfg_default.sib1.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag_present = true; + cfg_default.sib1.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag = 0; + + // Fill SIB2+ + cfg_default.nof_sibs = 1; + sib2_s& sib2 = cfg_default.sibs[0].set_sib2(); + sib2.cell_resel_info_common.q_hyst.value = sib2_s::cell_resel_info_common_s_::q_hyst_opts::db5; + // FIXME: Fill SIB2 values + + // set loglevel + cfg_default.log_level = "debug"; + cfg_default.log_hex_limit = 10000; + + return cfg_default; +} + +// This function is called from PRACH worker (can wait) +void rrc_nr::add_user(uint16_t rnti) +{ + if (users.count(rnti) == 0) { + users.insert(std::make_pair(rnti, std::unique_ptr(new ue(this, rnti)))); + rlc->add_user(rnti); + pdcp->add_user(rnti); + m_log->info("Added new user rnti=0x%x\n", rnti); + } else { + m_log->error("Adding user rnti=0x%x (already exists)\n", rnti); + } +} + +void rrc_nr::config_mac() +{ + // Fill MAC scheduler configuration for SIBs + srsenb::sched_interface::cell_cfg_t sched_cfg; + set_sched_cell_cfg_sib1(&sched_cfg, cfg.sib1); + + // set SIB length + for (uint32_t i = 0; i < nof_si_messages + 1; i++) { + sched_cfg.sibs[i].len = sib_buffer[i]->N_bytes; + } + + // PUCCH width + sched_cfg.nrb_pucch = SRSLTE_MAX(cfg.sr_cfg.nof_prb, cfg.cqi_cfg.nof_prb); + m_log->info("Allocating %d PRBs for PUCCH\n", sched_cfg.nrb_pucch); + + // Copy Cell configuration + sched_cfg.cell = cfg.cell; + + // Configure MAC scheduler + mac->cell_cfg(&sched_cfg); +} + +uint32_t rrc_nr::generate_sibs() +{ + // MIB packing + bcch_bch_msg_s mib_msg; + mib_s& mib = mib_msg.msg.set_mib(); + mib = cfg.mib; + { + srslte::unique_byte_buffer_t mib_buf = srslte::allocate_unique_buffer(*pool); + asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom()); + mib_msg.pack(bref); + mib_buf->N_bytes = bref.distance_bytes(); + m_log->debug_hex(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)\n", mib_buf->N_bytes); + mib_buffer = std::move(mib_buf); + } + + si_sched_info_s::sched_info_list_l_& sched_info = cfg.sib1.si_sched_info.sched_info_list; + uint32_t nof_messages = cfg.sib1.si_sched_info_present ? cfg.sib1.si_sched_info.sched_info_list.size() : 0; + + // msg is array of SI messages, each SI message msg[i] may contain multiple SIBs + // all SIBs in a SI message msg[i] share the same periodicity + sib_buffer.reserve(nof_messages + 1); + asn1::dyn_array msg(nof_messages + 1); + + // Copy SIB1 to first SI message + msg[0].msg.set_c1().set_sib_type1() = cfg.sib1; + + // Copy rest of SIBs + for (uint32_t sched_info_elem = 0; sched_info_elem < nof_messages; sched_info_elem++) { + uint32_t msg_index = sched_info_elem + 1; // first msg is SIB1, therefore start with second + + msg[msg_index].msg.set_c1().set_sys_info().crit_exts.set_sys_info_r15(); + auto& sib_list = msg[msg_index].msg.c1().sys_info().crit_exts.sys_info_r15().sib_type_and_info; + + for (uint32_t mapping = 0; mapping < sched_info[sched_info_elem].sib_map_info.size(); ++mapping) { + uint32_t sibidx = sched_info[sched_info_elem].sib_map_info[mapping].type; // SIB2 == 0 + sib_list.push_back(cfg.sibs[sibidx]); + } + } + + // Pack payload for all messages + for (uint32_t msg_index = 0; msg_index < nof_messages + 1; msg_index++) { + srslte::unique_byte_buffer_t sib = srslte::allocate_unique_buffer(*pool); + asn1::bit_ref bref(sib->msg, sib->get_tailroom()); + msg[msg_index].pack(bref); + sib->N_bytes = bref.distance_bytes(); + sib_buffer.push_back(std::move(sib)); + + // Log SIBs in JSON format + log_rrc_message("SIB payload", Tx, sib_buffer.back().get(), msg[msg_index]); + } + + return sib_buffer.size() - 1; +} + +/******************************************************************************* + MAC interface +*******************************************************************************/ + +int rrc_nr::read_pdu_bcch_bch(const uint32_t tti, srslte::unique_byte_buffer_t& buffer) +{ + if (mib_buffer == nullptr || buffer->get_tailroom() < mib_buffer->N_bytes) { + return SRSLTE_ERROR; + } + memcpy(buffer->msg, mib_buffer->msg, mib_buffer->N_bytes); + buffer->N_bytes = mib_buffer->N_bytes; + return SRSLTE_SUCCESS; +} + +int rrc_nr::read_pdu_bcch_dlsch(uint32_t sib_index, srslte::unique_byte_buffer_t& buffer) +{ + if (sib_index >= sib_buffer.size()) { + m_log->error("SIB %d is not a configured SIB.\n", sib_index); + return SRSLTE_ERROR; + } + + if (buffer->get_tailroom() < sib_buffer[sib_index]->N_bytes) { + m_log->error("Not enough space to fit SIB %d into buffer (%d < %d)\n", + sib_index, + buffer->get_tailroom(), + sib_buffer[sib_index]->N_bytes); + return SRSLTE_ERROR; + } + + memcpy(buffer->msg, sib_buffer[sib_index]->msg, sib_buffer[sib_index]->N_bytes); + buffer->N_bytes = sib_buffer[sib_index]->N_bytes; + + return SRSLTE_SUCCESS; +} + +void rrc_nr::get_metrics(srsenb::rrc_metrics_t& m) +{ + // return metrics +} + +void rrc_nr::handle_pdu(uint16_t rnti, uint32_t lcid, srslte::unique_byte_buffer_t pdu) +{ + if (pdu) { + m_log->info_hex(pdu->msg, pdu->N_bytes, "Rx %s PDU", srslte::to_string(static_cast(lcid))); + } + + if (users.count(rnti) == 1) { + switch (lcid) { + case srsenb::RB_ID_SRB0: + // parse_ul_ccch(rnti, std::move(pdu)); + break; + case srsenb::RB_ID_SRB1: + case srsenb::RB_ID_SRB2: + // parse_ul_dcch(p.rnti, p.lcid, std::move(p.pdu)); + break; + default: + m_log->error("Rx PDU with invalid bearer id: %d", lcid); + break; + } + } else { + m_log->warning("Discarding PDU for removed rnti=0x%x\n", rnti); + } +} + +/******************************************************************************* + PDCP interface +*******************************************************************************/ +void rrc_nr::write_pdu(uint16_t rnti, uint32_t lcid, srslte::unique_byte_buffer_t pdu) +{ + handle_pdu(rnti, lcid, std::move(pdu)); +} + +/******************************************************************************* + UE class + + Every function in UE class is called from a mutex environment thus does not + need extra protection. +*******************************************************************************/ +rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_) : parent(parent_), rnti(rnti_) +{ + pool = srslte::byte_buffer_pool::get_instance(); + + // setup periodic RRCSetup send + rrc_setup_periodic_timer = parent->timers->get_unique_timer(); + rrc_setup_periodic_timer.set(5000, [this](uint32_t tid) { + send_connection_setup(); + rrc_setup_periodic_timer.run(); + }); + rrc_setup_periodic_timer.run(); +} + +void rrc_nr::ue::send_connection_setup() +{ + dl_ccch_msg_s dl_ccch_msg; + dl_ccch_msg.msg.set_c1().set_rrc_setup().rrc_transaction_id = ((transaction_id++) % 4u); + rrc_setup_ies_s& setup = dl_ccch_msg.msg.c1().rrc_setup().crit_exts.set_rrc_setup(); + radio_bearer_cfg_s& rr_cfg = setup.radio_bearer_cfg; + + // Add DRB1 to cfg + rr_cfg.drb_to_add_mod_list_present = true; + rr_cfg.drb_to_add_mod_list.resize(1); + auto& drb_item = rr_cfg.drb_to_add_mod_list[0]; + drb_item.drb_id = 1; + drb_item.pdcp_cfg_present = true; + drb_item.pdcp_cfg.ciphering_disabled_present = true; + // drb_item.cn_assoc_present = true; + // drb_item.cn_assoc.set_eps_bearer_id() = ; + drb_item.recover_pdcp_present = false; + + // TODO: send config to RLC/PDCP + + send_dl_ccch(&dl_ccch_msg); +} + +void rrc_nr::ue::send_dl_ccch(dl_ccch_msg_s* dl_ccch_msg) +{ + // Allocate a new PDU buffer, pack the message and send to PDCP + srslte::unique_byte_buffer_t pdu = srslte::allocate_unique_buffer(*pool); + if (pdu == nullptr) { + parent->m_log->error("Allocating pdu\n"); + } + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + if (dl_ccch_msg->pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) { + parent->m_log->error("Failed to pack DL-CCCH message. Discarding msg.\n"); + } + pdu->N_bytes = 1u + (uint32_t)bref.distance_bytes(pdu->msg); + + char buf[32] = {}; + sprintf(buf, "SRB0 - rnti=0x%x", rnti); + parent->log_rrc_message(buf, Tx, pdu.get(), *dl_ccch_msg); + parent->rlc->write_sdu(rnti, RB_ID_SRB0, std::move(pdu)); +} + +} // namespace srsenb \ No newline at end of file