From 28bd93be4000c77c3a4622a1e35cad6775afa9cb Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 30 Nov 2021 17:11:43 +0100 Subject: [PATCH 1/2] ue,sa: add minimal cell selection and setup request procedure skeleton based on EUTRA procedures --- srsue/hdr/stack/rrc_nr/rrc_nr.h | 15 +- srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h | 59 +++++- srsue/src/stack/rrc_nr/rrc_nr.cc | 70 ++++++- srsue/src/stack/rrc_nr/rrc_nr_procedures.cc | 214 +++++++++++++++++++- 4 files changed, 342 insertions(+), 16 deletions(-) diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h index 5ba89bb87..a74730627 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -132,7 +132,9 @@ public: private: // senders + void send_setup_request(srsran::nr_establishment_cause_t cause); void send_ul_info_transfer(srsran::unique_byte_buffer_t nas_msg); + void send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg); srsran::task_sched_handle task_sched; struct cmd_msg_t { @@ -159,6 +161,10 @@ private: meas_cell_list meas_cells; + // PLMN + bool plmn_is_selected = false; + srsran::unique_byte_buffer_t dedicated_info_nas; + const uint32_t sim_measurement_timer_duration_ms = 250; uint32_t sim_measurement_carrier_freq_r15; srsran::timer_handler::unique_timer sim_measurement_timer; @@ -173,7 +179,7 @@ private: const static char* rrc_nr_state_text[RRC_NR_STATE_N_ITEMS]; rrc_nr_state_t state = RRC_NR_STATE_IDLE; - // Stores the state of the PHy configuration setting + // Stores the state of the PHY configuration setting enum { PHY_CFG_STATE_NONE = 0, PHY_CFG_STATE_APPLY_SP_CELL, @@ -219,9 +225,14 @@ private: typedef enum { mcg_srb1, en_dc_srb3, nr } reconf_initiator_t; // RRC procedures + enum class cell_search_result_t { changed_cell, same_cell, no_cell }; + class cell_selection_proc; class connection_reconf_no_ho_proc; + class setup_request_proc; - srsran::proc_t conn_recfg_proc; + srsran::proc_t cell_selector; + srsran::proc_t conn_recfg_proc; + srsran::proc_t setup_req_proc; srsran::proc_manager_list_t callback_list; }; diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h b/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h index 9f5e65561..8980c81d1 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h @@ -22,10 +22,65 @@ namespace srsue { * Procedures *******************************/ +class rrc_nr::cell_selection_proc +{ +public: + using cell_selection_complete_ev = srsran::proc_result_t; + + explicit cell_selection_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(); + srsran::proc_outcome_t step(); + cell_search_result_t get_result() const { return cell_search_ret; } + static const char* name() { return "Cell Selection"; } + srsran::proc_outcome_t react(const bool& event); + void then(const srsran::proc_result_t& proc_result) const; + +private: + bool is_serv_cell_suitable() const; + srsran::proc_outcome_t set_proc_complete(); + + // consts + rrc_nr& rrc_handle; + meas_cell_list& meas_cells; + + // state variables + enum class search_state_t { cell_selection, serv_cell_camp, cell_config, cell_search }; + cell_search_result_t cell_search_ret; + search_state_t state; + srsran::proc_future_t cell_search_fut; + srsran::proc_future_t serv_cell_cfg_fut; + phy_cell_t init_serv_cell; +}; + +class rrc_nr::setup_request_proc +{ +public: + explicit setup_request_proc(rrc_nr& parent_); + srsran::proc_outcome_t init(srsran::nr_establishment_cause_t cause_, + srsran::unique_byte_buffer_t dedicated_info_nas_); + srsran::proc_outcome_t step(); + void then(const srsran::proc_state_t& result); + srsran::proc_outcome_t react(const cell_selection_proc::cell_selection_complete_ev& e); + static const char* name() { return "Setup Request"; } + +private: + // const + rrc_nr& rrc_handle; + srslog::basic_logger& logger; + // args + srsran::nr_establishment_cause_t cause; + srsran::unique_byte_buffer_t dedicated_info_nas; + + // state variables + enum class state_t { cell_selection, config_serving_cell, wait_t300 } state; + cell_search_result_t cell_search_ret; + srsran::proc_future_t serv_cfg_fut; +}; + class rrc_nr::connection_reconf_no_ho_proc { public: - explicit connection_reconf_no_ho_proc(rrc_nr* parent_); + explicit connection_reconf_no_ho_proc(rrc_nr& parent_); srsran::proc_outcome_t init(const reconf_initiator_t initiator_, const bool endc_release_and_add_r15, const bool nr_secondary_cell_group_cfg_r15_present, @@ -41,7 +96,7 @@ public: private: // const - rrc_nr* rrc_ptr = nullptr; + rrc_nr& rrc_handle; reconf_initiator_t initiator; asn1::rrc_nr::rrc_recfg_s rrc_recfg; asn1::rrc_nr::cell_group_cfg_s cell_group_cfg; diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index 0523e7bb5..029296997 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -27,7 +27,12 @@ namespace srsue { const char* rrc_nr::rrc_nr_state_text[] = {"IDLE", "CONNECTED", "CONNECTED-INACTIVE"}; rrc_nr::rrc_nr(srsran::task_sched_handle task_sched_) : - logger(srslog::fetch_basic_logger("RRC-NR")), task_sched(task_sched_), conn_recfg_proc(this), meas_cells(task_sched_) + logger(srslog::fetch_basic_logger("RRC-NR")), + task_sched(task_sched_), + conn_recfg_proc(*this), + setup_req_proc(*this), + cell_selector(*this), + meas_cells(task_sched_) {} rrc_nr::~rrc_nr() = default; @@ -53,6 +58,8 @@ int rrc_nr::init(phy_interface_rrc_nr* phy_, stack = stack_; args = args_; + plmn_is_selected = true; // short-cut SA test + running = true; sim_measurement_timer = task_sched.get_unique_timer(); return SRSRAN_SUCCESS; @@ -221,8 +228,13 @@ bool rrc_nr::is_connected() return false; } -int rrc_nr::connection_request(srsran::nr_establishment_cause_t cause, srsran::unique_byte_buffer_t sdu) +int rrc_nr::connection_request(srsran::nr_establishment_cause_t cause, srsran::unique_byte_buffer_t dedicated_info_nas_) { + if (not setup_req_proc.launch(cause, std::move(dedicated_info_nas_))) { + logger.error("Failed to initiate setup request procedure"); + return SRSRAN_ERROR; + } + callback_list.add_proc(setup_req_proc); return SRSRAN_SUCCESS; } @@ -242,6 +254,58 @@ void rrc_nr::send_ul_info_transfer(unique_byte_buffer_t nas_msg) logger.warning("%s not implemented yet.", __FUNCTION__); } +void rrc_nr::send_setup_request(srsran::nr_establishment_cause_t cause) +{ + logger.debug("Preparing RRC Setup Request"); + + // Prepare SetupRequest packet + ul_ccch_msg_s ul_ccch_msg; + rrc_setup_request_ies_s* rrc_setup_req = &ul_ccch_msg.msg.set_c1().set_rrc_setup_request().rrc_setup_request; + + // TODO: implement ng_minus5_g_s_tmsi_part1 + rrc_setup_req->ue_id.set_random_value(); + // TODO use proper RNG + uint64_t random_id = 0; + for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes + random_id |= ((uint64_t)rand() & 0xFF) << i * 8; + } + rrc_setup_req->ue_id.random_value().from_number(random_id); + rrc_setup_req->establishment_cause = (establishment_cause_opts::options)cause; + + send_ul_ccch_msg(ul_ccch_msg); +} + +void rrc_nr::send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (pdu == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return; + } + + asn1::bit_ref bref(pdu->msg, pdu->get_tailroom()); + msg.pack(bref); + bref.align_bytes_zero(); + pdu->N_bytes = (uint32_t)bref.distance_bytes(pdu->msg); + pdu->set_timestamp(); + + // Set UE contention resolution ID in MAC + uint64_t uecri = 0; + uint8_t* ue_cri_ptr = (uint8_t*)&uecri; + uint32_t nbytes = 6; + for (uint32_t i = 0; i < nbytes; i++) { + ue_cri_ptr[nbytes - i - 1] = pdu->msg[i]; + } + + logger.debug("Setting UE contention resolution ID: %" PRIu64 "", uecri); + mac->set_contention_id(uecri); + + uint32_t lcid = 0; + log_rrc_message(get_rb_name(lcid), Tx, pdu.get(), msg, msg.msg.c1().type().to_string()); + + rlc->write_sdu(lcid, std::move(pdu)); +} + // EUTRA-RRC interface int rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) { @@ -1542,6 +1606,7 @@ bool rrc_nr::apply_radio_bearer_cfg(const radio_bearer_cfg_s& radio_bearer_cfg) } return true; } + // RLC interface void rrc_nr::max_retx_attempted() {} void rrc_nr::protocol_failure() {} @@ -1553,6 +1618,7 @@ void rrc_nr::ra_completed() phy->set_config(phy_cfg); phy_cfg_state = PHY_CFG_STATE_RA_COMPLETED; } + void rrc_nr::ra_problem() { rrc_eutra->nr_scg_failure_information(scg_failure_cause_t::random_access_problem); diff --git a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc index cfae613e0..7cf9d2ef0 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc @@ -13,10 +13,10 @@ #include "srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h" #include "srsran/common/standard_streams.h" -#define Error(fmt, ...) rrc_ptr->logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Warning(fmt, ...) rrc_ptr->logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Info(fmt, ...) rrc_ptr->logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) -#define Debug(fmt, ...) rrc_ptr->logger.debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Error(fmt, ...) rrc_handle.logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Warning(fmt, ...) rrc_handle.logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Info(fmt, ...) rrc_handle.logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) +#define Debug(fmt, ...) rrc_handle.logger.debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__) using namespace asn1::rrc_nr; using namespace asn1; @@ -24,7 +24,8 @@ using namespace srsran; namespace srsue { -rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr* parent_) : rrc_ptr(parent_), initiator(nr) {} +rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr& parent_) : rrc_handle(parent_), initiator(nr) +{} proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator_t initiator_, const bool endc_release_and_add_r15, @@ -55,6 +56,7 @@ proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator rrc_ptr->log_rrc_message( "RRC NR Reconfiguration", Rx, nr_secondary_cell_group_cfg_r15, rrc_recfg, "NR Secondary Cell Group Cfg R15"); #endif + if (rrc_recfg.crit_exts.type() != asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg) { Error("Reconfiguration does not contain Secondary Cell Group Config"); return proc_outcome_t::error; @@ -83,14 +85,14 @@ proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator #endif Info("Applying Cell Group Cfg"); - if (!rrc_ptr->apply_cell_group_cfg(cell_group_cfg)) { + if (!rrc_handle.apply_cell_group_cfg(cell_group_cfg)) { return proc_outcome_t::error; } } if (sk_counter_r15_present) { Info("Applying Cell Group Cfg"); - if (!rrc_ptr->configure_sk_counter((uint16_t)sk_counter_r15)) { + if (!rrc_handle.configure_sk_counter((uint16_t)sk_counter_r15)) { return proc_outcome_t::error; } } @@ -110,7 +112,7 @@ proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator #endif Info("Applying Radio Bearer Cfg"); - if (!rrc_ptr->apply_radio_bearer_cfg(radio_bearer_cfg)) { + if (!rrc_handle.apply_radio_bearer_cfg(radio_bearer_cfg)) { return proc_outcome_t::error; } } @@ -140,12 +142,12 @@ void rrc_nr::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& resu if (result.is_success()) { Info("Finished %s successfully", name()); srsran::console("RRC NR reconfiguration successful.\n"); - rrc_ptr->rrc_eutra->nr_rrc_con_reconfig_complete(true); + rrc_handle.rrc_eutra->nr_rrc_con_reconfig_complete(true); } else { // 5.3.5.8.2 Inability to comply with RRCReconfiguration switch (initiator) { case reconf_initiator_t::mcg_srb1: - rrc_ptr->rrc_eutra->nr_notify_reconfiguration_failure(); + rrc_handle.rrc_eutra->nr_notify_reconfiguration_failure(); break; default: Warning("Reconfiguration failure not implemented for initiator %d", initiator); @@ -157,4 +159,196 @@ void rrc_nr::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& resu return; } +/************************************** + * RRC Setup Request Procedure + *************************************/ + +rrc_nr::setup_request_proc::setup_request_proc(rrc_nr& parent_) : + rrc_handle(parent_), logger(srslog::fetch_basic_logger("RRC")) +{} + +proc_outcome_t rrc_nr::setup_request_proc::init(srsran::nr_establishment_cause_t cause_, + srsran::unique_byte_buffer_t dedicated_info_nas_) +{ + cause = cause_; + dedicated_info_nas = std::move(dedicated_info_nas_); + + if (!rrc_handle.plmn_is_selected) { + Error("Trying to connect but PLMN not selected."); + return proc_outcome_t::error; + } + + if (rrc_handle.state != RRC_NR_STATE_IDLE) { + logger.warning("Requested RRC connection establishment while not in IDLE"); + return proc_outcome_t::error; + } + + // TODO: add T302 handling + + Info("Initiation of Connection establishment procedure"); + + cell_search_ret = cell_search_result_t::no_cell; + + state = state_t::cell_selection; + if (rrc_handle.cell_selector.is_idle()) { + // No one is running cell selection + if (not rrc_handle.cell_selector.launch()) { + Error("Failed to initiate cell selection procedure..."); + return proc_outcome_t::error; + } + rrc_handle.callback_list.add_proc(rrc_handle.cell_selector); + } else { + Info("Cell selection proc already on-going. Wait for its result"); + } + return proc_outcome_t::yield; +} + +proc_outcome_t rrc_nr::setup_request_proc::step() +{ + if (state == state_t::cell_selection) { + // NOTE: cell selection will signal back with an event trigger + return proc_outcome_t::yield; + } + + if (state == state_t::config_serving_cell) { + // TODO: start serving cell config and start T300 + + // Send setup request message to lower layers + rrc_handle.send_setup_request(cause); + + Info("Waiting for RRCSetup/Reject or expiry"); + state = state_t::wait_t300; + return step(); + + } else if (state == state_t::wait_t300) { + // TODO: add T300 waiting + } + + return proc_outcome_t::error; +} + +void rrc_nr::setup_request_proc::then(const srsran::proc_state_t& result) +{ + if (result.is_error()) { + logger.warning("Could not establish connection. Deallocating dedicatedInfoNAS PDU"); + dedicated_info_nas.reset(); + rrc_handle.dedicated_info_nas.reset(); + } else { + Info("Finished connection request procedure successfully."); + } + // TODO: signal back to NAS + // rrc_handle.nas->connection_request_completed(result.is_success()); +} + +srsran::proc_outcome_t rrc_nr::setup_request_proc::react(const cell_selection_proc::cell_selection_complete_ev& e) +{ + if (state != state_t::cell_selection) { + // ignore if we are not expecting an cell selection result + return proc_outcome_t::yield; + } + if (e.is_error()) { + return proc_outcome_t::error; + } + cell_search_ret = *e.value(); + // .. and SI acquisition + // TODO @ismagom use appropiate PHY interface + if (true /*rrc_handle.phy->cell_is_camping()*/) { + // TODO: Set default configurations + // rrc_handle.set_phy_default(); + // rrc_handle.set_mac_default(); + + // CCCH configuration applied already at start + // timeAlignmentCommon applied in configure_serving_cell + + Info("Configuring serving cell..."); + state = state_t::config_serving_cell; + + // Skip SI acquisition + return step(); + } else { + switch (cell_search_ret) { + case cell_search_result_t::same_cell: + logger.warning("Did not reselect cell but serving cell is out-of-sync."); + break; + case cell_search_result_t::changed_cell: + logger.warning("Selected a new cell but could not camp on. Setting out-of-sync."); + break; + default: + logger.warning("Could not find any suitable cell to connect"); + } + return proc_outcome_t::error; + } +} + +/************************************** + * Basic Cell Selection Procedure + *************************************/ + +rrc_nr::cell_selection_proc::cell_selection_proc(rrc_nr& parent_) : + rrc_handle(parent_), meas_cells(rrc_handle.meas_cells) +{} + +/// Verifies if serving cell passes selection criteria, UE is camping, and required SIBs were obtained +bool rrc_nr::cell_selection_proc::is_serv_cell_suitable() const +{ + // TODO: add selection criteria + return true; +} + +/// Called on procedure exit to set result +proc_outcome_t rrc_nr::cell_selection_proc::set_proc_complete() +{ + if (is_serv_cell_suitable()) { + cell_search_ret = is_same_cell(init_serv_cell, meas_cells.serving_cell()) ? cell_search_result_t::same_cell + : cell_search_result_t::changed_cell; + return proc_outcome_t::success; + } + cell_search_ret = cell_search_result_t::no_cell; + return proc_outcome_t::error; +} + +proc_outcome_t rrc_nr::cell_selection_proc::init() +{ + init_serv_cell = meas_cells.serving_cell().phy_cell; + + // Skip cell selection if serving cell is suitable and there are no stronger neighbours in same earfcn + if (is_serv_cell_suitable()) { + Debug("Skipping cell selection procedure as there are no stronger neighbours in same EARFCN."); + return set_proc_complete(); + } + + // TODO: add full cell selection + return set_proc_complete(); +} + +proc_outcome_t rrc_nr::cell_selection_proc::step() +{ + switch (state) { + case search_state_t::cell_selection: + // this state waits for phy event + return proc_outcome_t::yield; + case search_state_t::serv_cell_camp: + // this state waits for phy event + return proc_outcome_t::yield; + case search_state_t::cell_config: + // return step_cell_config(); + return proc_outcome_t::yield; + case search_state_t::cell_search: + // return step_cell_search(); + return proc_outcome_t::yield; + } + return proc_outcome_t::error; +} + +void rrc_nr::cell_selection_proc::then(const srsran::proc_result_t& proc_result) const +{ + Info("Completed with %s.", proc_result.is_success() ? "success" : "failure"); + // Inform Connection Request Procedure + rrc_handle.task_sched.defer_task([this, proc_result]() { + if (rrc_handle.setup_req_proc.is_busy()) { + rrc_handle.setup_req_proc.trigger(proc_result); + } + }); +} + } // namespace srsue From 38da5a9c94d703a165776a1db4f12296073b7493 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Tue, 30 Nov 2021 18:13:31 +0100 Subject: [PATCH 2/2] ue,rrc,test: add RRC conn setup testcase --- srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc | 54 ++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc index 774eb4db5..0a0898475 100644 --- a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc +++ b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc @@ -58,7 +58,15 @@ class dummy_rlc : public rlc_interface_rrc bool has_bearer(uint32_t lcid) { return true; } bool has_data(const uint32_t lcid) { return true; } bool is_suspended(const uint32_t lcid) { return true; } - void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) {} + void write_sdu(uint32_t lcid, srsran::unique_byte_buffer_t sdu) + { + last_lcid = lcid; + last_sdu = std::move(sdu); + } + +public: + uint32_t last_lcid = 99; + srsran::unique_byte_buffer_t last_sdu; }; class dummy_pdcp : public pdcp_interface_rrc @@ -207,9 +215,53 @@ int rrc_nr_reconfig_test() return SRSRAN_SUCCESS; } +int rrc_nr_conn_setup_test() +{ + srslog::init(); + srslog::basic_logger& logger = srslog::fetch_basic_logger("RRC-NR"); + logger.set_level(srslog::basic_levels::debug); + logger.set_hex_dump_max_size(-1); + srsran::task_scheduler task_sched{512, 100}; + srsran::task_sched_handle task_sched_handle(&task_sched); + rrc_nr rrc_nr(task_sched_handle); + srsran::byte_buffer_t caps; + + dummy_phy dummy_phy; + dummy_mac dummy_mac; + dummy_rlc dummy_rlc; + dummy_pdcp dummy_pdcp; + dummy_gw dummy_gw; + dummy_eutra dummy_eutra; + dummy_sim dummy_sim; + dummy_stack dummy_stack; + rrc_nr_args_t rrc_nr_args; + + rrc_nr_args.supported_bands_nr.push_back(78); + + TESTASSERT(rrc_nr.init(&dummy_phy, + &dummy_mac, + &dummy_rlc, + &dummy_pdcp, + &dummy_gw, + &dummy_eutra, + &dummy_sim, + task_sched.get_timer_handler(), + &dummy_stack, + rrc_nr_args) == SRSRAN_SUCCESS); + rrc_nr.connection_request(srsran::nr_establishment_cause_t::mt_Access, nullptr); + task_sched.run_pending_tasks(); + + TESTASSERT(dummy_rlc.last_lcid == 0); // SRB0 transmission + TESTASSERT(dummy_rlc.last_sdu->N_bytes == 6); // RRC Setup Request is 6 Bytes long + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { TESTASSERT(rrc_nr_cap_request_test() == SRSRAN_SUCCESS); TESTASSERT(rrc_nr_reconfig_test() == SRSRAN_SUCCESS); + TESTASSERT(rrc_nr_conn_setup_test() == SRSRAN_SUCCESS); + return SRSRAN_SUCCESS; }