diff --git a/lib/include/srsran/rlc/rlc_am_nr.h b/lib/include/srsran/rlc/rlc_am_nr.h index 717752d9f..7b24f8299 100644 --- a/lib/include/srsran/rlc/rlc_am_nr.h +++ b/lib/include/srsran/rlc/rlc_am_nr.h @@ -140,6 +140,7 @@ public: // Window helpers bool inside_tx_window(uint32_t sn) const; + bool valid_ack_sn(uint32_t sn) const; private: rlc_am* parent = nullptr; @@ -248,7 +249,8 @@ public: // Data handling methods int handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, const uint8_t* payload, uint32_t nof_bytes); int handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header, const uint8_t* payload, uint32_t nof_bytes); - bool inside_rx_window(uint32_t sn); + bool inside_rx_window(uint32_t sn) const; + bool valid_ack_sn(uint32_t sn) const; void write_to_upper_layers(uint32_t lcid, unique_byte_buffer_t sdu); void insert_received_segment(rlc_amd_rx_pdu_nr segment, rlc_amd_rx_sdu_nr_t::segment_list_t& segment_list) const; /** diff --git a/lib/src/rlc/rlc_am_nr.cc b/lib/src/rlc/rlc_am_nr.cc index ef8959241..c38e90c58 100644 --- a/lib/src/rlc/rlc_am_nr.cc +++ b/lib/src/rlc/rlc_am_nr.cc @@ -186,7 +186,7 @@ uint32_t rlc_am_nr_tx::build_new_pdu(uint8_t* payload, uint32_t nof_bytes) } // do not build any more PDU if window is already full - if (tx_window->size() >= RLC_AM_WINDOW_SIZE) { + if (tx_window->full()) { RlcInfo("Cannot build data PDU - Tx window full."); return 0; } @@ -793,17 +793,20 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) /* * Sanity check the received status report. - * Checking if the ACK_SN is inside the the TX_WINDOW makes sure we discard out of order status reports - * Checking if ACK_SN > Tx_Next makes sure we do not receive a ACK/NACK for something we did not TX + * Checking if the ACK_SN is inside the valid ACK_SN window (the TX window "off-by-one") + * makes sure we discard out of order status reports. + * Checking if ACK_SN > Tx_Next + 1 makes sure we do not receive a ACK/NACK for something we did not TX + * ACK_SN may be equal to TX_NEXT + 1, if not all SDU segments with SN=TX_NEXT have been transmitted. */ - if (not inside_tx_window(status.ack_sn)) { + if (not valid_ack_sn(status.ack_sn)) { RlcInfo("Received ACK with SN outside of TX_WINDOW, ignoring status report. ACK_SN=%d, TX_NEXT_ACK=%d.", status.ack_sn, st.tx_next_ack); info_state(); return; } - if (tx_mod_base_nr(status.ack_sn) > tx_mod_base_nr(st.tx_next)) { + + if (tx_mod_base_nr(status.ack_sn) > tx_mod_base_nr(st.tx_next + 1)) { RlcWarning("Received ACK with SN larger than TX_NEXT, ignoring status report. SN=%d, TX_NEXT_ACK=%d, TX_NEXT=%d", status.ack_sn, st.tx_next_ack, @@ -819,7 +822,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) * - if t-PollRetransmit is running: * - stop and reset t-PollRetransmit. */ - if (tx_mod_base_nr(st.poll_sn) <= tx_mod_base_nr(status.ack_sn)) { + if (tx_mod_base_nr(st.poll_sn) < tx_mod_base_nr(status.ack_sn)) { if (poll_retransmit_timer.is_running()) { RlcDebug("Received ACK or NACK for POLL_SN=%d. Stopping t-PollRetransmit", st.poll_sn); poll_retransmit_timer.stop(); @@ -1318,6 +1321,21 @@ bool rlc_am_nr_tx::inside_tx_window(uint32_t sn) const return tx_mod_base_nr(sn) < tx_window_size(); } +/* + * This function is used to check if a received status report + * as a valid ACK_SN. + * + * ACK_SN may be equal to TX_NEXT + AM_Window_Size if the PDU + * with SN=TX_NEXT+AM_Window_Size has been received by the RX + * An ACK_SN == Tx_Next_Ack doesn't ACK or NACKs any PDUs, as + * such, such a status report can be discarded. + */ +bool rlc_am_nr_tx::valid_ack_sn(uint32_t sn) const +{ + // Tx_Next_Ack < SN <= TX_Next + AM_Window_Size + return (0 < tx_mod_base_nr(sn)) && (tx_mod_base_nr(sn) <= tx_window_size()); +} + /* * Debug Helpers */ @@ -1428,21 +1446,25 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes) RlcHexInfo(payload, nof_bytes, "Rx data PDU SN=%d (%d B)", header.sn, nof_bytes); log_rlc_am_nr_pdu_header_to_string(logger.debug, header, rb_name); - // Check whether SDU is within Rx Window - if (!inside_rx_window(header.sn)) { - RlcInfo("SN=%d outside rx window [%d:%d] - discarding", header.sn, st.rx_next, st.rx_next + rx_window_size()); - return; - } - // Trigger polling if poll bit is set. - // We do this before discarding duplicate SDUs/SDU segments + // We do this before checking if the PDU is inside the RX window, + // as the RX window may have advanced without the TX having received the ACKs + // This can cause a data stall, whereby the TX keeps retransmiting + // a PDU outside of the Rx window. + // Also, we do this before discarding duplicate SDUs/SDU segments // Because t-PollRetransmit may transmit a PDU that was already // received. - if (header.p) { + if (header.p != 0U) { RlcInfo("status packet requested through polling bit"); do_status = true; } + // Check whether SDU is within Rx Window + if (!inside_rx_window(header.sn)) { + RlcInfo("SN=%d outside rx window [%d:%d] - discarding", header.sn, st.rx_next, st.rx_next + rx_window_size()); + return; + } + // Section 5.2.3.2.2, discard duplicate PDUs if (rx_window->has_sn(header.sn) && (*rx_window)[header.sn].fully_received) { RlcInfo("discarding duplicate SN=%d", header.sn); @@ -1853,11 +1875,11 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id) } } st.rx_highest_status = sn_upd; - if (not inside_rx_window(st.rx_highest_status)) { + if (not valid_ack_sn(st.rx_highest_status)) { RlcError("Rx_Highest_Status not inside RX window"); debug_state(); } - srsran_assert(inside_rx_window(st.rx_highest_status), "Error: rx_highest_status assigned outside rx window"); + srsran_assert(valid_ack_sn(st.rx_highest_status), "Error: rx_highest_status assigned outside rx window"); bool restart_reassembly_timer = false; if (rx_mod_base_nr(st.rx_next_highest) > rx_mod_base_nr(st.rx_highest_status + 1)) { @@ -1946,12 +1968,27 @@ uint32_t rlc_am_nr_rx::rx_window_size() const return am_window_size(cfg.rx_sn_field_length); } -bool rlc_am_nr_rx::inside_rx_window(uint32_t sn) +bool rlc_am_nr_rx::inside_rx_window(uint32_t sn) const { // RX_Next <= SN < RX_Next + AM_Window_Size return rx_mod_base_nr(sn) < rx_window_size(); } +/* + * This function is used to check if the Rx_Highest_Status is + * valid when t-Reasseambly expires. + * + * ACK_SN may be equal to RX_NEXT + AM_Window_Size if the PDU + * with SN=RX_NEXT+AM_Window_Size has been received by the RX. + * An ACK_SN == Rx_Next should not update Rx_Highest_Status, + * it should be updated when Rx_Next is updated. + */ +bool rlc_am_nr_rx::valid_ack_sn(uint32_t sn) const +{ + // RX_Next < SN <= RX_Next + AM_Window_Size + return (0 < rx_mod_base_nr(sn)) && (rx_mod_base_nr(sn) <= rx_window_size()); +} + /* * Debug Helpers */ diff --git a/lib/test/rlc/rlc_am_lte_test.cc b/lib/test/rlc/rlc_am_lte_test.cc index 7b368d897..50ecc5d8a 100644 --- a/lib/test/rlc/rlc_am_lte_test.cc +++ b/lib/test/rlc/rlc_am_lte_test.cc @@ -105,7 +105,7 @@ int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS]) int basic_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -164,7 +164,7 @@ int basic_test() int concat_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); @@ -238,7 +238,7 @@ int concat_test() int segment_test(bool in_seq_rx) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; @@ -333,7 +333,7 @@ int segment_test(bool in_seq_rx) int retx_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; @@ -469,7 +469,7 @@ int retx_test() // Test correct upper layer signaling when maxRetx (default 4) have been reached int max_retx_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; @@ -532,7 +532,7 @@ int max_retx_test() // Purpose: test correct retx of lost segment and pollRetx timer expiration int segment_retx_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; @@ -657,7 +657,7 @@ int resegment_test_1() // PDUs: | 10 | 10 | 10 | 10 | 10 | // Retx PDU segments: | 5 | 5| - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; @@ -815,7 +815,7 @@ int resegment_test_2() // PDUs: | 5 | 10 | 20 | 10 | 5 | // Retx PDU segments: | 10 | 10 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); @@ -947,7 +947,7 @@ int resegment_test_3() // PDUs: | 5 | 5| 20 | 10 | 10 | // Retx PDU segments: | 10 | 10 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); @@ -1077,7 +1077,7 @@ int resegment_test_4() // PDUs: | 5 | 5| 30 | 5 | 5| // Retx PDU segments: | 15 | 15 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); @@ -1209,7 +1209,7 @@ int resegment_test_5() // PDUs: |2|3| 40 |3|2| // Retx PDU segments: | 20 | 20 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); @@ -1335,7 +1335,7 @@ int resegment_test_6() // PDUs: |10|10|10| 270 | 54 | // Retx PDU segments: | 120 | 150 | - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; @@ -1502,9 +1502,9 @@ int resegment_test_7() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_test7.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, nullptr); #endif srsran::timer_handler timers(8); @@ -1687,9 +1687,9 @@ int resegment_test_8() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_test8.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, nullptr); #endif srsran::timer_handler timers(8); @@ -1839,9 +1839,9 @@ int resegment_test_9() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_resegment_test_9.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, nullptr); #endif srsran::timer_handler timers(8); @@ -1981,9 +1981,9 @@ int resegment_test_10() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_resegment_test_10.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2130,9 +2130,9 @@ int resegment_test_11() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_resegment_test_11.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2288,9 +2288,9 @@ int resegment_test_12() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_resegment_test_12.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2432,9 +2432,9 @@ int header_reconstruction_test(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2495,9 +2495,9 @@ int header_reconstruction_test2(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test2.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2558,9 +2558,9 @@ int header_reconstruction_test3(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test3.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2644,9 +2644,9 @@ int header_reconstruction_test4(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test4.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2721,9 +2721,9 @@ int header_reconstruction_test5(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test5.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2800,9 +2800,9 @@ int header_reconstruction_test6(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test6.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -2899,9 +2899,9 @@ int header_reconstruction_test7(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test7.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -3001,9 +3001,9 @@ int header_reconstruction_test8(srsran::log_sink_message_spy& spy) #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_header_reconstruction_test8.pcap", rlc_config_t::default_rlc_am_config()); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -3035,7 +3035,7 @@ int header_reconstruction_test8(srsran::log_sink_message_spy& spy) bool reset_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; @@ -3077,7 +3077,7 @@ bool reset_test() bool resume_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; @@ -3119,7 +3119,7 @@ bool resume_test() bool stop_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); rlc_am rlc1(srsran_rat_t::lte, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); @@ -3145,7 +3145,7 @@ bool stop_test() // be enough to fit all SNs that would need to be NACKed bool status_pdu_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; @@ -3254,7 +3254,7 @@ bool status_pdu_test() // The incidence is reported to the upper layers. bool incorrect_status_pdu_test() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; @@ -3319,7 +3319,7 @@ bool incorrect_status_pdu_test() /// In contrast to the without explicitly NACK-ing specific SNs bool incorrect_status_pdu_test2() { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); srsran::timer_handler timers(8); int len = 0; @@ -3430,9 +3430,9 @@ bool reestablish_test() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_reestablish_test.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, nullptr); #endif srsran::timer_handler timers(8); @@ -3550,9 +3550,9 @@ bool discard_test() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_reestablish_test.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); @@ -3640,9 +3640,9 @@ bool poll_retx_expiry_test() #if HAVE_PCAP rlc_pcap pcap; pcap.open("rlc_am_poll_rext_expiry_test.pcap", config); - rlc_am_tester tester(&pcap); + rlc_am_tester tester(true, &pcap); #else - rlc_am_tester tester(NULL); + rlc_am_tester tester(true, NULL); #endif srsran::timer_handler timers(8); diff --git a/lib/test/rlc/rlc_am_nr_test.cc b/lib/test/rlc/rlc_am_nr_test.cc index db17454a3..413cb552c 100644 --- a/lib/test/rlc/rlc_am_nr_test.cc +++ b/lib/test/rlc/rlc_am_nr_test.cc @@ -69,7 +69,7 @@ int basic_test_tx(rlc_am* rlc, byte_buffer_t pdu_bufs[NBUFS], rlc_am_nr_sn_size_ */ int window_checker_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -133,7 +133,7 @@ int window_checker_test(rlc_am_nr_sn_size_t sn_size) */ int retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -219,7 +219,7 @@ int retx_segmentation_required_checker_test(rlc_am_nr_sn_size_t sn_size) */ int basic_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -327,7 +327,7 @@ int basic_test(rlc_am_nr_sn_size_t sn_size) */ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -502,7 +502,7 @@ int lost_pdu_test(rlc_am_nr_sn_size_t sn_size) */ int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -687,7 +687,7 @@ int lost_pdu_duplicated_nack_test(rlc_am_nr_sn_size_t sn_size) */ int lost_pdus_trimmed_nack_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -906,7 +906,7 @@ int lost_pdus_trimmed_nack_test(rlc_am_nr_sn_size_t sn_size) */ int clean_retx_queue_of_acked_sdus_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -1094,7 +1094,7 @@ int clean_retx_queue_of_acked_sdus_test(rlc_am_nr_sn_size_t sn_size) */ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); test_delimit_logger delimiter("basic segmentation ({} bit SN)", to_number(sn_size)); @@ -1186,7 +1186,7 @@ int basic_segmentation_test(rlc_am_nr_sn_size_t sn_size) // - Check metrics and state int segment_retx_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -1385,7 +1385,7 @@ int segment_retx_test(rlc_am_nr_sn_size_t sn_size) // - Check metrics and state int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -1649,7 +1649,7 @@ int segment_retx_and_loose_segments_test(rlc_am_nr_sn_size_t sn_size) int retx_segment_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -1961,11 +1961,100 @@ int retx_segment_test(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } +// We only increment TX_NEXT after transmitting the last segment of a SDU +// This means that we need to handle status reports where ACK_SN may be larger +// than TX_NEXT, as it may contain a NACK for the partially transmitted PDU with +// SN==TX_NEXT. +int handle_status_of_non_tx_last_segment(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("basic segmentation ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + if (not rlc1.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + if (not rlc2.configure(rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)))) { + return -1; + } + + // after configuring entity + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + + // Push 1 SDU into RLC1 + unique_byte_buffer_t sdu; + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + sdu = srsran::make_byte_buffer(); + TESTASSERT(nullptr != sdu); + sdu->msg[0] = 0; // Write the index into the buffer + sdu->N_bytes = payload_size; // Give the SDU the size of 3 bytes + sdu->md.pdcp_sn = 0; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu)); + + // Read 2 PDUs. Leave last one in the tx_window. + constexpr uint16_t n_pdus = 2; + unique_byte_buffer_t pdu_bufs[n_pdus]; + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + constexpr uint32_t so_size = 2; + constexpr uint32_t segment_size = 1; + uint32_t pdu_size_first = header_size + segment_size; + uint32_t pdu_size_continued = header_size + so_size + segment_size; + for (int i = 0; i < n_pdus; i++) { + pdu_bufs[i] = srsran::make_byte_buffer(); + TESTASSERT(nullptr != pdu_bufs[i]); + if (i == 0) { + pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); + TESTASSERT_EQ(pdu_size_first, pdu_bufs[i]->N_bytes); + } else { + pdu_bufs[i]->N_bytes = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); + TESTASSERT_EQ(pdu_size_continued, pdu_bufs[i]->N_bytes); + } + } + + // Only middle PDU into RLC2 + // First PDU is lost to trigger status report + for (int i = 0; i < n_pdus; i++) { + if (i == 1) { + rlc2.write_pdu(pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes); + } + } + + // Advance timer to trigger status report + for (uint8_t t = 0; t < 35; t++) { + timers.step_all(); + } + TESTASSERT_NEQ(0, rlc2.get_buffer_state()); + + // Make sure RLC 1 has only the last segment to TX before getting the status report + TESTASSERT_EQ(pdu_size_continued, rlc1.get_buffer_state()); + + // Get status report from RLC 2 + // and write it to RLC 1 + { + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + status_buf->N_bytes = rlc2.read_pdu(status_buf->msg, 100); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + } + + // Make sure RLC 1 now has the last segment to TX and the RETX of the first segment + TESTASSERT_EQ(pdu_size_continued + pdu_size_first, rlc1.get_buffer_state()); + return SRSRAN_SUCCESS; +} + // This test checks whether RLC informs upper layer when max retransmission has been reached // due to lost SDUs as a whole int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; @@ -2040,7 +2129,7 @@ int max_retx_lost_sdu_test(rlc_am_nr_sn_size_t sn_size) // due to lost SDU segments int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); int len = 0; @@ -2169,7 +2258,7 @@ int max_retx_lost_segments_test(rlc_am_nr_sn_size_t sn_size) // This test checks the correct functioning of RLC discard functionality int discard_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -2275,7 +2364,7 @@ int discard_test(rlc_am_nr_sn_size_t sn_size) // Test p bit set on new TX with PollPDU int poll_pdu(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -2331,7 +2420,7 @@ int poll_pdu(rlc_am_nr_sn_size_t sn_size) // Test p bit set on new TX with PollBYTE int poll_byte(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -2387,7 +2476,7 @@ int poll_byte(rlc_am_nr_sn_size_t sn_size) // Test p bit set on RETXes that cause an empty retx queue. int poll_retx(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -2505,7 +2594,7 @@ int poll_retx(rlc_am_nr_sn_size_t sn_size) // It checks if the poll retx timer is re-armed upon receiving an ACK for POLL_SN bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -2660,7 +2749,7 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size) int rx_nack_range_no_so_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -2752,7 +2841,7 @@ int rx_nack_range_no_so_test(rlc_am_nr_sn_size_t sn_size) int rx_nack_range_with_so_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -2848,7 +2937,7 @@ int rx_nack_range_with_so_test(rlc_am_nr_sn_size_t sn_size) int rx_nack_range_with_so_starting_with_full_sdu_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -2955,7 +3044,7 @@ int rx_nack_range_with_so_starting_with_full_sdu_test(rlc_am_nr_sn_size_t sn_siz int rx_nack_range_with_so_ending_with_full_sdu_test(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); auto& test_logger = srslog::fetch_basic_logger("TESTER "); @@ -3062,7 +3151,7 @@ int rx_nack_range_with_so_ending_with_full_sdu_test(rlc_am_nr_sn_size_t sn_size) int out_of_order_status(rlc_am_nr_sn_size_t sn_size) { - rlc_am_tester tester; + rlc_am_tester tester(true, nullptr); timer_handler timers(8); byte_buffer_t pdu_bufs[NBUFS]; @@ -3130,6 +3219,152 @@ int out_of_order_status(rlc_am_nr_sn_size_t sn_size) return SRSRAN_SUCCESS; } +// If we lose the status report +int lost_status_and_advanced_rx_window(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(true, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("Lost status report and advance RX window ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc1.configure(cfg)) { + return -1; + } + if (not rlc2.configure(cfg)) { + return -1; + } + uint32_t mod_nr = cardinality(cfg.am_nr.tx_sn_field_length); + + // Fill up the RX window + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (uint32_t sn = 0; sn < 10; ++sn) { + // Write SDU + unique_byte_buffer_t sdu_buf = srsran::make_byte_buffer(); + sdu_buf->msg[0] = sn; // Write the index into the buffer + sdu_buf->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_buf->md.pdcp_sn = sn; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_buf)); + + // Read PDU + unique_byte_buffer_t pdu_buf = srsran::make_byte_buffer(); + pdu_buf->N_bytes = rlc1.read_pdu(pdu_buf->msg, 100); + + // Write PDU into RLC 2 + // We receive all PDUs + rlc2.write_pdu(pdu_buf->msg, pdu_buf->N_bytes); + } + + // We got the polling bit, so we generate the status report. + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + + // Read status PDU + { + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + status_buf->N_bytes = rlc2.read_pdu(status_buf->msg, 3); + } + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + + // We do not write the status report into RLC 1 + // We step trought the timers to let t-PollRetransmission expire + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + for (int t = 0; t < 45; t++) { + timers.step_all(); + } + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); + + // Read RETX of POLL_SN and check if it triggered the + // Status report + { + unique_byte_buffer_t pdu_buf = srsran::make_byte_buffer(); + pdu_buf->N_bytes = rlc1.read_pdu(pdu_buf->msg, 100); + TESTASSERT_EQ(0, rlc2.get_buffer_state()); + rlc2.write_pdu(pdu_buf->msg, pdu_buf->N_bytes); + TESTASSERT_EQ(3, rlc2.get_buffer_state()); + } + + return SRSRAN_SUCCESS; +} + +int full_rx_window_t_reassembly_expiry(rlc_am_nr_sn_size_t sn_size) +{ + rlc_am_tester tester(false, nullptr); + timer_handler timers(8); + byte_buffer_t pdu_bufs[NBUFS]; + + auto& test_logger = srslog::fetch_basic_logger("TESTER "); + test_delimit_logger delimiter("Full RX window and t-Reassmbly expiry test ({} bit SN)", to_number(sn_size)); + rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers); + rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers); + + rlc_am_nr_tx* tx1 = dynamic_cast(rlc1.get_tx()); + rlc_am_nr_rx* rx1 = dynamic_cast(rlc1.get_rx()); + rlc_am_nr_tx* tx2 = dynamic_cast(rlc2.get_tx()); + rlc_am_nr_rx* rx2 = dynamic_cast(rlc2.get_rx()); + + auto cfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size)); + if (not rlc1.configure(cfg)) { + return -1; + } + if (not rlc2.configure(cfg)) { + return -1; + } + uint32_t mod_nr = cardinality(cfg.am_nr.tx_sn_field_length); + + // Fill up the RX window + constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes + uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3; + for (uint32_t sn = 0; sn < am_window_size(sn_size); ++sn) { + // Write SDU + unique_byte_buffer_t sdu_buf = srsran::make_byte_buffer(); + sdu_buf->msg[0] = sn; // Write the index into the buffer + sdu_buf->N_bytes = payload_size; // Give each buffer a size of 3 bytes + sdu_buf->md.pdcp_sn = sn; // PDCP SN for notifications + rlc1.write_sdu(std::move(sdu_buf)); + + // Read PDU + unique_byte_buffer_t pdu_buf = srsran::make_byte_buffer(); + pdu_buf->N_bytes = rlc1.read_pdu(pdu_buf->msg, 100); + + // Write PDUs into RLC 2 + // Do not write SN=0 to fill up the RX window + if (sn != 0) { + rlc2.write_pdu(pdu_buf->msg, pdu_buf->N_bytes); + } + } + + // Step timers until reassambly timeout expires + for (int cnt = 0; cnt < 35; cnt++) { + timers.step_all(); + } + + // Read status PDU + { + TESTASSERT_EQ(0, rlc1.get_buffer_state()); + unique_byte_buffer_t status_buf = srsran::make_byte_buffer(); + status_buf->N_bytes = rlc2.read_pdu(status_buf->msg, 1000); + rlc1.write_pdu(status_buf->msg, status_buf->N_bytes); + TESTASSERT_EQ(header_size + payload_size, rlc1.get_buffer_state()); + } + // Check Rx_Status_Highest + { + rlc_am_nr_rx_state_t st = rx2->get_rx_state(); + TESTASSERT_EQ(2048, st.rx_highest_status); + } + + return SRSRAN_SUCCESS; +} + int main() { // Setup the log message spy to intercept error and warning log entries from RLC @@ -3168,6 +3403,7 @@ int main() TESTASSERT(segment_retx_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(segment_retx_and_loose_segments_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(retx_segment_test(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(handle_status_of_non_tx_last_segment(sn_size) == SRSRAN_SUCCESS); TESTASSERT(max_retx_lost_sdu_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(max_retx_lost_segments_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(discard_test(sn_size) == SRSRAN_SUCCESS); @@ -3180,6 +3416,8 @@ int main() TESTASSERT(rx_nack_range_with_so_starting_with_full_sdu_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(rx_nack_range_with_so_ending_with_full_sdu_test(sn_size) == SRSRAN_SUCCESS); TESTASSERT(out_of_order_status(sn_size) == SRSRAN_SUCCESS); + TESTASSERT(lost_status_and_advanced_rx_window(sn_size) == SRSRAN_SUCCESS); } + TESTASSERT(full_rx_window_t_reassembly_expiry(rlc_am_nr_sn_size_t::size12bits) == SRSRAN_SUCCESS); return SRSRAN_SUCCESS; } diff --git a/lib/test/rlc/rlc_test_common.h b/lib/test/rlc/rlc_test_common.h index 808aa2cf1..90086b752 100644 --- a/lib/test/rlc/rlc_test_common.h +++ b/lib/test/rlc/rlc_test_common.h @@ -81,13 +81,15 @@ public: class rlc_am_tester : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc { public: - rlc_am_tester(rlc_pcap* pcap_ = NULL) : pcap(pcap_) {} + explicit rlc_am_tester(bool save_sdus_, rlc_pcap* pcap_) : save_sdus(save_sdus_), pcap(pcap_) {} // PDCP interface void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu) { assert(lcid == 1); - sdus.push_back(std::move(sdu)); + if (save_sdus) { + sdus.push_back(std::move(sdu)); + } } void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {} void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {} @@ -119,6 +121,7 @@ public: rlc_pcap* pcap = nullptr; bool max_retx_triggered = false; bool protocol_failure_triggered = false; + bool save_sdus = true; std::map notified_counts; // Map of PDCP SNs to number of notifications }; diff --git a/srsenb/hdr/stack/rrc/mac_controller.h b/srsenb/hdr/stack/rrc/mac_controller.h index 1aab03e9d..61f4040e9 100644 --- a/srsenb/hdr/stack/rrc/mac_controller.h +++ b/srsenb/hdr/stack/rrc/mac_controller.h @@ -70,6 +70,7 @@ public: bool is_crnti_set() const { return crnti_set; } void set_scell_activation(const std::bitset& scell_mask); + void set_srb2_activation(bool active); void set_drb_activation(bool active); void update_mac(); diff --git a/srsenb/src/stack/rrc/mac_controller.cc b/srsenb/src/stack/rrc/mac_controller.cc index 3fb674177..e744b2783 100644 --- a/srsenb/src/stack/rrc/mac_controller.cc +++ b/srsenb/src/stack/rrc/mac_controller.cc @@ -146,6 +146,9 @@ int mac_controller::handle_crnti_ce(uint32_t temp_crnti) current_sched_ue_cfg.ue_bearers[i] = next_sched_ue_cfg.ue_bearers[i]; } + // keep SRB2 disabled until RRCReconfComplete is received + set_srb2_activation(false); + return mac->ue_set_crnti(temp_crnti, rnti, current_sched_ue_cfg); } @@ -229,7 +232,10 @@ void mac_controller::handle_con_reconf_complete() { current_sched_ue_cfg = next_sched_ue_cfg; - // Setup all bearers + // Setup SRB2 + set_srb2_activation(true); + + // Setup all data bearers apply_current_bearers_cfg(); // Apply SCell+Bearer changes to MAC @@ -265,7 +271,9 @@ void mac_controller::handle_target_enb_ho_cmd(const asn1::rrc::rrc_conn_recfg_r8 ue_cfg_apply_capabilities(next_sched_ue_cfg, *rrc_cfg, uecaps); ue_cfg_apply_reconf_complete_updates(next_sched_ue_cfg, conn_recfg, ue_cell_list); - // Temporarily freeze new allocations for DRBs (SRBs are needed to send RRC Reconf Message) + // Temporarily freeze SRB2 and DRBs. SRB1 is needed to send + // RRC Reconfiguration and receive RRC Reconfiguration Complete + set_srb2_activation(false); set_drb_activation(false); // Apply changes to MAC scheduler @@ -329,6 +337,12 @@ void mac_controller::set_scell_activation(const std::bitset } } +void mac_controller::set_srb2_activation(bool active) +{ + current_sched_ue_cfg.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction = + active ? mac_lc_ch_cfg_t::BOTH : mac_lc_ch_cfg_t::IDLE; +} + void mac_controller::set_drb_activation(bool active) { for (const drb_to_add_mod_s& drb : bearer_list.get_established_drbs()) { diff --git a/srsenb/test/rrc/rrc_mobility_test.cc b/srsenb/test/rrc/rrc_mobility_test.cc index f8b592121..7abb07cb6 100644 --- a/srsenb/test/rrc/rrc_mobility_test.cc +++ b/srsenb/test/rrc/rrc_mobility_test.cc @@ -327,13 +327,24 @@ int test_s1ap_tenb_mobility(test_event test_params) TESTASSERT(tester.rrc.get_nof_users() == 0); return SRSRAN_SUCCESS; } + TESTASSERT(tester.mac.ue_db.count(0x46)); + auto& mac_ue = tester.mac.ue_db[0x46]; + TESTASSERT(mac_ue.supported_cc_list[0].active); + TESTASSERT(mac_ue.supported_cc_list[0].enb_cc_idx == 0); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); + tester.tic(); TESTASSERT(tester.rrc.get_nof_users() == 1); TESTASSERT(tester.mac.ue_db.count(0x46)); - auto& mac_ue = tester.mac.ue_db[0x46]; TESTASSERT(mac_ue.supported_cc_list[0].active); TESTASSERT(mac_ue.supported_cc_list[0].enb_cc_idx == 0); TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); // Check Security Configuration TESTASSERT(tester.pdcp.bearers.count(0x46)); TESTASSERT(tester.pdcp.bearers[0x46].count(srb_to_lcid(lte_srb::srb1)) and @@ -350,6 +361,10 @@ int test_s1ap_tenb_mobility(test_event test_params) TESTASSERT(tester.pdcp.bearers[0x46][srb_to_lcid(lte_srb::srb1)].sec_cfg.cipher_algo == as_sec_cfg.cipher_algo); TESTASSERT(tester.pdcp.bearers[0x46][srb_to_lcid(lte_srb::srb1)].sec_cfg.integ_algo == as_sec_cfg.integ_algo); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); + // Check if S1AP Handover Request ACK send is called TESTASSERT(tester.s1ap.last_ho_req_ack.rnti == 0x46); TESTASSERT(tester.s1ap.last_ho_req_ack.ho_cmd_pdu != nullptr); @@ -365,6 +380,10 @@ int test_s1ap_tenb_mobility(test_event test_params) TESTASSERT(dl_dcch_msg.msg.c1().type().value == dl_dcch_msg_type_c::c1_c_::types_opts::rrc_conn_recfg); auto& recfg_r8 = dl_dcch_msg.msg.c1().rrc_conn_recfg().crit_exts.c1().rrc_conn_recfg_r8(); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); + // Receives MMEStatusTransfer asn1::s1ap::bearers_subject_to_status_transfer_list_l bearers; bearers.resize(1); @@ -381,6 +400,10 @@ int test_s1ap_tenb_mobility(test_event test_params) TESTASSERT(tester.pdcp.bearers[0x46][3].state.next_pdcp_rx_sn == 120); TESTASSERT(tester.pdcp.bearers[0x46][3].state.rx_hfn == 4); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == mac_lc_ch_cfg_t::BOTH); + TESTASSERT(mac_ue.ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == mac_lc_ch_cfg_t::IDLE); + TESTASSERT(mac_ue.ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == mac_lc_ch_cfg_t::IDLE); + // user PRACHs and sends C-RNTI CE sched_interface::ue_cfg_t ue_cfg{}; ue_cfg.supported_cc_list.resize(1); @@ -496,7 +519,8 @@ int test_intraenb_mobility(srsran::log_sink_spy& spy, test_event test_params) TESTASSERT(tester.phy.last_cfg[0].enb_cc_idx == ue_cfg->supported_cc_list[0].enb_cc_idx); TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); - TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == srsenb::mac_lc_ch_cfg_t::IDLE); + TESTASSERT(ue_cfg->ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == srsenb::mac_lc_ch_cfg_t::IDLE); /* Test Case: The UE receives a duplicate C-RNTI CE. Nothing should happen */ if (test_params == test_event::duplicate_crnti_ce) { @@ -510,7 +534,8 @@ int test_intraenb_mobility(srsran::log_sink_spy& spy, test_event test_params) /* Test Case: Terminate first Handover. No extra messages should be sent DL. SR/CQI resources match recfg message */ uint8_t recfg_complete[] = {0x10, 0x00}; copy_msg_to_buffer(pdu, recfg_complete); - tester.rrc.write_pdu(tester.rnti, srb_to_lcid(lte_srb::srb2), std::move(pdu)); + tester.rrc.write_pdu(tester.rnti, srb_to_lcid(lte_srb::srb1), std::move(pdu)); + tester.tic(); TESTASSERT(tester.pdcp.last_sdu.sdu == nullptr); ue_cfg = &tester.mac.ue_db[tester.rnti]; TESTASSERT(ue_cfg->pucch_cfg.sr_configured); @@ -519,6 +544,10 @@ int test_intraenb_mobility(srsran::log_sink_spy& spy, test_event test_params) TESTASSERT(ue_cfg->supported_cc_list[0].dl_cfg.cqi_report.pmi_idx == phy_cfg_ded.cqi_report_cfg.cqi_report_periodic.setup().cqi_pmi_cfg_idx); TESTASSERT(ue_cfg->pucch_cfg.n_pucch == phy_cfg_ded.cqi_report_cfg.cqi_report_periodic.setup().cqi_pucch_res_idx); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb0)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb1)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[srb_to_lcid(lte_srb::srb2)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); + TESTASSERT(ue_cfg->ue_bearers[drb_to_lcid(lte_drb::drb1)].direction == srsenb::mac_lc_ch_cfg_t::BOTH); /* Test Case: The RRC should be able to start a new handover */ uint8_t meas_report[] = {0x08, 0x10, 0x38, 0x74, 0x00, 0x05, 0xBC, 0x80}; // PCI == 1