diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h index ab6cebdb4..a8fa39515 100644 --- a/lib/include/srslte/interfaces/ue_interfaces.h +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -260,6 +260,13 @@ public: virtual void enable_encryption(uint32_t lcid, srslte::srslte_direction_t direction = srslte::srslte_direction_t::DIRECTION_TXRX) = 0; }; +// RRC NR interface for RRC (LTE) +class rrc_nr_interface_rrc +{ +public: + virtual void get_eutra_nr_capabilities(srslte::byte_buffer_t* eutra_nr_caps) = 0; + virtual void get_nr_capabilities(srslte::byte_buffer_t* nr_cap) = 0; +}; // PDCP interface for RLC class pdcp_interface_rlc diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index 5cb339b0a..3be8eb19d 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -79,7 +79,10 @@ public: nas_interface_rrc* nas_, usim_interface_rrc* usim_, gw_interface_rrc* gw_, - const rrc_args_t& args_); +#ifdef HAVE_5GNR + rrc_nr_interface_rrc* rrc_nr_, +#endif + const rrc_args_t& args_); void stop(); @@ -179,7 +182,9 @@ private: nas_interface_rrc* nas = nullptr; usim_interface_rrc* usim = nullptr; gw_interface_rrc* gw = nullptr; - +#ifdef HAVE_5GNR + rrc_nr_interface_rrc* rrc_nr = nullptr; +#endif srslte::unique_byte_buffer_t dedicated_info_nas; void send_ul_ccch_msg(const asn1::rrc::ul_ccch_msg_s& msg); @@ -410,6 +415,10 @@ private: void set_phy_default(); void set_mac_default(); void set_rrc_default(); + + // Helpers for nr communicaiton + asn1::rrc::ue_cap_rat_container_s get_eutra_nr_capabilities(); + asn1::rrc::ue_cap_rat_container_s get_nr_capabilities(); }; } // namespace srsue diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc/rrc_nr.h index 113cf45a5..054c2a361 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc/rrc_nr.h @@ -41,6 +41,7 @@ struct rrc_nr_metrics_t {}; class rrc_nr final : public rrc_interface_phy_nr, public rrc_interface_pdcp, public rrc_interface_rlc, + public rrc_nr_interface_rrc, public srslte::timer_callback { public: @@ -85,6 +86,10 @@ public: void write_pdu_pcch(srslte::unique_byte_buffer_t pdu) final; void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) final; + // RRC (LTE) interface + void get_eutra_nr_capabilities(srslte::byte_buffer_t* eutra_nr_caps); + void get_nr_capabilities(srslte::byte_buffer_t* eutra_nr_caps); + // STACK interface void cell_search_completed(const rrc_interface_phy_lte::cell_search_ret_t& cs_ret, const phy_cell_t& found_cell); diff --git a/srsue/hdr/stack/ue_stack_base.h b/srsue/hdr/stack/ue_stack_base.h index 4deaed457..9447b79d3 100644 --- a/srsue/hdr/stack/ue_stack_base.h +++ b/srsue/hdr/stack/ue_stack_base.h @@ -19,6 +19,7 @@ #include "srsue/hdr/ue_metrics_interface.h" #include "rrc/rrc.h" +#include "rrc/rrc_nr.h" #include "upper/gw.h" #include "upper/usim.h" diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index 207a0ec40..90584f0bd 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -167,6 +167,9 @@ private: srslte::rlc rlc; srslte::pdcp pdcp; srsue::rrc rrc; +#ifdef HAVE_5GNR + srsue::rrc_nr rrc_nr; +#endif srsue::nas nas; std::unique_ptr usim; }; diff --git a/srsue/src/stack/rrc/CMakeLists.txt b/srsue/src/stack/rrc/CMakeLists.txt index be7cd97e8..91a1a3633 100644 --- a/srsue/src/stack/rrc/CMakeLists.txt +++ b/srsue/src/stack/rrc/CMakeLists.txt @@ -7,6 +7,11 @@ # set(SOURCES rrc.cc rrc_procedures.cc rrc_meas.cc rrc_cell.cc phy_controller.cc) + +if(ENABLE_5GNR) + set(SOURCES ${SOURCES} rrc_nr.cc) +endif() + add_library(srsue_rrc STATIC ${SOURCES}) if(ENABLE_5GNR) diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 85db27320..44a2d1875 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -99,7 +99,10 @@ void rrc::init(phy_interface_rrc_lte* phy_, nas_interface_rrc* nas_, usim_interface_rrc* usim_, gw_interface_rrc* gw_, - const rrc_args_t& args_) +#ifdef HAVE_5GNR + rrc_nr_interface_rrc* rrc_nr_, +#endif + const rrc_args_t& args_) { pool = byte_buffer_pool::get_instance(); phy = phy_; @@ -109,7 +112,9 @@ void rrc::init(phy_interface_rrc_lte* phy_, nas = nas_; usim = usim_; gw = gw_; - +#ifdef HAVE_5GNR + rrc_nr = rrc_nr_; +#endif args = args_; auto on_every_cell_selection = [this](uint32_t earfcn, uint32_t pci, bool csel_result) { @@ -154,7 +159,6 @@ void rrc::init(phy_interface_rrc_lte* phy_, // initiate unique procedures ue_required_sibs.assign(&required_sibs[0], &required_sibs[NOF_REQUIRED_SIBS]); - running = true; initiated = true; } @@ -1764,6 +1768,9 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) cap.feature_group_inds_present = true; cap.feature_group_inds.from_number(args.feature_group); + ue_eutra_cap_v1280_ies_s* ue_eutra_cap_v1280_ies; + ue_eutra_cap_v1360_ies_s* ue_eutra_cap_v1360_ies; + ue_eutra_cap_v1450_ies_s* ue_eutra_cap_v1450_ies; if (args.release > 8) { ue_eutra_cap_v920_ies_s cap_v920; @@ -1919,6 +1926,64 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) .non_crit_ext.non_crit_ext_present = true; cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext .non_crit_ext.non_crit_ext = cap_v1250; + // 12.50 + cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext.non_crit_ext.non_crit_ext_present = true; + // 12.60 + cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true; + // 12.70 + cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true; + } + // Release 13 + if (args.release > 12) { + // 12.80 + ue_eutra_cap_v1280_ies = + &cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + ue_eutra_cap_v1280_ies->non_crit_ext_present = true; + // 13.10 + ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext_present = true; + // 13.20 + ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext_present = true; + // 13.30 + ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true; + // 13.40 + ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true; + // 13.50 + ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = + true; + } + // Release 14 + if (args.release > 13) { + // 13.60 + ue_eutra_cap_v1360_ies = + &ue_eutra_cap_v1280_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; + ue_eutra_cap_v1360_ies->non_crit_ext_present = true; + // 14.30 + ue_eutra_cap_v1360_ies->non_crit_ext.non_crit_ext_present = true; + // 14.40 + ue_eutra_cap_v1360_ies->non_crit_ext.non_crit_ext.non_crit_ext_present = true; + // 14.50 + ue_eutra_cap_v1360_ies->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true; + } + // Release 15 + if (args.release > 14) { + ue_eutra_cap_v1450_ies = &ue_eutra_cap_v1360_ies->non_crit_ext.non_crit_ext.non_crit_ext; + // 14.60 + ue_eutra_cap_v1450_ies->non_crit_ext_present = true; + + irat_params_nr_r15_s irat_params_nr_r15; + irat_params_nr_r15.en_dc_r15_present = true; + irat_params_nr_r15.supported_band_list_en_dc_r15_present = true; + + supported_band_nr_r15_s supported_band_nr_r15; + supported_band_nr_r15.band_nr_r15 = 78; + + irat_params_nr_r15.supported_band_list_en_dc_r15.push_back(supported_band_nr_r15); + ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext.irat_params_nr_r15_present = true; + ue_eutra_cap_v1450_ies->non_crit_ext.non_crit_ext.irat_params_nr_r15 = irat_params_nr_r15; } // Pack caps and copy to cap info @@ -1930,12 +1995,49 @@ void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry) info->ue_cap_rat_container_list[rat_idx].ue_cap_rat_container.resize(cap_len); memcpy(info->ue_cap_rat_container_list[rat_idx].ue_cap_rat_container.data(), buf, cap_len); rat_idx++; + + } +#ifdef HAVE_5GNR + else if (enquiry.crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[i] == rat_type_e::eutra_nr && has_nr_dc()) { + info->ue_cap_rat_container_list[rat_idx] = get_eutra_nr_capabilities(); + rrc_log->info("Including EUTRA-NR capabilities in UE Capability Info (%d B)\n", + info->ue_cap_rat_container_list[rat_idx].ue_cap_rat_container.size()); + rat_idx++; + } else if (enquiry.crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[i] == rat_type_e::nr && has_nr_dc()) { + info->ue_cap_rat_container_list[rat_idx] = get_nr_capabilities(); + rrc_log->info("Including NR capabilities in UE Capability Info (%d B)\n", + info->ue_cap_rat_container_list[rat_idx].ue_cap_rat_container.size()); + rat_idx++; + } +#endif + else { + rrc_log->error("RAT Type of UE Cap request not supported or not configured\n"); } } - // resize container back to the actually filled items info->ue_cap_rat_container_list.resize(rat_idx); +#ifdef HAVE_5GNR + if (enquiry.crit_exts.c1().ue_cap_enquiry_r8().non_crit_ext_present) { + if (enquiry.crit_exts.c1().ue_cap_enquiry_r8().non_crit_ext.non_crit_ext_present) { + if (enquiry.crit_exts.c1().ue_cap_enquiry_r8().non_crit_ext.non_crit_ext.non_crit_ext_present) { + if (enquiry.crit_exts.c1().ue_cap_enquiry_r8().non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present) { + if (enquiry.crit_exts.c1() + .ue_cap_enquiry_r8() + .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present) { + if (enquiry.crit_exts.c1() + .ue_cap_enquiry_r8() + .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext + .requested_freq_bands_nr_mrdc_r15_present) { + rrc_log->debug("Requested Freq Bands NR MRDC R15 present\n"); + } + } + } + } + } + } +#endif + send_ul_dcch_msg(RB_ID_SRB1, ul_dcch_msg); } @@ -2565,5 +2667,36 @@ void rrc::set_rrc_default() const std::string rrc::rb_id_str[] = {"SRB0", "SRB1", "SRB2", "DRB1", "DRB2", "DRB3", "DRB4", "DRB5", "DRB6", "DRB7", "DRB8"}; +// Helpers for nr communicaiton + +asn1::rrc::ue_cap_rat_container_s rrc::get_eutra_nr_capabilities() +{ + srslte::byte_buffer_t caps_buf; + asn1::rrc::ue_cap_rat_container_s cap; +#ifdef HAVE_5GNR + rrc_nr->get_eutra_nr_capabilities(&caps_buf); +#else + rrc_log->error("Not able to access get_eutra_nr_capabilities function\n"); +#endif + cap.rat_type = asn1::rrc::rat_type_e::eutra_nr; + cap.ue_cap_rat_container.resize(caps_buf.N_bytes); + memcpy(cap.ue_cap_rat_container.data(), caps_buf.msg, caps_buf.N_bytes); + return cap; +} + +asn1::rrc::ue_cap_rat_container_s rrc::get_nr_capabilities() +{ + srslte::byte_buffer_t caps_buf; + asn1::rrc::ue_cap_rat_container_s cap; +#ifdef HAVE_5GNR + rrc_nr->get_nr_capabilities(&caps_buf); +#else + rrc_log->error("Not able to access get_nr_capabilities function\n"); +#endif + cap.rat_type = asn1::rrc::rat_type_e::nr; + cap.ue_cap_rat_container.resize(caps_buf.N_bytes); + memcpy(cap.ue_cap_rat_container.data(), caps_buf.msg, caps_buf.N_bytes); + return cap; +} } // namespace srsue diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index 7ef742f50..28192fa64 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -86,6 +86,33 @@ void rrc_nr::write_pdu_bcch_dlsch(srslte::unique_byte_buffer_t pdu) {} void rrc_nr::write_pdu_pcch(srslte::unique_byte_buffer_t pdu) {} void rrc_nr::write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) {} +void rrc_nr::get_eutra_nr_capabilities(srslte::byte_buffer_t* eutra_nr_caps) +{ + uint8_t eutra_nr_cap_raw[] = {0x01, 0x1c, 0x04, 0x81, 0x60, 0x00, 0x1c, 0x4d, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x40, 0x04, 0x04, 0xd0, 0x10, 0x74, 0x06, 0x14, 0xe8, 0x1b, 0x10, + 0x78, 0x00, 0x00, 0x20, 0x00, 0x10, 0x08, 0x08, 0x01, 0x00, 0x20}; + + memcpy(eutra_nr_caps->msg, eutra_nr_cap_raw, sizeof(eutra_nr_cap_raw)); + eutra_nr_caps->N_bytes = sizeof(eutra_nr_cap_raw); + log_h->debug_hex( + eutra_nr_caps->msg, eutra_nr_caps->N_bytes, "EUTRA-NR capabilities (%u B)\n", eutra_nr_caps->N_bytes); + return; +} + +void rrc_nr::get_nr_capabilities(srslte::byte_buffer_t* nr_caps) +{ + uint8_t nr_cap_raw[] = { + 0xe1, 0x00, 0x00, 0x00, 0x01, 0x47, 0x7a, 0x03, 0x02, 0x00, 0x00, 0x01, 0x40, 0x48, 0x07, 0x06, 0x0e, 0x02, 0x0c, + 0x00, 0x02, 0x13, 0x60, 0x10, 0x73, 0xe4, 0x20, 0xf0, 0x00, 0x80, 0xc1, 0x30, 0x08, 0x0c, 0x00, 0x00, 0x0a, 0x05, + 0x89, 0xba, 0xc2, 0x19, 0x43, 0x40, 0x88, 0x10, 0x74, 0x18, 0x60, 0x4c, 0x04, 0x41, 0x6c, 0x90, 0x14, 0x06, 0x0c, + 0x78, 0xc7, 0x3e, 0x42, 0x0f, 0x00, 0x58, 0x0c, 0x0e, 0x0e, 0x02, 0x21, 0x3c, 0x84, 0xfc, 0x4d, 0xe0, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0xe5, 0x4d, 0x00, 0x01, 0x00, 0x00, 0x04, 0x18, 0x60, 0x00, 0x34, 0xaa, 0x60}; + memcpy(nr_caps->msg, nr_cap_raw, sizeof(nr_cap_raw)); + nr_caps->N_bytes = sizeof(nr_cap_raw); + log_h->debug_hex(nr_caps->msg, nr_caps->N_bytes, "NR capabilities (%u B)\n", nr_caps->N_bytes); + return; +} + // RLC interface void rrc_nr::max_retx_attempted() {} diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index 98150c0aa..66a9bee8b 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -31,6 +31,9 @@ ue_stack_lte::ue_stack_lte() : rlc("RLC"), mac("MAC", &task_sched), rrc(this, &task_sched), +#ifdef HAVE_5GNR + rrc_nr(), +#endif pdcp(&task_sched, "PDCP"), nas(&task_sched), thread("STACK"), @@ -118,7 +121,11 @@ int ue_stack_lte::init(const stack_args_t& args_, srslte::logger* logger_) rlc.init(&pdcp, &rrc, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */); pdcp.init(&rlc, &rrc, gw); nas.init(usim.get(), &rrc, gw, args.nas); +#ifdef HAVE_5GNR + rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc); +#else rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, args.rrc); +#endif running = true; start(STACK_MAIN_THREAD_PRIO); diff --git a/srsue/test/upper/rrc_meas_test.cc b/srsue/test/upper/rrc_meas_test.cc index db002f74f..a43368783 100644 --- a/srsue/test/upper/rrc_meas_test.cc +++ b/srsue/test/upper/rrc_meas_test.cc @@ -233,7 +233,11 @@ public: nastest = std::unique_ptr(new nas_test(&stack->task_sched)); pdcptest = std::unique_ptr(new pdcp_test(log_->get_service_name().c_str(), &stack->task_sched)); } +#ifdef HAVE_5GNR + void init() { rrc::init(&phytest, &mactest, nullptr, pdcptest.get(), nastest.get(), nullptr, nullptr, nullptr, {}); } +#else void init() { rrc::init(&phytest, &mactest, nullptr, pdcptest.get(), nastest.get(), nullptr, nullptr, {}); } +#endif void run_tti(uint32_t tti_) {