diff --git a/lib/include/srslte/interfaces/ue_interfaces.h b/lib/include/srslte/interfaces/ue_interfaces.h index 186a4bdf0..9f8261209 100644 --- a/lib/include/srslte/interfaces/ue_interfaces.h +++ b/lib/include/srslte/interfaces/ue_interfaces.h @@ -275,15 +275,16 @@ public: class rlc_interface_rrc { public: - virtual void reset() = 0; - virtual void reestablish() = 0; - virtual void reestablish(uint32_t lcid) = 0; + virtual void reset() = 0; + virtual void reestablish() = 0; + virtual void reestablish(uint32_t lcid) = 0; virtual void add_bearer(uint32_t lcid, srslte::rlc_config_t cnfg) = 0; - virtual void add_bearer_mrb(uint32_t lcid) = 0; - virtual void del_bearer(uint32_t lcid) = 0; + virtual void add_bearer_mrb(uint32_t lcid) = 0; + virtual void del_bearer(uint32_t lcid) = 0; + virtual void suspend_bearer(uint32_t lcid) = 0; virtual void resume_bearer(uint32_t lcid) = 0; - virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0; - virtual bool has_bearer(uint32_t lcid) = 0; + virtual void change_lcid(uint32_t old_lcid, uint32_t new_lcid) = 0; + virtual bool has_bearer(uint32_t lcid) = 0; virtual bool has_data(const uint32_t lcid) = 0; virtual void write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu, bool blocking = true) = 0; }; diff --git a/lib/include/srslte/upper/rlc.h b/lib/include/srslte/upper/rlc.h index d4f1f99e3..48457a1ef 100644 --- a/lib/include/srslte/upper/rlc.h +++ b/lib/include/srslte/upper/rlc.h @@ -80,6 +80,7 @@ public: void add_bearer_mrb(uint32_t lcid); void del_bearer(uint32_t lcid); void del_bearer_mrb(uint32_t lcid); + void suspend_bearer(uint32_t lcid); void resume_bearer(uint32_t lcid); void change_lcid(uint32_t old_lcid, uint32_t new_lcid); bool has_bearer(uint32_t lcid); diff --git a/lib/include/srslte/upper/rlc_am.h b/lib/include/srslte/upper/rlc_am.h index 1ce60d21e..e41a3d70a 100644 --- a/lib/include/srslte/upper/rlc_am.h +++ b/lib/include/srslte/upper/rlc_am.h @@ -70,7 +70,6 @@ public: srsue::rrc_interface_rlc* rrc_, srslte::mac_interface_timers* mac_timers_); ~rlc_am(); - bool resume(); bool configure(rlc_config_t cfg_); void reestablish(); void stop(); @@ -283,7 +282,6 @@ private: mac_interface_timers* mac_timers = nullptr; uint32_t lcid = 0; rlc_config_t cfg = {}; - bool has_configuration = false; std::string rb_name; static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested diff --git a/lib/include/srslte/upper/rlc_common.h b/lib/include/srslte/upper/rlc_common.h index f3edc25d5..c3647067a 100644 --- a/lib/include/srslte/upper/rlc_common.h +++ b/lib/include/srslte/upper/rlc_common.h @@ -22,6 +22,8 @@ #ifndef SRSLTE_RLC_COMMON_H #define SRSLTE_RLC_COMMON_H +#include "srslte/common/block_queue.h" + namespace srslte { /**************************************************************************** @@ -141,11 +143,43 @@ public: const static int RLC_BUFFER_NOF_PDU = 128; virtual ~rlc_common() {} - virtual bool configure(rlc_config_t cnfg) = 0; - virtual bool resume() = 0; - virtual void stop() = 0; - virtual void reestablish() = 0; - virtual void empty_queue() = 0; + virtual bool configure(rlc_config_t cnfg) = 0; + virtual void stop() = 0; + virtual void reestablish() = 0; + virtual void empty_queue() = 0; + + bool suspend() + { + if (is_suspended) { + return false; + } + is_suspended = true; + return true; + } + + // Pops all PDUs from queue and calls write_pdu() method for the bearer type + bool resume() + { + if (!is_suspended) { + return false; + } + pdu_t p; + // Do not block + while (rx_pdu_resume_queue.try_pop(&p)) { + write_pdu(p.payload, p.nof_bytes); + } + is_suspended = false; + return true; + } + + void write_pdu_s(uint8_t* payload, uint32_t nof_bytes) + { + if (is_suspended) { + queue_pdu(payload, nof_bytes); + } else { + write_pdu(payload, nof_bytes); + } + } virtual rlc_mode_t get_mode() = 0; virtual uint32_t get_bearer() = 0; @@ -162,6 +196,27 @@ public: virtual uint32_t get_buffer_state() = 0; virtual int read_pdu(uint8_t *payload, uint32_t nof_bytes) = 0; virtual void write_pdu(uint8_t *payload, uint32_t nof_bytes) = 0; + +private: + bool is_suspended = false; + + // Enqueues the PDU in the resume queue + void queue_pdu(uint8_t* payload, uint32_t nof_bytes) + { + pdu_t p = {payload, nof_bytes}; + // Do not block ever + if (!rx_pdu_resume_queue.try_push(p)) { + fprintf(stderr, "Error dropping PDUs while bearer suspended. Queue should be unbounded\n"); + return; + } + } + + typedef struct { + uint8_t* payload; + uint32_t nof_bytes; + } pdu_t; + + block_queue rx_pdu_resume_queue; }; } // namespace srslte diff --git a/lib/include/srslte/upper/rlc_tm.h b/lib/include/srslte/upper/rlc_tm.h index 60f2b2dcf..107b1310b 100644 --- a/lib/include/srslte/upper/rlc_tm.h +++ b/lib/include/srslte/upper/rlc_tm.h @@ -42,7 +42,6 @@ public: uint32_t queue_len = 16); ~rlc_tm(); bool configure(rlc_config_t cnfg); - bool resume(); void stop(); void reestablish(); void empty_queue(); diff --git a/lib/include/srslte/upper/rlc_um.h b/lib/include/srslte/upper/rlc_um.h index eeb56782e..26cc41f70 100644 --- a/lib/include/srslte/upper/rlc_um.h +++ b/lib/include/srslte/upper/rlc_um.h @@ -50,7 +50,6 @@ public: mac_interface_timers* mac_timers_); ~rlc_um(); bool configure(rlc_config_t cnfg); - bool resume(); void reestablish(); void stop(); void empty_queue(); @@ -204,7 +203,6 @@ private: srslte::log* log = nullptr; uint32_t lcid = 0; rlc_config_t cfg = {}; - bool has_configuration = false; std::string rb_name; byte_buffer_pool* pool = nullptr; diff --git a/lib/src/upper/rlc.cc b/lib/src/upper/rlc.cc index 4b87f7eff..7c0613a50 100644 --- a/lib/src/upper/rlc.cc +++ b/lib/src/upper/rlc.cc @@ -143,6 +143,7 @@ void rlc::reestablish(uint32_t lcid) { pthread_rwlock_rdlock(&rwlock); if (valid_lcid(lcid)) { + rlc_log->info("Reestablishing LCID %d\n", lcid); rlc_array.at(lcid)->reestablish(); } else { rlc_log->warning("RLC LCID %d doesn't exist. Deallocating SDU\n", lcid); @@ -309,7 +310,7 @@ void rlc::write_pdu(uint32_t lcid, uint8_t *payload, uint32_t nof_bytes) { pthread_rwlock_rdlock(&rwlock); if (valid_lcid(lcid)) { - rlc_array.at(lcid)->write_pdu(payload, nof_bytes); + rlc_array.at(lcid)->write_pdu_s(payload, nof_bytes); } else { rlc_log->warning("LCID %d doesn't exist. Dropping PDU.\n", lcid); } @@ -381,8 +382,7 @@ void rlc::add_bearer(uint32_t lcid, rlc_config_t cnfg) rlc_common *rlc_entity = NULL; if (not valid_lcid(lcid)) { - switch(cnfg.rlc_mode) - { + switch (cnfg.rlc_mode) { case rlc_mode_t::tm: rlc_entity = new rlc_tm(rlc_log, lcid, pdcp, rrc, mac_timers); break; @@ -397,24 +397,24 @@ void rlc::add_bearer(uint32_t lcid, rlc_config_t cnfg) goto unlock_and_exit; } - // configure and add to array - if (cnfg.rlc_mode != rlc_mode_t::tm) { - if (not rlc_entity->configure(cnfg)) { - rlc_log->error("Error configuring RLC entity\n."); - goto delete_and_exit; - } - } - if (not rlc_array.insert(rlc_map_pair_t(lcid, rlc_entity)).second) { rlc_log->error("Error inserting RLC entity in to array\n."); goto delete_and_exit; } rlc_log->info("Added radio bearer %s in %s\n", rrc->get_rb_name(lcid).c_str(), to_string(cnfg.rlc_mode).c_str()); - goto unlock_and_exit; - } else { - rlc_log->warning("Bearer %s already created.\n", rrc->get_rb_name(lcid).c_str()); + rlc_entity = NULL; } + // configure and add to array + if (cnfg.rlc_mode != rlc_mode_t::tm) { + if (not rlc_array.at(lcid)->configure(cnfg)) { + rlc_log->error("Error configuring RLC entity\n."); + goto delete_and_exit; + } + } + + rlc_log->info("Configured radio bearer %s in %s\n", rrc->get_rb_name(lcid).c_str(), to_string(cnfg.rlc_mode).c_str()); + delete_and_exit: if (rlc_entity) { delete(rlc_entity); @@ -521,15 +521,33 @@ exit: pthread_rwlock_unlock(&rwlock); } +void rlc::suspend_bearer(uint32_t lcid) +{ + pthread_rwlock_wrlock(&rwlock); + + if (valid_lcid(lcid)) { + if (rlc_array.at(lcid)->suspend()) { + rlc_log->info("Suspended radio bearer %s\n", rrc->get_rb_name(lcid).c_str()); + } else { + rlc_log->error("Error suspending RLC entity: bearer already suspended\n."); + } + } else { + rlc_log->error("Suspending bearer: bearer %s not configured.\n", rrc->get_rb_name(lcid).c_str()); + } + + pthread_rwlock_unlock(&rwlock); +} + void rlc::resume_bearer(uint32_t lcid) { pthread_rwlock_wrlock(&rwlock); + rlc_log->info("Resuming radio bearer %s\n", rrc->get_rb_name(lcid).c_str()); if (valid_lcid(lcid)) { if (rlc_array.at(lcid)->resume()) { rlc_log->info("Resumed radio bearer %s\n", rrc->get_rb_name(lcid).c_str()); } else { - rlc_log->error("Error resuming RLC entity\n."); + rlc_log->error("Error resuming RLC entity: bearer not suspended\n."); } } else { rlc_log->error("Resuming bearer: bearer %s not configured.\n", rrc->get_rb_name(lcid).c_str()); diff --git a/lib/src/upper/rlc_am.cc b/lib/src/upper/rlc_am.cc index 24f3a13f8..7690a557b 100644 --- a/lib/src/upper/rlc_am.cc +++ b/lib/src/upper/rlc_am.cc @@ -51,12 +51,14 @@ rlc_am::~rlc_am() { } -bool rlc_am::resume() +// Applies new configuration. Must be just reestablished or initiated +bool rlc_am::configure(rlc_config_t cfg_) { - if (not has_configuration) { - log->error("Error resuming bearer: no previous configuration found\n"); - return false; - } + // determine bearer name and configure Rx/Tx objects + rb_name = rrc->get_rb_name(lcid); + + // store config + cfg = cfg_; if (not rx.configure(cfg.am)) { log->error("Error resuming bearer (RX)\n"); @@ -68,34 +70,18 @@ bool rlc_am::resume() return false; } + log->info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " + "t_reordering=%d, t_status_prohibit=%d\n", + rb_name.c_str(), + cfg.am.t_poll_retx, + cfg.am.poll_pdu, + cfg.am.poll_byte, + cfg.am.max_retx_thresh, + cfg.am.t_reordering, + cfg.am.t_status_prohibit); return true; } -bool rlc_am::configure(rlc_config_t cfg_) -{ - // determine bearer name and configure Rx/Tx objects - rb_name = rrc->get_rb_name(lcid); - - // store config - cfg = cfg_; - has_configuration = true; - - if (resume()) { - log->info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, " - "t_reordering=%d, t_status_prohibit=%d\n", - rb_name.c_str(), - cfg.am.t_poll_retx, - cfg.am.poll_pdu, - cfg.am.poll_byte, - cfg.am.max_retx_thresh, - cfg.am.t_reordering, - cfg.am.t_status_prohibit); - return true; - } else { - return false; - } -} - void rlc_am::empty_queue() { // Drop all messages in TX SDU queue diff --git a/lib/src/upper/rlc_tm.cc b/lib/src/upper/rlc_tm.cc index 3ace9063b..bcc6757fc 100644 --- a/lib/src/upper/rlc_tm.cc +++ b/lib/src/upper/rlc_tm.cc @@ -49,11 +49,6 @@ bool rlc_tm::configure(rlc_config_t cnfg) return true; } -bool rlc_tm::resume() -{ - return true; -} - void rlc_tm::empty_queue() { // Drop all messages in TX queue diff --git a/lib/src/upper/rlc_um.cc b/lib/src/upper/rlc_um.cc index a5ca951da..aa059d24a 100644 --- a/lib/src/upper/rlc_um.cc +++ b/lib/src/upper/rlc_um.cc @@ -48,12 +48,13 @@ rlc_um::~rlc_um() stop(); } -bool rlc_um::resume() +bool rlc_um::configure(rlc_config_t cnfg_) { - if (not has_configuration) { - log->error("Error resuming bearer: no previous configuration found\n"); - return false; - } + // determine bearer name and configure Rx/Tx objects + rb_name = get_rb_name(rrc, lcid, cnfg_.um.is_mrb); + + // store config + cfg = cnfg_; if (not rx.configure(cfg, rb_name)) { return false; @@ -63,31 +64,15 @@ bool rlc_um::resume() return false; } + log->info("%s configured in %s: t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n", + rb_name.c_str(), + srslte::to_string(cnfg_.rlc_mode).c_str(), + cfg.um.t_reordering, + srslte::to_number(cfg.um.rx_sn_field_length), + srslte::to_number(cfg.um.tx_sn_field_length)); return true; } -bool rlc_um::configure(rlc_config_t cnfg_) -{ - // determine bearer name and configure Rx/Tx objects - rb_name = get_rb_name(rrc, lcid, cnfg_.um.is_mrb); - - // store config - cfg = cnfg_; - has_configuration = true; - - if (resume()) { - log->info("%s configured in %s: t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n", - rb_name.c_str(), - srslte::to_string(cnfg_.rlc_mode).c_str(), - cfg.um.t_reordering, - srslte::to_number(cfg.um.rx_sn_field_length), - srslte::to_number(cfg.um.tx_sn_field_length)); - return true; - } else { - return false; - } -} - bool rlc_um::rlc_um_rx::configure(rlc_config_t cnfg_, std::string rb_name_) { cfg = cnfg_.um; diff --git a/lib/test/upper/CMakeLists.txt b/lib/test/upper/CMakeLists.txt index 46fefac67..43e77ce01 100644 --- a/lib/test/upper/CMakeLists.txt +++ b/lib/test/upper/CMakeLists.txt @@ -47,6 +47,10 @@ add_executable(rlc_um_test rlc_um_test.cc) target_link_libraries(rlc_um_test srslte_upper srslte_phy) add_test(rlc_um_test rlc_um_test) +add_executable(rlc_common_test rlc_common_test.cc) +target_link_libraries(rlc_common_test srslte_upper srslte_phy) +add_test(rlc_common_test rlc_common_test) + ######################################################################## # Option to run command after build (useful for remote builds) ######################################################################## diff --git a/lib/test/upper/rlc_common_test.cc b/lib/test/upper/rlc_common_test.cc new file mode 100644 index 000000000..d6cbc5fd1 --- /dev/null +++ b/lib/test/upper/rlc_common_test.cc @@ -0,0 +1,224 @@ +/* + * Copyright 2013-2019 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/common/log_filter.h" +#include "srslte/upper/rlc.h" +#include + +#define TESTASSERT(cond) \ + { \ + if (!(cond)) { \ + std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \ + return -1; \ + } \ + } + +#define MAX_NBUFS 100 +#define NBUFS 5 + +using namespace srslte; +using namespace srsue; +using namespace asn1::rrc; + +class mac_dummy_timers : public srslte::mac_interface_timers +{ +public: + srslte::timers::timer* timer_get(uint32_t timer_id) { return &t; } + uint32_t timer_get_unique_id() { return 0; } + void step() { t.step(); } + void timer_release_id(uint32_t timer_id) {} + +private: + srslte::timers::timer t; +}; + +class rlc_tester : public pdcp_interface_rlc, public rrc_interface_rlc +{ +public: + rlc_tester() + { + bzero(sdus, sizeof(sdus)); + n_sdus = 0; + expected_sdu_len = 0; + } + + // PDCP interface + void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu) + { + if (lcid != 3 && sdu->N_bytes != expected_sdu_len) { + printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, expected_sdu_len); + exit(-1); + } + sdus[n_sdus++] = std::move(sdu); + } + void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} + void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {} + void write_pdu_pcch(unique_byte_buffer_t sdu) {} + void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t sdu) { sdus[n_sdus++] = std::move(sdu); } + + // RRC interface + void max_retx_attempted() {} + std::string get_rb_name(uint32_t lcid) { return std::string("TestRB"); } + void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; } + + unique_byte_buffer_t sdus[MAX_NBUFS]; + int n_sdus; + uint32_t expected_sdu_len; +}; + +int basic_test() +{ + srslte::log_filter log1("RLC_1"); + srslte::log_filter log2("RLC_2"); + log1.set_level(srslte::LOG_LEVEL_DEBUG); + log2.set_level(srslte::LOG_LEVEL_DEBUG); + log1.set_hex_limit(-1); + log2.set_hex_limit(-1); + rlc_tester tester; + mac_dummy_timers timers; + + int len = 0; + + rlc rlc1(&log1); + rlc rlc2(&log2); + + rlc1.init(&tester, &tester, &timers, 0); + rlc2.init(&tester, &tester, &timers, 0); + + rlc_config_t cnfg = rlc_config_t::default_rlc_um_config(10); + cnfg.rlc_mode = rlc_mode_t::um; + cnfg.um.t_reordering = 5; + cnfg.um.rx_sn_field_length = rlc_umd_sn_size_t::size10bits; + cnfg.um.rx_window_size = 512; + cnfg.um.rx_mod = 1024; + cnfg.um.tx_sn_field_length = rlc_umd_sn_size_t::size10bits; + cnfg.um.tx_mod = 1024; + + uint32_t lcid = 1; + rlc1.add_bearer(lcid, cnfg); + rlc2.add_bearer(lcid, cnfg); + + byte_buffer_pool* pool = byte_buffer_pool::get_instance(); + unique_byte_buffer_t sdu_bufs[NBUFS]; + + tester.set_expected_sdu_len(1); + + // Push 5 SDUs into RLC1 + for (int i = 0; i < NBUFS; i++) { + sdu_bufs[i] = srslte::allocate_unique_buffer(*pool, true); + *sdu_bufs[i]->msg = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte + rlc1.write_sdu(lcid, std::move(sdu_bufs[i])); + } + + TESTASSERT(14 == rlc1.get_buffer_state(lcid)); + + // Reestablish + rlc1.reestablish(1); + + TESTASSERT(0 == rlc1.get_buffer_state(lcid)); + + // Push again 5 SDUs, SN should start from 0 + for (int i = 0; i < NBUFS; i++) { + sdu_bufs[i] = srslte::allocate_unique_buffer(*pool, true); + *sdu_bufs[i]->msg = i; // Write the index into the buffer + sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte + rlc1.write_sdu(lcid, std::move(sdu_bufs[i])); + } + + TESTASSERT(14 == rlc1.get_buffer_state(lcid)); + + // Read 5 PDUs from RLC1 (1 byte each) + byte_buffer_t pdu_bufs[NBUFS]; + for (int i = 0; i < NBUFS; i++) { + len = rlc1.read_pdu(lcid, pdu_bufs[i].msg, 4); // 3 bytes for header + payload + pdu_bufs[i].N_bytes = len; + } + + TESTASSERT(0 == rlc1.get_buffer_state(lcid)); + + // Write 5 PDUs into RLC2 + for (int i = 0; i < NBUFS; i++) { + rlc2.write_pdu(lcid, pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } + + // Check they have been passed to PDCP + TESTASSERT(NBUFS == tester.n_sdus); + + rlc2.reestablish(lcid); + + tester.n_sdus = 0; + + // Push again + for (int i = 0; i < NBUFS; i++) { + rlc2.write_pdu(lcid, pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } + + // Check the are again in the buffer + TESTASSERT(NBUFS == tester.n_sdus); + + for (int i = 0; i < NBUFS; i++) { + TESTASSERT(tester.sdus[i]->N_bytes == 1); + TESTASSERT(*(tester.sdus[i]->msg) == i); + } + + // Resume unexisting and unsuspended bearer + rlc2.resume_bearer(lcid + 1); + rlc2.resume_bearer(lcid); + + // Suspend unexisting bearer and twice + rlc2.suspend_bearer(lcid + 1); + rlc2.suspend_bearer(lcid); + rlc2.suspend_bearer(lcid); + + // Reestablish and push again while in suspended + rlc2.reestablish(lcid); + + tester.n_sdus = 0; + + // Push again + for (int i = 0; i < NBUFS; i++) { + rlc2.write_pdu(lcid, pdu_bufs[i].msg, pdu_bufs[i].N_bytes); + } + + // Check they are not being passed to PDCP + TESTASSERT(0 == tester.n_sdus); + + rlc2.resume_bearer(lcid); + + // Check now they are being passed + TESTASSERT(NBUFS == tester.n_sdus); + + for (int i = 0; i < NBUFS; i++) { + TESTASSERT(tester.sdus[i]->N_bytes == 1); + TESTASSERT(*(tester.sdus[i]->msg) == i); + } + + return 0; +} + +int main(int argc, char** argv) +{ + if (basic_test()) { + return -1; + } + byte_buffer_pool::get_instance()->cleanup(); +} diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index b3e8c6976..37e237493 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -1812,8 +1812,12 @@ void rrc::proc_con_restablish_request() mac_timers->timer_get(t311)->reset(); mac_timers->timer_get(t311)->run(); - // TODO: suspend all RBs except SRB0; - // is this that RLC tx queue stops accepting SDUs? + // Suspend all RB except SRB0 + for (int i = 1; i < SRSLTE_N_RADIO_BEARERS; i++) { + if (rlc->has_bearer(i)) { + rlc->suspend_bearer(i); + } + } // reset MAC; mac->reset(); @@ -3068,7 +3072,7 @@ void rrc::handle_con_reest(rrc_conn_reest_s* setup) // Apply the Radio Resource configuration apply_rr_config_dedicated(&setup->crit_exts.c1().rrc_conn_reest_r8().rr_cfg_ded); - // Resume SRB1 (if already configured in rr_config, this function does nothing) + // Resume SRB1 rlc->resume_bearer(1); // Send ConnectionSetupComplete message